test(editor): move blocksuite test to tests folder (#10917)
This commit is contained in:
598
tests/blocksuite/e2e/edgeless/edgeless-text.spec.ts
Normal file
598
tests/blocksuite/e2e/edgeless/edgeless-text.spec.ts
Normal file
@@ -0,0 +1,598 @@
|
||||
import type { EdgelessTextBlockComponent } from '@blocksuite/affine/blocks/edgeless-text';
|
||||
import { Bound } from '@blocksuite/affine/global/gfx';
|
||||
import { expect, type Page } from '@playwright/test';
|
||||
|
||||
import {
|
||||
autoFit,
|
||||
captureHistory,
|
||||
cutByKeyboard,
|
||||
dragBetweenIndices,
|
||||
enterPlaygroundRoom,
|
||||
getEdgelessSelectedRect,
|
||||
getPageSnapshot,
|
||||
initEmptyEdgelessState,
|
||||
pasteByKeyboard,
|
||||
pressArrowDown,
|
||||
pressArrowLeft,
|
||||
pressArrowRight,
|
||||
pressArrowUp,
|
||||
pressBackspace,
|
||||
pressEnter,
|
||||
pressEscape,
|
||||
selectAllByKeyboard,
|
||||
setEdgelessTool,
|
||||
switchEditorMode,
|
||||
toViewCoord,
|
||||
type,
|
||||
undoByKeyboard,
|
||||
waitNextFrame,
|
||||
} from '../utils/actions/index.js';
|
||||
import {
|
||||
assertBlockChildrenIds,
|
||||
assertBlockFlavour,
|
||||
assertBlockTextContent,
|
||||
assertRichTextInlineDeltas,
|
||||
assertRichTextInlineRange,
|
||||
} from '../utils/asserts.js';
|
||||
import { test } from '../utils/playwright.js';
|
||||
import { getFormatBar } from '../utils/query.js';
|
||||
|
||||
async function assertEdgelessTextModelRect(
|
||||
page: Page,
|
||||
id: string,
|
||||
bound: Bound
|
||||
) {
|
||||
const realXYWH = await page.evaluate(id => {
|
||||
const block = window.host.view.getBlock(id) as EdgelessTextBlockComponent;
|
||||
return block?.model.xywh;
|
||||
}, id);
|
||||
const realBound = Bound.deserialize(realXYWH);
|
||||
expect(realBound.x).toBeCloseTo(bound.x, 0);
|
||||
expect(realBound.y).toBeCloseTo(bound.y, 0);
|
||||
expect(realBound.w).toBeCloseTo(bound.w, 0);
|
||||
expect(realBound.h).toBeCloseTo(bound.h, 0);
|
||||
}
|
||||
|
||||
test.describe('edgeless text block', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await enterPlaygroundRoom(page);
|
||||
await initEmptyEdgelessState(page);
|
||||
await switchEditorMode(page);
|
||||
});
|
||||
|
||||
test('add text block in default mode', async ({ page }) => {
|
||||
await setEdgelessTool(page, 'default');
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
|
||||
// https://github.com/toeverything/blocksuite/pull/8574
|
||||
await pressBackspace(page);
|
||||
|
||||
await type(page, 'aaa');
|
||||
await pressEnter(page);
|
||||
await type(page, 'bbb');
|
||||
await pressEnter(page);
|
||||
await type(page, 'ccc');
|
||||
|
||||
await assertBlockFlavour(page, 4, 'affine:edgeless-text');
|
||||
await assertBlockFlavour(page, 5, 'affine:paragraph');
|
||||
await assertBlockFlavour(page, 6, 'affine:paragraph');
|
||||
await assertBlockFlavour(page, 7, 'affine:paragraph');
|
||||
await assertBlockChildrenIds(page, '4', ['5', '6', '7']);
|
||||
await assertBlockTextContent(page, 5, 'aaa');
|
||||
await assertBlockTextContent(page, 6, 'bbb');
|
||||
await assertBlockTextContent(page, 7, 'ccc');
|
||||
|
||||
await dragBetweenIndices(page, [1, 1], [3, 2]);
|
||||
await captureHistory(page);
|
||||
await pressBackspace(page);
|
||||
await assertBlockChildrenIds(page, '4', ['5']);
|
||||
await assertBlockTextContent(page, 5, 'ac');
|
||||
|
||||
await undoByKeyboard(page);
|
||||
await assertBlockChildrenIds(page, '4', ['5', '6', '7']);
|
||||
await assertBlockTextContent(page, 5, 'aaa');
|
||||
await assertBlockTextContent(page, 6, 'bbb');
|
||||
await assertBlockTextContent(page, 7, 'ccc');
|
||||
|
||||
const { boldBtn } = getFormatBar(page);
|
||||
await boldBtn.click();
|
||||
await assertRichTextInlineDeltas(
|
||||
page,
|
||||
[
|
||||
{
|
||||
insert: 'a',
|
||||
},
|
||||
{
|
||||
insert: 'aa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
1
|
||||
);
|
||||
await assertRichTextInlineDeltas(
|
||||
page,
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
2
|
||||
);
|
||||
await assertRichTextInlineDeltas(
|
||||
page,
|
||||
[
|
||||
{
|
||||
insert: 'cc',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'c',
|
||||
},
|
||||
],
|
||||
3
|
||||
);
|
||||
|
||||
await pressArrowRight(page);
|
||||
await assertRichTextInlineRange(page, 3, 2);
|
||||
await pressArrowUp(page);
|
||||
await assertRichTextInlineRange(page, 2, 2);
|
||||
});
|
||||
|
||||
test('edgeless text width auto-adjusting', async ({ page }) => {
|
||||
await setEdgelessTool(page, 'default');
|
||||
const point = await toViewCoord(page, [0, 0]);
|
||||
await page.mouse.dblclick(point[0], point[1], {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 220, 26));
|
||||
|
||||
await type(page, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
||||
await waitNextFrame(page, 1000);
|
||||
// just width changed
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 323, 26));
|
||||
|
||||
await type(page, '\nbbb');
|
||||
// width not changed, height changed
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 323, 50));
|
||||
await type(page, '\nccccccccccccccccccccccccccccccccccccccccccccccccc');
|
||||
await waitNextFrame(page, 1000);
|
||||
|
||||
// width and height changed
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 395, 74));
|
||||
|
||||
// blur, max width set to true
|
||||
await page.mouse.click(point[0] - 50, point[1], {
|
||||
delay: 100,
|
||||
});
|
||||
await page.mouse.dblclick(point[0], point[1], {
|
||||
delay: 100,
|
||||
});
|
||||
// to end of line
|
||||
await pressArrowDown(page, 3);
|
||||
await type(page, 'dddddddddddddddddddd');
|
||||
await waitNextFrame(page, 1000);
|
||||
// width not changed, height changed
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 395, 98));
|
||||
});
|
||||
|
||||
test('edgeless text width fixed when drag moving', async ({ page }) => {
|
||||
// https://github.com/toeverything/blocksuite/pull/7486
|
||||
|
||||
await setEdgelessTool(page, 'default');
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await type(page, 'aaaaaa bbbb ');
|
||||
await pressEscape(page);
|
||||
await waitNextFrame(page);
|
||||
await page.mouse.click(130, 140);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(800, 800, {
|
||||
steps: 15,
|
||||
});
|
||||
|
||||
const rect = await page.evaluate(() => {
|
||||
const container = document.querySelector(
|
||||
'.edgeless-text-block-container'
|
||||
)!;
|
||||
return container.getBoundingClientRect();
|
||||
});
|
||||
const modelXYWH = await page.evaluate(() => {
|
||||
const block = window.host.view.getBlock(
|
||||
'4'
|
||||
) as EdgelessTextBlockComponent;
|
||||
return block.model.xywh;
|
||||
});
|
||||
const bound = Bound.deserialize(modelXYWH);
|
||||
expect(rect.width).toBeCloseTo(bound.w);
|
||||
expect(rect.height).toBeCloseTo(bound.h);
|
||||
});
|
||||
|
||||
test('When creating edgeless text, if the input is empty, it will be automatically deleted', async ({
|
||||
page,
|
||||
}) => {
|
||||
await setEdgelessTool(page, 'default');
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
let block = page.locator('affine-edgeless-text[data-block-id="4"]');
|
||||
expect(await block.isVisible()).toBe(true);
|
||||
await page.mouse.click(0, 0);
|
||||
expect(await block.isVisible()).toBe(false);
|
||||
|
||||
block = page.locator('affine-edgeless-text[data-block-id="6"]');
|
||||
expect(await block.isVisible()).not.toBe(true);
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
expect(await block.isVisible()).toBe(true);
|
||||
await type(page, '\na');
|
||||
expect(await block.isVisible()).toBe(true);
|
||||
await page.mouse.click(0, 0);
|
||||
expect(await block.isVisible()).not.toBe(false);
|
||||
});
|
||||
|
||||
test('edgeless text should maintain selection when deleting across multiple lines', async ({
|
||||
page,
|
||||
}) => {
|
||||
// https://github.com/toeverything/blocksuite/pull/7443
|
||||
|
||||
await setEdgelessTool(page, 'default');
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await type(page, 'aaaa\nbbbb');
|
||||
await assertBlockTextContent(page, 5, 'aaaa');
|
||||
await assertBlockTextContent(page, 6, 'bbbb');
|
||||
|
||||
await pressArrowLeft(page);
|
||||
await page.keyboard.down('Shift');
|
||||
await pressArrowLeft(page, 3);
|
||||
await pressArrowUp(page);
|
||||
await pressArrowRight(page);
|
||||
await page.keyboard.up('Shift');
|
||||
await pressBackspace(page);
|
||||
await assertBlockTextContent(page, 5, 'ab');
|
||||
await type(page, 'sss\n');
|
||||
await assertBlockTextContent(page, 5, 'asss');
|
||||
await assertBlockTextContent(page, 7, 'b');
|
||||
});
|
||||
|
||||
test('edgeless text should not blur after pressing backspace', async ({
|
||||
page,
|
||||
}) => {
|
||||
// https://github.com/toeverything/blocksuite/pull/7555
|
||||
|
||||
await setEdgelessTool(page, 'default');
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await type(page, 'a');
|
||||
await assertBlockTextContent(page, 5, 'a');
|
||||
await pressBackspace(page);
|
||||
await type(page, 'b');
|
||||
await assertBlockTextContent(page, 5, 'b');
|
||||
});
|
||||
|
||||
// FIXME(@flrande): This test fails randomly on CI
|
||||
test.fixme('edgeless text max width', async ({ page }) => {
|
||||
await setEdgelessTool(page, 'default');
|
||||
const point = await toViewCoord(page, [0, 0]);
|
||||
await page.mouse.dblclick(point[0], point[1], {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 50, 56));
|
||||
|
||||
await type(page, 'aaaaaa');
|
||||
await waitNextFrame(page);
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 71, 56));
|
||||
await type(page, 'bbb');
|
||||
await waitNextFrame(page, 200);
|
||||
// height not changed
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 98, 56));
|
||||
|
||||
// blur
|
||||
await page.mouse.click(0, 0);
|
||||
// select text element
|
||||
await page.mouse.click(point[0] + 10, point[1] + 10);
|
||||
|
||||
let selectedRect = await getEdgelessSelectedRect(page);
|
||||
|
||||
// move cursor to the right edge and drag it to resize the width of text
|
||||
|
||||
// from left to right
|
||||
await page.mouse.move(
|
||||
selectedRect.x + selectedRect.width,
|
||||
selectedRect.y + selectedRect.height / 2
|
||||
);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(
|
||||
selectedRect.x + selectedRect.width + 30,
|
||||
selectedRect.y + selectedRect.height / 2,
|
||||
{
|
||||
steps: 10,
|
||||
}
|
||||
);
|
||||
await page.mouse.up();
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 128, 56));
|
||||
selectedRect = await getEdgelessSelectedRect(page);
|
||||
let textRect = await page
|
||||
.locator('affine-edgeless-text[data-block-id="4"]')
|
||||
.boundingBox();
|
||||
expect(selectedRect).not.toBeNull();
|
||||
expect(selectedRect.width).toBeCloseTo(textRect!.width);
|
||||
expect(selectedRect.height).toBeCloseTo(textRect!.height);
|
||||
expect(selectedRect.x).toBeCloseTo(textRect!.x);
|
||||
expect(selectedRect.y).toBeCloseTo(textRect!.y);
|
||||
|
||||
// from right to left
|
||||
await page.mouse.move(
|
||||
selectedRect.x + selectedRect.width,
|
||||
selectedRect.y + selectedRect.height / 2
|
||||
);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(
|
||||
selectedRect.x + selectedRect.width - 45,
|
||||
selectedRect.y + selectedRect.height / 2,
|
||||
{
|
||||
steps: 10,
|
||||
}
|
||||
);
|
||||
await page.mouse.up();
|
||||
// height changed
|
||||
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 83, 80));
|
||||
selectedRect = await getEdgelessSelectedRect(page);
|
||||
textRect = await page
|
||||
.locator('affine-edgeless-text[data-block-id="4"]')
|
||||
.boundingBox();
|
||||
expect(selectedRect).not.toBeNull();
|
||||
expect(selectedRect.width).toBeCloseTo(textRect!.width);
|
||||
expect(selectedRect.height).toBeCloseTo(textRect!.height);
|
||||
expect(selectedRect.x).toBeCloseTo(textRect!.x);
|
||||
expect(selectedRect.y).toBeCloseTo(textRect!.y);
|
||||
});
|
||||
|
||||
test('min width limit for embed block', async ({ page }, testInfo) => {
|
||||
await setEdgelessTool(page, 'default');
|
||||
const point = await toViewCoord(page, [0, 0]);
|
||||
await page.mouse.dblclick(point[0], point[1], {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page, 2000);
|
||||
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_init.json`
|
||||
);
|
||||
|
||||
await type(page, '@');
|
||||
await pressEnter(page);
|
||||
await waitNextFrame(page, 200);
|
||||
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_add_linked_doc.json`
|
||||
);
|
||||
|
||||
await page.locator('affine-reference').hover();
|
||||
await page.getByLabel('Switch view').click();
|
||||
await page.getByTestId('link-to-card').click();
|
||||
await autoFit(page);
|
||||
|
||||
await waitNextFrame(page);
|
||||
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_link_to_card.json`
|
||||
);
|
||||
|
||||
// blur
|
||||
await page.mouse.click(0, 0);
|
||||
// select text element
|
||||
await page.mouse.click(point[0] + 10, point[1] + 10);
|
||||
await waitNextFrame(page, 200);
|
||||
const selectedRect0 = await getEdgelessSelectedRect(page);
|
||||
|
||||
// from right to left
|
||||
await page.mouse.move(
|
||||
selectedRect0.x + selectedRect0.width,
|
||||
selectedRect0.y + selectedRect0.height / 2
|
||||
);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(
|
||||
selectedRect0.x,
|
||||
selectedRect0.y + selectedRect0.height / 2,
|
||||
{
|
||||
steps: 10,
|
||||
}
|
||||
);
|
||||
await page.mouse.up();
|
||||
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_link_to_card_min_width.json`
|
||||
);
|
||||
|
||||
const selectedRect1 = await getEdgelessSelectedRect(page);
|
||||
// from left to right
|
||||
await page.mouse.move(
|
||||
selectedRect1.x + selectedRect1.width,
|
||||
selectedRect1.y + selectedRect1.height / 2
|
||||
);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(
|
||||
selectedRect0.x + selectedRect0.width + 45,
|
||||
selectedRect1.y + selectedRect1.height / 2,
|
||||
{
|
||||
steps: 10,
|
||||
}
|
||||
);
|
||||
await page.mouse.up();
|
||||
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_drag.json`
|
||||
);
|
||||
});
|
||||
|
||||
test('cut edgeless text', async ({ page }) => {
|
||||
await setEdgelessTool(page, 'default');
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await type(page, 'aaaa\nbbbb\ncccc');
|
||||
|
||||
const edgelessText = page.locator('affine-edgeless-text');
|
||||
const paragraph = page.locator('affine-edgeless-text affine-paragraph');
|
||||
|
||||
expect(await edgelessText.count()).toBe(1);
|
||||
expect(await paragraph.count()).toBe(3);
|
||||
|
||||
await page.mouse.click(50, 50, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await page.mouse.click(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await cutByKeyboard(page);
|
||||
expect(await edgelessText.count()).toBe(0);
|
||||
expect(await paragraph.count()).toBe(0);
|
||||
|
||||
await pasteByKeyboard(page);
|
||||
expect(await edgelessText.count()).toBe(1);
|
||||
expect(await paragraph.count()).toBe(3);
|
||||
});
|
||||
|
||||
test('latex in edgeless text', async ({ page }) => {
|
||||
await setEdgelessTool(page, 'default');
|
||||
await page.mouse.dblclick(130, 140, {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page);
|
||||
await type(page, '$$bbb$$ ');
|
||||
await assertRichTextInlineDeltas(
|
||||
page,
|
||||
[
|
||||
{
|
||||
insert: ' ',
|
||||
attributes: {
|
||||
latex: 'bbb',
|
||||
},
|
||||
},
|
||||
],
|
||||
1
|
||||
);
|
||||
|
||||
await page.locator('affine-latex-node').click();
|
||||
await waitNextFrame(page);
|
||||
await type(page, 'ccc');
|
||||
const menu = page.locator('latex-editor-menu');
|
||||
const confirm = menu.locator('.latex-editor-confirm');
|
||||
await confirm.click();
|
||||
await assertRichTextInlineDeltas(
|
||||
page,
|
||||
[
|
||||
{
|
||||
insert: ' ',
|
||||
attributes: {
|
||||
latex: 'bbbccc',
|
||||
},
|
||||
},
|
||||
],
|
||||
1
|
||||
);
|
||||
|
||||
await page.locator('affine-latex-node').click();
|
||||
await page.locator('.latex-editor-hint').click();
|
||||
await type(page, 'sss');
|
||||
await assertRichTextInlineDeltas(
|
||||
page,
|
||||
[
|
||||
{
|
||||
insert: ' ',
|
||||
attributes: {
|
||||
latex: 'bbbccc',
|
||||
},
|
||||
},
|
||||
],
|
||||
1
|
||||
);
|
||||
await page.locator('latex-editor-unit').click();
|
||||
await selectAllByKeyboard(page);
|
||||
await type(page, 'sss');
|
||||
await confirm.click();
|
||||
await assertRichTextInlineDeltas(
|
||||
page,
|
||||
[
|
||||
{
|
||||
insert: ' ',
|
||||
attributes: {
|
||||
latex: 'sss',
|
||||
},
|
||||
},
|
||||
],
|
||||
1
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('press backspace at the start of first line when edgeless text exist', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await enterPlaygroundRoom(page);
|
||||
await page.evaluate(() => {
|
||||
const { doc } = window;
|
||||
const rootId = doc.addBlock('affine:page', {
|
||||
title: new window.$blocksuite.store.Text(),
|
||||
});
|
||||
doc.addBlock('affine:surface', {}, rootId);
|
||||
doc.addBlock('affine:note', {}, rootId);
|
||||
|
||||
// do not add paragraph block
|
||||
|
||||
doc.resetHistory();
|
||||
});
|
||||
await switchEditorMode(page);
|
||||
|
||||
await setEdgelessTool(page, 'default');
|
||||
const point = await toViewCoord(page, [0, 0]);
|
||||
await page.mouse.dblclick(point[0], point[1], {
|
||||
delay: 100,
|
||||
});
|
||||
await waitNextFrame(page, 2000);
|
||||
await type(page, 'aaa');
|
||||
|
||||
await waitNextFrame(page);
|
||||
await switchEditorMode(page);
|
||||
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_note_empty.json`
|
||||
);
|
||||
|
||||
await page.locator('.affine-page-root-block-container').click();
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_note_not_empty.json`
|
||||
);
|
||||
|
||||
await type(page, 'bbb');
|
||||
await pressArrowLeft(page, 3);
|
||||
await pressBackspace(page);
|
||||
await waitNextFrame(page);
|
||||
|
||||
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
||||
`${testInfo.title}_finial.json`
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user