mirror of
https://github.com/facebook/lexical.git
synced 2025-08-06 16:39:33 +08:00
1587 lines
45 KiB
JavaScript
1587 lines
45 KiB
JavaScript
/**
|
|
* 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 {
|
|
deleteBackward,
|
|
deleteForward,
|
|
deleteLineBackward,
|
|
deleteLineForward,
|
|
moveDown,
|
|
moveLeft,
|
|
moveRight,
|
|
moveToEditorBeginning,
|
|
moveToEditorEnd,
|
|
moveToLineBeginning,
|
|
moveToLineEnd,
|
|
moveToPrevWord,
|
|
moveUp,
|
|
pressShiftEnter,
|
|
selectAll,
|
|
selectPrevWord,
|
|
} from '../keyboardShortcuts/index.mjs';
|
|
import {
|
|
assertHTML,
|
|
assertSelection,
|
|
assertTableSelectionCoordinates,
|
|
click,
|
|
createHumanReadableSelection,
|
|
evaluate,
|
|
expect,
|
|
focusEditor,
|
|
html,
|
|
initialize,
|
|
insertCollapsible,
|
|
insertHorizontalRule,
|
|
insertImageCaption,
|
|
insertSampleImage,
|
|
insertTable,
|
|
insertYouTubeEmbed,
|
|
IS_LINUX,
|
|
IS_MAC,
|
|
IS_WINDOWS,
|
|
pasteFromClipboard,
|
|
pressToggleBold,
|
|
pressToggleItalic,
|
|
prettifyHTML,
|
|
SAMPLE_IMAGE_URL,
|
|
selectFromFormatDropdown,
|
|
sleep,
|
|
test,
|
|
YOUTUBE_SAMPLE_URL,
|
|
} from '../utils/index.mjs';
|
|
|
|
test.describe.parallel('Selection', () => {
|
|
test.beforeEach(({isCollab, page}) =>
|
|
initialize({isCollab, page, tableHorizontalScroll: false}),
|
|
);
|
|
test('does not focus the editor on load', async ({page}) => {
|
|
const editorHasFocus = async () =>
|
|
await evaluate(page, () => {
|
|
const editorElement = document.querySelector(
|
|
'div[contenteditable="true"]',
|
|
);
|
|
return document.activeElement === editorElement;
|
|
});
|
|
|
|
await focusEditor(page);
|
|
await evaluate(page, () => {
|
|
const editorElement = document.querySelector(
|
|
'div[contenteditable="true"]',
|
|
);
|
|
return editorElement.blur();
|
|
});
|
|
expect(await editorHasFocus()).toEqual(false);
|
|
await sleep(500);
|
|
expect(await editorHasFocus()).toEqual(false);
|
|
});
|
|
|
|
test('keeps single active selection for nested editors', async ({
|
|
page,
|
|
isPlainText,
|
|
browserName,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
const hasSelection = async (parentSelector) =>
|
|
await evaluate(
|
|
page,
|
|
(_parentSelector) => {
|
|
return (
|
|
document
|
|
.querySelector(`${_parentSelector} > .tree-view-output pre`)
|
|
.__lexicalEditor.getEditorState()._selection !== null
|
|
);
|
|
},
|
|
parentSelector,
|
|
);
|
|
|
|
await focusEditor(page);
|
|
await insertSampleImage(page);
|
|
await insertImageCaption(page, 'Hello world');
|
|
expect(await hasSelection('.image-caption-container')).toBe(true);
|
|
expect(await hasSelection('.editor-shell')).toBe(false);
|
|
|
|
// Click outside of the editor and check that selection remains the same
|
|
await click(page, 'header img');
|
|
expect(await hasSelection('.image-caption-container')).toBe(true);
|
|
expect(await hasSelection('.editor-shell')).toBe(false);
|
|
|
|
// Back to root editor
|
|
if (browserName === 'firefox') {
|
|
// TODO:
|
|
// In firefox .focus() on editor does not trigger selectionchange, while checking it
|
|
// explicitly clicking on an editor (passing position that is on the right side to
|
|
// prevent clicking on image and its nested editor)
|
|
await click(page, '.editor-shell', {position: {x: 600, y: 150}});
|
|
} else {
|
|
await focusEditor(page);
|
|
}
|
|
expect(await hasSelection('.image-caption-container')).toBe(false);
|
|
expect(await hasSelection('.editor-shell')).toBe(true);
|
|
|
|
// Click outside of the editor and check that selection remains the same
|
|
await click(page, 'header img');
|
|
expect(await hasSelection('.image-caption-container')).toBe(false);
|
|
expect(await hasSelection('.editor-shell')).toBe(true);
|
|
|
|
// Back to nested editor editor
|
|
await focusEditor(page, '.image-caption-container');
|
|
expect(await hasSelection('.image-caption-container')).toBe(true);
|
|
expect(await hasSelection('.editor-shell')).toBe(false);
|
|
});
|
|
|
|
test('can wrap post-linebreak nodes into new element', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await page.keyboard.type('Line1');
|
|
await pressShiftEnter(page);
|
|
await page.keyboard.type('Line2');
|
|
await page.keyboard.down('Shift');
|
|
await moveToLineBeginning(page);
|
|
await page.keyboard.up('Shift');
|
|
await selectFromFormatDropdown(page, '.code');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">Line1</span>
|
|
</p>
|
|
<code
|
|
class="PlaygroundEditorTheme__code PlaygroundEditorTheme__ltr"
|
|
dir="ltr"
|
|
spellcheck="false"
|
|
data-gutter="1"
|
|
data-highlight-language="javascript"
|
|
data-language="javascript">
|
|
<span data-lexical-text="true">Line2</span>
|
|
</code>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('can delete text by line backwards with CMD+delete', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText || !IS_MAC);
|
|
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.press('Enter');
|
|
await page.keyboard.type('Three');
|
|
|
|
const p = (text) =>
|
|
text
|
|
? html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">${text}</span>
|
|
</p>
|
|
`
|
|
: html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`;
|
|
const lines = (...args) => html`
|
|
${args.map(p).join('')}
|
|
`;
|
|
|
|
await deleteLineBackward(page);
|
|
await assertHTML(page, lines('One', 'Two', '', ''));
|
|
await page.keyboard.press('Backspace');
|
|
await deleteLineBackward(page);
|
|
await assertHTML(page, lines('One', 'Two'));
|
|
await deleteLineBackward(page);
|
|
await assertHTML(page, lines('One', ''));
|
|
await page.keyboard.press('Backspace');
|
|
await deleteLineBackward(page);
|
|
await assertHTML(page, lines(''));
|
|
});
|
|
|
|
test('can delete text by line forwards with opt+CMD+delete', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText || !IS_MAC);
|
|
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.press('Enter');
|
|
await page.keyboard.type('Three');
|
|
|
|
const p = (text) =>
|
|
text
|
|
? html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">${text}</span>
|
|
</p>
|
|
`
|
|
: html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`;
|
|
const lines = (...args) => html`
|
|
${args.map(p).join('')}
|
|
`;
|
|
await assertHTML(page, lines('One', 'Two', '', 'Three'));
|
|
// Move to the end of the line of 'Two'
|
|
await moveUp(page, 2);
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines('One', 'Two', 'Three'));
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines('One', 'TwoThree'));
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines('One', 'Two'));
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines('One', 'Two'));
|
|
await moveToEditorBeginning(page);
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines('', 'Two'));
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines('Two'));
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines(''));
|
|
await deleteLineForward(page);
|
|
await assertHTML(page, lines(''));
|
|
});
|
|
|
|
test('can delete text by line forwards with control+K', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
const deleteLineForwardWithControlK = async () => {
|
|
await page.keyboard.down('Control');
|
|
await page.keyboard.press('k');
|
|
await page.keyboard.up('Control');
|
|
};
|
|
|
|
test.skip(isPlainText || !IS_MAC);
|
|
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.press('Enter');
|
|
await page.keyboard.type('Three');
|
|
|
|
const p = (text) =>
|
|
text
|
|
? html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">${text}</span>
|
|
</p>
|
|
`
|
|
: html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`;
|
|
const lines = (...args) => html`
|
|
${args.map(p).join('')}
|
|
`;
|
|
await assertHTML(page, lines('One', 'Two', '', 'Three'));
|
|
// Move to the end of the line of 'Two'
|
|
await moveUp(page, 2);
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines('One', 'Two', 'Three'));
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines('One', 'TwoThree'));
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines('One', 'Two'));
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines('One', 'Two'));
|
|
await moveToEditorBeginning(page);
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines('', 'Two'));
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines('Two'));
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines(''));
|
|
await deleteLineForwardWithControlK(page);
|
|
await assertHTML(page, lines(''));
|
|
});
|
|
|
|
test('can delete line which ends with element backwards with CMD+delete', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText || !IS_MAC);
|
|
await focusEditor(page);
|
|
await page.keyboard.type('One');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('Two');
|
|
// sample image
|
|
await pasteFromClipboard(page, {
|
|
'text/html': `
|
|
<span class="editor-image" data-lexical-decorator="true" contenteditable="false">
|
|
<div draggable="false">
|
|
<img src="${SAMPLE_IMAGE_URL}" alt="Yellow flower in tilt shift lens" draggable="false" style="height: inherit; max-width: 500px; width: inherit;">
|
|
</div>
|
|
</span>
|
|
`,
|
|
});
|
|
await deleteLineBackward(page);
|
|
await page.keyboard.press('Backspace');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">One</span>
|
|
</p>
|
|
`,
|
|
);
|
|
await page.keyboard.press('Backspace');
|
|
await deleteLineBackward(page);
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('can delete line which starts with element forwards with opt+CMD+delete', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText || !IS_MAC);
|
|
const modifyImageHTML = async (originalHtml) =>
|
|
await prettifyHTML(
|
|
originalHtml
|
|
.replace(
|
|
/<button\s+class="image-edit-button">\s*Edit\s*<\/button>/gi,
|
|
'',
|
|
)
|
|
.replace(/(src=")https?:\/\/[^/]+/gi, '$1'),
|
|
);
|
|
const assertImageHTML = async (page_, expectedHtml) => {
|
|
await assertHTML(
|
|
page_,
|
|
expectedHtml,
|
|
expectedHtml,
|
|
{ignoreInlineStyles: true},
|
|
modifyImageHTML,
|
|
);
|
|
};
|
|
const pasteImageHtml = html`
|
|
<img
|
|
alt="Yellow flower in tilt shift lens"
|
|
draggable="false"
|
|
src="${SAMPLE_IMAGE_URL}"
|
|
style="height: inherit; max-width: 500px; width: inherit;" />
|
|
`;
|
|
const imageHtml = html`
|
|
<span
|
|
class="inline-editor-image"
|
|
contenteditable="false"
|
|
data-lexical-decorator="true">
|
|
<span draggable="false">${pasteImageHtml}</span>
|
|
</span>
|
|
`;
|
|
|
|
await focusEditor(page);
|
|
await page.keyboard.type('One');
|
|
await page.keyboard.press('Enter');
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">One</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
|
|
await pasteFromClipboard(page, {
|
|
'text/html': pasteImageHtml,
|
|
});
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">One</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph">
|
|
${imageHtml}
|
|
<br />
|
|
</p>
|
|
`,
|
|
);
|
|
|
|
await page.keyboard.type('Two');
|
|
await page.keyboard.press('Enter');
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">One</span>
|
|
</p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
${imageHtml}
|
|
<span data-lexical-text="true">Two</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
|
|
// This puts the caret before the decorator in an awkward way, see comments below
|
|
await moveToEditorBeginning(page);
|
|
await moveToLineEnd(page);
|
|
await moveRight(page, 1);
|
|
// TODO: move arrow down doesn't work for this because it skips over the inline decorator
|
|
// if (arrow_down_works_with_decorators) {
|
|
// await moveToEditorBeginning(page);
|
|
// await moveDown(page, 1);
|
|
// }
|
|
// TODO: move to line beginning stops after the inline decorator
|
|
// if (line_beginning_works_with_decorators) {
|
|
// await moveUp(page, 1);
|
|
// await moveToLineBeginning(page);
|
|
// }
|
|
await deleteLineForward(page);
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">One</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
|
|
await deleteLineForward(page);
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">One</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
|
|
// We're now at the end of the document so delete forward is a no-op
|
|
await deleteLineForward(page);
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">One</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
|
|
await moveToEditorBeginning(page);
|
|
await deleteLineForward(page);
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
|
|
await deleteLineForward(page);
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
|
|
await deleteLineForward(page);
|
|
await assertImageHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('can delete line by collapse', async ({page, isPlainText}) => {
|
|
test.skip(isPlainText || !IS_MAC);
|
|
await focusEditor(page);
|
|
await insertCollapsible(page);
|
|
await page.keyboard.type('text');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('ArrowUp');
|
|
|
|
await deleteLineBackward(page);
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">text</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Can insert inline element within text and put selection after it', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await page.keyboard.type('Hello world');
|
|
await moveToPrevWord(page);
|
|
await pasteFromClipboard(page, {
|
|
'text/html': `<a href="https://test.com">link</a>`,
|
|
});
|
|
await sleep(3000);
|
|
await assertSelection(page, {
|
|
anchorOffset: 4,
|
|
anchorPath: [0, 1, 0, 0],
|
|
focusOffset: 4,
|
|
focusPath: [0, 1, 0, 0],
|
|
});
|
|
});
|
|
|
|
test('Can delete at boundary #4221', async ({page, isPlainText}) => {
|
|
test.skip(!isPlainText);
|
|
await focusEditor(page);
|
|
await page.keyboard.type('aaa');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('b');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('c');
|
|
|
|
await page.keyboard.down('Shift');
|
|
await moveLeft(page, 3);
|
|
await page.keyboard.up('Shift');
|
|
await page.keyboard.press('Delete');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">aaa</span>
|
|
<br />
|
|
<br />
|
|
</p>
|
|
`,
|
|
);
|
|
|
|
await page.keyboard.down('Shift');
|
|
await moveLeft(page, 1);
|
|
await page.keyboard.up('Shift');
|
|
await page.keyboard.press('Delete');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">aaa</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Can select all with node selection', async ({page, isPlainText}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await page.keyboard.type('# Text before');
|
|
await insertSampleImage(page);
|
|
await page.keyboard.type('Text after');
|
|
await selectAll(page);
|
|
await deleteBackward(page);
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test(`Can't delete forward a Collapsible`, async ({
|
|
page,
|
|
browserName,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
if (!IS_MAC) {
|
|
// Do Windows/Linux have equivalent shortcuts?
|
|
return;
|
|
}
|
|
await focusEditor(page);
|
|
await page.keyboard.type('abc');
|
|
await insertCollapsible(page);
|
|
await page.keyboard.type('title');
|
|
await moveToEditorBeginning(page);
|
|
await moveRight(page, 3);
|
|
await deleteForward(page);
|
|
|
|
const collapsibleTag = browserName === 'chromium' ? 'div' : 'details';
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">abc</span>
|
|
</p>
|
|
<${collapsibleTag} class="Collapsible__container" open="">
|
|
<summary class="Collapsible__title">
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">title</span>
|
|
</p>
|
|
</summary>
|
|
<div class="Collapsible__content">
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
</div>
|
|
</${collapsibleTag}>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test(`Can't delete backward a Collapsible`, async ({
|
|
page,
|
|
browserName,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
if (!IS_MAC) {
|
|
// Do Windows/Linux have equivalent shortcuts?
|
|
return;
|
|
}
|
|
await focusEditor(page);
|
|
await page.keyboard.type('abc');
|
|
await insertCollapsible(page);
|
|
await page.keyboard.type('title');
|
|
await moveRight(page, 2);
|
|
await page.keyboard.type('after');
|
|
await moveLeft(page, 'after'.length);
|
|
await deleteBackward(page);
|
|
|
|
const collapsibleTag = browserName === 'chromium' ? 'div' : 'details';
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">abc</span>
|
|
</p>
|
|
<${collapsibleTag} class="Collapsible__container" open="">
|
|
<summary class="Collapsible__title">
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">title</span>
|
|
</p>
|
|
</summary>
|
|
<div class="Collapsible__content">
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
</div>
|
|
</${collapsibleTag}>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">after</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test(`Can't delete forward a Table`, async ({page, isPlainText}) => {
|
|
test.skip(isPlainText);
|
|
if (!IS_MAC) {
|
|
// Do Windows/Linux have equivalent shortcuts?
|
|
return;
|
|
}
|
|
await focusEditor(page);
|
|
await page.keyboard.type('abc');
|
|
await insertTable(page, 1, 2);
|
|
await moveToEditorBeginning(page);
|
|
await moveRight(page, 3);
|
|
await deleteForward(page);
|
|
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">abc</span>
|
|
</p>
|
|
<table class="PlaygroundEditorTheme__table">
|
|
<colgroup>
|
|
<col style="width: 92px" />
|
|
<col style="width: 92px" />
|
|
</colgroup>
|
|
<tr>
|
|
<th
|
|
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
</th>
|
|
<th
|
|
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
</th>
|
|
</tr>
|
|
</table>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test(`Can't delete backward a Table`, async ({page, isPlainText}) => {
|
|
test.skip(isPlainText);
|
|
if (!IS_MAC) {
|
|
// Do Windows/Linux have equivalent shortcuts?
|
|
return;
|
|
}
|
|
await focusEditor(page);
|
|
await page.keyboard.type('abc');
|
|
await insertTable(page, 1, 2);
|
|
await moveToEditorEnd(page);
|
|
await page.keyboard.type('after');
|
|
await moveLeft(page, 'after'.length);
|
|
await deleteBackward(page);
|
|
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">abc</span>
|
|
</p>
|
|
<table class="PlaygroundEditorTheme__table">
|
|
<colgroup>
|
|
<col style="width: 92px" />
|
|
<col style="width: 92px" />
|
|
</colgroup>
|
|
<tr>
|
|
<th
|
|
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
</th>
|
|
<th
|
|
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
</th>
|
|
</tr>
|
|
</table>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">after</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Can delete block elements', async ({page, isPlainText}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await page.keyboard.type('# A');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('b');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<h1
|
|
class="PlaygroundEditorTheme__h1 PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">A</span>
|
|
</h1>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">b</span>
|
|
</p>
|
|
`,
|
|
);
|
|
await moveLeft(page, 2);
|
|
|
|
await deleteBackward(page);
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<h1 class="PlaygroundEditorTheme__h1">
|
|
<br />
|
|
</h1>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">b</span>
|
|
</p>
|
|
`,
|
|
);
|
|
|
|
await deleteBackward(page);
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph">
|
|
<br />
|
|
</p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">b</span>
|
|
</p>
|
|
`,
|
|
);
|
|
|
|
await deleteBackward(page);
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">b</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test(
|
|
'Can delete sibling elements forward',
|
|
{
|
|
tag: '@flaky',
|
|
},
|
|
async ({page, isPlainText}) => {
|
|
test.skip(isPlainText);
|
|
|
|
await focusEditor(page);
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('# Title');
|
|
await page.keyboard.press('ArrowUp');
|
|
await deleteForward(page);
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<h1
|
|
class="PlaygroundEditorTheme__h1 PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">Title</span>
|
|
</h1>
|
|
`,
|
|
);
|
|
},
|
|
);
|
|
|
|
test('Can adjust tripple click selection', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
}) => {
|
|
test.skip(isPlainText || isCollab);
|
|
|
|
await page.keyboard.type('Paragraph 1');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('Paragraph 2');
|
|
await page
|
|
.locator('div[contenteditable="true"] > p')
|
|
.first()
|
|
.click({clickCount: 3});
|
|
|
|
await click(page, '.block-controls');
|
|
await click(page, '.dropdown .item:has(.icon.h1)');
|
|
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<h1
|
|
class="PlaygroundEditorTheme__h1 PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">Paragraph 1</span>
|
|
</h1>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">Paragraph 2</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Can adjust tripple click selection with', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
}) => {
|
|
test.skip(isPlainText || isCollab);
|
|
|
|
await pasteFromClipboard(page, {
|
|
'text/html': `<p><a href="https://test.com">Hello</a>world</p><p>!</p>`,
|
|
});
|
|
|
|
await page
|
|
.locator('div[contenteditable="true"] > p')
|
|
.first()
|
|
.click({clickCount: 3});
|
|
|
|
await pressToggleBold(page);
|
|
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<a
|
|
class="PlaygroundEditorTheme__link PlaygroundEditorTheme__ltr"
|
|
dir="ltr"
|
|
href="https://test.com">
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold"
|
|
data-lexical-text="true">
|
|
Hello
|
|
</strong>
|
|
</a>
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold"
|
|
data-lexical-text="true">
|
|
world
|
|
</strong>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph">
|
|
<span data-lexical-text="true">!</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Select all from Node selection #4658', async ({page, isPlainText}) => {
|
|
// TODO selectAll is bad for Linux #4665
|
|
test.skip(isPlainText || IS_LINUX);
|
|
|
|
await insertYouTubeEmbed(page, YOUTUBE_SAMPLE_URL);
|
|
await page.keyboard.type('abcdefg');
|
|
await moveLeft(page, 'abcdefg'.length + 1);
|
|
|
|
await selectAll(page);
|
|
await page.keyboard.press('Backspace');
|
|
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Select all (DecoratorNode at start) #4670', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
// TODO selectAll is bad for Linux #4665
|
|
test.skip(isPlainText || IS_LINUX);
|
|
|
|
await insertYouTubeEmbed(page, YOUTUBE_SAMPLE_URL);
|
|
// Delete empty paragraph in front
|
|
await moveLeft(page, 2);
|
|
await page.keyboard.press('Backspace');
|
|
await moveRight(page, 2);
|
|
await page.keyboard.type('abcdefg');
|
|
|
|
await selectAll(page);
|
|
await page.keyboard.press('Backspace');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Can use block controls on selections including decorator nodes #5371', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
}) => {
|
|
test.skip(isPlainText || isCollab);
|
|
|
|
await page.keyboard.type('Some text');
|
|
await insertHorizontalRule(page);
|
|
await page.keyboard.type('More text');
|
|
await selectAll(page);
|
|
|
|
await click(page, '.block-controls');
|
|
await click(page, '.dropdown .icon.h1');
|
|
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<h1
|
|
class="PlaygroundEditorTheme__h1 PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">Some text</span>
|
|
</h1>
|
|
<hr
|
|
class="PlaygroundEditorTheme__hr PlaygroundEditorTheme__hrSelected"
|
|
contenteditable="false"
|
|
data-lexical-decorator="true" />
|
|
<h1
|
|
class="PlaygroundEditorTheme__h1 PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">More text</span>
|
|
</h1>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Can delete table node present at the end #5543', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
browserName,
|
|
legacyEvents,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
test.fixme(
|
|
legacyEvents && browserName === 'chromium' && IS_WINDOWS,
|
|
'Flaky on Windows + Chromium + legacy events',
|
|
);
|
|
|
|
await focusEditor(page);
|
|
await insertTable(page, 1, 2);
|
|
await page.keyboard.press('ArrowDown');
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowUp');
|
|
await page.keyboard.up('Shift');
|
|
await page.keyboard.press('Backspace');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Triple-clicking last cell in table should not select entire document', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
browserName,
|
|
legacyEvents,
|
|
}) => {
|
|
test.skip(isPlainText || isCollab);
|
|
|
|
await focusEditor(page);
|
|
await page.keyboard.type('Line1');
|
|
await insertTable(page, 1, 2);
|
|
|
|
const lastCell = page.locator(
|
|
'.PlaygroundEditorTheme__tableCell:last-child',
|
|
);
|
|
await lastCell.click();
|
|
const cellText = 'Foo';
|
|
await page.keyboard.type(cellText);
|
|
|
|
const lastCellText = lastCell.locator('span');
|
|
const tripleClickDelay = 50;
|
|
await lastCellText.click({clickCount: 3, delay: tripleClickDelay});
|
|
|
|
// Expect consistent behavior - select the clicked cell's content
|
|
const expectedSelection = createHumanReadableSelection(
|
|
'the full text of the last cell in the table',
|
|
{
|
|
anchorOffset: {desc: 'beginning of cell', value: 0},
|
|
anchorPath: [
|
|
{desc: 'index of table in root', value: 1},
|
|
{desc: 'first table row', value: 1},
|
|
{desc: 'second cell', value: 1},
|
|
{desc: 'first paragraph', value: 0},
|
|
{desc: 'first span', value: 0},
|
|
{desc: 'beginning of text', value: 0},
|
|
],
|
|
focusOffset: {desc: 'full text length', value: cellText.length},
|
|
focusPath: [
|
|
{desc: 'index of table in root', value: 1},
|
|
{desc: 'first table row', value: 1},
|
|
{desc: 'second cell', value: 1},
|
|
{desc: 'first paragraph', value: 0},
|
|
{desc: 'first span', value: 0},
|
|
{desc: 'beginning of text', value: 0},
|
|
],
|
|
},
|
|
);
|
|
|
|
await assertSelection(page, expectedSelection);
|
|
});
|
|
|
|
/**
|
|
* Dragging down from a table cell onto paragraph text below the table should select the entire table
|
|
* and select the paragraph text below the table.
|
|
*/
|
|
test('Selecting table cell then dragging to outside of table should select entire table', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
browserName,
|
|
legacyEvents,
|
|
}) => {
|
|
test.skip(isPlainText || isCollab);
|
|
|
|
await focusEditor(page);
|
|
await insertTable(page, 1, 2);
|
|
await moveToEditorEnd(page);
|
|
|
|
const endParagraphText = 'Some text';
|
|
await page.keyboard.type(endParagraphText);
|
|
|
|
const lastCell = page.locator(
|
|
'.PlaygroundEditorTheme__tableCell:last-child',
|
|
);
|
|
await lastCell.click();
|
|
await page.keyboard.type('Foo');
|
|
|
|
// Move the mouse to the last cell
|
|
await lastCell.hover();
|
|
await page.mouse.down();
|
|
// Move the mouse to the end of the document
|
|
await page.mouse.move(500, 500);
|
|
|
|
const expectedSelection = createHumanReadableSelection(
|
|
'the full table from beginning to the end of the text in the last cell',
|
|
{
|
|
anchorOffset: {desc: 'beginning of cell', value: 0},
|
|
anchorPath: [
|
|
{desc: 'index of table in root', value: 1},
|
|
{desc: 'first table row', value: 1},
|
|
{desc: 'first cell', value: 0},
|
|
],
|
|
focusOffset: {desc: 'full text length', value: endParagraphText.length},
|
|
focusPath: [
|
|
{desc: 'index of last paragraph', value: 2},
|
|
{desc: 'index of first span', value: 0},
|
|
{desc: 'index of text block', value: 0},
|
|
],
|
|
},
|
|
);
|
|
await assertSelection(page, expectedSelection);
|
|
});
|
|
|
|
test('Can persist the text format from the paragraph', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await pressToggleBold(page);
|
|
await page.keyboard.type('Line1');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('Line2');
|
|
await page.keyboard.press('ArrowUp');
|
|
await page.keyboard.type('Line3');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold"
|
|
data-lexical-text="true">
|
|
Line1
|
|
</strong>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold"
|
|
data-lexical-text="true">
|
|
Line3
|
|
</strong>
|
|
</p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold"
|
|
data-lexical-text="true">
|
|
Line2
|
|
</strong>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('toggle format at the start of paragraph to a different format persists the format', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await pressToggleBold(page);
|
|
await page.keyboard.type('Line1');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Enter');
|
|
await pressToggleItalic(page);
|
|
await page.keyboard.type('Line2');
|
|
await page.keyboard.press('ArrowUp');
|
|
await pressToggleBold(page);
|
|
await page.keyboard.type('Line3');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold"
|
|
data-lexical-text="true">
|
|
Line1
|
|
</strong>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">Line3</span>
|
|
</p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold PlaygroundEditorTheme__textItalic"
|
|
data-lexical-text="true">
|
|
Line2
|
|
</strong>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('formatting is persisted after deleting all nodes from the paragraph node', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await pressToggleBold(page);
|
|
await page.keyboard.type('Line1');
|
|
await page.keyboard.press('Enter');
|
|
await pressToggleBold(page);
|
|
await page.keyboard.type('Line2');
|
|
await selectPrevWord(page);
|
|
await page.keyboard.press('Backspace');
|
|
await page.keyboard.type('Line3');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<strong
|
|
class="PlaygroundEditorTheme__textBold"
|
|
data-lexical-text="true">
|
|
Line1
|
|
</strong>
|
|
</p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span data-lexical-text="true">Line3</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('Can persist the text style (color) from the paragraph', async ({
|
|
page,
|
|
isPlainText,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await click(page, '.color-picker');
|
|
await click(page, '.color-picker-basic-color > button');
|
|
await click(page, '.PlaygroundEditorTheme__paragraph');
|
|
await page.keyboard.type('Line1');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type('Line2');
|
|
await page.keyboard.press('ArrowUp');
|
|
await page.keyboard.type('Line3');
|
|
await assertHTML(
|
|
page,
|
|
html`
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span style="color: rgb(208, 2, 27)" data-lexical-text="true">
|
|
Line1
|
|
</span>
|
|
</p>
|
|
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span style="color: rgb(208, 2, 27)" data-lexical-text="true">
|
|
Line3
|
|
</span>
|
|
</p>
|
|
<p
|
|
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
|
|
dir="ltr">
|
|
<span style="color: rgb(208, 2, 27)" data-lexical-text="true">
|
|
Line2
|
|
</span>
|
|
</p>
|
|
`,
|
|
);
|
|
});
|
|
|
|
test('shift+arrowdown into a table selects the whole table', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
browserName,
|
|
legacyEvents,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
test.fixme(
|
|
browserName === 'firefox' || IS_LINUX || (legacyEvents && IS_WINDOWS),
|
|
);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
await moveToEditorBeginning(page);
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowDown');
|
|
await page.keyboard.up('Shift');
|
|
await assertSelection(page, {
|
|
anchorOffset: 0,
|
|
anchorPath: [0],
|
|
focusOffset: 1,
|
|
focusPath: [1, 2, 1],
|
|
});
|
|
});
|
|
|
|
test('shift+arrowup into a table selects the whole table', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
browserName,
|
|
legacyEvents,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
test.fixme(
|
|
browserName === 'firefox' || IS_LINUX || (legacyEvents && IS_WINDOWS),
|
|
);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
await moveToEditorEnd(page);
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowUp');
|
|
await page.keyboard.up('Shift');
|
|
await assertSelection(page, {
|
|
anchorOffset: 0,
|
|
anchorPath: [2],
|
|
focusOffset: 1,
|
|
focusPath: [1, 1, 0],
|
|
});
|
|
});
|
|
|
|
test(
|
|
'shift+arrowdown into a table, when the table is the last node, selects the whole table',
|
|
{tag: '@flaky'},
|
|
async ({page, isPlainText, isCollab, browserName, legacyEvents}) => {
|
|
test.skip(isPlainText);
|
|
test.fixme(browserName === 'chromium' && legacyEvents);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
await moveToEditorEnd(page);
|
|
await deleteBackward(page);
|
|
await moveToEditorBeginning(page);
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowDown');
|
|
await page.keyboard.up('Shift');
|
|
await assertSelection(page, {
|
|
anchorOffset: 0,
|
|
anchorPath: [0],
|
|
focusOffset: 1,
|
|
focusPath: [1, 2, 1],
|
|
});
|
|
},
|
|
);
|
|
|
|
test(
|
|
'shift+arrowup into a table, when the table is the first node, selects the whole table',
|
|
{tag: '@flaky'},
|
|
async ({page, isPlainText, isCollab, browserName, legacyEvents}) => {
|
|
test.skip(isPlainText);
|
|
test.fixme(browserName === 'chromium' && legacyEvents);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
await moveToEditorBeginning(page);
|
|
await deleteBackward(page);
|
|
await moveToEditorEnd(page);
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowUp');
|
|
await page.keyboard.up('Shift');
|
|
await assertSelection(page, {
|
|
anchorOffset: 0,
|
|
anchorPath: [1],
|
|
focusOffset: 1,
|
|
focusPath: [0, 1, 0],
|
|
});
|
|
},
|
|
);
|
|
|
|
test(
|
|
'shift+arrowdown into a table, when the table is the only node, selects the whole table',
|
|
{tag: '@flaky'},
|
|
async ({page, isPlainText, isCollab, legacyEvents, browserName}) => {
|
|
test.skip(isPlainText);
|
|
test.fixme(browserName === 'chromium' && legacyEvents);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
await moveToEditorBeginning(page);
|
|
await deleteBackward(page);
|
|
await moveToEditorEnd(page);
|
|
await deleteBackward(page);
|
|
await moveToEditorBeginning(page);
|
|
await moveUp(page, 1);
|
|
await assertSelection(page, {
|
|
anchorOffset: 0,
|
|
anchorPath: [],
|
|
focusOffset: 0,
|
|
focusPath: [],
|
|
});
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowDown');
|
|
await page.keyboard.up('Shift');
|
|
await assertTableSelectionCoordinates(page, {
|
|
anchor: {x: 0, y: 0},
|
|
focus: {x: 1, y: 1},
|
|
});
|
|
},
|
|
);
|
|
|
|
test('shift+arrowup into a table, when the table is the only node, selects the whole table', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
legacyEvents,
|
|
browserName,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
test.fixme(browserName === 'chromium' && legacyEvents);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
// delete the paragraph before the table
|
|
await moveToEditorBeginning(page);
|
|
await deleteBackward(page);
|
|
await moveToEditorEnd(page);
|
|
// delete the paragraph after the table
|
|
await deleteBackward(page);
|
|
await moveDown(page, 1);
|
|
await assertSelection(page, {
|
|
anchorOffset: 1,
|
|
anchorPath: [],
|
|
focusOffset: 1,
|
|
focusPath: [],
|
|
});
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowUp');
|
|
await page.keyboard.up('Shift');
|
|
await assertTableSelectionCoordinates(page, {
|
|
anchor: {x: 0, y: 0},
|
|
focus: {x: 1, y: 1},
|
|
});
|
|
});
|
|
|
|
test('shift+arrowdown into a table does not select element after', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
legacyEvents,
|
|
browserName,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
|
|
await moveToEditorEnd(page);
|
|
await page.keyboard.type('def');
|
|
|
|
await moveToEditorBeginning(page);
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowDown');
|
|
await page.keyboard.up('Shift');
|
|
await assertSelection(page, {
|
|
anchorOffset: 0,
|
|
anchorPath: [0],
|
|
focusOffset: 1,
|
|
focusPath: [1, 2, 1],
|
|
});
|
|
});
|
|
|
|
test('shift+arrowup into a table does not select element before', async ({
|
|
page,
|
|
isPlainText,
|
|
isCollab,
|
|
legacyEvents,
|
|
browserName,
|
|
}) => {
|
|
test.skip(isPlainText);
|
|
await focusEditor(page);
|
|
await insertTable(page, 2, 2);
|
|
await moveToEditorBeginning(page);
|
|
await page.keyboard.type('abc');
|
|
|
|
await moveToEditorEnd(page);
|
|
await page.keyboard.down('Shift');
|
|
await page.keyboard.press('ArrowUp');
|
|
await page.keyboard.up('Shift');
|
|
await assertSelection(page, {
|
|
anchorOffset: 0,
|
|
anchorPath: [2],
|
|
focusOffset: 1,
|
|
focusPath: [1, 1, 0],
|
|
});
|
|
});
|
|
});
|