Files
AFFiNE-Mirror/tests/blocksuite/e2e/edgeless/lock.spec.ts
L-Sun a9ad01491c test(core): enable no-floating-promises rule for tests (#11915)
Sometimes, missing `await` in the test code can cause timing issues, leading to test failures. This PR enables the `no-floating-promises` rule for the test code to ensure that such errors do not occur.
2025-04-23 08:17:41 +00:00

557 lines
19 KiB
TypeScript

import { expect, type Page } from '@playwright/test';
import { clickView, dblclickView, moveView } from '../utils/actions/click.js';
import {
createBrushElement,
createConnectorElement,
createEdgelessText,
createFrame,
createMindmap,
createNote as _createNote,
createShapeElement,
deleteAll,
dragBetweenViewCoords,
edgelessCommonSetup,
getContainerChildIds,
getSelectedBound,
getSelectedIds,
getTypeById,
setEdgelessTool,
} from '../utils/actions/edgeless.js';
import {
copyByKeyboard,
pasteByKeyboard,
pressArrowDown,
pressBackspace,
pressEscape,
pressForwardDelete,
pressTab,
selectAllByKeyboard,
SHORT_KEY,
type,
undoByKeyboard,
} from '../utils/actions/keyboard.js';
import { waitNextFrame } from '../utils/actions/misc.js';
import {
assertCanvasElementsCount,
assertEdgelessElementBound,
assertEdgelessSelectedModelRect,
assertRichTexts,
} from '../utils/asserts.js';
import { test } from '../utils/playwright.js';
test.describe('lock', () => {
const getButtons = (page: Page) => {
const toolbar = page.locator('affine-toolbar-widget');
return {
lock: toolbar.getByTestId('lock'),
unlock: toolbar.getByTestId('unlock'),
};
};
async function createNote(page: Page, coord1: number[], content?: string) {
await _createNote(page, coord1, content);
await pressEscape(page, 3);
}
test('edgeless element can be locked and unlocked', async ({ page }) => {
await edgelessCommonSetup(page);
const wrapTest = async <F extends (...args: any) => any>(
elementCreateFn: F,
...args: Parameters<F>
) => {
await elementCreateFn(...args);
await waitNextFrame(page);
await pressEscape(page);
await selectAllByKeyboard(page);
const ids = await getSelectedIds(page);
expect(ids).toHaveLength(1);
const type = await getTypeById(page, ids[0]);
const message = `element(${type}) should be able to be (un)locked`;
const { lock, unlock } = getButtons(page);
await expect(lock, message).toBeVisible();
await expect(unlock, message).toBeHidden();
await lock.click();
await expect(lock, message).toBeHidden();
await expect(unlock, message).toBeVisible();
await unlock.click();
await expect(lock, message).toBeVisible();
await expect(unlock, message).toBeHidden();
await deleteAll(page);
await waitNextFrame(page);
};
await wrapTest(createBrushElement, page, [100, 100], [150, 150]);
await wrapTest(createConnectorElement, page, [100, 100], [150, 150]);
await wrapTest(createShapeElement, page, [100, 100], [150, 150]);
await wrapTest(createEdgelessText, page, [100, 100]);
await wrapTest(createMindmap, page, [100, 100]);
await wrapTest(createFrame, page, [100, 100], [150, 150]);
await wrapTest(createNote, page, [100, 100]);
await wrapTest(async () => {
await createShapeElement(page, [100, 100], [150, 150]);
await createShapeElement(page, [150, 150], [200, 200]);
await selectAllByKeyboard(page);
await page.keyboard.press(`${SHORT_KEY}+g`);
});
});
test('locked element should be selectable by clicking or short-cut', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createShapeElement(page, [100, 100], [150, 150]);
await selectAllByKeyboard(page);
await getButtons(page).lock.click();
expect(await getSelectedIds(page)).toHaveLength(1);
await pressEscape(page);
expect(await getSelectedIds(page)).toHaveLength(0);
await selectAllByKeyboard(page);
expect(await getSelectedIds(page)).toHaveLength(1);
await pressEscape(page);
await clickView(page, [125, 125]);
expect(await getSelectedIds(page)).toHaveLength(1);
});
test('locked element should not be selectable by dragging default tool. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createShapeElement(page, [100, 100], [150, 150]);
await selectAllByKeyboard(page);
const { lock, unlock } = getButtons(page);
await lock.click();
await pressEscape(page);
await dragBetweenViewCoords(page, [90, 90], [160, 160]);
expect(await getSelectedIds(page)).toHaveLength(0);
await clickView(page, [125, 125]);
await unlock.click();
await dragBetweenViewCoords(page, [90, 90], [160, 160]);
expect(await getSelectedIds(page)).toHaveLength(1);
});
test('descendant of locked element should not be selectable. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
const shapeId = await createShapeElement(page, [100, 100], [150, 150]);
await createShapeElement(page, [150, 150], [200, 200]);
await selectAllByKeyboard(page);
await page.keyboard.press(`${SHORT_KEY}+g`);
const groupId = (await getSelectedIds(page))[0];
const { lock, unlock } = getButtons(page);
await lock.click();
await pressEscape(page);
await clickView(page, [125, 125]);
expect(await getSelectedIds(page)).toEqual([groupId]);
await clickView(page, [125, 125]);
expect(await getSelectedIds(page)).toEqual([groupId]);
await unlock.click();
await clickView(page, [125, 125]);
expect(await getSelectedIds(page)).toEqual([shapeId]);
await pressEscape(page);
const frameId = await createFrame(page, [50, 50], [250, 250]);
await selectAllByKeyboard(page);
await lock.click();
await pressEscape(page);
await clickView(page, [125, 125]);
expect(await getSelectedIds(page)).toEqual([frameId]);
await unlock.click();
await clickView(page, [125, 125]);
expect(await getSelectedIds(page)).toEqual([shapeId]);
});
test('the selected rect of locked element should contain descendant. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
// frame
await createFrame(page, [0, 0], [100, 100]);
await createShapeElement(page, [100, 100], [150, 150]);
await dragBetweenViewCoords(page, [125, 125], [95, 95]); // add shape to frame, and partial area out of frame
await selectAllByKeyboard(page);
await assertEdgelessSelectedModelRect(page, [0, 0, 100, 100]); // only frame outline
const { lock, unlock } = getButtons(page);
await lock.click();
await assertEdgelessSelectedModelRect(page, [0, 0, 125, 125]); // frame outline and shape
await pressEscape(page);
await clickView(page, [100, 100]);
await assertEdgelessSelectedModelRect(page, [0, 0, 125, 125]);
await unlock.click();
await assertEdgelessSelectedModelRect(page, [0, 0, 100, 100]);
await pressEscape(page);
await clickView(page, [100, 100]);
await assertEdgelessSelectedModelRect(page, [75, 75, 50, 50]);
await deleteAll(page);
// mindmap
await createMindmap(page, [100, 100]);
const bound = await getSelectedBound(page);
const rootNodePos: [number, number] = [
bound[0] + 10,
bound[1] + 0.5 * bound[3],
];
await clickView(page, rootNodePos);
const rootNodeBound = await getSelectedBound(page);
await lock.click();
await assertEdgelessSelectedModelRect(page, bound);
await clickView(page, rootNodePos);
await assertEdgelessSelectedModelRect(page, bound);
await unlock.click();
await assertEdgelessSelectedModelRect(page, bound);
await clickView(page, rootNodePos);
await assertEdgelessSelectedModelRect(page, rootNodeBound);
});
test('locked element should be copyable, and the copy is unlocked', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createShapeElement(page, [100, 100], [150, 150]);
await selectAllByKeyboard(page);
await getButtons(page).lock.click();
await pressEscape(page);
await clickView(page, [125, 125]);
await copyByKeyboard(page);
await moveView(page, [200, 200]);
await pasteByKeyboard(page);
await clickView(page, [200, 200]);
await expect(getButtons(page).lock).toBeVisible();
});
test('locked element and descendant should not be draggable and moved by arrow key. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
const frame = await createFrame(page, [50, 50], [250, 250]);
const shape1 = await createShapeElement(page, [100, 100], [150, 150]);
const shape2 = await createShapeElement(page, [150, 150], [200, 200]);
await selectAllByKeyboard(page);
await getButtons(page).lock.click();
await pressEscape(page);
await dragBetweenViewCoords(page, [100, 100], [150, 150]);
await assertEdgelessElementBound(page, frame, [50, 50, 200, 200]);
await assertEdgelessElementBound(page, shape1, [100, 100, 50, 50]);
await assertEdgelessElementBound(page, shape2, [150, 150, 50, 50]);
await pressArrowDown(page, 3);
await assertEdgelessElementBound(page, frame, [50, 50, 200, 200]);
await assertEdgelessElementBound(page, shape1, [100, 100, 50, 50]);
await assertEdgelessElementBound(page, shape2, [150, 150, 50, 50]);
await getButtons(page).unlock.click();
await dragBetweenViewCoords(page, [100, 100], [150, 150]);
await assertEdgelessElementBound(page, frame, [100, 100, 200, 200]);
await assertEdgelessElementBound(page, shape1, [150, 150, 50, 50]);
await assertEdgelessElementBound(page, shape2, [200, 200, 50, 50]);
await pressArrowDown(page, 3);
await assertEdgelessElementBound(page, frame, [100, 103, 200, 200]);
await assertEdgelessElementBound(page, shape1, [150, 153, 50, 50]);
await assertEdgelessElementBound(page, shape2, [200, 203, 50, 50]);
});
test('locked element should be moved if parent is moved', async ({
page,
}) => {
await edgelessCommonSetup(page);
const frame = await createFrame(page, [50, 50], [250, 250]);
const shape = await createShapeElement(page, [100, 100], [150, 150]);
await clickView(page, [125, 125]);
await getButtons(page).lock.click();
await selectAllByKeyboard(page);
await dragBetweenViewCoords(page, [100, 100], [150, 150]);
await assertEdgelessElementBound(page, frame, [100, 100, 200, 200]);
await assertEdgelessElementBound(page, shape, [150, 150, 50, 50]);
});
test('locked element should not be scalable and rotatable. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createShapeElement(page, [100, 100], [150, 150]);
await selectAllByKeyboard(page);
const rect = page.locator('edgeless-selected-rect');
const { lock, unlock } = getButtons(page);
await expect(rect.locator('.resize')).toHaveCount(8);
await expect(rect.locator('.rotate')).toHaveCount(4);
await lock.click();
await expect(rect.locator('.resize')).toHaveCount(0);
await expect(rect.locator('.rotate')).toHaveCount(0);
await unlock.click();
await expect(rect.locator('.resize')).toHaveCount(8);
await expect(rect.locator('.rotate')).toHaveCount(4);
});
test('locked element should not be editable. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
const { lock, unlock } = getButtons(page);
// Shape
{
await createShapeElement(page, [100, 100], [150, 150]);
await selectAllByKeyboard(page);
await lock.click();
await dblclickView(page, [125, 125]);
await expect(page.locator('edgeless-shape-text-editor')).toHaveCount(0);
await unlock.click();
await dblclickView(page, [125, 125]);
await expect(page.locator('edgeless-shape-text-editor')).toHaveCount(1);
await deleteAll(page);
}
// Connector
{
await createConnectorElement(page, [100, 100], [150, 150]);
await selectAllByKeyboard(page);
await lock.click();
await dblclickView(page, [125, 125]);
await expect(page.locator('edgeless-connector-label-editor')).toHaveCount(
0
);
await unlock.click();
await dblclickView(page, [125, 125]);
await expect(page.locator('edgeless-connector-label-editor')).toHaveCount(
1
);
await deleteAll(page);
}
// Mindmap
{
await createMindmap(page, [100, 100]);
const bound = await getSelectedBound(page);
const rootPos: [number, number] = [
bound[0] + 10,
bound[1] + 0.5 * bound[3],
];
await lock.click();
await dblclickView(page, rootPos);
await expect(page.locator('edgeless-shape-text-editor')).toHaveCount(0);
await unlock.click();
await dblclickView(page, rootPos);
await expect(page.locator('edgeless-shape-text-editor')).toHaveCount(1);
await deleteAll(page);
}
// Edgeless Text
{
await createEdgelessText(page, [100, 100], 'text');
await selectAllByKeyboard(page);
await lock.click();
const text = page.locator('affine-edgeless-text');
await text.dblclick();
await type(page, '111');
await expect(text).toHaveText('text');
await unlock.click();
await text.dblclick();
await type(page, '111');
await expect(text).toHaveText('111');
await deleteAll(page);
}
// Note
{
await createNote(page, [100, 100], 'note');
await selectAllByKeyboard(page);
await lock.click();
const note = page.locator('affine-edgeless-note');
await note.dblclick();
await page.keyboard.press('End');
await type(page, '111');
await assertRichTexts(page, ['note']);
await unlock.click();
await note.dblclick();
await page.keyboard.press('End');
await type(page, '111');
await assertRichTexts(page, ['note111']);
await pressEscape(page, 3);
await deleteAll(page);
}
});
test('locked element should not be deletable. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createShapeElement(page, [100, 100], [150, 150]);
await selectAllByKeyboard(page);
const { lock, unlock } = getButtons(page);
await lock.click();
await clickView(page, [125, 125]);
await pressBackspace(page);
await assertCanvasElementsCount(page, 1);
await page.keyboard.press('Delete');
await assertCanvasElementsCount(page, 1);
await pressForwardDelete(page);
await assertCanvasElementsCount(page, 1);
await setEdgelessTool(page, 'eraser');
await dragBetweenViewCoords(page, [90, 90], [160, 160], { steps: 2 });
await assertCanvasElementsCount(page, 1);
await setEdgelessTool(page, 'default');
await selectAllByKeyboard(page);
await unlock.click();
await page.evaluate(() => {
window.doc.captureSync();
});
await pressBackspace(page);
await assertCanvasElementsCount(page, 0);
await undoByKeyboard(page);
await assertCanvasElementsCount(page, 1);
await page.keyboard.press('Delete');
await assertCanvasElementsCount(page, 0);
await undoByKeyboard(page);
await assertCanvasElementsCount(page, 1);
await pressForwardDelete(page);
await assertCanvasElementsCount(page, 0);
await undoByKeyboard(page);
await assertCanvasElementsCount(page, 1);
await setEdgelessTool(page, 'eraser');
await dragBetweenViewCoords(page, [90, 90], [160, 160], { steps: 2 });
await assertCanvasElementsCount(page, 0);
});
test('locked frame should not add new child element. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
const frame = await createFrame(page, [50, 50], [250, 250]);
await selectAllByKeyboard(page);
const frameTitle = page.locator('affine-frame-title');
const { lock, unlock } = getButtons(page);
await lock.click();
const shape = await createShapeElement(page, [100, 100], [150, 150]);
expect(await getContainerChildIds(page, frame)).toHaveLength(0);
await frameTitle.click();
await unlock.click();
expect(await getContainerChildIds(page, frame)).toHaveLength(0);
await clickView(page, [125, 125]);
await dragBetweenViewCoords(page, [125, 125], [130, 130]); // move shape into frame
expect(await getContainerChildIds(page, frame)).toEqual([shape]);
});
test('locked mindmap can not create new node by pressing Tab. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createMindmap(page, [100, 100]);
await selectAllByKeyboard(page);
const bound = await getSelectedBound(page);
const rootPos: [number, number] = [
bound[0] + 10,
bound[1] + 0.5 * bound[3],
];
const nodeEditor = page.locator('edgeless-shape-text-editor');
const { lock, unlock } = getButtons(page);
await lock.click();
await clickView(page, rootPos);
await pressTab(page);
await expect(nodeEditor).toHaveCount(0);
await unlock.click();
await clickView(page, rootPos);
await pressTab(page);
await expect(nodeEditor).toHaveCount(1);
await expect(nodeEditor).toHaveText('New node');
});
test('endpoint of locked connector should not be changeable. unlocking will recover', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createConnectorElement(page, [100, 100], [150, 150]);
const handles = page.locator('edgeless-connector-handle');
await expect(handles).toHaveCount(1);
const { lock, unlock } = getButtons(page);
await lock.click();
await expect(handles).toHaveCount(0);
await unlock.click();
await expect(handles).toHaveCount(1);
});
test('locking multiple elements will create locked group. unlocking a group will release elements', async ({
page,
}) => {
await edgelessCommonSetup(page);
const shape1 = await createShapeElement(page, [100, 100], [150, 150]);
const shape2 = await createShapeElement(page, [150, 150], [200, 200]);
await selectAllByKeyboard(page);
const { lock, unlock } = getButtons(page);
await lock.click();
const group = (await getSelectedIds(page))[0];
expect(group).not.toBeUndefined();
expect(await getTypeById(page, group)).toBe('group');
await unlock.click();
expect(await getSelectedIds(page)).toEqual([shape1, shape2]);
});
test('locking a group should not create a new group', async ({ page }) => {
await edgelessCommonSetup(page);
await createShapeElement(page, [100, 100], [150, 150]);
await createShapeElement(page, [150, 150], [200, 200]);
await selectAllByKeyboard(page);
await page.keyboard.press(`${SHORT_KEY}+g`);
const group = (await getSelectedIds(page))[0];
await getButtons(page).lock.click();
expect(await getSelectedIds(page)).toEqual([group]);
});
test('unlocking an element should not unlock its locked descendant', async ({
page,
}) => {
await edgelessCommonSetup(page);
await createFrame(page, [50, 50], [250, 250]);
await createShapeElement(page, [150, 150], [200, 200]);
const { lock, unlock } = getButtons(page);
await clickView(page, [175, 175]);
await lock.click();
await page.locator('affine-frame-title').click();
await lock.click();
await unlock.click();
await clickView(page, [175, 175]);
await expect(lock).toBeHidden();
await expect(unlock).toBeVisible();
});
});