mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-24 10:23:16 +08:00
Lexical: Adapted a range of further existing tests
This commit is contained in:
parent
ccd486f2a9
commit
787e06e3d8
|
@ -1,212 +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 {ListItemNode, ListNode} from '@lexical/list';
|
|
||||||
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
|
|
||||||
import {ContentEditable} from '@lexical/react/LexicalContentEditable';
|
|
||||||
import {LexicalErrorBoundary} from '@lexical/react/LexicalErrorBoundary';
|
|
||||||
import {ListPlugin} from '@lexical/react/LexicalListPlugin';
|
|
||||||
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
|
|
||||||
import {
|
|
||||||
INDENT_CONTENT_COMMAND,
|
|
||||||
LexicalEditor,
|
|
||||||
OUTDENT_CONTENT_COMMAND,
|
|
||||||
} from 'lexical';
|
|
||||||
import {
|
|
||||||
expectHtmlToBeEqual,
|
|
||||||
html,
|
|
||||||
TestComposer,
|
|
||||||
} from 'lexical/__tests__/utils';
|
|
||||||
import {createRoot, Root} from 'react-dom/client';
|
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
|
||||||
|
|
||||||
import {
|
|
||||||
INSERT_UNORDERED_LIST_COMMAND,
|
|
||||||
REMOVE_LIST_COMMAND,
|
|
||||||
} from '../../../../lexical-list/src/index';
|
|
||||||
|
|
||||||
describe('@lexical/list tests', () => {
|
|
||||||
let container: HTMLDivElement;
|
|
||||||
let reactRoot: Root;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
container = document.createElement('div');
|
|
||||||
reactRoot = createRoot(container);
|
|
||||||
document.body.appendChild(container);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
container.remove();
|
|
||||||
// @ts-ignore
|
|
||||||
container = null;
|
|
||||||
|
|
||||||
jest.restoreAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Shared instance across tests
|
|
||||||
let editor: LexicalEditor;
|
|
||||||
|
|
||||||
function Test(): JSX.Element {
|
|
||||||
function TestPlugin() {
|
|
||||||
// Plugin used just to get our hands on the Editor object
|
|
||||||
[editor] = useLexicalComposerContext();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TestComposer config={{nodes: [ListNode, ListItemNode], theme: {}}}>
|
|
||||||
<RichTextPlugin
|
|
||||||
contentEditable={<ContentEditable />}
|
|
||||||
placeholder={
|
|
||||||
<div className="editor-placeholder">Enter some text...</div>
|
|
||||||
}
|
|
||||||
ErrorBoundary={LexicalErrorBoundary}
|
|
||||||
/>
|
|
||||||
<TestPlugin />
|
|
||||||
<ListPlugin />
|
|
||||||
</TestComposer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
test('Toggle an empty list on/off', async () => {
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
reactRoot.render(<Test key="MegaSeeds, Morty!" />);
|
|
||||||
});
|
|
||||||
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
await editor.update(() => {
|
|
||||||
editor.focus();
|
|
||||||
editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
expectHtmlToBeEqual(
|
|
||||||
container.innerHTML,
|
|
||||||
html`
|
|
||||||
<div
|
|
||||||
contenteditable="true"
|
|
||||||
role="textbox"
|
|
||||||
spellcheck="true"
|
|
||||||
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
|
|
||||||
data-lexical-editor="true">
|
|
||||||
<ul>
|
|
||||||
<li value="1">
|
|
||||||
<br />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
await editor.update(() => {
|
|
||||||
editor.focus();
|
|
||||||
editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
expectHtmlToBeEqual(
|
|
||||||
container.innerHTML,
|
|
||||||
html`
|
|
||||||
<div
|
|
||||||
contenteditable="true"
|
|
||||||
role="textbox"
|
|
||||||
spellcheck="true"
|
|
||||||
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
|
|
||||||
data-lexical-editor="true">
|
|
||||||
<p>
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="editor-placeholder">Enter some text...</div>
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Can create a list and indent/outdent it', async () => {
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
reactRoot.render(<Test key="MegaSeeds, Morty!" />);
|
|
||||||
});
|
|
||||||
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
await editor.update(() => {
|
|
||||||
editor.focus();
|
|
||||||
editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
expectHtmlToBeEqual(
|
|
||||||
container.innerHTML,
|
|
||||||
html`
|
|
||||||
<div
|
|
||||||
contenteditable="true"
|
|
||||||
role="textbox"
|
|
||||||
spellcheck="true"
|
|
||||||
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
|
|
||||||
data-lexical-editor="true">
|
|
||||||
<ul>
|
|
||||||
<li value="1">
|
|
||||||
<br />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
await editor.update(() => {
|
|
||||||
editor.focus();
|
|
||||||
editor.dispatchCommand(INDENT_CONTENT_COMMAND, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
expectHtmlToBeEqual(
|
|
||||||
container.innerHTML,
|
|
||||||
html`
|
|
||||||
<div
|
|
||||||
contenteditable="true"
|
|
||||||
role="textbox"
|
|
||||||
spellcheck="true"
|
|
||||||
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
|
|
||||||
data-lexical-editor="true">
|
|
||||||
<ul>
|
|
||||||
<li value="1">
|
|
||||||
<ul>
|
|
||||||
<li value="1"><br /></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await ReactTestUtils.act(async () => {
|
|
||||||
await editor.update(() => {
|
|
||||||
editor.focus();
|
|
||||||
editor.dispatchCommand(OUTDENT_CONTENT_COMMAND, undefined);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
expectHtmlToBeEqual(
|
|
||||||
container.innerHTML,
|
|
||||||
html`
|
|
||||||
<div
|
|
||||||
contenteditable="true"
|
|
||||||
role="textbox"
|
|
||||||
spellcheck="true"
|
|
||||||
style="user-select: text; white-space: pre-wrap; word-break: break-word;"
|
|
||||||
data-lexical-editor="true">
|
|
||||||
<ul>
|
|
||||||
<li value="1">
|
|
||||||
<br />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
File diff suppressed because one or more lines are too long
|
@ -89,7 +89,7 @@ export function initializeUnitTest(
|
||||||
document.body.appendChild(testEnv.container);
|
document.body.appendChild(testEnv.container);
|
||||||
|
|
||||||
const editorEl = document.createElement('div');
|
const editorEl = document.createElement('div');
|
||||||
editorEl.contentEditable = 'true';
|
editorEl.setAttribute('contenteditable', 'true');
|
||||||
testEnv.container.append(editorEl);
|
testEnv.container.append(editorEl);
|
||||||
|
|
||||||
const lexicalEditor = createTestEditor(editorConfig);
|
const lexicalEditor = createTestEditor(editorConfig);
|
||||||
|
|
|
@ -16,10 +16,6 @@ import {
|
||||||
LexicalNode,
|
LexicalNode,
|
||||||
TextNode,
|
TextNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import * as React from 'react';
|
|
||||||
import {createRef, useEffect} from 'react';
|
|
||||||
import {createRoot} from 'react-dom/client';
|
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
$createTestElementNode,
|
$createTestElementNode,
|
||||||
|
@ -44,34 +40,20 @@ describe('LexicalElementNode tests', () => {
|
||||||
|
|
||||||
async function update(fn: () => void) {
|
async function update(fn: () => void) {
|
||||||
editor.update(fn);
|
editor.update(fn);
|
||||||
|
editor.commitUpdates();
|
||||||
return Promise.resolve().then();
|
return Promise.resolve().then();
|
||||||
}
|
}
|
||||||
|
|
||||||
function useLexicalEditor(rootElementRef: React.RefObject<HTMLDivElement>) {
|
|
||||||
const editor = React.useMemo(() => createTestEditor(), []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const rootElement = rootElementRef.current;
|
|
||||||
editor.setRootElement(rootElement);
|
|
||||||
}, [rootElementRef, editor]);
|
|
||||||
|
|
||||||
return editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
let editor: LexicalEditor;
|
let editor: LexicalEditor;
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const ref = createRef<HTMLDivElement>();
|
const root = document.createElement('div');
|
||||||
|
root.setAttribute('contenteditable', 'true');
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.appendChild(root);
|
||||||
|
|
||||||
function TestBase() {
|
editor = createTestEditor();
|
||||||
editor = useLexicalEditor(ref);
|
editor.setRootElement(root);
|
||||||
|
|
||||||
return <div ref={ref} contentEditable={true} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
createRoot(container).render(<TestBase />);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Insert initial block
|
// Insert initial block
|
||||||
await update(() => {
|
await update(() => {
|
|
@ -11,7 +11,6 @@ import {
|
||||||
$insertDataTransferForRichText,
|
$insertDataTransferForRichText,
|
||||||
} from '@lexical/clipboard';
|
} from '@lexical/clipboard';
|
||||||
import {$createListItemNode, $createListNode} from '@lexical/list';
|
import {$createListItemNode, $createListNode} from '@lexical/list';
|
||||||
import {registerTabIndentation} from '@lexical/react/LexicalTabIndentationPlugin';
|
|
||||||
import {$createHeadingNode, registerRichText} from '@lexical/rich-text';
|
import {$createHeadingNode, registerRichText} from '@lexical/rich-text';
|
||||||
import {
|
import {
|
||||||
$createParagraphNode,
|
$createParagraphNode,
|
||||||
|
@ -112,134 +111,6 @@ describe('LexicalTabNode tests', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('element indents when selection at the start of the block', async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
registerRichText(editor);
|
|
||||||
registerTabIndentation(editor);
|
|
||||||
await editor.update(() => {
|
|
||||||
const selection = $getSelection()!;
|
|
||||||
selection.insertText('foo');
|
|
||||||
$getRoot().selectStart();
|
|
||||||
});
|
|
||||||
await editor.dispatchCommand(
|
|
||||||
KEY_TAB_COMMAND,
|
|
||||||
new KeyboardEvent('keydown'),
|
|
||||||
);
|
|
||||||
expect(testEnv.innerHTML).toBe(
|
|
||||||
'<p dir="ltr" style="padding-inline-start: calc(1 * 40px);"><span data-lexical-text="true">foo</span></p>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('elements indent when selection spans across multiple blocks', async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
registerRichText(editor);
|
|
||||||
registerTabIndentation(editor);
|
|
||||||
await editor.update(() => {
|
|
||||||
const root = $getRoot();
|
|
||||||
const paragraph = root.getFirstChild();
|
|
||||||
invariant($isElementNode(paragraph));
|
|
||||||
const heading = $createHeadingNode('h1');
|
|
||||||
const list = $createListNode('number');
|
|
||||||
const listItem = $createListItemNode();
|
|
||||||
const paragraphText = $createTextNode('foo');
|
|
||||||
const headingText = $createTextNode('bar');
|
|
||||||
const listItemText = $createTextNode('xyz');
|
|
||||||
root.append(heading, list);
|
|
||||||
paragraph.append(paragraphText);
|
|
||||||
heading.append(headingText);
|
|
||||||
list.append(listItem);
|
|
||||||
listItem.append(listItemText);
|
|
||||||
const selection = $createRangeSelection();
|
|
||||||
selection.focus.set(paragraphText.getKey(), 1, 'text');
|
|
||||||
selection.anchor.set(listItemText.getKey(), 1, 'text');
|
|
||||||
$setSelection(selection);
|
|
||||||
});
|
|
||||||
await editor.dispatchCommand(
|
|
||||||
KEY_TAB_COMMAND,
|
|
||||||
new KeyboardEvent('keydown'),
|
|
||||||
);
|
|
||||||
expect(testEnv.innerHTML).toBe(
|
|
||||||
'<p dir="ltr" style="padding-inline-start: calc(1 * 40px);"><span data-lexical-text="true">foo</span></p><h1 dir="ltr" style="padding-inline-start: calc(1 * 40px);"><span data-lexical-text="true">bar</span></h1><ol><li value="1"><ol><li value="1" dir="ltr"><span data-lexical-text="true">xyz</span></li></ol></li></ol>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('element tabs when selection is not at the start (1)', async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
registerRichText(editor);
|
|
||||||
registerTabIndentation(editor);
|
|
||||||
await editor.update(() => {
|
|
||||||
$getSelection()!.insertText('foo');
|
|
||||||
});
|
|
||||||
await editor.dispatchCommand(
|
|
||||||
KEY_TAB_COMMAND,
|
|
||||||
new KeyboardEvent('keydown'),
|
|
||||||
);
|
|
||||||
expect(testEnv.innerHTML).toBe(
|
|
||||||
'<p dir="ltr"><span data-lexical-text="true">foo</span><span data-lexical-text="true">\t</span></p>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('element tabs when selection is not at the start (2)', async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
registerRichText(editor);
|
|
||||||
registerTabIndentation(editor);
|
|
||||||
await editor.update(() => {
|
|
||||||
$getSelection()!.insertText('foo');
|
|
||||||
const textNode = $getRoot().getLastDescendant();
|
|
||||||
invariant($isTextNode(textNode));
|
|
||||||
textNode.select(1, 1);
|
|
||||||
});
|
|
||||||
await editor.dispatchCommand(
|
|
||||||
KEY_TAB_COMMAND,
|
|
||||||
new KeyboardEvent('keydown'),
|
|
||||||
);
|
|
||||||
expect(testEnv.innerHTML).toBe(
|
|
||||||
'<p dir="ltr"><span data-lexical-text="true">f</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">oo</span></p>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('element tabs when selection is not at the start (3)', async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
registerRichText(editor);
|
|
||||||
registerTabIndentation(editor);
|
|
||||||
await editor.update(() => {
|
|
||||||
$getSelection()!.insertText('foo');
|
|
||||||
const textNode = $getRoot().getLastDescendant();
|
|
||||||
invariant($isTextNode(textNode));
|
|
||||||
textNode.select(1, 2);
|
|
||||||
});
|
|
||||||
await editor.dispatchCommand(
|
|
||||||
KEY_TAB_COMMAND,
|
|
||||||
new KeyboardEvent('keydown'),
|
|
||||||
);
|
|
||||||
expect(testEnv.innerHTML).toBe(
|
|
||||||
'<p dir="ltr"><span data-lexical-text="true">f</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">o</span></p>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('elements tabs when selection is not at the start and overlaps another tab', async () => {
|
|
||||||
const {editor} = testEnv;
|
|
||||||
registerRichText(editor);
|
|
||||||
registerTabIndentation(editor);
|
|
||||||
await editor.update(() => {
|
|
||||||
$getSelection()!.insertRawText('hello\tworld');
|
|
||||||
const root = $getRoot();
|
|
||||||
const firstTextNode = root.getFirstDescendant();
|
|
||||||
const lastTextNode = root.getLastDescendant();
|
|
||||||
const selection = $createRangeSelection();
|
|
||||||
selection.anchor.set(firstTextNode!.getKey(), 'hell'.length, 'text');
|
|
||||||
selection.focus.set(lastTextNode!.getKey(), 'wo'.length, 'text');
|
|
||||||
$setSelection(selection);
|
|
||||||
});
|
|
||||||
await editor.dispatchCommand(
|
|
||||||
KEY_TAB_COMMAND,
|
|
||||||
new KeyboardEvent('keydown'),
|
|
||||||
);
|
|
||||||
expect(testEnv.innerHTML).toBe(
|
|
||||||
'<p dir="ltr"><span data-lexical-text="true">hell</span><span data-lexical-text="true">\t</span><span data-lexical-text="true">rld</span></p>',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('can type between two (leaf nodes) canInsertBeforeAfter false', async () => {
|
test('can type between two (leaf nodes) canInsertBeforeAfter false', async () => {
|
||||||
const {editor} = testEnv;
|
const {editor} = testEnv;
|
||||||
await editor.update(() => {
|
await editor.update(() => {
|
|
@ -21,9 +21,6 @@ import {
|
||||||
TextModeType,
|
TextModeType,
|
||||||
TextNode,
|
TextNode,
|
||||||
} from 'lexical';
|
} from 'lexical';
|
||||||
import * as React from 'react';
|
|
||||||
import {createRef, useEffect, useMemo} from 'react';
|
|
||||||
import {createRoot} from 'react-dom/client';
|
|
||||||
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
import * as ReactTestUtils from 'lexical/shared/react-test-utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -78,35 +75,20 @@ describe('LexicalTextNode tests', () => {
|
||||||
|
|
||||||
async function update(fn: () => void) {
|
async function update(fn: () => void) {
|
||||||
editor.update(fn);
|
editor.update(fn);
|
||||||
|
editor.commitUpdates();
|
||||||
return Promise.resolve().then();
|
return Promise.resolve().then();
|
||||||
}
|
}
|
||||||
|
|
||||||
function useLexicalEditor(rootElementRef: React.RefObject<HTMLDivElement>) {
|
|
||||||
const editor = useMemo(() => createTestEditor(editorConfig), []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const rootElement = rootElementRef.current;
|
|
||||||
|
|
||||||
editor.setRootElement(rootElement);
|
|
||||||
}, [rootElementRef, editor]);
|
|
||||||
|
|
||||||
return editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
let editor: LexicalEditor;
|
let editor: LexicalEditor;
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const ref = createRef<HTMLDivElement>();
|
const root = document.createElement('div');
|
||||||
|
root.setAttribute('contenteditable', 'true');
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.appendChild(root);
|
||||||
|
|
||||||
function TestBase() {
|
editor = createTestEditor();
|
||||||
editor = useLexicalEditor(ref);
|
editor.setRootElement(root);
|
||||||
|
|
||||||
return <div ref={ref} contentEditable={true} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReactTestUtils.act(() => {
|
|
||||||
createRoot(container).render(<TestBase />);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Insert initial block
|
// Insert initial block
|
||||||
await update(() => {
|
await update(() => {
|
|
@ -112,7 +112,6 @@ describe('HTML', () => {
|
||||||
ListNode,
|
ListNode,
|
||||||
ListItemNode,
|
ListItemNode,
|
||||||
QuoteNode,
|
QuoteNode,
|
||||||
CodeNode,
|
|
||||||
LinkNode,
|
LinkNode,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user