chore(editor): fix imports in legacy tests (#10300)

This commit is contained in:
Saul-Mirone
2025-02-20 03:30:05 +00:00
parent e0b2b2b33c
commit c3fc0a0d88
119 changed files with 96 additions and 90 deletions

View File

@@ -0,0 +1,592 @@
import type {
RichTextCell,
RichTextCellEditing,
} from '@blocksuite/affine-block-database';
import { ZERO_WIDTH_SPACE } from '@blocksuite/inline';
import { expect, type Locator, type Page } from '@playwright/test';
import {
pressEnter,
selectAllByKeyboard,
type,
} from '../utils/actions/keyboard.js';
import {
getBoundingBox,
getBoundingClientRect,
getEditorLocator,
waitNextFrame,
} from '../utils/actions/misc.js';
export async function press(page: Page, content: string) {
await page.keyboard.press(content, { delay: 50 });
await page.waitForTimeout(50);
}
export async function initDatabaseColumn(page: Page, title = '') {
const editor = getEditorLocator(page);
await editor.locator('affine-data-view-table-group').first().hover();
const columnAddBtn = editor.locator('.header-add-column-button');
await columnAddBtn.click();
await waitNextFrame(page, 200);
if (title) {
await selectAllByKeyboard(page);
await type(page, title);
await waitNextFrame(page);
await pressEnter(page);
} else {
await pressEnter(page);
}
}
export const renameColumn = async (page: Page, name: string) => {
const column = page.locator('affine-database-header-column', {
hasText: name,
});
await column.click();
};
export async function performColumnAction(
page: Page,
name: string,
action: string
) {
await renameColumn(page, name);
const actionMenu = page.locator(`.affine-menu-button`, { hasText: action });
await actionMenu.click();
}
export async function switchColumnType(
page: Page,
columnType: string,
columnIndex = 1
) {
const { typeIcon } = await getDatabaseHeaderColumn(page, columnIndex);
await typeIcon.click();
await clickColumnType(page, columnType);
}
export function clickColumnType(page: Page, columnType: string) {
const typeMenu = page.locator(`.affine-menu-button`, {
hasText: new RegExp(`${columnType}`),
});
return typeMenu.click();
}
export function getDatabaseBodyRows(page: Page) {
const rowContainer = page.locator('.affine-database-block-rows');
return rowContainer.locator('.database-row');
}
export function getDatabaseBodyRow(page: Page, rowIndex = 0) {
const rows = getDatabaseBodyRows(page);
return rows.nth(rowIndex);
}
export function getDatabaseTableContainer(page: Page) {
const container = page.locator('.affine-database-table-container');
return container;
}
export async function assertDatabaseTitleColumnText(
page: Page,
title: string,
index = 0
) {
const text = await page.evaluate(index => {
const rowContainer = document.querySelector('.affine-database-block-rows');
const row = rowContainer?.querySelector(
`.database-row:nth-child(${index + 1})`
);
const titleColumnCell = row?.querySelector('.database-cell:nth-child(1)');
const titleSpan = titleColumnCell?.querySelector(
'.data-view-header-area-rich-text'
) as HTMLElement;
if (!titleSpan) throw new Error('Cannot find database title column editor');
return titleSpan.innerText;
}, index);
if (title === '') {
expect(text).toMatch(new RegExp(`^(|[${ZERO_WIDTH_SPACE}])$`));
} else {
expect(text).toBe(title);
}
}
export function getDatabaseBodyCell(
page: Page,
{
rowIndex,
columnIndex,
}: {
rowIndex: number;
columnIndex: number;
}
) {
const row = getDatabaseBodyRow(page, rowIndex);
const cell = row.locator('.database-cell').nth(columnIndex);
return cell;
}
export function getDatabaseBodyCellContent(
page: Page,
{
rowIndex,
columnIndex,
cellClass,
}: {
rowIndex: number;
columnIndex: number;
cellClass: string;
}
) {
const cell = getDatabaseBodyCell(page, { rowIndex, columnIndex });
const cellContent = cell.locator(`.${cellClass}`);
return cellContent;
}
export function getFirstColumnCell(page: Page, cellClass: string) {
const cellContent = getDatabaseBodyCellContent(page, {
rowIndex: 0,
columnIndex: 1,
cellClass,
});
return cellContent;
}
export async function clickSelectOption(page: Page, index = 0) {
await page.locator('.select-option-icon').nth(index).click();
}
export async function performSelectColumnTagAction(
page: Page,
name: string,
index = 0
) {
await clickSelectOption(page, index);
await page
.locator('.affine-menu-button', { hasText: new RegExp(name) })
.click();
}
export async function assertSelectedStyle(
page: Page,
key: keyof CSSStyleDeclaration,
value: string
) {
const style = await getElementStyle(page, '.select-selected', key);
expect(style).toBe(value);
}
export async function clickDatabaseOutside(page: Page) {
const docTitle = page.locator('.doc-title-container');
await docTitle.click();
}
export async function assertColumnWidth(locator: Locator, width: number) {
const box = await getBoundingBox(locator);
expect(box.width).toBe(width + 1);
return box;
}
export async function assertDatabaseCellRichTexts(
page: Page,
{
rowIndex = 0,
columnIndex = 1,
text,
}: {
rowIndex?: number;
columnIndex?: number;
text: string;
}
) {
const cellContainer = page.locator(
`affine-database-cell-container[data-row-index='${rowIndex}'][data-column-index='${columnIndex}']`
);
const cellEditing = cellContainer.locator(
'affine-database-rich-text-cell-editing'
);
const cell = cellContainer.locator('affine-database-rich-text-cell');
const richText = (await cellEditing.count()) === 0 ? cell : cellEditing;
const actualTexts = await richText.evaluate(ele => {
return (ele as RichTextCellEditing).inlineEditor?.yTextString;
});
expect(actualTexts).toEqual(text);
}
export async function assertDatabaseCellNumber(
page: Page,
{
rowIndex = 0,
columnIndex = 1,
text,
}: {
rowIndex?: number;
columnIndex?: number;
text: string;
}
) {
const actualText = await page
.locator('.affine-database-block-rows')
.locator('.database-row')
.nth(rowIndex)
.locator('.database-cell')
.nth(columnIndex)
.locator('.number')
.textContent();
expect(actualText?.trim()).toEqual(text);
}
export async function assertDatabaseCellLink(
page: Page,
{
rowIndex = 0,
columnIndex = 1,
text,
}: {
rowIndex?: number;
columnIndex?: number;
text: string;
}
) {
const actualTexts = await page.evaluate(
({ rowIndex, columnIndex }) => {
const rows = document.querySelector('.affine-database-block-rows');
const row = rows?.querySelector(
`.database-row:nth-child(${rowIndex + 1})`
);
const cell = row?.querySelector(
`.database-cell:nth-child(${columnIndex + 1})`
);
const richText =
cell?.querySelector<RichTextCell>('affine-database-link-cell') ??
cell?.querySelector<RichTextCellEditing>(
'affine-database-link-cell-editing'
);
if (!richText) throw new Error('Missing database rich text cell');
return richText.inlineEditor!.yText.toString();
},
{ rowIndex, columnIndex }
);
expect(actualTexts).toEqual(text);
}
export async function assertDatabaseTitleText(page: Page, text: string) {
const dbTitle = page.locator('[data-block-is-database-title="true"]');
expect(await dbTitle.inputValue()).toEqual(text);
}
export async function waitSearchTransitionEnd(page: Page) {
await waitNextFrame(page, 400);
}
export async function assertDatabaseSearching(
page: Page,
isSearching: boolean
) {
const searchExpand = page.locator('.search-container-expand');
const count = await searchExpand.count();
expect(count).toBe(isSearching ? 1 : 0);
}
export async function focusDatabaseSearch(page: Page) {
await (await getDatabaseMouse(page)).mouseOver();
const searchExpand = page.locator('.search-container-expand');
const count = await searchExpand.count();
if (count === 1) {
const input = page.locator('.affine-database-search-input');
await input.click();
} else {
const searchIcon = page.locator('.affine-database-search-input-icon');
await searchIcon.click();
await waitSearchTransitionEnd(page);
}
}
export async function blurDatabaseSearch(page: Page) {
const dbTitle = page.locator('[data-block-is-database-title="true"]');
await dbTitle.click();
}
export async function focusDatabaseHeader(page: Page, columnIndex = 0) {
const column = page.locator('.affine-database-column').nth(columnIndex);
const box = await getBoundingBox(column);
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await waitNextFrame(page);
return column;
}
export async function getDatabaseMouse(page: Page) {
const databaseRect = await getBoundingClientRect(
page,
'.affine-database-table'
);
return {
mouseOver: async () => {
await page.mouse.move(databaseRect.x, databaseRect.y);
},
mouseLeave: async () => {
await page.mouse.move(databaseRect.x - 1, databaseRect.y - 1);
},
};
}
export async function getDatabaseHeaderColumn(page: Page, index = 0) {
const column = page.locator('.affine-database-column').nth(index);
const box = await getBoundingBox(column);
const textElement = column.locator('.affine-database-column-text-input');
const text = await textElement.innerText();
const typeIcon = column.locator('.affine-database-column-type-icon');
return {
column,
box,
text,
textElement,
typeIcon,
};
}
export async function assertRowsSelection(
page: Page,
rowIndexes: [start: number, end: number]
) {
const rows = page.locator('data-view-table-row');
const startIndex = rowIndexes[0];
const endIndex = rowIndexes[1];
for (let i = startIndex; i <= endIndex; i++) {
const row = rows.nth(i);
await row.locator('.row-select-checkbox .selected').isVisible();
}
}
export async function assertCellsSelection(
page: Page,
cellIndexes: {
start: [rowIndex: number, columnIndex: number];
end?: [rowIndex: number, columnIndex: number];
}
) {
const { start, end } = cellIndexes;
if (!end) {
// single cell
const focus = page.locator('.database-focus');
const focusBox = await getBoundingBox(focus);
const [rowIndex, columnIndex] = start;
const cell = getDatabaseBodyCell(page, { rowIndex, columnIndex });
const cellBox = await getBoundingBox(cell);
expect(focusBox).toEqual({
x: cellBox.x,
y: cellBox.y - 1,
height: cellBox.height + 2,
width: cellBox.width + 1,
});
} else {
// multi cells
const selection = page.locator('.database-selection');
const selectionBox = await getBoundingBox(selection);
const [startRowIndex, startColumnIndex] = start;
const [endRowIndex, endColumnIndex] = end;
const rowIndexStart = Math.min(startRowIndex, endRowIndex);
const rowIndexEnd = Math.max(startRowIndex, endRowIndex);
const columnIndexStart = Math.min(startColumnIndex, endColumnIndex);
const columnIndexEnd = Math.max(startColumnIndex, endColumnIndex);
let height = 0;
let width = 0;
let x = 0;
let y = 0;
for (let i = rowIndexStart; i <= rowIndexEnd; i++) {
const cell = getDatabaseBodyCell(page, {
rowIndex: i,
columnIndex: columnIndexStart,
});
const box = await getBoundingBox(cell);
height += box.height + 1;
if (i === rowIndexStart) {
y = box.y;
}
}
for (let j = columnIndexStart; j <= columnIndexEnd; j++) {
const cell = getDatabaseBodyCell(page, {
rowIndex: rowIndexStart,
columnIndex: j,
});
const box = await getBoundingBox(cell);
width += box.width;
if (j === columnIndexStart) {
x = box.x;
}
}
expect(selectionBox).toEqual({
x,
y,
height,
width: width + 1,
});
}
}
export async function getElementStyle(
page: Page,
selector: string,
key: keyof CSSStyleDeclaration
) {
const style = await page.evaluate(
({ key, selector }) => {
const el = document.querySelector<HTMLElement>(selector);
if (!el) throw new Error(`Missing ${selector} tag`);
// @ts-ignore
return el.style[key];
},
{
key,
selector,
}
);
return style;
}
export async function focusKanbanCardHeader(page: Page, index = 0) {
const cardHeader = page.locator('data-view-header-area-text').nth(index);
await cardHeader.click();
}
export async function clickKanbanCardHeader(page: Page, index = 0) {
const cardHeader = page.locator('data-view-header-area-text').nth(index);
await cardHeader.click();
await cardHeader.click();
}
export async function assertKanbanCardHeaderText(
page: Page,
text: string,
index = 0
) {
const cardHeader = page.locator('data-view-header-area-text').nth(index);
await expect(cardHeader).toHaveText(text);
}
export async function assertKanbanCellSelected(
page: Page,
{
groupIndex,
cardIndex,
cellIndex,
}: {
groupIndex: number;
cardIndex: number;
cellIndex: number;
}
) {
const border = await page.evaluate(
({ groupIndex, cardIndex, cellIndex }) => {
const group = document.querySelector(
`affine-data-view-kanban-group:nth-child(${groupIndex + 1})`
);
const card = group?.querySelector(
`affine-data-view-kanban-card:nth-child(${cardIndex + 1})`
);
const cells = Array.from(
card?.querySelectorAll<HTMLElement>(`affine-data-view-kanban-cell`) ??
[]
);
const cell = cells[cellIndex];
if (!cell) throw new Error(`Missing cell tag`);
return cell.style.border;
},
{
groupIndex,
cardIndex,
cellIndex,
}
);
expect(border).toEqual('1px solid var(--affine-primary-color)');
}
export async function assertKanbanCardSelected(
page: Page,
{
groupIndex,
cardIndex,
}: {
groupIndex: number;
cardIndex: number;
}
) {
const border = await page.evaluate(
({ groupIndex, cardIndex }) => {
const group = document.querySelector(
`affine-data-view-kanban-group:nth-child(${groupIndex + 1})`
);
const card = group?.querySelector<HTMLElement>(
`affine-data-view-kanban-card:nth-child(${cardIndex + 1})`
);
if (!card) throw new Error(`Missing card tag`);
return card.style.border;
},
{
groupIndex,
cardIndex,
}
);
expect(border).toEqual('1px solid var(--affine-primary-color)');
}
export function getKanbanCard(
page: Page,
{
groupIndex,
cardIndex,
}: {
groupIndex: number;
cardIndex: number;
}
) {
const group = page.locator('affine-data-view-kanban-group').nth(groupIndex);
const card = group.locator('affine-data-view-kanban-card').nth(cardIndex);
return card;
}
export const moveToCenterOf = async (page: Page, locator: Locator) => {
const box = (await locator.boundingBox())!;
expect(box).toBeDefined();
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
};
export const changeColumnType = async (
page: Page,
column: number,
name: string
) => {
await waitNextFrame(page);
await page.locator('affine-database-header-column').nth(column).click();
await waitNextFrame(page, 200);
await pressKey(page, 'Escape');
await pressKey(page, 'ArrowDown');
await pressKey(page, 'Enter');
await type(page, name);
await pressKey(page, 'ArrowDown');
await pressKey(page, 'Enter');
};
export const pressKey = async (page: Page, key: string, count: number = 1) => {
for (let i = 0; i < count; i++) {
await waitNextFrame(page);
await press(page, key);
}
await waitNextFrame(page);
};

View File

@@ -0,0 +1,176 @@
import { expect } from '@playwright/test';
import { dragBetweenCoords } from '../utils/actions/drag.js';
import {
copyByKeyboard,
pasteByKeyboard,
pressArrowDown,
pressArrowUp,
pressEnter,
pressEscape,
type,
} from '../utils/actions/keyboard.js';
import {
enterPlaygroundRoom,
focusRichText,
getBoundingBox,
initDatabaseDynamicRowWithData,
initDatabaseRowWithData,
initEmptyDatabaseState,
initEmptyDatabaseWithParagraphState,
waitNextFrame,
} from '../utils/actions/misc.js';
import { assertRichTexts } from '../utils/asserts.js';
import { test } from '../utils/playwright.js';
import {
assertDatabaseTitleColumnText,
getDatabaseBodyCell,
getElementStyle,
initDatabaseColumn,
switchColumnType,
} from './actions.js';
test.describe('copy&paste when editing', () => {
test.skip('should support copy&paste of the title column', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseWithParagraphState(page);
await initDatabaseColumn(page);
await initDatabaseRowWithData(page, 'abc123');
await pressEscape(page);
await pressEnter(page);
await page.keyboard.down('Shift');
for (let i = 0; i < 4; i++) {
await page.keyboard.press('ArrowLeft');
}
await page.keyboard.up('Shift');
await copyByKeyboard(page);
const bgValue = await getElementStyle(page, '.database-focus', 'boxShadow');
expect(bgValue).not.toBe('unset');
await focusRichText(page, 1);
await pasteByKeyboard(page);
await assertRichTexts(page, ['Database 1', 'c123']);
});
});
test.describe('copy&paste when selecting', () => {
test.skip('should support copy&paste of a single cell', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseRowWithData(page, 'abc123');
await initDatabaseRowWithData(page, '');
await pressEscape(page);
await waitNextFrame(page, 100);
await pressArrowUp(page);
await copyByKeyboard(page);
await pressArrowDown(page);
await pasteByKeyboard(page, false);
await waitNextFrame(page);
await assertDatabaseTitleColumnText(page, 'abc123', 1);
});
test.skip('should support copy&paste of multi cells', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseRowWithData(page, 'text1');
await initDatabaseDynamicRowWithData(page, '123', false);
await pressEscape(page);
await initDatabaseRowWithData(page, 'text2');
await initDatabaseDynamicRowWithData(page, 'a', false);
await pressEscape(page);
await initDatabaseRowWithData(page, '');
await initDatabaseRowWithData(page, '');
const startCell = getDatabaseBodyCell(page, {
rowIndex: 0,
columnIndex: 0,
});
const startCellBox = await getBoundingBox(startCell);
const endCell = getDatabaseBodyCell(page, { rowIndex: 1, columnIndex: 1 });
const endCellBox = await getBoundingBox(endCell);
const startX = startCellBox.x + startCellBox.width / 2;
const startY = startCellBox.y + startCellBox.height / 2;
const endX = endCellBox.x + endCellBox.width / 2;
const endY = endCellBox.y + endCellBox.height / 2;
await dragBetweenCoords(
page,
{ x: startX, y: startY },
{ x: endX, y: endY },
{
steps: 50,
}
);
await copyByKeyboard(page);
await pressArrowDown(page);
await pressArrowDown(page);
await waitNextFrame(page);
await pasteByKeyboard(page, false);
await assertDatabaseTitleColumnText(page, 'text1', 2);
await assertDatabaseTitleColumnText(page, 'text2', 3);
const selectCell21 = getDatabaseBodyCell(page, {
rowIndex: 2,
columnIndex: 1,
});
const selectCell31 = getDatabaseBodyCell(page, {
rowIndex: 3,
columnIndex: 1,
});
expect(await selectCell21.innerText()).toBe('123');
expect(await selectCell31.innerText()).toBe('a');
});
test.skip('should support copy&paste of a single row', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseRowWithData(page, 'text1');
await pressEscape(page);
await waitNextFrame(page, 100);
await initDatabaseDynamicRowWithData(page, 'abc', false);
await pressEscape(page);
await waitNextFrame(page, 100);
await initDatabaseColumn(page);
await switchColumnType(page, 'Number', 2);
const numberCell = getDatabaseBodyCell(page, {
rowIndex: 0,
columnIndex: 2,
});
await numberCell.click();
await waitNextFrame(page);
await type(page, '123');
await pressEscape(page);
await pressEscape(page);
await copyByKeyboard(page);
await initDatabaseRowWithData(page, '');
await pressEscape(page);
await pasteByKeyboard(page, false);
await waitNextFrame(page);
await assertDatabaseTitleColumnText(page, 'text1', 1);
const selectCell = getDatabaseBodyCell(page, {
rowIndex: 1,
columnIndex: 1,
});
expect(await selectCell.innerText()).toBe('abc');
const selectNumberCell = getDatabaseBodyCell(page, {
rowIndex: 1,
columnIndex: 2,
});
expect(await selectNumberCell.innerText()).toBe('123');
});
});

View File

@@ -0,0 +1,599 @@
import { expect } from '@playwright/test';
import {
assertDatabaseColumnOrder,
dragBetweenCoords,
enterPlaygroundRoom,
getBoundingBox,
initDatabaseDynamicRowWithData,
initEmptyDatabaseState,
pressArrowRight,
pressArrowUp,
pressArrowUpWithShiftKey,
pressBackspace,
pressEnter,
pressEscape,
redoByClick,
selectAllByKeyboard,
type,
undoByClick,
waitNextFrame,
} from '../utils/actions/index.js';
import { test } from '../utils/playwright.js';
import {
assertDatabaseCellNumber,
assertDatabaseCellRichTexts,
assertSelectedStyle,
changeColumnType,
clickDatabaseOutside,
clickSelectOption,
getDatabaseHeaderColumn,
getFirstColumnCell,
initDatabaseColumn,
performColumnAction,
performSelectColumnTagAction,
switchColumnType,
} from './actions.js';
test.describe('column operations', () => {
test('should support rename column', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, 'abc');
const { textElement } = await getDatabaseHeaderColumn(page, 1);
expect(await textElement.innerText()).toBe('abc');
await textElement.click();
await waitNextFrame(page, 200);
await pressArrowRight(page);
await type(page, '123');
await pressEnter(page);
expect(await textElement.innerText()).toBe('abc123');
await undoByClick(page);
expect(await textElement.innerText()).toBe('abc');
await redoByClick(page);
expect(await textElement.innerText()).toBe('abc123');
});
test('should support add new column', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await pressEscape(page);
const { text: title1 } = await getDatabaseHeaderColumn(page, 1);
expect(title1).toBe('Column 1');
const selected = getFirstColumnCell(page, 'select-selected');
expect(await selected.innerText()).toBe('123');
await initDatabaseColumn(page, 'abc');
const { text: title2 } = await getDatabaseHeaderColumn(page, 2);
expect(title2).toBe('abc');
await initDatabaseColumn(page);
const { text: title3 } = await getDatabaseHeaderColumn(page, 3);
expect(title3).toBe('Column 2');
});
test('should support right insert column', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, '1');
await performColumnAction(page, '1', 'Insert right');
await selectAllByKeyboard(page);
await type(page, '2');
await pressEnter(page);
const columns = page.locator('.affine-database-column');
expect(await columns.count()).toBe(3);
await assertDatabaseColumnOrder(page, ['1', '2']);
});
test('should support left insert column', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, '1');
await performColumnAction(page, '1', 'Insert left');
await selectAllByKeyboard(page);
await type(page, '2');
await pressEnter(page);
const columns = page.locator('.affine-database-column');
expect(await columns.count()).toBe(3);
await assertDatabaseColumnOrder(page, ['2', '1']);
});
test('should support delete column', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, '1');
const columns = page.locator('.affine-database-column');
expect(await columns.count()).toBe(2);
await performColumnAction(page, '1', 'Delete');
expect(await columns.count()).toBe(1);
});
test('should support duplicate column', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, '1');
await initDatabaseDynamicRowWithData(page, '123', true);
await pressEscape(page);
await performColumnAction(page, '1', 'duplicate');
await pressEscape(page);
const cells = page.locator('affine-database-multi-select-cell');
expect(await cells.count()).toBe(2);
const secondCell = cells.nth(1);
const selected = secondCell.locator('.select-selected');
expect(await selected.innerText()).toBe('123');
});
test('should support move column right', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, '1');
await initDatabaseDynamicRowWithData(page, '123', true);
await pressEscape(page);
await initDatabaseColumn(page, '2');
await initDatabaseDynamicRowWithData(page, 'abc', false, 1);
await pressEscape(page);
await assertDatabaseColumnOrder(page, ['1', '2']);
await waitNextFrame(page, 350);
await performColumnAction(page, '1', 'Move right');
await assertDatabaseColumnOrder(page, ['2', '1']);
await undoByClick(page);
const { column } = await getDatabaseHeaderColumn(page, 2);
await column.click();
const moveLeft = page.locator('.action', { hasText: 'Move right' });
expect(await moveLeft.count()).toBe(0);
});
test('should support move column left', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, '1');
await initDatabaseDynamicRowWithData(page, '123', true);
await pressEscape(page);
await initDatabaseColumn(page, '2');
await initDatabaseDynamicRowWithData(page, 'abc', false, 1);
await pressEscape(page);
await assertDatabaseColumnOrder(page, ['1', '2']);
const { column } = await getDatabaseHeaderColumn(page, 0);
await column.click();
const moveLeft = page.locator('.action', { hasText: 'Move left' });
expect(await moveLeft.count()).toBe(0);
await waitNextFrame(page, 200);
await pressEscape(page);
await pressEscape(page);
await performColumnAction(page, '2', 'Move left');
await assertDatabaseColumnOrder(page, ['2', '1']);
});
});
test.describe('switch column type', () => {
test('switch to number', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123abc', true);
await pressEscape(page);
await changeColumnType(page, 1, 'Number');
const cell = getFirstColumnCell(page, 'number');
await assertDatabaseCellNumber(page, {
text: '',
});
await pressEnter(page);
await type(page, '123abc');
await pressEscape(page);
expect((await cell.textContent())?.trim()).toBe('123');
});
test('switch to rich-text', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123abc', true);
await pressEscape(page);
await switchColumnType(page, 'Text');
// For now, rich-text will only be initialized on click
// Therefore, for the time being, here is to detect whether there is '.affine-database-rich-text'
const cell = getFirstColumnCell(page, 'affine-database-rich-text');
expect(await cell.count()).toBe(1);
await pressEnter(page);
await type(page, '123');
await pressEscape(page);
await pressEnter(page);
await type(page, 'abc');
await pressEscape(page);
await assertDatabaseCellRichTexts(page, { text: '123abc123abc' });
});
test('switch between multi-select and select', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await type(page, 'abc');
await pressEnter(page);
await pressEscape(page);
const cell = getFirstColumnCell(page, 'select-selected');
expect(await cell.count()).toBe(2);
await switchColumnType(page, 'Select', 1);
expect(await cell.count()).toBe(1);
expect(await cell.innerText()).toBe('123');
await pressEnter(page);
await type(page, 'def');
await pressEnter(page);
expect(await cell.innerText()).toBe('def');
await switchColumnType(page, 'Multi-select');
await pressEnter(page);
await type(page, '666');
await pressEnter(page);
await pressEscape(page);
expect(await cell.count()).toBe(2);
expect(await cell.nth(0).innerText()).toBe('def');
expect(await cell.nth(1).innerText()).toBe('666');
await switchColumnType(page, 'Select');
expect(await cell.count()).toBe(1);
expect(await cell.innerText()).toBe('def');
await pressEnter(page);
await type(page, '888');
await pressEnter(page);
expect(await cell.innerText()).toBe('888');
});
test('switch between number and rich-text', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Number');
await initDatabaseDynamicRowWithData(page, '123abc', true);
await assertDatabaseCellNumber(page, {
text: '123',
});
await switchColumnType(page, 'Text');
await pressEnter(page);
await type(page, 'abc');
await pressEscape(page);
await assertDatabaseCellRichTexts(page, { text: '123abc' });
await switchColumnType(page, 'Number');
await assertDatabaseCellNumber(page, {
text: '123',
});
});
test('switch number to select', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Number');
await initDatabaseDynamicRowWithData(page, '123', true);
const cell = getFirstColumnCell(page, 'number');
expect((await cell.textContent())?.trim()).toBe('123');
await switchColumnType(page, 'Select');
await initDatabaseDynamicRowWithData(page, 'abc');
const selectCell = getFirstColumnCell(page, 'select-selected');
expect(await selectCell.innerText()).toBe('abc');
await switchColumnType(page, 'Number');
await assertDatabaseCellNumber(page, {
text: '',
});
});
test('switch to checkbox', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await changeColumnType(page, 1, 'Checkbox');
const checkbox = getFirstColumnCell(page, 'checkbox');
await expect(checkbox).not.toHaveClass('checked');
await waitNextFrame(page, 500);
await checkbox.click();
await expect(checkbox).toHaveClass(/checked/);
await undoByClick(page);
await expect(checkbox).not.toHaveClass('checked');
});
test('checkbox to text', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await changeColumnType(page, 1, 'Checkbox');
let checkbox = getFirstColumnCell(page, 'checkbox');
await expect(checkbox).not.toHaveClass('checked');
// checked
await checkbox.click();
await changeColumnType(page, 1, 'Text');
await clickDatabaseOutside(page);
await waitNextFrame(page, 100);
await assertDatabaseCellRichTexts(page, { text: 'Yes' });
await clickDatabaseOutside(page);
await waitNextFrame(page, 100);
await changeColumnType(page, 1, 'Checkbox');
checkbox = getFirstColumnCell(page, 'checkbox');
await expect(checkbox).toHaveClass(/checked/);
// not checked
await checkbox.click();
await changeColumnType(page, 1, 'Text');
await clickDatabaseOutside(page);
await waitNextFrame(page, 100);
await assertDatabaseCellRichTexts(page, { text: 'No' });
await clickDatabaseOutside(page);
await waitNextFrame(page, 100);
await changeColumnType(page, 1, 'Checkbox');
checkbox = getFirstColumnCell(page, 'checkbox');
await expect(checkbox).not.toHaveClass('checked');
});
test('switch to progress', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await switchColumnType(page, 'Progress');
const progress = getFirstColumnCell(page, 'progress');
expect(await progress.textContent()).toBe('0');
await waitNextFrame(page, 500);
const progressBg = page.locator('.affine-database-progress-bg');
const {
x: progressBgX,
y: progressBgY,
width: progressBgWidth,
} = await getBoundingBox(progressBg);
await page.mouse.move(progressBgX, progressBgY);
await page.mouse.click(progressBgX, progressBgY);
const dragHandle = page.locator('.affine-database-progress-drag-handle');
const {
x: dragX,
y: dragY,
width,
height,
} = await getBoundingBox(dragHandle);
const dragCenterX = dragX + width / 2;
const dragCenterY = dragY + height / 2;
await page.mouse.move(dragCenterX, dragCenterY);
const endX = dragCenterX + progressBgWidth;
await dragBetweenCoords(
page,
{ x: dragCenterX, y: dragCenterY },
{ x: endX, y: dragCenterY }
);
expect(await progress.textContent()).toBe('100');
await pressEscape(page);
await undoByClick(page);
expect(await progress.textContent()).toBe('0');
});
test('switch to link', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await switchColumnType(page, 'Link');
const linkText = 'http://example.com';
const cell = getFirstColumnCell(page, 'affine-database-link');
await pressEnter(page);
await type(page, linkText);
await pressEscape(page);
const link = cell.locator('affine-database-link-node > a');
const linkContent = link.locator('.link-node-text');
await expect(link).toHaveAttribute('href', linkText);
expect(await linkContent.textContent()).toBe(linkText);
// not link text
await cell.hover();
const linkEdit = getFirstColumnCell(page, 'affine-database-link-icon');
await linkEdit.click();
await selectAllByKeyboard(page);
await type(page, 'abc');
await pressEnter(page);
await expect(link).toBeHidden();
});
});
test.describe('select column tag action', () => {
test('should support select tag renaming', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await type(page, 'abc');
await pressEnter(page);
await clickSelectOption(page);
await waitNextFrame(page);
await pressArrowRight(page);
await type(page, '4567abc00');
await pressEnter(page);
const options = page.locator('.select-options-container .tag-text');
expect(await options.nth(0).innerText()).toBe('abc4567abc00');
expect(await options.nth(1).innerText()).toBe('123');
});
test('should select tag renaming support shortcut key', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await clickSelectOption(page);
await waitNextFrame(page);
await pressArrowRight(page);
await type(page, '456');
// esc
await pressEscape(page);
await pressEscape(page);
const options = page.locator('.select-options-container .tag-text');
const option1 = options.nth(0);
expect(await option1.innerText()).toBe('123456');
});
test('should support select tag deletion', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await performSelectColumnTagAction(page, 'Delete');
const options = page.locator('.select-option-name');
expect(await options.count()).toBe(0);
});
test('should support modifying select tag color', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await performSelectColumnTagAction(page, 'Red');
await pressEscape(page);
await assertSelectedStyle(
page,
'backgroundColor',
'var(--affine-v2-chip-label-red)'
);
});
});
test.describe('drag-to-fill', () => {
test('should show when cell in focus and hide on blur', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
const dragToFillHandle = page.locator('.drag-to-fill');
await expect(dragToFillHandle).toBeVisible();
await pressEscape(page);
await expect(dragToFillHandle).toBeHidden();
});
test('should not show in multi (row or column) selection', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
const dragToFillHandle = page.locator('.drag-to-fill');
await expect(dragToFillHandle).toBeVisible();
await pressArrowUpWithShiftKey(page);
await expect(dragToFillHandle).toBeHidden();
await pressArrowUp(page);
await expect(dragToFillHandle).toBeVisible();
});
test('should fill columns with data', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, 'thing', true);
await pressEscape(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressBackspace(page);
await type(page, 'aaa');
await pressEnter(page);
await pressEnter(page);
await pressEscape(page);
await pressArrowUp(page);
const cells = page.locator('affine-database-multi-select-cell');
expect(await cells.nth(0).innerText()).toBe('thing');
expect(await cells.nth(1).innerText()).toBe('aaa');
const dragToFillHandle = page.locator('.drag-to-fill');
await expect(dragToFillHandle).toBeVisible();
const bbox = await getBoundingBox(dragToFillHandle);
if (!bbox) throw new Error('Expected a bounding box');
await dragBetweenCoords(
page,
{ x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 },
{ x: bbox.x, y: bbox.y + 200 }
);
expect(await cells.nth(0).innerText()).toBe('thing');
expect(await cells.nth(1).innerText()).toBe('thing');
});
});

View File

@@ -0,0 +1,670 @@
import { expect } from '@playwright/test';
import {
dragBetweenCoords,
enterPlaygroundRoom,
focusDatabaseTitle,
getBoundingBox,
initDatabaseDynamicRowWithData,
initDatabaseRowWithData,
initEmptyDatabaseState,
pressArrowLeft,
pressArrowRight,
pressBackspace,
pressEnter,
pressEscape,
pressShiftEnter,
redoByKeyboard,
selectAllByKeyboard,
setInlineRangeInInlineEditor,
switchReadonly,
type,
undoByClick,
undoByKeyboard,
waitNextFrame,
} from '../utils/actions/index.js';
import {
assertBlockProps,
assertInlineEditorDeltas,
assertRowCount,
} from '../utils/asserts.js';
import { test } from '../utils/playwright.js';
import { getFormatBar } from '../utils/query.js';
import {
assertColumnWidth,
assertDatabaseCellRichTexts,
assertDatabaseSearching,
assertDatabaseTitleText,
blurDatabaseSearch,
clickColumnType,
clickDatabaseOutside,
focusDatabaseHeader,
focusDatabaseSearch,
getDatabaseBodyCell,
getDatabaseHeaderColumn,
getFirstColumnCell,
initDatabaseColumn,
switchColumnType,
} from './actions.js';
test('edit database block title and create new rows', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
const locator = page.locator('affine-database');
await expect(locator).toBeVisible();
const dbTitle = 'Database 1';
await assertBlockProps(page, '2', {
title: dbTitle,
});
await focusDatabaseTitle(page);
await selectAllByKeyboard(page);
await pressBackspace(page);
const expected = 'hello';
await type(page, expected);
await assertBlockProps(page, '2', {
title: 'hello',
});
await undoByClick(page);
await assertBlockProps(page, '2', {
title: 'Database 1',
});
await initDatabaseRowWithData(page, '');
await initDatabaseRowWithData(page, '');
await assertRowCount(page, 2);
await waitNextFrame(page, 100);
await pressEscape(page);
await undoByClick(page);
await undoByClick(page);
await assertRowCount(page, 0);
});
test('edit column title', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, '1');
// first added column
const { column } = await getDatabaseHeaderColumn(page, 1);
expect(await column.innerText()).toBe('1');
await undoByClick(page);
expect(await column.innerText()).toBe('Column 1');
});
test('should modify the value when the input loses focus', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Number');
await initDatabaseDynamicRowWithData(page, '1', true);
await clickDatabaseOutside(page);
const cell = getFirstColumnCell(page, 'number');
const text = await cell.textContent();
expect(text?.trim()).toBe('1');
});
test('should rich-text column support soft enter', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Text');
await initDatabaseDynamicRowWithData(page, '123', true);
const cell = getFirstColumnCell(page, 'affine-database-rich-text');
await cell.click();
await pressArrowLeft(page);
await pressEnter(page);
await assertDatabaseCellRichTexts(page, { text: '123' });
await cell.click();
await pressArrowRight(page);
await pressArrowLeft(page);
await pressShiftEnter(page);
await pressEnter(page);
await assertDatabaseCellRichTexts(page, { text: '12\n3' });
});
test('should the multi-select mode work correctly', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '1', true);
await pressEscape(page);
await initDatabaseDynamicRowWithData(page, '2');
await pressEscape(page);
const cell = getFirstColumnCell(page, 'select-selected');
expect(await cell.count()).toBe(2);
expect(await cell.nth(0).innerText()).toBe('1');
expect(await cell.nth(1).innerText()).toBe('2');
});
test('should database search work', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseRowWithData(page, 'text1');
await initDatabaseDynamicRowWithData(page, '123', false);
await pressEscape(page);
await initDatabaseRowWithData(page, 'text2');
await initDatabaseDynamicRowWithData(page, 'a', false);
await pressEscape(page);
await initDatabaseRowWithData(page, 'text3');
await initDatabaseDynamicRowWithData(page, '26', false);
await pressEscape(page);
// search for '2'
await focusDatabaseSearch(page);
await type(page, '2');
const rows = page.locator('.affine-database-block-row');
expect(await rows.count()).toBe(3);
// search for '23'
await type(page, '3');
expect(await rows.count()).toBe(1);
const cell = page.locator('.select-selected');
expect(await cell.innerText()).toBe('123');
// clear search input
const closeIcon = page.locator('.close-icon');
await closeIcon.click();
expect(await rows.count()).toBe(3);
});
test('should database search input displayed correctly', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await focusDatabaseSearch(page);
await blurDatabaseSearch(page);
await assertDatabaseSearching(page, false);
await focusDatabaseSearch(page);
await type(page, '2');
await blurDatabaseSearch(page);
await assertDatabaseSearching(page, true);
await focusDatabaseSearch(page);
await pressBackspace(page);
await blurDatabaseSearch(page);
await assertDatabaseSearching(page, false);
await focusDatabaseSearch(page);
await type(page, '2');
const closeIcon = page.locator('.close-icon');
await closeIcon.click();
await blurDatabaseSearch(page);
await assertDatabaseSearching(page, false);
await focusDatabaseSearch(page);
await type(page, '2');
await pressEscape(page);
await blurDatabaseSearch(page);
await assertDatabaseSearching(page, false);
});
test('should database title and rich-text support undo/redo', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Text');
await initDatabaseDynamicRowWithData(page, '123', true);
await undoByKeyboard(page);
await assertDatabaseCellRichTexts(page, { text: '' });
await pressEscape(page);
await redoByKeyboard(page);
await assertDatabaseCellRichTexts(page, { text: '123' });
await focusDatabaseTitle(page);
await type(page, 'abc');
await assertDatabaseTitleText(page, 'Database 1abc');
await undoByKeyboard(page);
await assertDatabaseTitleText(page, 'Database 1');
await redoByKeyboard(page);
await assertDatabaseTitleText(page, 'Database 1abc');
});
test('should support drag to change column width', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
const headerColumns = page.locator('.affine-database-column');
const titleColumn = headerColumns.nth(0);
const normalColumn = headerColumns.nth(1);
const dragDistance = 100;
const titleColumnWidth = 260;
const normalColumnWidth = 180;
await assertColumnWidth(titleColumn, titleColumnWidth - 1);
const box = await assertColumnWidth(normalColumn, normalColumnWidth - 1);
await dragBetweenCoords(
page,
{ x: box.x, y: box.y },
{ x: box.x + dragDistance, y: box.y },
{
steps: 50,
beforeMouseUp: async () => {
await waitNextFrame(page);
},
}
);
await assertColumnWidth(titleColumn, titleColumnWidth + dragDistance);
await assertColumnWidth(normalColumn, normalColumnWidth - 1);
await undoByClick(page);
await assertColumnWidth(titleColumn, titleColumnWidth - 1);
await assertColumnWidth(normalColumn, normalColumnWidth - 1);
});
test('should display the add column button on the right side of database correctly', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
const normalColumn = page.locator('.affine-database-column').nth(1);
const addColumnBtn = page.locator('.header-add-column-button');
const box = await getBoundingBox(normalColumn);
await dragBetweenCoords(
page,
{ x: box.x, y: box.y },
{ x: box.x + 400, y: box.y },
{
steps: 50,
beforeMouseUp: async () => {
await waitNextFrame(page);
},
}
);
await focusDatabaseHeader(page);
await expect(addColumnBtn).toBeVisible();
});
test('should support drag and drop to move columns', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page, 'column1');
await initDatabaseColumn(page, 'column2');
await initDatabaseColumn(page, 'column3');
const column1 = await focusDatabaseHeader(page, 1);
const moveIcon = column1.locator('.affine-database-column-move');
const moveIconBox = await getBoundingBox(moveIcon);
const x = moveIconBox.x + moveIconBox.width / 2;
const y = moveIconBox.y + moveIconBox.height / 2;
await dragBetweenCoords(
page,
{ x, y },
{ x: x + 100, y },
{
steps: 50,
beforeMouseUp: async () => {
await waitNextFrame(page);
const indicator = page.locator('.vertical-indicator').first();
await expect(indicator).toBeVisible();
const { box } = await getDatabaseHeaderColumn(page, 2);
const indicatorBox = await getBoundingBox(indicator);
expect(box.x + box.width - indicatorBox.x < 10).toBe(true);
},
}
);
const { text } = await getDatabaseHeaderColumn(page, 2);
expect(text).toBe('column1');
});
test('should title column support quick renaming', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, 'a', true);
await pressEscape(page);
await focusDatabaseHeader(page, 1);
const { textElement } = await getDatabaseHeaderColumn(page, 1);
await textElement.click();
await waitNextFrame(page);
await selectAllByKeyboard(page);
await type(page, '123');
await pressEnter(page);
expect(await textElement.innerText()).toBe('123');
await undoByClick(page);
expect(await textElement.innerText()).toBe('Column 1');
await textElement.click();
await waitNextFrame(page);
await selectAllByKeyboard(page);
await type(page, '123');
await pressEnter(page);
expect(await textElement.innerText()).toBe('123');
});
test('should title column support quick changing of column type', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, 'a', true);
await pressEscape(page);
await initDatabaseDynamicRowWithData(page, 'b');
await pressEscape(page);
await focusDatabaseHeader(page, 1);
const { typeIcon } = await getDatabaseHeaderColumn(page, 1);
await typeIcon.click();
await waitNextFrame(page);
await clickColumnType(page, 'Select');
const cell = getFirstColumnCell(page, 'select-selected');
expect(await cell.count()).toBe(1);
});
test('database format-bar in header and text column', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Text');
await initDatabaseDynamicRowWithData(page, 'column', true);
await pressArrowLeft(page);
await pressEnter(page);
await type(page, 'header');
// Title | Column1
// ----------------
// header | column
const formatBar = getFormatBar(page);
await setInlineRangeInInlineEditor(page, { index: 1, length: 4 }, 1);
expect(await formatBar.formatBar.isVisible()).toBe(true);
// Title | Column1
// ----------------
// h|eade|r | column
await assertInlineEditorDeltas(
page,
[
{
insert: 'header',
},
],
1
);
await formatBar.boldBtn.click();
await assertInlineEditorDeltas(
page,
[
{
insert: 'h',
},
{
insert: 'eade',
attributes: {
bold: true,
},
},
{
insert: 'r',
},
],
1
);
await pressEscape(page);
await pressArrowRight(page);
await pressEnter(page);
await setInlineRangeInInlineEditor(page, { index: 2, length: 2 }, 2);
expect(await formatBar.formatBar.isVisible()).toBe(true);
// Title | Column1
// ----------------
// header | co|lu|mn
await assertInlineEditorDeltas(
page,
[
{
insert: 'column',
},
],
2
);
await formatBar.boldBtn.click();
await assertInlineEditorDeltas(
page,
[
{
insert: 'co',
},
{
insert: 'lu',
attributes: {
bold: true,
},
},
{
insert: 'mn',
},
],
2
);
});
test.describe('readonly mode', () => {
test('database title should not be edited in readonly mode', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
const locator = page.locator('affine-database');
await expect(locator).toBeVisible();
const dbTitle = 'Database 1';
await assertBlockProps(page, '2', {
title: dbTitle,
});
await focusDatabaseTitle(page);
await selectAllByKeyboard(page);
await pressBackspace(page);
await type(page, 'hello');
await assertBlockProps(page, '2', {
title: 'hello',
});
await switchReadonly(page);
await type(page, ' world');
await assertBlockProps(page, '2', {
title: 'hello',
});
await pressBackspace(page, 'hello world'.length);
await assertBlockProps(page, '2', {
title: 'hello',
});
});
test('should rich-text not be edited in readonly mode', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Text');
await initDatabaseDynamicRowWithData(page, '', true);
const cell = getFirstColumnCell(page, 'affine-database-rich-text');
await cell.click();
await type(page, '123');
await assertDatabaseCellRichTexts(page, { text: '123' });
await switchReadonly(page);
await pressBackspace(page);
await type(page, '789');
await assertDatabaseCellRichTexts(page, { text: '123' });
});
test('should hide edit widget after switch to readonly mode', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Text');
await initDatabaseDynamicRowWithData(page, '', true);
const database = page.locator('affine-database');
await expect(database).toBeVisible();
const databaseMenu = database.locator('.database-ops');
await expect(databaseMenu).toBeVisible();
const addViewButton = database.getByTestId('database-add-view-button');
await expect(addViewButton).toBeVisible();
const titleHeader = page.locator('affine-database-header-column').filter({
hasText: 'Title',
});
await titleHeader.hover();
const columnDragBar = titleHeader.locator('.control-r');
await expect(columnDragBar).toBeVisible();
const filter = database.locator('data-view-header-tools-filter');
const search = database.locator('data-view-header-tools-search');
const options = database.locator('data-view-header-tools-view-options');
const headerAddRow = database.locator('data-view-header-tools-add-row');
await database.hover();
await expect(filter).toBeVisible();
await expect(search).toBeVisible();
await expect(options).toBeVisible();
await expect(headerAddRow).toBeVisible();
const row = database.locator('data-view-table-row');
const rowOptions = row.locator('.row-op');
const rowDragBar = row.locator('.data-view-table-view-drag-handler>div');
await row.hover();
await expect(rowOptions).toHaveCount(2);
await expect(rowOptions.nth(0)).toBeVisible();
await expect(rowOptions.nth(1)).toBeVisible();
await expect(rowDragBar).toBeVisible();
const addRow = database.locator('.data-view-table-group-add-row');
await expect(addRow).toBeVisible();
// Readonly Mode
{
await switchReadonly(page);
await expect(databaseMenu).toBeHidden();
await expect(addViewButton).toBeHidden();
await titleHeader.hover();
await expect(columnDragBar).toBeHidden();
await database.hover();
await expect(filter).toBeHidden();
await expect(search).toBeVisible(); // Note the search should not be hidden
await expect(options).toBeHidden();
await expect(headerAddRow).toBeHidden();
await row.hover();
await expect(rowOptions.nth(0)).toBeHidden();
await expect(rowOptions.nth(1)).toBeHidden();
await expect(rowDragBar).toBeHidden();
await expect(addRow).toBeHidden();
}
});
test('should hide focus border after switch to readonly mode', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Text');
await initDatabaseDynamicRowWithData(page, '', true);
const database = page.locator('affine-database');
await expect(database).toBeVisible();
const cell = getFirstColumnCell(page, 'affine-database-rich-text');
await cell.click();
const focusBorder = database.locator(
'data-view-table-selection .database-focus'
);
await expect(focusBorder).toBeVisible();
await switchReadonly(page);
await expect(focusBorder).toBeHidden();
});
test('should hide selection after switch to readonly mode', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await switchColumnType(page, 'Text');
await initDatabaseDynamicRowWithData(page, '', true);
const database = page.locator('affine-database');
await expect(database).toBeVisible();
const startCell = getDatabaseBodyCell(page, {
rowIndex: 0,
columnIndex: 0,
});
const endCell = getDatabaseBodyCell(page, {
rowIndex: 0,
columnIndex: 1,
});
const startBox = await getBoundingBox(startCell);
const endBox = await getBoundingBox(endCell);
const startX = startBox.x + startBox.width / 2;
const startY = startBox.y + startBox.height / 2;
const endX = endBox.x + endBox.width / 2;
const endY = endBox.y + endBox.height / 2;
await dragBetweenCoords(
page,
{ x: startX, y: startY },
{ x: endX, y: endY }
);
const selection = database.locator(
'data-view-table-selection .database-selection'
);
await expect(selection).toBeVisible();
await switchReadonly(page);
await expect(selection).toBeHidden();
});
});

View File

@@ -0,0 +1,567 @@
import { expect } from '@playwright/test';
import { dragBetweenCoords } from '../utils/actions/drag.js';
import { shiftClick } from '../utils/actions/edgeless.js';
import {
pressArrowDown,
pressArrowDownWithShiftKey,
pressArrowLeft,
pressArrowRight,
pressArrowUp,
pressArrowUpWithShiftKey,
pressBackspace,
pressEnter,
pressEscape,
type,
} from '../utils/actions/keyboard.js';
import {
enterPlaygroundRoom,
getBoundingBox,
initDatabaseDynamicRowWithData,
initDatabaseRowWithData,
initEmptyDatabaseState,
initKanbanViewState,
waitNextFrame,
} from '../utils/actions/misc.js';
import { test } from '../utils/playwright.js';
import {
assertCellsSelection,
assertDatabaseTitleColumnText,
assertKanbanCardHeaderText,
assertKanbanCardSelected,
assertKanbanCellSelected,
assertRowsSelection,
clickKanbanCardHeader,
focusKanbanCardHeader,
getDatabaseBodyCell,
getKanbanCard,
initDatabaseColumn,
switchColumnType,
} from './actions.js';
test.describe('focus', () => {
test('should support move focus by arrow key', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await pressEscape(page);
await waitNextFrame(page, 100);
await pressEscape(page);
await assertRowsSelection(page, [0, 0]);
});
test('should support multi row selection', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await switchColumnType(page, 'Number');
await initDatabaseDynamicRowWithData(page, '123', true);
const selectColumn = getDatabaseBodyCell(page, {
rowIndex: 1,
columnIndex: 1,
});
const endBox = await getBoundingBox(selectColumn);
const endX = endBox.x + endBox.width / 2;
await dragBetweenCoords(
page,
{ x: endX, y: endBox.y },
{ x: endX, y: endBox.y + endBox.height }
);
await pressEscape(page);
await assertRowsSelection(page, [0, 1]);
});
test('should support row selection with dynamic height', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123123', true);
await type(page, '456456');
await pressEnter(page);
await type(page, 'abcabc');
await pressEnter(page);
await type(page, 'defdef');
await pressEnter(page);
await pressEscape(page);
await pressEscape(page);
await assertRowsSelection(page, [0, 0]);
});
});
test.describe('row-level selection', () => {
test('should support title selection', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseRowWithData(page, 'title');
await pressEscape(page);
await waitNextFrame(page, 100);
await assertCellsSelection(page, {
start: [0, 0],
});
await pressEscape(page);
await assertRowsSelection(page, [0, 0]);
});
test('should support pressing esc to trigger row selection', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await pressEscape(page);
await waitNextFrame(page, 100);
await pressEscape(page);
await assertRowsSelection(page, [0, 0]);
});
test('should support multi row selection', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await switchColumnType(page, 'Number');
await initDatabaseDynamicRowWithData(page, '123', true);
const selectColumn = getDatabaseBodyCell(page, {
rowIndex: 1,
columnIndex: 1,
});
const endBox = await getBoundingBox(selectColumn);
const endX = endBox.x + endBox.width / 2;
await dragBetweenCoords(
page,
{ x: endX, y: endBox.y },
{ x: endX, y: endBox.y + endBox.height }
);
await pressEscape(page);
await assertRowsSelection(page, [0, 1]);
});
test('should support row selection with dynamic height', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '123123', true);
await type(page, '456456');
await pressEnter(page);
await type(page, 'abcabc');
await pressEnter(page);
await type(page, 'defdef');
await pressEnter(page);
await pressEscape(page);
await pressEscape(page);
await assertRowsSelection(page, [0, 0]);
});
test('move row selection with (up | down)', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
// add two rows
await initDatabaseDynamicRowWithData(page, '123123', true);
await pressEscape(page);
await initDatabaseDynamicRowWithData(page, '123123', true);
await pressEscape(page);
await pressEscape(page); // switch to row selection
await assertRowsSelection(page, [1, 1]);
await pressArrowUp(page);
await assertRowsSelection(page, [0, 0]);
// should not allow under selection
await pressArrowUp(page);
await assertRowsSelection(page, [0, 0]);
await pressArrowDown(page);
await assertRowsSelection(page, [1, 1]);
// should not allow over selection
await pressArrowDown(page);
await assertRowsSelection(page, [1, 1]);
});
test('increment decrement row selection with shift+(up | down)', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
// add two rows
await initDatabaseDynamicRowWithData(page, '123123', true);
await pressEscape(page);
await initDatabaseDynamicRowWithData(page, '123123', true);
await pressEscape(page);
await pressEscape(page); // switch to row selection
await pressArrowUpWithShiftKey(page);
await assertRowsSelection(page, [0, 1]);
await pressArrowDownWithShiftKey(page);
await assertRowsSelection(page, [1, 1]); // should decrement back
await pressArrowUp(page); // go to first row
await pressArrowDownWithShiftKey(page);
await assertRowsSelection(page, [0, 1]);
await pressArrowUpWithShiftKey(page);
await assertRowsSelection(page, [0, 0]);
});
});
test.describe('cell-level selection', () => {
test('should support multi cell selection', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseDynamicRowWithData(page, '', true);
await pressEscape(page);
await switchColumnType(page, 'Number');
await initDatabaseDynamicRowWithData(page, '123', true);
const startCell = getDatabaseBodyCell(page, {
rowIndex: 0,
columnIndex: 0,
});
const endCell = getDatabaseBodyCell(page, {
rowIndex: 1,
columnIndex: 1,
});
const startBox = await getBoundingBox(startCell);
const endBox = await getBoundingBox(endCell);
const startX = startBox.x + startBox.width / 2;
const startY = startBox.y + startBox.height / 2;
const endX = endBox.x + endBox.width / 2;
const endY = endBox.y + endBox.height / 2;
await dragBetweenCoords(
page,
{ x: startX, y: startY },
{ x: endX, y: endY }
);
await assertCellsSelection(page, {
start: [0, 0],
end: [1, 1],
});
});
test("should support backspace key to delete cell's content", async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseColumn(page);
await initDatabaseRowWithData(page, 'row1');
await initDatabaseDynamicRowWithData(page, 'abc', false);
await pressEscape(page);
await initDatabaseRowWithData(page, 'row2');
await initDatabaseDynamicRowWithData(page, '123', false);
await pressEscape(page);
const startCell = getDatabaseBodyCell(page, {
rowIndex: 0,
columnIndex: 0,
});
const endCell = getDatabaseBodyCell(page, {
rowIndex: 1,
columnIndex: 1,
});
const startBox = await getBoundingBox(startCell);
const endBox = await getBoundingBox(endCell);
const startX = startBox.x + startBox.width / 2;
const startY = startBox.y + startBox.height / 2;
const endX = endBox.x + endBox.width / 2;
const endY = endBox.y + endBox.height / 2;
await dragBetweenCoords(
page,
{ x: startX, y: startY },
{ x: endX, y: endY }
);
await pressBackspace(page);
await assertDatabaseTitleColumnText(page, '', 0);
await assertDatabaseTitleColumnText(page, '', 1);
const selectCell1 = getDatabaseBodyCell(page, {
rowIndex: 0,
columnIndex: 1,
});
expect(await selectCell1.innerText()).toBe('');
const selectCell2 = getDatabaseBodyCell(page, {
rowIndex: 1,
columnIndex: 1,
});
expect(await selectCell2.innerText()).toBe('');
});
});
test.describe('kanban view selection', () => {
test("should support move cell's focus by arrow key(up&down) within a card", async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initKanbanViewState(page, {
rows: ['row1'],
columns: [
{
type: 'number',
value: [1],
},
{
type: 'rich-text',
value: ['text'],
},
],
});
await focusKanbanCardHeader(page);
await assertKanbanCellSelected(page, {
// group by `number` column, the first(groupIndex: 0) group is `Ungroups`
groupIndex: 1,
cardIndex: 0,
cellIndex: 0,
});
await pressArrowDown(page, 3);
await assertKanbanCellSelected(page, {
groupIndex: 1,
cardIndex: 0,
cellIndex: 0,
});
await pressArrowUp(page);
await assertKanbanCellSelected(page, {
groupIndex: 1,
cardIndex: 0,
cellIndex: 2,
});
});
test("should support move cell's focus by arrow key(up&down) within multi cards", async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initKanbanViewState(page, {
rows: ['row1', 'row2'],
columns: [
{
type: 'number',
value: [1, 2],
},
{
type: 'rich-text',
value: ['text'],
},
],
});
await focusKanbanCardHeader(page);
await pressArrowUp(page);
await assertKanbanCellSelected(page, {
groupIndex: 1,
cardIndex: 1,
cellIndex: 2,
});
await pressArrowDown(page);
await assertKanbanCellSelected(page, {
groupIndex: 1,
cardIndex: 0,
cellIndex: 0,
});
});
test("should support move cell's focus by arrow key(left&right)", async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initKanbanViewState(page, {
rows: ['row1', 'row2', 'row3'],
columns: [
{
type: 'number',
value: [undefined, 1, 10],
},
],
});
await focusKanbanCardHeader(page);
await pressArrowRight(page, 3);
await assertKanbanCellSelected(page, {
groupIndex: 0,
cardIndex: 0,
cellIndex: 0,
});
await pressArrowLeft(page);
await assertKanbanCellSelected(page, {
groupIndex: 2,
cardIndex: 0,
cellIndex: 0,
});
});
test("should support move card's focus by arrow key(up&down)", async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initKanbanViewState(page, {
rows: ['row1', 'row2', 'row3'],
columns: [
{
type: 'number',
value: [undefined, undefined, undefined],
},
],
});
await focusKanbanCardHeader(page);
await pressEscape(page);
await pressEscape(page);
await assertKanbanCardSelected(page, {
groupIndex: 0,
cardIndex: 0,
});
await pressArrowDown(page, 3);
await assertKanbanCardSelected(page, {
groupIndex: 0,
cardIndex: 0,
});
await pressArrowUp(page);
await assertKanbanCardSelected(page, {
groupIndex: 0,
cardIndex: 2,
});
});
test("should support move card's focus by arrow key(left&right)", async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initKanbanViewState(page, {
rows: ['row1', 'row2', 'row3'],
columns: [
{
type: 'number',
value: [undefined, 1, 10],
},
],
});
await focusKanbanCardHeader(page);
await pressEscape(page);
await pressEscape(page);
await pressArrowRight(page, 3);
await assertKanbanCardSelected(page, {
groupIndex: 0,
cardIndex: 0,
});
await pressArrowLeft(page);
await assertKanbanCardSelected(page, {
groupIndex: 2,
cardIndex: 0,
});
});
test('should support multi card selection', async ({ page }) => {
await enterPlaygroundRoom(page);
await initKanbanViewState(page, {
rows: ['row1', 'row2'],
columns: [
{
type: 'number',
value: [undefined, 1],
},
],
});
await focusKanbanCardHeader(page);
await pressEscape(page);
await pressEscape(page);
const card = getKanbanCard(page, {
groupIndex: 1,
cardIndex: 0,
});
const box = await getBoundingBox(card);
await shiftClick(page, {
x: box.x + box.width / 2,
y: box.y + box.height / 2,
});
await assertKanbanCardSelected(page, {
groupIndex: 0,
cardIndex: 0,
});
await assertKanbanCardSelected(page, {
groupIndex: 1,
cardIndex: 0,
});
});
test("should support move cursor in card's title by arrow key(left&right)", async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initKanbanViewState(page, {
rows: ['row1'],
columns: [
{
type: 'rich-text',
value: ['text'],
},
],
});
await clickKanbanCardHeader(page);
await type(page, 'abc');
await pressArrowLeft(page, 2);
await pressArrowRight(page);
await pressBackspace(page);
await pressEscape(page);
await assertKanbanCardHeaderText(page, 'row1ac');
});
});

View File

@@ -0,0 +1,112 @@
import { expect, type Locator } from '@playwright/test';
import {
enterPlaygroundRoom,
initDatabaseDynamicRowWithData,
initEmptyDatabaseState,
waitNextFrame,
} from '../utils/actions/index.js';
import { test } from '../utils/playwright.js';
import { initDatabaseColumn, switchColumnType } from './actions.js';
test('database sort with multiple rules', async ({ page }) => {
// Initialize database
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
// Add test columns: Name (text) and Age (number)
await initDatabaseColumn(page, 'Name');
await switchColumnType(page, 'Text', 1);
await initDatabaseColumn(page, 'Age');
await switchColumnType(page, 'Number', 2);
// Add test data
const testData = [
{ name: 'Alice', age: '25' },
{ name: 'Bob', age: '30' },
{ name: 'Alice', age: '20' },
{ name: 'Charlie', age: '25' },
];
for (const data of testData) {
await initDatabaseDynamicRowWithData(page, data.name, true, 0);
await initDatabaseDynamicRowWithData(page, data.age, false, 1);
}
// Open sort menu
const sortButton = page.locator('data-view-header-tools-sort');
await sortButton.click();
// Add first sort rule: Name ascending
await page.locator('affine-menu').getByText('Name').click();
await waitNextFrame(page);
// Add second sort rule: Age ascending
await page.getByText('Add sort').click();
await page.locator('affine-menu').getByText('Age').click();
await waitNextFrame(page);
// Get all rows after sorting
const rows = await page.locator('affine-database-row').all();
const getCellText = async (row: Locator, index: number) => {
const cell = row.locator('.cell').nth(index);
return cell.innerText();
};
// Verify sorting results
// Should be sorted by Name first, then by Age
const expectedOrder = [
{ name: 'Alice', age: '20' },
{ name: 'Alice', age: '25' },
{ name: 'Bob', age: '30' },
{ name: 'Charlie', age: '25' },
];
for (let i = 0; i < rows.length; i++) {
const name = await getCellText(rows[i], 1);
const age = await getCellText(rows[i], 2);
expect(name).toBe(expectedOrder[i].name);
expect(age).toBe(expectedOrder[i].age);
}
// Change sort order of Name to descending
await page.locator('.sort-item').first().getByText('Ascending').click();
await page.getByText('Descending').click();
await waitNextFrame(page);
// Verify new sorting results
const expectedOrderDesc = [
{ name: 'Charlie', age: '25' },
{ name: 'Bob', age: '30' },
{ name: 'Alice', age: '20' },
{ name: 'Alice', age: '25' },
];
const rowsAfterDesc = await page.locator('affine-database-row').all();
for (let i = 0; i < rowsAfterDesc.length; i++) {
const name = await getCellText(rowsAfterDesc[i], 1);
const age = await getCellText(rowsAfterDesc[i], 2);
expect(name).toBe(expectedOrderDesc[i].name);
expect(age).toBe(expectedOrderDesc[i].age);
}
// Remove first sort rule
await page.locator('.sort-item').first().getByRole('img').last().click();
await waitNextFrame(page);
// Verify sorting now only by Age
const expectedOrderAgeOnly = [
{ name: 'Alice', age: '20' },
{ name: 'Alice', age: '25' },
{ name: 'Charlie', age: '25' },
{ name: 'Bob', age: '30' },
];
const rowsAfterRemove = await page.locator('affine-database-row').all();
for (let i = 0; i < rowsAfterRemove.length; i++) {
const name = await getCellText(rowsAfterRemove[i], 1);
const age = await getCellText(rowsAfterRemove[i], 2);
expect(name).toBe(expectedOrderAgeOnly[i].name);
expect(age).toBe(expectedOrderAgeOnly[i].age);
}
});

View File

@@ -0,0 +1,107 @@
import { expect, type Page } from '@playwright/test';
import { type } from '../utils/actions/index.js';
import {
enterPlaygroundRoom,
getAddRow,
initEmptyDatabaseState,
waitNextFrame,
} from '../utils/actions/misc.js';
import { test } from '../utils/playwright.js';
import {
changeColumnType,
moveToCenterOf,
press,
pressKey,
} from './actions.js';
const addRow = async (page: Page, count: number = 1) => {
await waitNextFrame(page);
const addRow = getAddRow(page);
for (let i = 0; i < count; i++) {
await addRow.click();
}
await press(page, 'Escape');
await waitNextFrame(page);
};
const insertRightColumn = async (page: Page, index = 0) => {
await waitNextFrame(page);
await page.locator('affine-database-header-column').nth(index).click();
await waitNextFrame(page, 200);
await pressKey(page, 'Escape');
const menu = page.locator('.affine-menu-button', {
hasText: new RegExp('Insert Right'),
});
await menu.click();
await waitNextFrame(page, 200);
await pressKey(page, 'Enter');
};
const menuSelect = async (page: Page, selectors: string[]) => {
await waitNextFrame(page);
for (const name of selectors) {
const menu = page.locator('.affine-menu-button', {
hasText: new RegExp(name),
});
await menu.click();
}
};
test.describe('title', () => {
test('empty count', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await addRow(page, 3);
const statCell = page.locator('affine-database-column-stats-cell').nth(0);
await moveToCenterOf(page, statCell);
await statCell.click();
await menuSelect(page, ['Count', 'Count Empty']);
const value = statCell.locator('.value');
expect((await value.textContent())?.trim()).toBe('3');
await page.locator('affine-database-cell-container').nth(0).click();
await pressKey(page, 'Enter');
await type(page, 'asd');
await pressKey(page, 'Escape');
expect((await value.textContent())?.trim()).toBe('2');
});
});
test.describe('rich-text', () => {
test('empty count', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await addRow(page, 3);
await insertRightColumn(page);
await changeColumnType(page, 1, 'text');
const statCell = page.locator('affine-database-column-stats-cell').nth(1);
await moveToCenterOf(page, statCell);
await statCell.click();
await menuSelect(page, ['Count', 'Count Empty']);
const value = statCell.locator('.value');
expect((await value.textContent())?.trim()).toBe('3');
await page.locator('affine-database-cell-container').nth(1).click();
await pressKey(page, 'Enter');
await type(page, 'asd');
await pressKey(page, 'Escape');
expect((await value.textContent())?.trim()).toBe('2');
});
});
test.describe('select', () => {
test('empty count', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await addRow(page, 3);
await insertRightColumn(page);
await changeColumnType(page, 1, 'select');
const statCell = page.locator('affine-database-column-stats-cell').nth(1);
await moveToCenterOf(page, statCell);
await statCell.click();
await menuSelect(page, ['Count', 'Count Empty']);
const value = statCell.locator('.value');
expect((await value.textContent())?.trim()).toBe('3');
await page.locator('affine-database-cell-container').nth(1).click();
await pressKey(page, 'Enter');
await type(page, 'select');
await pressKey(page, 'Enter');
expect((await value.textContent())?.trim()).toBe('2');
});
});

View File

@@ -0,0 +1,19 @@
import { expect } from '@playwright/test';
import {
enterPlaygroundRoom,
initDatabaseDynamicRowWithData,
initEmptyDatabaseState,
} from '../utils/actions/misc.js';
import { test } from '../utils/playwright.js';
import { press } from './actions.js';
test.describe('title', () => {
test('should able to link doc by press @', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyDatabaseState(page);
await initDatabaseDynamicRowWithData(page, '123', true);
await press(page, '@');
await expect(page.locator('.linked-doc-popover')).toBeVisible();
});
});