@khanacademy/perseus-linter 0.0.0-PR973-20240207204706 → 0.0.0-PR973-20240207213425
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/.eslintrc.js +12 -0
- package/CHANGELOG.md +168 -0
- package/package.json +4 -7
- package/src/README.md +41 -0
- package/src/__tests__/matcher.test.ts +498 -0
- package/src/__tests__/rule.test.ts +110 -0
- package/src/__tests__/rules.test.ts +548 -0
- package/src/__tests__/selector-parser.test.ts +51 -0
- package/src/__tests__/tree-transformer.test.ts +444 -0
- package/src/index.ts +281 -0
- package/src/proptypes.ts +19 -0
- package/src/rule.ts +419 -0
- package/src/rules/absolute-url.ts +23 -0
- package/src/rules/all-rules.ts +71 -0
- package/src/rules/blockquoted-math.ts +9 -0
- package/src/rules/blockquoted-widget.ts +9 -0
- package/src/rules/double-spacing-after-terminal.ts +11 -0
- package/src/rules/extra-content-spacing.ts +11 -0
- package/src/rules/heading-level-1.ts +13 -0
- package/src/rules/heading-level-skip.ts +19 -0
- package/src/rules/heading-sentence-case.ts +10 -0
- package/src/rules/heading-title-case.ts +68 -0
- package/src/rules/image-alt-text.ts +20 -0
- package/src/rules/image-in-table.ts +9 -0
- package/src/rules/image-spaces-around-urls.ts +34 -0
- package/src/rules/image-widget.ts +49 -0
- package/src/rules/link-click-here.ts +10 -0
- package/src/rules/lint-utils.ts +47 -0
- package/src/rules/long-paragraph.ts +13 -0
- package/src/rules/math-adjacent.ts +9 -0
- package/src/rules/math-align-extra-break.ts +10 -0
- package/src/rules/math-align-linebreaks.ts +42 -0
- package/src/rules/math-empty.ts +9 -0
- package/src/rules/math-font-size.ts +11 -0
- package/src/rules/math-frac.ts +9 -0
- package/src/rules/math-nested.ts +10 -0
- package/src/rules/math-starts-with-space.ts +11 -0
- package/src/rules/math-text-empty.ts +9 -0
- package/src/rules/math-without-dollars.ts +13 -0
- package/src/rules/nested-lists.ts +10 -0
- package/src/rules/profanity.ts +9 -0
- package/src/rules/table-missing-cells.ts +19 -0
- package/src/rules/unbalanced-code-delimiters.ts +13 -0
- package/src/rules/unescaped-dollar.ts +9 -0
- package/src/rules/widget-in-table.ts +9 -0
- package/src/selector.ts +504 -0
- package/src/tree-transformer.ts +583 -0
- package/src/types.ts +7 -0
- package/src/version.ts +10 -0
- package/tsconfig-build.json +12 -0
- package/tsconfig-build.tsbuildinfo +1 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import TreeTransformer from "../tree-transformer";
|
|
2
|
+
|
|
3
|
+
import type {TraversalState} from "../tree-transformer";
|
|
4
|
+
|
|
5
|
+
describe("PerseusLinter tree transformer", () => {
|
|
6
|
+
function clone(o) {
|
|
7
|
+
return JSON.parse(JSON.stringify(o));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const tree1 = {
|
|
11
|
+
id: 0,
|
|
12
|
+
type: "root",
|
|
13
|
+
content: [
|
|
14
|
+
{id: 1, type: "text", content: "Hello, "},
|
|
15
|
+
{
|
|
16
|
+
id: 2,
|
|
17
|
+
type: "em",
|
|
18
|
+
content: {
|
|
19
|
+
id: 3,
|
|
20
|
+
type: "text",
|
|
21
|
+
content: "World!",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 4,
|
|
26
|
+
type: "list",
|
|
27
|
+
items: [
|
|
28
|
+
{id: 5, type: "text", content: "A"},
|
|
29
|
+
{id: 6, type: "text", content: "B"},
|
|
30
|
+
{id: 7, type: "text", content: "C"},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
// These are three variants on the same tree where we use arrays
|
|
37
|
+
// instead of single nodes. The tests below will be run over all
|
|
38
|
+
// four variants, and should work the same for all.
|
|
39
|
+
const tree2 = [clone(tree1)];
|
|
40
|
+
const tree3 = clone(tree1);
|
|
41
|
+
tree3.content[1].content = [tree3.content[1].content];
|
|
42
|
+
const tree4 = clone(tree2);
|
|
43
|
+
tree4[0].content[1].content = [tree4[0].content[1].content];
|
|
44
|
+
|
|
45
|
+
const trees = [tree1, tree2, tree3, tree4];
|
|
46
|
+
|
|
47
|
+
const postOrderTraversalOrder = [1, 3, 2, 5, 6, 7, 4, 0];
|
|
48
|
+
const parentNodeIds = [-1, 0, 0, 2, 0, 4, 4, 4];
|
|
49
|
+
const previousNodeIds = [-1, -1, 1, -1, 2, -1, 5, 6];
|
|
50
|
+
const nextNodeIds = [-1, 2, 4, -1, -1, 6, 7, -1];
|
|
51
|
+
|
|
52
|
+
// The first test will fill in this array mapping numbers to nodes
|
|
53
|
+
// Then subsequent tests can use it
|
|
54
|
+
const nodes: Array<any> = [];
|
|
55
|
+
|
|
56
|
+
function getTraversalOrder(tree: any) {
|
|
57
|
+
const order: Array<any> = [];
|
|
58
|
+
new TreeTransformer(tree).traverse((n, state) => {
|
|
59
|
+
// @ts-expect-error - TS2339 - Property 'id' does not exist on type 'TreeNode'.
|
|
60
|
+
order.push(n.id);
|
|
61
|
+
});
|
|
62
|
+
return order;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
trees.forEach((tree: any, treenum: number) => {
|
|
66
|
+
it(
|
|
67
|
+
"does post-order traversal of each node in the tree " + treenum,
|
|
68
|
+
() => {
|
|
69
|
+
const tt = new TreeTransformer(tree);
|
|
70
|
+
const ids: Array<any> = [];
|
|
71
|
+
|
|
72
|
+
tt.traverse((n: any) => {
|
|
73
|
+
nodes[n.id] = n; // Remember the nodes by id for later tests
|
|
74
|
+
ids.push(n.id);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Post-order traversal means we visit the nodes on the way
|
|
78
|
+
// back up, not on the way down.
|
|
79
|
+
expect(ids).toEqual(postOrderTraversalOrder);
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
it("tracks the current node " + treenum, () => {
|
|
84
|
+
new TreeTransformer(tree).traverse((n, state) => {
|
|
85
|
+
expect(state.currentNode()).toEqual(n);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("correctly gets the siblings for each node " + treenum, () => {
|
|
90
|
+
new TreeTransformer(tree).traverse((n: any, state) => {
|
|
91
|
+
const previd = previousNodeIds[n.id];
|
|
92
|
+
expect(state.hasPreviousSibling()).toEqual(previd >= 0);
|
|
93
|
+
expect(state.previousSibling()).toEqual(
|
|
94
|
+
previd >= 0 ? nodes[previd] : null,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const nextid = nextNodeIds[n.id];
|
|
98
|
+
expect(state.nextSibling()).toEqual(
|
|
99
|
+
nextid >= 0 ? nodes[nextid] : null,
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("knows the ancestors for each node: " + treenum, () => {
|
|
105
|
+
const ancestorsById = [
|
|
106
|
+
[],
|
|
107
|
+
[0],
|
|
108
|
+
[0],
|
|
109
|
+
[0, 2],
|
|
110
|
+
[0],
|
|
111
|
+
[0, 4],
|
|
112
|
+
[0, 4],
|
|
113
|
+
[0, 4],
|
|
114
|
+
];
|
|
115
|
+
const ancestorTypesById = [
|
|
116
|
+
[],
|
|
117
|
+
["root"],
|
|
118
|
+
["root"],
|
|
119
|
+
["root", "em"],
|
|
120
|
+
["root"],
|
|
121
|
+
["root", "list"],
|
|
122
|
+
["root", "list"],
|
|
123
|
+
["root", "list"],
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
new TreeTransformer(tree).traverse((n: any, state) => {
|
|
127
|
+
expect(state.hasParent()).toEqual(
|
|
128
|
+
ancestorsById[n.id].length > 0,
|
|
129
|
+
);
|
|
130
|
+
const ancestorids = ancestorsById[n.id];
|
|
131
|
+
expect(state.parent()).toEqual(
|
|
132
|
+
nodes[ancestorids[ancestorids.length - 1]],
|
|
133
|
+
);
|
|
134
|
+
expect(state.ancestors()).toEqual(
|
|
135
|
+
ancestorsById[n.id].map((id) => nodes[id]),
|
|
136
|
+
);
|
|
137
|
+
expect(ancestorsById[n.id].map((id) => nodes[id].type)).toEqual(
|
|
138
|
+
ancestorTypesById[n.id],
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("computes the textContent for each node " + treenum, () => {
|
|
144
|
+
const textContentForNode = [
|
|
145
|
+
"Hello, World!ABC",
|
|
146
|
+
"Hello, ",
|
|
147
|
+
"World!",
|
|
148
|
+
"World!",
|
|
149
|
+
"ABC",
|
|
150
|
+
"A",
|
|
151
|
+
"B",
|
|
152
|
+
"C",
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
new TreeTransformer(tree).traverse((n: any, state, content) => {
|
|
156
|
+
expect(content).toEqual(textContentForNode[n.id]);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("can remove the next sibling " + treenum, () => {
|
|
161
|
+
const expectedTraversals = [
|
|
162
|
+
// if a node has no next sibling, the test is a no-op
|
|
163
|
+
postOrderTraversalOrder,
|
|
164
|
+
[1, 5, 6, 7, 4, 0],
|
|
165
|
+
[1, 3, 2, 0],
|
|
166
|
+
postOrderTraversalOrder,
|
|
167
|
+
postOrderTraversalOrder,
|
|
168
|
+
[1, 3, 2, 5, 7, 4, 0],
|
|
169
|
+
[1, 3, 2, 5, 6, 4, 0],
|
|
170
|
+
postOrderTraversalOrder,
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
// For each node in the tree
|
|
174
|
+
for (let id = 0; id < nodes.length; id++) {
|
|
175
|
+
// Start with a copy of the tree
|
|
176
|
+
const copy = clone(tree);
|
|
177
|
+
|
|
178
|
+
// Remove the next sibling of the node with this id
|
|
179
|
+
new TreeTransformer(copy).traverse((n: any, state: any) => {
|
|
180
|
+
if (n.id === id) {
|
|
181
|
+
state.removeNextSibling();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Ensure that we don't iterate the removed sibling
|
|
185
|
+
expect(n.id).not.toEqual(nextNodeIds[id]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// And then get the traversal order of the resulting tree
|
|
189
|
+
const traversal = getTraversalOrder(copy);
|
|
190
|
+
|
|
191
|
+
// Compare it to the expected value
|
|
192
|
+
expect(traversal).toEqual(expectedTraversals[id]);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("won't try to replace the root of the tree " + treenum, () => {
|
|
197
|
+
const copy = clone(tree);
|
|
198
|
+
new TreeTransformer(copy).traverse((n, state) => {
|
|
199
|
+
if (n === state.root) {
|
|
200
|
+
expect(() => state.replace()).toThrow();
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("Can remove nodes " + treenum, () => {
|
|
206
|
+
const expectedTraversals = [
|
|
207
|
+
null,
|
|
208
|
+
[3, 2, 5, 6, 7, 4, 0],
|
|
209
|
+
[1, 5, 6, 7, 4, 0],
|
|
210
|
+
[1, 2, 5, 6, 7, 4, 0],
|
|
211
|
+
[1, 3, 2, 0],
|
|
212
|
+
[1, 3, 2, 6, 7, 4, 0],
|
|
213
|
+
[1, 3, 2, 5, 7, 4, 0],
|
|
214
|
+
[1, 3, 2, 5, 6, 4, 0],
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
// Loop through all the nodes except the root
|
|
218
|
+
for (let id = 1; id < nodes.length; id++) {
|
|
219
|
+
// Make a copy of the tree
|
|
220
|
+
const copy = clone(tree);
|
|
221
|
+
// Remove this node from it
|
|
222
|
+
new TreeTransformer(copy).traverse((n: any, state: any) => {
|
|
223
|
+
if (n.id === id) {
|
|
224
|
+
state.replace();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Traverse what remains and see if we get what is expected
|
|
229
|
+
expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("Can replace nodes " + treenum, () => {
|
|
234
|
+
const expectedTraversals = [
|
|
235
|
+
null,
|
|
236
|
+
[99, 3, 2, 5, 6, 7, 4, 0],
|
|
237
|
+
[1, 99, 5, 6, 7, 4, 0],
|
|
238
|
+
[1, 99, 2, 5, 6, 7, 4, 0],
|
|
239
|
+
[1, 3, 2, 99, 0],
|
|
240
|
+
[1, 3, 2, 99, 6, 7, 4, 0],
|
|
241
|
+
[1, 3, 2, 5, 99, 7, 4, 0],
|
|
242
|
+
[1, 3, 2, 5, 6, 99, 4, 0],
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
// Loop through all the nodes except the root
|
|
246
|
+
for (let id = 1; id < nodes.length; id++) {
|
|
247
|
+
// Make a copy of the tree
|
|
248
|
+
const copy = clone(tree);
|
|
249
|
+
// Replace the node with a different one
|
|
250
|
+
new TreeTransformer(copy).traverse((n: any, state) => {
|
|
251
|
+
if (n.id === id) {
|
|
252
|
+
state.replace({
|
|
253
|
+
// @ts-expect-error - TS2345 - Argument of type '{ id: number; type: string; }' is not assignable to parameter of type 'TreeNode'.
|
|
254
|
+
id: 99,
|
|
255
|
+
type: "replacement",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Ensure that we don't traverse the new node
|
|
260
|
+
expect(n.id).not.toEqual(99);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Traverse what remains and see if we get what is expected
|
|
264
|
+
expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("Can reparent nodes " + treenum, () => {
|
|
269
|
+
const expectedTraversals = [
|
|
270
|
+
null,
|
|
271
|
+
[1, 99, 3, 2, 5, 6, 7, 4, 0],
|
|
272
|
+
[1, 3, 2, 99, 5, 6, 7, 4, 0],
|
|
273
|
+
[1, 3, 99, 2, 5, 6, 7, 4, 0],
|
|
274
|
+
[1, 3, 2, 5, 6, 7, 4, 99, 0],
|
|
275
|
+
[1, 3, 2, 5, 99, 6, 7, 4, 0],
|
|
276
|
+
[1, 3, 2, 5, 6, 99, 7, 4, 0],
|
|
277
|
+
[1, 3, 2, 5, 6, 7, 99, 4, 0],
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
// Loop through all the nodes except the root
|
|
281
|
+
for (let id = 1; id < nodes.length; id++) {
|
|
282
|
+
// Make a copy of the tree
|
|
283
|
+
const copy = clone(tree);
|
|
284
|
+
let count = 0;
|
|
285
|
+
// Replace the node with a different one
|
|
286
|
+
new TreeTransformer(copy).traverse((n: any, state) => {
|
|
287
|
+
if (n.id === id) {
|
|
288
|
+
// Ensure that we don't traverse the node more than once
|
|
289
|
+
expect(++count).toEqual(1);
|
|
290
|
+
state.replace({
|
|
291
|
+
// @ts-expect-error - TS2345 - Argument of type '{ id: number; type: string; content: any; }' is not assignable to parameter of type 'TreeNode'.
|
|
292
|
+
id: 99,
|
|
293
|
+
type: "reparent",
|
|
294
|
+
content: n,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Ensure that we don't traverse the new node
|
|
299
|
+
expect(n.id).not.toEqual(99);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Traverse what remains and see if we get what is expected
|
|
303
|
+
expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("Can replace nodes with an array of nodes " + treenum, () => {
|
|
308
|
+
const expectedTraversals = [
|
|
309
|
+
null,
|
|
310
|
+
[99, 101, 100, 3, 2, 5, 6, 7, 4, 0],
|
|
311
|
+
[1, 99, 101, 100, 5, 6, 7, 4, 0],
|
|
312
|
+
[1, 99, 101, 100, 2, 5, 6, 7, 4, 0],
|
|
313
|
+
[1, 3, 2, 99, 101, 100, 0],
|
|
314
|
+
[1, 3, 2, 99, 101, 100, 6, 7, 4, 0],
|
|
315
|
+
[1, 3, 2, 5, 99, 101, 100, 7, 4, 0],
|
|
316
|
+
[1, 3, 2, 5, 6, 99, 101, 100, 4, 0],
|
|
317
|
+
];
|
|
318
|
+
|
|
319
|
+
// Loop through all the nodes except the root
|
|
320
|
+
for (let id = 1; id < nodes.length; id++) {
|
|
321
|
+
// Make a copy of the tree
|
|
322
|
+
const copy = clone(tree);
|
|
323
|
+
// Replace the node with two new ones
|
|
324
|
+
new TreeTransformer(copy).traverse((n: any, state) => {
|
|
325
|
+
if (n.id === id) {
|
|
326
|
+
// @ts-expect-error - TS2345 - Argument of type '({ id: number; type: string; } | { id: number; type: string; content: { id: number; type: string; }; })[]' is not assignable to parameter of type 'TreeNode'.
|
|
327
|
+
state.replace([
|
|
328
|
+
{
|
|
329
|
+
id: 99,
|
|
330
|
+
type: "replacement",
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: 100,
|
|
334
|
+
type: "replacement",
|
|
335
|
+
content: {
|
|
336
|
+
id: 101,
|
|
337
|
+
type: "nested",
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
]);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Ensure that we don't traverse any new nodes
|
|
344
|
+
expect(n.id).not.toEqual(99);
|
|
345
|
+
expect(n.id).not.toEqual(100);
|
|
346
|
+
expect(n.id).not.toEqual(101);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Traverse what remains and see if we get what is expected
|
|
350
|
+
expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("Can replace nodes with two nodes " + treenum, () => {
|
|
355
|
+
const expectedTraversals = [
|
|
356
|
+
null,
|
|
357
|
+
[99, 101, 100, 3, 2, 5, 6, 7, 4, 0],
|
|
358
|
+
[1, 99, 101, 100, 5, 6, 7, 4, 0],
|
|
359
|
+
[1, 99, 101, 100, 2, 5, 6, 7, 4, 0],
|
|
360
|
+
[1, 3, 2, 99, 101, 100, 0],
|
|
361
|
+
[1, 3, 2, 99, 101, 100, 6, 7, 4, 0],
|
|
362
|
+
[1, 3, 2, 5, 99, 101, 100, 7, 4, 0],
|
|
363
|
+
[1, 3, 2, 5, 6, 99, 101, 100, 4, 0],
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
// Loop through all the nodes except the root
|
|
367
|
+
for (let id = 1; id < nodes.length; id++) {
|
|
368
|
+
// Make a copy of the tree
|
|
369
|
+
const copy = clone(tree);
|
|
370
|
+
// Replace the node with two new ones
|
|
371
|
+
new TreeTransformer(copy).traverse((n: any, state) => {
|
|
372
|
+
if (n.id === id) {
|
|
373
|
+
state.replace(
|
|
374
|
+
{
|
|
375
|
+
// @ts-expect-error - TS2345 - Argument of type '{ id: number; type: string; }' is not assignable to parameter of type 'TreeNode'.
|
|
376
|
+
id: 99,
|
|
377
|
+
type: "replacement",
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
id: 100,
|
|
381
|
+
type: "replacement",
|
|
382
|
+
content: {
|
|
383
|
+
id: 101,
|
|
384
|
+
type: "nested",
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Ensure that we don't traverse any new nodes
|
|
391
|
+
expect(n.id).not.toEqual(99);
|
|
392
|
+
expect(n.id).not.toEqual(100);
|
|
393
|
+
expect(n.id).not.toEqual(101);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Traverse what remains and see if we get what is expected
|
|
397
|
+
expect(getTraversalOrder(copy)).toEqual(expectedTraversals[id]);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("goToParent() and goToPreviousSibling() work " + treenum, () => {
|
|
402
|
+
// Traverse the tree, saving copies of the state object
|
|
403
|
+
// for each node. Then check that goToParent() and
|
|
404
|
+
// goToPreviousSibling() on each saved state object
|
|
405
|
+
// modify the states in apprpriate ways.
|
|
406
|
+
const states: Array<TraversalState> = [];
|
|
407
|
+
|
|
408
|
+
const tt = new TreeTransformer(tree);
|
|
409
|
+
tt.traverse((n: any, state, content) => {
|
|
410
|
+
states[n.id] = state.clone();
|
|
411
|
+
// Verify that a clone() and equal() work as expected
|
|
412
|
+
expect(state.equals(states[n.id])).toBeTruthy();
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// Check that goToPreviousSibling() works
|
|
416
|
+
for (let n = 0; n < states.length; n++) {
|
|
417
|
+
const state = states[n].clone();
|
|
418
|
+
const previousNodeId = previousNodeIds[n];
|
|
419
|
+
if (previousNodeId === -1) {
|
|
420
|
+
expect(() => {
|
|
421
|
+
state.goToPreviousSibling();
|
|
422
|
+
}).toThrow();
|
|
423
|
+
} else {
|
|
424
|
+
state.goToPreviousSibling();
|
|
425
|
+
expect(state.equals(states[previousNodeId])).toBeTruthy();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Check that goToParent() works
|
|
430
|
+
for (let n = 0; n < states.length; n++) {
|
|
431
|
+
const state = states[n].clone();
|
|
432
|
+
const parentNodeId = parentNodeIds[n];
|
|
433
|
+
if (parentNodeId === -1) {
|
|
434
|
+
expect(() => {
|
|
435
|
+
state.goToParent();
|
|
436
|
+
}).toThrow();
|
|
437
|
+
} else {
|
|
438
|
+
state.goToParent();
|
|
439
|
+
expect(state.equals(states[parentNodeId])).toBeTruthy();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
});
|