]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/services/__tests__/keyboard-handling.test.ts
Opensearch: Fixed XML declaration when php short tags enabled
[bookstack] / resources / js / wysiwyg / services / __tests__ / keyboard-handling.test.ts
1 import {
2     createTestContext, destroyFromContext,
3     dispatchKeydownEventForNode,
4     dispatchKeydownEventForSelectedNode, expectNodeShapeToMatch,
5 } from "lexical/__tests__/utils";
6 import {
7     $createParagraphNode, $createTextNode,
8     $getRoot, $getSelection, LexicalEditor, LexicalNode,
9     ParagraphNode, TextNode,
10 } from "lexical";
11 import {$createDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
12 import {registerKeyboardHandling} from "../keyboard-handling";
13 import {registerRichText} from "@lexical/rich-text";
14 import {EditorUiContext} from "../../ui/framework/core";
15 import {$createListItemNode, $createListNode, ListItemNode, ListNode} from "@lexical/list";
16 import {$createImageNode, ImageNode} from "@lexical/rich-text/LexicalImageNode";
17
18 describe('Keyboard-handling service tests', () => {
19
20     let context!: EditorUiContext;
21     let editor!: LexicalEditor;
22
23     beforeEach(() => {
24         context = createTestContext();
25         editor = context.editor;
26         registerRichText(editor);
27         registerKeyboardHandling(context);
28     });
29
30     afterEach(() => {
31         destroyFromContext(context);
32     });
33
34     test('Details: down key on last lines creates new sibling node', () => {
35         let lastRootChild!: LexicalNode|null;
36         let detailsPara!: ParagraphNode;
37
38         editor.updateAndCommit(() => {
39             const root = $getRoot()
40             const details = $createDetailsNode();
41             detailsPara = $createParagraphNode();
42             details.append(detailsPara);
43             $getRoot().append(details);
44             detailsPara.select();
45
46             lastRootChild = root.getLastChild();
47         });
48
49         expect(lastRootChild).toBeInstanceOf(DetailsNode);
50
51         dispatchKeydownEventForNode(detailsPara, editor, 'ArrowDown');
52
53         editor.getEditorState().read(() => {
54             lastRootChild = $getRoot().getLastChild();
55         });
56
57         expect(lastRootChild).toBeInstanceOf(ParagraphNode);
58     });
59
60     test('Details: enter on last empty block creates new sibling node', () => {
61         registerRichText(editor);
62
63         let lastRootChild!: LexicalNode|null;
64         let detailsPara!: ParagraphNode;
65
66         editor.updateAndCommit(() => {
67             const root = $getRoot()
68             const details = $createDetailsNode();
69             const text = $createTextNode('Hello!');
70             detailsPara = $createParagraphNode();
71             detailsPara.append(text);
72             details.append(detailsPara);
73             $getRoot().append(details);
74             text.selectEnd();
75
76             lastRootChild = root.getLastChild();
77         });
78
79         expect(lastRootChild).toBeInstanceOf(DetailsNode);
80
81         dispatchKeydownEventForNode(detailsPara, editor, 'Enter');
82         dispatchKeydownEventForSelectedNode(editor, 'Enter');
83
84         let detailsChildren!: LexicalNode[];
85         let lastDetailsText!: string;
86
87         editor.getEditorState().read(() => {
88             detailsChildren = (lastRootChild as DetailsNode).getChildren();
89             lastRootChild = $getRoot().getLastChild();
90             lastDetailsText = detailsChildren[0].getTextContent();
91         });
92
93         expect(lastRootChild).toBeInstanceOf(ParagraphNode);
94         expect(detailsChildren).toHaveLength(1);
95         expect(lastDetailsText).toBe('Hello!');
96     });
97
98     test('Lists: tab on empty list item insets item', () => {
99
100         let list!: ListNode;
101         let listItemB!: ListItemNode;
102
103         editor.updateAndCommit(() => {
104             const root = $getRoot();
105             list = $createListNode('bullet');
106             const listItemA = $createListItemNode();
107             listItemA.append($createTextNode('Hello!'));
108             listItemB = $createListItemNode();
109             list.append(listItemA, listItemB);
110             root.append(list);
111             listItemB.selectStart();
112         });
113
114         dispatchKeydownEventForNode(listItemB, editor, 'Tab');
115
116         editor.getEditorState().read(() => {
117             const list = $getRoot().getChildren()[0] as ListNode;
118             const listChild = list.getChildren()[0] as ListItemNode;
119             const children = listChild.getChildren();
120             expect(children).toHaveLength(2);
121             expect(children[0]).toBeInstanceOf(TextNode);
122             expect(children[0].getTextContent()).toBe('Hello!');
123             expect(children[1]).toBeInstanceOf(ListNode);
124
125             const innerList = children[1] as ListNode;
126             const selectedNode = $getSelection()?.getNodes()[0];
127             expect(selectedNode).toBeInstanceOf(ListItemNode);
128             expect(selectedNode?.getKey()).toBe(innerList.getChildren()[0].getKey());
129         });
130     });
131
132     test('Images: up on selected image creates new paragraph if none above', () => {
133         let image!: ImageNode;
134         editor.updateAndCommit(() => {
135             const root = $getRoot();
136             const imageWrap = $createParagraphNode();
137             image = $createImageNode('https://example.com/cat.png');
138             imageWrap.append(image);
139             root.append(imageWrap);
140             image.select();
141         });
142
143         expectNodeShapeToMatch(editor, [{
144             type: 'paragraph',
145             children: [
146                 {type: 'image'}
147             ],
148         }]);
149
150         dispatchKeydownEventForNode(image, editor, 'ArrowUp');
151
152         expectNodeShapeToMatch(editor, [{
153             type: 'paragraph',
154         }, {
155             type: 'paragraph',
156             children: [
157                 {type: 'image'}
158             ],
159         }]);
160     });
161 });