diff --git a/demos/playground/src/__tests__/e2e/AutoLinks.spec.mjs b/demos/playground/src/__tests__/e2e/AutoLinks.spec.mjs index 81b6e622..916b1bfc 100644 --- a/demos/playground/src/__tests__/e2e/AutoLinks.spec.mjs +++ b/demos/playground/src/__tests__/e2e/AutoLinks.spec.mjs @@ -12,6 +12,8 @@ import { moveToLineBeginning, moveToLineEnd, selectAll, + selectCharacters, + toggleBold, } from '../keyboardShortcuts/index.mjs'; import { assertHTML, @@ -156,45 +158,43 @@ test.describe('Auto Links', () => { ); }); - test.fixme( - 'Does not create redundant auto-link', - async ({page, isPlainText}) => { - test.skip(isPlainText); - await focusEditor(page); - await page.keyboard.type('hm'); + test('Does not create redundant auto-link', async ({page, isPlainText}) => { + test.skip(isPlainText); + await focusEditor(page); + await page.keyboard.type('hm'); - await selectAll(page); - await click(page, '.link'); + await selectAll(page); + await click(page, '.link'); + await click(page, '.link-confirm'); - await assertHTML( - page, - html` -
- - hm - -
- `, - undefined, - {ignoreClasses: true}, - ); - await moveLeft(page, 1); - await moveRight(page, 1); - await page.keyboard.type('ttps://facebook.co'); - await assertHTML( - page, - html` - - `, - undefined, - {ignoreClasses: true}, - ); - }, - ); + await assertHTML( + page, + html` ++ + hm + +
+ `, + undefined, + {ignoreClasses: true}, + ); + await moveLeft(page, 1); + await moveRight(page, 1); + await page.keyboard.type('ttps://facebook.co'); + await assertHTML( + page, + html` + + `, + undefined, + {ignoreClasses: true}, + ); + }); test('Can create links when pasting text with multiple autolinks in a row separated by non-alphanumeric characters, but not whitespaces', async ({ page, @@ -259,4 +259,76 @@ test.describe('Auto Links', () => { {ignoreClasses: true}, ); }); + + test('Handles autolink following an invalid autolink', async ({ + page, + isPlainText, + }) => { + test.skip(isPlainText); + await focusEditor(page); + await page.keyboard.type('Hellohttps://example.com https://example.com'); + + await assertHTML( + page, + html` ++ Hellohttps://example.com + + https://example.com + +
+ `, + undefined, + {ignoreClasses: true}, + ); + }); + + test('Can convert url-like text with formatting into links', async ({ + page, + isPlainText, + }) => { + test.skip(isPlainText); + await focusEditor(page); + await page.keyboard.type('Hellohttp://example.com and more'); + + // Add bold formatting to com + await moveToLineBeginning(page); + await moveRight(page, 20); + await selectCharacters(page, 'right', 3); + await toggleBold(page); + + await assertHTML( + page, + html` ++ Hellohttp://example. + com + and more +
+ `, + undefined, + {ignoreClasses: true}, + ); + + // Add space before formatted link text + await moveToLineBeginning(page); + await moveRight(page, 5); + await page.keyboard.type(' '); + + await assertHTML( + page, + html` ++ Hello + + http://example. + com + + and more +
+ `, + undefined, + {ignoreClasses: true}, + ); + }); }); diff --git a/demos/playground/src/__tests__/e2e/Autocomplete.spec.mjs b/demos/playground/src/__tests__/e2e/Autocomplete.spec.mjs index 58bd485a..70e58bbe 100644 --- a/demos/playground/src/__tests__/e2e/Autocomplete.spec.mjs +++ b/demos/playground/src/__tests__/e2e/Autocomplete.spec.mjs @@ -19,7 +19,7 @@ test.describe('Autocomplete', () => { test.beforeEach(({isCollab, page}) => initialize({isAutocomplete: true, isCollab, page}), ); - test.fixme('Can autocomplete a word', async ({page, isPlainText}) => { + test('Can autocomplete a word', async ({page, isPlainText}) => { await focusEditor(page); await page.keyboard.type('Sort by alpha'); await sleep(500); diff --git a/demos/playground/src/__tests__/e2e/BlockWithAlignableContents.spec.mjs b/demos/playground/src/__tests__/e2e/BlockWithAlignableContents.spec.mjs index 44927655..d25df507 100644 --- a/demos/playground/src/__tests__/e2e/BlockWithAlignableContents.spec.mjs +++ b/demos/playground/src/__tests__/e2e/BlockWithAlignableContents.spec.mjs @@ -15,9 +15,9 @@ import { insertYouTubeEmbed, selectFromAlignDropdown, test, + YOUTUBE_SAMPLE_URL, } from '../utils/index.mjs'; -const TEST_URL = 'https://www.youtube-nocookie.com/embed/jNQXAC9IVRw'; test.describe('BlockWithAlignableContents', () => { test.fixme(); test.beforeEach(({isCollab, page}) => initialize({isCollab, page})); @@ -39,7 +39,7 @@ test.describe('BlockWithAlignableContents', () => { `, ); - await insertYouTubeEmbed(page, TEST_URL); + await insertYouTubeEmbed(page, YOUTUBE_SAMPLE_URL); await assertHTML( page, html` @@ -55,7 +55,7 @@ test.describe('BlockWithAlignableContents', () => { allowfullscreen="" frameborder="0" height="315" - src="${TEST_URL}" + src="${YOUTUBE_SAMPLE_URL}" title="YouTube video" width="560"> @@ -72,7 +72,7 @@ test.describe('BlockWithAlignableContents', () => { test.skip(isPlainText); await focusEditor(page); await page.keyboard.type('Hello world'); - await insertYouTubeEmbed(page, TEST_URL); + await insertYouTubeEmbed(page, YOUTUBE_SAMPLE_URL); await assertHTML( page, html` @@ -88,7 +88,7 @@ test.describe('BlockWithAlignableContents', () => { allowfullscreen="" frameborder="0" height="315" - src="${TEST_URL}" + src="${YOUTUBE_SAMPLE_URL}" title="YouTube video" width="560"> @@ -116,7 +116,7 @@ test.describe('BlockWithAlignableContents', () => { allowfullscreen="" frameborder="0" height="315" - src="${TEST_URL}" + src="${YOUTUBE_SAMPLE_URL}" title="YouTube video" width="560"> diff --git a/demos/playground/src/__tests__/e2e/ClearFormatting.spec.mjs b/demos/playground/src/__tests__/e2e/ClearFormatting.spec.mjs index 81f4a22a..cf49dba9 100644 --- a/demos/playground/src/__tests__/e2e/ClearFormatting.spec.mjs +++ b/demos/playground/src/__tests__/e2e/ClearFormatting.spec.mjs @@ -30,7 +30,7 @@ test.describe('Clear All Formatting', () => { test.fixme(); test.beforeEach(({isPlainText, isCollab, page}) => { test.skip(isPlainText); - initialize({isCollab, page}); + return initialize({isCollab, page}); }); test(`Can clear BIU formatting`, async ({page}) => { await focusEditor(page); @@ -134,7 +134,7 @@ test.describe('Clear All Formatting', () => { await clearEditor(page); - await page.keyboard.type('Luke'); + await page.keyboard.type('@Luke'); await waitForSelector(page, '#typeahead-menu ul li'); await assertHTML( @@ -143,7 +143,7 @@ test.describe('Clear All Formatting', () => {- Luke + @Luke
`, ); diff --git a/demos/playground/src/__tests__/e2e/CodeActionMenu.spec.mjs b/demos/playground/src/__tests__/e2e/CodeActionMenu.spec.mjs index 8dd763bf..b5e09f49 100644 --- a/demos/playground/src/__tests__/e2e/CodeActionMenu.spec.mjs +++ b/demos/playground/src/__tests__/e2e/CodeActionMenu.spec.mjs @@ -6,6 +6,8 @@ * */ +/* eslint-disable no-useless-escape */ + import {paste} from '../keyboardShortcuts/index.mjs'; import { assertHTML, @@ -21,7 +23,7 @@ import { } from '../utils/index.mjs'; test.describe('CodeActionMenu', () => { - test.fixme(); + //test.fixme(); test.beforeEach(({isCollab, page}) => initialize({isCollab, page})); test('Can copy code, when click `Copy` button', async ({ page, @@ -29,7 +31,8 @@ test.describe('CodeActionMenu', () => { isPlainText, browserName, }) => { - test.skip(isPlainText); + test.skip(true); + await focusEditor(page); await page.keyboard.type('``` '); await page.keyboard.press('Space'); @@ -251,21 +254,19 @@ test.describe('CodeActionMenu', () => { ); }); - test('If the code syntax is incorrect, an error message should be displayed', async ({ - page, - isCollab, - isPlainText, - }) => { - test.skip(isCollab); - test.skip(isPlainText); - await focusEditor(page); - await page.keyboard.type('``` '); - await page.keyboard.press('Space'); - await page.keyboard.type(`cons luci = 'Hello World'`); + test.fixme( + 'If the code syntax is incorrect, an error message should be displayed', + async ({page, isCollab, isPlainText}) => { + test.skip(isCollab); + test.skip(isPlainText); + await focusEditor(page); + await page.keyboard.type('``` '); + await page.keyboard.press('Space'); + await page.keyboard.type(`cons luci = 'Hello World'`); - await assertHTML( - page, - ` + await assertHTML( + page, + ` {
`,
- );
+ );
- await mouseMoveToSelector(page, 'code.PlaygroundEditorTheme__code');
- await click(page, 'button[aria-label=prettier]');
+ await mouseMoveToSelector(page, 'code.PlaygroundEditorTheme__code');
+ await click(page, 'button[aria-label=prettier]');
- await page.waitForTimeout(3000);
+ await page.waitForTimeout(3000);
- expect(await page.$('i.format.prettier-error')).toBeTruthy();
+ expect(await page.$('i.format.prettier-error')).toBeTruthy();
- const errorTips = await page.$('pre.code-error-tips');
+ const errorTips = await page.$('pre.code-error-tips');
- expect(errorTips).toBeTruthy();
+ expect(errorTips).toBeTruthy();
- const tips = await evaluate(page, () => {
- return document.querySelector('pre.code-error-tips').innerText;
- });
+ const tips = await evaluate(page, () => {
+ return document.querySelector('pre.code-error-tips').innerText;
+ });
- expect(tips).toBe(
- 'Missing semicolon. (1:6)\n' +
- "> 1 | cons luci = 'Hello World'\n" +
- ' | ^',
- );
- });
+ expect(tips).toBe(
+ 'Missing semicolon. (1:6)\n' +
+ "> 1 | cons luci = 'Hello World'\n" +
+ ' | ^',
+ );
+ },
+ );
});
diff --git a/demos/playground/src/__tests__/e2e/CodeBlock.spec.mjs b/demos/playground/src/__tests__/e2e/CodeBlock.spec.mjs
index 5b6a566a..fdafb961 100644
--- a/demos/playground/src/__tests__/e2e/CodeBlock.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/CodeBlock.spec.mjs
@@ -31,149 +31,146 @@ async function toggleCodeBlock(page) {
test.describe('CodeBlock', () => {
test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
- test.fixme(
- 'Can create code block with markdown',
- async ({page, isRichText}) => {
- await focusEditor(page);
- await page.keyboard.type('``` alert(1);');
- if (isRichText) {
- await assertSelection(page, {
- anchorOffset: 1,
- anchorPath: [0, 4, 0],
- focusOffset: 1,
- focusPath: [0, 4, 0],
- });
- await assertHTML(
- page,
- html`
-
-
- alert
-
-
- (
-
-
- 1
-
-
- )
-
-
- ;
-
-
- `,
- );
-
- // Remove code block (back to a normal paragraph) and check that highlights are converted into regular text
- await moveToEditorBeginning(page);
- await page.keyboard.press('Backspace');
- await assertHTML(
- page,
- html`
- - alert(1); -
- `, - ); - } else { - await assertHTML( - page, - html` -- \`\`\` alert(1); -
- `, - ); - } - }, - ); + test('Can create code block with markdown', async ({page, isRichText}) => { + await focusEditor(page); + await page.keyboard.type('``` alert(1);'); + if (isRichText) { + await assertSelection(page, { + anchorOffset: 1, + anchorPath: [0, 4, 0], + focusOffset: 1, + focusPath: [0, 4, 0], + }); + await assertHTML( + page, + html` +
+
+ alert
+
+
+ (
+
+
+ 1
+
+
+ )
+
+
+ ;
+
+
+ `,
+ );
- test.fixme(
- 'Can create code block with markdown and wrap existing text',
- async ({page, isRichText}) => {
- await focusEditor(page);
- await page.keyboard.type('alert(1);');
+ // Remove code block (back to a normal paragraph) and check that highlights are converted into regular text
await moveToEditorBeginning(page);
- await page.keyboard.type('``` ');
- if (isRichText) {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 0,
- focusPath: [0, 0, 0],
- });
- await assertHTML(
- page,
- html`
-
-
- alert
-
-
- (
-
-
- 1
-
-
- )
-
-
- ;
-
-
- `,
- );
- } else {
- await assertHTML(
- page,
- html`
- - \`\`\` alert(1); -
- `, - ); - } - }, - ); + await page.keyboard.press('Backspace'); + await assertHTML( + page, + html` ++ alert(1); +
+ `, + ); + } else { + await assertHTML( + page, + html` ++ \`\`\` alert(1); +
+ `, + ); + } + }); + + test('Can create code block with markdown and wrap existing text', async ({ + page, + isRichText, + }) => { + await focusEditor(page); + await page.keyboard.type('alert(1);'); + await moveToEditorBeginning(page); + await page.keyboard.type('``` '); + if (isRichText) { + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [0, 0, 0], + focusOffset: 0, + focusPath: [0, 0, 0], + }); + await assertHTML( + page, + html` +
+
+ alert
+
+
+ (
+
+
+ 1
+
+
+ )
+
+
+ ;
+
+
+ `,
+ );
+ } else {
+ await assertHTML(
+ page,
+ html`
+ + \`\`\` alert(1); +
+ `, + ); + } + }); test('Can select multiple paragraphs and convert to code block', async ({ page, @@ -243,171 +240,13 @@ test.describe('CodeBlock', () => { ); }); - test.fixme( - 'Can switch highlighting language in a toolbar', - async ({page, isRichText}) => { - await focusEditor(page); - await page.keyboard.type('``` select * from users'); - if (isRichText) { - await assertHTML( - page, - html` -
- select
-
- *
-
- from users
-
- `,
- );
- await click(page, '.toolbar-item.code-language');
- await click(page, 'button:has-text("SQL")');
- await assertHTML(
- page,
- html`
-
-
- select
-
-
-
- *
-
-
-
- from
-
- users
-
- `,
- );
- } else {
- await assertHTML(
- page,
- html`
- - \`\`\` select * from users -
- `, - ); - } - }, - ); - - test.fixme( - 'Can maintain indent when creating new lines', - async ({page, isRichText, isPlainText}) => { - test.skip(isPlainText); - await focusEditor(page); - await page.keyboard.type('``` alert(1);'); - await page.keyboard.press('Enter'); - await click(page, '.toolbar-item.alignment'); - await click(page, 'button:has-text("Indent")'); - await page.keyboard.type('alert(2);'); - await page.keyboard.press('Enter'); - await assertHTML( - page, - html` -
-
- alert
-
-
- (
-
-
- 1
-
-
- )
-
-
- ;
-
-
-
-
- alert
-
-
- (
-
-
- 2
-
-
- )
-
-
- ;
-
-
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'Can (un)indent multiple lines at once',
- async ({page, isRichText, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
- await page.keyboard.type('``` if (x) {');
- await page.keyboard.press('Enter');
- await click(page, '.toolbar-item.alignment');
- await click(page, 'button:has-text("Indent")');
- await page.keyboard.type('x();');
- await page.keyboard.press('Enter');
- await page.keyboard.press('Backspace');
- await page.keyboard.type('}');
+ test('Can switch highlighting language in a toolbar', async ({
+ page,
+ isRichText,
+ }) => {
+ await focusEditor(page);
+ await page.keyboard.type('``` select * from users');
+ if (isRichText) {
await assertHTML(
page,
html`
@@ -415,70 +254,20 @@ test.describe('CodeBlock', () => {
class="PlaygroundEditorTheme__code PlaygroundEditorTheme__ltr"
spellcheck="false"
dir="ltr"
- data-gutter="123"
+ data-gutter="1"
data-highlight-language="javascript">
+ select
- if
-
-
-
- (
-
- x
-
- )
-
-
-
- {
-
-
- if
-
-
-
- (
-
- x
-
- )
-
-
-
- {
-
-
-
-
- x
-
-
- (
-
-
- )
-
-
- ;
-
-
-
-
- }
+ from
+ users
`,
);
- await click(page, '.toolbar-item.alignment');
- await click(page, 'button:has-text("Outdent")');
- await click(page, '.toolbar-item.alignment');
- await click(page, 'button:has-text("Outdent")');
+ } else {
await assertHTML(
page,
html`
-
-
- if
-
-
-
- (
-
- x
-
- )
-
-
-
- {
-
-
-
- x
-
-
- (
-
-
- )
-
-
- ;
-
-
-
- }
-
-
+ + \`\`\` select * from users +
`, ); - }, - ); + } + }); + + test('Can maintain indent when creating new lines', async ({ + page, + isRichText, + isPlainText, + }) => { + test.skip(isPlainText); + await focusEditor(page); + await page.keyboard.type('``` alert(1);'); + await page.keyboard.press('Enter'); + await click(page, '.toolbar-item.alignment'); + await click(page, 'button:has-text("Indent")'); + await page.keyboard.type('alert(2);'); + await page.keyboard.press('Enter'); + await page.keyboard.type(';'); + await assertHTML( + page, + html` +
+
+ alert
+
+
+ (
+
+
+ 1
+
+
+ )
+
+
+ ;
+
+
+
+
+ alert
+
+
+ (
+
+
+ 2
+
+
+ )
+
+
+ ;
+
+
+
+
+ ;
+
+
+ `,
+ );
+ });
- test.fixme(
- 'Can move around lines with option+arrow keys',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- const abcHTML = html`
+ test('Can (un)indent multiple lines at once', async ({
+ page,
+ isRichText,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+ await page.keyboard.type('``` if (x) {');
+ await page.keyboard.press('Enter');
+ await click(page, '.toolbar-item.alignment');
+ await click(page, 'button:has-text("Indent")');
+ await page.keyboard.type('x();');
+ await page.keyboard.press('Enter');
+ await page.keyboard.press('Backspace');
+ await page.keyboard.type('}');
+ await assertHTML(
+ page,
+ html`
{
data-gutter="123"
data-highlight-language="javascript">
- a
+ if
+
(
+ x
)
+
- ;
+ {
+
- b
+ x
{
;
+
+ }
+
+
+ `,
+ );
+ await page.keyboard.down('Shift');
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.up('Shift');
+ await click(page, '.toolbar-item.alignment');
+ await click(page, 'button:has-text("Indent")');
+ await click(page, '.toolbar-item.alignment');
+ await click(page, 'button:has-text("Indent")');
+ await assertHTML(
+ page,
+ html`
+
+
+
+
+ if
+
+
+
+ (
+
+ x
+
+ )
+
+
+
+ {
+
+
+
+
+
- c
+ x
{
data-lexical-text="true">
;
+
+
+
+
+ }
+
- `;
- const bcaHTML = html`
+ `,
+ );
+ await page.keyboard.down('Shift');
+ await click(page, '.toolbar-item.alignment');
+ await click(page, 'button:has-text("Outdent")');
+ await page.keyboard.up('Shift');
+ await assertHTML(
+ page,
+ html`
+
- b
+ if
+
(
+ x
)
+
- ;
+ {
+
+
- c
+ x
{
;
+
+
+ }
+
+
+ `,
+ );
+ await click(page, '.toolbar-item.alignment');
+ await click(page, 'button:has-text("Outdent")');
+ await click(page, '.toolbar-item.alignment');
+ await click(page, 'button:has-text("Outdent")');
+ await assertHTML(
+ page,
+ html`
+
+
+ if
+
+
+
+ (
+
+ x
+
+ )
+
+
+
+ {
+
+
- a
+ x
{
data-lexical-text="true">
;
+
+
+ }
+
- `;
- const endOfFirstLine = {
- anchorOffset: 1,
- anchorPath: [0, 3, 0],
- focusOffset: 1,
- focusPath: [0, 3, 0],
- };
- const endOfLastLine = {
- anchorOffset: 1,
- anchorPath: [0, 13, 0],
- focusOffset: 1,
- focusPath: [0, 13, 0],
- };
- await focusEditor(page);
- await page.keyboard.type('``` a();\nb();\nc();');
- await assertHTML(page, abcHTML);
- await assertSelection(page, endOfLastLine);
- await page.keyboard.press('ArrowUp');
- await page.keyboard.press('ArrowUp');
- // Workaround for #1173: just insert and remove a space to fix Firefox losing the selection
- await page.keyboard.type(' ');
- await page.keyboard.press('Backspace');
- await assertSelection(page, endOfFirstLine);
- // End workaround
- // Ensure attempting to move a line up at the top of a codeblock no-ops
- await page.keyboard.down('Alt');
- await page.keyboard.press('ArrowUp');
- await assertSelection(page, endOfFirstLine);
- await assertHTML(page, abcHTML);
- await page.keyboard.press('ArrowDown');
- await page.keyboard.press('ArrowDown');
- await assertSelection(page, endOfLastLine);
- // Can't move a line down and out of codeblock
- await assertHTML(page, bcaHTML);
- await page.keyboard.press('ArrowDown');
- await assertSelection(page, endOfLastLine);
- await assertHTML(page, bcaHTML);
- },
- );
+ `,
+ );
+ });
+
+ test('Can move around lines with option+arrow keys', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ const abcHTML = html`
+
+
+ a
+
+
+ (
+
+
+ )
+
+
+ ;
+
+
+
+ b
+
+
+ (
+
+
+ )
+
+
+ ;
+
+
+
+ c
+
+
+ (
+
+
+ )
+
+
+ ;
+
+
+ `;
+ const bcaHTML = html`
+
+
+ b
+
+
+ (
+
+
+ )
+
+
+ ;
+
+
+
+ c
+
+
+ (
+
+
+ )
+
+
+ ;
+
+
+
+ a
+
+
+ (
+
+
+ )
+
+
+ ;
+
+
+ `;
+ const endOfFirstLine = {
+ anchorOffset: 1,
+ anchorPath: [0, 3, 0],
+ focusOffset: 1,
+ focusPath: [0, 3, 0],
+ };
+ const endOfLastLine = {
+ anchorOffset: 1,
+ anchorPath: [0, 13, 0],
+ focusOffset: 1,
+ focusPath: [0, 13, 0],
+ };
+ await focusEditor(page);
+ await page.keyboard.type('``` a();\nb();\nc();');
+ await assertHTML(page, abcHTML);
+ await assertSelection(page, endOfLastLine);
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+ // Workaround for #1173: just insert and remove a space to fix Firefox losing the selection
+ await page.keyboard.type(' ');
+ await page.keyboard.press('Backspace');
+ await assertSelection(page, endOfFirstLine);
+ // End workaround
+ // Ensure attempting to move a line up at the top of a codeblock no-ops
+ await page.keyboard.down('Alt');
+ await page.keyboard.press('ArrowUp');
+ await assertSelection(page, endOfFirstLine);
+ await assertHTML(page, abcHTML);
+ await page.keyboard.press('ArrowDown');
+ await page.keyboard.press('ArrowDown');
+ await assertSelection(page, endOfLastLine);
+ // Can't move a line down and out of codeblock
+ await assertHTML(page, bcaHTML);
+ await page.keyboard.press('ArrowDown');
+ await assertSelection(page, endOfLastLine);
+ await assertHTML(page, bcaHTML);
+ });
/**
* Code example for tests:
@@ -970,6 +980,69 @@ test.describe('CodeBlock', () => {
`;
+ const EXPECTED_HTML_GOOGLE_SPREADSHEET = html`
+
+ + + Surface + + + |
+
+ + + MWP_WORK_LS_COMPOSER + + + |
+
+ + + 77349 + + + |
+
+ + Lexical + + |
+
+ + XDS_RICH_TEXT_AREA + + |
+ + sdvd + + sdfvsfs + + | +
1
-
+
@@ -1038,7 +1111,14 @@ test.describe('CodeBlock', () => {
`,
name: 'Multiline ',
- pastedHTML: `1\n2
`,
+ // TODO This is not correct. This resembles how Lexical exports code right now but
+ // semantically it should be wrapped in a pre
+ pastedHTML: `1
2
`,
+ },
+ {
+ expectedHTML: EXPECTED_HTML_GOOGLE_SPREADSHEET,
+ name: 'Google Spreadsheet',
+ pastedHTML: `
`,
},
];
@@ -1057,85 +1137,85 @@ test.describe('CodeBlock', () => {
});
});
- test.fixme(
- 'When pressing CMD/Ctrl + Left, CMD/Ctrl + Right, the cursor should go to the start of the code',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
- await page.keyboard.type('``` ');
- await page.keyboard.press('Space');
- await click(page, '.toolbar-item.alignment');
- await click(page, 'button:has-text("Indent")');
- await page.keyboard.type('a b');
- await page.keyboard.press('Space');
- await page.keyboard.press('Enter');
- await page.keyboard.type('c d');
- await page.keyboard.press('Space');
- await assertHTML(
- page,
- `
+ test('When pressing CMD/Ctrl + Left, CMD/Ctrl + Right, the cursor should go to the start of the code', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+ await page.keyboard.type('``` ');
+ await page.keyboard.press('Space');
+ await click(page, '.toolbar-item.alignment');
+ await click(page, 'button:has-text("Indent")');
+ await page.keyboard.type('a b');
+ await page.keyboard.press('Space');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('c d');
+ await page.keyboard.press('Space');
+ await assertHTML(
+ page,
+ `
+
a b
+
c d
`,
- );
-
- await selectCharacters(page, 'left', 13);
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 2, 0],
- focusOffset: 0,
- focusPath: [0, 0, 0],
- });
+ );
- await moveToStart(page);
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0],
- focusOffset: 2,
- focusPath: [0, 0, 0],
- });
+ await selectCharacters(page, 'left', 11);
+ await assertSelection(page, {
+ anchorOffset: 5,
+ anchorPath: [0, 4, 0],
+ focusOffset: 1,
+ focusPath: [0, 1, 0],
+ });
- await moveToEnd(page);
- await assertSelection(page, {
- anchorOffset: 5,
- anchorPath: [0, 0, 0],
- focusOffset: 5,
- focusPath: [0, 0, 0],
- });
+ await moveToStart(page);
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 0,
+ focusPath: [0, 0, 0],
+ });
- await moveToStart(page);
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0],
- focusOffset: 2,
- focusPath: [0, 0, 0],
- });
+ await moveToEnd(page);
+ await assertSelection(page, {
+ anchorOffset: 5,
+ anchorPath: [0, 1, 0],
+ focusOffset: 5,
+ focusPath: [0, 1, 0],
+ });
- await selectCharacters(page, 'right', 11);
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0],
- focusOffset: 6,
- focusPath: [0, 2, 0],
- });
+ await moveToStart(page);
+ await assertSelection(page, {
+ anchorOffset: 1,
+ anchorPath: [0, 1, 0],
+ focusOffset: 1,
+ focusPath: [0, 1, 0],
+ });
- await moveToEnd(page);
- await assertSelection(page, {
- anchorOffset: 5,
- anchorPath: [0, 2, 0],
- focusOffset: 5,
- focusPath: [0, 2, 0],
- });
+ await selectCharacters(page, 'right', 11);
+ await assertSelection(page, {
+ anchorOffset: 1,
+ anchorPath: [0, 1, 0],
+ focusOffset: 5,
+ focusPath: [0, 4, 0],
+ });
- await page.pause();
- },
- );
+ await moveToEnd(page);
+ await assertSelection(page, {
+ anchorOffset: 5,
+ anchorPath: [0, 4, 0],
+ focusOffset: 5,
+ focusPath: [0, 4, 0],
+ });
+ });
});
diff --git a/demos/playground/src/__tests__/e2e/Composition.spec.mjs b/demos/playground/src/__tests__/e2e/Composition.spec.mjs
index aa0d86e9..a48a7f8e 100644
--- a/demos/playground/src/__tests__/e2e/Composition.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/Composition.spec.mjs
@@ -181,27 +181,73 @@ test.describe('Composition', () => {
});
test.describe('IME', () => {
- test.fixme();
test('Can type Hiragana via IME', async ({page, browserName}) => {
// We don't yet support FF.
- test.skip(browserName === 'firefox');
-
+ test.skip(browserName !== 'chromium');
await focusEditor(page);
await enableCompositionKeyEvents(page);
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
- await page.keyboard.type(' ');
- await page.keyboard.imeSetComposition('m', 1, 1);
- await page.keyboard.imeSetComposition('も', 1, 1);
- await page.keyboard.imeSetComposition('もj', 2, 2);
- await page.keyboard.imeSetComposition('もじ', 2, 2);
- await page.keyboard.imeSetComposition('もじあ', 3, 3);
- await page.keyboard.insertText('もじあ');
+ const client = await page.context().newCDPSession(page);
+
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
+ await client.send('Input.insertText', {
+ text: ' ',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'm',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'も',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もj',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もじ',
+ });
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'もじあ',
+ });
+ await client.send('Input.insertText', {
+ text: 'もじあ',
+ });
await assertHTML(
page,
@@ -226,7 +272,7 @@ test.describe('Composition', () => {
browserName,
}) => {
// We don't yet support FF.
- test.skip(browserName === 'firefox');
+ test.skip(browserName !== 'chromium');
await focusEditor(page);
await enableCompositionKeyEvents(page);
@@ -241,19 +287,79 @@ test.describe('Composition', () => {
await page.keyboard.press('ArrowLeft');
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
- await page.keyboard.type(' ');
- await page.keyboard.imeSetComposition('m', 1, 1);
- await page.keyboard.imeSetComposition('も', 1, 1);
- await page.keyboard.imeSetComposition('もj', 2, 2);
- await page.keyboard.imeSetComposition('もじ', 2, 2);
- await page.keyboard.imeSetComposition('もじあ', 3, 3);
- await page.keyboard.insertText('もじあ');
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
+ // await page.keyboard.type(' ');
+ await client.send('Input.insertText', {
+ text: ' ',
+ });
+ // await page.keyboard.imeSetComposition('m', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'm',
+ });
+ // await page.keyboard.imeSetComposition('も', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'も',
+ });
+ // await page.keyboard.imeSetComposition('もj', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もj',
+ });
+ // await page.keyboard.imeSetComposition('もじ', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もじ',
+ });
+ // await page.keyboard.imeSetComposition('もじあ', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'もじあ',
+ });
+ // await page.keyboard.insertText('もじあ');
+ await client.send('Input.insertText', {
+ text: 'もじあ',
+ });
await assertHTML(
page,
@@ -282,7 +388,7 @@ test.describe('Composition', () => {
isPlainText,
}) => {
// We don't yet support FF.
- test.skip(browserName === 'firefox' || isPlainText);
+ test.skip(browserName !== 'chromium' || isPlainText);
await focusEditor(page);
await enableCompositionKeyEvents(page);
@@ -293,12 +399,41 @@ test.describe('Composition', () => {
await page.keyboard.press('b');
await keyUpCtrlOrMeta(page);
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
await assertHTML(
page,
@@ -323,230 +458,506 @@ test.describe('Composition', () => {
});
});
- test('Can type Hiragana via IME between emojis', async ({
- page,
- browserName,
- }) => {
- test.skip(browserName === 'firefox');
- await focusEditor(page);
- await enableCompositionKeyEvents(page);
-
- await page.keyboard.type(':):)');
-
- await page.keyboard.press('ArrowLeft');
-
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
- await page.keyboard.type(' ');
- await page.keyboard.imeSetComposition('m', 1, 1);
- await page.keyboard.imeSetComposition('も', 1, 1);
- await page.keyboard.imeSetComposition('もj', 2, 2);
- await page.keyboard.imeSetComposition('もじ', 2, 2);
- await page.keyboard.imeSetComposition('もじあ', 3, 3);
- await page.keyboard.insertText('もじあ');
-
- await assertHTML(
- page,
- html`
-
-
- 🙂
-
- すし もじあ
-
- 🙂
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 1, 0],
- focusOffset: 6,
- focusPath: [0, 1, 0],
- });
-
- await pressBackspace(page, 6);
- await assertHTML(
- page,
- html`
-
-
- 🙂
-
-
- 🙂
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0, 0],
- focusOffset: 2,
- focusPath: [0, 0, 0, 0],
- });
+ test.fixme(
+ 'Can type Hiragana via IME between emojis',
+ async ({page, browserName}) => {
+ test.skip(browserName !== 'chromium');
+ await focusEditor(page);
+ await enableCompositionKeyEvents(page);
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('', 0, 0);
- // Escape would fire here
- await page.keyboard.insertText('');
+ await page.keyboard.type(':):)');
- await assertHTML(
- page,
- html`
-
-
- 🙂
-
-
- 🙂
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0, 0],
- focusOffset: 2,
- focusPath: [0, 0, 0, 0],
- });
- });
-
- test('Can type Hiragana via IME at the end of a mention', async ({
- page,
- browserName,
- }) => {
- // We don't yet support FF.
- test.skip(browserName === 'firefox');
+ await page.keyboard.press('ArrowLeft');
- await focusEditor(page);
- await enableCompositionKeyEvents(page);
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
+ await page.keyboard.type(' ');
+ // await page.keyboard.imeSetComposition('m', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'm',
+ });
+ // await page.keyboard.imeSetComposition('も', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'も',
+ });
+ // await page.keyboard.imeSetComposition('もj', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もj',
+ });
+ // await page.keyboard.imeSetComposition('もじ', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もじ',
+ });
+ // await page.keyboard.imeSetComposition('もじあ', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'もじあ',
+ });
+ // await page.keyboard.insertText('もじあ');
+ await client.send('Input.insertText', {
+ text: 'もじあ',
+ });
- await page.keyboard.type('Luke');
- await waitForSelector(page, '#typeahead-menu ul li');
- await page.keyboard.press('Enter');
+ await assertHTML(
+ page,
+ html`
+
+
+ 🙂
+
+ すし もじあ
+
+ 🙂
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 1, 0],
+ focusOffset: 6,
+ focusPath: [0, 1, 0],
+ });
- await waitForSelector(page, '.mention');
+ await pressBackspace(page, 6);
+ await assertHTML(
+ page,
+ html`
+
+
+ 🙂
+
+
+ 🙂
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 2,
+ anchorPath: [0, 0, 0, 0],
+ focusOffset: 2,
+ focusPath: [0, 0, 0, 0],
+ });
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
- await page.keyboard.type(' ');
- await page.keyboard.imeSetComposition('m', 1, 1);
- await page.keyboard.imeSetComposition('も', 1, 1);
- await page.keyboard.imeSetComposition('もj', 2, 2);
- await page.keyboard.imeSetComposition('もじ', 2, 2);
- await page.keyboard.imeSetComposition('もじあ', 3, 3);
- await page.keyboard.insertText('もじあ');
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('', 0, 0);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 0,
+ text: '',
+ });
+ // Escape would fire here
+ await page.keyboard.insertText('');
- await assertHTML(
- page,
- html`
-
-
- Luke Skywalker
-
- すし もじあ
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 1, 0],
- focusOffset: 6,
- focusPath: [0, 1, 0],
- });
- });
+ await assertHTML(
+ page,
+ html`
+
+
+ 🙂
+
+
+ 🙂
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 2,
+ anchorPath: [0, 0, 0, 0],
+ focusOffset: 2,
+ focusPath: [0, 0, 0, 0],
+ });
+ },
+ );
- test('Can type Hiragana via IME part way through a mention', async ({
- page,
- browserName,
- }) => {
- // We don't yet support FF.
- test.skip(browserName === 'firefox');
+ test.fixme(
+ 'Can type Hiragana via IME at the end of a mention',
+ async ({page, browserName}) => {
+ // We don't yet support FF.
+ test.skip(browserName !== 'chromium');
- await focusEditor(page);
- await enableCompositionKeyEvents(page);
+ await focusEditor(page);
+ await enableCompositionKeyEvents(page);
- await page.keyboard.type('Luke');
- await waitForSelector(page, '#typeahead-menu ul li');
- await page.keyboard.press('Enter');
+ await page.keyboard.type('@Luke');
+ await waitForSelector(page, '#typeahead-menu ul li');
+ await page.keyboard.press('Enter');
- await waitForSelector(page, '.mention');
+ await waitForSelector(page, '.mention');
- await moveLeft(page, 9);
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
+ await page.keyboard.type(' ');
+ // await page.keyboard.imeSetComposition('m', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'm',
+ });
+ // await page.keyboard.imeSetComposition('も', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'も',
+ });
+ // await page.keyboard.imeSetComposition('もj', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もj',
+ });
+ // await page.keyboard.imeSetComposition('もじ', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もじ',
+ });
+ // await page.keyboard.imeSetComposition('もじあ', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'もじあ',
+ });
+ // await page.keyboard.insertText('もじあ');
+ await client.send('Input.insertText', {
+ text: 'もじあ',
+ });
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
- await page.keyboard.type(' ');
- await page.keyboard.imeSetComposition('m', 1, 1);
- await page.keyboard.imeSetComposition('も', 1, 1);
- await page.keyboard.imeSetComposition('もj', 2, 2);
- await page.keyboard.imeSetComposition('もじ', 2, 2);
- await page.keyboard.imeSetComposition('もじあ', 3, 3);
- await page.keyboard.insertText('もじあ');
-
- if (browserName === 'webkit')
await assertHTML(
page,
html`
-
- Luke すし もじあSkywalker
+
+ Luke Skywalker
+ すし もじあ
`,
);
- /* eslint-disable no-irregular-whitespace */
- if (browserName === 'chromium')
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 1, 0],
+ focusOffset: 6,
+ focusPath: [0, 1, 0],
+ });
+ },
+ );
+
+ test.fixme(
+ 'Can type Hiragana via IME part way through a mention',
+ async ({page, browserName}) => {
+ // We don't yet support FF.
+ test.skip(browserName !== 'chromium');
+
+ await focusEditor(page);
+ await enableCompositionKeyEvents(page);
+
+ await page.keyboard.type('@Luke');
+ await waitForSelector(page, '#typeahead-menu ul li');
+ await page.keyboard.press('Enter');
+
+ await waitForSelector(page, '.mention');
+
+ await moveLeft(page, 9);
+
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
+ await page.keyboard.type(' ');
+ // await page.keyboard.imeSetComposition('m', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'm',
+ });
+ // await page.keyboard.imeSetComposition('も', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'も',
+ });
+ // await page.keyboard.imeSetComposition('もj', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もj',
+ });
+ // await page.keyboard.imeSetComposition('もじ', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もじ',
+ });
+ // await page.keyboard.imeSetComposition('もじあ', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'もじあ',
+ });
+ // await page.keyboard.insertText('もじあ');
+ await client.send('Input.insertText', {
+ text: 'もじあ',
+ });
+
+ if (browserName === 'webkit') {
+ await assertHTML(
+ page,
+ html`
+
+
+ Luke すし もじあSkywalker
+
+
+ `,
+ );
+ }
+ /* eslint-disable no-irregular-whitespace */
+ if (browserName === 'chromium') {
+ await assertHTML(
+ page,
+ html`
+
+ Luke すし もじあSkywalker
+
+ `,
+ );
+ }
+
+ await assertSelection(page, {
+ anchorOffset: 12,
+ anchorPath: [0, 0, 0],
+ focusOffset: 12,
+ focusPath: [0, 0, 0],
+ });
+ },
+ );
+
+ test.fixme(
+ 'Typing after mention with IME should not break it',
+ async ({page, browserName, isPlainText}) => {
+ // We don't yet support FF.
+ test.skip(browserName !== 'chromium');
+
+ await focusEditor(page);
+ await enableCompositionKeyEvents(page);
+
+ await page.keyboard.type('@Luke');
+ await waitForSelector(page, '#typeahead-menu ul li');
+ await page.keyboard.press('Enter');
+
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
+
await assertHTML(
page,
html`
- Luke すし もじあSkywalker
+
+ Luke Skywalker
+
+ すし
`,
);
-
- await assertSelection(page, {
- anchorOffset: 12,
- anchorPath: [0, 0, 0],
- focusOffset: 12,
- focusPath: [0, 0, 0],
- });
- });
+ },
+ );
test('Can type Hiragana via IME with hashtags', async ({
page,
@@ -554,27 +965,84 @@ test.describe('Composition', () => {
isCollab,
}) => {
// We don't yet support FF.
- test.skip(browserName === 'firefox');
+ test.skip(browserName !== 'chromium');
await focusEditor(page);
await enableCompositionKeyEvents(page);
await page.keyboard.type('#');
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
await page.keyboard.type(' ');
- await page.keyboard.imeSetComposition('m', 1, 1);
- await page.keyboard.imeSetComposition('も', 1, 1);
- await page.keyboard.imeSetComposition('もj', 2, 2);
- await page.keyboard.imeSetComposition('もじ', 2, 2);
- await page.keyboard.imeSetComposition('もじあ', 3, 3);
- await page.keyboard.insertText('もじあ');
+ // await page.keyboard.imeSetComposition('m', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'm',
+ });
+ // await page.keyboard.imeSetComposition('も', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'も',
+ });
+ // await page.keyboard.imeSetComposition('もj', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もj',
+ });
+ // await page.keyboard.imeSetComposition('もじ', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もじ',
+ });
+ // await page.keyboard.imeSetComposition('もじあ', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'もじあ',
+ });
+ // await page.keyboard.insertText('もじあ');
+ await client.send('Input.insertText', {
+ text: 'もじあ',
+ });
await assertHTML(
page,
@@ -600,12 +1068,40 @@ test.describe('Composition', () => {
await moveToLineBeginning(page);
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
await assertHTML(
page,
@@ -625,83 +1121,180 @@ test.describe('Composition', () => {
});
});
- test.fixme(
- 'Can type, delete and cancel Hiragana via IME',
- async ({page, browserName}) => {
- // We don't yet support FF.
- test.skip(browserName === 'firefox');
+ test('Can type, delete and cancel Hiragana via IME', async ({
+ page,
+ browserName,
+ }) => {
+ // We don't yet support FF.
+ test.skip(browserName !== 'chromium');
- await focusEditor(page);
- await enableCompositionKeyEvents(page);
+ await focusEditor(page);
+ await enableCompositionKeyEvents(page);
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('', 0, 0);
- // Escape would fire here
- await page.keyboard.insertText('');
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('', 0, 0);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 0,
+ text: '',
+ });
+ // Escape would fire here
+ await page.keyboard.insertText('');
- await assertHTML(
- page,
- html`
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0],
- focusOffset: 0,
- focusPath: [0],
- });
+ await assertHTML(
+ page,
+ html`
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0],
+ focusOffset: 0,
+ focusPath: [0],
+ });
- await page.keyboard.type(' ');
- await page.keyboard.press('ArrowLeft');
+ await page.keyboard.type(' ');
+ await page.keyboard.press('ArrowLeft');
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('', 0, 0);
- // Escape would fire here
- await page.keyboard.insertText('');
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('', 0, 0);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 0,
+ text: '',
+ });
+ // Escape would fire here
+ await page.keyboard.insertText('');
- await assertHTML(
- page,
- html`
-
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 0,
- focusPath: [0, 0, 0],
- });
- },
- );
+ await assertHTML(
+ page,
+ html`
+
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 0,
+ focusPath: [0, 0, 0],
+ });
+ });
test.fixme(
'Floating toolbar should not be displayed when using IME',
async ({page, browserName, isPlainText}) => {
- test.skip(isPlainText);
// We don't yet support FF.
- test.skip(browserName === 'firefox');
+ test.skip(browserName !== 'chromium' || isPlainText);
await focusEditor(page);
await enableCompositionKeyEvents(page);
- await page.keyboard.imeSetComposition('s', 0, 1);
- await page.keyboard.imeSetComposition('す', 0, 1);
- await page.keyboard.imeSetComposition('すs', 0, 2);
- await page.keyboard.imeSetComposition('すsh', 0, 3);
- await page.keyboard.imeSetComposition('すsh', 0, 4);
+ const client = await page.context().newCDPSession(page);
+
+ // await page.keyboard.imeSetComposition('s', 0, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 0, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 0, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 0, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 0, 4);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 0,
+ selectionEnd: 4,
+ text: 'すsh',
+ });
const isFloatingToolbarDisplayedWhenUseIME = await evaluate(
page,
@@ -712,7 +1305,10 @@ test.describe('Composition', () => {
expect(isFloatingToolbarDisplayedWhenUseIME).toEqual(false);
- await page.keyboard.insertText('すsh');
+ // await page.keyboard.insertText('すsh');
+ await client.send('Input.insertText', {
+ text: 'すsh',
+ });
await selectCharacters(page, 'left', 3);
const isFloatingToolbarDisplayed = await evaluate(page, () => {
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste.spec.mjs
deleted file mode 100644
index 4cc7a1e7..00000000
--- a/demos/playground/src/__tests__/e2e/CopyAndPaste.spec.mjs
+++ /dev/null
@@ -1,3306 +0,0 @@
-/**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-import {expect} from '@playwright/test';
-
-import {
- extendToNextWord,
- moveLeft,
- moveToEditorBeginning,
- moveToEditorEnd,
- moveToLineBeginning,
- moveToLineEnd,
- moveToNextWord,
- moveToPrevWord,
- selectAll,
- selectCharacters,
- undo,
-} from '../keyboardShortcuts/index.mjs';
-import {
- assertHTML,
- assertSelection,
- clearEditor,
- click,
- copyToClipboard,
- focus,
- focusEditor,
- html,
- initialize,
- insertTable,
- IS_LINUX,
- IS_WINDOWS,
- LEXICAL_IMAGE_BASE64,
- pasteFromClipboard,
- selectCellsFromTableCords,
- selectFromAlignDropdown,
- sleepInsertImage,
- test,
-} from '../utils/index.mjs';
-
-test.describe('CopyAndPaste', () => {
- test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
- test('Basic copy + paste', async ({isRichText, page, browserName}) => {
- await focusEditor(page);
-
- // Add paragraph
- await page.keyboard.type('Copy + pasting?');
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
- await page.keyboard.type('Sounds good!');
- if (isRichText) {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
-
- Sounds good!
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 12,
- anchorPath: [2, 0, 0],
- focusOffset: 12,
- focusPath: [2, 0, 0],
- });
- } else {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
- Sounds good!
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 12,
- anchorPath: [0, 3, 0],
- focusOffset: 12,
- focusPath: [0, 3, 0],
- });
- }
-
- // Select all the text
- await selectAll(page);
- if (isRichText) {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
-
- Sounds good!
-
- `,
- );
- if (browserName === 'firefox') {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [],
- focusOffset: 3,
- focusPath: [],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 12,
- focusPath: [2, 0, 0],
- });
- }
- } else {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
- Sounds good!
-
- `,
- );
- if (browserName === 'firefox') {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [],
- focusOffset: 1,
- focusPath: [],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 12,
- focusPath: [0, 3, 0],
- });
- }
- }
-
- // Copy all the text
- const clipboard = await copyToClipboard(page);
- if (isRichText) {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
-
- Sounds good!
-
- `,
- );
- } else {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
- Sounds good!
-
- `,
- );
- }
-
- // Paste after
- await page.keyboard.press('ArrowRight');
- await pasteFromClipboard(page, clipboard);
- if (isRichText) {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
-
- Sounds good!Copy + pasting?
-
-
-
- Sounds good!
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 12,
- anchorPath: [4, 0, 0],
- focusOffset: 12,
- focusPath: [4, 0, 0],
- });
- } else {
- await assertHTML(
- page,
- html`
-
- Copy + pasting?
-
-
- Sounds good!Copy + pasting?
-
-
- Sounds good!
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 12,
- anchorPath: [0, 6, 0],
- focusOffset: 12,
- focusPath: [0, 6, 0],
- });
- }
- });
-
- test.fixme(
- `Copy and paste heading`,
- async ({isPlainText, page, browserName}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
- await page.keyboard.type('# Heading');
- await page.keyboard.press('Enter');
- await page.keyboard.type('Some text');
-
- await moveToEditorBeginning(page);
- await page.keyboard.down('Shift');
- await moveToLineEnd(page);
- await page.keyboard.up('Shift');
-
- const clipboard = await copyToClipboard(page);
-
- await moveToEditorEnd(page);
- await page.keyboard.press('Enter');
-
- // Paste the content
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- Heading
-
-
- Some text
-
-
- Heading
-
- `,
- );
-
- await assertSelection(page, {
- anchorOffset: 7,
- anchorPath: [2, 0, 0],
- focusOffset: 7,
- focusPath: [2, 0, 0],
- });
- },
- );
-
- test.fixme(
- `Copy and paste between sections`,
- async ({isRichText, page, browserName}) => {
- await focusEditor(page);
- await page.keyboard.type('Hello world #foobar test #foobar2 when #not');
-
- await page.keyboard.press('Enter');
- await page.keyboard.type('Next #line of #text test #foo');
-
- if (isRichText) {
- await assertHTML(
- page,
- html`
-
- Hello world
-
- #foobar
-
- test
-
- #foobar2
-
- when
-
- #not
-
-
-
- Next
-
- #line
-
- of
-
- #text
-
- test
-
- #foo
-
-
- `,
- );
-
- await assertSelection(page, {
- anchorOffset: 4,
- anchorPath: [1, 5, 0],
- focusOffset: 4,
- focusPath: [1, 5, 0],
- });
- } else {
- await assertHTML(
- page,
- html`
-
- Hello world
-
- #foobar
-
- test
-
- #foobar2
-
- when
-
- #not
-
-
- Next
-
- #line
-
- of
-
- #text
-
- test
-
- #foo
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 4,
- anchorPath: [0, 12, 0],
- focusOffset: 4,
- focusPath: [0, 12, 0],
- });
- }
-
- // Select all the content
- await selectAll(page);
-
- if (isRichText) {
- if (browserName === 'firefox') {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [],
- focusOffset: 2,
- focusPath: [],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 4,
- focusPath: [1, 5, 0],
- });
- }
- } else {
- if (browserName === 'firefox') {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [],
- focusOffset: 1,
- focusPath: [],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 4,
- focusPath: [0, 12, 0],
- });
- }
- }
-
- // Copy all the text
- let clipboard = await copyToClipboard(page);
- await page.keyboard.press('Delete');
- // Paste the content
- await pasteFromClipboard(page, clipboard);
-
- if (isRichText) {
- await assertHTML(
- page,
- html`
-
- Hello world
-
- #foobar
-
- test
-
- #foobar2
-
- when
-
- #not
-
-
-
- Next
-
- #line
-
- of
-
- #text
-
- test
-
- #foo
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 4,
- anchorPath: [1, 5, 0],
- focusOffset: 4,
- focusPath: [1, 5, 0],
- });
- } else {
- await assertHTML(
- page,
- html`
-
- Hello world
-
- #foobar
-
- test
-
- #foobar2
-
- when
-
- #not
-
-
- Next
-
- #line
-
- of
-
- #text
-
- test
-
- #foo
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 4,
- anchorPath: [0, 12, 0],
- focusOffset: 4,
- focusPath: [0, 12, 0],
- });
- }
-
- await moveToPrevWord(page);
- await page.keyboard.down('Shift');
- await page.keyboard.press('ArrowUp');
- await moveToPrevWord(page);
- // Once more for linux on Chromium
- if (IS_LINUX && browserName === 'chromium') {
- await moveToPrevWord(page);
- }
- await page.keyboard.up('Shift');
-
- if (isRichText) {
- await assertSelection(page, {
- anchorOffset: 1,
- anchorPath: [1, 5, 0],
- focusOffset: 1,
- focusPath: [0, 2, 0],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 1,
- anchorPath: [0, 12, 0],
- focusOffset: 1,
- focusPath: [0, 2, 0],
- });
- }
-
- // Copy selected text
- clipboard = await copyToClipboard(page);
- await page.keyboard.press('Delete');
- // Paste the content
- await pasteFromClipboard(page, clipboard);
-
- if (isRichText) {
- await assertHTML(
- page,
- html`
-
- Hello world
-
- #foobar
-
- test
-
- #foobar2
-
- when
-
- #not
-
-
-
- Next
-
- #line
-
- of
-
- #text
-
- test
-
- #foo
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 1,
- anchorPath: [1, 5, 0],
- focusOffset: 1,
- focusPath: [1, 5, 0],
- });
- } else {
- await assertHTML(
- page,
- html`
-
- Hello world
-
- #foobar
-
- test
-
- #foobar2
-
- when
-
- #not
-
-
- Next
-
- #line
-
- of
-
- #text
-
- test
-
- #foo
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 1,
- anchorPath: [0, 12, 0],
- focusOffset: 1,
- focusPath: [0, 12, 0],
- });
- }
-
- // Select all the content
- await selectAll(page);
-
- if (isRichText) {
- if (browserName === 'firefox') {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [],
- focusOffset: 2,
- focusPath: [],
- });
- } else {
- if (browserName === 'firefox') {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 3,
- focusPath: [1, 5, 0],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 4,
- focusPath: [1, 5, 0],
- });
- }
- }
- } else {
- if (browserName === 'firefox') {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [],
- focusOffset: 1,
- focusPath: [],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 0, 0],
- focusOffset: 4,
- focusPath: [0, 12, 0],
- });
- }
- }
-
- await page.keyboard.press('Delete');
- await assertHTML(
- page,
- html`
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0],
- focusOffset: 0,
- focusPath: [0],
- });
- },
- );
-
- test.fixme(
- 'Copy and paste of partial list items into an empty editor',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
-
- // Add three list items
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
-
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
-
- // Add a paragraph
- await page.keyboard.type('Some text.');
-
- await assertHTML(
- page,
- '- one
- two
- three
Some text.
',
- );
- await assertSelection(page, {
- anchorOffset: 10,
- anchorPath: [1, 0, 0],
- focusOffset: 10,
- focusPath: [1, 0, 0],
- });
-
- await page.keyboard.down('Shift');
- await moveToLineBeginning(page);
- await moveLeft(page, 3);
- await page.keyboard.up('Shift');
-
- await assertSelection(page, {
- anchorOffset: 10,
- anchorPath: [1, 0, 0],
- focusOffset: 3,
- focusPath: [0, 2, 0, 0],
- });
-
- // Copy the partial list item and paragraph
- const clipboard = await copyToClipboard(page);
-
- // Select all and remove content
- await selectAll(page);
- await page.keyboard.press('Backspace');
- await page.keyboard.press('Backspace');
-
- await assertHTML(
- page,
- html`
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0],
- focusOffset: 0,
- focusPath: [0],
- });
-
- // Paste
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- ee
Some text.
',
- );
- await assertSelection(page, {
- anchorOffset: 10,
- anchorPath: [1, 0, 0],
- focusOffset: 10,
- focusPath: [1, 0, 0],
- });
- },
- );
-
- test.fixme(
- 'Copy and paste of partial list items into the list',
- async ({page, isPlainText, isCollab, browserName}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- // Add three list items
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
-
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
-
- // Add a paragraph
- await page.keyboard.type('Some text.');
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
- two
-
- -
- three
-
-
-
- Some text.
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 10,
- anchorPath: [1, 0, 0],
- focusOffset: 10,
- focusPath: [1, 0, 0],
- });
-
- await page.keyboard.down('Shift');
- await moveToLineBeginning(page);
- await moveLeft(page, 3);
- await page.keyboard.up('Shift');
-
- await assertSelection(page, {
- anchorOffset: 10,
- anchorPath: [1, 0, 0],
- focusOffset: 3,
- focusPath: [0, 2, 0, 0],
- });
-
- // Copy the partial list item and paragraph
- const clipboard = await copyToClipboard(page);
-
- // Select all and remove content
- await page.keyboard.press('ArrowUp');
- await page.keyboard.press('ArrowUp');
- if (!IS_WINDOWS && browserName === 'firefox') {
- await page.keyboard.press('ArrowUp');
- }
- await moveToLineEnd(page);
-
- await page.keyboard.down('Enter');
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
-
-
- -
- two
-
- -
- three
-
-
-
- Some text.
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 1],
- focusOffset: 0,
- focusPath: [0, 1],
- });
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
- ee
-
-
-
- Some text.
-
-
- -
- three
-
- -
- two
-
-
-
- Some text.
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 10,
- anchorPath: [1, 0, 0],
- focusOffset: 10,
- focusPath: [1, 0, 0],
- });
- },
- );
-
- test.fixme(
- 'Copy list items and paste back into list',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
- await page.keyboard.press('Enter');
- await page.keyboard.type('four');
- await page.keyboard.press('Enter');
- await page.keyboard.type('five');
-
- await page.keyboard.press('ArrowUp');
- await page.keyboard.press('ArrowUp');
-
- await moveToLineBeginning(page);
- await page.keyboard.down('Shift');
- await page.keyboard.press('ArrowDown');
- await moveToLineEnd(page);
- await page.keyboard.up('Shift');
-
- await assertHTML(
- page,
- '- one
- two
- three
- four
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 2, 0, 0],
- focusOffset: 4,
- focusPath: [0, 3, 0, 0],
- });
-
- const clipboard = await copyToClipboard(page);
-
- await page.keyboard.press('Backspace');
-
- await assertHTML(
- page,
- '- one
- two
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 2],
- focusOffset: 0,
- focusPath: [0, 2],
- });
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- one
- two
- three
- four
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 4,
- anchorPath: [0, 3, 0, 0],
- focusOffset: 4,
- focusPath: [0, 3, 0, 0],
- });
- },
- );
-
- test.fixme(
- 'Copy list items and paste into list',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
- await page.keyboard.press('Enter');
- await page.keyboard.type('four');
- await page.keyboard.press('Enter');
- await page.keyboard.type('five');
-
- await selectAll(page);
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
- two
-
- -
- three
-
- -
- four
-
- -
- five
-
-
- `,
- );
-
- const clipboard = await copyToClipboard(page);
-
- await page.keyboard.press('ArrowDown');
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
-
- await page.keyboard.type('12345');
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
- two
-
- -
- three
-
- -
- four
-
- -
- five
-
-
-
- 12345
-
- `,
- );
-
- await page.keyboard.press('ArrowLeft');
- await page.keyboard.press('ArrowLeft');
- await selectCharacters(page, 'left', 1);
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
- two
-
- -
- three
-
- -
- four
-
- -
- five
-
-
-
- 12one
-
-
- -
- two
-
- -
- three
-
- -
- four
-
- -
- five
-
- -
- 45
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'Copy and paste of list items and paste back into list on an existing item',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
- await page.keyboard.press('Enter');
- await page.keyboard.type('four');
- await page.keyboard.press('Enter');
- await page.keyboard.type('five');
-
- await page.keyboard.press('ArrowUp');
- await page.keyboard.press('ArrowUp');
-
- await moveToLineBeginning(page);
- await page.keyboard.down('Shift');
- await page.keyboard.press('ArrowDown');
- await moveToLineEnd(page);
- await page.keyboard.up('Shift');
-
- await assertHTML(
- page,
- '- one
- two
- three
- four
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0, 2, 0, 0],
- focusOffset: 4,
- focusPath: [0, 3, 0, 0],
- });
-
- const clipboard = await copyToClipboard(page);
-
- await page.keyboard.press('ArrowRight');
-
- await assertHTML(
- page,
- '- one
- two
- three
- four
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 4,
- anchorPath: [0, 3, 0, 0],
- focusOffset: 4,
- focusPath: [0, 3, 0, 0],
- });
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- one
- two
- three
- fourthree
- four
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 4,
- anchorPath: [0, 4, 0, 0],
- focusOffset: 4,
- focusPath: [0, 4, 0, 0],
- });
- },
- );
-
- test.fixme(
- 'Copy list of a different type and paste into list on an existing item - should merge the lists.',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Tab');
- await page.keyboard.press('Enter');
- await page.keyboard.type('a');
-
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
-
- await page.keyboard.type('1. four');
- await page.keyboard.press('Enter');
- await page.keyboard.type('five');
- await page.keyboard.press('Tab');
-
- await page.keyboard.press('ArrowUp');
-
- await moveToLineBeginning(page);
- await page.keyboard.down('Shift');
- await page.keyboard.press('ArrowDown');
- await page.keyboard.press('ArrowDown');
- await page.keyboard.up('Shift');
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
-
- -
- two
-
- -
- a
-
-
-
-
-
-
- -
- four
-
- -
-
- -
- five
-
-
-
-
- `,
- );
-
- const clipboard = await copyToClipboard(page);
-
- await page.keyboard.press('ArrowLeft');
- await page.keyboard.press('ArrowLeft');
- await page.keyboard.press('ArrowLeft');
- await page.keyboard.press('Backspace');
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
-
- -
- two
-
- -
- four
-
- -
-
- -
- five
-
-
-
-
-
-
-
-
- -
- four
-
- -
-
- -
- five
-
-
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'Copy and paste two paragraphs into list on an existing item',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
-
- await page.keyboard.type('Hello');
- await page.keyboard.press('Enter');
- await page.keyboard.type('World');
-
- await selectAll(page);
-
- const clipboard = await copyToClipboard(page);
-
- await page.keyboard.press('Backspace');
-
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
- await page.keyboard.press('Enter');
- await page.keyboard.type('four');
- await page.keyboard.press('Enter');
- await page.keyboard.type('five');
-
- await page.keyboard.press('ArrowUp');
- await page.keyboard.press('ArrowUp');
-
- await moveToLineBeginning(page);
- await page.keyboard.press('ArrowDown');
- await moveToLineEnd(page);
- await moveLeft(page, 2);
-
- await assertHTML(
- page,
- '- one
- two
- three
- four
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 3, 0, 0],
- focusOffset: 2,
- focusPath: [0, 3, 0, 0],
- });
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- one
- two
- three
- foHello
Worldur
- five
',
- );
- await assertSelection(page, {
- anchorOffset: 5,
- anchorPath: [1, 0, 0],
- focusOffset: 5,
- focusPath: [1, 0, 0],
- });
- },
- );
-
- test.fixme(
- 'Copy and paste two paragraphs at the end of a list',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- await page.keyboard.type('Hello');
- await page.keyboard.press('Enter');
- await page.keyboard.type('World');
-
- await selectAll(page);
-
- const clipboard = await copyToClipboard(page);
-
- await page.keyboard.press('Backspace');
-
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
- await page.keyboard.press('Enter');
- await page.keyboard.type('four');
- await page.keyboard.press('Enter');
- await page.keyboard.type('five');
- await page.keyboard.press('Enter');
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- one
- two
- three
- four
- five
- Hello
World
',
- );
- await assertSelection(page, {
- anchorOffset: 5,
- anchorPath: [1, 0, 0],
- focusOffset: 5,
- focusPath: [1, 0, 0],
- });
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- one
- two
- three
- four
- five
- Hello
WorldHello
World
',
- );
- await assertSelection(page, {
- anchorOffset: 5,
- anchorPath: [2, 0, 0],
- focusOffset: 5,
- focusPath: [2, 0, 0],
- });
- },
- );
-
- test.fixme(
- 'Copy and paste an inline element into a leaf node',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
-
- // Root
- // |- Paragraph
- // |- Link
- // |- Text "Hello"
- // |- Text "World"
- await page.keyboard.type('Hello');
- await selectAll(page);
- await click(page, '.link');
- await page.keyboard.press('ArrowRight');
- await page.keyboard.press('Space');
- await page.keyboard.type('World');
-
- await selectAll(page);
-
- const clipboard = await copyToClipboard(page);
-
- await page.keyboard.press('ArrowRight');
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
-
- Hello
-
- World
-
- Hello
-
- World
-
- `,
- );
- },
- );
-
- test('HTML Copy + paste a plain DOM text node', async ({
- page,
- isPlainText,
- }) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {'text/html': 'Hello!'};
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- Hello!
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 0, 0],
- focusOffset: 6,
- focusPath: [0, 0, 0],
- });
- });
-
- test('HTML Copy + paste a paragraph element', async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {'text/html': 'Hello!
'};
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- Hello!
-
-
- `,
- );
-
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [1],
- focusOffset: 0,
- focusPath: [1],
- });
- });
-
- test('HTML Copy + paste an anchor element', async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': 'Facebook!',
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
-
- Facebook!
-
-
- `,
- );
-
- await assertSelection(page, {
- anchorOffset: 9,
- anchorPath: [0, 0, 0, 0],
- focusOffset: 9,
- focusPath: [0, 0, 0, 0],
- });
-
- await selectAll(page);
-
- await click(page, '.link');
-
- await assertHTML(
- page,
- html`
-
- Facebook!
-
- `,
- );
-
- await click(page, '.link');
- //await click(page, '.link-edit'); // link editor opens in edit mode by default so we don't need to click edit button
- await focus(page, '.link-input');
- await page.keyboard.type('facebook.com');
- await page.keyboard.press('Enter');
-
- await assertHTML(
- page,
- html`
-
-
- Facebook!
-
-
- `,
- );
- });
-
- test('HTML Copy + paste a list element', async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {'text/html': '- Hello
- world!
'};
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- Hello
- world!
',
- );
-
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 1, 0, 0],
- focusOffset: 6,
- focusPath: [0, 1, 0, 0],
- });
-
- await selectFromAlignDropdown(page, '.indent');
-
- await assertHTML(
- page,
- '- Hello
- world!
',
- );
-
- await selectFromAlignDropdown(page, '.outdent');
-
- await assertHTML(
- page,
- '- Hello
- world!
',
- );
- });
-
- test('HTML Copy + paste a Lexical nested list', async ({
- page,
- isPlainText,
- }) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html':
- '- Hello
- awesome
- world!
',
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- '- Hello
- awesome
- world!
',
- );
- });
-
- test.fixme(
- 'HTML Copy + paste (Nested List - directly nested ul)',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': '- Hello
- world!
',
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- -
-
- -
- Hello
-
-
-
- -
- world!
-
-
- `,
- );
-
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 1, 0, 0],
- focusOffset: 6,
- focusPath: [0, 1, 0, 0],
- });
-
- await selectFromAlignDropdown(page, '.indent');
-
- await assertHTML(
- page,
- html`
-
- -
-
- -
- Hello
-
- -
- world!
-
-
-
-
- `,
- );
-
- await page.keyboard.press('ArrowUp');
-
- await selectFromAlignDropdown(page, '.outdent');
-
- await assertHTML(
- page,
- html`
-
- -
- Hello
-
- -
-
- -
- world!
-
-
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'HTML Copy + paste (Nested List - li with non-list content plus ul child)',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': '- Hello
- world!
',
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- -
- Hello
-
- -
-
- -
- world!
-
-
-
-
- `,
- );
-
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 1, 0, 0, 0, 0],
- focusOffset: 6,
- focusPath: [0, 1, 0, 0, 0, 0],
- });
-
- await selectFromAlignDropdown(page, '.outdent');
-
- await assertHTML(
- page,
- html`
-
- -
- Hello
-
- -
- world!
-
-
- `,
- );
-
- await page.keyboard.press('ArrowUp');
-
- await selectFromAlignDropdown(page, '.indent');
-
- await assertHTML(
- page,
- html`
-
- -
-
- -
- Hello
-
-
-
- -
- world!
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'HTML Copy + paste (Table - Google Docs)',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText);
-
- test.fixme(
- isCollab,
- 'Table selection styles are not properly synced to the right hand frame',
- );
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': `
`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
-
-
-
- a
-
-
-
-
- b
-
-
- b
-
-
-
-
- c
-
-
-
-
-
-
- d
-
-
-
-
- e
-
-
-
-
- f
-
-
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'HTML Copy + paste (Table - Quip)',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': `a b
b c d e f
`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
-
-
-
- a
-
-
-
-
- b
-
-
- b
-
-
-
-
- c
-
-
-
-
-
-
- d
-
-
-
-
- e
-
-
-
-
- f
-
-
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'HTML Copy + paste (Table - Google Sheets)',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': `
`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
-
-
-
- a
-
-
-
-
- b
-
-
- b
-
-
-
-
- c
-
-
-
-
-
-
- d
-
-
-
-
- e
-
-
-
-
- f
-
-
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'Merge Grids on Copy + paste',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
- await insertTable(page, 4, 4);
-
- const clipboard = {
- 'text/html': `a
b
c
d
`,
- };
-
- await selectCellsFromTableCords(
- page,
- {x: 0, y: 0},
- {x: 3, y: 3},
- true,
- false,
- );
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
-
-
-
-
- a
-
-
-
-
- b
-
-
-
-
-
-
-
-
-
-
-
-
- c
-
-
-
-
- d
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `,
- html`
-
-
-
-
-
- a
-
-
-
-
- b
-
-
-
-
-
-
-
-
-
-
-
-
- c
-
-
-
-
- d
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `,
- );
- },
- );
-
- test('HTML Copy + paste multi line html with extra newlines', async ({
- page,
- isPlainText,
- isCollab,
- }) => {
- test.skip(isPlainText || isCollab);
-
- await focusEditor(page);
- await pasteFromClipboard(page, {
- 'text/html':
- 'Hello\n
\n\n\n\nWorld\n\n
\n\nHello\n\n World \n\n!\n\n
Hello World !
',
- });
-
- const paragraphs = page.locator('div[contenteditable="true"] > p');
- await expect(paragraphs).toHaveCount(4);
-
- // Explicitly checking inner text, since regular assertHTML will prettify it and strip all
- // extra newlines, which makes this test less acurate
- await expect(paragraphs.nth(0)).toHaveText('Hello', {useInnerText: true});
- await expect(paragraphs.nth(1)).toHaveText('World', {useInnerText: true});
- await expect(paragraphs.nth(2)).toHaveText('Hello World !', {
- useInnerText: true,
- });
- await expect(paragraphs.nth(3)).toHaveText('Hello World !', {
- useInnerText: true,
- });
- });
-
- test.fixme(
- 'HTML Copy + paste in front of or after a link',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
- await pasteFromClipboard(page, {
- 'text/html': `textlinktext`,
- });
- await moveToEditorBeginning(page);
- await pasteFromClipboard(page, {
- 'text/html': 'before',
- });
- await moveToEditorEnd(page);
- await pasteFromClipboard(page, {
- 'text/html': 'after',
- });
- await assertHTML(
- page,
- html`
-
- beforetext
-
- link
-
- textafter
-
- `,
- );
- },
- );
-
- test.fixme(
- 'HTML Copy + paste link by selecting its (partial) content',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
- await pasteFromClipboard(page, {
- 'text/html': `textlinktext`,
- });
- await moveLeft(page, 5);
- await page.keyboard.down('Shift');
- await moveLeft(page, 2);
- await page.keyboard.up('Shift');
- const clipboard = await copyToClipboard(page);
- await moveToEditorEnd(page);
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- text
-
- link
-
- text
-
- in
-
-
- `,
- );
- },
- );
-
- test.fixme(
- 'Copy + paste multi-line plain text into rich text produces separate paragraphs',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
- await focusEditor(page);
- await page.keyboard.type('# Hello ');
- await pasteFromClipboard(page, {
- 'text/plain': 'world\nAnd text below',
- });
- await assertHTML(
- page,
- html`
-
- Hello world
-
-
- And text below
-
- `,
- );
- },
- );
-
- test('HTML Copy + paste html with BIU formatting', async ({
- page,
- isPlainText,
- }) => {
- test.skip(isPlainText);
- await focusEditor(page);
- const clipboardData = {
- 'text/html': `Bold
Italic
underline
Bold Italic Underline
`,
- };
- await pasteFromClipboard(page, clipboardData);
- await assertHTML(
- page,
- html`
-
-
- Bold
-
-
-
-
- Italic
-
-
-
-
- underline
-
-
-
-
- Bold Italic Underline
-
-
-
-
-
- `,
- );
- });
-
- test('HTML Copy + paste text with subscript and superscript', async ({
- page,
- isPlainText,
- }) => {
- test.skip(isPlainText);
- await focusEditor(page);
- const clipboardData = {
- 'text/html':
- 'subscript and superscript',
- };
- await pasteFromClipboard(page, clipboardData);
- await assertHTML(
- page,
- html`
-
-
- subscript
-
- and
-
-
- superscript
-
-
-
- `,
- );
- });
-
- test('HTML Copy + paste a Title from Google Docs', async ({
- page,
- isPlainText,
- }) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': `My document`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- My document
-
- `,
- );
-
- await clearEditor(page);
- await focusEditor(page);
-
- // These can sometimes be put onto the clipboard wrapped in a paragraph element
- clipboard['text/html'] =
- `My document
`;
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- My document
-
- `,
- );
- });
-
- test('HTML Copy + paste a checklist', async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': `- Hello
- world
`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- -
- Hello
-
- -
- world
-
-
- `,
- );
-
- await clearEditor(page);
- await focusEditor(page);
-
- // Ensure we preserve checked status.
- clipboard['text/html'] =
- `- Hello
- world
`;
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- -
- Hello
-
- -
- world
-
-
- `,
- );
- });
-
- test.fixme(
- 'HTML Copy + paste a code block with BR',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'text/html': `Code block
function foo() {
return 'Hey there';
}
--end--
`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- Code block
-
-
-
- function
-
-
-
- foo
-
-
- (
-
-
- )
-
-
-
- {
-
-
-
-
- return
-
-
-
- 'Hey there'
-
-
- ;
-
-
-
- }
-
-
-
- --end--
-
- `,
- );
- },
- );
-
- test.fixme(
- 'HTML Copy + paste empty link #3193',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- // eslint-disable-next-line no-irregular-whitespace
- 'text/html': `Line 0- Â Line 1Â Some link.
- Â Line 2.
`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- Line 0
-
-
- -
- â..ï¸. Line 1Â
-
- Some link
-
- .
-
- -
- â..ï¸. Line 2.
-
-
- `,
- );
- },
- );
-
- test.fixme('HTML Paste a link into text', async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- await page.keyboard.type('A Lexical in the wild');
- await page.pause();
- await moveToLineBeginning(page);
- await moveToNextWord(page);
- await extendToNextWord(page);
-
- const clipboard = {
- text: `https://lexical.dev`,
- };
-
- await pasteFromClipboard(page, clipboard);
-
- await assertHTML(
- page,
- html`
-
- A
-
- Lexical
-
- in the wild
-
- `,
- );
- });
-
- test.fixme('HTML Copy + paste an image', async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'playwright/base64': [LEXICAL_IMAGE_BASE64, 'image/png'],
- };
-
- await page.keyboard.type('An image');
- await moveLeft(page, 'image'.length);
- await pasteFromClipboard(page, clipboard);
- await page.keyboard.type(' inline ');
- await sleepInsertImage();
-
- await assertHTML(
- page,
- html`
-
- An
-
-
-
-
-
- inline image
-
- `,
- );
- });
-
- test.fixme(
- 'HTML Copy + paste + undo multiple image',
- async ({page, isPlainText}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- const clipboard = {
- 'playwright/base64_1': [LEXICAL_IMAGE_BASE64, 'image/png'],
- 'playwright/base64_2': [LEXICAL_IMAGE_BASE64, 'image/png'],
- };
-
- await pasteFromClipboard(page, clipboard);
- await sleepInsertImage(2);
-
- await assertHTML(
- page,
- html`
-
-
-
-
-
-
-
-
-
-
-
-
-
- `,
- );
-
- await undo(page);
- await assertHTML(
- page,
- html`
-
- `,
- );
- },
- );
-
- test.fixme(
- 'HTML Copy + paste a paragraph element between horizontal rules',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText);
-
- await focusEditor(page);
-
- let clipboard = {'text/html': '
'};
-
- await pasteFromClipboard(page, clipboard);
- // Collab doesn't process the cursor correctly
- if (!isCollab) {
- await assertHTML(
- page,
- html`
-
-
-
-
- `,
- );
- }
- await click(page, 'hr:first-of-type');
-
- // sets focus between HRs
- await page.keyboard.press('ArrowRight');
-
- clipboard = {'text/html': 'Text between HRs
'};
-
- await pasteFromClipboard(page, clipboard);
- await assertHTML(
- page,
- html`
-
-
-
- Text between HRs
-
-
- `,
- );
- await assertSelection(page, {
- anchorOffset: 16,
- anchorPath: [2, 0, 0],
- focusOffset: 16,
- focusPath: [2, 0, 0],
- });
- },
- );
-
- test.fixme(
- 'Paste top level element in the middle of paragraph',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText || isCollab);
- await focusEditor(page);
- await page.keyboard.type('Hello world');
- await moveToPrevWord(page);
- await pasteFromClipboard(page, {
- 'text/html': `
`,
- });
-
- await assertHTML(
- page,
- html`
-
- Hello
-
-
-
- world
-
- `,
- );
- },
- );
-
- test.fixme(
- 'Paste top level element in the middle of list',
- async ({page, isPlainText, isCollab}) => {
- test.skip(isPlainText || isCollab);
- await focusEditor(page);
- // Add three list items
- await page.keyboard.type('- one');
- await page.keyboard.press('Enter');
- await page.keyboard.type('two');
- await page.keyboard.press('Enter');
- await page.keyboard.type('three');
- await page.keyboard.press('Enter');
- await page.keyboard.type('four');
-
- await page.keyboard.press('Enter');
- await page.keyboard.press('Enter');
- await page.keyboard.press('ArrowUp');
- await moveLeft(page, 4);
- await page.keyboard.press('ArrowUp');
- await page.keyboard.press('ArrowUp');
- await pasteFromClipboard(page, {
- 'text/html': `
`,
- });
-
- await assertHTML(
- page,
- html`
-
- -
- one
-
- -
- two
-
-
-
-
-
- -
- three
-
- -
- four
-
-
-
- `,
- );
- },
- );
-});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/html/HTMLCopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/HTMLCopyAndPaste.spec.mjs
new file mode 100644
index 00000000..3b832edd
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/HTMLCopyAndPaste.spec.mjs
@@ -0,0 +1,278 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+import {expect} from '@playwright/test';
+
+import {moveToPrevWord} from '../../../keyboardShortcuts/index.mjs';
+import {
+ assertHTML,
+ assertSelection,
+ click,
+ focusEditor,
+ html,
+ initialize,
+ pasteFromClipboard,
+ test,
+} from '../../../utils/index.mjs';
+
+test.describe('HTML CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
+ test('Copy + paste a plain DOM text node', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {'text/html': 'Hello!'};
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ Hello!
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 0, 0],
+ focusOffset: 6,
+ focusPath: [0, 0, 0],
+ });
+ });
+
+ test('Copy + paste a paragraph element', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {'text/html': 'Hello!
'};
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ Hello!
+
+
+ `,
+ );
+
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [1],
+ focusOffset: 0,
+ focusPath: [1],
+ });
+ });
+
+ test('Copy + paste multi line html with extra newlines', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.skip(isPlainText || isCollab);
+
+ await focusEditor(page);
+ await pasteFromClipboard(page, {
+ 'text/html':
+ 'Hello\n
\n\n\n\nWorld\n\n
\n\nHello\n\n World \n\n!\n\n
Hello World !
',
+ });
+
+ const paragraphs = page.locator('div[contenteditable="true"] > p');
+ await expect(paragraphs).toHaveCount(4);
+
+ // Explicitly checking inner text, since regular assertHTML will prettify it and strip all
+ // extra newlines, which makes this test less acurate
+ await expect(paragraphs.nth(0)).toHaveText('Hello', {useInnerText: true});
+ await expect(paragraphs.nth(1)).toHaveText('World', {useInnerText: true});
+ await expect(paragraphs.nth(2)).toHaveText('Hello World !', {
+ useInnerText: true,
+ });
+ await expect(paragraphs.nth(3)).toHaveText('Hello World !', {
+ useInnerText: true,
+ });
+ });
+
+ test('Copy + paste a code block with BR', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': `Code block
function foo() {
return 'Hey there';
}
--end--
`,
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ Code block
+
+
+
+ function
+
+
+
+ foo
+
+
+ (
+
+
+ )
+
+
+
+ {
+
+
+
+ return
+
+
+
+ 'Hey there'
+
+
+ ;
+
+
+
+ }
+
+
+
+ --end--
+
+ `,
+ );
+ });
+
+ test('Copy + paste a paragraph element between horizontal rules', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ let clipboard = {'text/html': '
'};
+
+ await pasteFromClipboard(page, clipboard);
+ // Collab doesn't process the cursor correctly
+ if (!isCollab) {
+ await assertHTML(
+ page,
+ html`
+
+
+
+ `,
+ );
+ }
+ await click(page, 'hr:first-of-type');
+
+ // sets focus between HRs
+ await page.keyboard.press('ArrowRight');
+
+ clipboard = {'text/html': 'Text between HRs
'};
+
+ await pasteFromClipboard(page, clipboard);
+ await assertHTML(
+ page,
+ html`
+
+
+ Text between HRs
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 16,
+ anchorPath: [1, 0, 0],
+ focusOffset: 16,
+ focusPath: [1, 0, 0],
+ });
+ });
+
+ test('Paste top level element in the middle of paragraph', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.skip(isPlainText || isCollab);
+ await focusEditor(page);
+ await page.keyboard.type('Hello world');
+ await moveToPrevWord(page);
+ await pasteFromClipboard(page, {
+ 'text/html': `
`,
+ });
+
+ await assertHTML(
+ page,
+ html`
+
+ Hello
+
+
+
+ world
+
+ `,
+ );
+ });
+});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/html/ImageHTMLCopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/ImageHTMLCopyAndPaste.spec.mjs
new file mode 100644
index 00000000..dd120ef1
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/ImageHTMLCopyAndPaste.spec.mjs
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {moveLeft, undo} from '../../../keyboardShortcuts/index.mjs';
+import {
+ assertHTML,
+ focusEditor,
+ html,
+ initialize,
+ LEXICAL_IMAGE_BASE64,
+ pasteFromClipboard,
+ sleepInsertImage,
+ test,
+} from '../../../utils/index.mjs';
+
+test.describe('HTML Image CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
+ test('Copy + paste an image', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'playwright/base64': [LEXICAL_IMAGE_BASE64, 'image/png'],
+ };
+
+ await page.keyboard.type('An image');
+ await moveLeft(page, 'image'.length);
+ await pasteFromClipboard(page, clipboard);
+ await sleepInsertImage();
+ await page.keyboard.type(' inline ');
+
+ await assertHTML(
+ page,
+ html`
+
+ An
+
+
+
+
+
+ inline image
+
+ `,
+ );
+ });
+
+ test('Copy + paste + undo multiple image', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'playwright/base64_1': [LEXICAL_IMAGE_BASE64, 'image/png'],
+ 'playwright/base64_2': [LEXICAL_IMAGE_BASE64, 'image/png'],
+ };
+
+ await pasteFromClipboard(page, clipboard);
+ await sleepInsertImage(2);
+
+ await assertHTML(
+ page,
+ html`
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ );
+
+ await undo(page);
+ await assertHTML(
+ page,
+ html`
+
+ `,
+ );
+ });
+});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/html/LinksHTMLCopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/LinksHTMLCopyAndPaste.spec.mjs
new file mode 100644
index 00000000..2489924c
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/LinksHTMLCopyAndPaste.spec.mjs
@@ -0,0 +1,440 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {
+ extendToNextWord,
+ moveLeft,
+ moveRight,
+ moveToEditorBeginning,
+ moveToEditorEnd,
+ moveToLineBeginning,
+ moveToNextWord,
+ selectAll,
+} from '../../../keyboardShortcuts/index.mjs';
+import {
+ assertHTML,
+ assertSelection,
+ click,
+ copyToClipboard,
+ focusEditor,
+ html,
+ initialize,
+ pasteFromClipboard,
+ test,
+} from '../../../utils/index.mjs';
+
+test.describe('HTML Links CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
+ test('Copy + paste an anchor element', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': 'Facebook!',
+ };
+
+ await pasteFromClipboard(page, clipboard);
+ await assertHTML(
+ page,
+ html`
+
+
+ Facebook!
+
+
+ `,
+ );
+
+ await assertSelection(page, {
+ anchorOffset: 9,
+ anchorPath: [0, 0, 0, 0],
+ focusOffset: 9,
+ focusPath: [0, 0, 0, 0],
+ });
+
+ await selectAll(page);
+
+ // unlink
+ await click(page, '.link');
+
+ await assertHTML(
+ page,
+ html`
+
+ Facebook!
+
+ `,
+ );
+
+ await click(page, '.link');
+ await page.keyboard.type('facebook.com');
+ await click(page, '.link-confirm');
+
+ await assertHTML(
+ page,
+ html`
+
+
+ Facebook!
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste in front of or after a link', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+ await pasteFromClipboard(page, {
+ 'text/html': `textlinktext`,
+ });
+ await moveToEditorBeginning(page);
+ await pasteFromClipboard(page, {
+ 'text/html': 'before',
+ });
+ await moveToEditorEnd(page);
+ await pasteFromClipboard(page, {
+ 'text/html': 'after',
+ });
+ await assertHTML(
+ page,
+ html`
+
+ beforetext
+
+ link
+
+ textafter
+
+ `,
+ );
+ });
+
+ test('Copy + paste link by selecting its (partial) content', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+ await pasteFromClipboard(page, {
+ 'text/html': `textlinktext`,
+ });
+ await moveLeft(page, 5);
+ await page.keyboard.down('Shift');
+ await moveLeft(page, 2);
+ await page.keyboard.up('Shift');
+ const clipboard = await copyToClipboard(page);
+ await moveToEditorEnd(page);
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ text
+
+ link
+
+ text
+
+ in
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste empty link #3193', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ // eslint-disable-next-line no-irregular-whitespace
+ 'text/html': `Line 0- Â Line 1Â Some link.
- Â Line 2.
`,
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ Line 0
+
+
+ -
+ â..ï¸. Line 1Â
+
+ Some link
+
+ .
+
+ -
+ â..ï¸. Line 2.
+
+
+ `,
+ );
+ });
+
+ test('Paste a link into text', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ await page.keyboard.type('A Lexical in the wild');
+ await page.pause();
+ await moveToLineBeginning(page);
+ await moveToNextWord(page);
+ await extendToNextWord(page);
+
+ const clipboard = {
+ text: `https://lexical.dev`,
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ A
+
+ Lexical
+
+ in the wild
+
+ `,
+ );
+ });
+
+ test('Paste text into a link', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+
+ await page.keyboard.type('Link text');
+ await selectAll(page);
+ await click(page, '.link');
+ await click(page, '.link-confirm');
+ await moveRight(page, 1);
+ await moveLeft(page, 4);
+
+ await pasteFromClipboard(page, {
+ 'text/html': 'normal text',
+ });
+
+ await assertHTML(
+ page,
+ html`
+
+
+ Link
+
+ normal text
+
+ text
+
+
+ `,
+ );
+ });
+
+ test('Paste formatted text into a link', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+
+ await page.keyboard.type('Link text');
+ await selectAll(page);
+ await click(page, '.link');
+ await click(page, '.link-confirm');
+ await moveRight(page, 1);
+ await moveLeft(page, 4);
+
+ await pasteFromClipboard(page, {
+ 'text/html': 'bold text',
+ });
+
+ await assertHTML(
+ page,
+ html`
+
+
+ Link
+
+
+ bold
+
+ text
+
+ text
+
+
+ `,
+ );
+ });
+
+ test('Paste a link into a link', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+
+ await page.keyboard.type('Link text');
+ await selectAll(page);
+ await click(page, '.link');
+ await click(page, '.link-confirm');
+ await moveRight(page, 1);
+ await moveLeft(page, 4);
+
+ await pasteFromClipboard(page, {
+ 'text/html': 'text with link',
+ });
+
+ await assertHTML(
+ page,
+ html`
+
+
+ Link
+
+ text with
+
+ link
+
+
+ text
+
+
+ `,
+ );
+ });
+
+ test('Paste multiple blocks into a link', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+
+ await page.keyboard.type('Link text');
+ await selectAll(page);
+ await click(page, '.link');
+ await click(page, '.link-confirm');
+ await moveRight(page, 1);
+ await moveLeft(page, 4);
+
+ await pasteFromClipboard(page, {
+ 'text/html': 'para 1
para 2
',
+ });
+
+ await assertHTML(
+ page,
+ html`
+
+
+ Link
+
+ para 1
+
+
+ para 2
+
+ text
+
+
+ `,
+ );
+ });
+});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/html/ListsHTMLCopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/ListsHTMLCopyAndPaste.spec.mjs
new file mode 100644
index 00000000..33bf100a
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/ListsHTMLCopyAndPaste.spec.mjs
@@ -0,0 +1,419 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {
+ assertHTML,
+ assertSelection,
+ clearEditor,
+ focusEditor,
+ html,
+ initialize,
+ pasteFromClipboard,
+ selectFromAlignDropdown,
+ test,
+} from '../../../utils/index.mjs';
+
+test.describe('HTML Lists CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
+ test('Copy + paste a list element', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {'text/html': '- Hello
- world!
'};
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- Hello
- world!
',
+ );
+
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 1, 0, 0],
+ focusOffset: 6,
+ focusPath: [0, 1, 0, 0],
+ });
+
+ await selectFromAlignDropdown(page, '.indent');
+
+ await assertHTML(
+ page,
+ '- Hello
- world!
',
+ );
+
+ await selectFromAlignDropdown(page, '.outdent');
+
+ await assertHTML(
+ page,
+ '- Hello
- world!
',
+ );
+ });
+
+ test('Copy + paste a Lexical nested list', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html':
+ '- Hello
- awesome
- world!
',
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- Hello
- awesome
- world!
',
+ );
+ });
+
+ test('Copy + paste (Nested List - directly nested ul)', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': '- Hello
- world!
',
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+
+ -
+ Hello
+
+
+
+ -
+ world!
+
+
+ `,
+ );
+
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 1, 0, 0],
+ focusOffset: 6,
+ focusPath: [0, 1, 0, 0],
+ });
+
+ await selectFromAlignDropdown(page, '.indent');
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+
+ -
+ Hello
+
+ -
+ world!
+
+
+
+
+ `,
+ );
+
+ await page.keyboard.press('ArrowUp');
+
+ await selectFromAlignDropdown(page, '.outdent');
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ Hello
+
+ -
+
+ -
+ world!
+
+
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste (Nested List - li with non-list content plus ul child)', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': '- Hello
- world!
',
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ Hello
+
+ -
+
+ -
+ world!
+
+
+
+
+ `,
+ );
+
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 1, 0, 0, 0, 0],
+ focusOffset: 6,
+ focusPath: [0, 1, 0, 0, 0, 0],
+ });
+
+ await selectFromAlignDropdown(page, '.outdent');
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ Hello
+
+ -
+ world!
+
+
+ `,
+ );
+
+ await page.keyboard.press('ArrowUp');
+
+ await selectFromAlignDropdown(page, '.indent');
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+
+ -
+ Hello
+
+
+
+ -
+ world!
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste a checklist', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': `- Hello
- world
`,
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ Hello
+
+ -
+ world
+
+
+ `,
+ );
+
+ await clearEditor(page);
+ await focusEditor(page);
+
+ // Ensure we preserve checked status.
+ clipboard[
+ 'text/html'
+ ] = `- Hello
- world
`;
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ Hello
+
+ -
+ world
+
+
+ `,
+ );
+ });
+
+ test('Paste top level element in the middle of list', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.skip(isPlainText || isCollab);
+ await focusEditor(page);
+ // Add three list items
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('four');
+
+ await page.keyboard.press('Enter');
+ await page.keyboard.press('Enter');
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+ await pasteFromClipboard(page, {
+ 'text/html': `
`,
+ });
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ one
+
+
+
+
+ -
+ two
+
+ -
+ three
+
+ -
+ four
+
+
+
+ `,
+ );
+ });
+});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/html/TablesHTMLCopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/TablesHTMLCopyAndPaste.spec.mjs
new file mode 100644
index 00000000..6ef1a126
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/TablesHTMLCopyAndPaste.spec.mjs
@@ -0,0 +1,470 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+import {
+ assertHTML,
+ focusEditor,
+ html,
+ initialize,
+ insertTable,
+ pasteFromClipboard,
+ selectCellsFromTableCords,
+ test,
+} from '../../../utils/index.mjs';
+
+test.describe('HTML Tables CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
+ test('Copy + paste (Table - Google Docs)', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.skip(isPlainText);
+
+ test.fixme(
+ isCollab,
+ 'Table selection styles are not properly synced to the right hand frame',
+ );
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': `
`,
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+
+
+
+ a
+
+
+
+
+ b
+
+
+ b
+
+
+
+
+ c
+
+
+
+
+
+
+ d
+
+
+
+
+ e
+
+
+
+
+ f
+
+
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste (Table - Quip)', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': `a b
b c d e f
`,
+ };
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+
+
+
+ a
+
+
+
+
+ b
+
+
+ b
+
+
+
+
+ c
+
+
+
+
+
+
+ d
+
+
+
+
+ e
+
+
+
+
+ f
+
+
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste (Table - Google Sheets)', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': `
`,
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+
+
+
+ a
+
+
+
+
+ b
+
+
+ b
+
+
+
+
+ c
+
+
+
+
+
+
+ d
+
+
+
+
+ e
+
+
+
+
+ f
+
+
+
+
+ `,
+ );
+ });
+
+ test.fixme(
+ 'Copy + paste - Merge Grids',
+ async ({page, isPlainText, isCollab}) => {
+ test.skip(isPlainText);
+ test.fixme(
+ isCollab,
+ 'Table selection styles are not properly selected/deselected',
+ );
+
+ await focusEditor(page);
+ await insertTable(page, 4, 4);
+
+ const clipboard = {
+ 'text/html': `a
b
c
d
`,
+ };
+
+ await selectCellsFromTableCords(
+ page,
+ {x: 0, y: 0},
+ {x: 3, y: 3},
+ true,
+ false,
+ );
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+
+
+
+
+ a
+
+
+
+
+ b
+
+
+
+
+
+
+
+
+
+
+
+
+ c
+
+
+
+
+ d
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ html`
+
+
+
+
+
+ a
+
+
+
+
+ b
+
+
+
+
+
+
+
+
+
+
+
+
+ c
+
+
+
+
+ d
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ );
+ },
+ );
+});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/html/TextFormatHTMLCopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/TextFormatHTMLCopyAndPaste.spec.mjs
new file mode 100644
index 00000000..a53b7144
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/html/TextFormatHTMLCopyAndPaste.spec.mjs
@@ -0,0 +1,149 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+import {
+ assertHTML,
+ clearEditor,
+ focusEditor,
+ html,
+ initialize,
+ pasteFromClipboard,
+ test,
+} from '../../../utils/index.mjs';
+
+test.describe('HTML CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
+ test('Copy + paste html with BIU formatting', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+ const clipboardData = {
+ 'text/html': `Bold
Italic
underline
Bold Italic Underline
`,
+ };
+ await pasteFromClipboard(page, clipboardData);
+ await assertHTML(
+ page,
+ html`
+
+
+ Bold
+
+
+
+
+ Italic
+
+
+
+
+ underline
+
+
+
+
+ Bold Italic Underline
+
+
+
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste text with subscript and superscript', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+ const clipboardData = {
+ 'text/html':
+ 'subscript and superscript',
+ };
+ await pasteFromClipboard(page, clipboardData);
+ await assertHTML(
+ page,
+ html`
+
+
+ subscript
+
+ and
+
+
+ superscript
+
+
+
+ `,
+ );
+ });
+
+ test('Copy + paste a Title from Google Docs', async ({page, isPlainText}) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ const clipboard = {
+ 'text/html': `My document`,
+ };
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ My document
+
+ `,
+ );
+
+ await clearEditor(page);
+ await focusEditor(page);
+
+ // These can sometimes be put onto the clipboard wrapped in a paragraph element
+ clipboard[
+ 'text/html'
+ ] = `My document
`;
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ My document
+
+ `,
+ );
+ });
+});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/lexical/CopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/lexical/CopyAndPaste.spec.mjs
new file mode 100644
index 00000000..bbda6085
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/lexical/CopyAndPaste.spec.mjs
@@ -0,0 +1,913 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+import {
+ moveToEditorBeginning,
+ moveToEditorEnd,
+ moveToLineEnd,
+ moveToPrevWord,
+ selectAll,
+} from '../../../keyboardShortcuts/index.mjs';
+import {
+ assertHTML,
+ assertSelection,
+ click,
+ copyToClipboard,
+ focusEditor,
+ html,
+ initialize,
+ insertYouTubeEmbed,
+ IS_LINUX,
+ pasteFromClipboard,
+ test,
+ YOUTUBE_SAMPLE_URL,
+} from '../../../utils/index.mjs';
+
+test.describe('CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+ test('Basic copy + paste', async ({isRichText, page, browserName}) => {
+ await focusEditor(page);
+
+ // Add paragraph
+ await page.keyboard.type('Copy + pasting?');
+ await page.keyboard.press('Enter');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('Sounds good!');
+ if (isRichText) {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+
+ Sounds good!
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 12,
+ anchorPath: [2, 0, 0],
+ focusOffset: 12,
+ focusPath: [2, 0, 0],
+ });
+ } else {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+ Sounds good!
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 12,
+ anchorPath: [0, 3, 0],
+ focusOffset: 12,
+ focusPath: [0, 3, 0],
+ });
+ }
+
+ // Select all the text
+ await selectAll(page);
+ if (isRichText) {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+
+ Sounds good!
+
+ `,
+ );
+ if (browserName === 'firefox') {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [],
+ focusOffset: 3,
+ focusPath: [],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 12,
+ focusPath: [2, 0, 0],
+ });
+ }
+ } else {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+ Sounds good!
+
+ `,
+ );
+ if (browserName === 'firefox') {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [],
+ focusOffset: 1,
+ focusPath: [],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 12,
+ focusPath: [0, 3, 0],
+ });
+ }
+ }
+
+ // Copy all the text
+ const clipboard = await copyToClipboard(page);
+ if (isRichText) {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+
+ Sounds good!
+
+ `,
+ );
+ } else {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+ Sounds good!
+
+ `,
+ );
+ }
+
+ // Paste after
+ await page.keyboard.press('ArrowRight');
+ await pasteFromClipboard(page, clipboard);
+ if (isRichText) {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+
+ Sounds good!Copy + pasting?
+
+
+
+ Sounds good!
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 12,
+ anchorPath: [4, 0, 0],
+ focusOffset: 12,
+ focusPath: [4, 0, 0],
+ });
+ } else {
+ await assertHTML(
+ page,
+ html`
+
+ Copy + pasting?
+
+
+ Sounds good!Copy + pasting?
+
+
+ Sounds good!
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 12,
+ anchorPath: [0, 6, 0],
+ focusOffset: 12,
+ focusPath: [0, 6, 0],
+ });
+ }
+ });
+
+ test(`Copy and paste heading`, async ({
+ isPlainText,
+ isCollab,
+ page,
+ browserName,
+ }) => {
+ test.fixme(isCollab && IS_LINUX, 'Flaky on Linux + Collab');
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+ await page.keyboard.type('# Heading');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('Some text');
+
+ await moveToEditorBeginning(page);
+ await page.keyboard.down('Shift');
+ await moveToLineEnd(page);
+ await page.keyboard.up('Shift');
+
+ const clipboard = await copyToClipboard(page);
+
+ await moveToEditorEnd(page);
+ await page.keyboard.press('Enter');
+
+ // Paste the content
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ Heading
+
+
+ Some text
+
+
+ Heading
+
+ `,
+ );
+
+ await assertSelection(page, {
+ anchorOffset: 7,
+ anchorPath: [2, 0, 0],
+ focusOffset: 7,
+ focusPath: [2, 0, 0],
+ });
+ });
+
+ test(`Copy and paste between sections`, async ({
+ isRichText,
+ page,
+ browserName,
+ }) => {
+ await focusEditor(page);
+ await page.keyboard.type('Hello world #foobar test #foobar2 when #not');
+
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('Next #line of #text test #foo');
+
+ if (isRichText) {
+ await assertHTML(
+ page,
+ html`
+
+ Hello world
+
+ #foobar
+
+ test
+
+ #foobar2
+
+ when
+
+ #not
+
+
+
+ Next
+
+ #line
+
+ of
+
+ #text
+
+ test
+
+ #foo
+
+
+ `,
+ );
+
+ await assertSelection(page, {
+ anchorOffset: 4,
+ anchorPath: [1, 5, 0],
+ focusOffset: 4,
+ focusPath: [1, 5, 0],
+ });
+ } else {
+ await assertHTML(
+ page,
+ html`
+
+ Hello world
+
+ #foobar
+
+ test
+
+ #foobar2
+
+ when
+
+ #not
+
+
+ Next
+
+ #line
+
+ of
+
+ #text
+
+ test
+
+ #foo
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 4,
+ anchorPath: [0, 12, 0],
+ focusOffset: 4,
+ focusPath: [0, 12, 0],
+ });
+ }
+
+ // Select all the content
+ await selectAll(page);
+
+ if (isRichText) {
+ if (browserName === 'firefox') {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [],
+ focusOffset: 2,
+ focusPath: [],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 4,
+ focusPath: [1, 5, 0],
+ });
+ }
+ } else {
+ if (browserName === 'firefox') {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [],
+ focusOffset: 1,
+ focusPath: [],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 4,
+ focusPath: [0, 12, 0],
+ });
+ }
+ }
+
+ // Copy all the text
+ let clipboard = await copyToClipboard(page);
+ await page.keyboard.press('Delete');
+ // Paste the content
+ await pasteFromClipboard(page, clipboard);
+
+ if (isRichText) {
+ await assertHTML(
+ page,
+ html`
+
+ Hello world
+
+ #foobar
+
+ test
+
+ #foobar2
+
+ when
+
+ #not
+
+
+
+ Next
+
+ #line
+
+ of
+
+ #text
+
+ test
+
+ #foo
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 4,
+ anchorPath: [1, 5, 0],
+ focusOffset: 4,
+ focusPath: [1, 5, 0],
+ });
+ } else {
+ await assertHTML(
+ page,
+ html`
+
+ Hello world
+
+ #foobar
+
+ test
+
+ #foobar2
+
+ when
+
+ #not
+
+
+ Next
+
+ #line
+
+ of
+
+ #text
+
+ test
+
+ #foo
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 4,
+ anchorPath: [0, 12, 0],
+ focusOffset: 4,
+ focusPath: [0, 12, 0],
+ });
+ }
+
+ await moveToPrevWord(page);
+ await page.keyboard.down('Shift');
+ await page.keyboard.press('ArrowUp');
+ await moveToPrevWord(page);
+ // Once more for linux on Chromium
+ if (IS_LINUX && browserName === 'chromium') {
+ await moveToPrevWord(page);
+ }
+ await page.keyboard.up('Shift');
+
+ if (isRichText) {
+ await assertSelection(page, {
+ anchorOffset: 1,
+ anchorPath: [1, 5, 0],
+ focusOffset: 1,
+ focusPath: [0, 2, 0],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 1,
+ anchorPath: [0, 12, 0],
+ focusOffset: 1,
+ focusPath: [0, 2, 0],
+ });
+ }
+
+ // Copy selected text
+ clipboard = await copyToClipboard(page);
+ await page.keyboard.press('Delete');
+ // Paste the content
+ await pasteFromClipboard(page, clipboard);
+
+ if (isRichText) {
+ await assertHTML(
+ page,
+ html`
+
+ Hello world
+
+ #foobar
+
+ test
+
+ #foobar2
+
+ when
+
+ #not
+
+
+
+ Next
+
+ #line
+
+ of
+
+ #text
+
+ test
+
+ #foo
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 1,
+ anchorPath: [1, 5, 0],
+ focusOffset: 1,
+ focusPath: [1, 5, 0],
+ });
+ } else {
+ await assertHTML(
+ page,
+ html`
+
+ Hello world
+
+ #foobar
+
+ test
+
+ #foobar2
+
+ when
+
+ #not
+
+
+ Next
+
+ #line
+
+ of
+
+ #text
+
+ test
+
+ #foo
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 1,
+ anchorPath: [0, 12, 0],
+ focusOffset: 1,
+ focusPath: [0, 12, 0],
+ });
+ }
+
+ // Select all the content
+ await selectAll(page);
+
+ if (isRichText) {
+ if (browserName === 'firefox') {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [],
+ focusOffset: 2,
+ focusPath: [],
+ });
+ } else {
+ if (browserName === 'firefox') {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 3,
+ focusPath: [1, 5, 0],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 4,
+ focusPath: [1, 5, 0],
+ });
+ }
+ }
+ } else {
+ if (browserName === 'firefox') {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [],
+ focusOffset: 1,
+ focusPath: [],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 0, 0],
+ focusOffset: 4,
+ focusPath: [0, 12, 0],
+ });
+ }
+ }
+
+ await page.keyboard.press('Delete');
+ await assertHTML(
+ page,
+ html`
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0],
+ focusOffset: 0,
+ focusPath: [0],
+ });
+ });
+
+ test('Copy and paste an inline element into a leaf node', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+
+ // Root
+ // |- Paragraph
+ // |- Link
+ // |- Text "Hello"
+ // |- Text "World"
+ await page.keyboard.type('Hello');
+ await selectAll(page);
+ await click(page, '.link');
+ await click(page, '.link-confirm');
+ await page.keyboard.press('ArrowRight');
+ await page.keyboard.press('Space');
+ await page.keyboard.type('World');
+
+ await selectAll(page);
+
+ const clipboard = await copyToClipboard(page);
+
+ await page.keyboard.press('ArrowRight');
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+
+ Hello
+
+ World
+
+ Hello
+
+ World
+
+ `,
+ );
+ });
+
+ test('Copy + paste multi-line plain text into rich text produces separate paragraphs', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+ await page.keyboard.type('# Hello ');
+ await pasteFromClipboard(page, {
+ 'text/plain': 'world\nAnd text below',
+ });
+ await assertHTML(
+ page,
+ html`
+
+ Hello world
+
+
+ And text below
+
+ `,
+ );
+ });
+
+ test('Pasting a decorator node on a blank line inserts before the line', async ({
+ page,
+ isCollab,
+ isPlainText,
+ }) => {
+ test.fixme(); // TODO: flaky
+ test.skip(isPlainText);
+
+ // copying and pasting the node is easier than creating the clipboard data
+ await focusEditor(page);
+ await insertYouTubeEmbed(page, YOUTUBE_SAMPLE_URL);
+ await page.keyboard.press('ArrowLeft'); // this selects the node
+ const clipboard = await copyToClipboard(page);
+ await page.keyboard.press('ArrowRight'); // this moves to a new line (empty paragraph node)
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+
+
+
+
+
+
+ `,
+ );
+ });
+});
diff --git a/demos/playground/src/__tests__/e2e/CopyAndPaste/lexical/ListsCopyAndPaste.spec.mjs b/demos/playground/src/__tests__/e2e/CopyAndPaste/lexical/ListsCopyAndPaste.spec.mjs
new file mode 100644
index 00000000..1b2dcd6e
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/CopyAndPaste/lexical/ListsCopyAndPaste.spec.mjs
@@ -0,0 +1,732 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+import {
+ moveLeft,
+ moveToLineBeginning,
+ moveToLineEnd,
+ selectAll,
+ selectCharacters,
+} from '../../../keyboardShortcuts/index.mjs';
+import {
+ assertHTML,
+ assertSelection,
+ copyToClipboard,
+ focusEditor,
+ html,
+ initialize,
+ IS_LINUX,
+ IS_WINDOWS,
+ pasteFromClipboard,
+ test,
+} from '../../../utils/index.mjs';
+
+test.describe('Lists CopyAndPaste', () => {
+ test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
+ test('Copy and paste of partial list items into an empty editor', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+
+ // Add three list items
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+
+ await page.keyboard.press('Enter');
+ await page.keyboard.press('Enter');
+
+ // Add a paragraph
+ await page.keyboard.type('Some text.');
+
+ await assertHTML(
+ page,
+ '
+ - one
- two
- three
Some text.
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 10,
+ anchorPath: [1, 0, 0],
+ focusOffset: 10,
+ focusPath: [1, 0, 0],
+ });
+
+ await page.keyboard.down('Shift');
+ await moveToLineBeginning(page);
+ await moveLeft(page, 3);
+ await page.keyboard.up('Shift');
+
+ await assertSelection(page, {
+ anchorOffset: 10,
+ anchorPath: [1, 0, 0],
+ focusOffset: 3,
+ focusPath: [0, 2, 0, 0],
+ });
+
+ // Copy the partial list item and paragraph
+ const clipboard = await copyToClipboard(page);
+
+ // Select all and remove content
+ await selectAll(page);
+ await page.keyboard.press('Backspace');
+ await page.keyboard.press('Backspace');
+
+ await assertHTML(
+ page,
+ html`
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0],
+ focusOffset: 0,
+ focusPath: [0],
+ });
+
+ // Paste
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- ee
Some text.
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 10,
+ anchorPath: [1, 0, 0],
+ focusOffset: 10,
+ focusPath: [1, 0, 0],
+ });
+ });
+
+ test('Copy and paste of partial list items into the list', async ({
+ page,
+ isPlainText,
+ isCollab,
+ browserName,
+ }) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ // Add three list items
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+
+ await page.keyboard.press('Enter');
+ await page.keyboard.press('Enter');
+
+ // Add a paragraph
+ await page.keyboard.type('Some text.');
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ one
+
+ -
+ two
+
+ -
+ three
+
+
+
+ Some text.
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 10,
+ anchorPath: [1, 0, 0],
+ focusOffset: 10,
+ focusPath: [1, 0, 0],
+ });
+
+ await page.keyboard.down('Shift');
+ await moveToLineBeginning(page);
+ await moveLeft(page, 3);
+ await page.keyboard.up('Shift');
+
+ await assertSelection(page, {
+ anchorOffset: 10,
+ anchorPath: [1, 0, 0],
+ focusOffset: 3,
+ focusPath: [0, 2, 0, 0],
+ });
+
+ // Copy the partial list item and paragraph
+ const clipboard = await copyToClipboard(page);
+
+ // Select all and remove content
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+ if (!IS_WINDOWS && browserName === 'firefox') {
+ await page.keyboard.press('ArrowUp');
+ }
+ await moveToLineEnd(page);
+
+ await page.keyboard.down('Enter');
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ one
+
+ -
+
+
+ -
+ two
+
+ -
+ three
+
+
+
+ Some text.
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 1],
+ focusOffset: 0,
+ focusPath: [0, 1],
+ });
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ one
+
+ -
+ ee
+
+
+
+ Some text.
+
+
+ -
+ two
+
+ -
+ three
+
+
+
+ Some text.
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 10,
+ anchorPath: [1, 0, 0],
+ focusOffset: 10,
+ focusPath: [1, 0, 0],
+ });
+ });
+
+ test('Copy list items and paste back into list', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('four');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('five');
+
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+
+ await moveToLineBeginning(page);
+ await page.keyboard.down('Shift');
+ await page.keyboard.press('ArrowDown');
+ await moveToLineEnd(page);
+ await page.keyboard.up('Shift');
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 2, 0, 0],
+ focusOffset: 4,
+ focusPath: [0, 3, 0, 0],
+ });
+
+ const clipboard = await copyToClipboard(page);
+
+ await page.keyboard.press('Backspace');
+
+ await assertHTML(
+ page,
+ '- one
- two
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 2],
+ focusOffset: 0,
+ focusPath: [0, 2],
+ });
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 4,
+ anchorPath: [0, 3, 0, 0],
+ focusOffset: 4,
+ focusPath: [0, 3, 0, 0],
+ });
+ });
+
+ test('Copy list items and paste into list', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.fixme(isCollab && IS_LINUX, 'Flaky on Linux + Collab');
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('four');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('five');
+
+ await selectAll(page);
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ one
+
+ -
+ two
+
+ -
+ three
+
+ -
+ four
+
+ -
+ five
+
+
+ `,
+ );
+
+ const clipboard = await copyToClipboard(page);
+
+ await page.keyboard.press('ArrowDown');
+ await page.keyboard.press('Enter');
+ await page.keyboard.press('Enter');
+
+ await page.keyboard.type('12345');
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ one
+
+ -
+ two
+
+ -
+ three
+
+ -
+ four
+
+ -
+ five
+
+
+
+ 12345
+
+ `,
+ );
+
+ await page.keyboard.press('ArrowLeft');
+ await page.keyboard.press('ArrowLeft');
+ await selectCharacters(page, 'left', 1);
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ html`
+
+ -
+ one
+
+ -
+ two
+
+ -
+ three
+
+ -
+ four
+
+ -
+ five
+
+
+
+ 12
+
+
+ -
+ one
+
+ -
+ two
+
+ -
+ three
+
+ -
+ four
+
+ -
+ five
+
+
+
+ 45
+
+ `,
+ );
+ });
+
+ test('Copy and paste of list items and paste back into list on an existing item', async ({
+ page,
+ isPlainText,
+ isCollab,
+ }) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('four');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('five');
+
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+
+ await moveToLineBeginning(page);
+ await page.keyboard.down('Shift');
+ await page.keyboard.press('ArrowDown');
+ await moveToLineEnd(page);
+ await page.keyboard.up('Shift');
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0, 2, 0, 0],
+ focusOffset: 4,
+ focusPath: [0, 3, 0, 0],
+ });
+
+ const clipboard = await copyToClipboard(page);
+
+ await page.keyboard.press('ArrowRight');
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 4,
+ anchorPath: [0, 3, 0, 0],
+ focusOffset: 4,
+ focusPath: [0, 3, 0, 0],
+ });
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- three
- four
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 4,
+ anchorPath: [0, 5, 0, 0],
+ focusOffset: 4,
+ focusPath: [0, 5, 0, 0],
+ });
+ });
+
+ test('Copy and paste two paragraphs into list on an existing item', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+ await focusEditor(page);
+
+ await page.keyboard.type('Hello');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('World');
+
+ await selectAll(page);
+
+ const clipboard = await copyToClipboard(page);
+
+ await page.keyboard.press('Backspace');
+
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('four');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('five');
+
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowUp');
+
+ await moveToLineBeginning(page);
+ await page.keyboard.press('ArrowDown');
+ await moveToLineEnd(page);
+ await moveLeft(page, 2);
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 2,
+ anchorPath: [0, 3, 0, 0],
+ focusOffset: 2,
+ focusPath: [0, 3, 0, 0],
+ });
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- foHello
Worldur
- five
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 5,
+ anchorPath: [1, 0, 0],
+ focusOffset: 5,
+ focusPath: [1, 0, 0],
+ });
+ });
+
+ test('Copy and paste two paragraphs at the end of a list', async ({
+ page,
+ isPlainText,
+ }) => {
+ test.skip(isPlainText);
+
+ await focusEditor(page);
+
+ await page.keyboard.type('Hello');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('World');
+
+ await selectAll(page);
+
+ const clipboard = await copyToClipboard(page);
+
+ await page.keyboard.press('Backspace');
+
+ await page.keyboard.type('- one');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('two');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('three');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('four');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('five');
+ await page.keyboard.press('Enter');
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- five
- Hello
World
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 5,
+ anchorPath: [1, 0, 0],
+ focusOffset: 5,
+ focusPath: [1, 0, 0],
+ });
+
+ await pasteFromClipboard(page, clipboard);
+
+ await assertHTML(
+ page,
+ '- one
- two
- three
- four
- five
- Hello
WorldHello
World
',
+ );
+ await assertSelection(page, {
+ anchorOffset: 5,
+ anchorPath: [2, 0, 0],
+ focusOffset: 5,
+ focusPath: [2, 0, 0],
+ });
+ });
+});
diff --git a/demos/playground/src/__tests__/e2e/DraggableBlock.spec.mjs b/demos/playground/src/__tests__/e2e/DraggableBlock.spec.mjs
index 3623927a..a442f58c 100644
--- a/demos/playground/src/__tests__/e2e/DraggableBlock.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/DraggableBlock.spec.mjs
@@ -151,4 +151,41 @@ test.describe('DraggableBlock', () => {
`,
);
});
+
+ test('Dragging the first paragraph to an empty space in the middle of the editor works correctly', async ({
+ page,
+ isPlainText,
+ browserName,
+ isCollab,
+ }) => {
+ test.skip(isCollab);
+ test.skip(isPlainText);
+ test.skip(browserName === 'firefox');
+
+ await focusEditor(page);
+ await page.keyboard.type('Paragraph 1');
+ await page.keyboard.press('Enter');
+ await page.keyboard.type('Paragraph 2');
+
+ await mouseMoveToSelector(page, 'p:has-text("Paragraph 1")');
+ await page.pause();
+ await dragDraggableMenuTo(page, '.ContentEditable__root');
+
+ await assertHTML(
+ page,
+ `
+
+ Paragraph 2
+
+
+ Paragraph 1
+
+ `,
+ );
+ });
});
diff --git a/demos/playground/src/__tests__/e2e/ElementFormat.spec.mjs b/demos/playground/src/__tests__/e2e/ElementFormat.spec.mjs
index cd861f30..47d3685a 100644
--- a/demos/playground/src/__tests__/e2e/ElementFormat.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/ElementFormat.spec.mjs
@@ -18,10 +18,9 @@ import {
} from '../utils/index.mjs';
test.describe('Element format', () => {
- test.fixme();
test.beforeEach(({isCollab, isPlainText, page}) => {
test.skip(isPlainText);
- initialize({isCollab, page});
+ return initialize({isCollab, page});
});
test('Can indent/align paragraph when caret is within link', async ({
diff --git a/demos/playground/src/__tests__/e2e/Events.spec.mjs b/demos/playground/src/__tests__/e2e/Events.spec.mjs
index 0bed45df..11fbca86 100644
--- a/demos/playground/src/__tests__/e2e/Events.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/Events.spec.mjs
@@ -99,79 +99,79 @@ test.describe('Events', () => {
);
});
- test.fixme(
- 'Add period with double-space after emoji (MacOS specific) #3953',
- async ({page, isPlainText}) => {
- if (LEGACY_EVENTS) {
- return;
- }
- await focusEditor(page);
- await page.keyboard.type(':)');
- await assertHTML(
- page,
- html`
-
-
- 🙂
-
-
- `,
- );
- await page.keyboard.type(' ');
+ test('Add period with double-space after emoji (MacOS specific) #3953', async ({
+ page,
+ isPlainText,
+ }) => {
+ if (LEGACY_EVENTS) {
+ return;
+ }
+ await focusEditor(page);
+ await page.keyboard.type(':)');
+ await assertHTML(
+ page,
+ html`
+
+
+ 🙂
+
+
+ `,
+ );
+ await page.keyboard.type(' ');
- await evaluate(page, () => {
- const editable = document.querySelector('[contenteditable="true"]');
- const spans = editable.querySelectorAll('span');
- const lastSpan = spans[spans.length - 1];
- const lastSpanTextNode = lastSpan.firstChild;
- function singleRangeFn(
- startContainer,
- startOffset,
- endContainer,
- endOffset,
- ) {
- return () => [
- new StaticRange({
- endContainer,
- endOffset,
- startContainer,
- startOffset,
- }),
- ];
- }
- const characterBeforeInputEvent = new InputEvent('beforeinput', {
- bubbles: true,
- cancelable: true,
- data: '. ',
- inputType: 'insertText',
- });
- characterBeforeInputEvent.getTargetRanges = singleRangeFn(
- lastSpanTextNode,
- 0,
- lastSpanTextNode,
- 1,
- );
- // We don't do textNode.textContent += character; intentionally; if the code prevents default
- // Lexical should add it via controlled mode.
- editable.dispatchEvent(characterBeforeInputEvent);
+ await evaluate(page, () => {
+ const editable = document.querySelector('[contenteditable="true"]');
+ const spans = editable.querySelectorAll('span');
+ const lastSpan = spans[spans.length - 1];
+ const lastSpanTextNode = lastSpan.firstChild;
+ function singleRangeFn(
+ startContainer,
+ startOffset,
+ endContainer,
+ endOffset,
+ ) {
+ return () => [
+ new StaticRange({
+ endContainer,
+ endOffset,
+ startContainer,
+ startOffset,
+ }),
+ ];
+ }
+ const characterBeforeInputEvent = new InputEvent('beforeinput', {
+ bubbles: true,
+ cancelable: true,
+ data: '. ',
+ inputType: 'insertText',
});
- await page.pause();
-
- await assertHTML(
- page,
- html`
-
-
- 🙂
-
- .
-
- `,
+ characterBeforeInputEvent.getTargetRanges = singleRangeFn(
+ lastSpanTextNode,
+ 0,
+ lastSpanTextNode,
+ 1,
);
- },
- );
+ // We don't do textNode.textContent += character; intentionally; if the code prevents default
+ // Lexical should add it via controlled mode.
+ editable.dispatchEvent(characterBeforeInputEvent);
+ });
+ await page.pause();
+
+ await assertHTML(
+ page,
+ html`
+
+
+ 🙂
+
+ .
+
+ `,
+ );
+ });
});
diff --git a/demos/playground/src/__tests__/e2e/Extensions.spec.mjs b/demos/playground/src/__tests__/e2e/Extensions.spec.mjs
index 838511ea..e054da71 100644
--- a/demos/playground/src/__tests__/e2e/Extensions.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/Extensions.spec.mjs
@@ -200,7 +200,7 @@ test.describe('Extensions', () => {
isPlainText,
}) => {
// This test is flaky in collab #3915
- test.fixme(isCollab); // lexical
+ test.fixme(isCollab);
test.skip(isPlainText);
await focusEditor(page);
diff --git a/demos/playground/src/__tests__/e2e/Hashtags.spec.mjs b/demos/playground/src/__tests__/e2e/Hashtags.spec.mjs
index d85e9066..924eda6f 100644
--- a/demos/playground/src/__tests__/e2e/Hashtags.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/Hashtags.spec.mjs
@@ -302,4 +302,47 @@ test.describe('Hashtags', () => {
`,
);
});
+
+ test('Should not break with multiple leading "#" #5636', async ({page}) => {
+ await focusEditor(page);
+ await page.keyboard.type('#hello');
+
+ await waitForSelector(page, '.PlaygroundEditorTheme__hashtag');
+
+ await assertHTML(
+ page,
+ html`
+
+
+ #hello
+
+
+ `,
+ );
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 0, 0],
+ focusOffset: 6,
+ focusPath: [0, 0, 0],
+ });
+
+ await moveToEditorBeginning(page);
+ await page.keyboard.type('#');
+
+ await assertHTML(
+ page,
+ html`
+
+ #
+
+ #hello
+
+
+ `,
+ );
+ });
});
diff --git a/demos/playground/src/__tests__/e2e/Headings.spec.mjs b/demos/playground/src/__tests__/e2e/Headings.spec.mjs
deleted file mode 100644
index 00aa0cbf..00000000
--- a/demos/playground/src/__tests__/e2e/Headings.spec.mjs
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- */
-
-import {moveRight, moveToEditorBeginning} from '../keyboardShortcuts/index.mjs';
-import {
- assertHTML,
- click,
- focusEditor,
- html,
- initialize,
- test,
- IS_WINDOWS,
-} from '../utils/index.mjs';
-
-test.describe('Headings', () => {
- test.beforeEach(({isPlainText, isCollab, browserName, page}) => {
- test.fixme(IS_WINDOWS && browserName === 'firefox' && isCollab);
- test.skip(isPlainText);
- initialize({isCollab, page});
- });
-
- test('Stays as a heading when you backspace at the start of a heading with no previous sibling nodes present', async ({
- page,
- isCollab,
- browserName,
- }) => {
- if (IS_WINDOWS && browserName === 'firefox' && isCollab) {
- test.fixme();
- }
- await focusEditor(page);
-
- await click(page, '.block-controls');
- await click(page, '.dropdown .icon.h1');
-
- await page.keyboard.type('Welcome to the playground');
-
- await assertHTML(
- page,
- html`
-
- Welcome to the playground
-
- `,
- );
-
- await moveToEditorBeginning(page);
-
- await page.keyboard.press('Backspace');
-
- await assertHTML(
- page,
- html`
-
- Welcome to the playground
-
- `,
- );
- });
-
- test('Stays as a heading when you press enter in the middle of a heading', async ({
- page,
- }) => {
- await focusEditor(page);
-
- await click(page, '.block-controls');
- await click(page, '.dropdown .icon.h1');
-
- await page.keyboard.type('Welcome to the playground');
-
- await assertHTML(
- page,
- html`
-
- Welcome to the playground
-
- `,
- );
-
- await moveToEditorBeginning(page);
-
- await moveRight(page, 5);
-
- await page.keyboard.press('Enter');
-
- await assertHTML(
- page,
- html`
-
- Welco
-
-
- me to the playground
-
- `,
- );
- });
-
- test('Changes to a paragraph when you press enter at the end of a heading', async ({
- page,
- browserName,
- isCollab,
- }) => {
- if (IS_WINDOWS && browserName === 'firefox' && isCollab) {
- test.fixme();
- }
- await focusEditor(page);
-
- await click(page, '.block-controls');
- await click(page, '.dropdown .icon.h1');
-
- await page.keyboard.type('Welcome to the playground');
-
- await assertHTML(
- page,
- html`
-
- Welcome to the playground
-
- `,
- );
-
- await page.keyboard.press('Enter');
-
- await assertHTML(
- page,
- html`
-
- Welcome to the playground
-
-
- `,
- );
- });
-});
diff --git a/demos/playground/src/__tests__/e2e/Headings/HeadingsBackspaceAtStart.spec.mjs b/demos/playground/src/__tests__/e2e/Headings/HeadingsBackspaceAtStart.spec.mjs
new file mode 100644
index 00000000..703bf182
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/Headings/HeadingsBackspaceAtStart.spec.mjs
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {moveToEditorBeginning} from '../../keyboardShortcuts/index.mjs';
+import {
+ assertHTML,
+ click,
+ focusEditor,
+ html,
+ initialize,
+ test,
+} from '../../utils/index.mjs';
+
+test('Headings - stays as a heading when you backspace at the start of a heading with no previous sibling nodes present', async ({
+ page,
+ isPlainText,
+ isCollab,
+}) => {
+ test.skip(isPlainText);
+ await initialize({isCollab, page});
+ await focusEditor(page);
+
+ await click(page, '.block-controls');
+ await click(page, '.dropdown .icon.h1');
+
+ await page.keyboard.type('Welcome to the playground');
+
+ await assertHTML(
+ page,
+ html`
+
+ Welcome to the playground
+
+ `,
+ );
+
+ await moveToEditorBeginning(page);
+
+ await page.keyboard.press('Backspace');
+
+ await assertHTML(
+ page,
+ html`
+
+ Welcome to the playground
+
+ `,
+ );
+});
diff --git a/demos/playground/src/__tests__/e2e/Headings/HeadingsEnterAtEnd.spec.mjs b/demos/playground/src/__tests__/e2e/Headings/HeadingsEnterAtEnd.spec.mjs
new file mode 100644
index 00000000..37239396
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/Headings/HeadingsEnterAtEnd.spec.mjs
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {
+ moveRight,
+ moveToEditorBeginning,
+} from '../../keyboardShortcuts/index.mjs';
+import {
+ assertHTML,
+ click,
+ focusEditor,
+ html,
+ initialize,
+ test,
+} from '../../utils/index.mjs';
+
+test(`Headings - stays as a heading when you press enter in the middle of a heading`, async ({
+ page,
+ isCollab,
+ isPlainText,
+}) => {
+ test.skip(isPlainText);
+ await initialize({isCollab, page});
+ await focusEditor(page);
+
+ await click(page, '.block-controls');
+ await click(page, '.dropdown .icon.h1');
+
+ await page.keyboard.type('Welcome to the playground');
+
+ await assertHTML(
+ page,
+ html`
+
+ Welcome to the playground
+
+ `,
+ );
+
+ await moveToEditorBeginning(page);
+
+ await moveRight(page, 5);
+
+ await page.keyboard.press('Enter');
+
+ await assertHTML(
+ page,
+ html`
+
+ Welco
+
+
+ me to the playground
+
+ `,
+ );
+});
diff --git a/demos/playground/src/__tests__/e2e/Headings/HeadingsEnterInMiddle.spec.mjs b/demos/playground/src/__tests__/e2e/Headings/HeadingsEnterInMiddle.spec.mjs
new file mode 100644
index 00000000..5a15096e
--- /dev/null
+++ b/demos/playground/src/__tests__/e2e/Headings/HeadingsEnterInMiddle.spec.mjs
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {
+ assertHTML,
+ click,
+ focusEditor,
+ html,
+ initialize,
+ test,
+} from '../../utils/index.mjs';
+
+test('Headings - changes to a paragraph when you press enter at the end of a heading', async ({
+ page,
+ isPlainText,
+ isCollab,
+}) => {
+ test.skip(isPlainText);
+ await initialize({isCollab, page});
+ await focusEditor(page);
+
+ await click(page, '.block-controls');
+ await click(page, '.dropdown .icon.h1');
+
+ await page.keyboard.type('Welcome to the playground');
+
+ await assertHTML(
+ page,
+ html`
+
+ Welcome to the playground
+
+ `,
+ );
+
+ await page.keyboard.press('Enter');
+
+ await assertHTML(
+ page,
+ html`
+
+ Welcome to the playground
+
+
+ `,
+ );
+});
diff --git a/demos/playground/src/__tests__/e2e/History.spec.mjs b/demos/playground/src/__tests__/e2e/History.spec.mjs
index 9978ad21..c601e10b 100644
--- a/demos/playground/src/__tests__/e2e/History.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/History.spec.mjs
@@ -564,77 +564,168 @@ test.describe('History', () => {
test.describe('History - IME', () => {
test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
- test.fixme(
- 'Can undo composed Hirigana via IME after composition ends (#2479)',
- async ({page, browserName, isCollab, isPlainText, legacyEvents}) => {
- // We don't yet support FF.
- test.skip(isCollab || isPlainText || browserName === 'firefox');
+ test('Can undo composed Hirigana via IME after composition ends (#2479)', async ({
+ page,
+ browserName,
+ isCollab,
+ isPlainText,
+ legacyEvents,
+ }) => {
+ // We don't yet support FF.
+ test.skip(isCollab || isPlainText || browserName !== 'chromium');
- await focusEditor(page);
- await enableCompositionKeyEvents(page);
+ await focusEditor(page);
+ await enableCompositionKeyEvents(page);
+
+ const client = await page.context().newCDPSession(page);
+ // await page.keyboard.imeSetComposition('s', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 's',
+ });
+ // await page.keyboard.imeSetComposition('す', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'す',
+ });
+ // await page.keyboard.imeSetComposition('すs', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すs',
+ });
+ // await page.keyboard.imeSetComposition('すsh', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'すsh',
+ });
+ // await page.keyboard.imeSetComposition('すし', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'すし',
+ });
+ // await page.keyboard.insertText('すし');
+ await client.send('Input.insertText', {
+ text: 'すし',
+ });
- await page.keyboard.imeSetComposition('s', 1, 1);
- await page.keyboard.imeSetComposition('す', 1, 1);
- await page.keyboard.imeSetComposition('すs', 2, 2);
- await page.keyboard.imeSetComposition('すsh', 3, 3);
- await page.keyboard.imeSetComposition('すし', 2, 2);
- await page.keyboard.insertText('すし');
+ await sleep(1050); // default merge interval is 1000, add 50ms as overhead due to CI latency.
- await sleep(1050); // default merge interval is 1000, add 50ms as overhead due to CI latency.
+ await page.keyboard.type(' ');
- await page.keyboard.type(' ');
+ await sleep(1050);
- await sleep(1050);
+ // await page.keyboard.imeSetComposition('m', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'm',
+ });
+ // await page.keyboard.imeSetComposition('も', 1, 1);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 1,
+ selectionEnd: 1,
+ text: 'も',
+ });
+ // await page.keyboard.imeSetComposition('もj', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もj',
+ });
+ // await page.keyboard.imeSetComposition('もじ', 2, 2);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 2,
+ selectionEnd: 2,
+ text: 'もじ',
+ });
+ // await page.keyboard.imeSetComposition('もじあ', 3, 3);
+ await client.send('Input.imeSetComposition', {
+ selectionStart: 3,
+ selectionEnd: 3,
+ text: 'もじあ',
+ });
+ // await page.keyboard.insertText('もじあ');
+ await client.send('Input.insertText', {
+ text: 'もじあ',
+ });
- await page.keyboard.imeSetComposition('m', 1, 1);
- await page.keyboard.imeSetComposition('も', 1, 1);
- await page.keyboard.imeSetComposition('もj', 2, 2);
- await page.keyboard.imeSetComposition('もじ', 2, 2);
- await page.keyboard.imeSetComposition('もじあ', 3, 3);
- await page.keyboard.insertText('もじあ');
+ await assertHTML(
+ page,
+ html`
+
+ すし もじあ
+
+ `,
+ );
- await assertHTML(
- page,
- html`
-
- すし もじあ
-
- `,
- );
+ await assertSelection(page, {
+ anchorOffset: 6,
+ anchorPath: [0, 0, 0],
+ focusOffset: 6,
+ focusPath: [0, 0, 0],
+ });
- await assertSelection(page, {
- anchorOffset: 6,
- anchorPath: [0, 0, 0],
- focusOffset: 6,
- focusPath: [0, 0, 0],
- });
+ await undo(page);
- await undo(page);
+ const WHITESPACE_TOKEN = ' ';
- const WHITESPACE_TOKEN = ' ';
+ await assertHTML(
+ page,
+ html`
+
+ すし${WHITESPACE_TOKEN}
+
+ `,
+ );
- await assertHTML(
- page,
- html`
-
- すし${WHITESPACE_TOKEN}
-
- `,
- );
+ await assertSelection(page, {
+ anchorOffset: 3,
+ anchorPath: [0, 0, 0],
+ focusOffset: 3,
+ focusPath: [0, 0, 0],
+ });
+
+ await undo(page);
+
+ await assertHTML(
+ page,
+ html`
+
+ すし
+
+ `,
+ );
+ if (browserName === 'webkit' && !legacyEvents) {
await assertSelection(page, {
anchorOffset: 3,
anchorPath: [0, 0, 0],
focusOffset: 3,
focusPath: [0, 0, 0],
});
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 2,
+ anchorPath: [0, 0, 0],
+ focusOffset: 2,
+ focusPath: [0, 0, 0],
+ });
+ }
- await undo(page);
+ await undo(page);
+ if (browserName === 'webkit' && !legacyEvents) {
await assertHTML(
page,
html`
@@ -645,89 +736,58 @@ test.describe('History - IME', () => {
`,
);
-
- if (browserName === 'webkit' && !legacyEvents) {
- await assertSelection(page, {
- anchorOffset: 3,
- anchorPath: [0, 0, 0],
- focusOffset: 3,
- focusPath: [0, 0, 0],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0],
- focusOffset: 2,
- focusPath: [0, 0, 0],
- });
- }
-
- await undo(page);
-
- if (browserName === 'webkit' && !legacyEvents) {
- await assertHTML(
- page,
- html`
-
- すし
-
- `,
- );
- } else {
- await assertHTML(
- page,
- html`
-
- `,
- );
- }
-
- if (browserName === 'webkit' && !legacyEvents) {
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0],
- focusOffset: 2,
- focusPath: [0, 0, 0],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 0,
- anchorPath: [0],
- focusOffset: 0,
- focusPath: [0],
- });
- }
-
- await redo(page);
-
+ } else {
await assertHTML(
page,
html`
-
- すし
-
+
`,
);
+ }
+
+ if (browserName === 'webkit' && !legacyEvents) {
+ await assertSelection(page, {
+ anchorOffset: 2,
+ anchorPath: [0, 0, 0],
+ focusOffset: 2,
+ focusPath: [0, 0, 0],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 0,
+ anchorPath: [0],
+ focusOffset: 0,
+ focusPath: [0],
+ });
+ }
+
+ await redo(page);
- if (browserName === 'webkit' && !legacyEvents) {
- await assertSelection(page, {
- anchorOffset: 3,
- anchorPath: [0, 0, 0],
- focusOffset: 3,
- focusPath: [0, 0, 0],
- });
- } else {
- await assertSelection(page, {
- anchorOffset: 2,
- anchorPath: [0, 0, 0],
- focusOffset: 2,
- focusPath: [0, 0, 0],
- });
- }
- },
- );
+ await assertHTML(
+ page,
+ html`
+
+ すし
+
+ `,
+ );
+
+ if (browserName === 'webkit' && !legacyEvents) {
+ await assertSelection(page, {
+ anchorOffset: 3,
+ anchorPath: [0, 0, 0],
+ focusOffset: 3,
+ focusPath: [0, 0, 0],
+ });
+ } else {
+ await assertSelection(page, {
+ anchorOffset: 2,
+ anchorPath: [0, 0, 0],
+ focusOffset: 2,
+ focusPath: [0, 0, 0],
+ });
+ }
+ });
});
diff --git a/demos/playground/src/__tests__/e2e/HorizontalRule.spec.mjs b/demos/playground/src/__tests__/e2e/HorizontalRule.spec.mjs
index a9ea3492..5ef60940 100644
--- a/demos/playground/src/__tests__/e2e/HorizontalRule.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/HorizontalRule.spec.mjs
@@ -23,7 +23,6 @@ import {
selectFromInsertDropdown,
test,
waitForSelector,
- IS_WINDOWS,
} from '../utils/index.mjs';
test.describe('HorizontalRule', () => {
@@ -34,9 +33,6 @@ test.describe('HorizontalRule', () => {
isPlainText,
browserName,
}) => {
- if (IS_WINDOWS && browserName === 'firefox' && isCollab) {
- test.fixme();
- }
test.skip(isPlainText);
await focusEditor(page);
@@ -48,7 +44,7 @@ test.describe('HorizontalRule', () => {
page,
html`
-
+
`,
);
@@ -111,7 +107,7 @@ test.describe('HorizontalRule', () => {
dir="ltr">
Some text
-
+
@@ -139,7 +135,7 @@ test.describe('HorizontalRule', () => {
if (!isCollab) {
await assertHTML(
page,
- '
Some more text
',
+ '
Some more text
',
);
}
@@ -191,7 +187,7 @@ test.describe('HorizontalRule', () => {
dir="ltr">
Test
-
+
`,
);
@@ -252,7 +248,7 @@ test.describe('HorizontalRule', () => {
dir="ltr">
Te
-
+
@@ -269,15 +265,7 @@ test.describe('HorizontalRule', () => {
});
});
- test('Can copy and paste a horizontal rule', async ({
- page,
- isPlainText,
- browserName,
- isCollab,
- }) => {
- if (IS_WINDOWS && browserName === 'firefox' && isCollab) {
- test.fixme();
- }
+ test('Can copy and paste a horizontal rule', async ({page, isPlainText}) => {
test.skip(isPlainText);
await focusEditor(page);
@@ -290,7 +278,7 @@ test.describe('HorizontalRule', () => {
page,
html`
-
+
`,
);
@@ -317,7 +305,7 @@ test.describe('HorizontalRule', () => {
page,
html`
-
+
`,
);
@@ -338,9 +326,9 @@ test.describe('HorizontalRule', () => {
page,
html`
-
+
-
+
`,
);
@@ -371,7 +359,7 @@ test.describe('HorizontalRule', () => {
page,
html`
-
+
`,
);
diff --git a/demos/playground/src/__tests__/e2e/Images.spec.mjs b/demos/playground/src/__tests__/e2e/Images.spec.mjs
index 3b5ec433..d2f1cf93 100644
--- a/demos/playground/src/__tests__/e2e/Images.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/Images.spec.mjs
@@ -23,6 +23,7 @@ import {
insertUploadImage,
insertUrlImage,
IS_WINDOWS,
+ LEGACY_EVENTS,
SAMPLE_IMAGE_URL,
SAMPLE_LANDSCAPE_IMAGE_URL,
selectorBoundingBox,
@@ -404,8 +405,6 @@ test.describe('Images', () => {
'a pretty yellow flower :)',
);
- await page.waitForTimeout(3000);
-
await assertHTML(
page,
html`
@@ -588,7 +587,7 @@ test.describe('Images', () => {
isCollab,
}) => {
// This test is flaky in collab #3915
- test.fixme(isCollab); // lexical
+ test.fixme(isCollab);
test.skip(isPlainText);
await page.setViewportSize({
@@ -619,7 +618,10 @@ test.describe('Images', () => {
test('Node selection: can select multiple image nodes and replace them with a new image', async ({
page,
isPlainText,
+ browserName,
}) => {
+ // It doesn't work in legacy events mode in WebKit #5673
+ test.fixme(LEGACY_EVENTS && browserName === 'webkit');
test.skip(isPlainText);
await focusEditor(page);
@@ -738,4 +740,51 @@ test.describe('Images', () => {
`,
);
});
+
+ test('Can resolve selection correctly when the image is clicked and dragged right', async ({
+ page,
+ isPlainText,
+ browserName,
+ isCollab,
+ }) => {
+ test.skip(isPlainText);
+ let leftFrame = page;
+ if (isCollab) {
+ leftFrame = await page.frame('left');
+ }
+ await focusEditor(page);
+
+ await page.keyboard.type('HelloWorld');
+ await insertSampleImage(page);
+ await click(page, '.editor-image img');
+
+ await leftFrame.locator('.editor-image img').hover();
+ await page.mouse.down();
+ await leftFrame.locator('.PlaygroundEditorTheme__paragraph').hover();
+ await page.mouse.up();
+ await waitForSelector(page, '.editor-image img');
+ await assertHTML(
+ page,
+ html`
+
+ HelloWorld
+
+
+
+
+
+
+
+ `,
+ );
+ });
});
diff --git a/demos/playground/src/__tests__/e2e/Indentation.spec.mjs b/demos/playground/src/__tests__/e2e/Indentation.spec.mjs
index feeaf2c7..e65846a6 100644
--- a/demos/playground/src/__tests__/e2e/Indentation.spec.mjs
+++ b/demos/playground/src/__tests__/e2e/Indentation.spec.mjs
@@ -19,6 +19,7 @@ import {
test.describe('Identation', () => {
test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));
+
test.fixme(
`Can create content and indent and outdent it all`,
async ({page, browserName, isPlainText, isCollab}) => {
@@ -99,7 +100,8 @@ test.describe('Identation', () => {
code
{
style="padding-inline-start: calc(40px)">
-
|
---|