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