]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/linter/linter.js
import 8.41.0 source
[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 create: () => ({
85 Program() {
86 throw new Error("Intentional error.");
87 }
88 })
89 });
90
91 assert.throws(() => {
92 linter.verify(code, config, filename);
93 }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "checker"`);
94 });
95
96 it("does not call rule listeners with a `this` value", () => {
97 const spy = sinon.spy();
98
99 linter.defineRule("checker", {
100 create: () => ({ Program: spy })
101 });
102 linter.verify("foo", { rules: { checker: "error" } });
103 assert(spy.calledOnce, "Rule should have been called");
104 assert.strictEqual(spy.firstCall.thisValue, void 0, "this value should be undefined");
105 });
106
107 it("does not allow listeners to use special EventEmitter values", () => {
108 const spy = sinon.spy();
109
110 linter.defineRule("checker", {
111 create: () => ({ newListener: spy })
112 });
113 linter.verify("foo", { rules: { checker: "error", "no-undef": "error" } });
114 assert(spy.notCalled);
115 });
116
117 it("has all the `parent` properties on nodes when the rule listeners are created", () => {
118 const spy = sinon.spy(context => {
119 assert.strictEqual(context.getSourceCode(), context.sourceCode);
120 const ast = context.sourceCode.ast;
121
122 assert.strictEqual(ast.body[0].parent, ast);
123 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
124 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
125 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
126
127 return {};
128 });
129
130 linter.defineRule("checker", { create: spy });
131
132 linter.verify("foo + bar", { rules: { checker: "error" } });
133 assert(spy.calledOnce);
134 });
135 });
136
137 describe("context.getSourceLines()", () => {
138
139 it("should get proper lines when using \\n as a line break", () => {
140 const code = "a;\nb;";
141 const spy = sinon.spy(context => {
142 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
143 return {};
144 });
145
146 linter.defineRule("checker", { create: spy });
147 linter.verify(code, { rules: { checker: "error" } });
148 assert(spy.calledOnce);
149 });
150
151 it("should get proper lines when using \\r\\n as a line break", () => {
152 const code = "a;\r\nb;";
153 const spy = sinon.spy(context => {
154 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
155 return {};
156 });
157
158 linter.defineRule("checker", { create: spy });
159 linter.verify(code, { rules: { checker: "error" } });
160 assert(spy.calledOnce);
161 });
162
163 it("should get proper lines when using \\r as a line break", () => {
164 const code = "a;\rb;";
165 const spy = sinon.spy(context => {
166 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
167 return {};
168 });
169
170 linter.defineRule("checker", { create: spy });
171 linter.verify(code, { rules: { checker: "error" } });
172 assert(spy.calledOnce);
173 });
174
175 it("should get proper lines when using \\u2028 as a line break", () => {
176 const code = "a;\u2028b;";
177 const spy = sinon.spy(context => {
178 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
179 return {};
180 });
181
182 linter.defineRule("checker", { create: spy });
183 linter.verify(code, { rules: { checker: "error" } });
184 assert(spy.calledOnce);
185 });
186
187 it("should get proper lines when using \\u2029 as a line break", () => {
188 const code = "a;\u2029b;";
189 const spy = sinon.spy(context => {
190 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
191 return {};
192 });
193
194 linter.defineRule("checker", { create: spy });
195 linter.verify(code, { rules: { checker: "error" } });
196 assert(spy.calledOnce);
197 });
198
199
200 });
201
202 describe("getSourceCode()", () => {
203 const code = TEST_CODE;
204
205 it("should retrieve SourceCode object after reset", () => {
206 linter.verify(code, {}, filename, true);
207
208 const sourceCode = linter.getSourceCode();
209
210 assert.isObject(sourceCode);
211 assert.strictEqual(sourceCode.text, code);
212 assert.isObject(sourceCode.ast);
213 });
214
215 it("should retrieve SourceCode object without reset", () => {
216 linter.verify(code, {}, filename);
217
218 const sourceCode = linter.getSourceCode();
219
220 assert.isObject(sourceCode);
221 assert.strictEqual(sourceCode.text, code);
222 assert.isObject(sourceCode.ast);
223 });
224
225 });
226
227 describe("getSuppressedMessages()", () => {
228 it("should have no suppressed messages", () => {
229 const suppressedMessages = linter.getSuppressedMessages();
230
231 assert.strictEqual(suppressedMessages.length, 0);
232 });
233
234 it("should have a suppressed message", () => {
235 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test\");";
236 const config = {
237 rules: { "no-alert": 1 }
238 };
239 const messages = linter.verify(code, config);
240 const suppressedMessages = linter.getSuppressedMessages();
241
242 assert.strictEqual(messages.length, 0);
243
244 assert.strictEqual(suppressedMessages.length, 1);
245 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
246 assert.deepStrictEqual(
247 suppressedMessages[0].suppressions,
248 [{ kind: "directive", justification: "justification" }]
249 );
250 });
251
252 it("should have a suppressed message", () => {
253 const code = [
254 "/* eslint-disable no-alert --- j1",
255 " * --- j2",
256 " */",
257 "alert(\"test\");"
258 ].join("\n");
259 const config = {
260 rules: { "no-alert": 1 }
261 };
262 const messages = linter.verify(code, config);
263 const suppressedMessages = linter.getSuppressedMessages();
264
265 assert.strictEqual(messages.length, 0);
266
267 assert.strictEqual(suppressedMessages.length, 1);
268 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
269 assert.deepStrictEqual(
270 suppressedMessages[0].suppressions,
271 [{ kind: "directive", justification: "j1\n * --- j2" }]
272 );
273 });
274
275 it("should not report a lint message", () => {
276 const code = [
277 "/* eslint-disable -- j1 */",
278 "// eslint-disable-next-line -- j2",
279 "alert(\"test\");"
280 ].join("\n");
281 const config = {
282 rules: { "no-alert": 1 }
283 };
284 const messages = linter.verify(code, config);
285 const suppressedMessages = linter.getSuppressedMessages();
286
287 assert.strictEqual(messages.length, 0);
288
289 assert.strictEqual(suppressedMessages.length, 1);
290 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
291 assert.deepStrictEqual(
292 suppressedMessages[0].suppressions,
293 [
294 { kind: "directive", justification: "j1" },
295 { kind: "directive", justification: "j2" }
296 ]
297 );
298 });
299
300 it("should not report a lint message", () => {
301 const code = [
302 "/* eslint-disable -- j1 */",
303 "alert(\"test\"); // eslint-disable-line -- j2"
304 ].join("\n");
305 const config = {
306 rules: { "no-alert": 1 }
307 };
308 const messages = linter.verify(code, config);
309 const suppressedMessages = linter.getSuppressedMessages();
310
311 assert.strictEqual(messages.length, 0);
312
313 assert.strictEqual(suppressedMessages.length, 1);
314 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
315 assert.deepStrictEqual(
316 suppressedMessages[0].suppressions,
317 [
318 { kind: "directive", justification: "j1" },
319 { kind: "directive", justification: "j2" }
320 ]
321 );
322 });
323
324 it("should have a suppressed message with multiple suppressions", () => {
325 const code = [
326 "/* eslint-disable no-alert -- j1 */",
327 "/* eslint-disable no-console -- unused */",
328 "/* eslint-disable-next-line no-alert -- j2 */",
329 "alert(\"test\"); // eslint-disable-line no-alert -- j3"
330 ].join("\n");
331 const config = {
332 rules: { "no-alert": 1 }
333 };
334 const messages = linter.verify(code, config);
335 const suppressedMessages = linter.getSuppressedMessages();
336
337 assert.strictEqual(messages.length, 0);
338
339 assert.strictEqual(suppressedMessages.length, 1);
340 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
341 assert.deepStrictEqual(
342 suppressedMessages[0].suppressions,
343 [
344 { kind: "directive", justification: "j1" },
345 { kind: "directive", justification: "j2" },
346 { kind: "directive", justification: "j3" }
347 ]
348 );
349 });
350 });
351
352 describe("context.getSource()", () => {
353 const code = TEST_CODE;
354
355 it("should retrieve all text when used without parameters", () => {
356
357 const config = { rules: { checker: "error" } };
358 let spy;
359
360 linter.defineRule("checker", {
361 create(context) {
362 spy = sinon.spy(() => {
363 assert.strictEqual(context.getSource(), TEST_CODE);
364 });
365 return { Program: spy };
366 }
367 });
368
369 linter.verify(code, config);
370 assert(spy && spy.calledOnce);
371 });
372
373 it("should retrieve all text for root node", () => {
374 const config = { rules: { checker: "error" } };
375 let spy;
376
377 linter.defineRule("checker", {
378 create(context) {
379 spy = sinon.spy(node => {
380 assert.strictEqual(context.getSource(node), TEST_CODE);
381 });
382 return { Program: spy };
383 }
384 });
385
386 linter.verify(code, config);
387 assert(spy && spy.calledOnce);
388 });
389
390 it("should clamp to valid range when retrieving characters before start of source", () => {
391 const config = { rules: { checker: "error" } };
392 let spy;
393
394 linter.defineRule("checker", {
395 create(context) {
396 spy = sinon.spy(node => {
397 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
398 });
399 return { Program: spy };
400 }
401 });
402
403 linter.verify(code, config);
404 assert(spy && spy.calledOnce);
405 });
406
407 it("should retrieve all text for binary expression", () => {
408 const config = { rules: { checker: "error" } };
409 let spy;
410
411 linter.defineRule("checker", {
412 create(context) {
413 spy = sinon.spy(node => {
414 assert.strictEqual(context.getSource(node), "6 * 7");
415 });
416 return { BinaryExpression: spy };
417 }
418 });
419
420 linter.verify(code, config);
421 assert(spy && spy.calledOnce);
422 });
423
424 it("should retrieve all text plus two characters before for binary expression", () => {
425 const config = { rules: { checker: "error" } };
426 let spy;
427
428 linter.defineRule("checker", {
429 create(context) {
430 spy = sinon.spy(node => {
431 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
432 });
433 return { BinaryExpression: spy };
434 }
435 });
436
437 linter.verify(code, config);
438 assert(spy && spy.calledOnce);
439 });
440
441 it("should retrieve all text plus one character after for binary expression", () => {
442 const config = { rules: { checker: "error" } };
443 let spy;
444
445 linter.defineRule("checker", {
446 create(context) {
447 spy = sinon.spy(node => {
448 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
449 });
450 return { BinaryExpression: spy };
451 }
452 });
453
454 linter.verify(code, config);
455 assert(spy && spy.calledOnce);
456 });
457
458 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
459 const config = { rules: { checker: "error" } };
460 let spy;
461
462 linter.defineRule("checker", {
463 create(context) {
464 spy = sinon.spy(node => {
465 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
466 });
467 return { BinaryExpression: spy };
468 }
469 });
470
471 linter.verify(code, config);
472 assert(spy && spy.calledOnce);
473 });
474
475 });
476
477 describe("when calling context.getAncestors", () => {
478 const code = TEST_CODE;
479
480 it("should retrieve all ancestors when used", () => {
481
482 const config = { rules: { checker: "error" } };
483 let spy;
484
485 linter.defineRule("checker", {
486 create(context) {
487 spy = sinon.spy(() => {
488 const ancestors = context.getAncestors();
489
490 assert.strictEqual(ancestors.length, 3);
491 });
492 return { BinaryExpression: spy };
493 }
494 });
495
496 linter.verify(code, config, filename, true);
497 assert(spy && spy.calledOnce);
498 });
499
500 it("should retrieve empty ancestors for root node", () => {
501 const config = { rules: { checker: "error" } };
502 let spy;
503
504 linter.defineRule("checker", {
505 create(context) {
506 spy = sinon.spy(() => {
507 const ancestors = context.getAncestors();
508
509 assert.strictEqual(ancestors.length, 0);
510 });
511
512 return { Program: spy };
513 }
514 });
515
516 linter.verify(code, config);
517 assert(spy && spy.calledOnce);
518 });
519 });
520
521 describe("when calling context.getNodeByRangeIndex", () => {
522 const code = TEST_CODE;
523
524 it("should retrieve a node starting at the given index", () => {
525 const config = { rules: { checker: "error" } };
526 const spy = sinon.spy(context => {
527 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
528 return {};
529 });
530
531 linter.defineRule("checker", { create: spy });
532 linter.verify(code, config);
533 assert(spy.calledOnce);
534 });
535
536 it("should retrieve a node containing the given index", () => {
537 const config = { rules: { checker: "error" } };
538 const spy = sinon.spy(context => {
539 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
540 return {};
541 });
542
543 linter.defineRule("checker", { create: spy });
544 linter.verify(code, config);
545 assert(spy.calledOnce);
546 });
547
548 it("should retrieve a node that is exactly the given index", () => {
549 const config = { rules: { checker: "error" } };
550 const spy = sinon.spy(context => {
551 const node = context.getNodeByRangeIndex(13);
552
553 assert.strictEqual(node.type, "Literal");
554 assert.strictEqual(node.value, 6);
555 return {};
556 });
557
558 linter.defineRule("checker", { create: spy });
559 linter.verify(code, config);
560 assert(spy.calledOnce);
561 });
562
563 it("should retrieve a node ending with the given index", () => {
564 const config = { rules: { checker: "error" } };
565 const spy = sinon.spy(context => {
566 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
567 return {};
568 });
569
570 linter.defineRule("checker", { create: spy });
571 linter.verify(code, config);
572 assert(spy.calledOnce);
573 });
574
575 it("should retrieve the deepest node containing the given index", () => {
576 const config = { rules: { checker: "error" } };
577 const spy = sinon.spy(context => {
578 const node1 = context.getNodeByRangeIndex(14);
579
580 assert.strictEqual(node1.type, "BinaryExpression");
581
582 const node2 = context.getNodeByRangeIndex(3);
583
584 assert.strictEqual(node2.type, "VariableDeclaration");
585 return {};
586 });
587
588 linter.defineRule("checker", { create: spy });
589 linter.verify(code, config);
590 assert(spy.calledOnce);
591 });
592
593 it("should return null if the index is outside the range of any node", () => {
594 const config = { rules: { checker: "error" } };
595 const spy = sinon.spy(context => {
596 const node1 = context.getNodeByRangeIndex(-1);
597
598 assert.isNull(node1);
599
600 const node2 = context.getNodeByRangeIndex(-99);
601
602 assert.isNull(node2);
603 return {};
604 });
605
606 linter.defineRule("checker", { create: spy });
607 linter.verify(code, config);
608 assert(spy.calledOnce);
609 });
610 });
611
612
613 describe("when calling context.getScope", () => {
614 const code = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
615
616 it("should retrieve the global scope correctly from a Program", () => {
617 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
618 let spy;
619
620 linter.defineRule("checker", {
621 create(context) {
622 spy = sinon.spy(() => {
623 const scope = context.getScope();
624
625 assert.strictEqual(scope.type, "global");
626 });
627 return { Program: spy };
628 }
629 });
630
631 linter.verify(code, config);
632 assert(spy && spy.calledOnce);
633 });
634
635 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
636 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
637 let spy;
638
639 linter.defineRule("checker", {
640 create(context) {
641 spy = sinon.spy(() => {
642 const scope = context.getScope();
643
644 assert.strictEqual(scope.type, "function");
645 });
646 return { FunctionDeclaration: spy };
647 }
648 });
649
650 linter.verify(code, config);
651 assert(spy && spy.calledTwice);
652 });
653
654 it("should retrieve the function scope correctly from a LabeledStatement", () => {
655 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
656 let spy;
657
658 linter.defineRule("checker", {
659 create(context) {
660 spy = sinon.spy(() => {
661 const scope = context.getScope();
662
663 assert.strictEqual(scope.type, "function");
664 assert.strictEqual(scope.block.id.name, "foo");
665 });
666 return { LabeledStatement: spy };
667 }
668 });
669
670 linter.verify(code, config);
671 assert(spy && spy.calledOnce);
672 });
673
674 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
675 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
676 let spy;
677
678 linter.defineRule("checker", {
679 create(context) {
680 spy = sinon.spy(() => {
681 const scope = context.getScope();
682
683 assert.strictEqual(scope.type, "function");
684 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
685 });
686
687 return { ReturnStatement: spy };
688 }
689 });
690
691 linter.verify(code, config);
692 assert(spy && spy.calledOnce);
693 });
694
695 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
696 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
697 let spy;
698
699 linter.defineRule("checker", {
700 create(context) {
701 spy = sinon.spy(() => {
702 const scope = context.getScope();
703
704 assert.strictEqual(scope.type, "switch");
705 assert.strictEqual(scope.block.type, "SwitchStatement");
706 });
707
708 return { SwitchStatement: spy };
709 }
710 });
711
712 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
713 assert(spy && spy.calledOnce);
714 });
715
716 it("should retrieve the function scope correctly from within a BlockStatement", () => {
717 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
718 let spy;
719
720 linter.defineRule("checker", {
721 create(context) {
722 spy = sinon.spy(() => {
723 const scope = context.getScope();
724
725 assert.strictEqual(scope.type, "block");
726 assert.strictEqual(scope.block.type, "BlockStatement");
727 });
728
729 return { BlockStatement: spy };
730 }
731 });
732
733 linter.verify("var x; {let y = 1}", config);
734 assert(spy && spy.calledOnce);
735 });
736
737 it("should retrieve the function scope correctly from within a nested block statement", () => {
738 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
739 let spy;
740
741 linter.defineRule("checker", {
742 create(context) {
743 spy = sinon.spy(() => {
744 const scope = context.getScope();
745
746 assert.strictEqual(scope.type, "block");
747 assert.strictEqual(scope.block.type, "BlockStatement");
748 });
749
750 return { BlockStatement: spy };
751 }
752 });
753
754 linter.verify("if (true) { let x = 1 }", config);
755 assert(spy && spy.calledOnce);
756 });
757
758 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
759 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
760 let spy;
761
762 linter.defineRule("checker", {
763 create(context) {
764 spy = sinon.spy(() => {
765 const scope = context.getScope();
766
767 assert.strictEqual(scope.type, "function");
768 assert.strictEqual(scope.block.type, "FunctionDeclaration");
769 });
770
771 return { FunctionDeclaration: spy };
772 }
773 });
774
775 linter.verify("function foo() {}", config);
776 assert(spy && spy.calledOnce);
777 });
778
779 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
780 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
781 let spy;
782
783 linter.defineRule("checker", {
784 create(context) {
785 spy = sinon.spy(() => {
786 const scope = context.getScope();
787
788 assert.strictEqual(scope.type, "function");
789 assert.strictEqual(scope.block.type, "FunctionExpression");
790 });
791
792 return { FunctionExpression: spy };
793 }
794 });
795
796 linter.verify("(function foo() {})();", config);
797 assert(spy && spy.calledOnce);
798 });
799
800 it("should retrieve the catch scope correctly from within a CatchClause", () => {
801 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
802 let spy;
803
804 linter.defineRule("checker", {
805 create(context) {
806 spy = sinon.spy(() => {
807 const scope = context.getScope();
808
809 assert.strictEqual(scope.type, "catch");
810 assert.strictEqual(scope.block.type, "CatchClause");
811 });
812
813 return { CatchClause: spy };
814 }
815 });
816
817 linter.verify("try {} catch (err) {}", config);
818 assert(spy && spy.calledOnce);
819 });
820
821 it("should retrieve module scope correctly from an ES6 module", () => {
822 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } };
823 let spy;
824
825 linter.defineRule("checker", {
826 create(context) {
827 spy = sinon.spy(() => {
828 const scope = context.getScope();
829
830 assert.strictEqual(scope.type, "module");
831 });
832
833 return { AssignmentExpression: spy };
834 }
835 });
836
837 linter.verify("var foo = {}; foo.bar = 1;", config);
838 assert(spy && spy.calledOnce);
839 });
840
841 it("should retrieve function scope correctly when globalReturn is true", () => {
842 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, ecmaFeatures: { globalReturn: true } } };
843 let spy;
844
845 linter.defineRule("checker", {
846 create(context) {
847 spy = sinon.spy(() => {
848 const scope = context.getScope();
849
850 assert.strictEqual(scope.type, "function");
851 });
852
853 return { AssignmentExpression: spy };
854 }
855 });
856
857 linter.verify("var foo = {}; foo.bar = 1;", config);
858 assert(spy && spy.calledOnce);
859 });
860 });
861
862 describe("marking variables as used", () => {
863 it("should mark variables in current scope as used", () => {
864 const code = "var a = 1, b = 2;";
865 let spy;
866
867 linter.defineRule("checker", {
868 create(context) {
869 spy = sinon.spy(() => {
870 assert.isTrue(context.markVariableAsUsed("a"));
871
872 const scope = context.getScope();
873
874 assert.isTrue(getVariable(scope, "a").eslintUsed);
875 assert.notOk(getVariable(scope, "b").eslintUsed);
876 });
877
878 return { "Program:exit": spy };
879 }
880 });
881
882 linter.verify(code, { rules: { checker: "error" } });
883 assert(spy && spy.calledOnce);
884 });
885 it("should mark variables in function args as used", () => {
886 const code = "function abc(a, b) { return 1; }";
887 let spy;
888
889 linter.defineRule("checker", {
890 create(context) {
891 spy = sinon.spy(() => {
892 assert.isTrue(context.markVariableAsUsed("a"));
893
894 const scope = context.getScope();
895
896 assert.isTrue(getVariable(scope, "a").eslintUsed);
897 assert.notOk(getVariable(scope, "b").eslintUsed);
898 });
899
900 return { ReturnStatement: spy };
901 }
902 });
903
904 linter.verify(code, { rules: { checker: "error" } });
905 assert(spy && spy.calledOnce);
906 });
907 it("should mark variables in higher scopes as used", () => {
908 const code = "var a, b; function abc() { return 1; }";
909 let returnSpy, exitSpy;
910
911 linter.defineRule("checker", {
912 create(context) {
913 returnSpy = sinon.spy(() => {
914 assert.isTrue(context.markVariableAsUsed("a"));
915 });
916 exitSpy = sinon.spy(() => {
917 const scope = context.getScope();
918
919 assert.isTrue(getVariable(scope, "a").eslintUsed);
920 assert.notOk(getVariable(scope, "b").eslintUsed);
921 });
922
923 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
924 }
925 });
926
927 linter.verify(code, { rules: { checker: "error" } });
928 assert(returnSpy && returnSpy.calledOnce);
929 assert(exitSpy && exitSpy.calledOnce);
930 });
931
932 it("should mark variables in Node.js environment as used", () => {
933 const code = "var a = 1, b = 2;";
934 let spy;
935
936 linter.defineRule("checker", {
937 create(context) {
938 spy = sinon.spy(() => {
939 const globalScope = context.getScope(),
940 childScope = globalScope.childScopes[0];
941
942 assert.isTrue(context.markVariableAsUsed("a"));
943
944 assert.isTrue(getVariable(childScope, "a").eslintUsed);
945 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
946 });
947
948 return { "Program:exit": spy };
949 }
950 });
951
952 linter.verify(code, { rules: { checker: "error" }, env: { node: true } });
953 assert(spy && spy.calledOnce);
954 });
955
956 it("should mark variables in modules as used", () => {
957 const code = "var a = 1, b = 2;";
958 let spy;
959
960 linter.defineRule("checker", {
961 create(context) {
962 spy = sinon.spy(() => {
963 const globalScope = context.getScope(),
964 childScope = globalScope.childScopes[0];
965
966 assert.isTrue(context.markVariableAsUsed("a"));
967
968 assert.isTrue(getVariable(childScope, "a").eslintUsed);
969 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
970 });
971
972 return { "Program:exit": spy };
973 }
974 });
975
976 linter.verify(code, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } }, filename, true);
977 assert(spy && spy.calledOnce);
978 });
979
980 it("should return false if the given variable is not found", () => {
981 const code = "var a = 1, b = 2;";
982 let spy;
983
984 linter.defineRule("checker", {
985 create(context) {
986 spy = sinon.spy(() => {
987 assert.isFalse(context.markVariableAsUsed("c"));
988 });
989
990 return { "Program:exit": spy };
991 }
992 });
993
994 linter.verify(code, { rules: { checker: "error" } });
995 assert(spy && spy.calledOnce);
996 });
997 });
998
999 describe("when evaluating code", () => {
1000 const code = TEST_CODE;
1001
1002 it("events for each node type should fire", () => {
1003 const config = { rules: { checker: "error" } };
1004
1005 // spies for various AST node types
1006 const spyLiteral = sinon.spy(),
1007 spyVariableDeclarator = sinon.spy(),
1008 spyVariableDeclaration = sinon.spy(),
1009 spyIdentifier = sinon.spy(),
1010 spyBinaryExpression = sinon.spy();
1011
1012 linter.defineRule("checker", {
1013 create: () => ({
1014 Literal: spyLiteral,
1015 VariableDeclarator: spyVariableDeclarator,
1016 VariableDeclaration: spyVariableDeclaration,
1017 Identifier: spyIdentifier,
1018 BinaryExpression: spyBinaryExpression
1019 })
1020 });
1021
1022 const messages = linter.verify(code, config, filename, true);
1023 const suppressedMessages = linter.getSuppressedMessages();
1024
1025 assert.strictEqual(messages.length, 0);
1026 assert.strictEqual(suppressedMessages.length, 0);
1027 sinon.assert.calledOnce(spyVariableDeclaration);
1028 sinon.assert.calledOnce(spyVariableDeclarator);
1029 sinon.assert.calledOnce(spyIdentifier);
1030 sinon.assert.calledTwice(spyLiteral);
1031 sinon.assert.calledOnce(spyBinaryExpression);
1032 });
1033
1034 it("should throw an error if a rule reports a problem without a message", () => {
1035 linter.defineRule("invalid-report", {
1036 create: context => ({
1037 Program(node) {
1038 context.report({ node });
1039 }
1040 })
1041 });
1042
1043 assert.throws(
1044 () => linter.verify("foo", { rules: { "invalid-report": "error" } }),
1045 TypeError,
1046 "Missing `message` property in report() call; add a message that describes the linting problem."
1047 );
1048 });
1049 });
1050
1051 describe("when config has shared settings for rules", () => {
1052 const code = "test-rule";
1053
1054 it("should pass settings to all rules", () => {
1055 linter.defineRule(code, {
1056 create: context => ({
1057 Literal(node) {
1058 context.report(node, context.settings.info);
1059 }
1060 })
1061 });
1062
1063 const config = { rules: {}, settings: { info: "Hello" } };
1064
1065 config.rules[code] = 1;
1066
1067 const messages = linter.verify("0", config, filename);
1068 const suppressedMessages = linter.getSuppressedMessages();
1069
1070 assert.strictEqual(messages.length, 1);
1071 assert.strictEqual(messages[0].message, "Hello");
1072 assert.strictEqual(suppressedMessages.length, 0);
1073 });
1074
1075 it("should not have any settings if they were not passed in", () => {
1076 linter.defineRule(code, {
1077 create: context => ({
1078 Literal(node) {
1079 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
1080 context.report(node, "Settings should be empty");
1081 }
1082 }
1083 })
1084 });
1085
1086 const config = { rules: {} };
1087
1088 config.rules[code] = 1;
1089
1090 const messages = linter.verify("0", config, filename);
1091 const suppressedMessages = linter.getSuppressedMessages();
1092
1093 assert.strictEqual(messages.length, 0);
1094 assert.strictEqual(suppressedMessages.length, 0);
1095 });
1096 });
1097
1098 describe("when config has parseOptions", () => {
1099
1100 it("should pass ecmaFeatures to all rules when provided on config", () => {
1101
1102 const parserOptions = {
1103 ecmaFeatures: {
1104 jsx: true,
1105 globalReturn: true
1106 }
1107 };
1108
1109 linter.defineRule("test-rule", {
1110 create: sinon.mock().withArgs(
1111 sinon.match({ parserOptions })
1112 ).returns({})
1113 });
1114
1115 const config = { rules: { "test-rule": 2 }, parserOptions };
1116
1117 linter.verify("0", config, filename);
1118 });
1119
1120 it("should pass parserOptions to all rules when default parserOptions is used", () => {
1121
1122 const parserOptions = {};
1123
1124 linter.defineRule("test-rule", {
1125 create: sinon.mock().withArgs(
1126 sinon.match({ parserOptions })
1127 ).returns({})
1128 });
1129
1130 const config = { rules: { "test-rule": 2 } };
1131
1132 linter.verify("0", config, filename);
1133 });
1134
1135 });
1136
1137 describe("when a custom parser is defined using defineParser", () => {
1138
1139 it("should be able to define a custom parser", () => {
1140 const parser = {
1141 parseForESLint: function parse(code, options) {
1142 return {
1143 ast: esprima.parse(code, options),
1144 services: {
1145 test: {
1146 getMessage() {
1147 return "Hi!";
1148 }
1149 }
1150 }
1151 };
1152 }
1153 };
1154
1155 linter.defineParser("test-parser", parser);
1156 const config = { rules: {}, parser: "test-parser" };
1157 const messages = linter.verify("0", config, filename);
1158 const suppressedMessages = linter.getSuppressedMessages();
1159
1160 assert.strictEqual(messages.length, 0);
1161 assert.strictEqual(suppressedMessages.length, 0);
1162 });
1163
1164 });
1165
1166 describe("when config has parser", () => {
1167
1168 it("should pass parser as parserPath to all rules when provided on config", () => {
1169
1170 const alternateParser = "esprima";
1171
1172 linter.defineParser("esprima", esprima);
1173 linter.defineRule("test-rule", {
1174 create: sinon.mock().withArgs(
1175 sinon.match({ parserPath: alternateParser })
1176 ).returns({})
1177 });
1178
1179 const config = { rules: { "test-rule": 2 }, parser: alternateParser };
1180
1181 linter.verify("0", config, filename);
1182 });
1183
1184 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
1185 const config = { rules: {}, parser: "enhanced-parser" };
1186
1187 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1188 const messages = linter.verify("0", config, filename);
1189 const suppressedMessages = linter.getSuppressedMessages();
1190
1191 assert.strictEqual(messages.length, 0);
1192 assert.strictEqual(suppressedMessages.length, 0);
1193 });
1194
1195 it("should expose parser services when using parseForESLint() and services are specified", () => {
1196 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1197 linter.defineRule("test-service-rule", {
1198 create: context => ({
1199 Literal(node) {
1200 context.report({
1201 node,
1202 message: context.parserServices.test.getMessage()
1203 });
1204 }
1205 })
1206 });
1207
1208 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
1209 const messages = linter.verify("0", config, filename);
1210 const suppressedMessages = linter.getSuppressedMessages();
1211
1212 assert.strictEqual(messages.length, 1);
1213 assert.strictEqual(messages[0].message, "Hi!");
1214 assert.strictEqual(suppressedMessages.length, 0);
1215 });
1216
1217 it("should use the same parserServices if source code object is reused", () => {
1218 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1219 linter.defineRule("test-service-rule", {
1220 create: context => ({
1221 Literal(node) {
1222 context.report({
1223 node,
1224 message: context.parserServices.test.getMessage()
1225 });
1226 }
1227 })
1228 });
1229
1230 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
1231 const messages = linter.verify("0", config, filename);
1232 const suppressedMessages = linter.getSuppressedMessages();
1233
1234 assert.strictEqual(messages.length, 1);
1235 assert.strictEqual(messages[0].message, "Hi!");
1236 assert.strictEqual(suppressedMessages.length, 0);
1237
1238 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
1239 const suppressedMessages2 = linter.getSuppressedMessages();
1240
1241 assert.strictEqual(messages2.length, 1);
1242 assert.strictEqual(messages2[0].message, "Hi!");
1243 assert.strictEqual(suppressedMessages2.length, 0);
1244 });
1245
1246 it("should pass parser as parserPath to all rules when default parser is used", () => {
1247 linter.defineRule("test-rule", {
1248 create: sinon.mock().withArgs(
1249 sinon.match({ parserPath: "espree" })
1250 ).returns({})
1251 });
1252
1253 const config = { rules: { "test-rule": 2 } };
1254
1255 linter.verify("0", config, filename);
1256 });
1257
1258 });
1259
1260
1261 describe("when passing in configuration values for rules", () => {
1262 const code = "var answer = 6 * 7";
1263
1264 it("should be configurable by only setting the integer value", () => {
1265 const rule = "semi",
1266 config = { rules: {} };
1267
1268 config.rules[rule] = 1;
1269
1270 const messages = linter.verify(code, config, filename, true);
1271 const suppressedMessages = linter.getSuppressedMessages();
1272
1273 assert.strictEqual(messages.length, 1);
1274 assert.strictEqual(messages[0].ruleId, rule);
1275
1276 assert.strictEqual(suppressedMessages.length, 0);
1277 });
1278
1279 it("should be configurable by only setting the string value", () => {
1280 const rule = "semi",
1281 config = { rules: {} };
1282
1283 config.rules[rule] = "warn";
1284
1285 const messages = linter.verify(code, config, filename, true);
1286 const suppressedMessages = linter.getSuppressedMessages();
1287
1288 assert.strictEqual(messages.length, 1);
1289 assert.strictEqual(messages[0].severity, 1);
1290 assert.strictEqual(messages[0].ruleId, rule);
1291
1292 assert.strictEqual(suppressedMessages.length, 0);
1293 });
1294
1295 it("should be configurable by passing in values as an array", () => {
1296 const rule = "semi",
1297 config = { rules: {} };
1298
1299 config.rules[rule] = [1];
1300
1301 const messages = linter.verify(code, config, filename, true);
1302 const suppressedMessages = linter.getSuppressedMessages();
1303
1304 assert.strictEqual(messages.length, 1);
1305 assert.strictEqual(messages[0].ruleId, rule);
1306
1307 assert.strictEqual(suppressedMessages.length, 0);
1308 });
1309
1310 it("should be configurable by passing in string value as an array", () => {
1311 const rule = "semi",
1312 config = { rules: {} };
1313
1314 config.rules[rule] = ["warn"];
1315
1316 const messages = linter.verify(code, config, filename, true);
1317 const suppressedMessages = linter.getSuppressedMessages();
1318
1319 assert.strictEqual(messages.length, 1);
1320 assert.strictEqual(messages[0].severity, 1);
1321 assert.strictEqual(messages[0].ruleId, rule);
1322
1323 assert.strictEqual(suppressedMessages.length, 0);
1324 });
1325
1326 it("should not be configurable by setting other value", () => {
1327 const rule = "semi",
1328 config = { rules: {} };
1329
1330 config.rules[rule] = "1";
1331
1332 const messages = linter.verify(code, config, filename, true);
1333 const suppressedMessages = linter.getSuppressedMessages();
1334
1335 assert.strictEqual(messages.length, 0);
1336 assert.strictEqual(suppressedMessages.length, 0);
1337 });
1338
1339 it("should process empty config", () => {
1340 const config = {};
1341 const messages = linter.verify(code, config, filename, true);
1342 const suppressedMessages = linter.getSuppressedMessages();
1343
1344 assert.strictEqual(messages.length, 0);
1345 assert.strictEqual(suppressedMessages.length, 0);
1346 });
1347 });
1348
1349 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
1350
1351 it("variables should be available in global scope", () => {
1352 const config = { rules: { checker: "error" }, globals: { Array: "off", ConfigGlobal: "writeable" } };
1353 const code = `
1354 /*global a b:true c:false d:readable e:writeable Math:off */
1355 function foo() {}
1356 /*globals f:true*/
1357 /* global ConfigGlobal : readable */
1358 `;
1359 let spy;
1360
1361 linter.defineRule("checker", {
1362 create(context) {
1363 spy = sinon.spy(() => {
1364 const scope = context.getScope();
1365 const a = getVariable(scope, "a"),
1366 b = getVariable(scope, "b"),
1367 c = getVariable(scope, "c"),
1368 d = getVariable(scope, "d"),
1369 e = getVariable(scope, "e"),
1370 f = getVariable(scope, "f"),
1371 mathGlobal = getVariable(scope, "Math"),
1372 arrayGlobal = getVariable(scope, "Array"),
1373 configGlobal = getVariable(scope, "ConfigGlobal");
1374
1375 assert.strictEqual(a.name, "a");
1376 assert.strictEqual(a.writeable, false);
1377 assert.strictEqual(b.name, "b");
1378 assert.strictEqual(b.writeable, true);
1379 assert.strictEqual(c.name, "c");
1380 assert.strictEqual(c.writeable, false);
1381 assert.strictEqual(d.name, "d");
1382 assert.strictEqual(d.writeable, false);
1383 assert.strictEqual(e.name, "e");
1384 assert.strictEqual(e.writeable, true);
1385 assert.strictEqual(f.name, "f");
1386 assert.strictEqual(f.writeable, true);
1387 assert.strictEqual(mathGlobal, null);
1388 assert.strictEqual(arrayGlobal, null);
1389 assert.strictEqual(configGlobal.name, "ConfigGlobal");
1390 assert.strictEqual(configGlobal.writeable, false);
1391 });
1392
1393 return { Program: spy };
1394 }
1395 });
1396
1397 linter.verify(code, config);
1398 assert(spy && spy.calledOnce);
1399 });
1400 });
1401
1402 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
1403 const code = "/* global a b : true c: false*/";
1404
1405 it("variables should be available in global scope", () => {
1406 const config = { rules: { checker: "error" } };
1407 let spy;
1408
1409 linter.defineRule("checker", {
1410 create(context) {
1411 spy = sinon.spy(() => {
1412 const scope = context.getScope(),
1413 a = getVariable(scope, "a"),
1414 b = getVariable(scope, "b"),
1415 c = getVariable(scope, "c");
1416
1417 assert.strictEqual(a.name, "a");
1418 assert.strictEqual(a.writeable, false);
1419 assert.strictEqual(b.name, "b");
1420 assert.strictEqual(b.writeable, true);
1421 assert.strictEqual(c.name, "c");
1422 assert.strictEqual(c.writeable, false);
1423 });
1424
1425 return { Program: spy };
1426 }
1427 });
1428
1429 linter.verify(code, config);
1430 assert(spy && spy.calledOnce);
1431 });
1432 });
1433
1434 describe("when evaluating code containing a /*global */ block with specific variables", () => {
1435 const code = "/* global toString hasOwnProperty valueOf: true */";
1436
1437 it("should not throw an error if comment block has global variables which are Object.prototype contains", () => {
1438 const config = { rules: { checker: "error" } };
1439
1440 linter.verify(code, config);
1441 });
1442 });
1443
1444 describe("when evaluating code containing /*eslint-env */ block", () => {
1445 it("variables should be available in global scope", () => {
1446 const code = `/*${ESLINT_ENV} node*/ function f() {} /*${ESLINT_ENV} browser, foo*/`;
1447 const config = { rules: { checker: "error" } };
1448 let spy;
1449
1450 linter.defineRule("checker", {
1451 create(context) {
1452 spy = sinon.spy(() => {
1453 const scope = context.getScope(),
1454 exports = getVariable(scope, "exports"),
1455 window = getVariable(scope, "window");
1456
1457 assert.strictEqual(exports.writeable, true);
1458 assert.strictEqual(window.writeable, false);
1459 });
1460
1461 return { Program: spy };
1462 }
1463 });
1464
1465 linter.verify(code, config);
1466 assert(spy && spy.calledOnce);
1467 });
1468 });
1469
1470 describe("when evaluating code containing /*eslint-env */ block with sloppy whitespace", () => {
1471 const code = `/* ${ESLINT_ENV} ,, node , no-browser ,, */`;
1472
1473 it("variables should be available in global scope", () => {
1474 const config = { rules: { checker: "error" } };
1475 let spy;
1476
1477 linter.defineRule("checker", {
1478 create(context) {
1479 spy = sinon.spy(() => {
1480 const scope = context.getScope(),
1481 exports = getVariable(scope, "exports"),
1482 window = getVariable(scope, "window");
1483
1484 assert.strictEqual(exports.writeable, true);
1485 assert.strictEqual(window, null);
1486 });
1487
1488 return { Program: spy };
1489 }
1490 });
1491
1492 linter.verify(code, config);
1493 assert(spy && spy.calledOnce);
1494 });
1495 });
1496
1497 describe("when evaluating code containing /*exported */ block", () => {
1498
1499 it("we should behave nicely when no matching variable is found", () => {
1500 const code = "/* exported horse */";
1501 const config = { rules: {} };
1502
1503 linter.verify(code, config, filename, true);
1504 });
1505
1506 it("variables should be exported", () => {
1507 const code = "/* exported horse */\n\nvar horse = 'circus'";
1508 const config = { rules: { checker: "error" } };
1509 let spy;
1510
1511 linter.defineRule("checker", {
1512 create(context) {
1513 spy = sinon.spy(() => {
1514 const scope = context.getScope(),
1515 horse = getVariable(scope, "horse");
1516
1517 assert.strictEqual(horse.eslintUsed, true);
1518 });
1519
1520 return { Program: spy };
1521 }
1522 });
1523
1524 linter.verify(code, config);
1525 assert(spy && spy.calledOnce);
1526 });
1527
1528 it("undefined variables should not be exported", () => {
1529 const code = "/* exported horse */\n\nhorse = 'circus'";
1530 const config = { rules: { checker: "error" } };
1531 let spy;
1532
1533 linter.defineRule("checker", {
1534 create(context) {
1535 spy = sinon.spy(() => {
1536 const scope = context.getScope(),
1537 horse = getVariable(scope, "horse");
1538
1539 assert.strictEqual(horse, null);
1540 });
1541
1542 return { Program: spy };
1543 }
1544 });
1545
1546 linter.verify(code, config);
1547 assert(spy && spy.calledOnce);
1548 });
1549
1550 it("variables should be exported in strict mode", () => {
1551 const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'";
1552 const config = { rules: { checker: "error" } };
1553 let spy;
1554
1555 linter.defineRule("checker", {
1556 create(context) {
1557 spy = sinon.spy(() => {
1558 const scope = context.getScope(),
1559 horse = getVariable(scope, "horse");
1560
1561 assert.strictEqual(horse.eslintUsed, true);
1562 });
1563
1564 return { Program: spy };
1565 }
1566 });
1567
1568 linter.verify(code, config);
1569 assert(spy && spy.calledOnce);
1570 });
1571
1572 it("variables should not be exported in the es6 module environment", () => {
1573 const code = "/* exported horse */\nvar horse = 'circus'";
1574 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } };
1575 let spy;
1576
1577 linter.defineRule("checker", {
1578 create(context) {
1579 spy = sinon.spy(() => {
1580 const scope = context.getScope(),
1581 horse = getVariable(scope, "horse");
1582
1583 assert.strictEqual(horse, null); // there is no global scope at all
1584 });
1585
1586 return { Program: spy };
1587 }
1588 });
1589
1590 linter.verify(code, config);
1591 assert(spy && spy.calledOnce);
1592 });
1593
1594 it("variables should not be exported when in the node environment", () => {
1595 const code = "/* exported horse */\nvar horse = 'circus'";
1596 const config = { rules: { checker: "error" }, env: { node: true } };
1597 let spy;
1598
1599 linter.defineRule("checker", {
1600 create(context) {
1601 spy = sinon.spy(() => {
1602 const scope = context.getScope(),
1603 horse = getVariable(scope, "horse");
1604
1605 assert.strictEqual(horse, null); // there is no global scope at all
1606 });
1607
1608 return { Program: spy };
1609 }
1610 });
1611
1612 linter.verify(code, config);
1613 assert(spy && spy.calledOnce);
1614 });
1615 });
1616
1617 describe("when evaluating code containing a line comment", () => {
1618 const code = "//global a \n function f() {}";
1619
1620 it("should not introduce a global variable", () => {
1621 const config = { rules: { checker: "error" } };
1622 let spy;
1623
1624 linter.defineRule("checker", {
1625 create(context) {
1626 spy = sinon.spy(() => {
1627 const scope = context.getScope();
1628
1629 assert.strictEqual(getVariable(scope, "a"), null);
1630 });
1631
1632 return { Program: spy };
1633 }
1634 });
1635
1636 linter.verify(code, config);
1637 assert(spy && spy.calledOnce);
1638 });
1639 });
1640
1641 describe("when evaluating code containing normal block comments", () => {
1642 const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
1643
1644 it("should not introduce a global variable", () => {
1645 const config = { rules: { checker: "error" } };
1646 let spy;
1647
1648 linter.defineRule("checker", {
1649 create(context) {
1650 spy = sinon.spy(() => {
1651 const scope = context.getScope();
1652
1653 assert.strictEqual(getVariable(scope, "a"), null);
1654 assert.strictEqual(getVariable(scope, "b"), null);
1655 assert.strictEqual(getVariable(scope, "foo"), null);
1656 assert.strictEqual(getVariable(scope, "c"), null);
1657 });
1658
1659 return { Program: spy };
1660 }
1661 });
1662
1663 linter.verify(code, config);
1664 assert(spy && spy.calledOnce);
1665 });
1666 });
1667
1668 describe("when evaluating any code", () => {
1669 const code = "x";
1670
1671 it("builtin global variables should be available in the global scope", () => {
1672 const config = { rules: { checker: "error" } };
1673 let spy;
1674
1675 linter.defineRule("checker", {
1676 create(context) {
1677 spy = sinon.spy(() => {
1678 const scope = context.getScope();
1679
1680 assert.notStrictEqual(getVariable(scope, "Object"), null);
1681 assert.notStrictEqual(getVariable(scope, "Array"), null);
1682 assert.notStrictEqual(getVariable(scope, "undefined"), null);
1683 });
1684
1685 return { Program: spy };
1686 }
1687 });
1688
1689 linter.verify(code, config);
1690 assert(spy && spy.calledOnce);
1691 });
1692
1693 it("ES6 global variables should not be available by default", () => {
1694 const config = { rules: { checker: "error" } };
1695 let spy;
1696
1697 linter.defineRule("checker", {
1698 create(context) {
1699 spy = sinon.spy(() => {
1700 const scope = context.getScope();
1701
1702 assert.strictEqual(getVariable(scope, "Promise"), null);
1703 assert.strictEqual(getVariable(scope, "Symbol"), null);
1704 assert.strictEqual(getVariable(scope, "WeakMap"), null);
1705 });
1706
1707 return { Program: spy };
1708 }
1709 });
1710
1711 linter.verify(code, config);
1712 assert(spy && spy.calledOnce);
1713 });
1714
1715 it("ES6 global variables should be available in the es6 environment", () => {
1716 const config = { rules: { checker: "error" }, env: { es6: true } };
1717 let spy;
1718
1719 linter.defineRule("checker", {
1720 create(context) {
1721 spy = sinon.spy(() => {
1722 const scope = context.getScope();
1723
1724 assert.notStrictEqual(getVariable(scope, "Promise"), null);
1725 assert.notStrictEqual(getVariable(scope, "Symbol"), null);
1726 assert.notStrictEqual(getVariable(scope, "WeakMap"), null);
1727 });
1728
1729 return { Program: spy };
1730 }
1731 });
1732
1733 linter.verify(code, config);
1734 assert(spy && spy.calledOnce);
1735 });
1736
1737 it("ES6 global variables can be disabled when the es6 environment is enabled", () => {
1738 const config = { rules: { checker: "error" }, globals: { Promise: "off", Symbol: "off", WeakMap: "off" }, env: { es6: true } };
1739 let spy;
1740
1741 linter.defineRule("checker", {
1742 create(context) {
1743 spy = sinon.spy(() => {
1744 const scope = context.getScope();
1745
1746 assert.strictEqual(getVariable(scope, "Promise"), null);
1747 assert.strictEqual(getVariable(scope, "Symbol"), null);
1748 assert.strictEqual(getVariable(scope, "WeakMap"), null);
1749 });
1750
1751 return { Program: spy };
1752 }
1753 });
1754
1755 linter.verify(code, config);
1756 assert(spy && spy.calledOnce);
1757 });
1758 });
1759
1760 describe("at any time", () => {
1761 const code = "new-rule";
1762
1763 it("can add a rule dynamically", () => {
1764 linter.defineRule(code, {
1765 create: context => ({
1766 Literal(node) {
1767 context.report(node, "message");
1768 }
1769 })
1770 });
1771
1772 const config = { rules: {} };
1773
1774 config.rules[code] = 1;
1775
1776 const messages = linter.verify("0", config, filename);
1777 const suppressedMessages = linter.getSuppressedMessages();
1778
1779 assert.strictEqual(messages.length, 1);
1780 assert.strictEqual(messages[0].ruleId, code);
1781 assert.strictEqual(messages[0].nodeType, "Literal");
1782
1783 assert.strictEqual(suppressedMessages.length, 0);
1784 });
1785 });
1786
1787 describe("at any time", () => {
1788 const code = ["new-rule-0", "new-rule-1"];
1789
1790 it("can add multiple rules dynamically", () => {
1791 const config = { rules: {} };
1792 const newRules = {};
1793
1794 code.forEach(item => {
1795 config.rules[item] = 1;
1796 newRules[item] = {
1797 create(context) {
1798 return {
1799 Literal(node) {
1800 context.report(node, "message");
1801 }
1802 };
1803 }
1804 };
1805 });
1806 linter.defineRules(newRules);
1807
1808 const messages = linter.verify("0", config, filename);
1809 const suppressedMessages = linter.getSuppressedMessages();
1810
1811 assert.strictEqual(messages.length, code.length);
1812 code.forEach(item => {
1813 assert.ok(messages.some(message => message.ruleId === item));
1814 });
1815 messages.forEach(message => {
1816 assert.strictEqual(message.nodeType, "Literal");
1817 });
1818
1819 assert.strictEqual(suppressedMessages.length, 0);
1820 });
1821 });
1822
1823 describe("at any time", () => {
1824 const code = "filename-rule";
1825
1826 it("has access to the filename", () => {
1827 linter.defineRule(code, {
1828 create: context => ({
1829 Literal(node) {
1830 assert.strictEqual(context.getFilename(), context.filename);
1831 context.report(node, context.filename);
1832 }
1833 })
1834 });
1835
1836 const config = { rules: {} };
1837
1838 config.rules[code] = 1;
1839
1840 const messages = linter.verify("0", config, filename);
1841 const suppressedMessages = linter.getSuppressedMessages();
1842
1843 assert.strictEqual(messages[0].message, filename);
1844 assert.strictEqual(suppressedMessages.length, 0);
1845 });
1846
1847 it("has access to the physicalFilename", () => {
1848 linter.defineRule(code, {
1849 create: context => ({
1850 Literal(node) {
1851 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
1852 context.report(node, context.physicalFilename);
1853 }
1854 })
1855 });
1856
1857 const config = { rules: {} };
1858
1859 config.rules[code] = 1;
1860
1861 const messages = linter.verify("0", config, filename);
1862 const suppressedMessages = linter.getSuppressedMessages();
1863
1864 assert.strictEqual(messages[0].message, filename);
1865 assert.strictEqual(suppressedMessages.length, 0);
1866 });
1867
1868 it("defaults filename to '<input>'", () => {
1869 linter.defineRule(code, {
1870 create: context => ({
1871 Literal(node) {
1872 assert.strictEqual(context.getFilename(), context.filename);
1873 context.report(node, context.filename);
1874 }
1875 })
1876 });
1877
1878 const config = { rules: {} };
1879
1880 config.rules[code] = 1;
1881
1882 const messages = linter.verify("0", config);
1883 const suppressedMessages = linter.getSuppressedMessages();
1884
1885 assert.strictEqual(messages[0].message, "<input>");
1886 assert.strictEqual(suppressedMessages.length, 0);
1887 });
1888 });
1889
1890 describe("when evaluating code with comments to enable rules", () => {
1891
1892 it("should report a violation", () => {
1893 const code = "/*eslint no-alert:1*/ alert('test');";
1894 const config = { rules: {} };
1895
1896 const messages = linter.verify(code, config, filename);
1897 const suppressedMessages = linter.getSuppressedMessages();
1898
1899 assert.strictEqual(messages.length, 1);
1900 assert.strictEqual(messages[0].ruleId, "no-alert");
1901 assert.strictEqual(messages[0].message, "Unexpected alert.");
1902 assert.include(messages[0].nodeType, "CallExpression");
1903
1904 assert.strictEqual(suppressedMessages.length, 0);
1905 });
1906
1907 it("rules should not change initial config", () => {
1908 const config = { rules: { strict: 2 } };
1909 const codeA = "/*eslint strict: 0*/ function bar() { return 2; }";
1910 const codeB = "function foo() { return 1; }";
1911 let messages = linter.verify(codeA, config, filename, false);
1912 let suppressedMessages = linter.getSuppressedMessages();
1913
1914 assert.strictEqual(messages.length, 0);
1915 assert.strictEqual(suppressedMessages.length, 0);
1916
1917 messages = linter.verify(codeB, config, filename, false);
1918 suppressedMessages = linter.getSuppressedMessages();
1919 assert.strictEqual(messages.length, 1);
1920
1921 assert.strictEqual(suppressedMessages.length, 0);
1922 });
1923
1924 it("rules should not change initial config", () => {
1925 const config = { rules: { quotes: [2, "double"] } };
1926 const codeA = "/*eslint quotes: 0*/ function bar() { return '2'; }";
1927 const codeB = "function foo() { return '1'; }";
1928 let messages = linter.verify(codeA, config, filename, false);
1929 let suppressedMessages = linter.getSuppressedMessages();
1930
1931 assert.strictEqual(messages.length, 0);
1932 assert.strictEqual(suppressedMessages.length, 0);
1933
1934 messages = linter.verify(codeB, config, filename, false);
1935 suppressedMessages = linter.getSuppressedMessages();
1936 assert.strictEqual(messages.length, 1);
1937
1938 assert.strictEqual(suppressedMessages.length, 0);
1939 });
1940
1941 it("rules should not change initial config", () => {
1942 const config = { rules: { quotes: [2, "double"] } };
1943 const codeA = "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
1944 const codeB = "function foo() { return '1'; }";
1945
1946 let messages = linter.verify(codeA, config, filename, false);
1947 let suppressedMessages = linter.getSuppressedMessages();
1948
1949 assert.strictEqual(messages.length, 0);
1950 assert.strictEqual(suppressedMessages.length, 0);
1951
1952 messages = linter.verify(codeB, config, filename, false);
1953 suppressedMessages = linter.getSuppressedMessages();
1954
1955 assert.strictEqual(messages.length, 1);
1956 assert.strictEqual(suppressedMessages.length, 0);
1957 });
1958
1959 it("rules should not change initial config", () => {
1960 const config = { rules: { "no-unused-vars": [2, { vars: "all" }] } };
1961 const codeA = "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
1962 const codeB = "var b = 55;";
1963
1964 let messages = linter.verify(codeA, config, filename, false);
1965 let suppressedMessages = linter.getSuppressedMessages();
1966
1967 assert.strictEqual(messages.length, 0);
1968 assert.strictEqual(suppressedMessages.length, 0);
1969
1970 messages = linter.verify(codeB, config, filename, false);
1971 suppressedMessages = linter.getSuppressedMessages();
1972
1973 assert.strictEqual(messages.length, 1);
1974 assert.strictEqual(suppressedMessages.length, 0);
1975 });
1976 });
1977
1978 describe("when evaluating code with invalid comments to enable rules", () => {
1979 it("should report a violation when the config is not a valid rule configuration", () => {
1980 const messages = linter.verify("/*eslint no-alert:true*/ alert('test');", {});
1981 const suppressedMessages = linter.getSuppressedMessages();
1982
1983 assert.deepStrictEqual(
1984 messages,
1985 [
1986 {
1987 severity: 2,
1988 ruleId: "no-alert",
1989 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",
1990 line: 1,
1991 column: 1,
1992 endLine: 1,
1993 endColumn: 25,
1994 nodeType: null
1995 }
1996 ]
1997 );
1998
1999 assert.strictEqual(suppressedMessages.length, 0);
2000 });
2001
2002 it("should report a violation when the config violates a rule's schema", () => {
2003 const messages = linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {});
2004 const suppressedMessages = linter.getSuppressedMessages();
2005
2006 assert.deepStrictEqual(
2007 messages,
2008 [
2009 {
2010 severity: 2,
2011 ruleId: "no-alert",
2012 message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
2013 line: 1,
2014 column: 1,
2015 endLine: 1,
2016 endColumn: 63,
2017 nodeType: null
2018 }
2019 ]
2020 );
2021
2022 assert.strictEqual(suppressedMessages.length, 0);
2023 });
2024 });
2025
2026 describe("when evaluating code with comments to disable rules", () => {
2027 const code = "/*eslint no-alert:0*/ alert('test');";
2028
2029 it("should not report a violation", () => {
2030 const config = { rules: { "no-alert": 1 } };
2031
2032 const messages = linter.verify(code, config, filename);
2033 const suppressedMessages = linter.getSuppressedMessages();
2034
2035 assert.strictEqual(messages.length, 0);
2036 assert.strictEqual(suppressedMessages.length, 0);
2037 });
2038 });
2039
2040 describe("when evaluating code with comments to disable rules", () => {
2041 let code, messages, suppressedMessages;
2042
2043 it("should report an error when disabling a non-existent rule in inline comment", () => {
2044 code = "/*eslint foo:0*/ ;";
2045 messages = linter.verify(code, {}, filename);
2046 suppressedMessages = linter.getSuppressedMessages();
2047 assert.strictEqual(messages.length, 1);
2048 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
2049 assert.strictEqual(suppressedMessages.length, 0);
2050
2051 code = "/*eslint-disable foo*/ ;";
2052 messages = linter.verify(code, {}, filename);
2053 suppressedMessages = linter.getSuppressedMessages();
2054 assert.strictEqual(messages.length, 1);
2055 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
2056 assert.strictEqual(suppressedMessages.length, 0);
2057
2058 code = "/*eslint-disable-line foo*/ ;";
2059 messages = linter.verify(code, {}, filename);
2060 suppressedMessages = linter.getSuppressedMessages();
2061 assert.strictEqual(messages.length, 1);
2062 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
2063 assert.strictEqual(suppressedMessages.length, 0);
2064
2065 code = "/*eslint-disable-next-line foo*/ ;";
2066 messages = linter.verify(code, {}, filename);
2067 suppressedMessages = linter.getSuppressedMessages();
2068 assert.strictEqual(messages.length, 1);
2069 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
2070 assert.strictEqual(suppressedMessages.length, 0);
2071 });
2072
2073 it("should not report an error, when disabling a non-existent rule in config", () => {
2074 messages = linter.verify("", { rules: { foo: 0 } }, filename);
2075 suppressedMessages = linter.getSuppressedMessages();
2076
2077 assert.strictEqual(messages.length, 0);
2078 assert.strictEqual(suppressedMessages.length, 0);
2079 });
2080
2081 it("should report an error, when config a non-existent rule in config", () => {
2082 messages = linter.verify("", { rules: { foo: 1 } }, filename);
2083 suppressedMessages = linter.getSuppressedMessages();
2084 assert.strictEqual(messages.length, 1);
2085 assert.strictEqual(messages[0].severity, 2);
2086 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
2087 assert.strictEqual(suppressedMessages.length, 0);
2088
2089 messages = linter.verify("", { rules: { foo: 2 } }, filename);
2090 suppressedMessages = linter.getSuppressedMessages();
2091 assert.strictEqual(messages.length, 1);
2092 assert.strictEqual(messages[0].severity, 2);
2093 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
2094 assert.strictEqual(suppressedMessages.length, 0);
2095 });
2096 });
2097
2098 describe("when evaluating code with comments to enable multiple rules", () => {
2099 const code = "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
2100
2101 it("should report a violation", () => {
2102 const config = { rules: {} };
2103
2104 const messages = linter.verify(code, config, filename);
2105 const suppressedMessages = linter.getSuppressedMessages();
2106
2107 assert.strictEqual(messages.length, 2);
2108 assert.strictEqual(messages[0].ruleId, "no-alert");
2109 assert.strictEqual(messages[0].message, "Unexpected alert.");
2110 assert.include(messages[0].nodeType, "CallExpression");
2111 assert.strictEqual(messages[1].ruleId, "no-console");
2112
2113 assert.strictEqual(suppressedMessages.length, 0);
2114 });
2115 });
2116
2117 describe("when evaluating code with comments to enable and disable multiple rules", () => {
2118 const code = "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
2119
2120 it("should report a violation", () => {
2121 const config = { rules: { "no-console": 1, "no-alert": 0 } };
2122
2123 const messages = linter.verify(code, config, filename);
2124 const suppressedMessages = linter.getSuppressedMessages();
2125
2126 assert.strictEqual(messages.length, 1);
2127 assert.strictEqual(messages[0].ruleId, "no-alert");
2128 assert.strictEqual(messages[0].message, "Unexpected alert.");
2129 assert.include(messages[0].nodeType, "CallExpression");
2130
2131 assert.strictEqual(suppressedMessages.length, 0);
2132 });
2133 });
2134
2135 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
2136
2137 beforeEach(() => {
2138 linter.defineRule("test-plugin/test-rule", {
2139 create(context) {
2140 return {
2141 Literal(node) {
2142 if (node.value === "trigger violation") {
2143 context.report(node, "Reporting violation.");
2144 }
2145 }
2146 };
2147 }
2148 });
2149 });
2150
2151 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
2152 const config = { rules: {} };
2153 const code = "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
2154
2155 const messages = linter.verify(code, config, filename);
2156 const suppressedMessages = linter.getSuppressedMessages();
2157
2158 assert.strictEqual(messages.length, 0);
2159 assert.strictEqual(suppressedMessages.length, 0);
2160 });
2161
2162 it("should not report a violation when inline comment disables plugin rule", () => {
2163 const code = "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
2164 const config = { rules: { "test-plugin/test-rule": 1 } };
2165
2166 const messages = linter.verify(code, config, filename);
2167 const suppressedMessages = linter.getSuppressedMessages();
2168
2169 assert.strictEqual(messages.length, 0);
2170 assert.strictEqual(suppressedMessages.length, 0);
2171 });
2172
2173 it("should report a violation when the report is right before the comment", () => {
2174 const code = " /* eslint-disable */ ";
2175
2176 linter.defineRule("checker", {
2177 create: context => ({
2178 Program() {
2179 context.report({ loc: { line: 1, column: 0 }, message: "foo" });
2180 }
2181 })
2182 });
2183 const problems = linter.verify(code, { rules: { checker: "error" } });
2184 const suppressedMessages = linter.getSuppressedMessages();
2185
2186 assert.strictEqual(problems.length, 1);
2187 assert.strictEqual(problems[0].message, "foo");
2188 assert.strictEqual(suppressedMessages.length, 0);
2189 });
2190
2191 it("should not report a violation when the report is right at the start of the comment", () => {
2192 const code = " /* eslint-disable */ ";
2193
2194 linter.defineRule("checker", {
2195 create: context => ({
2196 Program() {
2197 context.report({ loc: { line: 1, column: 1 }, message: "foo" });
2198 }
2199 })
2200 });
2201 const problems = linter.verify(code, { rules: { checker: "error" } });
2202 const suppressedMessages = linter.getSuppressedMessages();
2203
2204 assert.strictEqual(problems.length, 0);
2205
2206 assert.strictEqual(suppressedMessages.length, 1);
2207 assert.strictEqual(suppressedMessages[0].message, "foo");
2208 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
2209 assert.strictEqual(suppressedMessages[0].suppressions[0].justification, "");
2210 });
2211
2212 it("rules should not change initial config", () => {
2213 const config = { rules: { "test-plugin/test-rule": 2 } };
2214 const codeA = "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
2215 const codeB = "var a = \"trigger violation\";";
2216
2217 let messages = linter.verify(codeA, config, filename, false);
2218 let suppressedMessages = linter.getSuppressedMessages();
2219
2220 assert.strictEqual(messages.length, 0);
2221 assert.strictEqual(suppressedMessages.length, 0);
2222
2223 messages = linter.verify(codeB, config, filename, false);
2224 suppressedMessages = linter.getSuppressedMessages();
2225
2226 assert.strictEqual(messages.length, 1);
2227 assert.strictEqual(suppressedMessages.length, 0);
2228 });
2229 });
2230
2231 describe("when evaluating code with comments to enable and disable all reporting", () => {
2232 it("should report a violation", () => {
2233
2234 const code = [
2235 "/*eslint-disable */",
2236 "alert('test');",
2237 "/*eslint-enable */",
2238 "alert('test');"
2239 ].join("\n");
2240 const config = { rules: { "no-alert": 1 } };
2241
2242 const messages = linter.verify(code, config, filename);
2243 const suppressedMessages = linter.getSuppressedMessages();
2244
2245 assert.strictEqual(messages.length, 1);
2246 assert.strictEqual(messages[0].ruleId, "no-alert");
2247 assert.strictEqual(messages[0].message, "Unexpected alert.");
2248 assert.include(messages[0].nodeType, "CallExpression");
2249 assert.strictEqual(messages[0].line, 4);
2250
2251 assert.strictEqual(suppressedMessages.length, 1);
2252 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2253 assert.strictEqual(suppressedMessages[0].message, "Unexpected alert.");
2254 assert.strictEqual(suppressedMessages[0].line, 2);
2255 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
2256 assert.strictEqual(suppressedMessages[0].suppressions[0].justification, "");
2257 });
2258
2259 it("should not report a violation", () => {
2260 const code = [
2261 "/*eslint-disable */",
2262 "alert('test');",
2263 "alert('test');"
2264 ].join("\n");
2265 const config = { rules: { "no-alert": 1 } };
2266
2267 const messages = linter.verify(code, config, filename);
2268 const suppressedMessages = linter.getSuppressedMessages();
2269
2270 assert.strictEqual(messages.length, 0);
2271
2272 assert.strictEqual(suppressedMessages.length, 2);
2273 assert.strictEqual(suppressedMessages[0].line, 2);
2274 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
2275 assert.strictEqual(suppressedMessages[1].line, 3);
2276 assert.strictEqual(suppressedMessages[1].suppressions.length, 1);
2277 });
2278
2279 it("should not report a violation", () => {
2280 const code = [
2281 " alert('test1');/*eslint-disable */\n",
2282 "alert('test');",
2283 " alert('test');\n",
2284 "/*eslint-enable */alert('test2');"
2285 ].join("");
2286 const config = { rules: { "no-alert": 1 } };
2287
2288 const messages = linter.verify(code, config, filename);
2289 const suppressedMessages = linter.getSuppressedMessages();
2290
2291 assert.strictEqual(messages.length, 2);
2292 assert.strictEqual(messages[0].column, 21);
2293 assert.strictEqual(messages[1].column, 19);
2294
2295 assert.strictEqual(suppressedMessages.length, 2);
2296 assert.strictEqual(suppressedMessages[0].column, 1);
2297 assert.strictEqual(suppressedMessages[1].column, 56);
2298 });
2299
2300 it("should report a violation", () => {
2301
2302 const code = [
2303 "/*eslint-disable */",
2304 "alert('test');",
2305 "/*eslint-disable */",
2306 "alert('test');",
2307 "/*eslint-enable*/",
2308 "alert('test');",
2309 "/*eslint-enable*/"
2310 ].join("\n");
2311
2312 const config = { rules: { "no-alert": 1 } };
2313
2314 const messages = linter.verify(code, config, filename);
2315 const suppressedMessages = linter.getSuppressedMessages();
2316
2317 assert.strictEqual(messages.length, 1);
2318 assert.strictEqual(suppressedMessages.length, 2);
2319 });
2320
2321
2322 it("should not report a violation", () => {
2323 const code = [
2324 "/*eslint-disable */",
2325 "(function(){ var b = 44;})()",
2326 "/*eslint-enable */;any();"
2327 ].join("\n");
2328
2329 const config = { rules: { "no-unused-vars": 1 } };
2330
2331 const messages = linter.verify(code, config, filename);
2332 const suppressedMessages = linter.getSuppressedMessages();
2333
2334 assert.strictEqual(messages.length, 0);
2335 assert.strictEqual(suppressedMessages.length, 1);
2336 });
2337
2338 it("should not report a violation", () => {
2339 const code = [
2340 "(function(){ /*eslint-disable */ var b = 44;})()",
2341 "/*eslint-enable */;any();"
2342 ].join("\n");
2343
2344 const config = { rules: { "no-unused-vars": 1 } };
2345
2346 const messages = linter.verify(code, config, filename);
2347 const suppressedMessages = linter.getSuppressedMessages();
2348
2349 assert.strictEqual(messages.length, 0);
2350 assert.strictEqual(suppressedMessages.length, 1);
2351 });
2352 });
2353
2354 describe("when evaluating code with comments to ignore reporting on specific rules on a specific line", () => {
2355
2356 describe("eslint-disable-line", () => {
2357 it("should report a violation", () => {
2358 const code = [
2359 "alert('test'); // eslint-disable-line no-alert",
2360 "console.log('test');" // here
2361 ].join("\n");
2362 const config = {
2363 rules: {
2364 "no-alert": 1,
2365 "no-console": 1
2366 }
2367 };
2368
2369 const messages = linter.verify(code, config, filename);
2370 const suppressedMessages = linter.getSuppressedMessages();
2371
2372 assert.strictEqual(messages.length, 1);
2373 assert.strictEqual(messages[0].ruleId, "no-console");
2374
2375 assert.strictEqual(suppressedMessages.length, 1);
2376 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2377 });
2378
2379 it("should report a violation", () => {
2380 const code = [
2381 "alert('test'); // eslint-disable-line no-alert",
2382 "console.log('test'); // eslint-disable-line no-console",
2383 "alert('test');" // here
2384 ].join("\n");
2385 const config = {
2386 rules: {
2387 "no-alert": 1,
2388 "no-console": 1
2389 }
2390 };
2391
2392 const messages = linter.verify(code, config, filename);
2393 const suppressedMessages = linter.getSuppressedMessages();
2394
2395 assert.strictEqual(messages.length, 1);
2396 assert.strictEqual(messages[0].ruleId, "no-alert");
2397
2398 assert.strictEqual(suppressedMessages.length, 2);
2399 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2400 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
2401 });
2402
2403 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
2404 const code = [
2405 "/* eslint-disable-line",
2406 "*",
2407 "*/ console.log('test');" // here
2408 ].join("\n");
2409 const config = {
2410 rules: {
2411 "no-console": 1
2412 }
2413 };
2414
2415 const messages = linter.verify(code, config, filename);
2416 const suppressedMessages = linter.getSuppressedMessages();
2417
2418 assert.strictEqual(messages.length, 2);
2419 assert.strictEqual(messages[1].ruleId, "no-console");
2420 assert.strictEqual(suppressedMessages.length, 0);
2421 });
2422
2423 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
2424 const code = [
2425 "alert('test'); /* eslint-disable-line ",
2426 "no-alert */"
2427 ].join("\n");
2428 const config = {
2429 rules: {
2430 "no-alert": 1
2431 }
2432 };
2433
2434 const messages = linter.verify(code, config);
2435 const suppressedMessages = linter.getSuppressedMessages();
2436
2437 assert.deepStrictEqual(messages, [
2438 {
2439 ruleId: "no-alert",
2440 severity: 1,
2441 line: 1,
2442 column: 1,
2443 endLine: 1,
2444 endColumn: 14,
2445 message: "Unexpected alert.",
2446 messageId: "unexpected",
2447 nodeType: "CallExpression"
2448 },
2449 {
2450 ruleId: null,
2451 severity: 2,
2452 message: "eslint-disable-line comment should not span multiple lines.",
2453 line: 1,
2454 column: 16,
2455 endLine: 2,
2456 endColumn: 12,
2457 nodeType: null
2458 }
2459 ]);
2460
2461 assert.strictEqual(suppressedMessages.length, 0);
2462 });
2463
2464 it("should not report a violation for eslint-disable-line in block comment", () => {
2465 const code = [
2466 "alert('test'); // eslint-disable-line no-alert",
2467 "alert('test'); /*eslint-disable-line no-alert*/"
2468 ].join("\n");
2469 const config = {
2470 rules: {
2471 "no-alert": 1
2472 }
2473 };
2474
2475 const messages = linter.verify(code, config, filename);
2476 const suppressedMessages = linter.getSuppressedMessages();
2477
2478 assert.strictEqual(messages.length, 0);
2479
2480 assert.strictEqual(suppressedMessages.length, 2);
2481 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2482 assert.strictEqual(suppressedMessages[1].ruleId, "no-alert");
2483 });
2484
2485 it("should not report a violation", () => {
2486 const code = [
2487 "alert('test'); // eslint-disable-line no-alert",
2488 "console.log('test'); // eslint-disable-line no-console"
2489 ].join("\n");
2490 const config = {
2491 rules: {
2492 "no-alert": 1,
2493 "no-console": 1
2494 }
2495 };
2496
2497 const messages = linter.verify(code, config, filename);
2498 const suppressedMessages = linter.getSuppressedMessages();
2499
2500 assert.strictEqual(messages.length, 0);
2501
2502 assert.strictEqual(suppressedMessages.length, 2);
2503 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2504 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
2505 });
2506
2507 it("should not report a violation", () => {
2508 const code = [
2509 "alert('test') // eslint-disable-line no-alert, quotes, semi",
2510 "console.log('test'); // eslint-disable-line"
2511 ].join("\n");
2512 const config = {
2513 rules: {
2514 "no-alert": 1,
2515 quotes: [1, "double"],
2516 semi: [1, "always"],
2517 "no-console": 1
2518 }
2519 };
2520
2521 const messages = linter.verify(code, config, filename);
2522 const suppressedMessages = linter.getSuppressedMessages();
2523
2524 assert.strictEqual(messages.length, 0);
2525 assert.strictEqual(suppressedMessages.length, 5);
2526 });
2527
2528 it("should not report a violation", () => {
2529 const code = [
2530 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
2531 "console.log('test'); /* eslint-disable-line */"
2532 ].join("\n");
2533 const config = {
2534 rules: {
2535 "no-alert": 1,
2536 quotes: [1, "double"],
2537 semi: [1, "always"],
2538 "no-console": 1
2539 }
2540 };
2541
2542 const messages = linter.verify(code, config, filename);
2543 const suppressedMessages = linter.getSuppressedMessages();
2544
2545 assert.strictEqual(messages.length, 0);
2546 assert.strictEqual(suppressedMessages.length, 5);
2547 });
2548
2549 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2550 const code = [
2551 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
2552 ].join("\n");
2553 const config = {
2554 rules: {
2555 "no-alert": 1,
2556 quotes: [1, "single"]
2557 }
2558 };
2559 const messages = linter.verify(code, config, filename);
2560 const suppressedMessages = linter.getSuppressedMessages();
2561
2562 assert.strictEqual(messages.length, 0);
2563
2564 assert.strictEqual(suppressedMessages.length, 2);
2565 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2566 });
2567 });
2568
2569 describe("eslint-disable-next-line", () => {
2570 it("should ignore violation of specified rule on next line", () => {
2571 const code = [
2572 "// eslint-disable-next-line no-alert",
2573 "alert('test');",
2574 "console.log('test');"
2575 ].join("\n");
2576 const config = {
2577 rules: {
2578 "no-alert": 1,
2579 "no-console": 1
2580 }
2581 };
2582 const messages = linter.verify(code, config, filename);
2583 const suppressedMessages = linter.getSuppressedMessages();
2584
2585 assert.strictEqual(messages.length, 1);
2586 assert.strictEqual(messages[0].ruleId, "no-console");
2587
2588 assert.strictEqual(suppressedMessages.length, 1);
2589 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2590 });
2591
2592 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2593 const code = [
2594 "/* eslint-disable-next-line no-alert */",
2595 "alert('test');",
2596 "console.log('test');"
2597 ].join("\n");
2598 const config = {
2599 rules: {
2600 "no-alert": 1,
2601 "no-console": 1
2602 }
2603 };
2604 const messages = linter.verify(code, config, filename);
2605 const suppressedMessages = linter.getSuppressedMessages();
2606
2607 assert.strictEqual(messages.length, 1);
2608 assert.strictEqual(messages[0].ruleId, "no-console");
2609
2610 assert.strictEqual(suppressedMessages.length, 1);
2611 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2612 });
2613 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2614 const code = [
2615 "/* eslint-disable-next-line no-alert */",
2616 "alert('test');"
2617 ].join("\n");
2618 const config = {
2619 rules: {
2620 "no-alert": 1
2621 }
2622 };
2623 const messages = linter.verify(code, config, filename);
2624 const suppressedMessages = linter.getSuppressedMessages();
2625
2626 assert.strictEqual(messages.length, 0);
2627
2628 assert.strictEqual(suppressedMessages.length, 1);
2629 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2630 });
2631
2632 it("should not ignore violation if code is not on next line", () => {
2633 const code = [
2634 "/* eslint-disable-next-line",
2635 "no-alert */alert('test');"
2636 ].join("\n");
2637 const config = {
2638 rules: {
2639 "no-alert": 1
2640 }
2641 };
2642 const messages = linter.verify(code, config, filename);
2643 const suppressedMessages = linter.getSuppressedMessages();
2644
2645 assert.strictEqual(messages.length, 1);
2646 assert.strictEqual(messages[0].ruleId, "no-alert");
2647
2648 assert.strictEqual(suppressedMessages.length, 0);
2649 });
2650
2651 it("should ignore violation if block comment span multiple lines", () => {
2652 const code = [
2653 "/* eslint-disable-next-line",
2654 "no-alert */",
2655 "alert('test');"
2656 ].join("\n");
2657 const config = {
2658 rules: {
2659 "no-alert": 1
2660 }
2661 };
2662 const messages = linter.verify(code, config, filename);
2663 const suppressedMessages = linter.getSuppressedMessages();
2664
2665 assert.strictEqual(messages.length, 0);
2666
2667 assert.strictEqual(suppressedMessages.length, 1);
2668 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2669 });
2670
2671 it("should ignore violations only of specified rule", () => {
2672 const code = [
2673 "// eslint-disable-next-line no-console",
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, 0);
2691 });
2692
2693 it("should ignore violations of multiple rules when specified", () => {
2694 const code = [
2695 "// eslint-disable-next-line no-alert, quotes",
2696 "alert(\"test\");",
2697 "console.log('test');"
2698 ].join("\n");
2699 const config = {
2700 rules: {
2701 "no-alert": 1,
2702 quotes: [1, "single"],
2703 "no-console": 1
2704 }
2705 };
2706 const messages = linter.verify(code, config, filename);
2707 const suppressedMessages = linter.getSuppressedMessages();
2708
2709 assert.strictEqual(messages.length, 1);
2710 assert.strictEqual(messages[0].ruleId, "no-console");
2711
2712 assert.strictEqual(suppressedMessages.length, 2);
2713 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2714 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
2715 });
2716
2717 it("should ignore violations of multiple rules when specified in multiple lines", () => {
2718 const code = [
2719 "/* eslint-disable-next-line",
2720 "no-alert,",
2721 "quotes",
2722 "*/",
2723 "alert(\"test\");",
2724 "console.log('test');"
2725 ].join("\n");
2726 const config = {
2727 rules: {
2728 "no-alert": 1,
2729 quotes: [1, "single"],
2730 "no-console": 1
2731 }
2732 };
2733 const messages = linter.verify(code, config, filename);
2734
2735 assert.strictEqual(messages.length, 1);
2736 assert.strictEqual(messages[0].ruleId, "no-console");
2737 });
2738
2739 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2740 const code = [
2741 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
2742 "alert(\"test\");"
2743 ].join("\n");
2744 const config = {
2745 rules: {
2746 "no-alert": 1,
2747 quotes: [1, "single"]
2748 }
2749 };
2750 const messages = linter.verify(code, config, filename);
2751 const suppressedMessages = linter.getSuppressedMessages();
2752
2753 assert.strictEqual(messages.length, 0);
2754
2755 assert.strictEqual(suppressedMessages.length, 2);
2756 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2757 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
2758 });
2759
2760 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
2761 const code = [
2762 "/* eslint-disable-next-line",
2763 "no-alert",
2764 "*/ // eslint-disable-next-line quotes",
2765 "alert(\"test\");"
2766 ].join("\n");
2767 const config = {
2768 rules: {
2769 "no-alert": 1,
2770 quotes: [1, "single"]
2771 }
2772 };
2773 const messages = linter.verify(code, config, filename);
2774
2775 assert.strictEqual(messages.length, 0);
2776 });
2777
2778 it("should ignore violations of only the specified rule on next line", () => {
2779 const code = [
2780 "// eslint-disable-next-line quotes",
2781 "alert(\"test\");",
2782 "console.log('test');"
2783 ].join("\n");
2784 const config = {
2785 rules: {
2786 "no-alert": 1,
2787 quotes: [1, "single"],
2788 "no-console": 1
2789 }
2790 };
2791 const messages = linter.verify(code, config, filename);
2792 const suppressedMessages = linter.getSuppressedMessages();
2793
2794 assert.strictEqual(messages.length, 2);
2795 assert.strictEqual(messages[0].ruleId, "no-alert");
2796 assert.strictEqual(messages[1].ruleId, "no-console");
2797
2798 assert.strictEqual(suppressedMessages.length, 1);
2799 assert.strictEqual(suppressedMessages[0].ruleId, "quotes");
2800 });
2801
2802 it("should ignore violations of specified rule on next line only", () => {
2803 const code = [
2804 "alert('test');",
2805 "// eslint-disable-next-line no-alert",
2806 "alert('test');",
2807 "console.log('test');"
2808 ].join("\n");
2809 const config = {
2810 rules: {
2811 "no-alert": 1,
2812 "no-console": 1
2813 }
2814 };
2815 const messages = linter.verify(code, config, filename);
2816 const suppressedMessages = linter.getSuppressedMessages();
2817
2818 assert.strictEqual(messages.length, 2);
2819 assert.strictEqual(messages[0].ruleId, "no-alert");
2820 assert.strictEqual(messages[1].ruleId, "no-console");
2821
2822 assert.strictEqual(suppressedMessages.length, 1);
2823 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2824 });
2825
2826 it("should ignore all rule violations on next line if none specified", () => {
2827 const code = [
2828 "// eslint-disable-next-line",
2829 "alert(\"test\");",
2830 "console.log('test')"
2831 ].join("\n");
2832 const config = {
2833 rules: {
2834 semi: [1, "never"],
2835 quotes: [1, "single"],
2836 "no-alert": 1,
2837 "no-console": 1
2838 }
2839 };
2840 const messages = linter.verify(code, config, filename);
2841 const suppressedMessages = linter.getSuppressedMessages();
2842
2843 assert.strictEqual(messages.length, 1);
2844 assert.strictEqual(messages[0].ruleId, "no-console");
2845
2846 assert.strictEqual(suppressedMessages.length, 3);
2847 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2848 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
2849 assert.strictEqual(suppressedMessages[2].ruleId, "semi");
2850 });
2851
2852 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
2853 const code = [
2854 "alert('test');",
2855 "/* eslint-disable-next-line no-alert */",
2856 "alert('test');",
2857 "console.log('test');"
2858 ].join("\n");
2859 const config = {
2860 rules: {
2861 "no-alert": 1,
2862 "no-console": 1
2863 }
2864 };
2865 const messages = linter.verify(code, config, filename);
2866 const suppressedMessages = linter.getSuppressedMessages();
2867
2868 assert.strictEqual(messages.length, 2);
2869 assert.strictEqual(messages[0].ruleId, "no-alert");
2870 assert.strictEqual(messages[1].ruleId, "no-console");
2871
2872 assert.strictEqual(suppressedMessages.length, 1);
2873 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2874 });
2875
2876 it("should report a violation", () => {
2877 const code = [
2878 "/* eslint-disable-next-line",
2879 "*",
2880 "*/",
2881 "console.log('test');" // here
2882 ].join("\n");
2883 const config = {
2884 rules: {
2885 "no-alert": 1,
2886 "no-console": 1
2887 }
2888 };
2889
2890 const messages = linter.verify(code, config, filename);
2891 const suppressedMessages = linter.getSuppressedMessages();
2892
2893 assert.strictEqual(messages.length, 2);
2894 assert.strictEqual(messages[1].ruleId, "no-console");
2895
2896 assert.strictEqual(suppressedMessages.length, 0);
2897 });
2898
2899 it("should not ignore violations if comment is of the type hashbang", () => {
2900 const code = [
2901 "#! eslint-disable-next-line no-alert",
2902 "alert('test');",
2903 "console.log('test');"
2904 ].join("\n");
2905 const config = {
2906 rules: {
2907 "no-alert": 1,
2908 "no-console": 1
2909 }
2910 };
2911 const messages = linter.verify(code, config, filename);
2912 const suppressedMessages = linter.getSuppressedMessages();
2913
2914 assert.strictEqual(messages.length, 2);
2915 assert.strictEqual(messages[0].ruleId, "no-alert");
2916 assert.strictEqual(messages[1].ruleId, "no-console");
2917
2918 assert.strictEqual(suppressedMessages.length, 0);
2919 });
2920 });
2921 });
2922
2923 describe("when evaluating code with comments to enable and disable reporting of specific rules", () => {
2924
2925 it("should report a violation", () => {
2926 const code = [
2927 "/*eslint-disable no-alert */",
2928 "alert('test');",
2929 "console.log('test');" // here
2930 ].join("\n");
2931 const config = { rules: { "no-alert": 1, "no-console": 1 } };
2932
2933 const messages = linter.verify(code, config, filename);
2934 const suppressedMessages = linter.getSuppressedMessages();
2935
2936 assert.strictEqual(messages.length, 1);
2937 assert.strictEqual(messages[0].ruleId, "no-console");
2938
2939 assert.strictEqual(suppressedMessages.length, 1);
2940 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
2941 });
2942
2943 it("should report no violation", () => {
2944 const code = [
2945 "/*eslint-disable no-unused-vars */",
2946 "var foo; // eslint-disable-line no-unused-vars",
2947 "var bar;",
2948 "/* eslint-enable no-unused-vars */" // here
2949 ].join("\n");
2950 const config = { rules: { "no-unused-vars": 2 } };
2951
2952 const messages = linter.verify(code, config, filename);
2953 const suppressedMessages = linter.getSuppressedMessages();
2954
2955 assert.strictEqual(messages.length, 0);
2956
2957 assert.strictEqual(suppressedMessages.length, 2);
2958 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
2959 assert.strictEqual(suppressedMessages[0].line, 2);
2960 assert.strictEqual(suppressedMessages[1].ruleId, "no-unused-vars");
2961 assert.strictEqual(suppressedMessages[1].line, 3);
2962 });
2963
2964 it("should report no violation", () => {
2965 const code = [
2966 "var foo1; // eslint-disable-line no-unused-vars",
2967 "var foo2; // eslint-disable-line no-unused-vars",
2968 "var foo3; // eslint-disable-line no-unused-vars",
2969 "var foo4; // eslint-disable-line no-unused-vars",
2970 "var foo5; // eslint-disable-line no-unused-vars"
2971 ].join("\n");
2972 const config = { rules: { "no-unused-vars": 2 } };
2973
2974 const messages = linter.verify(code, config, filename);
2975 const suppressedMessages = linter.getSuppressedMessages();
2976
2977 assert.strictEqual(messages.length, 0);
2978 assert.strictEqual(suppressedMessages.length, 5);
2979 });
2980
2981 it("should report no violation", () => {
2982 const code = [
2983 "/* eslint-disable quotes */",
2984 "console.log(\"foo\");",
2985 "/* eslint-enable quotes */"
2986 ].join("\n");
2987 const config = { rules: { quotes: 2 } };
2988
2989 const messages = linter.verify(code, config, filename);
2990 const suppressedMessages = linter.getSuppressedMessages();
2991
2992 assert.strictEqual(messages.length, 0);
2993 assert.strictEqual(suppressedMessages.length, 0);
2994 });
2995
2996 it("should report a violation", () => {
2997 const code = [
2998 "/*eslint-disable no-alert, no-console */",
2999 "alert('test');",
3000 "console.log('test');",
3001 "/*eslint-enable*/",
3002
3003 "alert('test');", // here
3004 "console.log('test');" // here
3005 ].join("\n");
3006 const config = { rules: { "no-alert": 1, "no-console": 1 } };
3007
3008 const messages = linter.verify(code, config, filename);
3009 const suppressedMessages = linter.getSuppressedMessages();
3010
3011 assert.strictEqual(messages.length, 2);
3012 assert.strictEqual(messages[0].ruleId, "no-alert");
3013 assert.strictEqual(messages[0].line, 5);
3014 assert.strictEqual(messages[1].ruleId, "no-console");
3015 assert.strictEqual(messages[1].line, 6);
3016
3017 assert.strictEqual(suppressedMessages.length, 2);
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 });
3023
3024 it("should report a violation", () => {
3025 const code = [
3026 "/*eslint-disable no-alert */",
3027 "alert('test');",
3028 "console.log('test');",
3029 "/*eslint-enable no-console */",
3030
3031 "alert('test');" // here
3032 ].join("\n");
3033 const config = { rules: { "no-alert": 1, "no-console": 1 } };
3034
3035 const messages = linter.verify(code, config, filename);
3036 const suppressedMessages = linter.getSuppressedMessages();
3037
3038 assert.strictEqual(messages.length, 1);
3039 assert.strictEqual(messages[0].ruleId, "no-console");
3040
3041 assert.strictEqual(suppressedMessages.length, 2);
3042 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
3043 assert.strictEqual(suppressedMessages[0].line, 2);
3044 assert.strictEqual(suppressedMessages[1].ruleId, "no-alert");
3045 assert.strictEqual(suppressedMessages[1].line, 5);
3046 });
3047
3048
3049 it("should report a violation", () => {
3050 const code = [
3051 "/*eslint-disable no-alert, no-console */",
3052 "alert('test');",
3053 "console.log('test');",
3054 "/*eslint-enable no-alert*/",
3055
3056 "alert('test');", // here
3057 "console.log('test');"
3058 ].join("\n");
3059 const config = { rules: { "no-alert": 1, "no-console": 1 } };
3060
3061 const messages = linter.verify(code, config, filename);
3062 const suppressedMessages = linter.getSuppressedMessages();
3063
3064 assert.strictEqual(messages.length, 1);
3065 assert.strictEqual(messages[0].ruleId, "no-alert");
3066 assert.strictEqual(messages[0].line, 5);
3067
3068 assert.strictEqual(suppressedMessages.length, 3);
3069 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
3070 assert.strictEqual(suppressedMessages[0].line, 2);
3071 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
3072 assert.strictEqual(suppressedMessages[1].line, 3);
3073 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
3074 assert.strictEqual(suppressedMessages[2].line, 6);
3075 });
3076
3077
3078 it("should report a violation", () => {
3079 const code = [
3080 "/*eslint-disable no-alert */",
3081
3082 "/*eslint-disable no-console */",
3083 "alert('test');",
3084 "console.log('test');",
3085 "/*eslint-enable */",
3086
3087 "alert('test');", // here
3088 "console.log('test');", // here
3089
3090 "/*eslint-enable */",
3091
3092 "alert('test');", // here
3093 "console.log('test');", // here
3094
3095 "/*eslint-enable*/"
3096 ].join("\n");
3097 const config = { rules: { "no-alert": 1, "no-console": 1 } };
3098
3099 const messages = linter.verify(code, config, filename);
3100 const suppressedMessages = linter.getSuppressedMessages();
3101
3102 assert.strictEqual(messages.length, 4);
3103 assert.strictEqual(messages[0].ruleId, "no-alert");
3104 assert.strictEqual(messages[0].line, 6);
3105 assert.strictEqual(messages[1].ruleId, "no-console");
3106 assert.strictEqual(messages[1].line, 7);
3107 assert.strictEqual(messages[2].ruleId, "no-alert");
3108 assert.strictEqual(messages[2].line, 9);
3109 assert.strictEqual(messages[3].ruleId, "no-console");
3110 assert.strictEqual(messages[3].line, 10);
3111
3112 assert.strictEqual(suppressedMessages.length, 2);
3113 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
3114 assert.strictEqual(suppressedMessages[0].line, 3);
3115 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
3116 assert.strictEqual(suppressedMessages[1].line, 4);
3117 });
3118
3119 it("should report a violation", () => {
3120 const code = [
3121 "/*eslint-disable no-alert, no-console */",
3122 "alert('test');",
3123 "console.log('test');",
3124
3125 "/*eslint-enable no-alert */",
3126
3127 "alert('test');", // here
3128 "console.log('test');",
3129
3130 "/*eslint-enable no-console */",
3131
3132 "alert('test');", // here
3133 "console.log('test');", // here
3134 "/*eslint-enable no-console */"
3135 ].join("\n");
3136 const config = { rules: { "no-alert": 1, "no-console": 1 } };
3137
3138 const messages = linter.verify(code, config, filename);
3139 const suppressedMessages = linter.getSuppressedMessages();
3140
3141 assert.strictEqual(messages.length, 3);
3142 assert.strictEqual(messages[0].ruleId, "no-alert");
3143 assert.strictEqual(messages[0].line, 5);
3144 assert.strictEqual(messages[1].ruleId, "no-alert");
3145 assert.strictEqual(messages[1].line, 8);
3146 assert.strictEqual(messages[2].ruleId, "no-console");
3147 assert.strictEqual(messages[2].line, 9);
3148
3149 assert.strictEqual(suppressedMessages.length, 3);
3150 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
3151 assert.strictEqual(suppressedMessages[0].line, 2);
3152 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
3153 assert.strictEqual(suppressedMessages[1].line, 3);
3154 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
3155 assert.strictEqual(suppressedMessages[2].line, 6);
3156 });
3157
3158 it("should report a violation when severity is warn", () => {
3159 const code = [
3160 "/*eslint-disable no-alert, no-console */",
3161 "alert('test');",
3162 "console.log('test');",
3163
3164 "/*eslint-enable no-alert */",
3165
3166 "alert('test');", // here
3167 "console.log('test');",
3168
3169 "/*eslint-enable no-console */",
3170
3171 "alert('test');", // here
3172 "console.log('test');", // here
3173 "/*eslint-enable no-console */"
3174 ].join("\n");
3175 const config = { rules: { "no-alert": "warn", "no-console": "warn" } };
3176
3177 const messages = linter.verify(code, config, filename);
3178 const suppressedMessages = linter.getSuppressedMessages();
3179
3180 assert.strictEqual(messages.length, 3);
3181 assert.strictEqual(messages[0].ruleId, "no-alert");
3182 assert.strictEqual(messages[0].line, 5);
3183 assert.strictEqual(messages[1].ruleId, "no-alert");
3184 assert.strictEqual(messages[1].line, 8);
3185 assert.strictEqual(messages[2].ruleId, "no-console");
3186 assert.strictEqual(messages[2].line, 9);
3187
3188 assert.strictEqual(suppressedMessages.length, 3);
3189 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
3190 assert.strictEqual(suppressedMessages[0].line, 2);
3191 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
3192 assert.strictEqual(suppressedMessages[1].line, 3);
3193 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
3194 assert.strictEqual(suppressedMessages[2].line, 6);
3195 });
3196 });
3197
3198 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
3199 const code = "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
3200
3201 it("should report a violation", () => {
3202 const config = { rules: { "no-console": 1, "no-alert": 0 } };
3203
3204 const messages = linter.verify(code, config, filename);
3205 const suppressedMessages = linter.getSuppressedMessages();
3206
3207 assert.strictEqual(messages.length, 1);
3208 assert.strictEqual(messages[0].ruleId, "no-alert");
3209 assert.strictEqual(messages[0].message, "Unexpected alert.");
3210 assert.include(messages[0].nodeType, "CallExpression");
3211
3212 assert.strictEqual(suppressedMessages.length, 0);
3213 });
3214 });
3215
3216 describe("when evaluating code with comments to enable configurable rule", () => {
3217 const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');";
3218
3219 it("should report a violation", () => {
3220 const config = { rules: { quotes: [2, "single"] } };
3221
3222 const messages = linter.verify(code, config, filename);
3223 const suppressedMessages = linter.getSuppressedMessages();
3224
3225 assert.strictEqual(messages.length, 1);
3226 assert.strictEqual(messages[0].ruleId, "quotes");
3227 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
3228 assert.include(messages[0].nodeType, "Literal");
3229
3230 assert.strictEqual(suppressedMessages.length, 0);
3231 });
3232 });
3233
3234 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
3235 const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
3236
3237 it("should report a violation", () => {
3238 const config = { rules: { quotes: [2, "single"] } };
3239
3240 const messages = linter.verify(code, config, filename);
3241 const suppressedMessages = linter.getSuppressedMessages();
3242
3243 assert.strictEqual(messages.length, 1);
3244 assert.strictEqual(messages[0].ruleId, "quotes");
3245 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
3246 assert.include(messages[0].nodeType, "Literal");
3247
3248 assert.strictEqual(suppressedMessages.length, 0);
3249 });
3250 });
3251
3252 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
3253 it("should report a violation", () => {
3254 const code = "/*eslint no-alert:'1'*/ alert('test');";
3255
3256 const config = { rules: { "no-alert": 1 } };
3257
3258 const messages = linter.verify(code, config, filename);
3259 const suppressedMessages = linter.getSuppressedMessages();
3260
3261 assert.strictEqual(messages.length, 2);
3262
3263 /*
3264 * Incorrectly formatted comment threw error;
3265 * message from caught exception
3266 * may differ amongst UAs, so verifying
3267 * first part only as defined in the
3268 * parseJsonConfig function in lib/eslint.js
3269 */
3270 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":'1'':/u);
3271 assert.strictEqual(messages[0].line, 1);
3272 assert.strictEqual(messages[0].column, 1);
3273
3274 assert.strictEqual(messages[1].ruleId, "no-alert");
3275 assert.strictEqual(messages[1].message, "Unexpected alert.");
3276 assert.include(messages[1].nodeType, "CallExpression");
3277
3278 assert.strictEqual(suppressedMessages.length, 0);
3279 });
3280
3281 it("should report a violation", () => {
3282 const code = "/*eslint no-alert:abc*/ alert('test');";
3283
3284 const config = { rules: { "no-alert": 1 } };
3285
3286 const messages = linter.verify(code, config, filename);
3287 const suppressedMessages = linter.getSuppressedMessages();
3288
3289 assert.strictEqual(messages.length, 2);
3290
3291 /*
3292 * Incorrectly formatted comment threw error;
3293 * message from caught exception
3294 * may differ amongst UAs, so verifying
3295 * first part only as defined in the
3296 * parseJsonConfig function in lib/eslint.js
3297 */
3298 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":abc':/u);
3299 assert.strictEqual(messages[0].line, 1);
3300 assert.strictEqual(messages[0].column, 1);
3301
3302 assert.strictEqual(messages[1].ruleId, "no-alert");
3303 assert.strictEqual(messages[1].message, "Unexpected alert.");
3304 assert.include(messages[1].nodeType, "CallExpression");
3305
3306 assert.strictEqual(suppressedMessages.length, 0);
3307 });
3308
3309 it("should report a violation", () => {
3310 const code = "/*eslint no-alert:0 2*/ alert('test');";
3311
3312 const config = { rules: { "no-alert": 1 } };
3313
3314 const messages = linter.verify(code, config, filename);
3315 const suppressedMessages = linter.getSuppressedMessages();
3316
3317 assert.strictEqual(messages.length, 2);
3318
3319 /*
3320 * Incorrectly formatted comment threw error;
3321 * message from caught exception
3322 * may differ amongst UAs, so verifying
3323 * first part only as defined in the
3324 * parseJsonConfig function in lib/eslint.js
3325 */
3326 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":0 2':/u);
3327 assert.strictEqual(messages[0].line, 1);
3328 assert.strictEqual(messages[0].column, 1);
3329
3330 assert.strictEqual(messages[1].ruleId, "no-alert");
3331 assert.strictEqual(messages[1].message, "Unexpected alert.");
3332 assert.include(messages[1].nodeType, "CallExpression");
3333
3334 assert.strictEqual(suppressedMessages.length, 0);
3335 });
3336 });
3337
3338 describe("when evaluating code with comments which have colon in its value", () => {
3339 const code = String.raw`
3340 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
3341 alert('test');
3342 `;
3343
3344 it("should not parse errors, should report a violation", () => {
3345 const messages = linter.verify(code, {}, filename);
3346 const suppressedMessages = linter.getSuppressedMessages();
3347
3348 assert.strictEqual(messages.length, 1);
3349 assert.strictEqual(messages[0].ruleId, "max-len");
3350 assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100.");
3351 assert.include(messages[0].nodeType, "Program");
3352
3353 assert.strictEqual(suppressedMessages.length, 0);
3354 });
3355 });
3356
3357 describe("when evaluating code with comments that contain escape sequences", () => {
3358 const code = String.raw`
3359 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
3360 console.log("test");
3361 consolexlog("test2");
3362 var a = "test2";
3363 `;
3364
3365 it("should validate correctly", () => {
3366 const config = { rules: {} };
3367 const messages = linter.verify(code, config, filename);
3368 const suppressedMessages = linter.getSuppressedMessages();
3369 const [message1, message2] = messages;
3370
3371 assert.strictEqual(messages.length, 2);
3372 assert.strictEqual(message1.ruleId, "max-len");
3373 assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1.");
3374 assert.strictEqual(message1.line, 4);
3375 assert.strictEqual(message1.column, 1);
3376 assert.include(message1.nodeType, "Program");
3377 assert.strictEqual(message2.ruleId, "max-len");
3378 assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1.");
3379 assert.strictEqual(message2.line, 5);
3380 assert.strictEqual(message2.column, 1);
3381 assert.include(message2.nodeType, "Program");
3382
3383 assert.strictEqual(suppressedMessages.length, 0);
3384 });
3385 });
3386
3387 describe("when evaluating a file with a hashbang", () => {
3388
3389 it("should preserve line numbers", () => {
3390 const code = "#!bin/program\n\nvar foo;;";
3391 const config = { rules: { "no-extra-semi": 1 } };
3392 const messages = linter.verify(code, config);
3393 const suppressedMessages = linter.getSuppressedMessages();
3394
3395 assert.strictEqual(messages.length, 1);
3396 assert.strictEqual(messages[0].ruleId, "no-extra-semi");
3397 assert.strictEqual(messages[0].nodeType, "EmptyStatement");
3398 assert.strictEqual(messages[0].line, 3);
3399
3400 assert.strictEqual(suppressedMessages.length, 0);
3401 });
3402
3403 it("should have a comment with the hashbang in it", () => {
3404 const code = "#!bin/program\n\nvar foo;;";
3405 const config = { rules: { checker: "error" } };
3406 const spy = sinon.spy(context => {
3407 const comments = context.getAllComments();
3408
3409 assert.strictEqual(comments.length, 1);
3410 assert.strictEqual(comments[0].type, "Shebang");
3411 return {};
3412 });
3413
3414 linter.defineRule("checker", { create: spy });
3415 linter.verify(code, config);
3416 assert(spy.calledOnce);
3417 });
3418
3419 it("should comment hashbang without breaking offset", () => {
3420 const code = "#!/usr/bin/env node\n'123';";
3421 const config = { rules: { checker: "error" } };
3422 let spy;
3423
3424 linter.defineRule("checker", {
3425 create(context) {
3426 spy = sinon.spy(node => {
3427 assert.strictEqual(context.getSource(node), "'123';");
3428 });
3429 return { ExpressionStatement: spy };
3430 }
3431 });
3432
3433 linter.verify(code, config);
3434 assert(spy && spy.calledOnce);
3435 });
3436
3437 });
3438
3439 describe("when evaluating broken code", () => {
3440 const code = BROKEN_TEST_CODE;
3441
3442 it("should report a violation with a useful parse error prefix", () => {
3443 const messages = linter.verify(code);
3444 const suppressedMessages = linter.getSuppressedMessages();
3445
3446 assert.strictEqual(messages.length, 1);
3447 assert.strictEqual(messages[0].severity, 2);
3448 assert.isNull(messages[0].ruleId);
3449 assert.strictEqual(messages[0].line, 1);
3450 assert.strictEqual(messages[0].column, 4);
3451 assert.isTrue(messages[0].fatal);
3452 assert.match(messages[0].message, /^Parsing error:/u);
3453
3454 assert.strictEqual(suppressedMessages.length, 0);
3455 });
3456
3457 it("should report source code where the issue is present", () => {
3458 const inValidCode = [
3459 "var x = 20;",
3460 "if (x ==4 {",
3461 " x++;",
3462 "}"
3463 ];
3464 const messages = linter.verify(inValidCode.join("\n"));
3465 const suppressedMessages = linter.getSuppressedMessages();
3466
3467 assert.strictEqual(messages.length, 1);
3468 assert.strictEqual(messages[0].severity, 2);
3469 assert.isTrue(messages[0].fatal);
3470 assert.match(messages[0].message, /^Parsing error:/u);
3471
3472 assert.strictEqual(suppressedMessages.length, 0);
3473 });
3474 });
3475
3476 describe("when using an invalid (undefined) rule", () => {
3477 linter = new Linter();
3478
3479 const code = TEST_CODE;
3480 let results, result, warningResult, arrayOptionResults, objectOptionResults, resultsMultiple;
3481
3482 beforeEach(() => {
3483 results = linter.verify(code, { rules: { foobar: 2 } });
3484 result = results[0];
3485 warningResult = linter.verify(code, { rules: { foobar: 1 } })[0];
3486 arrayOptionResults = linter.verify(code, { rules: { foobar: [2, "always"] } });
3487 objectOptionResults = linter.verify(code, { rules: { foobar: [1, { bar: false }] } });
3488 resultsMultiple = linter.verify(code, { rules: { foobar: 2, barfoo: 1 } });
3489 });
3490
3491 it("should report a problem", () => {
3492 assert.isNotNull(result);
3493 assert.isArray(results);
3494 assert.isObject(result);
3495 assert.property(result, "ruleId");
3496 assert.strictEqual(result.ruleId, "foobar");
3497 });
3498
3499 it("should report that the rule does not exist", () => {
3500 assert.property(result, "message");
3501 assert.strictEqual(result.message, "Definition for rule 'foobar' was not found.");
3502 });
3503
3504 it("should report at the correct severity", () => {
3505 assert.property(result, "severity");
3506 assert.strictEqual(result.severity, 2);
3507 assert.strictEqual(warningResult.severity, 2); // this is 2, since the rulename is very likely to be wrong
3508 });
3509
3510 it("should accept any valid rule configuration", () => {
3511 assert.isObject(arrayOptionResults[0]);
3512 assert.isObject(objectOptionResults[0]);
3513 });
3514
3515 it("should report multiple missing rules", () => {
3516 assert.isArray(resultsMultiple);
3517
3518 assert.deepStrictEqual(
3519 resultsMultiple[1],
3520 {
3521 ruleId: "barfoo",
3522 message: "Definition for rule 'barfoo' was not found.",
3523 line: 1,
3524 column: 1,
3525 endLine: 1,
3526 endColumn: 2,
3527 severity: 2,
3528 nodeType: null
3529 }
3530 );
3531 });
3532 });
3533
3534 describe("when using a rule which has been replaced", () => {
3535 const code = TEST_CODE;
3536
3537 it("should report the new rule", () => {
3538 const results = linter.verify(code, { rules: { "no-comma-dangle": 2 } });
3539 const suppressedMessages = linter.getSuppressedMessages();
3540
3541 assert.strictEqual(results[0].ruleId, "no-comma-dangle");
3542 assert.strictEqual(results[0].message, "Rule 'no-comma-dangle' was removed and replaced by: comma-dangle");
3543
3544 assert.strictEqual(suppressedMessages.length, 0);
3545 });
3546 });
3547
3548 describe("when calling getRules", () => {
3549 it("should return all loaded rules", () => {
3550 const rules = linter.getRules();
3551
3552 assert.isAbove(rules.size, 230);
3553 assert.isObject(rules.get("no-alert"));
3554 });
3555 });
3556
3557 describe("when calling version", () => {
3558 it("should return current version number", () => {
3559 const version = linter.version;
3560
3561 assert.isString(version);
3562 assert.isTrue(parseInt(version[0], 10) >= 3);
3563 });
3564 });
3565
3566 describe("when evaluating an empty string", () => {
3567 it("runs rules", () => {
3568 linter.defineRule("no-programs", {
3569 create: context => ({
3570 Program(node) {
3571 context.report({ node, message: "No programs allowed." });
3572 }
3573 })
3574 });
3575
3576 assert.strictEqual(
3577 linter.verify("", { rules: { "no-programs": "error" } }).length,
3578 1
3579 );
3580 });
3581 });
3582
3583 describe("when evaluating code without comments to environment", () => {
3584 it("should report a violation when using typed array", () => {
3585 const code = "var array = new Uint8Array();";
3586
3587 const config = { rules: { "no-undef": 1 } };
3588
3589 const messages = linter.verify(code, config, filename);
3590 const suppressedMessages = linter.getSuppressedMessages();
3591
3592 assert.strictEqual(messages.length, 1);
3593 assert.strictEqual(suppressedMessages.length, 0);
3594 });
3595
3596 it("should report a violation when using Promise", () => {
3597 const code = "new Promise();";
3598
3599 const config = { rules: { "no-undef": 1 } };
3600
3601 const messages = linter.verify(code, config, filename);
3602 const suppressedMessages = linter.getSuppressedMessages();
3603
3604 assert.strictEqual(messages.length, 1);
3605 assert.strictEqual(suppressedMessages.length, 0);
3606 });
3607 });
3608
3609 describe("when evaluating code with comments to environment", () => {
3610 it("should not support legacy config", () => {
3611 const code = "/*jshint mocha:true */ describe();";
3612
3613 const config = { rules: { "no-undef": 1 } };
3614
3615 const messages = linter.verify(code, config, filename);
3616 const suppressedMessages = linter.getSuppressedMessages();
3617
3618 assert.strictEqual(messages.length, 1);
3619 assert.strictEqual(messages[0].ruleId, "no-undef");
3620 assert.strictEqual(messages[0].nodeType, "Identifier");
3621 assert.strictEqual(messages[0].line, 1);
3622
3623 assert.strictEqual(suppressedMessages.length, 0);
3624 });
3625
3626 it("should not report a violation", () => {
3627 const code = "/*eslint-env es6 */ new Promise();";
3628
3629 const config = { rules: { "no-undef": 1 } };
3630
3631 const messages = linter.verify(code, config, filename);
3632 const suppressedMessages = linter.getSuppressedMessages();
3633
3634 assert.strictEqual(messages.length, 0);
3635 assert.strictEqual(suppressedMessages.length, 0);
3636 });
3637
3638 // https://github.com/eslint/eslint/issues/14652
3639 it("should not report a violation", () => {
3640 const codes = [
3641 "/*eslint-env es6\n */ new Promise();",
3642 "/*eslint-env browser,\nes6 */ window;Promise;",
3643 "/*eslint-env\nbrowser,es6 */ window;Promise;"
3644 ];
3645 const config = { rules: { "no-undef": 1 } };
3646
3647 for (const code of codes) {
3648 const messages = linter.verify(code, config, filename);
3649 const suppressedMessages = linter.getSuppressedMessages();
3650
3651 assert.strictEqual(messages.length, 0);
3652 assert.strictEqual(suppressedMessages.length, 0);
3653 }
3654
3655 });
3656
3657 it("should not report a violation", () => {
3658 const code = `/*${ESLINT_ENV} mocha,node */ require();describe();`;
3659
3660 const config = { rules: { "no-undef": 1 } };
3661
3662 const messages = linter.verify(code, config, filename);
3663 const suppressedMessages = linter.getSuppressedMessages();
3664
3665 assert.strictEqual(messages.length, 0);
3666 assert.strictEqual(suppressedMessages.length, 0);
3667 });
3668
3669 it("should not report a violation", () => {
3670 const code = "/*eslint-env mocha */ suite();test();";
3671
3672 const config = { rules: { "no-undef": 1 } };
3673
3674 const messages = linter.verify(code, config, filename);
3675 const suppressedMessages = linter.getSuppressedMessages();
3676
3677 assert.strictEqual(messages.length, 0);
3678 assert.strictEqual(suppressedMessages.length, 0);
3679 });
3680
3681 it("should not report a violation", () => {
3682 const code = `/*${ESLINT_ENV} amd */ define();require();`;
3683
3684 const config = { rules: { "no-undef": 1 } };
3685
3686 const messages = linter.verify(code, config, filename);
3687 const suppressedMessages = linter.getSuppressedMessages();
3688
3689 assert.strictEqual(messages.length, 0);
3690 assert.strictEqual(suppressedMessages.length, 0);
3691 });
3692
3693 it("should not report a violation", () => {
3694 const code = `/*${ESLINT_ENV} jasmine */ expect();spyOn();`;
3695
3696 const config = { rules: { "no-undef": 1 } };
3697
3698 const messages = linter.verify(code, config, filename);
3699 const suppressedMessages = linter.getSuppressedMessages();
3700
3701 assert.strictEqual(messages.length, 0);
3702 assert.strictEqual(suppressedMessages.length, 0);
3703 });
3704
3705 it("should not report a violation", () => {
3706 const code = `/*globals require: true */ /*${ESLINT_ENV} node */ require = 1;`;
3707
3708 const config = { rules: { "no-undef": 1 } };
3709
3710 const messages = linter.verify(code, config, filename);
3711 const suppressedMessages = linter.getSuppressedMessages();
3712
3713 assert.strictEqual(messages.length, 0);
3714 assert.strictEqual(suppressedMessages.length, 0);
3715 });
3716
3717 it("should not report a violation", () => {
3718 const code = `/*${ESLINT_ENV} node */ process.exit();`;
3719
3720 const config = { rules: {} };
3721
3722 const messages = linter.verify(code, config, filename);
3723 const suppressedMessages = linter.getSuppressedMessages();
3724
3725 assert.strictEqual(messages.length, 0);
3726 assert.strictEqual(suppressedMessages.length, 0);
3727 });
3728
3729 it("should not report a violation", () => {
3730 const code = `/*eslint no-process-exit: 0 */ /*${ESLINT_ENV} node */ process.exit();`;
3731
3732 const config = { rules: { "no-undef": 1 } };
3733
3734 const messages = linter.verify(code, config, filename);
3735 const suppressedMessages = linter.getSuppressedMessages();
3736
3737 assert.strictEqual(messages.length, 0);
3738 assert.strictEqual(suppressedMessages.length, 0);
3739 });
3740 });
3741
3742 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
3743 it("should report a violation for disabling rules", () => {
3744 const code = [
3745 "alert('test'); // eslint-disable-line no-alert"
3746 ].join("\n");
3747 const config = {
3748 rules: {
3749 "no-alert": 1
3750 }
3751 };
3752
3753 const messages = linter.verify(code, config, {
3754 filename,
3755 allowInlineConfig: false
3756 });
3757 const suppressedMessages = linter.getSuppressedMessages();
3758
3759 assert.strictEqual(messages.length, 1);
3760 assert.strictEqual(messages[0].ruleId, "no-alert");
3761
3762 assert.strictEqual(suppressedMessages.length, 0);
3763 });
3764
3765 it("should report a violation for global variable declarations", () => {
3766 const code = [
3767 "/* global foo */"
3768 ].join("\n");
3769 const config = {
3770 rules: {
3771 test: 2
3772 }
3773 };
3774 let ok = false;
3775
3776 linter.defineRules({
3777 test: {
3778 create: context => ({
3779 Program() {
3780 const scope = context.getScope();
3781 const sourceCode = context.sourceCode;
3782 const comments = sourceCode.getAllComments();
3783
3784 assert.strictEqual(context.getSourceCode(), sourceCode);
3785 assert.strictEqual(1, comments.length);
3786
3787 const foo = getVariable(scope, "foo");
3788
3789 assert.notOk(foo);
3790
3791 ok = true;
3792 }
3793 })
3794 }
3795 });
3796
3797 linter.verify(code, config, { allowInlineConfig: false });
3798 assert(ok);
3799 });
3800
3801 it("should report a violation for eslint-disable", () => {
3802 const code = [
3803 "/* eslint-disable */",
3804 "alert('test');"
3805 ].join("\n");
3806 const config = {
3807 rules: {
3808 "no-alert": 1
3809 }
3810 };
3811
3812 const messages = linter.verify(code, config, {
3813 filename,
3814 allowInlineConfig: false
3815 });
3816 const suppressedMessages = linter.getSuppressedMessages();
3817
3818 assert.strictEqual(messages.length, 1);
3819 assert.strictEqual(messages[0].ruleId, "no-alert");
3820
3821 assert.strictEqual(suppressedMessages.length, 0);
3822 });
3823
3824 it("should not report a violation for rule changes", () => {
3825 const code = [
3826 "/*eslint no-alert:2*/",
3827 "alert('test');"
3828 ].join("\n");
3829 const config = {
3830 rules: {
3831 "no-alert": 0
3832 }
3833 };
3834
3835 const messages = linter.verify(code, config, {
3836 filename,
3837 allowInlineConfig: false
3838 });
3839 const suppressedMessages = linter.getSuppressedMessages();
3840
3841 assert.strictEqual(messages.length, 0);
3842 assert.strictEqual(suppressedMessages.length, 0);
3843 });
3844
3845 it("should report a violation for disable-line", () => {
3846 const code = [
3847 "alert('test'); // eslint-disable-line"
3848 ].join("\n");
3849 const config = {
3850 rules: {
3851 "no-alert": 2
3852 }
3853 };
3854
3855 const messages = linter.verify(code, config, {
3856 filename,
3857 allowInlineConfig: false
3858 });
3859 const suppressedMessages = linter.getSuppressedMessages();
3860
3861 assert.strictEqual(messages.length, 1);
3862 assert.strictEqual(messages[0].ruleId, "no-alert");
3863
3864 assert.strictEqual(suppressedMessages.length, 0);
3865 });
3866
3867 it("should report a violation for env changes", () => {
3868 const code = [
3869 `/*${ESLINT_ENV} browser*/ window`
3870 ].join("\n");
3871 const config = {
3872 rules: {
3873 "no-undef": 2
3874 }
3875 };
3876 const messages = linter.verify(code, config, { allowInlineConfig: false });
3877 const suppressedMessages = linter.getSuppressedMessages();
3878
3879 assert.strictEqual(messages.length, 1);
3880 assert.strictEqual(messages[0].ruleId, "no-undef");
3881
3882 assert.strictEqual(suppressedMessages.length, 0);
3883 });
3884 });
3885
3886 describe("when evaluating code with 'noInlineComment'", () => {
3887 for (const directive of [
3888 "globals foo",
3889 "global foo",
3890 "exported foo",
3891 "eslint eqeqeq: error",
3892 "eslint-disable eqeqeq",
3893 "eslint-disable-line eqeqeq",
3894 "eslint-disable-next-line eqeqeq",
3895 "eslint-enable eqeqeq",
3896 "eslint-env es6"
3897 ]) {
3898 // eslint-disable-next-line no-loop-func -- No closures
3899 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
3900 const messages = linter.verify(`/* ${directive} */`, { noInlineConfig: true });
3901 const suppressedMessages = linter.getSuppressedMessages();
3902
3903 assert.deepStrictEqual(messages.length, 1);
3904 assert.deepStrictEqual(messages[0].fatal, void 0);
3905 assert.deepStrictEqual(messages[0].ruleId, null);
3906 assert.deepStrictEqual(messages[0].severity, 1);
3907 assert.deepStrictEqual(messages[0].message, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
3908
3909 assert.strictEqual(suppressedMessages.length, 0);
3910 });
3911 }
3912
3913 for (const directive of [
3914 "eslint-disable-line eqeqeq",
3915 "eslint-disable-next-line eqeqeq"
3916 ]) {
3917 // eslint-disable-next-line no-loop-func -- No closures
3918 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
3919 const messages = linter.verify(`// ${directive}`, { noInlineConfig: true });
3920 const suppressedMessages = linter.getSuppressedMessages();
3921
3922 assert.deepStrictEqual(messages.length, 1);
3923 assert.deepStrictEqual(messages[0].fatal, void 0);
3924 assert.deepStrictEqual(messages[0].ruleId, null);
3925 assert.deepStrictEqual(messages[0].severity, 1);
3926 assert.deepStrictEqual(messages[0].message, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
3927
3928 assert.strictEqual(suppressedMessages.length, 0);
3929 });
3930 }
3931
3932 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
3933 const messages = linter.verify("/* globals foo */", { noInlineConfig: true }, { allowInlineConfig: false });
3934 const suppressedMessages = linter.getSuppressedMessages();
3935
3936 assert.deepStrictEqual(messages.length, 0);
3937 assert.strictEqual(suppressedMessages.length, 0);
3938 });
3939 });
3940
3941 describe("when receiving cwd in options during instantiation", () => {
3942 const code = "a;\nb;";
3943 const config = { rules: { checker: "error" } };
3944
3945 it("should get cwd correctly in the context", () => {
3946 const cwd = "cwd";
3947 const linterWithOption = new Linter({ cwd });
3948 let spy;
3949
3950 linterWithOption.defineRule("checker", {
3951 create(context) {
3952 spy = sinon.spy(() => {
3953 assert.strictEqual(context.getCwd(), context.cwd);
3954 assert.strictEqual(context.cwd, cwd);
3955 });
3956 return { Program: spy };
3957 }
3958 });
3959
3960 linterWithOption.verify(code, config);
3961 assert(spy && spy.calledOnce);
3962 });
3963
3964 it("should assign process.cwd() to it if cwd is undefined", () => {
3965 let spy;
3966 const linterWithOption = new Linter({ });
3967
3968 linterWithOption.defineRule("checker", {
3969 create(context) {
3970
3971 spy = sinon.spy(() => {
3972 assert.strictEqual(context.getCwd(), context.cwd);
3973 assert.strictEqual(context.cwd, process.cwd());
3974 });
3975 return { Program: spy };
3976 }
3977 });
3978
3979 linterWithOption.verify(code, config);
3980 assert(spy && spy.calledOnce);
3981 });
3982
3983 it("should assign process.cwd() to it if the option is undefined", () => {
3984 let spy;
3985
3986 linter.defineRule("checker", {
3987 create(context) {
3988
3989 spy = sinon.spy(() => {
3990 assert.strictEqual(context.getCwd(), context.cwd);
3991 assert.strictEqual(context.cwd, process.cwd());
3992 });
3993 return { Program: spy };
3994 }
3995 });
3996
3997 linter.verify(code, config);
3998 assert(spy && spy.calledOnce);
3999 });
4000 });
4001
4002 describe("reportUnusedDisable option", () => {
4003 it("reports problems for unused eslint-disable comments", () => {
4004 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: true });
4005 const suppressedMessages = linter.getSuppressedMessages();
4006
4007 assert.deepStrictEqual(
4008 messages,
4009 [
4010 {
4011 ruleId: null,
4012 message: "Unused eslint-disable directive (no problems were reported).",
4013 line: 1,
4014 column: 1,
4015 fix: {
4016 range: [0, 20],
4017 text: " "
4018 },
4019 severity: 2,
4020 nodeType: null
4021 }
4022 ]
4023 );
4024
4025 assert.strictEqual(suppressedMessages.length, 0);
4026 });
4027
4028 it("reports problems for multiple eslint-disable comments, including unused ones", () => {
4029 const code = [
4030 "/* eslint-disable no-alert -- j1 */",
4031 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
4032 ].join("\n");
4033 const config = {
4034 rules: {
4035 "no-alert": 2
4036 }
4037 };
4038 const messages = linter.verify(code, config, { reportUnusedDisableDirectives: true });
4039 const suppressedMessages = linter.getSuppressedMessages();
4040
4041 assert.strictEqual(messages.length, 1);
4042 assert.strictEqual(suppressedMessages.length, 1);
4043 assert.strictEqual(suppressedMessages[0].suppressions.length, 2);
4044 });
4045
4046 it("reports problems for eslint-disable-line and eslint-disable-next-line comments, including unused ones", () => {
4047 const code = [
4048 "// eslint-disable-next-line no-alert -- j1 */",
4049 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
4050 ].join("\n");
4051 const config = {
4052 rules: {
4053 "no-alert": 2
4054 }
4055 };
4056 const messages = linter.verify(code, config, { reportUnusedDisableDirectives: true });
4057 const suppressedMessages = linter.getSuppressedMessages();
4058
4059 assert.strictEqual(messages.length, 1);
4060 assert.strictEqual(suppressedMessages.length, 1);
4061 assert.strictEqual(suppressedMessages[0].suppressions.length, 2);
4062 });
4063
4064 it("reports problems for multiple unused eslint-disable comments with multiple ruleIds", () => {
4065 const code = [
4066 "/* eslint no-undef: 2, no-void: 2 */",
4067 "/* eslint-disable no-undef -- j1 */",
4068 "void foo; //eslint-disable-line no-undef, no-void -- j2"
4069 ].join("\n");
4070 const config = {
4071 rules: {
4072 "no-undef": 2,
4073 "no-void": 2
4074 }
4075 };
4076 const messages = linter.verify(code, config, { reportUnusedDisableDirectives: true });
4077 const suppressedMessages = linter.getSuppressedMessages();
4078
4079 assert.strictEqual(messages.length, 1);
4080 assert.strictEqual(suppressedMessages.length, 2);
4081 assert.strictEqual(suppressedMessages[0].ruleId, "no-void");
4082 assert.strictEqual(suppressedMessages[0].suppressions.length, 1);
4083 assert.strictEqual(suppressedMessages[1].ruleId, "no-undef");
4084 assert.strictEqual(suppressedMessages[1].suppressions.length, 2);
4085 });
4086
4087 it("reports problems for unused eslint-disable comments (error)", () => {
4088 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "error" });
4089 const suppressedMessages = linter.getSuppressedMessages();
4090
4091 assert.deepStrictEqual(
4092 messages,
4093 [
4094 {
4095 ruleId: null,
4096 message: "Unused eslint-disable directive (no problems were reported).",
4097 line: 1,
4098 column: 1,
4099 fix: {
4100 range: [0, 20],
4101 text: " "
4102 },
4103 severity: 2,
4104 nodeType: null
4105 }
4106 ]
4107 );
4108
4109 assert.strictEqual(suppressedMessages.length, 0);
4110 });
4111
4112 it("reports problems for unused eslint-disable comments (warn)", () => {
4113 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "warn" });
4114 const suppressedMessages = linter.getSuppressedMessages();
4115
4116 assert.deepStrictEqual(
4117 messages,
4118 [
4119 {
4120 ruleId: null,
4121 message: "Unused eslint-disable directive (no problems were reported).",
4122 line: 1,
4123 column: 1,
4124 fix: {
4125 range: [0, 20],
4126 text: " "
4127 },
4128 severity: 1,
4129 nodeType: null
4130 }
4131 ]
4132 );
4133
4134 assert.strictEqual(suppressedMessages.length, 0);
4135 });
4136
4137 it("reports problems for unused eslint-disable comments (in config)", () => {
4138 const messages = linter.verify("/* eslint-disable */", { reportUnusedDisableDirectives: true });
4139 const suppressedMessages = linter.getSuppressedMessages();
4140
4141 assert.deepStrictEqual(
4142 messages,
4143 [
4144 {
4145 ruleId: null,
4146 message: "Unused eslint-disable directive (no problems were reported).",
4147 line: 1,
4148 column: 1,
4149 fix: {
4150 range: [0, 20],
4151 text: " "
4152 },
4153 severity: 1,
4154 nodeType: null
4155 }
4156 ]
4157 );
4158
4159 assert.strictEqual(suppressedMessages.length, 0);
4160 });
4161
4162 it("reports problems for partially unused eslint-disable comments (in config)", () => {
4163 const code = "alert('test'); // eslint-disable-line no-alert, no-redeclare";
4164 const config = {
4165 reportUnusedDisableDirectives: true,
4166 rules: {
4167 "no-alert": 1,
4168 "no-redeclare": 1
4169 }
4170 };
4171
4172 const messages = linter.verify(code, config, {
4173 filename,
4174 allowInlineConfig: true
4175 });
4176 const suppressedMessages = linter.getSuppressedMessages();
4177
4178 assert.deepStrictEqual(
4179 messages,
4180 [
4181 {
4182 ruleId: null,
4183 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
4184 line: 1,
4185 column: 16,
4186 fix: {
4187 range: [46, 60],
4188 text: ""
4189 },
4190 severity: 1,
4191 nodeType: null
4192 }
4193 ]
4194 );
4195
4196 assert.strictEqual(suppressedMessages.length, 1);
4197 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
4198 });
4199
4200 it("reports no problems for no-fallthrough despite comment pattern match", () => {
4201 const code = "switch (foo) { case 0: a(); \n// eslint-disable-next-line no-fallthrough\n case 1: }";
4202 const config = {
4203 reportUnusedDisableDirectives: true,
4204 rules: {
4205 "no-fallthrough": 2
4206 }
4207 };
4208
4209 const messages = linter.verify(code, config, {
4210 filename,
4211 allowInlineConfig: true
4212 });
4213 const suppressedMessages = linter.getSuppressedMessages();
4214
4215 assert.strictEqual(messages.length, 0);
4216
4217 assert.strictEqual(suppressedMessages.length, 1);
4218 assert.strictEqual(suppressedMessages[0].ruleId, "no-fallthrough");
4219 });
4220
4221 describe("autofix", () => {
4222 const alwaysReportsRule = {
4223 create(context) {
4224 return {
4225 Program(node) {
4226 context.report({ message: "bad code", loc: node.loc.end });
4227 }
4228 };
4229 }
4230 };
4231
4232 const neverReportsRule = {
4233 create() {
4234 return {};
4235 }
4236 };
4237
4238 const ruleCount = 3;
4239 const usedRules = Array.from(
4240 { length: ruleCount },
4241 (_, index) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
4242 );
4243 const unusedRules = usedRules.map(name => `un${name}`); // "unused", "unused-1", "unused-2"
4244
4245 const config = {
4246 reportUnusedDisableDirectives: true,
4247 rules: {
4248 ...Object.fromEntries(usedRules.map(name => [name, "error"])),
4249 ...Object.fromEntries(unusedRules.map(name => [name, "error"]))
4250 }
4251 };
4252
4253 beforeEach(() => {
4254 linter.defineRules(Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])));
4255 linter.defineRules(Object.fromEntries(unusedRules.map(name => [name, neverReportsRule])));
4256 });
4257
4258 const tests = [
4259
4260 //-----------------------------------------------
4261 // Removing the entire comment
4262 //-----------------------------------------------
4263
4264 {
4265 code: "// eslint-disable-line unused",
4266 output: " "
4267 },
4268 {
4269 code: "foo// eslint-disable-line unused",
4270 output: "foo "
4271 },
4272 {
4273 code: "// eslint-disable-line ,unused,",
4274 output: " "
4275 },
4276 {
4277 code: "// eslint-disable-line unused-1, unused-2",
4278 output: " "
4279 },
4280 {
4281 code: "// eslint-disable-line ,unused-1,, unused-2,, -- comment",
4282 output: " "
4283 },
4284 {
4285 code: "// eslint-disable-next-line unused\n",
4286 output: " \n"
4287 },
4288 {
4289 code: "// eslint-disable-next-line unused\nfoo",
4290 output: " \nfoo"
4291 },
4292 {
4293 code: "/* eslint-disable \nunused\n*/",
4294 output: " "
4295 },
4296
4297 //-----------------------------------------------
4298 // Removing only individual rules
4299 //-----------------------------------------------
4300
4301 // content before the first rule should not be changed
4302 {
4303 code: "//eslint-disable-line unused, used",
4304 output: "//eslint-disable-line used"
4305 },
4306 {
4307 code: "// eslint-disable-line unused, used",
4308 output: "// eslint-disable-line used"
4309 },
4310 {
4311 code: "// eslint-disable-line unused, used",
4312 output: "// eslint-disable-line used"
4313 },
4314 {
4315 code: "/*\neslint-disable unused, used*/",
4316 output: "/*\neslint-disable used*/"
4317 },
4318 {
4319 code: "/*\n eslint-disable unused, used*/",
4320 output: "/*\n eslint-disable used*/"
4321 },
4322 {
4323 code: "/*\r\neslint-disable unused, used*/",
4324 output: "/*\r\neslint-disable used*/"
4325 },
4326 {
4327 code: "/*\u2028eslint-disable unused, used*/",
4328 output: "/*\u2028eslint-disable used*/"
4329 },
4330 {
4331 code: "/*\u00A0eslint-disable unused, used*/",
4332 output: "/*\u00A0eslint-disable used*/"
4333 },
4334 {
4335 code: "// eslint-disable-line unused, used",
4336 output: "// eslint-disable-line used"
4337 },
4338 {
4339 code: "/* eslint-disable\nunused, used*/",
4340 output: "/* eslint-disable\nused*/"
4341 },
4342 {
4343 code: "/* eslint-disable\n unused, used*/",
4344 output: "/* eslint-disable\n used*/"
4345 },
4346 {
4347 code: "/* eslint-disable\r\nunused, used*/",
4348 output: "/* eslint-disable\r\nused*/"
4349 },
4350 {
4351 code: "/* eslint-disable\u2028unused, used*/",
4352 output: "/* eslint-disable\u2028used*/"
4353 },
4354 {
4355 code: "/* eslint-disable\u00A0unused, used*/",
4356 output: "/* eslint-disable\u00A0used*/"
4357 },
4358
4359 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
4360 {
4361 code: "// eslint-disable-line unused,used",
4362 output: "// eslint-disable-line used"
4363 },
4364 {
4365 code: "// eslint-disable-line unused, used",
4366 output: "// eslint-disable-line used"
4367 },
4368 {
4369 code: "// eslint-disable-line unused , used",
4370 output: "// eslint-disable-line used"
4371 },
4372 {
4373 code: "// eslint-disable-line unused, used",
4374 output: "// eslint-disable-line used"
4375 },
4376 {
4377 code: "// eslint-disable-line unused ,used",
4378 output: "// eslint-disable-line used"
4379 },
4380 {
4381 code: "/* eslint-disable unused\n,\nused */",
4382 output: "/* eslint-disable used */"
4383 },
4384 {
4385 code: "/* eslint-disable unused \n \n,\n\n used */",
4386 output: "/* eslint-disable used */"
4387 },
4388 {
4389 code: "/* eslint-disable unused\u2028,\u2028used */",
4390 output: "/* eslint-disable used */"
4391 },
4392 {
4393 code: "// eslint-disable-line unused\u00A0,\u00A0used",
4394 output: "// eslint-disable-line used"
4395 },
4396 {
4397 code: "// eslint-disable-line unused,,used",
4398 output: "// eslint-disable-line ,used"
4399 },
4400 {
4401 code: "// eslint-disable-line unused, ,used",
4402 output: "// eslint-disable-line ,used"
4403 },
4404 {
4405 code: "// eslint-disable-line unused,, used",
4406 output: "// eslint-disable-line , used"
4407 },
4408 {
4409 code: "// eslint-disable-line unused,used ",
4410 output: "// eslint-disable-line used "
4411 },
4412 {
4413 code: "// eslint-disable-next-line unused,used\n",
4414 output: "// eslint-disable-next-line used\n"
4415 },
4416
4417 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
4418 {
4419 code: "// eslint-disable-line used-1,unused,used-2",
4420 output: "// eslint-disable-line used-1,used-2"
4421 },
4422 {
4423 code: "// eslint-disable-line used-1, unused,used-2",
4424 output: "// eslint-disable-line used-1,used-2"
4425 },
4426 {
4427 code: "// eslint-disable-line used-1,unused ,used-2",
4428 output: "// eslint-disable-line used-1,used-2"
4429 },
4430 {
4431 code: "// eslint-disable-line used-1, unused ,used-2",
4432 output: "// eslint-disable-line used-1,used-2"
4433 },
4434 {
4435 code: "/* eslint-disable used-1,\nunused\n,used-2 */",
4436 output: "/* eslint-disable used-1,used-2 */"
4437 },
4438 {
4439 code: "/* eslint-disable used-1,\n\n unused \n \n ,used-2 */",
4440 output: "/* eslint-disable used-1,used-2 */"
4441 },
4442 {
4443 code: "/* eslint-disable used-1,\u2028unused\u2028,used-2 */",
4444 output: "/* eslint-disable used-1,used-2 */"
4445 },
4446 {
4447 code: "// eslint-disable-line used-1,\u00A0unused\u00A0,used-2",
4448 output: "// eslint-disable-line used-1,used-2"
4449 },
4450
4451 // when removing a rule in the middle, content around commas should not be changed
4452 {
4453 code: "// eslint-disable-line used-1, unused ,used-2",
4454 output: "// eslint-disable-line used-1,used-2"
4455 },
4456 {
4457 code: "// eslint-disable-line used-1,unused, used-2",
4458 output: "// eslint-disable-line used-1, used-2"
4459 },
4460 {
4461 code: "// eslint-disable-line used-1 ,unused,used-2",
4462 output: "// eslint-disable-line used-1 ,used-2"
4463 },
4464 {
4465 code: "// eslint-disable-line used-1 ,unused, used-2",
4466 output: "// eslint-disable-line used-1 , used-2"
4467 },
4468 {
4469 code: "// eslint-disable-line used-1 , unused , used-2",
4470 output: "// eslint-disable-line used-1 , used-2"
4471 },
4472 {
4473 code: "/* eslint-disable used-1\n,unused,\nused-2 */",
4474 output: "/* eslint-disable used-1\n,\nused-2 */"
4475 },
4476 {
4477 code: "/* eslint-disable used-1\u2028,unused,\u2028used-2 */",
4478 output: "/* eslint-disable used-1\u2028,\u2028used-2 */"
4479 },
4480 {
4481 code: "// eslint-disable-line used-1\u00A0,unused,\u00A0used-2",
4482 output: "// eslint-disable-line used-1\u00A0,\u00A0used-2"
4483 },
4484 {
4485 code: "// eslint-disable-line , unused ,used",
4486 output: "// eslint-disable-line ,used"
4487 },
4488 {
4489 code: "/* eslint-disable\n, unused ,used */",
4490 output: "/* eslint-disable\n,used */"
4491 },
4492 {
4493 code: "/* eslint-disable used-1,\n,unused,used-2 */",
4494 output: "/* eslint-disable used-1,\n,used-2 */"
4495 },
4496 {
4497 code: "/* eslint-disable used-1,unused,\n,used-2 */",
4498 output: "/* eslint-disable used-1,\n,used-2 */"
4499 },
4500 {
4501 code: "/* eslint-disable used-1,\n,unused,\n,used-2 */",
4502 output: "/* eslint-disable used-1,\n,\n,used-2 */"
4503 },
4504 {
4505 code: "// eslint-disable-line used, unused,",
4506 output: "// eslint-disable-line used,"
4507 },
4508 {
4509 code: "// eslint-disable-next-line used, unused,\n",
4510 output: "// eslint-disable-next-line used,\n"
4511 },
4512 {
4513 code: "// eslint-disable-line used, unused, ",
4514 output: "// eslint-disable-line used, "
4515 },
4516 {
4517 code: "// eslint-disable-line used, unused, -- comment",
4518 output: "// eslint-disable-line used, -- comment"
4519 },
4520 {
4521 code: "/* eslint-disable used, unused,\n*/",
4522 output: "/* eslint-disable used,\n*/"
4523 },
4524
4525 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
4526 {
4527 code: "// eslint-disable-line used,unused",
4528 output: "// eslint-disable-line used"
4529 },
4530 {
4531 code: "// eslint-disable-line used, unused",
4532 output: "// eslint-disable-line used"
4533 },
4534 {
4535 code: "// eslint-disable-line used ,unused",
4536 output: "// eslint-disable-line used"
4537 },
4538 {
4539 code: "// eslint-disable-line used , unused",
4540 output: "// eslint-disable-line used"
4541 },
4542 {
4543 code: "// eslint-disable-line used, unused",
4544 output: "// eslint-disable-line used"
4545 },
4546 {
4547 code: "// eslint-disable-line used ,unused",
4548 output: "// eslint-disable-line used"
4549 },
4550 {
4551 code: "/* eslint-disable used\n,\nunused */",
4552 output: "/* eslint-disable used */"
4553 },
4554 {
4555 code: "/* eslint-disable used \n \n,\n\n unused */",
4556 output: "/* eslint-disable used */"
4557 },
4558 {
4559 code: "/* eslint-disable used\u2028,\u2028unused */",
4560 output: "/* eslint-disable used */"
4561 },
4562 {
4563 code: "// eslint-disable-line used\u00A0,\u00A0unused",
4564 output: "// eslint-disable-line used"
4565 },
4566 {
4567 code: "// eslint-disable-line used,,unused",
4568 output: "// eslint-disable-line used,"
4569 },
4570 {
4571 code: "// eslint-disable-line used, ,unused",
4572 output: "// eslint-disable-line used,"
4573 },
4574 {
4575 code: "/* eslint-disable used,\n,unused */",
4576 output: "/* eslint-disable used, */"
4577 },
4578 {
4579 code: "/* eslint-disable used\n, ,unused */",
4580 output: "/* eslint-disable used\n, */"
4581 },
4582
4583 // content after the last rule should not be changed
4584 {
4585 code: "// eslint-disable-line used,unused",
4586 output: "// eslint-disable-line used"
4587 },
4588 {
4589 code: "// eslint-disable-line used,unused ",
4590 output: "// eslint-disable-line used "
4591 },
4592 {
4593 code: "// eslint-disable-line used,unused ",
4594 output: "// eslint-disable-line used "
4595 },
4596 {
4597 code: "// eslint-disable-line used,unused -- comment",
4598 output: "// eslint-disable-line used -- comment"
4599 },
4600 {
4601 code: "// eslint-disable-next-line used,unused\n",
4602 output: "// eslint-disable-next-line used\n"
4603 },
4604 {
4605 code: "// eslint-disable-next-line used,unused \n",
4606 output: "// eslint-disable-next-line used \n"
4607 },
4608 {
4609 code: "/* eslint-disable used,unused\u2028*/",
4610 output: "/* eslint-disable used\u2028*/"
4611 },
4612 {
4613 code: "// eslint-disable-line used,unused\u00A0",
4614 output: "// eslint-disable-line used\u00A0"
4615 },
4616
4617 // multiply rules to remove
4618 {
4619 code: "// eslint-disable-line used, unused-1, unused-2",
4620 output: "// eslint-disable-line used"
4621 },
4622 {
4623 code: "// eslint-disable-line unused-1, used, unused-2",
4624 output: "// eslint-disable-line used"
4625 },
4626 {
4627 code: "// eslint-disable-line unused-1, unused-2, used",
4628 output: "// eslint-disable-line used"
4629 },
4630 {
4631 code: "// eslint-disable-line used-1, unused-1, used-2, unused-2",
4632 output: "// eslint-disable-line used-1, used-2"
4633 },
4634 {
4635 code: "// eslint-disable-line unused-1, used-1, unused-2, used-2",
4636 output: "// eslint-disable-line used-1, used-2"
4637 },
4638 {
4639 code: `
4640 /* eslint-disable unused-1,
4641 used-1,
4642 unused-2,
4643 used-2
4644 */
4645 `,
4646 output: `
4647 /* eslint-disable used-1,
4648 used-2
4649 */
4650 `
4651 },
4652 {
4653 code: `
4654 /* eslint-disable
4655 unused-1,
4656 used-1,
4657 unused-2,
4658 used-2
4659 */
4660 `,
4661 output: `
4662 /* eslint-disable
4663 used-1,
4664 used-2
4665 */
4666 `
4667 },
4668 {
4669 code: `
4670 /* eslint-disable
4671 used-1,
4672 unused-1,
4673 used-2,
4674 unused-2
4675 */
4676 `,
4677 output: `
4678 /* eslint-disable
4679 used-1,
4680 used-2
4681 */
4682 `
4683 },
4684 {
4685 code: `
4686 /* eslint-disable
4687 used-1,
4688 unused-1,
4689 used-2,
4690 unused-2,
4691 */
4692 `,
4693 output: `
4694 /* eslint-disable
4695 used-1,
4696 used-2,
4697 */
4698 `
4699 },
4700 {
4701 code: `
4702 /* eslint-disable
4703 ,unused-1
4704 ,used-1
4705 ,unused-2
4706 ,used-2
4707 */
4708 `,
4709 output: `
4710 /* eslint-disable
4711 ,used-1
4712 ,used-2
4713 */
4714 `
4715 },
4716 {
4717 code: `
4718 /* eslint-disable
4719 ,used-1
4720 ,unused-1
4721 ,used-2
4722 ,unused-2
4723 */
4724 `,
4725 output: `
4726 /* eslint-disable
4727 ,used-1
4728 ,used-2
4729 */
4730 `
4731 },
4732 {
4733 code: `
4734 /* eslint-disable
4735 used-1,
4736 unused-1,
4737 used-2,
4738 unused-2
4739
4740 -- comment
4741 */
4742 `,
4743 output: `
4744 /* eslint-disable
4745 used-1,
4746 used-2
4747
4748 -- comment
4749 */
4750 `
4751 },
4752
4753 // duplicates in the list
4754 {
4755 code: "// eslint-disable-line unused, unused, used",
4756 output: "// eslint-disable-line used"
4757 },
4758 {
4759 code: "// eslint-disable-line unused, used, unused",
4760 output: "// eslint-disable-line used"
4761 },
4762 {
4763 code: "// eslint-disable-line used, unused, unused, used",
4764 output: "// eslint-disable-line used, used"
4765 }
4766 ];
4767
4768 for (const { code, output } of tests) {
4769 // eslint-disable-next-line no-loop-func -- `linter` is getting updated in beforeEach()
4770 it(code, () => {
4771 assert.strictEqual(
4772 linter.verifyAndFix(code, config).output,
4773 output
4774 );
4775 });
4776 }
4777 });
4778 });
4779
4780 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
4781 it("should not report a violation", () => {
4782 const code = [
4783 "alert('test'); // eslint-disable-line no-alert"
4784 ].join("\n");
4785 const config = {
4786 rules: {
4787 "no-alert": 1
4788 }
4789 };
4790
4791 const messages = linter.verify(code, config, {
4792 filename,
4793 allowInlineConfig: true
4794 });
4795 const suppressedMessages = linter.getSuppressedMessages();
4796
4797 assert.strictEqual(messages.length, 0);
4798
4799 assert.strictEqual(suppressedMessages.length, 1);
4800 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
4801 });
4802 });
4803
4804 describe("when evaluating code with hashbang", () => {
4805 it("should comment hashbang without breaking offset", () => {
4806 const code = "#!/usr/bin/env node\n'123';";
4807 const config = { rules: { checker: "error" } };
4808 let spy;
4809
4810 linter.defineRule("checker", {
4811 create(context) {
4812 spy = sinon.spy(node => {
4813 assert.strictEqual(context.getSource(node), "'123';");
4814 });
4815 return { ExpressionStatement: spy };
4816 }
4817 });
4818
4819 linter.verify(code, config);
4820 assert(spy && spy.calledOnce);
4821 });
4822 });
4823
4824 describe("verify()", () => {
4825 describe("filenames", () => {
4826 it("should allow filename to be passed on options object", () => {
4827 const filenameChecker = sinon.spy(context => {
4828 assert.strictEqual(context.filename, "foo.js");
4829 return {};
4830 });
4831
4832 linter.defineRule("checker", { create: filenameChecker });
4833 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4834 assert(filenameChecker.calledOnce);
4835 });
4836
4837 it("should allow filename to be passed as third argument", () => {
4838 const filenameChecker = sinon.spy(context => {
4839 assert.strictEqual(context.filename, "bar.js");
4840 return {};
4841 });
4842
4843 linter.defineRule("checker", { create: filenameChecker });
4844 linter.verify("foo;", { rules: { checker: "error" } }, "bar.js");
4845 assert(filenameChecker.calledOnce);
4846 });
4847
4848 it("should default filename to <input> when options object doesn't have filename", () => {
4849 const filenameChecker = sinon.spy(context => {
4850 assert.strictEqual(context.filename, "<input>");
4851 return {};
4852 });
4853
4854 linter.defineRule("checker", { create: filenameChecker });
4855 linter.verify("foo;", { rules: { checker: "error" } }, {});
4856 assert(filenameChecker.calledOnce);
4857 });
4858
4859 it("should default filename to <input> when only two arguments are passed", () => {
4860 const filenameChecker = sinon.spy(context => {
4861 assert.strictEqual(context.filename, "<input>");
4862 return {};
4863 });
4864
4865 linter.defineRule("checker", { create: filenameChecker });
4866 linter.verify("foo;", { rules: { checker: "error" } });
4867 assert(filenameChecker.calledOnce);
4868 });
4869 });
4870
4871 describe("physicalFilenames", () => {
4872 it("should be same as `filename` passed on options object, if no processors are used", () => {
4873 const physicalFilenameChecker = sinon.spy(context => {
4874 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
4875 assert.strictEqual(context.physicalFilename, "foo.js");
4876 return {};
4877 });
4878
4879 linter.defineRule("checker", { create: physicalFilenameChecker });
4880 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4881 assert(physicalFilenameChecker.calledOnce);
4882 });
4883
4884 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
4885 const physicalFilenameChecker = sinon.spy(context => {
4886 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
4887 assert.strictEqual(context.physicalFilename, "<input>");
4888 return {};
4889 });
4890
4891 linter.defineRule("checker", { create: physicalFilenameChecker });
4892 linter.verify("foo;", { rules: { checker: "error" } }, {});
4893 assert(physicalFilenameChecker.calledOnce);
4894 });
4895
4896 it("should default physicalFilename to <input> when only two arguments are passed", () => {
4897 const physicalFilenameChecker = sinon.spy(context => {
4898 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
4899 assert.strictEqual(context.physicalFilename, "<input>");
4900 return {};
4901 });
4902
4903 linter.defineRule("checker", { create: physicalFilenameChecker });
4904 linter.verify("foo;", { rules: { checker: "error" } });
4905 assert(physicalFilenameChecker.calledOnce);
4906 });
4907 });
4908
4909 it("should report warnings in order by line and column when called", () => {
4910
4911 const code = "foo()\n alert('test')";
4912 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
4913
4914 const messages = linter.verify(code, config, filename);
4915 const suppressedMessages = linter.getSuppressedMessages();
4916
4917 assert.strictEqual(messages.length, 3);
4918 assert.strictEqual(messages[0].line, 1);
4919 assert.strictEqual(messages[0].column, 6);
4920 assert.strictEqual(messages[1].line, 2);
4921 assert.strictEqual(messages[1].column, 18);
4922 assert.strictEqual(messages[2].line, 2);
4923 assert.strictEqual(messages[2].column, 18);
4924
4925 assert.strictEqual(suppressedMessages.length, 0);
4926 });
4927
4928 describe("ecmaVersion", () => {
4929
4930 it("should not support ES6 when no ecmaVersion provided", () => {
4931 const messages = linter.verify("let x = 0;");
4932 const suppressedMessages = linter.getSuppressedMessages();
4933
4934 assert.strictEqual(messages.length, 1);
4935 assert.strictEqual(suppressedMessages.length, 0);
4936 });
4937
4938 it("supports ECMAScript version 'latest'", () => {
4939 const messages = linter.verify("let x = 5 ** 7;", {
4940 parserOptions: { ecmaVersion: "latest" }
4941 });
4942 const suppressedMessages = linter.getSuppressedMessages();
4943
4944 assert.strictEqual(messages.length, 0);
4945 assert.strictEqual(suppressedMessages.length, 0);
4946 });
4947
4948 it("the 'latest' is equal to espree.latestEcmaVersion", () => {
4949 let ecmaVersion = null;
4950 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4951
4952 linter.defineRule("ecma-version", {
4953 create: context => ({
4954 Program() {
4955 ecmaVersion = context.parserOptions.ecmaVersion;
4956 }
4957 })
4958 });
4959 linter.verify("", config);
4960 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion, "ecmaVersion should be 13");
4961 });
4962
4963 it("the 'latest' is not normalized for custom parsers", () => {
4964 let ecmaVersion = null;
4965 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "latest" } };
4966
4967 linter.defineParser("custom-parser", testParsers.enhancedParser);
4968 linter.defineRule("ecma-version", {
4969 create: context => ({
4970 Program() {
4971 ecmaVersion = context.parserOptions.ecmaVersion;
4972 }
4973 })
4974 });
4975 linter.verify("", config);
4976 assert.strictEqual(ecmaVersion, "latest", "ecmaVersion should be latest");
4977 });
4978
4979 it("the 'latest' is equal to espree.latestEcmaVersion on languageOptions", () => {
4980 let ecmaVersion = null;
4981 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4982
4983 linter.defineRule("ecma-version", {
4984 create: context => ({
4985 Program() {
4986 ecmaVersion = context.languageOptions.ecmaVersion;
4987 }
4988 })
4989 });
4990 linter.verify("", config);
4991 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
4992 });
4993
4994 it("the 'next' is equal to espree.latestEcmaVersion on languageOptions with custom parser", () => {
4995 let ecmaVersion = null;
4996 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "next" } };
4997
4998 linter.defineParser("custom-parser", testParsers.stubParser);
4999 linter.defineRule("ecma-version", {
5000 create: context => ({
5001 Program() {
5002 ecmaVersion = context.languageOptions.ecmaVersion;
5003 }
5004 })
5005 });
5006 linter.verify("", config);
5007 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
5008 });
5009
5010 it("missing ecmaVersion is equal to 5 on languageOptions with custom parser", () => {
5011 let ecmaVersion = null;
5012 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser" };
5013
5014 linter.defineParser("custom-parser", testParsers.enhancedParser);
5015 linter.defineRule("ecma-version", {
5016 create: context => ({
5017 Program() {
5018 ecmaVersion = context.languageOptions.ecmaVersion;
5019 }
5020 })
5021 });
5022 linter.verify("", config);
5023 assert.strictEqual(ecmaVersion, 5, "ecmaVersion should be 5");
5024 });
5025
5026 it("should pass normalized ecmaVersion to eslint-scope", () => {
5027 let blockScope = null;
5028
5029 linter.defineRule("block-scope", {
5030 create: context => ({
5031 BlockStatement() {
5032 blockScope = context.getScope();
5033 }
5034 })
5035 });
5036 linter.defineParser("custom-parser", {
5037 parse: (...args) => espree.parse(...args)
5038 });
5039
5040 // Use standard parser
5041 linter.verify("{}", {
5042 rules: { "block-scope": 2 },
5043 parserOptions: { ecmaVersion: "latest" }
5044 });
5045
5046 assert.strictEqual(blockScope.type, "block");
5047
5048 linter.verify("{}", {
5049 rules: { "block-scope": 2 },
5050 parserOptions: {} // ecmaVersion defaults to 5
5051 });
5052 assert.strictEqual(blockScope.type, "global");
5053
5054 // Use custom parser
5055 linter.verify("{}", {
5056 rules: { "block-scope": 2 },
5057 parser: "custom-parser",
5058 parserOptions: { ecmaVersion: "latest" }
5059 });
5060
5061 assert.strictEqual(blockScope.type, "block");
5062
5063 linter.verify("{}", {
5064 rules: { "block-scope": 2 },
5065 parser: "custom-parser",
5066 parserOptions: {} // ecmaVersion defaults to 5
5067 });
5068 assert.strictEqual(blockScope.type, "global");
5069 });
5070
5071 describe("it should properly parse let declaration when", () => {
5072 it("the ECMAScript version number is 6", () => {
5073 const messages = linter.verify("let x = 5;", {
5074 parserOptions: {
5075 ecmaVersion: 6
5076 }
5077 });
5078 const suppressedMessages = linter.getSuppressedMessages();
5079
5080 assert.strictEqual(messages.length, 0);
5081 assert.strictEqual(suppressedMessages.length, 0);
5082 });
5083
5084 it("the ECMAScript version number is 2015", () => {
5085 const messages = linter.verify("let x = 5;", {
5086 parserOptions: {
5087 ecmaVersion: 2015
5088 }
5089 });
5090 const suppressedMessages = linter.getSuppressedMessages();
5091
5092 assert.strictEqual(messages.length, 0);
5093 assert.strictEqual(suppressedMessages.length, 0);
5094 });
5095 });
5096
5097 it("should fail to parse exponentiation operator when the ECMAScript version number is 2015", () => {
5098 const messages = linter.verify("x ** y;", {
5099 parserOptions: {
5100 ecmaVersion: 2015
5101 }
5102 });
5103 const suppressedMessages = linter.getSuppressedMessages();
5104
5105 assert.strictEqual(messages.length, 1);
5106 assert.strictEqual(suppressedMessages.length, 0);
5107 });
5108
5109 describe("should properly parse exponentiation operator when", () => {
5110 it("the ECMAScript version number is 7", () => {
5111 const messages = linter.verify("x ** y;", {
5112 parserOptions: {
5113 ecmaVersion: 7
5114 }
5115 });
5116 const suppressedMessages = linter.getSuppressedMessages();
5117
5118 assert.strictEqual(messages.length, 0);
5119 assert.strictEqual(suppressedMessages.length, 0);
5120 });
5121
5122 it("the ECMAScript version number is 2016", () => {
5123 const messages = linter.verify("x ** y;", {
5124 parserOptions: {
5125 ecmaVersion: 2016
5126 }
5127 });
5128 const suppressedMessages = linter.getSuppressedMessages();
5129
5130 assert.strictEqual(messages.length, 0);
5131 assert.strictEqual(suppressedMessages.length, 0);
5132 });
5133 });
5134 });
5135
5136 it("should properly parse object spread when ecmaVersion is 2018", () => {
5137
5138 const messages = linter.verify("var x = { ...y };", {
5139 parserOptions: {
5140 ecmaVersion: 2018
5141 }
5142 }, filename);
5143 const suppressedMessages = linter.getSuppressedMessages();
5144
5145 assert.strictEqual(messages.length, 0);
5146 assert.strictEqual(suppressedMessages.length, 0);
5147 });
5148
5149 it("should properly parse global return when passed ecmaFeatures", () => {
5150
5151 const messages = linter.verify("return;", {
5152 parserOptions: {
5153 ecmaFeatures: {
5154 globalReturn: true
5155 }
5156 }
5157 }, filename);
5158 const suppressedMessages = linter.getSuppressedMessages();
5159
5160 assert.strictEqual(messages.length, 0);
5161 assert.strictEqual(suppressedMessages.length, 0);
5162 });
5163
5164 it("should properly parse global return when in Node.js environment", () => {
5165
5166 const messages = linter.verify("return;", {
5167 env: {
5168 node: true
5169 }
5170 }, filename);
5171 const suppressedMessages = linter.getSuppressedMessages();
5172
5173 assert.strictEqual(messages.length, 0);
5174 assert.strictEqual(suppressedMessages.length, 0);
5175 });
5176
5177 it("should not parse global return when in Node.js environment with globalReturn explicitly off", () => {
5178
5179 const messages = linter.verify("return;", {
5180 env: {
5181 node: true
5182 },
5183 parserOptions: {
5184 ecmaFeatures: {
5185 globalReturn: false
5186 }
5187 }
5188 }, filename);
5189 const suppressedMessages = linter.getSuppressedMessages();
5190
5191 assert.strictEqual(messages.length, 1);
5192 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5193
5194 assert.strictEqual(suppressedMessages.length, 0);
5195 });
5196
5197 it("should not parse global return when Node.js environment is false", () => {
5198
5199 const messages = linter.verify("return;", {}, filename);
5200 const suppressedMessages = linter.getSuppressedMessages();
5201
5202 assert.strictEqual(messages.length, 1);
5203 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5204
5205 assert.strictEqual(suppressedMessages.length, 0);
5206 });
5207
5208 it("should properly parse sloppy-mode code when impliedStrict is false", () => {
5209
5210 const messages = linter.verify("var private;", {}, filename);
5211 const suppressedMessages = linter.getSuppressedMessages();
5212
5213 assert.strictEqual(messages.length, 0);
5214 assert.strictEqual(suppressedMessages.length, 0);
5215 });
5216
5217 it("should not parse sloppy-mode code when impliedStrict is true", () => {
5218
5219 const messages = linter.verify("var private;", {
5220 parserOptions: {
5221 ecmaFeatures: {
5222 impliedStrict: true
5223 }
5224 }
5225 }, filename);
5226 const suppressedMessages = linter.getSuppressedMessages();
5227
5228 assert.strictEqual(messages.length, 1);
5229 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
5230
5231 assert.strictEqual(suppressedMessages.length, 0);
5232 });
5233
5234 it("should properly parse valid code when impliedStrict is true", () => {
5235
5236 const messages = linter.verify("var foo;", {
5237 parserOptions: {
5238 ecmaFeatures: {
5239 impliedStrict: true
5240 }
5241 }
5242 }, filename);
5243 const suppressedMessages = linter.getSuppressedMessages();
5244
5245 assert.strictEqual(messages.length, 0);
5246 assert.strictEqual(suppressedMessages.length, 0);
5247 });
5248
5249 it("should properly parse JSX when passed ecmaFeatures", () => {
5250
5251 const messages = linter.verify("var x = <div/>;", {
5252 parserOptions: {
5253 ecmaFeatures: {
5254 jsx: true
5255 }
5256 }
5257 }, filename);
5258 const suppressedMessages = linter.getSuppressedMessages();
5259
5260 assert.strictEqual(messages.length, 0);
5261 assert.strictEqual(suppressedMessages.length, 0);
5262 });
5263
5264 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
5265 const code = "var myDivElement = <div className=\"foo\" />;";
5266 const messages = linter.verify(code, {}, "filename");
5267 const suppressedMessages = linter.getSuppressedMessages();
5268
5269 assert.strictEqual(messages.length, 1);
5270 assert.strictEqual(messages[0].line, 1);
5271 assert.strictEqual(messages[0].column, 20);
5272 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
5273
5274 assert.strictEqual(suppressedMessages.length, 0);
5275 });
5276
5277 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
5278 const code = "var myDivElement = <div className=\"foo\" />;";
5279 const messages = linter.verify(code, { parserOptions: { ecmaFeatures: { jsx: true } } }, "filename");
5280 const suppressedMessages = linter.getSuppressedMessages();
5281
5282 assert.strictEqual(messages.length, 0);
5283 assert.strictEqual(suppressedMessages.length, 0);
5284 });
5285
5286 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
5287 const code = "var myDivElement = <div {...this.props} />;";
5288 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }, "filename");
5289 const suppressedMessages = linter.getSuppressedMessages();
5290
5291 assert.strictEqual(messages.length, 0);
5292 assert.strictEqual(suppressedMessages.length, 0);
5293 });
5294
5295 it("should not allow the use of reserved words as variable names in ES3", () => {
5296 const code = "var char;";
5297 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5298 const suppressedMessages = linter.getSuppressedMessages();
5299
5300 assert.strictEqual(messages.length, 1);
5301 assert.strictEqual(messages[0].severity, 2);
5302 assert.isTrue(messages[0].fatal);
5303 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5304
5305 assert.strictEqual(suppressedMessages.length, 0);
5306 });
5307
5308 it("should not allow the use of reserved words as property names in member expressions in ES3", () => {
5309 const code = "obj.char;";
5310 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5311 const suppressedMessages = linter.getSuppressedMessages();
5312
5313 assert.strictEqual(messages.length, 1);
5314 assert.strictEqual(messages[0].severity, 2);
5315 assert.isTrue(messages[0].fatal);
5316 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5317
5318 assert.strictEqual(suppressedMessages.length, 0);
5319 });
5320
5321 it("should not allow the use of reserved words as property names in object literals in ES3", () => {
5322 const code = "var obj = { char: 1 };";
5323 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5324 const suppressedMessages = linter.getSuppressedMessages();
5325
5326 assert.strictEqual(messages.length, 1);
5327 assert.strictEqual(messages[0].severity, 2);
5328 assert.isTrue(messages[0].fatal);
5329 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5330
5331 assert.strictEqual(suppressedMessages.length, 0);
5332 });
5333
5334 it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => {
5335 const code = "var char; obj.char; var obj = { char: 1 };";
5336 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3, allowReserved: true } }, filename);
5337 const suppressedMessages = linter.getSuppressedMessages();
5338
5339 assert.strictEqual(messages.length, 0);
5340 assert.strictEqual(suppressedMessages.length, 0);
5341 });
5342
5343 it("should not allow the use of reserved words as variable names in ES > 3", () => {
5344 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5345
5346 ecmaVersions.forEach(ecmaVersion => {
5347 const code = "var enum;";
5348 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
5349 const suppressedMessages = linter.getSuppressedMessages();
5350
5351 assert.strictEqual(messages.length, 1);
5352 assert.strictEqual(messages[0].severity, 2);
5353 assert.isTrue(messages[0].fatal);
5354 assert.match(messages[0].message, /^Parsing error:.*'enum'/u);
5355
5356 assert.strictEqual(suppressedMessages.length, 0);
5357 });
5358 });
5359
5360 it("should allow the use of reserved words as property names in ES > 3", () => {
5361 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5362
5363 ecmaVersions.forEach(ecmaVersion => {
5364 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
5365 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
5366 const suppressedMessages = linter.getSuppressedMessages();
5367
5368 assert.strictEqual(messages.length, 0);
5369 assert.strictEqual(suppressedMessages.length, 0);
5370 });
5371 });
5372
5373 it("should not allow `allowReserved: true` in ES > 3", () => {
5374 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5375
5376 ecmaVersions.forEach(ecmaVersion => {
5377 const code = "";
5378 const messages = linter.verify(code, { parserOptions: { ecmaVersion, allowReserved: true } }, filename);
5379 const suppressedMessages = linter.getSuppressedMessages();
5380
5381 assert.strictEqual(messages.length, 1);
5382 assert.strictEqual(messages[0].severity, 2);
5383 assert.isTrue(messages[0].fatal);
5384 assert.match(messages[0].message, /^Parsing error:.*allowReserved/u);
5385
5386 assert.strictEqual(suppressedMessages.length, 0);
5387 });
5388 });
5389
5390 it("should be able to use es6 features if there is a comment which has \"eslint-env es6\"", () => {
5391 const code = [
5392 "/* eslint-env es6 */",
5393 "var arrow = () => 0;",
5394 "var binary = 0b1010;",
5395 "{ let a = 0; const b = 1; }",
5396 "class A {}",
5397 "function defaultParams(a = 0) {}",
5398 "var {a = 1, b = 2} = {};",
5399 "for (var a of []) {}",
5400 "function* generator() { yield 0; }",
5401 "var computed = {[a]: 0};",
5402 "var duplicate = {dup: 0, dup: 1};",
5403 "var method = {foo() {}};",
5404 "var property = {a, b};",
5405 "var octal = 0o755;",
5406 "var u = /^.$/u.test('ð ®·');",
5407 "var y = /hello/y.test('hello');",
5408 "function restParam(a, ...rest) {}",
5409 "class B { superInFunc() { super.foo(); } }",
5410 "var template = `hello, ${a}`;",
5411 "var unicode = '\\u{20BB7}';"
5412 ].join("\n");
5413
5414 const messages = linter.verify(code, null, "eslint-env es6");
5415 const suppressedMessages = linter.getSuppressedMessages();
5416
5417 assert.strictEqual(messages.length, 0);
5418 assert.strictEqual(suppressedMessages.length, 0);
5419 });
5420
5421 it("should be able to return in global if there is a comment which enables the node environment with a comment", () => {
5422 const messages = linter.verify(`/* ${ESLINT_ENV} node */ return;`, null, "node environment");
5423 const suppressedMessages = linter.getSuppressedMessages();
5424
5425 assert.strictEqual(messages.length, 0);
5426 assert.strictEqual(suppressedMessages.length, 0);
5427 });
5428
5429 it("should attach a \"/*global\" comment node to declared variables", () => {
5430 const code = "/* global foo */\n/* global bar, baz */";
5431 let ok = false;
5432
5433 linter.defineRules({
5434 test: {
5435 create: context => ({
5436 Program() {
5437 const scope = context.getScope();
5438 const sourceCode = context.sourceCode;
5439 const comments = sourceCode.getAllComments();
5440
5441 assert.strictEqual(context.getSourceCode(), sourceCode);
5442 assert.strictEqual(2, comments.length);
5443
5444 const foo = getVariable(scope, "foo");
5445
5446 assert.strictEqual(foo.eslintExplicitGlobal, true);
5447 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
5448
5449 const bar = getVariable(scope, "bar");
5450
5451 assert.strictEqual(bar.eslintExplicitGlobal, true);
5452 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
5453
5454 const baz = getVariable(scope, "baz");
5455
5456 assert.strictEqual(baz.eslintExplicitGlobal, true);
5457 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
5458
5459 ok = true;
5460 }
5461 })
5462 }
5463 });
5464
5465 linter.verify(code, { rules: { test: 2 } });
5466 assert(ok);
5467 });
5468
5469 it("should report a linting error when a global is set to an invalid value", () => {
5470 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
5471 const suppressedMessages = linter.getSuppressedMessages();
5472
5473 assert.deepStrictEqual(results, [
5474 {
5475 ruleId: null,
5476 severity: 2,
5477 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
5478 line: 1,
5479 column: 1,
5480 endLine: 1,
5481 endColumn: 39,
5482 nodeType: null
5483 },
5484 {
5485 ruleId: "no-undef",
5486 messageId: "undef",
5487 severity: 2,
5488 message: "'foo' is not defined.",
5489 line: 2,
5490 column: 1,
5491 endLine: 2,
5492 endColumn: 4,
5493 nodeType: "Identifier"
5494 }
5495 ]);
5496
5497 assert.strictEqual(suppressedMessages.length, 0);
5498 });
5499
5500 it("should not crash when we reuse the SourceCode object", () => {
5501 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
5502 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
5503 });
5504
5505 it("should reuse the SourceCode object", () => {
5506 let ast1 = null,
5507 ast2 = null;
5508
5509 linter.defineRule("save-ast1", {
5510 create: () => ({
5511 Program(node) {
5512 ast1 = node;
5513 }
5514 })
5515 });
5516 linter.defineRule("save-ast2", {
5517 create: () => ({
5518 Program(node) {
5519 ast2 = node;
5520 }
5521 })
5522 });
5523
5524 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast1": 2 } });
5525 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast2": 2 } });
5526
5527 assert(ast1 !== null);
5528 assert(ast2 !== null);
5529 assert(ast1 === ast2);
5530 });
5531
5532 it("should allow 'await' as a property name in modules", () => {
5533 const result = linter.verify(
5534 "obj.await",
5535 { parserOptions: { ecmaVersion: 6, sourceType: "module" } }
5536 );
5537 const suppressedMessages = linter.getSuppressedMessages();
5538
5539 assert(result.length === 0);
5540 assert.strictEqual(suppressedMessages.length, 0);
5541 });
5542
5543
5544 it("should not modify config object passed as argument", () => {
5545 const config = {};
5546
5547 Object.freeze(config);
5548 linter.verify("var", config);
5549 });
5550
5551 it("should pass 'id' to rule contexts with the rule id", () => {
5552 const spy = sinon.spy(context => {
5553 assert.strictEqual(context.id, "foo-bar-baz");
5554 return {};
5555 });
5556
5557 linter.defineRule("foo-bar-baz", { create: spy });
5558 linter.verify("x", { rules: { "foo-bar-baz": "error" } });
5559 assert(spy.calledOnce);
5560 });
5561
5562 describe("descriptions in directive comments", () => {
5563 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
5564 const aaa = sinon.stub().returns({});
5565 const bbb = sinon.stub().returns({});
5566
5567 linter.defineRule("aaa", { create: aaa });
5568 linter.defineRule("bbb", { create: bbb });
5569 const messages = linter.verify(`
5570 /*eslint aaa:error -- bbb:error */
5571 console.log("hello")
5572 `, {});
5573 const suppressedMessages = linter.getSuppressedMessages();
5574
5575 // Don't include syntax error of the comment.
5576 assert.deepStrictEqual(messages, []);
5577
5578 // Use only `aaa`.
5579 assert.strictEqual(aaa.callCount, 1);
5580 assert.strictEqual(bbb.callCount, 0);
5581
5582 assert.strictEqual(suppressedMessages.length, 0);
5583 });
5584
5585 it("should ignore the part preceded by '--' in '/*eslint-env*/'.", () => {
5586 const messages = linter.verify(`
5587 /*eslint-env es2015 -- es2017 */
5588 var Promise = {}
5589 var Atomics = {}
5590 `, { rules: { "no-redeclare": "error" } });
5591 const suppressedMessages = linter.getSuppressedMessages();
5592
5593 // Don't include `Atomics`
5594 assert.deepStrictEqual(
5595 messages,
5596 [{
5597 column: 25,
5598 endColumn: 32,
5599 endLine: 3,
5600 line: 3,
5601 message: "'Promise' is already defined as a built-in global variable.",
5602 messageId: "redeclaredAsBuiltin",
5603 nodeType: "Identifier",
5604 ruleId: "no-redeclare",
5605 severity: 2
5606 }]
5607 );
5608
5609 assert.strictEqual(suppressedMessages.length, 0);
5610 });
5611
5612 it("should ignore the part preceded by '--' in '/*global*/'.", () => {
5613 const messages = linter.verify(`
5614 /*global aaa -- bbb */
5615 var aaa = {}
5616 var bbb = {}
5617 `, { rules: { "no-redeclare": "error" } });
5618 const suppressedMessages = linter.getSuppressedMessages();
5619
5620 // Don't include `bbb`
5621 assert.deepStrictEqual(
5622 messages,
5623 [{
5624 column: 30,
5625 endColumn: 33,
5626 line: 2,
5627 endLine: 2,
5628 message: "'aaa' is already defined by a variable declaration.",
5629 messageId: "redeclaredBySyntax",
5630 nodeType: "Block",
5631 ruleId: "no-redeclare",
5632 severity: 2
5633 }]
5634 );
5635
5636 assert.strictEqual(suppressedMessages.length, 0);
5637 });
5638
5639 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
5640 const messages = linter.verify(`
5641 /*globals aaa -- bbb */
5642 var aaa = {}
5643 var bbb = {}
5644 `, { rules: { "no-redeclare": "error" } });
5645 const suppressedMessages = linter.getSuppressedMessages();
5646
5647 // Don't include `bbb`
5648 assert.deepStrictEqual(
5649 messages,
5650 [{
5651 column: 31,
5652 endColumn: 34,
5653 line: 2,
5654 endLine: 2,
5655 message: "'aaa' is already defined by a variable declaration.",
5656 messageId: "redeclaredBySyntax",
5657 nodeType: "Block",
5658 ruleId: "no-redeclare",
5659 severity: 2
5660 }]
5661 );
5662
5663 assert.strictEqual(suppressedMessages.length, 0);
5664 });
5665
5666 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
5667 const messages = linter.verify(`
5668 /*exported aaa -- bbb */
5669 var aaa = {}
5670 var bbb = {}
5671 `, { rules: { "no-unused-vars": "error" } });
5672 const suppressedMessages = linter.getSuppressedMessages();
5673
5674 // Don't include `aaa`
5675 assert.deepStrictEqual(
5676 messages,
5677 [{
5678 column: 25,
5679 endColumn: 28,
5680 endLine: 4,
5681 line: 4,
5682 message: "'bbb' is assigned a value but never used.",
5683 messageId: "unusedVar",
5684 nodeType: "Identifier",
5685 ruleId: "no-unused-vars",
5686 severity: 2
5687 }]
5688 );
5689
5690 assert.strictEqual(suppressedMessages.length, 0);
5691 });
5692
5693 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
5694 const messages = linter.verify(`
5695 /*eslint-disable no-redeclare -- no-unused-vars */
5696 var aaa = {}
5697 var aaa = {}
5698 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5699 const suppressedMessages = linter.getSuppressedMessages();
5700
5701 // Do include `no-unused-vars` but not `no-redeclare`
5702 assert.deepStrictEqual(
5703 messages,
5704 [{
5705 column: 25,
5706 endLine: 4,
5707 endColumn: 28,
5708 line: 4,
5709 message: "'aaa' is assigned a value but never used.",
5710 messageId: "unusedVar",
5711 nodeType: "Identifier",
5712 ruleId: "no-unused-vars",
5713 severity: 2
5714 }]
5715 );
5716
5717 assert.deepStrictEqual(
5718 suppressedMessages,
5719 [{
5720 column: 25,
5721 endLine: 4,
5722 endColumn: 28,
5723 line: 4,
5724 message: "'aaa' is already defined.",
5725 messageId: "redeclared",
5726 nodeType: "Identifier",
5727 ruleId: "no-redeclare",
5728 severity: 2,
5729 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5730 }]
5731 );
5732 });
5733
5734 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
5735 const messages = linter.verify(`
5736 /*eslint-disable no-redeclare, no-unused-vars */
5737 /*eslint-enable no-redeclare -- no-unused-vars */
5738 var aaa = {}
5739 var aaa = {}
5740 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5741 const suppressedMessages = linter.getSuppressedMessages();
5742
5743 // Do include `no-redeclare` but not `no-unused-vars`
5744 assert.deepStrictEqual(
5745 messages,
5746 [{
5747 column: 25,
5748 endLine: 5,
5749 endColumn: 28,
5750 line: 5,
5751 message: "'aaa' is already defined.",
5752 messageId: "redeclared",
5753 nodeType: "Identifier",
5754 ruleId: "no-redeclare",
5755 severity: 2
5756 }]
5757 );
5758
5759 assert.deepStrictEqual(
5760 suppressedMessages,
5761 [{
5762 column: 25,
5763 endLine: 5,
5764 endColumn: 28,
5765 line: 5,
5766 message: "'aaa' is assigned a value but never used.",
5767 messageId: "unusedVar",
5768 nodeType: "Identifier",
5769 ruleId: "no-unused-vars",
5770 severity: 2,
5771 suppressions: [{ kind: "directive", justification: "" }]
5772 }]
5773 );
5774 });
5775
5776 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
5777 const messages = linter.verify(`
5778 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
5779 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
5780 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5781 const suppressedMessages = linter.getSuppressedMessages();
5782
5783 // Do include `no-unused-vars` but not `no-redeclare`
5784 assert.deepStrictEqual(
5785 messages,
5786 [{
5787 column: 25,
5788 endLine: 3,
5789 endColumn: 28,
5790 line: 3,
5791 message: "'aaa' is assigned a value but never used.",
5792 messageId: "unusedVar",
5793 nodeType: "Identifier",
5794 ruleId: "no-unused-vars",
5795 severity: 2
5796 }]
5797 );
5798
5799 assert.deepStrictEqual(
5800 suppressedMessages,
5801 [{
5802 column: 25,
5803 endLine: 3,
5804 endColumn: 28,
5805 line: 3,
5806 message: "'aaa' is already defined.",
5807 messageId: "redeclared",
5808 nodeType: "Identifier",
5809 ruleId: "no-redeclare",
5810 severity: 2,
5811 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5812 }]
5813 );
5814 });
5815
5816 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
5817 const messages = linter.verify(`
5818 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
5819 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
5820 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5821 const suppressedMessages = linter.getSuppressedMessages();
5822
5823 // Do include `no-unused-vars` but not `no-redeclare`
5824 assert.deepStrictEqual(
5825 messages,
5826 [{
5827 column: 25,
5828 endLine: 3,
5829 endColumn: 28,
5830 line: 3,
5831 message: "'aaa' is assigned a value but never used.",
5832 messageId: "unusedVar",
5833 nodeType: "Identifier",
5834 ruleId: "no-unused-vars",
5835 severity: 2
5836 }]
5837 );
5838
5839 assert.deepStrictEqual(
5840 suppressedMessages,
5841 [{
5842 column: 25,
5843 endLine: 3,
5844 endColumn: 28,
5845 line: 3,
5846 message: "'aaa' is already defined.",
5847 messageId: "redeclared",
5848 nodeType: "Identifier",
5849 ruleId: "no-redeclare",
5850 severity: 2,
5851 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5852 }]
5853 );
5854 });
5855
5856 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
5857 const messages = linter.verify(`
5858 //eslint-disable-next-line no-redeclare -- no-unused-vars
5859 var aaa = {}
5860 //eslint-disable-next-line no-redeclare -- no-unused-vars
5861 var aaa = {}
5862 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5863 const suppressedMessages = linter.getSuppressedMessages();
5864
5865 // Do include `no-unused-vars` but not `no-redeclare`
5866 assert.deepStrictEqual(
5867 messages,
5868 [{
5869 column: 25,
5870 endLine: 5,
5871 endColumn: 28,
5872 line: 5,
5873 message: "'aaa' is assigned a value but never used.",
5874 messageId: "unusedVar",
5875 nodeType: "Identifier",
5876 ruleId: "no-unused-vars",
5877 severity: 2
5878 }]
5879 );
5880
5881 assert.deepStrictEqual(
5882 suppressedMessages,
5883 [{
5884 column: 25,
5885 endLine: 5,
5886 endColumn: 28,
5887 line: 5,
5888 message: "'aaa' is already defined.",
5889 messageId: "redeclared",
5890 nodeType: "Identifier",
5891 ruleId: "no-redeclare",
5892 severity: 2,
5893 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5894 }]
5895 );
5896 });
5897
5898 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
5899 const messages = linter.verify(`
5900 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
5901 var aaa = {}
5902 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
5903 var aaa = {}
5904 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5905 const suppressedMessages = linter.getSuppressedMessages();
5906
5907 // Do include `no-unused-vars` but not `no-redeclare`
5908 assert.deepStrictEqual(
5909 messages,
5910 [{
5911 column: 25,
5912 endLine: 5,
5913 endColumn: 28,
5914 line: 5,
5915 message: "'aaa' is assigned a value but never used.",
5916 messageId: "unusedVar",
5917 nodeType: "Identifier",
5918 ruleId: "no-unused-vars",
5919 severity: 2
5920 }]
5921 );
5922
5923 assert.deepStrictEqual(
5924 suppressedMessages,
5925 [{
5926 column: 25,
5927 endLine: 5,
5928 endColumn: 28,
5929 line: 5,
5930 message: "'aaa' is already defined.",
5931 messageId: "redeclared",
5932 nodeType: "Identifier",
5933 ruleId: "no-redeclare",
5934 severity: 2,
5935 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5936 }]
5937 );
5938 });
5939
5940 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
5941 const rule = sinon.stub().returns({});
5942
5943 linter.defineRule("a--rule", { create: rule });
5944 const messages = linter.verify(`
5945 /*eslint a--rule:error */
5946 console.log("hello")
5947 `, {});
5948 const suppressedMessages = linter.getSuppressedMessages();
5949
5950 // Don't include syntax error of the comment.
5951 assert.deepStrictEqual(messages, []);
5952
5953 // Use `a--rule`.
5954 assert.strictEqual(rule.callCount, 1);
5955
5956 assert.strictEqual(suppressedMessages.length, 0);
5957 });
5958
5959 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
5960 const aaa = sinon.stub().returns({});
5961 const bbb = sinon.stub().returns({});
5962
5963 linter.defineRule("aaa", { create: aaa });
5964 linter.defineRule("bbb", { create: bbb });
5965 const messages = linter.verify(`
5966 /*eslint aaa:error -------- bbb:error */
5967 console.log("hello")
5968 `, {});
5969 const suppressedMessages = linter.getSuppressedMessages();
5970
5971 // Don't include syntax error of the comment.
5972 assert.deepStrictEqual(messages, []);
5973
5974 // Use only `aaa`.
5975 assert.strictEqual(aaa.callCount, 1);
5976 assert.strictEqual(bbb.callCount, 0);
5977
5978 assert.strictEqual(suppressedMessages.length, 0);
5979 });
5980
5981 it("should ignore the part preceded by '--' with line breaks.", () => {
5982 const aaa = sinon.stub().returns({});
5983 const bbb = sinon.stub().returns({});
5984
5985 linter.defineRule("aaa", { create: aaa });
5986 linter.defineRule("bbb", { create: bbb });
5987 const messages = linter.verify(`
5988 /*eslint aaa:error
5989 --------
5990 bbb:error */
5991 console.log("hello")
5992 `, {});
5993 const suppressedMessages = linter.getSuppressedMessages();
5994
5995 // Don't include syntax error of the comment.
5996 assert.deepStrictEqual(messages, []);
5997
5998 // Use only `aaa`.
5999 assert.strictEqual(aaa.callCount, 1);
6000 assert.strictEqual(bbb.callCount, 0);
6001
6002 assert.strictEqual(suppressedMessages.length, 0);
6003 });
6004 });
6005 });
6006
6007 describe("context.getScope()", () => {
6008
6009 /**
6010 * Get the scope on the node `astSelector` specified.
6011 * @param {string} code The source code to verify.
6012 * @param {string} astSelector The AST selector to get scope.
6013 * @param {number} [ecmaVersion=5] The ECMAScript version.
6014 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
6015 */
6016 function getScope(code, astSelector, ecmaVersion = 5) {
6017 let node, scope;
6018
6019 linter.defineRule("get-scope", {
6020 create: context => ({
6021 [astSelector](node0) {
6022 node = node0;
6023 scope = context.getScope();
6024 }
6025 })
6026 });
6027 linter.verify(
6028 code,
6029 {
6030 parserOptions: { ecmaVersion },
6031 rules: { "get-scope": 2 }
6032 }
6033 );
6034
6035 return { node, scope };
6036 }
6037
6038 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
6039 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
6040
6041 assert.strictEqual(scope.type, "function");
6042 assert.strictEqual(scope.block, node);
6043 });
6044
6045 it("should return 'function' scope on FunctionExpression (ES5)", () => {
6046 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
6047
6048 assert.strictEqual(scope.type, "function");
6049 assert.strictEqual(scope.block, node);
6050 });
6051
6052 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
6053 const { node, scope } = getScope("function f() {}", "BlockStatement");
6054
6055 assert.strictEqual(scope.type, "function");
6056 assert.strictEqual(scope.block, node.parent);
6057 });
6058
6059 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
6060 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
6061
6062 assert.strictEqual(scope.type, "function");
6063 assert.strictEqual(scope.block, node.parent);
6064 });
6065
6066 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
6067 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
6068
6069 assert.strictEqual(scope.type, "function");
6070 assert.strictEqual(scope.block, node.parent.parent);
6071 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
6072 });
6073
6074 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
6075 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
6076
6077 assert.strictEqual(scope.type, "block");
6078 assert.strictEqual(scope.upper.type, "function");
6079 assert.strictEqual(scope.block, node);
6080 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
6081 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
6082 });
6083
6084 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
6085 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
6086
6087 assert.strictEqual(scope.type, "block");
6088 assert.strictEqual(scope.upper.type, "block");
6089 assert.strictEqual(scope.upper.upper.type, "function");
6090 assert.strictEqual(scope.block, node);
6091 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
6092 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
6093 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
6094 });
6095
6096 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
6097 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
6098
6099 assert.strictEqual(scope.type, "function");
6100 assert.strictEqual(scope.block, node.parent.parent);
6101 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
6102 });
6103
6104 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
6105 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
6106
6107 assert.strictEqual(scope.type, "switch");
6108 assert.strictEqual(scope.block, node);
6109 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
6110 });
6111
6112 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
6113 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
6114
6115 assert.strictEqual(scope.type, "function");
6116 assert.strictEqual(scope.block, node.parent.parent.parent);
6117 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
6118 });
6119
6120 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
6121 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
6122
6123 assert.strictEqual(scope.type, "switch");
6124 assert.strictEqual(scope.block, node.parent);
6125 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
6126 });
6127
6128 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
6129 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
6130
6131 assert.strictEqual(scope.type, "catch");
6132 assert.strictEqual(scope.block, node);
6133 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
6134 });
6135
6136 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
6137 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
6138
6139 assert.strictEqual(scope.type, "catch");
6140 assert.strictEqual(scope.block, node);
6141 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
6142 });
6143
6144 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
6145 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
6146
6147 assert.strictEqual(scope.type, "catch");
6148 assert.strictEqual(scope.block, node.parent);
6149 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
6150 });
6151
6152 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
6153 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
6154
6155 assert.strictEqual(scope.type, "block");
6156 assert.strictEqual(scope.block, node);
6157 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
6158 });
6159
6160 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
6161 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
6162
6163 assert.strictEqual(scope.type, "function");
6164 assert.strictEqual(scope.block, node.parent.parent);
6165 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
6166 });
6167
6168 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
6169 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
6170
6171 assert.strictEqual(scope.type, "for");
6172 assert.strictEqual(scope.block, node);
6173 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
6174 });
6175
6176 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
6177 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
6178
6179 assert.strictEqual(scope.type, "function");
6180 assert.strictEqual(scope.block, node.parent.parent.parent);
6181 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
6182 });
6183
6184 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
6185 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
6186
6187 assert.strictEqual(scope.type, "block");
6188 assert.strictEqual(scope.upper.type, "for");
6189 assert.strictEqual(scope.block, node);
6190 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6191 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
6192 });
6193
6194 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
6195 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
6196
6197 assert.strictEqual(scope.type, "function");
6198 assert.strictEqual(scope.block, node.parent.parent);
6199 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
6200 });
6201
6202 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
6203 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
6204
6205 assert.strictEqual(scope.type, "for");
6206 assert.strictEqual(scope.block, node);
6207 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
6208 });
6209
6210 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
6211 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
6212
6213 assert.strictEqual(scope.type, "function");
6214 assert.strictEqual(scope.block, node.parent.parent.parent);
6215 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
6216 });
6217
6218 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
6219 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
6220
6221 assert.strictEqual(scope.type, "block");
6222 assert.strictEqual(scope.upper.type, "for");
6223 assert.strictEqual(scope.block, node);
6224 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6225 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
6226 });
6227
6228 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
6229 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
6230
6231 assert.strictEqual(scope.type, "for");
6232 assert.strictEqual(scope.block, node);
6233 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
6234 });
6235
6236 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
6237 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
6238
6239 assert.strictEqual(scope.type, "block");
6240 assert.strictEqual(scope.upper.type, "for");
6241 assert.strictEqual(scope.block, node);
6242 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6243 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
6244 });
6245
6246 it("should shadow the same name variable by the iteration variable.", () => {
6247 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
6248
6249 assert.strictEqual(scope.type, "for");
6250 assert.strictEqual(scope.upper.type, "global");
6251 assert.strictEqual(scope.block, node);
6252 assert.strictEqual(scope.upper.variables[0].references.length, 0);
6253 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
6254 assert.strictEqual(scope.references[1].identifier, node.right);
6255 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
6256 });
6257 });
6258
6259 describe("Variables and references", () => {
6260 const code = [
6261 "a;",
6262 "function foo() { b; }",
6263 "Object;",
6264 "foo;",
6265 "var c;",
6266 "c;",
6267 "/* global d */",
6268 "d;",
6269 "e;",
6270 "f;"
6271 ].join("\n");
6272 let scope = null;
6273
6274 beforeEach(() => {
6275 let ok = false;
6276
6277 linter.defineRules({
6278 test: {
6279 create: context => ({
6280 Program() {
6281 scope = context.getScope();
6282 ok = true;
6283 }
6284 })
6285 }
6286 });
6287 linter.verify(code, { rules: { test: 2 }, globals: { e: true, f: false } });
6288 assert(ok);
6289 });
6290
6291 afterEach(() => {
6292 scope = null;
6293 });
6294
6295 it("Scope#through should contain references of undefined variables", () => {
6296 assert.strictEqual(scope.through.length, 2);
6297 assert.strictEqual(scope.through[0].identifier.name, "a");
6298 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
6299 assert.strictEqual(scope.through[0].resolved, null);
6300 assert.strictEqual(scope.through[1].identifier.name, "b");
6301 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
6302 assert.strictEqual(scope.through[1].resolved, null);
6303 });
6304
6305 it("Scope#variables should contain global variables", () => {
6306 assert(scope.variables.some(v => v.name === "Object"));
6307 assert(scope.variables.some(v => v.name === "foo"));
6308 assert(scope.variables.some(v => v.name === "c"));
6309 assert(scope.variables.some(v => v.name === "d"));
6310 assert(scope.variables.some(v => v.name === "e"));
6311 assert(scope.variables.some(v => v.name === "f"));
6312 });
6313
6314 it("Scope#set should contain global variables", () => {
6315 assert(scope.set.get("Object"));
6316 assert(scope.set.get("foo"));
6317 assert(scope.set.get("c"));
6318 assert(scope.set.get("d"));
6319 assert(scope.set.get("e"));
6320 assert(scope.set.get("f"));
6321 });
6322
6323 it("Variables#references should contain their references", () => {
6324 assert.strictEqual(scope.set.get("Object").references.length, 1);
6325 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
6326 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
6327 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
6328 assert.strictEqual(scope.set.get("foo").references.length, 1);
6329 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
6330 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
6331 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
6332 assert.strictEqual(scope.set.get("c").references.length, 1);
6333 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
6334 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
6335 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
6336 assert.strictEqual(scope.set.get("d").references.length, 1);
6337 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
6338 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
6339 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
6340 assert.strictEqual(scope.set.get("e").references.length, 1);
6341 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
6342 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
6343 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
6344 assert.strictEqual(scope.set.get("f").references.length, 1);
6345 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
6346 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
6347 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
6348 });
6349
6350 it("Reference#resolved should be their variable", () => {
6351 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
6352 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
6353 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
6354 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
6355 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
6356 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
6357 });
6358 });
6359
6360 describe("context.getDeclaredVariables(node)", () => {
6361
6362 /**
6363 * Assert `context.getDeclaredVariables(node)` is valid.
6364 * @param {string} code A code to check.
6365 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
6366 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
6367 * @returns {void}
6368 */
6369 function verify(code, type, expectedNamesList) {
6370 linter.defineRules({
6371 test: {
6372 create(context) {
6373
6374 /**
6375 * Assert `context.getDeclaredVariables(node)` is empty.
6376 * @param {ASTNode} node A node to check.
6377 * @returns {void}
6378 */
6379 function checkEmpty(node) {
6380 assert.strictEqual(0, context.getDeclaredVariables(node).length);
6381 }
6382 const rule = {
6383 Program: checkEmpty,
6384 EmptyStatement: checkEmpty,
6385 BlockStatement: checkEmpty,
6386 ExpressionStatement: checkEmpty,
6387 LabeledStatement: checkEmpty,
6388 BreakStatement: checkEmpty,
6389 ContinueStatement: checkEmpty,
6390 WithStatement: checkEmpty,
6391 SwitchStatement: checkEmpty,
6392 ReturnStatement: checkEmpty,
6393 ThrowStatement: checkEmpty,
6394 TryStatement: checkEmpty,
6395 WhileStatement: checkEmpty,
6396 DoWhileStatement: checkEmpty,
6397 ForStatement: checkEmpty,
6398 ForInStatement: checkEmpty,
6399 DebuggerStatement: checkEmpty,
6400 ThisExpression: checkEmpty,
6401 ArrayExpression: checkEmpty,
6402 ObjectExpression: checkEmpty,
6403 Property: checkEmpty,
6404 SequenceExpression: checkEmpty,
6405 UnaryExpression: checkEmpty,
6406 BinaryExpression: checkEmpty,
6407 AssignmentExpression: checkEmpty,
6408 UpdateExpression: checkEmpty,
6409 LogicalExpression: checkEmpty,
6410 ConditionalExpression: checkEmpty,
6411 CallExpression: checkEmpty,
6412 NewExpression: checkEmpty,
6413 MemberExpression: checkEmpty,
6414 SwitchCase: checkEmpty,
6415 Identifier: checkEmpty,
6416 Literal: checkEmpty,
6417 ForOfStatement: checkEmpty,
6418 ArrowFunctionExpression: checkEmpty,
6419 YieldExpression: checkEmpty,
6420 TemplateLiteral: checkEmpty,
6421 TaggedTemplateExpression: checkEmpty,
6422 TemplateElement: checkEmpty,
6423 ObjectPattern: checkEmpty,
6424 ArrayPattern: checkEmpty,
6425 RestElement: checkEmpty,
6426 AssignmentPattern: checkEmpty,
6427 ClassBody: checkEmpty,
6428 MethodDefinition: checkEmpty,
6429 MetaProperty: checkEmpty
6430 };
6431
6432 rule[type] = function(node) {
6433 const expectedNames = expectedNamesList.shift();
6434 const variables = context.getDeclaredVariables(node);
6435
6436 assert(Array.isArray(expectedNames));
6437 assert(Array.isArray(variables));
6438 assert.strictEqual(expectedNames.length, variables.length);
6439 for (let i = variables.length - 1; i >= 0; i--) {
6440 assert.strictEqual(expectedNames[i], variables[i].name);
6441 }
6442 };
6443 return rule;
6444 }
6445 }
6446 });
6447 linter.verify(code, {
6448 rules: { test: 2 },
6449 parserOptions: {
6450 ecmaVersion: 6,
6451 sourceType: "module"
6452 }
6453 });
6454
6455 // Check all expected names are asserted.
6456 assert.strictEqual(0, expectedNamesList.length);
6457 }
6458
6459 it("VariableDeclaration", () => {
6460 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 ";
6461 const namesList = [
6462 ["a", "b", "c"],
6463 ["d", "e", "f"],
6464 ["g", "h", "i", "j", "k"],
6465 ["l"]
6466 ];
6467
6468 verify(code, "VariableDeclaration", namesList);
6469 });
6470
6471 it("VariableDeclaration (on for-in/of loop)", () => {
6472
6473 // TDZ scope is created here, so tests to exclude those.
6474 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 ";
6475 const namesList = [
6476 ["a", "b", "c"],
6477 ["g"],
6478 ["d", "e", "f"],
6479 ["h"]
6480 ];
6481
6482 verify(code, "VariableDeclaration", namesList);
6483 });
6484
6485 it("VariableDeclarator", () => {
6486
6487 // TDZ scope is created here, so tests to exclude those.
6488 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 ";
6489 const namesList = [
6490 ["a", "b", "c"],
6491 ["d", "e", "f"],
6492 ["g", "h", "i"],
6493 ["j", "k"],
6494 ["l"]
6495 ];
6496
6497 verify(code, "VariableDeclarator", namesList);
6498 });
6499
6500 it("FunctionDeclaration", () => {
6501 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 ";
6502 const namesList = [
6503 ["foo", "a", "b", "c", "d", "e"],
6504 ["bar", "f", "g", "h", "i", "j"]
6505 ];
6506
6507 verify(code, "FunctionDeclaration", namesList);
6508 });
6509
6510 it("FunctionExpression", () => {
6511 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 ";
6512 const namesList = [
6513 ["foo", "a", "b", "c", "d", "e"],
6514 ["bar", "f", "g", "h", "i", "j"],
6515 ["q"]
6516 ];
6517
6518 verify(code, "FunctionExpression", namesList);
6519 });
6520
6521 it("ArrowFunctionExpression", () => {
6522 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 ";
6523 const namesList = [
6524 ["a", "b", "c", "d", "e"],
6525 ["f", "g", "h", "i", "j"]
6526 ];
6527
6528 verify(code, "ArrowFunctionExpression", namesList);
6529 });
6530
6531 it("ClassDeclaration", () => {
6532 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
6533 const namesList = [
6534 ["A", "A"], // outer scope's and inner scope's.
6535 ["B", "B"]
6536 ];
6537
6538 verify(code, "ClassDeclaration", namesList);
6539 });
6540
6541 it("ClassExpression", () => {
6542 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
6543 const namesList = [
6544 ["A"],
6545 ["B"]
6546 ];
6547
6548 verify(code, "ClassExpression", namesList);
6549 });
6550
6551 it("CatchClause", () => {
6552 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
6553 const namesList = [
6554 ["a", "b"],
6555 ["c", "d"]
6556 ];
6557
6558 verify(code, "CatchClause", namesList);
6559 });
6560
6561 it("ImportDeclaration", () => {
6562 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6563 const namesList = [
6564 [],
6565 ["a"],
6566 ["b", "c", "d"]
6567 ];
6568
6569 verify(code, "ImportDeclaration", namesList);
6570 });
6571
6572 it("ImportSpecifier", () => {
6573 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6574 const namesList = [
6575 ["c"],
6576 ["d"]
6577 ];
6578
6579 verify(code, "ImportSpecifier", namesList);
6580 });
6581
6582 it("ImportDefaultSpecifier", () => {
6583 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6584 const namesList = [
6585 ["b"]
6586 ];
6587
6588 verify(code, "ImportDefaultSpecifier", namesList);
6589 });
6590
6591 it("ImportNamespaceSpecifier", () => {
6592 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6593 const namesList = [
6594 ["a"]
6595 ];
6596
6597 verify(code, "ImportNamespaceSpecifier", namesList);
6598 });
6599 });
6600
6601 describe("suggestions", () => {
6602 it("provides suggestion information for tools to use", () => {
6603 linter.defineRule("rule-with-suggestions", {
6604 meta: { hasSuggestions: true },
6605 create: context => ({
6606 Program(node) {
6607 context.report({
6608 node,
6609 message: "Incorrect spacing",
6610 suggest: [{
6611 desc: "Insert space at the beginning",
6612 fix: fixer => fixer.insertTextBefore(node, " ")
6613 }, {
6614 desc: "Insert space at the end",
6615 fix: fixer => fixer.insertTextAfter(node, " ")
6616 }]
6617 });
6618 }
6619 })
6620 });
6621
6622 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6623 const suppressedMessages = linter.getSuppressedMessages();
6624
6625 assert.deepStrictEqual(messages[0].suggestions, [{
6626 desc: "Insert space at the beginning",
6627 fix: {
6628 range: [0, 0],
6629 text: " "
6630 }
6631 }, {
6632 desc: "Insert space at the end",
6633 fix: {
6634 range: [10, 10],
6635 text: " "
6636 }
6637 }]);
6638
6639 assert.strictEqual(suppressedMessages.length, 0);
6640 });
6641
6642 it("supports messageIds for suggestions", () => {
6643 linter.defineRule("rule-with-suggestions", {
6644 meta: {
6645 messages: {
6646 suggestion1: "Insert space at the beginning",
6647 suggestion2: "Insert space at the end"
6648 },
6649 hasSuggestions: true
6650 },
6651 create: context => ({
6652 Program(node) {
6653 context.report({
6654 node,
6655 message: "Incorrect spacing",
6656 suggest: [{
6657 messageId: "suggestion1",
6658 fix: fixer => fixer.insertTextBefore(node, " ")
6659 }, {
6660 messageId: "suggestion2",
6661 fix: fixer => fixer.insertTextAfter(node, " ")
6662 }]
6663 });
6664 }
6665 })
6666 });
6667
6668 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6669 const suppressedMessages = linter.getSuppressedMessages();
6670
6671 assert.deepStrictEqual(messages[0].suggestions, [{
6672 messageId: "suggestion1",
6673 desc: "Insert space at the beginning",
6674 fix: {
6675 range: [0, 0],
6676 text: " "
6677 }
6678 }, {
6679 messageId: "suggestion2",
6680 desc: "Insert space at the end",
6681 fix: {
6682 range: [10, 10],
6683 text: " "
6684 }
6685 }]);
6686
6687 assert.strictEqual(suppressedMessages.length, 0);
6688 });
6689
6690 it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled", () => {
6691 linter.defineRule("rule-with-suggestions", {
6692 meta: { docs: {}, schema: [] },
6693 create: context => ({
6694 Program(node) {
6695 context.report({
6696 node,
6697 message: "hello world",
6698 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
6699 });
6700 }
6701 })
6702 });
6703
6704 assert.throws(() => {
6705 linter.verify("0", { rules: { "rule-with-suggestions": "error" } });
6706 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
6707 });
6708
6709 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", () => {
6710 linter.defineRule("rule-with-meta-docs-suggestion", {
6711 meta: { docs: { suggestion: true }, schema: [] },
6712 create: context => ({
6713 Program(node) {
6714 context.report({
6715 node,
6716 message: "hello world",
6717 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
6718 });
6719 }
6720 })
6721 });
6722
6723 assert.throws(() => {
6724 linter.verify("0", { rules: { "rule-with-meta-docs-suggestion": "error" } });
6725 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint.");
6726 });
6727 });
6728
6729 describe("mutability", () => {
6730 let linter1 = null;
6731 let linter2 = null;
6732
6733 beforeEach(() => {
6734 linter1 = new Linter();
6735 linter2 = new Linter();
6736 });
6737
6738 describe("rules", () => {
6739 it("with no changes, same rules are loaded", () => {
6740 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
6741 });
6742
6743 it("loading rule in one doesn't change the other", () => {
6744 linter1.defineRule("mock-rule", {
6745 create: () => ({})
6746 });
6747
6748 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
6749 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
6750 });
6751 });
6752 });
6753
6754 describe("processors", () => {
6755 let receivedFilenames = [];
6756 let receivedPhysicalFilenames = [];
6757
6758 beforeEach(() => {
6759 receivedFilenames = [];
6760 receivedPhysicalFilenames = [];
6761
6762 // A rule that always reports the AST with a message equal to the source text
6763 linter.defineRule("report-original-text", {
6764 create: context => ({
6765 Program(ast) {
6766 assert.strictEqual(context.getFilename(), context.filename);
6767 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
6768
6769 receivedFilenames.push(context.filename);
6770 receivedPhysicalFilenames.push(context.physicalFilename);
6771
6772 context.report({ node: ast, message: context.sourceCode.text });
6773 }
6774 })
6775 });
6776 });
6777
6778 describe("preprocessors", () => {
6779 it("should receive text and filename.", () => {
6780 const code = "foo bar baz";
6781 const preprocess = sinon.spy(text => text.split(" "));
6782
6783 linter.verify(code, {}, { filename, preprocess });
6784
6785 assert.strictEqual(preprocess.calledOnce, true);
6786 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6787 });
6788
6789 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
6790 const code = "foo bar baz";
6791 const problems = linter.verify(
6792 code,
6793 { rules: { "report-original-text": "error" } },
6794 {
6795
6796 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6797 preprocess(input) {
6798 return input.split(" ");
6799 }
6800 }
6801 );
6802
6803 assert.strictEqual(problems.length, 3);
6804 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
6805 });
6806
6807 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
6808 const code = "foo bar baz";
6809 const problems = linter.verify(
6810 code,
6811 { rules: { "report-original-text": "error" } },
6812 {
6813 filename,
6814
6815 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6816 preprocess(input) {
6817 return input.split(" ").map(text => ({
6818 filename: "block.js",
6819 text
6820 }));
6821 }
6822 }
6823 );
6824
6825 assert.strictEqual(problems.length, 3);
6826 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
6827
6828 // filename
6829 assert.strictEqual(receivedFilenames.length, 3);
6830 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
6831 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
6832 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
6833
6834 // physical filename
6835 assert.strictEqual(receivedPhysicalFilenames.length, 3);
6836 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
6837 });
6838
6839 it("should receive text even if a SourceCode object was given.", () => {
6840 const code = "foo";
6841 const preprocess = sinon.spy(text => text.split(" "));
6842
6843 linter.verify(code, {});
6844 const sourceCode = linter.getSourceCode();
6845
6846 linter.verify(sourceCode, {}, { filename, preprocess });
6847
6848 assert.strictEqual(preprocess.calledOnce, true);
6849 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6850 });
6851
6852 it("should receive text even if a SourceCode object was given (with BOM).", () => {
6853 const code = "\uFEFFfoo";
6854 const preprocess = sinon.spy(text => text.split(" "));
6855
6856 linter.verify(code, {});
6857 const sourceCode = linter.getSourceCode();
6858
6859 linter.verify(sourceCode, {}, { filename, preprocess });
6860
6861 assert.strictEqual(preprocess.calledOnce, true);
6862 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6863 });
6864
6865 it("should catch preprocess error.", () => {
6866 const code = "foo";
6867 const preprocess = sinon.spy(() => {
6868 throw Object.assign(new SyntaxError("Invalid syntax"), {
6869 lineNumber: 1,
6870 column: 1
6871 });
6872 });
6873
6874 const messages = linter.verify(code, {}, { filename, preprocess });
6875
6876 assert.strictEqual(preprocess.calledOnce, true);
6877 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6878 assert.deepStrictEqual(messages, [
6879 {
6880 ruleId: null,
6881 fatal: true,
6882 severity: 2,
6883 message: "Preprocessing error: Invalid syntax",
6884 line: 1,
6885 column: 1,
6886 nodeType: null
6887 }
6888 ]);
6889 });
6890 });
6891
6892 describe("postprocessors", () => {
6893 it("should receive result and filename.", () => {
6894 const code = "foo bar baz";
6895 const preprocess = sinon.spy(text => text.split(" "));
6896 const postprocess = sinon.spy(text => [text]);
6897
6898 linter.verify(code, {}, { filename, postprocess, preprocess });
6899
6900 assert.strictEqual(postprocess.calledOnce, true);
6901 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
6902 });
6903
6904 it("should apply a postprocessor to the reported messages", () => {
6905 const code = "foo bar baz";
6906
6907 const problems = linter.verify(
6908 code,
6909 { rules: { "report-original-text": "error" } },
6910 {
6911 preprocess: input => input.split(" "),
6912
6913 /*
6914 * Apply a postprocessor that updates the locations of the reported problems
6915 * to make sure they correspond to the locations in the original text.
6916 */
6917 postprocess(problemLists) {
6918 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
6919 return problemLists.reduce(
6920 (combinedList, problemList, index) =>
6921 combinedList.concat(
6922 problemList.map(
6923 problem =>
6924 Object.assign(
6925 {},
6926 problem,
6927 {
6928 message: problem.message.toUpperCase(),
6929 column: problem.column + index * 4
6930 }
6931 )
6932 )
6933 ),
6934 []
6935 );
6936 }
6937 }
6938 );
6939
6940 assert.strictEqual(problems.length, 3);
6941 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
6942 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
6943 });
6944
6945 it("should use postprocessed problem ranges when applying autofixes", () => {
6946 const code = "foo bar baz";
6947
6948 linter.defineRule("capitalize-identifiers", {
6949 meta: {
6950 fixable: "code"
6951 },
6952 create(context) {
6953 return {
6954 Identifier(node) {
6955 if (node.name !== node.name.toUpperCase()) {
6956 context.report({
6957 node,
6958 message: "Capitalize this identifier",
6959 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
6960 });
6961 }
6962 }
6963 };
6964 }
6965 });
6966
6967 const fixResult = linter.verifyAndFix(
6968 code,
6969 { rules: { "capitalize-identifiers": "error" } },
6970 {
6971
6972 /*
6973 * Apply a postprocessor that updates the locations of autofixes
6974 * to make sure they correspond to locations in the original text.
6975 */
6976 preprocess: input => input.split(" "),
6977 postprocess(problemLists) {
6978 return problemLists.reduce(
6979 (combinedProblems, problemList, blockIndex) =>
6980 combinedProblems.concat(
6981 problemList.map(problem =>
6982 Object.assign(problem, {
6983 fix: {
6984 text: problem.fix.text,
6985 range: problem.fix.range.map(
6986 rangeIndex => rangeIndex + blockIndex * 4
6987 )
6988 }
6989 }))
6990 ),
6991 []
6992 );
6993 }
6994 }
6995 );
6996
6997 assert.strictEqual(fixResult.fixed, true);
6998 assert.strictEqual(fixResult.messages.length, 0);
6999 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
7000 });
7001 });
7002 });
7003
7004 describe("verifyAndFix", () => {
7005 it("Fixes the code", () => {
7006 const messages = linter.verifyAndFix("var a", {
7007 rules: {
7008 semi: 2
7009 }
7010 }, { filename: "test.js" });
7011
7012 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
7013 assert.isTrue(messages.fixed);
7014 });
7015
7016 it("does not require a third argument", () => {
7017 const fixResult = linter.verifyAndFix("var a", {
7018 rules: {
7019 semi: 2
7020 }
7021 });
7022
7023 assert.deepStrictEqual(fixResult, {
7024 fixed: true,
7025 messages: [],
7026 output: "var a;"
7027 });
7028 });
7029
7030 it("does not include suggestions in autofix results", () => {
7031 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
7032 rules: {
7033 semi: 2,
7034 "no-useless-escape": 2
7035 }
7036 });
7037
7038 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
7039 assert.strictEqual(fixResult.fixed, true);
7040 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
7041 });
7042
7043 it("does not apply autofixes when fix argument is `false`", () => {
7044 const fixResult = linter.verifyAndFix("var a", {
7045 rules: {
7046 semi: 2
7047 }
7048 }, { fix: false });
7049
7050 assert.strictEqual(fixResult.fixed, false);
7051 });
7052
7053 it("stops fixing after 10 passes", () => {
7054
7055 linter.defineRule("add-spaces", {
7056 meta: {
7057 fixable: "whitespace"
7058 },
7059 create(context) {
7060 return {
7061 Program(node) {
7062 context.report({
7063 node,
7064 message: "Add a space before this node.",
7065 fix: fixer => fixer.insertTextBefore(node, " ")
7066 });
7067 }
7068 };
7069 }
7070 });
7071
7072 const fixResult = linter.verifyAndFix("a", { rules: { "add-spaces": "error" } });
7073
7074 assert.strictEqual(fixResult.fixed, true);
7075 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a`);
7076 assert.strictEqual(fixResult.messages.length, 1);
7077 });
7078
7079 it("should throw an error if fix is passed but meta has no `fixable` property", () => {
7080 linter.defineRule("test-rule", {
7081 meta: {
7082 docs: {},
7083 schema: []
7084 },
7085 create: context => ({
7086 Program(node) {
7087 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
7088 }
7089 })
7090 });
7091
7092 assert.throws(() => {
7093 linter.verify("0", { rules: { "test-rule": "error" } });
7094 }, /Fixable rules must set the `meta\.fixable` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test-rule"$/u);
7095 });
7096
7097 it("should throw an error if fix is passed and there is no metadata", () => {
7098 linter.defineRule("test-rule", {
7099 create: context => ({
7100 Program(node) {
7101 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
7102 }
7103 })
7104 });
7105
7106 assert.throws(() => {
7107 linter.verify("0", { rules: { "test-rule": "error" } });
7108 }, /Fixable rules must set the `meta\.fixable` property/u);
7109 });
7110
7111 it("should throw an error if fix is passed from a legacy-format rule", () => {
7112 linter.defineRule("test-rule", {
7113 create: context => ({
7114 Program(node) {
7115 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
7116 }
7117 })
7118 });
7119
7120 assert.throws(() => {
7121 linter.verify("0", { rules: { "test-rule": "error" } });
7122 }, /Fixable rules must set the `meta\.fixable` property/u);
7123 });
7124 });
7125
7126 describe("Edge cases", () => {
7127
7128 it("should properly parse import statements when sourceType is module", () => {
7129 const code = "import foo from 'foo';";
7130 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
7131 const suppressedMessages = linter.getSuppressedMessages();
7132
7133 assert.strictEqual(messages.length, 0);
7134 assert.strictEqual(suppressedMessages.length, 0);
7135 });
7136
7137 it("should properly parse import all statements when sourceType is module", () => {
7138 const code = "import * as foo from 'foo';";
7139 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
7140 const suppressedMessages = linter.getSuppressedMessages();
7141
7142 assert.strictEqual(messages.length, 0);
7143 assert.strictEqual(suppressedMessages.length, 0);
7144 });
7145
7146 it("should properly parse default export statements when sourceType is module", () => {
7147 const code = "export default function initialize() {}";
7148 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
7149 const suppressedMessages = linter.getSuppressedMessages();
7150
7151 assert.strictEqual(messages.length, 0);
7152 assert.strictEqual(suppressedMessages.length, 0);
7153 });
7154
7155 // https://github.com/eslint/eslint/issues/9687
7156 it("should report an error when invalid parserOptions found", () => {
7157 let messages = linter.verify("", { parserOptions: { ecmaVersion: 222 } });
7158 let suppressedMessages = linter.getSuppressedMessages();
7159
7160 assert.deepStrictEqual(messages.length, 1);
7161 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
7162 assert.strictEqual(suppressedMessages.length, 0);
7163
7164 messages = linter.verify("", { parserOptions: { sourceType: "foo" } });
7165 suppressedMessages = linter.getSuppressedMessages();
7166
7167 assert.deepStrictEqual(messages.length, 1);
7168 assert.ok(messages[0].message.includes("Invalid sourceType"));
7169 assert.strictEqual(suppressedMessages.length, 0);
7170
7171 messages = linter.verify("", { parserOptions: { ecmaVersion: 5, sourceType: "module" } });
7172 suppressedMessages = linter.getSuppressedMessages();
7173
7174 assert.deepStrictEqual(messages.length, 1);
7175 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
7176 assert.strictEqual(suppressedMessages.length, 0);
7177 });
7178
7179 it("should not crash when invalid parentheses syntax is encountered", () => {
7180 linter.verify("left = (aSize.width/2) - ()");
7181 });
7182
7183 it("should not crash when let is used inside of switch case", () => {
7184 linter.verify("switch(foo) { case 1: let bar=2; }", { parserOptions: { ecmaVersion: 6 } });
7185 });
7186
7187 it("should not crash when parsing destructured assignment", () => {
7188 linter.verify("var { a='a' } = {};", { parserOptions: { ecmaVersion: 6 } });
7189 });
7190
7191 it("should report syntax error when a keyword exists in object property shorthand", () => {
7192 const messages = linter.verify("let a = {this}", { parserOptions: { ecmaVersion: 6 } });
7193 const suppressedMessages = linter.getSuppressedMessages();
7194
7195 assert.strictEqual(messages.length, 1);
7196 assert.strictEqual(messages[0].fatal, true);
7197
7198 assert.strictEqual(suppressedMessages.length, 0);
7199 });
7200
7201 it("should not rewrite env setting in core (https://github.com/eslint/eslint/issues/4814)", () => {
7202
7203 /*
7204 * This test focuses on the instance of https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/conf/environments.js#L26-L28
7205 * This `verify()` takes the instance and runs https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/lib/eslint.js#L416
7206 */
7207 linter.defineRule("test", {
7208 create: () => ({})
7209 });
7210 linter.verify("var a = 0;", {
7211 env: { node: true },
7212 parserOptions: { ecmaVersion: 6, sourceType: "module" },
7213 rules: { test: 2 }
7214 });
7215
7216 // This `verify()` takes the instance and tests that the instance was not modified.
7217 let ok = false;
7218
7219 linter.defineRule("test", {
7220 create(context) {
7221 assert(
7222 context.parserOptions.ecmaFeatures.globalReturn,
7223 "`ecmaFeatures.globalReturn` of the node environment should not be modified."
7224 );
7225 ok = true;
7226 return {};
7227 }
7228 });
7229 linter.verify("var a = 0;", {
7230 env: { node: true },
7231 rules: { test: 2 }
7232 });
7233
7234 assert(ok);
7235 });
7236
7237 it("should throw when rule's create() function does not return an object", () => {
7238 const config = { rules: { checker: "error" } };
7239
7240 linter.defineRule("checker", {
7241 create: () => null
7242 }); // returns null
7243
7244 assert.throws(() => {
7245 linter.verify("abc", config, filename);
7246 }, "The create() function for rule 'checker' did not return an object.");
7247
7248 linter.defineRule("checker", {
7249 create() {}
7250 }); // returns undefined
7251
7252 assert.throws(() => {
7253 linter.verify("abc", config, filename);
7254 }, "The create() function for rule 'checker' did not return an object.");
7255 });
7256 });
7257
7258 describe("Custom parser", () => {
7259
7260 const errorPrefix = "Parsing error: ";
7261
7262 it("should have file path passed to it", () => {
7263 const code = "/* this is code */";
7264 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
7265
7266 linter.defineParser("stub-parser", testParsers.stubParser);
7267 linter.verify(code, { parser: "stub-parser" }, filename, true);
7268
7269 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
7270 });
7271
7272 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
7273 const code = "var myDivElement = <div {...this.props} />;";
7274
7275 linter.defineParser("esprima", esprima);
7276 const messages = linter.verify(code, { parser: "esprima", parserOptions: { jsx: true } }, "filename");
7277 const suppressedMessages = linter.getSuppressedMessages();
7278
7279 assert.strictEqual(messages.length, 0);
7280 assert.strictEqual(suppressedMessages.length, 0);
7281 });
7282
7283 it("should return an error when the custom parser can't be found", () => {
7284 const code = "var myDivElement = <div {...this.props} />;";
7285 const messages = linter.verify(code, { parser: "esprima-xyz" }, "filename");
7286 const suppressedMessages = linter.getSuppressedMessages();
7287
7288 assert.strictEqual(messages.length, 1);
7289 assert.strictEqual(messages[0].severity, 2);
7290 assert.strictEqual(messages[0].message, "Configured parser 'esprima-xyz' was not found.");
7291
7292 assert.strictEqual(suppressedMessages.length, 0);
7293 });
7294
7295 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
7296 const code = "null %% 'foo'";
7297
7298 linter.defineParser("unknown-logical-operator", testParsers.unknownLogicalOperator);
7299
7300 // This shouldn't throw
7301 const messages = linter.verify(code, { parser: "unknown-logical-operator" }, filename, true);
7302 const suppressedMessages = linter.getSuppressedMessages();
7303
7304 assert.strictEqual(messages.length, 0);
7305 assert.strictEqual(suppressedMessages.length, 0);
7306 });
7307
7308 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
7309 const code = "foo && bar %% baz";
7310
7311 linter.defineParser("unknown-logical-operator-nested", testParsers.unknownLogicalOperatorNested);
7312
7313 // This shouldn't throw
7314 const messages = linter.verify(code, { parser: "unknown-logical-operator-nested" }, filename, true);
7315 const suppressedMessages = linter.getSuppressedMessages();
7316
7317 assert.strictEqual(messages.length, 0);
7318 assert.strictEqual(suppressedMessages.length, 0);
7319 });
7320
7321 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
7322 const code = "foo && bar %% baz";
7323
7324 const nodes = [];
7325
7326 linter.defineRule("collect-node-types", {
7327 create: () => ({
7328 "*"(node) {
7329 nodes.push(node.type);
7330 }
7331 })
7332 });
7333
7334 linter.defineParser("non-js-parser", testParsers.nonJSParser);
7335
7336 const messages = linter.verify(code, {
7337 parser: "non-js-parser",
7338 rules: {
7339 "collect-node-types": "error"
7340 }
7341 }, filename, true);
7342 const suppressedMessages = linter.getSuppressedMessages();
7343
7344 assert.strictEqual(messages.length, 0);
7345 assert.isTrue(nodes.length > 0);
7346
7347 assert.strictEqual(suppressedMessages.length, 0);
7348 });
7349
7350 it("should strip leading line: prefix from parser error", () => {
7351 linter.defineParser("line-error", testParsers.lineError);
7352 const messages = linter.verify(";", { parser: "line-error" }, "filename");
7353 const suppressedMessages = linter.getSuppressedMessages();
7354
7355 assert.strictEqual(messages.length, 1);
7356 assert.strictEqual(messages[0].severity, 2);
7357 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
7358
7359 assert.strictEqual(suppressedMessages.length, 0);
7360 });
7361
7362 it("should not modify a parser error message without a leading line: prefix", () => {
7363 linter.defineParser("no-line-error", testParsers.noLineError);
7364 const messages = linter.verify(";", { parser: "no-line-error" }, filename);
7365 const suppressedMessages = linter.getSuppressedMessages();
7366
7367 assert.strictEqual(messages.length, 1);
7368 assert.strictEqual(messages[0].severity, 2);
7369 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
7370
7371 assert.strictEqual(suppressedMessages.length, 0);
7372 });
7373
7374 describe("if a parser provides 'visitorKeys'", () => {
7375 let types = [];
7376 let sourceCode;
7377 let scopeManager;
7378 let firstChildNodes = [];
7379
7380 beforeEach(() => {
7381 types = [];
7382 firstChildNodes = [];
7383 linter.defineRule("collect-node-types", {
7384 create: () => ({
7385 "*"(node) {
7386 types.push(node.type);
7387 }
7388 })
7389 });
7390 linter.defineRule("save-scope-manager", {
7391 create(context) {
7392 scopeManager = context.sourceCode.scopeManager;
7393
7394 return {};
7395 }
7396 });
7397 linter.defineRule("esquery-option", {
7398 create: () => ({
7399 ":first-child"(node) {
7400 firstChildNodes.push(node);
7401 }
7402 })
7403 });
7404 linter.defineParser("enhanced-parser2", testParsers.enhancedParser2);
7405 linter.verify("@foo class A {}", {
7406 parser: "enhanced-parser2",
7407 rules: {
7408 "collect-node-types": "error",
7409 "save-scope-manager": "error",
7410 "esquery-option": "error"
7411 }
7412 });
7413
7414 sourceCode = linter.getSourceCode();
7415 });
7416
7417 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
7418 assert.deepStrictEqual(
7419 types,
7420 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7421 );
7422 });
7423
7424 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7425 assert.deepStrictEqual(
7426 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
7427 ["experimentalDecorators", "id", "superClass", "body"]
7428 );
7429 });
7430
7431 it("should use the same visitorKeys if the source code object is reused", () => {
7432 const types2 = [];
7433
7434 linter.defineRule("collect-node-types", {
7435 create: () => ({
7436 "*"(node) {
7437 types2.push(node.type);
7438 }
7439 })
7440 });
7441 linter.verify(sourceCode, {
7442 rules: {
7443 "collect-node-types": "error"
7444 }
7445 });
7446
7447 assert.deepStrictEqual(
7448 types2,
7449 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7450 );
7451 });
7452
7453 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7454 assert.deepStrictEqual(
7455 firstChildNodes,
7456 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
7457 );
7458 });
7459 });
7460
7461 describe("if a parser provides 'scope'", () => {
7462 let scope = null;
7463 let sourceCode = null;
7464
7465 beforeEach(() => {
7466 linter.defineParser("enhanced-parser3", testParsers.enhancedParser3);
7467 linter.defineRule("save-scope1", {
7468 create: context => ({
7469 Program() {
7470 scope = context.getScope();
7471 }
7472 })
7473 });
7474 linter.verify("@foo class A {}", { parser: "enhanced-parser3", rules: { "save-scope1": 2 } });
7475
7476 sourceCode = linter.getSourceCode();
7477 });
7478
7479 it("should use the scope (so the global scope has the reference of '@foo')", () => {
7480 assert.strictEqual(scope.references.length, 1);
7481 assert.deepStrictEqual(
7482 scope.references[0].identifier.name,
7483 "foo"
7484 );
7485 });
7486
7487 it("should use the same scope if the source code object is reused", () => {
7488 let scope2 = null;
7489
7490 linter.defineRule("save-scope2", {
7491 create: context => ({
7492 Program() {
7493 scope2 = context.getScope();
7494 }
7495 })
7496 });
7497 linter.verify(sourceCode, { rules: { "save-scope2": 2 } }, "test.js");
7498
7499 assert(scope2 !== null);
7500 assert(scope2 === scope);
7501 });
7502 });
7503
7504 it("should not pass any default parserOptions to the parser", () => {
7505 linter.defineParser("throws-with-options", testParsers.throwsWithOptions);
7506 const messages = linter.verify(";", { parser: "throws-with-options" }, "filename");
7507 const suppressedMessages = linter.getSuppressedMessages();
7508
7509 assert.strictEqual(messages.length, 0);
7510 assert.strictEqual(suppressedMessages.length, 0);
7511 });
7512 });
7513
7514 describe("merging 'parserOptions'", () => {
7515 it("should deeply merge 'parserOptions' from an environment with 'parserOptions' from the provided config", () => {
7516 const code = "return <div/>";
7517 const config = {
7518 env: {
7519 node: true // ecmaFeatures: { globalReturn: true }
7520 },
7521 parserOptions: {
7522 ecmaFeatures: {
7523 jsx: true
7524 }
7525 }
7526 };
7527
7528 const messages = linter.verify(code, config);
7529 const suppressedMessages = linter.getSuppressedMessages();
7530
7531 // no parsing errors
7532 assert.strictEqual(messages.length, 0);
7533 assert.strictEqual(suppressedMessages.length, 0);
7534 });
7535 });
7536 });
7537
7538 describe("Linter with FlatConfigArray", () => {
7539
7540 let linter;
7541 const filename = "filename.js";
7542
7543 /**
7544 * Creates a config array with some default properties.
7545 * @param {FlatConfig|FlatConfig[]} value The value to base the
7546 * config array on.
7547 * @returns {FlatConfigArray} The created config array.
7548 */
7549 function createFlatConfigArray(value) {
7550 return new FlatConfigArray(value, { basePath: "" });
7551 }
7552
7553 beforeEach(() => {
7554 linter = new Linter({ configType: "flat" });
7555 });
7556
7557 describe("Static Members", () => {
7558 describe("version", () => {
7559 it("should return same version as instance property", () => {
7560 assert.strictEqual(Linter.version, linter.version);
7561 });
7562 });
7563 });
7564
7565 describe("Config Options", () => {
7566
7567 describe("languageOptions", () => {
7568
7569 describe("ecmaVersion", () => {
7570
7571 it("should error when accessing a global that isn't available in ecmaVersion 5", () => {
7572 const messages = linter.verify("new Map()", {
7573 languageOptions: {
7574 ecmaVersion: 5,
7575 sourceType: "script"
7576 },
7577 rules: {
7578 "no-undef": "error"
7579 }
7580 });
7581 const suppressedMessages = linter.getSuppressedMessages();
7582
7583 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7584 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7585
7586 assert.strictEqual(suppressedMessages.length, 0);
7587 });
7588
7589 it("should error when accessing a global that isn't available in ecmaVersion 3", () => {
7590 const messages = linter.verify("JSON.stringify({})", {
7591 languageOptions: {
7592 ecmaVersion: 3,
7593 sourceType: "script"
7594 },
7595 rules: {
7596 "no-undef": "error"
7597 }
7598 });
7599 const suppressedMessages = linter.getSuppressedMessages();
7600
7601 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7602 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7603
7604 assert.strictEqual(suppressedMessages.length, 0);
7605 });
7606
7607 it("should add globals for ES6 when ecmaVersion is 6", () => {
7608 const messages = linter.verify("new Map()", {
7609 languageOptions: {
7610 ecmaVersion: 6
7611 },
7612 rules: {
7613 "no-undef": "error"
7614 }
7615 });
7616 const suppressedMessages = linter.getSuppressedMessages();
7617
7618 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7619 assert.strictEqual(suppressedMessages.length, 0);
7620 });
7621
7622 it("should allow destructuring when ecmaVersion is 6", () => {
7623 const messages = linter.verify("let {a} = b", {
7624 languageOptions: {
7625 ecmaVersion: 6
7626 }
7627 });
7628 const suppressedMessages = linter.getSuppressedMessages();
7629
7630 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7631 assert.strictEqual(suppressedMessages.length, 0);
7632 });
7633
7634 it("ecmaVersion should be normalized to year name for ES 6", () => {
7635 const config = {
7636 plugins: {
7637 test: {
7638 rules: {
7639 checker: {
7640 create: context => ({
7641 Program() {
7642 assert.strictEqual(context.languageOptions.ecmaVersion, 2015);
7643 }
7644 })
7645 }
7646 }
7647 }
7648 },
7649 languageOptions: {
7650 ecmaVersion: 6
7651 },
7652 rules: { "test/checker": "error" }
7653 };
7654
7655 linter.verify("foo", config, filename);
7656 });
7657
7658 it("ecmaVersion should be normalized to latest year by default", () => {
7659 const config = {
7660 plugins: {
7661 test: {
7662 rules: {
7663 checker: {
7664 create: context => ({
7665 Program() {
7666 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7667 }
7668 })
7669 }
7670 }
7671 }
7672 },
7673 rules: { "test/checker": "error" }
7674 };
7675
7676 linter.verify("foo", config, filename);
7677 });
7678
7679 it("ecmaVersion should not be normalized to year name for ES 5", () => {
7680 const config = {
7681 plugins: {
7682 test: {
7683 rules: {
7684 checker: {
7685 create: context => ({
7686 Program() {
7687 assert.strictEqual(context.languageOptions.ecmaVersion, 5);
7688 }
7689 })
7690 }
7691 }
7692 }
7693 },
7694 languageOptions: {
7695 ecmaVersion: 5
7696 },
7697 rules: { "test/checker": "error" }
7698 };
7699
7700 linter.verify("foo", config, filename);
7701 });
7702
7703 it("ecmaVersion should be normalized to year name for 'latest'", () => {
7704 const config = {
7705 plugins: {
7706 test: {
7707 rules: {
7708 checker: {
7709 create: context => ({
7710 Program() {
7711 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7712 }
7713 })
7714 }
7715 }
7716 }
7717 },
7718 languageOptions: {
7719 ecmaVersion: "latest"
7720 },
7721 rules: { "test/checker": "error" }
7722 };
7723
7724 linter.verify("foo", config, filename);
7725 });
7726
7727
7728 });
7729
7730 describe("sourceType", () => {
7731
7732 it("should be module by default", () => {
7733 const config = {
7734 plugins: {
7735 test: {
7736 rules: {
7737 checker: {
7738 create: context => ({
7739 Program() {
7740 assert.strictEqual(context.languageOptions.sourceType, "module");
7741 }
7742 })
7743 }
7744 }
7745 }
7746 },
7747 rules: { "test/checker": "error" }
7748 };
7749
7750 linter.verify("import foo from 'bar'", config, filename);
7751 });
7752
7753 it("should default to commonjs when passed a .cjs filename", () => {
7754 const config = {
7755 plugins: {
7756 test: {
7757 rules: {
7758 checker: {
7759 create: context => ({
7760 Program() {
7761 assert.strictEqual(context.languageOptions.sourceType, "commonjs");
7762 }
7763 })
7764 }
7765 }
7766 }
7767 },
7768 rules: { "test/checker": "error" }
7769 };
7770
7771 linter.verify("import foo from 'bar'", config, `${filename}.cjs`);
7772 });
7773
7774
7775 it("should error when import is used in a script", () => {
7776 const messages = linter.verify("import foo from 'bar';", {
7777 languageOptions: {
7778 ecmaVersion: 6,
7779 sourceType: "script"
7780 }
7781 });
7782
7783 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
7784 assert.strictEqual(messages[0].message, "Parsing error: 'import' and 'export' may appear only with 'sourceType: module'");
7785 });
7786
7787 it("should not error when import is used in a module", () => {
7788 const messages = linter.verify("import foo from 'bar';", {
7789 languageOptions: {
7790 ecmaVersion: 6,
7791 sourceType: "module"
7792 }
7793 });
7794 const suppressedMessages = linter.getSuppressedMessages();
7795
7796 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7797 assert.strictEqual(suppressedMessages.length, 0);
7798 });
7799
7800 it("should error when return is used at the top-level outside of commonjs", () => {
7801 const messages = linter.verify("return", {
7802 languageOptions: {
7803 ecmaVersion: 6,
7804 sourceType: "script"
7805 }
7806 });
7807 const suppressedMessages = linter.getSuppressedMessages();
7808
7809 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
7810 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
7811
7812 assert.strictEqual(suppressedMessages.length, 0);
7813 });
7814
7815 it("should not error when top-level return is used in commonjs", () => {
7816 const messages = linter.verify("return", {
7817 languageOptions: {
7818 ecmaVersion: 6,
7819 sourceType: "commonjs"
7820 }
7821 });
7822 const suppressedMessages = linter.getSuppressedMessages();
7823
7824 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7825 assert.strictEqual(suppressedMessages.length, 0);
7826 });
7827
7828 it("should error when accessing a Node.js global outside of commonjs", () => {
7829 const messages = linter.verify("require()", {
7830 languageOptions: {
7831 ecmaVersion: 6
7832 },
7833 rules: {
7834 "no-undef": "error"
7835 }
7836 });
7837 const suppressedMessages = linter.getSuppressedMessages();
7838
7839 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7840 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7841
7842 assert.strictEqual(suppressedMessages.length, 0);
7843 });
7844
7845 it("should add globals for Node.js when sourceType is commonjs", () => {
7846 const messages = linter.verify("require()", {
7847 languageOptions: {
7848 ecmaVersion: 6,
7849 sourceType: "commonjs"
7850 },
7851 rules: {
7852 "no-undef": "error"
7853 }
7854 });
7855 const suppressedMessages = linter.getSuppressedMessages();
7856
7857 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7858 assert.strictEqual(suppressedMessages.length, 0);
7859 });
7860
7861 it("should allow 'await' as a property name in modules", () => {
7862 const result = linter.verify(
7863 "obj.await",
7864 {
7865 languageOptions: {
7866 ecmaVersion: 6,
7867 sourceType: "module"
7868 }
7869 }
7870 );
7871 const suppressedMessages = linter.getSuppressedMessages();
7872
7873 assert(result.length === 0);
7874 assert.strictEqual(suppressedMessages.length, 0);
7875 });
7876
7877 });
7878
7879 describe("parser", () => {
7880
7881 it("should be able to define a custom parser", () => {
7882 const parser = {
7883 parseForESLint: function parse(code, options) {
7884 return {
7885 ast: esprima.parse(code, options),
7886 services: {
7887 test: {
7888 getMessage() {
7889 return "Hi!";
7890 }
7891 }
7892 }
7893 };
7894 }
7895 };
7896
7897 const config = {
7898 languageOptions: {
7899 parser
7900 }
7901 };
7902
7903
7904 const messages = linter.verify("0", config, filename);
7905 const suppressedMessages = linter.getSuppressedMessages();
7906
7907 assert.strictEqual(messages.length, 0);
7908 assert.strictEqual(suppressedMessages.length, 0);
7909 });
7910
7911 it("should pass parser as context.languageOptions.parser to all rules when provided on config", () => {
7912
7913 const config = {
7914 plugins: {
7915 test: {
7916 rules: {
7917 "test-rule": {
7918 create: sinon.mock().withArgs(
7919 sinon.match({ languageOptions: { parser: esprima } })
7920 ).returns({})
7921 }
7922 }
7923 }
7924 },
7925 languageOptions: {
7926 parser: esprima
7927 },
7928 rules: {
7929 "test/test-rule": 2
7930 }
7931 };
7932
7933 linter.verify("0", config, filename);
7934 });
7935
7936 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
7937 const config = {
7938 languageOptions: {
7939 parser: testParsers.enhancedParser
7940 }
7941 };
7942
7943 const messages = linter.verify("0", config, filename);
7944 const suppressedMessages = linter.getSuppressedMessages();
7945
7946 assert.strictEqual(messages.length, 0);
7947 assert.strictEqual(suppressedMessages.length, 0);
7948 });
7949
7950 it("should expose parser services when using parseForESLint() and services are specified", () => {
7951
7952 const config = {
7953 plugins: {
7954 test: {
7955 rules: {
7956 "test-service-rule": {
7957 create: context => ({
7958 Literal(node) {
7959 context.report({
7960 node,
7961 message: context.parserServices.test.getMessage()
7962 });
7963 }
7964 })
7965 }
7966 }
7967 }
7968 },
7969 languageOptions: {
7970 parser: testParsers.enhancedParser
7971 },
7972 rules: {
7973 "test/test-service-rule": 2
7974 }
7975 };
7976
7977 const messages = linter.verify("0", config, filename);
7978 const suppressedMessages = linter.getSuppressedMessages();
7979
7980 assert.strictEqual(messages.length, 1);
7981 assert.strictEqual(messages[0].message, "Hi!");
7982
7983 assert.strictEqual(suppressedMessages.length, 0);
7984 });
7985
7986 it("should use the same parserServices if source code object is reused", () => {
7987
7988 const config = {
7989 plugins: {
7990 test: {
7991 rules: {
7992 "test-service-rule": {
7993 create: context => ({
7994 Literal(node) {
7995 context.report({
7996 node,
7997 message: context.parserServices.test.getMessage()
7998 });
7999 }
8000 })
8001 }
8002 }
8003 }
8004 },
8005 languageOptions: {
8006 parser: testParsers.enhancedParser
8007 },
8008 rules: {
8009 "test/test-service-rule": 2
8010 }
8011 };
8012
8013 const messages = linter.verify("0", config, filename);
8014 const suppressedMessages = linter.getSuppressedMessages();
8015
8016 assert.strictEqual(messages.length, 1);
8017 assert.strictEqual(messages[0].message, "Hi!");
8018 assert.strictEqual(suppressedMessages.length, 0);
8019
8020 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
8021 const suppressedMessages2 = linter.getSuppressedMessages();
8022
8023 assert.strictEqual(messages2.length, 1);
8024 assert.strictEqual(messages2[0].message, "Hi!");
8025 assert.strictEqual(suppressedMessages2.length, 0);
8026 });
8027
8028 it("should pass parser as context.languageOptions.parser to all rules when default parser is used", () => {
8029
8030 // references to Espree get messed up in a browser context, so wrap it
8031 const fakeParser = {
8032 parse: espree.parse
8033 };
8034
8035 const spy = sinon.spy(context => {
8036 assert.strictEqual(context.languageOptions.parser, fakeParser);
8037 return {};
8038 });
8039
8040 const config = {
8041 plugins: {
8042 test: {
8043 rules: {
8044 "test-rule": { create: spy }
8045 }
8046 }
8047 },
8048 languageOptions: {
8049 parser: fakeParser
8050 },
8051 rules: {
8052 "test/test-rule": 2
8053 }
8054 };
8055
8056 linter.verify("0", config, filename);
8057 assert.isTrue(spy.calledOnce);
8058 });
8059
8060
8061 describe("Custom Parsers", () => {
8062
8063 const errorPrefix = "Parsing error: ";
8064
8065 it("should have file path passed to it", () => {
8066 const code = "/* this is code */";
8067 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
8068 const config = {
8069 languageOptions: {
8070 parser: testParsers.stubParser
8071 }
8072 };
8073
8074 linter.verify(code, config, filename, true);
8075
8076 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
8077 });
8078
8079 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
8080 const code = "var myDivElement = <div {...this.props} />;";
8081 const config = {
8082 languageOptions: {
8083 parser: esprima,
8084 parserOptions: {
8085 jsx: true
8086 }
8087 }
8088 };
8089
8090 const messages = linter.verify(code, config, filename);
8091 const suppressedMessages = linter.getSuppressedMessages();
8092
8093 assert.strictEqual(messages.length, 0);
8094 assert.strictEqual(suppressedMessages.length, 0);
8095 });
8096
8097 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
8098 const code = "null %% 'foo'";
8099 const config = {
8100 languageOptions: {
8101 parser: testParsers.unknownLogicalOperator
8102 }
8103 };
8104
8105 // This shouldn't throw
8106 const messages = linter.verify(code, config, filename);
8107 const suppressedMessages = linter.getSuppressedMessages();
8108
8109 assert.strictEqual(messages.length, 0);
8110 assert.strictEqual(suppressedMessages.length, 0);
8111 });
8112
8113 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
8114 const code = "foo && bar %% baz";
8115 const config = {
8116 languageOptions: {
8117 parser: testParsers.unknownLogicalOperatorNested
8118 }
8119 };
8120
8121 // This shouldn't throw
8122 const messages = linter.verify(code, config, filename);
8123 const suppressedMessages = linter.getSuppressedMessages();
8124
8125 assert.strictEqual(messages.length, 0);
8126 assert.strictEqual(suppressedMessages.length, 0);
8127 });
8128
8129 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
8130 const code = "foo && bar %% baz";
8131 const nodes = [];
8132 const config = {
8133 plugins: {
8134 test: {
8135 rules: {
8136 "collect-node-types": {
8137 create: () => ({
8138 "*"(node) {
8139 nodes.push(node.type);
8140 }
8141 })
8142 }
8143 }
8144 }
8145 },
8146 languageOptions: {
8147 parser: testParsers.nonJSParser
8148 },
8149 rules: {
8150 "test/collect-node-types": "error"
8151 }
8152 };
8153
8154 const messages = linter.verify(code, config, filename, true);
8155 const suppressedMessages = linter.getSuppressedMessages();
8156
8157 assert.strictEqual(messages.length, 0);
8158 assert.isTrue(nodes.length > 0);
8159 assert.strictEqual(suppressedMessages.length, 0);
8160 });
8161
8162 it("should strip leading line: prefix from parser error", () => {
8163 const messages = linter.verify(";", {
8164 languageOptions: {
8165 parser: testParsers.lineError
8166 }
8167 }, filename);
8168 const suppressedMessages = linter.getSuppressedMessages();
8169
8170 assert.strictEqual(messages.length, 1);
8171 assert.strictEqual(messages[0].severity, 2);
8172 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
8173
8174 assert.strictEqual(suppressedMessages.length, 0);
8175 });
8176
8177 it("should not modify a parser error message without a leading line: prefix", () => {
8178 const messages = linter.verify(";", {
8179 languageOptions: {
8180 parser: testParsers.noLineError
8181 }
8182 }, filename);
8183 const suppressedMessages = linter.getSuppressedMessages();
8184
8185 assert.strictEqual(messages.length, 1);
8186 assert.strictEqual(messages[0].severity, 2);
8187 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
8188
8189 assert.strictEqual(suppressedMessages.length, 0);
8190 });
8191
8192 describe("if a parser provides 'visitorKeys'", () => {
8193 let types = [];
8194 let sourceCode;
8195 let scopeManager;
8196 let firstChildNodes = [];
8197
8198 beforeEach(() => {
8199 types = [];
8200 firstChildNodes = [];
8201 const config = {
8202 plugins: {
8203 test: {
8204 rules: {
8205 "collect-node-types": {
8206 create: () => ({
8207 "*"(node) {
8208 types.push(node.type);
8209 }
8210 })
8211 },
8212 "save-scope-manager": {
8213 create(context) {
8214 scopeManager = context.sourceCode.scopeManager;
8215
8216 return {};
8217 }
8218 },
8219 "esquery-option": {
8220 create: () => ({
8221 ":first-child"(node) {
8222 firstChildNodes.push(node);
8223 }
8224 })
8225 }
8226 }
8227 }
8228 },
8229 languageOptions: {
8230 parser: testParsers.enhancedParser2
8231 },
8232 rules: {
8233 "test/collect-node-types": "error",
8234 "test/save-scope-manager": "error",
8235 "test/esquery-option": "error"
8236 }
8237 };
8238
8239 linter.verify("@foo class A {}", config);
8240
8241 sourceCode = linter.getSourceCode();
8242 });
8243
8244 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
8245 assert.deepStrictEqual(
8246 types,
8247 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
8248 );
8249 });
8250
8251 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
8252 assert.deepStrictEqual(
8253 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
8254 ["experimentalDecorators", "id", "superClass", "body"]
8255 );
8256 });
8257
8258 it("should use the same visitorKeys if the source code object is reused", () => {
8259 const types2 = [];
8260 const config = {
8261 plugins: {
8262 test: {
8263 rules: {
8264 "collect-node-types": {
8265 create: () => ({
8266 "*"(node) {
8267 types2.push(node.type);
8268 }
8269 })
8270 }
8271 }
8272 }
8273 },
8274 rules: {
8275 "test/collect-node-types": "error"
8276 }
8277 };
8278
8279 linter.verify(sourceCode, config);
8280
8281 assert.deepStrictEqual(
8282 types2,
8283 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
8284 );
8285 });
8286
8287 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
8288 assert.deepStrictEqual(
8289 firstChildNodes,
8290 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
8291 );
8292 });
8293 });
8294
8295 describe("if a parser provides 'scope'", () => {
8296 let scope = null;
8297 let sourceCode = null;
8298
8299 beforeEach(() => {
8300 const config = {
8301 plugins: {
8302 test: {
8303 rules: {
8304 "save-scope1": {
8305 create: context => ({
8306 Program() {
8307 scope = context.getScope();
8308 }
8309 })
8310 }
8311 }
8312 }
8313 },
8314 languageOptions: {
8315 parser: testParsers.enhancedParser3
8316 },
8317 rules: {
8318 "test/save-scope1": "error"
8319 }
8320 };
8321
8322 linter.verify("@foo class A {}", config);
8323
8324 sourceCode = linter.getSourceCode();
8325 });
8326
8327 it("should use the scope (so the global scope has the reference of '@foo')", () => {
8328 assert.strictEqual(scope.references.length, 1);
8329 assert.deepStrictEqual(
8330 scope.references[0].identifier.name,
8331 "foo"
8332 );
8333 });
8334
8335 it("should use the same scope if the source code object is reused", () => {
8336 let scope2 = null;
8337 const config = {
8338 plugins: {
8339 test: {
8340 rules: {
8341 "save-scope2": {
8342 create: context => ({
8343 Program() {
8344 scope2 = context.getScope();
8345 }
8346 })
8347 }
8348 }
8349 }
8350 },
8351 rules: {
8352 "test/save-scope2": "error"
8353 }
8354 };
8355
8356 linter.verify(sourceCode, config, "test.js");
8357
8358 assert(scope2 !== null);
8359 assert(scope2 === scope);
8360 });
8361 });
8362
8363 it("should pass default languageOptions to the parser", () => {
8364
8365 const spy = sinon.spy((code, options) => espree.parse(code, options));
8366
8367 linter.verify(";", {
8368 languageOptions: {
8369 parser: {
8370 parse: spy
8371 }
8372 }
8373 }, "filename.js");
8374
8375 assert(spy.calledWithMatch(";", {
8376 ecmaVersion: espree.latestEcmaVersion + 2009,
8377 sourceType: "module"
8378 }));
8379 });
8380 });
8381
8382
8383 });
8384
8385 describe("parseOptions", () => {
8386
8387 it("should pass ecmaFeatures to all rules when provided on config", () => {
8388
8389 const parserOptions = {
8390 ecmaFeatures: {
8391 jsx: true
8392 }
8393 };
8394
8395 const config = {
8396 plugins: {
8397 test: {
8398 rules: {
8399 "test-rule": {
8400 create: sinon.mock().withArgs(
8401 sinon.match({ languageOptions: { parserOptions } })
8402 ).returns({})
8403 }
8404 }
8405 }
8406 },
8407 languageOptions: {
8408 parserOptions
8409 },
8410 rules: {
8411 "test/test-rule": 2
8412 }
8413 };
8414
8415 linter.verify("0", config, filename);
8416 });
8417
8418 it("should switch globalReturn to false if sourceType is module", () => {
8419
8420 const config = {
8421 plugins: {
8422 test: {
8423 rules: {
8424 "test-rule": {
8425 create: sinon.mock().withArgs(
8426 sinon.match({
8427 languageOptions: {
8428 parserOptions: {
8429 ecmaFeatures: {
8430 globalReturn: false
8431 }
8432 }
8433 }
8434 })
8435 ).returns({})
8436 }
8437 }
8438 }
8439 },
8440 languageOptions: {
8441 sourceType: "module",
8442 parserOptions: {
8443 ecmaFeatures: {
8444 globalReturn: true
8445 }
8446 }
8447 },
8448 rules: {
8449 "test/test-rule": 2
8450 }
8451 };
8452
8453 linter.verify("0", config, filename);
8454 });
8455
8456 it("should not parse sloppy-mode code when impliedStrict is true", () => {
8457
8458 const messages = linter.verify("var private;", {
8459 languageOptions: {
8460 parserOptions: {
8461 ecmaFeatures: {
8462 impliedStrict: true
8463 }
8464 }
8465 }
8466 }, filename);
8467 const suppressedMessages = linter.getSuppressedMessages();
8468
8469 assert.strictEqual(messages.length, 1);
8470 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
8471
8472 assert.strictEqual(suppressedMessages.length, 0);
8473 });
8474
8475 it("should properly parse valid code when impliedStrict is true", () => {
8476
8477 const messages = linter.verify("var foo;", {
8478 languageOptions: {
8479 parserOptions: {
8480 ecmaFeatures: {
8481 impliedStrict: true
8482 }
8483 }
8484 }
8485 }, filename);
8486 const suppressedMessages = linter.getSuppressedMessages();
8487
8488 assert.strictEqual(messages.length, 0);
8489 assert.strictEqual(suppressedMessages.length, 0);
8490 });
8491
8492 it("should properly parse JSX when passed ecmaFeatures", () => {
8493
8494 const messages = linter.verify("var x = <div/>;", {
8495 languageOptions: {
8496 parserOptions: {
8497 ecmaFeatures: {
8498 jsx: true
8499 }
8500 }
8501 }
8502 }, filename);
8503 const suppressedMessages = linter.getSuppressedMessages();
8504
8505 assert.strictEqual(messages.length, 0);
8506 assert.strictEqual(suppressedMessages.length, 0);
8507 });
8508
8509 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
8510 const code = "var myDivElement = <div className=\"foo\" />;";
8511 const messages = linter.verify(code, {}, filename);
8512 const suppressedMessages = linter.getSuppressedMessages();
8513
8514 assert.strictEqual(messages.length, 1);
8515 assert.strictEqual(messages[0].line, 1);
8516 assert.strictEqual(messages[0].column, 20);
8517 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
8518
8519 assert.strictEqual(suppressedMessages.length, 0);
8520 });
8521
8522 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
8523 const code = "var myDivElement = <div className=\"foo\" />;";
8524 const messages = linter.verify(code, {
8525 languageOptions: {
8526 parserOptions: {
8527 ecmaFeatures: {
8528 jsx: true
8529 }
8530 }
8531 }
8532 }, filename);
8533 const suppressedMessages = linter.getSuppressedMessages();
8534
8535 assert.strictEqual(messages.length, 0);
8536 assert.strictEqual(suppressedMessages.length, 0);
8537 });
8538
8539 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
8540 const code = "var myDivElement = <div {...this.props} />;";
8541 const messages = linter.verify(code, {
8542 languageOptions: {
8543 ecmaVersion: 6,
8544 parserOptions: {
8545 ecmaFeatures: {
8546 jsx: true
8547 }
8548 }
8549 }
8550
8551 }, "filename.js");
8552 const suppressedMessages = linter.getSuppressedMessages();
8553
8554 assert.strictEqual(messages.length, 0);
8555 assert.strictEqual(suppressedMessages.length, 0);
8556 });
8557
8558 it("should not allow the use of reserved words as variable names in ES3", () => {
8559 const code = "var char;";
8560 const messages = linter.verify(code, {
8561 languageOptions: {
8562 ecmaVersion: 3,
8563 sourceType: "script"
8564 }
8565 }, filename);
8566
8567 assert.strictEqual(messages.length, 1);
8568 assert.strictEqual(messages[0].severity, 2);
8569 assert.isTrue(messages[0].fatal);
8570 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8571 });
8572
8573 it("should not allow the use of reserved words as property names in member expressions in ES3", () => {
8574 const code = "obj.char;";
8575 const messages = linter.verify(code, {
8576 languageOptions: {
8577 ecmaVersion: 3,
8578 sourceType: "script"
8579 }
8580 }, filename);
8581
8582 assert.strictEqual(messages.length, 1);
8583 assert.strictEqual(messages[0].severity, 2);
8584 assert.isTrue(messages[0].fatal);
8585 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8586 });
8587
8588 it("should not allow the use of reserved words as property names in object literals in ES3", () => {
8589 const code = "var obj = { char: 1 };";
8590 const messages = linter.verify(code, {
8591 languageOptions: {
8592 ecmaVersion: 3,
8593 sourceType: "script"
8594 }
8595 }, filename);
8596
8597 assert.strictEqual(messages.length, 1);
8598 assert.strictEqual(messages[0].severity, 2);
8599 assert.isTrue(messages[0].fatal);
8600 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8601 });
8602
8603 it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => {
8604 const code = "var char; obj.char; var obj = { char: 1 };";
8605 const messages = linter.verify(code, {
8606 languageOptions: {
8607 ecmaVersion: 3,
8608 sourceType: "script",
8609 parserOptions: {
8610 allowReserved: true
8611 }
8612 }
8613 }, filename);
8614
8615 assert.strictEqual(messages.length, 0);
8616 });
8617
8618 it("should not allow the use of reserved words as variable names in ES > 3", () => {
8619 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8620
8621 ecmaVersions.forEach(ecmaVersion => {
8622 const code = "var enum;";
8623 const messages = linter.verify(code, {
8624 languageOptions: {
8625 ...(ecmaVersion ? { ecmaVersion } : {}),
8626 sourceType: "script"
8627 }
8628 }, filename);
8629
8630 assert.strictEqual(messages.length, 1);
8631 assert.strictEqual(messages[0].severity, 2);
8632 assert.isTrue(messages[0].fatal);
8633 assert.match(messages[0].message, /^Parsing error:.*'enum'/u);
8634 });
8635 });
8636
8637 it("should allow the use of reserved words as property names in ES > 3", () => {
8638 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8639
8640 ecmaVersions.forEach(ecmaVersion => {
8641 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
8642 const messages = linter.verify(code, {
8643 languageOptions: {
8644 ...(ecmaVersion ? { ecmaVersion } : {}),
8645 sourceType: "script"
8646 }
8647 }, filename);
8648
8649 assert.strictEqual(messages.length, 0);
8650 });
8651 });
8652
8653 it("should not allow `allowReserved: true` in ES > 3", () => {
8654 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8655
8656 ecmaVersions.forEach(ecmaVersion => {
8657 const code = "";
8658 const messages = linter.verify(code, {
8659 languageOptions: {
8660 ...(ecmaVersion ? { ecmaVersion } : {}),
8661 sourceType: "script",
8662 parserOptions: {
8663 allowReserved: true
8664 }
8665 }
8666 }, filename);
8667
8668 assert.strictEqual(messages.length, 1);
8669 assert.strictEqual(messages[0].severity, 2);
8670 assert.isTrue(messages[0].fatal);
8671 assert.match(messages[0].message, /^Parsing error:.*allowReserved/u);
8672 });
8673 });
8674 });
8675 });
8676
8677 describe("settings", () => {
8678 const ruleId = "test-rule";
8679
8680 it("should pass settings to all rules", () => {
8681
8682 const config = {
8683 plugins: {
8684 test: {
8685 rules: {
8686 [ruleId]: {
8687 create: context => ({
8688 Literal(node) {
8689 context.report(node, context.settings.info);
8690 }
8691 })
8692 }
8693 }
8694 }
8695 },
8696 settings: {
8697 info: "Hello"
8698 },
8699 rules: {
8700 [`test/${ruleId}`]: 1
8701 }
8702 };
8703
8704 const messages = linter.verify("0", config, filename);
8705 const suppressedMessages = linter.getSuppressedMessages();
8706
8707 assert.strictEqual(messages.length, 1);
8708 assert.strictEqual(messages[0].message, "Hello");
8709
8710 assert.strictEqual(suppressedMessages.length, 0);
8711 });
8712
8713 it("should not have any settings if they were not passed in", () => {
8714
8715 const config = {
8716 plugins: {
8717 test: {
8718 rules: {
8719 [ruleId]: {
8720 create: context => ({
8721 Literal(node) {
8722 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
8723 context.report(node, "Settings should be empty");
8724 }
8725 }
8726 })
8727 }
8728 }
8729 }
8730 },
8731 settings: {
8732 },
8733 rules: {
8734 [`test/${ruleId}`]: 1
8735 }
8736 };
8737
8738 const messages = linter.verify("0", config, filename);
8739 const suppressedMessages = linter.getSuppressedMessages();
8740
8741 assert.strictEqual(messages.length, 0);
8742 assert.strictEqual(suppressedMessages.length, 0);
8743 });
8744 });
8745
8746 describe("rules", () => {
8747 const code = "var answer = 6 * 7";
8748
8749 it("should be configurable by only setting the integer value", () => {
8750 const rule = "semi",
8751 config = { rules: {} };
8752
8753 config.rules[rule] = 1;
8754
8755 const messages = linter.verify(code, config, filename, true);
8756 const suppressedMessages = linter.getSuppressedMessages();
8757
8758 assert.strictEqual(messages.length, 1);
8759 assert.strictEqual(messages[0].ruleId, rule);
8760
8761 assert.strictEqual(suppressedMessages.length, 0);
8762 });
8763
8764 it("should be configurable by only setting the string value", () => {
8765 const rule = "semi",
8766 config = { rules: {} };
8767
8768 config.rules[rule] = "warn";
8769
8770 const messages = linter.verify(code, config, filename, true);
8771 const suppressedMessages = linter.getSuppressedMessages();
8772
8773 assert.strictEqual(messages.length, 1);
8774 assert.strictEqual(messages[0].severity, 1);
8775 assert.strictEqual(messages[0].ruleId, rule);
8776
8777 assert.strictEqual(suppressedMessages.length, 0);
8778 });
8779
8780 it("should be configurable by passing in values as an array", () => {
8781 const rule = "semi",
8782 config = { rules: {} };
8783
8784 config.rules[rule] = [1];
8785
8786 const messages = linter.verify(code, config, filename, true);
8787 const suppressedMessages = linter.getSuppressedMessages();
8788
8789 assert.strictEqual(messages.length, 1);
8790 assert.strictEqual(messages[0].ruleId, rule);
8791
8792 assert.strictEqual(suppressedMessages.length, 0);
8793 });
8794
8795 it("should be configurable by passing in string value as an array", () => {
8796 const rule = "semi",
8797 config = { rules: {} };
8798
8799 config.rules[rule] = ["warn"];
8800
8801 const messages = linter.verify(code, config, filename, true);
8802 const suppressedMessages = linter.getSuppressedMessages();
8803
8804 assert.strictEqual(messages.length, 1);
8805 assert.strictEqual(messages[0].severity, 1);
8806 assert.strictEqual(messages[0].ruleId, rule);
8807
8808 assert.strictEqual(suppressedMessages.length, 0);
8809 });
8810
8811 it("should not be configurable by setting other value", () => {
8812 const rule = "semi",
8813 config = { rules: {} };
8814
8815 config.rules[rule] = "1";
8816
8817 assert.throws(() => {
8818 linter.verify(code, config, filename, true);
8819 }, /Key "rules": Key "semi": Expected severity/u);
8820 });
8821
8822 it("should process empty config", () => {
8823 const config = {};
8824 const messages = linter.verify(code, config, filename, true);
8825 const suppressedMessages = linter.getSuppressedMessages();
8826
8827 assert.strictEqual(messages.length, 0);
8828 assert.strictEqual(suppressedMessages.length, 0);
8829 });
8830 });
8831
8832 });
8833
8834 describe("verify()", () => {
8835
8836 it("should report warnings in order by line and column when called", () => {
8837
8838 const code = "foo()\n alert('test')";
8839 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8840
8841 const messages = linter.verify(code, config, filename);
8842 const suppressedMessages = linter.getSuppressedMessages();
8843
8844 assert.strictEqual(messages.length, 3);
8845 assert.strictEqual(messages[0].line, 1);
8846 assert.strictEqual(messages[0].column, 6);
8847 assert.strictEqual(messages[1].line, 2);
8848 assert.strictEqual(messages[1].column, 18);
8849 assert.strictEqual(messages[2].line, 2);
8850 assert.strictEqual(messages[2].column, 18);
8851
8852 assert.strictEqual(suppressedMessages.length, 0);
8853 });
8854
8855 it("should report ignored file when filename isn't matched in the config array", () => {
8856
8857 const code = "foo()\n alert('test')";
8858 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8859
8860 const messages = linter.verify(code, config, "filename.ts");
8861
8862 assert.strictEqual(messages.length, 1);
8863 assert.deepStrictEqual(messages[0], {
8864 ruleId: null,
8865 severity: 1,
8866 message: "No matching configuration found for filename.ts.",
8867 line: 0,
8868 column: 0,
8869 nodeType: null
8870 });
8871 });
8872
8873 describe("Plugins", () => {
8874
8875 it("should not load rule definition when rule isn't used", () => {
8876
8877 const spy = sinon.spy();
8878
8879 const config = {
8880 plugins: {
8881 test: {
8882 rules: {
8883 checker: { create: spy }
8884 }
8885 }
8886 }
8887 };
8888
8889 linter.verify("code", config, filename);
8890 assert.isTrue(spy.notCalled, "Rule should not have been called");
8891 });
8892 });
8893
8894 describe("Rule Internals", () => {
8895
8896 const code = TEST_CODE;
8897
8898 it("should throw an error when an error occurs inside of a rule visitor", () => {
8899 const config = {
8900 plugins: {
8901 test: {
8902 rules: {
8903 checker: {
8904 create: () => ({
8905 Program() {
8906 throw new Error("Intentional error.");
8907 }
8908
8909 })
8910 }
8911 }
8912 }
8913 },
8914 rules: { "test/checker": "error" }
8915 };
8916
8917 assert.throws(() => {
8918 linter.verify(code, config, filename);
8919 }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "test/checker"`);
8920 });
8921
8922 it("should not call rule visitor with a `this` value", () => {
8923 const spy = sinon.spy();
8924 const config = {
8925 plugins: {
8926 test: {
8927 rules: {
8928 checker: {
8929 create: () => ({
8930 Program: spy
8931 })
8932 }
8933 }
8934 }
8935 },
8936 rules: { "test/checker": "error" }
8937 };
8938
8939 linter.verify("foo", config);
8940 assert(spy.calledOnce);
8941 assert.strictEqual(spy.firstCall.thisValue, void 0);
8942 });
8943
8944 it("should not call unrecognized rule visitor when present in a rule", () => {
8945 const spy = sinon.spy();
8946 const config = {
8947 plugins: {
8948 test: {
8949 rules: {
8950 checker: {
8951 create: () => ({
8952 newListener: spy
8953 })
8954 }
8955 }
8956 }
8957 },
8958 rules: {
8959 "test/checker": "error",
8960 "no-undef": "error"
8961 }
8962 };
8963
8964 linter.verify("foo", config);
8965 assert(spy.notCalled);
8966 });
8967
8968 it("should have all the `parent` properties on nodes when the rule visitors are created", () => {
8969 const spy = sinon.spy(context => {
8970 assert.strictEqual(context.getSourceCode(), context.sourceCode);
8971 const ast = context.sourceCode.ast;
8972
8973 assert.strictEqual(ast.body[0].parent, ast);
8974 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
8975 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
8976 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
8977
8978 return {};
8979 });
8980
8981 const config = {
8982 plugins: {
8983 test: {
8984 rules: {
8985 checker: { create: spy }
8986 }
8987 }
8988 },
8989 rules: { "test/checker": "error" }
8990 };
8991
8992 linter.verify("foo + bar", config);
8993 assert(spy.calledOnce);
8994 });
8995
8996 it("events for each node type should fire", () => {
8997
8998 // spies for various AST node types
8999 const spyLiteral = sinon.spy(),
9000 spyVariableDeclarator = sinon.spy(),
9001 spyVariableDeclaration = sinon.spy(),
9002 spyIdentifier = sinon.spy(),
9003 spyBinaryExpression = sinon.spy();
9004
9005 const config = {
9006 plugins: {
9007 test: {
9008 rules: {
9009 checker: {
9010 create() {
9011 return {
9012 Literal: spyLiteral,
9013 VariableDeclarator: spyVariableDeclarator,
9014 VariableDeclaration: spyVariableDeclaration,
9015 Identifier: spyIdentifier,
9016 BinaryExpression: spyBinaryExpression
9017 };
9018 }
9019 }
9020 }
9021 }
9022 },
9023 rules: { "test/checker": "error" }
9024 };
9025
9026 const messages = linter.verify(code, config, filename, true);
9027 const suppressedMessages = linter.getSuppressedMessages();
9028
9029 assert.strictEqual(messages.length, 0);
9030 sinon.assert.calledOnce(spyVariableDeclaration);
9031 sinon.assert.calledOnce(spyVariableDeclarator);
9032 sinon.assert.calledOnce(spyIdentifier);
9033 sinon.assert.calledTwice(spyLiteral);
9034 sinon.assert.calledOnce(spyBinaryExpression);
9035
9036 assert.strictEqual(suppressedMessages.length, 0);
9037 });
9038
9039 it("should throw an error if a rule reports a problem without a message", () => {
9040
9041 const config = {
9042 plugins: {
9043 test: {
9044 rules: {
9045 "invalid-report": {
9046 create: context => ({
9047 Program(node) {
9048 context.report({ node });
9049 }
9050 })
9051 }
9052 }
9053 }
9054 },
9055 rules: { "test/invalid-report": "error" }
9056 };
9057
9058 assert.throws(
9059 () => linter.verify("foo", config),
9060 TypeError,
9061 "Missing `message` property in report() call; add a message that describes the linting problem."
9062 );
9063 });
9064
9065
9066 });
9067
9068 describe("Rule Context", () => {
9069
9070 describe("context.getFilename()", () => {
9071 const ruleId = "filename-rule";
9072
9073 it("has access to the filename", () => {
9074
9075 const config = {
9076 plugins: {
9077 test: {
9078 rules: {
9079 [ruleId]: {
9080 create: context => ({
9081 Literal(node) {
9082 context.report(node, context.getFilename());
9083 }
9084 })
9085 }
9086 }
9087 }
9088 },
9089 rules: {
9090 [`test/${ruleId}`]: 1
9091 }
9092 };
9093
9094 const messages = linter.verify("0", config, filename);
9095 const suppressedMessages = linter.getSuppressedMessages();
9096
9097 assert.strictEqual(messages[0].message, filename);
9098 assert.strictEqual(suppressedMessages.length, 0);
9099 });
9100
9101 it("defaults filename to '<input>'", () => {
9102
9103 const config = {
9104 plugins: {
9105 test: {
9106 rules: {
9107 [ruleId]: {
9108 create: context => ({
9109 Literal(node) {
9110 context.report(node, context.getFilename());
9111 }
9112 })
9113 }
9114 }
9115 }
9116 },
9117 rules: {
9118 [`test/${ruleId}`]: 1
9119 }
9120 };
9121
9122
9123 const messages = linter.verify("0", config);
9124 const suppressedMessages = linter.getSuppressedMessages();
9125
9126 assert.strictEqual(messages[0].message, "<input>");
9127 assert.strictEqual(suppressedMessages.length, 0);
9128 });
9129 });
9130
9131 describe("context.filename", () => {
9132 const ruleId = "filename-rule";
9133
9134 it("has access to the filename", () => {
9135
9136 const config = {
9137 plugins: {
9138 test: {
9139 rules: {
9140 [ruleId]: {
9141 create: context => ({
9142 Literal(node) {
9143 assert.strictEqual(context.getFilename(), context.filename);
9144 context.report(node, context.filename);
9145 }
9146 })
9147 }
9148 }
9149 }
9150 },
9151 rules: {
9152 [`test/${ruleId}`]: 1
9153 }
9154 };
9155
9156 const messages = linter.verify("0", config, filename);
9157 const suppressedMessages = linter.getSuppressedMessages();
9158
9159 assert.strictEqual(messages[0].message, filename);
9160 assert.strictEqual(suppressedMessages.length, 0);
9161 });
9162
9163 it("defaults filename to '<input>'", () => {
9164
9165 const config = {
9166 plugins: {
9167 test: {
9168 rules: {
9169 [ruleId]: {
9170 create: context => ({
9171 Literal(node) {
9172 assert.strictEqual(context.getFilename(), context.filename);
9173 context.report(node, context.filename);
9174 }
9175 })
9176 }
9177 }
9178 }
9179 },
9180 rules: {
9181 [`test/${ruleId}`]: 1
9182 }
9183 };
9184
9185
9186 const messages = linter.verify("0", config);
9187 const suppressedMessages = linter.getSuppressedMessages();
9188
9189 assert.strictEqual(messages[0].message, "<input>");
9190 assert.strictEqual(suppressedMessages.length, 0);
9191 });
9192 });
9193
9194 describe("context.getPhysicalFilename()", () => {
9195
9196 const ruleId = "filename-rule";
9197
9198 it("has access to the physicalFilename", () => {
9199
9200 const config = {
9201 plugins: {
9202 test: {
9203 rules: {
9204 [ruleId]: {
9205 create: context => ({
9206 Literal(node) {
9207 context.report(node, context.getPhysicalFilename());
9208 }
9209 })
9210 }
9211 }
9212 }
9213 },
9214 rules: {
9215 [`test/${ruleId}`]: 1
9216 }
9217 };
9218
9219 const messages = linter.verify("0", config, filename);
9220 const suppressedMessages = linter.getSuppressedMessages();
9221
9222 assert.strictEqual(messages[0].message, filename);
9223 assert.strictEqual(suppressedMessages.length, 0);
9224 });
9225
9226 });
9227
9228 describe("context.physicalFilename", () => {
9229
9230 const ruleId = "filename-rule";
9231
9232 it("has access to the physicalFilename", () => {
9233
9234 const config = {
9235 plugins: {
9236 test: {
9237 rules: {
9238 [ruleId]: {
9239 create: context => ({
9240 Literal(node) {
9241 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
9242 context.report(node, context.physicalFilename);
9243 }
9244 })
9245 }
9246 }
9247 }
9248 },
9249 rules: {
9250 [`test/${ruleId}`]: 1
9251 }
9252 };
9253
9254 const messages = linter.verify("0", config, filename);
9255 const suppressedMessages = linter.getSuppressedMessages();
9256
9257 assert.strictEqual(messages[0].message, filename);
9258 assert.strictEqual(suppressedMessages.length, 0);
9259 });
9260
9261 });
9262
9263 describe("context.getSourceLines()", () => {
9264
9265 it("should get proper lines when using \\n as a line break", () => {
9266 const code = "a;\nb;";
9267 const spy = sinon.spy(context => {
9268 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
9269 return {};
9270 });
9271
9272 const config = {
9273 plugins: {
9274 test: {
9275 rules: {
9276 checker: { create: spy }
9277 }
9278 }
9279 },
9280 rules: { "test/checker": "error" }
9281 };
9282
9283 linter.verify(code, config);
9284 assert(spy.calledOnce);
9285 });
9286
9287 it("should get proper lines when using \\r\\n as a line break", () => {
9288 const code = "a;\r\nb;";
9289 const spy = sinon.spy(context => {
9290 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
9291 return {};
9292 });
9293
9294 const config = {
9295 plugins: {
9296 test: {
9297 rules: {
9298 checker: { create: spy }
9299 }
9300 }
9301 },
9302 rules: { "test/checker": "error" }
9303 };
9304
9305 linter.verify(code, config);
9306 assert(spy.calledOnce);
9307 });
9308
9309 it("should get proper lines when using \\r as a line break", () => {
9310 const code = "a;\rb;";
9311 const spy = sinon.spy(context => {
9312 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
9313 return {};
9314 });
9315
9316 const config = {
9317 plugins: {
9318 test: {
9319 rules: {
9320 checker: { create: spy }
9321 }
9322 }
9323 },
9324 rules: { "test/checker": "error" }
9325 };
9326
9327 linter.verify(code, config);
9328 assert(spy.calledOnce);
9329 });
9330
9331 it("should get proper lines when using \\u2028 as a line break", () => {
9332 const code = "a;\u2028b;";
9333 const spy = sinon.spy(context => {
9334 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
9335 return {};
9336 });
9337
9338 const config = {
9339 plugins: {
9340 test: {
9341 rules: {
9342 checker: { create: spy }
9343 }
9344 }
9345 },
9346 rules: { "test/checker": "error" }
9347 };
9348
9349 linter.verify(code, config);
9350 assert(spy.calledOnce);
9351 });
9352
9353 it("should get proper lines when using \\u2029 as a line break", () => {
9354 const code = "a;\u2029b;";
9355 const spy = sinon.spy(context => {
9356 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
9357 return {};
9358 });
9359
9360 const config = {
9361 plugins: {
9362 test: {
9363 rules: {
9364 checker: { create: spy }
9365 }
9366 }
9367 },
9368 rules: { "test/checker": "error" }
9369 };
9370
9371 linter.verify(code, config);
9372 assert(spy.calledOnce);
9373 });
9374
9375 });
9376
9377 describe("context.getSource()", () => {
9378 const code = TEST_CODE;
9379
9380 it("should retrieve all text when used without parameters", () => {
9381
9382 let spy;
9383
9384 const config = {
9385 plugins: {
9386 test: {
9387 rules: {
9388 checker: {
9389 create(context) {
9390 spy = sinon.spy(() => {
9391 assert.strictEqual(context.getSource(), TEST_CODE);
9392 });
9393 return { Program: spy };
9394 }
9395 }
9396 }
9397 }
9398 },
9399 rules: { "test/checker": "error" }
9400 };
9401
9402 linter.verify(code, config);
9403 assert(spy && spy.calledOnce);
9404 });
9405
9406 it("should retrieve all text for root node", () => {
9407
9408 let spy;
9409
9410 const config = {
9411 plugins: {
9412 test: {
9413 rules: {
9414 checker: {
9415 create(context) {
9416 spy = sinon.spy(node => {
9417 assert.strictEqual(context.getSource(node), TEST_CODE);
9418 });
9419 return { Program: spy };
9420 }
9421 }
9422 }
9423 }
9424 },
9425 rules: { "test/checker": "error" }
9426 };
9427
9428 linter.verify(code, config);
9429 assert(spy && spy.calledOnce);
9430 });
9431
9432 it("should clamp to valid range when retrieving characters before start of source", () => {
9433 let spy;
9434
9435 const config = {
9436 plugins: {
9437 test: {
9438 rules: {
9439 checker: {
9440 create(context) {
9441 spy = sinon.spy(node => {
9442 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
9443 });
9444 return { Program: spy };
9445 }
9446 }
9447 }
9448 }
9449 },
9450 rules: { "test/checker": "error" }
9451 };
9452
9453 linter.verify(code, config);
9454 assert(spy && spy.calledOnce);
9455 });
9456
9457 it("should retrieve all text for binary expression", () => {
9458 let spy;
9459
9460 const config = {
9461 plugins: {
9462 test: {
9463 rules: {
9464 checker: {
9465 create(context) {
9466 spy = sinon.spy(node => {
9467 assert.strictEqual(context.getSource(node), "6 * 7");
9468 });
9469 return { BinaryExpression: spy };
9470 }
9471 }
9472 }
9473 }
9474 },
9475 rules: { "test/checker": "error" }
9476 };
9477
9478 linter.verify(code, config);
9479 assert(spy && spy.calledOnce);
9480 });
9481
9482 it("should retrieve all text plus two characters before for binary expression", () => {
9483 let spy;
9484
9485 const config = {
9486 plugins: {
9487 test: {
9488 rules: {
9489 checker: {
9490 create(context) {
9491 spy = sinon.spy(node => {
9492 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
9493 });
9494 return { BinaryExpression: spy };
9495 }
9496 }
9497 }
9498 }
9499 },
9500 rules: { "test/checker": "error" }
9501 };
9502
9503 linter.verify(code, config);
9504 assert(spy && spy.calledOnce);
9505 });
9506
9507 it("should retrieve all text plus one character after for binary expression", () => {
9508 let spy;
9509
9510 const config = {
9511 plugins: {
9512 test: {
9513 rules: {
9514 checker: {
9515 create(context) {
9516 spy = sinon.spy(node => {
9517 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
9518 });
9519 return { BinaryExpression: spy };
9520 }
9521 }
9522 }
9523 }
9524 },
9525 rules: { "test/checker": "error" }
9526 };
9527
9528 linter.verify(code, config);
9529 assert(spy && spy.calledOnce);
9530 });
9531
9532 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
9533 let spy;
9534
9535 const config = {
9536 plugins: {
9537 test: {
9538 rules: {
9539 checker: {
9540 create(context) {
9541 spy = sinon.spy(node => {
9542 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
9543 });
9544 return { BinaryExpression: spy };
9545 }
9546 }
9547 }
9548 }
9549 },
9550 rules: { "test/checker": "error" }
9551 };
9552
9553 linter.verify(code, config);
9554 assert(spy && spy.calledOnce);
9555 });
9556
9557 });
9558
9559 describe("context.getAncestors()", () => {
9560 const code = TEST_CODE;
9561
9562 it("should retrieve all ancestors when used", () => {
9563
9564 let spy;
9565
9566 const config = {
9567 plugins: {
9568 test: {
9569 rules: {
9570 checker: {
9571 create(context) {
9572 spy = sinon.spy(() => {
9573 const ancestors = context.getAncestors();
9574
9575 assert.strictEqual(ancestors.length, 3);
9576 });
9577 return { BinaryExpression: spy };
9578 }
9579 }
9580 }
9581 }
9582 },
9583 rules: { "test/checker": "error" }
9584 };
9585
9586 linter.verify(code, config, filename, true);
9587 assert(spy && spy.calledOnce);
9588 });
9589
9590 it("should retrieve empty ancestors for root node", () => {
9591 let spy;
9592
9593 const config = {
9594 plugins: {
9595 test: {
9596 rules: {
9597 checker: {
9598 create(context) {
9599 spy = sinon.spy(() => {
9600 const ancestors = context.getAncestors();
9601
9602 assert.strictEqual(ancestors.length, 0);
9603 });
9604
9605 return { Program: spy };
9606 }
9607 }
9608 }
9609 }
9610 },
9611 rules: { "test/checker": "error" }
9612 };
9613
9614 linter.verify(code, config);
9615 assert(spy && spy.calledOnce);
9616 });
9617 });
9618
9619 describe("context.getNodeByRangeIndex()", () => {
9620 const code = TEST_CODE;
9621
9622 it("should retrieve a node starting at the given index", () => {
9623 const spy = sinon.spy(context => {
9624 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
9625 return {};
9626 });
9627
9628 const config = {
9629 plugins: {
9630 test: {
9631 rules: {
9632 checker: { create: spy }
9633 }
9634 }
9635 },
9636 rules: { "test/checker": "error" }
9637 };
9638
9639 linter.verify(code, config);
9640 assert(spy.calledOnce);
9641 });
9642
9643 it("should retrieve a node containing the given index", () => {
9644 const spy = sinon.spy(context => {
9645 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
9646 return {};
9647 });
9648
9649 const config = {
9650 plugins: {
9651 test: {
9652 rules: {
9653 checker: { create: spy }
9654 }
9655 }
9656 },
9657 rules: { "test/checker": "error" }
9658 };
9659
9660 linter.verify(code, config);
9661 assert(spy.calledOnce);
9662 });
9663
9664 it("should retrieve a node that is exactly the given index", () => {
9665 const spy = sinon.spy(context => {
9666 const node = context.getNodeByRangeIndex(13);
9667
9668 assert.strictEqual(node.type, "Literal");
9669 assert.strictEqual(node.value, 6);
9670 return {};
9671 });
9672
9673 const config = {
9674 plugins: {
9675 test: {
9676 rules: {
9677 checker: { create: spy }
9678 }
9679 }
9680 },
9681 rules: { "test/checker": "error" }
9682 };
9683
9684 linter.verify(code, config);
9685 assert(spy.calledOnce);
9686 });
9687
9688 it("should retrieve a node ending with the given index", () => {
9689 const spy = sinon.spy(context => {
9690 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
9691 return {};
9692 });
9693
9694 const config = {
9695 plugins: {
9696 test: {
9697 rules: {
9698 checker: { create: spy }
9699 }
9700 }
9701 },
9702 rules: { "test/checker": "error" }
9703 };
9704
9705 linter.verify(code, config);
9706 assert(spy.calledOnce);
9707 });
9708
9709 it("should retrieve the deepest node containing the given index", () => {
9710 const spy = sinon.spy(context => {
9711 const node1 = context.getNodeByRangeIndex(14);
9712
9713 assert.strictEqual(node1.type, "BinaryExpression");
9714
9715 const node2 = context.getNodeByRangeIndex(3);
9716
9717 assert.strictEqual(node2.type, "VariableDeclaration");
9718 return {};
9719 });
9720
9721 const config = {
9722 plugins: {
9723 test: {
9724 rules: {
9725 checker: { create: spy }
9726 }
9727 }
9728 },
9729 rules: { "test/checker": "error" }
9730 };
9731
9732 linter.verify(code, config);
9733 assert(spy.calledOnce);
9734 });
9735
9736 it("should return null if the index is outside the range of any node", () => {
9737 const spy = sinon.spy(context => {
9738 const node1 = context.getNodeByRangeIndex(-1);
9739
9740 assert.isNull(node1);
9741
9742 const node2 = context.getNodeByRangeIndex(-99);
9743
9744 assert.isNull(node2);
9745 return {};
9746 });
9747
9748 const config = {
9749 plugins: {
9750 test: {
9751 rules: {
9752 checker: { create: spy }
9753 }
9754 }
9755 },
9756 rules: { "test/checker": "error" }
9757 };
9758
9759 linter.verify(code, config);
9760 assert(spy.calledOnce);
9761 });
9762 });
9763
9764 describe("context.getScope()", () => {
9765 const codeToTestScope = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
9766
9767 it("should retrieve the global scope correctly from a Program", () => {
9768 let spy;
9769
9770 const config = {
9771 plugins: {
9772 test: {
9773 rules: {
9774 checker: {
9775 create(context) {
9776 spy = sinon.spy(() => {
9777 const scope = context.getScope();
9778
9779 assert.strictEqual(scope.type, "global");
9780 });
9781 return { Program: spy };
9782 }
9783 }
9784 }
9785 }
9786 },
9787 languageOptions: {
9788 ecmaVersion: 6
9789 },
9790 rules: { "test/checker": "error" }
9791 };
9792
9793 linter.verify(codeToTestScope, config);
9794 assert(spy && spy.calledOnce);
9795 });
9796
9797 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
9798 let spy;
9799
9800 const config = {
9801 plugins: {
9802 test: {
9803 rules: {
9804 checker: {
9805 create(context) {
9806 spy = sinon.spy(() => {
9807 const scope = context.getScope();
9808
9809 assert.strictEqual(scope.type, "function");
9810 });
9811 return { FunctionDeclaration: spy };
9812 }
9813 }
9814 }
9815 }
9816 },
9817 languageOptions: {
9818 ecmaVersion: 6
9819 },
9820 rules: { "test/checker": "error" }
9821 };
9822
9823 linter.verify(codeToTestScope, config);
9824 assert(spy && spy.calledTwice);
9825 });
9826
9827 it("should retrieve the function scope correctly from a LabeledStatement", () => {
9828 let spy;
9829
9830 const config = {
9831 plugins: {
9832 test: {
9833 rules: {
9834 checker: {
9835 create(context) {
9836 spy = sinon.spy(() => {
9837 const scope = context.getScope();
9838
9839 assert.strictEqual(scope.type, "function");
9840 assert.strictEqual(scope.block.id.name, "foo");
9841 });
9842 return { LabeledStatement: spy };
9843 }
9844 }
9845 }
9846 }
9847 },
9848 languageOptions: {
9849 ecmaVersion: 6
9850 },
9851 rules: { "test/checker": "error" }
9852 };
9853
9854
9855 linter.verify(codeToTestScope, config);
9856 assert(spy && spy.calledOnce);
9857 });
9858
9859 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
9860 let spy;
9861
9862 const config = {
9863 plugins: {
9864 test: {
9865 rules: {
9866 checker: {
9867 create(context) {
9868 spy = sinon.spy(() => {
9869 const scope = context.getScope();
9870
9871 assert.strictEqual(scope.type, "function");
9872 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
9873 });
9874
9875 return { ReturnStatement: spy };
9876 }
9877 }
9878 }
9879 }
9880 },
9881 languageOptions: {
9882 ecmaVersion: 6
9883 },
9884 rules: { "test/checker": "error" }
9885 };
9886
9887
9888 linter.verify(codeToTestScope, config);
9889 assert(spy && spy.calledOnce);
9890 });
9891
9892 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
9893 let spy;
9894
9895 const config = {
9896 plugins: {
9897 test: {
9898 rules: {
9899 checker: {
9900 create(context) {
9901 spy = sinon.spy(() => {
9902 const scope = context.getScope();
9903
9904 assert.strictEqual(scope.type, "switch");
9905 assert.strictEqual(scope.block.type, "SwitchStatement");
9906 });
9907
9908 return { SwitchStatement: spy };
9909 }
9910 }
9911 }
9912 }
9913 },
9914 languageOptions: {
9915 ecmaVersion: 6
9916 },
9917 rules: { "test/checker": "error" }
9918 };
9919
9920 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
9921 assert(spy && spy.calledOnce);
9922 });
9923
9924 it("should retrieve the function scope correctly from within a BlockStatement", () => {
9925 let spy;
9926
9927 const config = {
9928 plugins: {
9929 test: {
9930 rules: {
9931 checker: {
9932 create(context) {
9933 spy = sinon.spy(() => {
9934 const scope = context.getScope();
9935
9936 assert.strictEqual(scope.type, "block");
9937 assert.strictEqual(scope.block.type, "BlockStatement");
9938 });
9939
9940 return { BlockStatement: spy };
9941 }
9942 }
9943 }
9944 }
9945 },
9946 languageOptions: {
9947 ecmaVersion: 6
9948 },
9949 rules: { "test/checker": "error" }
9950 };
9951
9952
9953 linter.verify("var x; {let y = 1}", config);
9954 assert(spy && spy.calledOnce);
9955 });
9956
9957 it("should retrieve the function scope correctly from within a nested block statement", () => {
9958 let spy;
9959
9960 const config = {
9961 plugins: {
9962 test: {
9963 rules: {
9964 checker: {
9965 create(context) {
9966 spy = sinon.spy(() => {
9967 const scope = context.getScope();
9968
9969 assert.strictEqual(scope.type, "block");
9970 assert.strictEqual(scope.block.type, "BlockStatement");
9971 });
9972
9973 return { BlockStatement: spy };
9974 }
9975 }
9976 }
9977 }
9978 },
9979 languageOptions: {
9980 ecmaVersion: 6
9981 },
9982 rules: { "test/checker": "error" }
9983 };
9984
9985
9986 linter.verify("if (true) { let x = 1 }", config);
9987 assert(spy && spy.calledOnce);
9988 });
9989
9990 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
9991 let spy;
9992
9993 const config = {
9994 plugins: {
9995 test: {
9996 rules: {
9997 checker: {
9998 create(context) {
9999 spy = sinon.spy(() => {
10000 const scope = context.getScope();
10001
10002 assert.strictEqual(scope.type, "function");
10003 assert.strictEqual(scope.block.type, "FunctionDeclaration");
10004 });
10005
10006 return { FunctionDeclaration: spy };
10007 }
10008 }
10009 }
10010 }
10011 },
10012 languageOptions: {
10013 ecmaVersion: 6
10014 },
10015 rules: { "test/checker": "error" }
10016 };
10017
10018
10019 linter.verify("function foo() {}", config);
10020 assert(spy && spy.calledOnce);
10021 });
10022
10023 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
10024 let spy;
10025
10026 const config = {
10027 plugins: {
10028 test: {
10029 rules: {
10030 checker: {
10031 create(context) {
10032 spy = sinon.spy(() => {
10033 const scope = context.getScope();
10034
10035 assert.strictEqual(scope.type, "function");
10036 assert.strictEqual(scope.block.type, "FunctionExpression");
10037 });
10038
10039 return { FunctionExpression: spy };
10040 }
10041 }
10042 }
10043 }
10044 },
10045 languageOptions: {
10046 ecmaVersion: 6
10047 },
10048 rules: { "test/checker": "error" }
10049 };
10050
10051
10052 linter.verify("(function foo() {})();", config);
10053 assert(spy && spy.calledOnce);
10054 });
10055
10056 it("should retrieve the catch scope correctly from within a CatchClause", () => {
10057 let spy;
10058
10059 const config = {
10060 plugins: {
10061 test: {
10062 rules: {
10063 checker: {
10064 create(context) {
10065 spy = sinon.spy(() => {
10066 const scope = context.getScope();
10067
10068 assert.strictEqual(scope.type, "catch");
10069 assert.strictEqual(scope.block.type, "CatchClause");
10070 });
10071
10072 return { CatchClause: spy };
10073 }
10074 }
10075 }
10076 }
10077 },
10078 languageOptions: {
10079 ecmaVersion: 6
10080 },
10081 rules: { "test/checker": "error" }
10082 };
10083
10084 linter.verify("try {} catch (err) {}", config);
10085 assert(spy && spy.calledOnce);
10086 });
10087
10088 it("should retrieve module scope correctly from an ES6 module", () => {
10089 let spy;
10090
10091 const config = {
10092 plugins: {
10093 test: {
10094 rules: {
10095 checker: {
10096 create(context) {
10097 spy = sinon.spy(() => {
10098 const scope = context.getScope();
10099
10100 assert.strictEqual(scope.type, "module");
10101 });
10102
10103 return { AssignmentExpression: spy };
10104 }
10105 }
10106 }
10107 }
10108 },
10109 languageOptions: {
10110 ecmaVersion: 6,
10111 sourceType: "module"
10112 },
10113 rules: { "test/checker": "error" }
10114 };
10115
10116
10117 linter.verify("var foo = {}; foo.bar = 1;", config);
10118 assert(spy && spy.calledOnce);
10119 });
10120
10121 it("should retrieve function scope correctly when sourceType is commonjs", () => {
10122 let spy;
10123
10124 const config = {
10125 plugins: {
10126 test: {
10127 rules: {
10128 checker: {
10129 create(context) {
10130 spy = sinon.spy(() => {
10131 const scope = context.getScope();
10132
10133 assert.strictEqual(scope.type, "function");
10134 });
10135
10136 return { AssignmentExpression: spy };
10137 }
10138 }
10139 }
10140 }
10141 },
10142 languageOptions: {
10143 ecmaVersion: 6,
10144 sourceType: "commonjs"
10145 },
10146 rules: { "test/checker": "error" }
10147 };
10148
10149 linter.verify("var foo = {}; foo.bar = 1;", config);
10150 assert(spy && spy.calledOnce);
10151 });
10152
10153 describe("Scope Internals", () => {
10154
10155 /**
10156 * Get the scope on the node `astSelector` specified.
10157 * @param {string} codeToEvaluate The source code to verify.
10158 * @param {string} astSelector The AST selector to get scope.
10159 * @param {number} [ecmaVersion=5] The ECMAScript version.
10160 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
10161 */
10162 function getScope(codeToEvaluate, astSelector, ecmaVersion = 5) {
10163 let node, scope;
10164
10165 const config = {
10166 plugins: {
10167 test: {
10168 rules: {
10169 "get-scope": {
10170 create: context => ({
10171 [astSelector](node0) {
10172 node = node0;
10173 scope = context.getScope();
10174 }
10175 })
10176 }
10177 }
10178 }
10179 },
10180 languageOptions: {
10181 ecmaVersion,
10182 sourceType: "script"
10183 },
10184 rules: { "test/get-scope": "error" }
10185 };
10186
10187 linter.verify(
10188 codeToEvaluate,
10189 config
10190 );
10191
10192 return { node, scope };
10193 }
10194
10195 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
10196 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
10197
10198 assert.strictEqual(scope.type, "function");
10199 assert.strictEqual(scope.block, node);
10200 });
10201
10202 it("should return 'function' scope on FunctionExpression (ES5)", () => {
10203 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
10204
10205 assert.strictEqual(scope.type, "function");
10206 assert.strictEqual(scope.block, node);
10207 });
10208
10209 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
10210 const { node, scope } = getScope("function f() {}", "BlockStatement");
10211
10212 assert.strictEqual(scope.type, "function");
10213 assert.strictEqual(scope.block, node.parent);
10214 });
10215
10216 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
10217 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
10218
10219 assert.strictEqual(scope.type, "function");
10220 assert.strictEqual(scope.block, node.parent);
10221 });
10222
10223 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
10224 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
10225
10226 assert.strictEqual(scope.type, "function");
10227 assert.strictEqual(scope.block, node.parent.parent);
10228 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
10229 });
10230
10231 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
10232 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
10233
10234 assert.strictEqual(scope.type, "block");
10235 assert.strictEqual(scope.upper.type, "function");
10236 assert.strictEqual(scope.block, node);
10237 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
10238 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
10239 });
10240
10241 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
10242 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
10243
10244 assert.strictEqual(scope.type, "block");
10245 assert.strictEqual(scope.upper.type, "block");
10246 assert.strictEqual(scope.upper.upper.type, "function");
10247 assert.strictEqual(scope.block, node);
10248 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
10249 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
10250 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
10251 });
10252
10253 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
10254 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
10255
10256 assert.strictEqual(scope.type, "function");
10257 assert.strictEqual(scope.block, node.parent.parent);
10258 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
10259 });
10260
10261 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
10262 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
10263
10264 assert.strictEqual(scope.type, "switch");
10265 assert.strictEqual(scope.block, node);
10266 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
10267 });
10268
10269 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
10270 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
10271
10272 assert.strictEqual(scope.type, "function");
10273 assert.strictEqual(scope.block, node.parent.parent.parent);
10274 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
10275 });
10276
10277 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
10278 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
10279
10280 assert.strictEqual(scope.type, "switch");
10281 assert.strictEqual(scope.block, node.parent);
10282 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
10283 });
10284
10285 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
10286 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
10287
10288 assert.strictEqual(scope.type, "catch");
10289 assert.strictEqual(scope.block, node);
10290 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
10291 });
10292
10293 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
10294 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
10295
10296 assert.strictEqual(scope.type, "catch");
10297 assert.strictEqual(scope.block, node);
10298 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
10299 });
10300
10301 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
10302 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
10303
10304 assert.strictEqual(scope.type, "catch");
10305 assert.strictEqual(scope.block, node.parent);
10306 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
10307 });
10308
10309 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
10310 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
10311
10312 assert.strictEqual(scope.type, "block");
10313 assert.strictEqual(scope.block, node);
10314 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
10315 });
10316
10317 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
10318 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
10319
10320 assert.strictEqual(scope.type, "function");
10321 assert.strictEqual(scope.block, node.parent.parent);
10322 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
10323 });
10324
10325 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
10326 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
10327
10328 assert.strictEqual(scope.type, "for");
10329 assert.strictEqual(scope.block, node);
10330 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
10331 });
10332
10333 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
10334 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
10335
10336 assert.strictEqual(scope.type, "function");
10337 assert.strictEqual(scope.block, node.parent.parent.parent);
10338 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
10339 });
10340
10341 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
10342 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
10343
10344 assert.strictEqual(scope.type, "block");
10345 assert.strictEqual(scope.upper.type, "for");
10346 assert.strictEqual(scope.block, node);
10347 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
10348 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
10349 });
10350
10351 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
10352 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
10353
10354 assert.strictEqual(scope.type, "function");
10355 assert.strictEqual(scope.block, node.parent.parent);
10356 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
10357 });
10358
10359 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
10360 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
10361
10362 assert.strictEqual(scope.type, "for");
10363 assert.strictEqual(scope.block, node);
10364 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
10365 });
10366
10367 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
10368 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
10369
10370 assert.strictEqual(scope.type, "function");
10371 assert.strictEqual(scope.block, node.parent.parent.parent);
10372 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
10373 });
10374
10375 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
10376 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
10377
10378 assert.strictEqual(scope.type, "block");
10379 assert.strictEqual(scope.upper.type, "for");
10380 assert.strictEqual(scope.block, node);
10381 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
10382 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
10383 });
10384
10385 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
10386 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
10387
10388 assert.strictEqual(scope.type, "for");
10389 assert.strictEqual(scope.block, node);
10390 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
10391 });
10392
10393 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
10394 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
10395
10396 assert.strictEqual(scope.type, "block");
10397 assert.strictEqual(scope.upper.type, "for");
10398 assert.strictEqual(scope.block, node);
10399 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
10400 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
10401 });
10402
10403 it("should shadow the same name variable by the iteration variable.", () => {
10404 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
10405
10406 assert.strictEqual(scope.type, "for");
10407 assert.strictEqual(scope.upper.type, "global");
10408 assert.strictEqual(scope.block, node);
10409 assert.strictEqual(scope.upper.variables[0].references.length, 0);
10410 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
10411 assert.strictEqual(scope.references[1].identifier, node.right);
10412 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
10413 });
10414 });
10415
10416 describe("Variables and references", () => {
10417 const code = [
10418 "a;",
10419 "function foo() { b; }",
10420 "Object;",
10421 "foo;",
10422 "var c;",
10423 "c;",
10424 "/* global d */",
10425 "d;",
10426 "e;",
10427 "f;"
10428 ].join("\n");
10429 let scope = null;
10430
10431 beforeEach(() => {
10432 let ok = false;
10433
10434 const config = {
10435 plugins: {
10436 test: {
10437 rules: {
10438 test: {
10439 create: context => ({
10440 Program() {
10441 scope = context.getScope();
10442 ok = true;
10443 }
10444 })
10445 }
10446 }
10447 }
10448 },
10449 languageOptions: {
10450 globals: { e: true, f: false },
10451 sourceType: "script",
10452 ecmaVersion: 5
10453 },
10454 rules: {
10455 "test/test": 2
10456 }
10457 };
10458
10459 linter.verify(code, config);
10460 assert(ok);
10461 });
10462
10463 afterEach(() => {
10464 scope = null;
10465 });
10466
10467 it("Scope#through should contain references of undefined variables", () => {
10468 assert.strictEqual(scope.through.length, 2);
10469 assert.strictEqual(scope.through[0].identifier.name, "a");
10470 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
10471 assert.strictEqual(scope.through[0].resolved, null);
10472 assert.strictEqual(scope.through[1].identifier.name, "b");
10473 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
10474 assert.strictEqual(scope.through[1].resolved, null);
10475 });
10476
10477 it("Scope#variables should contain global variables", () => {
10478 assert(scope.variables.some(v => v.name === "Object"));
10479 assert(scope.variables.some(v => v.name === "foo"));
10480 assert(scope.variables.some(v => v.name === "c"));
10481 assert(scope.variables.some(v => v.name === "d"));
10482 assert(scope.variables.some(v => v.name === "e"));
10483 assert(scope.variables.some(v => v.name === "f"));
10484 });
10485
10486 it("Scope#set should contain global variables", () => {
10487 assert(scope.set.get("Object"));
10488 assert(scope.set.get("foo"));
10489 assert(scope.set.get("c"));
10490 assert(scope.set.get("d"));
10491 assert(scope.set.get("e"));
10492 assert(scope.set.get("f"));
10493 });
10494
10495 it("Variables#references should contain their references", () => {
10496 assert.strictEqual(scope.set.get("Object").references.length, 1);
10497 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
10498 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
10499 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
10500 assert.strictEqual(scope.set.get("foo").references.length, 1);
10501 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
10502 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
10503 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
10504 assert.strictEqual(scope.set.get("c").references.length, 1);
10505 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
10506 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
10507 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
10508 assert.strictEqual(scope.set.get("d").references.length, 1);
10509 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
10510 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
10511 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
10512 assert.strictEqual(scope.set.get("e").references.length, 1);
10513 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
10514 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
10515 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
10516 assert.strictEqual(scope.set.get("f").references.length, 1);
10517 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
10518 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
10519 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
10520 });
10521
10522 it("Reference#resolved should be their variable", () => {
10523 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
10524 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
10525 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
10526 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
10527 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
10528 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
10529 });
10530 });
10531 });
10532
10533 describe("context.getDeclaredVariables(node)", () => {
10534
10535 /**
10536 * Assert `context.getDeclaredVariables(node)` is valid.
10537 * @param {string} code A code to check.
10538 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
10539 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
10540 * @returns {void}
10541 */
10542 function verify(code, type, expectedNamesList) {
10543 const config = {
10544 plugins: {
10545 test: {
10546
10547 rules: {
10548 test: {
10549 create(context) {
10550
10551 /**
10552 * Assert `context.getDeclaredVariables(node)` is empty.
10553 * @param {ASTNode} node A node to check.
10554 * @returns {void}
10555 */
10556 function checkEmpty(node) {
10557 assert.strictEqual(0, context.getDeclaredVariables(node).length);
10558 }
10559 const rule = {
10560 Program: checkEmpty,
10561 EmptyStatement: checkEmpty,
10562 BlockStatement: checkEmpty,
10563 ExpressionStatement: checkEmpty,
10564 LabeledStatement: checkEmpty,
10565 BreakStatement: checkEmpty,
10566 ContinueStatement: checkEmpty,
10567 WithStatement: checkEmpty,
10568 SwitchStatement: checkEmpty,
10569 ReturnStatement: checkEmpty,
10570 ThrowStatement: checkEmpty,
10571 TryStatement: checkEmpty,
10572 WhileStatement: checkEmpty,
10573 DoWhileStatement: checkEmpty,
10574 ForStatement: checkEmpty,
10575 ForInStatement: checkEmpty,
10576 DebuggerStatement: checkEmpty,
10577 ThisExpression: checkEmpty,
10578 ArrayExpression: checkEmpty,
10579 ObjectExpression: checkEmpty,
10580 Property: checkEmpty,
10581 SequenceExpression: checkEmpty,
10582 UnaryExpression: checkEmpty,
10583 BinaryExpression: checkEmpty,
10584 AssignmentExpression: checkEmpty,
10585 UpdateExpression: checkEmpty,
10586 LogicalExpression: checkEmpty,
10587 ConditionalExpression: checkEmpty,
10588 CallExpression: checkEmpty,
10589 NewExpression: checkEmpty,
10590 MemberExpression: checkEmpty,
10591 SwitchCase: checkEmpty,
10592 Identifier: checkEmpty,
10593 Literal: checkEmpty,
10594 ForOfStatement: checkEmpty,
10595 ArrowFunctionExpression: checkEmpty,
10596 YieldExpression: checkEmpty,
10597 TemplateLiteral: checkEmpty,
10598 TaggedTemplateExpression: checkEmpty,
10599 TemplateElement: checkEmpty,
10600 ObjectPattern: checkEmpty,
10601 ArrayPattern: checkEmpty,
10602 RestElement: checkEmpty,
10603 AssignmentPattern: checkEmpty,
10604 ClassBody: checkEmpty,
10605 MethodDefinition: checkEmpty,
10606 MetaProperty: checkEmpty
10607 };
10608
10609 rule[type] = function(node) {
10610 const expectedNames = expectedNamesList.shift();
10611 const variables = context.getDeclaredVariables(node);
10612
10613 assert(Array.isArray(expectedNames));
10614 assert(Array.isArray(variables));
10615 assert.strictEqual(expectedNames.length, variables.length);
10616 for (let i = variables.length - 1; i >= 0; i--) {
10617 assert.strictEqual(expectedNames[i], variables[i].name);
10618 }
10619 };
10620 return rule;
10621 }
10622
10623 }
10624 }
10625
10626 }
10627 },
10628 languageOptions: {
10629 ecmaVersion: 6,
10630 sourceType: "module"
10631 },
10632 rules: {
10633 "test/test": 2
10634 }
10635 };
10636
10637 linter.verify(code, config);
10638
10639 // Check all expected names are asserted.
10640 assert.strictEqual(0, expectedNamesList.length);
10641 }
10642
10643 it("VariableDeclaration", () => {
10644 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 ";
10645 const namesList = [
10646 ["a", "b", "c"],
10647 ["d", "e", "f"],
10648 ["g", "h", "i", "j", "k"],
10649 ["l"]
10650 ];
10651
10652 verify(code, "VariableDeclaration", namesList);
10653 });
10654
10655 it("VariableDeclaration (on for-in/of loop)", () => {
10656
10657 // TDZ scope is created here, so tests to exclude those.
10658 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 ";
10659 const namesList = [
10660 ["a", "b", "c"],
10661 ["g"],
10662 ["d", "e", "f"],
10663 ["h"]
10664 ];
10665
10666 verify(code, "VariableDeclaration", namesList);
10667 });
10668
10669 it("VariableDeclarator", () => {
10670
10671 // TDZ scope is created here, so tests to exclude those.
10672 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 ";
10673 const namesList = [
10674 ["a", "b", "c"],
10675 ["d", "e", "f"],
10676 ["g", "h", "i"],
10677 ["j", "k"],
10678 ["l"]
10679 ];
10680
10681 verify(code, "VariableDeclarator", namesList);
10682 });
10683
10684 it("FunctionDeclaration", () => {
10685 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 ";
10686 const namesList = [
10687 ["foo", "a", "b", "c", "d", "e"],
10688 ["bar", "f", "g", "h", "i", "j"]
10689 ];
10690
10691 verify(code, "FunctionDeclaration", namesList);
10692 });
10693
10694 it("FunctionExpression", () => {
10695 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 ";
10696 const namesList = [
10697 ["foo", "a", "b", "c", "d", "e"],
10698 ["bar", "f", "g", "h", "i", "j"],
10699 ["q"]
10700 ];
10701
10702 verify(code, "FunctionExpression", namesList);
10703 });
10704
10705 it("ArrowFunctionExpression", () => {
10706 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 ";
10707 const namesList = [
10708 ["a", "b", "c", "d", "e"],
10709 ["f", "g", "h", "i", "j"]
10710 ];
10711
10712 verify(code, "ArrowFunctionExpression", namesList);
10713 });
10714
10715 it("ClassDeclaration", () => {
10716 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
10717 const namesList = [
10718 ["A", "A"], // outer scope's and inner scope's.
10719 ["B", "B"]
10720 ];
10721
10722 verify(code, "ClassDeclaration", namesList);
10723 });
10724
10725 it("ClassExpression", () => {
10726 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
10727 const namesList = [
10728 ["A"],
10729 ["B"]
10730 ];
10731
10732 verify(code, "ClassExpression", namesList);
10733 });
10734
10735 it("CatchClause", () => {
10736 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
10737 const namesList = [
10738 ["a", "b"],
10739 ["c", "d"]
10740 ];
10741
10742 verify(code, "CatchClause", namesList);
10743 });
10744
10745 it("ImportDeclaration", () => {
10746 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10747 const namesList = [
10748 [],
10749 ["a"],
10750 ["b", "c", "d"]
10751 ];
10752
10753 verify(code, "ImportDeclaration", namesList);
10754 });
10755
10756 it("ImportSpecifier", () => {
10757 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10758 const namesList = [
10759 ["c"],
10760 ["d"]
10761 ];
10762
10763 verify(code, "ImportSpecifier", namesList);
10764 });
10765
10766 it("ImportDefaultSpecifier", () => {
10767 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10768 const namesList = [
10769 ["b"]
10770 ];
10771
10772 verify(code, "ImportDefaultSpecifier", namesList);
10773 });
10774
10775 it("ImportNamespaceSpecifier", () => {
10776 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10777 const namesList = [
10778 ["a"]
10779 ];
10780
10781 verify(code, "ImportNamespaceSpecifier", namesList);
10782 });
10783 });
10784
10785 describe("context.markVariableAsUsed()", () => {
10786
10787 it("should mark variables in current scope as used", () => {
10788 const code = "var a = 1, b = 2;";
10789 let spy;
10790
10791 const config = {
10792 plugins: {
10793 test: {
10794 rules: {
10795 checker: {
10796 create(context) {
10797 spy = sinon.spy(() => {
10798 assert.isTrue(context.markVariableAsUsed("a"));
10799
10800 const scope = context.getScope();
10801
10802 assert.isTrue(getVariable(scope, "a").eslintUsed);
10803 assert.notOk(getVariable(scope, "b").eslintUsed);
10804 });
10805
10806 return { "Program:exit": spy };
10807 }
10808 }
10809 }
10810 }
10811 },
10812 languageOptions: {
10813 sourceType: "script"
10814 },
10815 rules: { "test/checker": "error" }
10816 };
10817
10818 linter.verify(code, config);
10819 assert(spy && spy.calledOnce);
10820 });
10821
10822 it("should mark variables in function args as used", () => {
10823 const code = "function abc(a, b) { return 1; }";
10824 let spy;
10825
10826 const config = {
10827 plugins: {
10828 test: {
10829 rules: {
10830 checker: {
10831 create(context) {
10832 spy = sinon.spy(() => {
10833 assert.isTrue(context.markVariableAsUsed("a"));
10834
10835 const scope = context.getScope();
10836
10837 assert.isTrue(getVariable(scope, "a").eslintUsed);
10838 assert.notOk(getVariable(scope, "b").eslintUsed);
10839 });
10840
10841 return { ReturnStatement: spy };
10842 }
10843 }
10844 }
10845 }
10846 },
10847 rules: { "test/checker": "error" }
10848 };
10849
10850 linter.verify(code, config);
10851 assert(spy && spy.calledOnce);
10852 });
10853
10854 it("should mark variables in higher scopes as used", () => {
10855 const code = "var a, b; function abc() { return 1; }";
10856 let returnSpy, exitSpy;
10857
10858 const config = {
10859 plugins: {
10860 test: {
10861 rules: {
10862 checker: {
10863 create(context) {
10864 returnSpy = sinon.spy(() => {
10865 assert.isTrue(context.markVariableAsUsed("a"));
10866 });
10867 exitSpy = sinon.spy(() => {
10868 const scope = context.getScope();
10869
10870 assert.isTrue(getVariable(scope, "a").eslintUsed);
10871 assert.notOk(getVariable(scope, "b").eslintUsed);
10872 });
10873
10874 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
10875 }
10876 }
10877 }
10878 }
10879 },
10880 languageOptions: {
10881 sourceType: "script"
10882 },
10883 rules: { "test/checker": "error" }
10884 };
10885
10886 linter.verify(code, config);
10887 assert(returnSpy && returnSpy.calledOnce);
10888 assert(exitSpy && exitSpy.calledOnce);
10889 });
10890
10891 it("should mark variables as used when sourceType is commonjs", () => {
10892 const code = "var a = 1, b = 2;";
10893 let spy;
10894
10895 const config = {
10896 plugins: {
10897 test: {
10898 rules: {
10899 checker: {
10900 create(context) {
10901 spy = sinon.spy(() => {
10902 const globalScope = context.getScope(),
10903 childScope = globalScope.childScopes[0];
10904
10905 assert.isTrue(context.markVariableAsUsed("a"), "Call to markVariableAsUsed should return true");
10906
10907 assert.isTrue(getVariable(childScope, "a").eslintUsed, "'a' should be marked as used.");
10908 assert.isUndefined(getVariable(childScope, "b").eslintUsed, "'b' should be marked as used.");
10909 });
10910
10911 return { "Program:exit": spy };
10912 }
10913 }
10914 }
10915 }
10916 },
10917 languageOptions: {
10918 sourceType: "commonjs"
10919 },
10920 rules: { "test/checker": "error" }
10921 };
10922
10923 linter.verify(code, config);
10924 assert(spy && spy.calledOnce, "Spy wasn't called.");
10925 });
10926
10927 it("should mark variables in modules as used", () => {
10928 const code = "var a = 1, b = 2;";
10929 let spy;
10930
10931 const config = {
10932 plugins: {
10933 test: {
10934 rules: {
10935 checker: {
10936 create(context) {
10937 spy = sinon.spy(() => {
10938 const globalScope = context.getScope(),
10939 childScope = globalScope.childScopes[0];
10940
10941 assert.isTrue(context.markVariableAsUsed("a"));
10942
10943 assert.isTrue(getVariable(childScope, "a").eslintUsed);
10944 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
10945 });
10946
10947 return { "Program:exit": spy };
10948 }
10949 }
10950 }
10951 }
10952 },
10953 languageOptions: {
10954 ecmaVersion: 6,
10955 sourceType: "module"
10956 },
10957 rules: { "test/checker": "error" }
10958 };
10959
10960 linter.verify(code, config);
10961 assert(spy && spy.calledOnce);
10962 });
10963
10964 it("should return false if the given variable is not found", () => {
10965 const code = "var a = 1, b = 2;";
10966 let spy;
10967
10968 const config = {
10969 plugins: {
10970 test: {
10971 rules: {
10972 checker: {
10973 create(context) {
10974 spy = sinon.spy(() => {
10975 assert.isFalse(context.markVariableAsUsed("c"));
10976 });
10977
10978 return { "Program:exit": spy };
10979 }
10980 }
10981 }
10982 }
10983 },
10984 rules: { "test/checker": "error" }
10985 };
10986
10987 linter.verify(code, config);
10988 assert(spy && spy.calledOnce);
10989 });
10990 });
10991
10992 describe("context.getCwd()", () => {
10993 const code = "a;\nb;";
10994 const baseConfig = { rules: { "test/checker": "error" } };
10995
10996 it("should get cwd correctly in the context", () => {
10997 const cwd = "cwd";
10998 const linterWithOption = new Linter({ cwd, configType: "flat" });
10999 let spy;
11000 const config = {
11001 plugins: {
11002 test: {
11003 rules: {
11004 checker: {
11005 create(context) {
11006 spy = sinon.spy(() => {
11007 assert.strictEqual(context.getCwd(), cwd);
11008 });
11009 return { Program: spy };
11010 }
11011 }
11012 }
11013 }
11014 },
11015 ...baseConfig
11016 };
11017
11018 linterWithOption.verify(code, config);
11019 assert(spy && spy.calledOnce);
11020 });
11021
11022 it("should assign process.cwd() to it if cwd is undefined", () => {
11023
11024 const linterWithOption = new Linter({ configType: "flat" });
11025 let spy;
11026 const config = {
11027 plugins: {
11028 test: {
11029 rules: {
11030 checker: {
11031 create(context) {
11032
11033 spy = sinon.spy(() => {
11034 assert.strictEqual(context.getCwd(), process.cwd());
11035 });
11036 return { Program: spy };
11037 }
11038 }
11039 }
11040 }
11041 },
11042 ...baseConfig
11043 };
11044
11045 linterWithOption.verify(code, config);
11046 assert(spy && spy.calledOnce);
11047 });
11048
11049 it("should assign process.cwd() to it if the option is undefined", () => {
11050 let spy;
11051 const config = {
11052 plugins: {
11053 test: {
11054 rules: {
11055 checker: {
11056 create(context) {
11057
11058 spy = sinon.spy(() => {
11059 assert.strictEqual(context.getCwd(), process.cwd());
11060 });
11061 return { Program: spy };
11062 }
11063 }
11064 }
11065 }
11066 },
11067 ...baseConfig
11068 };
11069
11070 linter.verify(code, config);
11071 assert(spy && spy.calledOnce);
11072 });
11073 });
11074
11075 describe("context.cwd", () => {
11076 const code = "a;\nb;";
11077 const baseConfig = { rules: { "test/checker": "error" } };
11078
11079 it("should get cwd correctly in the context", () => {
11080 const cwd = "cwd";
11081 const linterWithOption = new Linter({ cwd, configType: "flat" });
11082 let spy;
11083 const config = {
11084 plugins: {
11085 test: {
11086 rules: {
11087 checker: {
11088 create(context) {
11089 spy = sinon.spy(() => {
11090 assert.strictEqual(context.cwd, cwd);
11091 });
11092 return { Program: spy };
11093 }
11094 }
11095 }
11096 }
11097 },
11098 ...baseConfig
11099 };
11100
11101 linterWithOption.verify(code, config);
11102 assert(spy && spy.calledOnce);
11103 });
11104
11105 it("should assign process.cwd() to it if cwd is undefined", () => {
11106
11107 const linterWithOption = new Linter({ configType: "flat" });
11108 let spy;
11109 const config = {
11110 plugins: {
11111 test: {
11112 rules: {
11113 checker: {
11114 create(context) {
11115
11116 spy = sinon.spy(() => {
11117 assert.strictEqual(context.getCwd(), context.cwd);
11118 assert.strictEqual(context.cwd, process.cwd());
11119 });
11120 return { Program: spy };
11121 }
11122 }
11123 }
11124 }
11125 },
11126 ...baseConfig
11127 };
11128
11129 linterWithOption.verify(code, config);
11130 assert(spy && spy.calledOnce);
11131 });
11132
11133 it("should assign process.cwd() to it if the option is undefined", () => {
11134 let spy;
11135 const config = {
11136 plugins: {
11137 test: {
11138 rules: {
11139 checker: {
11140 create(context) {
11141
11142 spy = sinon.spy(() => {
11143 assert.strictEqual(context.getCwd(), context.cwd);
11144 assert.strictEqual(context.cwd, process.cwd());
11145 });
11146 return { Program: spy };
11147 }
11148 }
11149 }
11150 }
11151 },
11152 ...baseConfig
11153 };
11154
11155 linter.verify(code, config);
11156 assert(spy && spy.calledOnce);
11157 });
11158 });
11159
11160 });
11161
11162 describe("Rule Severity", () => {
11163
11164 it("rule should run as warning when set to 1 with a config array", () => {
11165 const ruleId = "semi",
11166 configs = createFlatConfigArray({
11167 files: ["**/*.js"],
11168 rules: {
11169 [ruleId]: 1
11170 }
11171 });
11172
11173 configs.normalizeSync();
11174 const messages = linter.verify("foo", configs, filename, true);
11175 const suppressedMessages = linter.getSuppressedMessages();
11176
11177 assert.strictEqual(messages.length, 1, "Message length is wrong");
11178 assert.strictEqual(messages[0].ruleId, ruleId);
11179
11180 assert.strictEqual(suppressedMessages.length, 0);
11181 });
11182
11183 it("rule should run as warning when set to 1 with a plain array", () => {
11184 const ruleId = "semi",
11185 configs = [{
11186 files: ["**/*.js"],
11187 rules: {
11188 [ruleId]: 1
11189 }
11190 }];
11191
11192 const messages = linter.verify("foo", configs, filename, true);
11193 const suppressedMessages = linter.getSuppressedMessages();
11194
11195 assert.strictEqual(messages.length, 1, "Message length is wrong");
11196 assert.strictEqual(messages[0].ruleId, ruleId);
11197
11198 assert.strictEqual(suppressedMessages.length, 0);
11199 });
11200
11201 it("rule should run as warning when set to 1 with an object", () => {
11202 const ruleId = "semi",
11203 config = {
11204 files: ["**/*.js"],
11205 rules: {
11206 [ruleId]: 1
11207 }
11208 };
11209
11210 const messages = linter.verify("foo", config, filename, true);
11211 const suppressedMessages = linter.getSuppressedMessages();
11212
11213 assert.strictEqual(messages.length, 1, "Message length is wrong");
11214 assert.strictEqual(messages[0].ruleId, ruleId);
11215
11216 assert.strictEqual(suppressedMessages.length, 0);
11217 });
11218 });
11219
11220 describe("Code with a hashbang comment", () => {
11221 const code = "#!bin/program\n\nvar foo;;";
11222
11223 it("should preserve line numbers", () => {
11224 const config = { rules: { "no-extra-semi": 1 } };
11225 const messages = linter.verify(code, config);
11226 const suppressedMessages = linter.getSuppressedMessages();
11227
11228 assert.strictEqual(messages.length, 1);
11229 assert.strictEqual(messages[0].ruleId, "no-extra-semi");
11230 assert.strictEqual(messages[0].nodeType, "EmptyStatement");
11231 assert.strictEqual(messages[0].line, 3);
11232
11233 assert.strictEqual(suppressedMessages.length, 0);
11234 });
11235
11236 it("should have a comment with the hashbang in it", () => {
11237 const spy = sinon.spy(context => {
11238 const comments = context.getAllComments();
11239
11240 assert.strictEqual(comments.length, 1);
11241 assert.strictEqual(comments[0].type, "Shebang");
11242 return {};
11243 });
11244
11245 const config = {
11246 plugins: {
11247 test: {
11248 rules: {
11249 checker: { create: spy }
11250 }
11251 }
11252 },
11253 rules: {
11254 "test/checker": "error"
11255 }
11256 };
11257
11258 linter.verify(code, config);
11259 assert(spy.calledOnce);
11260 });
11261 });
11262
11263 describe("Options", () => {
11264
11265 describe("filename", () => {
11266 it("should allow filename to be passed on options object", () => {
11267 const filenameChecker = sinon.spy(context => {
11268 assert.strictEqual(context.filename, "foo.js");
11269 return {};
11270 });
11271
11272 const config = {
11273 plugins: {
11274 test: {
11275 rules: {
11276 checker: { create: filenameChecker }
11277 }
11278 }
11279 },
11280 rules: {
11281 "test/checker": "error"
11282 }
11283 };
11284
11285 linter.verify("foo;", config, { filename: "foo.js" });
11286 assert(filenameChecker.calledOnce);
11287 });
11288
11289 it("should allow filename to be passed as third argument", () => {
11290 const filenameChecker = sinon.spy(context => {
11291 assert.strictEqual(context.filename, "bar.js");
11292 return {};
11293 });
11294
11295 const config = {
11296 plugins: {
11297 test: {
11298 rules: {
11299 checker: { create: filenameChecker }
11300 }
11301 }
11302 },
11303 rules: {
11304 "test/checker": "error"
11305 }
11306 };
11307
11308 linter.verify("foo;", config, "bar.js");
11309 assert(filenameChecker.calledOnce);
11310 });
11311
11312 it("should default filename to <input> when options object doesn't have filename", () => {
11313 const filenameChecker = sinon.spy(context => {
11314 assert.strictEqual(context.filename, "<input>");
11315 return {};
11316 });
11317
11318 const config = {
11319 plugins: {
11320 test: {
11321 rules: {
11322 checker: { create: filenameChecker }
11323 }
11324 }
11325 },
11326 rules: {
11327 "test/checker": "error"
11328 }
11329 };
11330
11331 linter.verify("foo;", config, {});
11332 assert(filenameChecker.calledOnce);
11333 });
11334
11335 it("should default filename to <input> when only two arguments are passed", () => {
11336 const filenameChecker = sinon.spy(context => {
11337 assert.strictEqual(context.filename, "<input>");
11338 return {};
11339 });
11340
11341 const config = {
11342 plugins: {
11343 test: {
11344 rules: {
11345 checker: { create: filenameChecker }
11346 }
11347 }
11348 },
11349 rules: {
11350 "test/checker": "error"
11351 }
11352 };
11353
11354 linter.verify("foo;", config);
11355 assert(filenameChecker.calledOnce);
11356 });
11357 });
11358
11359 describe("physicalFilename", () => {
11360 it("should be same as `filename` passed on options object, if no processors are used", () => {
11361 const physicalFilenameChecker = sinon.spy(context => {
11362 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
11363 assert.strictEqual(context.physicalFilename, "foo.js");
11364 return {};
11365 });
11366
11367 const config = {
11368 plugins: {
11369 test: {
11370 rules: {
11371 checker: { create: physicalFilenameChecker }
11372 }
11373 }
11374 },
11375 rules: {
11376 "test/checker": "error"
11377 }
11378 };
11379
11380 linter.verify("foo;", config, { filename: "foo.js" });
11381 assert(physicalFilenameChecker.calledOnce);
11382 });
11383
11384 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
11385 const physicalFilenameChecker = sinon.spy(context => {
11386 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
11387 assert.strictEqual(context.physicalFilename, "<input>");
11388 return {};
11389 });
11390
11391 const config = {
11392 plugins: {
11393 test: {
11394 rules: {
11395 checker: { create: physicalFilenameChecker }
11396 }
11397 }
11398 },
11399 rules: {
11400 "test/checker": "error"
11401 }
11402 };
11403
11404 linter.verify("foo;", config, {});
11405 assert(physicalFilenameChecker.calledOnce);
11406 });
11407
11408 it("should default physicalFilename to <input> when only two arguments are passed", () => {
11409 const physicalFilenameChecker = sinon.spy(context => {
11410 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
11411 assert.strictEqual(context.physicalFilename, "<input>");
11412 return {};
11413 });
11414
11415 const config = {
11416 plugins: {
11417 test: {
11418 rules: {
11419 checker: { create: physicalFilenameChecker }
11420 }
11421 }
11422 },
11423 rules: {
11424 "test/checker": "error"
11425 }
11426 };
11427
11428 linter.verify("foo;", config);
11429 assert(physicalFilenameChecker.calledOnce);
11430 });
11431 });
11432
11433 });
11434
11435 describe("Inline Directives", () => {
11436
11437 describe("/*global*/ Comments", () => {
11438
11439 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
11440
11441 it("variables should be available in global scope", () => {
11442 const code = `
11443 /*global a b:true c:false d:readable e:writeable Math:off */
11444 function foo() {}
11445 /*globals f:true*/
11446 /* global ConfigGlobal : readable */
11447 `;
11448 let spy;
11449
11450 const config = {
11451 plugins: {
11452 test: {
11453 rules: {
11454 checker: {
11455 create(context) {
11456 spy = sinon.spy(() => {
11457 const scope = context.getScope();
11458 const a = getVariable(scope, "a"),
11459 b = getVariable(scope, "b"),
11460 c = getVariable(scope, "c"),
11461 d = getVariable(scope, "d"),
11462 e = getVariable(scope, "e"),
11463 f = getVariable(scope, "f"),
11464 mathGlobal = getVariable(scope, "Math"),
11465 arrayGlobal = getVariable(scope, "Array"),
11466 configGlobal = getVariable(scope, "ConfigGlobal");
11467
11468 assert.strictEqual(a.name, "a");
11469 assert.strictEqual(a.writeable, false);
11470 assert.strictEqual(b.name, "b");
11471 assert.strictEqual(b.writeable, true);
11472 assert.strictEqual(c.name, "c");
11473 assert.strictEqual(c.writeable, false);
11474 assert.strictEqual(d.name, "d");
11475 assert.strictEqual(d.writeable, false);
11476 assert.strictEqual(e.name, "e");
11477 assert.strictEqual(e.writeable, true);
11478 assert.strictEqual(f.name, "f");
11479 assert.strictEqual(f.writeable, true);
11480 assert.strictEqual(mathGlobal, null);
11481 assert.strictEqual(arrayGlobal, null);
11482 assert.strictEqual(configGlobal.name, "ConfigGlobal");
11483 assert.strictEqual(configGlobal.writeable, false);
11484 });
11485
11486 return { Program: spy };
11487 }
11488 }
11489 }
11490 }
11491 },
11492 rules: { "test/checker": "error" },
11493 languageOptions: {
11494 globals: { Array: "off", ConfigGlobal: "writeable" }
11495 }
11496 };
11497
11498 linter.verify(code, config);
11499 assert(spy && spy.calledOnce);
11500 });
11501 });
11502
11503 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
11504 const code = "/* global a b : true c: false*/";
11505
11506 it("variables should be available in global scope", () => {
11507
11508 let spy;
11509 const config = {
11510 plugins: {
11511 test: {
11512 rules: {
11513 checker: {
11514 create(context) {
11515 spy = sinon.spy(() => {
11516 const scope = context.getScope(),
11517 a = getVariable(scope, "a"),
11518 b = getVariable(scope, "b"),
11519 c = getVariable(scope, "c");
11520
11521 assert.strictEqual(a.name, "a");
11522 assert.strictEqual(a.writeable, false);
11523 assert.strictEqual(b.name, "b");
11524 assert.strictEqual(b.writeable, true);
11525 assert.strictEqual(c.name, "c");
11526 assert.strictEqual(c.writeable, false);
11527 });
11528
11529 return { Program: spy };
11530 }
11531 }
11532 }
11533 }
11534 },
11535 rules: { "test/checker": "error" }
11536 };
11537
11538 linter.verify(code, config);
11539 assert(spy && spy.calledOnce);
11540 });
11541 });
11542
11543 describe("when evaluating code containing a line comment", () => {
11544 const code = "//global a \n function f() {}";
11545
11546 it("should not introduce a global variable", () => {
11547 let spy;
11548
11549 const config = {
11550 plugins: {
11551 test: {
11552 rules: {
11553 checker: {
11554 create(context) {
11555 spy = sinon.spy(() => {
11556 const scope = context.getScope();
11557
11558 assert.strictEqual(getVariable(scope, "a"), null);
11559 });
11560
11561 return { Program: spy };
11562 }
11563 }
11564 }
11565 }
11566 },
11567 rules: { "test/checker": "error" }
11568 };
11569
11570
11571 linter.verify(code, config);
11572 assert(spy && spy.calledOnce);
11573 });
11574 });
11575
11576 describe("when evaluating code containing normal block comments", () => {
11577 const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
11578
11579 it("should not introduce a global variable", () => {
11580 let spy;
11581
11582 const config = {
11583 plugins: {
11584 test: {
11585 rules: {
11586 checker: {
11587 create(context) {
11588 spy = sinon.spy(() => {
11589 const scope = context.getScope();
11590
11591 assert.strictEqual(getVariable(scope, "a"), null);
11592 assert.strictEqual(getVariable(scope, "b"), null);
11593 assert.strictEqual(getVariable(scope, "foo"), null);
11594 assert.strictEqual(getVariable(scope, "c"), null);
11595 });
11596
11597 return { Program: spy };
11598 }
11599 }
11600 }
11601 }
11602 },
11603 rules: { "test/checker": "error" }
11604 };
11605
11606
11607 linter.verify(code, config);
11608 assert(spy && spy.calledOnce);
11609 });
11610 });
11611
11612 it("should attach a \"/*global\" comment node to declared variables", () => {
11613 const code = "/* global foo */\n/* global bar, baz */";
11614 let ok = false;
11615 const config = {
11616 plugins: {
11617 test: {
11618 rules: {
11619 test: {
11620 create: context => ({
11621 Program() {
11622 const scope = context.getScope();
11623 const sourceCode = context.sourceCode;
11624 const comments = sourceCode.getAllComments();
11625
11626 assert.strictEqual(context.getSourceCode(), sourceCode);
11627 assert.strictEqual(2, comments.length);
11628
11629 const foo = getVariable(scope, "foo");
11630
11631 assert.strictEqual(foo.eslintExplicitGlobal, true);
11632 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
11633
11634 const bar = getVariable(scope, "bar");
11635
11636 assert.strictEqual(bar.eslintExplicitGlobal, true);
11637 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
11638
11639 const baz = getVariable(scope, "baz");
11640
11641 assert.strictEqual(baz.eslintExplicitGlobal, true);
11642 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
11643
11644 ok = true;
11645 }
11646 })
11647 }
11648 }
11649 }
11650 },
11651 rules: { "test/test": "error" }
11652 };
11653
11654
11655 linter.verify(code, config);
11656 assert(ok);
11657 });
11658
11659 it("should report a linting error when a global is set to an invalid value", () => {
11660 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
11661 const suppressedMessages = linter.getSuppressedMessages();
11662
11663 assert.deepStrictEqual(results, [
11664 {
11665 ruleId: null,
11666 severity: 2,
11667 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
11668 line: 1,
11669 column: 1,
11670 endLine: 1,
11671 endColumn: 39,
11672 nodeType: null
11673 },
11674 {
11675 ruleId: "no-undef",
11676 messageId: "undef",
11677 severity: 2,
11678 message: "'foo' is not defined.",
11679 line: 2,
11680 column: 1,
11681 endLine: 2,
11682 endColumn: 4,
11683 nodeType: "Identifier"
11684 }
11685 ]);
11686
11687 assert.strictEqual(suppressedMessages.length, 0);
11688 });
11689
11690 });
11691
11692 describe("/*exported*/ Comments", () => {
11693
11694 it("we should behave nicely when no matching variable is found", () => {
11695 const code = "/* exported horse */";
11696 const config = { rules: {} };
11697
11698 linter.verify(code, config, filename, true);
11699 });
11700
11701 it("variables should be exported", () => {
11702 const code = "/* exported horse */\n\nvar horse = 'circus'";
11703 let spy;
11704
11705 const config = {
11706 plugins: {
11707 test: {
11708 rules: {
11709 checker: {
11710 create(context) {
11711 spy = sinon.spy(() => {
11712 const scope = context.getScope(),
11713 horse = getVariable(scope, "horse");
11714
11715 assert.strictEqual(horse.eslintUsed, true);
11716 });
11717
11718 return { Program: spy };
11719 }
11720 }
11721 }
11722 }
11723 },
11724 languageOptions: {
11725 sourceType: "script"
11726 },
11727 rules: { "test/checker": "error" }
11728 };
11729
11730 linter.verify(code, config);
11731 assert(spy && spy.calledOnce);
11732 });
11733
11734 it("undefined variables should not be exported", () => {
11735 const code = "/* exported horse */\n\nhorse = 'circus'";
11736 let spy;
11737 const config = {
11738 plugins: {
11739 test: {
11740 rules: {
11741 checker: {
11742 create(context) {
11743 spy = sinon.spy(() => {
11744 const scope = context.getScope(),
11745 horse = getVariable(scope, "horse");
11746
11747 assert.strictEqual(horse, null);
11748 });
11749
11750 return { Program: spy };
11751 }
11752 }
11753 }
11754 }
11755 },
11756 languageOptions: {
11757 sourceType: "script"
11758 },
11759 rules: { "test/checker": "error" }
11760 };
11761
11762 linter.verify(code, config);
11763 assert(spy && spy.calledOnce);
11764 });
11765
11766 it("variables should be exported in strict mode", () => {
11767 const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'";
11768 let spy;
11769 const config = {
11770 plugins: {
11771 test: {
11772 rules: {
11773 checker: {
11774 create(context) {
11775 spy = sinon.spy(() => {
11776 const scope = context.getScope(),
11777 horse = getVariable(scope, "horse");
11778
11779 assert.strictEqual(horse.eslintUsed, true);
11780 });
11781
11782 return { Program: spy };
11783 }
11784 }
11785 }
11786 }
11787 },
11788 languageOptions: {
11789 sourceType: "script"
11790 },
11791 rules: { "test/checker": "error" }
11792 };
11793
11794 linter.verify(code, config);
11795 assert(spy && spy.calledOnce);
11796 });
11797
11798 it("variables should not be exported in the es6 module environment", () => {
11799 const code = "/* exported horse */\nvar horse = 'circus'";
11800 let spy;
11801 const config = {
11802 plugins: {
11803 test: {
11804 rules: {
11805 checker: {
11806 create(context) {
11807 spy = sinon.spy(() => {
11808 const scope = context.getScope(),
11809 horse = getVariable(scope, "horse");
11810
11811 assert.strictEqual(horse, null); // there is no global scope at all
11812 });
11813
11814 return { Program: spy };
11815 }
11816 }
11817 }
11818 }
11819 },
11820 languageOptions: {
11821 ecmaVersion: 6,
11822 sourceType: "module"
11823 },
11824 rules: { "test/checker": "error" }
11825 };
11826
11827 linter.verify(code, config);
11828 assert(spy && spy.calledOnce);
11829 });
11830
11831 it("variables should not be exported when in a commonjs file", () => {
11832 const code = "/* exported horse */\nvar horse = 'circus'";
11833 let spy;
11834 const config = {
11835 plugins: {
11836 test: {
11837 rules: {
11838 checker: {
11839 create(context) {
11840 spy = sinon.spy(() => {
11841 const scope = context.getScope(),
11842 horse = getVariable(scope, "horse");
11843
11844 assert.strictEqual(horse, null); // there is no global scope at all
11845 });
11846
11847 return { Program: spy };
11848 }
11849 }
11850 }
11851 }
11852 },
11853 languageOptions: {
11854 sourceType: "commonjs"
11855 },
11856 rules: { "test/checker": "error" }
11857 };
11858
11859 linter.verify(code, config);
11860 assert(spy && spy.calledOnce);
11861 });
11862 });
11863
11864 describe("/*eslint*/ Comments", () => {
11865 describe("when evaluating code with comments to enable rules", () => {
11866
11867 it("should report a violation", () => {
11868 const code = "/*eslint no-alert:1*/ alert('test');";
11869 const config = { rules: {} };
11870
11871 const messages = linter.verify(code, config, filename);
11872 const suppressedMessages = linter.getSuppressedMessages();
11873
11874 assert.strictEqual(messages.length, 1);
11875 assert.strictEqual(messages[0].ruleId, "no-alert");
11876 assert.strictEqual(messages[0].message, "Unexpected alert.");
11877 assert.include(messages[0].nodeType, "CallExpression");
11878
11879 assert.strictEqual(suppressedMessages.length, 0);
11880 });
11881
11882 it("rules should not change initial config", () => {
11883 const config = {
11884 languageOptions: {
11885 sourceType: "script"
11886 },
11887 rules: { strict: 2 }
11888 };
11889 const codeA = "/*eslint strict: 0*/ function bar() { return 2; }";
11890 const codeB = "function foo() { return 1; }";
11891 let messages = linter.verify(codeA, config, filename, false);
11892 let suppressedMessages = linter.getSuppressedMessages();
11893
11894 assert.strictEqual(messages.length, 0);
11895 assert.strictEqual(suppressedMessages.length, 0);
11896
11897 messages = linter.verify(codeB, config, filename, false);
11898 suppressedMessages = linter.getSuppressedMessages();
11899
11900 assert.strictEqual(messages.length, 1);
11901 assert.strictEqual(suppressedMessages.length, 0);
11902 });
11903
11904 it("rules should not change initial config", () => {
11905 const config = {
11906 languageOptions: {
11907 sourceType: "script"
11908 },
11909 rules: { quotes: [2, "double"] }
11910 };
11911 const codeA = "/*eslint quotes: 0*/ function bar() { return '2'; }";
11912 const codeB = "function foo() { return '1'; }";
11913 let messages = linter.verify(codeA, config, filename, false);
11914 let suppressedMessages = linter.getSuppressedMessages();
11915
11916 assert.strictEqual(messages.length, 0);
11917 assert.strictEqual(suppressedMessages.length, 0);
11918
11919 messages = linter.verify(codeB, config, filename, false);
11920 suppressedMessages = linter.getSuppressedMessages();
11921
11922 assert.strictEqual(messages.length, 1);
11923 assert.strictEqual(suppressedMessages.length, 0);
11924 });
11925
11926 it("rules should not change initial config", () => {
11927 const config = { rules: { quotes: [2, "double"] } };
11928 const codeA = "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
11929 const codeB = "function foo() { return '1'; }";
11930 let messages = linter.verify(codeA, config, filename, false);
11931 let suppressedMessages = linter.getSuppressedMessages();
11932
11933 assert.strictEqual(messages.length, 0);
11934 assert.strictEqual(suppressedMessages.length, 0);
11935
11936 messages = linter.verify(codeB, config, filename, false);
11937 suppressedMessages = linter.getSuppressedMessages();
11938
11939 assert.strictEqual(messages.length, 1);
11940 assert.strictEqual(suppressedMessages.length, 0);
11941 });
11942
11943 it("rules should not change initial config", () => {
11944 const config = {
11945 languageOptions: {
11946 sourceType: "script"
11947 },
11948 rules: { "no-unused-vars": [2, { vars: "all" }] }
11949 };
11950 const codeA = "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
11951 const codeB = "var b = 55;";
11952 let messages = linter.verify(codeA, config, filename, false);
11953 let suppressedMessages = linter.getSuppressedMessages();
11954
11955 assert.strictEqual(messages.length, 0);
11956 assert.strictEqual(suppressedMessages.length, 0);
11957
11958 messages = linter.verify(codeB, config, filename, false);
11959 suppressedMessages = linter.getSuppressedMessages();
11960
11961 assert.strictEqual(messages.length, 1);
11962 assert.strictEqual(suppressedMessages.length, 0);
11963 });
11964 });
11965
11966 describe("when evaluating code with invalid comments to enable rules", () => {
11967 it("should report a violation when the config is not a valid rule configuration", () => {
11968 const messages = linter.verify("/*eslint no-alert:true*/ alert('test');", {});
11969 const suppressedMessages = linter.getSuppressedMessages();
11970
11971 assert.deepStrictEqual(
11972 messages,
11973 [
11974 {
11975 severity: 2,
11976 ruleId: "no-alert",
11977 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",
11978 line: 1,
11979 column: 1,
11980 endLine: 1,
11981 endColumn: 25,
11982 nodeType: null
11983 }
11984 ]
11985 );
11986
11987 assert.strictEqual(suppressedMessages.length, 0);
11988 });
11989
11990 it("should report a violation when the config violates a rule's schema", () => {
11991 const messages = linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {});
11992 const suppressedMessages = linter.getSuppressedMessages();
11993
11994 assert.deepStrictEqual(
11995 messages,
11996 [
11997 {
11998 severity: 2,
11999 ruleId: "no-alert",
12000 message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
12001 line: 1,
12002 column: 1,
12003 endLine: 1,
12004 endColumn: 63,
12005 nodeType: null
12006 }
12007 ]
12008 );
12009
12010 assert.strictEqual(suppressedMessages.length, 0);
12011 });
12012 });
12013
12014 describe("when evaluating code with comments to disable rules", () => {
12015
12016 it("should not report a violation", () => {
12017 const config = { rules: { "no-alert": 1 } };
12018 const messages = linter.verify("/*eslint no-alert:0*/ alert('test');", config, filename);
12019 const suppressedMessages = linter.getSuppressedMessages();
12020
12021 assert.strictEqual(messages.length, 0);
12022 assert.strictEqual(suppressedMessages.length, 0);
12023 });
12024
12025 it("should report an error when disabling a non-existent rule in inline comment", () => {
12026 let code = "/*eslint foo:0*/ ;";
12027 let messages = linter.verify(code, {}, filename);
12028 let suppressedMessages = linter.getSuppressedMessages();
12029
12030 assert.strictEqual(messages.length, 1, "/*eslint*/ comment should report problem.");
12031 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
12032 assert.strictEqual(suppressedMessages.length, 0);
12033
12034 code = "/*eslint-disable foo*/ ;";
12035 messages = linter.verify(code, {}, filename);
12036 suppressedMessages = linter.getSuppressedMessages();
12037 assert.strictEqual(messages.length, 1, "/*eslint-disable*/ comment should report problem.");
12038 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
12039 assert.strictEqual(suppressedMessages.length, 0);
12040
12041 code = "/*eslint-disable-line foo*/ ;";
12042 messages = linter.verify(code, {}, filename);
12043 suppressedMessages = linter.getSuppressedMessages();
12044 assert.strictEqual(messages.length, 1, "/*eslint-disable-line*/ comment should report problem.");
12045 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
12046 assert.strictEqual(suppressedMessages.length, 0);
12047
12048 code = "/*eslint-disable-next-line foo*/ ;";
12049 messages = linter.verify(code, {}, filename);
12050 suppressedMessages = linter.getSuppressedMessages();
12051 assert.strictEqual(messages.length, 1, "/*eslint-disable-next-line*/ comment should report problem.");
12052 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
12053 assert.strictEqual(suppressedMessages.length, 0);
12054 });
12055
12056 it("should not report an error, when disabling a non-existent rule in config", () => {
12057 const messages = linter.verify("", { rules: { foo: 0 } }, filename);
12058 const suppressedMessages = linter.getSuppressedMessages();
12059
12060 assert.strictEqual(messages.length, 0);
12061 assert.strictEqual(suppressedMessages.length, 0);
12062 });
12063
12064 it("should throw an error when a non-existent rule in config", () => {
12065 assert.throws(() => {
12066 linter.verify("", { rules: { foo: 1 } }, filename);
12067 }, /Key "rules": Key "foo":/u);
12068
12069 assert.throws(() => {
12070 linter.verify("", { rules: { foo: 2 } }, filename);
12071 }, /Key "rules": Key "foo":/u);
12072
12073 });
12074 });
12075
12076 describe("when evaluating code with comments to enable multiple rules", () => {
12077 const code = "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
12078
12079 it("should report a violation", () => {
12080 const config = { rules: {} };
12081
12082 const messages = linter.verify(code, config, filename);
12083 const suppressedMessages = linter.getSuppressedMessages();
12084
12085 assert.strictEqual(messages.length, 2);
12086 assert.strictEqual(messages[0].ruleId, "no-alert");
12087 assert.strictEqual(messages[0].message, "Unexpected alert.");
12088 assert.include(messages[0].nodeType, "CallExpression");
12089 assert.strictEqual(messages[1].ruleId, "no-console");
12090
12091 assert.strictEqual(suppressedMessages.length, 0);
12092 });
12093 });
12094
12095 describe("when evaluating code with comments to enable and disable multiple rules", () => {
12096 const code = "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
12097
12098 it("should report a violation", () => {
12099 const config = { rules: { "no-console": 1, "no-alert": 0 } };
12100
12101 const messages = linter.verify(code, config, filename);
12102 const suppressedMessages = linter.getSuppressedMessages();
12103
12104 assert.strictEqual(messages.length, 1);
12105 assert.strictEqual(messages[0].ruleId, "no-alert");
12106 assert.strictEqual(messages[0].message, "Unexpected alert.");
12107 assert.include(messages[0].nodeType, "CallExpression");
12108
12109 assert.strictEqual(suppressedMessages.length, 0);
12110 });
12111 });
12112
12113 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
12114
12115 let baseConfig;
12116
12117 beforeEach(() => {
12118 baseConfig = {
12119 plugins: {
12120 "test-plugin": {
12121 rules: {
12122 "test-rule": {
12123 create: context => ({
12124 Literal(node) {
12125 if (node.value === "trigger violation") {
12126 context.report(node, "Reporting violation.");
12127 }
12128 }
12129 })
12130 }
12131 }
12132 }
12133 }
12134 };
12135
12136 });
12137
12138 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
12139 const config = { ...baseConfig, rules: {} };
12140 const code = "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
12141
12142 const messages = linter.verify(code, config, filename);
12143 const suppressedMessages = linter.getSuppressedMessages();
12144
12145 assert.strictEqual(messages.length, 0);
12146 assert.strictEqual(suppressedMessages.length, 0);
12147 });
12148
12149 it("should not report a violation when inline comment disables plugin rule", () => {
12150 const code = "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
12151 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 1 } };
12152
12153 const messages = linter.verify(code, config, filename);
12154 const suppressedMessages = linter.getSuppressedMessages();
12155
12156 assert.strictEqual(messages.length, 0);
12157 assert.strictEqual(suppressedMessages.length, 0);
12158 });
12159
12160 it("should report a violation when the report is right before the comment", () => {
12161 const code = " /* eslint-disable */ ";
12162
12163 const config = {
12164 plugins: {
12165 test: {
12166 rules: {
12167 checker: {
12168 create: context => ({
12169 Program() {
12170 context.report({ loc: { line: 1, column: 0 }, message: "foo" });
12171 }
12172 })
12173 }
12174 }
12175 }
12176 },
12177 rules: {
12178 "test/checker": "error"
12179 }
12180 };
12181
12182 const problems = linter.verify(code, config);
12183 const suppressedMessages = linter.getSuppressedMessages();
12184
12185 assert.strictEqual(problems.length, 1);
12186 assert.strictEqual(problems[0].message, "foo");
12187
12188 assert.strictEqual(suppressedMessages.length, 0);
12189 });
12190
12191 it("should not report a violation when the report is right at the start of the comment", () => {
12192 const code = " /* eslint-disable */ ";
12193
12194 const config = {
12195 plugins: {
12196 test: {
12197 rules: {
12198 checker: {
12199 create: context => ({
12200 Program() {
12201 context.report({ loc: { line: 1, column: 1 }, message: "foo" });
12202 }
12203 })
12204 }
12205 }
12206 }
12207 },
12208 rules: {
12209 "test/checker": "error"
12210 }
12211 };
12212
12213 const problems = linter.verify(code, config);
12214 const suppressedMessages = linter.getSuppressedMessages();
12215
12216 assert.strictEqual(problems.length, 0);
12217
12218 assert.strictEqual(suppressedMessages.length, 1);
12219 assert.strictEqual(suppressedMessages[0].message, "foo");
12220 assert.deepStrictEqual(suppressedMessages[0].suppressions, [{ kind: "directive", justification: "" }]);
12221 });
12222
12223 it("rules should not change initial config", () => {
12224 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 2 } };
12225 const codeA = "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
12226 const codeB = "var a = \"trigger violation\";";
12227 let messages = linter.verify(codeA, config, filename, false);
12228 let suppressedMessages = linter.getSuppressedMessages();
12229
12230 assert.strictEqual(messages.length, 0);
12231 assert.strictEqual(suppressedMessages.length, 0);
12232
12233 messages = linter.verify(codeB, config, filename, false);
12234 suppressedMessages = linter.getSuppressedMessages();
12235
12236 assert.strictEqual(messages.length, 1);
12237 assert.strictEqual(suppressedMessages.length, 0);
12238 });
12239 });
12240
12241 describe("when evaluating code with comments to enable and disable all reporting", () => {
12242 it("should report a violation", () => {
12243
12244 const code = [
12245 "/*eslint-disable */",
12246 "alert('test');",
12247 "/*eslint-enable */",
12248 "alert('test');"
12249 ].join("\n");
12250 const config = { rules: { "no-alert": 1 } };
12251
12252 const messages = linter.verify(code, config, filename);
12253 const suppressedMessages = linter.getSuppressedMessages();
12254
12255 assert.strictEqual(messages.length, 1);
12256 assert.strictEqual(messages[0].ruleId, "no-alert");
12257 assert.strictEqual(messages[0].message, "Unexpected alert.");
12258 assert.include(messages[0].nodeType, "CallExpression");
12259 assert.strictEqual(messages[0].line, 4);
12260
12261 assert.strictEqual(suppressedMessages.length, 1);
12262 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12263 assert.strictEqual(suppressedMessages[0].line, 2);
12264 });
12265
12266 it("should not report a violation", () => {
12267 const code = [
12268 "/*eslint-disable */",
12269 "alert('test');",
12270 "alert('test');"
12271 ].join("\n");
12272 const config = { rules: { "no-alert": 1 } };
12273
12274 const messages = linter.verify(code, config, filename);
12275 const suppressedMessages = linter.getSuppressedMessages();
12276
12277 assert.strictEqual(messages.length, 0);
12278 assert.strictEqual(suppressedMessages.length, 2);
12279 });
12280
12281 it("should not report a violation", () => {
12282 const code = [
12283 " alert('test1');/*eslint-disable */\n",
12284 "alert('test');",
12285 " alert('test');\n",
12286 "/*eslint-enable */alert('test2');"
12287 ].join("");
12288 const config = { rules: { "no-alert": 1 } };
12289
12290 const messages = linter.verify(code, config, filename);
12291 const suppressedMessages = linter.getSuppressedMessages();
12292
12293 assert.strictEqual(messages.length, 2);
12294 assert.strictEqual(messages[0].column, 21);
12295 assert.strictEqual(messages[1].column, 19);
12296
12297 assert.strictEqual(suppressedMessages.length, 2);
12298 assert.strictEqual(suppressedMessages[0].column, 1);
12299 assert.strictEqual(suppressedMessages[1].column, 56);
12300 });
12301
12302 it("should report a violation", () => {
12303
12304 const code = [
12305 "/*eslint-disable */",
12306 "alert('test');",
12307 "/*eslint-disable */",
12308 "alert('test');",
12309 "/*eslint-enable*/",
12310 "alert('test');",
12311 "/*eslint-enable*/"
12312 ].join("\n");
12313
12314 const config = { rules: { "no-alert": 1 } };
12315
12316 const messages = linter.verify(code, config, filename);
12317 const suppressedMessages = linter.getSuppressedMessages();
12318
12319 assert.strictEqual(messages.length, 1);
12320 assert.strictEqual(suppressedMessages.length, 2);
12321 });
12322
12323
12324 it("should not report a violation", () => {
12325 const code = [
12326 "/*eslint-disable */",
12327 "(function(){ var b = 44;})()",
12328 "/*eslint-enable */;any();"
12329 ].join("\n");
12330
12331 const config = { rules: { "no-unused-vars": 1 } };
12332
12333 const messages = linter.verify(code, config, filename);
12334 const suppressedMessages = linter.getSuppressedMessages();
12335
12336 assert.strictEqual(messages.length, 0);
12337
12338 assert.strictEqual(suppressedMessages.length, 1);
12339 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
12340 });
12341
12342 it("should not report a violation", () => {
12343 const code = [
12344 "(function(){ /*eslint-disable */ var b = 44;})()",
12345 "/*eslint-enable */;any();"
12346 ].join("\n");
12347
12348 const config = { rules: { "no-unused-vars": 1 } };
12349
12350 const messages = linter.verify(code, config, filename);
12351 const suppressedMessages = linter.getSuppressedMessages();
12352
12353 assert.strictEqual(messages.length, 0);
12354
12355 assert.strictEqual(suppressedMessages.length, 1);
12356 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
12357 });
12358 });
12359
12360 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
12361 const code = "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
12362
12363 it("should report a violation", () => {
12364 const config = { rules: { "no-console": 1, "no-alert": 0 } };
12365
12366 const messages = linter.verify(code, config, filename);
12367 const suppressedMessages = linter.getSuppressedMessages();
12368
12369 assert.strictEqual(messages.length, 1);
12370 assert.strictEqual(messages[0].ruleId, "no-alert");
12371 assert.strictEqual(messages[0].message, "Unexpected alert.");
12372 assert.include(messages[0].nodeType, "CallExpression");
12373
12374 assert.strictEqual(suppressedMessages.length, 0);
12375 });
12376 });
12377
12378 describe("when evaluating code with comments to enable configurable rule", () => {
12379 const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');";
12380
12381 it("should report a violation", () => {
12382 const config = { rules: { quotes: [2, "single"] } };
12383
12384 const messages = linter.verify(code, config, filename);
12385 const suppressedMessages = linter.getSuppressedMessages();
12386
12387 assert.strictEqual(messages.length, 1);
12388 assert.strictEqual(messages[0].ruleId, "quotes");
12389 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
12390 assert.include(messages[0].nodeType, "Literal");
12391
12392 assert.strictEqual(suppressedMessages.length, 0);
12393 });
12394 });
12395
12396 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
12397 const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
12398
12399 it("should report a violation", () => {
12400 const config = { rules: { quotes: [2, "single"] } };
12401
12402 const messages = linter.verify(code, config, filename);
12403 const suppressedMessages = linter.getSuppressedMessages();
12404
12405 assert.strictEqual(messages.length, 1);
12406 assert.strictEqual(messages[0].ruleId, "quotes");
12407 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
12408 assert.include(messages[0].nodeType, "Literal");
12409
12410 assert.strictEqual(suppressedMessages.length, 0);
12411 });
12412 });
12413
12414 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
12415 it("should report a violation", () => {
12416 const code = "/*eslint no-alert:'1'*/ alert('test');";
12417
12418 const config = { rules: { "no-alert": 1 } };
12419
12420 const messages = linter.verify(code, config, filename);
12421 const suppressedMessages = linter.getSuppressedMessages();
12422
12423 assert.strictEqual(messages.length, 2);
12424
12425 /*
12426 * Incorrectly formatted comment threw error;
12427 * message from caught exception
12428 * may differ amongst UAs, so verifying
12429 * first part only as defined in the
12430 * parseJsonConfig function in lib/eslint.js
12431 */
12432 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":'1'':/u);
12433 assert.strictEqual(messages[0].line, 1);
12434 assert.strictEqual(messages[0].column, 1);
12435
12436 assert.strictEqual(messages[1].ruleId, "no-alert");
12437 assert.strictEqual(messages[1].message, "Unexpected alert.");
12438 assert.include(messages[1].nodeType, "CallExpression");
12439
12440 assert.strictEqual(suppressedMessages.length, 0);
12441 });
12442
12443 it("should report a violation", () => {
12444 const code = "/*eslint no-alert:abc*/ alert('test');";
12445
12446 const config = { rules: { "no-alert": 1 } };
12447
12448 const messages = linter.verify(code, config, filename);
12449 const suppressedMessages = linter.getSuppressedMessages();
12450
12451 assert.strictEqual(messages.length, 2);
12452
12453 /*
12454 * Incorrectly formatted comment threw error;
12455 * message from caught exception
12456 * may differ amongst UAs, so verifying
12457 * first part only as defined in the
12458 * parseJsonConfig function in lib/eslint.js
12459 */
12460 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":abc':/u);
12461 assert.strictEqual(messages[0].line, 1);
12462 assert.strictEqual(messages[0].column, 1);
12463
12464 assert.strictEqual(messages[1].ruleId, "no-alert");
12465 assert.strictEqual(messages[1].message, "Unexpected alert.");
12466 assert.include(messages[1].nodeType, "CallExpression");
12467
12468 assert.strictEqual(suppressedMessages.length, 0);
12469 });
12470
12471 it("should report a violation", () => {
12472 const code = "/*eslint no-alert:0 2*/ alert('test');";
12473
12474 const config = { rules: { "no-alert": 1 } };
12475
12476 const messages = linter.verify(code, config, filename);
12477 const suppressedMessages = linter.getSuppressedMessages();
12478
12479 assert.strictEqual(messages.length, 2);
12480
12481 /*
12482 * Incorrectly formatted comment threw error;
12483 * message from caught exception
12484 * may differ amongst UAs, so verifying
12485 * first part only as defined in the
12486 * parseJsonConfig function in lib/eslint.js
12487 */
12488 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":0 2':/u);
12489 assert.strictEqual(messages[0].line, 1);
12490 assert.strictEqual(messages[0].column, 1);
12491
12492 assert.strictEqual(messages[1].ruleId, "no-alert");
12493 assert.strictEqual(messages[1].message, "Unexpected alert.");
12494 assert.include(messages[1].nodeType, "CallExpression");
12495
12496 assert.strictEqual(suppressedMessages.length, 0);
12497 });
12498 });
12499
12500 describe("when evaluating code with comments which have colon in its value", () => {
12501 const code = String.raw`
12502 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
12503 alert('test');
12504 `;
12505
12506 it("should not parse errors, should report a violation", () => {
12507 const messages = linter.verify(code, {}, filename);
12508 const suppressedMessages = linter.getSuppressedMessages();
12509
12510 assert.strictEqual(messages.length, 1);
12511 assert.strictEqual(messages[0].ruleId, "max-len");
12512 assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100.");
12513 assert.include(messages[0].nodeType, "Program");
12514
12515 assert.strictEqual(suppressedMessages.length, 0);
12516 });
12517 });
12518
12519 describe("when evaluating code with comments that contain escape sequences", () => {
12520 const code = String.raw`
12521 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
12522 console.log("test");
12523 consolexlog("test2");
12524 var a = "test2";
12525 `;
12526
12527 it("should validate correctly", () => {
12528 const config = { rules: {} };
12529 const messages = linter.verify(code, config, filename);
12530 const [message1, message2] = messages;
12531 const suppressedMessages = linter.getSuppressedMessages();
12532
12533 assert.strictEqual(messages.length, 2);
12534 assert.strictEqual(message1.ruleId, "max-len");
12535 assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1.");
12536 assert.strictEqual(message1.line, 4);
12537 assert.strictEqual(message1.column, 1);
12538 assert.include(message1.nodeType, "Program");
12539 assert.strictEqual(message2.ruleId, "max-len");
12540 assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1.");
12541 assert.strictEqual(message2.line, 5);
12542 assert.strictEqual(message2.column, 1);
12543 assert.include(message2.nodeType, "Program");
12544
12545 assert.strictEqual(suppressedMessages.length, 0);
12546 });
12547 });
12548
12549 });
12550
12551 describe("/*eslint-disable*/ and /*eslint-enable*/", () => {
12552 it("should report a violation", () => {
12553 const code = [
12554 "/*eslint-disable no-alert */",
12555 "alert('test');",
12556 "console.log('test');" // here
12557 ].join("\n");
12558 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12559
12560 const messages = linter.verify(code, config, filename);
12561 const suppressedMessages = linter.getSuppressedMessages();
12562
12563 assert.strictEqual(messages.length, 1);
12564 assert.strictEqual(messages[0].ruleId, "no-console");
12565
12566 assert.strictEqual(suppressedMessages.length, 1);
12567 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12568 });
12569
12570 it("should report no violation", () => {
12571 const code = [
12572 "/* eslint-disable quotes */",
12573 "console.log(\"foo\");",
12574 "/* eslint-enable quotes */"
12575 ].join("\n");
12576 const config = { rules: { quotes: 2 } };
12577
12578 const messages = linter.verify(code, config, filename);
12579 const suppressedMessages = linter.getSuppressedMessages();
12580
12581 assert.strictEqual(messages.length, 0);
12582 assert.strictEqual(suppressedMessages.length, 0);
12583 });
12584
12585 it("should report a violation", () => {
12586 const code = [
12587 "/*eslint-disable no-alert, no-console */",
12588 "alert('test');",
12589 "console.log('test');",
12590 "/*eslint-enable*/",
12591
12592 "alert('test');", // here
12593 "console.log('test');" // here
12594 ].join("\n");
12595 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12596
12597 const messages = linter.verify(code, config, filename);
12598 const suppressedMessages = linter.getSuppressedMessages();
12599
12600 assert.strictEqual(messages.length, 2);
12601 assert.strictEqual(messages[0].ruleId, "no-alert");
12602 assert.strictEqual(messages[0].line, 5);
12603 assert.strictEqual(messages[1].ruleId, "no-console");
12604 assert.strictEqual(messages[1].line, 6);
12605
12606 assert.strictEqual(suppressedMessages.length, 2);
12607 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12608 assert.strictEqual(suppressedMessages[0].line, 2);
12609 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12610 assert.strictEqual(suppressedMessages[1].line, 3);
12611 });
12612
12613 it("should report a violation", () => {
12614 const code = [
12615 "/*eslint-disable no-alert */",
12616 "alert('test');",
12617 "console.log('test');", // here
12618 "/*eslint-enable no-console */",
12619 "alert('test');"
12620 ].join("\n");
12621 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12622
12623 const messages = linter.verify(code, config, filename);
12624 const suppressedMessages = linter.getSuppressedMessages();
12625
12626 assert.strictEqual(messages.length, 1);
12627 assert.strictEqual(messages[0].ruleId, "no-console");
12628
12629 assert.strictEqual(suppressedMessages.length, 2);
12630 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12631 assert.strictEqual(suppressedMessages[0].line, 2);
12632 assert.strictEqual(suppressedMessages[1].ruleId, "no-alert");
12633 assert.strictEqual(suppressedMessages[1].line, 5);
12634 });
12635
12636
12637 it("should report a violation", () => {
12638 const code = [
12639 "/*eslint-disable no-alert, no-console */",
12640 "alert('test');",
12641 "console.log('test');",
12642 "/*eslint-enable no-alert*/",
12643
12644 "alert('test');", // here
12645 "console.log('test');"
12646 ].join("\n");
12647 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12648
12649 const messages = linter.verify(code, config, filename);
12650 const suppressedMessages = linter.getSuppressedMessages();
12651
12652 assert.strictEqual(messages.length, 1);
12653 assert.strictEqual(messages[0].ruleId, "no-alert");
12654 assert.strictEqual(messages[0].line, 5);
12655
12656 assert.strictEqual(suppressedMessages.length, 3);
12657 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12658 assert.strictEqual(suppressedMessages[0].line, 2);
12659 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12660 assert.strictEqual(suppressedMessages[1].line, 3);
12661 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12662 assert.strictEqual(suppressedMessages[2].line, 6);
12663 });
12664
12665
12666 it("should report a violation", () => {
12667 const code = [
12668 "/*eslint-disable no-alert */",
12669
12670 "/*eslint-disable no-console */",
12671 "alert('test');",
12672 "console.log('test');",
12673 "/*eslint-enable */",
12674
12675 "alert('test');", // here
12676 "console.log('test');", // here
12677
12678 "/*eslint-enable */",
12679
12680 "alert('test');", // here
12681 "console.log('test');", // here
12682
12683 "/*eslint-enable*/"
12684 ].join("\n");
12685 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12686
12687 const messages = linter.verify(code, config, filename);
12688 const suppressedMessages = linter.getSuppressedMessages();
12689
12690 assert.strictEqual(messages.length, 4);
12691 assert.strictEqual(messages[0].ruleId, "no-alert");
12692 assert.strictEqual(messages[0].line, 6);
12693 assert.strictEqual(messages[1].ruleId, "no-console");
12694 assert.strictEqual(messages[1].line, 7);
12695 assert.strictEqual(messages[2].ruleId, "no-alert");
12696 assert.strictEqual(messages[2].line, 9);
12697 assert.strictEqual(messages[3].ruleId, "no-console");
12698 assert.strictEqual(messages[3].line, 10);
12699
12700 assert.strictEqual(suppressedMessages.length, 2);
12701 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12702 assert.strictEqual(suppressedMessages[0].line, 3);
12703 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12704 assert.strictEqual(suppressedMessages[1].line, 4);
12705 });
12706
12707 it("should report a violation", () => {
12708 const code = [
12709 "/*eslint-disable no-alert, no-console */",
12710 "alert('test');",
12711 "console.log('test');",
12712
12713 "/*eslint-enable no-alert */",
12714
12715 "alert('test');", // here
12716 "console.log('test');",
12717
12718 "/*eslint-enable no-console */",
12719
12720 "alert('test');", // here
12721 "console.log('test');", // here
12722 "/*eslint-enable no-console */"
12723 ].join("\n");
12724 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12725
12726 const messages = linter.verify(code, config, filename);
12727 const suppressedMessages = linter.getSuppressedMessages();
12728
12729 assert.strictEqual(messages.length, 3);
12730 assert.strictEqual(messages[0].ruleId, "no-alert");
12731 assert.strictEqual(messages[0].line, 5);
12732 assert.strictEqual(messages[1].ruleId, "no-alert");
12733 assert.strictEqual(messages[1].line, 8);
12734 assert.strictEqual(messages[2].ruleId, "no-console");
12735 assert.strictEqual(messages[2].line, 9);
12736
12737 assert.strictEqual(suppressedMessages.length, 3);
12738 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12739 assert.strictEqual(suppressedMessages[0].line, 2);
12740 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12741 assert.strictEqual(suppressedMessages[1].line, 3);
12742 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12743 assert.strictEqual(suppressedMessages[2].line, 6);
12744 });
12745
12746 it("should report a violation when severity is warn", () => {
12747 const code = [
12748 "/*eslint-disable no-alert, no-console */",
12749 "alert('test');",
12750 "console.log('test');",
12751
12752 "/*eslint-enable no-alert */",
12753
12754 "alert('test');", // here
12755 "console.log('test');",
12756
12757 "/*eslint-enable no-console */",
12758
12759 "alert('test');", // here
12760 "console.log('test');", // here
12761 "/*eslint-enable no-console */"
12762 ].join("\n");
12763 const config = { rules: { "no-alert": "warn", "no-console": "warn" } };
12764
12765 const messages = linter.verify(code, config, filename);
12766 const suppressedMessages = linter.getSuppressedMessages();
12767
12768 assert.strictEqual(messages.length, 3);
12769 assert.strictEqual(messages[0].ruleId, "no-alert");
12770 assert.strictEqual(messages[0].line, 5);
12771 assert.strictEqual(messages[1].ruleId, "no-alert");
12772 assert.strictEqual(messages[1].line, 8);
12773 assert.strictEqual(messages[2].ruleId, "no-console");
12774 assert.strictEqual(messages[2].line, 9);
12775
12776 assert.strictEqual(suppressedMessages.length, 3);
12777 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12778 assert.strictEqual(suppressedMessages[0].line, 2);
12779 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12780 assert.strictEqual(suppressedMessages[1].line, 3);
12781 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12782 assert.strictEqual(suppressedMessages[2].line, 6);
12783 });
12784
12785 it("should report no violation", () => {
12786 const code = [
12787 "/*eslint-disable no-unused-vars */",
12788 "var foo; // eslint-disable-line no-unused-vars",
12789 "var bar;",
12790 "/* eslint-enable no-unused-vars */" // here
12791 ].join("\n");
12792 const config = { rules: { "no-unused-vars": 2 } };
12793
12794 const messages = linter.verify(code, config, filename);
12795 const suppressedMessages = linter.getSuppressedMessages();
12796
12797 assert.strictEqual(messages.length, 0);
12798
12799 assert.strictEqual(suppressedMessages.length, 2);
12800 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
12801 assert.strictEqual(suppressedMessages[0].line, 2);
12802 assert.strictEqual(suppressedMessages[1].ruleId, "no-unused-vars");
12803 assert.strictEqual(suppressedMessages[1].line, 3);
12804 });
12805
12806 });
12807
12808 describe("/*eslint-disable-line*/", () => {
12809
12810 it("should report a violation", () => {
12811 const code = [
12812 "alert('test'); // eslint-disable-line no-alert",
12813 "console.log('test');" // here
12814 ].join("\n");
12815 const config = {
12816 rules: {
12817 "no-alert": 1,
12818 "no-console": 1
12819 }
12820 };
12821
12822 const messages = linter.verify(code, config, filename);
12823 const suppressedMessages = linter.getSuppressedMessages();
12824
12825 assert.strictEqual(messages.length, 1);
12826 assert.strictEqual(messages[0].ruleId, "no-console");
12827 assert.strictEqual(messages[0].line, 2);
12828
12829 assert.strictEqual(suppressedMessages.length, 1);
12830 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12831 assert.strictEqual(suppressedMessages[0].line, 1);
12832 });
12833
12834 it("should report a violation", () => {
12835 const code = [
12836 "alert('test'); // eslint-disable-line no-alert",
12837 "console.log('test'); // eslint-disable-line no-console",
12838 "alert('test');" // here
12839 ].join("\n");
12840 const config = {
12841 rules: {
12842 "no-alert": 1,
12843 "no-console": 1
12844 }
12845 };
12846
12847 const messages = linter.verify(code, config, filename);
12848 const suppressedMessages = linter.getSuppressedMessages();
12849
12850 assert.strictEqual(messages.length, 1);
12851 assert.strictEqual(messages[0].ruleId, "no-alert");
12852 assert.strictEqual(messages[0].line, 3);
12853
12854 assert.strictEqual(suppressedMessages.length, 2);
12855 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12856 assert.strictEqual(suppressedMessages[0].line, 1);
12857 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12858 assert.strictEqual(suppressedMessages[1].line, 2);
12859 });
12860
12861 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
12862 const code = [
12863 "/* eslint-disable-line",
12864 "*",
12865 "*/ console.log('test');" // here
12866 ].join("\n");
12867 const config = {
12868 rules: {
12869 "no-console": 1
12870 }
12871 };
12872
12873 const messages = linter.verify(code, config, filename);
12874 const suppressedMessages = linter.getSuppressedMessages();
12875
12876 assert.strictEqual(messages.length, 2);
12877 assert.strictEqual(messages[1].ruleId, "no-console");
12878
12879 assert.strictEqual(suppressedMessages.length, 0);
12880 });
12881
12882 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
12883 const code = [
12884 "alert('test'); /* eslint-disable-line ",
12885 "no-alert */"
12886 ].join("\n");
12887 const config = {
12888 rules: {
12889 "no-alert": 1
12890 }
12891 };
12892
12893 const messages = linter.verify(code, config);
12894 const suppressedMessages = linter.getSuppressedMessages();
12895
12896 assert.deepStrictEqual(messages, [
12897 {
12898 ruleId: "no-alert",
12899 severity: 1,
12900 line: 1,
12901 column: 1,
12902 endLine: 1,
12903 endColumn: 14,
12904 message: "Unexpected alert.",
12905 messageId: "unexpected",
12906 nodeType: "CallExpression"
12907 },
12908 {
12909 ruleId: null,
12910 severity: 2,
12911 message: "eslint-disable-line comment should not span multiple lines.",
12912 line: 1,
12913 column: 16,
12914 endLine: 2,
12915 endColumn: 12,
12916 nodeType: null
12917 }
12918 ]);
12919
12920 assert.strictEqual(suppressedMessages.length, 0);
12921 });
12922
12923 it("should not report a violation for eslint-disable-line in block comment", () => {
12924 const code = [
12925 "alert('test'); // eslint-disable-line no-alert",
12926 "alert('test'); /*eslint-disable-line no-alert*/"
12927 ].join("\n");
12928 const config = {
12929 rules: {
12930 "no-alert": 1
12931 }
12932 };
12933
12934 const messages = linter.verify(code, config, filename);
12935 const suppressedMessages = linter.getSuppressedMessages();
12936
12937 assert.strictEqual(messages.length, 0);
12938 assert.strictEqual(suppressedMessages.length, 2);
12939 });
12940
12941 it("should not report a violation", () => {
12942 const code = [
12943 "alert('test'); // eslint-disable-line no-alert",
12944 "console.log('test'); // eslint-disable-line no-console"
12945 ].join("\n");
12946 const config = {
12947 rules: {
12948 "no-alert": 1,
12949 "no-console": 1
12950 }
12951 };
12952
12953 const messages = linter.verify(code, config, filename);
12954 const suppressedMessages = linter.getSuppressedMessages();
12955
12956 assert.strictEqual(messages.length, 0);
12957 assert.strictEqual(suppressedMessages.length, 2);
12958 });
12959
12960 it("should not report a violation", () => {
12961 const code = [
12962 "alert('test') // eslint-disable-line no-alert, quotes, semi",
12963 "console.log('test'); // eslint-disable-line"
12964 ].join("\n");
12965 const config = {
12966 rules: {
12967 "no-alert": 1,
12968 quotes: [1, "double"],
12969 semi: [1, "always"],
12970 "no-console": 1
12971 }
12972 };
12973
12974 const messages = linter.verify(code, config, filename);
12975 const suppressedMessages = linter.getSuppressedMessages();
12976
12977 assert.strictEqual(messages.length, 0);
12978 assert.strictEqual(suppressedMessages.length, 5);
12979 });
12980
12981 it("should not report a violation", () => {
12982 const code = [
12983 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
12984 "console.log('test'); /* eslint-disable-line */"
12985 ].join("\n");
12986 const config = {
12987 rules: {
12988 "no-alert": 1,
12989 quotes: [1, "double"],
12990 semi: [1, "always"],
12991 "no-console": 1
12992 }
12993 };
12994
12995 const messages = linter.verify(code, config, filename);
12996 const suppressedMessages = linter.getSuppressedMessages();
12997
12998 assert.strictEqual(messages.length, 0);
12999 assert.strictEqual(suppressedMessages.length, 5);
13000 });
13001
13002 it("should ignore violations of multiple rules when specified in mixed comments", () => {
13003 const code = [
13004 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
13005 ].join("\n");
13006 const config = {
13007 rules: {
13008 "no-alert": 1,
13009 quotes: [1, "single"]
13010 }
13011 };
13012 const messages = linter.verify(code, config, filename);
13013 const suppressedMessages = linter.getSuppressedMessages();
13014
13015 assert.strictEqual(messages.length, 0);
13016
13017 assert.strictEqual(suppressedMessages.length, 2);
13018 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13019 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
13020 });
13021
13022 it("should report no violation", () => {
13023 const code = [
13024 "var foo1; // eslint-disable-line no-unused-vars",
13025 "var foo2; // eslint-disable-line no-unused-vars",
13026 "var foo3; // eslint-disable-line no-unused-vars",
13027 "var foo4; // eslint-disable-line no-unused-vars",
13028 "var foo5; // eslint-disable-line no-unused-vars"
13029 ].join("\n");
13030 const config = { rules: { "no-unused-vars": 2 } };
13031
13032 const messages = linter.verify(code, config, filename);
13033 const suppressedMessages = linter.getSuppressedMessages();
13034
13035 assert.strictEqual(messages.length, 0);
13036 assert.strictEqual(suppressedMessages.length, 5);
13037 });
13038
13039 });
13040
13041 describe("/*eslint-disable-next-line*/", () => {
13042 it("should ignore violation of specified rule on next line", () => {
13043 const code = [
13044 "// eslint-disable-next-line no-alert",
13045 "alert('test');",
13046 "console.log('test');"
13047 ].join("\n");
13048 const config = {
13049 rules: {
13050 "no-alert": 1,
13051 "no-console": 1
13052 }
13053 };
13054 const messages = linter.verify(code, config, filename);
13055 const suppressedMessages = linter.getSuppressedMessages();
13056
13057 assert.strictEqual(messages.length, 1);
13058 assert.strictEqual(messages[0].ruleId, "no-console");
13059
13060 assert.strictEqual(suppressedMessages.length, 1);
13061 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13062 });
13063
13064 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
13065 const code = [
13066 "/* eslint-disable-next-line no-alert */",
13067 "alert('test');",
13068 "console.log('test');"
13069 ].join("\n");
13070 const config = {
13071 rules: {
13072 "no-alert": 1,
13073 "no-console": 1
13074 }
13075 };
13076 const messages = linter.verify(code, config, filename);
13077 const suppressedMessages = linter.getSuppressedMessages();
13078
13079 assert.strictEqual(messages.length, 1);
13080 assert.strictEqual(messages[0].ruleId, "no-console");
13081
13082 assert.strictEqual(suppressedMessages.length, 1);
13083 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13084 });
13085 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
13086 const code = [
13087 "/* eslint-disable-next-line no-alert */",
13088 "alert('test');"
13089 ].join("\n");
13090 const config = {
13091 rules: {
13092 "no-alert": 1
13093 }
13094 };
13095 const messages = linter.verify(code, config, filename);
13096 const suppressedMessages = linter.getSuppressedMessages();
13097
13098 assert.strictEqual(messages.length, 0);
13099
13100 assert.strictEqual(suppressedMessages.length, 1);
13101 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13102 });
13103
13104 it("should not ignore violation if code is not on next line", () => {
13105 const code = [
13106 "/* eslint-disable-next-line",
13107 "no-alert */alert('test');"
13108 ].join("\n");
13109 const config = {
13110 rules: {
13111 "no-alert": 1
13112 }
13113 };
13114 const messages = linter.verify(code, config, filename);
13115 const suppressedMessages = linter.getSuppressedMessages();
13116
13117 assert.strictEqual(messages.length, 1);
13118 assert.strictEqual(messages[0].ruleId, "no-alert");
13119
13120 assert.strictEqual(suppressedMessages.length, 0);
13121 });
13122
13123 it("should ignore violation if block comment span multiple lines", () => {
13124 const code = [
13125 "/* eslint-disable-next-line",
13126 "no-alert */",
13127 "alert('test');"
13128 ].join("\n");
13129 const config = {
13130 rules: {
13131 "no-alert": 1
13132 }
13133 };
13134 const messages = linter.verify(code, config, filename);
13135 const suppressedMessages = linter.getSuppressedMessages();
13136
13137 assert.strictEqual(messages.length, 0);
13138
13139 assert.strictEqual(suppressedMessages.length, 1);
13140 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13141 });
13142
13143 // For https://github.com/eslint/eslint/issues/14284
13144 it("should ignore violation if block comment span multiple lines with description", () => {
13145 const code = `
13146 /* eslint-disable-next-line no-alert --
13147 description on why this exception is seen as appropriate but past a
13148 comfortable reading line length
13149 */
13150 alert("buzz");
13151 `;
13152 const config = {
13153 rules: {
13154 "no-alert": 1
13155 }
13156 };
13157 const messages = linter.verify(code, config, filename);
13158 const suppressedMessages = linter.getSuppressedMessages();
13159
13160 assert.strictEqual(messages.length, 0);
13161
13162 assert.strictEqual(suppressedMessages.length, 1);
13163 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13164 });
13165
13166 it("should ignore violations only of specified rule", () => {
13167 const code = [
13168 "// eslint-disable-next-line no-console",
13169 "alert('test');",
13170 "console.log('test');"
13171 ].join("\n");
13172 const config = {
13173 rules: {
13174 "no-alert": 1,
13175 "no-console": 1
13176 }
13177 };
13178 const messages = linter.verify(code, config, filename);
13179 const suppressedMessages = linter.getSuppressedMessages();
13180
13181 assert.strictEqual(messages.length, 2);
13182 assert.strictEqual(messages[0].ruleId, "no-alert");
13183 assert.strictEqual(messages[1].ruleId, "no-console");
13184
13185 assert.strictEqual(suppressedMessages.length, 0);
13186 });
13187
13188 it("should ignore violations only of specified rule when block comment span multiple lines", () => {
13189 const code = [
13190 "/* eslint-disable-next-line",
13191 "no-console */",
13192 "alert('test');",
13193 "console.log('test');"
13194 ].join("\n");
13195 const config = {
13196 rules: {
13197 "no-alert": 1,
13198 "no-console": 1
13199 }
13200 };
13201 const messages = linter.verify(code, config, filename);
13202
13203 assert.strictEqual(messages.length, 2);
13204 assert.strictEqual(messages[0].ruleId, "no-alert");
13205 assert.strictEqual(messages[1].ruleId, "no-console");
13206 });
13207
13208 it("should ignore violations of multiple rules when specified", () => {
13209 const code = [
13210 "// eslint-disable-next-line no-alert, quotes",
13211 "alert(\"test\");",
13212 "console.log('test');"
13213 ].join("\n");
13214 const config = {
13215 rules: {
13216 "no-alert": 1,
13217 quotes: [1, "single"],
13218 "no-console": 1
13219 }
13220 };
13221 const messages = linter.verify(code, config, filename);
13222 const suppressedMessages = linter.getSuppressedMessages();
13223
13224 assert.strictEqual(messages.length, 1);
13225 assert.strictEqual(messages[0].ruleId, "no-console");
13226
13227 assert.strictEqual(suppressedMessages.length, 2);
13228 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13229 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
13230 });
13231
13232 it("should ignore violations of multiple rules when specified in multiple lines", () => {
13233 const code = [
13234 "/* eslint-disable-next-line",
13235 "no-alert,",
13236 "quotes",
13237 "*/",
13238 "alert(\"test\");",
13239 "console.log('test');"
13240 ].join("\n");
13241 const config = {
13242 rules: {
13243 "no-alert": 1,
13244 quotes: [1, "single"],
13245 "no-console": 1
13246 }
13247 };
13248 const messages = linter.verify(code, config, filename);
13249
13250 assert.strictEqual(messages.length, 1);
13251 assert.strictEqual(messages[0].ruleId, "no-console");
13252 });
13253
13254 it("should ignore violations of multiple rules when specified in mixed comments", () => {
13255 const code = [
13256 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
13257 "alert(\"test\");"
13258 ].join("\n");
13259 const config = {
13260 rules: {
13261 "no-alert": 1,
13262 quotes: [1, "single"]
13263 }
13264 };
13265 const messages = linter.verify(code, config, filename);
13266 const suppressedMessages = linter.getSuppressedMessages();
13267
13268 assert.strictEqual(messages.length, 0);
13269
13270 assert.strictEqual(suppressedMessages.length, 2);
13271 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13272 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
13273 });
13274
13275 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
13276 const code = [
13277 "/* eslint-disable-next-line",
13278 "no-alert",
13279 "*/ // eslint-disable-next-line quotes",
13280 "alert(\"test\");"
13281 ].join("\n");
13282 const config = {
13283 rules: {
13284 "no-alert": 1,
13285 quotes: [1, "single"]
13286 }
13287 };
13288 const messages = linter.verify(code, config, filename);
13289
13290 assert.strictEqual(messages.length, 0);
13291 });
13292
13293 it("should ignore violations of only the specified rule on next line", () => {
13294 const code = [
13295 "// eslint-disable-next-line quotes",
13296 "alert(\"test\");",
13297 "console.log('test');"
13298 ].join("\n");
13299 const config = {
13300 rules: {
13301 "no-alert": 1,
13302 quotes: [1, "single"],
13303 "no-console": 1
13304 }
13305 };
13306 const messages = linter.verify(code, config, filename);
13307 const suppressedMessages = linter.getSuppressedMessages();
13308
13309 assert.strictEqual(messages.length, 2);
13310 assert.strictEqual(messages[0].ruleId, "no-alert");
13311 assert.strictEqual(messages[1].ruleId, "no-console");
13312
13313 assert.strictEqual(suppressedMessages.length, 1);
13314 assert.strictEqual(suppressedMessages[0].ruleId, "quotes");
13315 });
13316
13317 it("should ignore violations of specified rule on next line only", () => {
13318 const code = [
13319 "alert('test');",
13320 "// eslint-disable-next-line no-alert",
13321 "alert('test');",
13322 "console.log('test');"
13323 ].join("\n");
13324 const config = {
13325 rules: {
13326 "no-alert": 1,
13327 "no-console": 1
13328 }
13329 };
13330 const messages = linter.verify(code, config, filename);
13331 const suppressedMessages = linter.getSuppressedMessages();
13332
13333 assert.strictEqual(messages.length, 2);
13334 assert.strictEqual(messages[0].ruleId, "no-alert");
13335 assert.strictEqual(messages[1].ruleId, "no-console");
13336
13337 assert.strictEqual(suppressedMessages.length, 1);
13338 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13339 });
13340
13341 it("should ignore all rule violations on next line if none specified", () => {
13342 const code = [
13343 "// eslint-disable-next-line",
13344 "alert(\"test\");",
13345 "console.log('test')"
13346 ].join("\n");
13347 const config = {
13348 rules: {
13349 semi: [1, "never"],
13350 quotes: [1, "single"],
13351 "no-alert": 1,
13352 "no-console": 1
13353 }
13354 };
13355 const messages = linter.verify(code, config, filename);
13356 const suppressedMessages = linter.getSuppressedMessages();
13357
13358 assert.strictEqual(messages.length, 1);
13359 assert.strictEqual(messages[0].ruleId, "no-console");
13360
13361 assert.strictEqual(suppressedMessages.length, 3);
13362 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13363 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
13364 assert.strictEqual(suppressedMessages[2].ruleId, "semi");
13365 });
13366
13367 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
13368 const code = [
13369 "alert('test');",
13370 "/* eslint-disable-next-line no-alert */",
13371 "alert('test');",
13372 "console.log('test');"
13373 ].join("\n");
13374 const config = {
13375 rules: {
13376 "no-alert": 1,
13377 "no-console": 1
13378 }
13379 };
13380 const messages = linter.verify(code, config, filename);
13381 const suppressedMessages = linter.getSuppressedMessages();
13382
13383 assert.strictEqual(messages.length, 2);
13384 assert.strictEqual(messages[0].ruleId, "no-alert");
13385 assert.strictEqual(messages[1].ruleId, "no-console");
13386
13387 assert.strictEqual(suppressedMessages.length, 1);
13388 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13389 });
13390
13391 it("should report a violation", () => {
13392 const code = [
13393 "/* eslint-disable-next-line",
13394 "*",
13395 "*/",
13396 "console.log('test');" // here
13397 ].join("\n");
13398 const config = {
13399 rules: {
13400 "no-alert": 1,
13401 "no-console": 1
13402 }
13403 };
13404
13405 const messages = linter.verify(code, config, filename);
13406 const suppressedMessages = linter.getSuppressedMessages();
13407
13408 assert.strictEqual(messages.length, 2);
13409 assert.strictEqual(messages[1].ruleId, "no-console");
13410
13411 assert.strictEqual(suppressedMessages.length, 0);
13412 });
13413
13414 it("should not ignore violations if comment is of the type hashbang", () => {
13415 const code = [
13416 "#! eslint-disable-next-line no-alert",
13417 "alert('test');",
13418 "console.log('test');"
13419 ].join("\n");
13420 const config = {
13421 rules: {
13422 "no-alert": 1,
13423 "no-console": 1
13424 }
13425 };
13426 const messages = linter.verify(code, config, filename);
13427 const suppressedMessages = linter.getSuppressedMessages();
13428
13429 assert.strictEqual(messages.length, 2);
13430 assert.strictEqual(messages[0].ruleId, "no-alert");
13431 assert.strictEqual(messages[1].ruleId, "no-console");
13432
13433 assert.strictEqual(suppressedMessages.length, 0);
13434 });
13435 });
13436
13437 describe("descriptions in directive comments", () => {
13438 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
13439 const aaa = sinon.stub().returns({});
13440 const bbb = sinon.stub().returns({});
13441 const config = {
13442 plugins: {
13443 test: {
13444 rules: {
13445 aaa: { create: aaa },
13446 bbb: { create: bbb }
13447 }
13448 }
13449 }
13450 };
13451
13452 const messages = linter.verify(`
13453 /*eslint test/aaa:error -- test/bbb:error */
13454 console.log("hello")
13455 `, config);
13456 const suppressedMessages = linter.getSuppressedMessages();
13457
13458 // Don't include syntax error of the comment.
13459 assert.deepStrictEqual(messages, []);
13460
13461 // Use only `aaa`.
13462 assert.strictEqual(aaa.callCount, 1);
13463 assert.strictEqual(bbb.callCount, 0);
13464
13465 assert.strictEqual(suppressedMessages.length, 0);
13466 });
13467
13468 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
13469 const messages = linter.verify(`
13470 /*globals aaa -- bbb */
13471 var aaa = {}
13472 var bbb = {}
13473 `, {
13474 languageOptions: {
13475 sourceType: "script"
13476 },
13477 rules: { "no-redeclare": "error" }
13478 });
13479 const suppressedMessages = linter.getSuppressedMessages();
13480
13481 // Don't include `bbb`
13482 assert.deepStrictEqual(
13483 messages,
13484 [{
13485 column: 31,
13486 endColumn: 34,
13487 line: 2,
13488 endLine: 2,
13489 message: "'aaa' is already defined by a variable declaration.",
13490 messageId: "redeclaredBySyntax",
13491 nodeType: "Block",
13492 ruleId: "no-redeclare",
13493 severity: 2
13494 }]
13495 );
13496
13497 assert.strictEqual(suppressedMessages.length, 0);
13498 });
13499
13500 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
13501 const messages = linter.verify(`
13502 /*exported aaa -- bbb */
13503 var aaa = {}
13504 var bbb = {}
13505 `, {
13506 languageOptions: {
13507 sourceType: "script"
13508 },
13509 rules: { "no-unused-vars": "error" }
13510 });
13511 const suppressedMessages = linter.getSuppressedMessages();
13512
13513 // Don't include `aaa`
13514 assert.deepStrictEqual(
13515 messages,
13516 [{
13517 column: 25,
13518 endColumn: 28,
13519 endLine: 4,
13520 line: 4,
13521 message: "'bbb' is assigned a value but never used.",
13522 messageId: "unusedVar",
13523 nodeType: "Identifier",
13524 ruleId: "no-unused-vars",
13525 severity: 2
13526 }]
13527 );
13528
13529 assert.strictEqual(suppressedMessages.length, 0);
13530 });
13531
13532 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
13533 const messages = linter.verify(`
13534 /*eslint-disable no-redeclare -- no-unused-vars */
13535 var aaa = {}
13536 var aaa = {}
13537 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13538 const suppressedMessages = linter.getSuppressedMessages();
13539
13540 // Do include `no-unused-vars` but not `no-redeclare`
13541 assert.deepStrictEqual(
13542 messages,
13543 [{
13544 column: 25,
13545 endLine: 4,
13546 endColumn: 28,
13547 line: 4,
13548 message: "'aaa' is assigned a value but never used.",
13549 messageId: "unusedVar",
13550 nodeType: "Identifier",
13551 ruleId: "no-unused-vars",
13552 severity: 2
13553 }]
13554 );
13555
13556 assert.deepStrictEqual(
13557 suppressedMessages,
13558 [{
13559 column: 25,
13560 endColumn: 28,
13561 endLine: 4,
13562 line: 4,
13563 message: "'aaa' is already defined.",
13564 messageId: "redeclared",
13565 nodeType: "Identifier",
13566 ruleId: "no-redeclare",
13567 severity: 2,
13568 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13569 }]
13570 );
13571 });
13572
13573 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
13574 const messages = linter.verify(`
13575 /*eslint-disable no-redeclare, no-unused-vars */
13576 /*eslint-enable no-redeclare -- no-unused-vars */
13577 var aaa = {}
13578 var aaa = {}
13579 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13580 const suppressedMessages = linter.getSuppressedMessages();
13581
13582 // Do include `no-redeclare` but not `no-unused-vars`
13583 assert.deepStrictEqual(
13584 messages,
13585 [{
13586 column: 25,
13587 endLine: 5,
13588 endColumn: 28,
13589 line: 5,
13590 message: "'aaa' is already defined.",
13591 messageId: "redeclared",
13592 nodeType: "Identifier",
13593 ruleId: "no-redeclare",
13594 severity: 2
13595 }]
13596 );
13597
13598 assert.deepStrictEqual(
13599 suppressedMessages,
13600 [{
13601 column: 25,
13602 endLine: 5,
13603 endColumn: 28,
13604 line: 5,
13605 message: "'aaa' is assigned a value but never used.",
13606 messageId: "unusedVar",
13607 nodeType: "Identifier",
13608 ruleId: "no-unused-vars",
13609 severity: 2,
13610 suppressions: [{ kind: "directive", justification: "" }]
13611 }]
13612 );
13613 });
13614
13615 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
13616 const messages = linter.verify(`
13617 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
13618 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
13619 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13620 const suppressedMessages = linter.getSuppressedMessages();
13621
13622 // Do include `no-unused-vars` but not `no-redeclare`
13623 assert.deepStrictEqual(
13624 messages,
13625 [{
13626 column: 25,
13627 endLine: 3,
13628 endColumn: 28,
13629 line: 3,
13630 message: "'aaa' is assigned a value but never used.",
13631 messageId: "unusedVar",
13632 nodeType: "Identifier",
13633 ruleId: "no-unused-vars",
13634 severity: 2
13635 }]
13636 );
13637
13638 assert.deepStrictEqual(
13639 suppressedMessages,
13640 [{
13641 column: 25,
13642 endLine: 3,
13643 endColumn: 28,
13644 line: 3,
13645 message: "'aaa' is already defined.",
13646 messageId: "redeclared",
13647 nodeType: "Identifier",
13648 ruleId: "no-redeclare",
13649 severity: 2,
13650 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13651 }]
13652 );
13653 });
13654
13655 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
13656 const messages = linter.verify(`
13657 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
13658 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
13659 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13660 const suppressedMessages = linter.getSuppressedMessages();
13661
13662 // Do include `no-unused-vars` but not `no-redeclare`
13663 assert.deepStrictEqual(
13664 messages,
13665 [{
13666 column: 25,
13667 endLine: 3,
13668 endColumn: 28,
13669 line: 3,
13670 message: "'aaa' is assigned a value but never used.",
13671 messageId: "unusedVar",
13672 nodeType: "Identifier",
13673 ruleId: "no-unused-vars",
13674 severity: 2
13675 }]
13676 );
13677
13678 assert.deepStrictEqual(
13679 suppressedMessages,
13680 [{
13681 column: 25,
13682 endLine: 3,
13683 endColumn: 28,
13684 line: 3,
13685 message: "'aaa' is already defined.",
13686 messageId: "redeclared",
13687 nodeType: "Identifier",
13688 ruleId: "no-redeclare",
13689 severity: 2,
13690 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13691 }]
13692 );
13693 });
13694
13695 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
13696 const messages = linter.verify(`
13697 //eslint-disable-next-line no-redeclare -- no-unused-vars
13698 var aaa = {}
13699 //eslint-disable-next-line no-redeclare -- no-unused-vars
13700 var aaa = {}
13701 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13702 const suppressedMessages = linter.getSuppressedMessages();
13703
13704 // Do include `no-unused-vars` but not `no-redeclare`
13705 assert.deepStrictEqual(
13706 messages,
13707 [{
13708 column: 25,
13709 endLine: 5,
13710 endColumn: 28,
13711 line: 5,
13712 message: "'aaa' is assigned a value but never used.",
13713 messageId: "unusedVar",
13714 nodeType: "Identifier",
13715 ruleId: "no-unused-vars",
13716 severity: 2
13717 }]
13718 );
13719
13720 assert.deepStrictEqual(
13721 suppressedMessages,
13722 [{
13723 column: 25,
13724 endLine: 5,
13725 endColumn: 28,
13726 line: 5,
13727 message: "'aaa' is already defined.",
13728 messageId: "redeclared",
13729 nodeType: "Identifier",
13730 ruleId: "no-redeclare",
13731 severity: 2,
13732 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13733 }]
13734 );
13735 });
13736
13737 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
13738 const messages = linter.verify(`
13739 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
13740 var aaa = {}
13741 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
13742 var aaa = {}
13743 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13744 const suppressedMessages = linter.getSuppressedMessages();
13745
13746 // Do include `no-unused-vars` but not `no-redeclare`
13747 assert.deepStrictEqual(
13748 messages,
13749 [{
13750 column: 25,
13751 endLine: 5,
13752 endColumn: 28,
13753 line: 5,
13754 message: "'aaa' is assigned a value but never used.",
13755 messageId: "unusedVar",
13756 nodeType: "Identifier",
13757 ruleId: "no-unused-vars",
13758 severity: 2
13759 }]
13760 );
13761
13762 assert.deepStrictEqual(
13763 suppressedMessages,
13764 [{
13765 column: 25,
13766 endLine: 5,
13767 endColumn: 28,
13768 line: 5,
13769 message: "'aaa' is already defined.",
13770 messageId: "redeclared",
13771 nodeType: "Identifier",
13772 ruleId: "no-redeclare",
13773 severity: 2,
13774 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13775 }]
13776 );
13777 });
13778
13779 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
13780 const rule = sinon.stub().returns({});
13781 const config = {
13782 plugins: {
13783 test: {
13784 rules: {
13785 "a--rule": { create: rule }
13786 }
13787 }
13788 }
13789 };
13790
13791 const messages = linter.verify(`
13792 /*eslint test/a--rule:error */
13793 console.log("hello")
13794 `, config);
13795 const suppressedMessages = linter.getSuppressedMessages();
13796
13797 // Don't include syntax error of the comment.
13798 assert.deepStrictEqual(messages, []);
13799
13800 // Use `a--rule`.
13801 assert.strictEqual(rule.callCount, 1);
13802
13803 assert.strictEqual(suppressedMessages.length, 0);
13804 });
13805
13806 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
13807 const aaa = sinon.stub().returns({});
13808 const bbb = sinon.stub().returns({});
13809 const config = {
13810 plugins: {
13811 test: {
13812 rules: {
13813 aaa: { create: aaa },
13814 bbb: { create: bbb }
13815 }
13816 }
13817 }
13818 };
13819
13820 const messages = linter.verify(`
13821 /*eslint test/aaa:error -------- test/bbb:error */
13822 console.log("hello")
13823 `, config);
13824 const suppressedMessages = linter.getSuppressedMessages();
13825
13826 // Don't include syntax error of the comment.
13827 assert.deepStrictEqual(messages, []);
13828
13829 // Use only `aaa`.
13830 assert.strictEqual(aaa.callCount, 1);
13831 assert.strictEqual(bbb.callCount, 0);
13832
13833 assert.strictEqual(suppressedMessages.length, 0);
13834 });
13835
13836 it("should ignore the part preceded by '--' with line breaks.", () => {
13837 const aaa = sinon.stub().returns({});
13838 const bbb = sinon.stub().returns({});
13839 const config = {
13840 plugins: {
13841 test: {
13842 rules: {
13843 aaa: { create: aaa },
13844 bbb: { create: bbb }
13845 }
13846 }
13847 }
13848 };
13849
13850 const messages = linter.verify(`
13851 /*eslint test/aaa:error
13852 --------
13853 test/bbb:error */
13854 console.log("hello")
13855 `, config);
13856 const suppressedMessages = linter.getSuppressedMessages();
13857
13858 // Don't include syntax error of the comment.
13859 assert.deepStrictEqual(messages, []);
13860
13861 // Use only `aaa`.
13862 assert.strictEqual(aaa.callCount, 1);
13863 assert.strictEqual(bbb.callCount, 0);
13864
13865 assert.strictEqual(suppressedMessages.length, 0);
13866 });
13867 });
13868
13869 describe("allowInlineConfig option", () => {
13870 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
13871 it("should report a violation for disabling rules", () => {
13872 const code = [
13873 "alert('test'); // eslint-disable-line no-alert"
13874 ].join("\n");
13875 const config = {
13876 rules: {
13877 "no-alert": 1
13878 }
13879 };
13880
13881 const messages = linter.verify(code, config, {
13882 filename,
13883 allowInlineConfig: false
13884 });
13885 const suppressedMessages = linter.getSuppressedMessages();
13886
13887 assert.strictEqual(messages.length, 1);
13888 assert.strictEqual(messages[0].ruleId, "no-alert");
13889
13890 assert.strictEqual(suppressedMessages.length, 0);
13891 });
13892
13893 it("should report a violation for global variable declarations", () => {
13894 let ok = false;
13895 const code = [
13896 "/* global foo */"
13897 ].join("\n");
13898 const config = {
13899 plugins: {
13900 test: {
13901 rules: {
13902 test: {
13903 create: context => ({
13904 Program() {
13905 const scope = context.getScope();
13906 const sourceCode = context.sourceCode;
13907 const comments = sourceCode.getAllComments();
13908
13909 assert.strictEqual(context.getSourceCode(), sourceCode);
13910 assert.strictEqual(1, comments.length);
13911
13912 const foo = getVariable(scope, "foo");
13913
13914 assert.notOk(foo);
13915
13916 ok = true;
13917 }
13918 })
13919 }
13920 }
13921 }
13922 },
13923 rules: {
13924 "test/test": 2
13925 }
13926 };
13927
13928 linter.verify(code, config, { allowInlineConfig: false });
13929 assert(ok);
13930 });
13931
13932 it("should report a violation for eslint-disable", () => {
13933 const code = [
13934 "/* eslint-disable */",
13935 "alert('test');"
13936 ].join("\n");
13937 const config = {
13938 rules: {
13939 "no-alert": 1
13940 }
13941 };
13942
13943 const messages = linter.verify(code, config, {
13944 filename,
13945 allowInlineConfig: false
13946 });
13947 const suppressedMessages = linter.getSuppressedMessages();
13948
13949 assert.strictEqual(messages.length, 1);
13950 assert.strictEqual(messages[0].ruleId, "no-alert");
13951
13952 assert.strictEqual(suppressedMessages.length, 0);
13953 });
13954
13955 it("should not report a violation for rule changes", () => {
13956 const code = [
13957 "/*eslint no-alert:2*/",
13958 "alert('test');"
13959 ].join("\n");
13960 const config = {
13961 rules: {
13962 "no-alert": 0
13963 }
13964 };
13965
13966 const messages = linter.verify(code, config, {
13967 filename,
13968 allowInlineConfig: false
13969 });
13970 const suppressedMessages = linter.getSuppressedMessages();
13971
13972 assert.strictEqual(messages.length, 0);
13973 assert.strictEqual(suppressedMessages.length, 0);
13974 });
13975
13976 it("should report a violation for disable-line", () => {
13977 const code = [
13978 "alert('test'); // eslint-disable-line"
13979 ].join("\n");
13980 const config = {
13981 rules: {
13982 "no-alert": 2
13983 }
13984 };
13985
13986 const messages = linter.verify(code, config, {
13987 filename,
13988 allowInlineConfig: false
13989 });
13990 const suppressedMessages = linter.getSuppressedMessages();
13991
13992 assert.strictEqual(messages.length, 1);
13993 assert.strictEqual(messages[0].ruleId, "no-alert");
13994
13995 assert.strictEqual(suppressedMessages.length, 0);
13996 });
13997
13998 });
13999
14000 describe("when evaluating code with 'noInlineConfig'", () => {
14001 for (const directive of [
14002 "globals foo",
14003 "global foo",
14004 "exported foo",
14005 "eslint eqeqeq: error",
14006 "eslint-disable eqeqeq",
14007 "eslint-disable-line eqeqeq",
14008 "eslint-disable-next-line eqeqeq",
14009 "eslint-enable eqeqeq"
14010 ]) {
14011 // eslint-disable-next-line no-loop-func -- No closures
14012 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
14013 const messages = linter.verify(`/* ${directive} */`, {
14014 linterOptions: {
14015 noInlineConfig: true
14016 }
14017 });
14018 const suppressedMessages = linter.getSuppressedMessages();
14019
14020 assert.deepStrictEqual(messages.length, 1);
14021 assert.deepStrictEqual(messages[0].fatal, void 0);
14022 assert.deepStrictEqual(messages[0].ruleId, null);
14023 assert.deepStrictEqual(messages[0].severity, 1);
14024 assert.deepStrictEqual(messages[0].message, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
14025
14026 assert.strictEqual(suppressedMessages.length, 0);
14027 });
14028 }
14029
14030 for (const directive of [
14031 "eslint-disable-line eqeqeq",
14032 "eslint-disable-next-line eqeqeq"
14033 ]) {
14034 // eslint-disable-next-line no-loop-func -- No closures
14035 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
14036 const messages = linter.verify(`// ${directive}`, {
14037 linterOptions: {
14038 noInlineConfig: true
14039 }
14040 });
14041 const suppressedMessages = linter.getSuppressedMessages();
14042
14043 assert.deepStrictEqual(messages.length, 1);
14044 assert.deepStrictEqual(messages[0].fatal, void 0);
14045 assert.deepStrictEqual(messages[0].ruleId, null);
14046 assert.deepStrictEqual(messages[0].severity, 1);
14047 assert.deepStrictEqual(messages[0].message, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
14048
14049 assert.strictEqual(suppressedMessages.length, 0);
14050 });
14051 }
14052
14053 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
14054 const messages = linter.verify("/* globals foo */", {
14055 linterOptions: {
14056 noInlineConfig: true
14057 }
14058 }, { allowInlineConfig: false });
14059 const suppressedMessages = linter.getSuppressedMessages();
14060
14061 assert.deepStrictEqual(messages.length, 0);
14062 assert.strictEqual(suppressedMessages.length, 0);
14063 });
14064 });
14065
14066 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
14067 it("should not report a violation", () => {
14068 const code = [
14069 "alert('test'); // eslint-disable-line no-alert"
14070 ].join("\n");
14071 const config = {
14072 rules: {
14073 "no-alert": 1
14074 }
14075 };
14076
14077 const messages = linter.verify(code, config, {
14078 filename,
14079 allowInlineConfig: true
14080 });
14081 const suppressedMessages = linter.getSuppressedMessages();
14082
14083 assert.strictEqual(messages.length, 0);
14084
14085 assert.strictEqual(suppressedMessages.length, 1);
14086 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
14087 });
14088 });
14089
14090 });
14091
14092 describe("reportUnusedDisableDirectives option", () => {
14093 it("reports problems for unused eslint-disable comments", () => {
14094 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: true });
14095 const suppressedMessages = linter.getSuppressedMessages();
14096
14097 assert.deepStrictEqual(
14098 messages,
14099 [
14100 {
14101 ruleId: null,
14102 message: "Unused eslint-disable directive (no problems were reported).",
14103 line: 1,
14104 column: 1,
14105 fix: {
14106 range: [0, 20],
14107 text: " "
14108 },
14109 severity: 2,
14110 nodeType: null
14111 }
14112 ]
14113 );
14114
14115 assert.strictEqual(suppressedMessages.length, 0);
14116 });
14117
14118 it("reports problems for unused eslint-disable comments (error)", () => {
14119 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "error" });
14120 const suppressedMessages = linter.getSuppressedMessages();
14121
14122 assert.deepStrictEqual(
14123 messages,
14124 [
14125 {
14126 ruleId: null,
14127 message: "Unused eslint-disable directive (no problems were reported).",
14128 line: 1,
14129 column: 1,
14130 fix: {
14131 range: [0, 20],
14132 text: " "
14133 },
14134 severity: 2,
14135 nodeType: null
14136 }
14137 ]
14138 );
14139
14140 assert.strictEqual(suppressedMessages.length, 0);
14141 });
14142
14143 it("reports problems for unused eslint-disable comments (warn)", () => {
14144 const messages = linter.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives: "warn" });
14145 const suppressedMessages = linter.getSuppressedMessages();
14146
14147 assert.deepStrictEqual(
14148 messages,
14149 [
14150 {
14151 ruleId: null,
14152 message: "Unused eslint-disable directive (no problems were reported).",
14153 line: 1,
14154 column: 1,
14155 fix: {
14156 range: [0, 20],
14157 text: " "
14158 },
14159 severity: 1,
14160 nodeType: null
14161 }
14162 ]
14163 );
14164
14165 assert.strictEqual(suppressedMessages.length, 0);
14166 });
14167
14168 it("reports problems for unused eslint-disable comments (in config)", () => {
14169 const messages = linter.verify("/* eslint-disable */", {
14170 linterOptions: {
14171 reportUnusedDisableDirectives: true
14172 }
14173 });
14174 const suppressedMessages = linter.getSuppressedMessages();
14175
14176 assert.deepStrictEqual(
14177 messages,
14178 [
14179 {
14180 ruleId: null,
14181 message: "Unused eslint-disable directive (no problems were reported).",
14182 line: 1,
14183 column: 1,
14184 fix: {
14185 range: [0, 20],
14186 text: " "
14187 },
14188 severity: 1,
14189 nodeType: null
14190 }
14191 ]
14192 );
14193
14194 assert.strictEqual(suppressedMessages.length, 0);
14195 });
14196
14197 it("reports problems for partially unused eslint-disable comments (in config)", () => {
14198 const code = "alert('test'); // eslint-disable-line no-alert, no-redeclare";
14199 const config = {
14200 linterOptions: {
14201 reportUnusedDisableDirectives: true
14202 },
14203 rules: {
14204 "no-alert": 1,
14205 "no-redeclare": 1
14206 }
14207 };
14208
14209 const messages = linter.verify(code, config, {
14210 filename,
14211 allowInlineConfig: true
14212 });
14213 const suppressedMessages = linter.getSuppressedMessages();
14214
14215 assert.deepStrictEqual(
14216 messages,
14217 [
14218 {
14219 ruleId: null,
14220 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
14221 line: 1,
14222 column: 16,
14223 fix: {
14224 range: [46, 60],
14225 text: ""
14226 },
14227 severity: 1,
14228 nodeType: null
14229 }
14230 ]
14231 );
14232
14233 assert.strictEqual(suppressedMessages.length, 1);
14234 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
14235 });
14236
14237 it("reports problems for unused eslint-disable-next-line comments (in config)", () => {
14238 assert.deepStrictEqual(
14239 linter.verify("// eslint-disable-next-line", {
14240 linterOptions: {
14241 reportUnusedDisableDirectives: true
14242 }
14243 }),
14244 [
14245 {
14246 ruleId: null,
14247 message: "Unused eslint-disable directive (no problems were reported).",
14248 line: 1,
14249 column: 1,
14250 fix: {
14251 range: [0, 27],
14252 text: " "
14253 },
14254 severity: 1,
14255 nodeType: null
14256 }
14257 ]
14258 );
14259 });
14260
14261 it("reports problems for unused multiline eslint-disable-next-line comments (in config)", () => {
14262 assert.deepStrictEqual(
14263 linter.verify("/* \neslint-disable-next-line\n */", {
14264 linterOptions: {
14265 reportUnusedDisableDirectives: true
14266 }
14267 }),
14268 [
14269 {
14270 ruleId: null,
14271 message: "Unused eslint-disable directive (no problems were reported).",
14272 line: 1,
14273 column: 1,
14274 fix: {
14275 range: [0, 32],
14276 text: " "
14277 },
14278 severity: 1,
14279 nodeType: null
14280 }
14281 ]
14282 );
14283 });
14284
14285 it("reports problems for partially unused eslint-disable-next-line comments (in config)", () => {
14286 const code = "// eslint-disable-next-line no-alert, no-redeclare \nalert('test');";
14287 const config = {
14288 linterOptions: {
14289 reportUnusedDisableDirectives: true
14290 },
14291 rules: {
14292 "no-alert": 1,
14293 "no-redeclare": 1
14294 }
14295 };
14296
14297 const messages = linter.verify(code, config, {
14298 filename,
14299 allowInlineConfig: true
14300 });
14301
14302 assert.deepStrictEqual(
14303 messages,
14304 [
14305 {
14306 ruleId: null,
14307 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
14308 line: 1,
14309 column: 1,
14310 fix: {
14311 range: [36, 50],
14312 text: ""
14313 },
14314 severity: 1,
14315 nodeType: null
14316 }
14317 ]
14318 );
14319 });
14320
14321 it("reports problems for partially unused multiline eslint-disable-next-line comments (in config)", () => {
14322 const code = `
14323 /* eslint-disable-next-line no-alert, no-redeclare --
14324 * Here's a very long description about why this configuration is necessary
14325 * along with some additional information
14326 **/
14327 alert('test');
14328 `;
14329 const config = {
14330 linterOptions: {
14331 reportUnusedDisableDirectives: true
14332 },
14333 rules: {
14334 "no-alert": 1,
14335 "no-redeclare": 1
14336 }
14337 };
14338
14339 const messages = linter.verify(code, config, {
14340 filename,
14341 allowInlineConfig: true
14342 });
14343
14344 assert.deepStrictEqual(
14345 messages,
14346 [
14347 {
14348 ruleId: null,
14349 message: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
14350 line: 2,
14351 column: 21,
14352 fix: {
14353 range: [57, 71],
14354 text: ""
14355 },
14356 severity: 1,
14357 nodeType: null
14358 }
14359 ]
14360 );
14361 });
14362
14363 describe("autofix", () => {
14364 const alwaysReportsRule = {
14365 create(context) {
14366 return {
14367 Program(node) {
14368 context.report({ message: "bad code", loc: node.loc.end });
14369 }
14370 };
14371 }
14372 };
14373
14374 const neverReportsRule = {
14375 create() {
14376 return {};
14377 }
14378 };
14379
14380 const ruleCount = 3;
14381 const usedRules = Array.from(
14382 { length: ruleCount },
14383 (_, index) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
14384 );
14385 const unusedRules = usedRules.map(name => `un${name}`); // "unused", "unused-1", "unused-2"
14386
14387 const config = {
14388 plugins: {
14389 test: {
14390 rules: {}
14391 }
14392 },
14393 linterOptions: {
14394 reportUnusedDisableDirectives: true
14395 },
14396 rules: {
14397 ...Object.fromEntries(usedRules.map(name => [`test/${name}`, "error"])),
14398 ...Object.fromEntries(unusedRules.map(name => [`test/${name}`, "error"]))
14399 }
14400 };
14401
14402 beforeEach(() => {
14403 config.plugins.test.rules = {
14404 ...Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])),
14405 ...Object.fromEntries(unusedRules.map(name => [name, neverReportsRule]))
14406 };
14407 });
14408
14409 const tests = [
14410
14411 //-----------------------------------------------
14412 // Removing the entire comment
14413 //-----------------------------------------------
14414
14415 {
14416 code: "// eslint-disable-line test/unused",
14417 output: " "
14418 },
14419 {
14420 code: "foo// eslint-disable-line test/unused",
14421 output: "foo "
14422 },
14423 {
14424 code: "// eslint-disable-line ,test/unused,",
14425 output: " "
14426 },
14427 {
14428 code: "// eslint-disable-line test/unused-1, test/unused-2",
14429 output: " "
14430 },
14431 {
14432 code: "// eslint-disable-line ,test/unused-1,, test/unused-2,, -- comment",
14433 output: " "
14434 },
14435 {
14436 code: "// eslint-disable-next-line test/unused\n",
14437 output: " \n"
14438 },
14439 {
14440 code: "// eslint-disable-next-line test/unused\nfoo",
14441 output: " \nfoo"
14442 },
14443 {
14444 code: "/* eslint-disable \ntest/unused\n*/",
14445 output: " "
14446 },
14447
14448 //-----------------------------------------------
14449 // Removing only individual rules
14450 //-----------------------------------------------
14451
14452 // content before the first rule should not be changed
14453 {
14454 code: "//eslint-disable-line test/unused, test/used",
14455 output: "//eslint-disable-line test/used"
14456 },
14457 {
14458 code: "// eslint-disable-line test/unused, test/used",
14459 output: "// eslint-disable-line test/used"
14460 },
14461 {
14462 code: "// eslint-disable-line test/unused, test/used",
14463 output: "// eslint-disable-line test/used"
14464 },
14465 {
14466 code: "/*\neslint-disable test/unused, test/used*/",
14467 output: "/*\neslint-disable test/used*/"
14468 },
14469 {
14470 code: "/*\n eslint-disable test/unused, test/used*/",
14471 output: "/*\n eslint-disable test/used*/"
14472 },
14473 {
14474 code: "/*\r\neslint-disable test/unused, test/used*/",
14475 output: "/*\r\neslint-disable test/used*/"
14476 },
14477 {
14478 code: "/*\u2028eslint-disable test/unused, test/used*/",
14479 output: "/*\u2028eslint-disable test/used*/"
14480 },
14481 {
14482 code: "/*\u00A0eslint-disable test/unused, test/used*/",
14483 output: "/*\u00A0eslint-disable test/used*/"
14484 },
14485 {
14486 code: "// eslint-disable-line test/unused, test/used",
14487 output: "// eslint-disable-line test/used"
14488 },
14489 {
14490 code: "/* eslint-disable\ntest/unused, test/used*/",
14491 output: "/* eslint-disable\ntest/used*/"
14492 },
14493 {
14494 code: "/* eslint-disable\n test/unused, test/used*/",
14495 output: "/* eslint-disable\n test/used*/"
14496 },
14497 {
14498 code: "/* eslint-disable\r\ntest/unused, test/used*/",
14499 output: "/* eslint-disable\r\ntest/used*/"
14500 },
14501 {
14502 code: "/* eslint-disable\u2028test/unused, test/used*/",
14503 output: "/* eslint-disable\u2028test/used*/"
14504 },
14505 {
14506 code: "/* eslint-disable\u00A0test/unused, test/used*/",
14507 output: "/* eslint-disable\u00A0test/used*/"
14508 },
14509
14510 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
14511 {
14512 code: "// eslint-disable-line test/unused,test/used",
14513 output: "// eslint-disable-line test/used"
14514 },
14515 {
14516 code: "// eslint-disable-line test/unused, test/used",
14517 output: "// eslint-disable-line test/used"
14518 },
14519 {
14520 code: "// eslint-disable-line test/unused , test/used",
14521 output: "// eslint-disable-line test/used"
14522 },
14523 {
14524 code: "// eslint-disable-line test/unused, test/used",
14525 output: "// eslint-disable-line test/used"
14526 },
14527 {
14528 code: "// eslint-disable-line test/unused ,test/used",
14529 output: "// eslint-disable-line test/used"
14530 },
14531 {
14532 code: "/* eslint-disable test/unused\n,\ntest/used */",
14533 output: "/* eslint-disable test/used */"
14534 },
14535 {
14536 code: "/* eslint-disable test/unused \n \n,\n\n test/used */",
14537 output: "/* eslint-disable test/used */"
14538 },
14539 {
14540 code: "/* eslint-disable test/unused\u2028,\u2028test/used */",
14541 output: "/* eslint-disable test/used */"
14542 },
14543 {
14544 code: "// eslint-disable-line test/unused\u00A0,\u00A0test/used",
14545 output: "// eslint-disable-line test/used"
14546 },
14547 {
14548 code: "// eslint-disable-line test/unused,,test/used",
14549 output: "// eslint-disable-line ,test/used"
14550 },
14551 {
14552 code: "// eslint-disable-line test/unused, ,test/used",
14553 output: "// eslint-disable-line ,test/used"
14554 },
14555 {
14556 code: "// eslint-disable-line test/unused,, test/used",
14557 output: "// eslint-disable-line , test/used"
14558 },
14559 {
14560 code: "// eslint-disable-line test/unused,test/used ",
14561 output: "// eslint-disable-line test/used "
14562 },
14563 {
14564 code: "// eslint-disable-next-line test/unused,test/used\n",
14565 output: "// eslint-disable-next-line test/used\n"
14566 },
14567
14568 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
14569 {
14570 code: "// eslint-disable-line test/used-1,test/unused,test/used-2",
14571 output: "// eslint-disable-line test/used-1,test/used-2"
14572 },
14573 {
14574 code: "// eslint-disable-line test/used-1, test/unused,test/used-2",
14575 output: "// eslint-disable-line test/used-1,test/used-2"
14576 },
14577 {
14578 code: "// eslint-disable-line test/used-1,test/unused ,test/used-2",
14579 output: "// eslint-disable-line test/used-1,test/used-2"
14580 },
14581 {
14582 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14583 output: "// eslint-disable-line test/used-1,test/used-2"
14584 },
14585 {
14586 code: "/* eslint-disable test/used-1,\ntest/unused\n,test/used-2 */",
14587 output: "/* eslint-disable test/used-1,test/used-2 */"
14588 },
14589 {
14590 code: "/* eslint-disable test/used-1,\n\n test/unused \n \n ,test/used-2 */",
14591 output: "/* eslint-disable test/used-1,test/used-2 */"
14592 },
14593 {
14594 code: "/* eslint-disable test/used-1,\u2028test/unused\u2028,test/used-2 */",
14595 output: "/* eslint-disable test/used-1,test/used-2 */"
14596 },
14597 {
14598 code: "// eslint-disable-line test/used-1,\u00A0test/unused\u00A0,test/used-2",
14599 output: "// eslint-disable-line test/used-1,test/used-2"
14600 },
14601
14602 // when removing a rule in the middle, content around commas should not be changed
14603 {
14604 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14605 output: "// eslint-disable-line test/used-1,test/used-2"
14606 },
14607 {
14608 code: "// eslint-disable-line test/used-1,test/unused, test/used-2",
14609 output: "// eslint-disable-line test/used-1, test/used-2"
14610 },
14611 {
14612 code: "// eslint-disable-line test/used-1 ,test/unused,test/used-2",
14613 output: "// eslint-disable-line test/used-1 ,test/used-2"
14614 },
14615 {
14616 code: "// eslint-disable-line test/used-1 ,test/unused, test/used-2",
14617 output: "// eslint-disable-line test/used-1 , test/used-2"
14618 },
14619 {
14620 code: "// eslint-disable-line test/used-1 , test/unused , test/used-2",
14621 output: "// eslint-disable-line test/used-1 , test/used-2"
14622 },
14623 {
14624 code: "/* eslint-disable test/used-1\n,test/unused,\ntest/used-2 */",
14625 output: "/* eslint-disable test/used-1\n,\ntest/used-2 */"
14626 },
14627 {
14628 code: "/* eslint-disable test/used-1\u2028,test/unused,\u2028test/used-2 */",
14629 output: "/* eslint-disable test/used-1\u2028,\u2028test/used-2 */"
14630 },
14631 {
14632 code: "// eslint-disable-line test/used-1\u00A0,test/unused,\u00A0test/used-2",
14633 output: "// eslint-disable-line test/used-1\u00A0,\u00A0test/used-2"
14634 },
14635 {
14636 code: "// eslint-disable-line , test/unused ,test/used",
14637 output: "// eslint-disable-line ,test/used"
14638 },
14639 {
14640 code: "/* eslint-disable\n, test/unused ,test/used */",
14641 output: "/* eslint-disable\n,test/used */"
14642 },
14643 {
14644 code: "/* eslint-disable test/used-1,\n,test/unused,test/used-2 */",
14645 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
14646 },
14647 {
14648 code: "/* eslint-disable test/used-1,test/unused,\n,test/used-2 */",
14649 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
14650 },
14651 {
14652 code: "/* eslint-disable test/used-1,\n,test/unused,\n,test/used-2 */",
14653 output: "/* eslint-disable test/used-1,\n,\n,test/used-2 */"
14654 },
14655 {
14656 code: "// eslint-disable-line test/used, test/unused,",
14657 output: "// eslint-disable-line test/used,"
14658 },
14659 {
14660 code: "// eslint-disable-next-line test/used, test/unused,\n",
14661 output: "// eslint-disable-next-line test/used,\n"
14662 },
14663 {
14664 code: "// eslint-disable-line test/used, test/unused, ",
14665 output: "// eslint-disable-line test/used, "
14666 },
14667 {
14668 code: "// eslint-disable-line test/used, test/unused, -- comment",
14669 output: "// eslint-disable-line test/used, -- comment"
14670 },
14671 {
14672 code: "/* eslint-disable test/used, test/unused,\n*/",
14673 output: "/* eslint-disable test/used,\n*/"
14674 },
14675
14676 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
14677 {
14678 code: "// eslint-disable-line test/used,test/unused",
14679 output: "// eslint-disable-line test/used"
14680 },
14681 {
14682 code: "// eslint-disable-line test/used, test/unused",
14683 output: "// eslint-disable-line test/used"
14684 },
14685 {
14686 code: "// eslint-disable-line test/used ,test/unused",
14687 output: "// eslint-disable-line test/used"
14688 },
14689 {
14690 code: "// eslint-disable-line test/used , test/unused",
14691 output: "// eslint-disable-line test/used"
14692 },
14693 {
14694 code: "// eslint-disable-line test/used, test/unused",
14695 output: "// eslint-disable-line test/used"
14696 },
14697 {
14698 code: "// eslint-disable-line test/used ,test/unused",
14699 output: "// eslint-disable-line test/used"
14700 },
14701 {
14702 code: "/* eslint-disable test/used\n,\ntest/unused */",
14703 output: "/* eslint-disable test/used */"
14704 },
14705 {
14706 code: "/* eslint-disable test/used \n \n,\n\n test/unused */",
14707 output: "/* eslint-disable test/used */"
14708 },
14709 {
14710 code: "/* eslint-disable test/used\u2028,\u2028test/unused */",
14711 output: "/* eslint-disable test/used */"
14712 },
14713 {
14714 code: "// eslint-disable-line test/used\u00A0,\u00A0test/unused",
14715 output: "// eslint-disable-line test/used"
14716 },
14717 {
14718 code: "// eslint-disable-line test/used,,test/unused",
14719 output: "// eslint-disable-line test/used,"
14720 },
14721 {
14722 code: "// eslint-disable-line test/used, ,test/unused",
14723 output: "// eslint-disable-line test/used,"
14724 },
14725 {
14726 code: "/* eslint-disable test/used,\n,test/unused */",
14727 output: "/* eslint-disable test/used, */"
14728 },
14729 {
14730 code: "/* eslint-disable test/used\n, ,test/unused */",
14731 output: "/* eslint-disable test/used\n, */"
14732 },
14733
14734 // content after the last rule should not be changed
14735 {
14736 code: "// eslint-disable-line test/used,test/unused",
14737 output: "// eslint-disable-line test/used"
14738 },
14739 {
14740 code: "// eslint-disable-line test/used,test/unused ",
14741 output: "// eslint-disable-line test/used "
14742 },
14743 {
14744 code: "// eslint-disable-line test/used,test/unused ",
14745 output: "// eslint-disable-line test/used "
14746 },
14747 {
14748 code: "// eslint-disable-line test/used,test/unused -- comment",
14749 output: "// eslint-disable-line test/used -- comment"
14750 },
14751 {
14752 code: "// eslint-disable-next-line test/used,test/unused\n",
14753 output: "// eslint-disable-next-line test/used\n"
14754 },
14755 {
14756 code: "// eslint-disable-next-line test/used,test/unused \n",
14757 output: "// eslint-disable-next-line test/used \n"
14758 },
14759 {
14760 code: "/* eslint-disable test/used,test/unused\u2028*/",
14761 output: "/* eslint-disable test/used\u2028*/"
14762 },
14763 {
14764 code: "// eslint-disable-line test/used,test/unused\u00A0",
14765 output: "// eslint-disable-line test/used\u00A0"
14766 },
14767
14768 // multiply rules to remove
14769 {
14770 code: "// eslint-disable-line test/used, test/unused-1, test/unused-2",
14771 output: "// eslint-disable-line test/used"
14772 },
14773 {
14774 code: "// eslint-disable-line test/unused-1, test/used, test/unused-2",
14775 output: "// eslint-disable-line test/used"
14776 },
14777 {
14778 code: "// eslint-disable-line test/unused-1, test/unused-2, test/used",
14779 output: "// eslint-disable-line test/used"
14780 },
14781 {
14782 code: "// eslint-disable-line test/used-1, test/unused-1, test/used-2, test/unused-2",
14783 output: "// eslint-disable-line test/used-1, test/used-2"
14784 },
14785 {
14786 code: "// eslint-disable-line test/unused-1, test/used-1, test/unused-2, test/used-2",
14787 output: "// eslint-disable-line test/used-1, test/used-2"
14788 },
14789 {
14790 code: `
14791 /* eslint-disable test/unused-1,
14792 test/used-1,
14793 test/unused-2,
14794 test/used-2
14795 */
14796 `,
14797 output: `
14798 /* eslint-disable test/used-1,
14799 test/used-2
14800 */
14801 `
14802 },
14803 {
14804 code: `
14805 /* eslint-disable
14806 test/unused-1,
14807 test/used-1,
14808 test/unused-2,
14809 test/used-2
14810 */
14811 `,
14812 output: `
14813 /* eslint-disable
14814 test/used-1,
14815 test/used-2
14816 */
14817 `
14818 },
14819 {
14820 code: `
14821 /* eslint-disable
14822 test/used-1,
14823 test/unused-1,
14824 test/used-2,
14825 test/unused-2
14826 */
14827 `,
14828 output: `
14829 /* eslint-disable
14830 test/used-1,
14831 test/used-2
14832 */
14833 `
14834 },
14835 {
14836 code: `
14837 /* eslint-disable
14838 test/used-1,
14839 test/unused-1,
14840 test/used-2,
14841 test/unused-2,
14842 */
14843 `,
14844 output: `
14845 /* eslint-disable
14846 test/used-1,
14847 test/used-2,
14848 */
14849 `
14850 },
14851 {
14852 code: `
14853 /* eslint-disable
14854 ,test/unused-1
14855 ,test/used-1
14856 ,test/unused-2
14857 ,test/used-2
14858 */
14859 `,
14860 output: `
14861 /* eslint-disable
14862 ,test/used-1
14863 ,test/used-2
14864 */
14865 `
14866 },
14867 {
14868 code: `
14869 /* eslint-disable
14870 ,test/used-1
14871 ,test/unused-1
14872 ,test/used-2
14873 ,test/unused-2
14874 */
14875 `,
14876 output: `
14877 /* eslint-disable
14878 ,test/used-1
14879 ,test/used-2
14880 */
14881 `
14882 },
14883 {
14884 code: `
14885 /* eslint-disable
14886 test/used-1,
14887 test/unused-1,
14888 test/used-2,
14889 test/unused-2
14890
14891 -- comment
14892 */
14893 `,
14894 output: `
14895 /* eslint-disable
14896 test/used-1,
14897 test/used-2
14898
14899 -- comment
14900 */
14901 `
14902 },
14903
14904 // duplicates in the list
14905 {
14906 code: "// eslint-disable-line test/unused, test/unused, test/used",
14907 output: "// eslint-disable-line test/used"
14908 },
14909 {
14910 code: "// eslint-disable-line test/unused, test/used, test/unused",
14911 output: "// eslint-disable-line test/used"
14912 },
14913 {
14914 code: "// eslint-disable-line test/used, test/unused, test/unused, test/used",
14915 output: "// eslint-disable-line test/used, test/used"
14916 }
14917 ];
14918
14919 for (const { code, output } of tests) {
14920 // eslint-disable-next-line no-loop-func -- `linter` is getting updated in beforeEach()
14921 it(code, () => {
14922 assert.strictEqual(
14923 linter.verifyAndFix(code, config).output,
14924 output
14925 );
14926 });
14927 }
14928 });
14929 });
14930
14931 });
14932
14933 describe("Default Global Variables", () => {
14934 const code = "x";
14935
14936 it("builtin global variables should be available in the global scope", () => {
14937 let spy;
14938 const config = {
14939 plugins: {
14940 test: {
14941 rules: {
14942 checker: {
14943 create(context) {
14944 spy = sinon.spy(() => {
14945 const scope = context.getScope();
14946
14947 assert.notStrictEqual(getVariable(scope, "Object"), null);
14948 assert.notStrictEqual(getVariable(scope, "Array"), null);
14949 assert.notStrictEqual(getVariable(scope, "undefined"), null);
14950 });
14951
14952 return { Program: spy };
14953 }
14954 }
14955 }
14956 }
14957 },
14958 languageOptions: {
14959 ecmaVersion: 5,
14960 sourceType: "script"
14961 },
14962 rules: {
14963 "test/checker": "error"
14964 }
14965 };
14966
14967 linter.verify(code, config);
14968 assert(spy && spy.calledOnce, "Rule should have been called.");
14969 });
14970
14971 it("ES6 global variables should be available by default", () => {
14972 let spy;
14973 const config = {
14974 plugins: {
14975 test: {
14976 rules: {
14977 checker: {
14978 create(context) {
14979 spy = sinon.spy(() => {
14980 const scope = context.getScope();
14981
14982 assert.notStrictEqual(getVariable(scope, "Promise"), null);
14983 assert.notStrictEqual(getVariable(scope, "Symbol"), null);
14984 assert.notStrictEqual(getVariable(scope, "WeakMap"), null);
14985 });
14986
14987 return { Program: spy };
14988 }
14989 }
14990 }
14991 }
14992 },
14993 languageOptions: {
14994 sourceType: "script"
14995 },
14996 rules: {
14997 "test/checker": "error"
14998 }
14999 };
15000
15001 linter.verify(code, config);
15002 assert(spy && spy.calledOnce);
15003 });
15004
15005 });
15006
15007 describe("Suggestions", () => {
15008 it("provides suggestion information for tools to use", () => {
15009
15010 const config = {
15011 plugins: {
15012 test: {
15013 rules: {
15014 "rule-with-suggestions": {
15015 meta: { hasSuggestions: true },
15016 create: context => ({
15017 Program(node) {
15018 context.report({
15019 node,
15020 message: "Incorrect spacing",
15021 suggest: [{
15022 desc: "Insert space at the beginning",
15023 fix: fixer => fixer.insertTextBefore(node, " ")
15024 }, {
15025 desc: "Insert space at the end",
15026 fix: fixer => fixer.insertTextAfter(node, " ")
15027 }]
15028 });
15029 }
15030 })
15031 }
15032 }
15033 }
15034 },
15035 rules: {
15036 "test/rule-with-suggestions": "error"
15037 }
15038 };
15039
15040 const messages = linter.verify("var a = 1;", config);
15041 const suppressedMessages = linter.getSuppressedMessages();
15042
15043 assert.deepStrictEqual(messages[0].suggestions, [{
15044 desc: "Insert space at the beginning",
15045 fix: {
15046 range: [0, 0],
15047 text: " "
15048 }
15049 }, {
15050 desc: "Insert space at the end",
15051 fix: {
15052 range: [10, 10],
15053 text: " "
15054 }
15055 }]);
15056
15057 assert.strictEqual(suppressedMessages.length, 0);
15058 });
15059
15060 it("supports messageIds for suggestions", () => {
15061
15062 const config = {
15063 plugins: {
15064 test: {
15065 rules: {
15066 "rule-with-suggestions": {
15067 meta: {
15068 messages: {
15069 suggestion1: "Insert space at the beginning",
15070 suggestion2: "Insert space at the end"
15071 },
15072 hasSuggestions: true
15073 },
15074 create: context => ({
15075 Program(node) {
15076 context.report({
15077 node,
15078 message: "Incorrect spacing",
15079 suggest: [{
15080 messageId: "suggestion1",
15081 fix: fixer => fixer.insertTextBefore(node, " ")
15082 }, {
15083 messageId: "suggestion2",
15084 fix: fixer => fixer.insertTextAfter(node, " ")
15085 }]
15086 });
15087 }
15088 })
15089 }
15090 }
15091 }
15092 },
15093 rules: {
15094 "test/rule-with-suggestions": "error"
15095 }
15096 };
15097
15098 const messages = linter.verify("var a = 1;", config);
15099 const suppressedMessages = linter.getSuppressedMessages();
15100
15101 assert.deepStrictEqual(messages[0].suggestions, [{
15102 messageId: "suggestion1",
15103 desc: "Insert space at the beginning",
15104 fix: {
15105 range: [0, 0],
15106 text: " "
15107 }
15108 }, {
15109 messageId: "suggestion2",
15110 desc: "Insert space at the end",
15111 fix: {
15112 range: [10, 10],
15113 text: " "
15114 }
15115 }]);
15116
15117 assert.strictEqual(suppressedMessages.length, 0);
15118 });
15119
15120 it("should throw an error if suggestion is passed but `meta.hasSuggestions` property is not enabled", () => {
15121
15122 const config = {
15123 plugins: {
15124 test: {
15125 rules: {
15126 "rule-with-suggestions": {
15127 meta: { docs: {}, schema: [] },
15128 create: context => ({
15129 Program(node) {
15130 context.report({
15131 node,
15132 message: "hello world",
15133 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
15134 });
15135 }
15136 })
15137 }
15138 }
15139 }
15140 },
15141 rules: {
15142 "test/rule-with-suggestions": "error"
15143 }
15144 };
15145
15146 assert.throws(() => {
15147 linter.verify("0", config);
15148 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
15149 });
15150
15151 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", () => {
15152
15153 const config = {
15154 plugins: {
15155 test: {
15156 rules: {
15157 "rule-with-meta-docs-suggestion": {
15158 meta: { docs: { suggestion: true }, schema: [] },
15159 create: context => ({
15160 Program(node) {
15161 context.report({
15162 node,
15163 message: "hello world",
15164 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
15165 });
15166 }
15167 })
15168 }
15169 }
15170 }
15171 },
15172 rules: {
15173 "test/rule-with-meta-docs-suggestion": "error"
15174 }
15175 };
15176
15177 assert.throws(() => {
15178 linter.verify("0", config);
15179 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`. `meta.docs.suggestion` is ignored by ESLint.");
15180 });
15181 });
15182
15183
15184 describe("Error Conditions", () => {
15185 describe("when evaluating broken code", () => {
15186 const code = BROKEN_TEST_CODE;
15187
15188 it("should report a violation with a useful parse error prefix", () => {
15189 const messages = linter.verify(code);
15190 const suppressedMessages = linter.getSuppressedMessages();
15191
15192 assert.strictEqual(messages.length, 1);
15193 assert.strictEqual(messages[0].severity, 2);
15194 assert.isNull(messages[0].ruleId);
15195 assert.strictEqual(messages[0].line, 1);
15196 assert.strictEqual(messages[0].column, 4);
15197 assert.isTrue(messages[0].fatal);
15198 assert.match(messages[0].message, /^Parsing error:/u);
15199
15200 assert.strictEqual(suppressedMessages.length, 0);
15201 });
15202
15203 it("should report source code where the issue is present", () => {
15204 const inValidCode = [
15205 "var x = 20;",
15206 "if (x ==4 {",
15207 " x++;",
15208 "}"
15209 ];
15210 const messages = linter.verify(inValidCode.join("\n"));
15211 const suppressedMessages = linter.getSuppressedMessages();
15212
15213 assert.strictEqual(messages.length, 1);
15214 assert.strictEqual(messages[0].severity, 2);
15215 assert.isTrue(messages[0].fatal);
15216 assert.match(messages[0].message, /^Parsing error:/u);
15217
15218 assert.strictEqual(suppressedMessages.length, 0);
15219 });
15220 });
15221
15222 describe("when using a rule which has been replaced", () => {
15223 const code = TEST_CODE;
15224
15225 it("should report the new rule", () => {
15226
15227 assert.throws(() => {
15228 linter.verify(code, { rules: { "no-comma-dangle": 2 } });
15229 }, /Key "rules": Key "no-comma-dangle": Rule "no-comma-dangle" was removed and replaced by "comma-dangle"/u);
15230
15231 });
15232 });
15233
15234 });
15235 });
15236
15237 describe("getSourceCode()", () => {
15238 const code = TEST_CODE;
15239
15240 it("should retrieve SourceCode object after reset", () => {
15241 linter.verify(code, {}, filename, true);
15242
15243 const sourceCode = linter.getSourceCode();
15244
15245 assert.isObject(sourceCode);
15246 assert.strictEqual(sourceCode.text, code);
15247 assert.isObject(sourceCode.ast);
15248 });
15249
15250 it("should retrieve SourceCode object without reset", () => {
15251 linter.verify(code, {}, filename);
15252
15253 const sourceCode = linter.getSourceCode();
15254
15255 assert.isObject(sourceCode);
15256 assert.strictEqual(sourceCode.text, code);
15257 assert.isObject(sourceCode.ast);
15258 });
15259
15260 });
15261
15262 describe("getSuppressedMessages()", () => {
15263 it("should have no suppressed messages", () => {
15264 const suppressedMessages = linter.getSuppressedMessages();
15265
15266 assert.strictEqual(suppressedMessages.length, 0);
15267 });
15268
15269 it("should have a suppressed message", () => {
15270 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test\");";
15271 const config = {
15272 rules: { "no-alert": 1 }
15273 };
15274 const messages = linter.verify(code, config);
15275 const suppressedMessages = linter.getSuppressedMessages();
15276
15277 assert.strictEqual(messages.length, 0);
15278
15279 assert.strictEqual(suppressedMessages.length, 1);
15280 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
15281 assert.deepStrictEqual(
15282 suppressedMessages[0].suppressions,
15283 [{ kind: "directive", justification: "justification" }]
15284 );
15285 });
15286
15287 it("should have a suppressed message", () => {
15288 const code = [
15289 "/* eslint-disable no-alert -- j1",
15290 " * j2",
15291 " */",
15292 "alert(\"test\");"
15293 ].join("\n");
15294 const config = {
15295 rules: { "no-alert": 1 }
15296 };
15297 const messages = linter.verify(code, config);
15298 const suppressedMessages = linter.getSuppressedMessages();
15299
15300 assert.strictEqual(messages.length, 0);
15301
15302 assert.strictEqual(suppressedMessages.length, 1);
15303 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
15304 assert.deepStrictEqual(
15305 suppressedMessages[0].suppressions,
15306 [{ kind: "directive", justification: "j1\n * j2" }]
15307 );
15308 });
15309 });
15310
15311 describe("defineRule()", () => {
15312 it("should throw an error when called in flat config mode", () => {
15313 assert.throws(() => {
15314 linter.defineRule("foo", {
15315 create() {}
15316 });
15317 }, /This method cannot be used with flat config/u);
15318 });
15319 });
15320
15321 describe("defineRules()", () => {
15322 it("should throw an error when called in flat config mode", () => {
15323 assert.throws(() => {
15324 linter.defineRules({});
15325 }, /This method cannot be used with flat config/u);
15326 });
15327 });
15328
15329 describe("defineParser()", () => {
15330 it("should throw an error when called in flat config mode", () => {
15331 assert.throws(() => {
15332 linter.defineParser("foo", {});
15333 }, /This method cannot be used with flat config/u);
15334 });
15335 });
15336
15337 describe("getRules()", () => {
15338 it("should throw an error when called in flat config mode", () => {
15339 assert.throws(() => {
15340 linter.getRules();
15341 }, /This method cannot be used with flat config/u);
15342 });
15343 });
15344
15345 describe("version", () => {
15346 it("should return current version number", () => {
15347 const version = linter.version;
15348
15349 assert.isString(version);
15350 assert.isTrue(parseInt(version[0], 10) >= 3);
15351 });
15352 });
15353
15354 describe("verifyAndFix()", () => {
15355 it("Fixes the code", () => {
15356 const messages = linter.verifyAndFix("var a", {
15357 rules: {
15358 semi: 2
15359 }
15360 }, { filename: "test.js" });
15361 const suppressedMessages = linter.getSuppressedMessages();
15362
15363 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
15364 assert.isTrue(messages.fixed);
15365
15366 assert.strictEqual(suppressedMessages.length, 0);
15367 });
15368
15369 it("does not require a third argument", () => {
15370 const fixResult = linter.verifyAndFix("var a", {
15371 rules: {
15372 semi: 2
15373 }
15374 });
15375 const suppressedMessages = linter.getSuppressedMessages();
15376
15377 assert.deepStrictEqual(fixResult, {
15378 fixed: true,
15379 messages: [],
15380 output: "var a;"
15381 });
15382
15383 assert.strictEqual(suppressedMessages.length, 0);
15384 });
15385
15386 it("does not include suggestions in autofix results", () => {
15387 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
15388 rules: {
15389 semi: 2,
15390 "no-useless-escape": 2
15391 }
15392 });
15393 const suppressedMessages = linter.getSuppressedMessages();
15394
15395 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
15396 assert.strictEqual(fixResult.fixed, true);
15397 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
15398
15399 assert.strictEqual(suppressedMessages.length, 0);
15400 });
15401
15402 it("does not apply autofixes when fix argument is `false`", () => {
15403 const fixResult = linter.verifyAndFix("var a", {
15404 rules: {
15405 semi: 2
15406 }
15407 }, { fix: false });
15408 const suppressedMessages = linter.getSuppressedMessages();
15409
15410 assert.strictEqual(fixResult.fixed, false);
15411 assert.strictEqual(suppressedMessages.length, 0);
15412 });
15413
15414 it("stops fixing after 10 passes", () => {
15415
15416 const config = {
15417 plugins: {
15418 test: {
15419 rules: {
15420 "add-spaces": {
15421 meta: {
15422 fixable: "whitespace"
15423 },
15424 create(context) {
15425 return {
15426 Program(node) {
15427 context.report({
15428 node,
15429 message: "Add a space before this node.",
15430 fix: fixer => fixer.insertTextBefore(node, " ")
15431 });
15432 }
15433 };
15434 }
15435 }
15436 }
15437 }
15438 },
15439 rules: {
15440 "test/add-spaces": "error"
15441 }
15442 };
15443
15444 const fixResult = linter.verifyAndFix("a", config);
15445 const suppressedMessages = linter.getSuppressedMessages();
15446
15447 assert.strictEqual(fixResult.fixed, true);
15448 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a`);
15449 assert.strictEqual(fixResult.messages.length, 1);
15450
15451 assert.strictEqual(suppressedMessages.length, 0);
15452 });
15453
15454 it("should throw an error if fix is passed but meta has no `fixable` property", () => {
15455
15456 const config = {
15457 plugins: {
15458 test: {
15459 rules: {
15460 "test-rule": {
15461 meta: {
15462 docs: {},
15463 schema: []
15464 },
15465 create: context => ({
15466 Program(node) {
15467 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
15468 }
15469 })
15470 }
15471 }
15472 }
15473 },
15474 rules: {
15475 "test/test-rule": "error"
15476 }
15477 };
15478
15479
15480 assert.throws(() => {
15481 linter.verify("0", config);
15482 }, /Fixable rules must set the `meta\.fixable` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test\/test-rule"$/u);
15483 });
15484
15485 it("should throw an error if fix is passed and there is no metadata", () => {
15486
15487 const config = {
15488 plugins: {
15489 test: {
15490 rules: {
15491 "test-rule": {
15492 create: context => ({
15493 Program(node) {
15494 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
15495 }
15496 })
15497 }
15498 }
15499 }
15500 },
15501 rules: {
15502 "test/test-rule": "error"
15503 }
15504 };
15505
15506 assert.throws(() => {
15507 linter.verify("0", config);
15508 }, /Fixable rules must set the `meta\.fixable` property/u);
15509 });
15510
15511 it("should throw an error if fix is passed from a legacy-format rule", () => {
15512
15513 const config = {
15514 plugins: {
15515 test: {
15516 rules: {
15517 "test-rule": {
15518 create: context => ({
15519 Program(node) {
15520 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
15521 }
15522 })
15523 }
15524 }
15525 }
15526 },
15527 rules: {
15528 "test/test-rule": "error"
15529 }
15530 };
15531
15532
15533 assert.throws(() => {
15534 linter.verify("0", config);
15535 }, /Fixable rules must set the `meta\.fixable` property/u);
15536 });
15537 });
15538
15539 describe("Mutability", () => {
15540 let linter1 = null;
15541 let linter2 = null;
15542
15543 beforeEach(() => {
15544 linter1 = new Linter();
15545 linter2 = new Linter();
15546 });
15547
15548 describe("rules", () => {
15549 it("with no changes, same rules are loaded", () => {
15550 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
15551 });
15552
15553 it("loading rule in one doesn't change the other", () => {
15554 linter1.defineRule("mock-rule", {
15555 create: () => ({})
15556 });
15557
15558 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
15559 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
15560 });
15561 });
15562 });
15563
15564
15565 describe("processors", () => {
15566 let receivedFilenames = [];
15567 let receivedPhysicalFilenames = [];
15568 const extraConfig = {
15569 plugins: {
15570 test: {
15571 rules: {
15572 "report-original-text": {
15573 meta: {
15574
15575 },
15576 create(context) {
15577 return {
15578 Program(ast) {
15579 assert.strictEqual(context.getFilename(), context.filename);
15580 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
15581
15582 receivedFilenames.push(context.filename);
15583 receivedPhysicalFilenames.push(context.physicalFilename);
15584
15585 context.report({ node: ast, message: context.sourceCode.text });
15586 }
15587 };
15588 }
15589 }
15590 }
15591 }
15592 }
15593 };
15594
15595 beforeEach(() => {
15596 receivedFilenames = [];
15597 receivedPhysicalFilenames = [];
15598 });
15599
15600 describe("preprocessors", () => {
15601 it("should receive text and filename.", () => {
15602 const code = "foo bar baz";
15603 const preprocess = sinon.spy(text => text.split(" "));
15604 const configs = createFlatConfigArray({});
15605
15606 configs.normalizeSync();
15607
15608 linter.verify(code, configs, { filename, preprocess });
15609
15610 assert.strictEqual(preprocess.calledOnce, true, "preprocess wasn't called");
15611 assert.deepStrictEqual(preprocess.args[0], [code, filename], "preprocess was called with the wrong arguments");
15612 });
15613
15614 it("should run preprocess only once", () => {
15615 const logs = [];
15616 const config = {
15617 files: ["*.md"],
15618 processor: {
15619 preprocess(text, filenameForText) {
15620 logs.push({
15621 text,
15622 filename: filenameForText
15623 });
15624
15625 return [{ text: "bar", filename: "0.js" }];
15626 },
15627 postprocess() {
15628 return [];
15629 }
15630 }
15631 };
15632
15633 linter.verify("foo", config, "a.md");
15634 assert.strictEqual(logs.length, 1, "preprocess() should only be called once.");
15635 });
15636
15637 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
15638 const code = "foo bar baz";
15639 const configs = createFlatConfigArray([
15640 extraConfig,
15641 { rules: { "test/report-original-text": "error" } }
15642 ]);
15643
15644 configs.normalizeSync();
15645
15646 const problems = linter.verify(
15647 code,
15648 configs,
15649 {
15650 filename,
15651
15652 // Apply a preprocessor that splits the source text into spaces and lints each word individually
15653 preprocess(input) {
15654 return input.split(" ");
15655 }
15656 }
15657 );
15658 const suppressedMessages = linter.getSuppressedMessages();
15659
15660 assert.strictEqual(problems.length, 3);
15661 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15662
15663 assert.strictEqual(suppressedMessages.length, 0);
15664 });
15665
15666 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
15667 const code = "foo bar baz";
15668 const configs = createFlatConfigArray([
15669 extraConfig,
15670 { rules: { "test/report-original-text": "error" } }
15671 ]);
15672
15673 configs.normalizeSync();
15674
15675 const problems = linter.verify(
15676 code,
15677 configs,
15678 {
15679 filename,
15680
15681 // Apply a preprocessor that splits the source text into spaces and lints each word individually
15682 preprocess(input) {
15683 return input.split(" ").map(text => ({
15684 filename: "block.js",
15685 text
15686 }));
15687 }
15688 }
15689 );
15690 const suppressedMessages = linter.getSuppressedMessages();
15691
15692 assert.strictEqual(problems.length, 3);
15693 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15694
15695 assert.strictEqual(suppressedMessages.length, 0);
15696
15697 // filename
15698 assert.strictEqual(receivedFilenames.length, 3);
15699 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
15700 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
15701 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
15702
15703 // physical filename
15704 assert.strictEqual(receivedPhysicalFilenames.length, 3);
15705 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
15706 });
15707
15708 it("should receive text even if a SourceCode object was given.", () => {
15709 const code = "foo";
15710 const preprocess = sinon.spy(text => text.split(" "));
15711 const configs = createFlatConfigArray([
15712 extraConfig
15713 ]);
15714
15715 configs.normalizeSync();
15716
15717 linter.verify(code, configs);
15718 const sourceCode = linter.getSourceCode();
15719
15720 linter.verify(sourceCode, configs, { filename, preprocess });
15721
15722 assert.strictEqual(preprocess.calledOnce, true);
15723 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15724 });
15725
15726 it("should receive text even if a SourceCode object was given (with BOM).", () => {
15727 const code = "\uFEFFfoo";
15728 const preprocess = sinon.spy(text => text.split(" "));
15729 const configs = createFlatConfigArray([
15730 extraConfig
15731 ]);
15732
15733 configs.normalizeSync();
15734
15735 linter.verify(code, configs);
15736 const sourceCode = linter.getSourceCode();
15737
15738 linter.verify(sourceCode, configs, { filename, preprocess });
15739
15740 assert.strictEqual(preprocess.calledOnce, true);
15741 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15742 });
15743
15744 it("should catch preprocess error.", () => {
15745 const code = "foo";
15746 const preprocess = sinon.spy(() => {
15747 throw Object.assign(new SyntaxError("Invalid syntax"), {
15748 lineNumber: 1,
15749 column: 1
15750 });
15751 });
15752
15753 const configs = createFlatConfigArray([
15754 extraConfig
15755 ]);
15756
15757 configs.normalizeSync();
15758
15759 const messages = linter.verify(code, configs, { filename, preprocess });
15760
15761 assert.strictEqual(preprocess.calledOnce, true);
15762 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15763 assert.deepStrictEqual(messages, [
15764 {
15765 ruleId: null,
15766 fatal: true,
15767 severity: 2,
15768 message: "Preprocessing error: Invalid syntax",
15769 line: 1,
15770 column: 1,
15771 nodeType: null
15772 }
15773 ]);
15774 });
15775 });
15776
15777 describe("postprocessors", () => {
15778 it("should receive result and filename.", () => {
15779 const code = "foo bar baz";
15780 const preprocess = sinon.spy(text => text.split(" "));
15781 const postprocess = sinon.spy(text => [text]);
15782 const configs = createFlatConfigArray([
15783 extraConfig
15784 ]);
15785
15786 configs.normalizeSync();
15787
15788 linter.verify(code, configs, { filename, postprocess, preprocess });
15789
15790 assert.strictEqual(postprocess.calledOnce, true);
15791 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
15792 });
15793
15794 it("should apply a postprocessor to the reported messages", () => {
15795 const code = "foo bar baz";
15796 const configs = createFlatConfigArray([
15797 extraConfig,
15798 { rules: { "test/report-original-text": "error" } }
15799 ]);
15800
15801 configs.normalizeSync();
15802
15803 const problems = linter.verify(
15804 code,
15805 configs,
15806 {
15807 preprocess: input => input.split(" "),
15808
15809 /*
15810 * Apply a postprocessor that updates the locations of the reported problems
15811 * to make sure they correspond to the locations in the original text.
15812 */
15813 postprocess(problemLists) {
15814 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
15815 return problemLists.reduce(
15816 (combinedList, problemList, index) =>
15817 combinedList.concat(
15818 problemList.map(
15819 problem =>
15820 Object.assign(
15821 {},
15822 problem,
15823 {
15824 message: problem.message.toUpperCase(),
15825 column: problem.column + index * 4
15826 }
15827 )
15828 )
15829 ),
15830 []
15831 );
15832 }
15833 }
15834 );
15835 const suppressedMessages = linter.getSuppressedMessages();
15836
15837 assert.strictEqual(problems.length, 3);
15838 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
15839 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
15840
15841 assert.strictEqual(suppressedMessages.length, 0);
15842 });
15843
15844 it("should use postprocessed problem ranges when applying autofixes", () => {
15845 const code = "foo bar baz";
15846 const configs = createFlatConfigArray([
15847 extraConfig,
15848 {
15849 plugins: {
15850 test2: {
15851 rules: {
15852 "capitalize-identifiers": {
15853 meta: {
15854 fixable: "code"
15855 },
15856 create(context) {
15857 return {
15858 Identifier(node) {
15859 if (node.name !== node.name.toUpperCase()) {
15860 context.report({
15861 node,
15862 message: "Capitalize this identifier",
15863 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
15864 });
15865 }
15866 }
15867 };
15868 }
15869 }
15870 }
15871 }
15872 }
15873 },
15874 { rules: { "test2/capitalize-identifiers": "error" } }
15875 ]);
15876
15877 configs.normalizeSync();
15878
15879 const fixResult = linter.verifyAndFix(
15880 code,
15881 configs,
15882 {
15883
15884 /*
15885 * Apply a postprocessor that updates the locations of autofixes
15886 * to make sure they correspond to locations in the original text.
15887 */
15888 preprocess: input => input.split(" "),
15889 postprocess(problemLists) {
15890 return problemLists.reduce(
15891 (combinedProblems, problemList, blockIndex) =>
15892 combinedProblems.concat(
15893 problemList.map(problem =>
15894 Object.assign(problem, {
15895 fix: {
15896 text: problem.fix.text,
15897 range: problem.fix.range.map(
15898 rangeIndex => rangeIndex + blockIndex * 4
15899 )
15900 }
15901 }))
15902 ),
15903 []
15904 );
15905 }
15906 }
15907 );
15908 const suppressedMessages = linter.getSuppressedMessages();
15909
15910 assert.strictEqual(fixResult.fixed, true);
15911 assert.strictEqual(fixResult.messages.length, 0);
15912 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
15913
15914 assert.strictEqual(suppressedMessages.length, 0);
15915 });
15916 });
15917 });
15918
15919 describe("Edge cases", () => {
15920
15921 describe("Modules", () => {
15922 const moduleConfig = {
15923 languageOptions: {
15924 sourceType: "module",
15925 ecmaVersion: 6
15926 }
15927 };
15928
15929 it("should properly parse import statements when sourceType is module", () => {
15930 const code = "import foo from 'foo';";
15931 const messages = linter.verify(code, moduleConfig);
15932 const suppressedMessages = linter.getSuppressedMessages();
15933
15934 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15935 assert.strictEqual(suppressedMessages.length, 0);
15936 });
15937
15938 it("should properly parse import all statements when sourceType is module", () => {
15939 const code = "import * as foo from 'foo';";
15940 const messages = linter.verify(code, moduleConfig);
15941 const suppressedMessages = linter.getSuppressedMessages();
15942
15943 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15944 assert.strictEqual(suppressedMessages.length, 0);
15945 });
15946
15947 it("should properly parse default export statements when sourceType is module", () => {
15948 const code = "export default function initialize() {}";
15949 const messages = linter.verify(code, moduleConfig);
15950 const suppressedMessages = linter.getSuppressedMessages();
15951
15952 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15953 assert.strictEqual(suppressedMessages.length, 0);
15954 });
15955
15956 });
15957
15958
15959 // https://github.com/eslint/eslint/issues/9687
15960 it("should report an error when invalid languageOptions found", () => {
15961 let messages = linter.verify("", { languageOptions: { ecmaVersion: 222 } });
15962
15963 assert.deepStrictEqual(messages.length, 1);
15964 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
15965
15966 assert.throws(() => {
15967 linter.verify("", { languageOptions: { sourceType: "foo" } });
15968 }, /Expected "script", "module", or "commonjs"./u);
15969
15970
15971 messages = linter.verify("", { languageOptions: { ecmaVersion: 5, sourceType: "module" } });
15972 const suppressedMessages = linter.getSuppressedMessages();
15973
15974 assert.deepStrictEqual(messages.length, 1);
15975 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
15976
15977 assert.strictEqual(suppressedMessages.length, 0);
15978 });
15979
15980 it("should not crash when invalid parentheses syntax is encountered", () => {
15981 linter.verify("left = (aSize.width/2) - ()");
15982 });
15983
15984 it("should not crash when let is used inside of switch case", () => {
15985 linter.verify("switch(foo) { case 1: let bar=2; }", { languageOptions: { ecmaVersion: 6 } });
15986 });
15987
15988 it("should not crash when parsing destructured assignment", () => {
15989 linter.verify("var { a='a' } = {};", { languageOptions: { ecmaVersion: 6 } });
15990 });
15991
15992 it("should report syntax error when a keyword exists in object property shorthand", () => {
15993 const messages = linter.verify("let a = {this}", { languageOptions: { ecmaVersion: 6 } });
15994 const suppressedMessages = linter.getSuppressedMessages();
15995
15996 assert.strictEqual(messages.length, 1);
15997 assert.strictEqual(messages[0].fatal, true);
15998
15999 assert.strictEqual(suppressedMessages.length, 0);
16000 });
16001
16002 it("should not crash when we reuse the SourceCode object", () => {
16003 const config = {
16004 languageOptions: {
16005 ecmaVersion: 6,
16006 parserOptions: {
16007 ecmaFeatures: { jsx: true }
16008 }
16009 }
16010 };
16011
16012 linter.verify("function render() { return <div className='test'>{hello}</div> }", config);
16013 linter.verify(linter.getSourceCode(), config);
16014 });
16015
16016 it("should reuse the SourceCode object", () => {
16017 let ast1 = null,
16018 ast2 = null;
16019
16020 const config = {
16021 plugins: {
16022 test: {
16023 rules: {
16024 "save-ast1": {
16025 create: () => ({
16026 Program(node) {
16027 ast1 = node;
16028 }
16029 })
16030 },
16031
16032 "save-ast2": {
16033 create: () => ({
16034 Program(node) {
16035 ast2 = node;
16036 }
16037 })
16038 }
16039
16040 }
16041 }
16042 },
16043 languageOptions: {
16044 ecmaVersion: 6,
16045 parserOptions: {
16046 ecmaFeatures: { jsx: true }
16047 }
16048 }
16049 };
16050
16051
16052 linter.verify("function render() { return <div className='test'>{hello}</div> }", { ...config, rules: { "test/save-ast1": "error" } });
16053 linter.verify(linter.getSourceCode(), { ...config, rules: { "test/save-ast2": "error" } });
16054
16055 assert(ast1 !== null);
16056 assert(ast2 !== null);
16057 assert(ast1 === ast2);
16058 });
16059
16060 it("should not modify config object passed as argument", () => {
16061 const config = {};
16062
16063 Object.freeze(config);
16064 linter.verify("var", config);
16065 });
16066
16067 it("should pass 'id' to rule contexts with the rule id", () => {
16068
16069 const spy = sinon.spy(context => {
16070 assert.strictEqual(context.id, "test/foo-bar-baz");
16071 return {};
16072 });
16073
16074 const config = {
16075 plugins: {
16076 test: {
16077 rules: {
16078 "foo-bar-baz": { create: spy }
16079 }
16080 }
16081 },
16082 rules: {
16083 "test/foo-bar-baz": "error"
16084 }
16085 };
16086
16087
16088 linter.verify("x", config);
16089 assert(spy.calledOnce);
16090 });
16091
16092
16093 describe("when evaluating an empty string", () => {
16094 it("runs rules", () => {
16095
16096 const config = {
16097 plugins: {
16098 test: {
16099 rules: {
16100 "no-programs": {
16101 create(context) {
16102 return {
16103 Program(node) {
16104 context.report({ node, message: "No programs allowed." });
16105 }
16106 };
16107 }
16108 }
16109 }
16110 }
16111 },
16112 rules: {
16113 "test/no-programs": "error"
16114 }
16115 };
16116
16117 assert.strictEqual(
16118 linter.verify("", config).length,
16119 1
16120 );
16121 });
16122 });
16123
16124 });
16125
16126 });