@khanacademy/perseus 0.2.7 → 0.3.1

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.
Files changed (97) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +5 -0
  3. package/dist/es/index.css +1 -436
  4. package/dist/es/index.js +3172 -5450
  5. package/dist/es/index.js.map +1 -1
  6. package/dist/index.css +1 -436
  7. package/dist/index.js +13520 -15805
  8. package/dist/index.js.map +1 -1
  9. package/package.json +4 -2
  10. package/src/__tests__/perseus-markdown_test.jsx +6 -6
  11. package/src/__tests__/renderer_test.jsx +9 -9
  12. package/src/article-renderer.jsx +4 -5
  13. package/src/components/input-with-examples.jsx +4 -5
  14. package/src/components/lint.jsx +2 -1
  15. package/src/components/sortable.jsx +6 -7
  16. package/src/hint-renderer.jsx +3 -4
  17. package/src/hints-renderer.jsx +3 -4
  18. package/src/index.js +1 -0
  19. package/src/item-renderer.jsx +4 -5
  20. package/src/jquery.mobile.vmouse.js +2 -1
  21. package/src/perseus-markdown.jsx +3 -3
  22. package/src/renderer.jsx +31 -33
  23. package/src/styles/perseus-renderer.less +1 -1
  24. package/src/{not-gorgon.js → translation-linter.js} +10 -14
  25. package/src/types.js +3 -1
  26. package/src/widget-container.jsx +1 -1
  27. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +30 -12
  28. package/src/widgets/__tests__/definition_test.js +18 -33
  29. package/src/widgets/__tests__/grapher.cypress.jsx +2 -2
  30. package/src/widgets/__tests__/interactive-graph.cypress.jsx +2 -2
  31. package/src/widgets/categorizer.jsx +1 -1
  32. package/src/widgets/definition.jsx +36 -71
  33. package/src/widgets/explanation.jsx +1 -1
  34. package/src/widgets/expression.jsx +1 -1
  35. package/src/widgets/graded-group-set.jsx +1 -1
  36. package/src/widgets/graded-group.jsx +1 -1
  37. package/src/widgets/group.jsx +1 -1
  38. package/src/widgets/image.jsx +1 -1
  39. package/src/widgets/input-number.jsx +1 -1
  40. package/src/widgets/matcher.jsx +4 -1
  41. package/src/widgets/matrix.jsx +1 -1
  42. package/src/widgets/numeric-input.jsx +1 -1
  43. package/src/widgets/orderer.jsx +4 -1
  44. package/src/widgets/passage-ref-target.jsx +4 -1
  45. package/src/widgets/passage.jsx +1 -1
  46. package/src/widgets/radio/widget.jsx +1 -1
  47. package/src/widgets/sequence.jsx +4 -1
  48. package/src/widgets/sorter.jsx +4 -1
  49. package/src/widgets/table.jsx +4 -1
  50. package/src/README.md +0 -35
  51. package/src/__tests__/__snapshots__/pure-markdown_test.js.snap +0 -113
  52. package/src/__tests__/pure-markdown_test.js +0 -552
  53. package/src/gorgon/README +0 -40
  54. package/src/gorgon/__tests__/matcher_test.js +0 -497
  55. package/src/gorgon/__tests__/rule_test.js +0 -101
  56. package/src/gorgon/__tests__/rules_test.js +0 -487
  57. package/src/gorgon/__tests__/selector-parser_test.js +0 -52
  58. package/src/gorgon/__tests__/tree-transformer_test.js +0 -446
  59. package/src/gorgon/gorgon.js +0 -290
  60. package/src/gorgon/proptypes.js +0 -29
  61. package/src/gorgon/rule.js +0 -413
  62. package/src/gorgon/rules/absolute-url.js +0 -24
  63. package/src/gorgon/rules/all-rules.js +0 -72
  64. package/src/gorgon/rules/blockquoted-math.js +0 -10
  65. package/src/gorgon/rules/blockquoted-widget.js +0 -10
  66. package/src/gorgon/rules/double-spacing-after-terminal.js +0 -12
  67. package/src/gorgon/rules/extra-content-spacing.js +0 -12
  68. package/src/gorgon/rules/heading-level-1.js +0 -14
  69. package/src/gorgon/rules/heading-level-skip.js +0 -20
  70. package/src/gorgon/rules/heading-sentence-case.js +0 -11
  71. package/src/gorgon/rules/heading-title-case.js +0 -63
  72. package/src/gorgon/rules/image-alt-text.js +0 -21
  73. package/src/gorgon/rules/image-in-table.js +0 -10
  74. package/src/gorgon/rules/image-spaces-around-urls.js +0 -35
  75. package/src/gorgon/rules/image-widget.js +0 -50
  76. package/src/gorgon/rules/link-click-here.js +0 -11
  77. package/src/gorgon/rules/lint-utils.js +0 -48
  78. package/src/gorgon/rules/long-paragraph.js +0 -14
  79. package/src/gorgon/rules/math-adjacent.js +0 -10
  80. package/src/gorgon/rules/math-align-extra-break.js +0 -11
  81. package/src/gorgon/rules/math-align-linebreaks.js +0 -43
  82. package/src/gorgon/rules/math-empty.js +0 -10
  83. package/src/gorgon/rules/math-font-size.js +0 -12
  84. package/src/gorgon/rules/math-frac.js +0 -10
  85. package/src/gorgon/rules/math-nested.js +0 -11
  86. package/src/gorgon/rules/math-starts-with-space.js +0 -12
  87. package/src/gorgon/rules/math-text-empty.js +0 -10
  88. package/src/gorgon/rules/math-without-dollars.js +0 -14
  89. package/src/gorgon/rules/nested-lists.js +0 -11
  90. package/src/gorgon/rules/profanity.js +0 -10
  91. package/src/gorgon/rules/table-missing-cells.js +0 -20
  92. package/src/gorgon/rules/unbalanced-code-delimiters.js +0 -14
  93. package/src/gorgon/rules/unescaped-dollar.js +0 -10
  94. package/src/gorgon/rules/widget-in-table.js +0 -10
  95. package/src/gorgon/selector.js +0 -506
  96. package/src/gorgon/tree-transformer.js +0 -588
  97. package/src/pure-markdown.js +0 -347
package/src/gorgon/README DELETED
@@ -1,40 +0,0 @@
1
- Gorgon is the linter engine for Perseus. It is implemented by the
2
- following files:
3
-
4
- tree-transformer.js:
5
-
6
- This file defines a TreeTransformer class for traversing (and
7
- optionally transforming by inserting, reparenting and removing
8
- nodes) markdown parse trees. When traversing a tree, a
9
- TreeTransformer calls the function you specify on each node. This is
10
- a post-order traversal: the function is called on the way back up,
11
- not on the way down. When the function is invoked, it is passed the
12
- current node, a state object, and the concatenated content of the
13
- node and its descendants. The state object is the interesting one:
14
- it is an instance of TraversalState, which is a class defined in
15
- (but not exported by) this same file. TraversalState has an API for
16
- querying the ancestors and siblings of the current node, and also an
17
- API for replacing the current node with a new one.
18
-
19
- selector.js:
20
-
21
- This file defines the Selector class which works like a CSS selector
22
- for markdown parse trees. Selector.parse() converts strings like
23
- "heading + paragraph > text" to Selector objects. A Selector object
24
- has a match() method that tests whether a given node in a parse tree
25
- matches. The match() method takes a TraversalState object as its
26
- argument, so selectors can only be used during a TreeTransformer
27
- traversal.
28
-
29
- rule.js:
30
-
31
- This file defines the Rule class which represents a single lint
32
- rule. A Rule object has a check() method that takes the same (node,
33
- state, content) arguments that a TreeTransformer passes to the
34
- traversal callback function. Rules can have a selector, a regular
35
- expression, and a function. If a node matches the selector and its
36
- content matches the regular expression, then the function is called
37
- to check the node and return a warning message if the node does, in
38
- fact, have lint.
39
-
40
- See the individual file for additional documentation.
@@ -1,497 +0,0 @@
1
- // @flow
2
- /* These tests exercise the Selector.match() method and also test that we
3
- * can integrate ../../perseus-markdown.js with ../tree-transform.js and
4
- * ../selector.js
5
- */
6
- import PerseusMarkdown from "../../perseus-markdown.jsx";
7
- import Selector from "../selector.js";
8
- import TreeTransformer from "../tree-transformer.js";
9
-
10
- describe("Gorgon selector matching:", () => {
11
- const markdown = `
12
- ### A
13
-
14
- B
15
-
16
- C
17
-
18
- - D
19
- - E
20
- - F
21
-
22
- *G*H
23
- `;
24
-
25
- function parseTree() {
26
- return PerseusMarkdown.parse(markdown);
27
- }
28
-
29
- it("wildcards match every node", () => {
30
- const tree = parseTree();
31
- const selector = Selector.parse("*");
32
- const tt = new TreeTransformer(tree);
33
- tt.traverse((n, state, content) => {
34
- // The wildcard selector should match at every node
35
- // $FlowFixMe[incompatible-use]
36
- expect(selector.match(state)[0]).toEqual(n);
37
- });
38
- });
39
-
40
- it("type-based matching works", () => {
41
- const tree = parseTree();
42
- const selectors = {};
43
- const tt = new TreeTransformer(tree);
44
-
45
- // Traverse the tree once and create a selector for every type
46
- // of node we find
47
- tt.traverse((n, state, content) => {
48
- if (!selectors[n.type]) {
49
- selectors[n.type] = Selector.parse(n.type);
50
- }
51
- });
52
-
53
- const types = Object.keys(selectors);
54
-
55
- // Now traverse the tree again. At each node run all of the
56
- // selectors we've created. Only those with matching types
57
- // should match.
58
- tt.traverse((n, state, content) => {
59
- types.forEach((type) => {
60
- const selector = selectors[type];
61
- const match = selector.match(state);
62
- if (n.type === type) {
63
- expect(match[0]).toEqual(n);
64
- } else {
65
- expect(match).toEqual(null);
66
- }
67
- });
68
-
69
- if (!selectors[n.type]) {
70
- selectors[n.type] = Selector.parse(n.type);
71
- }
72
- });
73
- });
74
-
75
- it("parent combinator", () => {
76
- const selector = Selector.parse("paragraph > text");
77
- const tree = parseTree();
78
- const tt = new TreeTransformer(tree);
79
- let numMatches = 0;
80
- let matchedText = "";
81
-
82
- tt.traverse((n, state, content) => {
83
- const match = selector.match(state);
84
- const parent = state.parent();
85
- // $FlowFixMe[incompatible-use]
86
- // $FlowFixMe[incompatible-type]
87
- if (n.type === "text" && parent.type === "paragraph") {
88
- expect(Array.isArray(match)).toBeTruthy();
89
- expect(match).toHaveLength(2);
90
- // $FlowFixMe[incompatible-use]
91
- expect(match[0]).toEqual(parent);
92
- // $FlowFixMe[incompatible-use]
93
- expect(match[1]).toEqual(n);
94
- matchedText += content;
95
- numMatches++;
96
- } else {
97
- expect(match).toEqual(null);
98
- }
99
- });
100
-
101
- expect(numMatches).toEqual(3);
102
- expect(matchedText).toEqual("BCH");
103
- });
104
-
105
- it("double parent combinator", () => {
106
- const selector = Selector.parse("paragraph > em > text");
107
- const tree = parseTree();
108
- const tt = new TreeTransformer(tree);
109
- let numMatches = 0;
110
- let matchedText = "";
111
-
112
- tt.traverse((n, state, content) => {
113
- const match = selector.match(state);
114
- // Make a mutable copy before popping.
115
- const ancestors = [...state.ancestors()];
116
- const parent = ancestors.pop();
117
- const grandparent = ancestors.pop();
118
- if (
119
- n.type === "text" &&
120
- parent.type === "em" &&
121
- grandparent.type === "paragraph"
122
- ) {
123
- expect(Array.isArray(match)).toBeTruthy();
124
- expect(match).toHaveLength(3);
125
- // $FlowFixMe[incompatible-use]
126
- expect(match[0]).toEqual(grandparent);
127
- // $FlowFixMe[incompatible-use]
128
- expect(match[1]).toEqual(parent);
129
- // $FlowFixMe[incompatible-use]
130
- expect(match[2]).toEqual(n);
131
- matchedText += content;
132
- numMatches++;
133
- } else {
134
- expect(match).toEqual(null);
135
- }
136
- });
137
-
138
- expect(numMatches).toEqual(1);
139
- expect(matchedText).toEqual("G");
140
- });
141
-
142
- it("ancestor combinator", () => {
143
- const selector = Selector.parse("paragraph text");
144
- const tree = parseTree();
145
- const tt = new TreeTransformer(tree);
146
- let numMatches = 0;
147
- let matchedText = "";
148
-
149
- tt.traverse((n, state, content) => {
150
- const match = selector.match(state);
151
- if (match !== null) {
152
- expect(Array.isArray(match)).toBeTruthy();
153
- expect(match).toHaveLength(2);
154
- // $FlowFixMe[incompatible-use]
155
- expect(match[0].type).toEqual("paragraph");
156
- // $FlowFixMe[incompatible-use]
157
- expect(match[1].type).toEqual("text");
158
- // $FlowFixMe[incompatible-use]
159
- expect(match[1]).toEqual(n);
160
- matchedText += content;
161
- numMatches++;
162
- }
163
- });
164
-
165
- expect(numMatches).toEqual(4);
166
- expect(matchedText).toEqual("BCGH");
167
- });
168
-
169
- it("double ancestor combinator", () => {
170
- const selector = Selector.parse("paragraph em text");
171
- const tree = parseTree();
172
- const tt = new TreeTransformer(tree);
173
- let numMatches = 0;
174
- let matchedText = "";
175
-
176
- tt.traverse((n, state, content) => {
177
- const match = selector.match(state);
178
- if (match !== null) {
179
- expect(Array.isArray(match)).toBeTruthy();
180
- expect(match).toHaveLength(3);
181
- // $FlowFixMe[incompatible-use]
182
- expect(match[0].type).toEqual("paragraph");
183
- // $FlowFixMe[incompatible-use]
184
- expect(match[1].type).toEqual("em");
185
- // $FlowFixMe[incompatible-use]
186
- expect(match[2].type).toEqual("text");
187
- // $FlowFixMe[incompatible-use]
188
- expect(match[2]).toEqual(n);
189
- matchedText += content;
190
- numMatches++;
191
- }
192
- });
193
-
194
- expect(numMatches).toEqual(1);
195
- expect(matchedText).toEqual("G");
196
- });
197
-
198
- it("previous combinator", () => {
199
- const selector = Selector.parse("heading + paragraph");
200
- const tree = parseTree();
201
- const tt = new TreeTransformer(tree);
202
- let numMatches = 0;
203
- let matchedText = "";
204
-
205
- tt.traverse((n, state, content) => {
206
- const match = selector.match(state);
207
- if (match !== null) {
208
- expect(Array.isArray(match)).toBeTruthy();
209
- expect(match).toHaveLength(2);
210
- // $FlowFixMe[incompatible-use]
211
- expect(match[0].type).toEqual("heading");
212
- // $FlowFixMe[incompatible-use]
213
- expect(match[0]).toEqual(state.previousSibling());
214
- // $FlowFixMe[incompatible-use]
215
- expect(match[1].type).toEqual("paragraph");
216
- // $FlowFixMe[incompatible-use]
217
- expect(match[1]).toEqual(n);
218
- matchedText += content;
219
- numMatches++;
220
- }
221
- });
222
-
223
- expect(numMatches).toEqual(1);
224
- expect(matchedText).toEqual("B");
225
- });
226
-
227
- it("double previous combinator", () => {
228
- const selector = Selector.parse("heading + paragraph + paragraph");
229
- const tree = parseTree();
230
- const tt = new TreeTransformer(tree);
231
- let numMatches = 0;
232
- let matchedText = "";
233
-
234
- tt.traverse((n, state, content) => {
235
- const match = selector.match(state);
236
- if (match !== null) {
237
- expect(Array.isArray(match)).toBeTruthy();
238
- expect(match).toHaveLength(3);
239
- // $FlowFixMe[incompatible-use]
240
- expect(match[0].type).toEqual("heading");
241
- // $FlowFixMe[incompatible-use]
242
- expect(match[1].type).toEqual("paragraph");
243
- // $FlowFixMe[incompatible-use]
244
- expect(match[1]).toEqual(state.previousSibling());
245
- // $FlowFixMe[incompatible-use]
246
- expect(match[2].type).toEqual("paragraph");
247
- // $FlowFixMe[incompatible-use]
248
- expect(match[2]).toEqual(n);
249
- matchedText += content;
250
- numMatches++;
251
- }
252
- });
253
-
254
- expect(numMatches).toEqual(1);
255
- expect(matchedText).toEqual("C");
256
- });
257
-
258
- it("sibling combinator", () => {
259
- const selector = Selector.parse("heading ~ paragraph");
260
- const tree = parseTree();
261
- const tt = new TreeTransformer(tree);
262
- let numMatches = 0;
263
- let matchedText = "";
264
-
265
- tt.traverse((n, state, content) => {
266
- const match = selector.match(state);
267
- if (match !== null) {
268
- expect(Array.isArray(match)).toBeTruthy();
269
- expect(match).toHaveLength(2);
270
- // $FlowFixMe[incompatible-use]
271
- expect(match[0].type).toEqual("heading");
272
- // $FlowFixMe[incompatible-use]
273
- expect(match[1].type).toEqual("paragraph");
274
- // $FlowFixMe[incompatible-use]
275
- expect(match[1]).toEqual(n);
276
- matchedText += content;
277
- numMatches++;
278
- }
279
- });
280
-
281
- expect(numMatches).toEqual(3);
282
- expect(matchedText).toEqual("BCGH");
283
- });
284
-
285
- it("double sibling combinator", () => {
286
- const selector = Selector.parse("heading ~ paragraph ~ paragraph");
287
- const tree = parseTree();
288
- const tt = new TreeTransformer(tree);
289
- let numMatches = 0;
290
- let matchedText = "";
291
-
292
- tt.traverse((n, state, content) => {
293
- const match = selector.match(state);
294
- if (match !== null) {
295
- expect(Array.isArray(match)).toBeTruthy();
296
- expect(match).toHaveLength(3);
297
- // $FlowFixMe[incompatible-use]
298
- expect(match[0].type).toEqual("heading");
299
- // $FlowFixMe[incompatible-use]
300
- expect(match[1].type).toEqual("paragraph");
301
- // $FlowFixMe[incompatible-use]
302
- expect(match[2].type).toEqual("paragraph");
303
- // $FlowFixMe[incompatible-use]
304
- expect(match[2]).toEqual(n);
305
- matchedText += content;
306
- numMatches++;
307
- }
308
- });
309
-
310
- expect(numMatches).toEqual(2);
311
- expect(matchedText).toEqual("CGH");
312
- });
313
-
314
- it("mixed combinators", () => {
315
- const selector = Selector.parse("list + paragraph > em");
316
- const tree = parseTree();
317
- const tt = new TreeTransformer(tree);
318
- let numMatches = 0;
319
- let matchedText = "";
320
-
321
- tt.traverse((n, state, content) => {
322
- const match = selector.match(state);
323
- if (match !== null) {
324
- expect(Array.isArray(match)).toBeTruthy();
325
- expect(match).toHaveLength(3);
326
- // $FlowFixMe[incompatible-use]
327
- expect(match[0].type).toEqual("list");
328
- // $FlowFixMe[incompatible-use]
329
- expect(match[1].type).toEqual("paragraph");
330
- // $FlowFixMe[incompatible-use]
331
- expect(match[1]).toEqual(state.parent());
332
- // $FlowFixMe[incompatible-use]
333
- expect(match[2].type).toEqual("em");
334
- // $FlowFixMe[incompatible-use]
335
- expect(match[2]).toEqual(n);
336
- matchedText += content;
337
- numMatches++;
338
- }
339
- });
340
-
341
- expect(numMatches).toEqual(1);
342
- expect(matchedText).toEqual("G");
343
- });
344
-
345
- it("mixed combinators 2", () => {
346
- const selector = Selector.parse("list ~ paragraph em");
347
- const tree = parseTree();
348
- const tt = new TreeTransformer(tree);
349
- let numMatches = 0;
350
- let matchedText = "";
351
-
352
- tt.traverse((n, state, content) => {
353
- const match = selector.match(state);
354
- if (match !== null) {
355
- expect(Array.isArray(match)).toBeTruthy();
356
- expect(match).toHaveLength(3);
357
- // $FlowFixMe[incompatible-use]
358
- expect(match[0].type).toEqual("list");
359
- // $FlowFixMe[incompatible-use]
360
- expect(match[1].type).toEqual("paragraph");
361
- // $FlowFixMe[incompatible-use]
362
- expect(match[1]).toEqual(state.parent());
363
- // $FlowFixMe[incompatible-use]
364
- expect(match[2].type).toEqual("em");
365
- // $FlowFixMe[incompatible-use]
366
- expect(match[2]).toEqual(n);
367
- matchedText += content;
368
- numMatches++;
369
- }
370
- });
371
-
372
- expect(numMatches).toEqual(1);
373
- expect(matchedText).toEqual("G");
374
- });
375
-
376
- it("mixed combinators 3", () => {
377
- const selector = Selector.parse("paragraph > em + text");
378
- const tree = parseTree();
379
- const tt = new TreeTransformer(tree);
380
- let numMatches = 0;
381
- let matchedText = "";
382
-
383
- tt.traverse((n, state, content) => {
384
- const match = selector.match(state);
385
- if (match !== null) {
386
- expect(Array.isArray(match)).toBeTruthy();
387
- expect(match).toHaveLength(3);
388
- // $FlowFixMe[incompatible-use]
389
- expect(match[0].type).toEqual("paragraph");
390
- // $FlowFixMe[incompatible-use]
391
- expect(match[0]).toEqual(state.parent());
392
- // $FlowFixMe[incompatible-use]
393
- expect(match[1].type).toEqual("em");
394
- // $FlowFixMe[incompatible-use]
395
- expect(match[1]).toEqual(state.previousSibling());
396
- // $FlowFixMe[incompatible-use]
397
- expect(match[2].type).toEqual("text");
398
- // $FlowFixMe[incompatible-use]
399
- expect(match[2]).toEqual(n);
400
- matchedText += content;
401
- numMatches++;
402
- }
403
- });
404
-
405
- expect(numMatches).toEqual(1);
406
- expect(matchedText).toEqual("H");
407
- });
408
-
409
- it("mixed combinators 4", () => {
410
- const selector = Selector.parse("paragraph em ~ text");
411
- const tree = parseTree();
412
- const tt = new TreeTransformer(tree);
413
- let numMatches = 0;
414
- let matchedText = "";
415
-
416
- tt.traverse((n, state, content) => {
417
- const match = selector.match(state);
418
- if (match !== null) {
419
- expect(Array.isArray(match)).toBeTruthy();
420
- expect(match).toHaveLength(3);
421
- // $FlowFixMe[incompatible-use]
422
- expect(match[0].type).toEqual("paragraph");
423
- // $FlowFixMe[incompatible-use]
424
- expect(match[0]).toEqual(state.parent());
425
- // $FlowFixMe[incompatible-use]
426
- expect(match[1].type).toEqual("em");
427
- // $FlowFixMe[incompatible-use]
428
- expect(match[1]).toEqual(state.previousSibling());
429
- // $FlowFixMe[incompatible-use]
430
- expect(match[2].type).toEqual("text");
431
- // $FlowFixMe[incompatible-use]
432
- expect(match[2]).toEqual(n);
433
- matchedText += content;
434
- numMatches++;
435
- }
436
- });
437
-
438
- expect(numMatches).toEqual(1);
439
- expect(matchedText).toEqual("H");
440
- });
441
-
442
- it("selector list", () => {
443
- const selector = Selector.parse("paragraph, list");
444
- const tree = parseTree();
445
- const tt = new TreeTransformer(tree);
446
- let numMatches = 0;
447
- let matchedText = "";
448
-
449
- tt.traverse((n, state, content) => {
450
- const match = selector.match(state);
451
- if (match !== null) {
452
- expect(Array.isArray(match)).toBeTruthy();
453
- expect(match).toHaveLength(1);
454
- // $FlowFixMe[incompatible-use]
455
- expect(match[0]).toEqual(n);
456
- expect(
457
- n.type === "paragraph" || n.type === "list",
458
- ).toBeTruthy();
459
- matchedText += content;
460
- numMatches++;
461
- }
462
- });
463
-
464
- expect(numMatches).toEqual(4);
465
- expect(matchedText).toEqual("BCDEFGH");
466
- });
467
-
468
- it("selector list 2", () => {
469
- const selector = Selector.parse("heading, paragraph text, list>text");
470
- const tree = parseTree();
471
- const tt = new TreeTransformer(tree);
472
- let numMatches = 0;
473
- let matchedText = "";
474
-
475
- tt.traverse((n, state, content) => {
476
- const match = selector.match(state);
477
- if (match !== null) {
478
- expect(Array.isArray(match)).toBeTruthy();
479
- if (n.type === "heading") {
480
- expect(match).toHaveLength(1);
481
- // $FlowFixMe[incompatible-use]
482
- expect(match[0]).toEqual(n);
483
- } else {
484
- expect(match).toHaveLength(2);
485
- // $FlowFixMe[incompatible-use]
486
- expect(match[1]).toEqual(n);
487
- expect(n.type).toEqual("text");
488
- }
489
- matchedText += content;
490
- numMatches++;
491
- }
492
- });
493
-
494
- expect(numMatches).toEqual(8);
495
- expect(matchedText).toEqual("ABCDEFGH");
496
- });
497
- });
@@ -1,101 +0,0 @@
1
- // @flow
2
- import PerseusMarkdown from "../../perseus-markdown.jsx";
3
- import Rule from "../rule.js";
4
- import TreeTransformer from "../tree-transformer.js";
5
-
6
- describe("Gorgon lint Rules class", () => {
7
- const markdown = `
8
- ## This Heading is in Title Case
9
-
10
- This paragraph contains forbidden words. Poop!
11
-
12
- This paragraph contains an unescaped $ sign.
13
-
14
- #### This heading skipped a level
15
- `;
16
-
17
- const ruleDescriptions = [
18
- {
19
- name: "heading-title-case",
20
- selector: "heading",
21
- pattern: "\\s[A-Z][a-z]",
22
- message: `Title case in heading:
23
- Only capitalize the first word of headings.`,
24
- },
25
- {
26
- name: "profanity",
27
- pattern: "/poop|crap/i",
28
- message: `Profanity:
29
- this is a family website!`,
30
- },
31
- {
32
- name: "unescaped-dollar",
33
- selector: "unescapedDollar",
34
- message: `Unescaped '$':
35
- If writing math, pair with another $.
36
- Otherwise escape it by writing \\$.`,
37
- },
38
- {
39
- name: "heading-level-skip",
40
- selector: "heading ~ heading",
41
- lint: function (state, content, nodes) {
42
- const currentHeading = nodes[1];
43
- const previousHeading = nodes[0];
44
-
45
- expect(nodes).toHaveLength(2);
46
- expect(nodes[1]).toEqual(state.currentNode());
47
-
48
- // A heading can have a level less than, the same as
49
- // or one more than the previous heading. But going up
50
- // by 2 or more levels is not right
51
- if (currentHeading.level > previousHeading.level + 1) {
52
- return `Skipped heading level:
53
- this heading is level ${currentHeading.level} but
54
- the previous heading was level ${previousHeading.level}`;
55
- }
56
- return false;
57
- },
58
- },
59
- ];
60
-
61
- let rules = [];
62
-
63
- function parseTree() {
64
- return PerseusMarkdown.parse(markdown);
65
- }
66
-
67
- it("makeRules() factory method", () => {
68
- rules = ruleDescriptions.map((r) => Rule.makeRule(r));
69
- expect(rules).toHaveLength(ruleDescriptions.length);
70
- rules.forEach((r) => expect(r instanceof Rule).toBeTruthy());
71
- });
72
-
73
- it("check() method", () => {
74
- const tree = parseTree();
75
- const tt = new TreeTransformer(tree);
76
- const warnings = [];
77
-
78
- tt.traverse((node, state, content) => {
79
- rules.forEach((r) => {
80
- const lint = r.check(node, state, content);
81
- if (lint) {
82
- warnings.push(lint);
83
- }
84
- });
85
- });
86
-
87
- expect(warnings).toHaveLength(4);
88
- expect(warnings[0].rule).toEqual(ruleDescriptions[0].name);
89
- expect(warnings[0].message).toEqual(ruleDescriptions[0].message);
90
-
91
- expect(warnings[1].rule).toEqual(ruleDescriptions[1].name);
92
- expect(warnings[1].message).toEqual(ruleDescriptions[1].message);
93
- expect(warnings[1].start).toEqual(2);
94
- expect(warnings[1].end).toEqual(6);
95
-
96
- expect(warnings[2].rule).toEqual(ruleDescriptions[2].name);
97
- expect(warnings[2].message).toEqual(ruleDescriptions[2].message);
98
-
99
- expect(warnings[3].rule).toEqual(ruleDescriptions[3].name);
100
- });
101
- });