chore: migrate blocksuite test (#9222)

This commit is contained in:
Saul-Mirone
2024-12-20 11:08:21 +00:00
parent f788fdd0a4
commit bfcc53dc1f
325 changed files with 55958 additions and 26 deletions

View File

@@ -0,0 +1,590 @@
import type { DeltaInsert } from '@inline/types.js';
import { expect } from '@playwright/test';
import {
addNoteByClick,
captureHistory,
click,
disconnectByClick,
enterPlaygroundRoom,
focusRichText,
focusTitle,
getCurrentEditorTheme,
getCurrentHTMLTheme,
getPageSnapshot,
initEmptyEdgelessState,
initEmptyParagraphState,
pressArrowLeft,
pressArrowRight,
pressBackspace,
pressEnter,
pressForwardDelete,
pressForwardDeleteWord,
pressShiftEnter,
redoByClick,
redoByKeyboard,
setSelection,
switchEditorMode,
toggleDarkMode,
type,
undoByClick,
undoByKeyboard,
waitDefaultPageLoaded,
waitNextFrame,
} from './utils/actions/index.js';
import {
assertBlockChildrenIds,
assertEmpty,
assertRichTextInlineDeltas,
assertRichTexts,
assertText,
assertTitle,
} from './utils/asserts.js';
import { scoped, test } from './utils/playwright.js';
import { getFormatBar } from './utils/query.js';
const BASIC_DEFAULT_SNAPSHOT = 'basic test default';
test(scoped`basic input`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'hello');
await test.expect(page).toHaveTitle(/BlockSuite/);
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
`${BASIC_DEFAULT_SNAPSHOT}.json`
);
await assertText(page, 'hello');
});
test(scoped`basic init with external text`, async ({ page }) => {
await enterPlaygroundRoom(page);
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text('hello'),
});
const note = doc.addBlock('affine:note', {}, rootId);
const text = new doc.Text('world');
doc.addBlock('affine:paragraph', { text }, note);
const delta = [
{ insert: 'foo ' },
{ insert: 'bar', attributes: { bold: true } },
];
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text(delta as DeltaInsert[]),
},
note
);
});
await assertTitle(page, 'hello');
await assertRichTexts(page, ['world', 'foo bar']);
await focusRichText(page);
});
test(scoped`basic multi user state`, async ({ context, page: pageA }) => {
const room = await enterPlaygroundRoom(pageA);
await initEmptyParagraphState(pageA);
await waitNextFrame(pageA);
await waitDefaultPageLoaded(pageA);
await focusTitle(pageA);
await type(pageA, 'hello');
const pageB = await context.newPage();
await enterPlaygroundRoom(pageB, {
flags: {},
room,
noInit: true,
});
await waitDefaultPageLoaded(pageB);
await focusTitle(pageB);
await assertTitle(pageB, 'hello');
await type(pageB, ' world');
await assertTitle(pageA, 'hello world');
});
test(
scoped`A open and edit, then joins B`,
async ({ context, page: pageA }) => {
const room = await enterPlaygroundRoom(pageA);
await initEmptyParagraphState(pageA);
await waitNextFrame(pageA);
await focusRichText(pageA);
await type(pageA, 'hello');
const pageB = await context.newPage();
await enterPlaygroundRoom(pageB, {
flags: {},
room,
noInit: true,
});
// wait until pageB content updated
await assertText(pageB, 'hello');
await Promise.all([
assertText(pageA, 'hello'),
expect(await getPageSnapshot(pageA, true)).toMatchSnapshot(
`${BASIC_DEFAULT_SNAPSHOT}.json`
),
expect(await getPageSnapshot(pageB, true)).toMatchSnapshot(
`${BASIC_DEFAULT_SNAPSHOT}.json`
),
assertBlockChildrenIds(pageA, '0', ['1']),
assertBlockChildrenIds(pageB, '0', ['1']),
]);
}
);
test(scoped`A first open, B first edit`, async ({ context, page: pageA }) => {
const room = await enterPlaygroundRoom(pageA);
await initEmptyParagraphState(pageA);
await waitNextFrame(pageA);
await focusRichText(pageA);
const pageB = await context.newPage();
await enterPlaygroundRoom(pageB, {
room,
noInit: true,
});
await pageB.waitForTimeout(500);
await focusRichText(pageB);
await waitNextFrame(pageA);
await waitNextFrame(pageB);
await type(pageB, 'hello');
await pageA.waitForTimeout(500);
// wait until pageA content updated
await assertText(pageA, 'hello');
await assertText(pageB, 'hello');
await Promise.all([
expect(await getPageSnapshot(pageA, true)).toMatchSnapshot(
`${BASIC_DEFAULT_SNAPSHOT}.json`
),
expect(await getPageSnapshot(pageB, true)).toMatchSnapshot(
`${BASIC_DEFAULT_SNAPSHOT}.json`
),
]);
});
test(
scoped`does not sync when disconnected`,
async ({ browser, page: pageA }) => {
test.fail();
const room = await enterPlaygroundRoom(pageA);
const pageB = await browser.newPage();
await enterPlaygroundRoom(pageB, { flags: {}, room });
await disconnectByClick(pageA);
await disconnectByClick(pageB);
// click together, both init with default id should lead to conflicts
await initEmptyParagraphState(pageA);
await initEmptyParagraphState(pageB);
await waitNextFrame(pageA);
await focusRichText(pageA);
await waitNextFrame(pageB);
await focusRichText(pageB);
await waitNextFrame(pageA);
await type(pageA, '');
await waitNextFrame(pageB);
await type(pageB, '');
await waitNextFrame(pageA);
await type(pageA, 'hello');
await waitNextFrame(pageB);
await assertText(pageB, 'hello');
await assertText(pageA, 'hello'); // actually '\n'
}
);
test(scoped`basic paired undo/redo`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'hello');
await assertText(page, 'hello');
await undoByClick(page);
await assertEmpty(page);
await redoByClick(page);
await assertText(page, 'hello');
await undoByClick(page);
await assertEmpty(page);
await redoByClick(page);
await assertText(page, 'hello');
});
test(scoped`undo/redo with keyboard`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'hello');
await assertText(page, 'hello');
await undoByKeyboard(page);
await assertEmpty(page);
await redoByClick(page);
await assertText(page, 'hello');
});
test(scoped`undo after adding block twice`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'hello');
await pressEnter(page);
await type(page, 'world');
await undoByKeyboard(page);
await assertRichTexts(page, ['hello']);
await redoByKeyboard(page);
await assertRichTexts(page, ['hello', 'world']);
});
test(scoped`undo/redo twice after adding block twice`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'hello');
await pressEnter(page);
await type(page, 'world');
await assertRichTexts(page, ['hello', 'world']);
await undoByKeyboard(page);
await assertRichTexts(page, ['hello']);
await undoByKeyboard(page);
await assertRichTexts(page, ['']);
await redoByClick(page);
await assertRichTexts(page, ['hello']);
await redoByKeyboard(page);
await assertRichTexts(page, ['hello', 'world']);
});
test(scoped`should undo/redo works on title`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await waitNextFrame(page);
await focusTitle(page);
await type(page, 'title');
await focusRichText(page);
await type(page, 'hello world');
await assertTitle(page, 'title');
await assertRichTexts(page, ['hello world']);
await captureHistory(page);
await pressBackspace(page, 5);
await captureHistory(page);
await focusTitle(page);
await type(page, ' something');
await assertTitle(page, 'title something');
await assertRichTexts(page, ['hello ']);
await focusRichText(page);
await undoByKeyboard(page);
await assertTitle(page, 'title');
await assertRichTexts(page, ['hello ']);
await undoByKeyboard(page);
await assertTitle(page, 'title');
await assertRichTexts(page, ['hello world']);
await redoByKeyboard(page);
await assertTitle(page, 'title');
await assertRichTexts(page, ['hello ']);
await redoByKeyboard(page);
await assertTitle(page, 'title something');
await assertRichTexts(page, ['hello ']);
});
test(scoped`undo multi notes`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await addNoteByClick(page);
await assertRichTexts(page, ['', '']);
await undoByClick(page);
await assertRichTexts(page, ['']);
await redoByClick(page);
await assertRichTexts(page, ['', '']);
});
test(scoped`change theme`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
const currentTheme = await getCurrentHTMLTheme(page);
await toggleDarkMode(page);
const expectNextTheme = currentTheme === 'light' ? 'dark' : 'light';
const nextHTMLTheme = await getCurrentHTMLTheme(page);
expect(nextHTMLTheme).toBe(expectNextTheme);
const nextEditorTheme = await getCurrentEditorTheme(page);
expect(nextEditorTheme).toBe(expectNextTheme);
});
test(
scoped`should be able to delete an emoji completely by pressing backspace once`,
async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/toeverything/blocksuite/issues/2138',
});
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, '🌷🙅‍♂️🏳️‍🌈');
await pressBackspace(page);
await pressBackspace(page);
await pressBackspace(page);
await assertText(page, '');
}
);
test(scoped`delete emoji in the middle of the text`, async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/toeverything/blocksuite/issues/2138',
});
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, '1🌷1🙅1🏳🌈1👨👩👧👦1');
await pressArrowLeft(page, 1);
await pressBackspace(page);
await pressArrowLeft(page, 1);
await pressBackspace(page);
await pressArrowLeft(page, 1);
await pressBackspace(page);
await pressArrowLeft(page, 1);
await pressBackspace(page);
await assertText(page, '11111');
});
test(scoped`delete emoji forward`, async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, '1🌷1🙅1🏳🌈1👨👩👧👦1');
await pressArrowLeft(page, 8);
await pressForwardDelete(page);
await pressArrowRight(page, 1);
await pressForwardDelete(page);
await pressArrowRight(page, 1);
await pressForwardDelete(page);
await pressArrowRight(page, 1);
await pressForwardDelete(page);
await assertText(page, '11111');
});
test(
scoped`ZERO_WIDTH_SPACE should be counted by one cursor position`,
async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await pressShiftEnter(page);
await type(page, 'asdfg');
await pressEnter(page);
await undoByKeyboard(page);
await page.waitForTimeout(300);
await pressBackspace(page);
await assertRichTexts(page, ['\nasdf']);
}
);
test('when no note block, click editing area auto add a new note block', async ({
page,
}) => {
await enterPlaygroundRoom(page);
await initEmptyEdgelessState(page);
await switchEditorMode(page);
await page.locator('affine-edgeless-note').click({ force: true });
await pressBackspace(page);
await switchEditorMode(page);
const edgelessNote = await page.evaluate(() => {
return document.querySelector('affine-edgeless-note');
});
expect(edgelessNote).toBeNull();
await click(page, { x: 200, y: 280 });
const pageNote = await page.evaluate(() => {
return document.querySelector('affine-note');
});
expect(pageNote).not.toBeNull();
});
test(scoped`automatic identify url text`, async ({ page }, testInfo) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'abc https://google.com ');
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
`${testInfo.title}_final.json`
);
});
test('ctrl+delete to delete one word forward', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'aaa bbb ccc');
await pressArrowLeft(page, 8);
await pressForwardDeleteWord(page);
await assertText(page, 'aaa ccc');
});
test('extended inline format', async ({ page }) => {
await enterPlaygroundRoom(page);
await initEmptyParagraphState(page);
await focusRichText(page);
await type(page, 'aaabbbaaa');
const { boldBtn, italicBtn, underlineBtn, strikeBtn, codeBtn } =
getFormatBar(page);
await setSelection(page, 0, 3, 0, 6);
await boldBtn.click();
await italicBtn.click();
await underlineBtn.click();
await strikeBtn.click();
await codeBtn.click();
await assertRichTextInlineDeltas(page, [
{
insert: 'aaa',
},
{
insert: 'bbb',
attributes: {
bold: true,
italic: true,
underline: true,
strike: true,
code: true,
},
},
{
insert: 'aaa',
},
]);
// aaa|bbbccc
await setSelection(page, 2, 3, 2, 3);
await captureHistory(page);
await type(page, 'c');
await assertRichTextInlineDeltas(page, [
{
insert: 'aaac',
},
{
insert: 'bbb',
attributes: {
bold: true,
italic: true,
underline: true,
strike: true,
code: true,
},
},
{
insert: 'aaa',
},
]);
await undoByKeyboard(page);
// aaab|bbccc
await setSelection(page, 2, 4, 2, 4);
await type(page, 'c');
await assertRichTextInlineDeltas(page, [
{
insert: 'aaa',
},
{
insert: 'bcbb',
attributes: {
bold: true,
italic: true,
underline: true,
strike: true,
code: true,
},
},
{
insert: 'aaa',
},
]);
await undoByKeyboard(page);
// aaab|b|bccc
await setSelection(page, 2, 4, 2, 5);
await type(page, 'c');
await assertRichTextInlineDeltas(page, [
{
insert: 'aaa',
},
{
insert: 'bcb',
attributes: {
bold: true,
italic: true,
underline: true,
strike: true,
code: true,
},
},
{
insert: 'aaa',
},
]);
await undoByKeyboard(page);
// aaabbb|ccc
await setSelection(page, 2, 6, 2, 6);
await type(page, 'c');
await assertRichTextInlineDeltas(page, [
{
insert: 'aaa',
},
{
insert: 'bbb',
attributes: {
bold: true,
italic: true,
underline: true,
strike: true,
code: true,
},
},
{
insert: 'c',
attributes: {
bold: true,
italic: true,
underline: true,
strike: true,
},
},
{
insert: 'aaa',
},
]);
});