mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
600 lines
19 KiB
TypeScript
600 lines
19 KiB
TypeScript
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');
|
|
});
|
|
});
|