]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/utils/__tests__/unit/LexicalNodeHelpers.test.ts
82d2dddf88da9e5b24dab303c5b00922ba51b925
[bookstack] / resources / js / wysiwyg / lexical / utils / __tests__ / unit / LexicalNodeHelpers.test.ts
1 /**
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  *
7  */
8
9 import {
10   $createParagraphNode,
11   $createTextNode,
12   $getNodeByKey,
13   $getRoot,
14   $isElementNode,
15   LexicalEditor,
16   NodeKey,
17 } from 'lexical';
18 import {
19   $createTestElementNode,
20   initializeUnitTest,
21   invariant,
22 } from 'lexical/src/__tests__/utils';
23
24 import {$dfs} from '../..';
25
26 describe('LexicalNodeHelpers tests', () => {
27   initializeUnitTest((testEnv) => {
28     /**
29      *               R
30      *        P1            P2
31      *     B1     B2     T4 T5 B3
32      *     T1   T2 T3          T6
33      *
34      *  DFS: R, P1, B1, T1, B2, T2, T3, P2, T4, T5, B3, T6
35      */
36     test('DFS node order', async () => {
37       const editor: LexicalEditor = testEnv.editor;
38
39       let expectedKeys: Array<{
40         depth: number;
41         node: NodeKey;
42       }> = [];
43
44       await editor.update(() => {
45         const root = $getRoot();
46
47         const paragraph1 = $createParagraphNode();
48         const paragraph2 = $createParagraphNode();
49
50         const block1 = $createTestElementNode();
51         const block2 = $createTestElementNode();
52         const block3 = $createTestElementNode();
53
54         const text1 = $createTextNode('text1');
55         const text2 = $createTextNode('text2');
56         const text3 = $createTextNode('text3');
57         const text4 = $createTextNode('text4');
58         const text5 = $createTextNode('text5');
59         const text6 = $createTextNode('text6');
60
61         root.append(paragraph1, paragraph2);
62         paragraph1.append(block1, block2);
63         paragraph2.append(text4, text5);
64
65         text5.toggleFormat('bold'); // Prevent from merging with text 4
66
67         paragraph2.append(block3);
68         block1.append(text1);
69         block2.append(text2, text3);
70
71         text3.toggleFormat('bold'); // Prevent from merging with text2
72
73         block3.append(text6);
74
75         expectedKeys = [
76           {
77             depth: 0,
78             node: root.getKey(),
79           },
80           {
81             depth: 1,
82             node: paragraph1.getKey(),
83           },
84           {
85             depth: 2,
86             node: block1.getKey(),
87           },
88           {
89             depth: 3,
90             node: text1.getKey(),
91           },
92           {
93             depth: 2,
94             node: block2.getKey(),
95           },
96           {
97             depth: 3,
98             node: text2.getKey(),
99           },
100           {
101             depth: 3,
102             node: text3.getKey(),
103           },
104           {
105             depth: 1,
106             node: paragraph2.getKey(),
107           },
108           {
109             depth: 2,
110             node: text4.getKey(),
111           },
112           {
113             depth: 2,
114             node: text5.getKey(),
115           },
116           {
117             depth: 2,
118             node: block3.getKey(),
119           },
120           {
121             depth: 3,
122             node: text6.getKey(),
123           },
124         ];
125       });
126
127       editor.getEditorState().read(() => {
128         const expectedNodes = expectedKeys.map(({depth, node: nodeKey}) => ({
129           depth,
130           node: $getNodeByKey(nodeKey)!.getLatest(),
131         }));
132
133         const first = expectedNodes[0];
134         const second = expectedNodes[1];
135         const last = expectedNodes[expectedNodes.length - 1];
136         const secondToLast = expectedNodes[expectedNodes.length - 2];
137
138         expect($dfs(first.node, last.node)).toEqual(expectedNodes);
139         expect($dfs(second.node, secondToLast.node)).toEqual(
140           expectedNodes.slice(1, expectedNodes.length - 1),
141         );
142         expect($dfs()).toEqual(expectedNodes);
143         expect($dfs($getRoot())).toEqual(expectedNodes);
144       });
145     });
146
147     test('DFS triggers getLatest()', async () => {
148       const editor: LexicalEditor = testEnv.editor;
149
150       let rootKey: string;
151       let paragraphKey: string;
152       let block1Key: string;
153       let block2Key: string;
154
155       await editor.update(() => {
156         const root = $getRoot();
157
158         const paragraph = $createParagraphNode();
159         const block1 = $createTestElementNode();
160         const block2 = $createTestElementNode();
161
162         rootKey = root.getKey();
163         paragraphKey = paragraph.getKey();
164         block1Key = block1.getKey();
165         block2Key = block2.getKey();
166
167         root.append(paragraph);
168         paragraph.append(block1, block2);
169       });
170
171       await editor.update(() => {
172         const root = $getNodeByKey(rootKey);
173         const paragraph = $getNodeByKey(paragraphKey);
174         const block1 = $getNodeByKey(block1Key);
175         const block2 = $getNodeByKey(block2Key);
176
177         const block3 = $createTestElementNode();
178         invariant($isElementNode(block1));
179
180         block1.append(block3);
181
182         expect($dfs(root!)).toEqual([
183           {
184             depth: 0,
185             node: root!.getLatest(),
186           },
187           {
188             depth: 1,
189             node: paragraph!.getLatest(),
190           },
191           {
192             depth: 2,
193             node: block1.getLatest(),
194           },
195           {
196             depth: 3,
197             node: block3.getLatest(),
198           },
199           {
200             depth: 2,
201             node: block2!.getLatest(),
202           },
203         ]);
204       });
205     });
206
207     test('DFS of empty ParagraphNode returns only itself', async () => {
208       const editor: LexicalEditor = testEnv.editor;
209
210       let paragraphKey: string;
211
212       await editor.update(() => {
213         const root = $getRoot();
214
215         const paragraph = $createParagraphNode();
216         const paragraph2 = $createParagraphNode();
217         const text = $createTextNode('test');
218
219         paragraphKey = paragraph.getKey();
220
221         paragraph2.append(text);
222         root.append(paragraph, paragraph2);
223       });
224       await editor.update(() => {
225         const paragraph = $getNodeByKey(paragraphKey)!;
226
227         expect($dfs(paragraph ?? undefined)).toEqual([
228           {
229             depth: 1,
230             node: paragraph?.getLatest(),
231           },
232         ]);
233       });
234     });
235   });
236 });