Created
September 16, 2023 14:17
-
-
Save fantactuka/cf83eb3ce3999941baf6f31207a25559 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 type {LexicalEditor, LexicalNode} from 'lexical'; | |
import {$generateHtmlFromNodes} from '@lexical/html'; | |
import {$createLinkNode} from '@lexical/link'; | |
import {$createListItemNode, $createListNode} from '@lexical/list'; | |
import {$createHeadingNode} from '@lexical/rich-text'; | |
import { | |
$createParagraphNode, | |
$createTextNode, | |
$getRoot, | |
$getSelection, | |
$isRangeSelection, | |
} from 'lexical'; | |
import { | |
$createTestDecoratorNode, | |
createTestEditor, | |
} from 'lexical/src/__tests__/utils'; | |
type TestCase = { | |
name: string; | |
insert: () => Array<LexicalNode>; | |
expectation: string; | |
}; | |
describe('LexicalSelection#insertNodes', () => { | |
let editor: LexicalEditor; | |
const update = (updateFn) => { | |
editor.update(updateFn, {discrete: true}); | |
}; | |
const toHtml = (): string => { | |
return editor.getEditorState().read(() => $generateHtmlFromNodes(editor)); | |
}; | |
beforeEach(async () => { | |
editor = createTestEditor(); | |
editor._headless = true; | |
}); | |
// Creates test that would insert provided nodes into current | |
// selection and match resulted state (converted to html-like string) | |
// with expected one | |
const createTest = (testCaseFn: (string) => TestCase) => { | |
let itFn = it; | |
const runner = (expectation: string) => { | |
const testCase = testCaseFn(expectation); | |
itFn(testCase.name, async () => { | |
await Promise.resolve(); | |
update(() => { | |
const selection = $getSelection(); | |
if (!$isRangeSelection(selection)) { | |
throw new Error('Expected range selection'); | |
} | |
selection.insertNodes(testCase.insert()); | |
}); | |
expect(toHtml()).toEqual(testCase.expectation); | |
}); | |
}; | |
runner.only = (expectation: string) => { | |
// eslint-disable-next-line no-only-tests/no-only-tests | |
itFn = it.only; | |
runner(expectation); | |
itFn = it; | |
}; | |
runner.skip = (expectation: string) => { | |
// eslint-disable-next-line no-only-tests/no-only-tests | |
itFn = it.skip; | |
runner(expectation); | |
itFn = it; | |
}; | |
return runner; | |
}; | |
const testTextNode = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [$createTextNode('inserted text')]; | |
}, | |
name: 'insert text node', | |
}; | |
}); | |
const testParagraphNode = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [ | |
$createParagraphNode().append($createTextNode('inserted text')), | |
]; | |
}, | |
name: 'insert paragraph element node', | |
}; | |
}); | |
const testNonParagraphNode = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [ | |
$createHeadingNode('h1').append($createTextNode('inserted text')), | |
]; | |
}, | |
name: 'insert non-paragraph element node', | |
}; | |
}); | |
const testInlineElement = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [ | |
$createLinkNode('https://lexical.dev').append( | |
$createTextNode('inserted text'), | |
), | |
]; | |
}, | |
name: 'insert inline element', | |
}; | |
}); | |
const testInlineElementAndText = createTest( | |
(expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [ | |
$createLinkNode('https://lexical.dev').append( | |
$createTextNode('inserted'), | |
), | |
$createTextNode('text'), | |
]; | |
}, | |
name: 'insert inline element and text', | |
}; | |
}, | |
); | |
const testDecorator = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [$createTestDecoratorNode()]; | |
}, | |
name: 'insert decorator', | |
}; | |
}); | |
const testDecoratorAndText = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [$createTestDecoratorNode(), $createTextNode('inserted text')]; | |
}, | |
name: 'insert decorator and text', | |
}; | |
}); | |
const testDecoratorAndInline = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [ | |
$createTestDecoratorNode(), | |
$createLinkNode('https://lexical.dev').append( | |
$createTextNode('inserted text'), | |
), | |
]; | |
}, | |
name: 'insert decorator and inline', | |
}; | |
}); | |
const testList = createTest((expectation: string): TestCase => { | |
return { | |
expectation, | |
insert() { | |
return [ | |
$createListNode('bullet').append( | |
$createListItemNode().append($createTextNode('item 1')), | |
$createListItemNode().append($createTextNode('item 2')), | |
$createListItemNode().append( | |
$createListNode('bullet').append( | |
$createListItemNode().append($createTextNode('item 2.1')), | |
), | |
), | |
), | |
]; | |
}, | |
name: 'insert list', | |
}; | |
}); | |
describe('inserting into empty element', () => { | |
beforeEach(() => { | |
update(() => { | |
$getRoot().append($createParagraphNode()).selectEnd(); | |
}); | |
}); | |
testTextNode('<p><span>inserted text</span></p>'); | |
testParagraphNode('<p><span>inserted text</span></p>'); | |
testNonParagraphNode('<h1><span>inserted text</span></h1>'); | |
testInlineElement( | |
'<p><a href="https://lexical.dev"><span>inserted text</span></a></p>', | |
); | |
testInlineElementAndText( | |
'<p><a href="https://lexical.dev"><span>inserted</span></a><span>text</span></p>', | |
); | |
testDecorator('<p><test-decorator></test-decorator></p>'); | |
testDecoratorAndText( | |
'<p><test-decorator></test-decorator><span>inserted text</span></p>', | |
); | |
testDecoratorAndInline( | |
'<p><test-decorator></test-decorator><a href="https://lexical.dev"><span>inserted text</span></a></p>', | |
); | |
testList( | |
'<ul><li value="1"><span>item 1</span></li><li value="2"><span>item 2</span></li><li value="3"><ul><li value="1"><span>item 2.1</span></li></ul></li></ul>', | |
); | |
}); | |
describe('inserting into element with text', () => { | |
beforeEach(() => { | |
update(() => { | |
$getRoot() | |
.append( | |
$createParagraphNode().append($createTextNode('text before ')), | |
) | |
.selectEnd(); | |
}); | |
}); | |
testTextNode('<p><span>text before inserted text</span></p>'); | |
testParagraphNode('<p><span>text before inserted text</span></p>'); | |
testNonParagraphNode('<p><span>text before inserted text</span></p>'); | |
testInlineElement( | |
'<p><span>text before </span><a href="https://lexical.dev"><span>inserted text</span></a></p>', | |
); | |
testInlineElementAndText( | |
'<p><span>text before </span><a href="https://lexical.dev"><span>inserted</span></a><span>text</span></p>', | |
); | |
testDecorator( | |
'<p><span>text before </span><test-decorator></test-decorator></p>', | |
); | |
testDecoratorAndText( | |
'<p><span>text before </span><test-decorator></test-decorator><span>inserted text</span></p>', | |
); | |
testDecoratorAndInline( | |
'<p><span>text before </span><test-decorator></test-decorator><a href="https://lexical.dev"><span>inserted text</span></a></p>', | |
); | |
testList( | |
'<p><span>text before </span></p><ul><li value="1"><span>item 1</span></li><li value="2"><span>item 2</span></li><li><ul><li value="1"><span>item 2.1</span></li></ul></li></ul>', | |
); | |
}); | |
describe('inserting into inline element', () => { | |
beforeEach(() => { | |
update(() => { | |
const textNode = $createTextNode('beforeafter'); | |
$getRoot().append( | |
$createParagraphNode().append( | |
$createLinkNode('https://lexical.dev').append(textNode), | |
), | |
); | |
textNode.select(6, 6); | |
}); | |
}); | |
testTextNode( | |
'<p><a href="https://lexical.dev"><span>beforeinserted textafter</span></a></p>', | |
); | |
testParagraphNode( | |
'<p><a href="https://lexical.dev"><span>beforeinserted textafter</span></a></p>', | |
); | |
testNonParagraphNode( | |
'<p><a href="https://lexical.dev"><span>beforeinserted textafter</span></a></p>', | |
); | |
testInlineElement( | |
'<p><a href="https://lexical.dev"><span>before</span></a><a href="https://lexical.dev"><span>inserted text</span></a><a href="https://lexical.dev"><span>after</span></a></p>', | |
); | |
testInlineElementAndText( | |
'<p><a href="https://lexical.dev"><span>before</span></a><a href="https://lexical.dev"><span>inserted</span></a><span>textundefined</span></p>', | |
); | |
testDecorator( | |
'<p><a href="https://lexical.dev"><span>before</span><test-decorator></test-decorator><span>after</span></a></p>', | |
); | |
testDecoratorAndText( | |
'<p><a href="https://lexical.dev"><span>before</span><test-decorator></test-decorator><span>inserted textafter</span></a></p>', | |
); | |
testDecoratorAndInline( | |
'<p><a href="https://lexical.dev"><span>before</span><test-decorator></test-decorator><span>inserted textafter</span></a></p>', | |
); | |
testList( | |
'<p><a href="https://lexical.dev">before</a></p>' + | |
'<ul><li>item 1</li><li>item 2</li><li><ul><li>item 2.1</li></ul></li></ul>' + | |
'<p><a href="https://lexical.dev"><span>after</span></a></p>', | |
); | |
}); | |
describe('inserting after inline element', () => { | |
beforeEach(() => { | |
update(() => { | |
const textAfter = $createTextNode('after'); | |
$getRoot().append( | |
$createParagraphNode().append( | |
$createTextNode('before'), | |
$createLinkNode('https://lexical.dev').append( | |
$createTextNode('inline'), | |
), | |
textAfter, | |
), | |
); | |
textAfter.select(0, 0); | |
}); | |
}); | |
testTextNode( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><span>inserted textafter</span></p>', | |
); | |
testParagraphNode( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><span>inserted textafter</span></p>', | |
); | |
testNonParagraphNode( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><span>inserted textafter</span></p>', | |
); | |
testInlineElement( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><a href="https://lexical.dev"><span>inserted text</span></a><span>after</span></p>', | |
); | |
testInlineElementAndText( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><a href="https://lexical.dev"><span>inserted</span></a><span>textafter</span></p>', | |
); | |
testDecorator( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><test-decorator></test-decorator><span>after</span></p>', | |
); | |
testDecoratorAndText( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><test-decorator></test-decorator><span>inserted textafter</span></p>', | |
); | |
testDecoratorAndInline( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a><test-decorator></test-decorator><a href="https://lexical.dev"><span>inserted text</span></a>after</p>', | |
); | |
testList( | |
'<p><span>before</span><a href="https://lexical.dev"><span>inline</span></a>after</p>', | |
); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment