]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/html/__tests__/unit/LexicalHtml.test.ts
a4e2d23138952b55d4853b76e8a6f01911db0103
[bookstack] / resources / js / wysiwyg / lexical / html / __tests__ / unit / LexicalHtml.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 //@ts-ignore-next-line
10 import type {RangeSelection} from 'lexical';
11
12 import {createHeadlessEditor} from '@lexical/headless';
13 import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html';
14 import {LinkNode} from '@lexical/link';
15 import {ListItemNode, ListNode} from '@lexical/list';
16 import {
17   $createParagraphNode,
18   $createRangeSelection,
19   $createTextNode,
20   $getRoot,
21 } from 'lexical';
22 import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
23 import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
24
25 describe('HTML', () => {
26   type Input = Array<{
27     name: string;
28     html: string;
29     initializeEditorState: () => void;
30   }>;
31
32   const HTML_SERIALIZE: Input = [
33     {
34       html: '<p><br></p>',
35       initializeEditorState: () => {
36         $getRoot().append($createParagraphNode());
37       },
38       name: 'Empty editor state',
39     },
40   ];
41   for (const {name, html, initializeEditorState} of HTML_SERIALIZE) {
42     test(`[Lexical -> HTML]: ${name}`, () => {
43       const editor = createHeadlessEditor({
44         nodes: [
45           HeadingNode,
46           ListNode,
47           ListItemNode,
48           QuoteNode,
49           LinkNode,
50         ],
51       });
52
53       editor.update(initializeEditorState, {
54         discrete: true,
55       });
56
57       expect(
58         editor.getEditorState().read(() => $generateHtmlFromNodes(editor)),
59       ).toBe(html);
60     });
61   }
62
63   test(`[Lexical -> HTML]: Use provided selection`, () => {
64     const editor = createHeadlessEditor({
65       nodes: [
66         HeadingNode,
67         ListNode,
68         ListItemNode,
69         QuoteNode,
70         LinkNode,
71       ],
72     });
73
74     let selection: RangeSelection | null = null;
75
76     editor.update(
77       () => {
78         const root = $getRoot();
79         const p1 = $createParagraphNode();
80         const text1 = $createTextNode('Hello');
81         p1.append(text1);
82         const p2 = $createParagraphNode();
83         const text2 = $createTextNode('World');
84         p2.append(text2);
85         root.append(p1).append(p2);
86         // Root
87         // - ParagraphNode
88         // -- TextNode "Hello"
89         // - ParagraphNode
90         // -- TextNode "World"
91         p1.select(0, text1.getTextContentSize());
92         selection = $createRangeSelection();
93         selection.setTextNodeRange(text2, 0, text2, text2.getTextContentSize());
94       },
95       {
96         discrete: true,
97       },
98     );
99
100     let html = '';
101
102     editor.update(() => {
103       html = $generateHtmlFromNodes(editor, selection);
104     });
105
106     expect(html).toBe('World');
107   });
108
109   test(`[Lexical -> HTML]: Default selection (undefined) should serialize entire editor state`, () => {
110     const editor = createHeadlessEditor({
111       nodes: [
112         HeadingNode,
113         ListNode,
114         ListItemNode,
115         QuoteNode,
116         LinkNode,
117       ],
118     });
119
120     editor.update(
121       () => {
122         const root = $getRoot();
123         const p1 = $createParagraphNode();
124         const text1 = $createTextNode('Hello');
125         p1.append(text1);
126         const p2 = $createParagraphNode();
127         const text2 = $createTextNode('World');
128         p2.append(text2);
129         root.append(p1).append(p2);
130         // Root
131         // - ParagraphNode
132         // -- TextNode "Hello"
133         // - ParagraphNode
134         // -- TextNode "World"
135         p1.select(0, text1.getTextContentSize());
136       },
137       {
138         discrete: true,
139       },
140     );
141
142     let html = '';
143
144     editor.update(() => {
145       html = $generateHtmlFromNodes(editor);
146     });
147
148     expect(html).toBe(
149       '<p>Hello</p><p>World</p>',
150     );
151   });
152
153   test(`If alignment is set on the paragraph, don't overwrite from parent empty format`, () => {
154     const editor = createHeadlessEditor();
155     const parser = new DOMParser();
156     const rightAlignedParagraphInDiv =
157       '<div><p style="text-align: center;">Hello world!</p></div>';
158
159     editor.update(
160       () => {
161         const root = $getRoot();
162         const dom = parser.parseFromString(
163           rightAlignedParagraphInDiv,
164           'text/html',
165         );
166         const nodes = $generateNodesFromDOM(editor, dom);
167         root.append(...nodes);
168       },
169       {discrete: true},
170     );
171
172     let html = '';
173
174     editor.update(() => {
175       html = $generateHtmlFromNodes(editor);
176     });
177
178     expect(html).toBe(
179       '<p style="text-align: center;">Hello world!</p>',
180     );
181   });
182
183   test(`If alignment is set on the paragraph, it should take precedence over its parent block alignment`, () => {
184     const editor = createHeadlessEditor();
185     const parser = new DOMParser();
186     const rightAlignedParagraphInDiv =
187       '<div style="text-align: right;"><p style="text-align: center;">Hello world!</p></div>';
188
189     editor.update(
190       () => {
191         const root = $getRoot();
192         const dom = parser.parseFromString(
193           rightAlignedParagraphInDiv,
194           'text/html',
195         );
196         const nodes = $generateNodesFromDOM(editor, dom);
197         root.append(...nodes);
198       },
199       {discrete: true},
200     );
201
202     let html = '';
203
204     editor.update(() => {
205       html = $generateHtmlFromNodes(editor);
206     });
207
208     expect(html).toBe(
209       '<p style="text-align: center;">Hello world!</p>',
210     );
211   });
212 });