mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
# After: https://github.com/user-attachments/assets/990d500d-2da7-488e-ac32-dd7bd229f896 # Before: https://github.com/user-attachments/assets/6676766a-c76a-414b-a35e-53d2cda10c24
416 lines
12 KiB
TypeScript
416 lines
12 KiB
TypeScript
import '../utils/declare-test-window.js';
|
|
|
|
import { expect } from '@playwright/test';
|
|
|
|
import {
|
|
captureHistory,
|
|
copyByKeyboard,
|
|
dragOverTitle,
|
|
enterPlaygroundRoom,
|
|
focusRichText,
|
|
focusTitle,
|
|
getClipboardHTML,
|
|
getClipboardSnapshot,
|
|
getClipboardText,
|
|
getCurrentEditorDocId,
|
|
getEditorLocator,
|
|
getPageSnapshot,
|
|
initEmptyParagraphState,
|
|
mockParseDocUrlService,
|
|
pasteByKeyboard,
|
|
pasteContent,
|
|
pressArrowRight,
|
|
pressEnter,
|
|
pressShiftTab,
|
|
pressTab,
|
|
resetHistory,
|
|
selectAllByKeyboard,
|
|
setInlineRangeInSelectedRichText,
|
|
setSelection,
|
|
SHORT_KEY,
|
|
type,
|
|
undoByClick,
|
|
undoByKeyboard,
|
|
waitNextFrame,
|
|
} from '../utils/actions/index.js';
|
|
import {
|
|
assertBlockTypes,
|
|
assertClipItems,
|
|
assertRichTexts,
|
|
assertText,
|
|
assertTitle,
|
|
} from '../utils/asserts.js';
|
|
import { scoped, test } from '../utils/playwright.js';
|
|
|
|
test(scoped`clipboard copy paste`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
await type(page, 'test');
|
|
await setInlineRangeInSelectedRichText(page, 0, 3);
|
|
await waitNextFrame(page);
|
|
await copyByKeyboard(page);
|
|
await focusRichText(page);
|
|
await page.keyboard.press(`${SHORT_KEY}+v`);
|
|
await assertText(page, 'testtes');
|
|
});
|
|
|
|
test(scoped`clipboard copy paste title`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusTitle(page);
|
|
|
|
await type(page, 'test');
|
|
await dragOverTitle(page);
|
|
await waitNextFrame(page);
|
|
await copyByKeyboard(page);
|
|
await focusTitle(page);
|
|
await page.keyboard.press(`${SHORT_KEY}+v`);
|
|
await assertTitle(page, 'testtest');
|
|
});
|
|
|
|
test(scoped`clipboard paste html`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
// set up clipboard data using html
|
|
const clipData = {
|
|
'text/html': `<span>aaa</span><span>bbb</span><span>ccc</span><bdi>ddd</bdi>`,
|
|
};
|
|
await waitNextFrame(page);
|
|
await page.evaluate(
|
|
({ clipData }) => {
|
|
const dT = new DataTransfer();
|
|
const e = new ClipboardEvent('paste', { clipboardData: dT });
|
|
Object.defineProperty(e, 'target', {
|
|
writable: false,
|
|
value: document,
|
|
});
|
|
e.clipboardData?.setData('text/html', clipData['text/html']);
|
|
document.dispatchEvent(e);
|
|
},
|
|
{ clipData }
|
|
);
|
|
await assertText(page, 'aaabbbcccddd');
|
|
});
|
|
|
|
test(scoped`split block when paste`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await resetHistory(page);
|
|
|
|
const clipData = {
|
|
'text/plain': `# text
|
|
# h1
|
|
`,
|
|
};
|
|
await type(page, 'abc');
|
|
|
|
await setInlineRangeInSelectedRichText(page, 1, 1);
|
|
await captureHistory(page);
|
|
await pasteContent(page, clipData);
|
|
await waitNextFrame(page);
|
|
|
|
await assertRichTexts(page, ['atext', 'h1c']);
|
|
|
|
await undoByKeyboard(page);
|
|
await assertRichTexts(page, ['abc']);
|
|
|
|
await type(page, 'aa');
|
|
await pressEnter(page);
|
|
await type(page, 'bb');
|
|
const topLeft123 = await getEditorLocator(page)
|
|
.locator('[data-block-id="2"] .inline-editor')
|
|
.boundingBox();
|
|
const bottomRight789 = await getEditorLocator(page)
|
|
.locator('[data-block-id="4"] .inline-editor')
|
|
.boundingBox();
|
|
if (!topLeft123) {
|
|
throw new Error('topLeft123 is not found');
|
|
}
|
|
if (!bottomRight789) {
|
|
throw new Error('bottomRight789 is not found');
|
|
}
|
|
await selectAllByKeyboard(page);
|
|
await pressArrowRight(page);
|
|
await pressEnter(page);
|
|
|
|
await pasteContent(page, clipData);
|
|
await assertRichTexts(page, ['aaa', 'bbc', 'text', 'h1']);
|
|
});
|
|
|
|
test(scoped`copy clipItems format`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await captureHistory(page);
|
|
|
|
const clipData = `
|
|
- aa
|
|
- bb
|
|
- cc
|
|
- dd
|
|
`;
|
|
|
|
await pasteContent(page, { 'text/plain': clipData });
|
|
await page.waitForTimeout(100);
|
|
await setSelection(page, 4, 1, 5, 1);
|
|
assertClipItems(page, 'text/plain', 'bc');
|
|
assertClipItems(page, 'text/html', '<ul><li>b<ul><li>c</li></ul></li></ul>');
|
|
await undoByClick(page);
|
|
await assertRichTexts(page, ['']);
|
|
});
|
|
|
|
test(scoped`copy partially selected text`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
await type(page, '123 456 789');
|
|
|
|
// select 456
|
|
await setInlineRangeInSelectedRichText(page, 4, 3);
|
|
await copyByKeyboard(page);
|
|
assertClipItems(page, 'text/plain', '456');
|
|
|
|
// move to line end
|
|
await setInlineRangeInSelectedRichText(page, 11, 0);
|
|
await pressEnter(page);
|
|
await pasteByKeyboard(page);
|
|
await waitNextFrame(page);
|
|
|
|
await assertRichTexts(page, ['123 456 789', '456']);
|
|
});
|
|
|
|
test(scoped`copy & paste outside editor`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await page.evaluate(() => {
|
|
const input = document.createElement('input');
|
|
input.setAttribute('id', 'input-test');
|
|
input.value = '123';
|
|
document.body.querySelector('#app')?.append(input);
|
|
});
|
|
await page.focus('#input-test');
|
|
await page.dblclick('#input-test');
|
|
await copyByKeyboard(page);
|
|
await focusRichText(page);
|
|
await pasteByKeyboard(page);
|
|
await waitNextFrame(page);
|
|
await assertRichTexts(page, ['123']);
|
|
});
|
|
|
|
test('should keep first line format when pasted into a new line', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
const clipData = `
|
|
- [ ] aaa
|
|
`;
|
|
|
|
await pasteContent(page, { 'text/plain': clipData });
|
|
await waitNextFrame(page);
|
|
await assertRichTexts(page, ['aaa']);
|
|
await assertBlockTypes(page, ['todo']);
|
|
});
|
|
|
|
test(scoped`auto identify url`, async ({ page }, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
// set up clipboard data using html
|
|
const clipData = {
|
|
'text/plain': `test https://www.google.com`,
|
|
};
|
|
await waitNextFrame(page);
|
|
await page.evaluate(
|
|
({ clipData }) => {
|
|
const dT = new DataTransfer();
|
|
const e = new ClipboardEvent('paste', { clipboardData: dT });
|
|
Object.defineProperty(e, 'target', {
|
|
writable: false,
|
|
value: document,
|
|
});
|
|
e.clipboardData?.setData('text/plain', clipData['text/plain']);
|
|
document.dispatchEvent(e);
|
|
},
|
|
{ clipData }
|
|
);
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_final.json`
|
|
);
|
|
});
|
|
|
|
test(scoped`pasting internal url`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusTitle(page);
|
|
await type(page, 'test page');
|
|
|
|
await focusRichText(page);
|
|
const docId = await getCurrentEditorDocId(page);
|
|
await mockParseDocUrlService(page, {
|
|
'http://workspace/doc-id': docId,
|
|
});
|
|
await pasteContent(page, {
|
|
'text/plain': 'http://workspace/doc-id',
|
|
});
|
|
await expect(page.locator('affine-reference')).toContainText('test page');
|
|
});
|
|
|
|
test(scoped`pasting internal url with params`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusTitle(page);
|
|
await type(page, 'test page');
|
|
|
|
await focusRichText(page);
|
|
const docId = await getCurrentEditorDocId(page);
|
|
await mockParseDocUrlService(page, {
|
|
'http://workspace/doc-id?mode=page&blockIds=rL2_GXbtLU2SsJVfCSmh_': docId,
|
|
});
|
|
await pasteContent(page, {
|
|
'text/plain':
|
|
'http://workspace/doc-id?mode=page&blockIds=rL2_GXbtLU2SsJVfCSmh_',
|
|
});
|
|
await expect(page.locator('affine-reference')).toContainText('test page');
|
|
});
|
|
|
|
test(
|
|
scoped`pasting an external URL from clipboard to automatically creating a link from selection`,
|
|
async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusTitle(page);
|
|
await type(page, 'test page');
|
|
|
|
await focusRichText(page);
|
|
await type(page, 'title alias');
|
|
await setSelection(page, 1, 6, 1, 11);
|
|
|
|
await pasteContent(page, {
|
|
'text/plain': 'https://affine.pro/',
|
|
});
|
|
await expect(page.locator('affine-link')).toContainText('alias');
|
|
}
|
|
);
|
|
|
|
test(
|
|
scoped`pasting an internal URL from clipboard to automatically creating a link from selection`,
|
|
async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusTitle(page);
|
|
await type(page, 'test page');
|
|
|
|
await focusRichText(page);
|
|
await type(page, 'title alias');
|
|
await setSelection(page, 1, 6, 1, 11);
|
|
|
|
const docId = await getCurrentEditorDocId(page);
|
|
await mockParseDocUrlService(page, {
|
|
'http://workspace/doc-id': docId,
|
|
});
|
|
await pasteContent(page, {
|
|
'text/plain': 'http://workspace/doc-id',
|
|
});
|
|
await expect(page.locator('affine-reference')).toContainText('alias');
|
|
}
|
|
);
|
|
|
|
test(scoped`paste parent block`, async ({ page }) => {
|
|
test.info().annotations.push({
|
|
type: 'issue',
|
|
description: 'https://github.com/toeverything/blocksuite/issues/3153',
|
|
});
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await type(page, 'This is parent');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Tab');
|
|
await type(page, 'This is child 1');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Tab');
|
|
await type(page, 'This is child 2');
|
|
await setInlineRangeInSelectedRichText(page, 0, 3);
|
|
await copyByKeyboard(page);
|
|
await focusRichText(page, 2);
|
|
await page.keyboard.press(`${SHORT_KEY}+v`);
|
|
await assertRichTexts(page, [
|
|
'This is parent',
|
|
'This is child 1',
|
|
'This is child 2Thi',
|
|
]);
|
|
});
|
|
|
|
test(scoped`clipboard copy multi selection`, async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
await type(page, 'abc');
|
|
await pressEnter(page);
|
|
await type(page, 'def');
|
|
await setSelection(page, 2, 1, 3, 1);
|
|
await waitNextFrame(page);
|
|
await copyByKeyboard(page);
|
|
await waitNextFrame(page);
|
|
await focusRichText(page, 1);
|
|
await pasteByKeyboard(page);
|
|
await waitNextFrame(page);
|
|
await type(page, 'cursor');
|
|
await waitNextFrame(page);
|
|
await assertRichTexts(page, ['abc', 'defbc', 'dcursor']);
|
|
});
|
|
|
|
test(scoped`clipboard copy nested items`, async ({ page }, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
await type(page, 'abc');
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, 'def');
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, 'ghi');
|
|
await pressEnter(page);
|
|
await pressShiftTab(page);
|
|
await pressShiftTab(page);
|
|
await type(page, 'jkl');
|
|
await setSelection(page, 2, 1, 3, 1);
|
|
await waitNextFrame(page);
|
|
await copyByKeyboard(page);
|
|
|
|
const text = await getClipboardText(page);
|
|
const html = await getClipboardHTML(page);
|
|
const snapshot = await getClipboardSnapshot(page);
|
|
expect(text).toMatchSnapshot(`${testInfo.title}-clipboard.md`);
|
|
expect(JSON.stringify(snapshot.snapshot.content, null, 2)).toMatchSnapshot(
|
|
`${testInfo.title}-clipboard.json`
|
|
);
|
|
expect(html).toMatchSnapshot(`${testInfo.title}-clipboard.html`);
|
|
|
|
await setSelection(page, 4, 1, 5, 1);
|
|
await waitNextFrame(page);
|
|
await copyByKeyboard(page);
|
|
|
|
const text2 = await getClipboardText(page);
|
|
const html2 = await getClipboardHTML(page);
|
|
const snapshot2 = await getClipboardSnapshot(page);
|
|
expect(text2).toMatchSnapshot(`${testInfo.title}-clipboard2.md`);
|
|
expect(JSON.stringify(snapshot2.snapshot.content, null, 2)).toMatchSnapshot(
|
|
`${testInfo.title}-clipboard2.json`
|
|
);
|
|
expect(html2).toMatchSnapshot(`${testInfo.title}-clipboard2.html`);
|
|
});
|