forked from KaTeX/KaTeX
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherrors-spec.js
331 lines (299 loc) · 13.9 KB
/
errors-spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
import {strictSettings} from "./helpers";
describe("Parser:", function() {
describe("#handleInfixNodes", function() {
it("rejects repeated infix operators", function() {
expect`1\over 2\over 3`.toFailWithParseError(
"only one infix operator per group at position 9: " +
"1\\over 2\\̲o̲v̲e̲r̲ ̲3");
});
it("rejects conflicting infix operators", function() {
expect`1\over 2\choose 3`.toFailWithParseError(
"only one infix operator per group at position 9: " +
"1\\over 2\\̲c̲h̲o̲o̲s̲e̲ ̲3");
});
});
describe("#handleSupSubscript", function() {
it("rejects ^ at end of group", function() {
expect`{1^}`.toFailWithParseError(
"Expected group after '^' at position 3: {1^̲}");
});
it("rejects _ at end of input", function() {
expect`1_`.toFailWithParseError(
"Expected group after '_' at position 2: 1_̲");
});
it("rejects \\sqrt as argument to ^", function() {
expect`1^\sqrt{2}`.toFailWithParseError(
"Got function '\\sqrt' with no arguments as superscript" +
" at position 3: 1^\\̲s̲q̲r̲t̲{2}");
});
});
describe("#parseAtom", function() {
it("rejects \\limits without operator", function() {
expect`\alpha\limits\omega`.toFailWithParseError(
"Limit controls must follow a math operator" +
" at position 7: \\alpha\\̲l̲i̲m̲i̲t̲s̲\\omega");
});
it("rejects \\limits at the beginning of the input", function() {
expect`\limits\omega`.toFailWithParseError(
"Limit controls must follow a math operator" +
" at position 1: \\̲l̲i̲m̲i̲t̲s̲\\omega");
});
it("rejects double superscripts", function() {
expect`1^2^3`.toFailWithParseError(
"Double superscript at position 4: 1^2^̲3");
expect`1^{2+3}_4^5`.toFailWithParseError(
"Double superscript at position 10: 1^{2+3}_4^̲5");
});
it("rejects double superscripts involving primes", function() {
expect`1'_2^3`.toFailWithParseError(
"Double superscript at position 5: 1'_2^̲3");
expect`1^2'`.toFailWithParseError(
"Double superscript at position 4: 1^2'̲");
expect`1^2_3'`.toFailWithParseError(
"Double superscript at position 6: 1^2_3'̲");
expect`1'_2'`.toFailWithParseError(
"Double superscript at position 5: 1'_2'̲");
});
it("rejects double subscripts", function() {
expect`1_2_3`.toFailWithParseError(
"Double subscript at position 4: 1_2_̲3");
expect`1_{2+3}^4_5`.toFailWithParseError(
"Double subscript at position 10: 1_{2+3}^4_̲5");
});
});
describe("#parseImplicitGroup", function() {
it("reports unknown environments", function() {
expect`\begin{foo}bar\end{foo}`.toFailWithParseError(
"No such environment: foo at position 7:" +
" \\begin{̲f̲o̲o̲}̲bar\\end{foo}");
});
it("reports mismatched environments", function() {
expect`\begin{pmatrix}1&2\\3&4\end{bmatrix}+5`
.toFailWithParseError(
"Mismatch: \\begin{pmatrix} matched by \\end{bmatrix}" +
" at position 24: …matrix}1&2\\\\3&4\\̲e̲n̲d̲{bmatrix}+5");
});
});
describe("#parseFunction", function() {
it("rejects math-mode functions in text mode", function() {
expect`\text{\sqrt2 is irrational}`.toFailWithParseError(
"Can't use function '\\sqrt' in text mode" +
" at position 7: \\text{\\̲s̲q̲r̲t̲2 is irrational…");
});
it("rejects text-mode-only functions in math mode", () => {
expect`$`.toFailWithParseError(
"Can't use function '$' in math mode at position 1: $̲");
});
it("rejects strict-mode text-mode-only functions in math mode", () => {
expect`\'echec`.toFailWithParseError("LaTeX-incompatible input " +
"and strict mode is set to 'error': LaTeX's accent \\' works " +
"only in text mode [mathVsTextAccents]", strictSettings);
});
});
describe("#parseArguments", function() {
it("complains about missing argument at end of input", function() {
expect`2\sqrt`.toFailWithParseError(
"Expected group as argument to '\\sqrt'" +
" at end of input: 2\\sqrt");
});
it("complains about missing argument at end of group", function() {
expect`1^{2\sqrt}`.toFailWithParseError(
"Expected group as argument to '\\sqrt'" +
" at position 10: 1^{2\\sqrt}̲");
});
it("complains about functions as arguments to others", function() {
expect`\sqrt\over2`.toFailWithParseError(
"Got function '\\over' with no arguments as argument to" +
" '\\sqrt' at position 6: \\sqrt\\̲o̲v̲e̲r̲2");
});
});
describe("#parseGroup", function() {
it("complains about undefined control sequence", function() {
expect`\xyz`.toFailWithParseError(
"Undefined control sequence: \\xyz" +
" at position 1: \\̲x̲y̲z̲");
});
});
describe("#verb", function() {
it("complains about mismatched \\verb with end of string", function() {
expect`\verb|hello`.toFailWithParseError(
"\\verb ended by end of line instead of matching delimiter");
});
it("complains about mismatched \\verb with end of line", function() {
expect("\\verb|hello\nworld|").toFailWithParseError(
"\\verb ended by end of line instead of matching delimiter");
});
});
});
describe("Parser.expect calls:", function() {
describe("#parseInput expecting EOF", function() {
it("complains about extra }", function() {
expect`{1+2}}`.toFailWithParseError(
"Expected 'EOF', got '}' at position 6: {1+2}}̲");
});
it("complains about extra \\end", function() {
expect`x\end{matrix}`.toFailWithParseError(
"Expected 'EOF', got '\\end' at position 2:" +
" x\\̲e̲n̲d̲{matrix}");
});
it("complains about top-level &", function() {
expect`1&2`.toFailWithParseError(
"Expected 'EOF', got '&' at position 2: 1&̲2");
});
});
describe("#parseImplicitGroup expecting \\right", function() {
it("rejects missing \\right", function() {
expect`\left(1+2)`.toFailWithParseError(
"Expected '\\right', got 'EOF' at end of input:" +
" \\left(1+2)");
});
it("rejects incorrectly scoped \\right", function() {
expect`{\left(1+2}\right)`.toFailWithParseError(
"Expected '\\right', got '}' at position 11:" +
" {\\left(1+2}̲\\right)");
});
});
// Can't test the expectation for \end after an environment
// since all existing arrays use parseArray which has its own expectation.
describe("#parseSpecialGroup expecting braces", function() {
it("complains about missing { for color", function() {
expect`\textcolor#ffffff{text}`.toFailWithParseError(
"Invalid color: '#' at position 11:" +
" \\textcolor#̲ffffff{text}");
});
it("complains about missing { for size", function() {
expect`\rule{1em}[2em]`.toFailWithParseError(
"Invalid size: '[' at position 11: \\rule{1em}[̲2em]");
});
// Can't test for the [ of an optional group since it's optional
it("complains about missing } for color", function() {
expect`\textcolor{#ffffff{text}`.toFailWithParseError(
"Unexpected end of input in a macro argument," +
" expected '}' at end of input: …r{#ffffff{text}");
});
it("complains about missing ] for size", function() {
expect`\rule[1em{2em}{3em}`.toFailWithParseError(
"Unexpected end of input in a macro argument," +
" expected ']' at end of input: …e[1em{2em}{3em}");
});
it("complains about missing ] for size at end of input", function() {
expect`\rule[1em`.toFailWithParseError(
"Unexpected end of input in a macro argument," +
" expected ']' at end of input: \\rule[1em");
});
it("complains about missing } for color at end of input", function() {
expect`\textcolor{#123456`.toFailWithParseError(
"Unexpected end of input in a macro argument," +
" expected '}' at end of input: …xtcolor{#123456");
});
});
describe("#parseGroup expecting }", function() {
it("at end of file", function() {
expect`\sqrt{2`.toFailWithParseError(
"Expected '}', got 'EOF' at end of input: \\sqrt{2");
});
});
describe("#parseOptionalGroup expecting ]", function() {
it("at end of file", function() {
expect`\sqrt[3`.toFailWithParseError(
"Unexpected end of input in a macro argument," +
" expected ']' at end of input: \\sqrt[3");
});
it("before group", function() {
expect`\sqrt[3{2}`.toFailWithParseError(
"Unexpected end of input in a macro argument," +
" expected ']' at end of input: \\sqrt[3{2}");
});
});
});
describe("environments.js:", function() {
describe("parseArray", function() {
it("rejects missing \\end", function() {
expect`\begin{matrix}1`.toFailWithParseError(
"Expected & or \\\\ or \\cr or \\end at end of input:" +
" \\begin{matrix}1");
});
it("rejects incorrectly scoped \\end", function() {
expect`{\begin{matrix}1}\end{matrix}`.toFailWithParseError(
"Expected & or \\\\ or \\cr or \\end at position 17:" +
" …\\begin{matrix}1}̲\\end{matrix}");
});
});
describe("array environment", function() {
it("rejects unknown column types", function() {
expect`\begin{array}{cba}\end{array}`.toFailWithParseError(
"Unknown column alignment: b at position 16:" +
" \\begin{array}{cb̲a}\\end{array}");
});
});
});
describe("functions.js:", function() {
describe("delimiter functions", function() {
it("reject invalid opening delimiters", function() {
expect`\bigl 1 + 2 \bigr`.toFailWithParseError(
"Invalid delimiter '1' after '\\bigl' at position 7:" +
" \\bigl 1̲ + 2 \\bigr");
});
it("reject invalid closing delimiters", function() {
expect`\bigl(1+2\bigr=3`.toFailWithParseError(
"Invalid delimiter '=' after '\\bigr' at position 15:" +
" \\bigl(1+2\\bigr=̲3");
});
it("reject group opening delimiters", function() {
expect`\bigl{(}1+2\bigr)3`.toFailWithParseError(
"Invalid delimiter type 'ordgroup' at position 6:" +
" \\bigl{̲(̲}̲1+2\\bigr)3");
});
it("reject group closing delimiters", function() {
expect`\bigl(1+2\bigr{)}3`.toFailWithParseError(
"Invalid delimiter type 'ordgroup' at position 15:" +
" \\bigl(1+2\\bigr{̲)̲}̲3");
});
});
describe("\\begin and \\end", function() {
it("reject invalid environment names", function() {
expect`\begin x\end y`.toFailWithParseError(
"No such environment: x at position 8: \\begin x̲\\end y");
});
});
});
describe("Lexer:", function() {
describe("#_innerLex", function() {
it("rejects lone surrogate char", function() {
expect("\udcba ").toFailWithParseError(
"Unexpected character: '\udcba' at position 1:" +
" \udcba\u0332 ");
});
it("rejects lone backslash at end of input", function() {
expect("\\").toFailWithParseError(
"Unexpected character: '\\' at position 1: \\̲");
});
});
describe("#_innerLexColor", function() {
it("reject 3-digit hex notation without #", function() {
expect`\textcolor{1a2}{foo}`.toFailWithParseError(
"Invalid color: '1a2'" +
" at position 11: \\textcolor{̲1̲a̲2̲}̲{foo}");
});
});
describe("#_innerLexSize", function() {
it("reject size without unit", function() {
expect`\rule{0}{2em}`.toFailWithParseError(
"Invalid size: '0' at position 6: \\rule{̲0̲}̲{2em}");
});
it("reject size with bogus unit", function() {
expect`\rule{1au}{2em}`.toFailWithParseError(
"Invalid unit: 'au' at position 6: \\rule{̲1̲a̲u̲}̲{2em}");
});
it("reject size without number", function() {
expect`\rule{em}{2em}`.toFailWithParseError(
"Invalid size: 'em' at position 6: \\rule{̲e̲m̲}̲{2em}");
});
});
});
describe("Unicode accents", function() {
it("should return error for invalid combining characters", function() {
expect("A\u0328").toFailWithParseError(
"Unknown accent ' ̨' at position 1: Ą̲̲");
});
});