vitest-prosemirror 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/vitest-prosemirror.d.ts +13 -16
- package/dist/vitest-prosemirror.js +678 -328
- package/package.json +36 -19
- package/dist/vitest-prosemirror.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/vitest-prosemirror)
|
|
4
4
|
[](https://github.com/marekdedic/vitest-prosemirror/actions)
|
|
5
|
-
[](https://app.codecov.io/gh/marekdedic/vitest-prosemirror)
|
|
6
6
|
[](https://www.npmjs.com/package/vitest-prosemirror)
|
|
7
7
|
[](https://github.com/marekdedic/vitest-prosemirror/blob/master/LICENSE)
|
|
8
8
|
|
|
@@ -1,45 +1,42 @@
|
|
|
1
1
|
import 'vitest';
|
|
2
|
+
import { EditorState } from 'prosemirror-state';
|
|
2
3
|
import { Node as Node_2 } from 'prosemirror-model';
|
|
3
4
|
import { Plugin as Plugin_2 } from 'prosemirror-state';
|
|
4
|
-
import { Schema } from 'prosemirror-model';
|
|
5
5
|
import { Selection as Selection_2 } from 'prosemirror-state';
|
|
6
6
|
|
|
7
7
|
export declare interface CustomMatchers<R = unknown> {
|
|
8
8
|
toEqualProseMirrorNode(expected: Node_2): R;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export declare interface KeyboardModifiers {
|
|
12
|
-
altKey?: boolean;
|
|
13
|
-
ctrlKey?: boolean;
|
|
14
|
-
metaKey?: boolean;
|
|
15
|
-
shiftKey?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
11
|
export declare interface Options {
|
|
12
|
+
autoCleanup: boolean;
|
|
19
13
|
plugins: Array<Plugin_2>;
|
|
20
14
|
}
|
|
21
15
|
|
|
22
16
|
export declare class ProseMirrorTester {
|
|
23
17
|
get doc(): Node_2;
|
|
24
|
-
get
|
|
18
|
+
get html(): string;
|
|
19
|
+
get state(): EditorState;
|
|
20
|
+
get text(): string;
|
|
21
|
+
private destroyed;
|
|
22
|
+
private readonly element;
|
|
25
23
|
private readonly view;
|
|
26
24
|
constructor(documentRoot: Node_2, options?: Partial<Options>);
|
|
27
|
-
|
|
25
|
+
destroy(): void;
|
|
26
|
+
insertText(text: string): void;
|
|
28
27
|
selectText(selection: TesterSelection): void;
|
|
29
|
-
private
|
|
28
|
+
private assertAlive;
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
export declare type TesterSelection = "all" | "end" | "start" | {
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
anchor: number;
|
|
33
|
+
head: number;
|
|
35
34
|
} | Selection_2 | number;
|
|
36
35
|
|
|
37
36
|
export { }
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
declare module "vitest" {
|
|
41
|
-
interface
|
|
42
|
-
}
|
|
43
|
-
interface AsymmetricMatchersContaining extends CustomMatchers {
|
|
40
|
+
interface Matchers<R extends Promise<void> | void = Promise<void> | void, T = unknown> extends CustomMatchers<R> {
|
|
44
41
|
}
|
|
45
42
|
}
|
|
@@ -1,342 +1,692 @@
|
|
|
1
|
-
import { expect } from "vitest";
|
|
2
|
-
import
|
|
3
|
-
import { EditorState, AllSelection, TextSelection } from "prosemirror-state";
|
|
1
|
+
import { afterEach, expect } from "vitest";
|
|
2
|
+
import { AllSelection, EditorState, Selection, TextSelection } from "prosemirror-state";
|
|
4
3
|
import { EditorView } from "prosemirror-view";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
indent: " ",
|
|
14
|
-
inlineCharacterLimit: 1e3
|
|
15
|
-
})
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
content = `${mark.type.name}(${items.join(", ")})`;
|
|
19
|
-
}
|
|
20
|
-
return content;
|
|
21
|
-
}
|
|
22
|
-
const renamedTypes = {
|
|
23
|
-
hardBreak: "br",
|
|
24
|
-
heading: "h",
|
|
25
|
-
horizontalRule: "hr",
|
|
26
|
-
paragraph: "p"
|
|
4
|
+
import stringifyObject from "stringify-object";
|
|
5
|
+
//#region src/mockRangeRects.ts
|
|
6
|
+
var mockRangeRects = () => {
|
|
7
|
+
const zeroRect = () => new DOMRect(0, 0, 0, 0);
|
|
8
|
+
const emptyRectList = () => [];
|
|
9
|
+
const rangePrototype = Range.prototype;
|
|
10
|
+
if (typeof rangePrototype.getBoundingClientRect !== "function") Range.prototype.getBoundingClientRect = zeroRect;
|
|
11
|
+
if (typeof rangePrototype.getClientRects !== "function") Range.prototype.getClientRects = emptyRectList;
|
|
27
12
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/MutationObserverMock.ts
|
|
15
|
+
var MutationObserverMock = class MutationObserverMock {
|
|
16
|
+
static activeObservers = /* @__PURE__ */ new Map();
|
|
17
|
+
callback;
|
|
18
|
+
target;
|
|
19
|
+
constructor(callback) {
|
|
20
|
+
this.callback = callback;
|
|
21
|
+
this.target = void 0;
|
|
22
|
+
}
|
|
23
|
+
static createMutation(target, mutationRecords) {
|
|
24
|
+
const observer = MutationObserverMock.activeObservers.get(target);
|
|
25
|
+
if (observer === void 0) return;
|
|
26
|
+
observer.callback(mutationRecords.map((record) => ({
|
|
27
|
+
addedNodes: [],
|
|
28
|
+
attributeName: null,
|
|
29
|
+
attributeNamespace: null,
|
|
30
|
+
nextSibling: null,
|
|
31
|
+
oldValue: null,
|
|
32
|
+
previousSibling: null,
|
|
33
|
+
removedNodes: [],
|
|
34
|
+
...record
|
|
35
|
+
})), observer);
|
|
36
|
+
}
|
|
37
|
+
disconnect() {
|
|
38
|
+
if (this.target !== void 0) MutationObserverMock.activeObservers.delete(this.target);
|
|
39
|
+
this.target = void 0;
|
|
40
|
+
}
|
|
41
|
+
observe(target) {
|
|
42
|
+
this.target = target;
|
|
43
|
+
MutationObserverMock.activeObservers.set(target, this);
|
|
44
|
+
}
|
|
45
|
+
takeRecords() {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/selection.ts
|
|
51
|
+
var resolveSelection = (doc, selection) => {
|
|
52
|
+
if (selection === "all") return new AllSelection(doc);
|
|
53
|
+
if (typeof selection === "object" && "$anchor" in selection && "$head" in selection) return selection;
|
|
54
|
+
if (typeof selection === "object" && "anchor" in selection && "head" in selection) return TextSelection.between(doc.resolve(selection.anchor), doc.resolve(selection.head));
|
|
55
|
+
if (typeof selection === "object" && "from" in selection && "to" in selection) throw new Error("selectText no longer accepts { from, to } — use { anchor, head } instead. The values map directly: { from: a, to: b } becomes { anchor: a, head: b }.");
|
|
56
|
+
let pos = 0;
|
|
57
|
+
if (selection === "end") pos = doc.nodeSize - 2;
|
|
58
|
+
else if (selection !== "start") pos = selection;
|
|
59
|
+
return TextSelection.near(doc.resolve(pos));
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/typing/dom.ts
|
|
63
|
+
var characterDataAt = (node, offset) => {
|
|
64
|
+
if (node instanceof CharacterData) return {
|
|
65
|
+
offset,
|
|
66
|
+
target: node
|
|
67
|
+
};
|
|
68
|
+
const after = node.childNodes[offset];
|
|
69
|
+
if (after instanceof CharacterData) return {
|
|
70
|
+
offset: 0,
|
|
71
|
+
target: after
|
|
72
|
+
};
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
function deleteText(view, direction) {
|
|
76
|
+
const { selection } = view.state;
|
|
77
|
+
const from = selection.empty && direction === -1 ? selection.from - 1 : selection.from;
|
|
78
|
+
const to = selection.empty && direction === 1 ? selection.to + 1 : selection.to;
|
|
79
|
+
const fromDOM = view.domAtPos(from, -1);
|
|
80
|
+
const target = characterDataAt(fromDOM.node, fromDOM.offset)?.target;
|
|
81
|
+
if (target === void 0) throw new Error("Cannot simulate deleting a range that is not inside a single text node");
|
|
82
|
+
const nodeStart = view.posAtDOM(target, 0);
|
|
83
|
+
const fromOffset = from - nodeStart;
|
|
84
|
+
const toOffset = to - nodeStart;
|
|
85
|
+
if (fromOffset < 0 || toOffset > target.data.length) throw new Error("Cannot simulate deleting a range that is not inside a single text node");
|
|
86
|
+
const oldValue = target.data;
|
|
87
|
+
target.data = target.data.slice(0, fromOffset) + target.data.slice(toOffset);
|
|
88
|
+
MutationObserverMock.createMutation(view.dom, [{
|
|
89
|
+
oldValue,
|
|
90
|
+
target,
|
|
91
|
+
type: "characterData"
|
|
92
|
+
}]);
|
|
93
|
+
}
|
|
94
|
+
function moveCaret(view, direction) {
|
|
95
|
+
const { selection } = view.state;
|
|
96
|
+
let pos = direction === -1 ? selection.from : selection.to;
|
|
97
|
+
if (selection.empty) pos += direction;
|
|
98
|
+
view.dispatch(view.state.tr.setSelection(Selection.near(view.state.doc.resolve(pos), direction)));
|
|
52
99
|
}
|
|
100
|
+
function typeCharacter(view, character) {
|
|
101
|
+
const { node, offset: domOffset } = view.domAtPos(view.state.selection.from, -1);
|
|
102
|
+
const point = characterDataAt(node, domOffset);
|
|
103
|
+
if (point !== null) {
|
|
104
|
+
const { offset, target } = point;
|
|
105
|
+
const oldValue = target.data;
|
|
106
|
+
target.data = target.data.slice(0, offset) + character + target.data.slice(offset);
|
|
107
|
+
MutationObserverMock.createMutation(view.dom, [{
|
|
108
|
+
oldValue,
|
|
109
|
+
target,
|
|
110
|
+
type: "characterData"
|
|
111
|
+
}]);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const textNode = new Text(character);
|
|
115
|
+
const nextSibling = node.childNodes.item(domOffset);
|
|
116
|
+
node.insertBefore(textNode, nextSibling);
|
|
117
|
+
MutationObserverMock.createMutation(view.dom, [{
|
|
118
|
+
addedNodes: [textNode],
|
|
119
|
+
nextSibling,
|
|
120
|
+
previousSibling: textNode.previousSibling,
|
|
121
|
+
target: node,
|
|
122
|
+
type: "childList"
|
|
123
|
+
}]);
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/typing/keyboardInput.ts
|
|
53
127
|
function tokenizeKeyboardInput(input) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
currentGroupOpener = null;
|
|
76
|
-
group = "";
|
|
77
|
-
} else {
|
|
78
|
-
group += char;
|
|
79
|
-
}
|
|
80
|
-
} else if (["[", "{"].includes(char)) {
|
|
81
|
-
currentGroupOpener = char;
|
|
82
|
-
} else {
|
|
83
|
-
output.push(char);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
output.forEach(assertSupported);
|
|
87
|
-
return output;
|
|
128
|
+
const output = [];
|
|
129
|
+
let currentGroupOpener = null;
|
|
130
|
+
let group = "";
|
|
131
|
+
for (const char of input) if (currentGroupOpener !== null) {
|
|
132
|
+
if (["]", "}"].includes(char)) {
|
|
133
|
+
if (group.endsWith("\\") && char === matchingBrace(currentGroupOpener)) group = group.slice(0, -2) + char;
|
|
134
|
+
else if (char === matchingBrace(currentGroupOpener)) {
|
|
135
|
+
if (group.length === 4 && group.startsWith("Key")) output.push(group.slice(3).toLowerCase());
|
|
136
|
+
else output.push(group);
|
|
137
|
+
currentGroupOpener = null;
|
|
138
|
+
group = "";
|
|
139
|
+
} else group += char;
|
|
140
|
+
} else if (group === "" && currentGroupOpener === char) {
|
|
141
|
+
output.push(char);
|
|
142
|
+
currentGroupOpener = null;
|
|
143
|
+
group = "";
|
|
144
|
+
} else group += char;
|
|
145
|
+
} else if (["[", "{"].includes(char)) currentGroupOpener = char;
|
|
146
|
+
else output.push(char);
|
|
147
|
+
output.forEach(assertSupported);
|
|
148
|
+
return output;
|
|
88
149
|
}
|
|
89
150
|
function assertSupported(character) {
|
|
90
|
-
|
|
91
|
-
throw new Error("Unsupported keyboard input");
|
|
92
|
-
}
|
|
151
|
+
if (/^\/.+/u.exec(character) || /.+>[\d]*\/?$/u.exec(character)) throw new Error("Unsupported keyboard input");
|
|
93
152
|
}
|
|
94
153
|
function matchingBrace(opener) {
|
|
95
|
-
|
|
154
|
+
return opener === "{" ? "}" : "]";
|
|
96
155
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/typing/keyChord.ts
|
|
158
|
+
function parseKeyChord(token) {
|
|
159
|
+
const parts = token.split(/-(?!$)/u);
|
|
160
|
+
const key = parts[parts.length - 1] === "Space" ? " " : parts[parts.length - 1];
|
|
161
|
+
const modifiers = {};
|
|
162
|
+
for (const modifier of parts.slice(0, -1)) if (/^(cmd|meta|m)$/iu.test(modifier)) modifiers.metaKey = true;
|
|
163
|
+
else if (/^a(lt)?$/iu.test(modifier)) modifiers.altKey = true;
|
|
164
|
+
else if (/^(c|ctrl|control)$/iu.test(modifier)) modifiers.ctrlKey = true;
|
|
165
|
+
else if (/^s(hift)?$/iu.test(modifier)) modifiers.shiftKey = true;
|
|
166
|
+
else if (/^mod$/iu.test(modifier)) modifiers.ctrlKey = true;
|
|
167
|
+
else throw new Error(`Unrecognized modifier name: ${modifier}`);
|
|
168
|
+
return {
|
|
169
|
+
key,
|
|
170
|
+
modifiers
|
|
171
|
+
};
|
|
106
172
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/typing/keyIdentity.ts
|
|
175
|
+
var namedKeys = new Map(Object.entries({
|
|
176
|
+
Alt: 18,
|
|
177
|
+
ArrowDown: 40,
|
|
178
|
+
ArrowLeft: 37,
|
|
179
|
+
ArrowRight: 39,
|
|
180
|
+
ArrowUp: 38,
|
|
181
|
+
Backspace: 8,
|
|
182
|
+
CapsLock: 20,
|
|
183
|
+
Control: 17,
|
|
184
|
+
Delete: 46,
|
|
185
|
+
End: 35,
|
|
186
|
+
Enter: 13,
|
|
187
|
+
Escape: 27,
|
|
188
|
+
F1: 112,
|
|
189
|
+
F2: 113,
|
|
190
|
+
F3: 114,
|
|
191
|
+
F4: 115,
|
|
192
|
+
F5: 116,
|
|
193
|
+
F6: 117,
|
|
194
|
+
F7: 118,
|
|
195
|
+
F8: 119,
|
|
196
|
+
F9: 120,
|
|
197
|
+
F10: 121,
|
|
198
|
+
F11: 122,
|
|
199
|
+
F12: 123,
|
|
200
|
+
Home: 36,
|
|
201
|
+
Insert: 45,
|
|
202
|
+
Meta: 91,
|
|
203
|
+
PageDown: 34,
|
|
204
|
+
PageUp: 33,
|
|
205
|
+
Shift: 16,
|
|
206
|
+
Tab: 9
|
|
207
|
+
}));
|
|
208
|
+
var sidedCodes = new Map(Object.entries({
|
|
209
|
+
Alt: "AltLeft",
|
|
210
|
+
Control: "ControlLeft",
|
|
211
|
+
Meta: "MetaLeft",
|
|
212
|
+
Shift: "ShiftLeft"
|
|
213
|
+
}));
|
|
214
|
+
var standardLocation = 0;
|
|
215
|
+
var leftLocation = 1;
|
|
216
|
+
var namedCharCodes = new Map(Object.entries({ Enter: 13 }));
|
|
217
|
+
var noCharacter = 0;
|
|
218
|
+
var characterKeys = new Map(Object.entries({
|
|
219
|
+
" ": {
|
|
220
|
+
charCode: 32,
|
|
221
|
+
code: "Space",
|
|
222
|
+
key: " ",
|
|
223
|
+
keyCode: 32,
|
|
224
|
+
location: standardLocation
|
|
225
|
+
},
|
|
226
|
+
"!": {
|
|
227
|
+
charCode: 33,
|
|
228
|
+
code: "Digit1",
|
|
229
|
+
key: "!",
|
|
230
|
+
keyCode: 49,
|
|
231
|
+
location: standardLocation
|
|
232
|
+
},
|
|
233
|
+
"\"": {
|
|
234
|
+
charCode: 34,
|
|
235
|
+
code: "Quote",
|
|
236
|
+
key: "\"",
|
|
237
|
+
keyCode: 222,
|
|
238
|
+
location: standardLocation
|
|
239
|
+
},
|
|
240
|
+
"#": {
|
|
241
|
+
charCode: 35,
|
|
242
|
+
code: "Digit3",
|
|
243
|
+
key: "#",
|
|
244
|
+
keyCode: 51,
|
|
245
|
+
location: standardLocation
|
|
246
|
+
},
|
|
247
|
+
$: {
|
|
248
|
+
charCode: 36,
|
|
249
|
+
code: "Digit4",
|
|
250
|
+
key: "$",
|
|
251
|
+
keyCode: 52,
|
|
252
|
+
location: standardLocation
|
|
253
|
+
},
|
|
254
|
+
"%": {
|
|
255
|
+
charCode: 37,
|
|
256
|
+
code: "Digit5",
|
|
257
|
+
key: "%",
|
|
258
|
+
keyCode: 53,
|
|
259
|
+
location: standardLocation
|
|
260
|
+
},
|
|
261
|
+
"&": {
|
|
262
|
+
charCode: 38,
|
|
263
|
+
code: "Digit7",
|
|
264
|
+
key: "&",
|
|
265
|
+
keyCode: 55,
|
|
266
|
+
location: standardLocation
|
|
267
|
+
},
|
|
268
|
+
"'": {
|
|
269
|
+
charCode: 39,
|
|
270
|
+
code: "Quote",
|
|
271
|
+
key: "'",
|
|
272
|
+
keyCode: 222,
|
|
273
|
+
location: standardLocation
|
|
274
|
+
},
|
|
275
|
+
"(": {
|
|
276
|
+
charCode: 40,
|
|
277
|
+
code: "Digit9",
|
|
278
|
+
key: "(",
|
|
279
|
+
keyCode: 57,
|
|
280
|
+
location: standardLocation
|
|
281
|
+
},
|
|
282
|
+
")": {
|
|
283
|
+
charCode: 41,
|
|
284
|
+
code: "Digit0",
|
|
285
|
+
key: ")",
|
|
286
|
+
keyCode: 48,
|
|
287
|
+
location: standardLocation
|
|
288
|
+
},
|
|
289
|
+
"*": {
|
|
290
|
+
charCode: 42,
|
|
291
|
+
code: "Digit8",
|
|
292
|
+
key: "*",
|
|
293
|
+
keyCode: 56,
|
|
294
|
+
location: standardLocation
|
|
295
|
+
},
|
|
296
|
+
"+": {
|
|
297
|
+
charCode: 43,
|
|
298
|
+
code: "Equal",
|
|
299
|
+
key: "+",
|
|
300
|
+
keyCode: 187,
|
|
301
|
+
location: standardLocation
|
|
302
|
+
},
|
|
303
|
+
",": {
|
|
304
|
+
charCode: 44,
|
|
305
|
+
code: "Comma",
|
|
306
|
+
key: ",",
|
|
307
|
+
keyCode: 188,
|
|
308
|
+
location: standardLocation
|
|
309
|
+
},
|
|
310
|
+
"-": {
|
|
311
|
+
charCode: 45,
|
|
312
|
+
code: "Minus",
|
|
313
|
+
key: "-",
|
|
314
|
+
keyCode: 189,
|
|
315
|
+
location: standardLocation
|
|
316
|
+
},
|
|
317
|
+
".": {
|
|
318
|
+
charCode: 46,
|
|
319
|
+
code: "Period",
|
|
320
|
+
key: ".",
|
|
321
|
+
keyCode: 190,
|
|
322
|
+
location: standardLocation
|
|
323
|
+
},
|
|
324
|
+
"/": {
|
|
325
|
+
charCode: 47,
|
|
326
|
+
code: "Slash",
|
|
327
|
+
key: "/",
|
|
328
|
+
keyCode: 191,
|
|
329
|
+
location: standardLocation
|
|
330
|
+
},
|
|
331
|
+
":": {
|
|
332
|
+
charCode: 58,
|
|
333
|
+
code: "Semicolon",
|
|
334
|
+
key: ":",
|
|
335
|
+
keyCode: 186,
|
|
336
|
+
location: standardLocation
|
|
337
|
+
},
|
|
338
|
+
";": {
|
|
339
|
+
charCode: 59,
|
|
340
|
+
code: "Semicolon",
|
|
341
|
+
key: ";",
|
|
342
|
+
keyCode: 186,
|
|
343
|
+
location: standardLocation
|
|
344
|
+
},
|
|
345
|
+
"<": {
|
|
346
|
+
charCode: 60,
|
|
347
|
+
code: "Comma",
|
|
348
|
+
key: "<",
|
|
349
|
+
keyCode: 188,
|
|
350
|
+
location: standardLocation
|
|
351
|
+
},
|
|
352
|
+
"=": {
|
|
353
|
+
charCode: 61,
|
|
354
|
+
code: "Equal",
|
|
355
|
+
key: "=",
|
|
356
|
+
keyCode: 187,
|
|
357
|
+
location: standardLocation
|
|
358
|
+
},
|
|
359
|
+
">": {
|
|
360
|
+
charCode: 62,
|
|
361
|
+
code: "Period",
|
|
362
|
+
key: ">",
|
|
363
|
+
keyCode: 190,
|
|
364
|
+
location: standardLocation
|
|
365
|
+
},
|
|
366
|
+
"?": {
|
|
367
|
+
charCode: 63,
|
|
368
|
+
code: "Slash",
|
|
369
|
+
key: "?",
|
|
370
|
+
keyCode: 191,
|
|
371
|
+
location: standardLocation
|
|
372
|
+
},
|
|
373
|
+
"@": {
|
|
374
|
+
charCode: 64,
|
|
375
|
+
code: "Digit2",
|
|
376
|
+
key: "@",
|
|
377
|
+
keyCode: 50,
|
|
378
|
+
location: standardLocation
|
|
379
|
+
},
|
|
380
|
+
"[": {
|
|
381
|
+
charCode: 91,
|
|
382
|
+
code: "BracketLeft",
|
|
383
|
+
key: "[",
|
|
384
|
+
keyCode: 219,
|
|
385
|
+
location: standardLocation
|
|
386
|
+
},
|
|
387
|
+
"\\": {
|
|
388
|
+
charCode: 92,
|
|
389
|
+
code: "Backslash",
|
|
390
|
+
key: "\\",
|
|
391
|
+
keyCode: 220,
|
|
392
|
+
location: standardLocation
|
|
393
|
+
},
|
|
394
|
+
"]": {
|
|
395
|
+
charCode: 93,
|
|
396
|
+
code: "BracketRight",
|
|
397
|
+
key: "]",
|
|
398
|
+
keyCode: 221,
|
|
399
|
+
location: standardLocation
|
|
400
|
+
},
|
|
401
|
+
"^": {
|
|
402
|
+
charCode: 94,
|
|
403
|
+
code: "Digit6",
|
|
404
|
+
key: "^",
|
|
405
|
+
keyCode: 54,
|
|
406
|
+
location: standardLocation
|
|
407
|
+
},
|
|
408
|
+
_: {
|
|
409
|
+
charCode: 95,
|
|
410
|
+
code: "Minus",
|
|
411
|
+
key: "_",
|
|
412
|
+
keyCode: 189,
|
|
413
|
+
location: standardLocation
|
|
414
|
+
},
|
|
415
|
+
"`": {
|
|
416
|
+
charCode: 96,
|
|
417
|
+
code: "Backquote",
|
|
418
|
+
key: "`",
|
|
419
|
+
keyCode: 192,
|
|
420
|
+
location: standardLocation
|
|
421
|
+
},
|
|
422
|
+
"{": {
|
|
423
|
+
charCode: 123,
|
|
424
|
+
code: "BracketLeft",
|
|
425
|
+
key: "{",
|
|
426
|
+
keyCode: 219,
|
|
427
|
+
location: standardLocation
|
|
428
|
+
},
|
|
429
|
+
"|": {
|
|
430
|
+
charCode: 124,
|
|
431
|
+
code: "Backslash",
|
|
432
|
+
key: "|",
|
|
433
|
+
keyCode: 220,
|
|
434
|
+
location: standardLocation
|
|
435
|
+
},
|
|
436
|
+
"}": {
|
|
437
|
+
charCode: 125,
|
|
438
|
+
code: "BracketRight",
|
|
439
|
+
key: "}",
|
|
440
|
+
keyCode: 221,
|
|
441
|
+
location: standardLocation
|
|
442
|
+
},
|
|
443
|
+
"~": {
|
|
444
|
+
charCode: 126,
|
|
445
|
+
code: "Backquote",
|
|
446
|
+
key: "~",
|
|
447
|
+
keyCode: 192,
|
|
448
|
+
location: standardLocation
|
|
449
|
+
}
|
|
450
|
+
}));
|
|
451
|
+
function keyIdentity(key) {
|
|
452
|
+
const namedKeyCode = namedKeys.get(key);
|
|
453
|
+
if (namedKeyCode !== void 0) {
|
|
454
|
+
const sidedCode = sidedCodes.get(key);
|
|
455
|
+
return {
|
|
456
|
+
charCode: namedCharCodes.get(key) ?? noCharacter,
|
|
457
|
+
code: sidedCode ?? key,
|
|
458
|
+
key,
|
|
459
|
+
keyCode: namedKeyCode,
|
|
460
|
+
location: sidedCode === void 0 ? standardLocation : leftLocation
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (/^[a-z]$/iu.exec(key)) {
|
|
464
|
+
const upperCaseKey = key.toUpperCase();
|
|
465
|
+
return {
|
|
466
|
+
charCode: key.charCodeAt(0),
|
|
467
|
+
code: `Key${upperCaseKey}`,
|
|
468
|
+
key,
|
|
469
|
+
keyCode: upperCaseKey.charCodeAt(0),
|
|
470
|
+
location: standardLocation
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (/^\d$/u.exec(key)) return {
|
|
474
|
+
charCode: key.charCodeAt(0),
|
|
475
|
+
code: `Digit${key}`,
|
|
476
|
+
key,
|
|
477
|
+
keyCode: key.charCodeAt(0),
|
|
478
|
+
location: standardLocation
|
|
479
|
+
};
|
|
480
|
+
const characterKey = characterKeys.get(key);
|
|
481
|
+
if (characterKey !== void 0) return characterKey;
|
|
482
|
+
return {
|
|
483
|
+
charCode: key.length === 1 ? key.charCodeAt(0) : noCharacter,
|
|
484
|
+
code: "",
|
|
485
|
+
key,
|
|
486
|
+
keyCode: 0,
|
|
487
|
+
location: standardLocation
|
|
488
|
+
};
|
|
282
489
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region src/typing/typing.ts
|
|
492
|
+
var ignoredKeys = /* @__PURE__ */ new Set([
|
|
493
|
+
"Alt",
|
|
494
|
+
"CapsLock",
|
|
495
|
+
"Control",
|
|
496
|
+
"Meta",
|
|
497
|
+
"Shift",
|
|
498
|
+
"Tab"
|
|
499
|
+
]);
|
|
500
|
+
var hasModifiers = (modifiers) => modifiers !== void 0 && Object.values(modifiers).includes(true);
|
|
501
|
+
var suppressesCharacter = (modifiers) => modifiers?.altKey === true || modifiers?.ctrlKey === true || modifiers?.metaKey === true;
|
|
502
|
+
var isLetter = (key) => /^[a-z]$/iu.test(key);
|
|
503
|
+
function insertText(view, text) {
|
|
504
|
+
for (const token of tokenizeKeyboardInput(text)) {
|
|
505
|
+
const { key, modifiers } = parseKeyChord(token);
|
|
506
|
+
const character = isLetter(key) && modifiers.shiftKey === true ? key.toUpperCase() : key;
|
|
507
|
+
const shiftKey = isLetter(character) ? character === character.toUpperCase() : modifiers.shiftKey ?? false;
|
|
508
|
+
const identity = keyIdentity(character);
|
|
509
|
+
const eventInit = {
|
|
510
|
+
bubbles: true,
|
|
511
|
+
cancelable: true,
|
|
512
|
+
charCode: 0,
|
|
513
|
+
code: identity.code,
|
|
514
|
+
composed: true,
|
|
515
|
+
key: identity.key,
|
|
516
|
+
keyCode: identity.keyCode,
|
|
517
|
+
location: identity.location,
|
|
518
|
+
...modifiers,
|
|
519
|
+
shiftKey
|
|
520
|
+
};
|
|
521
|
+
const keydownEvent = new KeyboardEvent("keydown", eventInit);
|
|
522
|
+
view.dispatchEvent(keydownEvent);
|
|
523
|
+
if (!keydownEvent.defaultPrevented) {
|
|
524
|
+
const action = keyAction(character, modifiers);
|
|
525
|
+
if (action.type === "delete") deleteText(view, action.direction);
|
|
526
|
+
else if (action.type === "moveCaret") moveCaret(view, action.direction);
|
|
527
|
+
else if (action.type === "type") {
|
|
528
|
+
view.dispatchEvent(new KeyboardEvent("keypress", {
|
|
529
|
+
...eventInit,
|
|
530
|
+
charCode: identity.charCode,
|
|
531
|
+
keyCode: identity.charCode
|
|
532
|
+
}));
|
|
533
|
+
typeCharacter(view, action.character);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
view.dispatchEvent(new KeyboardEvent("keyup", eventInit));
|
|
537
|
+
}
|
|
294
538
|
}
|
|
295
|
-
function
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
539
|
+
function keyAction(key, modifiers) {
|
|
540
|
+
if (ignoredKeys.has(key)) return { type: "ignore" };
|
|
541
|
+
if (key === "Backspace") return {
|
|
542
|
+
direction: -1,
|
|
543
|
+
type: "delete"
|
|
544
|
+
};
|
|
545
|
+
if (key === "Delete") return {
|
|
546
|
+
direction: 1,
|
|
547
|
+
type: "delete"
|
|
548
|
+
};
|
|
549
|
+
if ((key === "ArrowLeft" || key === "ArrowRight") && !hasModifiers(modifiers)) return {
|
|
550
|
+
direction: key === "ArrowLeft" ? -1 : 1,
|
|
551
|
+
type: "moveCaret"
|
|
552
|
+
};
|
|
553
|
+
if (key.length === 1) {
|
|
554
|
+
if (suppressesCharacter(modifiers)) return { type: "ignore" };
|
|
555
|
+
return {
|
|
556
|
+
character: key,
|
|
557
|
+
type: "type"
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
throw new Error(`Cannot simulate the "${key}" key`);
|
|
303
561
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
`;
|
|
312
|
-
const pass = this.equals(receivedDoc, expectedDoc);
|
|
313
|
-
const message = pass ? () => `${this.utils.matcherHint(".not.toEqualProsemirrorNode")}
|
|
314
|
-
|
|
315
|
-
Expected value of document to not equal:
|
|
316
|
-
${this.utils.printExpected(expectedDoc)}
|
|
317
|
-
Actual:
|
|
318
|
-
${this.utils.printReceived(receivedDoc)}` : () => {
|
|
319
|
-
const diffString = this.utils.diff(expectedDoc, receivedDoc, {
|
|
320
|
-
expand: this.expand ?? false
|
|
321
|
-
});
|
|
322
|
-
return `${this.utils.matcherHint(".toEqualProsemirrorNode")}
|
|
323
|
-
|
|
324
|
-
Expected value of document to equal:
|
|
325
|
-
${this.utils.printExpected(expectedDoc)}
|
|
326
|
-
Actual:
|
|
327
|
-
${this.utils.printReceived(receivedDoc)}${diffString !== void 0 ? `
|
|
328
|
-
|
|
329
|
-
Difference:
|
|
330
|
-
|
|
331
|
-
${diffString}` : ""}`;
|
|
332
|
-
};
|
|
333
|
-
return {
|
|
334
|
-
message,
|
|
335
|
-
pass
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
export {
|
|
340
|
-
ProseMirrorTester
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/ProseMirrorTester.ts
|
|
564
|
+
var originalMutationObserver = global.MutationObserver;
|
|
565
|
+
var liveTesters = /* @__PURE__ */ new Set();
|
|
566
|
+
var autoCleanupTesters = /* @__PURE__ */ new Set();
|
|
567
|
+
var cleanupTesters = () => {
|
|
568
|
+
for (const tester of [...autoCleanupTesters]) tester.destroy();
|
|
341
569
|
};
|
|
342
|
-
|
|
570
|
+
var ProseMirrorTester = class {
|
|
571
|
+
get doc() {
|
|
572
|
+
this.assertAlive();
|
|
573
|
+
return this.view.state.doc;
|
|
574
|
+
}
|
|
575
|
+
get html() {
|
|
576
|
+
this.assertAlive();
|
|
577
|
+
return this.view.dom.innerHTML;
|
|
578
|
+
}
|
|
579
|
+
get state() {
|
|
580
|
+
this.assertAlive();
|
|
581
|
+
return this.view.state;
|
|
582
|
+
}
|
|
583
|
+
get text() {
|
|
584
|
+
this.assertAlive();
|
|
585
|
+
return this.view.dom.textContent;
|
|
586
|
+
}
|
|
587
|
+
destroyed = false;
|
|
588
|
+
element;
|
|
589
|
+
view;
|
|
590
|
+
constructor(documentRoot, options = {}) {
|
|
591
|
+
if (typeof document === "undefined") throw new Error("TODO");
|
|
592
|
+
this.element = document.createElement("div");
|
|
593
|
+
document.body.append(this.element);
|
|
594
|
+
const state = EditorState.create({
|
|
595
|
+
doc: documentRoot,
|
|
596
|
+
plugins: options.plugins ?? []
|
|
597
|
+
});
|
|
598
|
+
global.MutationObserver = MutationObserverMock;
|
|
599
|
+
mockRangeRects();
|
|
600
|
+
this.view = new EditorView(this.element, { state });
|
|
601
|
+
liveTesters.add(this);
|
|
602
|
+
if (options.autoCleanup ?? true) autoCleanupTesters.add(this);
|
|
603
|
+
}
|
|
604
|
+
destroy() {
|
|
605
|
+
if (this.destroyed) return;
|
|
606
|
+
this.destroyed = true;
|
|
607
|
+
this.view.destroy();
|
|
608
|
+
this.element.remove();
|
|
609
|
+
document.getSelection()?.removeAllRanges();
|
|
610
|
+
autoCleanupTesters.delete(this);
|
|
611
|
+
liveTesters.delete(this);
|
|
612
|
+
if (liveTesters.size === 0) global.MutationObserver = originalMutationObserver;
|
|
613
|
+
}
|
|
614
|
+
insertText(text) {
|
|
615
|
+
this.assertAlive();
|
|
616
|
+
insertText(this.view, text);
|
|
617
|
+
}
|
|
618
|
+
selectText(selection) {
|
|
619
|
+
this.assertAlive();
|
|
620
|
+
this.view.dispatch(this.view.state.tr.setSelection(resolveSelection(this.doc, selection)));
|
|
621
|
+
}
|
|
622
|
+
assertAlive() {
|
|
623
|
+
if (this.destroyed) throw new Error("This ProseMirrorTester has been destroyed. Testers are destroyed automatically after each test; pass { autoCleanup: false } to keep one alive (e.g. across a beforeAll).");
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region src/stringifyProseMirrorNode.ts
|
|
628
|
+
function stringifyProseMirrorNode(node, indentation = "") {
|
|
629
|
+
if (node.type.spec.toDebugString !== void 0) return `${indentation}${wrapMarks(node.marks, node.type.spec.toDebugString(node))}`;
|
|
630
|
+
if (node.type.name === "text") return `${indentation}${wrapMarks(node.marks, escapeText(node.textContent))}`;
|
|
631
|
+
const type = node.type.name;
|
|
632
|
+
const content = [];
|
|
633
|
+
const nextIndentation = `${indentation} `;
|
|
634
|
+
if (Object.keys(node.attrs).length > 0) content.push(`${nextIndentation}${stringifyObject(node.attrs, {
|
|
635
|
+
indent: " ",
|
|
636
|
+
inlineCharacterLimit: 1e3
|
|
637
|
+
})},`);
|
|
638
|
+
node.content.forEach((item) => {
|
|
639
|
+
content.push(`${stringifyProseMirrorNode(item, nextIndentation)},`);
|
|
640
|
+
});
|
|
641
|
+
let body;
|
|
642
|
+
if (content.length === 0) body = `${type}()`;
|
|
643
|
+
else if (content.length === 1 && node.content.firstChild?.type.name === "text") body = `${type}(${stringifyProseMirrorNode(node.content.firstChild, "")})`;
|
|
644
|
+
else body = `${type}(\n${content.join("\n")}\n${indentation})`;
|
|
645
|
+
return `${indentation}${wrapMarks(node.marks, body)}`;
|
|
646
|
+
}
|
|
647
|
+
function escapeText(text) {
|
|
648
|
+
return `'${text.replace(/[\p{Cc}'\\]/gu, (char) => {
|
|
649
|
+
switch (char) {
|
|
650
|
+
case "\f": return "\\f";
|
|
651
|
+
case "\n": return "\\n";
|
|
652
|
+
case "\r": return "\\r";
|
|
653
|
+
case " ": return "\\t";
|
|
654
|
+
case "\b": return "\\b";
|
|
655
|
+
case "'": return "\\'";
|
|
656
|
+
case "\\": return "\\\\";
|
|
657
|
+
default: return `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
658
|
+
}
|
|
659
|
+
})}'`;
|
|
660
|
+
}
|
|
661
|
+
function wrapMarks(marks, origContent) {
|
|
662
|
+
let content = origContent;
|
|
663
|
+
for (const mark of [...marks].reverse()) {
|
|
664
|
+
const hasAttrs = Object.keys(mark.attrs).length > 0;
|
|
665
|
+
const items = [content];
|
|
666
|
+
if (hasAttrs) items.unshift(stringifyObject(mark.attrs, {
|
|
667
|
+
indent: " ",
|
|
668
|
+
inlineCharacterLimit: 1e3
|
|
669
|
+
}));
|
|
670
|
+
content = `${mark.type.name}(${items.join(", ")})`;
|
|
671
|
+
}
|
|
672
|
+
return content;
|
|
673
|
+
}
|
|
674
|
+
//#endregion
|
|
675
|
+
//#region src/index.ts
|
|
676
|
+
afterEach(cleanupTesters);
|
|
677
|
+
expect.extend({ toEqualProseMirrorNode(received, expected) {
|
|
678
|
+
const receivedDoc = `\n${stringifyProseMirrorNode(received)}\n`;
|
|
679
|
+
const expectedDoc = `\n${stringifyProseMirrorNode(expected)}\n`;
|
|
680
|
+
const sameSchema = received.type.schema === expected.type.schema;
|
|
681
|
+
const pass = sameSchema && this.equals(receivedDoc, expectedDoc);
|
|
682
|
+
return {
|
|
683
|
+
message: pass ? () => `${this.utils.matcherHint(".not.toEqualProsemirrorNode")}\n\nExpected value of document to not equal:\n ${this.utils.printExpected(expectedDoc)}\nActual:\n ${this.utils.printReceived(receivedDoc)}` : () => {
|
|
684
|
+
if (!sameSchema && this.equals(receivedDoc, expectedDoc)) return `${this.utils.matcherHint(".toEqualProsemirrorNode")}\n\nThe documents stringify identically but come from different schemas:\n${this.utils.printReceived(receivedDoc)}`;
|
|
685
|
+
const diffString = this.utils.diff(expectedDoc, receivedDoc);
|
|
686
|
+
return `${this.utils.matcherHint(".toEqualProsemirrorNode")}\n\nExpected value of document to equal:\n${this.utils.printExpected(expectedDoc)}\nActual:\n${this.utils.printReceived(receivedDoc)}${diffString === void 0 ? "" : `\n\nDifference:\n\n${diffString}`}`;
|
|
687
|
+
},
|
|
688
|
+
pass
|
|
689
|
+
};
|
|
690
|
+
} });
|
|
691
|
+
//#endregion
|
|
692
|
+
export { ProseMirrorTester };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-prosemirror",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A plugin for Vitest that enables you to write tests using the ProseMirror editor",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vitest",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"type": "module",
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
|
24
|
+
"types": "./dist/vitest-prosemirror.d.ts",
|
|
24
25
|
"import": "./dist/vitest-prosemirror.js"
|
|
25
26
|
}
|
|
26
27
|
},
|
|
@@ -32,51 +33,67 @@
|
|
|
32
33
|
"scripts": {
|
|
33
34
|
"prebuild": "npm run clean",
|
|
34
35
|
"build": "vite build",
|
|
36
|
+
"check": "FORCE_COLOR=1 run-p -c --aggregate-output check:*",
|
|
37
|
+
"check:js-compat": "es-check es2023 --module --checkFeatures 'dist/**/*.js'",
|
|
35
38
|
"clean": "rimraf dist/*",
|
|
36
|
-
"lint": "
|
|
39
|
+
"lint": "FORCE_COLOR=1 run-p -c --aggregate-output lint:*",
|
|
40
|
+
"lint:eslint": "eslint",
|
|
41
|
+
"lint:ts": "FORCE_COLOR=1 run-p -c --aggregate-output lint:ts:*",
|
|
42
|
+
"lint:ts:attw": "attw --pack",
|
|
43
|
+
"lint:ts:typecheck": "tsc --noEmit",
|
|
44
|
+
"prepack": "npm run build",
|
|
37
45
|
"start": "vite build --watch",
|
|
38
|
-
"test": "vitest",
|
|
39
|
-
"test-
|
|
46
|
+
"test": "vitest run --coverage",
|
|
47
|
+
"test-watch": "vitest"
|
|
40
48
|
},
|
|
41
49
|
"dependencies": {
|
|
42
50
|
"prosemirror-model": "^1.24.1",
|
|
43
51
|
"prosemirror-state": "^1.4.3",
|
|
44
52
|
"prosemirror-view": "^1.38.0",
|
|
45
|
-
"stringify-object": "^
|
|
53
|
+
"stringify-object": "^7.0.0"
|
|
46
54
|
},
|
|
47
55
|
"devDependencies": {
|
|
56
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
48
57
|
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.0",
|
|
49
|
-
"@eslint/js": "^
|
|
50
|
-
"@eslint/json": "^0.
|
|
51
|
-
"@eslint/markdown": "^
|
|
52
|
-
"@
|
|
58
|
+
"@eslint/js": "^10.0.1",
|
|
59
|
+
"@eslint/json": "^2.0.0",
|
|
60
|
+
"@eslint/markdown": "^8.0.0",
|
|
61
|
+
"@microsoft/api-extractor": "^7.58.7",
|
|
62
|
+
"@types/node": "^26.0.0",
|
|
53
63
|
"@types/stringify-object": "^4.0.5",
|
|
54
|
-
"@vitest/coverage-v8": "^
|
|
64
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
55
65
|
"@vitest/eslint-plugin": "^1.3.26",
|
|
56
|
-
"
|
|
66
|
+
"es-check": "^9.6.4",
|
|
67
|
+
"eslint": "^10.0.2",
|
|
57
68
|
"eslint-config-prettier": "^10.0.1",
|
|
58
|
-
"eslint-plugin-package-json": "^0.
|
|
59
|
-
"eslint-plugin-perfectionist": "^
|
|
69
|
+
"eslint-plugin-package-json": "^1.0.0",
|
|
70
|
+
"eslint-plugin-perfectionist": "^5.0.0",
|
|
60
71
|
"eslint-plugin-prefer-arrow-functions": "^3.9.1",
|
|
61
72
|
"eslint-plugin-prettier": "^5.1.3",
|
|
62
|
-
"jsdom": "^
|
|
73
|
+
"jsdom": "^30.0.0",
|
|
74
|
+
"npm-run-all2": "^9.0.3",
|
|
63
75
|
"prettier": "^3.3.0",
|
|
64
76
|
"prosemirror-commands": "^1.7.1",
|
|
65
77
|
"prosemirror-inputrules": "^1.5.0",
|
|
66
78
|
"prosemirror-keymap": "^1.2.3",
|
|
67
79
|
"prosemirror-schema-basic": "^1.2.3",
|
|
68
80
|
"rimraf": "^6.0.1",
|
|
69
|
-
"typescript": "^
|
|
70
|
-
"typescript-eslint": "^8.
|
|
71
|
-
"
|
|
72
|
-
"vite
|
|
81
|
+
"typescript": "^6.0.2",
|
|
82
|
+
"typescript-eslint": "^8.58.0",
|
|
83
|
+
"unplugin-dts": "^1.0.1",
|
|
84
|
+
"vite": "^8.0.0",
|
|
85
|
+
"vitest": "^5.0.0",
|
|
86
|
+
"w3c-keyname": "^2.2.8"
|
|
73
87
|
},
|
|
74
88
|
"peerDependencies": {
|
|
75
|
-
"vitest": "^2.1.9 || ^3.0.7 || ^4.0.4"
|
|
89
|
+
"vitest": "^2.1.9 || ^3.0.7 || ^4.0.4 || ^5.0.0"
|
|
76
90
|
},
|
|
77
91
|
"peerDependenciesMeta": {
|
|
78
92
|
"vitest": {
|
|
79
93
|
"optional": true
|
|
80
94
|
}
|
|
95
|
+
},
|
|
96
|
+
"engines": {
|
|
97
|
+
"node": ">=20.19.0"
|
|
81
98
|
}
|
|
82
99
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vitest-prosemirror.js","sources":["../src/stringifyProseMirrorNode.ts","../src/utils/keyboardInput.ts","../src/ProseMirrorTester.ts","../src/index.ts"],"sourcesContent":["import type { Mark, Node } from \"prosemirror-model\";\n\nimport stringifyObject from \"stringify-object\";\n\nfunction getMarks(marks: ReadonlyArray<Mark>, origContent: string): string {\n let content = `'${origContent}'`;\n\n for (const mark of [...marks].reverse()) {\n const hasAttrs = Object.keys(mark.attrs).length > 0;\n const items: Array<string> = [content];\n\n if (hasAttrs) {\n items.unshift(\n stringifyObject(mark.attrs, {\n indent: \" \",\n inlineCharacterLimit: 1000,\n }),\n );\n }\n\n content = `${mark.type.name}(${items.join(\", \")})`;\n }\n\n return content;\n}\n\nconst renamedTypes: Record<string, string> = {\n hardBreak: \"br\",\n heading: \"h\",\n horizontalRule: \"hr\",\n paragraph: \"p\",\n};\n\nexport function stringifyProseMirrorNode(node: Node, indentation = \"\"): string {\n if (node.type.name === \"text\") {\n return `${indentation}${getMarks(node.marks, node.text ?? \"\")}`;\n }\n\n const type = renamedTypes[node.type.name] ?? node.type.name;\n const content: Array<string> = [];\n const nextIndentation = `${indentation} `;\n\n if (Object.keys(node.attrs).length > 0) {\n content.push(\n `${nextIndentation}${stringifyObject(node.attrs, { indent: \" \", inlineCharacterLimit: 1000 })},`,\n );\n }\n\n node.content.forEach((item) => {\n content.push(`${stringifyProseMirrorNode(item, nextIndentation)},`);\n });\n\n if (content.length === 0) {\n return `${indentation}${type}()`;\n }\n\n if (content.length === 1 && node.content.firstChild?.type.name === \"text\") {\n return `${indentation}${type}(${stringifyProseMirrorNode(node.content.firstChild, \"\")})`;\n }\n\n return `${indentation}${type}(\\n${content.join(\"\\n\")}\\n${indentation})`;\n}\n","export function tokenizeKeyboardInput(input: string): Array<string> {\n const output = [];\n\n let currentGroupOpener: \"[\" | \"{\" | null = null;\n let group = \"\";\n for (const char of input) {\n if (currentGroupOpener !== null) {\n if ([\"]\", \"}\"].includes(char)) {\n if (\n group.endsWith(\"\\\\\") &&\n char === matchingBrace(currentGroupOpener)\n ) {\n group = group.slice(0, -2) + char;\n } else if (char === matchingBrace(currentGroupOpener)) {\n if (group.length === 4 && group.startsWith(\"Key\")) {\n output.push(group.slice(3).toLowerCase());\n } else {\n output.push(group);\n }\n currentGroupOpener = null;\n group = \"\";\n } else {\n group += char;\n }\n } else if (group === \"\" && currentGroupOpener === char) {\n output.push(char);\n currentGroupOpener = null;\n group = \"\";\n } else {\n group += char;\n }\n } else if ([\"[\", \"{\"].includes(char)) {\n currentGroupOpener = char as \"[\" | \"{\";\n } else {\n output.push(char);\n }\n }\n\n output.forEach(assertSupported);\n return output;\n}\n\nfunction assertSupported(character: string): never | void {\n if (/^\\/.+/u.exec(character) || /.+>[\\d]*\\/?$/u.exec(character)) {\n throw new Error(\"Unsupported keyboard input\");\n }\n}\n\nfunction matchingBrace(opener: \"[\" | \"{\"): \"]\" | \"}\" {\n return opener === \"{\" ? \"}\" : \"]\";\n}\n","import type { Node as ProseMirrorNode, Schema } from \"prosemirror-model\";\n\nimport {\n AllSelection,\n EditorState,\n type Plugin,\n type Selection,\n TextSelection,\n} from \"prosemirror-state\";\nimport { EditorView } from \"prosemirror-view\";\n\nimport { tokenizeKeyboardInput } from \"./utils/keyboardInput\";\n\nexport interface KeyboardModifiers {\n altKey?: boolean;\n ctrlKey?: boolean;\n metaKey?: boolean;\n shiftKey?: boolean;\n}\n\nexport interface Options {\n plugins: Array<Plugin>;\n}\n\nexport type TesterSelection =\n | \"all\"\n | \"end\"\n | \"start\"\n | { from: number; to: number }\n | Selection\n | number;\n\ntype UsableMutationRecord = Omit<\n MutationRecord,\n \"addedNodes\" | \"removedNodes\"\n> & {\n addedNodes: Array<Node>;\n removedNodes: Array<Node>;\n};\n\nclass KeyboardEventMock extends KeyboardEvent {\n private readonly onPreventDefault: () => void;\n\n public constructor(\n onPreventDefault: () => void,\n type: string,\n eventInitDict?: KeyboardEventInit,\n ) {\n super(type, eventInitDict);\n this.onPreventDefault = onPreventDefault;\n }\n public override preventDefault(): void {\n super.preventDefault();\n this.onPreventDefault();\n }\n}\n\nclass MutationObserverMock {\n private static readonly activeObservers: Map<Node, MutationObserverMock> =\n new Map<Node, MutationObserverMock>();\n\n private readonly callback: MutationCallback;\n private target: Node | undefined;\n\n public constructor(callback: MutationCallback) {\n this.callback = callback;\n this.target = undefined;\n }\n\n public static createMutation(\n target: Node,\n mutationRecords: Array<UsableMutationRecord>,\n ): void {\n const observer = MutationObserverMock.activeObservers.get(target);\n if (observer === undefined) {\n return;\n }\n observer.callback(\n mutationRecords as unknown as Array<MutationRecord>,\n observer,\n );\n }\n\n public disconnect(): void {\n if (this.target !== undefined) {\n MutationObserverMock.activeObservers.delete(this.target);\n }\n this.target = undefined;\n }\n\n public observe(target: Node): void {\n this.target = target;\n MutationObserverMock.activeObservers.set(target, this);\n }\n\n // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Mocking another method\n public takeRecords(): Array<MutationRecord> {\n return [];\n }\n}\n\nexport class ProseMirrorTester {\n public get doc(): ProseMirrorNode {\n return this.view.state.doc;\n }\n\n public get schema(): Schema {\n return this.view.state.schema;\n }\n\n private readonly view: EditorView;\n\n public constructor(\n documentRoot: ProseMirrorNode,\n options: Partial<Options> = {},\n ) {\n if (typeof document === \"undefined\") {\n throw new Error(\"TODO\");\n }\n\n const element = document.createElement(\"div\");\n document.body.append(element);\n\n const state = EditorState.create({\n doc: documentRoot,\n plugins: options.plugins ?? [],\n });\n\n global.MutationObserver = MutationObserverMock;\n\n this.view = new EditorView(element, {\n state,\n });\n }\n\n public insertText(text: string, modifiers?: KeyboardModifiers): void {\n for (const key of tokenizeKeyboardInput(text)) {\n const character = keyToChar(key);\n\n let keydownPrevented = false;\n this.view.dispatchEvent(\n new KeyboardEventMock(\n () => {\n keydownPrevented = true;\n },\n \"keydown\",\n {\n bubbles: true,\n charCode: character.charCodeAt(0),\n key,\n ...modifiers,\n },\n ),\n );\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- False positive due to the value being set in a callback\n if (keydownPrevented) {\n continue;\n }\n\n this.view.dispatchEvent(\n new KeyboardEvent(\"keypress\", {\n bubbles: true,\n charCode: character.charCodeAt(0),\n key,\n keyCode: character.charCodeAt(0),\n ...modifiers,\n }),\n );\n\n const domNode = this.view.domAtPos(this.view.state.selection.from).node;\n if (\n domNode.childNodes.length === 1 &&\n domNode.firstChild instanceof HTMLBRElement &&\n domNode.firstChild.classList.contains(\"ProseMirror-trailingBreak\")\n ) {\n const brNode = domNode.firstChild;\n const textNode = new Text(character);\n domNode.removeChild(brNode);\n domNode.appendChild(textNode);\n MutationObserverMock.createMutation(this.view.dom, [\n {\n addedNodes: [textNode],\n attributeName: null,\n attributeNamespace: null,\n nextSibling: brNode,\n oldValue: null,\n previousSibling: null,\n removedNodes: [],\n target: domNode,\n type: \"childList\",\n },\n {\n addedNodes: [],\n attributeName: null,\n attributeNamespace: null,\n nextSibling: null,\n oldValue: null,\n previousSibling: textNode,\n removedNodes: [brNode],\n target: domNode,\n type: \"childList\",\n },\n ]);\n } else {\n const target = findLastCharacterDataNode(domNode);\n if (target === null) {\n continue;\n }\n const oldValue = target.data;\n const domOffset =\n this.view.state.selection.from - this.view.posAtDOM(target, 0);\n target.data =\n target.data.slice(0, domOffset) +\n character +\n target.data.slice(domOffset);\n MutationObserverMock.createMutation(this.view.dom, [\n {\n addedNodes: [],\n attributeName: null,\n attributeNamespace: null,\n nextSibling: null,\n oldValue,\n previousSibling: null,\n removedNodes: [],\n target,\n type: \"characterData\",\n },\n ]);\n }\n\n this.view.dispatchEvent(\n new KeyboardEvent(\"keyup\", {\n bubbles: true,\n charCode: character.charCodeAt(0),\n key,\n keyCode: character.charCodeAt(0),\n ...modifiers,\n }),\n );\n }\n }\n\n public selectText(selection: TesterSelection): void {\n this.view.dispatch(\n this.view.state.tr.setSelection(this.getSelection(selection)),\n );\n }\n\n private getSelection(selection: TesterSelection): Selection {\n if (selection === \"all\") {\n return new AllSelection(this.doc);\n }\n\n if (\n typeof selection === \"object\" &&\n \"$anchor\" in selection &&\n \"$head\" in selection\n ) {\n return selection;\n }\n\n if (\n typeof selection === \"object\" &&\n \"from\" in selection &&\n \"to\" in selection\n ) {\n return TextSelection.between(\n this.doc.resolve(selection.from),\n this.doc.resolve(selection.to),\n );\n }\n\n let pos = 0;\n if (selection === \"start\") {\n pos = 0;\n } else if (selection === \"end\") {\n pos = this.doc.nodeSize - 2;\n } else {\n pos = selection;\n }\n\n return TextSelection.near(this.doc.resolve(pos));\n }\n}\n\nfunction findLastCharacterDataNode(node: Node): CharacterData | null {\n if (node instanceof CharacterData) {\n return node;\n }\n for (const child of Array.from(node.childNodes).reverse()) {\n const textNode = findLastCharacterDataNode(child);\n if (textNode !== null) {\n return textNode;\n }\n }\n return null;\n}\n\nfunction keyToChar(key: string): string {\n if (key === \"Enter\") {\n return \"\\n\";\n }\n if (key === \"Tab\") {\n return \"\\t\";\n }\n return key;\n}\n","import type { Node } from \"prosemirror-model\";\n\nimport { expect } from \"vitest\";\n\nimport { stringifyProseMirrorNode } from \"./stringifyProseMirrorNode\";\n\nexport {\n type KeyboardModifiers,\n type Options,\n ProseMirrorTester,\n type TesterSelection,\n} from \"./ProseMirrorTester\";\n\nexport interface CustomMatchers<R = unknown> {\n toEqualProseMirrorNode(expected: Node): R;\n}\n\n/* eslint-disable @typescript-eslint/no-empty-object-type, @typescript-eslint/no-explicit-any -- These are overridest for vitest matchers */\n\ndeclare module \"vitest\" {\n interface Assertion<T = any> extends CustomMatchers<T> {}\n interface AsymmetricMatchersContaining extends CustomMatchers {}\n}\n\n/* eslint-enable */\n\nexpect.extend({\n toEqualProseMirrorNode(received: Node, expected: Node) {\n const receivedDoc = `\\n${stringifyProseMirrorNode(received)}\\n`;\n const expectedDoc = `\\n${stringifyProseMirrorNode(expected)}\\n`;\n const pass = this.equals(receivedDoc, expectedDoc);\n const message = pass\n ? (): string =>\n `${this.utils.matcherHint(\".not.toEqualProsemirrorNode\")}\\n\\n` +\n `Expected value of document to not equal:\\n ${this.utils.printExpected(expectedDoc)}\\n` +\n `Actual:\\n ${this.utils.printReceived(receivedDoc)}`\n : (): string => {\n const diffString = this.utils.diff(expectedDoc, receivedDoc, {\n expand: this.expand ?? false,\n });\n return `${this.utils.matcherHint(\".toEqualProsemirrorNode\")}\\n\\nExpected value of document to equal:\\n${this.utils.printExpected(expectedDoc)}\\nActual:\\n${this.utils.printReceived(receivedDoc)}${diffString !== undefined ? `\\n\\nDifference:\\n\\n${diffString}` : \"\"}`;\n };\n return {\n message,\n pass,\n };\n },\n});\n"],"names":[],"mappings":"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"}
|