mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
1014 lines
25 KiB
TypeScript
1014 lines
25 KiB
TypeScript
import {
|
||
assertSelection,
|
||
enterInlineEditorPlayground,
|
||
focusInlineRichText,
|
||
getDeltaFromInlineRichText,
|
||
getInlineRangeIndexRect,
|
||
getInlineRichTextLine,
|
||
press,
|
||
setInlineRichTextRange,
|
||
type,
|
||
} from '@inline/__tests__/utils.js';
|
||
import { ZERO_WIDTH_SPACE } from '@inline/consts.js';
|
||
import type { InlineEditor } from '@inline/index.js';
|
||
import { expect, test } from '@playwright/test';
|
||
|
||
test('basic input', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
const editorAUndo = page.getByText('undo').nth(0);
|
||
const editorARedo = page.getByText('redo').nth(0);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abcd😃efg👨👨👧👦hj');
|
||
|
||
expect(await editorA.innerText()).toBe('abcd😃efg👨👨👧👦hj');
|
||
expect(await editorB.innerText()).toBe('abcd😃efg👨👨👧👦hj');
|
||
|
||
await editorAUndo.click();
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await editorARedo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abcd😃efg👨👨👧👦hj');
|
||
expect(await editorB.innerText()).toBe('abcd😃efg👨👨👧👦hj');
|
||
|
||
await focusInlineRichText(page);
|
||
await press(page, 'Backspace');
|
||
await press(page, 'Backspace');
|
||
await press(page, 'Backspace');
|
||
|
||
expect(await editorA.innerText()).toBe('abcd😃efg');
|
||
expect(await editorB.innerText()).toBe('abcd😃efg');
|
||
|
||
await editorAUndo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abcd😃efg👨👨👧👦hj');
|
||
expect(await editorB.innerText()).toBe('abcd😃efg👨👨👧👦hj');
|
||
|
||
await editorARedo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abcd😃efg');
|
||
expect(await editorB.innerText()).toBe('abcd😃efg');
|
||
|
||
await focusInlineRichText(page);
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'Delete');
|
||
await press(page, 'Delete');
|
||
|
||
await type(page, '🥰👨👨👧👦');
|
||
expect(await editorA.innerText()).toBe('abc🥰👨👨👧👦efg');
|
||
expect(await editorB.innerText()).toBe('abc🥰👨👨👧👦efg');
|
||
|
||
await setInlineRichTextRange(page, {
|
||
index: 3,
|
||
length: 16,
|
||
});
|
||
await page.waitForTimeout(100);
|
||
await press(page, 'Delete');
|
||
|
||
expect(await editorA.innerText()).toBe('abc');
|
||
expect(await editorA.innerText()).toBe('abc');
|
||
|
||
await editorAUndo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abcd😃efg');
|
||
expect(await editorB.innerText()).toBe('abcd😃efg');
|
||
|
||
await editorARedo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abc');
|
||
expect(await editorB.innerText()).toBe('abc');
|
||
|
||
await focusInlineRichText(page);
|
||
await page.waitForTimeout(100);
|
||
await press(page, 'Enter');
|
||
await press(page, 'Enter');
|
||
await type(page, 'bbb');
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
|
||
expect(await editorB.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
|
||
|
||
await editorAUndo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abc');
|
||
expect(await editorB.innerText()).toBe('abc');
|
||
|
||
await editorARedo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
|
||
expect(await editorB.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
|
||
|
||
await focusInlineRichText(page);
|
||
await page.waitForTimeout(100);
|
||
await press(page, 'Backspace');
|
||
await press(page, 'Backspace');
|
||
await press(page, 'Backspace');
|
||
await press(page, 'Backspace');
|
||
await press(page, 'Backspace');
|
||
|
||
expect(await editorA.innerText()).toBe('abc');
|
||
expect(await editorB.innerText()).toBe('abc');
|
||
|
||
await editorAUndo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
|
||
expect(await editorB.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nbbb');
|
||
|
||
await editorARedo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abc');
|
||
|
||
await focusInlineRichText(page);
|
||
await page.waitForTimeout(100);
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await type(page, 'bb');
|
||
await press(page, 'ArrowRight');
|
||
await press(page, 'ArrowRight');
|
||
await type(page, 'dd');
|
||
|
||
expect(await editorA.innerText()).toBe('abbbcdd');
|
||
expect(await editorB.innerText()).toBe('abbbcdd');
|
||
|
||
await editorAUndo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abc');
|
||
|
||
await editorARedo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abbbcdd');
|
||
expect(await editorB.innerText()).toBe('abbbcdd');
|
||
|
||
await focusInlineRichText(page);
|
||
await page.waitForTimeout(100);
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'Enter');
|
||
await press(page, 'Enter');
|
||
|
||
expect(await editorA.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');
|
||
expect(await editorB.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');
|
||
|
||
await editorAUndo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abbbcdd');
|
||
expect(await editorB.innerText()).toBe('abbbcdd');
|
||
|
||
await editorARedo.click();
|
||
|
||
expect(await editorA.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');
|
||
expect(await editorB.innerText()).toBe('abbbc\n' + ZERO_WIDTH_SPACE + '\ndd');
|
||
});
|
||
|
||
test('chinese input', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
const client = await page.context().newCDPSession(page);
|
||
await client.send('Input.imeSetComposition', {
|
||
selectionStart: 0,
|
||
selectionEnd: 0,
|
||
text: 'n',
|
||
});
|
||
await client.send('Input.imeSetComposition', {
|
||
selectionStart: 0,
|
||
selectionEnd: 1,
|
||
text: 'ni',
|
||
});
|
||
await client.send('Input.insertText', {
|
||
text: '你',
|
||
});
|
||
expect(await editorA.innerText()).toBe('你');
|
||
expect(await editorB.innerText()).toBe('你');
|
||
});
|
||
|
||
test('type many times in one moment', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
await page.waitForTimeout(100);
|
||
await Promise.all(
|
||
'aaaaaaaaaaaaaaaaaaaa'.split('').map(s => page.keyboard.type(s))
|
||
);
|
||
const preOffset = await page.evaluate(() => {
|
||
return getSelection()?.getRangeAt(0).endOffset;
|
||
});
|
||
await page.keyboard.press('ArrowRight');
|
||
await page.keyboard.press('ArrowRight');
|
||
await page.keyboard.press('ArrowRight');
|
||
const offset = await page.evaluate(() => {
|
||
return getSelection()?.getRangeAt(0).endOffset;
|
||
});
|
||
expect(preOffset).toBe(offset);
|
||
});
|
||
|
||
test('readonly mode', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abcdefg');
|
||
|
||
expect(await editorA.innerText()).toBe('abcdefg');
|
||
expect(await editorB.innerText()).toBe('abcdefg');
|
||
|
||
await page.evaluate(() => {
|
||
const richTextA = document
|
||
.querySelector('test-page')
|
||
?.querySelector('test-rich-text');
|
||
|
||
if (!richTextA) {
|
||
throw new Error('Cannot find editor');
|
||
}
|
||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
(richTextA as any).inlineEditor.setReadonly(true);
|
||
});
|
||
|
||
await type(page, 'aaaa');
|
||
|
||
expect(await editorA.innerText()).toBe('abcdefg');
|
||
expect(await editorB.innerText()).toBe('abcdefg');
|
||
});
|
||
|
||
test('basic styles', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
const editorABold = page.getByText('bold').nth(0);
|
||
const editorAItalic = page.getByText('italic').nth(0);
|
||
const editorAUnderline = page.getByText('underline').nth(0);
|
||
const editorAStrike = page.getByText('strike').nth(0);
|
||
const editorACode = page.getByText('code').nth(0);
|
||
|
||
const editorAUndo = page.getByText('undo').nth(0);
|
||
const editorARedo = page.getByText('redo').nth(0);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abcdefg');
|
||
|
||
expect(await editorA.innerText()).toBe('abcdefg');
|
||
expect(await editorB.innerText()).toBe('abcdefg');
|
||
|
||
let delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'abcdefg',
|
||
},
|
||
]);
|
||
|
||
await setInlineRichTextRange(page, { index: 2, length: 3 });
|
||
|
||
await editorABold.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorAItalic.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorAUnderline.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
underline: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorAStrike.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
underline: true,
|
||
strike: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorACode.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
underline: true,
|
||
strike: true,
|
||
code: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorAUndo.click({
|
||
clickCount: 5,
|
||
});
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'abcdefg',
|
||
},
|
||
]);
|
||
|
||
await editorARedo.click({
|
||
clickCount: 5,
|
||
});
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
underline: true,
|
||
strike: true,
|
||
code: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorABold.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
italic: true,
|
||
underline: true,
|
||
strike: true,
|
||
code: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorAItalic.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
underline: true,
|
||
strike: true,
|
||
code: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorAUnderline.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
strike: true,
|
||
code: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorAStrike.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'ab',
|
||
},
|
||
{
|
||
insert: 'cde',
|
||
attributes: {
|
||
code: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'fg',
|
||
},
|
||
]);
|
||
|
||
await editorACode.click();
|
||
await page.waitForTimeout(100);
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'abcdefg',
|
||
},
|
||
]);
|
||
});
|
||
|
||
test('overlapping styles', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
const editorABold = page.getByText('bold').nth(0);
|
||
const editorAItalic = page.getByText('italic').nth(0);
|
||
|
||
const editorAUndo = page.getByText('undo').nth(0);
|
||
const editorARedo = page.getByText('redo').nth(0);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abcdefghijk');
|
||
|
||
expect(await editorA.innerText()).toBe('abcdefghijk');
|
||
expect(await editorB.innerText()).toBe('abcdefghijk');
|
||
|
||
let delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'abcdefghijk',
|
||
},
|
||
]);
|
||
|
||
await setInlineRichTextRange(page, { index: 1, length: 3 });
|
||
await editorABold.click();
|
||
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'a',
|
||
},
|
||
{
|
||
insert: 'bcd',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'efghijk',
|
||
},
|
||
]);
|
||
|
||
await setInlineRichTextRange(page, { index: 7, length: 3 });
|
||
await editorABold.click();
|
||
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'a',
|
||
},
|
||
{
|
||
insert: 'bcd',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'efg',
|
||
},
|
||
{
|
||
insert: 'hij',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'k',
|
||
},
|
||
]);
|
||
|
||
await setInlineRichTextRange(page, { index: 3, length: 5 });
|
||
await editorAItalic.click();
|
||
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'a',
|
||
},
|
||
{
|
||
insert: 'bc',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'd',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'efg',
|
||
attributes: {
|
||
italic: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'h',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'ij',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'k',
|
||
},
|
||
]);
|
||
|
||
await editorAUndo.click({
|
||
clickCount: 3,
|
||
});
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'abcdefghijk',
|
||
},
|
||
]);
|
||
|
||
await editorARedo.click({
|
||
clickCount: 3,
|
||
});
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'a',
|
||
},
|
||
{
|
||
insert: 'bc',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'd',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'efg',
|
||
attributes: {
|
||
italic: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'h',
|
||
attributes: {
|
||
bold: true,
|
||
italic: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'ij',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'k',
|
||
},
|
||
]);
|
||
});
|
||
|
||
test('input continuous spaces', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abc def');
|
||
|
||
expect(await editorA.innerText()).toBe('abc def');
|
||
expect(await editorB.innerText()).toBe('abc def');
|
||
|
||
await focusInlineRichText(page);
|
||
await page.waitForTimeout(100);
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
|
||
await press(page, 'Enter');
|
||
|
||
expect(await editorA.innerText()).toBe('abc \n' + ' def');
|
||
expect(await editorB.innerText()).toBe('abc \n' + ' def');
|
||
});
|
||
|
||
test('select from the start of line using shift+arrow', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abc');
|
||
await press(page, 'Enter');
|
||
await type(page, 'def');
|
||
await press(page, 'Enter');
|
||
await type(page, 'ghi');
|
||
|
||
expect(await editorA.innerText()).toBe('abc\ndef\nghi');
|
||
expect(await editorB.innerText()).toBe('abc\ndef\nghi');
|
||
|
||
/**
|
||
* abc
|
||
* def
|
||
* |ghi
|
||
*/
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await assertSelection(page, 0, 8);
|
||
|
||
/**
|
||
* |abc
|
||
* def
|
||
* |ghi
|
||
*/
|
||
await page.keyboard.down('Shift');
|
||
await press(page, 'ArrowUp');
|
||
await press(page, 'ArrowUp');
|
||
await assertSelection(page, 0, 0, 8);
|
||
|
||
/**
|
||
* a|bc
|
||
* def
|
||
* |ghi
|
||
*/
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 1, 7);
|
||
await press(page, 'Backspace');
|
||
await page.waitForTimeout(100);
|
||
|
||
expect(await editorA.innerText()).toBe('aghi');
|
||
expect(await editorB.innerText()).toBe('aghi');
|
||
});
|
||
|
||
test('getLine', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorB = page.locator('[data-v-root="true"]').nth(1);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
expect(await editorB.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abc\ndef\nghi');
|
||
|
||
expect(await editorA.innerText()).toBe('abc\ndef\nghi');
|
||
expect(await editorB.innerText()).toBe('abc\ndef\nghi');
|
||
|
||
const [line1, offset1] = await getInlineRichTextLine(page, 0);
|
||
const [line2, offset2] = await getInlineRichTextLine(page, 1);
|
||
const [line3, offset3] = await getInlineRichTextLine(page, 4);
|
||
const [line4, offset4] = await getInlineRichTextLine(page, 5);
|
||
const [line5, offset5] = await getInlineRichTextLine(page, 8);
|
||
const [line6, offset6] = await getInlineRichTextLine(page, 11);
|
||
|
||
expect(line1).toEqual('abc');
|
||
expect(offset1).toEqual(0);
|
||
expect(line2).toEqual('abc');
|
||
expect(offset2).toEqual(1);
|
||
expect(line3).toEqual('def');
|
||
expect(offset3).toEqual(0);
|
||
expect(line4).toEqual('def');
|
||
expect(offset4).toEqual(1);
|
||
expect(line5).toEqual('ghi');
|
||
expect(offset5).toEqual(0);
|
||
expect(line6).toEqual('ghi');
|
||
expect(offset6).toEqual(3);
|
||
});
|
||
|
||
test('yText should not contain \r', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
await page.waitForTimeout(100);
|
||
const message = await page.evaluate(() => {
|
||
const richText = document
|
||
.querySelector('test-page')
|
||
?.querySelector('test-rich-text');
|
||
|
||
if (!richText) {
|
||
throw new Error('Cannot find test-rich-text');
|
||
}
|
||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
const editor = (richText as any).inlineEditor as InlineEditor;
|
||
|
||
try {
|
||
editor.insertText({ index: 0, length: 0 }, 'abc\r');
|
||
} catch (e) {
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
return (e as any).message;
|
||
}
|
||
});
|
||
|
||
expect(message).toBe(
|
||
'yText must not contain "\\r" because it will break the range synchronization'
|
||
);
|
||
});
|
||
|
||
test('embed', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorAEmbed = page.getByText('embed').nth(0);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'abcde');
|
||
|
||
expect(await editorA.innerText()).toBe('abcde');
|
||
|
||
await press(page, 'ArrowLeft');
|
||
await page.waitForTimeout(100);
|
||
await page.keyboard.down('Shift');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await press(page, 'ArrowLeft');
|
||
await page.keyboard.up('Shift');
|
||
await page.waitForTimeout(100);
|
||
await assertSelection(page, 0, 1, 3);
|
||
|
||
await editorAEmbed.click();
|
||
const embedCount = await page.locator('[data-v-embed="true"]').count();
|
||
expect(embedCount).toBe(3);
|
||
|
||
// try to update cursor position using arrow keys
|
||
await assertSelection(page, 0, 1, 3);
|
||
await press(page, 'ArrowLeft');
|
||
await assertSelection(page, 0, 1, 0);
|
||
await press(page, 'ArrowLeft');
|
||
await assertSelection(page, 0, 0, 0);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 1, 0);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 1, 1);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 2, 0);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 2, 1);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 3, 0);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 3, 1);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 4, 0);
|
||
await press(page, 'ArrowRight');
|
||
await assertSelection(page, 0, 5, 0);
|
||
await press(page, 'ArrowLeft');
|
||
await assertSelection(page, 0, 4, 0);
|
||
await press(page, 'ArrowLeft');
|
||
await assertSelection(page, 0, 3, 1);
|
||
|
||
// try to update cursor position and select embed element by clicking embed element
|
||
let rect = await getInlineRangeIndexRect(page, [0, 1]);
|
||
await page.mouse.click(rect.x + 3, rect.y);
|
||
await assertSelection(page, 0, 1, 1);
|
||
|
||
rect = await getInlineRangeIndexRect(page, [0, 2]);
|
||
await page.mouse.click(rect.x + 3, rect.y);
|
||
await assertSelection(page, 0, 2, 1);
|
||
|
||
rect = await getInlineRangeIndexRect(page, [0, 3]);
|
||
await page.mouse.click(rect.x + 3, rect.y);
|
||
await assertSelection(page, 0, 3, 1);
|
||
});
|
||
|
||
test('delete embed when pressing backspace after embed', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
const editorAEmbed = page.getByText('embed').nth(0);
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
await page.waitForTimeout(100);
|
||
await type(page, 'ab');
|
||
expect(await editorA.innerText()).toBe('ab');
|
||
|
||
await page.keyboard.down('Shift');
|
||
await press(page, 'ArrowLeft');
|
||
await page.keyboard.up('Shift');
|
||
await page.waitForTimeout(100);
|
||
await assertSelection(page, 0, 1, 1);
|
||
await editorAEmbed.click();
|
||
|
||
let delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'a',
|
||
},
|
||
{
|
||
insert: 'b',
|
||
attributes: {
|
||
embed: true,
|
||
},
|
||
},
|
||
]);
|
||
|
||
const rect = await getInlineRangeIndexRect(page, [0, 2]);
|
||
// use click to select right side of the embed instead of use arrow key
|
||
await page.mouse.click(rect.x + 3, rect.y);
|
||
await assertSelection(page, 0, 2, 0);
|
||
await press(page, 'Backspace');
|
||
|
||
delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'a',
|
||
},
|
||
]);
|
||
});
|
||
|
||
test('markdown shortcut using keyboard util', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
await page.waitForTimeout(100);
|
||
|
||
await type(page, 'aaa**bbb** ccc');
|
||
|
||
const delta = await getDeltaFromInlineRichText(page);
|
||
expect(delta).toEqual([
|
||
{
|
||
insert: 'aaa',
|
||
},
|
||
{
|
||
insert: 'bbb',
|
||
attributes: {
|
||
bold: true,
|
||
},
|
||
},
|
||
{
|
||
insert: 'ccc',
|
||
},
|
||
]);
|
||
});
|
||
|
||
test('triple click to select line', async ({ page }) => {
|
||
await enterInlineEditorPlayground(page);
|
||
await focusInlineRichText(page);
|
||
|
||
const editorA = page.locator('[data-v-root="true"]').nth(0);
|
||
|
||
expect(await editorA.innerText()).toBe(ZERO_WIDTH_SPACE);
|
||
await page.waitForTimeout(100);
|
||
await type(page, 'abc\nabc abc abc\nabc');
|
||
|
||
expect(await editorA.innerText()).toBe('abc\nabc abc abc\nabc');
|
||
|
||
const rect = await getInlineRangeIndexRect(page, [0, 10]);
|
||
await page.mouse.click(rect.x, rect.y, {
|
||
clickCount: 3,
|
||
});
|
||
await assertSelection(page, 0, 4, 11);
|
||
|
||
await press(page, 'Backspace');
|
||
expect(await editorA.innerText()).toBe('abc\n' + ZERO_WIDTH_SPACE + '\nabc');
|
||
});
|