]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Tests for ast utils. | |
3 | * @author Gyandeep Singh | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | //------------------------------------------------------------------------------ | |
9 | // Requirements | |
10 | //------------------------------------------------------------------------------ | |
11 | ||
12 | const assert = require("chai").assert, | |
13 | espree = require("espree"), | |
14 | astUtils = require("../../../../lib/rules/utils/ast-utils"), | |
15 | { Linter } = require("../../../../lib/linter"), | |
16 | { SourceCode } = require("../../../../lib/source-code"); | |
17 | ||
18 | //------------------------------------------------------------------------------ | |
19 | // Tests | |
20 | //------------------------------------------------------------------------------ | |
21 | ||
22 | const ESPREE_CONFIG = { | |
23 | ecmaVersion: 6, | |
24 | comment: true, | |
25 | tokens: true, | |
26 | range: true, | |
27 | loc: true | |
28 | }; | |
29 | const linter = new Linter(); | |
30 | ||
31 | describe("ast-utils", () => { | |
32 | let callCounts; | |
33 | ||
34 | beforeEach(() => { | |
35 | callCounts = new Map(); | |
36 | }); | |
37 | ||
38 | /** | |
39 | * Asserts that a given function is called at least once during a test | |
40 | * @param {Function} func The function that must be called at least once | |
41 | * @returns {Function} A wrapper around the same function | |
42 | */ | |
43 | function mustCall(func) { | |
44 | callCounts.set(func, 0); | |
45 | return function Wrapper(...args) { | |
46 | callCounts.set(func, callCounts.get(func) + 1); | |
47 | ||
48 | return func.call(this, ...args); | |
49 | }; | |
50 | } | |
51 | ||
52 | afterEach(() => { | |
53 | callCounts.forEach((callCount, func) => { | |
54 | assert( | |
55 | callCount > 0, | |
56 | `Expected ${func.toString()} to be called at least once but it was not called` | |
57 | ); | |
58 | }); | |
59 | }); | |
60 | ||
61 | describe("isTokenOnSameLine", () => { | |
62 | it("should return false if the tokens are not on the same line", () => { | |
63 | linter.defineRule("checker", mustCall(context => ({ | |
64 | BlockStatement: mustCall(node => { | |
65 | assert.isFalse(astUtils.isTokenOnSameLine(context.getTokenBefore(node), node)); | |
66 | }) | |
67 | }))); | |
68 | ||
69 | linter.verify("if(a)\n{}", { rules: { checker: "error" } }); | |
70 | }); | |
71 | ||
72 | it("should return true if the tokens are on the same line", () => { | |
73 | ||
74 | linter.defineRule("checker", mustCall(context => ({ | |
75 | BlockStatement: mustCall(node => { | |
76 | assert.isTrue(astUtils.isTokenOnSameLine(context.getTokenBefore(node), node)); | |
77 | }) | |
78 | }))); | |
79 | ||
80 | linter.verify("if(a){}", { rules: { checker: "error" } }); | |
81 | }); | |
82 | }); | |
83 | ||
84 | describe("isNullOrUndefined", () => { | |
85 | it("should return true if the argument is null", () => { | |
86 | assert.isTrue(astUtils.isNullOrUndefined(espree.parse("null").body[0].expression)); | |
87 | }); | |
88 | ||
89 | it("should return true if the argument is undefined", () => { | |
90 | assert.isTrue(astUtils.isNullOrUndefined(espree.parse("undefined").body[0].expression)); | |
91 | }); | |
92 | ||
93 | it("should return false if the argument is a number", () => { | |
94 | assert.isFalse(astUtils.isNullOrUndefined(espree.parse("1").body[0].expression)); | |
95 | }); | |
96 | ||
97 | it("should return false if the argument is a string", () => { | |
98 | assert.isFalse(astUtils.isNullOrUndefined(espree.parse("'test'").body[0].expression)); | |
99 | }); | |
100 | ||
101 | it("should return false if the argument is a boolean", () => { | |
102 | assert.isFalse(astUtils.isNullOrUndefined(espree.parse("true").body[0].expression)); | |
103 | }); | |
104 | ||
105 | it("should return false if the argument is an object", () => { | |
106 | assert.isFalse(astUtils.isNullOrUndefined(espree.parse("({})").body[0].expression)); | |
107 | }); | |
108 | ||
109 | it("should return false if the argument is a unicode regex", () => { | |
110 | assert.isFalse(astUtils.isNullOrUndefined(espree.parse("/abc/u", { ecmaVersion: 6 }).body[0].expression)); | |
111 | }); | |
112 | }); | |
113 | ||
114 | describe("checkReference", () => { | |
115 | ||
116 | // catch | |
117 | it("should return true if reference is assigned for catch", () => { | |
118 | linter.defineRule("checker", mustCall(context => ({ | |
119 | CatchClause: mustCall(node => { | |
120 | const variables = context.getDeclaredVariables(node); | |
121 | ||
122 | assert.lengthOf(astUtils.getModifyingReferences(variables[0].references), 1); | |
123 | }) | |
124 | }))); | |
125 | ||
126 | linter.verify("try { } catch (e) { e = 10; }", { rules: { checker: "error" } }); | |
127 | }); | |
128 | ||
129 | // const | |
130 | it("should return true if reference is assigned for const", () => { | |
131 | linter.defineRule("checker", mustCall(context => ({ | |
132 | VariableDeclaration: mustCall(node => { | |
133 | const variables = context.getDeclaredVariables(node); | |
134 | ||
135 | assert.lengthOf(astUtils.getModifyingReferences(variables[0].references), 1); | |
136 | }) | |
137 | }))); | |
138 | ||
139 | linter.verify("const a = 1; a = 2;", { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } }); | |
140 | }); | |
141 | ||
142 | it("should return false if reference is not assigned for const", () => { | |
143 | linter.defineRule("checker", mustCall(context => ({ | |
144 | VariableDeclaration: mustCall(node => { | |
145 | const variables = context.getDeclaredVariables(node); | |
146 | ||
147 | assert.lengthOf(astUtils.getModifyingReferences(variables[0].references), 0); | |
148 | }) | |
149 | }))); | |
150 | ||
151 | linter.verify("const a = 1; c = 2;", { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } }); | |
152 | }); | |
153 | ||
154 | // class | |
155 | it("should return true if reference is assigned for class", () => { | |
156 | linter.defineRule("checker", mustCall(context => ({ | |
157 | ClassDeclaration: mustCall(node => { | |
158 | const variables = context.getDeclaredVariables(node); | |
159 | ||
160 | assert.lengthOf(astUtils.getModifyingReferences(variables[0].references), 1); | |
161 | assert.lengthOf(astUtils.getModifyingReferences(variables[1].references), 0); | |
162 | }) | |
163 | }))); | |
164 | ||
165 | linter.verify("class A { }\n A = 1;", { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } }); | |
166 | }); | |
167 | ||
168 | it("should return false if reference is not assigned for class", () => { | |
169 | linter.defineRule("checker", mustCall(context => ({ | |
170 | ClassDeclaration: mustCall(node => { | |
171 | const variables = context.getDeclaredVariables(node); | |
172 | ||
173 | assert.lengthOf(astUtils.getModifyingReferences(variables[0].references), 0); | |
174 | }) | |
175 | }))); | |
176 | ||
177 | linter.verify("class A { } foo(A);", { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } }); | |
178 | }); | |
179 | }); | |
180 | ||
181 | describe("isDirectiveComment", () => { | |
182 | ||
183 | /** | |
184 | * Asserts the node is NOT a directive comment | |
185 | * @param {ASTNode} node node to assert | |
186 | * @returns {void} | |
187 | * | |
188 | */ | |
189 | function assertFalse(node) { | |
190 | assert.isFalse(astUtils.isDirectiveComment(node)); | |
191 | } | |
192 | ||
193 | /** | |
194 | * Asserts the node is a directive comment | |
195 | * @param {ASTNode} node node to assert | |
196 | * @returns {void} | |
197 | * | |
198 | */ | |
199 | function assertTrue(node) { | |
200 | assert.isTrue(astUtils.isDirectiveComment(node)); | |
201 | } | |
202 | ||
203 | it("should return false if it is not a directive line comment", () => { | |
204 | const code = [ | |
205 | "// lalala I'm a normal comment", | |
206 | "// trying to confuse eslint ", | |
207 | "//trying to confuse eslint-directive-detection", | |
208 | "//eslint is awesome" | |
209 | ].join("\n"); | |
210 | const ast = espree.parse(code, ESPREE_CONFIG); | |
211 | const sourceCode = new SourceCode(code, ast); | |
212 | const comments = sourceCode.getAllComments(); | |
213 | ||
214 | comments.forEach(assertFalse); | |
215 | }); | |
216 | ||
217 | it("should return false if it is not a directive block comment", () => { | |
218 | const code = [ | |
219 | "/* lalala I'm a normal comment */", | |
220 | "/* trying to confuse eslint */", | |
221 | "/* trying to confuse eslint-directive-detection */", | |
222 | "/*eSlInT is awesome*/" | |
223 | ].join("\n"); | |
224 | const ast = espree.parse(code, ESPREE_CONFIG); | |
225 | const sourceCode = new SourceCode(code, ast); | |
226 | const comments = sourceCode.getAllComments(); | |
227 | ||
228 | comments.forEach(assertFalse); | |
229 | }); | |
230 | ||
231 | it("should return true if it is a directive line comment", () => { | |
232 | const code = [ | |
233 | "// eslint-disable-line no-undef", | |
234 | "// eslint-secret-directive 4 8 15 16 23 42 ", | |
235 | "// eslint-directive-without-argument", | |
236 | "//eslint-directive-without-padding" | |
237 | ].join("\n"); | |
238 | const ast = espree.parse(code, ESPREE_CONFIG); | |
239 | const sourceCode = new SourceCode(code, ast); | |
240 | const comments = sourceCode.getAllComments(); | |
241 | ||
242 | comments.forEach(assertTrue); | |
243 | }); | |
244 | ||
245 | it("should return true if it is a directive block comment", () => { | |
246 | const code = [ | |
247 | "/* eslint-disable no-undef */", | |
248 | "/*eslint-enable no-undef*/", | |
249 | "/* eslint-env {\"es6\": true} */", | |
250 | "/* eslint foo */", | |
251 | "/*eslint bar*/" | |
252 | ].join("\n"); | |
253 | const ast = espree.parse(code, ESPREE_CONFIG); | |
254 | const sourceCode = new SourceCode(code, ast); | |
255 | const comments = sourceCode.getAllComments(); | |
256 | ||
257 | comments.forEach(assertTrue); | |
258 | }); | |
259 | }); | |
260 | ||
261 | describe("isParenthesised", () => { | |
262 | it("should return false for not parenthesised nodes", () => { | |
263 | const code = "condition ? 1 : 2"; | |
264 | const ast = espree.parse(code, ESPREE_CONFIG); | |
265 | const sourceCode = new SourceCode(code, ast); | |
266 | ||
267 | assert.isFalse(astUtils.isParenthesised(sourceCode, ast.body[0].expression)); | |
268 | }); | |
269 | ||
270 | it("should return true for not parenthesised nodes", () => { | |
271 | const code = "(condition ? 1 : 2)"; | |
272 | const ast = espree.parse(code, ESPREE_CONFIG); | |
273 | const sourceCode = new SourceCode(code, ast); | |
274 | ||
275 | assert.isTrue(astUtils.isParenthesised(sourceCode, ast.body[0].expression)); | |
276 | }); | |
277 | }); | |
278 | ||
279 | describe("isFunction", () => { | |
280 | it("should return true for FunctionDeclaration", () => { | |
281 | const ast = espree.parse("function a() {}"); | |
282 | const node = ast.body[0]; | |
283 | ||
284 | assert(astUtils.isFunction(node)); | |
285 | }); | |
286 | ||
287 | it("should return true for FunctionExpression", () => { | |
288 | const ast = espree.parse("(function a() {})"); | |
289 | const node = ast.body[0].expression; | |
290 | ||
291 | assert(astUtils.isFunction(node)); | |
292 | }); | |
293 | ||
294 | it("should return true for AllowFunctionExpression", () => { | |
295 | const ast = espree.parse("(() => {})", { ecmaVersion: 6 }); | |
296 | const node = ast.body[0].expression; | |
297 | ||
298 | assert(astUtils.isFunction(node)); | |
299 | }); | |
300 | ||
301 | it("should return false for Program, VariableDeclaration, BlockStatement", () => { | |
302 | const ast = espree.parse("var a; { }"); | |
303 | ||
304 | assert(!astUtils.isFunction(ast)); | |
305 | assert(!astUtils.isFunction(ast.body[0])); | |
306 | assert(!astUtils.isFunction(ast.body[1])); | |
307 | }); | |
308 | }); | |
309 | ||
310 | describe("isLoop", () => { | |
311 | it("should return true for DoWhileStatement", () => { | |
312 | const ast = espree.parse("do {} while (a)"); | |
313 | const node = ast.body[0]; | |
314 | ||
315 | assert(astUtils.isLoop(node)); | |
316 | }); | |
317 | ||
318 | it("should return true for ForInStatement", () => { | |
319 | const ast = espree.parse("for (var k in obj) {}"); | |
320 | const node = ast.body[0]; | |
321 | ||
322 | assert(astUtils.isLoop(node)); | |
323 | }); | |
324 | ||
325 | it("should return true for ForOfStatement", () => { | |
326 | const ast = espree.parse("for (var x of list) {}", { ecmaVersion: 6 }); | |
327 | const node = ast.body[0]; | |
328 | ||
329 | assert(astUtils.isLoop(node)); | |
330 | }); | |
331 | ||
332 | it("should return true for ForStatement", () => { | |
333 | const ast = espree.parse("for (var i = 0; i < 10; ++i) {}"); | |
334 | const node = ast.body[0]; | |
335 | ||
336 | assert(astUtils.isLoop(node)); | |
337 | }); | |
338 | ||
339 | it("should return true for WhileStatement", () => { | |
340 | const ast = espree.parse("while (a) {}"); | |
341 | const node = ast.body[0]; | |
342 | ||
343 | assert(astUtils.isLoop(node)); | |
344 | }); | |
345 | ||
346 | it("should return false for Program, VariableDeclaration, BlockStatement", () => { | |
347 | const ast = espree.parse("var a; { }"); | |
348 | ||
349 | assert(!astUtils.isLoop(ast)); | |
350 | assert(!astUtils.isLoop(ast.body[0])); | |
351 | assert(!astUtils.isLoop(ast.body[1])); | |
352 | }); | |
353 | }); | |
354 | ||
355 | describe("isInLoop", () => { | |
356 | ||
357 | /** | |
358 | * Asserts that the unique node of the given type in the code is either | |
359 | * in a loop or not in a loop. | |
360 | * @param {string} code the code to check. | |
361 | * @param {string} nodeType the type of the node to consider. The code | |
362 | * must have exactly one node of ths type. | |
363 | * @param {boolean} expectedInLoop the expected result for whether the | |
364 | * node is in a loop. | |
365 | * @returns {void} | |
366 | */ | |
367 | function assertNodeTypeInLoop(code, nodeType, expectedInLoop) { | |
368 | const results = []; | |
369 | ||
370 | linter.defineRule("checker", mustCall(() => ({ | |
371 | [nodeType]: mustCall(node => { | |
372 | results.push(astUtils.isInLoop(node)); | |
373 | }) | |
374 | }))); | |
375 | linter.verify(code, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } }); | |
376 | ||
377 | assert.lengthOf(results, 1); | |
378 | assert.strictEqual(results[0], expectedInLoop); | |
379 | } | |
380 | ||
381 | it("should return true for a loop itself", () => { | |
382 | assertNodeTypeInLoop("while (a) {}", "WhileStatement", true); | |
383 | }); | |
384 | ||
385 | it("should return true for a loop condition", () => { | |
386 | assertNodeTypeInLoop("while (a) {}", "Identifier", true); | |
387 | }); | |
388 | ||
389 | it("should return true for a loop assignee", () => { | |
390 | assertNodeTypeInLoop("for (var a in b) {}", "VariableDeclaration", true); | |
391 | }); | |
392 | ||
393 | it("should return true for a node within a loop body", () => { | |
394 | assertNodeTypeInLoop("for (var a of b) { console.log('Hello'); }", "Literal", true); | |
395 | }); | |
396 | ||
397 | it("should return false for a node outside a loop body", () => { | |
398 | assertNodeTypeInLoop("while (true) {} a(b);", "CallExpression", false); | |
399 | }); | |
400 | ||
401 | it("should return false when the loop is not in the current function", () => { | |
402 | assertNodeTypeInLoop("while (true) { funcs.push(() => { var a; }); }", "VariableDeclaration", false); | |
403 | }); | |
404 | }); | |
405 | ||
406 | describe("getStaticStringValue", () => { | |
407 | ||
408 | /* eslint-disable quote-props */ | |
409 | const expectedResults = { | |
410 | ||
411 | // string literals | |
412 | "''": "", | |
413 | "'foo'": "foo", | |
414 | ||
415 | // boolean literals | |
416 | "false": "false", | |
417 | "true": "true", | |
418 | ||
419 | // null literal | |
420 | "null": "null", | |
421 | ||
422 | // number literals | |
423 | "0": "0", | |
424 | "0.": "0", | |
425 | ".0": "0", | |
426 | "1": "1", | |
427 | "1.": "1", | |
428 | ".1": "0.1", | |
429 | "12": "12", | |
430 | ".12": "0.12", | |
431 | "0.12": "0.12", | |
432 | "12.34": "12.34", | |
433 | "12e3": "12000", | |
434 | "12e-3": "0.012", | |
435 | "12.34e5": "1234000", | |
436 | "12.34e-5": "0.0001234", | |
437 | "011": "9", | |
438 | "081": "81", | |
439 | "0b11": "3", | |
440 | "0b011": "3", | |
441 | "0o11": "9", | |
442 | "0o011": "9", | |
443 | "0x11": "17", | |
444 | "0x011": "17", | |
445 | ||
446 | // regexp literals | |
447 | "/a/": "/a/", | |
448 | "/a/i": "/a/i", | |
449 | "/[0-9]/": "/[0-9]/", | |
450 | "/(?<zero>0)/": "/(?<zero>0)/", | |
451 | "/(?<zero>0)/s": "/(?<zero>0)/s", | |
452 | "/(?<=a)b/s": "/(?<=a)b/s", | |
453 | ||
454 | // simple template literals | |
455 | "``": "", | |
456 | "`foo`": "foo", | |
457 | ||
458 | // unsupported | |
459 | "`${''}`": null, | |
460 | "`${0}`": null, | |
461 | "tag``": null, | |
462 | "-0": null, | |
463 | "-1": null, | |
464 | "1 + 2": null, | |
465 | "[]": null, | |
466 | "({})": null, | |
467 | "foo": null, | |
468 | "undefined": null, | |
469 | "this": null, | |
470 | "(function () {})": null | |
471 | }; | |
472 | /* eslint-enable quote-props */ | |
473 | ||
474 | Object.keys(expectedResults).forEach(key => { | |
475 | it(`should return ${expectedResults[key]} for ${key}`, () => { | |
476 | const ast = espree.parse(key, { ecmaVersion: 2018 }); | |
477 | ||
478 | assert.strictEqual(astUtils.getStaticStringValue(ast.body[0].expression), expectedResults[key]); | |
479 | }); | |
480 | }); | |
481 | }); | |
482 | ||
483 | describe("getStaticPropertyName", () => { | |
484 | it("should return 'b' for `a.b`", () => { | |
485 | const ast = espree.parse("a.b"); | |
486 | const node = ast.body[0].expression; | |
487 | ||
488 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
489 | }); | |
490 | ||
491 | it("should return 'b' for `a['b']`", () => { | |
492 | const ast = espree.parse("a['b']"); | |
493 | const node = ast.body[0].expression; | |
494 | ||
495 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
496 | }); | |
497 | ||
498 | it("should return 'b' for `a[`b`]`", () => { | |
499 | const ast = espree.parse("a[`b`]", { ecmaVersion: 6 }); | |
500 | const node = ast.body[0].expression; | |
501 | ||
502 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
503 | }); | |
504 | ||
505 | it("should return '100' for `a[100]`", () => { | |
506 | const ast = espree.parse("a[100]"); | |
507 | const node = ast.body[0].expression; | |
508 | ||
509 | assert.strictEqual(astUtils.getStaticPropertyName(node), "100"); | |
510 | }); | |
511 | ||
512 | it("should return null for `a[b]`", () => { | |
513 | const ast = espree.parse("a[b]"); | |
514 | const node = ast.body[0].expression; | |
515 | ||
516 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
517 | }); | |
518 | ||
519 | it("should return null for `a['a' + 'b']`", () => { | |
520 | const ast = espree.parse("a['a' + 'b']"); | |
521 | const node = ast.body[0].expression; | |
522 | ||
523 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
524 | }); | |
525 | ||
526 | it("should return null for `a[tag`b`]`", () => { | |
527 | const ast = espree.parse("a[tag`b`]", { ecmaVersion: 6 }); | |
528 | const node = ast.body[0].expression; | |
529 | ||
530 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
531 | }); | |
532 | ||
533 | it("should return null for `a[`${b}`]`", () => { | |
534 | const ast = espree.parse("a[`${b}`]", { ecmaVersion: 6 }); | |
535 | const node = ast.body[0].expression; | |
536 | ||
537 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
538 | }); | |
539 | ||
540 | it("should return 'b' for `b: 1`", () => { | |
541 | const ast = espree.parse("({b: 1})"); | |
542 | const node = ast.body[0].expression.properties[0]; | |
543 | ||
544 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
545 | }); | |
546 | ||
547 | it("should return 'b' for `b() {}`", () => { | |
548 | const ast = espree.parse("({b() {}})", { ecmaVersion: 6 }); | |
549 | const node = ast.body[0].expression.properties[0]; | |
550 | ||
551 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
552 | }); | |
553 | ||
554 | it("should return 'b' for `get b() {}`", () => { | |
555 | const ast = espree.parse("({get b() {}})", { ecmaVersion: 6 }); | |
556 | const node = ast.body[0].expression.properties[0]; | |
557 | ||
558 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
559 | }); | |
560 | ||
561 | it("should return 'b' for `['b']: 1`", () => { | |
562 | const ast = espree.parse("({['b']: 1})", { ecmaVersion: 6 }); | |
563 | const node = ast.body[0].expression.properties[0]; | |
564 | ||
565 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
566 | }); | |
567 | ||
568 | it("should return 'b' for `['b']() {}`", () => { | |
569 | const ast = espree.parse("({['b']() {}})", { ecmaVersion: 6 }); | |
570 | const node = ast.body[0].expression.properties[0]; | |
571 | ||
572 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
573 | }); | |
574 | ||
575 | it("should return 'b' for `[`b`]: 1`", () => { | |
576 | const ast = espree.parse("({[`b`]: 1})", { ecmaVersion: 6 }); | |
577 | const node = ast.body[0].expression.properties[0]; | |
578 | ||
579 | assert.strictEqual(astUtils.getStaticPropertyName(node), "b"); | |
580 | }); | |
581 | ||
582 | it("should return '100' for` [100]: 1`", () => { | |
583 | const ast = espree.parse("({[100]: 1})", { ecmaVersion: 6 }); | |
584 | const node = ast.body[0].expression.properties[0]; | |
585 | ||
586 | assert.strictEqual(astUtils.getStaticPropertyName(node), "100"); | |
587 | }); | |
588 | ||
589 | it("should return '/(?<zero>0)/' for `[/(?<zero>0)/]: 1`", () => { | |
590 | const ast = espree.parse("({[/(?<zero>0)/]: 1})", { ecmaVersion: 2018 }); | |
591 | const node = ast.body[0].expression.properties[0]; | |
592 | ||
593 | assert.strictEqual(astUtils.getStaticPropertyName(node), "/(?<zero>0)/"); | |
594 | }); | |
595 | ||
596 | it("should return null for `[b]: 1`", () => { | |
597 | const ast = espree.parse("({[b]: 1})", { ecmaVersion: 6 }); | |
598 | const node = ast.body[0].expression.properties[0]; | |
599 | ||
600 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
601 | }); | |
602 | ||
603 | it("should return null for `['a' + 'b']: 1`", () => { | |
604 | const ast = espree.parse("({['a' + 'b']: 1})", { ecmaVersion: 6 }); | |
605 | const node = ast.body[0].expression.properties[0]; | |
606 | ||
607 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
608 | }); | |
609 | ||
610 | it("should return null for `[tag`b`]: 1`", () => { | |
611 | const ast = espree.parse("({[tag`b`]: 1})", { ecmaVersion: 6 }); | |
612 | const node = ast.body[0].expression.properties[0]; | |
613 | ||
614 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
615 | }); | |
616 | ||
617 | it("should return null for `[`${b}`]: 1`", () => { | |
618 | const ast = espree.parse("({[`${b}`]: 1})", { ecmaVersion: 6 }); | |
619 | const node = ast.body[0].expression.properties[0]; | |
620 | ||
621 | assert.strictEqual(astUtils.getStaticPropertyName(node), null); | |
622 | }); | |
623 | ||
624 | it("should return null for non member expressions", () => { | |
625 | const ast = espree.parse("foo()"); | |
626 | ||
627 | assert.strictEqual(astUtils.getStaticPropertyName(ast.body[0].expression), null); | |
628 | assert.strictEqual(astUtils.getStaticPropertyName(ast.body[0]), null); | |
629 | assert.strictEqual(astUtils.getStaticPropertyName(ast.body), null); | |
630 | assert.strictEqual(astUtils.getStaticPropertyName(ast), null); | |
631 | assert.strictEqual(astUtils.getStaticPropertyName(null), null); | |
632 | }); | |
633 | }); | |
634 | ||
635 | describe("getDirectivePrologue", () => { | |
636 | it("should return empty array if node is not a Program, FunctionDeclaration, FunctionExpression, or ArrowFunctionExpression", () => { | |
637 | const ast = espree.parse("if (a) { b(); }"); | |
638 | const node = ast.body[0]; | |
639 | ||
640 | assert.deepStrictEqual(astUtils.getDirectivePrologue(node), []); | |
641 | }); | |
642 | ||
643 | it("should return empty array if node is a braceless ArrowFunctionExpression node", () => { | |
644 | const ast = espree.parse("var foo = () => 'use strict';", { ecmaVersion: 6 }); | |
645 | const node = ast.body[0].declarations[0].init; | |
646 | ||
647 | assert.deepStrictEqual(astUtils.getDirectivePrologue(node), []); | |
648 | }); | |
649 | ||
650 | it("should return empty array if there are no directives in Program body", () => { | |
651 | const ast = espree.parse("var foo;"); | |
652 | const node = ast; | |
653 | ||
654 | assert.deepStrictEqual(astUtils.getDirectivePrologue(node), []); | |
655 | }); | |
656 | ||
657 | it("should return empty array if there are no directives in FunctionDeclaration body", () => { | |
658 | const ast = espree.parse("function foo() { return bar; }"); | |
659 | const node = ast.body[0]; | |
660 | ||
661 | assert.deepStrictEqual(astUtils.getDirectivePrologue(node), []); | |
662 | }); | |
663 | ||
664 | it("should return empty array if there are no directives in FunctionExpression body", () => { | |
665 | const ast = espree.parse("var foo = function() { return bar; }"); | |
666 | const node = ast.body[0].declarations[0].init; | |
667 | ||
668 | assert.deepStrictEqual(astUtils.getDirectivePrologue(node), []); | |
669 | }); | |
670 | ||
671 | it("should return empty array if there are no directives in ArrowFunctionExpression body", () => { | |
672 | const ast = espree.parse("var foo = () => { return bar; };", { ecmaVersion: 6 }); | |
673 | const node = ast.body[0].declarations[0].init; | |
674 | ||
675 | assert.deepStrictEqual(astUtils.getDirectivePrologue(node), []); | |
676 | }); | |
677 | ||
678 | it("should return directives in Program body", () => { | |
679 | const ast = espree.parse("'use strict'; 'use asm'; var foo;"); | |
680 | const result = astUtils.getDirectivePrologue(ast); | |
681 | ||
682 | assert.strictEqual(result.length, 2); | |
683 | assert.strictEqual(result[0].expression.value, "use strict"); | |
684 | assert.strictEqual(result[1].expression.value, "use asm"); | |
685 | }); | |
686 | ||
687 | it("should return directives in FunctionDeclaration body", () => { | |
688 | const ast = espree.parse("function foo() { 'use strict'; 'use asm'; return bar; }"); | |
689 | const result = astUtils.getDirectivePrologue(ast.body[0]); | |
690 | ||
691 | assert.strictEqual(result.length, 2); | |
692 | assert.strictEqual(result[0].expression.value, "use strict"); | |
693 | assert.strictEqual(result[1].expression.value, "use asm"); | |
694 | }); | |
695 | ||
696 | it("should return directives in FunctionExpression body", () => { | |
697 | const ast = espree.parse("var foo = function() { 'use strict'; 'use asm'; return bar; }"); | |
698 | const result = astUtils.getDirectivePrologue(ast.body[0].declarations[0].init); | |
699 | ||
700 | assert.strictEqual(result.length, 2); | |
701 | assert.strictEqual(result[0].expression.value, "use strict"); | |
702 | assert.strictEqual(result[1].expression.value, "use asm"); | |
703 | }); | |
704 | ||
705 | it("should return directives in ArrowFunctionExpression body", () => { | |
706 | const ast = espree.parse("var foo = () => { 'use strict'; 'use asm'; return bar; };", { ecmaVersion: 6 }); | |
707 | const result = astUtils.getDirectivePrologue(ast.body[0].declarations[0].init); | |
708 | ||
709 | assert.strictEqual(result.length, 2); | |
710 | assert.strictEqual(result[0].expression.value, "use strict"); | |
711 | assert.strictEqual(result[1].expression.value, "use asm"); | |
712 | }); | |
713 | }); | |
714 | ||
715 | { | |
716 | const expectedResults = { | |
717 | 5: true, | |
718 | 0: true, | |
719 | "5.": false, | |
720 | "5.0": false, | |
721 | "05": false, | |
722 | "0x5": false, | |
723 | "5e0": false, | |
724 | "5e-0": false, | |
725 | "'5'": false | |
726 | }; | |
727 | ||
728 | describe("isDecimalInteger", () => { | |
729 | Object.keys(expectedResults).forEach(key => { | |
730 | it(`should return ${expectedResults[key]} for ${key}`, () => { | |
731 | assert.strictEqual(astUtils.isDecimalInteger(espree.parse(key).body[0].expression), expectedResults[key]); | |
732 | }); | |
733 | }); | |
734 | }); | |
735 | ||
736 | describe("isDecimalIntegerNumericToken", () => { | |
737 | Object.keys(expectedResults).forEach(key => { | |
738 | it(`should return ${expectedResults[key]} for ${key}`, () => { | |
739 | assert.strictEqual(astUtils.isDecimalIntegerNumericToken(espree.tokenize(key)[0]), expectedResults[key]); | |
740 | }); | |
741 | }); | |
742 | }); | |
743 | } | |
744 | ||
745 | describe("getFunctionNameWithKind", () => { | |
746 | const expectedResults = { | |
747 | "function foo() {}": "function 'foo'", | |
748 | "(function foo() {})": "function 'foo'", | |
749 | "(function() {})": "function", | |
750 | "function* foo() {}": "generator function 'foo'", | |
751 | "(function* foo() {})": "generator function 'foo'", | |
752 | "(function*() {})": "generator function", | |
753 | "() => {}": "arrow function", | |
754 | "async () => {}": "async arrow function", | |
755 | "({ foo: function foo() {} })": "method 'foo'", | |
756 | "({ foo: function() {} })": "method 'foo'", | |
757 | "({ '': function() {} })": "method ''", | |
758 | "({ ['foo']: function() {} })": "method 'foo'", | |
759 | "({ ['']: function() {} })": "method ''", | |
760 | "({ [foo]: function() {} })": "method", | |
761 | "({ foo() {} })": "method 'foo'", | |
762 | "({ foo: function* foo() {} })": "generator method 'foo'", | |
763 | "({ foo: function*() {} })": "generator method 'foo'", | |
764 | "({ ['foo']: function*() {} })": "generator method 'foo'", | |
765 | "({ [foo]: function*() {} })": "generator method", | |
766 | "({ *foo() {} })": "generator method 'foo'", | |
767 | "({ foo: async function foo() {} })": "async method 'foo'", | |
768 | "({ foo: async function() {} })": "async method 'foo'", | |
769 | "({ ['foo']: async function() {} })": "async method 'foo'", | |
770 | "({ [foo]: async function() {} })": "async method", | |
771 | "({ async foo() {} })": "async method 'foo'", | |
772 | "({ get foo() {} })": "getter 'foo'", | |
773 | "({ set foo(a) {} })": "setter 'foo'", | |
774 | "class A { constructor() {} }": "constructor", | |
775 | "class A { foo() {} }": "method 'foo'", | |
776 | "class A { *foo() {} }": "generator method 'foo'", | |
777 | "class A { async foo() {} }": "async method 'foo'", | |
778 | "class A { ['foo']() {} }": "method 'foo'", | |
779 | "class A { *['foo']() {} }": "generator method 'foo'", | |
780 | "class A { async ['foo']() {} }": "async method 'foo'", | |
781 | "class A { [foo]() {} }": "method", | |
782 | "class A { *[foo]() {} }": "generator method", | |
783 | "class A { async [foo]() {} }": "async method", | |
784 | "class A { get foo() {} }": "getter 'foo'", | |
785 | "class A { set foo(a) {} }": "setter 'foo'", | |
786 | "class A { static foo() {} }": "static method 'foo'", | |
787 | "class A { static *foo() {} }": "static generator method 'foo'", | |
788 | "class A { static async foo() {} }": "static async method 'foo'", | |
789 | "class A { static get foo() {} }": "static getter 'foo'", | |
790 | "class A { static set foo(a) {} }": "static setter 'foo'" | |
791 | }; | |
792 | ||
793 | Object.keys(expectedResults).forEach(key => { | |
794 | it(`should return "${expectedResults[key]}" for "${key}".`, () => { | |
795 | linter.defineRule("checker", mustCall(() => ({ | |
796 | ":function": mustCall(node => { | |
797 | assert.strictEqual( | |
798 | astUtils.getFunctionNameWithKind(node), | |
799 | expectedResults[key] | |
800 | ); | |
801 | }) | |
802 | }))); | |
803 | ||
804 | linter.verify(key, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 8 } }); | |
805 | }); | |
806 | }); | |
807 | }); | |
808 | ||
809 | describe("getFunctionHeadLoc", () => { | |
810 | const expectedResults = { | |
811 | "function foo() {}": [0, 12], | |
812 | "(function foo() {})": [1, 13], | |
813 | "(function() {})": [1, 9], | |
814 | "function* foo() {}": [0, 13], | |
815 | "(function* foo() {})": [1, 14], | |
816 | "(function*() {})": [1, 10], | |
817 | "() => {}": [3, 5], | |
818 | "async () => {}": [9, 11], | |
819 | "({ foo: function foo() {} })": [3, 20], | |
820 | "({ foo: function() {} })": [3, 16], | |
821 | "({ ['foo']: function() {} })": [3, 20], | |
822 | "({ [foo]: function() {} })": [3, 18], | |
823 | "({ foo() {} })": [3, 6], | |
824 | "({ foo: function* foo() {} })": [3, 21], | |
825 | "({ foo: function*() {} })": [3, 17], | |
826 | "({ ['foo']: function*() {} })": [3, 21], | |
827 | "({ [foo]: function*() {} })": [3, 19], | |
828 | "({ *foo() {} })": [3, 7], | |
829 | "({ foo: async function foo() {} })": [3, 26], | |
830 | "({ foo: async function() {} })": [3, 22], | |
831 | "({ ['foo']: async function() {} })": [3, 26], | |
832 | "({ [foo]: async function() {} })": [3, 24], | |
833 | "({ async foo() {} })": [3, 12], | |
834 | "({ get foo() {} })": [3, 10], | |
835 | "({ set foo(a) {} })": [3, 10], | |
836 | "class A { constructor() {} }": [10, 21], | |
837 | "class A { foo() {} }": [10, 13], | |
838 | "class A { *foo() {} }": [10, 14], | |
839 | "class A { async foo() {} }": [10, 19], | |
840 | "class A { ['foo']() {} }": [10, 17], | |
841 | "class A { *['foo']() {} }": [10, 18], | |
842 | "class A { async ['foo']() {} }": [10, 23], | |
843 | "class A { [foo]() {} }": [10, 15], | |
844 | "class A { *[foo]() {} }": [10, 16], | |
845 | "class A { async [foo]() {} }": [10, 21], | |
846 | "class A { get foo() {} }": [10, 17], | |
847 | "class A { set foo(a) {} }": [10, 17], | |
848 | "class A { static foo() {} }": [10, 20], | |
849 | "class A { static *foo() {} }": [10, 21], | |
850 | "class A { static async foo() {} }": [10, 26], | |
851 | "class A { static get foo() {} }": [10, 24], | |
852 | "class A { static set foo(a) {} }": [10, 24] | |
853 | }; | |
854 | ||
855 | Object.keys(expectedResults).forEach(key => { | |
856 | const expectedLoc = { | |
857 | start: { | |
858 | line: 1, | |
859 | column: expectedResults[key][0] | |
860 | }, | |
861 | end: { | |
862 | line: 1, | |
863 | column: expectedResults[key][1] | |
864 | } | |
865 | }; | |
866 | ||
867 | it(`should return "${JSON.stringify(expectedLoc)}" for "${key}".`, () => { | |
868 | linter.defineRule("checker", mustCall(() => ({ | |
869 | ":function": mustCall(node => { | |
870 | assert.deepStrictEqual( | |
871 | astUtils.getFunctionHeadLoc(node, linter.getSourceCode()), | |
872 | expectedLoc | |
873 | ); | |
874 | }) | |
875 | }))); | |
876 | ||
877 | linter.verify(key, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 8 } }, "test.js", true); | |
878 | }); | |
879 | }); | |
880 | }); | |
881 | ||
882 | describe("isEmptyBlock", () => { | |
883 | const expectedResults = { | |
884 | "{}": true, | |
885 | "{ a }": false, | |
886 | a: false | |
887 | }; | |
888 | ||
889 | Object.keys(expectedResults).forEach(key => { | |
890 | it(`should return ${expectedResults[key]} for ${key}`, () => { | |
891 | const ast = espree.parse(key); | |
892 | ||
893 | assert.strictEqual(astUtils.isEmptyBlock(ast.body[0]), expectedResults[key]); | |
894 | }); | |
895 | }); | |
896 | }); | |
897 | ||
898 | describe("isEmptyFunction", () => { | |
899 | const expectedResults = { | |
900 | "(function foo() {})": true, | |
901 | "(function foo() { a })": false, | |
902 | "(a) => {}": true, | |
903 | "(a) => { a }": false, | |
904 | "(a) => a": false | |
905 | }; | |
906 | ||
907 | Object.keys(expectedResults).forEach(key => { | |
908 | it(`should return ${expectedResults[key]} for ${key}`, () => { | |
909 | const ast = espree.parse(key, { ecmaVersion: 6 }); | |
910 | ||
911 | assert.strictEqual(astUtils.isEmptyFunction(ast.body[0].expression), expectedResults[key]); | |
912 | }); | |
913 | }); | |
914 | }); | |
915 | ||
916 | describe("getNextLocation", () => { | |
eb39fafa | 917 | |
ebb53d86 TL |
918 | /* eslint-disable quote-props */ |
919 | const expectedResults = { | |
920 | "": [[1, 0], null], | |
921 | "\n": [[1, 0], [2, 0], null], | |
922 | "\r\n": [[1, 0], [2, 0], null], | |
923 | "foo": [[1, 0], [1, 1], [1, 2], [1, 3], null], | |
924 | "foo\n": [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], null], | |
925 | "foo\r\n": [[1, 0], [1, 1], [1, 2], [1, 3], [2, 0], null], | |
926 | "foo;\n": [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [2, 0], null], | |
927 | "a\nb": [[1, 0], [1, 1], [2, 0], [2, 1], null], | |
928 | "a\nb\n": [[1, 0], [1, 1], [2, 0], [2, 1], [3, 0], null], | |
929 | "a\r\nb\r\n": [[1, 0], [1, 1], [2, 0], [2, 1], [3, 0], null], | |
930 | "a\nb\r\n": [[1, 0], [1, 1], [2, 0], [2, 1], [3, 0], null], | |
931 | "a\n\n": [[1, 0], [1, 1], [2, 0], [3, 0], null], | |
932 | "a\r\n\r\n": [[1, 0], [1, 1], [2, 0], [3, 0], null], | |
933 | "\n\r\n\n\r\n": [[1, 0], [2, 0], [3, 0], [4, 0], [5, 0], null], | |
934 | "ab\u2029c": [[1, 0], [1, 1], [1, 2], [2, 0], [2, 1], null], | |
935 | "ab\ncde\n": [[1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], null], | |
936 | "a ": [[1, 0], [1, 1], [1, 2], null], | |
937 | "a\t": [[1, 0], [1, 1], [1, 2], null], | |
938 | "a \n": [[1, 0], [1, 1], [1, 2], [2, 0], null] | |
939 | }; | |
940 | /* eslint-enable quote-props */ | |
eb39fafa | 941 | |
ebb53d86 TL |
942 | Object.keys(expectedResults).forEach(code => { |
943 | it(`should return expected locations for "${code}".`, () => { | |
944 | const ast = espree.parse(code, ESPREE_CONFIG); | |
945 | const sourceCode = new SourceCode(code, ast); | |
946 | const locations = expectedResults[code]; | |
947 | ||
948 | for (let i = 0; i < locations.length - 1; i++) { | |
949 | const location = { line: locations[i][0], column: locations[i][1] }; | |
950 | const expectedNextLocation = locations[i + 1] | |
951 | ? { line: locations[i + 1][0], column: locations[i + 1][1] } | |
952 | : null; | |
953 | ||
954 | assert.deepStrictEqual( | |
955 | astUtils.getNextLocation(sourceCode, location), | |
956 | expectedNextLocation | |
957 | ); | |
958 | } | |
959 | }); | |
eb39fafa DC |
960 | }); |
961 | }); | |
962 | ||
963 | describe("getParenthesisedText", () => { | |
964 | const expectedResults = { | |
965 | "(((foo))); bar;": "(((foo)))", | |
966 | "(/* comment */(((foo.bar())))); baz();": "(/* comment */(((foo.bar()))))", | |
967 | "(foo, bar)": "(foo, bar)" | |
968 | }; | |
969 | ||
970 | Object.keys(expectedResults).forEach(key => { | |
971 | it(`should return ${expectedResults[key]} for ${key}`, () => { | |
972 | const ast = espree.parse(key, { tokens: true, comment: true, range: true, loc: true }); | |
973 | const sourceCode = new SourceCode(key, ast); | |
974 | ||
975 | assert.strictEqual(astUtils.getParenthesisedText(sourceCode, ast.body[0].expression), expectedResults[key]); | |
976 | }); | |
977 | }); | |
978 | }); | |
979 | ||
980 | describe("couldBeError", () => { | |
981 | const EXPECTED_RESULTS = { | |
982 | 5: false, | |
983 | null: false, | |
984 | true: false, | |
985 | "'foo'": false, | |
986 | "`foo`": false, | |
987 | foo: true, | |
988 | "new Foo": true, | |
989 | "Foo()": true, | |
990 | "foo`bar`": true, | |
991 | "foo.bar": true, | |
992 | "(foo = bar)": true, | |
993 | "(foo = 1)": false, | |
994 | "(1, 2, 3)": false, | |
995 | "(foo, 2, 3)": false, | |
996 | "(1, 2, foo)": true, | |
997 | "1 && 2": false, | |
998 | "1 && foo": true, | |
999 | "foo && 2": true, | |
1000 | "foo ? 1 : 2": false, | |
1001 | "foo ? bar : 2": true, | |
1002 | "foo ? 1 : bar": true, | |
1003 | "[1, 2, 3]": false, | |
1004 | "({ foo: 1 })": false | |
1005 | }; | |
1006 | ||
1007 | Object.keys(EXPECTED_RESULTS).forEach(key => { | |
1008 | it(`returns ${EXPECTED_RESULTS[key]} for ${key}`, () => { | |
1009 | const ast = espree.parse(key, { ecmaVersion: 6 }); | |
1010 | ||
1011 | assert.strictEqual(astUtils.couldBeError(ast.body[0].expression), EXPECTED_RESULTS[key]); | |
1012 | }); | |
1013 | }); | |
1014 | }); | |
1015 | ||
1016 | describe("isArrowToken", () => { | |
1017 | const code = "() => 5"; | |
1018 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1019 | const expected = [false, false, true, false]; | |
1020 | ||
1021 | tokens.forEach((token, index) => { | |
1022 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1023 | assert.strictEqual(astUtils.isArrowToken(token), expected[index]); | |
1024 | }); | |
1025 | }); | |
1026 | }); | |
1027 | ||
1028 | { | |
1029 | const code = "if (obj && foo) { obj[foo](); }"; | |
1030 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1031 | const expected = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, true]; | |
1032 | ||
1033 | describe("isClosingBraceToken", () => { | |
1034 | tokens.forEach((token, index) => { | |
1035 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1036 | assert.strictEqual(astUtils.isClosingBraceToken(token), expected[index]); | |
1037 | }); | |
1038 | }); | |
1039 | }); | |
1040 | ||
1041 | describe("isNotClosingBraceToken", () => { | |
1042 | tokens.forEach((token, index) => { | |
1043 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1044 | assert.strictEqual(astUtils.isNotClosingBraceToken(token), !expected[index]); | |
1045 | }); | |
1046 | }); | |
1047 | }); | |
1048 | } | |
1049 | ||
1050 | { | |
1051 | const code = "if (obj && foo) { obj[foo](); }"; | |
1052 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1053 | const expected = [false, false, false, false, false, false, false, false, false, false, true, false, false, false, false]; | |
1054 | ||
1055 | describe("isClosingBracketToken", () => { | |
1056 | tokens.forEach((token, index) => { | |
1057 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1058 | assert.strictEqual(astUtils.isClosingBracketToken(token), expected[index]); | |
1059 | }); | |
1060 | }); | |
1061 | }); | |
1062 | ||
1063 | describe("isNotClosingBracketToken", () => { | |
1064 | tokens.forEach((token, index) => { | |
1065 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1066 | assert.strictEqual(astUtils.isNotClosingBracketToken(token), !expected[index]); | |
1067 | }); | |
1068 | }); | |
1069 | }); | |
1070 | } | |
1071 | ||
1072 | { | |
1073 | const code = "if (obj && foo) { obj[foo](); }"; | |
1074 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1075 | const expected = [false, false, false, false, false, true, false, false, false, false, false, false, true, false, false]; | |
1076 | ||
1077 | describe("isClosingParenToken", () => { | |
1078 | tokens.forEach((token, index) => { | |
1079 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1080 | assert.strictEqual(astUtils.isClosingParenToken(token), expected[index]); | |
1081 | }); | |
1082 | }); | |
1083 | }); | |
1084 | ||
1085 | describe("isNotClosingParenToken", () => { | |
1086 | tokens.forEach((token, index) => { | |
1087 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1088 | assert.strictEqual(astUtils.isNotClosingParenToken(token), !expected[index]); | |
1089 | }); | |
1090 | }); | |
1091 | }); | |
1092 | } | |
1093 | ||
1094 | { | |
1095 | const code = "const obj = {foo: 1, bar: 2};"; | |
1096 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1097 | const expected = [false, false, false, false, false, true, false, false, false, true, false, false, false]; | |
1098 | ||
1099 | describe("isColonToken", () => { | |
1100 | tokens.forEach((token, index) => { | |
1101 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1102 | assert.strictEqual(astUtils.isColonToken(token), expected[index]); | |
1103 | }); | |
1104 | }); | |
1105 | }); | |
1106 | ||
1107 | describe("isNotColonToken", () => { | |
1108 | tokens.forEach((token, index) => { | |
1109 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1110 | assert.strictEqual(astUtils.isNotColonToken(token), !expected[index]); | |
1111 | }); | |
1112 | }); | |
1113 | }); | |
1114 | } | |
1115 | ||
1116 | { | |
1117 | const code = "const obj = {foo: 1, bar: 2};"; | |
1118 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1119 | const expected = [false, false, false, false, false, false, false, true, false, false, false, false, false]; | |
1120 | ||
1121 | describe("isCommaToken", () => { | |
1122 | tokens.forEach((token, index) => { | |
1123 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1124 | assert.strictEqual(astUtils.isCommaToken(token), expected[index]); | |
1125 | }); | |
1126 | }); | |
1127 | }); | |
1128 | ||
1129 | describe("isNotCommaToken", () => { | |
1130 | tokens.forEach((token, index) => { | |
1131 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1132 | assert.strictEqual(astUtils.isNotCommaToken(token), !expected[index]); | |
1133 | }); | |
1134 | }); | |
1135 | }); | |
1136 | } | |
1137 | ||
1138 | { | |
1139 | const code = "const obj = {foo: 1.5, bar: a.b};"; | |
1140 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1141 | const expected = [false, false, false, false, false, false, false, false, false, false, false, true, false, false, false]; | |
1142 | ||
1143 | describe("isDotToken", () => { | |
1144 | tokens.forEach((token, index) => { | |
1145 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1146 | assert.strictEqual(astUtils.isDotToken(token), expected[index]); | |
1147 | }); | |
1148 | }); | |
1149 | }); | |
1150 | ||
1151 | describe("isNotDotToken", () => { | |
1152 | tokens.forEach((token, index) => { | |
1153 | it(`should return ${!expected[index]} for '${token.value}'.`, () => { | |
1154 | assert.strictEqual(astUtils.isNotDotToken(token), !expected[index]); | |
1155 | }); | |
1156 | }); | |
1157 | }); | |
1158 | } | |
1159 | ||
1160 | describe("isCommentToken", () => { | |
1161 | const code = "const obj = /*block*/ {foo: 1, bar: 2}; //line"; | |
1162 | const ast = espree.parse(code, { ecmaVersion: 6, tokens: true, comment: true }); | |
1163 | ||
1164 | ast.tokens.forEach(token => { | |
1165 | it(`should return false for '${token.value}'.`, () => { | |
1166 | assert.strictEqual(astUtils.isCommentToken(token), false); | |
1167 | }); | |
1168 | }); | |
1169 | ast.comments.forEach(comment => { | |
1170 | it(`should return true for '${comment.value}'.`, () => { | |
1171 | assert.strictEqual(astUtils.isCommentToken(comment), true); | |
1172 | }); | |
1173 | }); | |
1174 | }); | |
1175 | ||
1176 | describe("isKeywordToken", () => { | |
1177 | const code = "const obj = {foo: 1, bar: 2};"; | |
1178 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1179 | const expected = [true, false, false, false, false, false, false, false, false, false, false, false, false]; | |
1180 | ||
1181 | tokens.forEach((token, index) => { | |
1182 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1183 | assert.strictEqual(astUtils.isKeywordToken(token), expected[index]); | |
1184 | }); | |
1185 | }); | |
1186 | }); | |
1187 | ||
1188 | { | |
1189 | const code = "if (obj && foo) { obj[foo](); }"; | |
1190 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1191 | const expected = [false, false, false, false, false, false, true, false, false, false, false, false, false, false, false]; | |
1192 | ||
1193 | describe("isOpeningBraceToken", () => { | |
1194 | tokens.forEach((token, index) => { | |
1195 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1196 | assert.strictEqual(astUtils.isOpeningBraceToken(token), expected[index]); | |
1197 | }); | |
1198 | }); | |
1199 | }); | |
1200 | ||
1201 | describe("isNotOpeningBraceToken", () => { | |
1202 | tokens.forEach((token, index) => { | |
1203 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1204 | assert.strictEqual(astUtils.isNotOpeningBraceToken(token), !expected[index]); | |
1205 | }); | |
1206 | }); | |
1207 | }); | |
1208 | } | |
1209 | ||
1210 | { | |
1211 | const code = "if (obj && foo) { obj[foo](); }"; | |
1212 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1213 | const expected = [false, false, false, false, false, false, false, false, true, false, false, false, false, false, false]; | |
1214 | ||
1215 | describe("isOpeningBracketToken", () => { | |
1216 | tokens.forEach((token, index) => { | |
1217 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1218 | assert.strictEqual(astUtils.isOpeningBracketToken(token), expected[index]); | |
1219 | }); | |
1220 | }); | |
1221 | }); | |
1222 | ||
1223 | describe("isNotOpeningBracketToken", () => { | |
1224 | tokens.forEach((token, index) => { | |
1225 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1226 | assert.strictEqual(astUtils.isNotOpeningBracketToken(token), !expected[index]); | |
1227 | }); | |
1228 | }); | |
1229 | }); | |
1230 | } | |
1231 | ||
1232 | { | |
1233 | const code = "if (obj && foo) { obj[foo](); }"; | |
1234 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1235 | const expected = [false, true, false, false, false, false, false, false, false, false, false, true, false, false, false]; | |
1236 | ||
1237 | describe("isOpeningParenToken", () => { | |
1238 | tokens.forEach((token, index) => { | |
1239 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1240 | assert.strictEqual(astUtils.isOpeningParenToken(token), expected[index]); | |
1241 | }); | |
1242 | }); | |
1243 | }); | |
1244 | ||
1245 | describe("isNotOpeningParenToken", () => { | |
1246 | tokens.forEach((token, index) => { | |
1247 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1248 | assert.strictEqual(astUtils.isNotOpeningParenToken(token), !expected[index]); | |
1249 | }); | |
1250 | }); | |
1251 | }); | |
1252 | } | |
1253 | ||
1254 | { | |
1255 | const code = "if (obj && foo) { obj[foo](); }"; | |
1256 | const tokens = espree.parse(code, { ecmaVersion: 6, tokens: true }).tokens; | |
1257 | const expected = [false, false, false, false, false, false, false, false, false, false, false, false, false, true, false]; | |
1258 | ||
1259 | describe("isSemicolonToken", () => { | |
1260 | tokens.forEach((token, index) => { | |
1261 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1262 | assert.strictEqual(astUtils.isSemicolonToken(token), expected[index]); | |
1263 | }); | |
1264 | }); | |
1265 | }); | |
1266 | ||
1267 | describe("isNotSemicolonToken", () => { | |
1268 | tokens.forEach((token, index) => { | |
1269 | it(`should return ${expected[index]} for '${token.value}'.`, () => { | |
1270 | assert.strictEqual(astUtils.isNotSemicolonToken(token), !expected[index]); | |
1271 | }); | |
1272 | }); | |
1273 | }); | |
1274 | } | |
1275 | ||
1276 | describe("isNullLiteral", () => { | |
1277 | const EXPECTED_RESULTS = { | |
1278 | null: true, | |
1279 | "/abc/u": false, | |
1280 | 5: false, | |
1281 | true: false, | |
1282 | "'null'": false, | |
1283 | foo: false | |
1284 | }; | |
1285 | ||
1286 | Object.keys(EXPECTED_RESULTS).forEach(key => { | |
1287 | it(`returns ${EXPECTED_RESULTS[key]} for ${key}`, () => { | |
1288 | const ast = espree.parse(key, { ecmaVersion: 6 }); | |
1289 | ||
1290 | assert.strictEqual(astUtils.isNullLiteral(ast.body[0].expression), EXPECTED_RESULTS[key]); | |
1291 | }); | |
1292 | }); | |
1293 | }); | |
1294 | ||
1295 | describe("createGlobalLinebreakMatcher", () => { | |
1296 | it("returns a regular expression with the g flag", () => { | |
1297 | assert.instanceOf(astUtils.createGlobalLinebreakMatcher(), RegExp); | |
1298 | assert(astUtils.createGlobalLinebreakMatcher().toString().endsWith("/gu")); | |
1299 | }); | |
1300 | it("returns unique objects on each call", () => { | |
1301 | const firstObject = astUtils.createGlobalLinebreakMatcher(); | |
1302 | const secondObject = astUtils.createGlobalLinebreakMatcher(); | |
1303 | ||
1304 | assert.notStrictEqual(firstObject, secondObject); | |
1305 | }); | |
1306 | describe("correctly matches linebreaks", () => { | |
1307 | const LINE_COUNTS = { | |
1308 | foo: 1, | |
1309 | "foo\rbar": 2, | |
1310 | "foo\n": 2, | |
1311 | "foo\nbar": 2, | |
1312 | "foo\r\nbar": 2, | |
1313 | "foo\r\u2028bar": 3, | |
1314 | "foo\u2029bar": 2 | |
1315 | }; | |
1316 | ||
1317 | Object.keys(LINE_COUNTS).forEach(text => { | |
1318 | it(text, () => { | |
1319 | assert.strictEqual(text.split(astUtils.createGlobalLinebreakMatcher()).length, LINE_COUNTS[text]); | |
1320 | }); | |
1321 | }); | |
1322 | }); | |
1323 | }); | |
1324 | ||
1325 | describe("canTokensBeAdjacent", () => { | |
1326 | const CASES = new Map([ | |
1327 | [["foo", "bar"], false], | |
1328 | [[";foo", "bar"], false], | |
1329 | [[";", "bar"], true], | |
1330 | [[")", "bar"], true], | |
1331 | [["foo0", "bar"], false], | |
1332 | [["foo;", "bar"], true], | |
1333 | [["foo", "0"], false], | |
1334 | [["of", ".2"], true], | |
1335 | [["2", ".2"], false], | |
1336 | [["of", "'foo'"], true], | |
1337 | [["foo", "`bar`"], true], | |
1338 | [["`foo`", "in"], true], | |
1339 | [["of", "0.2"], false], | |
1340 | [["of", "0."], false], | |
1341 | [[".2", "foo"], false], | |
1342 | [["2.", "foo"], false], | |
1343 | [["+", "-"], true], | |
1344 | [["++", "-"], true], | |
1345 | [["+", "--"], true], | |
1346 | [["++", "--"], true], | |
1347 | [["-", "+"], true], | |
1348 | [["--", "+"], true], | |
1349 | [["-", "++"], true], | |
1350 | [["--", "++"], true], | |
1351 | [["+", "+"], false], | |
1352 | [["-", "-"], false], | |
1353 | [["++", "+"], false], | |
1354 | [["--", "-"], false], | |
1355 | [["+", "++"], false], | |
1356 | [["-", "--"], false], | |
1357 | [["a/", "b"], true], | |
1358 | [["a/", "+b"], true], | |
1359 | [["a+", "/^regex$/"], true], | |
1360 | [["a/", "/^regex$/"], false], | |
1361 | [["a+", "/**/b"], true], | |
1362 | [["a/", "/**/b"], false], | |
1363 | [["a+", "//\nb"], true], | |
1364 | [["a/", "//\nb"], false], | |
1365 | [["a/**/", "b"], true], | |
1366 | [["/**/a", "b"], false], | |
1367 | [["a", "/**/b"], true], | |
1368 | [["a", "b/**/"], false], | |
1369 | [["a", "//\nb"], true], | |
1370 | [["a", "b//"], false], | |
1371 | [["#!/usr/bin/env node", "("], false], | |
1372 | [["123invalidtoken", "("], false], | |
1373 | [["(", "123invalidtoken"], false], | |
1374 | [["(", "1n"], true], | |
1375 | [["1n", "+"], true], | |
1376 | [["1n", "in"], false] | |
1377 | ]); | |
1378 | ||
1379 | CASES.forEach((expectedResult, tokenStrings) => { | |
1380 | it(tokenStrings.join(", "), () => { | |
1381 | assert.strictEqual(astUtils.canTokensBeAdjacent(tokenStrings[0], tokenStrings[1]), expectedResult); | |
1382 | }); | |
1383 | }); | |
1384 | ||
1385 | it("#!/usr/bin/env node, (", () => { | |
1386 | assert.strictEqual( | |
1387 | astUtils.canTokensBeAdjacent( | |
1388 | { type: "Shebang", value: "#!/usr/bin/env node" }, | |
1389 | { type: "Punctuator", value: "(" } | |
1390 | ), | |
1391 | false | |
1392 | ); | |
1393 | }); | |
1394 | }); | |
1395 | ||
1396 | describe("equalTokens", () => { | |
1397 | it("should return true if tokens are equal", () => { | |
1398 | const code = "a=0;a=0;"; | |
1399 | const ast = espree.parse(code, ESPREE_CONFIG); | |
1400 | const sourceCode = new SourceCode(code, ast); | |
1401 | ||
1402 | assert.strictEqual(astUtils.equalTokens(ast.body[0], ast.body[1], sourceCode), true); | |
1403 | }); | |
1404 | ||
1405 | it("should return false if tokens are not equal", () => { | |
1406 | const code = "a=0;a=1;"; | |
1407 | const ast = espree.parse(code, ESPREE_CONFIG); | |
1408 | const sourceCode = new SourceCode(code, ast); | |
1409 | ||
1410 | assert.strictEqual(astUtils.equalTokens(ast.body[0], ast.body[1], sourceCode), false); | |
1411 | }); | |
1412 | }); | |
1413 | ||
1414 | describe("hasOctalEscapeSequence", () => { | |
1415 | ||
1416 | /* eslint-disable quote-props */ | |
1417 | const expectedResults = { | |
1418 | "\\1": true, | |
1419 | "\\2": true, | |
1420 | "\\7": true, | |
1421 | "\\00": true, | |
1422 | "\\01": true, | |
1423 | "\\02": true, | |
1424 | "\\07": true, | |
1425 | "\\08": true, | |
1426 | "\\09": true, | |
1427 | "\\10": true, | |
1428 | "\\12": true, | |
1429 | " \\1": true, | |
1430 | "\\1 ": true, | |
1431 | "a\\1": true, | |
1432 | "\\1a": true, | |
1433 | "a\\1a": true, | |
1434 | " \\01": true, | |
1435 | "\\01 ": true, | |
1436 | "a\\01": true, | |
1437 | "\\01a": true, | |
1438 | "a\\01a": true, | |
1439 | "a\\08a": true, | |
1440 | "\\0\\1": true, | |
1441 | "\\0\\01": true, | |
1442 | "\\0\\08": true, | |
1443 | "\\n\\1": true, | |
1444 | "\\n\\01": true, | |
1445 | "\\n\\08": true, | |
1446 | "\\\\\\1": true, | |
1447 | "\\\\\\01": true, | |
1448 | "\\\\\\08": true, | |
1449 | ||
1450 | "\\0": false, | |
1451 | "\\8": false, | |
1452 | "\\9": false, | |
1453 | " \\0": false, | |
1454 | "\\0 ": false, | |
1455 | "a\\0": false, | |
1456 | "\\0a": false, | |
1457 | "a\\8a": false, | |
1458 | "\\0\\8": false, | |
1459 | "\\8\\0": false, | |
1460 | "\\80": false, | |
1461 | "\\81": false, | |
1462 | "\\\\": false, | |
1463 | "\\\\0": false, | |
1464 | "\\\\01": false, | |
1465 | "\\\\08": false, | |
1466 | "\\\\1": false, | |
1467 | "\\\\12": false, | |
1468 | "\\\\\\0": false, | |
1469 | "\\\\\\8": false, | |
1470 | "\\0\\\\": false, | |
1471 | "0": false, | |
1472 | "1": false, | |
1473 | "8": false, | |
1474 | "01": false, | |
1475 | "08": false, | |
1476 | "80": false, | |
1477 | "12": false, | |
1478 | "\\a": false, | |
1479 | "\\n": false | |
1480 | }; | |
1481 | /* eslint-enable quote-props */ | |
1482 | ||
1483 | Object.keys(expectedResults).forEach(key => { | |
1484 | it(`should return ${expectedResults[key]} for ${key}`, () => { | |
1485 | const ast = espree.parse(`"${key}"`); | |
1486 | ||
1487 | assert.strictEqual(astUtils.hasOctalEscapeSequence(ast.body[0].expression.raw), expectedResults[key]); | |
1488 | }); | |
1489 | }); | |
1490 | }); | |
1491 | }); |