mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
chore(editor): fix imports in legacy tests (#10300)
This commit is contained in:
592
blocksuite/tests-legacy/e2e/database/actions.ts
Normal file
592
blocksuite/tests-legacy/e2e/database/actions.ts
Normal 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);
|
||||
};
|
||||
176
blocksuite/tests-legacy/e2e/database/clipboard.spec.ts
Normal file
176
blocksuite/tests-legacy/e2e/database/clipboard.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
599
blocksuite/tests-legacy/e2e/database/column.spec.ts
Normal file
599
blocksuite/tests-legacy/e2e/database/column.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
670
blocksuite/tests-legacy/e2e/database/database.spec.ts
Normal file
670
blocksuite/tests-legacy/e2e/database/database.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
567
blocksuite/tests-legacy/e2e/database/selection.spec.ts
Normal file
567
blocksuite/tests-legacy/e2e/database/selection.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
112
blocksuite/tests-legacy/e2e/database/sort.spec.ts
Normal file
112
blocksuite/tests-legacy/e2e/database/sort.spec.ts
Normal 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);
|
||||
}
|
||||
});
|
||||
107
blocksuite/tests-legacy/e2e/database/statistics.spec.ts
Normal file
107
blocksuite/tests-legacy/e2e/database/statistics.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
19
blocksuite/tests-legacy/e2e/database/title.spec.ts
Normal file
19
blocksuite/tests-legacy/e2e/database/title.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user