]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/linter/linter.js
407194d47e70099c7fd7f6b7391471cfdf2f6de5
[pve-eslint.git] / eslint / tests / lib / linter / linter.js
1 /**
2 * @fileoverview Tests for eslint object.
3 * @author Nicholas C. Zakas
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const assert = require("chai").assert,
13 sinon = require("sinon"),
14 espree = require("espree"),
15 esprima = require("esprima"),
16 testParsers = require("../../fixtures/parsers/linter-test-parsers");
17
18 const { Linter } = require("../../../lib/linter");
19 const { FlatConfigArray } = require("../../../lib/config/flat-config-array");
20
21 //------------------------------------------------------------------------------
22 // Constants
23 //------------------------------------------------------------------------------
24
25 const TEST_CODE = "var answer = 6 * 7;",
26 BROKEN_TEST_CODE = "var;";
27
28 //------------------------------------------------------------------------------
29 // Helpers
30 //------------------------------------------------------------------------------
31
32 /**
33 * Get variables in the current scope
34 * @param {Object} scope current scope
35 * @param {string} name name of the variable to look for
36 * @returns {ASTNode|null} The variable object
37 * @private
38 */
39 function getVariable(scope, name) {
40 return scope.variables.find(v => v.name === name) || null;
41 }
42
43 /**
44 * `eslint-env` comments are processed by doing a full source text match before parsing.
45 * As a result, if this source file contains `eslint- env` followed by an environment in a string,
46 * it will actually enable the given envs for this source file. This variable is used to avoid having a string
47 * like that appear in the code.
48 */
49 const ESLINT_ENV = "eslint-env";
50
51 //------------------------------------------------------------------------------
52 // Tests
53 //------------------------------------------------------------------------------
54
55 describe("Linter", () => {
56 const filename = "filename.js";
57
58 /** @type {InstanceType<import("../../../lib/linter/linter.js").Linter>} */
59 let linter;
60
61 beforeEach(() => {
62 linter = new Linter();
63 });
64
65 afterEach(() => {
66 sinon.verifyAndRestore();
67 });
68
69 describe("Static Members", () => {
70 describe("version", () => {
71 it("should return same version as instance property", () => {
72 assert.strictEqual(Linter.version, linter.version);
73 });
74 });
75 });
76
77 describe("when using events", () => {
78 const code = TEST_CODE;
79
80 it("an error should be thrown when an error occurs inside of an event handler", () => {
81 const config = { rules: { checker: "error" } };
82
83 linter.defineRule("checker", () => ({
84 Program() {
85 throw new Error("Intentional error.");
86 }
87 }));
88
89 assert.throws(() => {
90 linter.verify(code, config, filename);
91 }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "checker"`);
92 });
93
94 it("does not call rule listeners with a `this` value", () => {
95 const spy = sinon.spy();
96
97 linter.defineRule("checker", () => ({ Program: spy }));
98 linter.verify("foo", { rules: { checker: "error" } });
99 assert(spy.calledOnce, "Rule should have been called");
100 assert.strictEqual(spy.firstCall.thisValue, void 0, "this value should be undefined");
101 });
102
103 it("does not allow listeners to use special EventEmitter values", () => {
104 const spy = sinon.spy();
105
106 linter.defineRule("checker", () => ({ newListener: spy }));
107 linter.verify("foo", { rules: { checker: "error", "no-undef": "error" } });
108 assert(spy.notCalled);
109 });
110
111 it("has all the `parent` properties on nodes when the rule listeners are created", () => {
112 const spy = sinon.spy(context => {
113 const ast = context.getSourceCode().ast;
114
115 assert.strictEqual(ast.body[0].parent, ast);
116 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
117 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
118 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
119
120 return {};
121 });
122
123 linter.defineRule("checker", spy);
124
125 linter.verify("foo + bar", { rules: { checker: "error" } });
126 assert(spy.calledOnce);
127 });
128 });
129
130 describe("context.getSourceLines()", () => {
131
132 it("should get proper lines when using \\n as a line break", () => {
133 const code = "a;\nb;";
134 const spy = sinon.spy(context => {
135 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
136 return {};
137 });
138
139 linter.defineRule("checker", spy);
140 linter.verify(code, { rules: { checker: "error" } });
141 assert(spy.calledOnce);
142 });
143
144 it("should get proper lines when using \\r\\n as a line break", () => {
145 const code = "a;\r\nb;";
146 const spy = sinon.spy(context => {
147 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
148 return {};
149 });
150
151 linter.defineRule("checker", spy);
152 linter.verify(code, { rules: { checker: "error" } });
153 assert(spy.calledOnce);
154 });
155
156 it("should get proper lines when using \\r as a line break", () => {
157 const code = "a;\rb;";
158 const spy = sinon.spy(context => {
159 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
160 return {};
161 });
162
163 linter.defineRule("checker", spy);
164 linter.verify(code, { rules: { checker: "error" } });
165 assert(spy.calledOnce);
166 });
167
168 it("should get proper lines when using \\u2028 as a line break", () => {
169 const code = "a;\u2028b;";
170 const spy = sinon.spy(context => {
171 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
172 return {};
173 });
174
175 linter.defineRule("checker", spy);
176 linter.verify(code, { rules: { checker: "error" } });
177 assert(spy.calledOnce);
178 });
179
180 it("should get proper lines when using \\u2029 as a line break", () => {
181 const code = "a;\u2029b;";
182 const spy = sinon.spy(context => {
183 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
184 return {};
185 });
186
187 linter.defineRule("checker", spy);
188 linter.verify(code, { rules: { checker: "error" } });
189 assert(spy.calledOnce);
190 });
191
192
193 });
194
195 describe("getSourceCode()", () => {
196 const code = TEST_CODE;
197
198 it("should retrieve SourceCode object after reset", () => {
199 linter.verify(code, {}, filename, true);
200
201 const sourceCode = linter.getSourceCode();
202
203 assert.isObject(sourceCode);
204 assert.strictEqual(sourceCode.text, code);
205 assert.isObject(sourceCode.ast);
206 });
207
208 it("should retrieve SourceCode object without reset", () => {
209 linter.verify(code, {}, filename);
210
211 const sourceCode = linter.getSourceCode();
212
213 assert.isObject(sourceCode);
214 assert.strictEqual(sourceCode.text, code);
215 assert.isObject(sourceCode.ast);
216 });
217
218 });
219
220 describe("getSuppressedMessages()", () => {
221 it("should have no suppressed messages", () => {
222 const suppressedMessages = linter.getSuppressedMessages();
223
224 assert.strictEqual(suppressedMessages.length, 0);
225 });
226
227 it("should have a suppressed message", () => {
228 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test\");";
229 const config = {
230 rules: { "no-alert": 1 }
231 };
232 const messages = linter.verify(code, config);
233 const suppressedMessages = linter.getSuppressedMessages();
234
235 assert.strictEqual(messages.length, 0);
236
237 assert.strictEqual(suppressedMessages.length, 1);
238 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
239 assert.deepStrictEqual(
240 suppressedMessages[0].suppressions,
241 [{ kind: "directive", justification: "justification" }]
242 );
243 });
244
245 it("should have a suppressed message", () => {
246 const code = [
247 "/* eslint-disable no-alert --- j1",
248 " * --- j2",
249 " */",
250 "alert(\"test\");"
251 ].join("\n");
252 const config = {
253 rules: { "no-alert": 1 }
254 };
255 const messages = linter.verify(code, config);
256 const suppressedMessages = linter.getSuppressedMessages();
257
258 assert.strictEqual(messages.length, 0);
259
260 assert.strictEqual(suppressedMessages.length, 1);
261 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
262 assert.deepStrictEqual(
263 suppressedMessages[0].suppressions,
264 [{ kind: "directive", justification: "j1\n * --- j2" }]
265 );
266 });
267
268 it("should not report a lint message", () => {
269 const code = [
270 "/* eslint-disable -- j1 */",
271 "// eslint-disable-next-line -- j2",
272 "alert(\"test\");"
273 ].join("\n");
274 const config = {
275 rules: { "no-alert": 1 }
276 };
277 const messages = linter.verify(code, config);
278 const suppressedMessages = linter.getSuppressedMessages();
279
280 assert.strictEqual(messages.length, 0);
281
282 assert.strictEqual(suppressedMessages.length, 1);
283 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
284 assert.deepStrictEqual(
285 suppressedMessages[0].suppressions,
286 [
287 { kind: "directive", justification: "j1" },
288 { kind: "directive", justification: "j2" }
289 ]
290 );
291 });
292
293 it("should not report a lint message", () => {
294 const code = [
295 "/* eslint-disable -- j1 */",
296 "alert(\"test\"); // eslint-disable-line -- j2"
297 ].join("\n");
298 const config = {
299 rules: { "no-alert": 1 }
300 };
301 const messages = linter.verify(code, config);
302 const suppressedMessages = linter.getSuppressedMessages();
303
304 assert.strictEqual(messages.length, 0);
305
306 assert.strictEqual(suppressedMessages.length, 1);
307 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
308 assert.deepStrictEqual(
309 suppressedMessages[0].suppressions,
310 [
311 { kind: "directive", justification: "j1" },
312 { kind: "directive", justification: "j2" }
313 ]
314 );
315 });
316
317 it("should have a suppressed message with multiple suppressions", () => {
318 const code = [
319 "/* eslint-disable no-alert -- j1 */",
320 "/* eslint-disable no-console -- unused */",
321 "/* eslint-disable-next-line no-alert -- j2 */",
322 "alert(\"test\"); // eslint-disable-line no-alert -- j3"
323 ].join("\n");
324 const config = {
325 rules: { "no-alert": 1 }
326 };
327 const messages = linter.verify(code, config);
328 const suppressedMessages = linter.getSuppressedMessages();
329
330 assert.strictEqual(messages.length, 0);
331
332 assert.strictEqual(suppressedMessages.length, 1);
333 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
334 assert.deepStrictEqual(
335 suppressedMessages[0].suppressions,
336 [
337 { kind: "directive", justification: "j1" },
338 { kind: "directive", justification: "j2" },
339 { kind: "directive", justification: "j3" }
340 ]
341 );
342 });
343 });
344
345 describe("context.getSource()", () => {
346 const code = TEST_CODE;
347
348 it("should retrieve all text when used without parameters", () => {
349
350 const config = { rules: { checker: "error" } };
351 let spy;
352
353 linter.defineRule("checker", context => {
354 spy = sinon.spy(() => {
355 assert.strictEqual(context.getSource(), TEST_CODE);
356 });
357 return { Program: spy };
358 });
359
360 linter.verify(code, config);
361 assert(spy && spy.calledOnce);
362 });
363
364 it("should retrieve all text for root node", () => {
365 const config = { rules: { checker: "error" } };
366 let spy;
367
368 linter.defineRule("checker", context => {
369 spy = sinon.spy(node => {
370 assert.strictEqual(context.getSource(node), TEST_CODE);
371 });
372 return { Program: spy };
373 });
374
375 linter.verify(code, config);
376 assert(spy && spy.calledOnce);
377 });
378
379 it("should clamp to valid range when retrieving characters before start of source", () => {
380 const config = { rules: { checker: "error" } };
381 let spy;
382
383 linter.defineRule("checker", context => {
384 spy = sinon.spy(node => {
385 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
386 });
387 return { Program: spy };
388 });
389
390 linter.verify(code, config);
391 assert(spy && spy.calledOnce);
392 });
393
394 it("should retrieve all text for binary expression", () => {
395 const config = { rules: { checker: "error" } };
396 let spy;
397
398 linter.defineRule("checker", context => {
399 spy = sinon.spy(node => {
400 assert.strictEqual(context.getSource(node), "6 * 7");
401 });
402 return { BinaryExpression: spy };
403 });
404
405 linter.verify(code, config);
406 assert(spy && spy.calledOnce);
407 });
408
409 it("should retrieve all text plus two characters before for binary expression", () => {
410 const config = { rules: { checker: "error" } };
411 let spy;
412
413 linter.defineRule("checker", context => {
414 spy = sinon.spy(node => {
415 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
416 });
417 return { BinaryExpression: spy };
418 });
419
420 linter.verify(code, config);
421 assert(spy && spy.calledOnce);
422 });
423
424 it("should retrieve all text plus one character after for binary expression", () => {
425 const config = { rules: { checker: "error" } };
426 let spy;
427
428 linter.defineRule("checker", context => {
429 spy = sinon.spy(node => {
430 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
431 });
432 return { BinaryExpression: spy };
433 });
434
435 linter.verify(code, config);
436 assert(spy && spy.calledOnce);
437 });
438
439 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
440 const config = { rules: { checker: "error" } };
441 let spy;
442
443 linter.defineRule("checker", context => {
444 spy = sinon.spy(node => {
445 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
446 });
447 return { BinaryExpression: spy };
448 });
449
450 linter.verify(code, config);
451 assert(spy && spy.calledOnce);
452 });
453
454 });
455
456 describe("when calling context.getAncestors", () => {
457 const code = TEST_CODE;
458
459 it("should retrieve all ancestors when used", () => {
460
461 const config = { rules: { checker: "error" } };
462 let spy;
463
464 linter.defineRule("checker", context => {
465 spy = sinon.spy(() => {
466 const ancestors = context.getAncestors();
467
468 assert.strictEqual(ancestors.length, 3);
469 });
470 return { BinaryExpression: spy };
471 });
472
473 linter.verify(code, config, filename, true);
474 assert(spy && spy.calledOnce);
475 });
476
477 it("should retrieve empty ancestors for root node", () => {
478 const config = { rules: { checker: "error" } };
479 let spy;
480
481 linter.defineRule("checker", context => {
482 spy = sinon.spy(() => {
483 const ancestors = context.getAncestors();
484
485 assert.strictEqual(ancestors.length, 0);
486 });
487
488 return { Program: spy };
489 });
490
491 linter.verify(code, config);
492 assert(spy && spy.calledOnce);
493 });
494 });
495
496 describe("when calling context.getNodeByRangeIndex", () => {
497 const code = TEST_CODE;
498
499 it("should retrieve a node starting at the given index", () => {
500 const config = { rules: { checker: "error" } };
501 const spy = sinon.spy(context => {
502 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
503 return {};
504 });
505
506 linter.defineRule("checker", spy);
507 linter.verify(code, config);
508 assert(spy.calledOnce);
509 });
510
511 it("should retrieve a node containing the given index", () => {
512 const config = { rules: { checker: "error" } };
513 const spy = sinon.spy(context => {
514 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
515 return {};
516 });
517
518 linter.defineRule("checker", spy);
519 linter.verify(code, config);
520 assert(spy.calledOnce);
521 });
522
523 it("should retrieve a node that is exactly the given index", () => {
524 const config = { rules: { checker: "error" } };
525 const spy = sinon.spy(context => {
526 const node = context.getNodeByRangeIndex(13);
527
528 assert.strictEqual(node.type, "Literal");
529 assert.strictEqual(node.value, 6);
530 return {};
531 });
532
533 linter.defineRule("checker", spy);
534 linter.verify(code, config);
535 assert(spy.calledOnce);
536 });
537
538 it("should retrieve a node ending with the given index", () => {
539 const config = { rules: { checker: "error" } };
540 const spy = sinon.spy(context => {
541 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
542 return {};
543 });
544
545 linter.defineRule("checker", spy);
546 linter.verify(code, config);
547 assert(spy.calledOnce);
548 });
549
550 it("should retrieve the deepest node containing the given index", () => {
551 const config = { rules: { checker: "error" } };
552 const spy = sinon.spy(context => {
553 const node1 = context.getNodeByRangeIndex(14);
554
555 assert.strictEqual(node1.type, "BinaryExpression");
556
557 const node2 = context.getNodeByRangeIndex(3);
558
559 assert.strictEqual(node2.type, "VariableDeclaration");
560 return {};
561 });
562
563 linter.defineRule("checker", spy);
564 linter.verify(code, config);
565 assert(spy.calledOnce);
566 });
567
568 it("should return null if the index is outside the range of any node", () => {
569 const config = { rules: { checker: "error" } };
570 const spy = sinon.spy(context => {
571 const node1 = context.getNodeByRangeIndex(-1);
572
573 assert.isNull(node1);
574
575 const node2 = context.getNodeByRangeIndex(-99);
576
577 assert.isNull(node2);
578 return {};
579 });
580
581 linter.defineRule("checker", spy);
582 linter.verify(code, config);
583 assert(spy.calledOnce);
584 });
585 });
586
587
588 describe("when calling context.getScope", () => {
589 const code = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
590
591 it("should retrieve the global scope correctly from a Program", () => {
592 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
593 let spy;
594
595 linter.defineRule("checker", context => {
596 spy = sinon.spy(() => {
597 const scope = context.getScope();
598
599 assert.strictEqual(scope.type, "global");
600 });
601 return { Program: spy };
602 });
603
604 linter.verify(code, config);
605 assert(spy && spy.calledOnce);
606 });
607
608 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
609 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
610 let spy;
611
612 linter.defineRule("checker", context => {
613 spy = sinon.spy(() => {
614 const scope = context.getScope();
615
616 assert.strictEqual(scope.type, "function");
617 });
618 return { FunctionDeclaration: spy };
619 });
620
621 linter.verify(code, config);
622 assert(spy && spy.calledTwice);
623 });
624
625 it("should retrieve the function scope correctly from a LabeledStatement", () => {
626 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
627 let spy;
628
629 linter.defineRule("checker", context => {
630 spy = sinon.spy(() => {
631 const scope = context.getScope();
632
633 assert.strictEqual(scope.type, "function");
634 assert.strictEqual(scope.block.id.name, "foo");
635 });
636 return { LabeledStatement: spy };
637 });
638
639 linter.verify(code, config);
640 assert(spy && spy.calledOnce);
641 });
642
643 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
644 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
645 let spy;
646
647 linter.defineRule("checker", context => {
648 spy = sinon.spy(() => {
649 const scope = context.getScope();
650
651 assert.strictEqual(scope.type, "function");
652 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
653 });
654
655 return { ReturnStatement: spy };
656 });
657
658 linter.verify(code, config);
659 assert(spy && spy.calledOnce);
660 });
661
662 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
663 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
664 let spy;
665
666 linter.defineRule("checker", context => {
667 spy = sinon.spy(() => {
668 const scope = context.getScope();
669
670 assert.strictEqual(scope.type, "switch");
671 assert.strictEqual(scope.block.type, "SwitchStatement");
672 });
673
674 return { SwitchStatement: spy };
675 });
676
677 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
678 assert(spy && spy.calledOnce);
679 });
680
681 it("should retrieve the function scope correctly from within a BlockStatement", () => {
682 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
683 let spy;
684
685 linter.defineRule("checker", context => {
686 spy = sinon.spy(() => {
687 const scope = context.getScope();
688
689 assert.strictEqual(scope.type, "block");
690 assert.strictEqual(scope.block.type, "BlockStatement");
691 });
692
693 return { BlockStatement: spy };
694 });
695
696 linter.verify("var x; {let y = 1}", config);
697 assert(spy && spy.calledOnce);
698 });
699
700 it("should retrieve the function scope correctly from within a nested block statement", () => {
701 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
702 let spy;
703
704 linter.defineRule("checker", context => {
705 spy = sinon.spy(() => {
706 const scope = context.getScope();
707
708 assert.strictEqual(scope.type, "block");
709 assert.strictEqual(scope.block.type, "BlockStatement");
710 });
711
712 return { BlockStatement: spy };
713 });
714
715 linter.verify("if (true) { let x = 1 }", config);
716 assert(spy && spy.calledOnce);
717 });
718
719 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
720 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
721 let spy;
722
723 linter.defineRule("checker", context => {
724 spy = sinon.spy(() => {
725 const scope = context.getScope();
726
727 assert.strictEqual(scope.type, "function");
728 assert.strictEqual(scope.block.type, "FunctionDeclaration");
729 });
730
731 return { FunctionDeclaration: spy };
732 });
733
734 linter.verify("function foo() {}", config);
735 assert(spy && spy.calledOnce);
736 });
737
738 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
739 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
740 let spy;
741
742 linter.defineRule("checker", context => {
743 spy = sinon.spy(() => {
744 const scope = context.getScope();
745
746 assert.strictEqual(scope.type, "function");
747 assert.strictEqual(scope.block.type, "FunctionExpression");
748 });
749
750 return { FunctionExpression: spy };
751 });
752
753 linter.verify("(function foo() {})();", config);
754 assert(spy && spy.calledOnce);
755 });
756
757 it("should retrieve the catch scope correctly from within a CatchClause", () => {
758 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
759 let spy;
760
761 linter.defineRule("checker", context => {
762 spy = sinon.spy(() => {
763 const scope = context.getScope();
764
765 assert.strictEqual(scope.type, "catch");
766 assert.strictEqual(scope.block.type, "CatchClause");
767 });
768
769 return { CatchClause: spy };
770 });
771
772 linter.verify("try {} catch (err) {}", config);
773 assert(spy && spy.calledOnce);
774 });
775
776 it("should retrieve module scope correctly from an ES6 module", () => {
777 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } };
778 let spy;
779
780 linter.defineRule("checker", context => {
781 spy = sinon.spy(() => {
782 const scope = context.getScope();
783
784 assert.strictEqual(scope.type, "module");
785 });
786
787 return { AssignmentExpression: spy };
788 });
789
790 linter.verify("var foo = {}; foo.bar = 1;", config);
791 assert(spy && spy.calledOnce);
792 });
793
794 it("should retrieve function scope correctly when globalReturn is true", () => {
795 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, ecmaFeatures: { globalReturn: true } } };
796 let spy;
797
798 linter.defineRule("checker", context => {
799 spy = sinon.spy(() => {
800 const scope = context.getScope();
801
802 assert.strictEqual(scope.type, "function");
803 });
804
805 return { AssignmentExpression: spy };
806 });
807
808 linter.verify("var foo = {}; foo.bar = 1;", config);
809 assert(spy && spy.calledOnce);
810 });
811 });
812
813 describe("marking variables as used", () => {
814 it("should mark variables in current scope as used", () => {
815 const code = "var a = 1, b = 2;";
816 let spy;
817
818 linter.defineRule("checker", context => {
819 spy = sinon.spy(() => {
820 assert.isTrue(context.markVariableAsUsed("a"));
821
822 const scope = context.getScope();
823
824 assert.isTrue(getVariable(scope, "a").eslintUsed);
825 assert.notOk(getVariable(scope, "b").eslintUsed);
826 });
827
828 return { "Program:exit": spy };
829 });
830
831 linter.verify(code, { rules: { checker: "error" } });
832 assert(spy && spy.calledOnce);
833 });
834 it("should mark variables in function args as used", () => {
835 const code = "function abc(a, b) { return 1; }";
836 let spy;
837
838 linter.defineRule("checker", context => {
839 spy = sinon.spy(() => {
840 assert.isTrue(context.markVariableAsUsed("a"));
841
842 const scope = context.getScope();
843
844 assert.isTrue(getVariable(scope, "a").eslintUsed);
845 assert.notOk(getVariable(scope, "b").eslintUsed);
846 });
847
848 return { ReturnStatement: spy };
849 });
850
851 linter.verify(code, { rules: { checker: "error" } });
852 assert(spy && spy.calledOnce);
853 });
854 it("should mark variables in higher scopes as used", () => {
855 const code = "var a, b; function abc() { return 1; }";
856 let returnSpy, exitSpy;
857
858 linter.defineRule("checker", context => {
859 returnSpy = sinon.spy(() => {
860 assert.isTrue(context.markVariableAsUsed("a"));
861 });
862 exitSpy = sinon.spy(() => {
863 const scope = context.getScope();
864
865 assert.isTrue(getVariable(scope, "a").eslintUsed);
866 assert.notOk(getVariable(scope, "b").eslintUsed);
867 });
868
869 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
870 });
871
872 linter.verify(code, { rules: { checker: "error" } });
873 assert(returnSpy && returnSpy.calledOnce);
874 assert(exitSpy && exitSpy.calledOnce);
875 });
876
877 it("should mark variables in Node.js environment as used", () => {
878 const code = "var a = 1, b = 2;";
879 let spy;
880
881 linter.defineRule("checker", context => {
882 spy = sinon.spy(() => {
883 const globalScope = context.getScope(),
884 childScope = globalScope.childScopes[0];
885
886 assert.isTrue(context.markVariableAsUsed("a"));
887
888 assert.isTrue(getVariable(childScope, "a").eslintUsed);
889 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
890 });
891
892 return { "Program:exit": spy };
893 });
894
895 linter.verify(code, { rules: { checker: "error" }, env: { node: true } });
896 assert(spy && spy.calledOnce);
897 });
898
899 it("should mark variables in modules as used", () => {
900 const code = "var a = 1, b = 2;";
901 let spy;
902
903 linter.defineRule("checker", context => {
904 spy = sinon.spy(() => {
905 const globalScope = context.getScope(),
906 childScope = globalScope.childScopes[0];
907
908 assert.isTrue(context.markVariableAsUsed("a"));
909
910 assert.isTrue(getVariable(childScope, "a").eslintUsed);
911 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
912 });
913
914 return { "Program:exit": spy };
915 });
916
917 linter.verify(code, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } }, filename, true);
918 assert(spy && spy.calledOnce);
919 });
920
921 it("should return false if the given variable is not found", () => {
922 const code = "var a = 1, b = 2;";
923 let spy;
924
925 linter.defineRule("checker", context => {
926 spy = sinon.spy(() => {
927 assert.isFalse(context.markVariableAsUsed("c"));
928 });
929
930 return { "Program:exit": spy };
931 });
932
933 linter.verify(code, { rules: { checker: "error" } });
934 assert(spy && spy.calledOnce);
935 });
936 });
937
938 describe("when evaluating code", () => {
939 const code = TEST_CODE;
940
941 it("events for each node type should fire", () => {
942 const config = { rules: { checker: "error" } };
943
944 // spies for various AST node types
945 const spyLiteral = sinon.spy(),
946 spyVariableDeclarator = sinon.spy(),
947 spyVariableDeclaration = sinon.spy(),
948 spyIdentifier = sinon.spy(),
949 spyBinaryExpression = sinon.spy();
950
951 linter.defineRule("checker", () => ({
952 Literal: spyLiteral,
953 VariableDeclarator: spyVariableDeclarator,
954 VariableDeclaration: spyVariableDeclaration,
955 Identifier: spyIdentifier,
956 BinaryExpression: spyBinaryExpression
957 }));
958
959 const messages = linter.verify(code, config, filename, true);
960 const suppressedMessages = linter.getSuppressedMessages();
961
962 assert.strictEqual(messages.length, 0);
963 assert.strictEqual(suppressedMessages.length, 0);
964 sinon.assert.calledOnce(spyVariableDeclaration);
965 sinon.assert.calledOnce(spyVariableDeclarator);
966 sinon.assert.calledOnce(spyIdentifier);
967 sinon.assert.calledTwice(spyLiteral);
968 sinon.assert.calledOnce(spyBinaryExpression);
969 });
970
971 it("should throw an error if a rule reports a problem without a message", () => {
972 linter.defineRule("invalid-report", context => ({
973 Program(node) {
974 context.report({ node });
975 }
976 }));
977
978 assert.throws(
979 () => linter.verify("foo", { rules: { "invalid-report": "error" } }),
980 TypeError,
981 "Missing `message` property in report() call; add a message that describes the linting problem."
982 );
983 });
984 });
985
986 describe("when config has shared settings for rules", () => {
987 const code = "test-rule";
988
989 it("should pass settings to all rules", () => {
990 linter.defineRule(code, context => ({
991 Literal(node) {
992 context.report(node, context.settings.info);
993 }
994 }));
995
996 const config = { rules: {}, settings: { info: "Hello" } };
997
998 config.rules[code] = 1;
999
1000 const messages = linter.verify("0", config, filename);
1001 const suppressedMessages = linter.getSuppressedMessages();
1002
1003 assert.strictEqual(messages.length, 1);
1004 assert.strictEqual(messages[0].message, "Hello");
1005 assert.strictEqual(suppressedMessages.length, 0);
1006 });
1007
1008 it("should not have any settings if they were not passed in", () => {
1009 linter.defineRule(code, context => ({
1010 Literal(node) {
1011 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
1012 context.report(node, "Settings should be empty");
1013 }
1014 }
1015 }));
1016
1017 const config = { rules: {} };
1018
1019 config.rules[code] = 1;
1020
1021 const messages = linter.verify("0", config, filename);
1022 const suppressedMessages = linter.getSuppressedMessages();
1023
1024 assert.strictEqual(messages.length, 0);
1025 assert.strictEqual(suppressedMessages.length, 0);
1026 });
1027 });
1028
1029 describe("when config has parseOptions", () => {
1030
1031 it("should pass ecmaFeatures to all rules when provided on config", () => {
1032
1033 const parserOptions = {
1034 ecmaFeatures: {
1035 jsx: true,
1036 globalReturn: true
1037 }
1038 };
1039
1040 linter.defineRule("test-rule", sinon.mock().withArgs(
1041 sinon.match({ parserOptions })
1042 ).returns({}));
1043
1044 const config = { rules: { "test-rule": 2 }, parserOptions };
1045
1046 linter.verify("0", config, filename);
1047 });
1048
1049 it("should pass parserOptions to all rules when default parserOptions is used", () => {
1050
1051 const parserOptions = {};
1052
1053 linter.defineRule("test-rule", sinon.mock().withArgs(
1054 sinon.match({ parserOptions })
1055 ).returns({}));
1056
1057 const config = { rules: { "test-rule": 2 } };
1058
1059 linter.verify("0", config, filename);
1060 });
1061
1062 });
1063
1064 describe("when a custom parser is defined using defineParser", () => {
1065
1066 it("should be able to define a custom parser", () => {
1067 const parser = {
1068 parseForESLint: function parse(code, options) {
1069 return {
1070 ast: esprima.parse(code, options),
1071 services: {
1072 test: {
1073 getMessage() {
1074 return "Hi!";
1075 }
1076 }
1077 }
1078 };
1079 }
1080 };
1081
1082 linter.defineParser("test-parser", parser);
1083 const config = { rules: {}, parser: "test-parser" };
1084 const messages = linter.verify("0", config, filename);
1085 const suppressedMessages = linter.getSuppressedMessages();
1086
1087 assert.strictEqual(messages.length, 0);
1088 assert.strictEqual(suppressedMessages.length, 0);
1089 });
1090
1091 });
1092
1093 describe("when config has parser", () => {
1094
1095 it("should pass parser as parserPath to all rules when provided on config", () => {
1096
1097 const alternateParser = "esprima";
1098
1099 linter.defineParser("esprima", esprima);
1100 linter.defineRule("test-rule", sinon.mock().withArgs(
1101 sinon.match({ parserPath: alternateParser })
1102 ).returns({}));
1103
1104 const config = { rules: { "test-rule": 2 }, parser: alternateParser };
1105
1106 linter.verify("0", config, filename);
1107 });
1108
1109 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
1110 const config = { rules: {}, parser: "enhanced-parser" };
1111
1112 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1113 const messages = linter.verify("0", config, filename);
1114 const suppressedMessages = linter.getSuppressedMessages();
1115
1116 assert.strictEqual(messages.length, 0);
1117 assert.strictEqual(suppressedMessages.length, 0);
1118 });
1119
1120 it("should expose parser services when using parseForESLint() and services are specified", () => {
1121 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1122 linter.defineRule("test-service-rule", context => ({
1123 Literal(node) {
1124 context.report({
1125 node,
1126 message: context.parserServices.test.getMessage()
1127 });
1128 }
1129 }));
1130
1131 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
1132 const messages = linter.verify("0", config, filename);
1133 const suppressedMessages = linter.getSuppressedMessages();
1134
1135 assert.strictEqual(messages.length, 1);
1136 assert.strictEqual(messages[0].message, "Hi!");
1137 assert.strictEqual(suppressedMessages.length, 0);
1138 });
1139
1140 it("should use the same parserServices if source code object is reused", () => {
1141 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1142 linter.defineRule("test-service-rule", context => ({
1143 Literal(node) {
1144 context.report({
1145 node,
1146 message: context.parserServices.test.getMessage()
1147 });
1148 }
1149 }));
1150
1151 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
1152 const messages = linter.verify("0", config, filename);
1153 const suppressedMessages = linter.getSuppressedMessages();
1154
1155 assert.strictEqual(messages.length, 1);
1156 assert.strictEqual(messages[0].message, "Hi!");
1157 assert.strictEqual(suppressedMessages.length, 0);
1158
1159 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
1160 const suppressedMessages2 = linter.getSuppressedMessages();
1161
1162 assert.strictEqual(messages2.length, 1);
1163 assert.strictEqual(messages2[0].message, "Hi!");
1164 assert.strictEqual(suppressedMessages2.length, 0);
1165 });
1166
1167 it("should pass parser as parserPath to all rules when default parser is used", () => {
1168 linter.defineRule("test-rule", sinon.mock().withArgs(
1169 sinon.match({ parserPath: "espree" })
1170 ).returns({}));
1171
1172 const config = { rules: { "test-rule": 2 } };
1173
1174 linter.verify("0", config, filename);
1175 });
1176
1177 });
1178
1179
1180 describe("when passing in configuration values for rules", () => {
1181 const code = "var answer = 6 * 7";
1182
1183 it("should be configurable by only setting the integer value", () => {
1184 const rule = "semi",
1185 config = { rules: {} };
1186
1187 config.rules[rule] = 1;
1188
1189 const messages = linter.verify(code, config, filename, true);
1190 const suppressedMessages = linter.getSuppressedMessages();
1191
1192 assert.strictEqual(messages.length, 1);
1193 assert.strictEqual(messages[0].ruleId, rule);
1194
1195 assert.strictEqual(suppressedMessages.length, 0);
1196 });
1197
1198 it("should be configurable by only setting the string value", () => {
1199 const rule = "semi",
1200 config = { rules: {} };
1201
1202 config.rules[rule] = "warn";
1203
1204 const messages = linter.verify(code, config, filename, true);
1205 const suppressedMessages = linter.getSuppressedMessages();
1206
1207 assert.strictEqual(messages.length, 1);
1208 assert.strictEqual(messages[0].severity, 1);
1209 assert.strictEqual(messages[0].ruleId, rule);
1210
1211 assert.strictEqual(suppressedMessages.length, 0);
1212 });
1213
1214 it("should be configurable by passing in values as an array", () => {
1215 const rule = "semi",
1216 config = { rules: {} };
1217
1218 config.rules[rule] = [1];
1219
1220 const messages = linter.verify(code, config, filename, true);
1221 const suppressedMessages = linter.getSuppressedMessages();
1222
1223 assert.strictEqual(messages.length, 1);
1224 assert.strictEqual(messages[0].ruleId, rule);
1225
1226 assert.strictEqual(suppressedMessages.length, 0);
1227 });
1228
1229 it("should be configurable by passing in string value as an array", () => {
1230 const rule = "semi",
1231 config = { rules: {} };
1232
1233 config.rules[rule] = ["warn"];
1234
1235 const messages = linter.verify(code, config, filename, true);
1236 const suppressedMessages = linter.getSuppressedMessages();
1237
1238 assert.strictEqual(messages.length, 1);
1239 assert.strictEqual(messages[0].severity, 1);
1240 assert.strictEqual(messages[0].ruleId, rule);
1241
1242 assert.strictEqual(suppressedMessages.length, 0);
1243 });
1244
1245 it("should not be configurable by setting other value", () => {
1246 const rule = "semi",
1247 config = { rules: {} };
1248
1249 config.rules[rule] = "1";
1250
1251 const messages = linter.verify(code, config, filename, true);
1252 const suppressedMessages = linter.getSuppressedMessages();
1253
1254 assert.strictEqual(messages.length, 0);
1255 assert.strictEqual(suppressedMessages.length, 0);
1256 });
1257
1258 it("should process empty config", () => {
1259 const config = {};
1260 const messages = linter.verify(code, config, filename, true);
1261 const suppressedMessages = linter.getSuppressedMessages();
1262
1263 assert.strictEqual(messages.length, 0);
1264 assert.strictEqual(suppressedMessages.length, 0);
1265 });
1266 });
1267
1268 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
1269
1270 it("variables should be available in global scope", () => {
1271 const config = { rules: { checker: "error" }, globals: { Array: "off", ConfigGlobal: "writeable" } };
1272 const code = `
1273 /*global a b:true c:false d:readable e:writeable Math:off */
1274 function foo() {}
1275 /*globals f:true*/
1276 /* global ConfigGlobal : readable */
1277 `;
1278 let spy;
1279
1280 linter.defineRule("checker", context => {
1281 spy = sinon.spy(() => {
1282 const scope = context.getScope();
1283 const a = getVariable(scope, "a"),
1284 b = getVariable(scope, "b"),
1285 c = getVariable(scope, "c"),
1286 d = getVariable(scope, "d"),
1287 e = getVariable(scope, "e"),
1288 f = getVariable(scope, "f"),
1289 mathGlobal = getVariable(scope, "Math"),
1290 arrayGlobal = getVariable(scope, "Array"),
1291 configGlobal = getVariable(scope, "ConfigGlobal");
1292
1293 assert.strictEqual(a.name, "a");
1294 assert.strictEqual(a.writeable, false);
1295 assert.strictEqual(b.name, "b");
1296 assert.strictEqual(b.writeable, true);
1297 assert.strictEqual(c.name, "c");
1298 assert.strictEqual(c.writeable, false);
1299 assert.strictEqual(d.name, "d");
1300 assert.strictEqual(d.writeable, false);
1301 assert.strictEqual(e.name, "e");
1302 assert.strictEqual(e.writeable, true);
1303 assert.strictEqual(f.name, "f");
1304 assert.strictEqual(f.writeable, true);
1305 assert.strictEqual(mathGlobal, null);
1306 assert.strictEqual(arrayGlobal, null);
1307 assert.strictEqual(configGlobal.name, "ConfigGlobal");
1308 assert.strictEqual(configGlobal.writeable, false);
1309 });
1310
1311 return { Program: spy };
1312 });
1313
1314 linter.verify(code, config);
1315 assert(spy && spy.calledOnce);
1316 });
1317 });
1318
1319 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
1320 const code = "/* global a b : true c: false*/";
1321
1322 it("variables should be available in global scope", () => {
1323 const config = { rules: { checker: "error" } };
1324 let spy;
1325
1326 linter.defineRule("checker", context => {
1327 spy = sinon.spy(() => {
1328 const scope = context.getScope(),
1329 a = getVariable(scope, "a"),
1330 b = getVariable(scope, "b"),
1331 c = getVariable(scope, "c");
1332
1333 assert.strictEqual(a.name, "a");
1334 assert.strictEqual(a.writeable, false);
1335 assert.strictEqual(b.name, "b");
1336 assert.strictEqual(b.writeable, true);
1337 assert.strictEqual(c.name, "c");
1338 assert.strictEqual(c.writeable, false);
1339 });
1340
1341 return { Program: spy };
1342 });
1343
1344 linter.verify(code, config);
1345 assert(spy && spy.calledOnce);
1346 });
1347 });
1348
1349 describe("when evaluating code containing a /*global */ block with specific variables", () => {
1350 const code = "/* global toString hasOwnProperty valueOf: true */";
1351
1352 it("should not throw an error if comment block has global variables which are Object.prototype contains", () => {
1353 const config = { rules: { checker: "error" } };
1354
1355 linter.verify(code, config);
1356 });
1357 });
1358
1359 describe("when evaluating code containing /*eslint-env */ block", () => {
1360 it("variables should be available in global scope", () => {
1361 const code = `/*${ESLINT_ENV} node*/ function f() {} /*${ESLINT_ENV} browser, foo*/`;
1362 const config = { rules: { checker: "error" } };
1363 let spy;
1364
1365 linter.defineRule("checker", context => {
1366 spy = sinon.spy(() => {
1367 const scope = context.getScope(),
1368 exports = getVariable(scope, "exports"),
1369 window = getVariable(scope, "window");
1370
1371 assert.strictEqual(exports.writeable, true);
1372 assert.strictEqual(window.writeable, false);
1373 });
1374
1375 return { Program: spy };
1376 });
1377
1378 linter.verify(code, config);
1379 assert(spy && spy.calledOnce);
1380 });
1381 });
1382
1383 describe("when evaluating code containing /*eslint-env */ block with sloppy whitespace", () => {
1384 const code = `/* ${ESLINT_ENV} ,, node , no-browser ,, */`;
1385
1386 it("variables should be available in global scope", () => {
1387 const config = { rules: { checker: "error" } };
1388 let spy;
1389
1390 linter.defineRule("checker", context => {
1391 spy = sinon.spy(() => {
1392 const scope = context.getScope(),
1393 exports = getVariable(scope, "exports"),
1394 window = getVariable(scope, "window");
1395
1396 assert.strictEqual(exports.writeable, true);
1397 assert.strictEqual(window, null);
1398 });
1399
1400 return { Program: spy };
1401 });
1402
1403 linter.verify(code, config);
1404 assert(spy && spy.calledOnce);
1405 });
1406 });
1407
1408 describe("when evaluating code containing /*exported */ block", () => {
1409
1410 it("we should behave nicely when no matching variable is found", () => {
1411 const code = "/* exported horse */";
1412 const config = { rules: {} };
1413
1414 linter.verify(code, config, filename, true);
1415 });
1416
1417 it("variables should be exported", () => {
1418 const code = "/* exported horse */\n\nvar horse = 'circus'";
1419 const config = { rules: { checker: "error" } };
1420 let spy;
1421
1422 linter.defineRule("checker", context => {
1423 spy = sinon.spy(() => {
1424 const scope = context.getScope(),
1425 horse = getVariable(scope, "horse");
1426
1427 assert.strictEqual(horse.eslintUsed, true);
1428 });
1429
1430 return { Program: spy };
1431 });
1432
1433 linter.verify(code, config);
1434 assert(spy && spy.calledOnce);
1435 });
1436
1437 it("undefined variables should not be exported", () => {
1438 const code = "/* exported horse */\n\nhorse = 'circus'";
1439 const config = { rules: { checker: "error" } };
1440 let spy;
1441
1442 linter.defineRule("checker", context => {
1443 spy = sinon.spy(() => {
1444 const scope = context.getScope(),
1445 horse = getVariable(scope, "horse");
1446
1447 assert.strictEqual(horse, null);
1448 });
1449
1450 return { Program: spy };
1451 });
1452
1453 linter.verify(code, config);
1454 assert(spy && spy.calledOnce);
1455 });
1456
1457 it("variables should be exported in strict mode", () => {
1458 const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'";
1459 const config = { rules: { checker: "error" } };
1460 let spy;
1461
1462 linter.defineRule("checker", context => {
1463 spy = sinon.spy(() => {
1464 const scope = context.getScope(),
1465 horse = getVariable(scope, "horse");
1466
1467 assert.strictEqual(horse.eslintUsed, true);
1468 });
1469
1470 return { Program: spy };
1471 });
1472
1473 linter.verify(code, config);
1474 assert(spy && spy.calledOnce);
1475 });
1476
1477 it("variables should not be exported in the es6 module environment", () => {
1478 const code = "/* exported horse */\nvar horse = 'circus'";
1479 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } };
1480 let spy;
1481
1482 linter.defineRule("checker", context => {
1483 spy = sinon.spy(() => {
1484 const scope = context.getScope(),
1485 horse = getVariable(scope, "horse");
1486
1487 assert.strictEqual(horse, null); // there is no global scope at all
1488 });
1489
1490 return { Program: spy };
1491 });
1492
1493 linter.verify(code, config);
1494 assert(spy && spy.calledOnce);
1495 });
1496
1497 it("variables should not be exported when in the node environment", () => {
1498 const code = "/* exported horse */\nvar horse = 'circus'";
1499 const config = { rules: { checker: "error" }, env: { node: true } };
1500 let spy;
1501
1502 linter.defineRule("checker", context => {
1503 spy = sinon.spy(() => {
1504 const scope = context.getScope(),
1505 horse = getVariable(scope, "horse");
1506
1507 assert.strictEqual(horse, null); // there is no global scope at all
1508 });
1509
1510 return { Program: spy };
1511 });
1512
1513 linter.verify(code, config);
1514 assert(spy && spy.calledOnce);
1515 });
1516 });
1517
1518 describe("when evaluating code containing a line comment", () => {
1519 const code = "//global a \n function f() {}";
1520
1521 it("should not introduce a global variable", () => {
1522 const config = { rules: { checker: "error" } };
1523 let spy;
1524
1525 linter.defineRule("checker", context => {
1526 spy = sinon.spy(() => {
1527 const scope = context.getScope();
1528
1529 assert.strictEqual(getVariable(scope, "a"), null);
1530 });
1531
1532 return { Program: spy };
1533 });
1534
1535 linter.verify(code, config);
1536 assert(spy && spy.calledOnce);
1537 });
1538 });
1539
1540 describe("when evaluating code containing normal block comments", () => {
1541 const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
1542
1543 it("should not introduce a global variable", () => {
1544 const config = { rules: { checker: "error" } };
1545 let spy;
1546
1547 linter.defineRule("checker", context => {
1548 spy = sinon.spy(() => {
1549 const scope = context.getScope();
1550
1551 assert.strictEqual(getVariable(scope, "a"), null);
1552 assert.strictEqual(getVariable(scope, "b"), null);
1553 assert.strictEqual(getVariable(scope, "foo"), null);
1554 assert.strictEqual(getVariable(scope, "c"), null);
1555 });
1556
1557 return { Program: spy };
1558 });
1559
1560 linter.verify(code, config);
1561 assert(spy && spy.calledOnce);
1562 });
1563 });
1564
1565 describe("when evaluating any code", () => {
1566 const code = "x";
1567
1568 it("builtin global variables should be available in the global scope", () => {
1569 const config = { rules: { checker: "error" } };
1570 let spy;
1571
1572 linter.defineRule("checker", context => {
1573 spy = sinon.spy(() => {
1574 const scope = context.getScope();
1575
1576 assert.notStrictEqual(getVariable(scope, "Object"), null);
1577 assert.notStrictEqual(getVariable(scope, "Array"), null);
1578 assert.notStrictEqual(getVariable(scope, "undefined"), null);
1579 });
1580
1581 return { Program: spy };
1582 });
1583
1584 linter.verify(code, config);
1585 assert(spy && spy.calledOnce);
1586 });
1587
1588 it("ES6 global variables should not be available by default", () => {
1589 const config = { rules: { checker: "error" } };
1590 let spy;
1591
1592 linter.defineRule("checker", context => {
1593 spy = sinon.spy(() => {
1594 const scope = context.getScope();
1595
1596 assert.strictEqual(getVariable(scope, "Promise"), null);
1597 assert.strictEqual(getVariable(scope, "Symbol"), null);
1598 assert.strictEqual(getVariable(scope, "WeakMap"), null);
1599 });
1600
1601 return { Program: spy };
1602 });
1603
1604 linter.verify(code, config);
1605 assert(spy && spy.calledOnce);
1606 });
1607
1608 it("ES6 global variables should be available in the es6 environment", () => {
1609 const config = { rules: { checker: "error" }, env: { es6: true } };
1610 let spy;
1611
1612 linter.defineRule("checker", context => {
1613 spy = sinon.spy(() => {
1614 const scope = context.getScope();
1615
1616 assert.notStrictEqual(getVariable(scope, "Promise"), null);
1617 assert.notStrictEqual(getVariable(scope, "Symbol"), null);
1618 assert.notStrictEqual(getVariable(scope, "WeakMap"), null);
1619 });
1620
1621 return { Program: spy };
1622 });
1623
1624 linter.verify(code, config);
1625 assert(spy && spy.calledOnce);
1626 });
1627
1628 it("ES6 global variables can be disabled when the es6 environment is enabled", () => {
1629 const config = { rules: { checker: "error" }, globals: { Promise: "off", Symbol: "off", WeakMap: "off" }, env: { es6: true } };
1630 let spy;
1631
1632 linter.defineRule("checker", context => {
1633 spy = sinon.spy(() => {
1634 const scope = context.getScope();
1635
1636 assert.strictEqual(getVariable(scope, "Promise"), null);
1637 assert.strictEqual(getVariable(scope, "Symbol"), null);
1638 assert.strictEqual(getVariable(scope, "WeakMap"), null);
1639 });
1640
1641 return { Program: spy };
1642 });
1643
1644 linter.verify(code, config);
1645 assert(spy && spy.calledOnce);
1646 });
1647 });
1648
1649 describe("at any time", () => {
1650 const code = "new-rule";
1651
1652 it("can add a rule dynamically", () => {
1653 linter.defineRule(code, context => ({
1654 Literal(node) {
1655 context.report(node, "message");
1656 }
1657 }));
1658
1659 const config = { rules: {} };
1660
1661 config.rules[code] = 1;
1662
1663 const messages = linter.verify("0", config, filename);
1664 const suppressedMessages = linter.getSuppressedMessages();
1665
1666 assert.strictEqual(messages.length, 1);
1667 assert.strictEqual(messages[0].ruleId, code);
1668 assert.strictEqual(messages[0].nodeType, "Literal");
1669
1670 assert.strictEqual(suppressedMessages.length, 0);
1671 });
1672 });
1673
1674 describe("at any time", () => {
1675 const code = ["new-rule-0", "new-rule-1"];
1676
1677 it("can add multiple rules dynamically", () => {
1678 const config = { rules: {} };
1679 const newRules = {};
1680
1681 code.forEach(item => {
1682 config.rules[item] = 1;
1683 newRules[item] = function(context) {
1684 return {
1685 Literal(node) {
1686 context.report(node, "message");
1687 }
1688 };
1689 };
1690 });
1691 linter.defineRules(newRules);
1692
1693 const messages = linter.verify("0", config, filename);
1694 const suppressedMessages = linter.getSuppressedMessages();
1695
1696 assert.strictEqual(messages.length, code.length);
1697 code.forEach(item => {
1698 assert.ok(messages.some(message => message.ruleId === item));
1699 });
1700 messages.forEach(message => {
1701 assert.strictEqual(message.nodeType, "Literal");
1702 });
1703
1704 assert.strictEqual(suppressedMessages.length, 0);
1705 });
1706 });
1707
1708 describe("at any time", () => {
1709 const code = "filename-rule";
1710
1711 it("has access to the filename", () => {
1712 linter.defineRule(code, context => ({
1713 Literal(node) {
1714 context.report(node, context.getFilename());
1715 }
1716 }));
1717
1718 const config = { rules: {} };
1719
1720 config.rules[code] = 1;
1721
1722 const messages = linter.verify("0", config, filename);
1723 const suppressedMessages = linter.getSuppressedMessages();
1724
1725 assert.strictEqual(messages[0].message, filename);
1726 assert.strictEqual(suppressedMessages.length, 0);
1727 });
1728
1729 it("has access to the physicalFilename", () => {
1730 linter.defineRule(code, context => ({
1731 Literal(node) {
1732 context.report(node, context.getPhysicalFilename());
1733 }
1734 }));
1735
1736 const config = { rules: {} };
1737
1738 config.rules[code] = 1;
1739
1740 const messages = linter.verify("0", config, filename);
1741 const suppressedMessages = linter.getSuppressedMessages();
1742
1743 assert.strictEqual(messages[0].message, filename);
1744 assert.strictEqual(suppressedMessages.length, 0);
1745 });
1746
1747 it("defaults filename to '<input>'", () => {
1748 linter.defineRule(code, context => ({
1749 Literal(node) {
1750 context.report(node, context.getFilename());
1751 }
1752 }));
1753
1754 const config = { rules: {} };
1755
1756 config.rules[code] = 1;
1757
1758 const messages = linter.verify("0", config);
1759 const suppressedMessages = linter.getSuppressedMessages();
1760
1761 assert.strictEqual(messages[0].message, "<input>");
1762 assert.strictEqual(suppressedMessages.length, 0);
1763 });
1764 });
1765
1766 describe("when evaluating code with comments to enable rules", () => {
1767
1768 it("should report a violation", () => {
1769 const code = "/*eslint no-alert:1*/ alert('test');";
1770 const config = { rules: {} };
1771
1772 const messages = linter.verify(code, config, filename);
1773 const suppressedMessages = linter.getSuppressedMessages();
1774
1775 assert.strictEqual(messages.length, 1);
1776 assert.strictEqual(messages[0].ruleId, "no-alert");
1777 assert.strictEqual(messages[0].message, "Unexpected alert.");
1778 assert.include(messages[0].nodeType, "CallExpression");
1779
1780 assert.strictEqual(suppressedMessages.length, 0);
1781 });
1782
1783 it("rules should not change initial config", () => {
1784 const config = { rules: { strict: 2 } };
1785 const codeA = "/*eslint strict: 0*/ function bar() { return 2; }";
1786 const codeB = "function foo() { return 1; }";
1787 let messages = linter.verify(codeA, config, filename, false);
1788 let suppressedMessages = linter.getSuppressedMessages();
1789
1790 assert.strictEqual(messages.length, 0);
1791 assert.strictEqual(suppressedMessages.length, 0);
1792
1793 messages = linter.verify(codeB, config, filename, false);
1794 suppressedMessages = linter.getSuppressedMessages();
1795 assert.strictEqual(messages.length, 1);
1796
1797 assert.strictEqual(suppressedMessages.length, 0);
1798 });
1799
1800 it("rules should not change initial config", () => {
1801 const config = { rules: { quotes: [2, "double"] } };
1802 const codeA = "/*eslint quotes: 0*/ function bar() { return '2'; }";
1803 const codeB = "function foo() { return '1'; }";
1804 let messages = linter.verify(codeA, config, filename, false);
1805 let suppressedMessages = linter.getSuppressedMessages();
1806
1807 assert.strictEqual(messages.length, 0);
1808 assert.strictEqual(suppressedMessages.length, 0);
1809
1810 messages = linter.verify(codeB, config, filename, false);
1811 suppressedMessages = linter.getSuppressedMessages();
1812 assert.strictEqual(messages.length, 1);
1813
1814 assert.strictEqual(suppressedMessages.length, 0);
1815 });
1816
1817 it("rules should not change initial config", () => {
1818 const config = { rules: { quotes: [2, "double"] } };
1819 const codeA = "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
1820 const codeB = "function foo() { return '1'; }";
1821
1822 let messages = linter.verify(codeA, config, filename, false);
1823 let suppressedMessages = linter.getSuppressedMessages();
1824
1825 assert.strictEqual(messages.length, 0);
1826 assert.strictEqual(suppressedMessages.length, 0);
1827
1828 messages = linter.verify(codeB, config, filename, false);
1829 suppressedMessages = linter.getSuppressedMessages();
1830
1831 assert.strictEqual(messages.length, 1);
1832 assert.strictEqual(suppressedMessages.length, 0);
1833 });
1834
1835 it("rules should not change initial config", () => {
1836 const config = { rules: { "no-unused-vars": [2, { vars: "all" }] } };
1837 const codeA = "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
1838 const codeB = "var b = 55;";
1839
1840 let messages = linter.verify(codeA, config, filename, false);
1841 let suppressedMessages = linter.getSuppressedMessages();
1842
1843 assert.strictEqual(messages.length, 0);
1844 assert.strictEqual(suppressedMessages.length, 0);
1845
1846 messages = linter.verify(codeB, config, filename, false);
1847 suppressedMessages = linter.getSuppressedMessages();
1848
1849 assert.strictEqual(messages.length, 1);
1850 assert.strictEqual(suppressedMessages.length, 0);
1851 });
1852 });
1853
1854 describe("when evaluating code with invalid comments to enable rules", () => {
1855 it("should report a violation when the config is not a valid rule configuration", () => {
1856 const messages = linter.verify("/*eslint no-alert:true*/ alert('test');", {});
1857 const suppressedMessages = linter.getSuppressedMessages();
1858
1859 assert.deepStrictEqual(
1860 messages,
1861 [
1862 {
1863 severity: 2,
1864 ruleId: "no-alert",
1865 message: "Configuration for rule \"no-alert\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed 'true').\n",
1866 line: 1,
1867 column: 1,
1868 endLine: 1,
1869 endColumn: 25,
1870 nodeType: null
1871 }
1872 ]
1873 );
1874
1875 assert.strictEqual(suppressedMessages.length, 0);
1876 });
1877
1878 it("should report a violation when the config violates a rule's schema", () => {
1879 const messages = linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {});
1880 const suppressedMessages = linter.getSuppressedMessages();
1881
1882 assert.deepStrictEqual(
1883 messages,
1884 [
1885 {
1886 severity: 2,
1887 ruleId: "no-alert",
1888 message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
1889 line: 1,
1890 column: 1,
1891 endLine: 1,
1892 endColumn: 63,
1893 nodeType: null
1894 }
1895 ]
1896 );
1897
1898 assert.strictEqual(suppressedMessages.length, 0);
1899 });
1900 });
1901
1902 describe("when evaluating code with comments to disable rules", () => {
1903 const code = "/*eslint no-alert:0*/ alert('test');";
1904
1905 it("should not report a violation", () => {
1906 const config = { rules: { "no-alert": 1 } };
1907
1908 const messages = linter.verify(code, config, filename);
1909 const suppressedMessages = linter.getSuppressedMessages();
1910
1911 assert.strictEqual(messages.length, 0);
1912 assert.strictEqual(suppressedMessages.length, 0);
1913 });
1914 });
1915
1916 describe("when evaluating code with comments to disable rules", () => {
1917 let code, messages, suppressedMessages;
1918
1919 it("should report an error when disabling a non-existent rule in inline comment", () => {
1920 code = "/*eslint foo:0*/ ;";
1921 messages = linter.verify(code, {}, filename);
1922 suppressedMessages = linter.getSuppressedMessages();
1923 assert.strictEqual(messages.length, 1);
1924 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
1925 assert.strictEqual(suppressedMessages.length, 0);
1926
1927 code = "/*eslint-disable foo*/ ;";
1928 messages = linter.verify(code, {}, filename);
1929 suppressedMessages = linter.getSuppressedMessages();
1930 assert.strictEqual(messages.length, 1);
1931 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
1932 assert.strictEqual(suppressedMessages.length, 0);
1933
1934 code = "/*eslint-disable-line foo*/ ;";
1935 messages = linter.verify(code, {}, filename);
1936 suppressedMessages = linter.getSuppressedMessages();
1937 assert.strictEqual(messages.length, 1);
1938 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
1939 assert.strictEqual(suppressedMessages.length, 0);
1940
1941 code = "/*eslint-disable-next-line foo*/ ;";
1942 messages = linter.verify(code, {}, filename);
1943 suppressedMessages = linter.getSuppressedMessages();
1944 assert.strictEqual(messages.length, 1);
1945 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
1946 assert.strictEqual(suppressedMessages.length, 0);
1947 });
1948
1949 it("should not report an error, when disabling a non-existent rule in config", () => {
1950 messages = linter.verify("", { rules: { foo: 0 } }, filename);
1951 suppressedMessages = linter.getSuppressedMessages();
1952
1953 assert.strictEqual(messages.length, 0);
1954 assert.strictEqual(suppressedMessages.length, 0);
1955 });
1956
1957 it("should report an error, when config a non-existent rule in config", () => {
1958 messages = linter.verify("", { rules: { foo: 1 } }, filename);
1959 suppressedMessages = linter.getSuppressedMessages();
1960 assert.strictEqual(messages.length, 1);
1961 assert.strictEqual(messages[0].severity, 2);
1962 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
1963 assert.strictEqual(suppressedMessages.length, 0);
1964
1965 messages = linter.verify("", { rules: { foo: 2 } }, filename);
1966 suppressedMessages = linter.getSuppressedMessages();
1967 assert.strictEqual(messages.length, 1);
1968 assert.strictEqual(messages[0].severity, 2);
1969 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
1970 assert.strictEqual(suppressedMessages.length, 0);
1971 });
1972 });
1973
1974 describe("when evaluating code with comments to enable multiple rules", () => {
1975 const code = "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
1976
1977 it("should report a violation", () => {
1978 const config = { rules: {} };
1979
1980 const messages = linter.verify(code, config, filename);
1981 const suppressedMessages = linter.getSuppressedMessages();
1982
1983 assert.strictEqual(messages.length, 2);
1984 assert.strictEqual(messages[0].ruleId, "no-alert");
1985 assert.strictEqual(messages[0].message, "Unexpected alert.");
1986 assert.include(messages[0].nodeType, "CallExpression");
1987 assert.strictEqual(messages[1].ruleId, "no-console");
1988
1989 assert.strictEqual(suppressedMessages.length, 0);
1990 });
1991 });
1992
1993 describe("when evaluating code with comments to enable and disable multiple rules", () => {
1994 const code = "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
1995
1996 it("should report a violation", () => {
1997 const config = { rules: { "no-console": 1, "no-alert": 0 } };
1998
1999 const messages = linter.verify(code, config, filename);
2000 const suppressedMessages = linter.getSuppressedMessages();
2001
2002 assert.strictEqual(messages.length, 1);
2003 assert.strictEqual(messages[0].ruleId, "no-alert");
2004 assert.strictEqual(messages[0].message, "Unexpected alert.");
2005 assert.include(messages[0].nodeType, "CallExpression");
2006
2007 assert.strictEqual(suppressedMessages.length, 0);
2008 });
2009 });
2010
2011 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
2012
2013 beforeEach(() => {
2014 linter.defineRule("test-plugin/test-rule", context => ({
2015 Literal(node) {
2016 if (node.value === "trigger violation") {
2017 context.report(node, "Reporting violation.");
2018 }
2019 }
2020 }));
2021 });
2022
2023 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
2024 const config = { rules: {} };
2025 const code = "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
2026
2027 const messages = linter.verify(code, config, filename);
2028 const suppressedMessages = linter.getSuppressedMessages();
2029
2030 assert.strictEqual(messages.length, 0);
2031 assert.strictEqual(suppressedMessages.length, 0);
2032 });
2033
2034 it("should not report a violation when inline comment disables plugin rule", () => {
2035 const code = "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
2036 const config = { rules: { "test-plugin/test-rule": 1 } };
2037
2038 const messages = linter.verify(code, config, filename);
2039 const suppressedMessages = linter.getSuppressedMessages();
2040
2041 assert.strictEqual(messages.length, 0);
2042 assert.strictEqual(suppressedMessages.length, 0);
2043 });
2044
2045 it("should report a violation when the report is right before the comment", () => {
2046 const code = " /* eslint-disable */ ";
2047
2048 linter.defineRule("checker", context => ({
2049 Program() {
2050 context.report({ loc: { line: 1, column: 0 }, message: "foo" });
2051 }
2052 }));
2053 const problems = linter.verify(code, { rules: { checker: "error" } });
2054 const suppressedMessages = linter.getSuppressedMessages();
2055
2056 assert.strictEqual(problems.length, 1);
2057 assert.strictEqual(problems[0].message, "foo");
2058 assert.strictEqual(suppressedMessages.length, 0);
2059 });
2060
2061 it("should not report a violation when the report is right at the start of the comment", () => {
2062 const code = " /* eslint-disable */ ";
2063
2064 linter.defineRule("checker", context => ({
2065 Program() {
2066 context.report({ loc: { line: 1, column: 1 }, message: "foo" });
2067 }
2068 }));
2069 const problems = linter.verify(code, { rules: { checker: "error" } });
2070 const suppressedMessages = linter.getSuppressedMessages();
2071
2072 assert.strictEqual(problems.length, 0);
2073
2074 assert.strictEqual(suppressedMessages.length, 1);
2075 assert.strictEqual(suppressedMessages[0].message, "foo");
2076 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
2077 assert.strictEqual(suppressedMessages[0].suppressions[0].justification, "");
2078 });
2079
2080 it("rules should not change initial config", () => {
2081 const config = { rules: { "test-plugin/test-rule": 2 } };
2082 const codeA = "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
2083 const codeB = "var a = \"trigger violation\";";
2084
2085 let messages = linter.verify(codeA, config, filename, false);
2086 let suppressedMessages = linter.getSuppressedMessages();
2087
2088 assert.strictEqual(messages.length, 0);
2089 assert.strictEqual(suppressedMessages.length, 0);
2090
2091 messages = linter.verify(codeB, config, filename, false);
2092 suppressedMessages = linter.getSuppressedMessages();
2093
2094 assert.strictEqual(messages.length, 1);
2095 assert.strictEqual(suppressedMessages.length, 0);
2096 });
2097 });
2098
2099 describe("when evaluating code with comments to enable and disable all reporting", () => {
2100 it("should report a violation", () => {
2101
2102 const code = [
2103 "/*eslint-disable */",
2104 "alert('test');",
2105 "/*eslint-enable */",
2106 "alert('test');"
2107 ].join("\n");
2108 const config = { rules: { "no-alert": 1 } };
2109
2110 const messages = linter.verify(code, config, filename);
2111 const suppressedMessages = linter.getSuppressedMessages();
2112
2113 assert.strictEqual(messages.length, 1);
2114 assert.strictEqual(messages[0].ruleId, "no-alert");
2115 assert.strictEqual(messages[0].message, "Unexpected alert.");
2116 assert.include(messages[0].nodeType, "CallExpression");
2117 assert.strictEqual(messages[0].line, 4);
2118
2119 assert.strictEqual(suppressedMessages.length, 1);
2120 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2121 assert.strictEqual(suppressedMessages[0].message, "Unexpected alert.");
2122 assert.strictEqual(suppressedMessages[0].line, 2);
2123 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
2124 assert.strictEqual(suppressedMessages[0].suppressions[0].justification, "");
2125 });
2126
2127 it("should not report a violation", () => {
2128 const code = [
2129 "/*eslint-disable */",
2130 "alert('test');",
2131 "alert('test');"
2132 ].join("\n");
2133 const config = { rules: { "no-alert": 1 } };
2134
2135 const messages = linter.verify(code, config, filename);
2136 const suppressedMessages = linter.getSuppressedMessages();
2137
2138 assert.strictEqual(messages.length, 0);
2139
2140 assert.strictEqual(suppressedMessages.length, 2);
2141 assert.strictEqual(suppressedMessages[0].line, 2);
2142 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
2143 assert.strictEqual(suppressedMessages[1].line, 3);
2144 assert.strictEqual(suppressedMessages[1].suppressions.length, 1);
2145 });
2146
2147 it("should not report a violation", () => {
2148 const code = [
2149 " alert('test1');/*eslint-disable */\n",
2150 "alert('test');",
2151 " alert('test');\n",
2152 "/*eslint-enable */alert('test2');"
2153 ].join("");
2154 const config = { rules: { "no-alert": 1 } };
2155
2156 const messages = linter.verify(code, config, filename);
2157 const suppressedMessages = linter.getSuppressedMessages();
2158
2159 assert.strictEqual(messages.length, 2);
2160 assert.strictEqual(messages[0].column, 21);
2161 assert.strictEqual(messages[1].column, 19);
2162
2163 assert.strictEqual(suppressedMessages.length, 2);
2164 assert.strictEqual(suppressedMessages[0].column, 1);
2165 assert.strictEqual(suppressedMessages[1].column, 56);
2166 });
2167
2168 it("should report a violation", () => {
2169
2170 const code = [
2171 "/*eslint-disable */",
2172 "alert('test');",
2173 "/*eslint-disable */",
2174 "alert('test');",
2175 "/*eslint-enable*/",
2176 "alert('test');",
2177 "/*eslint-enable*/"
2178 ].join("\n");
2179
2180 const config = { rules: { "no-alert": 1 } };
2181
2182 const messages = linter.verify(code, config, filename);
2183 const suppressedMessages = linter.getSuppressedMessages();
2184
2185 assert.strictEqual(messages.length, 1);
2186 assert.strictEqual(suppressedMessages.length, 2);
2187 });
2188
2189
2190 it("should not report a violation", () => {
2191 const code = [
2192 "/*eslint-disable */",
2193 "(function(){ var b = 44;})()",
2194 "/*eslint-enable */;any();"
2195 ].join("\n");
2196
2197 const config = { rules: { "no-unused-vars": 1 } };
2198
2199 const messages = linter.verify(code, config, filename);
2200 const suppressedMessages = linter.getSuppressedMessages();
2201
2202 assert.strictEqual(messages.length, 0);
2203 assert.strictEqual(suppressedMessages.length, 1);
2204 });
2205
2206 it("should not report a violation", () => {
2207 const code = [
2208 "(function(){ /*eslint-disable */ var b = 44;})()",
2209 "/*eslint-enable */;any();"
2210 ].join("\n");
2211
2212 const config = { rules: { "no-unused-vars": 1 } };
2213
2214 const messages = linter.verify(code, config, filename);
2215 const suppressedMessages = linter.getSuppressedMessages();
2216
2217 assert.strictEqual(messages.length, 0);
2218 assert.strictEqual(suppressedMessages.length, 1);
2219 });
2220 });
2221
2222 describe("when evaluating code with comments to ignore reporting on specific rules on a specific line", () => {
2223
2224 describe("eslint-disable-line", () => {
2225 it("should report a violation", () => {
2226 const code = [
2227 "alert('test'); // eslint-disable-line no-alert",
2228 "console.log('test');" // here
2229 ].join("\n");
2230 const config = {
2231 rules: {
2232 "no-alert": 1,
2233 "no-console": 1
2234 }
2235 };
2236
2237 const messages = linter.verify(code, config, filename);
2238 const suppressedMessages = linter.getSuppressedMessages();
2239
2240 assert.strictEqual(messages.length, 1);
2241 assert.strictEqual(messages[0].ruleId, "no-console");
2242
2243 assert.strictEqual(suppressedMessages.length, 1);
2244 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2245 });
2246
2247 it("should report a violation", () => {
2248 const code = [
2249 "alert('test'); // eslint-disable-line no-alert",
2250 "console.log('test'); // eslint-disable-line no-console",
2251 "alert('test');" // here
2252 ].join("\n");
2253 const config = {
2254 rules: {
2255 "no-alert": 1,
2256 "no-console": 1
2257 }
2258 };
2259
2260 const messages = linter.verify(code, config, filename);
2261 const suppressedMessages = linter.getSuppressedMessages();
2262
2263 assert.strictEqual(messages.length, 1);
2264 assert.strictEqual(messages[0].ruleId, "no-alert");
2265
2266 assert.strictEqual(suppressedMessages.length, 2);
2267 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2268 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
2269 });
2270
2271 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
2272 const code = [
2273 "/* eslint-disable-line",
2274 "*",
2275 "*/ console.log('test');" // here
2276 ].join("\n");
2277 const config = {
2278 rules: {
2279 "no-console": 1
2280 }
2281 };
2282
2283 const messages = linter.verify(code, config, filename);
2284 const suppressedMessages = linter.getSuppressedMessages();
2285
2286 assert.strictEqual(messages.length, 2);
2287 assert.strictEqual(messages[1].ruleId, "no-console");
2288 assert.strictEqual(suppressedMessages.length, 0);
2289 });
2290
2291 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
2292 const code = [
2293 "alert('test'); /* eslint-disable-line ",
2294 "no-alert */"
2295 ].join("\n");
2296 const config = {
2297 rules: {
2298 "no-alert": 1
2299 }
2300 };
2301
2302 const messages = linter.verify(code, config);
2303 const suppressedMessages = linter.getSuppressedMessages();
2304
2305 assert.deepStrictEqual(messages, [
2306 {
2307 ruleId: "no-alert",
2308 severity: 1,
2309 line: 1,
2310 column: 1,
2311 endLine: 1,
2312 endColumn: 14,
2313 message: "Unexpected alert.",
2314 messageId: "unexpected",
2315 nodeType: "CallExpression"
2316 },
2317 {
2318 ruleId: null,
2319 severity: 2,
2320 message: "eslint-disable-line comment should not span multiple lines.",
2321 line: 1,
2322 column: 16,
2323 endLine: 2,
2324 endColumn: 12,
2325 nodeType: null
2326 }
2327 ]);
2328
2329 assert.strictEqual(suppressedMessages.length, 0);
2330 });
2331
2332 it("should not report a violation for eslint-disable-line in block comment", () => {
2333 const code = [
2334 "alert('test'); // eslint-disable-line no-alert",
2335 "alert('test'); /*eslint-disable-line no-alert*/"
2336 ].join("\n");
2337 const config = {
2338 rules: {
2339 "no-alert": 1
2340 }
2341 };
2342
2343 const messages = linter.verify(code, config, filename);
2344 const suppressedMessages = linter.getSuppressedMessages();
2345
2346 assert.strictEqual(messages.length, 0);
2347
2348 assert.strictEqual(suppressedMessages.length, 2);
2349 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2350 assert.strictEqual(suppressedMessages[1].ruleId, "no-alert");
2351 });
2352
2353 it("should not report a violation", () => {
2354 const code = [
2355 "alert('test'); // eslint-disable-line no-alert",
2356 "console.log('test'); // eslint-disable-line no-console"
2357 ].join("\n");
2358 const config = {
2359 rules: {
2360 "no-alert": 1,
2361 "no-console": 1
2362 }
2363 };
2364
2365 const messages = linter.verify(code, config, filename);
2366 const suppressedMessages = linter.getSuppressedMessages();
2367
2368 assert.strictEqual(messages.length, 0);
2369
2370 assert.strictEqual(suppressedMessages.length, 2);
2371 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2372 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
2373 });
2374
2375 it("should not report a violation", () => {
2376 const code = [
2377 "alert('test') // eslint-disable-line no-alert, quotes, semi",
2378 "console.log('test'); // eslint-disable-line"
2379 ].join("\n");
2380 const config = {
2381 rules: {
2382 "no-alert": 1,
2383 quotes: [1, "double"],
2384 semi: [1, "always"],
2385 "no-console": 1
2386 }
2387 };
2388
2389 const messages = linter.verify(code, config, filename);
2390 const suppressedMessages = linter.getSuppressedMessages();
2391
2392 assert.strictEqual(messages.length, 0);
2393 assert.strictEqual(suppressedMessages.length, 5);
2394 });
2395
2396 it("should not report a violation", () => {
2397 const code = [
2398 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
2399 "console.log('test'); /* eslint-disable-line */"
2400 ].join("\n");
2401 const config = {
2402 rules: {
2403 "no-alert": 1,
2404 quotes: [1, "double"],
2405 semi: [1, "always"],
2406 "no-console": 1
2407 }
2408 };
2409
2410 const messages = linter.verify(code, config, filename);
2411 const suppressedMessages = linter.getSuppressedMessages();
2412
2413 assert.strictEqual(messages.length, 0);
2414 assert.strictEqual(suppressedMessages.length, 5);
2415 });
2416
2417 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2418 const code = [
2419 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
2420 ].join("\n");
2421 const config = {
2422 rules: {
2423 "no-alert": 1,
2424 quotes: [1, "single"]
2425 }
2426 };
2427 const messages = linter.verify(code, config, filename);
2428 const suppressedMessages = linter.getSuppressedMessages();
2429
2430 assert.strictEqual(messages.length, 0);
2431
2432 assert.strictEqual(suppressedMessages.length, 2);
2433 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2434 });
2435 });
2436
2437 describe("eslint-disable-next-line", () => {
2438 it("should ignore violation of specified rule on next line", () => {
2439 const code = [
2440 "// eslint-disable-next-line no-alert",
2441 "alert('test');",
2442 "console.log('test');"
2443 ].join("\n");
2444 const config = {
2445 rules: {
2446 "no-alert": 1,
2447 "no-console": 1
2448 }
2449 };
2450 const messages = linter.verify(code, config, filename);
2451 const suppressedMessages = linter.getSuppressedMessages();
2452
2453 assert.strictEqual(messages.length, 1);
2454 assert.strictEqual(messages[0].ruleId, "no-console");
2455
2456 assert.strictEqual(suppressedMessages.length, 1);
2457 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2458 });
2459
2460 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2461 const code = [
2462 "/* eslint-disable-next-line no-alert */",
2463 "alert('test');",
2464 "console.log('test');"
2465 ].join("\n");
2466 const config = {
2467 rules: {
2468 "no-alert": 1,
2469 "no-console": 1
2470 }
2471 };
2472 const messages = linter.verify(code, config, filename);
2473 const suppressedMessages = linter.getSuppressedMessages();
2474
2475 assert.strictEqual(messages.length, 1);
2476 assert.strictEqual(messages[0].ruleId, "no-console");
2477
2478 assert.strictEqual(suppressedMessages.length, 1);
2479 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2480 });
2481 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2482 const code = [
2483 "/* eslint-disable-next-line no-alert */",
2484 "alert('test');"
2485 ].join("\n");
2486 const config = {
2487 rules: {
2488 "no-alert": 1
2489 }
2490 };
2491 const messages = linter.verify(code, config, filename);
2492 const suppressedMessages = linter.getSuppressedMessages();
2493
2494 assert.strictEqual(messages.length, 0);
2495
2496 assert.strictEqual(suppressedMessages.length, 1);
2497 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2498 });
2499
2500 it("should not ignore violation if code is not on next line", () => {
2501 const code = [
2502 "/* eslint-disable-next-line",
2503 "no-alert */alert('test');"
2504 ].join("\n");
2505 const config = {
2506 rules: {
2507 "no-alert": 1
2508 }
2509 };
2510 const messages = linter.verify(code, config, filename);
2511 const suppressedMessages = linter.getSuppressedMessages();
2512
2513 assert.strictEqual(messages.length, 1);
2514 assert.strictEqual(messages[0].ruleId, "no-alert");
2515
2516 assert.strictEqual(suppressedMessages.length, 0);
2517 });
2518
2519 it("should ignore violation if block comment span multiple lines", () => {
2520 const code = [
2521 "/* eslint-disable-next-line",
2522 "no-alert */",
2523 "alert('test');"
2524 ].join("\n");
2525 const config = {
2526 rules: {
2527 "no-alert": 1
2528 }
2529 };
2530 const messages = linter.verify(code, config, filename);
2531 const suppressedMessages = linter.getSuppressedMessages();
2532
2533 assert.strictEqual(messages.length, 0);
2534
2535 assert.strictEqual(suppressedMessages.length, 1);
2536 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2537 });
2538
2539 it("should ignore violations only of specified rule", () => {
2540 const code = [
2541 "// eslint-disable-next-line no-console",
2542 "alert('test');",
2543 "console.log('test');"
2544 ].join("\n");
2545 const config = {
2546 rules: {
2547 "no-alert": 1,
2548 "no-console": 1
2549 }
2550 };
2551 const messages = linter.verify(code, config, filename);
2552 const suppressedMessages = linter.getSuppressedMessages();
2553
2554 assert.strictEqual(messages.length, 2);
2555 assert.strictEqual(messages[0].ruleId, "no-alert");
2556 assert.strictEqual(messages[1].ruleId, "no-console");
2557
2558 assert.strictEqual(suppressedMessages.length, 0);
2559 });
2560
2561 it("should ignore violations of multiple rules when specified", () => {
2562 const code = [
2563 "// eslint-disable-next-line no-alert, quotes",
2564 "alert(\"test\");",
2565 "console.log('test');"
2566 ].join("\n");
2567 const config = {
2568 rules: {
2569 "no-alert": 1,
2570 quotes: [1, "single"],
2571 "no-console": 1
2572 }
2573 };
2574 const messages = linter.verify(code, config, filename);
2575 const suppressedMessages = linter.getSuppressedMessages();
2576
2577 assert.strictEqual(messages.length, 1);
2578 assert.strictEqual(messages[0].ruleId, "no-console");
2579
2580 assert.strictEqual(suppressedMessages.length, 2);
2581 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2582 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
2583 });
2584
2585 it("should ignore violations of multiple rules when specified in multiple lines", () => {
2586 const code = [
2587 "/* eslint-disable-next-line",
2588 "no-alert,",
2589 "quotes",
2590 "*/",
2591 "alert(\"test\");",
2592 "console.log('test');"
2593 ].join("\n");
2594 const config = {
2595 rules: {
2596 "no-alert": 1,
2597 quotes: [1, "single"],
2598 "no-console": 1
2599 }
2600 };
2601 const messages = linter.verify(code, config, filename);
2602
2603 assert.strictEqual(messages.length, 1);
2604 assert.strictEqual(messages[0].ruleId, "no-console");
2605 });
2606
2607 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2608 const code = [
2609 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
2610 "alert(\"test\");"
2611 ].join("\n");
2612 const config = {
2613 rules: {
2614 "no-alert": 1,
2615 quotes: [1, "single"]
2616 }
2617 };
2618 const messages = linter.verify(code, config, filename);
2619 const suppressedMessages = linter.getSuppressedMessages();
2620
2621 assert.strictEqual(messages.length, 0);
2622
2623 assert.strictEqual(suppressedMessages.length, 2);
2624 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2625 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
2626 });
2627
2628 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
2629 const code = [
2630 "/* eslint-disable-next-line",
2631 "no-alert",
2632 "*/ // eslint-disable-next-line quotes",
2633 "alert(\"test\");"
2634 ].join("\n");
2635 const config = {
2636 rules: {
2637 "no-alert": 1,
2638 quotes: [1, "single"]
2639 }
2640 };
2641 const messages = linter.verify(code, config, filename);
2642
2643 assert.strictEqual(messages.length, 0);
2644 });
2645
2646 it("should ignore violations of only the specified rule on next line", () => {
2647 const code = [
2648 "// eslint-disable-next-line quotes",
2649 "alert(\"test\");",
2650 "console.log('test');"
2651 ].join("\n");
2652 const config = {
2653 rules: {
2654 "no-alert": 1,
2655 quotes: [1, "single"],
2656 "no-console": 1
2657 }
2658 };
2659 const messages = linter.verify(code, config, filename);
2660 const suppressedMessages = linter.getSuppressedMessages();
2661
2662 assert.strictEqual(messages.length, 2);
2663 assert.strictEqual(messages[0].ruleId, "no-alert");
2664 assert.strictEqual(messages[1].ruleId, "no-console");
2665
2666 assert.strictEqual(suppressedMessages.length, 1);
2667 assert.strictEqual(suppressedMessages[0].ruleId, "quotes");
2668 });
2669
2670 it("should ignore violations of specified rule on next line only", () => {
2671 const code = [
2672 "alert('test');",
2673 "// eslint-disable-next-line no-alert",
2674 "alert('test');",
2675 "console.log('test');"
2676 ].join("\n");
2677 const config = {
2678 rules: {
2679 "no-alert": 1,
2680 "no-console": 1
2681 }
2682 };
2683 const messages = linter.verify(code, config, filename);
2684 const suppressedMessages = linter.getSuppressedMessages();
2685
2686 assert.strictEqual(messages.length, 2);
2687 assert.strictEqual(messages[0].ruleId, "no-alert");
2688 assert.strictEqual(messages[1].ruleId, "no-console");
2689
2690 assert.strictEqual(suppressedMessages.length, 1);
2691 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2692 });
2693
2694 it("should ignore all rule violations on next line if none specified", () => {
2695 const code = [
2696 "// eslint-disable-next-line",
2697 "alert(\"test\");",
2698 "console.log('test')"
2699 ].join("\n");
2700 const config = {
2701 rules: {
2702 semi: [1, "never"],
2703 quotes: [1, "single"],
2704 "no-alert": 1,
2705 "no-console": 1
2706 }
2707 };
2708 const messages = linter.verify(code, config, filename);
2709 const suppressedMessages = linter.getSuppressedMessages();
2710
2711 assert.strictEqual(messages.length, 1);
2712 assert.strictEqual(messages[0].ruleId, "no-console");
2713
2714 assert.strictEqual(suppressedMessages.length, 3);
2715 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2716 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
2717 assert.strictEqual(suppressedMessages[2].ruleId, "semi");
2718 });
2719
2720 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
2721 const code = [
2722 "alert('test');",
2723 "/* eslint-disable-next-line no-alert */",
2724 "alert('test');",
2725 "console.log('test');"
2726 ].join("\n");
2727 const config = {
2728 rules: {
2729 "no-alert": 1,
2730 "no-console": 1
2731 }
2732 };
2733 const messages = linter.verify(code, config, filename);
2734 const suppressedMessages = linter.getSuppressedMessages();
2735
2736 assert.strictEqual(messages.length, 2);
2737 assert.strictEqual(messages[0].ruleId, "no-alert");
2738 assert.strictEqual(messages[1].ruleId, "no-console");
2739
2740 assert.strictEqual(suppressedMessages.length, 1);
2741 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2742 });
2743
2744 it("should report a violation", () => {
2745 const code = [
2746 "/* eslint-disable-next-line",
2747 "*",
2748 "*/",
2749 "console.log('test');" // here
2750 ].join("\n");
2751 const config = {
2752 rules: {
2753 "no-alert": 1,
2754 "no-console": 1
2755 }
2756 };
2757
2758 const messages = linter.verify(code, config, filename);
2759 const suppressedMessages = linter.getSuppressedMessages();
2760
2761 assert.strictEqual(messages.length, 2);
2762 assert.strictEqual(messages[1].ruleId, "no-console");
2763
2764 assert.strictEqual(suppressedMessages.length, 0);
2765 });
2766
2767 it("should not ignore violations if comment is of the type hashbang", () => {
2768 const code = [
2769 "#! eslint-disable-next-line no-alert",
2770 "alert('test');",
2771 "console.log('test');"
2772 ].join("\n");
2773 const config = {
2774 rules: {
2775 "no-alert": 1,
2776 "no-console": 1
2777 }
2778 };
2779 const messages = linter.verify(code, config, filename);
2780 const suppressedMessages = linter.getSuppressedMessages();
2781
2782 assert.strictEqual(messages.length, 2);
2783 assert.strictEqual(messages[0].ruleId, "no-alert");
2784 assert.strictEqual(messages[1].ruleId, "no-console");
2785
2786 assert.strictEqual(suppressedMessages.length, 0);
2787 });
2788 });
2789 });
2790
2791 describe("when evaluating code with comments to enable and disable reporting of specific rules", () => {
2792
2793 it("should report a violation", () => {
2794 const code = [
2795 "/*eslint-disable no-alert */",
2796 "alert('test');",
2797 "console.log('test');" // here
2798 ].join("\n");
2799 const config = { rules: { "no-alert": 1, "no-console": 1 } };
2800
2801 const messages = linter.verify(code, config, filename);
2802 const suppressedMessages = linter.getSuppressedMessages();
2803
2804 assert.strictEqual(messages.length, 1);
2805 assert.strictEqual(messages[0].ruleId, "no-console");
2806
2807 assert.strictEqual(suppressedMessages.length, 1);
2808 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2809 });
2810
2811 it("should report no violation", () => {
2812 const code = [
2813 "/*eslint-disable no-unused-vars */",
2814 "var foo; // eslint-disable-line no-unused-vars",
2815 "var bar;",
2816 "/* eslint-enable no-unused-vars */" // here
2817 ].join("\n");
2818 const config = { rules: { "no-unused-vars": 2 } };
2819
2820 const messages = linter.verify(code, config, filename);
2821 const suppressedMessages = linter.getSuppressedMessages();
2822
2823 assert.strictEqual(messages.length, 0);
2824
2825 assert.strictEqual(suppressedMessages.length, 2);
2826 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
2827 assert.strictEqual(suppressedMessages[0].line, 2);
2828 assert.strictEqual(suppressedMessages[1].ruleId, "no-unused-vars");
2829 assert.strictEqual(suppressedMessages[1].line, 3);
2830 });
2831
2832 it("should report no violation", () => {
2833 const code = [
2834 "var foo1; // eslint-disable-line no-unused-vars",
2835 "var foo2; // eslint-disable-line no-unused-vars",
2836 "var foo3; // eslint-disable-line no-unused-vars",
2837 "var foo4; // eslint-disable-line no-unused-vars",
2838 "var foo5; // eslint-disable-line no-unused-vars"
2839 ].join("\n");
2840 const config = { rules: { "no-unused-vars": 2 } };
2841
2842 const messages = linter.verify(code, config, filename);
2843 const suppressedMessages = linter.getSuppressedMessages();
2844
2845 assert.strictEqual(messages.length, 0);
2846 assert.strictEqual(suppressedMessages.length, 5);
2847 });
2848
2849 it("should report no violation", () => {
2850 const code = [
2851 "/* eslint-disable quotes */",
2852 "console.log(\"foo\");",
2853 "/* eslint-enable quotes */"
2854 ].join("\n");
2855 const config = { rules: { quotes: 2 } };
2856
2857 const messages = linter.verify(code, config, filename);
2858 const suppressedMessages = linter.getSuppressedMessages();
2859
2860 assert.strictEqual(messages.length, 0);
2861 assert.strictEqual(suppressedMessages.length, 0);
2862 });
2863
2864 it("should report a violation", () => {
2865 const code = [
2866 "/*eslint-disable no-alert, no-console */",
2867 "alert('test');",
2868 "console.log('test');",
2869 "/*eslint-enable*/",
2870
2871 "alert('test');", // here
2872 "console.log('test');" // here
2873 ].join("\n");
2874 const config = { rules: { "no-alert": 1, "no-console": 1 } };
2875
2876 const messages = linter.verify(code, config, filename);
2877 const suppressedMessages = linter.getSuppressedMessages();
2878
2879 assert.strictEqual(messages.length, 2);
2880 assert.strictEqual(messages[0].ruleId, "no-alert");
2881 assert.strictEqual(messages[0].line, 5);
2882 assert.strictEqual(messages[1].ruleId, "no-console");
2883 assert.strictEqual(messages[1].line, 6);
2884
2885 assert.strictEqual(suppressedMessages.length, 2);
2886 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2887 assert.strictEqual(suppressedMessages[0].line, 2);
2888 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
2889 assert.strictEqual(suppressedMessages[1].line, 3);
2890 });
2891
2892 it("should report a violation", () => {
2893 const code = [
2894 "/*eslint-disable no-alert */",
2895 "alert('test');",
2896 "console.log('test');",
2897 "/*eslint-enable no-console */",
2898
2899 "alert('test');" // here
2900 ].join("\n");
2901 const config = { rules: { "no-alert": 1, "no-console": 1 } };
2902
2903 const messages = linter.verify(code, config, filename);
2904 const suppressedMessages = linter.getSuppressedMessages();
2905
2906 assert.strictEqual(messages.length, 1);
2907 assert.strictEqual(messages[0].ruleId, "no-console");
2908
2909 assert.strictEqual(suppressedMessages.length, 2);
2910 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2911 assert.strictEqual(suppressedMessages[0].line, 2);
2912 assert.strictEqual(suppressedMessages[1].ruleId, "no-alert");
2913 assert.strictEqual(suppressedMessages[1].line, 5);
2914 });
2915
2916
2917 it("should report a violation", () => {
2918 const code = [
2919 "/*eslint-disable no-alert, no-console */",
2920 "alert('test');",
2921 "console.log('test');",
2922 "/*eslint-enable no-alert*/",
2923
2924 "alert('test');", // here
2925 "console.log('test');"
2926 ].join("\n");
2927 const config = { rules: { "no-alert": 1, "no-console": 1 } };
2928
2929 const messages = linter.verify(code, config, filename);
2930 const suppressedMessages = linter.getSuppressedMessages();
2931
2932 assert.strictEqual(messages.length, 1);
2933 assert.strictEqual(messages[0].ruleId, "no-alert");
2934 assert.strictEqual(messages[0].line, 5);
2935
2936 assert.strictEqual(suppressedMessages.length, 3);
2937 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2938 assert.strictEqual(suppressedMessages[0].line, 2);
2939 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
2940 assert.strictEqual(suppressedMessages[1].line, 3);
2941 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
2942 assert.strictEqual(suppressedMessages[2].line, 6);
2943 });
2944
2945
2946 it("should report a violation", () => {
2947 const code = [
2948 "/*eslint-disable no-alert */",
2949
2950 "/*eslint-disable no-console */",
2951 "alert('test');",
2952 "console.log('test');",
2953 "/*eslint-enable */",
2954
2955 "alert('test');", // here
2956 "console.log('test');", // here
2957
2958 "/*eslint-enable */",
2959
2960 "alert('test');", // here
2961 "console.log('test');", // here
2962
2963 "/*eslint-enable*/"
2964 ].join("\n");
2965 const config = { rules: { "no-alert": 1, "no-console": 1 } };
2966
2967 const messages = linter.verify(code, config, filename);
2968 const suppressedMessages = linter.getSuppressedMessages();
2969
2970 assert.strictEqual(messages.length, 4);
2971 assert.strictEqual(messages[0].ruleId, "no-alert");
2972 assert.strictEqual(messages[0].line, 6);
2973 assert.strictEqual(messages[1].ruleId, "no-console");
2974 assert.strictEqual(messages[1].line, 7);
2975 assert.strictEqual(messages[2].ruleId, "no-alert");
2976 assert.strictEqual(messages[2].line, 9);
2977 assert.strictEqual(messages[3].ruleId, "no-console");
2978 assert.strictEqual(messages[3].line, 10);
2979
2980 assert.strictEqual(suppressedMessages.length, 2);
2981 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2982 assert.strictEqual(suppressedMessages[0].line, 3);
2983 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
2984 assert.strictEqual(suppressedMessages[1].line, 4);
2985 });
2986
2987 it("should report a violation", () => {
2988 const code = [
2989 "/*eslint-disable no-alert, no-console */",
2990 "alert('test');",
2991 "console.log('test');",
2992
2993 "/*eslint-enable no-alert */",
2994
2995 "alert('test');", // here
2996 "console.log('test');",
2997
2998 "/*eslint-enable no-console */",
2999
3000 "alert('test');", // here
3001 "console.log('test');", // here
3002 "/*eslint-enable no-console */"
3003 ].join("\n");
3004 const config = { rules: { "no-alert": 1, "no-console": 1 } };
3005
3006 const messages = linter.verify(code, config, filename);
3007 const suppressedMessages = linter.getSuppressedMessages();
3008
3009 assert.strictEqual(messages.length, 3);
3010 assert.strictEqual(messages[0].ruleId, "no-alert");
3011 assert.strictEqual(messages[0].line, 5);
3012 assert.strictEqual(messages[1].ruleId, "no-alert");
3013 assert.strictEqual(messages[1].line, 8);
3014 assert.strictEqual(messages[2].ruleId, "no-console");
3015 assert.strictEqual(messages[2].line, 9);
3016
3017 assert.strictEqual(suppressedMessages.length, 3);
3018 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
3019 assert.strictEqual(suppressedMessages[0].line, 2);
3020 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
3021 assert.strictEqual(suppressedMessages[1].line, 3);
3022 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
3023 assert.strictEqual(suppressedMessages[2].line, 6);
3024 });
3025
3026 it("should report a violation when severity is warn", () => {
3027 const code = [
3028 "/*eslint-disable no-alert, no-console */",
3029 "alert('test');",
3030 "console.log('test');",
3031
3032 "/*eslint-enable no-alert */",
3033
3034 "alert('test');", // here
3035 "console.log('test');",
3036
3037 "/*eslint-enable no-console */",
3038
3039 "alert('test');", // here
3040 "console.log('test');", // here
3041 "/*eslint-enable no-console */"
3042 ].join("\n");
3043 const config = { rules: { "no-alert": "warn", "no-console": "warn" } };
3044
3045 const messages = linter.verify(code, config, filename);
3046 const suppressedMessages = linter.getSuppressedMessages();
3047
3048 assert.strictEqual(messages.length, 3);
3049 assert.strictEqual(messages[0].ruleId, "no-alert");
3050 assert.strictEqual(messages[0].line, 5);
3051 assert.strictEqual(messages[1].ruleId, "no-alert");
3052 assert.strictEqual(messages[1].line, 8);
3053 assert.strictEqual(messages[2].ruleId, "no-console");
3054 assert.strictEqual(messages[2].line, 9);
3055
3056 assert.strictEqual(suppressedMessages.length, 3);
3057 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
3058 assert.strictEqual(suppressedMessages[0].line, 2);
3059 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
3060 assert.strictEqual(suppressedMessages[1].line, 3);
3061 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
3062 assert.strictEqual(suppressedMessages[2].line, 6);
3063 });
3064 });
3065
3066 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
3067 const code = "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
3068
3069 it("should report a violation", () => {
3070 const config = { rules: { "no-console": 1, "no-alert": 0 } };
3071
3072 const messages = linter.verify(code, config, filename);
3073 const suppressedMessages = linter.getSuppressedMessages();
3074
3075 assert.strictEqual(messages.length, 1);
3076 assert.strictEqual(messages[0].ruleId, "no-alert");
3077 assert.strictEqual(messages[0].message, "Unexpected alert.");
3078 assert.include(messages[0].nodeType, "CallExpression");
3079
3080 assert.strictEqual(suppressedMessages.length, 0);
3081 });
3082 });
3083
3084 describe("when evaluating code with comments to enable configurable rule", () => {
3085 const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');";
3086
3087 it("should report a violation", () => {
3088 const config = { rules: { quotes: [2, "single"] } };
3089
3090 const messages = linter.verify(code, config, filename);
3091 const suppressedMessages = linter.getSuppressedMessages();
3092
3093 assert.strictEqual(messages.length, 1);
3094 assert.strictEqual(messages[0].ruleId, "quotes");
3095 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
3096 assert.include(messages[0].nodeType, "Literal");
3097
3098 assert.strictEqual(suppressedMessages.length, 0);
3099 });
3100 });
3101
3102 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
3103 const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
3104
3105 it("should report a violation", () => {
3106 const config = { rules: { quotes: [2, "single"] } };
3107
3108 const messages = linter.verify(code, config, filename);
3109 const suppressedMessages = linter.getSuppressedMessages();
3110
3111 assert.strictEqual(messages.length, 1);
3112 assert.strictEqual(messages[0].ruleId, "quotes");
3113 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
3114 assert.include(messages[0].nodeType, "Literal");
3115
3116 assert.strictEqual(suppressedMessages.length, 0);
3117 });
3118 });
3119
3120 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
3121 it("should report a violation", () => {
3122 const code = "/*eslint no-alert:'1'*/ alert('test');";
3123
3124 const config = { rules: { "no-alert": 1 } };
3125
3126 const messages = linter.verify(code, config, filename);
3127 const suppressedMessages = linter.getSuppressedMessages();
3128
3129 assert.strictEqual(messages.length, 2);
3130
3131 /*
3132 * Incorrectly formatted comment threw error;
3133 * message from caught exception
3134 * may differ amongst UAs, so verifying
3135 * first part only as defined in the
3136 * parseJsonConfig function in lib/eslint.js
3137 */
3138 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":'1'':/u);
3139 assert.strictEqual(messages[0].line, 1);
3140 assert.strictEqual(messages[0].column, 1);
3141
3142 assert.strictEqual(messages[1].ruleId, "no-alert");
3143 assert.strictEqual(messages[1].message, "Unexpected alert.");
3144 assert.include(messages[1].nodeType, "CallExpression");
3145
3146 assert.strictEqual(suppressedMessages.length, 0);
3147 });
3148
3149 it("should report a violation", () => {
3150 const code = "/*eslint no-alert:abc*/ alert('test');";
3151
3152 const config = { rules: { "no-alert": 1 } };
3153
3154 const messages = linter.verify(code, config, filename);
3155 const suppressedMessages = linter.getSuppressedMessages();
3156
3157 assert.strictEqual(messages.length, 2);
3158
3159 /*
3160 * Incorrectly formatted comment threw error;
3161 * message from caught exception
3162 * may differ amongst UAs, so verifying
3163 * first part only as defined in the
3164 * parseJsonConfig function in lib/eslint.js
3165 */
3166 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":abc':/u);
3167 assert.strictEqual(messages[0].line, 1);
3168 assert.strictEqual(messages[0].column, 1);
3169
3170 assert.strictEqual(messages[1].ruleId, "no-alert");
3171 assert.strictEqual(messages[1].message, "Unexpected alert.");
3172 assert.include(messages[1].nodeType, "CallExpression");
3173
3174 assert.strictEqual(suppressedMessages.length, 0);
3175 });
3176
3177 it("should report a violation", () => {
3178 const code = "/*eslint no-alert:0 2*/ alert('test');";
3179
3180 const config = { rules: { "no-alert": 1 } };
3181
3182 const messages = linter.verify(code, config, filename);
3183 const suppressedMessages = linter.getSuppressedMessages();
3184
3185 assert.strictEqual(messages.length, 2);
3186
3187 /*
3188 * Incorrectly formatted comment threw error;
3189 * message from caught exception
3190 * may differ amongst UAs, so verifying
3191 * first part only as defined in the
3192 * parseJsonConfig function in lib/eslint.js
3193 */
3194 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":0 2':/u);
3195 assert.strictEqual(messages[0].line, 1);
3196 assert.strictEqual(messages[0].column, 1);
3197
3198 assert.strictEqual(messages[1].ruleId, "no-alert");
3199 assert.strictEqual(messages[1].message, "Unexpected alert.");
3200 assert.include(messages[1].nodeType, "CallExpression");
3201
3202 assert.strictEqual(suppressedMessages.length, 0);
3203 });
3204 });
3205
3206 describe("when evaluating code with comments which have colon in its value", () => {
3207 const code = String.raw`
3208 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
3209 alert('test');
3210 `;
3211
3212 it("should not parse errors, should report a violation", () => {
3213 const messages = linter.verify(code, {}, filename);
3214 const suppressedMessages = linter.getSuppressedMessages();
3215
3216 assert.strictEqual(messages.length, 1);
3217 assert.strictEqual(messages[0].ruleId, "max-len");
3218 assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100.");
3219 assert.include(messages[0].nodeType, "Program");
3220
3221 assert.strictEqual(suppressedMessages.length, 0);
3222 });
3223 });
3224
3225 describe("when evaluating code with comments that contain escape sequences", () => {
3226 const code = String.raw`
3227 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
3228 console.log("test");
3229 consolexlog("test2");
3230 var a = "test2";
3231 `;
3232
3233 it("should validate correctly", () => {
3234 const config = { rules: {} };
3235 const messages = linter.verify(code, config, filename);
3236 const suppressedMessages = linter.getSuppressedMessages();
3237 const [message1, message2] = messages;
3238
3239 assert.strictEqual(messages.length, 2);
3240 assert.strictEqual(message1.ruleId, "max-len");
3241 assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1.");
3242 assert.strictEqual(message1.line, 4);
3243 assert.strictEqual(message1.column, 1);
3244 assert.include(message1.nodeType, "Program");
3245 assert.strictEqual(message2.ruleId, "max-len");
3246 assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1.");
3247 assert.strictEqual(message2.line, 5);
3248 assert.strictEqual(message2.column, 1);
3249 assert.include(message2.nodeType, "Program");
3250
3251 assert.strictEqual(suppressedMessages.length, 0);
3252 });
3253 });
3254
3255 describe("when evaluating a file with a hashbang", () => {
3256
3257 it("should preserve line numbers", () => {
3258 const code = "#!bin/program\n\nvar foo;;";
3259 const config = { rules: { "no-extra-semi": 1 } };
3260 const messages = linter.verify(code, config);
3261 const suppressedMessages = linter.getSuppressedMessages();
3262
3263 assert.strictEqual(messages.length, 1);
3264 assert.strictEqual(messages[0].ruleId, "no-extra-semi");
3265 assert.strictEqual(messages[0].nodeType, "EmptyStatement");
3266 assert.strictEqual(messages[0].line, 3);
3267
3268 assert.strictEqual(suppressedMessages.length, 0);
3269 });
3270
3271 it("should have a comment with the hashbang in it", () => {
3272 const code = "#!bin/program\n\nvar foo;;";
3273 const config = { rules: { checker: "error" } };
3274 const spy = sinon.spy(context => {
3275 const comments = context.getAllComments();
3276
3277 assert.strictEqual(comments.length, 1);
3278 assert.strictEqual(comments[0].type, "Shebang");
3279 return {};
3280 });
3281
3282 linter.defineRule("checker", spy);
3283 linter.verify(code, config);
3284 assert(spy.calledOnce);
3285 });
3286
3287 it("should comment hashbang without breaking offset", () => {
3288 const code = "#!/usr/bin/env node\n'123';";
3289 const config = { rules: { checker: "error" } };
3290 let spy;
3291
3292 linter.defineRule("checker", context => {
3293 spy = sinon.spy(node => {
3294 assert.strictEqual(context.getSource(node), "'123';");
3295 });
3296 return { ExpressionStatement: spy };
3297 });
3298
3299 linter.verify(code, config);
3300 assert(spy && spy.calledOnce);
3301 });
3302
3303 });
3304
3305 describe("when evaluating broken code", () => {
3306 const code = BROKEN_TEST_CODE;
3307
3308 it("should report a violation with a useful parse error prefix", () => {
3309 const messages = linter.verify(code);
3310 const suppressedMessages = linter.getSuppressedMessages();
3311
3312 assert.strictEqual(messages.length, 1);
3313 assert.strictEqual(messages[0].severity, 2);
3314 assert.isNull(messages[0].ruleId);
3315 assert.strictEqual(messages[0].line, 1);
3316 assert.strictEqual(messages[0].column, 4);
3317 assert.isTrue(messages[0].fatal);
3318 assert.match(messages[0].message, /^Parsing error:/u);
3319
3320 assert.strictEqual(suppressedMessages.length, 0);
3321 });
3322
3323 it("should report source code where the issue is present", () => {
3324 const inValidCode = [
3325 "var x = 20;",
3326 "if (x ==4 {",
3327 " x++;",
3328 "}"
3329 ];
3330 const messages = linter.verify(inValidCode.join("\n"));
3331 const suppressedMessages = linter.getSuppressedMessages();
3332
3333 assert.strictEqual(messages.length, 1);
3334 assert.strictEqual(messages[0].severity, 2);
3335 assert.isTrue(messages[0].fatal);
3336 assert.match(messages[0].message, /^Parsing error:/u);
3337
3338 assert.strictEqual(suppressedMessages.length, 0);
3339 });
3340 });
3341
3342 describe("when using an invalid (undefined) rule", () => {
3343 linter = new Linter();
3344
3345 const code = TEST_CODE;
3346 let results, result, warningResult, arrayOptionResults, objectOptionResults, resultsMultiple;
3347
3348 beforeEach(() => {
3349 results = linter.verify(code, { rules: { foobar: 2 } });
3350 result = results[0];
3351 warningResult = linter.verify(code, { rules: { foobar: 1 } })[0];
3352 arrayOptionResults = linter.verify(code, { rules: { foobar: [2, "always"] } });
3353 objectOptionResults = linter.verify(code, { rules: { foobar: [1, { bar: false }] } });
3354 resultsMultiple = linter.verify(code, { rules: { foobar: 2, barfoo: 1 } });
3355 });
3356
3357 it("should report a problem", () => {
3358 assert.isNotNull(result);
3359 assert.isArray(results);
3360 assert.isObject(result);
3361 assert.property(result, "ruleId");
3362 assert.strictEqual(result.ruleId, "foobar");
3363 });
3364
3365 it("should report that the rule does not exist", () => {
3366 assert.property(result, "message");
3367 assert.strictEqual(result.message, "Definition for rule 'foobar' was not found.");
3368 });
3369
3370 it("should report at the correct severity", () => {
3371 assert.property(result, "severity");
3372 assert.strictEqual(result.severity, 2);
3373 assert.strictEqual(warningResult.severity, 2); // this is 2, since the rulename is very likely to be wrong
3374 });
3375
3376 it("should accept any valid rule configuration", () => {
3377 assert.isObject(arrayOptionResults[0]);
3378 assert.isObject(objectOptionResults[0]);
3379 });
3380
3381 it("should report multiple missing rules", () => {
3382 assert.isArray(resultsMultiple);
3383
3384 assert.deepStrictEqual(
3385 resultsMultiple[1],
3386 {
3387 ruleId: "barfoo",
3388 message: "Definition for rule 'barfoo' was not found.",
3389 line: 1,
3390 column: 1,
3391 endLine: 1,
3392 endColumn: 2,
3393 severity: 2,
3394 nodeType: null
3395 }
3396 );
3397 });
3398 });
3399
3400 describe("when using a rule which has been replaced", () => {
3401 const code = TEST_CODE;
3402
3403 it("should report the new rule", () => {
3404 const results = linter.verify(code, { rules: { "no-comma-dangle": 2 } });
3405 const suppressedMessages = linter.getSuppressedMessages();
3406
3407 assert.strictEqual(results[0].ruleId, "no-comma-dangle");
3408 assert.strictEqual(results[0].message, "Rule 'no-comma-dangle' was removed and replaced by: comma-dangle");
3409
3410 assert.strictEqual(suppressedMessages.length, 0);
3411 });
3412 });
3413
3414 describe("when calling getRules", () => {
3415 it("should return all loaded rules", () => {
3416 const rules = linter.getRules();
3417
3418 assert.isAbove(rules.size, 230);
3419 assert.isObject(rules.get("no-alert"));
3420 });
3421 });
3422
3423 describe("when calling version", () => {
3424 it("should return current version number", () => {
3425 const version = linter.version;
3426
3427 assert.isString(version);
3428 assert.isTrue(parseInt(version[0], 10) >= 3);
3429 });
3430 });
3431
3432 describe("when evaluating an empty string", () => {
3433 it("runs rules", () => {
3434 linter.defineRule("no-programs", context => ({
3435 Program(node) {
3436 context.report({ node, message: "No programs allowed." });
3437 }
3438 }));
3439
3440 assert.strictEqual(
3441 linter.verify("", { rules: { "no-programs": "error" } }).length,
3442 1
3443 );
3444 });
3445 });
3446
3447 describe("when evaluating code without comments to environment", () => {
3448 it("should report a violation when using typed array", () => {
3449 const code = "var array = new Uint8Array();";
3450
3451 const config = { rules: { "no-undef": 1 } };
3452
3453 const messages = linter.verify(code, config, filename);
3454 const suppressedMessages = linter.getSuppressedMessages();
3455
3456 assert.strictEqual(messages.length, 1);
3457 assert.strictEqual(suppressedMessages.length, 0);
3458 });
3459
3460 it("should report a violation when using Promise", () => {
3461 const code = "new Promise();";
3462
3463 const config = { rules: { "no-undef": 1 } };
3464
3465 const messages = linter.verify(code, config, filename);
3466 const suppressedMessages = linter.getSuppressedMessages();
3467
3468 assert.strictEqual(messages.length, 1);
3469 assert.strictEqual(suppressedMessages.length, 0);
3470 });
3471 });
3472
3473 describe("when evaluating code with comments to environment", () => {
3474 it("should not support legacy config", () => {
3475 const code = "/*jshint mocha:true */ describe();";
3476
3477 const config = { rules: { "no-undef": 1 } };
3478
3479 const messages = linter.verify(code, config, filename);
3480 const suppressedMessages = linter.getSuppressedMessages();
3481
3482 assert.strictEqual(messages.length, 1);
3483 assert.strictEqual(messages[0].ruleId, "no-undef");
3484 assert.strictEqual(messages[0].nodeType, "Identifier");
3485 assert.strictEqual(messages[0].line, 1);
3486
3487 assert.strictEqual(suppressedMessages.length, 0);
3488 });
3489
3490 it("should not report a violation", () => {
3491 const code = "/*eslint-env es6 */ new Promise();";
3492
3493 const config = { rules: { "no-undef": 1 } };
3494
3495 const messages = linter.verify(code, config, filename);
3496 const suppressedMessages = linter.getSuppressedMessages();
3497
3498 assert.strictEqual(messages.length, 0);
3499 assert.strictEqual(suppressedMessages.length, 0);
3500 });
3501
3502 // https://github.com/eslint/eslint/issues/14652
3503 it("should not report a violation", () => {
3504 const codes = [
3505 "/*eslint-env es6\n */ new Promise();",
3506 "/*eslint-env browser,\nes6 */ window;Promise;",
3507 "/*eslint-env\nbrowser,es6 */ window;Promise;"
3508 ];
3509 const config = { rules: { "no-undef": 1 } };
3510
3511 for (const code of codes) {
3512 const messages = linter.verify(code, config, filename);
3513 const suppressedMessages = linter.getSuppressedMessages();
3514
3515 assert.strictEqual(messages.length, 0);
3516 assert.strictEqual(suppressedMessages.length, 0);
3517 }
3518
3519 });
3520
3521 it("should not report a violation", () => {
3522 const code = `/*${ESLINT_ENV} mocha,node */ require();describe();`;
3523
3524 const config = { rules: { "no-undef": 1 } };
3525
3526 const messages = linter.verify(code, config, filename);
3527 const suppressedMessages = linter.getSuppressedMessages();
3528
3529 assert.strictEqual(messages.length, 0);
3530 assert.strictEqual(suppressedMessages.length, 0);
3531 });
3532
3533 it("should not report a violation", () => {
3534 const code = "/*eslint-env mocha */ suite();test();";
3535
3536 const config = { rules: { "no-undef": 1 } };
3537
3538 const messages = linter.verify(code, config, filename);
3539 const suppressedMessages = linter.getSuppressedMessages();
3540
3541 assert.strictEqual(messages.length, 0);
3542 assert.strictEqual(suppressedMessages.length, 0);
3543 });
3544
3545 it("should not report a violation", () => {
3546 const code = `/*${ESLINT_ENV} amd */ define();require();`;
3547
3548 const config = { rules: { "no-undef": 1 } };
3549
3550 const messages = linter.verify(code, config, filename);
3551 const suppressedMessages = linter.getSuppressedMessages();
3552
3553 assert.strictEqual(messages.length, 0);
3554 assert.strictEqual(suppressedMessages.length, 0);
3555 });
3556
3557 it("should not report a violation", () => {
3558 const code = `/*${ESLINT_ENV} jasmine */ expect();spyOn();`;
3559
3560 const config = { rules: { "no-undef": 1 } };
3561
3562 const messages = linter.verify(code, config, filename);
3563 const suppressedMessages = linter.getSuppressedMessages();
3564
3565 assert.strictEqual(messages.length, 0);
3566 assert.strictEqual(suppressedMessages.length, 0);
3567 });
3568
3569 it("should not report a violation", () => {
3570 const code = `/*globals require: true */ /*${ESLINT_ENV} node */ require = 1;`;
3571
3572 const config = { rules: { "no-undef": 1 } };
3573
3574 const messages = linter.verify(code, config, filename);
3575 const suppressedMessages = linter.getSuppressedMessages();
3576
3577 assert.strictEqual(messages.length, 0);
3578 assert.strictEqual(suppressedMessages.length, 0);
3579 });
3580
3581 it("should not report a violation", () => {
3582 const code = `/*${ESLINT_ENV} node */ process.exit();`;
3583
3584 const config = { rules: {} };
3585
3586 const messages = linter.verify(code, config, filename);
3587 const suppressedMessages = linter.getSuppressedMessages();
3588
3589 assert.strictEqual(messages.length, 0);
3590 assert.strictEqual(suppressedMessages.length, 0);
3591 });
3592
3593 it("should not report a violation", () => {
3594 const code = `/*eslint no-process-exit: 0 */ /*${ESLINT_ENV} node */ process.exit();`;
3595
3596 const config = { rules: { "no-undef": 1 } };
3597
3598 const messages = linter.verify(code, config, filename);
3599 const suppressedMessages = linter.getSuppressedMessages();
3600
3601 assert.strictEqual(messages.length, 0);
3602 assert.strictEqual(suppressedMessages.length, 0);
3603 });
3604 });
3605
3606 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
3607 it("should report a violation for disabling rules", () => {
3608 const code = [
3609 "alert('test'); // eslint-disable-line no-alert"
3610 ].join("\n");
3611 const config = {
3612 rules: {
3613 "no-alert": 1
3614 }
3615 };
3616
3617 const messages = linter.verify(code, config, {
3618 filename,
3619 allowInlineConfig: false
3620 });
3621 const suppressedMessages = linter.getSuppressedMessages();
3622
3623 assert.strictEqual(messages.length, 1);
3624 assert.strictEqual(messages[0].ruleId, "no-alert");
3625
3626 assert.strictEqual(suppressedMessages.length, 0);
3627 });
3628
3629 it("should report a violation for global variable declarations", () => {
3630 const code = [
3631 "/* global foo */"
3632 ].join("\n");
3633 const config = {
3634 rules: {
3635 test: 2
3636 }
3637 };
3638 let ok = false;
3639
3640 linter.defineRules({
3641 test(context) {
3642 return {
3643 Program() {
3644 const scope = context.getScope();
3645 const sourceCode = context.getSourceCode();
3646 const comments = sourceCode.getAllComments();
3647
3648 assert.strictEqual(1, comments.length);
3649
3650 const foo = getVariable(scope, "foo");
3651
3652 assert.notOk(foo);
3653
3654 ok = true;
3655 }
3656 };
3657 }
3658 });
3659
3660 linter.verify(code, config, { allowInlineConfig: false });
3661 assert(ok);
3662 });
3663
3664 it("should report a violation for eslint-disable", () => {
3665 const code = [
3666 "/* eslint-disable */",
3667 "alert('test');"
3668 ].join("\n");
3669 const config = {
3670 rules: {
3671 "no-alert": 1
3672 }
3673 };
3674
3675 const messages = linter.verify(code, config, {
3676 filename,
3677 allowInlineConfig: false
3678 });
3679 const suppressedMessages = linter.getSuppressedMessages();
3680
3681 assert.strictEqual(messages.length, 1);
3682 assert.strictEqual(messages[0].ruleId, "no-alert");
3683
3684 assert.strictEqual(suppressedMessages.length, 0);
3685 });
3686
3687 it("should not report a violation for rule changes", () => {
3688 const code = [
3689 "/*eslint no-alert:2*/",
3690 "alert('test');"
3691 ].join("\n");
3692 const config = {
3693 rules: {
3694 "no-alert": 0
3695 }
3696 };
3697
3698 const messages = linter.verify(code, config, {
3699 filename,
3700 allowInlineConfig: false
3701 });
3702 const suppressedMessages = linter.getSuppressedMessages();
3703
3704 assert.strictEqual(messages.length, 0);
3705 assert.strictEqual(suppressedMessages.length, 0);
3706 });
3707
3708 it("should report a violation for disable-line", () => {
3709 const code = [
3710 "alert('test'); // eslint-disable-line"
3711 ].join("\n");
3712 const config = {
3713 rules: {
3714 "no-alert": 2
3715 }
3716 };
3717
3718 const messages = linter.verify(code, config, {
3719 filename,
3720 allowInlineConfig: false
3721 });
3722 const suppressedMessages = linter.getSuppressedMessages();
3723
3724 assert.strictEqual(messages.length, 1);
3725 assert.strictEqual(messages[0].ruleId, "no-alert");
3726
3727 assert.strictEqual(suppressedMessages.length, 0);
3728 });
3729
3730 it("should report a violation for env changes", () => {
3731 const code = [
3732 `/*${ESLINT_ENV} browser*/ window`
3733 ].join("\n");
3734 const config = {
3735 rules: {
3736 "no-undef": 2
3737 }
3738 };
3739 const messages = linter.verify(code, config, { allowInlineConfig: false });
3740 const suppressedMessages = linter.getSuppressedMessages();
3741
3742 assert.strictEqual(messages.length, 1);
3743 assert.strictEqual(messages[0].ruleId, "no-undef");
3744
3745 assert.strictEqual(suppressedMessages.length, 0);
3746 });
3747 });
3748
3749 describe("when evaluating code with 'noInlineComment'", () => {
3750 for (const directive of [
3751 "globals foo",
3752 "global foo",
3753 "exported foo",
3754 "eslint eqeqeq: error",
3755 "eslint-disable eqeqeq",
3756 "eslint-disable-line eqeqeq",
3757 "eslint-disable-next-line eqeqeq",
3758 "eslint-enable eqeqeq",
3759 "eslint-env es6"
3760 ]) {
3761 // eslint-disable-next-line no-loop-func -- No closures
3762 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
3763 const messages = linter.verify(`/* ${directive} */`, { noInlineConfig: true });
3764 const suppressedMessages = linter.getSuppressedMessages();
3765
3766 assert.deepStrictEqual(messages.length, 1);
3767 assert.deepStrictEqual(messages[0].fatal, void 0);
3768 assert.deepStrictEqual(messages[0].ruleId, null);
3769 assert.deepStrictEqual(messages[0].severity, 1);
3770 assert.deepStrictEqual(messages[0].message, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
3771
3772 assert.strictEqual(suppressedMessages.length, 0);
3773 });
3774 }
3775
3776 for (const directive of [
3777 "eslint-disable-line eqeqeq",
3778 "eslint-disable-next-line eqeqeq"
3779 ]) {
3780 // eslint-disable-next-line no-loop-func -- No closures
3781 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
3782 const messages = linter.verify(`// ${directive}`, { noInlineConfig: true });
3783 const suppressedMessages = linter.getSuppressedMessages();
3784
3785 assert.deepStrictEqual(messages.length, 1);
3786 assert.deepStrictEqual(messages[0].fatal, void 0);
3787 assert.deepStrictEqual(messages[0].ruleId, null);
3788 assert.deepStrictEqual(messages[0].severity, 1);
3789 assert.deepStrictEqual(messages[0].message, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
3790
3791 assert.strictEqual(suppressedMessages.length, 0);
3792 });
3793 }
3794
3795 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
3796 const messages = linter.verify("/* globals foo */", { noInlineConfig: true }, { allowInlineConfig: false });
3797 const suppressedMessages = linter.getSuppressedMessages();
3798
3799 assert.deepStrictEqual(messages.length, 0);
3800 assert.strictEqual(suppressedMessages.length, 0);
3801 });
3802 });
3803
3804 describe("when receiving cwd in options during instantiation", () => {
3805 const code = "a;\nb;";
3806 const config = { rules: { checker: "error" } };
3807
3808 it("should get cwd correctly in the context", () => {
3809 const cwd = "cwd";
3810 const linterWithOption = new Linter({ cwd });
3811 let spy;
3812
3813 linterWithOption.defineRule("checker", context => {
3814 spy = sinon.spy(() => {
3815 assert.strictEqual(context.getCwd(), cwd);
3816 });
3817 return { Program: spy };
3818 });
3819
3820 linterWithOption.verify(code, config);
3821 assert(spy && spy.calledOnce);
3822 });
3823
3824 it("should assign process.cwd() to it if cwd is undefined", () => {
3825 let spy;
3826 const linterWithOption = new Linter({ });
3827
3828 linterWithOption.defineRule("checker", context => {
3829
3830 spy = sinon.spy(() => {
3831 assert.strictEqual(context.getCwd(), process.cwd());
3832 });
3833 return { Program: spy };
3834 });
3835
3836 linterWithOption.verify(code, config);
3837 assert(spy && spy.calledOnce);
3838 });
3839
3840 it("should assign process.cwd() to it if the option is undefined", () => {
3841 let spy;
3842
3843 linter.defineRule("checker", context => {
3844
3845 spy = sinon.spy(() => {
3846 assert.strictEqual(context.getCwd(), process.cwd());
3847 });
3848 return { Program: spy };
3849 });
3850
3851 linter.verify(code, config);
3852 assert(spy && spy.calledOnce);
3853 });
3854 });
3855
3856 describe("reportUnusedDisable option", () => {
3857 it("reports problems for unused eslint-disable comments", () => {
3858 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: true });
3859 const suppressedMessages = linter.getSuppressedMessages();
3860
3861 assert.deepStrictEqual(
3862 messages,
3863 [
3864 {
3865 ruleId: null,
3866 message: "Unused eslint-disable directive (no problems were reported).",
3867 line: 1,
3868 column: 1,
3869 fix: {
3870 range: [0, 20],
3871 text: " "
3872 },
3873 severity: 2,
3874 nodeType: null
3875 }
3876 ]
3877 );
3878
3879 assert.strictEqual(suppressedMessages.length, 0);
3880 });
3881
3882 it("reports problems for multiple eslint-disable comments, including unused ones", () => {
3883 const code = [
3884 "/* eslint-disable no-alert -- j1 */",
3885 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
3886 ].join("\n");
3887 const config = {
3888 rules: {
3889 "no-alert": 2
3890 }
3891 };
3892 const messages = linter.verify(code, config, { reportUnusedDisableDirectives: true });
3893 const suppressedMessages = linter.getSuppressedMessages();
3894
3895 assert.strictEqual(messages.length, 1);
3896 assert.strictEqual(suppressedMessages.length, 1);
3897 assert.strictEqual(suppressedMessages[0].suppressions.length, 2);
3898 });
3899
3900 it("reports problems for eslint-disable-line and eslint-disable-next-line comments, including unused ones", () => {
3901 const code = [
3902 "// eslint-disable-next-line no-alert -- j1 */",
3903 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
3904 ].join("\n");
3905 const config = {
3906 rules: {
3907 "no-alert": 2
3908 }
3909 };
3910 const messages = linter.verify(code, config, { reportUnusedDisableDirectives: true });
3911 const suppressedMessages = linter.getSuppressedMessages();
3912
3913 assert.strictEqual(messages.length, 1);
3914 assert.strictEqual(suppressedMessages.length, 1);
3915 assert.strictEqual(suppressedMessages[0].suppressions.length, 2);
3916 });
3917
3918 it("reports problems for multiple unused eslint-disable comments with multiple ruleIds", () => {
3919 const code = [
3920 "/* eslint no-undef: 2, no-void: 2 */",
3921 "/* eslint-disable no-undef -- j1 */",
3922 "void foo; //eslint-disable-line no-undef, no-void -- j2"
3923 ].join("\n");
3924 const config = {
3925 rules: {
3926 "no-undef": 2,
3927 "no-void": 2
3928 }
3929 };
3930 const messages = linter.verify(code, config, { reportUnusedDisableDirectives: true });
3931 const suppressedMessages = linter.getSuppressedMessages();
3932
3933 assert.strictEqual(messages.length, 1);
3934 assert.strictEqual(suppressedMessages.length, 2);
3935 assert.strictEqual(suppressedMessages[0].ruleId, "no-void");
3936 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
3937 assert.strictEqual(suppressedMessages[1].ruleId, "no-undef");
3938 assert.strictEqual(suppressedMessages[1].suppressions.length, 2);
3939 });
3940
3941 it("reports problems for unused eslint-disable comments (error)", () => {
3942 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "error" });
3943 const suppressedMessages = linter.getSuppressedMessages();
3944
3945 assert.deepStrictEqual(
3946 messages,
3947 [
3948 {
3949 ruleId: null,
3950 message: "Unused eslint-disable directive (no problems were reported).",
3951 line: 1,
3952 column: 1,
3953 fix: {
3954 range: [0, 20],
3955 text: " "
3956 },
3957 severity: 2,
3958 nodeType: null
3959 }
3960 ]
3961 );
3962
3963 assert.strictEqual(suppressedMessages.length, 0);
3964 });
3965
3966 it("reports problems for unused eslint-disable comments (warn)", () => {
3967 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "warn" });
3968 const suppressedMessages = linter.getSuppressedMessages();
3969
3970 assert.deepStrictEqual(
3971 messages,
3972 [
3973 {
3974 ruleId: null,
3975 message: "Unused eslint-disable directive (no problems were reported).",
3976 line: 1,
3977 column: 1,
3978 fix: {
3979 range: [0, 20],
3980 text: " "
3981 },
3982 severity: 1,
3983 nodeType: null
3984 }
3985 ]
3986 );
3987
3988 assert.strictEqual(suppressedMessages.length, 0);
3989 });
3990
3991 it("reports problems for unused eslint-disable comments (in config)", () => {
3992 const messages = linter.verify("/* eslint-disable */", { reportUnusedDisableDirectives: true });
3993 const suppressedMessages = linter.getSuppressedMessages();
3994
3995 assert.deepStrictEqual(
3996 messages,
3997 [
3998 {
3999 ruleId: null,
4000 message: "Unused eslint-disable directive (no problems were reported).",
4001 line: 1,
4002 column: 1,
4003 fix: {
4004 range: [0, 20],
4005 text: " "
4006 },
4007 severity: 1,
4008 nodeType: null
4009 }
4010 ]
4011 );
4012
4013 assert.strictEqual(suppressedMessages.length, 0);
4014 });
4015
4016 it("reports problems for partially unused eslint-disable comments (in config)", () => {
4017 const code = "alert('test'); // eslint-disable-line no-alert, no-redeclare";
4018 const config = {
4019 reportUnusedDisableDirectives: true,
4020 rules: {
4021 "no-alert": 1,
4022 "no-redeclare": 1
4023 }
4024 };
4025
4026 const messages = linter.verify(code, config, {
4027 filename,
4028 allowInlineConfig: true
4029 });
4030 const suppressedMessages = linter.getSuppressedMessages();
4031
4032 assert.deepStrictEqual(
4033 messages,
4034 [
4035 {
4036 ruleId: null,
4037 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
4038 line: 1,
4039 column: 16,
4040 fix: {
4041 range: [46, 60],
4042 text: ""
4043 },
4044 severity: 1,
4045 nodeType: null
4046 }
4047 ]
4048 );
4049
4050 assert.strictEqual(suppressedMessages.length, 1);
4051 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
4052 });
4053
4054 describe("autofix", () => {
4055 const alwaysReportsRule = {
4056 create(context) {
4057 return {
4058 Program(node) {
4059 context.report({ message: "bad code", loc: node.loc.end });
4060 }
4061 };
4062 }
4063 };
4064
4065 const neverReportsRule = {
4066 create() {
4067 return {};
4068 }
4069 };
4070
4071 const ruleCount = 3;
4072 const usedRules = Array.from(
4073 { length: ruleCount },
4074 (_, index) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
4075 );
4076 const unusedRules = usedRules.map(name => `un${name}`); // "unused", "unused-1", "unused-2"
4077
4078 const config = {
4079 reportUnusedDisableDirectives: true,
4080 rules: {
4081 ...Object.fromEntries(usedRules.map(name => [name, "error"])),
4082 ...Object.fromEntries(unusedRules.map(name => [name, "error"]))
4083 }
4084 };
4085
4086 beforeEach(() => {
4087 linter.defineRules(Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])));
4088 linter.defineRules(Object.fromEntries(unusedRules.map(name => [name, neverReportsRule])));
4089 });
4090
4091 const tests = [
4092
4093 //-----------------------------------------------
4094 // Removing the entire comment
4095 //-----------------------------------------------
4096
4097 {
4098 code: "// eslint-disable-line unused",
4099 output: " "
4100 },
4101 {
4102 code: "foo// eslint-disable-line unused",
4103 output: "foo "
4104 },
4105 {
4106 code: "// eslint-disable-line ,unused,",
4107 output: " "
4108 },
4109 {
4110 code: "// eslint-disable-line unused-1, unused-2",
4111 output: " "
4112 },
4113 {
4114 code: "// eslint-disable-line ,unused-1,, unused-2,, -- comment",
4115 output: " "
4116 },
4117 {
4118 code: "// eslint-disable-next-line unused\n",
4119 output: " \n"
4120 },
4121 {
4122 code: "// eslint-disable-next-line unused\nfoo",
4123 output: " \nfoo"
4124 },
4125 {
4126 code: "/* eslint-disable \nunused\n*/",
4127 output: " "
4128 },
4129
4130 //-----------------------------------------------
4131 // Removing only individual rules
4132 //-----------------------------------------------
4133
4134 // content before the first rule should not be changed
4135 {
4136 code: "//eslint-disable-line unused, used",
4137 output: "//eslint-disable-line used"
4138 },
4139 {
4140 code: "// eslint-disable-line unused, used",
4141 output: "// eslint-disable-line used"
4142 },
4143 {
4144 code: "// eslint-disable-line unused, used",
4145 output: "// eslint-disable-line used"
4146 },
4147 {
4148 code: "/*\neslint-disable unused, used*/",
4149 output: "/*\neslint-disable used*/"
4150 },
4151 {
4152 code: "/*\n eslint-disable unused, used*/",
4153 output: "/*\n eslint-disable used*/"
4154 },
4155 {
4156 code: "/*\r\neslint-disable unused, used*/",
4157 output: "/*\r\neslint-disable used*/"
4158 },
4159 {
4160 code: "/*\u2028eslint-disable unused, used*/",
4161 output: "/*\u2028eslint-disable used*/"
4162 },
4163 {
4164 code: "/*\u00A0eslint-disable unused, used*/",
4165 output: "/*\u00A0eslint-disable used*/"
4166 },
4167 {
4168 code: "// eslint-disable-line unused, used",
4169 output: "// eslint-disable-line used"
4170 },
4171 {
4172 code: "/* eslint-disable\nunused, used*/",
4173 output: "/* eslint-disable\nused*/"
4174 },
4175 {
4176 code: "/* eslint-disable\n unused, used*/",
4177 output: "/* eslint-disable\n used*/"
4178 },
4179 {
4180 code: "/* eslint-disable\r\nunused, used*/",
4181 output: "/* eslint-disable\r\nused*/"
4182 },
4183 {
4184 code: "/* eslint-disable\u2028unused, used*/",
4185 output: "/* eslint-disable\u2028used*/"
4186 },
4187 {
4188 code: "/* eslint-disable\u00A0unused, used*/",
4189 output: "/* eslint-disable\u00A0used*/"
4190 },
4191
4192 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
4193 {
4194 code: "// eslint-disable-line unused,used",
4195 output: "// eslint-disable-line used"
4196 },
4197 {
4198 code: "// eslint-disable-line unused, used",
4199 output: "// eslint-disable-line used"
4200 },
4201 {
4202 code: "// eslint-disable-line unused , used",
4203 output: "// eslint-disable-line used"
4204 },
4205 {
4206 code: "// eslint-disable-line unused, used",
4207 output: "// eslint-disable-line used"
4208 },
4209 {
4210 code: "// eslint-disable-line unused ,used",
4211 output: "// eslint-disable-line used"
4212 },
4213 {
4214 code: "/* eslint-disable unused\n,\nused */",
4215 output: "/* eslint-disable used */"
4216 },
4217 {
4218 code: "/* eslint-disable unused \n \n,\n\n used */",
4219 output: "/* eslint-disable used */"
4220 },
4221 {
4222 code: "/* eslint-disable unused\u2028,\u2028used */",
4223 output: "/* eslint-disable used */"
4224 },
4225 {
4226 code: "// eslint-disable-line unused\u00A0,\u00A0used",
4227 output: "// eslint-disable-line used"
4228 },
4229 {
4230 code: "// eslint-disable-line unused,,used",
4231 output: "// eslint-disable-line ,used"
4232 },
4233 {
4234 code: "// eslint-disable-line unused, ,used",
4235 output: "// eslint-disable-line ,used"
4236 },
4237 {
4238 code: "// eslint-disable-line unused,, used",
4239 output: "// eslint-disable-line , used"
4240 },
4241 {
4242 code: "// eslint-disable-line unused,used ",
4243 output: "// eslint-disable-line used "
4244 },
4245 {
4246 code: "// eslint-disable-next-line unused,used\n",
4247 output: "// eslint-disable-next-line used\n"
4248 },
4249
4250 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
4251 {
4252 code: "// eslint-disable-line used-1,unused,used-2",
4253 output: "// eslint-disable-line used-1,used-2"
4254 },
4255 {
4256 code: "// eslint-disable-line used-1, unused,used-2",
4257 output: "// eslint-disable-line used-1,used-2"
4258 },
4259 {
4260 code: "// eslint-disable-line used-1,unused ,used-2",
4261 output: "// eslint-disable-line used-1,used-2"
4262 },
4263 {
4264 code: "// eslint-disable-line used-1, unused ,used-2",
4265 output: "// eslint-disable-line used-1,used-2"
4266 },
4267 {
4268 code: "/* eslint-disable used-1,\nunused\n,used-2 */",
4269 output: "/* eslint-disable used-1,used-2 */"
4270 },
4271 {
4272 code: "/* eslint-disable used-1,\n\n unused \n \n ,used-2 */",
4273 output: "/* eslint-disable used-1,used-2 */"
4274 },
4275 {
4276 code: "/* eslint-disable used-1,\u2028unused\u2028,used-2 */",
4277 output: "/* eslint-disable used-1,used-2 */"
4278 },
4279 {
4280 code: "// eslint-disable-line used-1,\u00A0unused\u00A0,used-2",
4281 output: "// eslint-disable-line used-1,used-2"
4282 },
4283
4284 // when removing a rule in the middle, content around commas should not be changed
4285 {
4286 code: "// eslint-disable-line used-1, unused ,used-2",
4287 output: "// eslint-disable-line used-1,used-2"
4288 },
4289 {
4290 code: "// eslint-disable-line used-1,unused, used-2",
4291 output: "// eslint-disable-line used-1, used-2"
4292 },
4293 {
4294 code: "// eslint-disable-line used-1 ,unused,used-2",
4295 output: "// eslint-disable-line used-1 ,used-2"
4296 },
4297 {
4298 code: "// eslint-disable-line used-1 ,unused, used-2",
4299 output: "// eslint-disable-line used-1 , used-2"
4300 },
4301 {
4302 code: "// eslint-disable-line used-1 , unused , used-2",
4303 output: "// eslint-disable-line used-1 , used-2"
4304 },
4305 {
4306 code: "/* eslint-disable used-1\n,unused,\nused-2 */",
4307 output: "/* eslint-disable used-1\n,\nused-2 */"
4308 },
4309 {
4310 code: "/* eslint-disable used-1\u2028,unused,\u2028used-2 */",
4311 output: "/* eslint-disable used-1\u2028,\u2028used-2 */"
4312 },
4313 {
4314 code: "// eslint-disable-line used-1\u00A0,unused,\u00A0used-2",
4315 output: "// eslint-disable-line used-1\u00A0,\u00A0used-2"
4316 },
4317 {
4318 code: "// eslint-disable-line , unused ,used",
4319 output: "// eslint-disable-line ,used"
4320 },
4321 {
4322 code: "/* eslint-disable\n, unused ,used */",
4323 output: "/* eslint-disable\n,used */"
4324 },
4325 {
4326 code: "/* eslint-disable used-1,\n,unused,used-2 */",
4327 output: "/* eslint-disable used-1,\n,used-2 */"
4328 },
4329 {
4330 code: "/* eslint-disable used-1,unused,\n,used-2 */",
4331 output: "/* eslint-disable used-1,\n,used-2 */"
4332 },
4333 {
4334 code: "/* eslint-disable used-1,\n,unused,\n,used-2 */",
4335 output: "/* eslint-disable used-1,\n,\n,used-2 */"
4336 },
4337 {
4338 code: "// eslint-disable-line used, unused,",
4339 output: "// eslint-disable-line used,"
4340 },
4341 {
4342 code: "// eslint-disable-next-line used, unused,\n",
4343 output: "// eslint-disable-next-line used,\n"
4344 },
4345 {
4346 code: "// eslint-disable-line used, unused, ",
4347 output: "// eslint-disable-line used, "
4348 },
4349 {
4350 code: "// eslint-disable-line used, unused, -- comment",
4351 output: "// eslint-disable-line used, -- comment"
4352 },
4353 {
4354 code: "/* eslint-disable used, unused,\n*/",
4355 output: "/* eslint-disable used,\n*/"
4356 },
4357
4358 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
4359 {
4360 code: "// eslint-disable-line used,unused",
4361 output: "// eslint-disable-line used"
4362 },
4363 {
4364 code: "// eslint-disable-line used, unused",
4365 output: "// eslint-disable-line used"
4366 },
4367 {
4368 code: "// eslint-disable-line used ,unused",
4369 output: "// eslint-disable-line used"
4370 },
4371 {
4372 code: "// eslint-disable-line used , unused",
4373 output: "// eslint-disable-line used"
4374 },
4375 {
4376 code: "// eslint-disable-line used, unused",
4377 output: "// eslint-disable-line used"
4378 },
4379 {
4380 code: "// eslint-disable-line used ,unused",
4381 output: "// eslint-disable-line used"
4382 },
4383 {
4384 code: "/* eslint-disable used\n,\nunused */",
4385 output: "/* eslint-disable used */"
4386 },
4387 {
4388 code: "/* eslint-disable used \n \n,\n\n unused */",
4389 output: "/* eslint-disable used */"
4390 },
4391 {
4392 code: "/* eslint-disable used\u2028,\u2028unused */",
4393 output: "/* eslint-disable used */"
4394 },
4395 {
4396 code: "// eslint-disable-line used\u00A0,\u00A0unused",
4397 output: "// eslint-disable-line used"
4398 },
4399 {
4400 code: "// eslint-disable-line used,,unused",
4401 output: "// eslint-disable-line used,"
4402 },
4403 {
4404 code: "// eslint-disable-line used, ,unused",
4405 output: "// eslint-disable-line used,"
4406 },
4407 {
4408 code: "/* eslint-disable used,\n,unused */",
4409 output: "/* eslint-disable used, */"
4410 },
4411 {
4412 code: "/* eslint-disable used\n, ,unused */",
4413 output: "/* eslint-disable used\n, */"
4414 },
4415
4416 // content after the last rule should not be changed
4417 {
4418 code: "// eslint-disable-line used,unused",
4419 output: "// eslint-disable-line used"
4420 },
4421 {
4422 code: "// eslint-disable-line used,unused ",
4423 output: "// eslint-disable-line used "
4424 },
4425 {
4426 code: "// eslint-disable-line used,unused ",
4427 output: "// eslint-disable-line used "
4428 },
4429 {
4430 code: "// eslint-disable-line used,unused -- comment",
4431 output: "// eslint-disable-line used -- comment"
4432 },
4433 {
4434 code: "// eslint-disable-next-line used,unused\n",
4435 output: "// eslint-disable-next-line used\n"
4436 },
4437 {
4438 code: "// eslint-disable-next-line used,unused \n",
4439 output: "// eslint-disable-next-line used \n"
4440 },
4441 {
4442 code: "/* eslint-disable used,unused\u2028*/",
4443 output: "/* eslint-disable used\u2028*/"
4444 },
4445 {
4446 code: "// eslint-disable-line used,unused\u00A0",
4447 output: "// eslint-disable-line used\u00A0"
4448 },
4449
4450 // multiply rules to remove
4451 {
4452 code: "// eslint-disable-line used, unused-1, unused-2",
4453 output: "// eslint-disable-line used"
4454 },
4455 {
4456 code: "// eslint-disable-line unused-1, used, unused-2",
4457 output: "// eslint-disable-line used"
4458 },
4459 {
4460 code: "// eslint-disable-line unused-1, unused-2, used",
4461 output: "// eslint-disable-line used"
4462 },
4463 {
4464 code: "// eslint-disable-line used-1, unused-1, used-2, unused-2",
4465 output: "// eslint-disable-line used-1, used-2"
4466 },
4467 {
4468 code: "// eslint-disable-line unused-1, used-1, unused-2, used-2",
4469 output: "// eslint-disable-line used-1, used-2"
4470 },
4471 {
4472 code: `
4473 /* eslint-disable unused-1,
4474 used-1,
4475 unused-2,
4476 used-2
4477 */
4478 `,
4479 output: `
4480 /* eslint-disable used-1,
4481 used-2
4482 */
4483 `
4484 },
4485 {
4486 code: `
4487 /* eslint-disable
4488 unused-1,
4489 used-1,
4490 unused-2,
4491 used-2
4492 */
4493 `,
4494 output: `
4495 /* eslint-disable
4496 used-1,
4497 used-2
4498 */
4499 `
4500 },
4501 {
4502 code: `
4503 /* eslint-disable
4504 used-1,
4505 unused-1,
4506 used-2,
4507 unused-2
4508 */
4509 `,
4510 output: `
4511 /* eslint-disable
4512 used-1,
4513 used-2
4514 */
4515 `
4516 },
4517 {
4518 code: `
4519 /* eslint-disable
4520 used-1,
4521 unused-1,
4522 used-2,
4523 unused-2,
4524 */
4525 `,
4526 output: `
4527 /* eslint-disable
4528 used-1,
4529 used-2,
4530 */
4531 `
4532 },
4533 {
4534 code: `
4535 /* eslint-disable
4536 ,unused-1
4537 ,used-1
4538 ,unused-2
4539 ,used-2
4540 */
4541 `,
4542 output: `
4543 /* eslint-disable
4544 ,used-1
4545 ,used-2
4546 */
4547 `
4548 },
4549 {
4550 code: `
4551 /* eslint-disable
4552 ,used-1
4553 ,unused-1
4554 ,used-2
4555 ,unused-2
4556 */
4557 `,
4558 output: `
4559 /* eslint-disable
4560 ,used-1
4561 ,used-2
4562 */
4563 `
4564 },
4565 {
4566 code: `
4567 /* eslint-disable
4568 used-1,
4569 unused-1,
4570 used-2,
4571 unused-2
4572
4573 -- comment
4574 */
4575 `,
4576 output: `
4577 /* eslint-disable
4578 used-1,
4579 used-2
4580
4581 -- comment
4582 */
4583 `
4584 },
4585
4586 // duplicates in the list
4587 {
4588 code: "// eslint-disable-line unused, unused, used",
4589 output: "// eslint-disable-line used"
4590 },
4591 {
4592 code: "// eslint-disable-line unused, used, unused",
4593 output: "// eslint-disable-line used"
4594 },
4595 {
4596 code: "// eslint-disable-line used, unused, unused, used",
4597 output: "// eslint-disable-line used, used"
4598 }
4599 ];
4600
4601 for (const { code, output } of tests) {
4602 // eslint-disable-next-line no-loop-func -- `linter` is getting updated in beforeEach()
4603 it(code, () => {
4604 assert.strictEqual(
4605 linter.verifyAndFix(code, config).output,
4606 output
4607 );
4608 });
4609 }
4610 });
4611 });
4612
4613 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
4614 it("should not report a violation", () => {
4615 const code = [
4616 "alert('test'); // eslint-disable-line no-alert"
4617 ].join("\n");
4618 const config = {
4619 rules: {
4620 "no-alert": 1
4621 }
4622 };
4623
4624 const messages = linter.verify(code, config, {
4625 filename,
4626 allowInlineConfig: true
4627 });
4628 const suppressedMessages = linter.getSuppressedMessages();
4629
4630 assert.strictEqual(messages.length, 0);
4631
4632 assert.strictEqual(suppressedMessages.length, 1);
4633 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
4634 });
4635 });
4636
4637 describe("when evaluating code with hashbang", () => {
4638 it("should comment hashbang without breaking offset", () => {
4639 const code = "#!/usr/bin/env node\n'123';";
4640 const config = { rules: { checker: "error" } };
4641 let spy;
4642
4643 linter.defineRule("checker", context => {
4644 spy = sinon.spy(node => {
4645 assert.strictEqual(context.getSource(node), "'123';");
4646 });
4647 return { ExpressionStatement: spy };
4648 });
4649
4650 linter.verify(code, config);
4651 assert(spy && spy.calledOnce);
4652 });
4653 });
4654
4655 describe("verify()", () => {
4656 describe("filenames", () => {
4657 it("should allow filename to be passed on options object", () => {
4658 const filenameChecker = sinon.spy(context => {
4659 assert.strictEqual(context.getFilename(), "foo.js");
4660 return {};
4661 });
4662
4663 linter.defineRule("checker", filenameChecker);
4664 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4665 assert(filenameChecker.calledOnce);
4666 });
4667
4668 it("should allow filename to be passed as third argument", () => {
4669 const filenameChecker = sinon.spy(context => {
4670 assert.strictEqual(context.getFilename(), "bar.js");
4671 return {};
4672 });
4673
4674 linter.defineRule("checker", filenameChecker);
4675 linter.verify("foo;", { rules: { checker: "error" } }, "bar.js");
4676 assert(filenameChecker.calledOnce);
4677 });
4678
4679 it("should default filename to <input> when options object doesn't have filename", () => {
4680 const filenameChecker = sinon.spy(context => {
4681 assert.strictEqual(context.getFilename(), "<input>");
4682 return {};
4683 });
4684
4685 linter.defineRule("checker", filenameChecker);
4686 linter.verify("foo;", { rules: { checker: "error" } }, {});
4687 assert(filenameChecker.calledOnce);
4688 });
4689
4690 it("should default filename to <input> when only two arguments are passed", () => {
4691 const filenameChecker = sinon.spy(context => {
4692 assert.strictEqual(context.getFilename(), "<input>");
4693 return {};
4694 });
4695
4696 linter.defineRule("checker", filenameChecker);
4697 linter.verify("foo;", { rules: { checker: "error" } });
4698 assert(filenameChecker.calledOnce);
4699 });
4700 });
4701
4702 describe("physicalFilenames", () => {
4703 it("should be same as `filename` passed on options object, if no processors are used", () => {
4704 const physicalFilenameChecker = sinon.spy(context => {
4705 assert.strictEqual(context.getPhysicalFilename(), "foo.js");
4706 return {};
4707 });
4708
4709 linter.defineRule("checker", physicalFilenameChecker);
4710 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4711 assert(physicalFilenameChecker.calledOnce);
4712 });
4713
4714 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
4715 const physicalFilenameChecker = sinon.spy(context => {
4716 assert.strictEqual(context.getPhysicalFilename(), "<input>");
4717 return {};
4718 });
4719
4720 linter.defineRule("checker", physicalFilenameChecker);
4721 linter.verify("foo;", { rules: { checker: "error" } }, {});
4722 assert(physicalFilenameChecker.calledOnce);
4723 });
4724
4725 it("should default physicalFilename to <input> when only two arguments are passed", () => {
4726 const physicalFilenameChecker = sinon.spy(context => {
4727 assert.strictEqual(context.getPhysicalFilename(), "<input>");
4728 return {};
4729 });
4730
4731 linter.defineRule("checker", physicalFilenameChecker);
4732 linter.verify("foo;", { rules: { checker: "error" } });
4733 assert(physicalFilenameChecker.calledOnce);
4734 });
4735 });
4736
4737 it("should report warnings in order by line and column when called", () => {
4738
4739 const code = "foo()\n alert('test')";
4740 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
4741
4742 const messages = linter.verify(code, config, filename);
4743 const suppressedMessages = linter.getSuppressedMessages();
4744
4745 assert.strictEqual(messages.length, 3);
4746 assert.strictEqual(messages[0].line, 1);
4747 assert.strictEqual(messages[0].column, 6);
4748 assert.strictEqual(messages[1].line, 2);
4749 assert.strictEqual(messages[1].column, 18);
4750 assert.strictEqual(messages[2].line, 2);
4751 assert.strictEqual(messages[2].column, 18);
4752
4753 assert.strictEqual(suppressedMessages.length, 0);
4754 });
4755
4756 describe("ecmaVersion", () => {
4757
4758 it("should not support ES6 when no ecmaVersion provided", () => {
4759 const messages = linter.verify("let x = 0;");
4760 const suppressedMessages = linter.getSuppressedMessages();
4761
4762 assert.strictEqual(messages.length, 1);
4763 assert.strictEqual(suppressedMessages.length, 0);
4764 });
4765
4766 it("supports ECMAScript version 'latest'", () => {
4767 const messages = linter.verify("let x = 5 ** 7;", {
4768 parserOptions: { ecmaVersion: "latest" }
4769 });
4770 const suppressedMessages = linter.getSuppressedMessages();
4771
4772 assert.strictEqual(messages.length, 0);
4773 assert.strictEqual(suppressedMessages.length, 0);
4774 });
4775
4776 it("the 'latest' is equal to espree.latestEcmaVersion", () => {
4777 let ecmaVersion = null;
4778 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4779
4780 linter.defineRule("ecma-version", context => ({
4781 Program() {
4782 ecmaVersion = context.parserOptions.ecmaVersion;
4783 }
4784 }));
4785 linter.verify("", config);
4786 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion, "ecmaVersion should be 13");
4787 });
4788
4789 it("the 'latest' is not normalized for custom parsers", () => {
4790 let ecmaVersion = null;
4791 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "latest" } };
4792
4793 linter.defineParser("custom-parser", testParsers.enhancedParser);
4794 linter.defineRule("ecma-version", context => ({
4795 Program() {
4796 ecmaVersion = context.parserOptions.ecmaVersion;
4797 }
4798 }));
4799 linter.verify("", config);
4800 assert.strictEqual(ecmaVersion, "latest", "ecmaVersion should be latest");
4801 });
4802
4803 it("the 'latest' is equal to espree.latestEcmaVersion on languageOptions", () => {
4804 let ecmaVersion = null;
4805 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4806
4807 linter.defineRule("ecma-version", context => ({
4808 Program() {
4809 ecmaVersion = context.languageOptions.ecmaVersion;
4810 }
4811 }));
4812 linter.verify("", config);
4813 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
4814 });
4815
4816 it("the 'next' is equal to espree.latestEcmaVersion on languageOptions with custom parser", () => {
4817 let ecmaVersion = null;
4818 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "next" } };
4819
4820 linter.defineParser("custom-parser", testParsers.stubParser);
4821 linter.defineRule("ecma-version", context => ({
4822 Program() {
4823 ecmaVersion = context.languageOptions.ecmaVersion;
4824 }
4825 }));
4826 linter.verify("", config);
4827 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
4828 });
4829
4830 it("missing ecmaVersion is equal to 5 on languageOptions with custom parser", () => {
4831 let ecmaVersion = null;
4832 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser" };
4833
4834 linter.defineParser("custom-parser", testParsers.enhancedParser);
4835 linter.defineRule("ecma-version", context => ({
4836 Program() {
4837 ecmaVersion = context.languageOptions.ecmaVersion;
4838 }
4839 }));
4840 linter.verify("", config);
4841 assert.strictEqual(ecmaVersion, 5, "ecmaVersion should be 5");
4842 });
4843
4844 it("should pass normalized ecmaVersion to eslint-scope", () => {
4845 let blockScope = null;
4846
4847 linter.defineRule("block-scope", context => ({
4848 BlockStatement() {
4849 blockScope = context.getScope();
4850 }
4851 }));
4852 linter.defineParser("custom-parser", {
4853 parse: (...args) => espree.parse(...args)
4854 });
4855
4856 // Use standard parser
4857 linter.verify("{}", {
4858 rules: { "block-scope": 2 },
4859 parserOptions: { ecmaVersion: "latest" }
4860 });
4861
4862 assert.strictEqual(blockScope.type, "block");
4863
4864 linter.verify("{}", {
4865 rules: { "block-scope": 2 },
4866 parserOptions: {} // ecmaVersion defaults to 5
4867 });
4868 assert.strictEqual(blockScope.type, "global");
4869
4870 // Use custom parser
4871 linter.verify("{}", {
4872 rules: { "block-scope": 2 },
4873 parser: "custom-parser",
4874 parserOptions: { ecmaVersion: "latest" }
4875 });
4876
4877 assert.strictEqual(blockScope.type, "block");
4878
4879 linter.verify("{}", {
4880 rules: { "block-scope": 2 },
4881 parser: "custom-parser",
4882 parserOptions: {} // ecmaVersion defaults to 5
4883 });
4884 assert.strictEqual(blockScope.type, "global");
4885 });
4886
4887 describe("it should properly parse let declaration when", () => {
4888 it("the ECMAScript version number is 6", () => {
4889 const messages = linter.verify("let x = 5;", {
4890 parserOptions: {
4891 ecmaVersion: 6
4892 }
4893 });
4894 const suppressedMessages = linter.getSuppressedMessages();
4895
4896 assert.strictEqual(messages.length, 0);
4897 assert.strictEqual(suppressedMessages.length, 0);
4898 });
4899
4900 it("the ECMAScript version number is 2015", () => {
4901 const messages = linter.verify("let x = 5;", {
4902 parserOptions: {
4903 ecmaVersion: 2015
4904 }
4905 });
4906 const suppressedMessages = linter.getSuppressedMessages();
4907
4908 assert.strictEqual(messages.length, 0);
4909 assert.strictEqual(suppressedMessages.length, 0);
4910 });
4911 });
4912
4913 it("should fail to parse exponentiation operator when the ECMAScript version number is 2015", () => {
4914 const messages = linter.verify("x ** y;", {
4915 parserOptions: {
4916 ecmaVersion: 2015
4917 }
4918 });
4919 const suppressedMessages = linter.getSuppressedMessages();
4920
4921 assert.strictEqual(messages.length, 1);
4922 assert.strictEqual(suppressedMessages.length, 0);
4923 });
4924
4925 describe("should properly parse exponentiation operator when", () => {
4926 it("the ECMAScript version number is 7", () => {
4927 const messages = linter.verify("x ** y;", {
4928 parserOptions: {
4929 ecmaVersion: 7
4930 }
4931 });
4932 const suppressedMessages = linter.getSuppressedMessages();
4933
4934 assert.strictEqual(messages.length, 0);
4935 assert.strictEqual(suppressedMessages.length, 0);
4936 });
4937
4938 it("the ECMAScript version number is 2016", () => {
4939 const messages = linter.verify("x ** y;", {
4940 parserOptions: {
4941 ecmaVersion: 2016
4942 }
4943 });
4944 const suppressedMessages = linter.getSuppressedMessages();
4945
4946 assert.strictEqual(messages.length, 0);
4947 assert.strictEqual(suppressedMessages.length, 0);
4948 });
4949 });
4950 });
4951
4952 it("should properly parse object spread when ecmaVersion is 2018", () => {
4953
4954 const messages = linter.verify("var x = { ...y };", {
4955 parserOptions: {
4956 ecmaVersion: 2018
4957 }
4958 }, filename);
4959 const suppressedMessages = linter.getSuppressedMessages();
4960
4961 assert.strictEqual(messages.length, 0);
4962 assert.strictEqual(suppressedMessages.length, 0);
4963 });
4964
4965 it("should properly parse global return when passed ecmaFeatures", () => {
4966
4967 const messages = linter.verify("return;", {
4968 parserOptions: {
4969 ecmaFeatures: {
4970 globalReturn: true
4971 }
4972 }
4973 }, filename);
4974 const suppressedMessages = linter.getSuppressedMessages();
4975
4976 assert.strictEqual(messages.length, 0);
4977 assert.strictEqual(suppressedMessages.length, 0);
4978 });
4979
4980 it("should properly parse global return when in Node.js environment", () => {
4981
4982 const messages = linter.verify("return;", {
4983 env: {
4984 node: true
4985 }
4986 }, filename);
4987 const suppressedMessages = linter.getSuppressedMessages();
4988
4989 assert.strictEqual(messages.length, 0);
4990 assert.strictEqual(suppressedMessages.length, 0);
4991 });
4992
4993 it("should not parse global return when in Node.js environment with globalReturn explicitly off", () => {
4994
4995 const messages = linter.verify("return;", {
4996 env: {
4997 node: true
4998 },
4999 parserOptions: {
5000 ecmaFeatures: {
5001 globalReturn: false
5002 }
5003 }
5004 }, filename);
5005 const suppressedMessages = linter.getSuppressedMessages();
5006
5007 assert.strictEqual(messages.length, 1);
5008 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5009
5010 assert.strictEqual(suppressedMessages.length, 0);
5011 });
5012
5013 it("should not parse global return when Node.js environment is false", () => {
5014
5015 const messages = linter.verify("return;", {}, filename);
5016 const suppressedMessages = linter.getSuppressedMessages();
5017
5018 assert.strictEqual(messages.length, 1);
5019 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5020
5021 assert.strictEqual(suppressedMessages.length, 0);
5022 });
5023
5024 it("should properly parse sloppy-mode code when impliedStrict is false", () => {
5025
5026 const messages = linter.verify("var private;", {}, filename);
5027 const suppressedMessages = linter.getSuppressedMessages();
5028
5029 assert.strictEqual(messages.length, 0);
5030 assert.strictEqual(suppressedMessages.length, 0);
5031 });
5032
5033 it("should not parse sloppy-mode code when impliedStrict is true", () => {
5034
5035 const messages = linter.verify("var private;", {
5036 parserOptions: {
5037 ecmaFeatures: {
5038 impliedStrict: true
5039 }
5040 }
5041 }, filename);
5042 const suppressedMessages = linter.getSuppressedMessages();
5043
5044 assert.strictEqual(messages.length, 1);
5045 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
5046
5047 assert.strictEqual(suppressedMessages.length, 0);
5048 });
5049
5050 it("should properly parse valid code when impliedStrict is true", () => {
5051
5052 const messages = linter.verify("var foo;", {
5053 parserOptions: {
5054 ecmaFeatures: {
5055 impliedStrict: true
5056 }
5057 }
5058 }, filename);
5059 const suppressedMessages = linter.getSuppressedMessages();
5060
5061 assert.strictEqual(messages.length, 0);
5062 assert.strictEqual(suppressedMessages.length, 0);
5063 });
5064
5065 it("should properly parse JSX when passed ecmaFeatures", () => {
5066
5067 const messages = linter.verify("var x = <div/>;", {
5068 parserOptions: {
5069 ecmaFeatures: {
5070 jsx: true
5071 }
5072 }
5073 }, filename);
5074 const suppressedMessages = linter.getSuppressedMessages();
5075
5076 assert.strictEqual(messages.length, 0);
5077 assert.strictEqual(suppressedMessages.length, 0);
5078 });
5079
5080 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
5081 const code = "var myDivElement = <div className=\"foo\" />;";
5082 const messages = linter.verify(code, {}, "filename");
5083 const suppressedMessages = linter.getSuppressedMessages();
5084
5085 assert.strictEqual(messages.length, 1);
5086 assert.strictEqual(messages[0].line, 1);
5087 assert.strictEqual(messages[0].column, 20);
5088 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
5089
5090 assert.strictEqual(suppressedMessages.length, 0);
5091 });
5092
5093 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
5094 const code = "var myDivElement = <div className=\"foo\" />;";
5095 const messages = linter.verify(code, { parserOptions: { ecmaFeatures: { jsx: true } } }, "filename");
5096 const suppressedMessages = linter.getSuppressedMessages();
5097
5098 assert.strictEqual(messages.length, 0);
5099 assert.strictEqual(suppressedMessages.length, 0);
5100 });
5101
5102 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
5103 const code = "var myDivElement = <div {...this.props} />;";
5104 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }, "filename");
5105 const suppressedMessages = linter.getSuppressedMessages();
5106
5107 assert.strictEqual(messages.length, 0);
5108 assert.strictEqual(suppressedMessages.length, 0);
5109 });
5110
5111 it("should not allow the use of reserved words as variable names in ES3", () => {
5112 const code = "var char;";
5113 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5114 const suppressedMessages = linter.getSuppressedMessages();
5115
5116 assert.strictEqual(messages.length, 1);
5117 assert.strictEqual(messages[0].severity, 2);
5118 assert.isTrue(messages[0].fatal);
5119 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5120
5121 assert.strictEqual(suppressedMessages.length, 0);
5122 });
5123
5124 it("should not allow the use of reserved words as property names in member expressions in ES3", () => {
5125 const code = "obj.char;";
5126 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5127 const suppressedMessages = linter.getSuppressedMessages();
5128
5129 assert.strictEqual(messages.length, 1);
5130 assert.strictEqual(messages[0].severity, 2);
5131 assert.isTrue(messages[0].fatal);
5132 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5133
5134 assert.strictEqual(suppressedMessages.length, 0);
5135 });
5136
5137 it("should not allow the use of reserved words as property names in object literals in ES3", () => {
5138 const code = "var obj = { char: 1 };";
5139 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5140 const suppressedMessages = linter.getSuppressedMessages();
5141
5142 assert.strictEqual(messages.length, 1);
5143 assert.strictEqual(messages[0].severity, 2);
5144 assert.isTrue(messages[0].fatal);
5145 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5146
5147 assert.strictEqual(suppressedMessages.length, 0);
5148 });
5149
5150 it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => {
5151 const code = "var char; obj.char; var obj = { char: 1 };";
5152 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3, allowReserved: true } }, filename);
5153 const suppressedMessages = linter.getSuppressedMessages();
5154
5155 assert.strictEqual(messages.length, 0);
5156 assert.strictEqual(suppressedMessages.length, 0);
5157 });
5158
5159 it("should not allow the use of reserved words as variable names in ES > 3", () => {
5160 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5161
5162 ecmaVersions.forEach(ecmaVersion => {
5163 const code = "var enum;";
5164 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
5165 const suppressedMessages = linter.getSuppressedMessages();
5166
5167 assert.strictEqual(messages.length, 1);
5168 assert.strictEqual(messages[0].severity, 2);
5169 assert.isTrue(messages[0].fatal);
5170 assert.match(messages[0].message, /^Parsing error:.*'enum'/u);
5171
5172 assert.strictEqual(suppressedMessages.length, 0);
5173 });
5174 });
5175
5176 it("should allow the use of reserved words as property names in ES > 3", () => {
5177 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5178
5179 ecmaVersions.forEach(ecmaVersion => {
5180 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
5181 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
5182 const suppressedMessages = linter.getSuppressedMessages();
5183
5184 assert.strictEqual(messages.length, 0);
5185 assert.strictEqual(suppressedMessages.length, 0);
5186 });
5187 });
5188
5189 it("should not allow `allowReserved: true` in ES > 3", () => {
5190 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5191
5192 ecmaVersions.forEach(ecmaVersion => {
5193 const code = "";
5194 const messages = linter.verify(code, { parserOptions: { ecmaVersion, allowReserved: true } }, filename);
5195 const suppressedMessages = linter.getSuppressedMessages();
5196
5197 assert.strictEqual(messages.length, 1);
5198 assert.strictEqual(messages[0].severity, 2);
5199 assert.isTrue(messages[0].fatal);
5200 assert.match(messages[0].message, /^Parsing error:.*allowReserved/u);
5201
5202 assert.strictEqual(suppressedMessages.length, 0);
5203 });
5204 });
5205
5206 it("should be able to use es6 features if there is a comment which has \"eslint-env es6\"", () => {
5207 const code = [
5208 "/* eslint-env es6 */",
5209 "var arrow = () => 0;",
5210 "var binary = 0b1010;",
5211 "{ let a = 0; const b = 1; }",
5212 "class A {}",
5213 "function defaultParams(a = 0) {}",
5214 "var {a = 1, b = 2} = {};",
5215 "for (var a of []) {}",
5216 "function* generator() { yield 0; }",
5217 "var computed = {[a]: 0};",
5218 "var duplicate = {dup: 0, dup: 1};",
5219 "var method = {foo() {}};",
5220 "var property = {a, b};",
5221 "var octal = 0o755;",
5222 "var u = /^.$/u.test('ð ®·');",
5223 "var y = /hello/y.test('hello');",
5224 "function restParam(a, ...rest) {}",
5225 "class B { superInFunc() { super.foo(); } }",
5226 "var template = `hello, ${a}`;",
5227 "var unicode = '\\u{20BB7}';"
5228 ].join("\n");
5229
5230 const messages = linter.verify(code, null, "eslint-env es6");
5231 const suppressedMessages = linter.getSuppressedMessages();
5232
5233 assert.strictEqual(messages.length, 0);
5234 assert.strictEqual(suppressedMessages.length, 0);
5235 });
5236
5237 it("should be able to return in global if there is a comment which enables the node environment with a comment", () => {
5238 const messages = linter.verify(`/* ${ESLINT_ENV} node */ return;`, null, "node environment");
5239 const suppressedMessages = linter.getSuppressedMessages();
5240
5241 assert.strictEqual(messages.length, 0);
5242 assert.strictEqual(suppressedMessages.length, 0);
5243 });
5244
5245 it("should attach a \"/*global\" comment node to declared variables", () => {
5246 const code = "/* global foo */\n/* global bar, baz */";
5247 let ok = false;
5248
5249 linter.defineRules({
5250 test(context) {
5251 return {
5252 Program() {
5253 const scope = context.getScope();
5254 const sourceCode = context.getSourceCode();
5255 const comments = sourceCode.getAllComments();
5256
5257 assert.strictEqual(2, comments.length);
5258
5259 const foo = getVariable(scope, "foo");
5260
5261 assert.strictEqual(foo.eslintExplicitGlobal, true);
5262 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
5263
5264 const bar = getVariable(scope, "bar");
5265
5266 assert.strictEqual(bar.eslintExplicitGlobal, true);
5267 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
5268
5269 const baz = getVariable(scope, "baz");
5270
5271 assert.strictEqual(baz.eslintExplicitGlobal, true);
5272 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
5273
5274 ok = true;
5275 }
5276 };
5277 }
5278 });
5279
5280 linter.verify(code, { rules: { test: 2 } });
5281 assert(ok);
5282 });
5283
5284 it("should report a linting error when a global is set to an invalid value", () => {
5285 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
5286 const suppressedMessages = linter.getSuppressedMessages();
5287
5288 assert.deepStrictEqual(results, [
5289 {
5290 ruleId: null,
5291 severity: 2,
5292 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
5293 line: 1,
5294 column: 1,
5295 endLine: 1,
5296 endColumn: 39,
5297 nodeType: null
5298 },
5299 {
5300 ruleId: "no-undef",
5301 messageId: "undef",
5302 severity: 2,
5303 message: "'foo' is not defined.",
5304 line: 2,
5305 column: 1,
5306 endLine: 2,
5307 endColumn: 4,
5308 nodeType: "Identifier"
5309 }
5310 ]);
5311
5312 assert.strictEqual(suppressedMessages.length, 0);
5313 });
5314
5315 it("should not crash when we reuse the SourceCode object", () => {
5316 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
5317 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
5318 });
5319
5320 it("should reuse the SourceCode object", () => {
5321 let ast1 = null,
5322 ast2 = null;
5323
5324 linter.defineRule("save-ast1", () => ({
5325 Program(node) {
5326 ast1 = node;
5327 }
5328 }));
5329 linter.defineRule("save-ast2", () => ({
5330 Program(node) {
5331 ast2 = node;
5332 }
5333 }));
5334
5335 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast1": 2 } });
5336 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast2": 2 } });
5337
5338 assert(ast1 !== null);
5339 assert(ast2 !== null);
5340 assert(ast1 === ast2);
5341 });
5342
5343 it("should allow 'await' as a property name in modules", () => {
5344 const result = linter.verify(
5345 "obj.await",
5346 { parserOptions: { ecmaVersion: 6, sourceType: "module" } }
5347 );
5348 const suppressedMessages = linter.getSuppressedMessages();
5349
5350 assert(result.length === 0);
5351 assert.strictEqual(suppressedMessages.length, 0);
5352 });
5353
5354
5355 it("should not modify config object passed as argument", () => {
5356 const config = {};
5357
5358 Object.freeze(config);
5359 linter.verify("var", config);
5360 });
5361
5362 it("should pass 'id' to rule contexts with the rule id", () => {
5363 const spy = sinon.spy(context => {
5364 assert.strictEqual(context.id, "foo-bar-baz");
5365 return {};
5366 });
5367
5368 linter.defineRule("foo-bar-baz", spy);
5369 linter.verify("x", { rules: { "foo-bar-baz": "error" } });
5370 assert(spy.calledOnce);
5371 });
5372
5373 describe("descriptions in directive comments", () => {
5374 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
5375 const aaa = sinon.stub().returns({});
5376 const bbb = sinon.stub().returns({});
5377
5378 linter.defineRule("aaa", { create: aaa });
5379 linter.defineRule("bbb", { create: bbb });
5380 const messages = linter.verify(`
5381 /*eslint aaa:error -- bbb:error */
5382 console.log("hello")
5383 `, {});
5384 const suppressedMessages = linter.getSuppressedMessages();
5385
5386 // Don't include syntax error of the comment.
5387 assert.deepStrictEqual(messages, []);
5388
5389 // Use only `aaa`.
5390 assert.strictEqual(aaa.callCount, 1);
5391 assert.strictEqual(bbb.callCount, 0);
5392
5393 assert.strictEqual(suppressedMessages.length, 0);
5394 });
5395
5396 it("should ignore the part preceded by '--' in '/*eslint-env*/'.", () => {
5397 const messages = linter.verify(`
5398 /*eslint-env es2015 -- es2017 */
5399 var Promise = {}
5400 var Atomics = {}
5401 `, { rules: { "no-redeclare": "error" } });
5402 const suppressedMessages = linter.getSuppressedMessages();
5403
5404 // Don't include `Atomics`
5405 assert.deepStrictEqual(
5406 messages,
5407 [{
5408 column: 25,
5409 endColumn: 32,
5410 endLine: 3,
5411 line: 3,
5412 message: "'Promise' is already defined as a built-in global variable.",
5413 messageId: "redeclaredAsBuiltin",
5414 nodeType: "Identifier",
5415 ruleId: "no-redeclare",
5416 severity: 2
5417 }]
5418 );
5419
5420 assert.strictEqual(suppressedMessages.length, 0);
5421 });
5422
5423 it("should ignore the part preceded by '--' in '/*global*/'.", () => {
5424 const messages = linter.verify(`
5425 /*global aaa -- bbb */
5426 var aaa = {}
5427 var bbb = {}
5428 `, { rules: { "no-redeclare": "error" } });
5429 const suppressedMessages = linter.getSuppressedMessages();
5430
5431 // Don't include `bbb`
5432 assert.deepStrictEqual(
5433 messages,
5434 [{
5435 column: 30,
5436 endColumn: 33,
5437 line: 2,
5438 endLine: 2,
5439 message: "'aaa' is already defined by a variable declaration.",
5440 messageId: "redeclaredBySyntax",
5441 nodeType: "Block",
5442 ruleId: "no-redeclare",
5443 severity: 2
5444 }]
5445 );
5446
5447 assert.strictEqual(suppressedMessages.length, 0);
5448 });
5449
5450 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
5451 const messages = linter.verify(`
5452 /*globals aaa -- bbb */
5453 var aaa = {}
5454 var bbb = {}
5455 `, { rules: { "no-redeclare": "error" } });
5456 const suppressedMessages = linter.getSuppressedMessages();
5457
5458 // Don't include `bbb`
5459 assert.deepStrictEqual(
5460 messages,
5461 [{
5462 column: 31,
5463 endColumn: 34,
5464 line: 2,
5465 endLine: 2,
5466 message: "'aaa' is already defined by a variable declaration.",
5467 messageId: "redeclaredBySyntax",
5468 nodeType: "Block",
5469 ruleId: "no-redeclare",
5470 severity: 2
5471 }]
5472 );
5473
5474 assert.strictEqual(suppressedMessages.length, 0);
5475 });
5476
5477 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
5478 const messages = linter.verify(`
5479 /*exported aaa -- bbb */
5480 var aaa = {}
5481 var bbb = {}
5482 `, { rules: { "no-unused-vars": "error" } });
5483 const suppressedMessages = linter.getSuppressedMessages();
5484
5485 // Don't include `aaa`
5486 assert.deepStrictEqual(
5487 messages,
5488 [{
5489 column: 25,
5490 endColumn: 28,
5491 endLine: 4,
5492 line: 4,
5493 message: "'bbb' is assigned a value but never used.",
5494 messageId: "unusedVar",
5495 nodeType: "Identifier",
5496 ruleId: "no-unused-vars",
5497 severity: 2
5498 }]
5499 );
5500
5501 assert.strictEqual(suppressedMessages.length, 0);
5502 });
5503
5504 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
5505 const messages = linter.verify(`
5506 /*eslint-disable no-redeclare -- no-unused-vars */
5507 var aaa = {}
5508 var aaa = {}
5509 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5510 const suppressedMessages = linter.getSuppressedMessages();
5511
5512 // Do include `no-unused-vars` but not `no-redeclare`
5513 assert.deepStrictEqual(
5514 messages,
5515 [{
5516 column: 25,
5517 endLine: 4,
5518 endColumn: 28,
5519 line: 4,
5520 message: "'aaa' is assigned a value but never used.",
5521 messageId: "unusedVar",
5522 nodeType: "Identifier",
5523 ruleId: "no-unused-vars",
5524 severity: 2
5525 }]
5526 );
5527
5528 assert.deepStrictEqual(
5529 suppressedMessages,
5530 [{
5531 column: 25,
5532 endLine: 4,
5533 endColumn: 28,
5534 line: 4,
5535 message: "'aaa' is already defined.",
5536 messageId: "redeclared",
5537 nodeType: "Identifier",
5538 ruleId: "no-redeclare",
5539 severity: 2,
5540 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5541 }]
5542 );
5543 });
5544
5545 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
5546 const messages = linter.verify(`
5547 /*eslint-disable no-redeclare, no-unused-vars */
5548 /*eslint-enable no-redeclare -- no-unused-vars */
5549 var aaa = {}
5550 var aaa = {}
5551 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5552 const suppressedMessages = linter.getSuppressedMessages();
5553
5554 // Do include `no-redeclare` but not `no-unused-vars`
5555 assert.deepStrictEqual(
5556 messages,
5557 [{
5558 column: 25,
5559 endLine: 5,
5560 endColumn: 28,
5561 line: 5,
5562 message: "'aaa' is already defined.",
5563 messageId: "redeclared",
5564 nodeType: "Identifier",
5565 ruleId: "no-redeclare",
5566 severity: 2
5567 }]
5568 );
5569
5570 assert.deepStrictEqual(
5571 suppressedMessages,
5572 [{
5573 column: 25,
5574 endLine: 5,
5575 endColumn: 28,
5576 line: 5,
5577 message: "'aaa' is assigned a value but never used.",
5578 messageId: "unusedVar",
5579 nodeType: "Identifier",
5580 ruleId: "no-unused-vars",
5581 severity: 2,
5582 suppressions: [{ kind: "directive", justification: "" }]
5583 }]
5584 );
5585 });
5586
5587 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
5588 const messages = linter.verify(`
5589 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
5590 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
5591 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5592 const suppressedMessages = linter.getSuppressedMessages();
5593
5594 // Do include `no-unused-vars` but not `no-redeclare`
5595 assert.deepStrictEqual(
5596 messages,
5597 [{
5598 column: 25,
5599 endLine: 3,
5600 endColumn: 28,
5601 line: 3,
5602 message: "'aaa' is assigned a value but never used.",
5603 messageId: "unusedVar",
5604 nodeType: "Identifier",
5605 ruleId: "no-unused-vars",
5606 severity: 2
5607 }]
5608 );
5609
5610 assert.deepStrictEqual(
5611 suppressedMessages,
5612 [{
5613 column: 25,
5614 endLine: 3,
5615 endColumn: 28,
5616 line: 3,
5617 message: "'aaa' is already defined.",
5618 messageId: "redeclared",
5619 nodeType: "Identifier",
5620 ruleId: "no-redeclare",
5621 severity: 2,
5622 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5623 }]
5624 );
5625 });
5626
5627 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
5628 const messages = linter.verify(`
5629 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
5630 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
5631 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5632 const suppressedMessages = linter.getSuppressedMessages();
5633
5634 // Do include `no-unused-vars` but not `no-redeclare`
5635 assert.deepStrictEqual(
5636 messages,
5637 [{
5638 column: 25,
5639 endLine: 3,
5640 endColumn: 28,
5641 line: 3,
5642 message: "'aaa' is assigned a value but never used.",
5643 messageId: "unusedVar",
5644 nodeType: "Identifier",
5645 ruleId: "no-unused-vars",
5646 severity: 2
5647 }]
5648 );
5649
5650 assert.deepStrictEqual(
5651 suppressedMessages,
5652 [{
5653 column: 25,
5654 endLine: 3,
5655 endColumn: 28,
5656 line: 3,
5657 message: "'aaa' is already defined.",
5658 messageId: "redeclared",
5659 nodeType: "Identifier",
5660 ruleId: "no-redeclare",
5661 severity: 2,
5662 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5663 }]
5664 );
5665 });
5666
5667 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
5668 const messages = linter.verify(`
5669 //eslint-disable-next-line no-redeclare -- no-unused-vars
5670 var aaa = {}
5671 //eslint-disable-next-line no-redeclare -- no-unused-vars
5672 var aaa = {}
5673 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5674 const suppressedMessages = linter.getSuppressedMessages();
5675
5676 // Do include `no-unused-vars` but not `no-redeclare`
5677 assert.deepStrictEqual(
5678 messages,
5679 [{
5680 column: 25,
5681 endLine: 5,
5682 endColumn: 28,
5683 line: 5,
5684 message: "'aaa' is assigned a value but never used.",
5685 messageId: "unusedVar",
5686 nodeType: "Identifier",
5687 ruleId: "no-unused-vars",
5688 severity: 2
5689 }]
5690 );
5691
5692 assert.deepStrictEqual(
5693 suppressedMessages,
5694 [{
5695 column: 25,
5696 endLine: 5,
5697 endColumn: 28,
5698 line: 5,
5699 message: "'aaa' is already defined.",
5700 messageId: "redeclared",
5701 nodeType: "Identifier",
5702 ruleId: "no-redeclare",
5703 severity: 2,
5704 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5705 }]
5706 );
5707 });
5708
5709 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
5710 const messages = linter.verify(`
5711 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
5712 var aaa = {}
5713 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
5714 var aaa = {}
5715 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5716 const suppressedMessages = linter.getSuppressedMessages();
5717
5718 // Do include `no-unused-vars` but not `no-redeclare`
5719 assert.deepStrictEqual(
5720 messages,
5721 [{
5722 column: 25,
5723 endLine: 5,
5724 endColumn: 28,
5725 line: 5,
5726 message: "'aaa' is assigned a value but never used.",
5727 messageId: "unusedVar",
5728 nodeType: "Identifier",
5729 ruleId: "no-unused-vars",
5730 severity: 2
5731 }]
5732 );
5733
5734 assert.deepStrictEqual(
5735 suppressedMessages,
5736 [{
5737 column: 25,
5738 endLine: 5,
5739 endColumn: 28,
5740 line: 5,
5741 message: "'aaa' is already defined.",
5742 messageId: "redeclared",
5743 nodeType: "Identifier",
5744 ruleId: "no-redeclare",
5745 severity: 2,
5746 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5747 }]
5748 );
5749 });
5750
5751 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
5752 const rule = sinon.stub().returns({});
5753
5754 linter.defineRule("a--rule", { create: rule });
5755 const messages = linter.verify(`
5756 /*eslint a--rule:error */
5757 console.log("hello")
5758 `, {});
5759 const suppressedMessages = linter.getSuppressedMessages();
5760
5761 // Don't include syntax error of the comment.
5762 assert.deepStrictEqual(messages, []);
5763
5764 // Use `a--rule`.
5765 assert.strictEqual(rule.callCount, 1);
5766
5767 assert.strictEqual(suppressedMessages.length, 0);
5768 });
5769
5770 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
5771 const aaa = sinon.stub().returns({});
5772 const bbb = sinon.stub().returns({});
5773
5774 linter.defineRule("aaa", { create: aaa });
5775 linter.defineRule("bbb", { create: bbb });
5776 const messages = linter.verify(`
5777 /*eslint aaa:error -------- bbb:error */
5778 console.log("hello")
5779 `, {});
5780 const suppressedMessages = linter.getSuppressedMessages();
5781
5782 // Don't include syntax error of the comment.
5783 assert.deepStrictEqual(messages, []);
5784
5785 // Use only `aaa`.
5786 assert.strictEqual(aaa.callCount, 1);
5787 assert.strictEqual(bbb.callCount, 0);
5788
5789 assert.strictEqual(suppressedMessages.length, 0);
5790 });
5791
5792 it("should ignore the part preceded by '--' with line breaks.", () => {
5793 const aaa = sinon.stub().returns({});
5794 const bbb = sinon.stub().returns({});
5795
5796 linter.defineRule("aaa", { create: aaa });
5797 linter.defineRule("bbb", { create: bbb });
5798 const messages = linter.verify(`
5799 /*eslint aaa:error
5800 --------
5801 bbb:error */
5802 console.log("hello")
5803 `, {});
5804 const suppressedMessages = linter.getSuppressedMessages();
5805
5806 // Don't include syntax error of the comment.
5807 assert.deepStrictEqual(messages, []);
5808
5809 // Use only `aaa`.
5810 assert.strictEqual(aaa.callCount, 1);
5811 assert.strictEqual(bbb.callCount, 0);
5812
5813 assert.strictEqual(suppressedMessages.length, 0);
5814 });
5815 });
5816 });
5817
5818 describe("context.getScope()", () => {
5819
5820 /**
5821 * Get the scope on the node `astSelector` specified.
5822 * @param {string} code The source code to verify.
5823 * @param {string} astSelector The AST selector to get scope.
5824 * @param {number} [ecmaVersion=5] The ECMAScript version.
5825 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
5826 */
5827 function getScope(code, astSelector, ecmaVersion = 5) {
5828 let node, scope;
5829
5830 linter.defineRule("get-scope", context => ({
5831 [astSelector](node0) {
5832 node = node0;
5833 scope = context.getScope();
5834 }
5835 }));
5836 linter.verify(
5837 code,
5838 {
5839 parserOptions: { ecmaVersion },
5840 rules: { "get-scope": 2 }
5841 }
5842 );
5843
5844 return { node, scope };
5845 }
5846
5847 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
5848 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
5849
5850 assert.strictEqual(scope.type, "function");
5851 assert.strictEqual(scope.block, node);
5852 });
5853
5854 it("should return 'function' scope on FunctionExpression (ES5)", () => {
5855 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
5856
5857 assert.strictEqual(scope.type, "function");
5858 assert.strictEqual(scope.block, node);
5859 });
5860
5861 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
5862 const { node, scope } = getScope("function f() {}", "BlockStatement");
5863
5864 assert.strictEqual(scope.type, "function");
5865 assert.strictEqual(scope.block, node.parent);
5866 });
5867
5868 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
5869 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
5870
5871 assert.strictEqual(scope.type, "function");
5872 assert.strictEqual(scope.block, node.parent);
5873 });
5874
5875 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
5876 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
5877
5878 assert.strictEqual(scope.type, "function");
5879 assert.strictEqual(scope.block, node.parent.parent);
5880 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5881 });
5882
5883 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
5884 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
5885
5886 assert.strictEqual(scope.type, "block");
5887 assert.strictEqual(scope.upper.type, "function");
5888 assert.strictEqual(scope.block, node);
5889 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
5890 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
5891 });
5892
5893 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
5894 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
5895
5896 assert.strictEqual(scope.type, "block");
5897 assert.strictEqual(scope.upper.type, "block");
5898 assert.strictEqual(scope.upper.upper.type, "function");
5899 assert.strictEqual(scope.block, node);
5900 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5901 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
5902 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
5903 });
5904
5905 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
5906 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
5907
5908 assert.strictEqual(scope.type, "function");
5909 assert.strictEqual(scope.block, node.parent.parent);
5910 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5911 });
5912
5913 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
5914 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
5915
5916 assert.strictEqual(scope.type, "switch");
5917 assert.strictEqual(scope.block, node);
5918 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5919 });
5920
5921 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
5922 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
5923
5924 assert.strictEqual(scope.type, "function");
5925 assert.strictEqual(scope.block, node.parent.parent.parent);
5926 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5927 });
5928
5929 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
5930 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
5931
5932 assert.strictEqual(scope.type, "switch");
5933 assert.strictEqual(scope.block, node.parent);
5934 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5935 });
5936
5937 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
5938 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
5939
5940 assert.strictEqual(scope.type, "catch");
5941 assert.strictEqual(scope.block, node);
5942 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5943 });
5944
5945 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
5946 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
5947
5948 assert.strictEqual(scope.type, "catch");
5949 assert.strictEqual(scope.block, node);
5950 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5951 });
5952
5953 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
5954 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
5955
5956 assert.strictEqual(scope.type, "catch");
5957 assert.strictEqual(scope.block, node.parent);
5958 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5959 });
5960
5961 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
5962 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
5963
5964 assert.strictEqual(scope.type, "block");
5965 assert.strictEqual(scope.block, node);
5966 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
5967 });
5968
5969 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
5970 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
5971
5972 assert.strictEqual(scope.type, "function");
5973 assert.strictEqual(scope.block, node.parent.parent);
5974 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
5975 });
5976
5977 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
5978 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
5979
5980 assert.strictEqual(scope.type, "for");
5981 assert.strictEqual(scope.block, node);
5982 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
5983 });
5984
5985 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
5986 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
5987
5988 assert.strictEqual(scope.type, "function");
5989 assert.strictEqual(scope.block, node.parent.parent.parent);
5990 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
5991 });
5992
5993 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
5994 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
5995
5996 assert.strictEqual(scope.type, "block");
5997 assert.strictEqual(scope.upper.type, "for");
5998 assert.strictEqual(scope.block, node);
5999 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6000 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
6001 });
6002
6003 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
6004 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
6005
6006 assert.strictEqual(scope.type, "function");
6007 assert.strictEqual(scope.block, node.parent.parent);
6008 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
6009 });
6010
6011 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
6012 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
6013
6014 assert.strictEqual(scope.type, "for");
6015 assert.strictEqual(scope.block, node);
6016 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
6017 });
6018
6019 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
6020 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
6021
6022 assert.strictEqual(scope.type, "function");
6023 assert.strictEqual(scope.block, node.parent.parent.parent);
6024 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
6025 });
6026
6027 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
6028 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
6029
6030 assert.strictEqual(scope.type, "block");
6031 assert.strictEqual(scope.upper.type, "for");
6032 assert.strictEqual(scope.block, node);
6033 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6034 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
6035 });
6036
6037 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
6038 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
6039
6040 assert.strictEqual(scope.type, "for");
6041 assert.strictEqual(scope.block, node);
6042 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
6043 });
6044
6045 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
6046 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
6047
6048 assert.strictEqual(scope.type, "block");
6049 assert.strictEqual(scope.upper.type, "for");
6050 assert.strictEqual(scope.block, node);
6051 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6052 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
6053 });
6054
6055 it("should shadow the same name variable by the iteration variable.", () => {
6056 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
6057
6058 assert.strictEqual(scope.type, "for");
6059 assert.strictEqual(scope.upper.type, "global");
6060 assert.strictEqual(scope.block, node);
6061 assert.strictEqual(scope.upper.variables[0].references.length, 0);
6062 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
6063 assert.strictEqual(scope.references[1].identifier, node.right);
6064 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
6065 });
6066 });
6067
6068 describe("Variables and references", () => {
6069 const code = [
6070 "a;",
6071 "function foo() { b; }",
6072 "Object;",
6073 "foo;",
6074 "var c;",
6075 "c;",
6076 "/* global d */",
6077 "d;",
6078 "e;",
6079 "f;"
6080 ].join("\n");
6081 let scope = null;
6082
6083 beforeEach(() => {
6084 let ok = false;
6085
6086 linter.defineRules({
6087 test(context) {
6088 return {
6089 Program() {
6090 scope = context.getScope();
6091 ok = true;
6092 }
6093 };
6094 }
6095 });
6096 linter.verify(code, { rules: { test: 2 }, globals: { e: true, f: false } });
6097 assert(ok);
6098 });
6099
6100 afterEach(() => {
6101 scope = null;
6102 });
6103
6104 it("Scope#through should contain references of undefined variables", () => {
6105 assert.strictEqual(scope.through.length, 2);
6106 assert.strictEqual(scope.through[0].identifier.name, "a");
6107 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
6108 assert.strictEqual(scope.through[0].resolved, null);
6109 assert.strictEqual(scope.through[1].identifier.name, "b");
6110 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
6111 assert.strictEqual(scope.through[1].resolved, null);
6112 });
6113
6114 it("Scope#variables should contain global variables", () => {
6115 assert(scope.variables.some(v => v.name === "Object"));
6116 assert(scope.variables.some(v => v.name === "foo"));
6117 assert(scope.variables.some(v => v.name === "c"));
6118 assert(scope.variables.some(v => v.name === "d"));
6119 assert(scope.variables.some(v => v.name === "e"));
6120 assert(scope.variables.some(v => v.name === "f"));
6121 });
6122
6123 it("Scope#set should contain global variables", () => {
6124 assert(scope.set.get("Object"));
6125 assert(scope.set.get("foo"));
6126 assert(scope.set.get("c"));
6127 assert(scope.set.get("d"));
6128 assert(scope.set.get("e"));
6129 assert(scope.set.get("f"));
6130 });
6131
6132 it("Variables#references should contain their references", () => {
6133 assert.strictEqual(scope.set.get("Object").references.length, 1);
6134 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
6135 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
6136 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
6137 assert.strictEqual(scope.set.get("foo").references.length, 1);
6138 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
6139 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
6140 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
6141 assert.strictEqual(scope.set.get("c").references.length, 1);
6142 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
6143 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
6144 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
6145 assert.strictEqual(scope.set.get("d").references.length, 1);
6146 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
6147 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
6148 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
6149 assert.strictEqual(scope.set.get("e").references.length, 1);
6150 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
6151 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
6152 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
6153 assert.strictEqual(scope.set.get("f").references.length, 1);
6154 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
6155 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
6156 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
6157 });
6158
6159 it("Reference#resolved should be their variable", () => {
6160 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
6161 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
6162 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
6163 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
6164 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
6165 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
6166 });
6167 });
6168
6169 describe("context.getDeclaredVariables(node)", () => {
6170
6171 /**
6172 * Assert `context.getDeclaredVariables(node)` is valid.
6173 * @param {string} code A code to check.
6174 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
6175 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
6176 * @returns {void}
6177 */
6178 function verify(code, type, expectedNamesList) {
6179 linter.defineRules({
6180 test(context) {
6181
6182 /**
6183 * Assert `context.getDeclaredVariables(node)` is empty.
6184 * @param {ASTNode} node A node to check.
6185 * @returns {void}
6186 */
6187 function checkEmpty(node) {
6188 assert.strictEqual(0, context.getDeclaredVariables(node).length);
6189 }
6190 const rule = {
6191 Program: checkEmpty,
6192 EmptyStatement: checkEmpty,
6193 BlockStatement: checkEmpty,
6194 ExpressionStatement: checkEmpty,
6195 LabeledStatement: checkEmpty,
6196 BreakStatement: checkEmpty,
6197 ContinueStatement: checkEmpty,
6198 WithStatement: checkEmpty,
6199 SwitchStatement: checkEmpty,
6200 ReturnStatement: checkEmpty,
6201 ThrowStatement: checkEmpty,
6202 TryStatement: checkEmpty,
6203 WhileStatement: checkEmpty,
6204 DoWhileStatement: checkEmpty,
6205 ForStatement: checkEmpty,
6206 ForInStatement: checkEmpty,
6207 DebuggerStatement: checkEmpty,
6208 ThisExpression: checkEmpty,
6209 ArrayExpression: checkEmpty,
6210 ObjectExpression: checkEmpty,
6211 Property: checkEmpty,
6212 SequenceExpression: checkEmpty,
6213 UnaryExpression: checkEmpty,
6214 BinaryExpression: checkEmpty,
6215 AssignmentExpression: checkEmpty,
6216 UpdateExpression: checkEmpty,
6217 LogicalExpression: checkEmpty,
6218 ConditionalExpression: checkEmpty,
6219 CallExpression: checkEmpty,
6220 NewExpression: checkEmpty,
6221 MemberExpression: checkEmpty,
6222 SwitchCase: checkEmpty,
6223 Identifier: checkEmpty,
6224 Literal: checkEmpty,
6225 ForOfStatement: checkEmpty,
6226 ArrowFunctionExpression: checkEmpty,
6227 YieldExpression: checkEmpty,
6228 TemplateLiteral: checkEmpty,
6229 TaggedTemplateExpression: checkEmpty,
6230 TemplateElement: checkEmpty,
6231 ObjectPattern: checkEmpty,
6232 ArrayPattern: checkEmpty,
6233 RestElement: checkEmpty,
6234 AssignmentPattern: checkEmpty,
6235 ClassBody: checkEmpty,
6236 MethodDefinition: checkEmpty,
6237 MetaProperty: checkEmpty
6238 };
6239
6240 rule[type] = function(node) {
6241 const expectedNames = expectedNamesList.shift();
6242 const variables = context.getDeclaredVariables(node);
6243
6244 assert(Array.isArray(expectedNames));
6245 assert(Array.isArray(variables));
6246 assert.strictEqual(expectedNames.length, variables.length);
6247 for (let i = variables.length - 1; i >= 0; i--) {
6248 assert.strictEqual(expectedNames[i], variables[i].name);
6249 }
6250 };
6251 return rule;
6252 }
6253 });
6254 linter.verify(code, {
6255 rules: { test: 2 },
6256 parserOptions: {
6257 ecmaVersion: 6,
6258 sourceType: "module"
6259 }
6260 });
6261
6262 // Check all expected names are asserted.
6263 assert.strictEqual(0, expectedNamesList.length);
6264 }
6265
6266 it("VariableDeclaration", () => {
6267 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
6268 const namesList = [
6269 ["a", "b", "c"],
6270 ["d", "e", "f"],
6271 ["g", "h", "i", "j", "k"],
6272 ["l"]
6273 ];
6274
6275 verify(code, "VariableDeclaration", namesList);
6276 });
6277
6278 it("VariableDeclaration (on for-in/of loop)", () => {
6279
6280 // TDZ scope is created here, so tests to exclude those.
6281 const code = "\n for (var {a, x: [b], y: {c = 0}} in foo) {\n let g;\n }\n for (let {d, x: [e], y: {f = 0}} of foo) {\n let h;\n }\n ";
6282 const namesList = [
6283 ["a", "b", "c"],
6284 ["g"],
6285 ["d", "e", "f"],
6286 ["h"]
6287 ];
6288
6289 verify(code, "VariableDeclaration", namesList);
6290 });
6291
6292 it("VariableDeclarator", () => {
6293
6294 // TDZ scope is created here, so tests to exclude those.
6295 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
6296 const namesList = [
6297 ["a", "b", "c"],
6298 ["d", "e", "f"],
6299 ["g", "h", "i"],
6300 ["j", "k"],
6301 ["l"]
6302 ];
6303
6304 verify(code, "VariableDeclarator", namesList);
6305 });
6306
6307 it("FunctionDeclaration", () => {
6308 const code = "\n function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n }\n function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n }\n ";
6309 const namesList = [
6310 ["foo", "a", "b", "c", "d", "e"],
6311 ["bar", "f", "g", "h", "i", "j"]
6312 ];
6313
6314 verify(code, "FunctionDeclaration", namesList);
6315 });
6316
6317 it("FunctionExpression", () => {
6318 const code = "\n (function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n });\n (function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n });\n ";
6319 const namesList = [
6320 ["foo", "a", "b", "c", "d", "e"],
6321 ["bar", "f", "g", "h", "i", "j"],
6322 ["q"]
6323 ];
6324
6325 verify(code, "FunctionExpression", namesList);
6326 });
6327
6328 it("ArrowFunctionExpression", () => {
6329 const code = "\n (({a, x: [b], y: {c = 0}}, [d, e]) => {\n let z;\n });\n (({f, x: [g], y: {h = 0}}, [i, j]) => {\n let z;\n });\n ";
6330 const namesList = [
6331 ["a", "b", "c", "d", "e"],
6332 ["f", "g", "h", "i", "j"]
6333 ];
6334
6335 verify(code, "ArrowFunctionExpression", namesList);
6336 });
6337
6338 it("ClassDeclaration", () => {
6339 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
6340 const namesList = [
6341 ["A", "A"], // outer scope's and inner scope's.
6342 ["B", "B"]
6343 ];
6344
6345 verify(code, "ClassDeclaration", namesList);
6346 });
6347
6348 it("ClassExpression", () => {
6349 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
6350 const namesList = [
6351 ["A"],
6352 ["B"]
6353 ];
6354
6355 verify(code, "ClassExpression", namesList);
6356 });
6357
6358 it("CatchClause", () => {
6359 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
6360 const namesList = [
6361 ["a", "b"],
6362 ["c", "d"]
6363 ];
6364
6365 verify(code, "CatchClause", namesList);
6366 });
6367
6368 it("ImportDeclaration", () => {
6369 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6370 const namesList = [
6371 [],
6372 ["a"],
6373 ["b", "c", "d"]
6374 ];
6375
6376 verify(code, "ImportDeclaration", namesList);
6377 });
6378
6379 it("ImportSpecifier", () => {
6380 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6381 const namesList = [
6382 ["c"],
6383 ["d"]
6384 ];
6385
6386 verify(code, "ImportSpecifier", namesList);
6387 });
6388
6389 it("ImportDefaultSpecifier", () => {
6390 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6391 const namesList = [
6392 ["b"]
6393 ];
6394
6395 verify(code, "ImportDefaultSpecifier", namesList);
6396 });
6397
6398 it("ImportNamespaceSpecifier", () => {
6399 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6400 const namesList = [
6401 ["a"]
6402 ];
6403
6404 verify(code, "ImportNamespaceSpecifier", namesList);
6405 });
6406 });
6407
6408 describe("suggestions", () => {
6409 it("provides suggestion information for tools to use", () => {
6410 linter.defineRule("rule-with-suggestions", {
6411 meta: { hasSuggestions: true },
6412 create: context => ({
6413 Program(node) {
6414 context.report({
6415 node,
6416 message: "Incorrect spacing",
6417 suggest: [{
6418 desc: "Insert space at the beginning",
6419 fix: fixer => fixer.insertTextBefore(node, " ")
6420 }, {
6421 desc: "Insert space at the end",
6422 fix: fixer => fixer.insertTextAfter(node, " ")
6423 }]
6424 });
6425 }
6426 })
6427 });
6428
6429 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6430 const suppressedMessages = linter.getSuppressedMessages();
6431
6432 assert.deepStrictEqual(messages[0].suggestions, [{
6433 desc: "Insert space at the beginning",
6434 fix: {
6435 range: [0, 0],
6436 text: " "
6437 }
6438 }, {
6439 desc: "Insert space at the end",
6440 fix: {
6441 range: [10, 10],
6442 text: " "
6443 }
6444 }]);
6445
6446 assert.strictEqual(suppressedMessages.length, 0);
6447 });
6448
6449 it("supports messageIds for suggestions", () => {
6450 linter.defineRule("rule-with-suggestions", {
6451 meta: {
6452 messages: {
6453 suggestion1: "Insert space at the beginning",
6454 suggestion2: "Insert space at the end"
6455 },
6456 hasSuggestions: true
6457 },
6458 create: context => ({
6459 Program(node) {
6460 context.report({
6461 node,
6462 message: "Incorrect spacing",
6463 suggest: [{
6464 messageId: "suggestion1",
6465 fix: fixer => fixer.insertTextBefore(node, " ")
6466 }, {
6467 messageId: "suggestion2",
6468 fix: fixer => fixer.insertTextAfter(node, " ")
6469 }]
6470 });
6471 }
6472 })
6473 });
6474
6475 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6476 const suppressedMessages = linter.getSuppressedMessages();
6477
6478 assert.deepStrictEqual(messages[0].suggestions, [{
6479 messageId: "suggestion1",
6480 desc: "Insert space at the beginning",
6481 fix: {
6482 range: [0, 0],
6483 text: " "
6484 }
6485 }, {
6486 messageId: "suggestion2",
6487 desc: "Insert space at the end",
6488 fix: {
6489 range: [10, 10],
6490 text: " "
6491 }
6492 }]);
6493
6494 assert.strictEqual(suppressedMessages.length, 0);
6495 });
6496
6497 it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled", () => {
6498 linter.defineRule("rule-with-suggestions", {
6499 meta: { docs: {}, schema: [] },
6500 create: context => ({
6501 Program(node) {
6502 context.report({
6503 node,
6504 message: "hello world",
6505 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
6506 });
6507 }
6508 })
6509 });
6510
6511 assert.throws(() => {
6512 linter.verify("0", { rules: { "rule-with-suggestions": "error" } });
6513 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
6514 });
6515
6516 it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled and the rule has the obsolete `meta.docs.suggestion` property", () => {
6517 linter.defineRule("rule-with-meta-docs-suggestion", {
6518 meta: { docs: { suggestion: true }, schema: [] },
6519 create: context => ({
6520 Program(node) {
6521 context.report({
6522 node,
6523 message: "hello world",
6524 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
6525 });
6526 }
6527 })
6528 });
6529
6530 assert.throws(() => {
6531 linter.verify("0", { rules: { "rule-with-meta-docs-suggestion": "error" } });
6532 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint.");
6533 });
6534 });
6535
6536 describe("mutability", () => {
6537 let linter1 = null;
6538 let linter2 = null;
6539
6540 beforeEach(() => {
6541 linter1 = new Linter();
6542 linter2 = new Linter();
6543 });
6544
6545 describe("rules", () => {
6546 it("with no changes, same rules are loaded", () => {
6547 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
6548 });
6549
6550 it("loading rule in one doesn't change the other", () => {
6551 linter1.defineRule("mock-rule", () => ({}));
6552
6553 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
6554 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
6555 });
6556 });
6557 });
6558
6559 describe("processors", () => {
6560 let receivedFilenames = [];
6561 let receivedPhysicalFilenames = [];
6562
6563 beforeEach(() => {
6564 receivedFilenames = [];
6565 receivedPhysicalFilenames = [];
6566
6567 // A rule that always reports the AST with a message equal to the source text
6568 linter.defineRule("report-original-text", context => ({
6569 Program(ast) {
6570 receivedFilenames.push(context.getFilename());
6571 receivedPhysicalFilenames.push(context.getPhysicalFilename());
6572 context.report({ node: ast, message: context.getSourceCode().text });
6573 }
6574 }));
6575 });
6576
6577 describe("preprocessors", () => {
6578 it("should receive text and filename.", () => {
6579 const code = "foo bar baz";
6580 const preprocess = sinon.spy(text => text.split(" "));
6581
6582 linter.verify(code, {}, { filename, preprocess });
6583
6584 assert.strictEqual(preprocess.calledOnce, true);
6585 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6586 });
6587
6588 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
6589 const code = "foo bar baz";
6590 const problems = linter.verify(
6591 code,
6592 { rules: { "report-original-text": "error" } },
6593 {
6594
6595 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6596 preprocess(input) {
6597 return input.split(" ");
6598 }
6599 }
6600 );
6601
6602 assert.strictEqual(problems.length, 3);
6603 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
6604 });
6605
6606 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
6607 const code = "foo bar baz";
6608 const problems = linter.verify(
6609 code,
6610 { rules: { "report-original-text": "error" } },
6611 {
6612 filename,
6613
6614 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6615 preprocess(input) {
6616 return input.split(" ").map(text => ({
6617 filename: "block.js",
6618 text
6619 }));
6620 }
6621 }
6622 );
6623
6624 assert.strictEqual(problems.length, 3);
6625 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
6626
6627 // filename
6628 assert.strictEqual(receivedFilenames.length, 3);
6629 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
6630 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
6631 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
6632
6633 // physical filename
6634 assert.strictEqual(receivedPhysicalFilenames.length, 3);
6635 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
6636 });
6637
6638 it("should receive text even if a SourceCode object was given.", () => {
6639 const code = "foo";
6640 const preprocess = sinon.spy(text => text.split(" "));
6641
6642 linter.verify(code, {});
6643 const sourceCode = linter.getSourceCode();
6644
6645 linter.verify(sourceCode, {}, { filename, preprocess });
6646
6647 assert.strictEqual(preprocess.calledOnce, true);
6648 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6649 });
6650
6651 it("should receive text even if a SourceCode object was given (with BOM).", () => {
6652 const code = "\uFEFFfoo";
6653 const preprocess = sinon.spy(text => text.split(" "));
6654
6655 linter.verify(code, {});
6656 const sourceCode = linter.getSourceCode();
6657
6658 linter.verify(sourceCode, {}, { filename, preprocess });
6659
6660 assert.strictEqual(preprocess.calledOnce, true);
6661 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6662 });
6663
6664 it("should catch preprocess error.", () => {
6665 const code = "foo";
6666 const preprocess = sinon.spy(() => {
6667 throw Object.assign(new SyntaxError("Invalid syntax"), {
6668 lineNumber: 1,
6669 column: 1
6670 });
6671 });
6672
6673 const messages = linter.verify(code, {}, { filename, preprocess });
6674
6675 assert.strictEqual(preprocess.calledOnce, true);
6676 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6677 assert.deepStrictEqual(messages, [
6678 {
6679 ruleId: null,
6680 fatal: true,
6681 severity: 2,
6682 message: "Preprocessing error: Invalid syntax",
6683 line: 1,
6684 column: 1
6685 }
6686 ]);
6687 });
6688 });
6689
6690 describe("postprocessors", () => {
6691 it("should receive result and filename.", () => {
6692 const code = "foo bar baz";
6693 const preprocess = sinon.spy(text => text.split(" "));
6694 const postprocess = sinon.spy(text => [text]);
6695
6696 linter.verify(code, {}, { filename, postprocess, preprocess });
6697
6698 assert.strictEqual(postprocess.calledOnce, true);
6699 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
6700 });
6701
6702 it("should apply a postprocessor to the reported messages", () => {
6703 const code = "foo bar baz";
6704
6705 const problems = linter.verify(
6706 code,
6707 { rules: { "report-original-text": "error" } },
6708 {
6709 preprocess: input => input.split(" "),
6710
6711 /*
6712 * Apply a postprocessor that updates the locations of the reported problems
6713 * to make sure they correspond to the locations in the original text.
6714 */
6715 postprocess(problemLists) {
6716 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
6717 return problemLists.reduce(
6718 (combinedList, problemList, index) =>
6719 combinedList.concat(
6720 problemList.map(
6721 problem =>
6722 Object.assign(
6723 {},
6724 problem,
6725 {
6726 message: problem.message.toUpperCase(),
6727 column: problem.column + index * 4
6728 }
6729 )
6730 )
6731 ),
6732 []
6733 );
6734 }
6735 }
6736 );
6737
6738 assert.strictEqual(problems.length, 3);
6739 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
6740 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
6741 });
6742
6743 it("should use postprocessed problem ranges when applying autofixes", () => {
6744 const code = "foo bar baz";
6745
6746 linter.defineRule("capitalize-identifiers", {
6747 meta: {
6748 fixable: "code"
6749 },
6750 create(context) {
6751 return {
6752 Identifier(node) {
6753 if (node.name !== node.name.toUpperCase()) {
6754 context.report({
6755 node,
6756 message: "Capitalize this identifier",
6757 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
6758 });
6759 }
6760 }
6761 };
6762 }
6763 });
6764
6765 const fixResult = linter.verifyAndFix(
6766 code,
6767 { rules: { "capitalize-identifiers": "error" } },
6768 {
6769
6770 /*
6771 * Apply a postprocessor that updates the locations of autofixes
6772 * to make sure they correspond to locations in the original text.
6773 */
6774 preprocess: input => input.split(" "),
6775 postprocess(problemLists) {
6776 return problemLists.reduce(
6777 (combinedProblems, problemList, blockIndex) =>
6778 combinedProblems.concat(
6779 problemList.map(problem =>
6780 Object.assign(problem, {
6781 fix: {
6782 text: problem.fix.text,
6783 range: problem.fix.range.map(
6784 rangeIndex => rangeIndex + blockIndex * 4
6785 )
6786 }
6787 }))
6788 ),
6789 []
6790 );
6791 }
6792 }
6793 );
6794
6795 assert.strictEqual(fixResult.fixed, true);
6796 assert.strictEqual(fixResult.messages.length, 0);
6797 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
6798 });
6799 });
6800 });
6801
6802 describe("verifyAndFix", () => {
6803 it("Fixes the code", () => {
6804 const messages = linter.verifyAndFix("var a", {
6805 rules: {
6806 semi: 2
6807 }
6808 }, { filename: "test.js" });
6809
6810 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
6811 assert.isTrue(messages.fixed);
6812 });
6813
6814 it("does not require a third argument", () => {
6815 const fixResult = linter.verifyAndFix("var a", {
6816 rules: {
6817 semi: 2
6818 }
6819 });
6820
6821 assert.deepStrictEqual(fixResult, {
6822 fixed: true,
6823 messages: [],
6824 output: "var a;"
6825 });
6826 });
6827
6828 it("does not include suggestions in autofix results", () => {
6829 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
6830 rules: {
6831 semi: 2,
6832 "no-useless-escape": 2
6833 }
6834 });
6835
6836 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
6837 assert.strictEqual(fixResult.fixed, true);
6838 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
6839 });
6840
6841 it("does not apply autofixes when fix argument is `false`", () => {
6842 const fixResult = linter.verifyAndFix("var a", {
6843 rules: {
6844 semi: 2
6845 }
6846 }, { fix: false });
6847
6848 assert.strictEqual(fixResult.fixed, false);
6849 });
6850
6851 it("stops fixing after 10 passes", () => {
6852
6853 linter.defineRule("add-spaces", {
6854 meta: {
6855 fixable: "whitespace"
6856 },
6857 create(context) {
6858 return {
6859 Program(node) {
6860 context.report({
6861 node,
6862 message: "Add a space before this node.",
6863 fix: fixer => fixer.insertTextBefore(node, " ")
6864 });
6865 }
6866 };
6867 }
6868 });
6869
6870 const fixResult = linter.verifyAndFix("a", { rules: { "add-spaces": "error" } });
6871
6872 assert.strictEqual(fixResult.fixed, true);
6873 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a`);
6874 assert.strictEqual(fixResult.messages.length, 1);
6875 });
6876
6877 it("should throw an error if fix is passed but meta has no `fixable` property", () => {
6878 linter.defineRule("test-rule", {
6879 meta: {
6880 docs: {},
6881 schema: []
6882 },
6883 create: context => ({
6884 Program(node) {
6885 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6886 }
6887 })
6888 });
6889
6890 assert.throws(() => {
6891 linter.verify("0", { rules: { "test-rule": "error" } });
6892 }, /Fixable rules must set the `meta\.fixable` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test-rule"$/u);
6893 });
6894
6895 it("should throw an error if fix is passed and there is no metadata", () => {
6896 linter.defineRule("test-rule", {
6897 create: context => ({
6898 Program(node) {
6899 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6900 }
6901 })
6902 });
6903
6904 assert.throws(() => {
6905 linter.verify("0", { rules: { "test-rule": "error" } });
6906 }, /Fixable rules must set the `meta\.fixable` property/u);
6907 });
6908
6909 it("should throw an error if fix is passed from a legacy-format rule", () => {
6910 linter.defineRule("test-rule", context => ({
6911 Program(node) {
6912 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6913 }
6914 }));
6915
6916 assert.throws(() => {
6917 linter.verify("0", { rules: { "test-rule": "error" } });
6918 }, /Fixable rules must set the `meta\.fixable` property/u);
6919 });
6920 });
6921
6922 describe("Edge cases", () => {
6923
6924 it("should properly parse import statements when sourceType is module", () => {
6925 const code = "import foo from 'foo';";
6926 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6927 const suppressedMessages = linter.getSuppressedMessages();
6928
6929 assert.strictEqual(messages.length, 0);
6930 assert.strictEqual(suppressedMessages.length, 0);
6931 });
6932
6933 it("should properly parse import all statements when sourceType is module", () => {
6934 const code = "import * as foo from 'foo';";
6935 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6936 const suppressedMessages = linter.getSuppressedMessages();
6937
6938 assert.strictEqual(messages.length, 0);
6939 assert.strictEqual(suppressedMessages.length, 0);
6940 });
6941
6942 it("should properly parse default export statements when sourceType is module", () => {
6943 const code = "export default function initialize() {}";
6944 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6945 const suppressedMessages = linter.getSuppressedMessages();
6946
6947 assert.strictEqual(messages.length, 0);
6948 assert.strictEqual(suppressedMessages.length, 0);
6949 });
6950
6951 // https://github.com/eslint/eslint/issues/9687
6952 it("should report an error when invalid parserOptions found", () => {
6953 let messages = linter.verify("", { parserOptions: { ecmaVersion: 222 } });
6954 let suppressedMessages = linter.getSuppressedMessages();
6955
6956 assert.deepStrictEqual(messages.length, 1);
6957 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
6958 assert.strictEqual(suppressedMessages.length, 0);
6959
6960 messages = linter.verify("", { parserOptions: { sourceType: "foo" } });
6961 suppressedMessages = linter.getSuppressedMessages();
6962
6963 assert.deepStrictEqual(messages.length, 1);
6964 assert.ok(messages[0].message.includes("Invalid sourceType"));
6965 assert.strictEqual(suppressedMessages.length, 0);
6966
6967 messages = linter.verify("", { parserOptions: { ecmaVersion: 5, sourceType: "module" } });
6968 suppressedMessages = linter.getSuppressedMessages();
6969
6970 assert.deepStrictEqual(messages.length, 1);
6971 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
6972 assert.strictEqual(suppressedMessages.length, 0);
6973 });
6974
6975 it("should not crash when invalid parentheses syntax is encountered", () => {
6976 linter.verify("left = (aSize.width/2) - ()");
6977 });
6978
6979 it("should not crash when let is used inside of switch case", () => {
6980 linter.verify("switch(foo) { case 1: let bar=2; }", { parserOptions: { ecmaVersion: 6 } });
6981 });
6982
6983 it("should not crash when parsing destructured assignment", () => {
6984 linter.verify("var { a='a' } = {};", { parserOptions: { ecmaVersion: 6 } });
6985 });
6986
6987 it("should report syntax error when a keyword exists in object property shorthand", () => {
6988 const messages = linter.verify("let a = {this}", { parserOptions: { ecmaVersion: 6 } });
6989 const suppressedMessages = linter.getSuppressedMessages();
6990
6991 assert.strictEqual(messages.length, 1);
6992 assert.strictEqual(messages[0].fatal, true);
6993
6994 assert.strictEqual(suppressedMessages.length, 0);
6995 });
6996
6997 it("should not rewrite env setting in core (https://github.com/eslint/eslint/issues/4814)", () => {
6998
6999 /*
7000 * This test focuses on the instance of https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/conf/environments.js#L26-L28
7001 * This `verify()` takes the instance and runs https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/lib/eslint.js#L416
7002 */
7003 linter.defineRule("test", () => ({}));
7004 linter.verify("var a = 0;", {
7005 env: { node: true },
7006 parserOptions: { ecmaVersion: 6, sourceType: "module" },
7007 rules: { test: 2 }
7008 });
7009
7010 // This `verify()` takes the instance and tests that the instance was not modified.
7011 let ok = false;
7012
7013 linter.defineRule("test", context => {
7014 assert(
7015 context.parserOptions.ecmaFeatures.globalReturn,
7016 "`ecmaFeatures.globalReturn` of the node environment should not be modified."
7017 );
7018 ok = true;
7019 return {};
7020 });
7021 linter.verify("var a = 0;", {
7022 env: { node: true },
7023 rules: { test: 2 }
7024 });
7025
7026 assert(ok);
7027 });
7028
7029 it("should throw when rule's create() function does not return an object", () => {
7030 const config = { rules: { checker: "error" } };
7031
7032 linter.defineRule("checker", () => null); // returns null
7033
7034 assert.throws(() => {
7035 linter.verify("abc", config, filename);
7036 }, "The create() function for rule 'checker' did not return an object.");
7037
7038 linter.defineRule("checker", () => {}); // returns undefined
7039
7040 assert.throws(() => {
7041 linter.verify("abc", config, filename);
7042 }, "The create() function for rule 'checker' did not return an object.");
7043 });
7044 });
7045
7046 describe("Custom parser", () => {
7047
7048 const errorPrefix = "Parsing error: ";
7049
7050 it("should have file path passed to it", () => {
7051 const code = "/* this is code */";
7052 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
7053
7054 linter.defineParser("stub-parser", testParsers.stubParser);
7055 linter.verify(code, { parser: "stub-parser" }, filename, true);
7056
7057 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
7058 });
7059
7060 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
7061 const code = "var myDivElement = <div {...this.props} />;";
7062
7063 linter.defineParser("esprima", esprima);
7064 const messages = linter.verify(code, { parser: "esprima", parserOptions: { jsx: true } }, "filename");
7065 const suppressedMessages = linter.getSuppressedMessages();
7066
7067 assert.strictEqual(messages.length, 0);
7068 assert.strictEqual(suppressedMessages.length, 0);
7069 });
7070
7071 it("should return an error when the custom parser can't be found", () => {
7072 const code = "var myDivElement = <div {...this.props} />;";
7073 const messages = linter.verify(code, { parser: "esprima-xyz" }, "filename");
7074 const suppressedMessages = linter.getSuppressedMessages();
7075
7076 assert.strictEqual(messages.length, 1);
7077 assert.strictEqual(messages[0].severity, 2);
7078 assert.strictEqual(messages[0].message, "Configured parser 'esprima-xyz' was not found.");
7079
7080 assert.strictEqual(suppressedMessages.length, 0);
7081 });
7082
7083 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
7084 const code = "null %% 'foo'";
7085
7086 linter.defineParser("unknown-logical-operator", testParsers.unknownLogicalOperator);
7087
7088 // This shouldn't throw
7089 const messages = linter.verify(code, { parser: "unknown-logical-operator" }, filename, true);
7090 const suppressedMessages = linter.getSuppressedMessages();
7091
7092 assert.strictEqual(messages.length, 0);
7093 assert.strictEqual(suppressedMessages.length, 0);
7094 });
7095
7096 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
7097 const code = "foo && bar %% baz";
7098
7099 linter.defineParser("unknown-logical-operator-nested", testParsers.unknownLogicalOperatorNested);
7100
7101 // This shouldn't throw
7102 const messages = linter.verify(code, { parser: "unknown-logical-operator-nested" }, filename, true);
7103 const suppressedMessages = linter.getSuppressedMessages();
7104
7105 assert.strictEqual(messages.length, 0);
7106 assert.strictEqual(suppressedMessages.length, 0);
7107 });
7108
7109 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
7110 const code = "foo && bar %% baz";
7111
7112 const nodes = [];
7113
7114 linter.defineRule("collect-node-types", () => ({
7115 "*"(node) {
7116 nodes.push(node.type);
7117 }
7118 }));
7119
7120 linter.defineParser("non-js-parser", testParsers.nonJSParser);
7121
7122 const messages = linter.verify(code, {
7123 parser: "non-js-parser",
7124 rules: {
7125 "collect-node-types": "error"
7126 }
7127 }, filename, true);
7128 const suppressedMessages = linter.getSuppressedMessages();
7129
7130 assert.strictEqual(messages.length, 0);
7131 assert.isTrue(nodes.length > 0);
7132
7133 assert.strictEqual(suppressedMessages.length, 0);
7134 });
7135
7136 it("should strip leading line: prefix from parser error", () => {
7137 linter.defineParser("line-error", testParsers.lineError);
7138 const messages = linter.verify(";", { parser: "line-error" }, "filename");
7139 const suppressedMessages = linter.getSuppressedMessages();
7140
7141 assert.strictEqual(messages.length, 1);
7142 assert.strictEqual(messages[0].severity, 2);
7143 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
7144
7145 assert.strictEqual(suppressedMessages.length, 0);
7146 });
7147
7148 it("should not modify a parser error message without a leading line: prefix", () => {
7149 linter.defineParser("no-line-error", testParsers.noLineError);
7150 const messages = linter.verify(";", { parser: "no-line-error" }, filename);
7151 const suppressedMessages = linter.getSuppressedMessages();
7152
7153 assert.strictEqual(messages.length, 1);
7154 assert.strictEqual(messages[0].severity, 2);
7155 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
7156
7157 assert.strictEqual(suppressedMessages.length, 0);
7158 });
7159
7160 describe("if a parser provides 'visitorKeys'", () => {
7161 let types = [];
7162 let sourceCode;
7163 let scopeManager;
7164 let firstChildNodes = [];
7165
7166 beforeEach(() => {
7167 types = [];
7168 firstChildNodes = [];
7169 linter.defineRule("collect-node-types", () => ({
7170 "*"(node) {
7171 types.push(node.type);
7172 }
7173 }));
7174 linter.defineRule("save-scope-manager", context => {
7175 scopeManager = context.getSourceCode().scopeManager;
7176
7177 return {};
7178 });
7179 linter.defineRule("esquery-option", () => ({
7180 ":first-child"(node) {
7181 firstChildNodes.push(node);
7182 }
7183 }));
7184 linter.defineParser("enhanced-parser2", testParsers.enhancedParser2);
7185 linter.verify("@foo class A {}", {
7186 parser: "enhanced-parser2",
7187 rules: {
7188 "collect-node-types": "error",
7189 "save-scope-manager": "error",
7190 "esquery-option": "error"
7191 }
7192 });
7193
7194 sourceCode = linter.getSourceCode();
7195 });
7196
7197 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
7198 assert.deepStrictEqual(
7199 types,
7200 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7201 );
7202 });
7203
7204 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7205 assert.deepStrictEqual(
7206 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
7207 ["experimentalDecorators", "id", "superClass", "body"]
7208 );
7209 });
7210
7211 it("should use the same visitorKeys if the source code object is reused", () => {
7212 const types2 = [];
7213
7214 linter.defineRule("collect-node-types", () => ({
7215 "*"(node) {
7216 types2.push(node.type);
7217 }
7218 }));
7219 linter.verify(sourceCode, {
7220 rules: {
7221 "collect-node-types": "error"
7222 }
7223 });
7224
7225 assert.deepStrictEqual(
7226 types2,
7227 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7228 );
7229 });
7230
7231 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7232 assert.deepStrictEqual(
7233 firstChildNodes,
7234 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
7235 );
7236 });
7237 });
7238
7239 describe("if a parser provides 'scope'", () => {
7240 let scope = null;
7241 let sourceCode = null;
7242
7243 beforeEach(() => {
7244 linter.defineParser("enhanced-parser3", testParsers.enhancedParser3);
7245 linter.defineRule("save-scope1", context => ({
7246 Program() {
7247 scope = context.getScope();
7248 }
7249 }));
7250 linter.verify("@foo class A {}", { parser: "enhanced-parser3", rules: { "save-scope1": 2 } });
7251
7252 sourceCode = linter.getSourceCode();
7253 });
7254
7255 it("should use the scope (so the global scope has the reference of '@foo')", () => {
7256 assert.strictEqual(scope.references.length, 1);
7257 assert.deepStrictEqual(
7258 scope.references[0].identifier.name,
7259 "foo"
7260 );
7261 });
7262
7263 it("should use the same scope if the source code object is reused", () => {
7264 let scope2 = null;
7265
7266 linter.defineRule("save-scope2", context => ({
7267 Program() {
7268 scope2 = context.getScope();
7269 }
7270 }));
7271 linter.verify(sourceCode, { rules: { "save-scope2": 2 } }, "test.js");
7272
7273 assert(scope2 !== null);
7274 assert(scope2 === scope);
7275 });
7276 });
7277
7278 it("should not pass any default parserOptions to the parser", () => {
7279 linter.defineParser("throws-with-options", testParsers.throwsWithOptions);
7280 const messages = linter.verify(";", { parser: "throws-with-options" }, "filename");
7281 const suppressedMessages = linter.getSuppressedMessages();
7282
7283 assert.strictEqual(messages.length, 0);
7284 assert.strictEqual(suppressedMessages.length, 0);
7285 });
7286 });
7287
7288 describe("merging 'parserOptions'", () => {
7289 it("should deeply merge 'parserOptions' from an environment with 'parserOptions' from the provided config", () => {
7290 const code = "return <div/>";
7291 const config = {
7292 env: {
7293 node: true // ecmaFeatures: { globalReturn: true }
7294 },
7295 parserOptions: {
7296 ecmaFeatures: {
7297 jsx: true
7298 }
7299 }
7300 };
7301
7302 const messages = linter.verify(code, config);
7303 const suppressedMessages = linter.getSuppressedMessages();
7304
7305 // no parsing errors
7306 assert.strictEqual(messages.length, 0);
7307 assert.strictEqual(suppressedMessages.length, 0);
7308 });
7309 });
7310 });
7311
7312 describe("Linter with FlatConfigArray", () => {
7313
7314 let linter;
7315 const filename = "filename.js";
7316
7317 /**
7318 * Creates a config array with some default properties.
7319 * @param {FlatConfig|FlatConfig[]} value The value to base the
7320 * config array on.
7321 * @returns {FlatConfigArray} The created config array.
7322 */
7323 function createFlatConfigArray(value) {
7324 return new FlatConfigArray(value, { basePath: "" });
7325 }
7326
7327 beforeEach(() => {
7328 linter = new Linter({ configType: "flat" });
7329 });
7330
7331 describe("Static Members", () => {
7332 describe("version", () => {
7333 it("should return same version as instance property", () => {
7334 assert.strictEqual(Linter.version, linter.version);
7335 });
7336 });
7337 });
7338
7339 describe("Config Options", () => {
7340
7341 describe("languageOptions", () => {
7342
7343 describe("ecmaVersion", () => {
7344
7345 it("should error when accessing a global that isn't available in ecmaVersion 5", () => {
7346 const messages = linter.verify("new Map()", {
7347 languageOptions: {
7348 ecmaVersion: 5,
7349 sourceType: "script"
7350 },
7351 rules: {
7352 "no-undef": "error"
7353 }
7354 });
7355 const suppressedMessages = linter.getSuppressedMessages();
7356
7357 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7358 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7359
7360 assert.strictEqual(suppressedMessages.length, 0);
7361 });
7362
7363 it("should error when accessing a global that isn't available in ecmaVersion 3", () => {
7364 const messages = linter.verify("JSON.stringify({})", {
7365 languageOptions: {
7366 ecmaVersion: 3,
7367 sourceType: "script"
7368 },
7369 rules: {
7370 "no-undef": "error"
7371 }
7372 });
7373 const suppressedMessages = linter.getSuppressedMessages();
7374
7375 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7376 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7377
7378 assert.strictEqual(suppressedMessages.length, 0);
7379 });
7380
7381 it("should add globals for ES6 when ecmaVersion is 6", () => {
7382 const messages = linter.verify("new Map()", {
7383 languageOptions: {
7384 ecmaVersion: 6
7385 },
7386 rules: {
7387 "no-undef": "error"
7388 }
7389 });
7390 const suppressedMessages = linter.getSuppressedMessages();
7391
7392 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7393 assert.strictEqual(suppressedMessages.length, 0);
7394 });
7395
7396 it("should allow destructuring when ecmaVersion is 6", () => {
7397 const messages = linter.verify("let {a} = b", {
7398 languageOptions: {
7399 ecmaVersion: 6
7400 }
7401 });
7402 const suppressedMessages = linter.getSuppressedMessages();
7403
7404 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7405 assert.strictEqual(suppressedMessages.length, 0);
7406 });
7407
7408 it("ecmaVersion should be normalized to year name for ES 6", () => {
7409 const config = {
7410 plugins: {
7411 test: {
7412 rules: {
7413 checker(context) {
7414 return {
7415 Program() {
7416 assert.strictEqual(context.languageOptions.ecmaVersion, 2015);
7417 }
7418 };
7419 }
7420 }
7421 }
7422 },
7423 languageOptions: {
7424 ecmaVersion: 6
7425 },
7426 rules: { "test/checker": "error" }
7427 };
7428
7429 linter.verify("foo", config, filename);
7430 });
7431
7432 it("ecmaVersion should be normalized to latest year by default", () => {
7433 const config = {
7434 plugins: {
7435 test: {
7436 rules: {
7437 checker(context) {
7438 return {
7439 Program() {
7440 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7441 }
7442 };
7443 }
7444 }
7445 }
7446 },
7447 rules: { "test/checker": "error" }
7448 };
7449
7450 linter.verify("foo", config, filename);
7451 });
7452
7453 it("ecmaVersion should not be normalized to year name for ES 5", () => {
7454 const config = {
7455 plugins: {
7456 test: {
7457 rules: {
7458 checker(context) {
7459 return {
7460 Program() {
7461 assert.strictEqual(context.languageOptions.ecmaVersion, 5);
7462 }
7463 };
7464 }
7465 }
7466 }
7467 },
7468 languageOptions: {
7469 ecmaVersion: 5
7470 },
7471 rules: { "test/checker": "error" }
7472 };
7473
7474 linter.verify("foo", config, filename);
7475 });
7476
7477 it("ecmaVersion should be normalized to year name for 'latest'", () => {
7478 const config = {
7479 plugins: {
7480 test: {
7481 rules: {
7482 checker(context) {
7483 return {
7484 Program() {
7485 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7486 }
7487 };
7488 }
7489 }
7490 }
7491 },
7492 languageOptions: {
7493 ecmaVersion: "latest"
7494 },
7495 rules: { "test/checker": "error" }
7496 };
7497
7498 linter.verify("foo", config, filename);
7499 });
7500
7501
7502 });
7503
7504 describe("sourceType", () => {
7505
7506 it("should be module by default", () => {
7507 const config = {
7508 plugins: {
7509 test: {
7510 rules: {
7511 checker(context) {
7512 return {
7513 Program() {
7514 assert.strictEqual(context.languageOptions.sourceType, "module");
7515 }
7516 };
7517 }
7518 }
7519 }
7520 },
7521 rules: { "test/checker": "error" }
7522 };
7523
7524 linter.verify("import foo from 'bar'", config, filename);
7525 });
7526
7527 it("should default to commonjs when passed a .cjs filename", () => {
7528 const config = {
7529 plugins: {
7530 test: {
7531 rules: {
7532 checker(context) {
7533 return {
7534 Program() {
7535 assert.strictEqual(context.languageOptions.sourceType, "commonjs");
7536 }
7537 };
7538 }
7539 }
7540 }
7541 },
7542 rules: { "test/checker": "error" }
7543 };
7544
7545 linter.verify("import foo from 'bar'", config, `${filename}.cjs`);
7546 });
7547
7548
7549 it("should error when import is used in a script", () => {
7550 const messages = linter.verify("import foo from 'bar';", {
7551 languageOptions: {
7552 ecmaVersion: 6,
7553 sourceType: "script"
7554 }
7555 });
7556
7557 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
7558 assert.strictEqual(messages[0].message, "Parsing error: 'import' and 'export' may appear only with 'sourceType: module'");
7559 });
7560
7561 it("should not error when import is used in a module", () => {
7562 const messages = linter.verify("import foo from 'bar';", {
7563 languageOptions: {
7564 ecmaVersion: 6,
7565 sourceType: "module"
7566 }
7567 });
7568 const suppressedMessages = linter.getSuppressedMessages();
7569
7570 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7571 assert.strictEqual(suppressedMessages.length, 0);
7572 });
7573
7574 it("should error when return is used at the top-level outside of commonjs", () => {
7575 const messages = linter.verify("return", {
7576 languageOptions: {
7577 ecmaVersion: 6,
7578 sourceType: "script"
7579 }
7580 });
7581 const suppressedMessages = linter.getSuppressedMessages();
7582
7583 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
7584 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
7585
7586 assert.strictEqual(suppressedMessages.length, 0);
7587 });
7588
7589 it("should not error when top-level return is used in commonjs", () => {
7590 const messages = linter.verify("return", {
7591 languageOptions: {
7592 ecmaVersion: 6,
7593 sourceType: "commonjs"
7594 }
7595 });
7596 const suppressedMessages = linter.getSuppressedMessages();
7597
7598 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7599 assert.strictEqual(suppressedMessages.length, 0);
7600 });
7601
7602 it("should error when accessing a Node.js global outside of commonjs", () => {
7603 const messages = linter.verify("require()", {
7604 languageOptions: {
7605 ecmaVersion: 6
7606 },
7607 rules: {
7608 "no-undef": "error"
7609 }
7610 });
7611 const suppressedMessages = linter.getSuppressedMessages();
7612
7613 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7614 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7615
7616 assert.strictEqual(suppressedMessages.length, 0);
7617 });
7618
7619 it("should add globals for Node.js when sourceType is commonjs", () => {
7620 const messages = linter.verify("require()", {
7621 languageOptions: {
7622 ecmaVersion: 6,
7623 sourceType: "commonjs"
7624 },
7625 rules: {
7626 "no-undef": "error"
7627 }
7628 });
7629 const suppressedMessages = linter.getSuppressedMessages();
7630
7631 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7632 assert.strictEqual(suppressedMessages.length, 0);
7633 });
7634
7635 it("should allow 'await' as a property name in modules", () => {
7636 const result = linter.verify(
7637 "obj.await",
7638 {
7639 languageOptions: {
7640 ecmaVersion: 6,
7641 sourceType: "module"
7642 }
7643 }
7644 );
7645 const suppressedMessages = linter.getSuppressedMessages();
7646
7647 assert(result.length === 0);
7648 assert.strictEqual(suppressedMessages.length, 0);
7649 });
7650
7651 });
7652
7653 describe("parser", () => {
7654
7655 it("should be able to define a custom parser", () => {
7656 const parser = {
7657 parseForESLint: function parse(code, options) {
7658 return {
7659 ast: esprima.parse(code, options),
7660 services: {
7661 test: {
7662 getMessage() {
7663 return "Hi!";
7664 }
7665 }
7666 }
7667 };
7668 }
7669 };
7670
7671 const config = {
7672 plugins: {
7673 test: {
7674 parsers: {
7675 "test-parser": parser
7676 }
7677 }
7678 },
7679 languageOptions: {
7680 parser: "test/test-parser"
7681 }
7682 };
7683
7684
7685 const messages = linter.verify("0", config, filename);
7686 const suppressedMessages = linter.getSuppressedMessages();
7687
7688 assert.strictEqual(messages.length, 0);
7689 assert.strictEqual(suppressedMessages.length, 0);
7690 });
7691
7692 it("should pass parser as context.languageOptions.parser to all rules when provided on config", () => {
7693
7694 const config = {
7695 plugins: {
7696 test: {
7697 rules: {
7698 "test-rule": sinon.mock().withArgs(
7699 sinon.match({ languageOptions: { parser: esprima } })
7700 ).returns({})
7701 }
7702 }
7703 },
7704 languageOptions: {
7705 parser: esprima
7706 },
7707 rules: {
7708 "test/test-rule": 2
7709 }
7710 };
7711
7712 linter.verify("0", config, filename);
7713 });
7714
7715 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
7716 const config = {
7717 plugins: {
7718 test: {
7719 parsers: {
7720 "enhanced-parser": testParsers.enhancedParser
7721 }
7722 }
7723 },
7724 languageOptions: {
7725 parser: "test/enhanced-parser"
7726 }
7727 };
7728
7729 const messages = linter.verify("0", config, filename);
7730 const suppressedMessages = linter.getSuppressedMessages();
7731
7732 assert.strictEqual(messages.length, 0);
7733 assert.strictEqual(suppressedMessages.length, 0);
7734 });
7735
7736 it("should expose parser services when using parseForESLint() and services are specified", () => {
7737
7738 const config = {
7739 plugins: {
7740 test: {
7741 parsers: {
7742 "enhanced-parser": testParsers.enhancedParser
7743 },
7744 rules: {
7745 "test-service-rule": context => ({
7746 Literal(node) {
7747 context.report({
7748 node,
7749 message: context.parserServices.test.getMessage()
7750 });
7751 }
7752 })
7753 }
7754 }
7755 },
7756 languageOptions: {
7757 parser: "test/enhanced-parser"
7758 },
7759 rules: {
7760 "test/test-service-rule": 2
7761 }
7762 };
7763
7764 const messages = linter.verify("0", config, filename);
7765 const suppressedMessages = linter.getSuppressedMessages();
7766
7767 assert.strictEqual(messages.length, 1);
7768 assert.strictEqual(messages[0].message, "Hi!");
7769
7770 assert.strictEqual(suppressedMessages.length, 0);
7771 });
7772
7773 it("should use the same parserServices if source code object is reused", () => {
7774
7775 const config = {
7776 plugins: {
7777 test: {
7778 parsers: {
7779 "enhanced-parser": testParsers.enhancedParser
7780 },
7781 rules: {
7782 "test-service-rule": context => ({
7783 Literal(node) {
7784 context.report({
7785 node,
7786 message: context.parserServices.test.getMessage()
7787 });
7788 }
7789 })
7790 }
7791 }
7792 },
7793 languageOptions: {
7794 parser: "test/enhanced-parser"
7795 },
7796 rules: {
7797 "test/test-service-rule": 2
7798 }
7799 };
7800
7801 const messages = linter.verify("0", config, filename);
7802 const suppressedMessages = linter.getSuppressedMessages();
7803
7804 assert.strictEqual(messages.length, 1);
7805 assert.strictEqual(messages[0].message, "Hi!");
7806 assert.strictEqual(suppressedMessages.length, 0);
7807
7808 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
7809 const suppressedMessages2 = linter.getSuppressedMessages();
7810
7811 assert.strictEqual(messages2.length, 1);
7812 assert.strictEqual(messages2[0].message, "Hi!");
7813 assert.strictEqual(suppressedMessages2.length, 0);
7814 });
7815
7816 it("should pass parser as context.languageOptions.parser to all rules when default parser is used", () => {
7817
7818 // references to Espree get messed up in a browser context, so wrap it
7819 const fakeParser = {
7820 parse: espree.parse
7821 };
7822
7823 const spy = sinon.spy(context => {
7824 assert.strictEqual(context.languageOptions.parser, fakeParser);
7825 return {};
7826 });
7827
7828 const config = {
7829 plugins: {
7830 test: {
7831 rules: {
7832 "test-rule": spy
7833 }
7834 }
7835 },
7836 languageOptions: {
7837 parser: fakeParser
7838 },
7839 rules: {
7840 "test/test-rule": 2
7841 }
7842 };
7843
7844 linter.verify("0", config, filename);
7845 assert.isTrue(spy.calledOnce);
7846 });
7847
7848
7849 describe("Custom Parsers", () => {
7850
7851 const errorPrefix = "Parsing error: ";
7852
7853 it("should have file path passed to it", () => {
7854 const code = "/* this is code */";
7855 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
7856 const config = {
7857 languageOptions: {
7858 parser: testParsers.stubParser
7859 }
7860 };
7861
7862 linter.verify(code, config, filename, true);
7863
7864 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
7865 });
7866
7867 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
7868 const code = "var myDivElement = <div {...this.props} />;";
7869 const config = {
7870 languageOptions: {
7871 parser: esprima,
7872 parserOptions: {
7873 jsx: true
7874 }
7875 }
7876 };
7877
7878 const messages = linter.verify(code, config, filename);
7879 const suppressedMessages = linter.getSuppressedMessages();
7880
7881 assert.strictEqual(messages.length, 0);
7882 assert.strictEqual(suppressedMessages.length, 0);
7883 });
7884
7885 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
7886 const code = "null %% 'foo'";
7887 const config = {
7888 languageOptions: {
7889 parser: testParsers.unknownLogicalOperator
7890 }
7891 };
7892
7893 // This shouldn't throw
7894 const messages = linter.verify(code, config, filename);
7895 const suppressedMessages = linter.getSuppressedMessages();
7896
7897 assert.strictEqual(messages.length, 0);
7898 assert.strictEqual(suppressedMessages.length, 0);
7899 });
7900
7901 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
7902 const code = "foo && bar %% baz";
7903 const config = {
7904 languageOptions: {
7905 parser: testParsers.unknownLogicalOperatorNested
7906 }
7907 };
7908
7909 // This shouldn't throw
7910 const messages = linter.verify(code, config, filename);
7911 const suppressedMessages = linter.getSuppressedMessages();
7912
7913 assert.strictEqual(messages.length, 0);
7914 assert.strictEqual(suppressedMessages.length, 0);
7915 });
7916
7917 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
7918 const code = "foo && bar %% baz";
7919 const nodes = [];
7920 const config = {
7921 plugins: {
7922 test: {
7923 rules: {
7924 "collect-node-types": () => ({
7925 "*"(node) {
7926 nodes.push(node.type);
7927 }
7928 })
7929 }
7930 }
7931 },
7932 languageOptions: {
7933 parser: testParsers.nonJSParser
7934 },
7935 rules: {
7936 "test/collect-node-types": "error"
7937 }
7938 };
7939
7940 const messages = linter.verify(code, config, filename, true);
7941 const suppressedMessages = linter.getSuppressedMessages();
7942
7943 assert.strictEqual(messages.length, 0);
7944 assert.isTrue(nodes.length > 0);
7945 assert.strictEqual(suppressedMessages.length, 0);
7946 });
7947
7948 it("should strip leading line: prefix from parser error", () => {
7949 const messages = linter.verify(";", {
7950 languageOptions: {
7951 parser: testParsers.lineError
7952 }
7953 }, filename);
7954 const suppressedMessages = linter.getSuppressedMessages();
7955
7956 assert.strictEqual(messages.length, 1);
7957 assert.strictEqual(messages[0].severity, 2);
7958 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
7959
7960 assert.strictEqual(suppressedMessages.length, 0);
7961 });
7962
7963 it("should not modify a parser error message without a leading line: prefix", () => {
7964 const messages = linter.verify(";", {
7965 languageOptions: {
7966 parser: testParsers.noLineError
7967 }
7968 }, filename);
7969 const suppressedMessages = linter.getSuppressedMessages();
7970
7971 assert.strictEqual(messages.length, 1);
7972 assert.strictEqual(messages[0].severity, 2);
7973 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
7974
7975 assert.strictEqual(suppressedMessages.length, 0);
7976 });
7977
7978 describe("if a parser provides 'visitorKeys'", () => {
7979 let types = [];
7980 let sourceCode;
7981 let scopeManager;
7982 let firstChildNodes = [];
7983
7984 beforeEach(() => {
7985 types = [];
7986 firstChildNodes = [];
7987 const config = {
7988 plugins: {
7989 test: {
7990 rules: {
7991 "collect-node-types": () => ({
7992 "*"(node) {
7993 types.push(node.type);
7994 }
7995 }),
7996 "save-scope-manager": context => {
7997 scopeManager = context.getSourceCode().scopeManager;
7998
7999 return {};
8000 },
8001 "esquery-option": () => ({
8002 ":first-child"(node) {
8003 firstChildNodes.push(node);
8004 }
8005 })
8006 }
8007 }
8008 },
8009 languageOptions: {
8010 parser: testParsers.enhancedParser2
8011 },
8012 rules: {
8013 "test/collect-node-types": "error",
8014 "test/save-scope-manager": "error",
8015 "test/esquery-option": "error"
8016 }
8017 };
8018
8019 linter.verify("@foo class A {}", config);
8020
8021 sourceCode = linter.getSourceCode();
8022 });
8023
8024 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
8025 assert.deepStrictEqual(
8026 types,
8027 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
8028 );
8029 });
8030
8031 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
8032 assert.deepStrictEqual(
8033 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
8034 ["experimentalDecorators", "id", "superClass", "body"]
8035 );
8036 });
8037
8038 it("should use the same visitorKeys if the source code object is reused", () => {
8039 const types2 = [];
8040 const config = {
8041 plugins: {
8042 test: {
8043 rules: {
8044 "collect-node-types": () => ({
8045 "*"(node) {
8046 types2.push(node.type);
8047 }
8048 })
8049 }
8050 }
8051 },
8052 rules: {
8053 "test/collect-node-types": "error"
8054 }
8055 };
8056
8057 linter.verify(sourceCode, config);
8058
8059 assert.deepStrictEqual(
8060 types2,
8061 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
8062 );
8063 });
8064
8065 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
8066 assert.deepStrictEqual(
8067 firstChildNodes,
8068 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
8069 );
8070 });
8071 });
8072
8073 describe("if a parser provides 'scope'", () => {
8074 let scope = null;
8075 let sourceCode = null;
8076
8077 beforeEach(() => {
8078 const config = {
8079 plugins: {
8080 test: {
8081 rules: {
8082 "save-scope1": context => ({
8083 Program() {
8084 scope = context.getScope();
8085 }
8086 })
8087 }
8088 }
8089 },
8090 languageOptions: {
8091 parser: testParsers.enhancedParser3
8092 },
8093 rules: {
8094 "test/save-scope1": "error"
8095 }
8096 };
8097
8098 linter.verify("@foo class A {}", config);
8099
8100 sourceCode = linter.getSourceCode();
8101 });
8102
8103 it("should use the scope (so the global scope has the reference of '@foo')", () => {
8104 assert.strictEqual(scope.references.length, 1);
8105 assert.deepStrictEqual(
8106 scope.references[0].identifier.name,
8107 "foo"
8108 );
8109 });
8110
8111 it("should use the same scope if the source code object is reused", () => {
8112 let scope2 = null;
8113 const config = {
8114 plugins: {
8115 test: {
8116 rules: {
8117 "save-scope2": context => ({
8118 Program() {
8119 scope2 = context.getScope();
8120 }
8121 })
8122 }
8123 }
8124 },
8125 rules: {
8126 "test/save-scope2": "error"
8127 }
8128 };
8129
8130 linter.verify(sourceCode, config, "test.js");
8131
8132 assert(scope2 !== null);
8133 assert(scope2 === scope);
8134 });
8135 });
8136
8137 it("should pass default languageOptions to the parser", () => {
8138
8139 const spy = sinon.spy((code, options) => espree.parse(code, options));
8140
8141 linter.verify(";", {
8142 languageOptions: {
8143 parser: {
8144 parse: spy
8145 }
8146 }
8147 }, "filename.js");
8148
8149 assert(spy.calledWithMatch(";", {
8150 ecmaVersion: espree.latestEcmaVersion + 2009,
8151 sourceType: "module"
8152 }));
8153 });
8154 });
8155
8156
8157 });
8158
8159 describe("parseOptions", () => {
8160
8161 it("should pass ecmaFeatures to all rules when provided on config", () => {
8162
8163 const parserOptions = {
8164 ecmaFeatures: {
8165 jsx: true
8166 }
8167 };
8168
8169 const config = {
8170 plugins: {
8171 test: {
8172 rules: {
8173 "test-rule": sinon.mock().withArgs(
8174 sinon.match({ languageOptions: { parserOptions } })
8175 ).returns({})
8176 }
8177 }
8178 },
8179 languageOptions: {
8180 parserOptions
8181 },
8182 rules: {
8183 "test/test-rule": 2
8184 }
8185 };
8186
8187 linter.verify("0", config, filename);
8188 });
8189
8190 it("should switch globalReturn to false if sourceType is module", () => {
8191
8192 const config = {
8193 plugins: {
8194 test: {
8195 rules: {
8196 "test-rule": sinon.mock().withArgs(
8197 sinon.match({
8198 languageOptions: {
8199 parserOptions: {
8200 ecmaFeatures: {
8201 globalReturn: false
8202 }
8203 }
8204 }
8205 })
8206 ).returns({})
8207 }
8208 }
8209 },
8210 languageOptions: {
8211 sourceType: "module",
8212 parserOptions: {
8213 ecmaFeatures: {
8214 globalReturn: true
8215 }
8216 }
8217 },
8218 rules: {
8219 "test/test-rule": 2
8220 }
8221 };
8222
8223 linter.verify("0", config, filename);
8224 });
8225
8226 it("should not parse sloppy-mode code when impliedStrict is true", () => {
8227
8228 const messages = linter.verify("var private;", {
8229 languageOptions: {
8230 parserOptions: {
8231 ecmaFeatures: {
8232 impliedStrict: true
8233 }
8234 }
8235 }
8236 }, filename);
8237 const suppressedMessages = linter.getSuppressedMessages();
8238
8239 assert.strictEqual(messages.length, 1);
8240 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
8241
8242 assert.strictEqual(suppressedMessages.length, 0);
8243 });
8244
8245 it("should properly parse valid code when impliedStrict is true", () => {
8246
8247 const messages = linter.verify("var foo;", {
8248 languageOptions: {
8249 parserOptions: {
8250 ecmaFeatures: {
8251 impliedStrict: true
8252 }
8253 }
8254 }
8255 }, filename);
8256 const suppressedMessages = linter.getSuppressedMessages();
8257
8258 assert.strictEqual(messages.length, 0);
8259 assert.strictEqual(suppressedMessages.length, 0);
8260 });
8261
8262 it("should properly parse JSX when passed ecmaFeatures", () => {
8263
8264 const messages = linter.verify("var x = <div/>;", {
8265 languageOptions: {
8266 parserOptions: {
8267 ecmaFeatures: {
8268 jsx: true
8269 }
8270 }
8271 }
8272 }, filename);
8273 const suppressedMessages = linter.getSuppressedMessages();
8274
8275 assert.strictEqual(messages.length, 0);
8276 assert.strictEqual(suppressedMessages.length, 0);
8277 });
8278
8279 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
8280 const code = "var myDivElement = <div className=\"foo\" />;";
8281 const messages = linter.verify(code, {}, filename);
8282 const suppressedMessages = linter.getSuppressedMessages();
8283
8284 assert.strictEqual(messages.length, 1);
8285 assert.strictEqual(messages[0].line, 1);
8286 assert.strictEqual(messages[0].column, 20);
8287 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
8288
8289 assert.strictEqual(suppressedMessages.length, 0);
8290 });
8291
8292 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
8293 const code = "var myDivElement = <div className=\"foo\" />;";
8294 const messages = linter.verify(code, {
8295 languageOptions: {
8296 parserOptions: {
8297 ecmaFeatures: {
8298 jsx: true
8299 }
8300 }
8301 }
8302 }, filename);
8303 const suppressedMessages = linter.getSuppressedMessages();
8304
8305 assert.strictEqual(messages.length, 0);
8306 assert.strictEqual(suppressedMessages.length, 0);
8307 });
8308
8309 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
8310 const code = "var myDivElement = <div {...this.props} />;";
8311 const messages = linter.verify(code, {
8312 languageOptions: {
8313 ecmaVersion: 6,
8314 parserOptions: {
8315 ecmaFeatures: {
8316 jsx: true
8317 }
8318 }
8319 }
8320
8321 }, "filename.js");
8322 const suppressedMessages = linter.getSuppressedMessages();
8323
8324 assert.strictEqual(messages.length, 0);
8325 assert.strictEqual(suppressedMessages.length, 0);
8326 });
8327
8328 it("should not allow the use of reserved words as variable names in ES3", () => {
8329 const code = "var char;";
8330 const messages = linter.verify(code, {
8331 languageOptions: {
8332 ecmaVersion: 3,
8333 sourceType: "script"
8334 }
8335 }, filename);
8336
8337 assert.strictEqual(messages.length, 1);
8338 assert.strictEqual(messages[0].severity, 2);
8339 assert.isTrue(messages[0].fatal);
8340 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8341 });
8342
8343 it("should not allow the use of reserved words as property names in member expressions in ES3", () => {
8344 const code = "obj.char;";
8345 const messages = linter.verify(code, {
8346 languageOptions: {
8347 ecmaVersion: 3,
8348 sourceType: "script"
8349 }
8350 }, filename);
8351
8352 assert.strictEqual(messages.length, 1);
8353 assert.strictEqual(messages[0].severity, 2);
8354 assert.isTrue(messages[0].fatal);
8355 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8356 });
8357
8358 it("should not allow the use of reserved words as property names in object literals in ES3", () => {
8359 const code = "var obj = { char: 1 };";
8360 const messages = linter.verify(code, {
8361 languageOptions: {
8362 ecmaVersion: 3,
8363 sourceType: "script"
8364 }
8365 }, filename);
8366
8367 assert.strictEqual(messages.length, 1);
8368 assert.strictEqual(messages[0].severity, 2);
8369 assert.isTrue(messages[0].fatal);
8370 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8371 });
8372
8373 it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => {
8374 const code = "var char; obj.char; var obj = { char: 1 };";
8375 const messages = linter.verify(code, {
8376 languageOptions: {
8377 ecmaVersion: 3,
8378 sourceType: "script",
8379 parserOptions: {
8380 allowReserved: true
8381 }
8382 }
8383 }, filename);
8384
8385 assert.strictEqual(messages.length, 0);
8386 });
8387
8388 it("should not allow the use of reserved words as variable names in ES > 3", () => {
8389 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8390
8391 ecmaVersions.forEach(ecmaVersion => {
8392 const code = "var enum;";
8393 const messages = linter.verify(code, {
8394 languageOptions: {
8395 ...ecmaVersion ? { ecmaVersion } : {},
8396 sourceType: "script"
8397 }
8398 }, filename);
8399
8400 assert.strictEqual(messages.length, 1);
8401 assert.strictEqual(messages[0].severity, 2);
8402 assert.isTrue(messages[0].fatal);
8403 assert.match(messages[0].message, /^Parsing error:.*'enum'/u);
8404 });
8405 });
8406
8407 it("should allow the use of reserved words as property names in ES > 3", () => {
8408 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8409
8410 ecmaVersions.forEach(ecmaVersion => {
8411 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
8412 const messages = linter.verify(code, {
8413 languageOptions: {
8414 ...ecmaVersion ? { ecmaVersion } : {},
8415 sourceType: "script"
8416 }
8417 }, filename);
8418
8419 assert.strictEqual(messages.length, 0);
8420 });
8421 });
8422
8423 it("should not allow `allowReserved: true` in ES > 3", () => {
8424 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8425
8426 ecmaVersions.forEach(ecmaVersion => {
8427 const code = "";
8428 const messages = linter.verify(code, {
8429 languageOptions: {
8430 ...ecmaVersion ? { ecmaVersion } : {},
8431 sourceType: "script",
8432 parserOptions: {
8433 allowReserved: true
8434 }
8435 }
8436 }, filename);
8437
8438 assert.strictEqual(messages.length, 1);
8439 assert.strictEqual(messages[0].severity, 2);
8440 assert.isTrue(messages[0].fatal);
8441 assert.match(messages[0].message, /^Parsing error:.*allowReserved/u);
8442 });
8443 });
8444 });
8445 });
8446
8447 describe("settings", () => {
8448 const ruleId = "test-rule";
8449
8450 it("should pass settings to all rules", () => {
8451
8452 const config = {
8453 plugins: {
8454 test: {
8455 rules: {
8456 [ruleId]: context => ({
8457 Literal(node) {
8458 context.report(node, context.settings.info);
8459 }
8460 })
8461 }
8462 }
8463 },
8464 settings: {
8465 info: "Hello"
8466 },
8467 rules: {
8468 [`test/${ruleId}`]: 1
8469 }
8470 };
8471
8472 const messages = linter.verify("0", config, filename);
8473 const suppressedMessages = linter.getSuppressedMessages();
8474
8475 assert.strictEqual(messages.length, 1);
8476 assert.strictEqual(messages[0].message, "Hello");
8477
8478 assert.strictEqual(suppressedMessages.length, 0);
8479 });
8480
8481 it("should not have any settings if they were not passed in", () => {
8482
8483 const config = {
8484 plugins: {
8485 test: {
8486 rules: {
8487 [ruleId]: context => ({
8488 Literal(node) {
8489 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
8490 context.report(node, "Settings should be empty");
8491 }
8492 }
8493 })
8494 }
8495 }
8496 },
8497 settings: {
8498 },
8499 rules: {
8500 [`test/${ruleId}`]: 1
8501 }
8502 };
8503
8504 const messages = linter.verify("0", config, filename);
8505 const suppressedMessages = linter.getSuppressedMessages();
8506
8507 assert.strictEqual(messages.length, 0);
8508 assert.strictEqual(suppressedMessages.length, 0);
8509 });
8510 });
8511
8512 describe("rules", () => {
8513 const code = "var answer = 6 * 7";
8514
8515 it("should be configurable by only setting the integer value", () => {
8516 const rule = "semi",
8517 config = { rules: {} };
8518
8519 config.rules[rule] = 1;
8520
8521 const messages = linter.verify(code, config, filename, true);
8522 const suppressedMessages = linter.getSuppressedMessages();
8523
8524 assert.strictEqual(messages.length, 1);
8525 assert.strictEqual(messages[0].ruleId, rule);
8526
8527 assert.strictEqual(suppressedMessages.length, 0);
8528 });
8529
8530 it("should be configurable by only setting the string value", () => {
8531 const rule = "semi",
8532 config = { rules: {} };
8533
8534 config.rules[rule] = "warn";
8535
8536 const messages = linter.verify(code, config, filename, true);
8537 const suppressedMessages = linter.getSuppressedMessages();
8538
8539 assert.strictEqual(messages.length, 1);
8540 assert.strictEqual(messages[0].severity, 1);
8541 assert.strictEqual(messages[0].ruleId, rule);
8542
8543 assert.strictEqual(suppressedMessages.length, 0);
8544 });
8545
8546 it("should be configurable by passing in values as an array", () => {
8547 const rule = "semi",
8548 config = { rules: {} };
8549
8550 config.rules[rule] = [1];
8551
8552 const messages = linter.verify(code, config, filename, true);
8553 const suppressedMessages = linter.getSuppressedMessages();
8554
8555 assert.strictEqual(messages.length, 1);
8556 assert.strictEqual(messages[0].ruleId, rule);
8557
8558 assert.strictEqual(suppressedMessages.length, 0);
8559 });
8560
8561 it("should be configurable by passing in string value as an array", () => {
8562 const rule = "semi",
8563 config = { rules: {} };
8564
8565 config.rules[rule] = ["warn"];
8566
8567 const messages = linter.verify(code, config, filename, true);
8568 const suppressedMessages = linter.getSuppressedMessages();
8569
8570 assert.strictEqual(messages.length, 1);
8571 assert.strictEqual(messages[0].severity, 1);
8572 assert.strictEqual(messages[0].ruleId, rule);
8573
8574 assert.strictEqual(suppressedMessages.length, 0);
8575 });
8576
8577 it("should not be configurable by setting other value", () => {
8578 const rule = "semi",
8579 config = { rules: {} };
8580
8581 config.rules[rule] = "1";
8582
8583 assert.throws(() => {
8584 linter.verify(code, config, filename, true);
8585 }, /Key "rules": Key "semi"/u);
8586 });
8587
8588 it("should process empty config", () => {
8589 const config = {};
8590 const messages = linter.verify(code, config, filename, true);
8591 const suppressedMessages = linter.getSuppressedMessages();
8592
8593 assert.strictEqual(messages.length, 0);
8594 assert.strictEqual(suppressedMessages.length, 0);
8595 });
8596 });
8597
8598 });
8599
8600 describe("verify()", () => {
8601
8602 it("should report warnings in order by line and column when called", () => {
8603
8604 const code = "foo()\n alert('test')";
8605 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8606
8607 const messages = linter.verify(code, config, filename);
8608 const suppressedMessages = linter.getSuppressedMessages();
8609
8610 assert.strictEqual(messages.length, 3);
8611 assert.strictEqual(messages[0].line, 1);
8612 assert.strictEqual(messages[0].column, 6);
8613 assert.strictEqual(messages[1].line, 2);
8614 assert.strictEqual(messages[1].column, 18);
8615 assert.strictEqual(messages[2].line, 2);
8616 assert.strictEqual(messages[2].column, 18);
8617
8618 assert.strictEqual(suppressedMessages.length, 0);
8619 });
8620
8621 it("should report ignored file when filename isn't matched in the config array", () => {
8622
8623 const code = "foo()\n alert('test')";
8624 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8625
8626 const messages = linter.verify(code, config, "filename.ts");
8627
8628 assert.strictEqual(messages.length, 1);
8629 assert.deepStrictEqual(messages[0], {
8630 ruleId: null,
8631 severity: 1,
8632 message: "No matching configuration found for filename.ts.",
8633 line: 0,
8634 column: 0
8635 });
8636 });
8637
8638 describe("Plugins", () => {
8639
8640 it("should not load rule definition when rule isn't used", () => {
8641
8642 const spy = sinon.spy();
8643
8644 const config = {
8645 plugins: {
8646 test: {
8647 rules: {
8648 checker: spy
8649 }
8650 }
8651 }
8652 };
8653
8654 linter.verify("code", config, filename);
8655 assert.isTrue(spy.notCalled, "Rule should not have been called");
8656 });
8657 });
8658
8659 describe("Rule Internals", () => {
8660
8661 const code = TEST_CODE;
8662
8663 it("should throw an error when an error occurs inside of a rule visitor", () => {
8664 const config = {
8665 plugins: {
8666 test: {
8667 rules: {
8668 checker: () => ({
8669 Program() {
8670 throw new Error("Intentional error.");
8671 }
8672 })
8673 }
8674 }
8675 },
8676 rules: { "test/checker": "error" }
8677 };
8678
8679 assert.throws(() => {
8680 linter.verify(code, config, filename);
8681 }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "test/checker"`);
8682 });
8683
8684 it("should not call rule visitor with a `this` value", () => {
8685 const spy = sinon.spy();
8686 const config = {
8687 plugins: {
8688 test: {
8689 rules: {
8690 checker: () => ({
8691 Program: spy
8692 })
8693 }
8694 }
8695 },
8696 rules: { "test/checker": "error" }
8697 };
8698
8699 linter.verify("foo", config);
8700 assert(spy.calledOnce);
8701 assert.strictEqual(spy.firstCall.thisValue, void 0);
8702 });
8703
8704 it("should not call unrecognized rule visitor when present in a rule", () => {
8705 const spy = sinon.spy();
8706 const config = {
8707 plugins: {
8708 test: {
8709 rules: {
8710 checker: () => ({
8711 newListener: spy
8712 })
8713 }
8714 }
8715 },
8716 rules: {
8717 "test/checker": "error",
8718 "no-undef": "error"
8719 }
8720 };
8721
8722 linter.verify("foo", config);
8723 assert(spy.notCalled);
8724 });
8725
8726 it("should have all the `parent` properties on nodes when the rule visitors are created", () => {
8727 const spy = sinon.spy(context => {
8728 const ast = context.getSourceCode().ast;
8729
8730 assert.strictEqual(ast.body[0].parent, ast);
8731 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
8732 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
8733 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
8734
8735 return {};
8736 });
8737
8738 const config = {
8739 plugins: {
8740 test: {
8741 rules: {
8742 checker: spy
8743 }
8744 }
8745 },
8746 rules: { "test/checker": "error" }
8747 };
8748
8749 linter.verify("foo + bar", config);
8750 assert(spy.calledOnce);
8751 });
8752
8753 it("events for each node type should fire", () => {
8754
8755 // spies for various AST node types
8756 const spyLiteral = sinon.spy(),
8757 spyVariableDeclarator = sinon.spy(),
8758 spyVariableDeclaration = sinon.spy(),
8759 spyIdentifier = sinon.spy(),
8760 spyBinaryExpression = sinon.spy();
8761
8762 const config = {
8763 plugins: {
8764 test: {
8765 rules: {
8766 checker() {
8767 return {
8768 Literal: spyLiteral,
8769 VariableDeclarator: spyVariableDeclarator,
8770 VariableDeclaration: spyVariableDeclaration,
8771 Identifier: spyIdentifier,
8772 BinaryExpression: spyBinaryExpression
8773 };
8774 }
8775 }
8776 }
8777 },
8778 rules: { "test/checker": "error" }
8779 };
8780
8781 const messages = linter.verify(code, config, filename, true);
8782 const suppressedMessages = linter.getSuppressedMessages();
8783
8784 assert.strictEqual(messages.length, 0);
8785 sinon.assert.calledOnce(spyVariableDeclaration);
8786 sinon.assert.calledOnce(spyVariableDeclarator);
8787 sinon.assert.calledOnce(spyIdentifier);
8788 sinon.assert.calledTwice(spyLiteral);
8789 sinon.assert.calledOnce(spyBinaryExpression);
8790
8791 assert.strictEqual(suppressedMessages.length, 0);
8792 });
8793
8794 it("should throw an error if a rule reports a problem without a message", () => {
8795
8796 const config = {
8797 plugins: {
8798 test: {
8799 rules: {
8800 "invalid-report"(context) {
8801 return {
8802 Program(node) {
8803 context.report({ node });
8804 }
8805 };
8806 }
8807 }
8808 }
8809 },
8810 rules: { "test/invalid-report": "error" }
8811 };
8812
8813 assert.throws(
8814 () => linter.verify("foo", config),
8815 TypeError,
8816 "Missing `message` property in report() call; add a message that describes the linting problem."
8817 );
8818 });
8819
8820
8821 });
8822
8823 describe("Rule Context", () => {
8824
8825 describe("context.getFilename()", () => {
8826 const ruleId = "filename-rule";
8827
8828 it("has access to the filename", () => {
8829
8830 const config = {
8831 plugins: {
8832 test: {
8833 rules: {
8834 [ruleId]: context => ({
8835 Literal(node) {
8836 context.report(node, context.getFilename());
8837 }
8838 })
8839 }
8840 }
8841 },
8842 rules: {
8843 [`test/${ruleId}`]: 1
8844 }
8845 };
8846
8847 const messages = linter.verify("0", config, filename);
8848 const suppressedMessages = linter.getSuppressedMessages();
8849
8850 assert.strictEqual(messages[0].message, filename);
8851 assert.strictEqual(suppressedMessages.length, 0);
8852 });
8853
8854 it("defaults filename to '<input>'", () => {
8855
8856 const config = {
8857 plugins: {
8858 test: {
8859 rules: {
8860 [ruleId]: context => ({
8861 Literal(node) {
8862 context.report(node, context.getFilename());
8863 }
8864 })
8865 }
8866 }
8867 },
8868 rules: {
8869 [`test/${ruleId}`]: 1
8870 }
8871 };
8872
8873
8874 const messages = linter.verify("0", config);
8875 const suppressedMessages = linter.getSuppressedMessages();
8876
8877 assert.strictEqual(messages[0].message, "<input>");
8878 assert.strictEqual(suppressedMessages.length, 0);
8879 });
8880 });
8881
8882 describe("context.getPhysicalFilename()", () => {
8883
8884 const ruleId = "filename-rule";
8885
8886 it("has access to the physicalFilename", () => {
8887
8888 const config = {
8889 plugins: {
8890 test: {
8891 rules: {
8892 [ruleId]: context => ({
8893 Literal(node) {
8894 context.report(node, context.getPhysicalFilename());
8895 }
8896 })
8897 }
8898 }
8899 },
8900 rules: {
8901 [`test/${ruleId}`]: 1
8902 }
8903 };
8904
8905 const messages = linter.verify("0", config, filename);
8906 const suppressedMessages = linter.getSuppressedMessages();
8907
8908 assert.strictEqual(messages[0].message, filename);
8909 assert.strictEqual(suppressedMessages.length, 0);
8910 });
8911
8912 });
8913
8914 describe("context.getSourceLines()", () => {
8915
8916 it("should get proper lines when using \\n as a line break", () => {
8917 const code = "a;\nb;";
8918 const spy = sinon.spy(context => {
8919 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8920 return {};
8921 });
8922
8923 const config = {
8924 plugins: {
8925 test: {
8926 rules: {
8927 checker: spy
8928 }
8929 }
8930 },
8931 rules: { "test/checker": "error" }
8932 };
8933
8934 linter.verify(code, config);
8935 assert(spy.calledOnce);
8936 });
8937
8938 it("should get proper lines when using \\r\\n as a line break", () => {
8939 const code = "a;\r\nb;";
8940 const spy = sinon.spy(context => {
8941 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8942 return {};
8943 });
8944
8945 const config = {
8946 plugins: {
8947 test: {
8948 rules: {
8949 checker: spy
8950 }
8951 }
8952 },
8953 rules: { "test/checker": "error" }
8954 };
8955
8956 linter.verify(code, config);
8957 assert(spy.calledOnce);
8958 });
8959
8960 it("should get proper lines when using \\r as a line break", () => {
8961 const code = "a;\rb;";
8962 const spy = sinon.spy(context => {
8963 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8964 return {};
8965 });
8966
8967 const config = {
8968 plugins: {
8969 test: {
8970 rules: {
8971 checker: spy
8972 }
8973 }
8974 },
8975 rules: { "test/checker": "error" }
8976 };
8977
8978 linter.verify(code, config);
8979 assert(spy.calledOnce);
8980 });
8981
8982 it("should get proper lines when using \\u2028 as a line break", () => {
8983 const code = "a;\u2028b;";
8984 const spy = sinon.spy(context => {
8985 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8986 return {};
8987 });
8988
8989 const config = {
8990 plugins: {
8991 test: {
8992 rules: {
8993 checker: spy
8994 }
8995 }
8996 },
8997 rules: { "test/checker": "error" }
8998 };
8999
9000 linter.verify(code, config);
9001 assert(spy.calledOnce);
9002 });
9003
9004 it("should get proper lines when using \\u2029 as a line break", () => {
9005 const code = "a;\u2029b;";
9006 const spy = sinon.spy(context => {
9007 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
9008 return {};
9009 });
9010
9011 const config = {
9012 plugins: {
9013 test: {
9014 rules: {
9015 checker: spy
9016 }
9017 }
9018 },
9019 rules: { "test/checker": "error" }
9020 };
9021
9022 linter.verify(code, config);
9023 assert(spy.calledOnce);
9024 });
9025
9026 });
9027
9028 describe("context.getSource()", () => {
9029 const code = TEST_CODE;
9030
9031 it("should retrieve all text when used without parameters", () => {
9032
9033 let spy;
9034
9035 const config = {
9036 plugins: {
9037 test: {
9038 rules: {
9039 checker: context => {
9040 spy = sinon.spy(() => {
9041 assert.strictEqual(context.getSource(), TEST_CODE);
9042 });
9043 return { Program: spy };
9044 }
9045 }
9046 }
9047 },
9048 rules: { "test/checker": "error" }
9049 };
9050
9051 linter.verify(code, config);
9052 assert(spy && spy.calledOnce);
9053 });
9054
9055 it("should retrieve all text for root node", () => {
9056
9057 let spy;
9058
9059 const config = {
9060 plugins: {
9061 test: {
9062 rules: {
9063 checker: context => {
9064 spy = sinon.spy(node => {
9065 assert.strictEqual(context.getSource(node), TEST_CODE);
9066 });
9067 return { Program: spy };
9068 }
9069 }
9070 }
9071 },
9072 rules: { "test/checker": "error" }
9073 };
9074
9075 linter.verify(code, config);
9076 assert(spy && spy.calledOnce);
9077 });
9078
9079 it("should clamp to valid range when retrieving characters before start of source", () => {
9080 let spy;
9081
9082 const config = {
9083 plugins: {
9084 test: {
9085 rules: {
9086 checker: context => {
9087 spy = sinon.spy(node => {
9088 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
9089 });
9090 return { Program: spy };
9091 }
9092 }
9093 }
9094 },
9095 rules: { "test/checker": "error" }
9096 };
9097
9098 linter.verify(code, config);
9099 assert(spy && spy.calledOnce);
9100 });
9101
9102 it("should retrieve all text for binary expression", () => {
9103 let spy;
9104
9105 const config = {
9106 plugins: {
9107 test: {
9108 rules: {
9109 checker: context => {
9110 spy = sinon.spy(node => {
9111 assert.strictEqual(context.getSource(node), "6 * 7");
9112 });
9113 return { BinaryExpression: spy };
9114 }
9115 }
9116 }
9117 },
9118 rules: { "test/checker": "error" }
9119 };
9120
9121 linter.verify(code, config);
9122 assert(spy && spy.calledOnce);
9123 });
9124
9125 it("should retrieve all text plus two characters before for binary expression", () => {
9126 let spy;
9127
9128 const config = {
9129 plugins: {
9130 test: {
9131 rules: {
9132 checker: context => {
9133 spy = sinon.spy(node => {
9134 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
9135 });
9136 return { BinaryExpression: spy };
9137 }
9138 }
9139 }
9140 },
9141 rules: { "test/checker": "error" }
9142 };
9143
9144 linter.verify(code, config);
9145 assert(spy && spy.calledOnce);
9146 });
9147
9148 it("should retrieve all text plus one character after for binary expression", () => {
9149 let spy;
9150
9151 const config = {
9152 plugins: {
9153 test: {
9154 rules: {
9155 checker: context => {
9156 spy = sinon.spy(node => {
9157 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
9158 });
9159 return { BinaryExpression: spy };
9160 }
9161 }
9162 }
9163 },
9164 rules: { "test/checker": "error" }
9165 };
9166
9167 linter.verify(code, config);
9168 assert(spy && spy.calledOnce);
9169 });
9170
9171 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
9172 let spy;
9173
9174 const config = {
9175 plugins: {
9176 test: {
9177 rules: {
9178 checker: context => {
9179 spy = sinon.spy(node => {
9180 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
9181 });
9182 return { BinaryExpression: spy };
9183 }
9184 }
9185 }
9186 },
9187 rules: { "test/checker": "error" }
9188 };
9189
9190 linter.verify(code, config);
9191 assert(spy && spy.calledOnce);
9192 });
9193
9194 });
9195
9196 describe("context.getAncestors()", () => {
9197 const code = TEST_CODE;
9198
9199 it("should retrieve all ancestors when used", () => {
9200
9201 let spy;
9202
9203 const config = {
9204 plugins: {
9205 test: {
9206 rules: {
9207 checker: context => {
9208 spy = sinon.spy(() => {
9209 const ancestors = context.getAncestors();
9210
9211 assert.strictEqual(ancestors.length, 3);
9212 });
9213 return { BinaryExpression: spy };
9214 }
9215 }
9216 }
9217 },
9218 rules: { "test/checker": "error" }
9219 };
9220
9221 linter.verify(code, config, filename, true);
9222 assert(spy && spy.calledOnce);
9223 });
9224
9225 it("should retrieve empty ancestors for root node", () => {
9226 let spy;
9227
9228 const config = {
9229 plugins: {
9230 test: {
9231 rules: {
9232 checker: context => {
9233 spy = sinon.spy(() => {
9234 const ancestors = context.getAncestors();
9235
9236 assert.strictEqual(ancestors.length, 0);
9237 });
9238
9239 return { Program: spy };
9240 }
9241 }
9242 }
9243 },
9244 rules: { "test/checker": "error" }
9245 };
9246
9247 linter.verify(code, config);
9248 assert(spy && spy.calledOnce);
9249 });
9250 });
9251
9252 describe("context.getNodeByRangeIndex()", () => {
9253 const code = TEST_CODE;
9254
9255 it("should retrieve a node starting at the given index", () => {
9256 const spy = sinon.spy(context => {
9257 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
9258 return {};
9259 });
9260
9261 const config = {
9262 plugins: {
9263 test: {
9264 rules: {
9265 checker: spy
9266 }
9267 }
9268 },
9269 rules: { "test/checker": "error" }
9270 };
9271
9272 linter.verify(code, config);
9273 assert(spy.calledOnce);
9274 });
9275
9276 it("should retrieve a node containing the given index", () => {
9277 const spy = sinon.spy(context => {
9278 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
9279 return {};
9280 });
9281
9282 const config = {
9283 plugins: {
9284 test: {
9285 rules: {
9286 checker: spy
9287 }
9288 }
9289 },
9290 rules: { "test/checker": "error" }
9291 };
9292
9293 linter.verify(code, config);
9294 assert(spy.calledOnce);
9295 });
9296
9297 it("should retrieve a node that is exactly the given index", () => {
9298 const spy = sinon.spy(context => {
9299 const node = context.getNodeByRangeIndex(13);
9300
9301 assert.strictEqual(node.type, "Literal");
9302 assert.strictEqual(node.value, 6);
9303 return {};
9304 });
9305
9306 const config = {
9307 plugins: {
9308 test: {
9309 rules: {
9310 checker: spy
9311 }
9312 }
9313 },
9314 rules: { "test/checker": "error" }
9315 };
9316
9317 linter.verify(code, config);
9318 assert(spy.calledOnce);
9319 });
9320
9321 it("should retrieve a node ending with the given index", () => {
9322 const spy = sinon.spy(context => {
9323 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
9324 return {};
9325 });
9326
9327 const config = {
9328 plugins: {
9329 test: {
9330 rules: {
9331 checker: spy
9332 }
9333 }
9334 },
9335 rules: { "test/checker": "error" }
9336 };
9337
9338 linter.verify(code, config);
9339 assert(spy.calledOnce);
9340 });
9341
9342 it("should retrieve the deepest node containing the given index", () => {
9343 const spy = sinon.spy(context => {
9344 const node1 = context.getNodeByRangeIndex(14);
9345
9346 assert.strictEqual(node1.type, "BinaryExpression");
9347
9348 const node2 = context.getNodeByRangeIndex(3);
9349
9350 assert.strictEqual(node2.type, "VariableDeclaration");
9351 return {};
9352 });
9353
9354 const config = {
9355 plugins: {
9356 test: {
9357 rules: {
9358 checker: spy
9359 }
9360 }
9361 },
9362 rules: { "test/checker": "error" }
9363 };
9364
9365 linter.verify(code, config);
9366 assert(spy.calledOnce);
9367 });
9368
9369 it("should return null if the index is outside the range of any node", () => {
9370 const spy = sinon.spy(context => {
9371 const node1 = context.getNodeByRangeIndex(-1);
9372
9373 assert.isNull(node1);
9374
9375 const node2 = context.getNodeByRangeIndex(-99);
9376
9377 assert.isNull(node2);
9378 return {};
9379 });
9380
9381 const config = {
9382 plugins: {
9383 test: {
9384 rules: {
9385 checker: spy
9386 }
9387 }
9388 },
9389 rules: { "test/checker": "error" }
9390 };
9391
9392 linter.verify(code, config);
9393 assert(spy.calledOnce);
9394 });
9395 });
9396
9397 describe("context.getScope()", () => {
9398 const codeToTestScope = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
9399
9400 it("should retrieve the global scope correctly from a Program", () => {
9401 let spy;
9402
9403 const config = {
9404 plugins: {
9405 test: {
9406 rules: {
9407 checker: context => {
9408 spy = sinon.spy(() => {
9409 const scope = context.getScope();
9410
9411 assert.strictEqual(scope.type, "global");
9412 });
9413 return { Program: spy };
9414 }
9415 }
9416 }
9417 },
9418 languageOptions: {
9419 ecmaVersion: 6
9420 },
9421 rules: { "test/checker": "error" }
9422 };
9423
9424 linter.verify(codeToTestScope, config);
9425 assert(spy && spy.calledOnce);
9426 });
9427
9428 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
9429 let spy;
9430
9431 const config = {
9432 plugins: {
9433 test: {
9434 rules: {
9435 checker: context => {
9436 spy = sinon.spy(() => {
9437 const scope = context.getScope();
9438
9439 assert.strictEqual(scope.type, "function");
9440 });
9441 return { FunctionDeclaration: spy };
9442 }
9443 }
9444 }
9445 },
9446 languageOptions: {
9447 ecmaVersion: 6
9448 },
9449 rules: { "test/checker": "error" }
9450 };
9451
9452 linter.verify(codeToTestScope, config);
9453 assert(spy && spy.calledTwice);
9454 });
9455
9456 it("should retrieve the function scope correctly from a LabeledStatement", () => {
9457 let spy;
9458
9459 const config = {
9460 plugins: {
9461 test: {
9462 rules: {
9463 checker: context => {
9464 spy = sinon.spy(() => {
9465 const scope = context.getScope();
9466
9467 assert.strictEqual(scope.type, "function");
9468 assert.strictEqual(scope.block.id.name, "foo");
9469 });
9470 return { LabeledStatement: spy };
9471 }
9472 }
9473 }
9474 },
9475 languageOptions: {
9476 ecmaVersion: 6
9477 },
9478 rules: { "test/checker": "error" }
9479 };
9480
9481
9482 linter.verify(codeToTestScope, config);
9483 assert(spy && spy.calledOnce);
9484 });
9485
9486 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
9487 let spy;
9488
9489 const config = {
9490 plugins: {
9491 test: {
9492 rules: {
9493 checker: context => {
9494 spy = sinon.spy(() => {
9495 const scope = context.getScope();
9496
9497 assert.strictEqual(scope.type, "function");
9498 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
9499 });
9500
9501 return { ReturnStatement: spy };
9502 }
9503 }
9504 }
9505 },
9506 languageOptions: {
9507 ecmaVersion: 6
9508 },
9509 rules: { "test/checker": "error" }
9510 };
9511
9512
9513 linter.verify(codeToTestScope, config);
9514 assert(spy && spy.calledOnce);
9515 });
9516
9517 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
9518 let spy;
9519
9520 const config = {
9521 plugins: {
9522 test: {
9523 rules: {
9524 checker: context => {
9525 spy = sinon.spy(() => {
9526 const scope = context.getScope();
9527
9528 assert.strictEqual(scope.type, "switch");
9529 assert.strictEqual(scope.block.type, "SwitchStatement");
9530 });
9531
9532 return { SwitchStatement: spy };
9533 }
9534 }
9535 }
9536 },
9537 languageOptions: {
9538 ecmaVersion: 6
9539 },
9540 rules: { "test/checker": "error" }
9541 };
9542
9543 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
9544 assert(spy && spy.calledOnce);
9545 });
9546
9547 it("should retrieve the function scope correctly from within a BlockStatement", () => {
9548 let spy;
9549
9550 const config = {
9551 plugins: {
9552 test: {
9553 rules: {
9554 checker: context => {
9555 spy = sinon.spy(() => {
9556 const scope = context.getScope();
9557
9558 assert.strictEqual(scope.type, "block");
9559 assert.strictEqual(scope.block.type, "BlockStatement");
9560 });
9561
9562 return { BlockStatement: spy };
9563 }
9564 }
9565 }
9566 },
9567 languageOptions: {
9568 ecmaVersion: 6
9569 },
9570 rules: { "test/checker": "error" }
9571 };
9572
9573
9574 linter.verify("var x; {let y = 1}", config);
9575 assert(spy && spy.calledOnce);
9576 });
9577
9578 it("should retrieve the function scope correctly from within a nested block statement", () => {
9579 let spy;
9580
9581 const config = {
9582 plugins: {
9583 test: {
9584 rules: {
9585 checker: context => {
9586 spy = sinon.spy(() => {
9587 const scope = context.getScope();
9588
9589 assert.strictEqual(scope.type, "block");
9590 assert.strictEqual(scope.block.type, "BlockStatement");
9591 });
9592
9593 return { BlockStatement: spy };
9594 }
9595 }
9596 }
9597 },
9598 languageOptions: {
9599 ecmaVersion: 6
9600 },
9601 rules: { "test/checker": "error" }
9602 };
9603
9604
9605 linter.verify("if (true) { let x = 1 }", config);
9606 assert(spy && spy.calledOnce);
9607 });
9608
9609 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
9610 let spy;
9611
9612 const config = {
9613 plugins: {
9614 test: {
9615 rules: {
9616 checker: context => {
9617 spy = sinon.spy(() => {
9618 const scope = context.getScope();
9619
9620 assert.strictEqual(scope.type, "function");
9621 assert.strictEqual(scope.block.type, "FunctionDeclaration");
9622 });
9623
9624 return { FunctionDeclaration: spy };
9625 }
9626 }
9627 }
9628 },
9629 languageOptions: {
9630 ecmaVersion: 6
9631 },
9632 rules: { "test/checker": "error" }
9633 };
9634
9635
9636 linter.verify("function foo() {}", config);
9637 assert(spy && spy.calledOnce);
9638 });
9639
9640 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
9641 let spy;
9642
9643 const config = {
9644 plugins: {
9645 test: {
9646 rules: {
9647 checker: context => {
9648 spy = sinon.spy(() => {
9649 const scope = context.getScope();
9650
9651 assert.strictEqual(scope.type, "function");
9652 assert.strictEqual(scope.block.type, "FunctionExpression");
9653 });
9654
9655 return { FunctionExpression: spy };
9656 }
9657 }
9658 }
9659 },
9660 languageOptions: {
9661 ecmaVersion: 6
9662 },
9663 rules: { "test/checker": "error" }
9664 };
9665
9666
9667 linter.verify("(function foo() {})();", config);
9668 assert(spy && spy.calledOnce);
9669 });
9670
9671 it("should retrieve the catch scope correctly from within a CatchClause", () => {
9672 let spy;
9673
9674 const config = {
9675 plugins: {
9676 test: {
9677 rules: {
9678 checker: context => {
9679 spy = sinon.spy(() => {
9680 const scope = context.getScope();
9681
9682 assert.strictEqual(scope.type, "catch");
9683 assert.strictEqual(scope.block.type, "CatchClause");
9684 });
9685
9686 return { CatchClause: spy };
9687 }
9688 }
9689 }
9690 },
9691 languageOptions: {
9692 ecmaVersion: 6
9693 },
9694 rules: { "test/checker": "error" }
9695 };
9696
9697 linter.verify("try {} catch (err) {}", config);
9698 assert(spy && spy.calledOnce);
9699 });
9700
9701 it("should retrieve module scope correctly from an ES6 module", () => {
9702 let spy;
9703
9704 const config = {
9705 plugins: {
9706 test: {
9707 rules: {
9708 checker: context => {
9709 spy = sinon.spy(() => {
9710 const scope = context.getScope();
9711
9712 assert.strictEqual(scope.type, "module");
9713 });
9714
9715 return { AssignmentExpression: spy };
9716 }
9717 }
9718 }
9719 },
9720 languageOptions: {
9721 ecmaVersion: 6,
9722 sourceType: "module"
9723 },
9724 rules: { "test/checker": "error" }
9725 };
9726
9727
9728 linter.verify("var foo = {}; foo.bar = 1;", config);
9729 assert(spy && spy.calledOnce);
9730 });
9731
9732 it("should retrieve function scope correctly when sourceType is commonjs", () => {
9733 let spy;
9734
9735 const config = {
9736 plugins: {
9737 test: {
9738 rules: {
9739 checker: context => {
9740 spy = sinon.spy(() => {
9741 const scope = context.getScope();
9742
9743 assert.strictEqual(scope.type, "function");
9744 });
9745
9746 return { AssignmentExpression: spy };
9747 }
9748 }
9749 }
9750 },
9751 languageOptions: {
9752 ecmaVersion: 6,
9753 sourceType: "commonjs"
9754 },
9755 rules: { "test/checker": "error" }
9756 };
9757
9758 linter.verify("var foo = {}; foo.bar = 1;", config);
9759 assert(spy && spy.calledOnce);
9760 });
9761
9762 describe("Scope Internals", () => {
9763
9764 /**
9765 * Get the scope on the node `astSelector` specified.
9766 * @param {string} codeToEvaluate The source code to verify.
9767 * @param {string} astSelector The AST selector to get scope.
9768 * @param {number} [ecmaVersion=5] The ECMAScript version.
9769 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
9770 */
9771 function getScope(codeToEvaluate, astSelector, ecmaVersion = 5) {
9772 let node, scope;
9773
9774 const config = {
9775 plugins: {
9776 test: {
9777 rules: {
9778 "get-scope": context => ({
9779 [astSelector](node0) {
9780 node = node0;
9781 scope = context.getScope();
9782 }
9783 })
9784 }
9785 }
9786 },
9787 languageOptions: {
9788 ecmaVersion,
9789 sourceType: "script"
9790 },
9791 rules: { "test/get-scope": "error" }
9792 };
9793
9794 linter.verify(
9795 codeToEvaluate,
9796 config
9797 );
9798
9799 return { node, scope };
9800 }
9801
9802 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
9803 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
9804
9805 assert.strictEqual(scope.type, "function");
9806 assert.strictEqual(scope.block, node);
9807 });
9808
9809 it("should return 'function' scope on FunctionExpression (ES5)", () => {
9810 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
9811
9812 assert.strictEqual(scope.type, "function");
9813 assert.strictEqual(scope.block, node);
9814 });
9815
9816 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
9817 const { node, scope } = getScope("function f() {}", "BlockStatement");
9818
9819 assert.strictEqual(scope.type, "function");
9820 assert.strictEqual(scope.block, node.parent);
9821 });
9822
9823 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
9824 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
9825
9826 assert.strictEqual(scope.type, "function");
9827 assert.strictEqual(scope.block, node.parent);
9828 });
9829
9830 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
9831 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
9832
9833 assert.strictEqual(scope.type, "function");
9834 assert.strictEqual(scope.block, node.parent.parent);
9835 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
9836 });
9837
9838 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
9839 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
9840
9841 assert.strictEqual(scope.type, "block");
9842 assert.strictEqual(scope.upper.type, "function");
9843 assert.strictEqual(scope.block, node);
9844 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
9845 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
9846 });
9847
9848 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
9849 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
9850
9851 assert.strictEqual(scope.type, "block");
9852 assert.strictEqual(scope.upper.type, "block");
9853 assert.strictEqual(scope.upper.upper.type, "function");
9854 assert.strictEqual(scope.block, node);
9855 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
9856 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
9857 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
9858 });
9859
9860 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
9861 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
9862
9863 assert.strictEqual(scope.type, "function");
9864 assert.strictEqual(scope.block, node.parent.parent);
9865 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
9866 });
9867
9868 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
9869 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
9870
9871 assert.strictEqual(scope.type, "switch");
9872 assert.strictEqual(scope.block, node);
9873 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
9874 });
9875
9876 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
9877 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
9878
9879 assert.strictEqual(scope.type, "function");
9880 assert.strictEqual(scope.block, node.parent.parent.parent);
9881 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
9882 });
9883
9884 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
9885 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
9886
9887 assert.strictEqual(scope.type, "switch");
9888 assert.strictEqual(scope.block, node.parent);
9889 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
9890 });
9891
9892 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
9893 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
9894
9895 assert.strictEqual(scope.type, "catch");
9896 assert.strictEqual(scope.block, node);
9897 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
9898 });
9899
9900 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
9901 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
9902
9903 assert.strictEqual(scope.type, "catch");
9904 assert.strictEqual(scope.block, node);
9905 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
9906 });
9907
9908 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
9909 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
9910
9911 assert.strictEqual(scope.type, "catch");
9912 assert.strictEqual(scope.block, node.parent);
9913 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
9914 });
9915
9916 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
9917 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
9918
9919 assert.strictEqual(scope.type, "block");
9920 assert.strictEqual(scope.block, node);
9921 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
9922 });
9923
9924 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
9925 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
9926
9927 assert.strictEqual(scope.type, "function");
9928 assert.strictEqual(scope.block, node.parent.parent);
9929 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
9930 });
9931
9932 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
9933 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
9934
9935 assert.strictEqual(scope.type, "for");
9936 assert.strictEqual(scope.block, node);
9937 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
9938 });
9939
9940 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
9941 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
9942
9943 assert.strictEqual(scope.type, "function");
9944 assert.strictEqual(scope.block, node.parent.parent.parent);
9945 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
9946 });
9947
9948 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
9949 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
9950
9951 assert.strictEqual(scope.type, "block");
9952 assert.strictEqual(scope.upper.type, "for");
9953 assert.strictEqual(scope.block, node);
9954 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
9955 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
9956 });
9957
9958 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
9959 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
9960
9961 assert.strictEqual(scope.type, "function");
9962 assert.strictEqual(scope.block, node.parent.parent);
9963 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
9964 });
9965
9966 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
9967 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
9968
9969 assert.strictEqual(scope.type, "for");
9970 assert.strictEqual(scope.block, node);
9971 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
9972 });
9973
9974 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
9975 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
9976
9977 assert.strictEqual(scope.type, "function");
9978 assert.strictEqual(scope.block, node.parent.parent.parent);
9979 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
9980 });
9981
9982 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
9983 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
9984
9985 assert.strictEqual(scope.type, "block");
9986 assert.strictEqual(scope.upper.type, "for");
9987 assert.strictEqual(scope.block, node);
9988 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
9989 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
9990 });
9991
9992 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
9993 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
9994
9995 assert.strictEqual(scope.type, "for");
9996 assert.strictEqual(scope.block, node);
9997 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
9998 });
9999
10000 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
10001 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
10002
10003 assert.strictEqual(scope.type, "block");
10004 assert.strictEqual(scope.upper.type, "for");
10005 assert.strictEqual(scope.block, node);
10006 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
10007 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
10008 });
10009
10010 it("should shadow the same name variable by the iteration variable.", () => {
10011 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
10012
10013 assert.strictEqual(scope.type, "for");
10014 assert.strictEqual(scope.upper.type, "global");
10015 assert.strictEqual(scope.block, node);
10016 assert.strictEqual(scope.upper.variables[0].references.length, 0);
10017 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
10018 assert.strictEqual(scope.references[1].identifier, node.right);
10019 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
10020 });
10021 });
10022
10023 describe("Variables and references", () => {
10024 const code = [
10025 "a;",
10026 "function foo() { b; }",
10027 "Object;",
10028 "foo;",
10029 "var c;",
10030 "c;",
10031 "/* global d */",
10032 "d;",
10033 "e;",
10034 "f;"
10035 ].join("\n");
10036 let scope = null;
10037
10038 beforeEach(() => {
10039 let ok = false;
10040
10041 const config = {
10042 plugins: {
10043 test: {
10044 rules: {
10045 test(context) {
10046 return {
10047 Program() {
10048 scope = context.getScope();
10049 ok = true;
10050 }
10051 };
10052 }
10053 }
10054 }
10055 },
10056 languageOptions: {
10057 globals: { e: true, f: false },
10058 sourceType: "script",
10059 ecmaVersion: 5
10060 },
10061 rules: {
10062 "test/test": 2
10063 }
10064 };
10065
10066 linter.verify(code, config);
10067 assert(ok);
10068 });
10069
10070 afterEach(() => {
10071 scope = null;
10072 });
10073
10074 it("Scope#through should contain references of undefined variables", () => {
10075 assert.strictEqual(scope.through.length, 2);
10076 assert.strictEqual(scope.through[0].identifier.name, "a");
10077 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
10078 assert.strictEqual(scope.through[0].resolved, null);
10079 assert.strictEqual(scope.through[1].identifier.name, "b");
10080 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
10081 assert.strictEqual(scope.through[1].resolved, null);
10082 });
10083
10084 it("Scope#variables should contain global variables", () => {
10085 assert(scope.variables.some(v => v.name === "Object"));
10086 assert(scope.variables.some(v => v.name === "foo"));
10087 assert(scope.variables.some(v => v.name === "c"));
10088 assert(scope.variables.some(v => v.name === "d"));
10089 assert(scope.variables.some(v => v.name === "e"));
10090 assert(scope.variables.some(v => v.name === "f"));
10091 });
10092
10093 it("Scope#set should contain global variables", () => {
10094 assert(scope.set.get("Object"));
10095 assert(scope.set.get("foo"));
10096 assert(scope.set.get("c"));
10097 assert(scope.set.get("d"));
10098 assert(scope.set.get("e"));
10099 assert(scope.set.get("f"));
10100 });
10101
10102 it("Variables#references should contain their references", () => {
10103 assert.strictEqual(scope.set.get("Object").references.length, 1);
10104 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
10105 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
10106 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
10107 assert.strictEqual(scope.set.get("foo").references.length, 1);
10108 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
10109 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
10110 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
10111 assert.strictEqual(scope.set.get("c").references.length, 1);
10112 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
10113 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
10114 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
10115 assert.strictEqual(scope.set.get("d").references.length, 1);
10116 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
10117 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
10118 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
10119 assert.strictEqual(scope.set.get("e").references.length, 1);
10120 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
10121 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
10122 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
10123 assert.strictEqual(scope.set.get("f").references.length, 1);
10124 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
10125 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
10126 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
10127 });
10128
10129 it("Reference#resolved should be their variable", () => {
10130 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
10131 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
10132 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
10133 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
10134 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
10135 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
10136 });
10137 });
10138 });
10139
10140 describe("context.getDeclaredVariables(node)", () => {
10141
10142 /**
10143 * Assert `context.getDeclaredVariables(node)` is valid.
10144 * @param {string} code A code to check.
10145 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
10146 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
10147 * @returns {void}
10148 */
10149 function verify(code, type, expectedNamesList) {
10150 const config = {
10151 plugins: {
10152 test: {
10153
10154 rules: {
10155 test(context) {
10156
10157 /**
10158 * Assert `context.getDeclaredVariables(node)` is empty.
10159 * @param {ASTNode} node A node to check.
10160 * @returns {void}
10161 */
10162 function checkEmpty(node) {
10163 assert.strictEqual(0, context.getDeclaredVariables(node).length);
10164 }
10165 const rule = {
10166 Program: checkEmpty,
10167 EmptyStatement: checkEmpty,
10168 BlockStatement: checkEmpty,
10169 ExpressionStatement: checkEmpty,
10170 LabeledStatement: checkEmpty,
10171 BreakStatement: checkEmpty,
10172 ContinueStatement: checkEmpty,
10173 WithStatement: checkEmpty,
10174 SwitchStatement: checkEmpty,
10175 ReturnStatement: checkEmpty,
10176 ThrowStatement: checkEmpty,
10177 TryStatement: checkEmpty,
10178 WhileStatement: checkEmpty,
10179 DoWhileStatement: checkEmpty,
10180 ForStatement: checkEmpty,
10181 ForInStatement: checkEmpty,
10182 DebuggerStatement: checkEmpty,
10183 ThisExpression: checkEmpty,
10184 ArrayExpression: checkEmpty,
10185 ObjectExpression: checkEmpty,
10186 Property: checkEmpty,
10187 SequenceExpression: checkEmpty,
10188 UnaryExpression: checkEmpty,
10189 BinaryExpression: checkEmpty,
10190 AssignmentExpression: checkEmpty,
10191 UpdateExpression: checkEmpty,
10192 LogicalExpression: checkEmpty,
10193 ConditionalExpression: checkEmpty,
10194 CallExpression: checkEmpty,
10195 NewExpression: checkEmpty,
10196 MemberExpression: checkEmpty,
10197 SwitchCase: checkEmpty,
10198 Identifier: checkEmpty,
10199 Literal: checkEmpty,
10200 ForOfStatement: checkEmpty,
10201 ArrowFunctionExpression: checkEmpty,
10202 YieldExpression: checkEmpty,
10203 TemplateLiteral: checkEmpty,
10204 TaggedTemplateExpression: checkEmpty,
10205 TemplateElement: checkEmpty,
10206 ObjectPattern: checkEmpty,
10207 ArrayPattern: checkEmpty,
10208 RestElement: checkEmpty,
10209 AssignmentPattern: checkEmpty,
10210 ClassBody: checkEmpty,
10211 MethodDefinition: checkEmpty,
10212 MetaProperty: checkEmpty
10213 };
10214
10215 rule[type] = function(node) {
10216 const expectedNames = expectedNamesList.shift();
10217 const variables = context.getDeclaredVariables(node);
10218
10219 assert(Array.isArray(expectedNames));
10220 assert(Array.isArray(variables));
10221 assert.strictEqual(expectedNames.length, variables.length);
10222 for (let i = variables.length - 1; i >= 0; i--) {
10223 assert.strictEqual(expectedNames[i], variables[i].name);
10224 }
10225 };
10226 return rule;
10227 }
10228 }
10229
10230 }
10231 },
10232 languageOptions: {
10233 ecmaVersion: 6,
10234 sourceType: "module"
10235 },
10236 rules: {
10237 "test/test": 2
10238 }
10239 };
10240
10241 linter.verify(code, config);
10242
10243 // Check all expected names are asserted.
10244 assert.strictEqual(0, expectedNamesList.length);
10245 }
10246
10247 it("VariableDeclaration", () => {
10248 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
10249 const namesList = [
10250 ["a", "b", "c"],
10251 ["d", "e", "f"],
10252 ["g", "h", "i", "j", "k"],
10253 ["l"]
10254 ];
10255
10256 verify(code, "VariableDeclaration", namesList);
10257 });
10258
10259 it("VariableDeclaration (on for-in/of loop)", () => {
10260
10261 // TDZ scope is created here, so tests to exclude those.
10262 const code = "\n for (var {a, x: [b], y: {c = 0}} in foo) {\n let g;\n }\n for (let {d, x: [e], y: {f = 0}} of foo) {\n let h;\n }\n ";
10263 const namesList = [
10264 ["a", "b", "c"],
10265 ["g"],
10266 ["d", "e", "f"],
10267 ["h"]
10268 ];
10269
10270 verify(code, "VariableDeclaration", namesList);
10271 });
10272
10273 it("VariableDeclarator", () => {
10274
10275 // TDZ scope is created here, so tests to exclude those.
10276 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
10277 const namesList = [
10278 ["a", "b", "c"],
10279 ["d", "e", "f"],
10280 ["g", "h", "i"],
10281 ["j", "k"],
10282 ["l"]
10283 ];
10284
10285 verify(code, "VariableDeclarator", namesList);
10286 });
10287
10288 it("FunctionDeclaration", () => {
10289 const code = "\n function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n }\n function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n }\n ";
10290 const namesList = [
10291 ["foo", "a", "b", "c", "d", "e"],
10292 ["bar", "f", "g", "h", "i", "j"]
10293 ];
10294
10295 verify(code, "FunctionDeclaration", namesList);
10296 });
10297
10298 it("FunctionExpression", () => {
10299 const code = "\n (function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n });\n (function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n });\n ";
10300 const namesList = [
10301 ["foo", "a", "b", "c", "d", "e"],
10302 ["bar", "f", "g", "h", "i", "j"],
10303 ["q"]
10304 ];
10305
10306 verify(code, "FunctionExpression", namesList);
10307 });
10308
10309 it("ArrowFunctionExpression", () => {
10310 const code = "\n (({a, x: [b], y: {c = 0}}, [d, e]) => {\n let z;\n });\n (({f, x: [g], y: {h = 0}}, [i, j]) => {\n let z;\n });\n ";
10311 const namesList = [
10312 ["a", "b", "c", "d", "e"],
10313 ["f", "g", "h", "i", "j"]
10314 ];
10315
10316 verify(code, "ArrowFunctionExpression", namesList);
10317 });
10318
10319 it("ClassDeclaration", () => {
10320 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
10321 const namesList = [
10322 ["A", "A"], // outer scope's and inner scope's.
10323 ["B", "B"]
10324 ];
10325
10326 verify(code, "ClassDeclaration", namesList);
10327 });
10328
10329 it("ClassExpression", () => {
10330 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
10331 const namesList = [
10332 ["A"],
10333 ["B"]
10334 ];
10335
10336 verify(code, "ClassExpression", namesList);
10337 });
10338
10339 it("CatchClause", () => {
10340 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
10341 const namesList = [
10342 ["a", "b"],
10343 ["c", "d"]
10344 ];
10345
10346 verify(code, "CatchClause", namesList);
10347 });
10348
10349 it("ImportDeclaration", () => {
10350 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10351 const namesList = [
10352 [],
10353 ["a"],
10354 ["b", "c", "d"]
10355 ];
10356
10357 verify(code, "ImportDeclaration", namesList);
10358 });
10359
10360 it("ImportSpecifier", () => {
10361 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10362 const namesList = [
10363 ["c"],
10364 ["d"]
10365 ];
10366
10367 verify(code, "ImportSpecifier", namesList);
10368 });
10369
10370 it("ImportDefaultSpecifier", () => {
10371 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10372 const namesList = [
10373 ["b"]
10374 ];
10375
10376 verify(code, "ImportDefaultSpecifier", namesList);
10377 });
10378
10379 it("ImportNamespaceSpecifier", () => {
10380 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10381 const namesList = [
10382 ["a"]
10383 ];
10384
10385 verify(code, "ImportNamespaceSpecifier", namesList);
10386 });
10387 });
10388
10389 describe("context.markVariableAsUsed()", () => {
10390
10391 it("should mark variables in current scope as used", () => {
10392 const code = "var a = 1, b = 2;";
10393 let spy;
10394
10395 const config = {
10396 plugins: {
10397 test: {
10398 rules: {
10399 checker: context => {
10400 spy = sinon.spy(() => {
10401 assert.isTrue(context.markVariableAsUsed("a"));
10402
10403 const scope = context.getScope();
10404
10405 assert.isTrue(getVariable(scope, "a").eslintUsed);
10406 assert.notOk(getVariable(scope, "b").eslintUsed);
10407 });
10408
10409 return { "Program:exit": spy };
10410 }
10411 }
10412 }
10413 },
10414 languageOptions: {
10415 sourceType: "script"
10416 },
10417 rules: { "test/checker": "error" }
10418 };
10419
10420 linter.verify(code, config);
10421 assert(spy && spy.calledOnce);
10422 });
10423
10424 it("should mark variables in function args as used", () => {
10425 const code = "function abc(a, b) { return 1; }";
10426 let spy;
10427
10428 const config = {
10429 plugins: {
10430 test: {
10431 rules: {
10432 checker: context => {
10433 spy = sinon.spy(() => {
10434 assert.isTrue(context.markVariableAsUsed("a"));
10435
10436 const scope = context.getScope();
10437
10438 assert.isTrue(getVariable(scope, "a").eslintUsed);
10439 assert.notOk(getVariable(scope, "b").eslintUsed);
10440 });
10441
10442 return { ReturnStatement: spy };
10443 }
10444 }
10445 }
10446 },
10447 rules: { "test/checker": "error" }
10448 };
10449
10450 linter.verify(code, config);
10451 assert(spy && spy.calledOnce);
10452 });
10453
10454 it("should mark variables in higher scopes as used", () => {
10455 const code = "var a, b; function abc() { return 1; }";
10456 let returnSpy, exitSpy;
10457
10458 const config = {
10459 plugins: {
10460 test: {
10461 rules: {
10462 checker: context => {
10463 returnSpy = sinon.spy(() => {
10464 assert.isTrue(context.markVariableAsUsed("a"));
10465 });
10466 exitSpy = sinon.spy(() => {
10467 const scope = context.getScope();
10468
10469 assert.isTrue(getVariable(scope, "a").eslintUsed);
10470 assert.notOk(getVariable(scope, "b").eslintUsed);
10471 });
10472
10473 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
10474 }
10475 }
10476 }
10477 },
10478 languageOptions: {
10479 sourceType: "script"
10480 },
10481 rules: { "test/checker": "error" }
10482 };
10483
10484 linter.verify(code, config);
10485 assert(returnSpy && returnSpy.calledOnce);
10486 assert(exitSpy && exitSpy.calledOnce);
10487 });
10488
10489 it("should mark variables as used when sourceType is commonjs", () => {
10490 const code = "var a = 1, b = 2;";
10491 let spy;
10492
10493 const config = {
10494 plugins: {
10495 test: {
10496 rules: {
10497 checker: context => {
10498 spy = sinon.spy(() => {
10499 const globalScope = context.getScope(),
10500 childScope = globalScope.childScopes[0];
10501
10502 assert.isTrue(context.markVariableAsUsed("a"), "Call to markVariableAsUsed should return true");
10503
10504 assert.isTrue(getVariable(childScope, "a").eslintUsed, "'a' should be marked as used.");
10505 assert.isUndefined(getVariable(childScope, "b").eslintUsed, "'b' should be marked as used.");
10506 });
10507
10508 return { "Program:exit": spy };
10509 }
10510 }
10511 }
10512 },
10513 languageOptions: {
10514 sourceType: "commonjs"
10515 },
10516 rules: { "test/checker": "error" }
10517 };
10518
10519 linter.verify(code, config);
10520 assert(spy && spy.calledOnce, "Spy wasn't called.");
10521 });
10522
10523 it("should mark variables in modules as used", () => {
10524 const code = "var a = 1, b = 2;";
10525 let spy;
10526
10527 const config = {
10528 plugins: {
10529 test: {
10530 rules: {
10531 checker: context => {
10532 spy = sinon.spy(() => {
10533 const globalScope = context.getScope(),
10534 childScope = globalScope.childScopes[0];
10535
10536 assert.isTrue(context.markVariableAsUsed("a"));
10537
10538 assert.isTrue(getVariable(childScope, "a").eslintUsed);
10539 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
10540 });
10541
10542 return { "Program:exit": spy };
10543 }
10544 }
10545 }
10546 },
10547 languageOptions: {
10548 ecmaVersion: 6,
10549 sourceType: "module"
10550 },
10551 rules: { "test/checker": "error" }
10552 };
10553
10554 linter.verify(code, config);
10555 assert(spy && spy.calledOnce);
10556 });
10557
10558 it("should return false if the given variable is not found", () => {
10559 const code = "var a = 1, b = 2;";
10560 let spy;
10561
10562 const config = {
10563 plugins: {
10564 test: {
10565 rules: {
10566 checker: context => {
10567 spy = sinon.spy(() => {
10568 assert.isFalse(context.markVariableAsUsed("c"));
10569 });
10570
10571 return { "Program:exit": spy };
10572 }
10573 }
10574 }
10575 },
10576 rules: { "test/checker": "error" }
10577 };
10578
10579 linter.verify(code, config);
10580 assert(spy && spy.calledOnce);
10581 });
10582 });
10583
10584 describe("context.getCwd()", () => {
10585 const code = "a;\nb;";
10586 const baseConfig = { rules: { "test/checker": "error" } };
10587
10588 it("should get cwd correctly in the context", () => {
10589 const cwd = "cwd";
10590 const linterWithOption = new Linter({ cwd, configType: "flat" });
10591 let spy;
10592 const config = {
10593 plugins: {
10594 test: {
10595 rules: {
10596 checker: context => {
10597 spy = sinon.spy(() => {
10598 assert.strictEqual(context.getCwd(), cwd);
10599 });
10600 return { Program: spy };
10601 }
10602 }
10603 }
10604 },
10605 ...baseConfig
10606 };
10607
10608 linterWithOption.verify(code, config);
10609 assert(spy && spy.calledOnce);
10610 });
10611
10612 it("should assign process.cwd() to it if cwd is undefined", () => {
10613
10614 const linterWithOption = new Linter({ configType: "flat" });
10615 let spy;
10616 const config = {
10617 plugins: {
10618 test: {
10619 rules: {
10620 checker: context => {
10621
10622 spy = sinon.spy(() => {
10623 assert.strictEqual(context.getCwd(), process.cwd());
10624 });
10625 return { Program: spy };
10626 }
10627 }
10628 }
10629 },
10630 ...baseConfig
10631 };
10632
10633 linterWithOption.verify(code, config);
10634 assert(spy && spy.calledOnce);
10635 });
10636
10637 it("should assign process.cwd() to it if the option is undefined", () => {
10638 let spy;
10639 const config = {
10640 plugins: {
10641 test: {
10642 rules: {
10643 checker: context => {
10644
10645 spy = sinon.spy(() => {
10646 assert.strictEqual(context.getCwd(), process.cwd());
10647 });
10648 return { Program: spy };
10649 }
10650 }
10651 }
10652 },
10653 ...baseConfig
10654 };
10655
10656 linter.verify(code, config);
10657 assert(spy && spy.calledOnce);
10658 });
10659 });
10660
10661 });
10662
10663 describe("Rule Severity", () => {
10664
10665 it("rule should run as warning when set to 1 with a config array", () => {
10666 const ruleId = "semi",
10667 configs = createFlatConfigArray({
10668 files: ["**/*.js"],
10669 rules: {
10670 [ruleId]: 1
10671 }
10672 });
10673
10674 configs.normalizeSync();
10675 const messages = linter.verify("foo", configs, filename, true);
10676 const suppressedMessages = linter.getSuppressedMessages();
10677
10678 assert.strictEqual(messages.length, 1, "Message length is wrong");
10679 assert.strictEqual(messages[0].ruleId, ruleId);
10680
10681 assert.strictEqual(suppressedMessages.length, 0);
10682 });
10683
10684 it("rule should run as warning when set to 1 with a plain array", () => {
10685 const ruleId = "semi",
10686 configs = [{
10687 files: ["**/*.js"],
10688 rules: {
10689 [ruleId]: 1
10690 }
10691 }];
10692
10693 const messages = linter.verify("foo", configs, filename, true);
10694 const suppressedMessages = linter.getSuppressedMessages();
10695
10696 assert.strictEqual(messages.length, 1, "Message length is wrong");
10697 assert.strictEqual(messages[0].ruleId, ruleId);
10698
10699 assert.strictEqual(suppressedMessages.length, 0);
10700 });
10701
10702 it("rule should run as warning when set to 1 with an object", () => {
10703 const ruleId = "semi",
10704 config = {
10705 files: ["**/*.js"],
10706 rules: {
10707 [ruleId]: 1
10708 }
10709 };
10710
10711 const messages = linter.verify("foo", config, filename, true);
10712 const suppressedMessages = linter.getSuppressedMessages();
10713
10714 assert.strictEqual(messages.length, 1, "Message length is wrong");
10715 assert.strictEqual(messages[0].ruleId, ruleId);
10716
10717 assert.strictEqual(suppressedMessages.length, 0);
10718 });
10719 });
10720
10721 describe("Code with a hashbang comment", () => {
10722 const code = "#!bin/program\n\nvar foo;;";
10723
10724 it("should preserve line numbers", () => {
10725 const config = { rules: { "no-extra-semi": 1 } };
10726 const messages = linter.verify(code, config);
10727 const suppressedMessages = linter.getSuppressedMessages();
10728
10729 assert.strictEqual(messages.length, 1);
10730 assert.strictEqual(messages[0].ruleId, "no-extra-semi");
10731 assert.strictEqual(messages[0].nodeType, "EmptyStatement");
10732 assert.strictEqual(messages[0].line, 3);
10733
10734 assert.strictEqual(suppressedMessages.length, 0);
10735 });
10736
10737 it("should have a comment with the hashbang in it", () => {
10738 const spy = sinon.spy(context => {
10739 const comments = context.getAllComments();
10740
10741 assert.strictEqual(comments.length, 1);
10742 assert.strictEqual(comments[0].type, "Shebang");
10743 return {};
10744 });
10745
10746 const config = {
10747 plugins: {
10748 test: {
10749 rules: {
10750 checker: spy
10751 }
10752 }
10753 },
10754 rules: {
10755 "test/checker": "error"
10756 }
10757 };
10758
10759 linter.verify(code, config);
10760 assert(spy.calledOnce);
10761 });
10762 });
10763
10764 describe("Options", () => {
10765
10766 describe("filename", () => {
10767 it("should allow filename to be passed on options object", () => {
10768 const filenameChecker = sinon.spy(context => {
10769 assert.strictEqual(context.getFilename(), "foo.js");
10770 return {};
10771 });
10772
10773 const config = {
10774 plugins: {
10775 test: {
10776 rules: {
10777 checker: filenameChecker
10778 }
10779 }
10780 },
10781 rules: {
10782 "test/checker": "error"
10783 }
10784 };
10785
10786 linter.verify("foo;", config, { filename: "foo.js" });
10787 assert(filenameChecker.calledOnce);
10788 });
10789
10790 it("should allow filename to be passed as third argument", () => {
10791 const filenameChecker = sinon.spy(context => {
10792 assert.strictEqual(context.getFilename(), "bar.js");
10793 return {};
10794 });
10795
10796 const config = {
10797 plugins: {
10798 test: {
10799 rules: {
10800 checker: filenameChecker
10801 }
10802 }
10803 },
10804 rules: {
10805 "test/checker": "error"
10806 }
10807 };
10808
10809 linter.verify("foo;", config, "bar.js");
10810 assert(filenameChecker.calledOnce);
10811 });
10812
10813 it("should default filename to <input> when options object doesn't have filename", () => {
10814 const filenameChecker = sinon.spy(context => {
10815 assert.strictEqual(context.getFilename(), "<input>");
10816 return {};
10817 });
10818
10819 const config = {
10820 plugins: {
10821 test: {
10822 rules: {
10823 checker: filenameChecker
10824 }
10825 }
10826 },
10827 rules: {
10828 "test/checker": "error"
10829 }
10830 };
10831
10832 linter.verify("foo;", config, {});
10833 assert(filenameChecker.calledOnce);
10834 });
10835
10836 it("should default filename to <input> when only two arguments are passed", () => {
10837 const filenameChecker = sinon.spy(context => {
10838 assert.strictEqual(context.getFilename(), "<input>");
10839 return {};
10840 });
10841
10842 const config = {
10843 plugins: {
10844 test: {
10845 rules: {
10846 checker: filenameChecker
10847 }
10848 }
10849 },
10850 rules: {
10851 "test/checker": "error"
10852 }
10853 };
10854
10855 linter.verify("foo;", config);
10856 assert(filenameChecker.calledOnce);
10857 });
10858 });
10859
10860 describe("physicalFilename", () => {
10861 it("should be same as `filename` passed on options object, if no processors are used", () => {
10862 const physicalFilenameChecker = sinon.spy(context => {
10863 assert.strictEqual(context.getPhysicalFilename(), "foo.js");
10864 return {};
10865 });
10866
10867 const config = {
10868 plugins: {
10869 test: {
10870 rules: {
10871 checker: physicalFilenameChecker
10872 }
10873 }
10874 },
10875 rules: {
10876 "test/checker": "error"
10877 }
10878 };
10879
10880 linter.verify("foo;", config, { filename: "foo.js" });
10881 assert(physicalFilenameChecker.calledOnce);
10882 });
10883
10884 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
10885 const physicalFilenameChecker = sinon.spy(context => {
10886 assert.strictEqual(context.getPhysicalFilename(), "<input>");
10887 return {};
10888 });
10889
10890 const config = {
10891 plugins: {
10892 test: {
10893 rules: {
10894 checker: physicalFilenameChecker
10895 }
10896 }
10897 },
10898 rules: {
10899 "test/checker": "error"
10900 }
10901 };
10902
10903 linter.verify("foo;", config, {});
10904 assert(physicalFilenameChecker.calledOnce);
10905 });
10906
10907 it("should default physicalFilename to <input> when only two arguments are passed", () => {
10908 const physicalFilenameChecker = sinon.spy(context => {
10909 assert.strictEqual(context.getPhysicalFilename(), "<input>");
10910 return {};
10911 });
10912
10913 const config = {
10914 plugins: {
10915 test: {
10916 rules: {
10917 checker: physicalFilenameChecker
10918 }
10919 }
10920 },
10921 rules: {
10922 "test/checker": "error"
10923 }
10924 };
10925
10926 linter.verify("foo;", config);
10927 assert(physicalFilenameChecker.calledOnce);
10928 });
10929 });
10930
10931 });
10932
10933 describe("Inline Directives", () => {
10934
10935 describe("/*global*/ Comments", () => {
10936
10937 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
10938
10939 it("variables should be available in global scope", () => {
10940 const code = `
10941 /*global a b:true c:false d:readable e:writeable Math:off */
10942 function foo() {}
10943 /*globals f:true*/
10944 /* global ConfigGlobal : readable */
10945 `;
10946 let spy;
10947
10948 const config = {
10949 plugins: {
10950 test: {
10951 rules: {
10952 checker: context => {
10953 spy = sinon.spy(() => {
10954 const scope = context.getScope();
10955 const a = getVariable(scope, "a"),
10956 b = getVariable(scope, "b"),
10957 c = getVariable(scope, "c"),
10958 d = getVariable(scope, "d"),
10959 e = getVariable(scope, "e"),
10960 f = getVariable(scope, "f"),
10961 mathGlobal = getVariable(scope, "Math"),
10962 arrayGlobal = getVariable(scope, "Array"),
10963 configGlobal = getVariable(scope, "ConfigGlobal");
10964
10965 assert.strictEqual(a.name, "a");
10966 assert.strictEqual(a.writeable, false);
10967 assert.strictEqual(b.name, "b");
10968 assert.strictEqual(b.writeable, true);
10969 assert.strictEqual(c.name, "c");
10970 assert.strictEqual(c.writeable, false);
10971 assert.strictEqual(d.name, "d");
10972 assert.strictEqual(d.writeable, false);
10973 assert.strictEqual(e.name, "e");
10974 assert.strictEqual(e.writeable, true);
10975 assert.strictEqual(f.name, "f");
10976 assert.strictEqual(f.writeable, true);
10977 assert.strictEqual(mathGlobal, null);
10978 assert.strictEqual(arrayGlobal, null);
10979 assert.strictEqual(configGlobal.name, "ConfigGlobal");
10980 assert.strictEqual(configGlobal.writeable, false);
10981 });
10982
10983 return { Program: spy };
10984 }
10985 }
10986 }
10987 },
10988 rules: { "test/checker": "error" },
10989 languageOptions: {
10990 globals: { Array: "off", ConfigGlobal: "writeable" }
10991 }
10992 };
10993
10994 linter.verify(code, config);
10995 assert(spy && spy.calledOnce);
10996 });
10997 });
10998
10999 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
11000 const code = "/* global a b : true c: false*/";
11001
11002 it("variables should be available in global scope", () => {
11003
11004 let spy;
11005 const config = {
11006 plugins: {
11007 test: {
11008 rules: {
11009 checker: context => {
11010 spy = sinon.spy(() => {
11011 const scope = context.getScope(),
11012 a = getVariable(scope, "a"),
11013 b = getVariable(scope, "b"),
11014 c = getVariable(scope, "c");
11015
11016 assert.strictEqual(a.name, "a");
11017 assert.strictEqual(a.writeable, false);
11018 assert.strictEqual(b.name, "b");
11019 assert.strictEqual(b.writeable, true);
11020 assert.strictEqual(c.name, "c");
11021 assert.strictEqual(c.writeable, false);
11022 });
11023
11024 return { Program: spy };
11025 }
11026 }
11027 }
11028 },
11029 rules: { "test/checker": "error" }
11030 };
11031
11032 linter.verify(code, config);
11033 assert(spy && spy.calledOnce);
11034 });
11035 });
11036
11037 describe("when evaluating code containing a line comment", () => {
11038 const code = "//global a \n function f() {}";
11039
11040 it("should not introduce a global variable", () => {
11041 let spy;
11042
11043 const config = {
11044 plugins: {
11045 test: {
11046 rules: {
11047 checker: context => {
11048 spy = sinon.spy(() => {
11049 const scope = context.getScope();
11050
11051 assert.strictEqual(getVariable(scope, "a"), null);
11052 });
11053
11054 return { Program: spy };
11055 }
11056 }
11057 }
11058 },
11059 rules: { "test/checker": "error" }
11060 };
11061
11062
11063 linter.verify(code, config);
11064 assert(spy && spy.calledOnce);
11065 });
11066 });
11067
11068 describe("when evaluating code containing normal block comments", () => {
11069 const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
11070
11071 it("should not introduce a global variable", () => {
11072 let spy;
11073
11074 const config = {
11075 plugins: {
11076 test: {
11077 rules: {
11078 checker: context => {
11079 spy = sinon.spy(() => {
11080 const scope = context.getScope();
11081
11082 assert.strictEqual(getVariable(scope, "a"), null);
11083 assert.strictEqual(getVariable(scope, "b"), null);
11084 assert.strictEqual(getVariable(scope, "foo"), null);
11085 assert.strictEqual(getVariable(scope, "c"), null);
11086 });
11087
11088 return { Program: spy };
11089 }
11090 }
11091 }
11092 },
11093 rules: { "test/checker": "error" }
11094 };
11095
11096
11097 linter.verify(code, config);
11098 assert(spy && spy.calledOnce);
11099 });
11100 });
11101
11102 it("should attach a \"/*global\" comment node to declared variables", () => {
11103 const code = "/* global foo */\n/* global bar, baz */";
11104 let ok = false;
11105 const config = {
11106 plugins: {
11107 test: {
11108 rules: {
11109 test(context) {
11110 return {
11111 Program() {
11112 const scope = context.getScope();
11113 const sourceCode = context.getSourceCode();
11114 const comments = sourceCode.getAllComments();
11115
11116 assert.strictEqual(2, comments.length);
11117
11118 const foo = getVariable(scope, "foo");
11119
11120 assert.strictEqual(foo.eslintExplicitGlobal, true);
11121 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
11122
11123 const bar = getVariable(scope, "bar");
11124
11125 assert.strictEqual(bar.eslintExplicitGlobal, true);
11126 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
11127
11128 const baz = getVariable(scope, "baz");
11129
11130 assert.strictEqual(baz.eslintExplicitGlobal, true);
11131 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
11132
11133 ok = true;
11134 }
11135 };
11136 }
11137 }
11138 }
11139 },
11140 rules: { "test/test": "error" }
11141 };
11142
11143
11144 linter.verify(code, config);
11145 assert(ok);
11146 });
11147
11148 it("should report a linting error when a global is set to an invalid value", () => {
11149 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
11150 const suppressedMessages = linter.getSuppressedMessages();
11151
11152 assert.deepStrictEqual(results, [
11153 {
11154 ruleId: null,
11155 severity: 2,
11156 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
11157 line: 1,
11158 column: 1,
11159 endLine: 1,
11160 endColumn: 39,
11161 nodeType: null
11162 },
11163 {
11164 ruleId: "no-undef",
11165 messageId: "undef",
11166 severity: 2,
11167 message: "'foo' is not defined.",
11168 line: 2,
11169 column: 1,
11170 endLine: 2,
11171 endColumn: 4,
11172 nodeType: "Identifier"
11173 }
11174 ]);
11175
11176 assert.strictEqual(suppressedMessages.length, 0);
11177 });
11178
11179 });
11180
11181 describe("/*exported*/ Comments", () => {
11182
11183 it("we should behave nicely when no matching variable is found", () => {
11184 const code = "/* exported horse */";
11185 const config = { rules: {} };
11186
11187 linter.verify(code, config, filename, true);
11188 });
11189
11190 it("variables should be exported", () => {
11191 const code = "/* exported horse */\n\nvar horse = 'circus'";
11192 let spy;
11193
11194 const config = {
11195 plugins: {
11196 test: {
11197 rules: {
11198 checker: context => {
11199 spy = sinon.spy(() => {
11200 const scope = context.getScope(),
11201 horse = getVariable(scope, "horse");
11202
11203 assert.strictEqual(horse.eslintUsed, true);
11204 });
11205
11206 return { Program: spy };
11207 }
11208 }
11209 }
11210 },
11211 languageOptions: {
11212 sourceType: "script"
11213 },
11214 rules: { "test/checker": "error" }
11215 };
11216
11217 linter.verify(code, config);
11218 assert(spy && spy.calledOnce);
11219 });
11220
11221 it("undefined variables should not be exported", () => {
11222 const code = "/* exported horse */\n\nhorse = 'circus'";
11223 let spy;
11224 const config = {
11225 plugins: {
11226 test: {
11227 rules: {
11228 checker: context => {
11229 spy = sinon.spy(() => {
11230 const scope = context.getScope(),
11231 horse = getVariable(scope, "horse");
11232
11233 assert.strictEqual(horse, null);
11234 });
11235
11236 return { Program: spy };
11237 }
11238 }
11239 }
11240 },
11241 languageOptions: {
11242 sourceType: "script"
11243 },
11244 rules: { "test/checker": "error" }
11245 };
11246
11247 linter.verify(code, config);
11248 assert(spy && spy.calledOnce);
11249 });
11250
11251 it("variables should be exported in strict mode", () => {
11252 const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'";
11253 let spy;
11254 const config = {
11255 plugins: {
11256 test: {
11257 rules: {
11258 checker: context => {
11259 spy = sinon.spy(() => {
11260 const scope = context.getScope(),
11261 horse = getVariable(scope, "horse");
11262
11263 assert.strictEqual(horse.eslintUsed, true);
11264 });
11265
11266 return { Program: spy };
11267 }
11268 }
11269 }
11270 },
11271 languageOptions: {
11272 sourceType: "script"
11273 },
11274 rules: { "test/checker": "error" }
11275 };
11276
11277 linter.verify(code, config);
11278 assert(spy && spy.calledOnce);
11279 });
11280
11281 it("variables should not be exported in the es6 module environment", () => {
11282 const code = "/* exported horse */\nvar horse = 'circus'";
11283 let spy;
11284 const config = {
11285 plugins: {
11286 test: {
11287 rules: {
11288 checker: context => {
11289 spy = sinon.spy(() => {
11290 const scope = context.getScope(),
11291 horse = getVariable(scope, "horse");
11292
11293 assert.strictEqual(horse, null); // there is no global scope at all
11294 });
11295
11296 return { Program: spy };
11297 }
11298 }
11299 }
11300 },
11301 languageOptions: {
11302 ecmaVersion: 6,
11303 sourceType: "module"
11304 },
11305 rules: { "test/checker": "error" }
11306 };
11307
11308 linter.verify(code, config);
11309 assert(spy && spy.calledOnce);
11310 });
11311
11312 it("variables should not be exported when in a commonjs file", () => {
11313 const code = "/* exported horse */\nvar horse = 'circus'";
11314 let spy;
11315 const config = {
11316 plugins: {
11317 test: {
11318 rules: {
11319 checker: context => {
11320 spy = sinon.spy(() => {
11321 const scope = context.getScope(),
11322 horse = getVariable(scope, "horse");
11323
11324 assert.strictEqual(horse, null); // there is no global scope at all
11325 });
11326
11327 return { Program: spy };
11328 }
11329 }
11330 }
11331 },
11332 languageOptions: {
11333 sourceType: "commonjs"
11334 },
11335 rules: { "test/checker": "error" }
11336 };
11337
11338 linter.verify(code, config);
11339 assert(spy && spy.calledOnce);
11340 });
11341 });
11342
11343 describe("/*eslint*/ Comments", () => {
11344 describe("when evaluating code with comments to enable rules", () => {
11345
11346 it("should report a violation", () => {
11347 const code = "/*eslint no-alert:1*/ alert('test');";
11348 const config = { rules: {} };
11349
11350 const messages = linter.verify(code, config, filename);
11351 const suppressedMessages = linter.getSuppressedMessages();
11352
11353 assert.strictEqual(messages.length, 1);
11354 assert.strictEqual(messages[0].ruleId, "no-alert");
11355 assert.strictEqual(messages[0].message, "Unexpected alert.");
11356 assert.include(messages[0].nodeType, "CallExpression");
11357
11358 assert.strictEqual(suppressedMessages.length, 0);
11359 });
11360
11361 it("rules should not change initial config", () => {
11362 const config = {
11363 languageOptions: {
11364 sourceType: "script"
11365 },
11366 rules: { strict: 2 }
11367 };
11368 const codeA = "/*eslint strict: 0*/ function bar() { return 2; }";
11369 const codeB = "function foo() { return 1; }";
11370 let messages = linter.verify(codeA, config, filename, false);
11371 let suppressedMessages = linter.getSuppressedMessages();
11372
11373 assert.strictEqual(messages.length, 0);
11374 assert.strictEqual(suppressedMessages.length, 0);
11375
11376 messages = linter.verify(codeB, config, filename, false);
11377 suppressedMessages = linter.getSuppressedMessages();
11378
11379 assert.strictEqual(messages.length, 1);
11380 assert.strictEqual(suppressedMessages.length, 0);
11381 });
11382
11383 it("rules should not change initial config", () => {
11384 const config = {
11385 languageOptions: {
11386 sourceType: "script"
11387 },
11388 rules: { quotes: [2, "double"] }
11389 };
11390 const codeA = "/*eslint quotes: 0*/ function bar() { return '2'; }";
11391 const codeB = "function foo() { return '1'; }";
11392 let messages = linter.verify(codeA, config, filename, false);
11393 let suppressedMessages = linter.getSuppressedMessages();
11394
11395 assert.strictEqual(messages.length, 0);
11396 assert.strictEqual(suppressedMessages.length, 0);
11397
11398 messages = linter.verify(codeB, config, filename, false);
11399 suppressedMessages = linter.getSuppressedMessages();
11400
11401 assert.strictEqual(messages.length, 1);
11402 assert.strictEqual(suppressedMessages.length, 0);
11403 });
11404
11405 it("rules should not change initial config", () => {
11406 const config = { rules: { quotes: [2, "double"] } };
11407 const codeA = "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
11408 const codeB = "function foo() { return '1'; }";
11409 let messages = linter.verify(codeA, config, filename, false);
11410 let suppressedMessages = linter.getSuppressedMessages();
11411
11412 assert.strictEqual(messages.length, 0);
11413 assert.strictEqual(suppressedMessages.length, 0);
11414
11415 messages = linter.verify(codeB, config, filename, false);
11416 suppressedMessages = linter.getSuppressedMessages();
11417
11418 assert.strictEqual(messages.length, 1);
11419 assert.strictEqual(suppressedMessages.length, 0);
11420 });
11421
11422 it("rules should not change initial config", () => {
11423 const config = {
11424 languageOptions: {
11425 sourceType: "script"
11426 },
11427 rules: { "no-unused-vars": [2, { vars: "all" }] }
11428 };
11429 const codeA = "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
11430 const codeB = "var b = 55;";
11431 let messages = linter.verify(codeA, config, filename, false);
11432 let suppressedMessages = linter.getSuppressedMessages();
11433
11434 assert.strictEqual(messages.length, 0);
11435 assert.strictEqual(suppressedMessages.length, 0);
11436
11437 messages = linter.verify(codeB, config, filename, false);
11438 suppressedMessages = linter.getSuppressedMessages();
11439
11440 assert.strictEqual(messages.length, 1);
11441 assert.strictEqual(suppressedMessages.length, 0);
11442 });
11443 });
11444
11445 describe("when evaluating code with invalid comments to enable rules", () => {
11446 it("should report a violation when the config is not a valid rule configuration", () => {
11447 const messages = linter.verify("/*eslint no-alert:true*/ alert('test');", {});
11448 const suppressedMessages = linter.getSuppressedMessages();
11449
11450 assert.deepStrictEqual(
11451 messages,
11452 [
11453 {
11454 severity: 2,
11455 ruleId: "no-alert",
11456 message: "Configuration for rule \"no-alert\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed 'true').\n",
11457 line: 1,
11458 column: 1,
11459 endLine: 1,
11460 endColumn: 25,
11461 nodeType: null
11462 }
11463 ]
11464 );
11465
11466 assert.strictEqual(suppressedMessages.length, 0);
11467 });
11468
11469 it("should report a violation when the config violates a rule's schema", () => {
11470 const messages = linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {});
11471 const suppressedMessages = linter.getSuppressedMessages();
11472
11473 assert.deepStrictEqual(
11474 messages,
11475 [
11476 {
11477 severity: 2,
11478 ruleId: "no-alert",
11479 message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
11480 line: 1,
11481 column: 1,
11482 endLine: 1,
11483 endColumn: 63,
11484 nodeType: null
11485 }
11486 ]
11487 );
11488
11489 assert.strictEqual(suppressedMessages.length, 0);
11490 });
11491 });
11492
11493 describe("when evaluating code with comments to disable rules", () => {
11494
11495 it("should not report a violation", () => {
11496 const config = { rules: { "no-alert": 1 } };
11497 const messages = linter.verify("/*eslint no-alert:0*/ alert('test');", config, filename);
11498 const suppressedMessages = linter.getSuppressedMessages();
11499
11500 assert.strictEqual(messages.length, 0);
11501 assert.strictEqual(suppressedMessages.length, 0);
11502 });
11503
11504 it("should report an error when disabling a non-existent rule in inline comment", () => {
11505 let code = "/*eslint foo:0*/ ;";
11506 let messages = linter.verify(code, {}, filename);
11507 let suppressedMessages = linter.getSuppressedMessages();
11508
11509 assert.strictEqual(messages.length, 1, "/*eslint*/ comment should report problem.");
11510 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11511 assert.strictEqual(suppressedMessages.length, 0);
11512
11513 code = "/*eslint-disable foo*/ ;";
11514 messages = linter.verify(code, {}, filename);
11515 suppressedMessages = linter.getSuppressedMessages();
11516 assert.strictEqual(messages.length, 1, "/*eslint-disable*/ comment should report problem.");
11517 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11518 assert.strictEqual(suppressedMessages.length, 0);
11519
11520 code = "/*eslint-disable-line foo*/ ;";
11521 messages = linter.verify(code, {}, filename);
11522 suppressedMessages = linter.getSuppressedMessages();
11523 assert.strictEqual(messages.length, 1, "/*eslint-disable-line*/ comment should report problem.");
11524 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11525 assert.strictEqual(suppressedMessages.length, 0);
11526
11527 code = "/*eslint-disable-next-line foo*/ ;";
11528 messages = linter.verify(code, {}, filename);
11529 suppressedMessages = linter.getSuppressedMessages();
11530 assert.strictEqual(messages.length, 1, "/*eslint-disable-next-line*/ comment should report problem.");
11531 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11532 assert.strictEqual(suppressedMessages.length, 0);
11533 });
11534
11535 it("should not report an error, when disabling a non-existent rule in config", () => {
11536 const messages = linter.verify("", { rules: { foo: 0 } }, filename);
11537 const suppressedMessages = linter.getSuppressedMessages();
11538
11539 assert.strictEqual(messages.length, 0);
11540 assert.strictEqual(suppressedMessages.length, 0);
11541 });
11542
11543 it("should throw an error when a non-existent rule in config", () => {
11544 assert.throws(() => {
11545 linter.verify("", { rules: { foo: 1 } }, filename);
11546 }, /Key "rules": Key "foo":/u);
11547
11548 assert.throws(() => {
11549 linter.verify("", { rules: { foo: 2 } }, filename);
11550 }, /Key "rules": Key "foo":/u);
11551
11552 });
11553 });
11554
11555 describe("when evaluating code with comments to enable multiple rules", () => {
11556 const code = "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
11557
11558 it("should report a violation", () => {
11559 const config = { rules: {} };
11560
11561 const messages = linter.verify(code, config, filename);
11562 const suppressedMessages = linter.getSuppressedMessages();
11563
11564 assert.strictEqual(messages.length, 2);
11565 assert.strictEqual(messages[0].ruleId, "no-alert");
11566 assert.strictEqual(messages[0].message, "Unexpected alert.");
11567 assert.include(messages[0].nodeType, "CallExpression");
11568 assert.strictEqual(messages[1].ruleId, "no-console");
11569
11570 assert.strictEqual(suppressedMessages.length, 0);
11571 });
11572 });
11573
11574 describe("when evaluating code with comments to enable and disable multiple rules", () => {
11575 const code = "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
11576
11577 it("should report a violation", () => {
11578 const config = { rules: { "no-console": 1, "no-alert": 0 } };
11579
11580 const messages = linter.verify(code, config, filename);
11581 const suppressedMessages = linter.getSuppressedMessages();
11582
11583 assert.strictEqual(messages.length, 1);
11584 assert.strictEqual(messages[0].ruleId, "no-alert");
11585 assert.strictEqual(messages[0].message, "Unexpected alert.");
11586 assert.include(messages[0].nodeType, "CallExpression");
11587
11588 assert.strictEqual(suppressedMessages.length, 0);
11589 });
11590 });
11591
11592 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
11593
11594 let baseConfig;
11595
11596 beforeEach(() => {
11597 baseConfig = {
11598 plugins: {
11599 "test-plugin": {
11600 rules: {
11601 "test-rule"(context) {
11602 return {
11603 Literal(node) {
11604 if (node.value === "trigger violation") {
11605 context.report(node, "Reporting violation.");
11606 }
11607 }
11608 };
11609 }
11610 }
11611 }
11612 }
11613 };
11614
11615 });
11616
11617 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
11618 const config = { ...baseConfig, rules: {} };
11619 const code = "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
11620
11621 const messages = linter.verify(code, config, filename);
11622 const suppressedMessages = linter.getSuppressedMessages();
11623
11624 assert.strictEqual(messages.length, 0);
11625 assert.strictEqual(suppressedMessages.length, 0);
11626 });
11627
11628 it("should not report a violation when inline comment disables plugin rule", () => {
11629 const code = "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
11630 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 1 } };
11631
11632 const messages = linter.verify(code, config, filename);
11633 const suppressedMessages = linter.getSuppressedMessages();
11634
11635 assert.strictEqual(messages.length, 0);
11636 assert.strictEqual(suppressedMessages.length, 0);
11637 });
11638
11639 it("should report a violation when the report is right before the comment", () => {
11640 const code = " /* eslint-disable */ ";
11641
11642 const config = {
11643 plugins: {
11644 test: {
11645 rules: {
11646 checker: context => ({
11647 Program() {
11648 context.report({ loc: { line: 1, column: 0 }, message: "foo" });
11649 }
11650 })
11651 }
11652 }
11653 },
11654 rules: {
11655 "test/checker": "error"
11656 }
11657 };
11658
11659 const problems = linter.verify(code, config);
11660 const suppressedMessages = linter.getSuppressedMessages();
11661
11662 assert.strictEqual(problems.length, 1);
11663 assert.strictEqual(problems[0].message, "foo");
11664
11665 assert.strictEqual(suppressedMessages.length, 0);
11666 });
11667
11668 it("should not report a violation when the report is right at the start of the comment", () => {
11669 const code = " /* eslint-disable */ ";
11670
11671 const config = {
11672 plugins: {
11673 test: {
11674 rules: {
11675 checker: context => ({
11676 Program() {
11677 context.report({ loc: { line: 1, column: 1 }, message: "foo" });
11678 }
11679 })
11680 }
11681 }
11682 },
11683 rules: {
11684 "test/checker": "error"
11685 }
11686 };
11687
11688 const problems = linter.verify(code, config);
11689 const suppressedMessages = linter.getSuppressedMessages();
11690
11691 assert.strictEqual(problems.length, 0);
11692
11693 assert.strictEqual(suppressedMessages.length, 1);
11694 assert.strictEqual(suppressedMessages[0].message, "foo");
11695 assert.deepStrictEqual(suppressedMessages[0].suppressions, [{ kind: "directive", justification: "" }]);
11696 });
11697
11698 it("rules should not change initial config", () => {
11699 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 2 } };
11700 const codeA = "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
11701 const codeB = "var a = \"trigger violation\";";
11702 let messages = linter.verify(codeA, config, filename, false);
11703 let suppressedMessages = linter.getSuppressedMessages();
11704
11705 assert.strictEqual(messages.length, 0);
11706 assert.strictEqual(suppressedMessages.length, 0);
11707
11708 messages = linter.verify(codeB, config, filename, false);
11709 suppressedMessages = linter.getSuppressedMessages();
11710
11711 assert.strictEqual(messages.length, 1);
11712 assert.strictEqual(suppressedMessages.length, 0);
11713 });
11714 });
11715
11716 describe("when evaluating code with comments to enable and disable all reporting", () => {
11717 it("should report a violation", () => {
11718
11719 const code = [
11720 "/*eslint-disable */",
11721 "alert('test');",
11722 "/*eslint-enable */",
11723 "alert('test');"
11724 ].join("\n");
11725 const config = { rules: { "no-alert": 1 } };
11726
11727 const messages = linter.verify(code, config, filename);
11728 const suppressedMessages = linter.getSuppressedMessages();
11729
11730 assert.strictEqual(messages.length, 1);
11731 assert.strictEqual(messages[0].ruleId, "no-alert");
11732 assert.strictEqual(messages[0].message, "Unexpected alert.");
11733 assert.include(messages[0].nodeType, "CallExpression");
11734 assert.strictEqual(messages[0].line, 4);
11735
11736 assert.strictEqual(suppressedMessages.length, 1);
11737 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
11738 assert.strictEqual(suppressedMessages[0].line, 2);
11739 });
11740
11741 it("should not report a violation", () => {
11742 const code = [
11743 "/*eslint-disable */",
11744 "alert('test');",
11745 "alert('test');"
11746 ].join("\n");
11747 const config = { rules: { "no-alert": 1 } };
11748
11749 const messages = linter.verify(code, config, filename);
11750 const suppressedMessages = linter.getSuppressedMessages();
11751
11752 assert.strictEqual(messages.length, 0);
11753 assert.strictEqual(suppressedMessages.length, 2);
11754 });
11755
11756 it("should not report a violation", () => {
11757 const code = [
11758 " alert('test1');/*eslint-disable */\n",
11759 "alert('test');",
11760 " alert('test');\n",
11761 "/*eslint-enable */alert('test2');"
11762 ].join("");
11763 const config = { rules: { "no-alert": 1 } };
11764
11765 const messages = linter.verify(code, config, filename);
11766 const suppressedMessages = linter.getSuppressedMessages();
11767
11768 assert.strictEqual(messages.length, 2);
11769 assert.strictEqual(messages[0].column, 21);
11770 assert.strictEqual(messages[1].column, 19);
11771
11772 assert.strictEqual(suppressedMessages.length, 2);
11773 assert.strictEqual(suppressedMessages[0].column, 1);
11774 assert.strictEqual(suppressedMessages[1].column, 56);
11775 });
11776
11777 it("should report a violation", () => {
11778
11779 const code = [
11780 "/*eslint-disable */",
11781 "alert('test');",
11782 "/*eslint-disable */",
11783 "alert('test');",
11784 "/*eslint-enable*/",
11785 "alert('test');",
11786 "/*eslint-enable*/"
11787 ].join("\n");
11788
11789 const config = { rules: { "no-alert": 1 } };
11790
11791 const messages = linter.verify(code, config, filename);
11792 const suppressedMessages = linter.getSuppressedMessages();
11793
11794 assert.strictEqual(messages.length, 1);
11795 assert.strictEqual(suppressedMessages.length, 2);
11796 });
11797
11798
11799 it("should not report a violation", () => {
11800 const code = [
11801 "/*eslint-disable */",
11802 "(function(){ var b = 44;})()",
11803 "/*eslint-enable */;any();"
11804 ].join("\n");
11805
11806 const config = { rules: { "no-unused-vars": 1 } };
11807
11808 const messages = linter.verify(code, config, filename);
11809 const suppressedMessages = linter.getSuppressedMessages();
11810
11811 assert.strictEqual(messages.length, 0);
11812
11813 assert.strictEqual(suppressedMessages.length, 1);
11814 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
11815 });
11816
11817 it("should not report a violation", () => {
11818 const code = [
11819 "(function(){ /*eslint-disable */ var b = 44;})()",
11820 "/*eslint-enable */;any();"
11821 ].join("\n");
11822
11823 const config = { rules: { "no-unused-vars": 1 } };
11824
11825 const messages = linter.verify(code, config, filename);
11826 const suppressedMessages = linter.getSuppressedMessages();
11827
11828 assert.strictEqual(messages.length, 0);
11829
11830 assert.strictEqual(suppressedMessages.length, 1);
11831 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
11832 });
11833 });
11834
11835 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
11836 const code = "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
11837
11838 it("should report a violation", () => {
11839 const config = { rules: { "no-console": 1, "no-alert": 0 } };
11840
11841 const messages = linter.verify(code, config, filename);
11842 const suppressedMessages = linter.getSuppressedMessages();
11843
11844 assert.strictEqual(messages.length, 1);
11845 assert.strictEqual(messages[0].ruleId, "no-alert");
11846 assert.strictEqual(messages[0].message, "Unexpected alert.");
11847 assert.include(messages[0].nodeType, "CallExpression");
11848
11849 assert.strictEqual(suppressedMessages.length, 0);
11850 });
11851 });
11852
11853 describe("when evaluating code with comments to enable configurable rule", () => {
11854 const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');";
11855
11856 it("should report a violation", () => {
11857 const config = { rules: { quotes: [2, "single"] } };
11858
11859 const messages = linter.verify(code, config, filename);
11860 const suppressedMessages = linter.getSuppressedMessages();
11861
11862 assert.strictEqual(messages.length, 1);
11863 assert.strictEqual(messages[0].ruleId, "quotes");
11864 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
11865 assert.include(messages[0].nodeType, "Literal");
11866
11867 assert.strictEqual(suppressedMessages.length, 0);
11868 });
11869 });
11870
11871 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
11872 const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
11873
11874 it("should report a violation", () => {
11875 const config = { rules: { quotes: [2, "single"] } };
11876
11877 const messages = linter.verify(code, config, filename);
11878 const suppressedMessages = linter.getSuppressedMessages();
11879
11880 assert.strictEqual(messages.length, 1);
11881 assert.strictEqual(messages[0].ruleId, "quotes");
11882 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
11883 assert.include(messages[0].nodeType, "Literal");
11884
11885 assert.strictEqual(suppressedMessages.length, 0);
11886 });
11887 });
11888
11889 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
11890 it("should report a violation", () => {
11891 const code = "/*eslint no-alert:'1'*/ alert('test');";
11892
11893 const config = { rules: { "no-alert": 1 } };
11894
11895 const messages = linter.verify(code, config, filename);
11896 const suppressedMessages = linter.getSuppressedMessages();
11897
11898 assert.strictEqual(messages.length, 2);
11899
11900 /*
11901 * Incorrectly formatted comment threw error;
11902 * message from caught exception
11903 * may differ amongst UAs, so verifying
11904 * first part only as defined in the
11905 * parseJsonConfig function in lib/eslint.js
11906 */
11907 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":'1'':/u);
11908 assert.strictEqual(messages[0].line, 1);
11909 assert.strictEqual(messages[0].column, 1);
11910
11911 assert.strictEqual(messages[1].ruleId, "no-alert");
11912 assert.strictEqual(messages[1].message, "Unexpected alert.");
11913 assert.include(messages[1].nodeType, "CallExpression");
11914
11915 assert.strictEqual(suppressedMessages.length, 0);
11916 });
11917
11918 it("should report a violation", () => {
11919 const code = "/*eslint no-alert:abc*/ alert('test');";
11920
11921 const config = { rules: { "no-alert": 1 } };
11922
11923 const messages = linter.verify(code, config, filename);
11924 const suppressedMessages = linter.getSuppressedMessages();
11925
11926 assert.strictEqual(messages.length, 2);
11927
11928 /*
11929 * Incorrectly formatted comment threw error;
11930 * message from caught exception
11931 * may differ amongst UAs, so verifying
11932 * first part only as defined in the
11933 * parseJsonConfig function in lib/eslint.js
11934 */
11935 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":abc':/u);
11936 assert.strictEqual(messages[0].line, 1);
11937 assert.strictEqual(messages[0].column, 1);
11938
11939 assert.strictEqual(messages[1].ruleId, "no-alert");
11940 assert.strictEqual(messages[1].message, "Unexpected alert.");
11941 assert.include(messages[1].nodeType, "CallExpression");
11942
11943 assert.strictEqual(suppressedMessages.length, 0);
11944 });
11945
11946 it("should report a violation", () => {
11947 const code = "/*eslint no-alert:0 2*/ alert('test');";
11948
11949 const config = { rules: { "no-alert": 1 } };
11950
11951 const messages = linter.verify(code, config, filename);
11952 const suppressedMessages = linter.getSuppressedMessages();
11953
11954 assert.strictEqual(messages.length, 2);
11955
11956 /*
11957 * Incorrectly formatted comment threw error;
11958 * message from caught exception
11959 * may differ amongst UAs, so verifying
11960 * first part only as defined in the
11961 * parseJsonConfig function in lib/eslint.js
11962 */
11963 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":0 2':/u);
11964 assert.strictEqual(messages[0].line, 1);
11965 assert.strictEqual(messages[0].column, 1);
11966
11967 assert.strictEqual(messages[1].ruleId, "no-alert");
11968 assert.strictEqual(messages[1].message, "Unexpected alert.");
11969 assert.include(messages[1].nodeType, "CallExpression");
11970
11971 assert.strictEqual(suppressedMessages.length, 0);
11972 });
11973 });
11974
11975 describe("when evaluating code with comments which have colon in its value", () => {
11976 const code = String.raw`
11977 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
11978 alert('test');
11979 `;
11980
11981 it("should not parse errors, should report a violation", () => {
11982 const messages = linter.verify(code, {}, filename);
11983 const suppressedMessages = linter.getSuppressedMessages();
11984
11985 assert.strictEqual(messages.length, 1);
11986 assert.strictEqual(messages[0].ruleId, "max-len");
11987 assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100.");
11988 assert.include(messages[0].nodeType, "Program");
11989
11990 assert.strictEqual(suppressedMessages.length, 0);
11991 });
11992 });
11993
11994 describe("when evaluating code with comments that contain escape sequences", () => {
11995 const code = String.raw`
11996 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
11997 console.log("test");
11998 consolexlog("test2");
11999 var a = "test2";
12000 `;
12001
12002 it("should validate correctly", () => {
12003 const config = { rules: {} };
12004 const messages = linter.verify(code, config, filename);
12005 const [message1, message2] = messages;
12006 const suppressedMessages = linter.getSuppressedMessages();
12007
12008 assert.strictEqual(messages.length, 2);
12009 assert.strictEqual(message1.ruleId, "max-len");
12010 assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1.");
12011 assert.strictEqual(message1.line, 4);
12012 assert.strictEqual(message1.column, 1);
12013 assert.include(message1.nodeType, "Program");
12014 assert.strictEqual(message2.ruleId, "max-len");
12015 assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1.");
12016 assert.strictEqual(message2.line, 5);
12017 assert.strictEqual(message2.column, 1);
12018 assert.include(message2.nodeType, "Program");
12019
12020 assert.strictEqual(suppressedMessages.length, 0);
12021 });
12022 });
12023
12024 });
12025
12026 describe("/*eslint-disable*/ and /*eslint-enable*/", () => {
12027 it("should report a violation", () => {
12028 const code = [
12029 "/*eslint-disable no-alert */",
12030 "alert('test');",
12031 "console.log('test');" // here
12032 ].join("\n");
12033 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12034
12035 const messages = linter.verify(code, config, filename);
12036 const suppressedMessages = linter.getSuppressedMessages();
12037
12038 assert.strictEqual(messages.length, 1);
12039 assert.strictEqual(messages[0].ruleId, "no-console");
12040
12041 assert.strictEqual(suppressedMessages.length, 1);
12042 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12043 });
12044
12045 it("should report no violation", () => {
12046 const code = [
12047 "/* eslint-disable quotes */",
12048 "console.log(\"foo\");",
12049 "/* eslint-enable quotes */"
12050 ].join("\n");
12051 const config = { rules: { quotes: 2 } };
12052
12053 const messages = linter.verify(code, config, filename);
12054 const suppressedMessages = linter.getSuppressedMessages();
12055
12056 assert.strictEqual(messages.length, 0);
12057 assert.strictEqual(suppressedMessages.length, 0);
12058 });
12059
12060 it("should report a violation", () => {
12061 const code = [
12062 "/*eslint-disable no-alert, no-console */",
12063 "alert('test');",
12064 "console.log('test');",
12065 "/*eslint-enable*/",
12066
12067 "alert('test');", // here
12068 "console.log('test');" // here
12069 ].join("\n");
12070 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12071
12072 const messages = linter.verify(code, config, filename);
12073 const suppressedMessages = linter.getSuppressedMessages();
12074
12075 assert.strictEqual(messages.length, 2);
12076 assert.strictEqual(messages[0].ruleId, "no-alert");
12077 assert.strictEqual(messages[0].line, 5);
12078 assert.strictEqual(messages[1].ruleId, "no-console");
12079 assert.strictEqual(messages[1].line, 6);
12080
12081 assert.strictEqual(suppressedMessages.length, 2);
12082 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12083 assert.strictEqual(suppressedMessages[0].line, 2);
12084 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12085 assert.strictEqual(suppressedMessages[1].line, 3);
12086 });
12087
12088 it("should report a violation", () => {
12089 const code = [
12090 "/*eslint-disable no-alert */",
12091 "alert('test');",
12092 "console.log('test');", // here
12093 "/*eslint-enable no-console */",
12094 "alert('test');"
12095 ].join("\n");
12096 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12097
12098 const messages = linter.verify(code, config, filename);
12099 const suppressedMessages = linter.getSuppressedMessages();
12100
12101 assert.strictEqual(messages.length, 1);
12102 assert.strictEqual(messages[0].ruleId, "no-console");
12103
12104 assert.strictEqual(suppressedMessages.length, 2);
12105 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12106 assert.strictEqual(suppressedMessages[0].line, 2);
12107 assert.strictEqual(suppressedMessages[1].ruleId, "no-alert");
12108 assert.strictEqual(suppressedMessages[1].line, 5);
12109 });
12110
12111
12112 it("should report a violation", () => {
12113 const code = [
12114 "/*eslint-disable no-alert, no-console */",
12115 "alert('test');",
12116 "console.log('test');",
12117 "/*eslint-enable no-alert*/",
12118
12119 "alert('test');", // here
12120 "console.log('test');"
12121 ].join("\n");
12122 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12123
12124 const messages = linter.verify(code, config, filename);
12125 const suppressedMessages = linter.getSuppressedMessages();
12126
12127 assert.strictEqual(messages.length, 1);
12128 assert.strictEqual(messages[0].ruleId, "no-alert");
12129 assert.strictEqual(messages[0].line, 5);
12130
12131 assert.strictEqual(suppressedMessages.length, 3);
12132 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12133 assert.strictEqual(suppressedMessages[0].line, 2);
12134 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12135 assert.strictEqual(suppressedMessages[1].line, 3);
12136 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12137 assert.strictEqual(suppressedMessages[2].line, 6);
12138 });
12139
12140
12141 it("should report a violation", () => {
12142 const code = [
12143 "/*eslint-disable no-alert */",
12144
12145 "/*eslint-disable no-console */",
12146 "alert('test');",
12147 "console.log('test');",
12148 "/*eslint-enable */",
12149
12150 "alert('test');", // here
12151 "console.log('test');", // here
12152
12153 "/*eslint-enable */",
12154
12155 "alert('test');", // here
12156 "console.log('test');", // here
12157
12158 "/*eslint-enable*/"
12159 ].join("\n");
12160 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12161
12162 const messages = linter.verify(code, config, filename);
12163 const suppressedMessages = linter.getSuppressedMessages();
12164
12165 assert.strictEqual(messages.length, 4);
12166 assert.strictEqual(messages[0].ruleId, "no-alert");
12167 assert.strictEqual(messages[0].line, 6);
12168 assert.strictEqual(messages[1].ruleId, "no-console");
12169 assert.strictEqual(messages[1].line, 7);
12170 assert.strictEqual(messages[2].ruleId, "no-alert");
12171 assert.strictEqual(messages[2].line, 9);
12172 assert.strictEqual(messages[3].ruleId, "no-console");
12173 assert.strictEqual(messages[3].line, 10);
12174
12175 assert.strictEqual(suppressedMessages.length, 2);
12176 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12177 assert.strictEqual(suppressedMessages[0].line, 3);
12178 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12179 assert.strictEqual(suppressedMessages[1].line, 4);
12180 });
12181
12182 it("should report a violation", () => {
12183 const code = [
12184 "/*eslint-disable no-alert, no-console */",
12185 "alert('test');",
12186 "console.log('test');",
12187
12188 "/*eslint-enable no-alert */",
12189
12190 "alert('test');", // here
12191 "console.log('test');",
12192
12193 "/*eslint-enable no-console */",
12194
12195 "alert('test');", // here
12196 "console.log('test');", // here
12197 "/*eslint-enable no-console */"
12198 ].join("\n");
12199 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12200
12201 const messages = linter.verify(code, config, filename);
12202 const suppressedMessages = linter.getSuppressedMessages();
12203
12204 assert.strictEqual(messages.length, 3);
12205 assert.strictEqual(messages[0].ruleId, "no-alert");
12206 assert.strictEqual(messages[0].line, 5);
12207 assert.strictEqual(messages[1].ruleId, "no-alert");
12208 assert.strictEqual(messages[1].line, 8);
12209 assert.strictEqual(messages[2].ruleId, "no-console");
12210 assert.strictEqual(messages[2].line, 9);
12211
12212 assert.strictEqual(suppressedMessages.length, 3);
12213 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12214 assert.strictEqual(suppressedMessages[0].line, 2);
12215 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12216 assert.strictEqual(suppressedMessages[1].line, 3);
12217 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12218 assert.strictEqual(suppressedMessages[2].line, 6);
12219 });
12220
12221 it("should report a violation when severity is warn", () => {
12222 const code = [
12223 "/*eslint-disable no-alert, no-console */",
12224 "alert('test');",
12225 "console.log('test');",
12226
12227 "/*eslint-enable no-alert */",
12228
12229 "alert('test');", // here
12230 "console.log('test');",
12231
12232 "/*eslint-enable no-console */",
12233
12234 "alert('test');", // here
12235 "console.log('test');", // here
12236 "/*eslint-enable no-console */"
12237 ].join("\n");
12238 const config = { rules: { "no-alert": "warn", "no-console": "warn" } };
12239
12240 const messages = linter.verify(code, config, filename);
12241 const suppressedMessages = linter.getSuppressedMessages();
12242
12243 assert.strictEqual(messages.length, 3);
12244 assert.strictEqual(messages[0].ruleId, "no-alert");
12245 assert.strictEqual(messages[0].line, 5);
12246 assert.strictEqual(messages[1].ruleId, "no-alert");
12247 assert.strictEqual(messages[1].line, 8);
12248 assert.strictEqual(messages[2].ruleId, "no-console");
12249 assert.strictEqual(messages[2].line, 9);
12250
12251 assert.strictEqual(suppressedMessages.length, 3);
12252 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12253 assert.strictEqual(suppressedMessages[0].line, 2);
12254 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12255 assert.strictEqual(suppressedMessages[1].line, 3);
12256 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12257 assert.strictEqual(suppressedMessages[2].line, 6);
12258 });
12259
12260 it("should report no violation", () => {
12261 const code = [
12262 "/*eslint-disable no-unused-vars */",
12263 "var foo; // eslint-disable-line no-unused-vars",
12264 "var bar;",
12265 "/* eslint-enable no-unused-vars */" // here
12266 ].join("\n");
12267 const config = { rules: { "no-unused-vars": 2 } };
12268
12269 const messages = linter.verify(code, config, filename);
12270 const suppressedMessages = linter.getSuppressedMessages();
12271
12272 assert.strictEqual(messages.length, 0);
12273
12274 assert.strictEqual(suppressedMessages.length, 2);
12275 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
12276 assert.strictEqual(suppressedMessages[0].line, 2);
12277 assert.strictEqual(suppressedMessages[1].ruleId, "no-unused-vars");
12278 assert.strictEqual(suppressedMessages[1].line, 3);
12279 });
12280
12281 });
12282
12283 describe("/*eslint-disable-line*/", () => {
12284
12285 it("should report a violation", () => {
12286 const code = [
12287 "alert('test'); // eslint-disable-line no-alert",
12288 "console.log('test');" // here
12289 ].join("\n");
12290 const config = {
12291 rules: {
12292 "no-alert": 1,
12293 "no-console": 1
12294 }
12295 };
12296
12297 const messages = linter.verify(code, config, filename);
12298 const suppressedMessages = linter.getSuppressedMessages();
12299
12300 assert.strictEqual(messages.length, 1);
12301 assert.strictEqual(messages[0].ruleId, "no-console");
12302 assert.strictEqual(messages[0].line, 2);
12303
12304 assert.strictEqual(suppressedMessages.length, 1);
12305 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12306 assert.strictEqual(suppressedMessages[0].line, 1);
12307 });
12308
12309 it("should report a violation", () => {
12310 const code = [
12311 "alert('test'); // eslint-disable-line no-alert",
12312 "console.log('test'); // eslint-disable-line no-console",
12313 "alert('test');" // here
12314 ].join("\n");
12315 const config = {
12316 rules: {
12317 "no-alert": 1,
12318 "no-console": 1
12319 }
12320 };
12321
12322 const messages = linter.verify(code, config, filename);
12323 const suppressedMessages = linter.getSuppressedMessages();
12324
12325 assert.strictEqual(messages.length, 1);
12326 assert.strictEqual(messages[0].ruleId, "no-alert");
12327 assert.strictEqual(messages[0].line, 3);
12328
12329 assert.strictEqual(suppressedMessages.length, 2);
12330 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12331 assert.strictEqual(suppressedMessages[0].line, 1);
12332 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12333 assert.strictEqual(suppressedMessages[1].line, 2);
12334 });
12335
12336 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
12337 const code = [
12338 "/* eslint-disable-line",
12339 "*",
12340 "*/ console.log('test');" // here
12341 ].join("\n");
12342 const config = {
12343 rules: {
12344 "no-console": 1
12345 }
12346 };
12347
12348 const messages = linter.verify(code, config, filename);
12349 const suppressedMessages = linter.getSuppressedMessages();
12350
12351 assert.strictEqual(messages.length, 2);
12352 assert.strictEqual(messages[1].ruleId, "no-console");
12353
12354 assert.strictEqual(suppressedMessages.length, 0);
12355 });
12356
12357 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
12358 const code = [
12359 "alert('test'); /* eslint-disable-line ",
12360 "no-alert */"
12361 ].join("\n");
12362 const config = {
12363 rules: {
12364 "no-alert": 1
12365 }
12366 };
12367
12368 const messages = linter.verify(code, config);
12369 const suppressedMessages = linter.getSuppressedMessages();
12370
12371 assert.deepStrictEqual(messages, [
12372 {
12373 ruleId: "no-alert",
12374 severity: 1,
12375 line: 1,
12376 column: 1,
12377 endLine: 1,
12378 endColumn: 14,
12379 message: "Unexpected alert.",
12380 messageId: "unexpected",
12381 nodeType: "CallExpression"
12382 },
12383 {
12384 ruleId: null,
12385 severity: 2,
12386 message: "eslint-disable-line comment should not span multiple lines.",
12387 line: 1,
12388 column: 16,
12389 endLine: 2,
12390 endColumn: 12,
12391 nodeType: null
12392 }
12393 ]);
12394
12395 assert.strictEqual(suppressedMessages.length, 0);
12396 });
12397
12398 it("should not report a violation for eslint-disable-line in block comment", () => {
12399 const code = [
12400 "alert('test'); // eslint-disable-line no-alert",
12401 "alert('test'); /*eslint-disable-line no-alert*/"
12402 ].join("\n");
12403 const config = {
12404 rules: {
12405 "no-alert": 1
12406 }
12407 };
12408
12409 const messages = linter.verify(code, config, filename);
12410 const suppressedMessages = linter.getSuppressedMessages();
12411
12412 assert.strictEqual(messages.length, 0);
12413 assert.strictEqual(suppressedMessages.length, 2);
12414 });
12415
12416 it("should not report a violation", () => {
12417 const code = [
12418 "alert('test'); // eslint-disable-line no-alert",
12419 "console.log('test'); // eslint-disable-line no-console"
12420 ].join("\n");
12421 const config = {
12422 rules: {
12423 "no-alert": 1,
12424 "no-console": 1
12425 }
12426 };
12427
12428 const messages = linter.verify(code, config, filename);
12429 const suppressedMessages = linter.getSuppressedMessages();
12430
12431 assert.strictEqual(messages.length, 0);
12432 assert.strictEqual(suppressedMessages.length, 2);
12433 });
12434
12435 it("should not report a violation", () => {
12436 const code = [
12437 "alert('test') // eslint-disable-line no-alert, quotes, semi",
12438 "console.log('test'); // eslint-disable-line"
12439 ].join("\n");
12440 const config = {
12441 rules: {
12442 "no-alert": 1,
12443 quotes: [1, "double"],
12444 semi: [1, "always"],
12445 "no-console": 1
12446 }
12447 };
12448
12449 const messages = linter.verify(code, config, filename);
12450 const suppressedMessages = linter.getSuppressedMessages();
12451
12452 assert.strictEqual(messages.length, 0);
12453 assert.strictEqual(suppressedMessages.length, 5);
12454 });
12455
12456 it("should not report a violation", () => {
12457 const code = [
12458 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
12459 "console.log('test'); /* eslint-disable-line */"
12460 ].join("\n");
12461 const config = {
12462 rules: {
12463 "no-alert": 1,
12464 quotes: [1, "double"],
12465 semi: [1, "always"],
12466 "no-console": 1
12467 }
12468 };
12469
12470 const messages = linter.verify(code, config, filename);
12471 const suppressedMessages = linter.getSuppressedMessages();
12472
12473 assert.strictEqual(messages.length, 0);
12474 assert.strictEqual(suppressedMessages.length, 5);
12475 });
12476
12477 it("should ignore violations of multiple rules when specified in mixed comments", () => {
12478 const code = [
12479 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
12480 ].join("\n");
12481 const config = {
12482 rules: {
12483 "no-alert": 1,
12484 quotes: [1, "single"]
12485 }
12486 };
12487 const messages = linter.verify(code, config, filename);
12488 const suppressedMessages = linter.getSuppressedMessages();
12489
12490 assert.strictEqual(messages.length, 0);
12491
12492 assert.strictEqual(suppressedMessages.length, 2);
12493 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12494 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12495 });
12496
12497 it("should report no violation", () => {
12498 const code = [
12499 "var foo1; // eslint-disable-line no-unused-vars",
12500 "var foo2; // eslint-disable-line no-unused-vars",
12501 "var foo3; // eslint-disable-line no-unused-vars",
12502 "var foo4; // eslint-disable-line no-unused-vars",
12503 "var foo5; // eslint-disable-line no-unused-vars"
12504 ].join("\n");
12505 const config = { rules: { "no-unused-vars": 2 } };
12506
12507 const messages = linter.verify(code, config, filename);
12508 const suppressedMessages = linter.getSuppressedMessages();
12509
12510 assert.strictEqual(messages.length, 0);
12511 assert.strictEqual(suppressedMessages.length, 5);
12512 });
12513
12514 });
12515
12516 describe("/*eslint-disable-next-line*/", () => {
12517 it("should ignore violation of specified rule on next line", () => {
12518 const code = [
12519 "// eslint-disable-next-line no-alert",
12520 "alert('test');",
12521 "console.log('test');"
12522 ].join("\n");
12523 const config = {
12524 rules: {
12525 "no-alert": 1,
12526 "no-console": 1
12527 }
12528 };
12529 const messages = linter.verify(code, config, filename);
12530 const suppressedMessages = linter.getSuppressedMessages();
12531
12532 assert.strictEqual(messages.length, 1);
12533 assert.strictEqual(messages[0].ruleId, "no-console");
12534
12535 assert.strictEqual(suppressedMessages.length, 1);
12536 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12537 });
12538
12539 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
12540 const code = [
12541 "/* eslint-disable-next-line no-alert */",
12542 "alert('test');",
12543 "console.log('test');"
12544 ].join("\n");
12545 const config = {
12546 rules: {
12547 "no-alert": 1,
12548 "no-console": 1
12549 }
12550 };
12551 const messages = linter.verify(code, config, filename);
12552 const suppressedMessages = linter.getSuppressedMessages();
12553
12554 assert.strictEqual(messages.length, 1);
12555 assert.strictEqual(messages[0].ruleId, "no-console");
12556
12557 assert.strictEqual(suppressedMessages.length, 1);
12558 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12559 });
12560 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
12561 const code = [
12562 "/* eslint-disable-next-line no-alert */",
12563 "alert('test');"
12564 ].join("\n");
12565 const config = {
12566 rules: {
12567 "no-alert": 1
12568 }
12569 };
12570 const messages = linter.verify(code, config, filename);
12571 const suppressedMessages = linter.getSuppressedMessages();
12572
12573 assert.strictEqual(messages.length, 0);
12574
12575 assert.strictEqual(suppressedMessages.length, 1);
12576 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12577 });
12578
12579 it("should not ignore violation if code is not on next line", () => {
12580 const code = [
12581 "/* eslint-disable-next-line",
12582 "no-alert */alert('test');"
12583 ].join("\n");
12584 const config = {
12585 rules: {
12586 "no-alert": 1
12587 }
12588 };
12589 const messages = linter.verify(code, config, filename);
12590 const suppressedMessages = linter.getSuppressedMessages();
12591
12592 assert.strictEqual(messages.length, 1);
12593 assert.strictEqual(messages[0].ruleId, "no-alert");
12594
12595 assert.strictEqual(suppressedMessages.length, 0);
12596 });
12597
12598 it("should ignore violation if block comment span multiple lines", () => {
12599 const code = [
12600 "/* eslint-disable-next-line",
12601 "no-alert */",
12602 "alert('test');"
12603 ].join("\n");
12604 const config = {
12605 rules: {
12606 "no-alert": 1
12607 }
12608 };
12609 const messages = linter.verify(code, config, filename);
12610 const suppressedMessages = linter.getSuppressedMessages();
12611
12612 assert.strictEqual(messages.length, 0);
12613
12614 assert.strictEqual(suppressedMessages.length, 1);
12615 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12616 });
12617
12618 // For https://github.com/eslint/eslint/issues/14284
12619 it("should ignore violation if block comment span multiple lines with description", () => {
12620 const code = `
12621 /* eslint-disable-next-line no-alert --
12622 description on why this exception is seen as appropriate but past a
12623 comfortable reading line length
12624 */
12625 alert("buzz");
12626 `;
12627 const config = {
12628 rules: {
12629 "no-alert": 1
12630 }
12631 };
12632 const messages = linter.verify(code, config, filename);
12633 const suppressedMessages = linter.getSuppressedMessages();
12634
12635 assert.strictEqual(messages.length, 0);
12636
12637 assert.strictEqual(suppressedMessages.length, 1);
12638 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12639 });
12640
12641 it("should ignore violations only of specified rule", () => {
12642 const code = [
12643 "// eslint-disable-next-line no-console",
12644 "alert('test');",
12645 "console.log('test');"
12646 ].join("\n");
12647 const config = {
12648 rules: {
12649 "no-alert": 1,
12650 "no-console": 1
12651 }
12652 };
12653 const messages = linter.verify(code, config, filename);
12654 const suppressedMessages = linter.getSuppressedMessages();
12655
12656 assert.strictEqual(messages.length, 2);
12657 assert.strictEqual(messages[0].ruleId, "no-alert");
12658 assert.strictEqual(messages[1].ruleId, "no-console");
12659
12660 assert.strictEqual(suppressedMessages.length, 0);
12661 });
12662
12663 it("should ignore violations only of specified rule when block comment span multiple lines", () => {
12664 const code = [
12665 "/* eslint-disable-next-line",
12666 "no-console */",
12667 "alert('test');",
12668 "console.log('test');"
12669 ].join("\n");
12670 const config = {
12671 rules: {
12672 "no-alert": 1,
12673 "no-console": 1
12674 }
12675 };
12676 const messages = linter.verify(code, config, filename);
12677
12678 assert.strictEqual(messages.length, 2);
12679 assert.strictEqual(messages[0].ruleId, "no-alert");
12680 assert.strictEqual(messages[1].ruleId, "no-console");
12681 });
12682
12683 it("should ignore violations of multiple rules when specified", () => {
12684 const code = [
12685 "// eslint-disable-next-line no-alert, quotes",
12686 "alert(\"test\");",
12687 "console.log('test');"
12688 ].join("\n");
12689 const config = {
12690 rules: {
12691 "no-alert": 1,
12692 quotes: [1, "single"],
12693 "no-console": 1
12694 }
12695 };
12696 const messages = linter.verify(code, config, filename);
12697 const suppressedMessages = linter.getSuppressedMessages();
12698
12699 assert.strictEqual(messages.length, 1);
12700 assert.strictEqual(messages[0].ruleId, "no-console");
12701
12702 assert.strictEqual(suppressedMessages.length, 2);
12703 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12704 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12705 });
12706
12707 it("should ignore violations of multiple rules when specified in multiple lines", () => {
12708 const code = [
12709 "/* eslint-disable-next-line",
12710 "no-alert,",
12711 "quotes",
12712 "*/",
12713 "alert(\"test\");",
12714 "console.log('test');"
12715 ].join("\n");
12716 const config = {
12717 rules: {
12718 "no-alert": 1,
12719 quotes: [1, "single"],
12720 "no-console": 1
12721 }
12722 };
12723 const messages = linter.verify(code, config, filename);
12724
12725 assert.strictEqual(messages.length, 1);
12726 assert.strictEqual(messages[0].ruleId, "no-console");
12727 });
12728
12729 it("should ignore violations of multiple rules when specified in mixed comments", () => {
12730 const code = [
12731 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
12732 "alert(\"test\");"
12733 ].join("\n");
12734 const config = {
12735 rules: {
12736 "no-alert": 1,
12737 quotes: [1, "single"]
12738 }
12739 };
12740 const messages = linter.verify(code, config, filename);
12741 const suppressedMessages = linter.getSuppressedMessages();
12742
12743 assert.strictEqual(messages.length, 0);
12744
12745 assert.strictEqual(suppressedMessages.length, 2);
12746 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12747 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12748 });
12749
12750 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
12751 const code = [
12752 "/* eslint-disable-next-line",
12753 "no-alert",
12754 "*/ // eslint-disable-next-line quotes",
12755 "alert(\"test\");"
12756 ].join("\n");
12757 const config = {
12758 rules: {
12759 "no-alert": 1,
12760 quotes: [1, "single"]
12761 }
12762 };
12763 const messages = linter.verify(code, config, filename);
12764
12765 assert.strictEqual(messages.length, 0);
12766 });
12767
12768 it("should ignore violations of only the specified rule on next line", () => {
12769 const code = [
12770 "// eslint-disable-next-line quotes",
12771 "alert(\"test\");",
12772 "console.log('test');"
12773 ].join("\n");
12774 const config = {
12775 rules: {
12776 "no-alert": 1,
12777 quotes: [1, "single"],
12778 "no-console": 1
12779 }
12780 };
12781 const messages = linter.verify(code, config, filename);
12782 const suppressedMessages = linter.getSuppressedMessages();
12783
12784 assert.strictEqual(messages.length, 2);
12785 assert.strictEqual(messages[0].ruleId, "no-alert");
12786 assert.strictEqual(messages[1].ruleId, "no-console");
12787
12788 assert.strictEqual(suppressedMessages.length, 1);
12789 assert.strictEqual(suppressedMessages[0].ruleId, "quotes");
12790 });
12791
12792 it("should ignore violations of specified rule on next line only", () => {
12793 const code = [
12794 "alert('test');",
12795 "// eslint-disable-next-line no-alert",
12796 "alert('test');",
12797 "console.log('test');"
12798 ].join("\n");
12799 const config = {
12800 rules: {
12801 "no-alert": 1,
12802 "no-console": 1
12803 }
12804 };
12805 const messages = linter.verify(code, config, filename);
12806 const suppressedMessages = linter.getSuppressedMessages();
12807
12808 assert.strictEqual(messages.length, 2);
12809 assert.strictEqual(messages[0].ruleId, "no-alert");
12810 assert.strictEqual(messages[1].ruleId, "no-console");
12811
12812 assert.strictEqual(suppressedMessages.length, 1);
12813 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12814 });
12815
12816 it("should ignore all rule violations on next line if none specified", () => {
12817 const code = [
12818 "// eslint-disable-next-line",
12819 "alert(\"test\");",
12820 "console.log('test')"
12821 ].join("\n");
12822 const config = {
12823 rules: {
12824 semi: [1, "never"],
12825 quotes: [1, "single"],
12826 "no-alert": 1,
12827 "no-console": 1
12828 }
12829 };
12830 const messages = linter.verify(code, config, filename);
12831 const suppressedMessages = linter.getSuppressedMessages();
12832
12833 assert.strictEqual(messages.length, 1);
12834 assert.strictEqual(messages[0].ruleId, "no-console");
12835
12836 assert.strictEqual(suppressedMessages.length, 3);
12837 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12838 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12839 assert.strictEqual(suppressedMessages[2].ruleId, "semi");
12840 });
12841
12842 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
12843 const code = [
12844 "alert('test');",
12845 "/* eslint-disable-next-line no-alert */",
12846 "alert('test');",
12847 "console.log('test');"
12848 ].join("\n");
12849 const config = {
12850 rules: {
12851 "no-alert": 1,
12852 "no-console": 1
12853 }
12854 };
12855 const messages = linter.verify(code, config, filename);
12856 const suppressedMessages = linter.getSuppressedMessages();
12857
12858 assert.strictEqual(messages.length, 2);
12859 assert.strictEqual(messages[0].ruleId, "no-alert");
12860 assert.strictEqual(messages[1].ruleId, "no-console");
12861
12862 assert.strictEqual(suppressedMessages.length, 1);
12863 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12864 });
12865
12866 it("should report a violation", () => {
12867 const code = [
12868 "/* eslint-disable-next-line",
12869 "*",
12870 "*/",
12871 "console.log('test');" // here
12872 ].join("\n");
12873 const config = {
12874 rules: {
12875 "no-alert": 1,
12876 "no-console": 1
12877 }
12878 };
12879
12880 const messages = linter.verify(code, config, filename);
12881 const suppressedMessages = linter.getSuppressedMessages();
12882
12883 assert.strictEqual(messages.length, 2);
12884 assert.strictEqual(messages[1].ruleId, "no-console");
12885
12886 assert.strictEqual(suppressedMessages.length, 0);
12887 });
12888
12889 it("should not ignore violations if comment is of the type hashbang", () => {
12890 const code = [
12891 "#! eslint-disable-next-line no-alert",
12892 "alert('test');",
12893 "console.log('test');"
12894 ].join("\n");
12895 const config = {
12896 rules: {
12897 "no-alert": 1,
12898 "no-console": 1
12899 }
12900 };
12901 const messages = linter.verify(code, config, filename);
12902 const suppressedMessages = linter.getSuppressedMessages();
12903
12904 assert.strictEqual(messages.length, 2);
12905 assert.strictEqual(messages[0].ruleId, "no-alert");
12906 assert.strictEqual(messages[1].ruleId, "no-console");
12907
12908 assert.strictEqual(suppressedMessages.length, 0);
12909 });
12910 });
12911
12912 describe("descriptions in directive comments", () => {
12913 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
12914 const aaa = sinon.stub().returns({});
12915 const bbb = sinon.stub().returns({});
12916 const config = {
12917 plugins: {
12918 test: {
12919 rules: {
12920 aaa: { create: aaa },
12921 bbb: { create: bbb }
12922 }
12923 }
12924 }
12925 };
12926
12927 const messages = linter.verify(`
12928 /*eslint test/aaa:error -- test/bbb:error */
12929 console.log("hello")
12930 `, config);
12931 const suppressedMessages = linter.getSuppressedMessages();
12932
12933 // Don't include syntax error of the comment.
12934 assert.deepStrictEqual(messages, []);
12935
12936 // Use only `aaa`.
12937 assert.strictEqual(aaa.callCount, 1);
12938 assert.strictEqual(bbb.callCount, 0);
12939
12940 assert.strictEqual(suppressedMessages.length, 0);
12941 });
12942
12943 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
12944 const messages = linter.verify(`
12945 /*globals aaa -- bbb */
12946 var aaa = {}
12947 var bbb = {}
12948 `, {
12949 languageOptions: {
12950 sourceType: "script"
12951 },
12952 rules: { "no-redeclare": "error" }
12953 });
12954 const suppressedMessages = linter.getSuppressedMessages();
12955
12956 // Don't include `bbb`
12957 assert.deepStrictEqual(
12958 messages,
12959 [{
12960 column: 31,
12961 endColumn: 34,
12962 line: 2,
12963 endLine: 2,
12964 message: "'aaa' is already defined by a variable declaration.",
12965 messageId: "redeclaredBySyntax",
12966 nodeType: "Block",
12967 ruleId: "no-redeclare",
12968 severity: 2
12969 }]
12970 );
12971
12972 assert.strictEqual(suppressedMessages.length, 0);
12973 });
12974
12975 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
12976 const messages = linter.verify(`
12977 /*exported aaa -- bbb */
12978 var aaa = {}
12979 var bbb = {}
12980 `, {
12981 languageOptions: {
12982 sourceType: "script"
12983 },
12984 rules: { "no-unused-vars": "error" }
12985 });
12986 const suppressedMessages = linter.getSuppressedMessages();
12987
12988 // Don't include `aaa`
12989 assert.deepStrictEqual(
12990 messages,
12991 [{
12992 column: 25,
12993 endColumn: 28,
12994 endLine: 4,
12995 line: 4,
12996 message: "'bbb' is assigned a value but never used.",
12997 messageId: "unusedVar",
12998 nodeType: "Identifier",
12999 ruleId: "no-unused-vars",
13000 severity: 2
13001 }]
13002 );
13003
13004 assert.strictEqual(suppressedMessages.length, 0);
13005 });
13006
13007 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
13008 const messages = linter.verify(`
13009 /*eslint-disable no-redeclare -- no-unused-vars */
13010 var aaa = {}
13011 var aaa = {}
13012 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13013 const suppressedMessages = linter.getSuppressedMessages();
13014
13015 // Do include `no-unused-vars` but not `no-redeclare`
13016 assert.deepStrictEqual(
13017 messages,
13018 [{
13019 column: 25,
13020 endLine: 4,
13021 endColumn: 28,
13022 line: 4,
13023 message: "'aaa' is assigned a value but never used.",
13024 messageId: "unusedVar",
13025 nodeType: "Identifier",
13026 ruleId: "no-unused-vars",
13027 severity: 2
13028 }]
13029 );
13030
13031 assert.deepStrictEqual(
13032 suppressedMessages,
13033 [{
13034 column: 25,
13035 endColumn: 28,
13036 endLine: 4,
13037 line: 4,
13038 message: "'aaa' is already defined.",
13039 messageId: "redeclared",
13040 nodeType: "Identifier",
13041 ruleId: "no-redeclare",
13042 severity: 2,
13043 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13044 }]
13045 );
13046 });
13047
13048 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
13049 const messages = linter.verify(`
13050 /*eslint-disable no-redeclare, no-unused-vars */
13051 /*eslint-enable no-redeclare -- no-unused-vars */
13052 var aaa = {}
13053 var aaa = {}
13054 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13055 const suppressedMessages = linter.getSuppressedMessages();
13056
13057 // Do include `no-redeclare` but not `no-unused-vars`
13058 assert.deepStrictEqual(
13059 messages,
13060 [{
13061 column: 25,
13062 endLine: 5,
13063 endColumn: 28,
13064 line: 5,
13065 message: "'aaa' is already defined.",
13066 messageId: "redeclared",
13067 nodeType: "Identifier",
13068 ruleId: "no-redeclare",
13069 severity: 2
13070 }]
13071 );
13072
13073 assert.deepStrictEqual(
13074 suppressedMessages,
13075 [{
13076 column: 25,
13077 endLine: 5,
13078 endColumn: 28,
13079 line: 5,
13080 message: "'aaa' is assigned a value but never used.",
13081 messageId: "unusedVar",
13082 nodeType: "Identifier",
13083 ruleId: "no-unused-vars",
13084 severity: 2,
13085 suppressions: [{ kind: "directive", justification: "" }]
13086 }]
13087 );
13088 });
13089
13090 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
13091 const messages = linter.verify(`
13092 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
13093 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
13094 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13095 const suppressedMessages = linter.getSuppressedMessages();
13096
13097 // Do include `no-unused-vars` but not `no-redeclare`
13098 assert.deepStrictEqual(
13099 messages,
13100 [{
13101 column: 25,
13102 endLine: 3,
13103 endColumn: 28,
13104 line: 3,
13105 message: "'aaa' is assigned a value but never used.",
13106 messageId: "unusedVar",
13107 nodeType: "Identifier",
13108 ruleId: "no-unused-vars",
13109 severity: 2
13110 }]
13111 );
13112
13113 assert.deepStrictEqual(
13114 suppressedMessages,
13115 [{
13116 column: 25,
13117 endLine: 3,
13118 endColumn: 28,
13119 line: 3,
13120 message: "'aaa' is already defined.",
13121 messageId: "redeclared",
13122 nodeType: "Identifier",
13123 ruleId: "no-redeclare",
13124 severity: 2,
13125 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13126 }]
13127 );
13128 });
13129
13130 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
13131 const messages = linter.verify(`
13132 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
13133 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
13134 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13135 const suppressedMessages = linter.getSuppressedMessages();
13136
13137 // Do include `no-unused-vars` but not `no-redeclare`
13138 assert.deepStrictEqual(
13139 messages,
13140 [{
13141 column: 25,
13142 endLine: 3,
13143 endColumn: 28,
13144 line: 3,
13145 message: "'aaa' is assigned a value but never used.",
13146 messageId: "unusedVar",
13147 nodeType: "Identifier",
13148 ruleId: "no-unused-vars",
13149 severity: 2
13150 }]
13151 );
13152
13153 assert.deepStrictEqual(
13154 suppressedMessages,
13155 [{
13156 column: 25,
13157 endLine: 3,
13158 endColumn: 28,
13159 line: 3,
13160 message: "'aaa' is already defined.",
13161 messageId: "redeclared",
13162 nodeType: "Identifier",
13163 ruleId: "no-redeclare",
13164 severity: 2,
13165 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13166 }]
13167 );
13168 });
13169
13170 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
13171 const messages = linter.verify(`
13172 //eslint-disable-next-line no-redeclare -- no-unused-vars
13173 var aaa = {}
13174 //eslint-disable-next-line no-redeclare -- no-unused-vars
13175 var aaa = {}
13176 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13177 const suppressedMessages = linter.getSuppressedMessages();
13178
13179 // Do include `no-unused-vars` but not `no-redeclare`
13180 assert.deepStrictEqual(
13181 messages,
13182 [{
13183 column: 25,
13184 endLine: 5,
13185 endColumn: 28,
13186 line: 5,
13187 message: "'aaa' is assigned a value but never used.",
13188 messageId: "unusedVar",
13189 nodeType: "Identifier",
13190 ruleId: "no-unused-vars",
13191 severity: 2
13192 }]
13193 );
13194
13195 assert.deepStrictEqual(
13196 suppressedMessages,
13197 [{
13198 column: 25,
13199 endLine: 5,
13200 endColumn: 28,
13201 line: 5,
13202 message: "'aaa' is already defined.",
13203 messageId: "redeclared",
13204 nodeType: "Identifier",
13205 ruleId: "no-redeclare",
13206 severity: 2,
13207 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13208 }]
13209 );
13210 });
13211
13212 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
13213 const messages = linter.verify(`
13214 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
13215 var aaa = {}
13216 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
13217 var aaa = {}
13218 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13219 const suppressedMessages = linter.getSuppressedMessages();
13220
13221 // Do include `no-unused-vars` but not `no-redeclare`
13222 assert.deepStrictEqual(
13223 messages,
13224 [{
13225 column: 25,
13226 endLine: 5,
13227 endColumn: 28,
13228 line: 5,
13229 message: "'aaa' is assigned a value but never used.",
13230 messageId: "unusedVar",
13231 nodeType: "Identifier",
13232 ruleId: "no-unused-vars",
13233 severity: 2
13234 }]
13235 );
13236
13237 assert.deepStrictEqual(
13238 suppressedMessages,
13239 [{
13240 column: 25,
13241 endLine: 5,
13242 endColumn: 28,
13243 line: 5,
13244 message: "'aaa' is already defined.",
13245 messageId: "redeclared",
13246 nodeType: "Identifier",
13247 ruleId: "no-redeclare",
13248 severity: 2,
13249 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13250 }]
13251 );
13252 });
13253
13254 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
13255 const rule = sinon.stub().returns({});
13256 const config = {
13257 plugins: {
13258 test: {
13259 rules: {
13260 "a--rule": { create: rule }
13261 }
13262 }
13263 }
13264 };
13265
13266 const messages = linter.verify(`
13267 /*eslint test/a--rule:error */
13268 console.log("hello")
13269 `, config);
13270 const suppressedMessages = linter.getSuppressedMessages();
13271
13272 // Don't include syntax error of the comment.
13273 assert.deepStrictEqual(messages, []);
13274
13275 // Use `a--rule`.
13276 assert.strictEqual(rule.callCount, 1);
13277
13278 assert.strictEqual(suppressedMessages.length, 0);
13279 });
13280
13281 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
13282 const aaa = sinon.stub().returns({});
13283 const bbb = sinon.stub().returns({});
13284 const config = {
13285 plugins: {
13286 test: {
13287 rules: {
13288 aaa: { create: aaa },
13289 bbb: { create: bbb }
13290 }
13291 }
13292 }
13293 };
13294
13295 const messages = linter.verify(`
13296 /*eslint test/aaa:error -------- test/bbb:error */
13297 console.log("hello")
13298 `, config);
13299 const suppressedMessages = linter.getSuppressedMessages();
13300
13301 // Don't include syntax error of the comment.
13302 assert.deepStrictEqual(messages, []);
13303
13304 // Use only `aaa`.
13305 assert.strictEqual(aaa.callCount, 1);
13306 assert.strictEqual(bbb.callCount, 0);
13307
13308 assert.strictEqual(suppressedMessages.length, 0);
13309 });
13310
13311 it("should ignore the part preceded by '--' with line breaks.", () => {
13312 const aaa = sinon.stub().returns({});
13313 const bbb = sinon.stub().returns({});
13314 const config = {
13315 plugins: {
13316 test: {
13317 rules: {
13318 aaa: { create: aaa },
13319 bbb: { create: bbb }
13320 }
13321 }
13322 }
13323 };
13324
13325 const messages = linter.verify(`
13326 /*eslint test/aaa:error
13327 --------
13328 test/bbb:error */
13329 console.log("hello")
13330 `, config);
13331 const suppressedMessages = linter.getSuppressedMessages();
13332
13333 // Don't include syntax error of the comment.
13334 assert.deepStrictEqual(messages, []);
13335
13336 // Use only `aaa`.
13337 assert.strictEqual(aaa.callCount, 1);
13338 assert.strictEqual(bbb.callCount, 0);
13339
13340 assert.strictEqual(suppressedMessages.length, 0);
13341 });
13342 });
13343
13344 describe("allowInlineConfig option", () => {
13345 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
13346 it("should report a violation for disabling rules", () => {
13347 const code = [
13348 "alert('test'); // eslint-disable-line no-alert"
13349 ].join("\n");
13350 const config = {
13351 rules: {
13352 "no-alert": 1
13353 }
13354 };
13355
13356 const messages = linter.verify(code, config, {
13357 filename,
13358 allowInlineConfig: false
13359 });
13360 const suppressedMessages = linter.getSuppressedMessages();
13361
13362 assert.strictEqual(messages.length, 1);
13363 assert.strictEqual(messages[0].ruleId, "no-alert");
13364
13365 assert.strictEqual(suppressedMessages.length, 0);
13366 });
13367
13368 it("should report a violation for global variable declarations", () => {
13369 let ok = false;
13370 const code = [
13371 "/* global foo */"
13372 ].join("\n");
13373 const config = {
13374 plugins: {
13375 test: {
13376 rules: {
13377 test(context) {
13378 return {
13379 Program() {
13380 const scope = context.getScope();
13381 const sourceCode = context.getSourceCode();
13382 const comments = sourceCode.getAllComments();
13383
13384 assert.strictEqual(1, comments.length);
13385
13386 const foo = getVariable(scope, "foo");
13387
13388 assert.notOk(foo);
13389
13390 ok = true;
13391 }
13392 };
13393 }
13394 }
13395 }
13396 },
13397 rules: {
13398 "test/test": 2
13399 }
13400 };
13401
13402 linter.verify(code, config, { allowInlineConfig: false });
13403 assert(ok);
13404 });
13405
13406 it("should report a violation for eslint-disable", () => {
13407 const code = [
13408 "/* eslint-disable */",
13409 "alert('test');"
13410 ].join("\n");
13411 const config = {
13412 rules: {
13413 "no-alert": 1
13414 }
13415 };
13416
13417 const messages = linter.verify(code, config, {
13418 filename,
13419 allowInlineConfig: false
13420 });
13421 const suppressedMessages = linter.getSuppressedMessages();
13422
13423 assert.strictEqual(messages.length, 1);
13424 assert.strictEqual(messages[0].ruleId, "no-alert");
13425
13426 assert.strictEqual(suppressedMessages.length, 0);
13427 });
13428
13429 it("should not report a violation for rule changes", () => {
13430 const code = [
13431 "/*eslint no-alert:2*/",
13432 "alert('test');"
13433 ].join("\n");
13434 const config = {
13435 rules: {
13436 "no-alert": 0
13437 }
13438 };
13439
13440 const messages = linter.verify(code, config, {
13441 filename,
13442 allowInlineConfig: false
13443 });
13444 const suppressedMessages = linter.getSuppressedMessages();
13445
13446 assert.strictEqual(messages.length, 0);
13447 assert.strictEqual(suppressedMessages.length, 0);
13448 });
13449
13450 it("should report a violation for disable-line", () => {
13451 const code = [
13452 "alert('test'); // eslint-disable-line"
13453 ].join("\n");
13454 const config = {
13455 rules: {
13456 "no-alert": 2
13457 }
13458 };
13459
13460 const messages = linter.verify(code, config, {
13461 filename,
13462 allowInlineConfig: false
13463 });
13464 const suppressedMessages = linter.getSuppressedMessages();
13465
13466 assert.strictEqual(messages.length, 1);
13467 assert.strictEqual(messages[0].ruleId, "no-alert");
13468
13469 assert.strictEqual(suppressedMessages.length, 0);
13470 });
13471
13472 });
13473
13474 describe("when evaluating code with 'noInlineConfig'", () => {
13475 for (const directive of [
13476 "globals foo",
13477 "global foo",
13478 "exported foo",
13479 "eslint eqeqeq: error",
13480 "eslint-disable eqeqeq",
13481 "eslint-disable-line eqeqeq",
13482 "eslint-disable-next-line eqeqeq",
13483 "eslint-enable eqeqeq"
13484 ]) {
13485 // eslint-disable-next-line no-loop-func -- No closures
13486 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
13487 const messages = linter.verify(`/* ${directive} */`, {
13488 linterOptions: {
13489 noInlineConfig: true
13490 }
13491 });
13492 const suppressedMessages = linter.getSuppressedMessages();
13493
13494 assert.deepStrictEqual(messages.length, 1);
13495 assert.deepStrictEqual(messages[0].fatal, void 0);
13496 assert.deepStrictEqual(messages[0].ruleId, null);
13497 assert.deepStrictEqual(messages[0].severity, 1);
13498 assert.deepStrictEqual(messages[0].message, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
13499
13500 assert.strictEqual(suppressedMessages.length, 0);
13501 });
13502 }
13503
13504 for (const directive of [
13505 "eslint-disable-line eqeqeq",
13506 "eslint-disable-next-line eqeqeq"
13507 ]) {
13508 // eslint-disable-next-line no-loop-func -- No closures
13509 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
13510 const messages = linter.verify(`// ${directive}`, {
13511 linterOptions: {
13512 noInlineConfig: true
13513 }
13514 });
13515 const suppressedMessages = linter.getSuppressedMessages();
13516
13517 assert.deepStrictEqual(messages.length, 1);
13518 assert.deepStrictEqual(messages[0].fatal, void 0);
13519 assert.deepStrictEqual(messages[0].ruleId, null);
13520 assert.deepStrictEqual(messages[0].severity, 1);
13521 assert.deepStrictEqual(messages[0].message, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
13522
13523 assert.strictEqual(suppressedMessages.length, 0);
13524 });
13525 }
13526
13527 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
13528 const messages = linter.verify("/* globals foo */", {
13529 linterOptions: {
13530 noInlineConfig: true
13531 }
13532 }, { allowInlineConfig: false });
13533 const suppressedMessages = linter.getSuppressedMessages();
13534
13535 assert.deepStrictEqual(messages.length, 0);
13536 assert.strictEqual(suppressedMessages.length, 0);
13537 });
13538 });
13539
13540 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
13541 it("should not report a violation", () => {
13542 const code = [
13543 "alert('test'); // eslint-disable-line no-alert"
13544 ].join("\n");
13545 const config = {
13546 rules: {
13547 "no-alert": 1
13548 }
13549 };
13550
13551 const messages = linter.verify(code, config, {
13552 filename,
13553 allowInlineConfig: true
13554 });
13555 const suppressedMessages = linter.getSuppressedMessages();
13556
13557 assert.strictEqual(messages.length, 0);
13558
13559 assert.strictEqual(suppressedMessages.length, 1);
13560 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13561 });
13562 });
13563
13564 });
13565
13566 describe("reportUnusedDisableDirectives option", () => {
13567 it("reports problems for unused eslint-disable comments", () => {
13568 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: true });
13569 const suppressedMessages = linter.getSuppressedMessages();
13570
13571 assert.deepStrictEqual(
13572 messages,
13573 [
13574 {
13575 ruleId: null,
13576 message: "Unused eslint-disable directive (no problems were reported).",
13577 line: 1,
13578 column: 1,
13579 fix: {
13580 range: [0, 20],
13581 text: " "
13582 },
13583 severity: 2,
13584 nodeType: null
13585 }
13586 ]
13587 );
13588
13589 assert.strictEqual(suppressedMessages.length, 0);
13590 });
13591
13592 it("reports problems for unused eslint-disable comments (error)", () => {
13593 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "error" });
13594 const suppressedMessages = linter.getSuppressedMessages();
13595
13596 assert.deepStrictEqual(
13597 messages,
13598 [
13599 {
13600 ruleId: null,
13601 message: "Unused eslint-disable directive (no problems were reported).",
13602 line: 1,
13603 column: 1,
13604 fix: {
13605 range: [0, 20],
13606 text: " "
13607 },
13608 severity: 2,
13609 nodeType: null
13610 }
13611 ]
13612 );
13613
13614 assert.strictEqual(suppressedMessages.length, 0);
13615 });
13616
13617 it("reports problems for unused eslint-disable comments (warn)", () => {
13618 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "warn" });
13619 const suppressedMessages = linter.getSuppressedMessages();
13620
13621 assert.deepStrictEqual(
13622 messages,
13623 [
13624 {
13625 ruleId: null,
13626 message: "Unused eslint-disable directive (no problems were reported).",
13627 line: 1,
13628 column: 1,
13629 fix: {
13630 range: [0, 20],
13631 text: " "
13632 },
13633 severity: 1,
13634 nodeType: null
13635 }
13636 ]
13637 );
13638
13639 assert.strictEqual(suppressedMessages.length, 0);
13640 });
13641
13642 it("reports problems for unused eslint-disable comments (in config)", () => {
13643 const messages = linter.verify("/* eslint-disable */", {
13644 linterOptions: {
13645 reportUnusedDisableDirectives: true
13646 }
13647 });
13648 const suppressedMessages = linter.getSuppressedMessages();
13649
13650 assert.deepStrictEqual(
13651 messages,
13652 [
13653 {
13654 ruleId: null,
13655 message: "Unused eslint-disable directive (no problems were reported).",
13656 line: 1,
13657 column: 1,
13658 fix: {
13659 range: [0, 20],
13660 text: " "
13661 },
13662 severity: 1,
13663 nodeType: null
13664 }
13665 ]
13666 );
13667
13668 assert.strictEqual(suppressedMessages.length, 0);
13669 });
13670
13671 it("reports problems for partially unused eslint-disable comments (in config)", () => {
13672 const code = "alert('test'); // eslint-disable-line no-alert, no-redeclare";
13673 const config = {
13674 linterOptions: {
13675 reportUnusedDisableDirectives: true
13676 },
13677 rules: {
13678 "no-alert": 1,
13679 "no-redeclare": 1
13680 }
13681 };
13682
13683 const messages = linter.verify(code, config, {
13684 filename,
13685 allowInlineConfig: true
13686 });
13687 const suppressedMessages = linter.getSuppressedMessages();
13688
13689 assert.deepStrictEqual(
13690 messages,
13691 [
13692 {
13693 ruleId: null,
13694 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
13695 line: 1,
13696 column: 16,
13697 fix: {
13698 range: [46, 60],
13699 text: ""
13700 },
13701 severity: 1,
13702 nodeType: null
13703 }
13704 ]
13705 );
13706
13707 assert.strictEqual(suppressedMessages.length, 1);
13708 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13709 });
13710
13711 it("reports problems for unused eslint-disable-next-line comments (in config)", () => {
13712 assert.deepStrictEqual(
13713 linter.verify("// eslint-disable-next-line", {
13714 linterOptions: {
13715 reportUnusedDisableDirectives: true
13716 }
13717 }),
13718 [
13719 {
13720 ruleId: null,
13721 message: "Unused eslint-disable directive (no problems were reported).",
13722 line: 1,
13723 column: 1,
13724 fix: {
13725 range: [0, 27],
13726 text: " "
13727 },
13728 severity: 1,
13729 nodeType: null
13730 }
13731 ]
13732 );
13733 });
13734
13735 it("reports problems for unused multiline eslint-disable-next-line comments (in config)", () => {
13736 assert.deepStrictEqual(
13737 linter.verify("/* \neslint-disable-next-line\n */", {
13738 linterOptions: {
13739 reportUnusedDisableDirectives: true
13740 }
13741 }),
13742 [
13743 {
13744 ruleId: null,
13745 message: "Unused eslint-disable directive (no problems were reported).",
13746 line: 1,
13747 column: 1,
13748 fix: {
13749 range: [0, 32],
13750 text: " "
13751 },
13752 severity: 1,
13753 nodeType: null
13754 }
13755 ]
13756 );
13757 });
13758
13759 it("reports problems for partially unused eslint-disable-next-line comments (in config)", () => {
13760 const code = "// eslint-disable-next-line no-alert, no-redeclare \nalert('test');";
13761 const config = {
13762 linterOptions: {
13763 reportUnusedDisableDirectives: true
13764 },
13765 rules: {
13766 "no-alert": 1,
13767 "no-redeclare": 1
13768 }
13769 };
13770
13771 const messages = linter.verify(code, config, {
13772 filename,
13773 allowInlineConfig: true
13774 });
13775
13776 assert.deepStrictEqual(
13777 messages,
13778 [
13779 {
13780 ruleId: null,
13781 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
13782 line: 1,
13783 column: 1,
13784 fix: {
13785 range: [36, 50],
13786 text: ""
13787 },
13788 severity: 1,
13789 nodeType: null
13790 }
13791 ]
13792 );
13793 });
13794
13795 it("reports problems for partially unused multiline eslint-disable-next-line comments (in config)", () => {
13796 const code = `
13797 /* eslint-disable-next-line no-alert, no-redeclare --
13798 * Here's a very long description about why this configuration is necessary
13799 * along with some additional information
13800 **/
13801 alert('test');
13802 `;
13803 const config = {
13804 linterOptions: {
13805 reportUnusedDisableDirectives: true
13806 },
13807 rules: {
13808 "no-alert": 1,
13809 "no-redeclare": 1
13810 }
13811 };
13812
13813 const messages = linter.verify(code, config, {
13814 filename,
13815 allowInlineConfig: true
13816 });
13817
13818 assert.deepStrictEqual(
13819 messages,
13820 [
13821 {
13822 ruleId: null,
13823 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
13824 line: 2,
13825 column: 21,
13826 fix: {
13827 range: [57, 71],
13828 text: ""
13829 },
13830 severity: 1,
13831 nodeType: null
13832 }
13833 ]
13834 );
13835 });
13836
13837 describe("autofix", () => {
13838 const alwaysReportsRule = {
13839 create(context) {
13840 return {
13841 Program(node) {
13842 context.report({ message: "bad code", loc: node.loc.end });
13843 }
13844 };
13845 }
13846 };
13847
13848 const neverReportsRule = {
13849 create() {
13850 return {};
13851 }
13852 };
13853
13854 const ruleCount = 3;
13855 const usedRules = Array.from(
13856 { length: ruleCount },
13857 (_, index) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
13858 );
13859 const unusedRules = usedRules.map(name => `un${name}`); // "unused", "unused-1", "unused-2"
13860
13861 const config = {
13862 plugins: {
13863 test: {
13864 rules: {}
13865 }
13866 },
13867 linterOptions: {
13868 reportUnusedDisableDirectives: true
13869 },
13870 rules: {
13871 ...Object.fromEntries(usedRules.map(name => [`test/${name}`, "error"])),
13872 ...Object.fromEntries(unusedRules.map(name => [`test/${name}`, "error"]))
13873 }
13874 };
13875
13876 beforeEach(() => {
13877 config.plugins.test.rules = {
13878 ...Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])),
13879 ...Object.fromEntries(unusedRules.map(name => [name, neverReportsRule]))
13880 };
13881 });
13882
13883 const tests = [
13884
13885 //-----------------------------------------------
13886 // Removing the entire comment
13887 //-----------------------------------------------
13888
13889 {
13890 code: "// eslint-disable-line test/unused",
13891 output: " "
13892 },
13893 {
13894 code: "foo// eslint-disable-line test/unused",
13895 output: "foo "
13896 },
13897 {
13898 code: "// eslint-disable-line ,test/unused,",
13899 output: " "
13900 },
13901 {
13902 code: "// eslint-disable-line test/unused-1, test/unused-2",
13903 output: " "
13904 },
13905 {
13906 code: "// eslint-disable-line ,test/unused-1,, test/unused-2,, -- comment",
13907 output: " "
13908 },
13909 {
13910 code: "// eslint-disable-next-line test/unused\n",
13911 output: " \n"
13912 },
13913 {
13914 code: "// eslint-disable-next-line test/unused\nfoo",
13915 output: " \nfoo"
13916 },
13917 {
13918 code: "/* eslint-disable \ntest/unused\n*/",
13919 output: " "
13920 },
13921
13922 //-----------------------------------------------
13923 // Removing only individual rules
13924 //-----------------------------------------------
13925
13926 // content before the first rule should not be changed
13927 {
13928 code: "//eslint-disable-line test/unused, test/used",
13929 output: "//eslint-disable-line test/used"
13930 },
13931 {
13932 code: "// eslint-disable-line test/unused, test/used",
13933 output: "// eslint-disable-line test/used"
13934 },
13935 {
13936 code: "// eslint-disable-line test/unused, test/used",
13937 output: "// eslint-disable-line test/used"
13938 },
13939 {
13940 code: "/*\neslint-disable test/unused, test/used*/",
13941 output: "/*\neslint-disable test/used*/"
13942 },
13943 {
13944 code: "/*\n eslint-disable test/unused, test/used*/",
13945 output: "/*\n eslint-disable test/used*/"
13946 },
13947 {
13948 code: "/*\r\neslint-disable test/unused, test/used*/",
13949 output: "/*\r\neslint-disable test/used*/"
13950 },
13951 {
13952 code: "/*\u2028eslint-disable test/unused, test/used*/",
13953 output: "/*\u2028eslint-disable test/used*/"
13954 },
13955 {
13956 code: "/*\u00A0eslint-disable test/unused, test/used*/",
13957 output: "/*\u00A0eslint-disable test/used*/"
13958 },
13959 {
13960 code: "// eslint-disable-line test/unused, test/used",
13961 output: "// eslint-disable-line test/used"
13962 },
13963 {
13964 code: "/* eslint-disable\ntest/unused, test/used*/",
13965 output: "/* eslint-disable\ntest/used*/"
13966 },
13967 {
13968 code: "/* eslint-disable\n test/unused, test/used*/",
13969 output: "/* eslint-disable\n test/used*/"
13970 },
13971 {
13972 code: "/* eslint-disable\r\ntest/unused, test/used*/",
13973 output: "/* eslint-disable\r\ntest/used*/"
13974 },
13975 {
13976 code: "/* eslint-disable\u2028test/unused, test/used*/",
13977 output: "/* eslint-disable\u2028test/used*/"
13978 },
13979 {
13980 code: "/* eslint-disable\u00A0test/unused, test/used*/",
13981 output: "/* eslint-disable\u00A0test/used*/"
13982 },
13983
13984 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
13985 {
13986 code: "// eslint-disable-line test/unused,test/used",
13987 output: "// eslint-disable-line test/used"
13988 },
13989 {
13990 code: "// eslint-disable-line test/unused, test/used",
13991 output: "// eslint-disable-line test/used"
13992 },
13993 {
13994 code: "// eslint-disable-line test/unused , test/used",
13995 output: "// eslint-disable-line test/used"
13996 },
13997 {
13998 code: "// eslint-disable-line test/unused, test/used",
13999 output: "// eslint-disable-line test/used"
14000 },
14001 {
14002 code: "// eslint-disable-line test/unused ,test/used",
14003 output: "// eslint-disable-line test/used"
14004 },
14005 {
14006 code: "/* eslint-disable test/unused\n,\ntest/used */",
14007 output: "/* eslint-disable test/used */"
14008 },
14009 {
14010 code: "/* eslint-disable test/unused \n \n,\n\n test/used */",
14011 output: "/* eslint-disable test/used */"
14012 },
14013 {
14014 code: "/* eslint-disable test/unused\u2028,\u2028test/used */",
14015 output: "/* eslint-disable test/used */"
14016 },
14017 {
14018 code: "// eslint-disable-line test/unused\u00A0,\u00A0test/used",
14019 output: "// eslint-disable-line test/used"
14020 },
14021 {
14022 code: "// eslint-disable-line test/unused,,test/used",
14023 output: "// eslint-disable-line ,test/used"
14024 },
14025 {
14026 code: "// eslint-disable-line test/unused, ,test/used",
14027 output: "// eslint-disable-line ,test/used"
14028 },
14029 {
14030 code: "// eslint-disable-line test/unused,, test/used",
14031 output: "// eslint-disable-line , test/used"
14032 },
14033 {
14034 code: "// eslint-disable-line test/unused,test/used ",
14035 output: "// eslint-disable-line test/used "
14036 },
14037 {
14038 code: "// eslint-disable-next-line test/unused,test/used\n",
14039 output: "// eslint-disable-next-line test/used\n"
14040 },
14041
14042 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
14043 {
14044 code: "// eslint-disable-line test/used-1,test/unused,test/used-2",
14045 output: "// eslint-disable-line test/used-1,test/used-2"
14046 },
14047 {
14048 code: "// eslint-disable-line test/used-1, test/unused,test/used-2",
14049 output: "// eslint-disable-line test/used-1,test/used-2"
14050 },
14051 {
14052 code: "// eslint-disable-line test/used-1,test/unused ,test/used-2",
14053 output: "// eslint-disable-line test/used-1,test/used-2"
14054 },
14055 {
14056 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14057 output: "// eslint-disable-line test/used-1,test/used-2"
14058 },
14059 {
14060 code: "/* eslint-disable test/used-1,\ntest/unused\n,test/used-2 */",
14061 output: "/* eslint-disable test/used-1,test/used-2 */"
14062 },
14063 {
14064 code: "/* eslint-disable test/used-1,\n\n test/unused \n \n ,test/used-2 */",
14065 output: "/* eslint-disable test/used-1,test/used-2 */"
14066 },
14067 {
14068 code: "/* eslint-disable test/used-1,\u2028test/unused\u2028,test/used-2 */",
14069 output: "/* eslint-disable test/used-1,test/used-2 */"
14070 },
14071 {
14072 code: "// eslint-disable-line test/used-1,\u00A0test/unused\u00A0,test/used-2",
14073 output: "// eslint-disable-line test/used-1,test/used-2"
14074 },
14075
14076 // when removing a rule in the middle, content around commas should not be changed
14077 {
14078 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14079 output: "// eslint-disable-line test/used-1,test/used-2"
14080 },
14081 {
14082 code: "// eslint-disable-line test/used-1,test/unused, test/used-2",
14083 output: "// eslint-disable-line test/used-1, test/used-2"
14084 },
14085 {
14086 code: "// eslint-disable-line test/used-1 ,test/unused,test/used-2",
14087 output: "// eslint-disable-line test/used-1 ,test/used-2"
14088 },
14089 {
14090 code: "// eslint-disable-line test/used-1 ,test/unused, test/used-2",
14091 output: "// eslint-disable-line test/used-1 , test/used-2"
14092 },
14093 {
14094 code: "// eslint-disable-line test/used-1 , test/unused , test/used-2",
14095 output: "// eslint-disable-line test/used-1 , test/used-2"
14096 },
14097 {
14098 code: "/* eslint-disable test/used-1\n,test/unused,\ntest/used-2 */",
14099 output: "/* eslint-disable test/used-1\n,\ntest/used-2 */"
14100 },
14101 {
14102 code: "/* eslint-disable test/used-1\u2028,test/unused,\u2028test/used-2 */",
14103 output: "/* eslint-disable test/used-1\u2028,\u2028test/used-2 */"
14104 },
14105 {
14106 code: "// eslint-disable-line test/used-1\u00A0,test/unused,\u00A0test/used-2",
14107 output: "// eslint-disable-line test/used-1\u00A0,\u00A0test/used-2"
14108 },
14109 {
14110 code: "// eslint-disable-line , test/unused ,test/used",
14111 output: "// eslint-disable-line ,test/used"
14112 },
14113 {
14114 code: "/* eslint-disable\n, test/unused ,test/used */",
14115 output: "/* eslint-disable\n,test/used */"
14116 },
14117 {
14118 code: "/* eslint-disable test/used-1,\n,test/unused,test/used-2 */",
14119 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
14120 },
14121 {
14122 code: "/* eslint-disable test/used-1,test/unused,\n,test/used-2 */",
14123 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
14124 },
14125 {
14126 code: "/* eslint-disable test/used-1,\n,test/unused,\n,test/used-2 */",
14127 output: "/* eslint-disable test/used-1,\n,\n,test/used-2 */"
14128 },
14129 {
14130 code: "// eslint-disable-line test/used, test/unused,",
14131 output: "// eslint-disable-line test/used,"
14132 },
14133 {
14134 code: "// eslint-disable-next-line test/used, test/unused,\n",
14135 output: "// eslint-disable-next-line test/used,\n"
14136 },
14137 {
14138 code: "// eslint-disable-line test/used, test/unused, ",
14139 output: "// eslint-disable-line test/used, "
14140 },
14141 {
14142 code: "// eslint-disable-line test/used, test/unused, -- comment",
14143 output: "// eslint-disable-line test/used, -- comment"
14144 },
14145 {
14146 code: "/* eslint-disable test/used, test/unused,\n*/",
14147 output: "/* eslint-disable test/used,\n*/"
14148 },
14149
14150 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
14151 {
14152 code: "// eslint-disable-line test/used,test/unused",
14153 output: "// eslint-disable-line test/used"
14154 },
14155 {
14156 code: "// eslint-disable-line test/used, test/unused",
14157 output: "// eslint-disable-line test/used"
14158 },
14159 {
14160 code: "// eslint-disable-line test/used ,test/unused",
14161 output: "// eslint-disable-line test/used"
14162 },
14163 {
14164 code: "// eslint-disable-line test/used , test/unused",
14165 output: "// eslint-disable-line test/used"
14166 },
14167 {
14168 code: "// eslint-disable-line test/used, test/unused",
14169 output: "// eslint-disable-line test/used"
14170 },
14171 {
14172 code: "// eslint-disable-line test/used ,test/unused",
14173 output: "// eslint-disable-line test/used"
14174 },
14175 {
14176 code: "/* eslint-disable test/used\n,\ntest/unused */",
14177 output: "/* eslint-disable test/used */"
14178 },
14179 {
14180 code: "/* eslint-disable test/used \n \n,\n\n test/unused */",
14181 output: "/* eslint-disable test/used */"
14182 },
14183 {
14184 code: "/* eslint-disable test/used\u2028,\u2028test/unused */",
14185 output: "/* eslint-disable test/used */"
14186 },
14187 {
14188 code: "// eslint-disable-line test/used\u00A0,\u00A0test/unused",
14189 output: "// eslint-disable-line test/used"
14190 },
14191 {
14192 code: "// eslint-disable-line test/used,,test/unused",
14193 output: "// eslint-disable-line test/used,"
14194 },
14195 {
14196 code: "// eslint-disable-line test/used, ,test/unused",
14197 output: "// eslint-disable-line test/used,"
14198 },
14199 {
14200 code: "/* eslint-disable test/used,\n,test/unused */",
14201 output: "/* eslint-disable test/used, */"
14202 },
14203 {
14204 code: "/* eslint-disable test/used\n, ,test/unused */",
14205 output: "/* eslint-disable test/used\n, */"
14206 },
14207
14208 // content after the last rule should not be changed
14209 {
14210 code: "// eslint-disable-line test/used,test/unused",
14211 output: "// eslint-disable-line test/used"
14212 },
14213 {
14214 code: "// eslint-disable-line test/used,test/unused ",
14215 output: "// eslint-disable-line test/used "
14216 },
14217 {
14218 code: "// eslint-disable-line test/used,test/unused ",
14219 output: "// eslint-disable-line test/used "
14220 },
14221 {
14222 code: "// eslint-disable-line test/used,test/unused -- comment",
14223 output: "// eslint-disable-line test/used -- comment"
14224 },
14225 {
14226 code: "// eslint-disable-next-line test/used,test/unused\n",
14227 output: "// eslint-disable-next-line test/used\n"
14228 },
14229 {
14230 code: "// eslint-disable-next-line test/used,test/unused \n",
14231 output: "// eslint-disable-next-line test/used \n"
14232 },
14233 {
14234 code: "/* eslint-disable test/used,test/unused\u2028*/",
14235 output: "/* eslint-disable test/used\u2028*/"
14236 },
14237 {
14238 code: "// eslint-disable-line test/used,test/unused\u00A0",
14239 output: "// eslint-disable-line test/used\u00A0"
14240 },
14241
14242 // multiply rules to remove
14243 {
14244 code: "// eslint-disable-line test/used, test/unused-1, test/unused-2",
14245 output: "// eslint-disable-line test/used"
14246 },
14247 {
14248 code: "// eslint-disable-line test/unused-1, test/used, test/unused-2",
14249 output: "// eslint-disable-line test/used"
14250 },
14251 {
14252 code: "// eslint-disable-line test/unused-1, test/unused-2, test/used",
14253 output: "// eslint-disable-line test/used"
14254 },
14255 {
14256 code: "// eslint-disable-line test/used-1, test/unused-1, test/used-2, test/unused-2",
14257 output: "// eslint-disable-line test/used-1, test/used-2"
14258 },
14259 {
14260 code: "// eslint-disable-line test/unused-1, test/used-1, test/unused-2, test/used-2",
14261 output: "// eslint-disable-line test/used-1, test/used-2"
14262 },
14263 {
14264 code: `
14265 /* eslint-disable test/unused-1,
14266 test/used-1,
14267 test/unused-2,
14268 test/used-2
14269 */
14270 `,
14271 output: `
14272 /* eslint-disable test/used-1,
14273 test/used-2
14274 */
14275 `
14276 },
14277 {
14278 code: `
14279 /* eslint-disable
14280 test/unused-1,
14281 test/used-1,
14282 test/unused-2,
14283 test/used-2
14284 */
14285 `,
14286 output: `
14287 /* eslint-disable
14288 test/used-1,
14289 test/used-2
14290 */
14291 `
14292 },
14293 {
14294 code: `
14295 /* eslint-disable
14296 test/used-1,
14297 test/unused-1,
14298 test/used-2,
14299 test/unused-2
14300 */
14301 `,
14302 output: `
14303 /* eslint-disable
14304 test/used-1,
14305 test/used-2
14306 */
14307 `
14308 },
14309 {
14310 code: `
14311 /* eslint-disable
14312 test/used-1,
14313 test/unused-1,
14314 test/used-2,
14315 test/unused-2,
14316 */
14317 `,
14318 output: `
14319 /* eslint-disable
14320 test/used-1,
14321 test/used-2,
14322 */
14323 `
14324 },
14325 {
14326 code: `
14327 /* eslint-disable
14328 ,test/unused-1
14329 ,test/used-1
14330 ,test/unused-2
14331 ,test/used-2
14332 */
14333 `,
14334 output: `
14335 /* eslint-disable
14336 ,test/used-1
14337 ,test/used-2
14338 */
14339 `
14340 },
14341 {
14342 code: `
14343 /* eslint-disable
14344 ,test/used-1
14345 ,test/unused-1
14346 ,test/used-2
14347 ,test/unused-2
14348 */
14349 `,
14350 output: `
14351 /* eslint-disable
14352 ,test/used-1
14353 ,test/used-2
14354 */
14355 `
14356 },
14357 {
14358 code: `
14359 /* eslint-disable
14360 test/used-1,
14361 test/unused-1,
14362 test/used-2,
14363 test/unused-2
14364
14365 -- comment
14366 */
14367 `,
14368 output: `
14369 /* eslint-disable
14370 test/used-1,
14371 test/used-2
14372
14373 -- comment
14374 */
14375 `
14376 },
14377
14378 // duplicates in the list
14379 {
14380 code: "// eslint-disable-line test/unused, test/unused, test/used",
14381 output: "// eslint-disable-line test/used"
14382 },
14383 {
14384 code: "// eslint-disable-line test/unused, test/used, test/unused",
14385 output: "// eslint-disable-line test/used"
14386 },
14387 {
14388 code: "// eslint-disable-line test/used, test/unused, test/unused, test/used",
14389 output: "// eslint-disable-line test/used, test/used"
14390 }
14391 ];
14392
14393 for (const { code, output } of tests) {
14394 // eslint-disable-next-line no-loop-func -- `linter` is getting updated in beforeEach()
14395 it(code, () => {
14396 assert.strictEqual(
14397 linter.verifyAndFix(code, config).output,
14398 output
14399 );
14400 });
14401 }
14402 });
14403 });
14404
14405 });
14406
14407 describe("Default Global Variables", () => {
14408 const code = "x";
14409
14410 it("builtin global variables should be available in the global scope", () => {
14411 let spy;
14412 const config = {
14413 plugins: {
14414 test: {
14415 rules: {
14416 checker: context => {
14417 spy = sinon.spy(() => {
14418 const scope = context.getScope();
14419
14420 assert.notStrictEqual(getVariable(scope, "Object"), null);
14421 assert.notStrictEqual(getVariable(scope, "Array"), null);
14422 assert.notStrictEqual(getVariable(scope, "undefined"), null);
14423 });
14424
14425 return { Program: spy };
14426 }
14427 }
14428 }
14429 },
14430 languageOptions: {
14431 ecmaVersion: 5,
14432 sourceType: "script"
14433 },
14434 rules: {
14435 "test/checker": "error"
14436 }
14437 };
14438
14439 linter.verify(code, config);
14440 assert(spy && spy.calledOnce, "Rule should have been called.");
14441 });
14442
14443 it("ES6 global variables should be available by default", () => {
14444 let spy;
14445 const config = {
14446 plugins: {
14447 test: {
14448 rules: {
14449 checker: context => {
14450 spy = sinon.spy(() => {
14451 const scope = context.getScope();
14452
14453 assert.notStrictEqual(getVariable(scope, "Promise"), null);
14454 assert.notStrictEqual(getVariable(scope, "Symbol"), null);
14455 assert.notStrictEqual(getVariable(scope, "WeakMap"), null);
14456 });
14457
14458 return { Program: spy };
14459 }
14460 }
14461 }
14462 },
14463 languageOptions: {
14464 sourceType: "script"
14465 },
14466 rules: {
14467 "test/checker": "error"
14468 }
14469 };
14470
14471 linter.verify(code, config);
14472 assert(spy && spy.calledOnce);
14473 });
14474
14475 });
14476
14477 describe("Suggestions", () => {
14478 it("provides suggestion information for tools to use", () => {
14479
14480 const config = {
14481 plugins: {
14482 test: {
14483 rules: {
14484 "rule-with-suggestions": {
14485 meta: { hasSuggestions: true },
14486 create: context => ({
14487 Program(node) {
14488 context.report({
14489 node,
14490 message: "Incorrect spacing",
14491 suggest: [{
14492 desc: "Insert space at the beginning",
14493 fix: fixer => fixer.insertTextBefore(node, " ")
14494 }, {
14495 desc: "Insert space at the end",
14496 fix: fixer => fixer.insertTextAfter(node, " ")
14497 }]
14498 });
14499 }
14500 })
14501 }
14502 }
14503 }
14504 },
14505 rules: {
14506 "test/rule-with-suggestions": "error"
14507 }
14508 };
14509
14510 const messages = linter.verify("var a = 1;", config);
14511 const suppressedMessages = linter.getSuppressedMessages();
14512
14513 assert.deepStrictEqual(messages[0].suggestions, [{
14514 desc: "Insert space at the beginning",
14515 fix: {
14516 range: [0, 0],
14517 text: " "
14518 }
14519 }, {
14520 desc: "Insert space at the end",
14521 fix: {
14522 range: [10, 10],
14523 text: " "
14524 }
14525 }]);
14526
14527 assert.strictEqual(suppressedMessages.length, 0);
14528 });
14529
14530 it("supports messageIds for suggestions", () => {
14531
14532 const config = {
14533 plugins: {
14534 test: {
14535 rules: {
14536 "rule-with-suggestions": {
14537 meta: {
14538 messages: {
14539 suggestion1: "Insert space at the beginning",
14540 suggestion2: "Insert space at the end"
14541 },
14542 hasSuggestions: true
14543 },
14544 create: context => ({
14545 Program(node) {
14546 context.report({
14547 node,
14548 message: "Incorrect spacing",
14549 suggest: [{
14550 messageId: "suggestion1",
14551 fix: fixer => fixer.insertTextBefore(node, " ")
14552 }, {
14553 messageId: "suggestion2",
14554 fix: fixer => fixer.insertTextAfter(node, " ")
14555 }]
14556 });
14557 }
14558 })
14559 }
14560 }
14561 }
14562 },
14563 rules: {
14564 "test/rule-with-suggestions": "error"
14565 }
14566 };
14567
14568 const messages = linter.verify("var a = 1;", config);
14569 const suppressedMessages = linter.getSuppressedMessages();
14570
14571 assert.deepStrictEqual(messages[0].suggestions, [{
14572 messageId: "suggestion1",
14573 desc: "Insert space at the beginning",
14574 fix: {
14575 range: [0, 0],
14576 text: " "
14577 }
14578 }, {
14579 messageId: "suggestion2",
14580 desc: "Insert space at the end",
14581 fix: {
14582 range: [10, 10],
14583 text: " "
14584 }
14585 }]);
14586
14587 assert.strictEqual(suppressedMessages.length, 0);
14588 });
14589
14590 it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled", () => {
14591
14592 const config = {
14593 plugins: {
14594 test: {
14595 rules: {
14596 "rule-with-suggestions": {
14597 meta: { docs: {}, schema: [] },
14598 create: context => ({
14599 Program(node) {
14600 context.report({
14601 node,
14602 message: "hello world",
14603 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
14604 });
14605 }
14606 })
14607 }
14608 }
14609 }
14610 },
14611 rules: {
14612 "test/rule-with-suggestions": "error"
14613 }
14614 };
14615
14616 assert.throws(() => {
14617 linter.verify("0", config);
14618 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
14619 });
14620
14621 it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled and the rule has the obsolete `meta.docs.suggestion` property", () => {
14622
14623 const config = {
14624 plugins: {
14625 test: {
14626 rules: {
14627 "rule-with-meta-docs-suggestion": {
14628 meta: { docs: { suggestion: true }, schema: [] },
14629 create: context => ({
14630 Program(node) {
14631 context.report({
14632 node,
14633 message: "hello world",
14634 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
14635 });
14636 }
14637 })
14638 }
14639 }
14640 }
14641 },
14642 rules: {
14643 "test/rule-with-meta-docs-suggestion": "error"
14644 }
14645 };
14646
14647 assert.throws(() => {
14648 linter.verify("0", config);
14649 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint.");
14650 });
14651 });
14652
14653
14654 describe("Error Conditions", () => {
14655 describe("when evaluating broken code", () => {
14656 const code = BROKEN_TEST_CODE;
14657
14658 it("should report a violation with a useful parse error prefix", () => {
14659 const messages = linter.verify(code);
14660 const suppressedMessages = linter.getSuppressedMessages();
14661
14662 assert.strictEqual(messages.length, 1);
14663 assert.strictEqual(messages[0].severity, 2);
14664 assert.isNull(messages[0].ruleId);
14665 assert.strictEqual(messages[0].line, 1);
14666 assert.strictEqual(messages[0].column, 4);
14667 assert.isTrue(messages[0].fatal);
14668 assert.match(messages[0].message, /^Parsing error:/u);
14669
14670 assert.strictEqual(suppressedMessages.length, 0);
14671 });
14672
14673 it("should report source code where the issue is present", () => {
14674 const inValidCode = [
14675 "var x = 20;",
14676 "if (x ==4 {",
14677 " x++;",
14678 "}"
14679 ];
14680 const messages = linter.verify(inValidCode.join("\n"));
14681 const suppressedMessages = linter.getSuppressedMessages();
14682
14683 assert.strictEqual(messages.length, 1);
14684 assert.strictEqual(messages[0].severity, 2);
14685 assert.isTrue(messages[0].fatal);
14686 assert.match(messages[0].message, /^Parsing error:/u);
14687
14688 assert.strictEqual(suppressedMessages.length, 0);
14689 });
14690 });
14691
14692 describe("when using a rule which has been replaced", () => {
14693 const code = TEST_CODE;
14694
14695 it("should report the new rule", () => {
14696
14697 assert.throws(() => {
14698 linter.verify(code, { rules: { "no-comma-dangle": 2 } });
14699 }, /Key "rules": Key "no-comma-dangle": Rule "no-comma-dangle" was removed and replaced by "comma-dangle"/u);
14700
14701 });
14702 });
14703
14704 });
14705 });
14706
14707 describe("getSourceCode()", () => {
14708 const code = TEST_CODE;
14709
14710 it("should retrieve SourceCode object after reset", () => {
14711 linter.verify(code, {}, filename, true);
14712
14713 const sourceCode = linter.getSourceCode();
14714
14715 assert.isObject(sourceCode);
14716 assert.strictEqual(sourceCode.text, code);
14717 assert.isObject(sourceCode.ast);
14718 });
14719
14720 it("should retrieve SourceCode object without reset", () => {
14721 linter.verify(code, {}, filename);
14722
14723 const sourceCode = linter.getSourceCode();
14724
14725 assert.isObject(sourceCode);
14726 assert.strictEqual(sourceCode.text, code);
14727 assert.isObject(sourceCode.ast);
14728 });
14729
14730 });
14731
14732 describe("getSuppressedMessages()", () => {
14733 it("should have no suppressed messages", () => {
14734 const suppressedMessages = linter.getSuppressedMessages();
14735
14736 assert.strictEqual(suppressedMessages.length, 0);
14737 });
14738
14739 it("should have a suppressed message", () => {
14740 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test\");";
14741 const config = {
14742 rules: { "no-alert": 1 }
14743 };
14744 const messages = linter.verify(code, config);
14745 const suppressedMessages = linter.getSuppressedMessages();
14746
14747 assert.strictEqual(messages.length, 0);
14748
14749 assert.strictEqual(suppressedMessages.length, 1);
14750 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
14751 assert.deepStrictEqual(
14752 suppressedMessages[0].suppressions,
14753 [{ kind: "directive", justification: "justification" }]
14754 );
14755 });
14756
14757 it("should have a suppressed message", () => {
14758 const code = [
14759 "/* eslint-disable no-alert -- j1",
14760 " * j2",
14761 " */",
14762 "alert(\"test\");"
14763 ].join("\n");
14764 const config = {
14765 rules: { "no-alert": 1 }
14766 };
14767 const messages = linter.verify(code, config);
14768 const suppressedMessages = linter.getSuppressedMessages();
14769
14770 assert.strictEqual(messages.length, 0);
14771
14772 assert.strictEqual(suppressedMessages.length, 1);
14773 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
14774 assert.deepStrictEqual(
14775 suppressedMessages[0].suppressions,
14776 [{ kind: "directive", justification: "j1\n * j2" }]
14777 );
14778 });
14779 });
14780
14781 describe("defineRule()", () => {
14782 it("should throw an error when called in flat config mode", () => {
14783 assert.throws(() => {
14784 linter.defineRule("foo", () => {});
14785 }, /This method cannot be used with flat config/u);
14786 });
14787 });
14788
14789 describe("defineRules()", () => {
14790 it("should throw an error when called in flat config mode", () => {
14791 assert.throws(() => {
14792 linter.defineRules({});
14793 }, /This method cannot be used with flat config/u);
14794 });
14795 });
14796
14797 describe("defineParser()", () => {
14798 it("should throw an error when called in flat config mode", () => {
14799 assert.throws(() => {
14800 linter.defineParser("foo", {});
14801 }, /This method cannot be used with flat config/u);
14802 });
14803 });
14804
14805 describe("getRules()", () => {
14806 it("should throw an error when called in flat config mode", () => {
14807 assert.throws(() => {
14808 linter.getRules();
14809 }, /This method cannot be used with flat config/u);
14810 });
14811 });
14812
14813 describe("version", () => {
14814 it("should return current version number", () => {
14815 const version = linter.version;
14816
14817 assert.isString(version);
14818 assert.isTrue(parseInt(version[0], 10) >= 3);
14819 });
14820 });
14821
14822 describe("verifyAndFix()", () => {
14823 it("Fixes the code", () => {
14824 const messages = linter.verifyAndFix("var a", {
14825 rules: {
14826 semi: 2
14827 }
14828 }, { filename: "test.js" });
14829 const suppressedMessages = linter.getSuppressedMessages();
14830
14831 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
14832 assert.isTrue(messages.fixed);
14833
14834 assert.strictEqual(suppressedMessages.length, 0);
14835 });
14836
14837 it("does not require a third argument", () => {
14838 const fixResult = linter.verifyAndFix("var a", {
14839 rules: {
14840 semi: 2
14841 }
14842 });
14843 const suppressedMessages = linter.getSuppressedMessages();
14844
14845 assert.deepStrictEqual(fixResult, {
14846 fixed: true,
14847 messages: [],
14848 output: "var a;"
14849 });
14850
14851 assert.strictEqual(suppressedMessages.length, 0);
14852 });
14853
14854 it("does not include suggestions in autofix results", () => {
14855 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
14856 rules: {
14857 semi: 2,
14858 "no-useless-escape": 2
14859 }
14860 });
14861 const suppressedMessages = linter.getSuppressedMessages();
14862
14863 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
14864 assert.strictEqual(fixResult.fixed, true);
14865 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
14866
14867 assert.strictEqual(suppressedMessages.length, 0);
14868 });
14869
14870 it("does not apply autofixes when fix argument is `false`", () => {
14871 const fixResult = linter.verifyAndFix("var a", {
14872 rules: {
14873 semi: 2
14874 }
14875 }, { fix: false });
14876 const suppressedMessages = linter.getSuppressedMessages();
14877
14878 assert.strictEqual(fixResult.fixed, false);
14879 assert.strictEqual(suppressedMessages.length, 0);
14880 });
14881
14882 it("stops fixing after 10 passes", () => {
14883
14884 const config = {
14885 plugins: {
14886 test: {
14887 rules: {
14888 "add-spaces": {
14889 meta: {
14890 fixable: "whitespace"
14891 },
14892 create(context) {
14893 return {
14894 Program(node) {
14895 context.report({
14896 node,
14897 message: "Add a space before this node.",
14898 fix: fixer => fixer.insertTextBefore(node, " ")
14899 });
14900 }
14901 };
14902 }
14903 }
14904 }
14905 }
14906 },
14907 rules: {
14908 "test/add-spaces": "error"
14909 }
14910 };
14911
14912 const fixResult = linter.verifyAndFix("a", config);
14913 const suppressedMessages = linter.getSuppressedMessages();
14914
14915 assert.strictEqual(fixResult.fixed, true);
14916 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a`);
14917 assert.strictEqual(fixResult.messages.length, 1);
14918
14919 assert.strictEqual(suppressedMessages.length, 0);
14920 });
14921
14922 it("should throw an error if fix is passed but meta has no `fixable` property", () => {
14923
14924 const config = {
14925 plugins: {
14926 test: {
14927 rules: {
14928 "test-rule": {
14929 meta: {
14930 docs: {},
14931 schema: []
14932 },
14933 create: context => ({
14934 Program(node) {
14935 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
14936 }
14937 })
14938 }
14939 }
14940 }
14941 },
14942 rules: {
14943 "test/test-rule": "error"
14944 }
14945 };
14946
14947
14948 assert.throws(() => {
14949 linter.verify("0", config);
14950 }, /Fixable rules must set the `meta\.fixable` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test\/test-rule"$/u);
14951 });
14952
14953 it("should throw an error if fix is passed and there is no metadata", () => {
14954
14955 const config = {
14956 plugins: {
14957 test: {
14958 rules: {
14959 "test-rule": {
14960 create: context => ({
14961 Program(node) {
14962 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
14963 }
14964 })
14965 }
14966 }
14967 }
14968 },
14969 rules: {
14970 "test/test-rule": "error"
14971 }
14972 };
14973
14974 assert.throws(() => {
14975 linter.verify("0", config);
14976 }, /Fixable rules must set the `meta\.fixable` property/u);
14977 });
14978
14979 it("should throw an error if fix is passed from a legacy-format rule", () => {
14980
14981 const config = {
14982 plugins: {
14983 test: {
14984 rules: {
14985 "test-rule": context => ({
14986 Program(node) {
14987 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
14988 }
14989 })
14990 }
14991 }
14992 },
14993 rules: {
14994 "test/test-rule": "error"
14995 }
14996 };
14997
14998
14999 assert.throws(() => {
15000 linter.verify("0", config);
15001 }, /Fixable rules must set the `meta\.fixable` property/u);
15002 });
15003 });
15004
15005 describe("Mutability", () => {
15006 let linter1 = null;
15007 let linter2 = null;
15008
15009 beforeEach(() => {
15010 linter1 = new Linter();
15011 linter2 = new Linter();
15012 });
15013
15014 describe("rules", () => {
15015 it("with no changes, same rules are loaded", () => {
15016 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
15017 });
15018
15019 it("loading rule in one doesn't change the other", () => {
15020 linter1.defineRule("mock-rule", () => ({}));
15021
15022 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
15023 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
15024 });
15025 });
15026 });
15027
15028
15029 describe("processors", () => {
15030 let receivedFilenames = [];
15031 let receivedPhysicalFilenames = [];
15032 const extraConfig = {
15033 plugins: {
15034 test: {
15035 rules: {
15036 "report-original-text": {
15037 meta: {
15038
15039 },
15040 create(context) {
15041 return {
15042 Program(ast) {
15043 receivedFilenames.push(context.getFilename());
15044 receivedPhysicalFilenames.push(context.getPhysicalFilename());
15045 context.report({ node: ast, message: context.getSourceCode().text });
15046 }
15047 };
15048 }
15049 }
15050 }
15051 }
15052 }
15053 };
15054
15055 beforeEach(() => {
15056 receivedFilenames = [];
15057 receivedPhysicalFilenames = [];
15058 });
15059
15060 describe("preprocessors", () => {
15061 it("should receive text and filename.", () => {
15062 const code = "foo bar baz";
15063 const preprocess = sinon.spy(text => text.split(" "));
15064 const configs = createFlatConfigArray({});
15065
15066 configs.normalizeSync();
15067
15068 linter.verify(code, configs, { filename, preprocess });
15069
15070 assert.strictEqual(preprocess.calledOnce, true, "preprocess wasn't called");
15071 assert.deepStrictEqual(preprocess.args[0], [code, filename], "preprocess was called with the wrong arguments");
15072 });
15073
15074 it("should run preprocess only once", () => {
15075 const logs = [];
15076 const config = {
15077 files: ["*.md"],
15078 processor: {
15079 preprocess(text, filenameForText) {
15080 logs.push({
15081 text,
15082 filename: filenameForText
15083 });
15084
15085 return [{ text: "bar", filename: "0.js" }];
15086 },
15087 postprocess() {
15088 return [];
15089 }
15090 }
15091 };
15092
15093 linter.verify("foo", config, "a.md");
15094 assert.strictEqual(logs.length, 1, "preprocess() should only be called once.");
15095 });
15096
15097 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
15098 const code = "foo bar baz";
15099 const configs = createFlatConfigArray([
15100 extraConfig,
15101 { rules: { "test/report-original-text": "error" } }
15102 ]);
15103
15104 configs.normalizeSync();
15105
15106 const problems = linter.verify(
15107 code,
15108 configs,
15109 {
15110 filename,
15111
15112 // Apply a preprocessor that splits the source text into spaces and lints each word individually
15113 preprocess(input) {
15114 return input.split(" ");
15115 }
15116 }
15117 );
15118 const suppressedMessages = linter.getSuppressedMessages();
15119
15120 assert.strictEqual(problems.length, 3);
15121 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15122
15123 assert.strictEqual(suppressedMessages.length, 0);
15124 });
15125
15126 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
15127 const code = "foo bar baz";
15128 const configs = createFlatConfigArray([
15129 extraConfig,
15130 { rules: { "test/report-original-text": "error" } }
15131 ]);
15132
15133 configs.normalizeSync();
15134
15135 const problems = linter.verify(
15136 code,
15137 configs,
15138 {
15139 filename,
15140
15141 // Apply a preprocessor that splits the source text into spaces and lints each word individually
15142 preprocess(input) {
15143 return input.split(" ").map(text => ({
15144 filename: "block.js",
15145 text
15146 }));
15147 }
15148 }
15149 );
15150 const suppressedMessages = linter.getSuppressedMessages();
15151
15152 assert.strictEqual(problems.length, 3);
15153 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15154
15155 assert.strictEqual(suppressedMessages.length, 0);
15156
15157 // filename
15158 assert.strictEqual(receivedFilenames.length, 3);
15159 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
15160 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
15161 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
15162
15163 // physical filename
15164 assert.strictEqual(receivedPhysicalFilenames.length, 3);
15165 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
15166 });
15167
15168 it("should receive text even if a SourceCode object was given.", () => {
15169 const code = "foo";
15170 const preprocess = sinon.spy(text => text.split(" "));
15171 const configs = createFlatConfigArray([
15172 extraConfig
15173 ]);
15174
15175 configs.normalizeSync();
15176
15177 linter.verify(code, configs);
15178 const sourceCode = linter.getSourceCode();
15179
15180 linter.verify(sourceCode, configs, { filename, preprocess });
15181
15182 assert.strictEqual(preprocess.calledOnce, true);
15183 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15184 });
15185
15186 it("should receive text even if a SourceCode object was given (with BOM).", () => {
15187 const code = "\uFEFFfoo";
15188 const preprocess = sinon.spy(text => text.split(" "));
15189 const configs = createFlatConfigArray([
15190 extraConfig
15191 ]);
15192
15193 configs.normalizeSync();
15194
15195 linter.verify(code, configs);
15196 const sourceCode = linter.getSourceCode();
15197
15198 linter.verify(sourceCode, configs, { filename, preprocess });
15199
15200 assert.strictEqual(preprocess.calledOnce, true);
15201 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15202 });
15203
15204 it("should catch preprocess error.", () => {
15205 const code = "foo";
15206 const preprocess = sinon.spy(() => {
15207 throw Object.assign(new SyntaxError("Invalid syntax"), {
15208 lineNumber: 1,
15209 column: 1
15210 });
15211 });
15212
15213 const configs = createFlatConfigArray([
15214 extraConfig
15215 ]);
15216
15217 configs.normalizeSync();
15218
15219 const messages = linter.verify(code, configs, { filename, preprocess });
15220
15221 assert.strictEqual(preprocess.calledOnce, true);
15222 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15223 assert.deepStrictEqual(messages, [
15224 {
15225 ruleId: null,
15226 fatal: true,
15227 severity: 2,
15228 message: "Preprocessing error: Invalid syntax",
15229 line: 1,
15230 column: 1
15231 }
15232 ]);
15233 });
15234 });
15235
15236 describe("postprocessors", () => {
15237 it("should receive result and filename.", () => {
15238 const code = "foo bar baz";
15239 const preprocess = sinon.spy(text => text.split(" "));
15240 const postprocess = sinon.spy(text => [text]);
15241 const configs = createFlatConfigArray([
15242 extraConfig
15243 ]);
15244
15245 configs.normalizeSync();
15246
15247 linter.verify(code, configs, { filename, postprocess, preprocess });
15248
15249 assert.strictEqual(postprocess.calledOnce, true);
15250 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
15251 });
15252
15253 it("should apply a postprocessor to the reported messages", () => {
15254 const code = "foo bar baz";
15255 const configs = createFlatConfigArray([
15256 extraConfig,
15257 { rules: { "test/report-original-text": "error" } }
15258 ]);
15259
15260 configs.normalizeSync();
15261
15262 const problems = linter.verify(
15263 code,
15264 configs,
15265 {
15266 preprocess: input => input.split(" "),
15267
15268 /*
15269 * Apply a postprocessor that updates the locations of the reported problems
15270 * to make sure they correspond to the locations in the original text.
15271 */
15272 postprocess(problemLists) {
15273 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
15274 return problemLists.reduce(
15275 (combinedList, problemList, index) =>
15276 combinedList.concat(
15277 problemList.map(
15278 problem =>
15279 Object.assign(
15280 {},
15281 problem,
15282 {
15283 message: problem.message.toUpperCase(),
15284 column: problem.column + index * 4
15285 }
15286 )
15287 )
15288 ),
15289 []
15290 );
15291 }
15292 }
15293 );
15294 const suppressedMessages = linter.getSuppressedMessages();
15295
15296 assert.strictEqual(problems.length, 3);
15297 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
15298 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
15299
15300 assert.strictEqual(suppressedMessages.length, 0);
15301 });
15302
15303 it("should use postprocessed problem ranges when applying autofixes", () => {
15304 const code = "foo bar baz";
15305 const configs = createFlatConfigArray([
15306 extraConfig,
15307 {
15308 plugins: {
15309 test2: {
15310 rules: {
15311 "capitalize-identifiers": {
15312 meta: {
15313 fixable: "code"
15314 },
15315 create(context) {
15316 return {
15317 Identifier(node) {
15318 if (node.name !== node.name.toUpperCase()) {
15319 context.report({
15320 node,
15321 message: "Capitalize this identifier",
15322 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
15323 });
15324 }
15325 }
15326 };
15327 }
15328 }
15329 }
15330 }
15331 }
15332 },
15333 { rules: { "test2/capitalize-identifiers": "error" } }
15334 ]);
15335
15336 configs.normalizeSync();
15337
15338 const fixResult = linter.verifyAndFix(
15339 code,
15340 configs,
15341 {
15342
15343 /*
15344 * Apply a postprocessor that updates the locations of autofixes
15345 * to make sure they correspond to locations in the original text.
15346 */
15347 preprocess: input => input.split(" "),
15348 postprocess(problemLists) {
15349 return problemLists.reduce(
15350 (combinedProblems, problemList, blockIndex) =>
15351 combinedProblems.concat(
15352 problemList.map(problem =>
15353 Object.assign(problem, {
15354 fix: {
15355 text: problem.fix.text,
15356 range: problem.fix.range.map(
15357 rangeIndex => rangeIndex + blockIndex * 4
15358 )
15359 }
15360 }))
15361 ),
15362 []
15363 );
15364 }
15365 }
15366 );
15367 const suppressedMessages = linter.getSuppressedMessages();
15368
15369 assert.strictEqual(fixResult.fixed, true);
15370 assert.strictEqual(fixResult.messages.length, 0);
15371 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
15372
15373 assert.strictEqual(suppressedMessages.length, 0);
15374 });
15375 });
15376 });
15377
15378 describe("Edge cases", () => {
15379
15380 describe("Modules", () => {
15381 const moduleConfig = {
15382 languageOptions: {
15383 sourceType: "module",
15384 ecmaVersion: 6
15385 }
15386 };
15387
15388 it("should properly parse import statements when sourceType is module", () => {
15389 const code = "import foo from 'foo';";
15390 const messages = linter.verify(code, moduleConfig);
15391 const suppressedMessages = linter.getSuppressedMessages();
15392
15393 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15394 assert.strictEqual(suppressedMessages.length, 0);
15395 });
15396
15397 it("should properly parse import all statements when sourceType is module", () => {
15398 const code = "import * as foo from 'foo';";
15399 const messages = linter.verify(code, moduleConfig);
15400 const suppressedMessages = linter.getSuppressedMessages();
15401
15402 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15403 assert.strictEqual(suppressedMessages.length, 0);
15404 });
15405
15406 it("should properly parse default export statements when sourceType is module", () => {
15407 const code = "export default function initialize() {}";
15408 const messages = linter.verify(code, moduleConfig);
15409 const suppressedMessages = linter.getSuppressedMessages();
15410
15411 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15412 assert.strictEqual(suppressedMessages.length, 0);
15413 });
15414
15415 });
15416
15417
15418 // https://github.com/eslint/eslint/issues/9687
15419 it("should report an error when invalid languageOptions found", () => {
15420 let messages = linter.verify("", { languageOptions: { ecmaVersion: 222 } });
15421
15422 assert.deepStrictEqual(messages.length, 1);
15423 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
15424
15425 assert.throws(() => {
15426 linter.verify("", { languageOptions: { sourceType: "foo" } });
15427 }, /Expected "script", "module", or "commonjs"./u);
15428
15429
15430 messages = linter.verify("", { languageOptions: { ecmaVersion: 5, sourceType: "module" } });
15431 const suppressedMessages = linter.getSuppressedMessages();
15432
15433 assert.deepStrictEqual(messages.length, 1);
15434 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
15435
15436 assert.strictEqual(suppressedMessages.length, 0);
15437 });
15438
15439 it("should not crash when invalid parentheses syntax is encountered", () => {
15440 linter.verify("left = (aSize.width/2) - ()");
15441 });
15442
15443 it("should not crash when let is used inside of switch case", () => {
15444 linter.verify("switch(foo) { case 1: let bar=2; }", { languageOptions: { ecmaVersion: 6 } });
15445 });
15446
15447 it("should not crash when parsing destructured assignment", () => {
15448 linter.verify("var { a='a' } = {};", { languageOptions: { ecmaVersion: 6 } });
15449 });
15450
15451 it("should report syntax error when a keyword exists in object property shorthand", () => {
15452 const messages = linter.verify("let a = {this}", { languageOptions: { ecmaVersion: 6 } });
15453 const suppressedMessages = linter.getSuppressedMessages();
15454
15455 assert.strictEqual(messages.length, 1);
15456 assert.strictEqual(messages[0].fatal, true);
15457
15458 assert.strictEqual(suppressedMessages.length, 0);
15459 });
15460
15461 it("should not crash when we reuse the SourceCode object", () => {
15462 const config = {
15463 languageOptions: {
15464 ecmaVersion: 6,
15465 parserOptions: {
15466 ecmaFeatures: { jsx: true }
15467 }
15468 }
15469 };
15470
15471 linter.verify("function render() { return <div className='test'>{hello}</div> }", config);
15472 linter.verify(linter.getSourceCode(), config);
15473 });
15474
15475 it("should reuse the SourceCode object", () => {
15476 let ast1 = null,
15477 ast2 = null;
15478
15479 const config = {
15480 plugins: {
15481 test: {
15482 rules: {
15483 "save-ast1": () => ({
15484 Program(node) {
15485 ast1 = node;
15486 }
15487 }),
15488
15489 "save-ast2": () => ({
15490 Program(node) {
15491 ast2 = node;
15492 }
15493 })
15494
15495 }
15496 }
15497 },
15498 languageOptions: {
15499 ecmaVersion: 6,
15500 parserOptions: {
15501 ecmaFeatures: { jsx: true }
15502 }
15503 }
15504 };
15505
15506
15507 linter.verify("function render() { return <div className='test'>{hello}</div> }", { ...config, rules: { "test/save-ast1": "error" } });
15508 linter.verify(linter.getSourceCode(), { ...config, rules: { "test/save-ast2": "error" } });
15509
15510 assert(ast1 !== null);
15511 assert(ast2 !== null);
15512 assert(ast1 === ast2);
15513 });
15514
15515 it("should not modify config object passed as argument", () => {
15516 const config = {};
15517
15518 Object.freeze(config);
15519 linter.verify("var", config);
15520 });
15521
15522 it("should pass 'id' to rule contexts with the rule id", () => {
15523
15524 const spy = sinon.spy(context => {
15525 assert.strictEqual(context.id, "test/foo-bar-baz");
15526 return {};
15527 });
15528
15529 const config = {
15530 plugins: {
15531 test: {
15532 rules: {
15533 "foo-bar-baz": spy
15534 }
15535 }
15536 },
15537 rules: {
15538 "test/foo-bar-baz": "error"
15539 }
15540 };
15541
15542
15543 linter.verify("x", config);
15544 assert(spy.calledOnce);
15545 });
15546
15547
15548 describe("when evaluating an empty string", () => {
15549 it("runs rules", () => {
15550
15551 const config = {
15552 plugins: {
15553 test: {
15554 rules: {
15555 "no-programs": context => ({
15556 Program(node) {
15557 context.report({ node, message: "No programs allowed." });
15558 }
15559 })
15560 }
15561 }
15562 },
15563 rules: {
15564 "test/no-programs": "error"
15565 }
15566 };
15567
15568 assert.strictEqual(
15569 linter.verify("", config).length,
15570 1
15571 );
15572 });
15573 });
15574
15575 });
15576
15577 });