2 * @fileoverview Tests for eslint object.
3 * @author Nicholas C. Zakas
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
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");
18 const { Linter
} = require("../../../lib/linter");
19 const { FlatConfigArray
} = require("../../../lib/config/flat-config-array");
21 //------------------------------------------------------------------------------
23 //------------------------------------------------------------------------------
25 const TEST_CODE
= "var answer = 6 * 7;",
26 BROKEN_TEST_CODE
= "var;";
28 //------------------------------------------------------------------------------
30 //------------------------------------------------------------------------------
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
39 function getVariable(scope
, name
) {
40 return scope
.variables
.find(v
=> v
.name
=== name
) || null;
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.
49 const ESLINT_ENV
= "eslint-env";
51 //------------------------------------------------------------------------------
53 //------------------------------------------------------------------------------
55 describe("Linter", () => {
56 const filename
= "filename.js";
58 /** @type {InstanceType<import("../../../lib/linter/linter.js").Linter>} */
62 linter = new Linter();
66 sinon.verifyAndRestore();
69 describe("Static Members", () => {
70 describe("version", () => {
71 it("should return same version as instance property", () => {
72 assert.strictEqual(Linter.version, linter.version);
77 describe("when using events", () => {
78 const code = TEST_CODE;
80 it("an error should be thrown when an error occurs inside of an event handler", () => {
81 const config = { rules: { checker: "error" } };
83 linter.defineRule("checker", {
86 throw new Error("Intentional error.");
92 linter.verify(code, config, filename);
93 }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "checker"`);
96 it("does not call rule listeners with a `this` value", () => {
97 const spy = sinon.spy();
99 linter.defineRule("checker", {
100 create: () => ({ Program: spy })
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");
107 it("does not allow listeners to use special EventEmitter values", () => {
108 const spy = sinon.spy();
110 linter.defineRule("checker", {
111 create: () => ({ newListener: spy })
113 linter.verify("foo", { rules: { checker: "error", "no-undef": "error" } });
114 assert(spy.notCalled);
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;
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);
130 linter.defineRule("checker", { create: spy });
132 linter.verify("foo + bar", { rules: { checker: "error" } });
133 assert(spy.calledOnce);
137 describe("context.getSourceLines()", () => {
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;"]);
146 linter.defineRule("checker", { create: spy });
147 linter.verify(code, { rules: { checker: "error" } });
148 assert(spy.calledOnce);
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;"]);
158 linter.defineRule("checker", { create: spy });
159 linter.verify(code, { rules: { checker: "error" } });
160 assert(spy.calledOnce);
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;"]);
170 linter.defineRule("checker", { create: spy });
171 linter.verify(code, { rules: { checker: "error" } });
172 assert(spy.calledOnce);
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;"]);
182 linter.defineRule("checker", { create: spy });
183 linter.verify(code, { rules: { checker: "error" } });
184 assert(spy.calledOnce);
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;"]);
194 linter.defineRule("checker", { create: spy });
195 linter.verify(code, { rules: { checker: "error" } });
196 assert(spy.calledOnce);
202 describe("getSourceCode()", () => {
203 const code = TEST_CODE;
205 it("should retrieve SourceCode object after reset", () => {
206 linter.verify(code, {}, filename, true);
208 const sourceCode = linter.getSourceCode();
210 assert.isObject(sourceCode);
211 assert.strictEqual(sourceCode.text, code);
212 assert.isObject(sourceCode.ast);
215 it("should retrieve SourceCode object without reset", () => {
216 linter.verify(code, {}, filename);
218 const sourceCode = linter.getSourceCode();
220 assert.isObject(sourceCode);
221 assert.strictEqual(sourceCode.text, code);
222 assert.isObject(sourceCode.ast);
227 describe("getSuppressedMessages()", () => {
228 it("should have no suppressed messages", () => {
229 const suppressedMessages = linter.getSuppressedMessages();
231 assert.strictEqual(suppressedMessages.length, 0);
234 it("should have a suppressed message", () => {
235 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test
\");";
237 rules: { "no
-alert
": 1 }
239 const messages = linter.verify(code, config);
240 const suppressedMessages = linter.getSuppressedMessages();
242 assert.strictEqual(messages.length, 0);
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
" }]
252 it("should have a suppressed message
", () => {
254 "/* eslint-disable no-alert --- j1",
260 rules: { "no
-alert
": 1 }
262 const messages = linter.verify(code, config);
263 const suppressedMessages = linter.getSuppressedMessages();
265 assert.strictEqual(messages.length, 0);
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
" }]
275 it("should not report a lint message
", () => {
277 "/* eslint-disable -- j1 */",
278 "// eslint-disable-next-line -- j2",
282 rules
: { "no-alert": 1 }
284 const messages
= linter
.verify(code
, config
);
285 const suppressedMessages
= linter
.getSuppressedMessages();
287 assert
.strictEqual(messages
.length
, 0);
289 assert
.strictEqual(suppressedMessages
.length
, 1);
290 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
291 assert
.deepStrictEqual(
292 suppressedMessages
[0].suppressions
,
294 { kind
: "directive", justification
: "j1" },
295 { kind
: "directive", justification
: "j2" }
300 it("should not report a lint message", () => {
302 "/* eslint-disable -- j1 */",
303 "alert(\"test\"); // eslint-disable-line -- j2"
306 rules
: { "no-alert": 1 }
308 const messages
= linter
.verify(code
, config
);
309 const suppressedMessages
= linter
.getSuppressedMessages();
311 assert
.strictEqual(messages
.length
, 0);
313 assert
.strictEqual(suppressedMessages
.length
, 1);
314 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
315 assert
.deepStrictEqual(
316 suppressedMessages
[0].suppressions
,
318 { kind
: "directive", justification
: "j1" },
319 { kind
: "directive", justification
: "j2" }
324 it("should have a suppressed message with multiple suppressions", () => {
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"
332 rules
: { "no-alert": 1 }
334 const messages
= linter
.verify(code
, config
);
335 const suppressedMessages
= linter
.getSuppressedMessages();
337 assert
.strictEqual(messages
.length
, 0);
339 assert
.strictEqual(suppressedMessages
.length
, 1);
340 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
341 assert
.deepStrictEqual(
342 suppressedMessages
[0].suppressions
,
344 { kind
: "directive", justification
: "j1" },
345 { kind
: "directive", justification
: "j2" },
346 { kind
: "directive", justification
: "j3" }
352 describe("context.getSource()", () => {
353 const code
= TEST_CODE
;
355 it("should retrieve all text when used without parameters", () => {
357 const config
= { rules
: { checker
: "error" } };
360 linter
.defineRule("checker", {
362 spy
= sinon
.spy(() => {
363 assert
.strictEqual(context
.getSource(), TEST_CODE
);
365 return { Program
: spy
};
369 linter
.verify(code
, config
);
370 assert(spy
&& spy
.calledOnce
);
373 it("should retrieve all text for root node", () => {
374 const config
= { rules
: { checker
: "error" } };
377 linter
.defineRule("checker", {
379 spy
= sinon
.spy(node
=> {
380 assert
.strictEqual(context
.getSource(node
), TEST_CODE
);
382 return { Program
: spy
};
386 linter
.verify(code
, config
);
387 assert(spy
&& spy
.calledOnce
);
390 it("should clamp to valid range when retrieving characters before start of source", () => {
391 const config
= { rules
: { checker
: "error" } };
394 linter
.defineRule("checker", {
396 spy
= sinon
.spy(node
=> {
397 assert
.strictEqual(context
.getSource(node
, 2, 0), TEST_CODE
);
399 return { Program
: spy
};
403 linter
.verify(code
, config
);
404 assert(spy
&& spy
.calledOnce
);
407 it("should retrieve all text for binary expression", () => {
408 const config
= { rules
: { checker
: "error" } };
411 linter
.defineRule("checker", {
413 spy
= sinon
.spy(node
=> {
414 assert
.strictEqual(context
.getSource(node
), "6 * 7");
416 return { BinaryExpression
: spy
};
420 linter
.verify(code
, config
);
421 assert(spy
&& spy
.calledOnce
);
424 it("should retrieve all text plus two characters before for binary expression", () => {
425 const config
= { rules
: { checker
: "error" } };
428 linter
.defineRule("checker", {
430 spy
= sinon
.spy(node
=> {
431 assert
.strictEqual(context
.getSource(node
, 2), "= 6 * 7");
433 return { BinaryExpression
: spy
};
437 linter
.verify(code
, config
);
438 assert(spy
&& spy
.calledOnce
);
441 it("should retrieve all text plus one character after for binary expression", () => {
442 const config
= { rules
: { checker
: "error" } };
445 linter
.defineRule("checker", {
447 spy
= sinon
.spy(node
=> {
448 assert
.strictEqual(context
.getSource(node
, 0, 1), "6 * 7;");
450 return { BinaryExpression
: spy
};
454 linter
.verify(code
, config
);
455 assert(spy
&& spy
.calledOnce
);
458 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
459 const config
= { rules
: { checker
: "error" } };
462 linter
.defineRule("checker", {
464 spy
= sinon
.spy(node
=> {
465 assert
.strictEqual(context
.getSource(node
, 2, 1), "= 6 * 7;");
467 return { BinaryExpression
: spy
};
471 linter
.verify(code
, config
);
472 assert(spy
&& spy
.calledOnce
);
477 describe("when calling context.getAncestors", () => {
478 const code
= TEST_CODE
;
480 it("should retrieve all ancestors when used", () => {
482 const config
= { rules
: { checker
: "error" } };
485 linter
.defineRule("checker", {
487 spy
= sinon
.spy(() => {
488 const ancestors
= context
.getAncestors();
490 assert
.strictEqual(ancestors
.length
, 3);
492 return { BinaryExpression
: spy
};
496 linter
.verify(code
, config
, filename
, true);
497 assert(spy
&& spy
.calledOnce
);
500 it("should retrieve empty ancestors for root node", () => {
501 const config
= { rules
: { checker
: "error" } };
504 linter
.defineRule("checker", {
506 spy
= sinon
.spy(() => {
507 const ancestors
= context
.getAncestors();
509 assert
.strictEqual(ancestors
.length
, 0);
512 return { Program
: spy
};
516 linter
.verify(code
, config
);
517 assert(spy
&& spy
.calledOnce
);
521 describe("when calling context.getNodeByRangeIndex", () => {
522 const code
= TEST_CODE
;
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");
531 linter
.defineRule("checker", { create
: spy
});
532 linter
.verify(code
, config
);
533 assert(spy
.calledOnce
);
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");
543 linter
.defineRule("checker", { create
: spy
});
544 linter
.verify(code
, config
);
545 assert(spy
.calledOnce
);
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);
553 assert
.strictEqual(node
.type
, "Literal");
554 assert
.strictEqual(node
.value
, 6);
558 linter
.defineRule("checker", { create
: spy
});
559 linter
.verify(code
, config
);
560 assert(spy
.calledOnce
);
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");
570 linter
.defineRule("checker", { create
: spy
});
571 linter
.verify(code
, config
);
572 assert(spy
.calledOnce
);
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);
580 assert
.strictEqual(node1
.type
, "BinaryExpression");
582 const node2
= context
.getNodeByRangeIndex(3);
584 assert
.strictEqual(node2
.type
, "VariableDeclaration");
588 linter
.defineRule("checker", { create
: spy
});
589 linter
.verify(code
, config
);
590 assert(spy
.calledOnce
);
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);
598 assert
.isNull(node1
);
600 const node2
= context
.getNodeByRangeIndex(-99);
602 assert
.isNull(node2
);
606 linter
.defineRule("checker", { create
: spy
});
607 linter
.verify(code
, config
);
608 assert(spy
.calledOnce
);
613 describe("when calling context.getScope", () => {
614 const code
= "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
616 it("should retrieve the global scope correctly from a Program", () => {
617 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
620 linter
.defineRule("checker", {
622 spy
= sinon
.spy(() => {
623 const scope
= context
.getScope();
625 assert
.strictEqual(scope
.type
, "global");
627 return { Program
: spy
};
631 linter
.verify(code
, config
);
632 assert(spy
&& spy
.calledOnce
);
635 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
636 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
639 linter
.defineRule("checker", {
641 spy
= sinon
.spy(() => {
642 const scope
= context
.getScope();
644 assert
.strictEqual(scope
.type
, "function");
646 return { FunctionDeclaration
: spy
};
650 linter
.verify(code
, config
);
651 assert(spy
&& spy
.calledTwice
);
654 it("should retrieve the function scope correctly from a LabeledStatement", () => {
655 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
658 linter
.defineRule("checker", {
660 spy
= sinon
.spy(() => {
661 const scope
= context
.getScope();
663 assert
.strictEqual(scope
.type
, "function");
664 assert
.strictEqual(scope
.block
.id
.name
, "foo");
666 return { LabeledStatement
: spy
};
670 linter
.verify(code
, config
);
671 assert(spy
&& spy
.calledOnce
);
674 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
675 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
678 linter
.defineRule("checker", {
680 spy
= sinon
.spy(() => {
681 const scope
= context
.getScope();
683 assert
.strictEqual(scope
.type
, "function");
684 assert
.strictEqual(scope
.block
.type
, "ArrowFunctionExpression");
687 return { ReturnStatement
: spy
};
691 linter
.verify(code
, config
);
692 assert(spy
&& spy
.calledOnce
);
695 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
696 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
699 linter
.defineRule("checker", {
701 spy
= sinon
.spy(() => {
702 const scope
= context
.getScope();
704 assert
.strictEqual(scope
.type
, "switch");
705 assert
.strictEqual(scope
.block
.type
, "SwitchStatement");
708 return { SwitchStatement
: spy
};
712 linter
.verify("switch(foo){ case 'a': var b = 'foo'; }", config
);
713 assert(spy
&& spy
.calledOnce
);
716 it("should retrieve the function scope correctly from within a BlockStatement", () => {
717 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
720 linter
.defineRule("checker", {
722 spy
= sinon
.spy(() => {
723 const scope
= context
.getScope();
725 assert
.strictEqual(scope
.type
, "block");
726 assert
.strictEqual(scope
.block
.type
, "BlockStatement");
729 return { BlockStatement
: spy
};
733 linter
.verify("var x; {let y = 1}", config
);
734 assert(spy
&& spy
.calledOnce
);
737 it("should retrieve the function scope correctly from within a nested block statement", () => {
738 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
741 linter
.defineRule("checker", {
743 spy
= sinon
.spy(() => {
744 const scope
= context
.getScope();
746 assert
.strictEqual(scope
.type
, "block");
747 assert
.strictEqual(scope
.block
.type
, "BlockStatement");
750 return { BlockStatement
: spy
};
754 linter
.verify("if (true) { let x = 1 }", config
);
755 assert(spy
&& spy
.calledOnce
);
758 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
759 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
762 linter
.defineRule("checker", {
764 spy
= sinon
.spy(() => {
765 const scope
= context
.getScope();
767 assert
.strictEqual(scope
.type
, "function");
768 assert
.strictEqual(scope
.block
.type
, "FunctionDeclaration");
771 return { FunctionDeclaration
: spy
};
775 linter
.verify("function foo() {}", config
);
776 assert(spy
&& spy
.calledOnce
);
779 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
780 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
783 linter
.defineRule("checker", {
785 spy
= sinon
.spy(() => {
786 const scope
= context
.getScope();
788 assert
.strictEqual(scope
.type
, "function");
789 assert
.strictEqual(scope
.block
.type
, "FunctionExpression");
792 return { FunctionExpression
: spy
};
796 linter
.verify("(function foo() {})();", config
);
797 assert(spy
&& spy
.calledOnce
);
800 it("should retrieve the catch scope correctly from within a CatchClause", () => {
801 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
804 linter
.defineRule("checker", {
806 spy
= sinon
.spy(() => {
807 const scope
= context
.getScope();
809 assert
.strictEqual(scope
.type
, "catch");
810 assert
.strictEqual(scope
.block
.type
, "CatchClause");
813 return { CatchClause
: spy
};
817 linter
.verify("try {} catch (err) {}", config
);
818 assert(spy
&& spy
.calledOnce
);
821 it("should retrieve module scope correctly from an ES6 module", () => {
822 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } };
825 linter
.defineRule("checker", {
827 spy
= sinon
.spy(() => {
828 const scope
= context
.getScope();
830 assert
.strictEqual(scope
.type
, "module");
833 return { AssignmentExpression
: spy
};
837 linter
.verify("var foo = {}; foo.bar = 1;", config
);
838 assert(spy
&& spy
.calledOnce
);
841 it("should retrieve function scope correctly when globalReturn is true", () => {
842 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6, ecmaFeatures
: { globalReturn
: true } } };
845 linter
.defineRule("checker", {
847 spy
= sinon
.spy(() => {
848 const scope
= context
.getScope();
850 assert
.strictEqual(scope
.type
, "function");
853 return { AssignmentExpression
: spy
};
857 linter
.verify("var foo = {}; foo.bar = 1;", config
);
858 assert(spy
&& spy
.calledOnce
);
862 describe("marking variables as used", () => {
863 it("should mark variables in current scope as used", () => {
864 const code
= "var a = 1, b = 2;";
867 linter
.defineRule("checker", {
869 spy
= sinon
.spy(() => {
870 assert
.isTrue(context
.markVariableAsUsed("a"));
872 const scope
= context
.getScope();
874 assert
.isTrue(getVariable(scope
, "a").eslintUsed
);
875 assert
.notOk(getVariable(scope
, "b").eslintUsed
);
878 return { "Program:exit": spy
};
882 linter
.verify(code
, { rules
: { checker
: "error" } });
883 assert(spy
&& spy
.calledOnce
);
885 it("should mark variables in function args as used", () => {
886 const code
= "function abc(a, b) { return 1; }";
889 linter
.defineRule("checker", {
891 spy
= sinon
.spy(() => {
892 assert
.isTrue(context
.markVariableAsUsed("a"));
894 const scope
= context
.getScope();
896 assert
.isTrue(getVariable(scope
, "a").eslintUsed
);
897 assert
.notOk(getVariable(scope
, "b").eslintUsed
);
900 return { ReturnStatement
: spy
};
904 linter
.verify(code
, { rules
: { checker
: "error" } });
905 assert(spy
&& spy
.calledOnce
);
907 it("should mark variables in higher scopes as used", () => {
908 const code
= "var a, b; function abc() { return 1; }";
909 let returnSpy
, exitSpy
;
911 linter
.defineRule("checker", {
913 returnSpy
= sinon
.spy(() => {
914 assert
.isTrue(context
.markVariableAsUsed("a"));
916 exitSpy
= sinon
.spy(() => {
917 const scope
= context
.getScope();
919 assert
.isTrue(getVariable(scope
, "a").eslintUsed
);
920 assert
.notOk(getVariable(scope
, "b").eslintUsed
);
923 return { ReturnStatement
: returnSpy
, "Program:exit": exitSpy
};
927 linter
.verify(code
, { rules
: { checker
: "error" } });
928 assert(returnSpy
&& returnSpy
.calledOnce
);
929 assert(exitSpy
&& exitSpy
.calledOnce
);
932 it("should mark variables in Node.js environment as used", () => {
933 const code
= "var a = 1, b = 2;";
936 linter
.defineRule("checker", {
938 spy
= sinon
.spy(() => {
939 const globalScope
= context
.getScope(),
940 childScope
= globalScope
.childScopes
[0];
942 assert
.isTrue(context
.markVariableAsUsed("a"));
944 assert
.isTrue(getVariable(childScope
, "a").eslintUsed
);
945 assert
.isUndefined(getVariable(childScope
, "b").eslintUsed
);
948 return { "Program:exit": spy
};
952 linter
.verify(code
, { rules
: { checker
: "error" }, env
: { node
: true } });
953 assert(spy
&& spy
.calledOnce
);
956 it("should mark variables in modules as used", () => {
957 const code
= "var a = 1, b = 2;";
960 linter
.defineRule("checker", {
962 spy
= sinon
.spy(() => {
963 const globalScope
= context
.getScope(),
964 childScope
= globalScope
.childScopes
[0];
966 assert
.isTrue(context
.markVariableAsUsed("a"));
968 assert
.isTrue(getVariable(childScope
, "a").eslintUsed
);
969 assert
.isUndefined(getVariable(childScope
, "b").eslintUsed
);
972 return { "Program:exit": spy
};
976 linter
.verify(code
, { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } }, filename
, true);
977 assert(spy
&& spy
.calledOnce
);
980 it("should return false if the given variable is not found", () => {
981 const code
= "var a = 1, b = 2;";
984 linter
.defineRule("checker", {
986 spy
= sinon
.spy(() => {
987 assert
.isFalse(context
.markVariableAsUsed("c"));
990 return { "Program:exit": spy
};
994 linter
.verify(code
, { rules
: { checker
: "error" } });
995 assert(spy
&& spy
.calledOnce
);
999 describe("when evaluating code", () => {
1000 const code
= TEST_CODE
;
1002 it("events for each node type should fire", () => {
1003 const config
= { rules
: { checker
: "error" } };
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();
1012 linter
.defineRule("checker", {
1014 Literal
: spyLiteral
,
1015 VariableDeclarator
: spyVariableDeclarator
,
1016 VariableDeclaration
: spyVariableDeclaration
,
1017 Identifier
: spyIdentifier
,
1018 BinaryExpression
: spyBinaryExpression
1022 const messages
= linter
.verify(code
, config
, filename
, true);
1023 const suppressedMessages
= linter
.getSuppressedMessages();
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
);
1034 it("should throw an error if a rule reports a problem without a message", () => {
1035 linter
.defineRule("invalid-report", {
1036 create
: context
=> ({
1038 context
.report({ node
});
1044 () => linter
.verify("foo", { rules
: { "invalid-report": "error" } }),
1046 "Missing `message` property in report() call; add a message that describes the linting problem."
1051 describe("when config has shared settings for rules", () => {
1052 const code
= "test-rule";
1054 it("should pass settings to all rules", () => {
1055 linter
.defineRule(code
, {
1056 create
: context
=> ({
1058 context
.report(node
, context
.settings
.info
);
1063 const config
= { rules
: {}, settings
: { info
: "Hello" } };
1065 config
.rules
[code
] = 1;
1067 const messages
= linter
.verify("0", config
, filename
);
1068 const suppressedMessages
= linter
.getSuppressedMessages();
1070 assert
.strictEqual(messages
.length
, 1);
1071 assert
.strictEqual(messages
[0].message
, "Hello");
1072 assert
.strictEqual(suppressedMessages
.length
, 0);
1075 it("should not have any settings if they were not passed in", () => {
1076 linter
.defineRule(code
, {
1077 create
: context
=> ({
1079 if (Object
.getOwnPropertyNames(context
.settings
).length
!== 0) {
1080 context
.report(node
, "Settings should be empty");
1086 const config
= { rules
: {} };
1088 config
.rules
[code
] = 1;
1090 const messages
= linter
.verify("0", config
, filename
);
1091 const suppressedMessages
= linter
.getSuppressedMessages();
1093 assert
.strictEqual(messages
.length
, 0);
1094 assert
.strictEqual(suppressedMessages
.length
, 0);
1098 describe("when config has parseOptions", () => {
1100 it("should pass ecmaFeatures to all rules when provided on config", () => {
1102 const parserOptions
= {
1109 linter
.defineRule("test-rule", {
1110 create
: sinon
.mock().withArgs(
1111 sinon
.match({ parserOptions
})
1115 const config
= { rules
: { "test-rule": 2 }, parserOptions
};
1117 linter
.verify("0", config
, filename
);
1120 it("should pass parserOptions to all rules when default parserOptions is used", () => {
1122 const parserOptions
= {};
1124 linter
.defineRule("test-rule", {
1125 create
: sinon
.mock().withArgs(
1126 sinon
.match({ parserOptions
})
1130 const config
= { rules
: { "test-rule": 2 } };
1132 linter
.verify("0", config
, filename
);
1137 describe("when a custom parser is defined using defineParser", () => {
1139 it("should be able to define a custom parser", () => {
1141 parseForESLint
: function parse(code
, options
) {
1143 ast
: esprima
.parse(code
, options
),
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();
1160 assert
.strictEqual(messages
.length
, 0);
1161 assert
.strictEqual(suppressedMessages
.length
, 0);
1166 describe("when config has parser", () => {
1168 it("should pass parser as parserPath to all rules when provided on config", () => {
1170 const alternateParser
= "esprima";
1172 linter
.defineParser("esprima", esprima
);
1173 linter
.defineRule("test-rule", {
1174 create
: sinon
.mock().withArgs(
1175 sinon
.match({ parserPath
: alternateParser
})
1179 const config
= { rules
: { "test-rule": 2 }, parser
: alternateParser
};
1181 linter
.verify("0", config
, filename
);
1184 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
1185 const config
= { rules
: {}, parser
: "enhanced-parser" };
1187 linter
.defineParser("enhanced-parser", testParsers
.enhancedParser
);
1188 const messages
= linter
.verify("0", config
, filename
);
1189 const suppressedMessages
= linter
.getSuppressedMessages();
1191 assert
.strictEqual(messages
.length
, 0);
1192 assert
.strictEqual(suppressedMessages
.length
, 0);
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
=> ({
1202 message
: context
.parserServices
.test
.getMessage()
1208 const config
= { rules
: { "test-service-rule": 2 }, parser
: "enhanced-parser" };
1209 const messages
= linter
.verify("0", config
, filename
);
1210 const suppressedMessages
= linter
.getSuppressedMessages();
1212 assert
.strictEqual(messages
.length
, 1);
1213 assert
.strictEqual(messages
[0].message
, "Hi!");
1214 assert
.strictEqual(suppressedMessages
.length
, 0);
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
=> ({
1224 message
: context
.parserServices
.test
.getMessage()
1230 const config
= { rules
: { "test-service-rule": 2 }, parser
: "enhanced-parser" };
1231 const messages
= linter
.verify("0", config
, filename
);
1232 const suppressedMessages
= linter
.getSuppressedMessages();
1234 assert
.strictEqual(messages
.length
, 1);
1235 assert
.strictEqual(messages
[0].message
, "Hi!");
1236 assert
.strictEqual(suppressedMessages
.length
, 0);
1238 const messages2
= linter
.verify(linter
.getSourceCode(), config
, filename
);
1239 const suppressedMessages2
= linter
.getSuppressedMessages();
1241 assert
.strictEqual(messages2
.length
, 1);
1242 assert
.strictEqual(messages2
[0].message
, "Hi!");
1243 assert
.strictEqual(suppressedMessages2
.length
, 0);
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" })
1253 const config
= { rules
: { "test-rule": 2 } };
1255 linter
.verify("0", config
, filename
);
1261 describe("when passing in configuration values for rules", () => {
1262 const code
= "var answer = 6 * 7";
1264 it("should be configurable by only setting the integer value", () => {
1265 const rule
= "semi",
1266 config
= { rules
: {} };
1268 config
.rules
[rule
] = 1;
1270 const messages
= linter
.verify(code
, config
, filename
, true);
1271 const suppressedMessages
= linter
.getSuppressedMessages();
1273 assert
.strictEqual(messages
.length
, 1);
1274 assert
.strictEqual(messages
[0].ruleId
, rule
);
1276 assert
.strictEqual(suppressedMessages
.length
, 0);
1279 it("should be configurable by only setting the string value", () => {
1280 const rule
= "semi",
1281 config
= { rules
: {} };
1283 config
.rules
[rule
] = "warn";
1285 const messages
= linter
.verify(code
, config
, filename
, true);
1286 const suppressedMessages
= linter
.getSuppressedMessages();
1288 assert
.strictEqual(messages
.length
, 1);
1289 assert
.strictEqual(messages
[0].severity
, 1);
1290 assert
.strictEqual(messages
[0].ruleId
, rule
);
1292 assert
.strictEqual(suppressedMessages
.length
, 0);
1295 it("should be configurable by passing in values as an array", () => {
1296 const rule
= "semi",
1297 config
= { rules
: {} };
1299 config
.rules
[rule
] = [1];
1301 const messages
= linter
.verify(code
, config
, filename
, true);
1302 const suppressedMessages
= linter
.getSuppressedMessages();
1304 assert
.strictEqual(messages
.length
, 1);
1305 assert
.strictEqual(messages
[0].ruleId
, rule
);
1307 assert
.strictEqual(suppressedMessages
.length
, 0);
1310 it("should be configurable by passing in string value as an array", () => {
1311 const rule
= "semi",
1312 config
= { rules
: {} };
1314 config
.rules
[rule
] = ["warn"];
1316 const messages
= linter
.verify(code
, config
, filename
, true);
1317 const suppressedMessages
= linter
.getSuppressedMessages();
1319 assert
.strictEqual(messages
.length
, 1);
1320 assert
.strictEqual(messages
[0].severity
, 1);
1321 assert
.strictEqual(messages
[0].ruleId
, rule
);
1323 assert
.strictEqual(suppressedMessages
.length
, 0);
1326 it("should not be configurable by setting other value", () => {
1327 const rule
= "semi",
1328 config
= { rules
: {} };
1330 config
.rules
[rule
] = "1";
1332 const messages
= linter
.verify(code
, config
, filename
, true);
1333 const suppressedMessages
= linter
.getSuppressedMessages();
1335 assert
.strictEqual(messages
.length
, 0);
1336 assert
.strictEqual(suppressedMessages
.length
, 0);
1339 it("should process empty config", () => {
1341 const messages
= linter
.verify(code
, config
, filename
, true);
1342 const suppressedMessages
= linter
.getSuppressedMessages();
1344 assert
.strictEqual(messages
.length
, 0);
1345 assert
.strictEqual(suppressedMessages
.length
, 0);
1349 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
1351 it("variables should be available in global scope", () => {
1352 const config
= { rules
: { checker
: "error" }, globals
: { Array
: "off", ConfigGlobal
: "writeable" } };
1354 /*global a b:true c:false d:readable e:writeable Math:off */
1357 /* global ConfigGlobal : readable */
1361 linter
.defineRule("checker", {
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");
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);
1393 return { Program
: spy
};
1397 linter
.verify(code
, config
);
1398 assert(spy
&& spy
.calledOnce
);
1402 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
1403 const code
= "/* global a b : true c: false*/";
1405 it("variables should be available in global scope", () => {
1406 const config
= { rules
: { checker
: "error" } };
1409 linter
.defineRule("checker", {
1411 spy
= sinon
.spy(() => {
1412 const scope
= context
.getScope(),
1413 a
= getVariable(scope
, "a"),
1414 b
= getVariable(scope
, "b"),
1415 c
= getVariable(scope
, "c");
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);
1425 return { Program
: spy
};
1429 linter
.verify(code
, config
);
1430 assert(spy
&& spy
.calledOnce
);
1434 describe("when evaluating code containing a /*global */ block with specific variables", () => {
1435 const code
= "/* global toString hasOwnProperty valueOf: true */";
1437 it("should not throw an error if comment block has global variables which are Object.prototype contains", () => {
1438 const config
= { rules
: { checker
: "error" } };
1440 linter
.verify(code
, config
);
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" } };
1450 linter
.defineRule("checker", {
1452 spy
= sinon
.spy(() => {
1453 const scope
= context
.getScope(),
1454 exports
= getVariable(scope
, "exports"),
1455 window
= getVariable(scope
, "window");
1457 assert
.strictEqual(exports
.writeable
, true);
1458 assert
.strictEqual(window
.writeable
, false);
1461 return { Program
: spy
};
1465 linter
.verify(code
, config
);
1466 assert(spy
&& spy
.calledOnce
);
1470 describe("when evaluating code containing /*eslint-env */ block with sloppy whitespace", () => {
1471 const code
= `/* ${ESLINT_ENV} ,, node , no-browser ,, */`;
1473 it("variables should be available in global scope", () => {
1474 const config
= { rules
: { checker
: "error" } };
1477 linter
.defineRule("checker", {
1479 spy
= sinon
.spy(() => {
1480 const scope
= context
.getScope(),
1481 exports
= getVariable(scope
, "exports"),
1482 window
= getVariable(scope
, "window");
1484 assert
.strictEqual(exports
.writeable
, true);
1485 assert
.strictEqual(window
, null);
1488 return { Program
: spy
};
1492 linter
.verify(code
, config
);
1493 assert(spy
&& spy
.calledOnce
);
1497 describe("when evaluating code containing /*exported */ block", () => {
1499 it("we should behave nicely when no matching variable is found", () => {
1500 const code
= "/* exported horse */";
1501 const config
= { rules
: {} };
1503 linter
.verify(code
, config
, filename
, true);
1506 it("variables should be exported", () => {
1507 const code
= "/* exported horse */\n\nvar horse = 'circus'";
1508 const config
= { rules
: { checker
: "error" } };
1511 linter
.defineRule("checker", {
1513 spy
= sinon
.spy(() => {
1514 const scope
= context
.getScope(),
1515 horse
= getVariable(scope
, "horse");
1517 assert
.strictEqual(horse
.eslintUsed
, true);
1520 return { Program
: spy
};
1524 linter
.verify(code
, config
);
1525 assert(spy
&& spy
.calledOnce
);
1528 it("undefined variables should not be exported", () => {
1529 const code
= "/* exported horse */\n\nhorse = 'circus'";
1530 const config
= { rules
: { checker
: "error" } };
1533 linter
.defineRule("checker", {
1535 spy
= sinon
.spy(() => {
1536 const scope
= context
.getScope(),
1537 horse
= getVariable(scope
, "horse");
1539 assert
.strictEqual(horse
, null);
1542 return { Program
: spy
};
1546 linter
.verify(code
, config
);
1547 assert(spy
&& spy
.calledOnce
);
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" } };
1555 linter
.defineRule("checker", {
1557 spy
= sinon
.spy(() => {
1558 const scope
= context
.getScope(),
1559 horse
= getVariable(scope
, "horse");
1561 assert
.strictEqual(horse
.eslintUsed
, true);
1564 return { Program
: spy
};
1568 linter
.verify(code
, config
);
1569 assert(spy
&& spy
.calledOnce
);
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" } };
1577 linter
.defineRule("checker", {
1579 spy
= sinon
.spy(() => {
1580 const scope
= context
.getScope(),
1581 horse
= getVariable(scope
, "horse");
1583 assert
.strictEqual(horse
, null); // there is no global scope at all
1586 return { Program
: spy
};
1590 linter
.verify(code
, config
);
1591 assert(spy
&& spy
.calledOnce
);
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 } };
1599 linter
.defineRule("checker", {
1601 spy
= sinon
.spy(() => {
1602 const scope
= context
.getScope(),
1603 horse
= getVariable(scope
, "horse");
1605 assert
.strictEqual(horse
, null); // there is no global scope at all
1608 return { Program
: spy
};
1612 linter
.verify(code
, config
);
1613 assert(spy
&& spy
.calledOnce
);
1617 describe("when evaluating code containing a line comment", () => {
1618 const code
= "//global a \n function f() {}";
1620 it("should not introduce a global variable", () => {
1621 const config
= { rules
: { checker
: "error" } };
1624 linter
.defineRule("checker", {
1626 spy
= sinon
.spy(() => {
1627 const scope
= context
.getScope();
1629 assert
.strictEqual(getVariable(scope
, "a"), null);
1632 return { Program
: spy
};
1636 linter
.verify(code
, config
);
1637 assert(spy
&& spy
.calledOnce
);
1641 describe("when evaluating code containing normal block comments", () => {
1642 const code
= "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
1644 it("should not introduce a global variable", () => {
1645 const config
= { rules
: { checker
: "error" } };
1648 linter
.defineRule("checker", {
1650 spy
= sinon
.spy(() => {
1651 const scope
= context
.getScope();
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);
1659 return { Program
: spy
};
1663 linter
.verify(code
, config
);
1664 assert(spy
&& spy
.calledOnce
);
1668 describe("when evaluating any code", () => {
1671 it("builtin global variables should be available in the global scope", () => {
1672 const config
= { rules
: { checker
: "error" } };
1675 linter
.defineRule("checker", {
1677 spy
= sinon
.spy(() => {
1678 const scope
= context
.getScope();
1680 assert
.notStrictEqual(getVariable(scope
, "Object"), null);
1681 assert
.notStrictEqual(getVariable(scope
, "Array"), null);
1682 assert
.notStrictEqual(getVariable(scope
, "undefined"), null);
1685 return { Program
: spy
};
1689 linter
.verify(code
, config
);
1690 assert(spy
&& spy
.calledOnce
);
1693 it("ES6 global variables should not be available by default", () => {
1694 const config
= { rules
: { checker
: "error" } };
1697 linter
.defineRule("checker", {
1699 spy
= sinon
.spy(() => {
1700 const scope
= context
.getScope();
1702 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1703 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1704 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1707 return { Program
: spy
};
1711 linter
.verify(code
, config
);
1712 assert(spy
&& spy
.calledOnce
);
1715 it("ES6 global variables should be available in the es6 environment", () => {
1716 const config
= { rules
: { checker
: "error" }, env
: { es6
: true } };
1719 linter
.defineRule("checker", {
1721 spy
= sinon
.spy(() => {
1722 const scope
= context
.getScope();
1724 assert
.notStrictEqual(getVariable(scope
, "Promise"), null);
1725 assert
.notStrictEqual(getVariable(scope
, "Symbol"), null);
1726 assert
.notStrictEqual(getVariable(scope
, "WeakMap"), null);
1729 return { Program
: spy
};
1733 linter
.verify(code
, config
);
1734 assert(spy
&& spy
.calledOnce
);
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 } };
1741 linter
.defineRule("checker", {
1743 spy
= sinon
.spy(() => {
1744 const scope
= context
.getScope();
1746 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1747 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1748 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1751 return { Program
: spy
};
1755 linter
.verify(code
, config
);
1756 assert(spy
&& spy
.calledOnce
);
1760 describe("at any time", () => {
1761 const code
= "new-rule";
1763 it("can add a rule dynamically", () => {
1764 linter
.defineRule(code
, {
1765 create
: context
=> ({
1767 context
.report(node
, "message");
1772 const config
= { rules
: {} };
1774 config
.rules
[code
] = 1;
1776 const messages
= linter
.verify("0", config
, filename
);
1777 const suppressedMessages
= linter
.getSuppressedMessages();
1779 assert
.strictEqual(messages
.length
, 1);
1780 assert
.strictEqual(messages
[0].ruleId
, code
);
1781 assert
.strictEqual(messages
[0].nodeType
, "Literal");
1783 assert
.strictEqual(suppressedMessages
.length
, 0);
1787 describe("at any time", () => {
1788 const code
= ["new-rule-0", "new-rule-1"];
1790 it("can add multiple rules dynamically", () => {
1791 const config
= { rules
: {} };
1792 const newRules
= {};
1794 code
.forEach(item
=> {
1795 config
.rules
[item
] = 1;
1800 context
.report(node
, "message");
1806 linter
.defineRules(newRules
);
1808 const messages
= linter
.verify("0", config
, filename
);
1809 const suppressedMessages
= linter
.getSuppressedMessages();
1811 assert
.strictEqual(messages
.length
, code
.length
);
1812 code
.forEach(item
=> {
1813 assert
.ok(messages
.some(message
=> message
.ruleId
=== item
));
1815 messages
.forEach(message
=> {
1816 assert
.strictEqual(message
.nodeType
, "Literal");
1819 assert
.strictEqual(suppressedMessages
.length
, 0);
1823 describe("at any time", () => {
1824 const code
= "filename-rule";
1826 it("has access to the filename", () => {
1827 linter
.defineRule(code
, {
1828 create
: context
=> ({
1830 assert
.strictEqual(context
.getFilename(), context
.filename
);
1831 context
.report(node
, context
.filename
);
1836 const config
= { rules
: {} };
1838 config
.rules
[code
] = 1;
1840 const messages
= linter
.verify("0", config
, filename
);
1841 const suppressedMessages
= linter
.getSuppressedMessages();
1843 assert
.strictEqual(messages
[0].message
, filename
);
1844 assert
.strictEqual(suppressedMessages
.length
, 0);
1847 it("has access to the physicalFilename", () => {
1848 linter
.defineRule(code
, {
1849 create
: context
=> ({
1851 assert
.strictEqual(context
.getPhysicalFilename(), context
.physicalFilename
);
1852 context
.report(node
, context
.physicalFilename
);
1857 const config
= { rules
: {} };
1859 config
.rules
[code
] = 1;
1861 const messages
= linter
.verify("0", config
, filename
);
1862 const suppressedMessages
= linter
.getSuppressedMessages();
1864 assert
.strictEqual(messages
[0].message
, filename
);
1865 assert
.strictEqual(suppressedMessages
.length
, 0);
1868 it("defaults filename to '<input>'", () => {
1869 linter
.defineRule(code
, {
1870 create
: context
=> ({
1872 assert
.strictEqual(context
.getFilename(), context
.filename
);
1873 context
.report(node
, context
.filename
);
1878 const config
= { rules
: {} };
1880 config
.rules
[code
] = 1;
1882 const messages
= linter
.verify("0", config
);
1883 const suppressedMessages
= linter
.getSuppressedMessages();
1885 assert
.strictEqual(messages
[0].message
, "<input>");
1886 assert
.strictEqual(suppressedMessages
.length
, 0);
1890 describe("when evaluating code with comments to enable rules", () => {
1892 it("should report a violation", () => {
1893 const code
= "/*eslint no-alert:1*/ alert('test');";
1894 const config
= { rules
: {} };
1896 const messages
= linter
.verify(code
, config
, filename
);
1897 const suppressedMessages
= linter
.getSuppressedMessages();
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");
1904 assert
.strictEqual(suppressedMessages
.length
, 0);
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();
1914 assert
.strictEqual(messages
.length
, 0);
1915 assert
.strictEqual(suppressedMessages
.length
, 0);
1917 messages
= linter
.verify(codeB
, config
, filename
, false);
1918 suppressedMessages
= linter
.getSuppressedMessages();
1919 assert
.strictEqual(messages
.length
, 1);
1921 assert
.strictEqual(suppressedMessages
.length
, 0);
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();
1931 assert
.strictEqual(messages
.length
, 0);
1932 assert
.strictEqual(suppressedMessages
.length
, 0);
1934 messages
= linter
.verify(codeB
, config
, filename
, false);
1935 suppressedMessages
= linter
.getSuppressedMessages();
1936 assert
.strictEqual(messages
.length
, 1);
1938 assert
.strictEqual(suppressedMessages
.length
, 0);
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'; }";
1946 let messages
= linter
.verify(codeA
, config
, filename
, false);
1947 let suppressedMessages
= linter
.getSuppressedMessages();
1949 assert
.strictEqual(messages
.length
, 0);
1950 assert
.strictEqual(suppressedMessages
.length
, 0);
1952 messages
= linter
.verify(codeB
, config
, filename
, false);
1953 suppressedMessages
= linter
.getSuppressedMessages();
1955 assert
.strictEqual(messages
.length
, 1);
1956 assert
.strictEqual(suppressedMessages
.length
, 0);
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;";
1964 let messages
= linter
.verify(codeA
, config
, filename
, false);
1965 let suppressedMessages
= linter
.getSuppressedMessages();
1967 assert
.strictEqual(messages
.length
, 0);
1968 assert
.strictEqual(suppressedMessages
.length
, 0);
1970 messages
= linter
.verify(codeB
, config
, filename
, false);
1971 suppressedMessages
= linter
.getSuppressedMessages();
1973 assert
.strictEqual(messages
.length
, 1);
1974 assert
.strictEqual(suppressedMessages
.length
, 0);
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();
1983 assert
.deepStrictEqual(
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",
1999 assert
.strictEqual(suppressedMessages
.length
, 0);
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();
2006 assert
.deepStrictEqual(
2012 message
: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
2022 assert
.strictEqual(suppressedMessages
.length
, 0);
2026 describe("when evaluating code with comments to disable rules", () => {
2027 const code
= "/*eslint no-alert:0*/ alert('test');";
2029 it("should not report a violation", () => {
2030 const config
= { rules
: { "no-alert": 1 } };
2032 const messages
= linter
.verify(code
, config
, filename
);
2033 const suppressedMessages
= linter
.getSuppressedMessages();
2035 assert
.strictEqual(messages
.length
, 0);
2036 assert
.strictEqual(suppressedMessages
.length
, 0);
2040 describe("when evaluating code with comments to disable rules", () => {
2041 let code
, messages
, suppressedMessages
;
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);
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);
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);
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);
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();
2077 assert
.strictEqual(messages
.length
, 0);
2078 assert
.strictEqual(suppressedMessages
.length
, 0);
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);
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);
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');";
2101 it("should report a violation", () => {
2102 const config
= { rules
: {} };
2104 const messages
= linter
.verify(code
, config
, filename
);
2105 const suppressedMessages
= linter
.getSuppressedMessages();
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");
2113 assert
.strictEqual(suppressedMessages
.length
, 0);
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');";
2120 it("should report a violation", () => {
2121 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
2123 const messages
= linter
.verify(code
, config
, filename
);
2124 const suppressedMessages
= linter
.getSuppressedMessages();
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");
2131 assert
.strictEqual(suppressedMessages
.length
, 0);
2135 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
2138 linter
.defineRule("test-plugin/test-rule", {
2142 if (node
.value
=== "trigger violation") {
2143 context
.report(node
, "Reporting violation.");
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\";";
2155 const messages
= linter
.verify(code
, config
, filename
);
2156 const suppressedMessages
= linter
.getSuppressedMessages();
2158 assert
.strictEqual(messages
.length
, 0);
2159 assert
.strictEqual(suppressedMessages
.length
, 0);
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 } };
2166 const messages
= linter
.verify(code
, config
, filename
);
2167 const suppressedMessages
= linter
.getSuppressedMessages();
2169 assert
.strictEqual(messages
.length
, 0);
2170 assert
.strictEqual(suppressedMessages
.length
, 0);
2173 it("should report a violation when the report is right before the comment", () => {
2174 const code
= " /* eslint-disable */ ";
2176 linter
.defineRule("checker", {
2177 create
: context
=> ({
2179 context
.report({ loc
: { line
: 1, column
: 0 }, message
: "foo" });
2183 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
2184 const suppressedMessages
= linter
.getSuppressedMessages();
2186 assert
.strictEqual(problems
.length
, 1);
2187 assert
.strictEqual(problems
[0].message
, "foo");
2188 assert
.strictEqual(suppressedMessages
.length
, 0);
2191 it("should not report a violation when the report is right at the start of the comment", () => {
2192 const code
= " /* eslint-disable */ ";
2194 linter
.defineRule("checker", {
2195 create
: context
=> ({
2197 context
.report({ loc
: { line
: 1, column
: 1 }, message
: "foo" });
2201 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
2202 const suppressedMessages
= linter
.getSuppressedMessages();
2204 assert
.strictEqual(problems
.length
, 0);
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
, "");
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\";";
2217 let messages
= linter
.verify(codeA
, config
, filename
, false);
2218 let suppressedMessages
= linter
.getSuppressedMessages();
2220 assert
.strictEqual(messages
.length
, 0);
2221 assert
.strictEqual(suppressedMessages
.length
, 0);
2223 messages
= linter
.verify(codeB
, config
, filename
, false);
2224 suppressedMessages
= linter
.getSuppressedMessages();
2226 assert
.strictEqual(messages
.length
, 1);
2227 assert
.strictEqual(suppressedMessages
.length
, 0);
2231 describe("when evaluating code with comments to enable and disable all reporting", () => {
2232 it("should report a violation", () => {
2235 "/*eslint-disable */",
2237 "/*eslint-enable */",
2240 const config
= { rules
: { "no-alert": 1 } };
2242 const messages
= linter
.verify(code
, config
, filename
);
2243 const suppressedMessages
= linter
.getSuppressedMessages();
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);
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
, "");
2259 it("should not report a violation", () => {
2261 "/*eslint-disable */",
2265 const config
= { rules
: { "no-alert": 1 } };
2267 const messages
= linter
.verify(code
, config
, filename
);
2268 const suppressedMessages
= linter
.getSuppressedMessages();
2270 assert
.strictEqual(messages
.length
, 0);
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);
2279 it("should not report a violation", () => {
2281 " alert('test1');/*eslint-disable */\n",
2283 " alert('test');\n",
2284 "/*eslint-enable */alert('test2');"
2286 const config
= { rules
: { "no-alert": 1 } };
2288 const messages
= linter
.verify(code
, config
, filename
);
2289 const suppressedMessages
= linter
.getSuppressedMessages();
2291 assert
.strictEqual(messages
.length
, 2);
2292 assert
.strictEqual(messages
[0].column
, 21);
2293 assert
.strictEqual(messages
[1].column
, 19);
2295 assert
.strictEqual(suppressedMessages
.length
, 2);
2296 assert
.strictEqual(suppressedMessages
[0].column
, 1);
2297 assert
.strictEqual(suppressedMessages
[1].column
, 56);
2300 it("should report a violation", () => {
2303 "/*eslint-disable */",
2305 "/*eslint-disable */",
2307 "/*eslint-enable*/",
2312 const config
= { rules
: { "no-alert": 1 } };
2314 const messages
= linter
.verify(code
, config
, filename
);
2315 const suppressedMessages
= linter
.getSuppressedMessages();
2317 assert
.strictEqual(messages
.length
, 1);
2318 assert
.strictEqual(suppressedMessages
.length
, 2);
2322 it("should not report a violation", () => {
2324 "/*eslint-disable */",
2325 "(function(){ var b = 44;})()",
2326 "/*eslint-enable */;any();"
2329 const config
= { rules
: { "no-unused-vars": 1 } };
2331 const messages
= linter
.verify(code
, config
, filename
);
2332 const suppressedMessages
= linter
.getSuppressedMessages();
2334 assert
.strictEqual(messages
.length
, 0);
2335 assert
.strictEqual(suppressedMessages
.length
, 1);
2338 it("should not report a violation", () => {
2340 "(function(){ /*eslint-disable */ var b = 44;})()",
2341 "/*eslint-enable */;any();"
2344 const config
= { rules
: { "no-unused-vars": 1 } };
2346 const messages
= linter
.verify(code
, config
, filename
);
2347 const suppressedMessages
= linter
.getSuppressedMessages();
2349 assert
.strictEqual(messages
.length
, 0);
2350 assert
.strictEqual(suppressedMessages
.length
, 1);
2354 describe("when evaluating code with comments to ignore reporting on specific rules on a specific line", () => {
2356 describe("eslint-disable-line", () => {
2357 it("should report a violation", () => {
2359 "alert('test'); // eslint-disable-line no-alert",
2360 "console.log('test');" // here
2369 const messages
= linter
.verify(code
, config
, filename
);
2370 const suppressedMessages
= linter
.getSuppressedMessages();
2372 assert
.strictEqual(messages
.length
, 1);
2373 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2375 assert
.strictEqual(suppressedMessages
.length
, 1);
2376 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2379 it("should report a violation", () => {
2381 "alert('test'); // eslint-disable-line no-alert",
2382 "console.log('test'); // eslint-disable-line no-console",
2383 "alert('test');" // here
2392 const messages
= linter
.verify(code
, config
, filename
);
2393 const suppressedMessages
= linter
.getSuppressedMessages();
2395 assert
.strictEqual(messages
.length
, 1);
2396 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2398 assert
.strictEqual(suppressedMessages
.length
, 2);
2399 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2400 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
2403 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
2405 "/* eslint-disable-line",
2407 "*/ console.log('test');" // here
2415 const messages
= linter
.verify(code
, config
, filename
);
2416 const suppressedMessages
= linter
.getSuppressedMessages();
2418 assert
.strictEqual(messages
.length
, 2);
2419 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2420 assert
.strictEqual(suppressedMessages
.length
, 0);
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", () => {
2425 "alert('test'); /* eslint-disable-line ",
2434 const messages
= linter
.verify(code
, config
);
2435 const suppressedMessages
= linter
.getSuppressedMessages();
2437 assert
.deepStrictEqual(messages
, [
2445 message
: "Unexpected alert.",
2446 messageId
: "unexpected",
2447 nodeType
: "CallExpression"
2452 message
: "eslint-disable-line comment should not span multiple lines.",
2461 assert
.strictEqual(suppressedMessages
.length
, 0);
2464 it("should not report a violation for eslint-disable-line in block comment", () => {
2466 "alert('test'); // eslint-disable-line no-alert",
2467 "alert('test'); /*eslint-disable-line no-alert*/"
2475 const messages
= linter
.verify(code
, config
, filename
);
2476 const suppressedMessages
= linter
.getSuppressedMessages();
2478 assert
.strictEqual(messages
.length
, 0);
2480 assert
.strictEqual(suppressedMessages
.length
, 2);
2481 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2482 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-alert");
2485 it("should not report a violation", () => {
2487 "alert('test'); // eslint-disable-line no-alert",
2488 "console.log('test'); // eslint-disable-line no-console"
2497 const messages
= linter
.verify(code
, config
, filename
);
2498 const suppressedMessages
= linter
.getSuppressedMessages();
2500 assert
.strictEqual(messages
.length
, 0);
2502 assert
.strictEqual(suppressedMessages
.length
, 2);
2503 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2504 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
2507 it("should not report a violation", () => {
2509 "alert('test') // eslint-disable-line no-alert, quotes, semi",
2510 "console.log('test'); // eslint-disable-line"
2515 quotes
: [1, "double"],
2516 semi
: [1, "always"],
2521 const messages
= linter
.verify(code
, config
, filename
);
2522 const suppressedMessages
= linter
.getSuppressedMessages();
2524 assert
.strictEqual(messages
.length
, 0);
2525 assert
.strictEqual(suppressedMessages
.length
, 5);
2528 it("should not report a violation", () => {
2530 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
2531 "console.log('test'); /* eslint-disable-line */"
2536 quotes
: [1, "double"],
2537 semi
: [1, "always"],
2542 const messages
= linter
.verify(code
, config
, filename
);
2543 const suppressedMessages
= linter
.getSuppressedMessages();
2545 assert
.strictEqual(messages
.length
, 0);
2546 assert
.strictEqual(suppressedMessages
.length
, 5);
2549 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2551 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
2556 quotes
: [1, "single"]
2559 const messages
= linter
.verify(code
, config
, filename
);
2560 const suppressedMessages
= linter
.getSuppressedMessages();
2562 assert
.strictEqual(messages
.length
, 0);
2564 assert
.strictEqual(suppressedMessages
.length
, 2);
2565 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2569 describe("eslint-disable-next-line", () => {
2570 it("should ignore violation of specified rule on next line", () => {
2572 "// eslint-disable-next-line no-alert",
2574 "console.log('test');"
2582 const messages
= linter
.verify(code
, config
, filename
);
2583 const suppressedMessages
= linter
.getSuppressedMessages();
2585 assert
.strictEqual(messages
.length
, 1);
2586 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2588 assert
.strictEqual(suppressedMessages
.length
, 1);
2589 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2592 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2594 "/* eslint-disable-next-line no-alert */",
2596 "console.log('test');"
2604 const messages
= linter
.verify(code
, config
, filename
);
2605 const suppressedMessages
= linter
.getSuppressedMessages();
2607 assert
.strictEqual(messages
.length
, 1);
2608 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2610 assert
.strictEqual(suppressedMessages
.length
, 1);
2611 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2613 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2615 "/* eslint-disable-next-line no-alert */",
2623 const messages
= linter
.verify(code
, config
, filename
);
2624 const suppressedMessages
= linter
.getSuppressedMessages();
2626 assert
.strictEqual(messages
.length
, 0);
2628 assert
.strictEqual(suppressedMessages
.length
, 1);
2629 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2632 it("should not ignore violation if code is not on next line", () => {
2634 "/* eslint-disable-next-line",
2635 "no-alert */alert('test');"
2642 const messages
= linter
.verify(code
, config
, filename
);
2643 const suppressedMessages
= linter
.getSuppressedMessages();
2645 assert
.strictEqual(messages
.length
, 1);
2646 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2648 assert
.strictEqual(suppressedMessages
.length
, 0);
2651 it("should ignore violation if block comment span multiple lines", () => {
2653 "/* eslint-disable-next-line",
2662 const messages
= linter
.verify(code
, config
, filename
);
2663 const suppressedMessages
= linter
.getSuppressedMessages();
2665 assert
.strictEqual(messages
.length
, 0);
2667 assert
.strictEqual(suppressedMessages
.length
, 1);
2668 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2671 it("should ignore violations only of specified rule", () => {
2673 "// eslint-disable-next-line no-console",
2675 "console.log('test');"
2683 const messages
= linter
.verify(code
, config
, filename
);
2684 const suppressedMessages
= linter
.getSuppressedMessages();
2686 assert
.strictEqual(messages
.length
, 2);
2687 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2688 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2690 assert
.strictEqual(suppressedMessages
.length
, 0);
2693 it("should ignore violations of multiple rules when specified", () => {
2695 "// eslint-disable-next-line no-alert, quotes",
2697 "console.log('test');"
2702 quotes
: [1, "single"],
2706 const messages
= linter
.verify(code
, config
, filename
);
2707 const suppressedMessages
= linter
.getSuppressedMessages();
2709 assert
.strictEqual(messages
.length
, 1);
2710 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2712 assert
.strictEqual(suppressedMessages
.length
, 2);
2713 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2714 assert
.strictEqual(suppressedMessages
[1].ruleId
, "quotes");
2717 it("should ignore violations of multiple rules when specified in multiple lines", () => {
2719 "/* eslint-disable-next-line",
2724 "console.log('test');"
2729 quotes
: [1, "single"],
2733 const messages
= linter
.verify(code
, config
, filename
);
2735 assert
.strictEqual(messages
.length
, 1);
2736 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2739 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2741 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
2747 quotes
: [1, "single"]
2750 const messages
= linter
.verify(code
, config
, filename
);
2751 const suppressedMessages
= linter
.getSuppressedMessages();
2753 assert
.strictEqual(messages
.length
, 0);
2755 assert
.strictEqual(suppressedMessages
.length
, 2);
2756 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2757 assert
.strictEqual(suppressedMessages
[1].ruleId
, "quotes");
2760 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
2762 "/* eslint-disable-next-line",
2764 "*/ // eslint-disable-next-line quotes",
2770 quotes
: [1, "single"]
2773 const messages
= linter
.verify(code
, config
, filename
);
2775 assert
.strictEqual(messages
.length
, 0);
2778 it("should ignore violations of only the specified rule on next line", () => {
2780 "// eslint-disable-next-line quotes",
2782 "console.log('test');"
2787 quotes
: [1, "single"],
2791 const messages
= linter
.verify(code
, config
, filename
);
2792 const suppressedMessages
= linter
.getSuppressedMessages();
2794 assert
.strictEqual(messages
.length
, 2);
2795 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2796 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2798 assert
.strictEqual(suppressedMessages
.length
, 1);
2799 assert
.strictEqual(suppressedMessages
[0].ruleId
, "quotes");
2802 it("should ignore violations of specified rule on next line only", () => {
2805 "// eslint-disable-next-line no-alert",
2807 "console.log('test');"
2815 const messages
= linter
.verify(code
, config
, filename
);
2816 const suppressedMessages
= linter
.getSuppressedMessages();
2818 assert
.strictEqual(messages
.length
, 2);
2819 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2820 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2822 assert
.strictEqual(suppressedMessages
.length
, 1);
2823 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2826 it("should ignore all rule violations on next line if none specified", () => {
2828 "// eslint-disable-next-line",
2830 "console.log('test')"
2835 quotes
: [1, "single"],
2840 const messages
= linter
.verify(code
, config
, filename
);
2841 const suppressedMessages
= linter
.getSuppressedMessages();
2843 assert
.strictEqual(messages
.length
, 1);
2844 assert
.strictEqual(messages
[0].ruleId
, "no-console");
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");
2852 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
2855 "/* eslint-disable-next-line no-alert */",
2857 "console.log('test');"
2865 const messages
= linter
.verify(code
, config
, filename
);
2866 const suppressedMessages
= linter
.getSuppressedMessages();
2868 assert
.strictEqual(messages
.length
, 2);
2869 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2870 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2872 assert
.strictEqual(suppressedMessages
.length
, 1);
2873 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2876 it("should report a violation", () => {
2878 "/* eslint-disable-next-line",
2881 "console.log('test');" // here
2890 const messages
= linter
.verify(code
, config
, filename
);
2891 const suppressedMessages
= linter
.getSuppressedMessages();
2893 assert
.strictEqual(messages
.length
, 2);
2894 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2896 assert
.strictEqual(suppressedMessages
.length
, 0);
2899 it("should not ignore violations if comment is of the type hashbang", () => {
2901 "#! eslint-disable-next-line no-alert",
2903 "console.log('test');"
2911 const messages
= linter
.verify(code
, config
, filename
);
2912 const suppressedMessages
= linter
.getSuppressedMessages();
2914 assert
.strictEqual(messages
.length
, 2);
2915 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2916 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2918 assert
.strictEqual(suppressedMessages
.length
, 0);
2923 describe("when evaluating code with comments to enable and disable reporting of specific rules", () => {
2925 it("should report a violation", () => {
2927 "/*eslint-disable no-alert */",
2929 "console.log('test');" // here
2931 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2933 const messages
= linter
.verify(code
, config
, filename
);
2934 const suppressedMessages
= linter
.getSuppressedMessages();
2936 assert
.strictEqual(messages
.length
, 1);
2937 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2939 assert
.strictEqual(suppressedMessages
.length
, 1);
2940 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2943 it("should report no violation", () => {
2945 "/*eslint-disable no-unused-vars */",
2946 "var foo; // eslint-disable-line no-unused-vars",
2948 "/* eslint-enable no-unused-vars */" // here
2950 const config
= { rules
: { "no-unused-vars": 2 } };
2952 const messages
= linter
.verify(code
, config
, filename
);
2953 const suppressedMessages
= linter
.getSuppressedMessages();
2955 assert
.strictEqual(messages
.length
, 0);
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);
2964 it("should report no violation", () => {
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"
2972 const config
= { rules
: { "no-unused-vars": 2 } };
2974 const messages
= linter
.verify(code
, config
, filename
);
2975 const suppressedMessages
= linter
.getSuppressedMessages();
2977 assert
.strictEqual(messages
.length
, 0);
2978 assert
.strictEqual(suppressedMessages
.length
, 5);
2981 it("should report no violation", () => {
2983 "/* eslint-disable quotes */",
2984 "console.log(\"foo\");",
2985 "/* eslint-enable quotes */"
2987 const config
= { rules
: { quotes
: 2 } };
2989 const messages
= linter
.verify(code
, config
, filename
);
2990 const suppressedMessages
= linter
.getSuppressedMessages();
2992 assert
.strictEqual(messages
.length
, 0);
2993 assert
.strictEqual(suppressedMessages
.length
, 0);
2996 it("should report a violation", () => {
2998 "/*eslint-disable no-alert, no-console */",
3000 "console.log('test');",
3001 "/*eslint-enable*/",
3003 "alert('test');", // here
3004 "console.log('test');" // here
3006 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
3008 const messages
= linter
.verify(code
, config
, filename
);
3009 const suppressedMessages
= linter
.getSuppressedMessages();
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);
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);
3024 it("should report a violation", () => {
3026 "/*eslint-disable no-alert */",
3028 "console.log('test');",
3029 "/*eslint-enable no-console */",
3031 "alert('test');" // here
3033 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
3035 const messages
= linter
.verify(code
, config
, filename
);
3036 const suppressedMessages
= linter
.getSuppressedMessages();
3038 assert
.strictEqual(messages
.length
, 1);
3039 assert
.strictEqual(messages
[0].ruleId
, "no-console");
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);
3049 it("should report a violation", () => {
3051 "/*eslint-disable no-alert, no-console */",
3053 "console.log('test');",
3054 "/*eslint-enable no-alert*/",
3056 "alert('test');", // here
3057 "console.log('test');"
3059 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
3061 const messages
= linter
.verify(code
, config
, filename
);
3062 const suppressedMessages
= linter
.getSuppressedMessages();
3064 assert
.strictEqual(messages
.length
, 1);
3065 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3066 assert
.strictEqual(messages
[0].line
, 5);
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);
3078 it("should report a violation", () => {
3080 "/*eslint-disable no-alert */",
3082 "/*eslint-disable no-console */",
3084 "console.log('test');",
3085 "/*eslint-enable */",
3087 "alert('test');", // here
3088 "console.log('test');", // here
3090 "/*eslint-enable */",
3092 "alert('test');", // here
3093 "console.log('test');", // here
3097 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
3099 const messages
= linter
.verify(code
, config
, filename
);
3100 const suppressedMessages
= linter
.getSuppressedMessages();
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);
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);
3119 it("should report a violation", () => {
3121 "/*eslint-disable no-alert, no-console */",
3123 "console.log('test');",
3125 "/*eslint-enable no-alert */",
3127 "alert('test');", // here
3128 "console.log('test');",
3130 "/*eslint-enable no-console */",
3132 "alert('test');", // here
3133 "console.log('test');", // here
3134 "/*eslint-enable no-console */"
3136 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
3138 const messages
= linter
.verify(code
, config
, filename
);
3139 const suppressedMessages
= linter
.getSuppressedMessages();
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);
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);
3158 it("should report a violation when severity is warn", () => {
3160 "/*eslint-disable no-alert, no-console */",
3162 "console.log('test');",
3164 "/*eslint-enable no-alert */",
3166 "alert('test');", // here
3167 "console.log('test');",
3169 "/*eslint-enable no-console */",
3171 "alert('test');", // here
3172 "console.log('test');", // here
3173 "/*eslint-enable no-console */"
3175 const config
= { rules
: { "no-alert": "warn", "no-console": "warn" } };
3177 const messages
= linter
.verify(code
, config
, filename
);
3178 const suppressedMessages
= linter
.getSuppressedMessages();
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);
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);
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');";
3201 it("should report a violation", () => {
3202 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
3204 const messages
= linter
.verify(code
, config
, filename
);
3205 const suppressedMessages
= linter
.getSuppressedMessages();
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");
3212 assert
.strictEqual(suppressedMessages
.length
, 0);
3216 describe("when evaluating code with comments to enable configurable rule", () => {
3217 const code
= "/*eslint quotes:[2, \"double\"]*/ alert('test');";
3219 it("should report a violation", () => {
3220 const config
= { rules
: { quotes
: [2, "single"] } };
3222 const messages
= linter
.verify(code
, config
, filename
);
3223 const suppressedMessages
= linter
.getSuppressedMessages();
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");
3230 assert
.strictEqual(suppressedMessages
.length
, 0);
3234 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
3235 const code
= "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
3237 it("should report a violation", () => {
3238 const config
= { rules
: { quotes
: [2, "single"] } };
3240 const messages
= linter
.verify(code
, config
, filename
);
3241 const suppressedMessages
= linter
.getSuppressedMessages();
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");
3248 assert
.strictEqual(suppressedMessages
.length
, 0);
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');";
3256 const config
= { rules
: { "no-alert": 1 } };
3258 const messages
= linter
.verify(code
, config
, filename
);
3259 const suppressedMessages
= linter
.getSuppressedMessages();
3261 assert
.strictEqual(messages
.length
, 2);
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
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);
3274 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3275 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
3276 assert
.include(messages
[1].nodeType
, "CallExpression");
3278 assert
.strictEqual(suppressedMessages
.length
, 0);
3281 it("should report a violation", () => {
3282 const code
= "/*eslint no-alert:abc*/ alert('test');";
3284 const config
= { rules
: { "no-alert": 1 } };
3286 const messages
= linter
.verify(code
, config
, filename
);
3287 const suppressedMessages
= linter
.getSuppressedMessages();
3289 assert
.strictEqual(messages
.length
, 2);
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
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);
3302 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3303 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
3304 assert
.include(messages
[1].nodeType
, "CallExpression");
3306 assert
.strictEqual(suppressedMessages
.length
, 0);
3309 it("should report a violation", () => {
3310 const code
= "/*eslint no-alert:0 2*/ alert('test');";
3312 const config
= { rules
: { "no-alert": 1 } };
3314 const messages
= linter
.verify(code
, config
, filename
);
3315 const suppressedMessages
= linter
.getSuppressedMessages();
3317 assert
.strictEqual(messages
.length
, 2);
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
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);
3330 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3331 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
3332 assert
.include(messages
[1].nodeType
, "CallExpression");
3334 assert
.strictEqual(suppressedMessages
.length
, 0);
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|-\\*-"}] */
3344 it("should not parse errors, should report a violation", () => {
3345 const messages
= linter
.verify(code
, {}, filename
);
3346 const suppressedMessages
= linter
.getSuppressedMessages();
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");
3353 assert
.strictEqual(suppressedMessages
.length
, 0);
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");
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
;
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");
3383 assert
.strictEqual(suppressedMessages
.length
, 0);
3387 describe("when evaluating a file with a hashbang", () => {
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();
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);
3400 assert
.strictEqual(suppressedMessages
.length
, 0);
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();
3409 assert
.strictEqual(comments
.length
, 1);
3410 assert
.strictEqual(comments
[0].type
, "Shebang");
3414 linter
.defineRule("checker", { create
: spy
});
3415 linter
.verify(code
, config
);
3416 assert(spy
.calledOnce
);
3419 it("should comment hashbang without breaking offset", () => {
3420 const code
= "#!/usr/bin/env node\n'123';";
3421 const config
= { rules
: { checker
: "error" } };
3424 linter
.defineRule("checker", {
3426 spy
= sinon
.spy(node
=> {
3427 assert
.strictEqual(context
.getSource(node
), "'123';");
3429 return { ExpressionStatement
: spy
};
3433 linter
.verify(code
, config
);
3434 assert(spy
&& spy
.calledOnce
);
3439 describe("when evaluating broken code", () => {
3440 const code
= BROKEN_TEST_CODE
;
3442 it("should report a violation with a useful parse error prefix", () => {
3443 const messages
= linter
.verify(code
);
3444 const suppressedMessages
= linter
.getSuppressedMessages();
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);
3454 assert
.strictEqual(suppressedMessages
.length
, 0);
3457 it("should report source code where the issue is present", () => {
3458 const inValidCode
= [
3464 const messages
= linter
.verify(inValidCode
.join("\n"));
3465 const suppressedMessages
= linter
.getSuppressedMessages();
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);
3472 assert
.strictEqual(suppressedMessages
.length
, 0);
3476 describe("when using an invalid (undefined) rule", () => {
3477 linter
= new Linter();
3479 const code
= TEST_CODE
;
3480 let results
, result
, warningResult
, arrayOptionResults
, objectOptionResults
, resultsMultiple
;
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 } });
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");
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.");
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
3510 it("should accept any valid rule configuration", () => {
3511 assert
.isObject(arrayOptionResults
[0]);
3512 assert
.isObject(objectOptionResults
[0]);
3515 it("should report multiple missing rules", () => {
3516 assert
.isArray(resultsMultiple
);
3518 assert
.deepStrictEqual(
3522 message
: "Definition for rule 'barfoo' was not found.",
3534 describe("when using a rule which has been replaced", () => {
3535 const code
= TEST_CODE
;
3537 it("should report the new rule", () => {
3538 const results
= linter
.verify(code
, { rules
: { "no-comma-dangle": 2 } });
3539 const suppressedMessages
= linter
.getSuppressedMessages();
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");
3544 assert
.strictEqual(suppressedMessages
.length
, 0);
3548 describe("when calling getRules", () => {
3549 it("should return all loaded rules", () => {
3550 const rules
= linter
.getRules();
3552 assert
.isAbove(rules
.size
, 230);
3553 assert
.isObject(rules
.get("no-alert"));
3557 describe("when calling version", () => {
3558 it("should return current version number", () => {
3559 const version
= linter
.version
;
3561 assert
.isString(version
);
3562 assert
.isTrue(parseInt(version
[0], 10) >= 3);
3566 describe("when evaluating an empty string", () => {
3567 it("runs rules", () => {
3568 linter
.defineRule("no-programs", {
3569 create
: context
=> ({
3571 context
.report({ node
, message
: "No programs allowed." });
3577 linter
.verify("", { rules
: { "no-programs": "error" } }).length
,
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();";
3587 const config
= { rules
: { "no-undef": 1 } };
3589 const messages
= linter
.verify(code
, config
, filename
);
3590 const suppressedMessages
= linter
.getSuppressedMessages();
3592 assert
.strictEqual(messages
.length
, 1);
3593 assert
.strictEqual(suppressedMessages
.length
, 0);
3596 it("should report a violation when using Promise", () => {
3597 const code
= "new Promise();";
3599 const config
= { rules
: { "no-undef": 1 } };
3601 const messages
= linter
.verify(code
, config
, filename
);
3602 const suppressedMessages
= linter
.getSuppressedMessages();
3604 assert
.strictEqual(messages
.length
, 1);
3605 assert
.strictEqual(suppressedMessages
.length
, 0);
3609 describe("when evaluating code with comments to environment", () => {
3610 it("should not support legacy config", () => {
3611 const code
= "/*jshint mocha:true */ describe();";
3613 const config
= { rules
: { "no-undef": 1 } };
3615 const messages
= linter
.verify(code
, config
, filename
);
3616 const suppressedMessages
= linter
.getSuppressedMessages();
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);
3623 assert
.strictEqual(suppressedMessages
.length
, 0);
3626 it("should not report a violation", () => {
3627 const code
= "/*eslint-env es6 */ new Promise();";
3629 const config
= { rules
: { "no-undef": 1 } };
3631 const messages
= linter
.verify(code
, config
, filename
);
3632 const suppressedMessages
= linter
.getSuppressedMessages();
3634 assert
.strictEqual(messages
.length
, 0);
3635 assert
.strictEqual(suppressedMessages
.length
, 0);
3638 // https://github.com/eslint/eslint/issues/14652
3639 it("should not report a violation", () => {
3641 "/*eslint-env es6\n */ new Promise();",
3642 "/*eslint-env browser,\nes6 */ window;Promise;",
3643 "/*eslint-env\nbrowser,es6 */ window;Promise;"
3645 const config
= { rules
: { "no-undef": 1 } };
3647 for (const code
of codes
) {
3648 const messages
= linter
.verify(code
, config
, filename
);
3649 const suppressedMessages
= linter
.getSuppressedMessages();
3651 assert
.strictEqual(messages
.length
, 0);
3652 assert
.strictEqual(suppressedMessages
.length
, 0);
3657 it("should not report a violation", () => {
3658 const code
= `/*${ESLINT_ENV} mocha,node */ require();describe();`;
3660 const config
= { rules
: { "no-undef": 1 } };
3662 const messages
= linter
.verify(code
, config
, filename
);
3663 const suppressedMessages
= linter
.getSuppressedMessages();
3665 assert
.strictEqual(messages
.length
, 0);
3666 assert
.strictEqual(suppressedMessages
.length
, 0);
3669 it("should not report a violation", () => {
3670 const code
= "/*eslint-env mocha */ suite();test();";
3672 const config
= { rules
: { "no-undef": 1 } };
3674 const messages
= linter
.verify(code
, config
, filename
);
3675 const suppressedMessages
= linter
.getSuppressedMessages();
3677 assert
.strictEqual(messages
.length
, 0);
3678 assert
.strictEqual(suppressedMessages
.length
, 0);
3681 it("should not report a violation", () => {
3682 const code
= `/*${ESLINT_ENV} amd */ define();require();`;
3684 const config
= { rules
: { "no-undef": 1 } };
3686 const messages
= linter
.verify(code
, config
, filename
);
3687 const suppressedMessages
= linter
.getSuppressedMessages();
3689 assert
.strictEqual(messages
.length
, 0);
3690 assert
.strictEqual(suppressedMessages
.length
, 0);
3693 it("should not report a violation", () => {
3694 const code
= `/*${ESLINT_ENV} jasmine */ expect();spyOn();`;
3696 const config
= { rules
: { "no-undef": 1 } };
3698 const messages
= linter
.verify(code
, config
, filename
);
3699 const suppressedMessages
= linter
.getSuppressedMessages();
3701 assert
.strictEqual(messages
.length
, 0);
3702 assert
.strictEqual(suppressedMessages
.length
, 0);
3705 it("should not report a violation", () => {
3706 const code
= `/*globals require: true */ /*${ESLINT_ENV} node */ require = 1;`;
3708 const config
= { rules
: { "no-undef": 1 } };
3710 const messages
= linter
.verify(code
, config
, filename
);
3711 const suppressedMessages
= linter
.getSuppressedMessages();
3713 assert
.strictEqual(messages
.length
, 0);
3714 assert
.strictEqual(suppressedMessages
.length
, 0);
3717 it("should not report a violation", () => {
3718 const code
= `/*${ESLINT_ENV} node */ process.exit();`;
3720 const config
= { rules
: {} };
3722 const messages
= linter
.verify(code
, config
, filename
);
3723 const suppressedMessages
= linter
.getSuppressedMessages();
3725 assert
.strictEqual(messages
.length
, 0);
3726 assert
.strictEqual(suppressedMessages
.length
, 0);
3729 it("should not report a violation", () => {
3730 const code
= `/*eslint no-process-exit: 0 */ /*${ESLINT_ENV} node */ process.exit();`;
3732 const config
= { rules
: { "no-undef": 1 } };
3734 const messages
= linter
.verify(code
, config
, filename
);
3735 const suppressedMessages
= linter
.getSuppressedMessages();
3737 assert
.strictEqual(messages
.length
, 0);
3738 assert
.strictEqual(suppressedMessages
.length
, 0);
3742 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
3743 it("should report a violation for disabling rules", () => {
3745 "alert('test'); // eslint-disable-line no-alert"
3753 const messages
= linter
.verify(code
, config
, {
3755 allowInlineConfig
: false
3757 const suppressedMessages
= linter
.getSuppressedMessages();
3759 assert
.strictEqual(messages
.length
, 1);
3760 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3762 assert
.strictEqual(suppressedMessages
.length
, 0);
3765 it("should report a violation for global variable declarations", () => {
3776 linter
.defineRules({
3778 create
: context
=> ({
3780 const scope
= context
.getScope();
3781 const sourceCode
= context
.sourceCode
;
3782 const comments
= sourceCode
.getAllComments();
3784 assert
.strictEqual(context
.getSourceCode(), sourceCode
);
3785 assert
.strictEqual(1, comments
.length
);
3787 const foo
= getVariable(scope
, "foo");
3797 linter
.verify(code
, config
, { allowInlineConfig
: false });
3801 it("should report a violation for eslint-disable", () => {
3803 "/* eslint-disable */",
3812 const messages
= linter
.verify(code
, config
, {
3814 allowInlineConfig
: false
3816 const suppressedMessages
= linter
.getSuppressedMessages();
3818 assert
.strictEqual(messages
.length
, 1);
3819 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3821 assert
.strictEqual(suppressedMessages
.length
, 0);
3824 it("should not report a violation for rule changes", () => {
3826 "/*eslint no-alert:2*/",
3835 const messages
= linter
.verify(code
, config
, {
3837 allowInlineConfig
: false
3839 const suppressedMessages
= linter
.getSuppressedMessages();
3841 assert
.strictEqual(messages
.length
, 0);
3842 assert
.strictEqual(suppressedMessages
.length
, 0);
3845 it("should report a violation for disable-line", () => {
3847 "alert('test'); // eslint-disable-line"
3855 const messages
= linter
.verify(code
, config
, {
3857 allowInlineConfig
: false
3859 const suppressedMessages
= linter
.getSuppressedMessages();
3861 assert
.strictEqual(messages
.length
, 1);
3862 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3864 assert
.strictEqual(suppressedMessages
.length
, 0);
3867 it("should report a violation for env changes", () => {
3869 `/*${ESLINT_ENV} browser*/ window`
3876 const messages
= linter
.verify(code
, config
, { allowInlineConfig
: false });
3877 const suppressedMessages
= linter
.getSuppressedMessages();
3879 assert
.strictEqual(messages
.length
, 1);
3880 assert
.strictEqual(messages
[0].ruleId
, "no-undef");
3882 assert
.strictEqual(suppressedMessages
.length
, 0);
3886 describe("when evaluating code with 'noInlineComment'", () => {
3887 for (const directive
of [
3891 "eslint eqeqeq: error",
3892 "eslint-disable eqeqeq",
3893 "eslint-disable-line eqeqeq",
3894 "eslint-disable-next-line eqeqeq",
3895 "eslint-enable eqeqeq",
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();
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.`);
3909 assert
.strictEqual(suppressedMessages
.length
, 0);
3913 for (const directive
of [
3914 "eslint-disable-line eqeqeq",
3915 "eslint-disable-next-line eqeqeq"
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();
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.`);
3928 assert
.strictEqual(suppressedMessages
.length
, 0);
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();
3936 assert
.deepStrictEqual(messages
.length
, 0);
3937 assert
.strictEqual(suppressedMessages
.length
, 0);
3941 describe("when receiving cwd in options during instantiation", () => {
3942 const code
= "a;\nb;";
3943 const config
= { rules
: { checker
: "error" } };
3945 it("should get cwd correctly in the context", () => {
3947 const linterWithOption
= new Linter({ cwd
});
3950 linterWithOption
.defineRule("checker", {
3952 spy
= sinon
.spy(() => {
3953 assert
.strictEqual(context
.getCwd(), context
.cwd
);
3954 assert
.strictEqual(context
.cwd
, cwd
);
3956 return { Program
: spy
};
3960 linterWithOption
.verify(code
, config
);
3961 assert(spy
&& spy
.calledOnce
);
3964 it("should assign process.cwd() to it if cwd is undefined", () => {
3966 const linterWithOption
= new Linter({ });
3968 linterWithOption
.defineRule("checker", {
3971 spy
= sinon
.spy(() => {
3972 assert
.strictEqual(context
.getCwd(), context
.cwd
);
3973 assert
.strictEqual(context
.cwd
, process
.cwd());
3975 return { Program
: spy
};
3979 linterWithOption
.verify(code
, config
);
3980 assert(spy
&& spy
.calledOnce
);
3983 it("should assign process.cwd() to it if the option is undefined", () => {
3986 linter
.defineRule("checker", {
3989 spy
= sinon
.spy(() => {
3990 assert
.strictEqual(context
.getCwd(), context
.cwd
);
3991 assert
.strictEqual(context
.cwd
, process
.cwd());
3993 return { Program
: spy
};
3997 linter
.verify(code
, config
);
3998 assert(spy
&& spy
.calledOnce
);
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();
4007 assert
.deepStrictEqual(
4012 message
: "Unused eslint-disable directive (no problems were reported).",
4025 assert
.strictEqual(suppressedMessages
.length
, 0);
4028 it("reports problems for multiple eslint-disable comments, including unused ones", () => {
4030 "/* eslint-disable no-alert -- j1 */",
4031 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
4038 const messages
= linter
.verify(code
, config
, { reportUnusedDisableDirectives
: true });
4039 const suppressedMessages
= linter
.getSuppressedMessages();
4041 assert
.strictEqual(messages
.length
, 1);
4042 assert
.strictEqual(suppressedMessages
.length
, 1);
4043 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 2);
4046 it("reports problems for eslint-disable-line and eslint-disable-next-line comments, including unused ones", () => {
4048 "// eslint-disable-next-line no-alert -- j1 */",
4049 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
4056 const messages
= linter
.verify(code
, config
, { reportUnusedDisableDirectives
: true });
4057 const suppressedMessages
= linter
.getSuppressedMessages();
4059 assert
.strictEqual(messages
.length
, 1);
4060 assert
.strictEqual(suppressedMessages
.length
, 1);
4061 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 2);
4064 it("reports problems for multiple unused eslint-disable comments with multiple ruleIds", () => {
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"
4076 const messages
= linter
.verify(code
, config
, { reportUnusedDisableDirectives
: true });
4077 const suppressedMessages
= linter
.getSuppressedMessages();
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);
4087 it("reports problems for unused eslint-disable comments (error)", () => {
4088 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "error" });
4089 const suppressedMessages
= linter
.getSuppressedMessages();
4091 assert
.deepStrictEqual(
4096 message
: "Unused eslint-disable directive (no problems were reported).",
4109 assert
.strictEqual(suppressedMessages
.length
, 0);
4112 it("reports problems for unused eslint-disable comments (warn)", () => {
4113 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "warn" });
4114 const suppressedMessages
= linter
.getSuppressedMessages();
4116 assert
.deepStrictEqual(
4121 message
: "Unused eslint-disable directive (no problems were reported).",
4134 assert
.strictEqual(suppressedMessages
.length
, 0);
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();
4141 assert
.deepStrictEqual(
4146 message
: "Unused eslint-disable directive (no problems were reported).",
4159 assert
.strictEqual(suppressedMessages
.length
, 0);
4162 it("reports problems for partially unused eslint-disable comments (in config)", () => {
4163 const code
= "alert('test'); // eslint-disable-line no-alert, no-redeclare";
4165 reportUnusedDisableDirectives
: true,
4172 const messages
= linter
.verify(code
, config
, {
4174 allowInlineConfig
: true
4176 const suppressedMessages
= linter
.getSuppressedMessages();
4178 assert
.deepStrictEqual(
4183 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
4196 assert
.strictEqual(suppressedMessages
.length
, 1);
4197 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
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: }";
4203 reportUnusedDisableDirectives
: true,
4209 const messages
= linter
.verify(code
, config
, {
4211 allowInlineConfig
: true
4213 const suppressedMessages
= linter
.getSuppressedMessages();
4215 assert
.strictEqual(messages
.length
, 0);
4217 assert
.strictEqual(suppressedMessages
.length
, 1);
4218 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-fallthrough");
4221 describe("autofix", () => {
4222 const alwaysReportsRule
= {
4226 context
.report({ message
: "bad code", loc
: node
.loc
.end
});
4232 const neverReportsRule
= {
4238 const ruleCount
= 3;
4239 const usedRules
= Array
.from(
4240 { length
: ruleCount
},
4241 (_
, index
) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
4243 const unusedRules = usedRules.map(name => `un${name}
`); // "unused", "unused-1", "unused-2"
4246 reportUnusedDisableDirectives: true,
4248 ...Object.fromEntries(usedRules.map(name => [name, "error"])),
4249 ...Object.fromEntries(unusedRules.map(name => [name, "error"]))
4254 linter.defineRules(Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])));
4255 linter.defineRules(Object.fromEntries(unusedRules.map(name => [name, neverReportsRule])));
4260 //-----------------------------------------------
4261 // Removing the entire comment
4262 //-----------------------------------------------
4265 code: "// eslint-disable-line unused",
4269 code: "foo// eslint-disable-line unused",
4273 code: "// eslint-disable-line ,unused,",
4277 code: "// eslint-disable-line unused-1, unused-2",
4281 code: "// eslint-disable-line ,unused-1,, unused-2,, -- comment",
4285 code: "// eslint-disable-next-line unused\n",
4289 code: "// eslint-disable-next-line unused\nfoo",
4293 code: "/* eslint-disable \nunused\n*/",
4297 //-----------------------------------------------
4298 // Removing only individual rules
4299 //-----------------------------------------------
4301 // content before the first rule should not be changed
4303 code: "//eslint-disable-line unused, used",
4304 output: "//eslint-disable-line used"
4307 code: "// eslint-disable-line unused, used",
4308 output: "// eslint-disable-line used"
4311 code: "// eslint-disable-line unused, used",
4312 output: "// eslint-disable-line used"
4315 code: "/*\neslint-disable unused, used*/",
4316 output: "/*\neslint-disable used*/"
4319 code: "/*\n eslint-disable unused, used*/",
4320 output: "/*\n eslint-disable used*/"
4323 code: "/*\r\neslint-disable unused, used*/",
4324 output: "/*\r\neslint-disable used*/"
4327 code: "/*\u2028eslint-disable unused, used*/",
4328 output: "/*\u2028eslint-disable used*/"
4331 code: "/*\u00A0eslint-disable unused, used*/",
4332 output: "/*\u00A0eslint-disable used*/"
4335 code: "// eslint-disable-line unused, used",
4336 output: "// eslint-disable-line used"
4339 code: "/* eslint-disable\nunused, used*/",
4340 output: "/* eslint-disable\nused*/"
4343 code: "/* eslint-disable\n unused, used*/",
4344 output: "/* eslint-disable\n used*/"
4347 code: "/* eslint-disable\r\nunused, used*/",
4348 output: "/* eslint-disable\r\nused*/"
4351 code: "/* eslint-disable\u2028unused, used*/",
4352 output: "/* eslint-disable\u2028used*/"
4355 code: "/* eslint-disable\u00A0unused, used*/",
4356 output: "/* eslint-disable\u00A0used*/"
4359 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
4361 code: "// eslint-disable-line unused,used",
4362 output: "// eslint-disable-line used"
4365 code: "// eslint-disable-line unused, used",
4366 output: "// eslint-disable-line used"
4369 code: "// eslint-disable-line unused , used",
4370 output: "// eslint-disable-line used"
4373 code: "// eslint-disable-line unused, used",
4374 output: "// eslint-disable-line used"
4377 code: "// eslint-disable-line unused ,used",
4378 output: "// eslint-disable-line used"
4381 code: "/* eslint-disable unused\n,\nused */",
4382 output: "/* eslint-disable used */"
4385 code: "/* eslint-disable unused \n \n,\n\n used */",
4386 output: "/* eslint-disable used */"
4389 code: "/* eslint-disable unused\u2028,\u2028used */",
4390 output: "/* eslint-disable used */"
4393 code: "// eslint-disable-line unused\u00A0,\u00A0used",
4394 output: "// eslint-disable-line used"
4397 code: "// eslint-disable-line unused,,used",
4398 output: "// eslint-disable-line ,used"
4401 code: "// eslint-disable-line unused, ,used",
4402 output: "// eslint-disable-line ,used"
4405 code: "// eslint-disable-line unused,, used",
4406 output: "// eslint-disable-line , used"
4409 code: "// eslint-disable-line unused,used ",
4410 output: "// eslint-disable-line used "
4413 code: "// eslint-disable-next-line unused,used\n",
4414 output: "// eslint-disable-next-line used\n"
4417 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
4419 code: "// eslint-disable-line used-1,unused,used-2",
4420 output: "// eslint-disable-line used-1,used-2"
4423 code: "// eslint-disable-line used-1, unused,used-2",
4424 output: "// eslint-disable-line used-1,used-2"
4427 code: "// eslint-disable-line used-1,unused ,used-2",
4428 output: "// eslint-disable-line used-1,used-2"
4431 code: "// eslint-disable-line used-1, unused ,used-2",
4432 output: "// eslint-disable-line used-1,used-2"
4435 code: "/* eslint-disable used-1,\nunused\n,used-2 */",
4436 output: "/* eslint-disable used-1,used-2 */"
4439 code: "/* eslint-disable used-1,\n\n unused \n \n ,used-2 */",
4440 output: "/* eslint-disable used-1,used-2 */"
4443 code: "/* eslint-disable used-1,\u2028unused\u2028,used-2 */",
4444 output: "/* eslint-disable used-1,used-2 */"
4447 code: "// eslint-disable-line used-1,\u00A0unused\u00A0,used-2",
4448 output: "// eslint-disable-line used-1,used-2"
4451 // when removing a rule in the middle, content around commas should not be changed
4453 code: "// eslint-disable-line used-1, unused ,used-2",
4454 output: "// eslint-disable-line used-1,used-2"
4457 code: "// eslint-disable-line used-1,unused, used-2",
4458 output: "// eslint-disable-line used-1, used-2"
4461 code: "// eslint-disable-line used-1 ,unused,used-2",
4462 output: "// eslint-disable-line used-1 ,used-2"
4465 code: "// eslint-disable-line used-1 ,unused, used-2",
4466 output: "// eslint-disable-line used-1 , used-2"
4469 code: "// eslint-disable-line used-1 , unused , used-2",
4470 output: "// eslint-disable-line used-1 , used-2"
4473 code: "/* eslint-disable used-1\n,unused,\nused-2 */",
4474 output: "/* eslint-disable used-1\n,\nused-2 */"
4477 code: "/* eslint-disable used-1\u2028,unused,\u2028used-2 */",
4478 output: "/* eslint-disable used-1\u2028,\u2028used-2 */"
4481 code: "// eslint-disable-line used-1\u00A0,unused,\u00A0used-2",
4482 output: "// eslint-disable-line used-1\u00A0,\u00A0used-2"
4485 code: "// eslint-disable-line , unused ,used",
4486 output: "// eslint-disable-line ,used"
4489 code: "/* eslint-disable\n, unused ,used */",
4490 output: "/* eslint-disable\n,used */"
4493 code: "/* eslint-disable used-1,\n,unused,used-2 */",
4494 output: "/* eslint-disable used-1,\n,used-2 */"
4497 code: "/* eslint-disable used-1,unused,\n,used-2 */",
4498 output: "/* eslint-disable used-1,\n,used-2 */"
4501 code: "/* eslint-disable used-1,\n,unused,\n,used-2 */",
4502 output: "/* eslint-disable used-1,\n,\n,used-2 */"
4505 code: "// eslint-disable-line used, unused,",
4506 output: "// eslint-disable-line used,"
4509 code: "// eslint-disable-next-line used, unused,\n",
4510 output: "// eslint-disable-next-line used,\n"
4513 code: "// eslint-disable-line used, unused, ",
4514 output: "// eslint-disable-line used, "
4517 code: "// eslint-disable-line used, unused, -- comment",
4518 output: "// eslint-disable-line used, -- comment"
4521 code: "/* eslint-disable used, unused,\n*/",
4522 output: "/* eslint-disable used,\n*/"
4525 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
4527 code: "// eslint-disable-line used,unused",
4528 output: "// eslint-disable-line used"
4531 code: "// eslint-disable-line used, unused",
4532 output: "// eslint-disable-line used"
4535 code: "// eslint-disable-line used ,unused",
4536 output: "// eslint-disable-line used"
4539 code: "// eslint-disable-line used , unused",
4540 output: "// eslint-disable-line used"
4543 code: "// eslint-disable-line used, unused",
4544 output: "// eslint-disable-line used"
4547 code: "// eslint-disable-line used ,unused",
4548 output: "// eslint-disable-line used"
4551 code: "/* eslint-disable used\n,\nunused */",
4552 output: "/* eslint-disable used */"
4555 code: "/* eslint-disable used \n \n,\n\n unused */",
4556 output: "/* eslint-disable used */"
4559 code: "/* eslint-disable used\u2028,\u2028unused */",
4560 output: "/* eslint-disable used */"
4563 code: "// eslint-disable-line used\u00A0,\u00A0unused",
4564 output: "// eslint-disable-line used"
4567 code: "// eslint-disable-line used,,unused",
4568 output: "// eslint-disable-line used,"
4571 code: "// eslint-disable-line used, ,unused",
4572 output: "// eslint-disable-line used,"
4575 code: "/* eslint-disable used,\n,unused */",
4576 output: "/* eslint-disable used, */"
4579 code: "/* eslint-disable used\n, ,unused */",
4580 output: "/* eslint-disable used\n, */"
4583 // content after the last rule should not be changed
4585 code: "// eslint-disable-line used,unused",
4586 output: "// eslint-disable-line used"
4589 code: "// eslint-disable-line used,unused ",
4590 output: "// eslint-disable-line used "
4593 code: "// eslint-disable-line used,unused ",
4594 output: "// eslint-disable-line used "
4597 code: "// eslint-disable-line used,unused -- comment",
4598 output: "// eslint-disable-line used -- comment"
4601 code: "// eslint-disable-next-line used,unused\n",
4602 output: "// eslint-disable-next-line used\n"
4605 code: "// eslint-disable-next-line used,unused \n",
4606 output: "// eslint-disable-next-line used \n"
4609 code: "/* eslint-disable used,unused\u2028*/",
4610 output: "/* eslint-disable used\u2028*/"
4613 code: "// eslint-disable-line used,unused\u00A0",
4614 output: "// eslint-disable-line used\u00A0"
4617 // multiply rules to remove
4619 code: "// eslint-disable-line used, unused-1, unused-2",
4620 output: "// eslint-disable-line used"
4623 code: "// eslint-disable-line unused-1, used, unused-2",
4624 output: "// eslint-disable-line used"
4627 code: "// eslint-disable-line unused-1, unused-2, used",
4628 output: "// eslint-disable-line used"
4631 code: "// eslint-disable-line used-1, unused-1, used-2, unused-2",
4632 output: "// eslint-disable-line used-1, used-2"
4635 code: "// eslint-disable-line unused-1, used-1, unused-2, used-2",
4636 output: "// eslint-disable-line used-1, used-2"
4640 /* eslint-disable unused-1,
4647 /* eslint-disable used-1,
4753 // duplicates in the list
4755 code: "// eslint-disable-line unused, unused, used",
4756 output: "// eslint-disable-line used"
4759 code: "// eslint-disable-line unused, used, unused",
4760 output: "// eslint-disable-line used"
4763 code: "// eslint-disable-line used, unused, unused, used",
4764 output: "// eslint-disable-line used, used"
4768 for (const { code, output } of tests) {
4769 // eslint-disable-next-line no-loop-func -- `linter
` is getting updated in beforeEach()
4772 linter.verifyAndFix(code, config).output,
4780 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
4781 it("should not report a violation", () => {
4783 "alert('test'); // eslint-disable-line no-alert"
4791 const messages = linter.verify(code, config, {
4793 allowInlineConfig: true
4795 const suppressedMessages = linter.getSuppressedMessages();
4797 assert.strictEqual(messages.length, 0);
4799 assert.strictEqual(suppressedMessages.length, 1);
4800 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
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" } };
4810 linter.defineRule("checker", {
4812 spy = sinon.spy(node => {
4813 assert.strictEqual(context.getSource(node), "'123';");
4815 return { ExpressionStatement: spy };
4819 linter.verify(code, config);
4820 assert(spy && spy.calledOnce);
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");
4832 linter.defineRule("checker", { create: filenameChecker });
4833 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4834 assert(filenameChecker.calledOnce);
4837 it("should allow filename to be passed as third argument", () => {
4838 const filenameChecker = sinon.spy(context => {
4839 assert.strictEqual(context.filename, "bar.js");
4843 linter.defineRule("checker", { create: filenameChecker });
4844 linter.verify("foo;", { rules: { checker: "error" } }, "bar.js");
4845 assert(filenameChecker.calledOnce);
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>");
4854 linter.defineRule("checker", { create: filenameChecker });
4855 linter.verify("foo;", { rules: { checker: "error" } }, {});
4856 assert(filenameChecker.calledOnce);
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>");
4865 linter.defineRule("checker", { create: filenameChecker });
4866 linter.verify("foo;", { rules: { checker: "error" } });
4867 assert(filenameChecker.calledOnce);
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");
4879 linter.defineRule("checker", { create: physicalFilenameChecker });
4880 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4881 assert(physicalFilenameChecker.calledOnce);
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>");
4891 linter.defineRule("checker", { create: physicalFilenameChecker });
4892 linter.verify("foo;", { rules: { checker: "error" } }, {});
4893 assert(physicalFilenameChecker.calledOnce);
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>");
4903 linter.defineRule("checker", { create: physicalFilenameChecker });
4904 linter.verify("foo;", { rules: { checker: "error" } });
4905 assert(physicalFilenameChecker.calledOnce);
4909 it("should report warnings in order by line and column when called", () => {
4911 const code = "foo()\n alert('test')";
4912 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
4914 const messages = linter.verify(code, config, filename);
4915 const suppressedMessages = linter.getSuppressedMessages();
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);
4925 assert.strictEqual(suppressedMessages.length, 0);
4928 describe("ecmaVersion", () => {
4930 it("should not support ES6 when no ecmaVersion provided", () => {
4931 const messages = linter.verify("let x = 0;");
4932 const suppressedMessages = linter.getSuppressedMessages();
4934 assert.strictEqual(messages.length, 1);
4935 assert.strictEqual(suppressedMessages.length, 0);
4938 it("supports ECMAScript version 'latest'", () => {
4939 const messages = linter.verify("let x = 5 ** 7;", {
4940 parserOptions: { ecmaVersion: "latest" }
4942 const suppressedMessages = linter.getSuppressedMessages();
4944 assert.strictEqual(messages.length, 0);
4945 assert.strictEqual(suppressedMessages.length, 0);
4948 it("the 'latest' is equal to espree.latestEcmaVersion", () => {
4949 let ecmaVersion = null;
4950 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4952 linter.defineRule("ecma-version", {
4953 create: context => ({
4955 ecmaVersion = context.parserOptions.ecmaVersion;
4959 linter.verify("", config);
4960 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion, "ecmaVersion should be 13");
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" } };
4967 linter.defineParser("custom-parser", testParsers.enhancedParser);
4968 linter.defineRule("ecma-version", {
4969 create: context => ({
4971 ecmaVersion = context.parserOptions.ecmaVersion;
4975 linter.verify("", config);
4976 assert.strictEqual(ecmaVersion, "latest", "ecmaVersion should be latest");
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" } };
4983 linter.defineRule("ecma-version", {
4984 create: context => ({
4986 ecmaVersion = context.languageOptions.ecmaVersion;
4990 linter.verify("", config);
4991 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
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" } };
4998 linter.defineParser("custom-parser", testParsers.stubParser);
4999 linter.defineRule("ecma-version", {
5000 create: context => ({
5002 ecmaVersion = context.languageOptions.ecmaVersion;
5006 linter.verify("", config);
5007 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
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" };
5014 linter.defineParser("custom-parser", testParsers.enhancedParser);
5015 linter.defineRule("ecma-version", {
5016 create: context => ({
5018 ecmaVersion = context.languageOptions.ecmaVersion;
5022 linter.verify("", config);
5023 assert.strictEqual(ecmaVersion, 5, "ecmaVersion should be 5");
5026 it("should pass normalized ecmaVersion to eslint-scope", () => {
5027 let blockScope = null;
5029 linter.defineRule("block-scope", {
5030 create: context => ({
5032 blockScope = context.getScope();
5036 linter.defineParser("custom-parser", {
5037 parse: (...args) => espree.parse(...args)
5040 // Use standard parser
5041 linter.verify("{}", {
5042 rules: { "block-scope": 2 },
5043 parserOptions: { ecmaVersion: "latest" }
5046 assert.strictEqual(blockScope.type, "block");
5048 linter.verify("{}", {
5049 rules: { "block-scope": 2 },
5050 parserOptions: {} // ecmaVersion defaults to 5
5052 assert.strictEqual(blockScope.type, "global");
5054 // Use custom parser
5055 linter.verify("{}", {
5056 rules: { "block-scope": 2 },
5057 parser: "custom-parser",
5058 parserOptions: { ecmaVersion: "latest" }
5061 assert.strictEqual(blockScope.type, "block");
5063 linter.verify("{}", {
5064 rules: { "block-scope": 2 },
5065 parser: "custom-parser",
5066 parserOptions: {} // ecmaVersion defaults to 5
5068 assert.strictEqual(blockScope.type, "global");
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;", {
5078 const suppressedMessages = linter.getSuppressedMessages();
5080 assert.strictEqual(messages.length, 0);
5081 assert.strictEqual(suppressedMessages.length, 0);
5084 it("the ECMAScript version number is 2015", () => {
5085 const messages = linter.verify("let x = 5;", {
5090 const suppressedMessages = linter.getSuppressedMessages();
5092 assert.strictEqual(messages.length, 0);
5093 assert.strictEqual(suppressedMessages.length, 0);
5097 it("should fail to parse exponentiation operator when the ECMAScript version number is 2015", () => {
5098 const messages = linter.verify("x ** y;", {
5103 const suppressedMessages = linter.getSuppressedMessages();
5105 assert.strictEqual(messages.length, 1);
5106 assert.strictEqual(suppressedMessages.length, 0);
5109 describe("should properly parse exponentiation operator when", () => {
5110 it("the ECMAScript version number is 7", () => {
5111 const messages = linter.verify("x ** y;", {
5116 const suppressedMessages = linter.getSuppressedMessages();
5118 assert.strictEqual(messages.length, 0);
5119 assert.strictEqual(suppressedMessages.length, 0);
5122 it("the ECMAScript version number is 2016", () => {
5123 const messages = linter.verify("x ** y;", {
5128 const suppressedMessages = linter.getSuppressedMessages();
5130 assert.strictEqual(messages.length, 0);
5131 assert.strictEqual(suppressedMessages.length, 0);
5136 it("should properly parse object spread when ecmaVersion is 2018", () => {
5138 const messages = linter.verify("var x = { ...y };", {
5143 const suppressedMessages = linter.getSuppressedMessages();
5145 assert.strictEqual(messages.length, 0);
5146 assert.strictEqual(suppressedMessages.length, 0);
5149 it("should properly parse global return when passed ecmaFeatures", () => {
5151 const messages = linter.verify("return;", {
5158 const suppressedMessages = linter.getSuppressedMessages();
5160 assert.strictEqual(messages.length, 0);
5161 assert.strictEqual(suppressedMessages.length, 0);
5164 it("should properly parse global return when in Node.js environment", () => {
5166 const messages = linter.verify("return;", {
5171 const suppressedMessages = linter.getSuppressedMessages();
5173 assert.strictEqual(messages.length, 0);
5174 assert.strictEqual(suppressedMessages.length, 0);
5177 it("should not parse global return when in Node.js environment with globalReturn explicitly off", () => {
5179 const messages = linter.verify("return;", {
5189 const suppressedMessages = linter.getSuppressedMessages();
5191 assert.strictEqual(messages.length, 1);
5192 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5194 assert.strictEqual(suppressedMessages.length, 0);
5197 it("should not parse global return when Node.js environment is false", () => {
5199 const messages = linter.verify("return;", {}, filename);
5200 const suppressedMessages = linter.getSuppressedMessages();
5202 assert.strictEqual(messages.length, 1);
5203 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5205 assert.strictEqual(suppressedMessages.length, 0);
5208 it("should properly parse sloppy-mode code when impliedStrict is false", () => {
5210 const messages = linter.verify("var private;", {}, filename);
5211 const suppressedMessages = linter.getSuppressedMessages();
5213 assert.strictEqual(messages.length, 0);
5214 assert.strictEqual(suppressedMessages.length, 0);
5217 it("should not parse sloppy-mode code when impliedStrict is true", () => {
5219 const messages = linter.verify("var private;", {
5226 const suppressedMessages = linter.getSuppressedMessages();
5228 assert.strictEqual(messages.length, 1);
5229 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
5231 assert.strictEqual(suppressedMessages.length, 0);
5234 it("should properly parse valid code when impliedStrict is true", () => {
5236 const messages = linter.verify("var foo;", {
5243 const suppressedMessages = linter.getSuppressedMessages();
5245 assert.strictEqual(messages.length, 0);
5246 assert.strictEqual(suppressedMessages.length, 0);
5249 it("should properly parse JSX when passed ecmaFeatures", () => {
5251 const messages = linter.verify("var x = <div/>;", {
5258 const suppressedMessages = linter.getSuppressedMessages();
5260 assert.strictEqual(messages.length, 0);
5261 assert.strictEqual(suppressedMessages.length, 0);
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();
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 <");
5274 assert.strictEqual(suppressedMessages.length, 0);
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();
5282 assert.strictEqual(messages.length, 0);
5283 assert.strictEqual(suppressedMessages.length, 0);
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();
5291 assert.strictEqual(messages.length, 0);
5292 assert.strictEqual(suppressedMessages.length, 0);
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();
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);
5305 assert.strictEqual(suppressedMessages.length, 0);
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();
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);
5318 assert.strictEqual(suppressedMessages.length, 0);
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();
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);
5331 assert.strictEqual(suppressedMessages.length, 0);
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();
5339 assert.strictEqual(messages.length, 0);
5340 assert.strictEqual(suppressedMessages.length, 0);
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)];
5346 ecmaVersions.forEach(ecmaVersion => {
5347 const code = "var enum;";
5348 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
5349 const suppressedMessages = linter.getSuppressedMessages();
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);
5356 assert.strictEqual(suppressedMessages.length, 0);
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)];
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();
5368 assert.strictEqual(messages.length, 0);
5369 assert.strictEqual(suppressedMessages.length, 0);
5373 it("should not allow `allowReserved
: true` in ES > 3", () => {
5374 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5376 ecmaVersions.forEach(ecmaVersion => {
5378 const messages = linter.verify(code, { parserOptions: { ecmaVersion, allowReserved: true } }, filename);
5379 const suppressedMessages = linter.getSuppressedMessages();
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);
5386 assert.strictEqual(suppressedMessages.length, 0);
5390 it("should be able to use es6 features if there is a comment which has \"eslint-env es6\"", () => {
5392 "/* eslint-env es6 */",
5393 "var arrow = () => 0;",
5394 "var binary = 0b1010;",
5395 "{ let a = 0; const b = 1; }",
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}';"
5414 const messages = linter.verify(code, null, "eslint-env es6");
5415 const suppressedMessages = linter.getSuppressedMessages();
5417 assert.strictEqual(messages.length, 0);
5418 assert.strictEqual(suppressedMessages.length, 0);
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();
5425 assert.strictEqual(messages.length, 0);
5426 assert.strictEqual(suppressedMessages.length, 0);
5429 it("should attach a \"/*global\" comment node to declared variables", () => {
5430 const code = "/* global foo */\n/* global bar, baz */";
5433 linter.defineRules({
5435 create: context => ({
5437 const scope = context.getScope();
5438 const sourceCode = context.sourceCode;
5439 const comments = sourceCode.getAllComments();
5441 assert.strictEqual(context.getSourceCode(), sourceCode);
5442 assert.strictEqual(2, comments.length);
5444 const foo = getVariable(scope, "foo");
5446 assert.strictEqual(foo.eslintExplicitGlobal, true);
5447 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
5449 const bar = getVariable(scope, "bar");
5451 assert.strictEqual(bar.eslintExplicitGlobal, true);
5452 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
5454 const baz = getVariable(scope, "baz");
5456 assert.strictEqual(baz.eslintExplicitGlobal, true);
5457 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
5465 linter.verify(code, { rules: { test: 2 } });
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();
5473 assert.deepStrictEqual(results, [
5477 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
5488 message: "'foo' is not defined.",
5493 nodeType: "Identifier"
5497 assert.strictEqual(suppressedMessages.length, 0);
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 } } });
5505 it("should reuse the SourceCode object", () => {
5509 linter.defineRule("save-ast1", {
5516 linter.defineRule("save-ast2", {
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 } });
5527 assert(ast1 !== null);
5528 assert(ast2 !== null);
5529 assert(ast1 === ast2);
5532 it("should allow 'await' as a property name in modules", () => {
5533 const result = linter.verify(
5535 { parserOptions: { ecmaVersion: 6, sourceType: "module" } }
5537 const suppressedMessages = linter.getSuppressedMessages();
5539 assert(result.length === 0);
5540 assert.strictEqual(suppressedMessages.length, 0);
5544 it("should not modify config object passed as argument", () => {
5547 Object.freeze(config);
5548 linter.verify("var", config);
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");
5557 linter.defineRule("foo-bar-baz", { create: spy });
5558 linter.verify("x", { rules: { "foo-bar-baz": "error" } });
5559 assert(spy.calledOnce);
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({});
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")
5573 const suppressedMessages = linter.getSuppressedMessages();
5575 // Don't include syntax error of the comment.
5576 assert.deepStrictEqual(messages, []);
5579 assert.strictEqual(aaa.callCount, 1);
5580 assert.strictEqual(bbb.callCount, 0);
5582 assert.strictEqual(suppressedMessages.length, 0);
5585 it("should ignore the part preceded by '--' in '/*eslint-env*/'.", () => {
5586 const messages = linter.verify(`
5587 /*eslint-env es2015 -- es2017 */
5590 `, { rules: { "no-redeclare": "error" } });
5591 const suppressedMessages = linter.getSuppressedMessages();
5593 // Don't include `Atomics
`
5594 assert.deepStrictEqual(
5601 message: "'Promise' is already defined as a built-in global variable.",
5602 messageId: "redeclaredAsBuiltin",
5603 nodeType: "Identifier",
5604 ruleId: "no-redeclare",
5609 assert.strictEqual(suppressedMessages.length, 0);
5612 it("should ignore the part preceded by '--' in '/*global*/'.", () => {
5613 const messages = linter.verify(`
5614 /*global aaa -- bbb */
5617 `, { rules: { "no-redeclare": "error" } });
5618 const suppressedMessages = linter.getSuppressedMessages();
5620 // Don't include `bbb
`
5621 assert.deepStrictEqual(
5628 message: "'aaa' is already defined by a variable declaration.",
5629 messageId: "redeclaredBySyntax",
5631 ruleId: "no-redeclare",
5636 assert.strictEqual(suppressedMessages.length, 0);
5639 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
5640 const messages = linter.verify(`
5641 /*globals aaa -- bbb */
5644 `, { rules: { "no-redeclare": "error" } });
5645 const suppressedMessages = linter.getSuppressedMessages();
5647 // Don't include `bbb
`
5648 assert.deepStrictEqual(
5655 message: "'aaa' is already defined by a variable declaration.",
5656 messageId: "redeclaredBySyntax",
5658 ruleId: "no-redeclare",
5663 assert.strictEqual(suppressedMessages.length, 0);
5666 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
5667 const messages = linter.verify(`
5668 /*exported aaa -- bbb */
5671 `, { rules: { "no-unused-vars": "error" } });
5672 const suppressedMessages = linter.getSuppressedMessages();
5674 // Don't include `aaa
`
5675 assert.deepStrictEqual(
5682 message: "'bbb' is assigned a value but never used.",
5683 messageId: "unusedVar",
5684 nodeType: "Identifier",
5685 ruleId: "no-unused-vars",
5690 assert.strictEqual(suppressedMessages.length, 0);
5693 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
5694 const messages = linter.verify(`
5695 /*eslint-disable no-redeclare -- no-unused-vars */
5698 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5699 const suppressedMessages = linter.getSuppressedMessages();
5701 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5702 assert.deepStrictEqual(
5709 message: "'aaa' is assigned a value but never used.",
5710 messageId: "unusedVar",
5711 nodeType: "Identifier",
5712 ruleId: "no-unused-vars",
5717 assert.deepStrictEqual(
5724 message: "'aaa' is already defined.",
5725 messageId: "redeclared",
5726 nodeType: "Identifier",
5727 ruleId: "no-redeclare",
5729 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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 */
5740 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5741 const suppressedMessages = linter.getSuppressedMessages();
5743 // Do include `no
-redeclare
` but not `no
-unused
-vars
`
5744 assert.deepStrictEqual(
5751 message: "'aaa' is already defined.",
5752 messageId: "redeclared",
5753 nodeType: "Identifier",
5754 ruleId: "no-redeclare",
5759 assert.deepStrictEqual(
5766 message: "'aaa' is assigned a value but never used.",
5767 messageId: "unusedVar",
5768 nodeType: "Identifier",
5769 ruleId: "no-unused-vars",
5771 suppressions: [{ kind: "directive", justification: "" }]
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();
5783 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5784 assert.deepStrictEqual(
5791 message: "'aaa' is assigned a value but never used.",
5792 messageId: "unusedVar",
5793 nodeType: "Identifier",
5794 ruleId: "no-unused-vars",
5799 assert.deepStrictEqual(
5806 message: "'aaa' is already defined.",
5807 messageId: "redeclared",
5808 nodeType: "Identifier",
5809 ruleId: "no-redeclare",
5811 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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();
5823 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5824 assert.deepStrictEqual(
5831 message: "'aaa' is assigned a value but never used.",
5832 messageId: "unusedVar",
5833 nodeType: "Identifier",
5834 ruleId: "no-unused-vars",
5839 assert.deepStrictEqual(
5846 message: "'aaa' is already defined.",
5847 messageId: "redeclared",
5848 nodeType: "Identifier",
5849 ruleId: "no-redeclare",
5851 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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
5860 //eslint-disable-next-line no-redeclare -- no-unused-vars
5862 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5863 const suppressedMessages = linter.getSuppressedMessages();
5865 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5866 assert.deepStrictEqual(
5873 message: "'aaa' is assigned a value but never used.",
5874 messageId: "unusedVar",
5875 nodeType: "Identifier",
5876 ruleId: "no-unused-vars",
5881 assert.deepStrictEqual(
5888 message: "'aaa' is already defined.",
5889 messageId: "redeclared",
5890 nodeType: "Identifier",
5891 ruleId: "no-redeclare",
5893 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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 */
5902 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
5904 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5905 const suppressedMessages = linter.getSuppressedMessages();
5907 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5908 assert.deepStrictEqual(
5915 message: "'aaa' is assigned a value but never used.",
5916 messageId: "unusedVar",
5917 nodeType: "Identifier",
5918 ruleId: "no-unused-vars",
5923 assert.deepStrictEqual(
5930 message: "'aaa' is already defined.",
5931 messageId: "redeclared",
5932 nodeType: "Identifier",
5933 ruleId: "no-redeclare",
5935 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5940 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
5941 const rule = sinon.stub().returns({});
5943 linter.defineRule("a--rule", { create: rule });
5944 const messages = linter.verify(`
5945 /*eslint a--rule:error */
5946 console
.log("hello")
5948 const suppressedMessages = linter.getSuppressedMessages();
5950 // Don't include syntax error of the comment.
5951 assert.deepStrictEqual(messages, []);
5954 assert.strictEqual(rule.callCount, 1);
5956 assert.strictEqual(suppressedMessages.length, 0);
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({});
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")
5969 const suppressedMessages = linter.getSuppressedMessages();
5971 // Don't include syntax error of the comment.
5972 assert.deepStrictEqual(messages, []);
5975 assert.strictEqual(aaa.callCount, 1);
5976 assert.strictEqual(bbb.callCount, 0);
5978 assert.strictEqual(suppressedMessages.length, 0);
5981 it("should ignore the part preceded by '--' with line breaks.", () => {
5982 const aaa = sinon.stub().returns({});
5983 const bbb = sinon.stub().returns({});
5985 linter.defineRule("aaa", { create: aaa });
5986 linter.defineRule("bbb", { create: bbb });
5987 const messages = linter.verify(`
5991 console
.log("hello")
5993 const suppressedMessages = linter.getSuppressedMessages();
5995 // Don't include syntax error of the comment.
5996 assert.deepStrictEqual(messages, []);
5999 assert.strictEqual(aaa.callCount, 1);
6000 assert.strictEqual(bbb.callCount, 0);
6002 assert.strictEqual(suppressedMessages.length, 0);
6007 describe("context.getScope()", () => {
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.
6016 function getScope(code, astSelector, ecmaVersion = 5) {
6019 linter.defineRule("get-scope", {
6020 create: context => ({
6021 [astSelector](node0) {
6023 scope = context.getScope();
6030 parserOptions: { ecmaVersion },
6031 rules: { "get-scope": 2 }
6035 return { node, scope };
6038 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
6039 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
6041 assert.strictEqual(scope.type, "function");
6042 assert.strictEqual(scope.block, node);
6045 it("should return 'function' scope on FunctionExpression (ES5)", () => {
6046 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
6048 assert.strictEqual(scope.type, "function");
6049 assert.strictEqual(scope.block, node);
6052 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
6053 const { node, scope } = getScope("function f() {}", "BlockStatement");
6055 assert.strictEqual(scope.type, "function");
6056 assert.strictEqual(scope.block, node.parent);
6059 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
6060 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
6062 assert.strictEqual(scope.type, "function");
6063 assert.strictEqual(scope.block, node.parent);
6066 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
6067 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
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"]);
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);
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"]);
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);
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"]);
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");
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"]);
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);
6107 assert.strictEqual(scope.type, "switch");
6108 assert.strictEqual(scope.block, node);
6109 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
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");
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"]);
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);
6123 assert.strictEqual(scope.type, "switch");
6124 assert.strictEqual(scope.block, node.parent);
6125 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
6128 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
6129 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
6131 assert.strictEqual(scope.type, "catch");
6132 assert.strictEqual(scope.block, node);
6133 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
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);
6139 assert.strictEqual(scope.type, "catch");
6140 assert.strictEqual(scope.block, node);
6141 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
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");
6147 assert.strictEqual(scope.type, "catch");
6148 assert.strictEqual(scope.block, node.parent);
6149 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
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);
6155 assert.strictEqual(scope.type, "block");
6156 assert.strictEqual(scope.block, node);
6157 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
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");
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"]);
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);
6171 assert.strictEqual(scope.type, "for");
6172 assert.strictEqual(scope.block, node);
6173 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
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");
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"]);
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);
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"]);
6194 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
6195 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
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"]);
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);
6205 assert.strictEqual(scope.type, "for");
6206 assert.strictEqual(scope.block, node);
6207 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
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");
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"]);
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);
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"]);
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);
6231 assert.strictEqual(scope.type, "for");
6232 assert.strictEqual(scope.block, node);
6233 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
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);
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"]);
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);
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]);
6259 describe("Variables and references", () => {
6262 "function foo() { b; }",
6277 linter.defineRules({
6279 create: context => ({
6281 scope = context.getScope();
6287 linter.verify(code, { rules: { test: 2 }, globals: { e: true, f: false } });
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);
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"));
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"));
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"));
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"));
6360 describe("context.getDeclaredVariables(node)", () => {
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.
6369 function verify(code, type, expectedNamesList) {
6370 linter.defineRules({
6375 * Assert `context
.getDeclaredVariables(node
)` is empty.
6376 * @param {ASTNode} node A node to check.
6379 function checkEmpty(node) {
6380 assert.strictEqual(0, context.getDeclaredVariables(node).length);
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
6432 rule[type] = function(node) {
6433 const expectedNames = expectedNamesList.shift();
6434 const variables = context.getDeclaredVariables(node);
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);
6447 linter.verify(code, {
6451 sourceType: "module"
6455 // Check all expected names are asserted.
6456 assert.strictEqual(0, expectedNamesList.length);
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 ";
6464 ["g", "h", "i", "j", "k"],
6468 verify(code, "VariableDeclaration", namesList);
6471 it("VariableDeclaration (on for-in/of loop)", () => {
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 ";
6482 verify(code, "VariableDeclaration", namesList);
6485 it("VariableDeclarator", () => {
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 ";
6497 verify(code, "VariableDeclarator", namesList);
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 ";
6503 ["foo", "a", "b", "c", "d", "e"],
6504 ["bar", "f", "g", "h", "i", "j"]
6507 verify(code, "FunctionDeclaration", namesList);
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 ";
6513 ["foo", "a", "b", "c", "d", "e"],
6514 ["bar", "f", "g", "h", "i", "j"],
6518 verify(code, "FunctionExpression", namesList);
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 ";
6524 ["a", "b", "c", "d", "e"],
6525 ["f", "g", "h", "i", "j"]
6528 verify(code, "ArrowFunctionExpression", namesList);
6531 it("ClassDeclaration", () => {
6532 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
6534 ["A", "A"], // outer scope's and inner scope's.
6538 verify(code, "ClassDeclaration", namesList);
6541 it("ClassExpression", () => {
6542 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
6548 verify(code, "ClassExpression", namesList);
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 ";
6558 verify(code, "CatchClause", namesList);
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 ";
6569 verify(code, "ImportDeclaration", namesList);
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 ";
6579 verify(code, "ImportSpecifier", namesList);
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 ";
6588 verify(code, "ImportDefaultSpecifier", namesList);
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 ";
6597 verify(code, "ImportNamespaceSpecifier", namesList);
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 => ({
6609 message: "Incorrect spacing",
6611 desc: "Insert space at the beginning",
6612 fix: fixer => fixer.insertTextBefore(node, " ")
6614 desc: "Insert space at the end",
6615 fix: fixer => fixer.insertTextAfter(node, " ")
6622 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6623 const suppressedMessages = linter.getSuppressedMessages();
6625 assert.deepStrictEqual(messages[0].suggestions, [{
6626 desc: "Insert space at the beginning",
6632 desc: "Insert space at the end",
6639 assert.strictEqual(suppressedMessages.length, 0);
6642 it("supports messageIds for suggestions", () => {
6643 linter.defineRule("rule-with-suggestions", {
6646 suggestion1: "Insert space at the beginning",
6647 suggestion2: "Insert space at the end"
6649 hasSuggestions: true
6651 create: context => ({
6655 message: "Incorrect spacing",
6657 messageId: "suggestion1",
6658 fix: fixer => fixer.insertTextBefore(node, " ")
6660 messageId: "suggestion2",
6661 fix: fixer => fixer.insertTextAfter(node, " ")
6668 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6669 const suppressedMessages = linter.getSuppressedMessages();
6671 assert.deepStrictEqual(messages[0].suggestions, [{
6672 messageId: "suggestion1",
6673 desc: "Insert space at the beginning",
6679 messageId: "suggestion2",
6680 desc: "Insert space at the end",
6687 assert.strictEqual(suppressedMessages.length, 0);
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 => ({
6697 message: "hello world",
6698 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
6704 assert.throws(() => {
6705 linter.verify("0", { rules: { "rule-with-suggestions": "error" } });
6706 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`.");
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 => ({
6716 message: "hello world",
6717 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
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.");
6729 describe("mutability", () => {
6734 linter1 = new Linter();
6735 linter2 = new Linter();
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()));
6743 it("loading rule in one doesn't change the other", () => {
6744 linter1.defineRule("mock-rule", {
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");
6754 describe("processors", () => {
6755 let receivedFilenames = [];
6756 let receivedPhysicalFilenames = [];
6759 receivedFilenames = [];
6760 receivedPhysicalFilenames = [];
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 => ({
6766 assert.strictEqual(context.getFilename(), context.filename);
6767 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
6769 receivedFilenames.push(context.filename);
6770 receivedPhysicalFilenames.push(context.physicalFilename);
6772 context.report({ node: ast, message: context.sourceCode.text });
6778 describe("preprocessors", () => {
6779 it("should receive text and filename.", () => {
6780 const code = "foo bar baz";
6781 const preprocess = sinon.spy(text => text.split(" "));
6783 linter.verify(code, {}, { filename, preprocess });
6785 assert.strictEqual(preprocess.calledOnce, true);
6786 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
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(
6793 { rules: { "report-original-text": "error" } },
6796 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6798 return input.split(" ");
6803 assert.strictEqual(problems.length, 3);
6804 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
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(
6811 { rules: { "report-original-text": "error" } },
6815 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6817 return input.split(" ").map(text => ({
6818 filename: "block.js",
6825 assert.strictEqual(problems.length, 3);
6826 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
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]));
6834 // physical filename
6835 assert.strictEqual(receivedPhysicalFilenames.length, 3);
6836 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
6839 it("should receive text even if a SourceCode object was given.", () => {
6841 const preprocess = sinon.spy(text => text.split(" "));
6843 linter.verify(code, {});
6844 const sourceCode = linter.getSourceCode();
6846 linter.verify(sourceCode, {}, { filename, preprocess });
6848 assert.strictEqual(preprocess.calledOnce, true);
6849 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
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(" "));
6856 linter.verify(code, {});
6857 const sourceCode = linter.getSourceCode();
6859 linter.verify(sourceCode, {}, { filename, preprocess });
6861 assert.strictEqual(preprocess.calledOnce, true);
6862 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6865 it("should catch preprocess error.", () => {
6867 const preprocess = sinon.spy(() => {
6868 throw Object.assign(new SyntaxError("Invalid syntax"), {
6874 const messages = linter.verify(code, {}, { filename, preprocess });
6876 assert.strictEqual(preprocess.calledOnce, true);
6877 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6878 assert.deepStrictEqual(messages, [
6883 message: "Preprocessing error: Invalid syntax",
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]);
6898 linter.verify(code, {}, { filename, postprocess, preprocess });
6900 assert.strictEqual(postprocess.calledOnce, true);
6901 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
6904 it("should apply a postprocessor to the reported messages", () => {
6905 const code = "foo bar baz";
6907 const problems = linter.verify(
6909 { rules: { "report-original-text": "error" } },
6911 preprocess: input => input.split(" "),
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.
6917 postprocess(problemLists) {
6918 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
6919 return problemLists.reduce(
6920 (combinedList, problemList, index) =>
6921 combinedList.concat(
6928 message: problem.message.toUpperCase(),
6929 column: problem.column + index * 4
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]);
6945 it("should use postprocessed problem ranges when applying autofixes", () => {
6946 const code = "foo bar baz";
6948 linter.defineRule("capitalize-identifiers", {
6955 if (node.name !== node.name.toUpperCase()) {
6958 message: "Capitalize this identifier",
6959 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
6967 const fixResult = linter.verifyAndFix(
6969 { rules: { "capitalize-identifiers": "error" } },
6973 * Apply a postprocessor that updates the locations of autofixes
6974 * to make sure they correspond to locations in the original text.
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, {
6984 text: problem.fix.text,
6985 range: problem.fix.range.map(
6986 rangeIndex => rangeIndex + blockIndex * 4
6997 assert.strictEqual(fixResult.fixed, true);
6998 assert.strictEqual(fixResult.messages.length, 0);
6999 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
7004 describe("verifyAndFix", () => {
7005 it("Fixes the code", () => {
7006 const messages = linter.verifyAndFix("var a", {
7010 }, { filename: "test.js" });
7012 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
7013 assert.isTrue(messages.fixed);
7016 it("does not require a third argument", () => {
7017 const fixResult = linter.verifyAndFix("var a", {
7023 assert.deepStrictEqual(fixResult, {
7030 it("does not include suggestions in autofix results", () => {
7031 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
7034 "no-useless-escape": 2
7038 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
7039 assert.strictEqual(fixResult.fixed, true);
7040 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
7043 it("does not apply autofixes when fix argument is `false`", () => {
7044 const fixResult = linter.verifyAndFix("var a", {
7050 assert.strictEqual(fixResult.fixed, false);
7053 it("stops fixing after 10 passes", () => {
7055 linter.defineRule("add-spaces", {
7057 fixable: "whitespace"
7064 message: "Add a space before this node.",
7065 fix: fixer => fixer.insertTextBefore(node, " ")
7072 const fixResult = linter.verifyAndFix("a", { rules: { "add-spaces": "error" } });
7074 assert.strictEqual(fixResult.fixed, true);
7075 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a
`);
7076 assert.strictEqual(fixResult.messages.length, 1);
7079 it("should throw an error if fix is passed but meta has no `fixable
` property", () => {
7080 linter.defineRule("test-rule", {
7085 create: context => ({
7087 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
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);
7097 it("should throw an error if fix is passed and there is no metadata", () => {
7098 linter.defineRule("test-rule", {
7099 create: context => ({
7101 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
7106 assert.throws(() => {
7107 linter.verify("0", { rules: { "test-rule": "error" } });
7108 }, /Fixable rules must set the `meta
\.fixable
` property/u);
7111 it("should throw an error if fix is passed from a legacy-format rule", () => {
7112 linter.defineRule("test-rule", {
7113 create: context => ({
7115 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
7120 assert.throws(() => {
7121 linter.verify("0", { rules: { "test-rule": "error" } });
7122 }, /Fixable rules must set the `meta
\.fixable
` property/u);
7126 describe("Edge cases", () => {
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();
7133 assert.strictEqual(messages.length, 0);
7134 assert.strictEqual(suppressedMessages.length, 0);
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();
7142 assert.strictEqual(messages.length, 0);
7143 assert.strictEqual(suppressedMessages.length, 0);
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();
7151 assert.strictEqual(messages.length, 0);
7152 assert.strictEqual(suppressedMessages.length, 0);
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();
7160 assert.deepStrictEqual(messages.length, 1);
7161 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
7162 assert.strictEqual(suppressedMessages.length, 0);
7164 messages = linter.verify("", { parserOptions: { sourceType: "foo" } });
7165 suppressedMessages = linter.getSuppressedMessages();
7167 assert.deepStrictEqual(messages.length, 1);
7168 assert.ok(messages[0].message.includes("Invalid sourceType"));
7169 assert.strictEqual(suppressedMessages.length, 0);
7171 messages = linter.verify("", { parserOptions: { ecmaVersion: 5, sourceType: "module" } });
7172 suppressedMessages = linter.getSuppressedMessages();
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);
7179 it("should not crash when invalid parentheses syntax is encountered", () => {
7180 linter.verify("left = (aSize.width/2) - ()");
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 } });
7187 it("should not crash when parsing destructured assignment", () => {
7188 linter.verify("var { a='a' } = {};", { parserOptions: { ecmaVersion: 6 } });
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();
7195 assert.strictEqual(messages.length, 1);
7196 assert.strictEqual(messages[0].fatal, true);
7198 assert.strictEqual(suppressedMessages.length, 0);
7201 it("should not rewrite env setting in core (https://github.com/eslint/eslint/issues/4814)", () => {
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
7207 linter.defineRule("test", {
7210 linter.verify("var a = 0;", {
7211 env: { node: true },
7212 parserOptions: { ecmaVersion: 6, sourceType: "module" },
7216 // This `verify()` takes the instance and tests that the instance was not modified.
7219 linter.defineRule("test", {
7222 context.parserOptions.ecmaFeatures.globalReturn,
7223 "`ecmaFeatures
.globalReturn
` of the node environment should not be modified."
7229 linter.verify("var a = 0;", {
7230 env: { node: true },
7237 it("should throw when rule's create() function does not return an object", () => {
7238 const config = { rules: { checker: "error" } };
7240 linter.defineRule("checker", {
7244 assert.throws(() => {
7245 linter.verify("abc", config, filename);
7246 }, "The create() function for rule 'checker' did not return an object.");
7248 linter.defineRule("checker", {
7250 }); // returns undefined
7252 assert.throws(() => {
7253 linter.verify("abc", config, filename);
7254 }, "The create() function for rule 'checker' did not return an object.");
7258 describe("Custom parser", () => {
7260 const errorPrefix = "Parsing error: ";
7262 it("should have file path passed to it", () => {
7263 const code = "/* this is code */";
7264 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
7266 linter.defineParser("stub-parser", testParsers.stubParser);
7267 linter.verify(code, { parser: "stub-parser" }, filename, true);
7269 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
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} />;";
7275 linter.defineParser("esprima", esprima);
7276 const messages = linter.verify(code, { parser: "esprima", parserOptions: { jsx: true } }, "filename");
7277 const suppressedMessages = linter.getSuppressedMessages();
7279 assert.strictEqual(messages.length, 0);
7280 assert.strictEqual(suppressedMessages.length, 0);
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();
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.");
7292 assert.strictEqual(suppressedMessages.length, 0);
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'";
7298 linter.defineParser("unknown-logical-operator", testParsers.unknownLogicalOperator);
7300 // This shouldn't throw
7301 const messages = linter.verify(code, { parser: "unknown-logical-operator" }, filename, true);
7302 const suppressedMessages = linter.getSuppressedMessages();
7304 assert.strictEqual(messages.length, 0);
7305 assert.strictEqual(suppressedMessages.length, 0);
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";
7311 linter.defineParser("unknown-logical-operator-nested", testParsers.unknownLogicalOperatorNested);
7313 // This shouldn't throw
7314 const messages = linter.verify(code, { parser: "unknown-logical-operator-nested" }, filename, true);
7315 const suppressedMessages = linter.getSuppressedMessages();
7317 assert.strictEqual(messages.length, 0);
7318 assert.strictEqual(suppressedMessages.length, 0);
7321 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
7322 const code = "foo && bar %% baz";
7326 linter.defineRule("collect-node-types", {
7329 nodes.push(node.type);
7334 linter.defineParser("non-js-parser", testParsers.nonJSParser);
7336 const messages = linter.verify(code, {
7337 parser: "non-js-parser",
7339 "collect-node-types": "error"
7342 const suppressedMessages = linter.getSuppressedMessages();
7344 assert.strictEqual(messages.length, 0);
7345 assert.isTrue(nodes.length > 0);
7347 assert.strictEqual(suppressedMessages.length, 0);
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();
7355 assert.strictEqual(messages.length, 1);
7356 assert.strictEqual(messages[0].severity, 2);
7357 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
7359 assert.strictEqual(suppressedMessages.length, 0);
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();
7367 assert.strictEqual(messages.length, 1);
7368 assert.strictEqual(messages[0].severity, 2);
7369 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
7371 assert.strictEqual(suppressedMessages.length, 0);
7374 describe("if a parser provides 'visitorKeys'", () => {
7378 let firstChildNodes = [];
7382 firstChildNodes = [];
7383 linter.defineRule("collect-node-types", {
7386 types.push(node.type);
7390 linter.defineRule("save-scope-manager", {
7392 scopeManager = context.sourceCode.scopeManager;
7397 linter.defineRule("esquery-option", {
7399 ":first-child"(node) {
7400 firstChildNodes.push(node);
7404 linter.defineParser("enhanced-parser2", testParsers.enhancedParser2);
7405 linter.verify("@foo class A {}", {
7406 parser: "enhanced-parser2",
7408 "collect-node-types": "error",
7409 "save-scope-manager": "error",
7410 "esquery-option": "error"
7414 sourceCode = linter.getSourceCode();
7417 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
7418 assert.deepStrictEqual(
7420 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
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"]
7431 it("should use the same visitorKeys if the source code object is reused", () => {
7434 linter.defineRule("collect-node-types", {
7437 types2.push(node.type);
7441 linter.verify(sourceCode, {
7443 "collect-node-types": "error"
7447 assert.deepStrictEqual(
7449 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7453 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7454 assert.deepStrictEqual(
7456 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
7461 describe("if a parser provides 'scope'", () => {
7463 let sourceCode = null;
7466 linter.defineParser("enhanced-parser3", testParsers.enhancedParser3);
7467 linter.defineRule("save-scope1", {
7468 create: context => ({
7470 scope = context.getScope();
7474 linter.verify("@foo class A {}", { parser: "enhanced-parser3", rules: { "save-scope1": 2 } });
7476 sourceCode = linter.getSourceCode();
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,
7487 it("should use the same scope if the source code object is reused", () => {
7490 linter.defineRule("save-scope2", {
7491 create: context => ({
7493 scope2 = context.getScope();
7497 linter.verify(sourceCode, { rules: { "save-scope2": 2 } }, "test.js");
7499 assert(scope2 !== null);
7500 assert(scope2 === scope);
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();
7509 assert.strictEqual(messages.length, 0);
7510 assert.strictEqual(suppressedMessages.length, 0);
7514 describe("merging 'parserOptions'", () => {
7515 it("should deeply merge 'parserOptions' from an environment with 'parserOptions' from the provided config", () => {
7516 const code = "return <div/>";
7519 node: true // ecmaFeatures: { globalReturn: true }
7528 const messages = linter.verify(code, config);
7529 const suppressedMessages = linter.getSuppressedMessages();
7531 // no parsing errors
7532 assert.strictEqual(messages.length, 0);
7533 assert.strictEqual(suppressedMessages.length, 0);
7538 describe("Linter with FlatConfigArray", () => {
7541 const filename = "filename.js";
7544 * Creates a config array with some default properties.
7545 * @param {FlatConfig|FlatConfig[]} value The value to base the
7547 * @returns {FlatConfigArray} The created config array.
7549 function createFlatConfigArray(value) {
7550 return new FlatConfigArray(value, { basePath: "" });
7554 linter = new Linter({ configType: "flat" });
7557 describe("Static Members", () => {
7558 describe("version", () => {
7559 it("should return same version as instance property", () => {
7560 assert.strictEqual(Linter.version, linter.version);
7565 describe("Config Options", () => {
7567 describe("languageOptions", () => {
7569 describe("ecmaVersion", () => {
7571 it("should error when accessing a global that isn't available in ecmaVersion 5", () => {
7572 const messages = linter.verify("new Map()", {
7575 sourceType: "script"
7581 const suppressedMessages = linter.getSuppressedMessages();
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.");
7586 assert.strictEqual(suppressedMessages.length, 0);
7589 it("should error when accessing a global that isn't available in ecmaVersion 3", () => {
7590 const messages = linter.verify("JSON.stringify({})", {
7593 sourceType: "script"
7599 const suppressedMessages = linter.getSuppressedMessages();
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.");
7604 assert.strictEqual(suppressedMessages.length, 0);
7607 it("should add globals for ES6 when ecmaVersion is 6", () => {
7608 const messages = linter.verify("new Map()", {
7616 const suppressedMessages = linter.getSuppressedMessages();
7618 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7619 assert.strictEqual(suppressedMessages.length, 0);
7622 it("should allow destructuring when ecmaVersion is 6", () => {
7623 const messages = linter.verify("let {a} = b", {
7628 const suppressedMessages = linter.getSuppressedMessages();
7630 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7631 assert.strictEqual(suppressedMessages.length, 0);
7634 it("ecmaVersion should be normalized to year name for ES 6", () => {
7640 create: context => ({
7642 assert.strictEqual(context.languageOptions.ecmaVersion, 2015);
7652 rules: { "test/checker": "error" }
7655 linter.verify("foo", config, filename);
7658 it("ecmaVersion should be normalized to latest year by default", () => {
7664 create: context => ({
7666 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7673 rules: { "test/checker": "error" }
7676 linter.verify("foo", config, filename);
7679 it("ecmaVersion should not be normalized to year name for ES 5", () => {
7685 create: context => ({
7687 assert.strictEqual(context.languageOptions.ecmaVersion, 5);
7697 rules: { "test/checker": "error" }
7700 linter.verify("foo", config, filename);
7703 it("ecmaVersion should be normalized to year name for 'latest'", () => {
7709 create: context => ({
7711 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7719 ecmaVersion: "latest"
7721 rules: { "test/checker": "error" }
7724 linter.verify("foo", config, filename);
7730 describe("sourceType", () => {
7732 it("should be module by default", () => {
7738 create: context => ({
7740 assert.strictEqual(context.languageOptions.sourceType, "module");
7747 rules: { "test/checker": "error" }
7750 linter.verify("import foo from 'bar'", config, filename);
7753 it("should default to commonjs when passed a .cjs filename", () => {
7759 create: context => ({
7761 assert.strictEqual(context.languageOptions.sourceType, "commonjs");
7768 rules: { "test/checker": "error" }
7771 linter.verify("import foo from 'bar'", config, `${filename}
.cjs
`);
7775 it("should error when import is used in a script", () => {
7776 const messages = linter.verify("import foo from 'bar';", {
7779 sourceType: "script"
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'");
7787 it("should not error when import is used in a module", () => {
7788 const messages = linter.verify("import foo from 'bar';", {
7791 sourceType: "module"
7794 const suppressedMessages = linter.getSuppressedMessages();
7796 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7797 assert.strictEqual(suppressedMessages.length, 0);
7800 it("should error when return is used at the top-level outside of commonjs", () => {
7801 const messages = linter.verify("return", {
7804 sourceType: "script"
7807 const suppressedMessages = linter.getSuppressedMessages();
7809 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
7810 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
7812 assert.strictEqual(suppressedMessages.length, 0);
7815 it("should not error when top-level return is used in commonjs", () => {
7816 const messages = linter.verify("return", {
7819 sourceType: "commonjs"
7822 const suppressedMessages = linter.getSuppressedMessages();
7824 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7825 assert.strictEqual(suppressedMessages.length, 0);
7828 it("should error when accessing a Node.js global outside of commonjs", () => {
7829 const messages = linter.verify("require()", {
7837 const suppressedMessages = linter.getSuppressedMessages();
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.");
7842 assert.strictEqual(suppressedMessages.length, 0);
7845 it("should add globals for Node.js when sourceType is commonjs", () => {
7846 const messages = linter.verify("require()", {
7849 sourceType: "commonjs"
7855 const suppressedMessages = linter.getSuppressedMessages();
7857 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7858 assert.strictEqual(suppressedMessages.length, 0);
7861 it("should allow 'await' as a property name in modules", () => {
7862 const result = linter.verify(
7867 sourceType: "module"
7871 const suppressedMessages = linter.getSuppressedMessages();
7873 assert(result.length === 0);
7874 assert.strictEqual(suppressedMessages.length, 0);
7879 describe("parser", () => {
7881 it("should be able to define a custom parser", () => {
7883 parseForESLint: function parse(code, options) {
7885 ast: esprima.parse(code, options),
7904 const messages = linter.verify("0", config, filename);
7905 const suppressedMessages = linter.getSuppressedMessages();
7907 assert.strictEqual(messages.length, 0);
7908 assert.strictEqual(suppressedMessages.length, 0);
7911 it("should pass parser as context.languageOptions.parser to all rules when provided on config", () => {
7918 create: sinon.mock().withArgs(
7919 sinon.match({ languageOptions: { parser: esprima } })
7933 linter.verify("0", config, filename);
7936 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
7939 parser: testParsers.enhancedParser
7943 const messages = linter.verify("0", config, filename);
7944 const suppressedMessages = linter.getSuppressedMessages();
7946 assert.strictEqual(messages.length, 0);
7947 assert.strictEqual(suppressedMessages.length, 0);
7950 it("should expose parser services when using parseForESLint() and services are specified", () => {
7956 "test-service-rule": {
7957 create: context => ({
7961 message: context.parserServices.test.getMessage()
7970 parser: testParsers.enhancedParser
7973 "test/test-service-rule": 2
7977 const messages = linter.verify("0", config, filename);
7978 const suppressedMessages = linter.getSuppressedMessages();
7980 assert.strictEqual(messages.length, 1);
7981 assert.strictEqual(messages[0].message, "Hi!");
7983 assert.strictEqual(suppressedMessages.length, 0);
7986 it("should use the same parserServices if source code object is reused", () => {
7992 "test-service-rule": {
7993 create: context => ({
7997 message: context.parserServices.test.getMessage()
8006 parser: testParsers.enhancedParser
8009 "test/test-service-rule": 2
8013 const messages = linter.verify("0", config, filename);
8014 const suppressedMessages = linter.getSuppressedMessages();
8016 assert.strictEqual(messages.length, 1);
8017 assert.strictEqual(messages[0].message, "Hi!");
8018 assert.strictEqual(suppressedMessages.length, 0);
8020 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
8021 const suppressedMessages2 = linter.getSuppressedMessages();
8023 assert.strictEqual(messages2.length, 1);
8024 assert.strictEqual(messages2[0].message, "Hi!");
8025 assert.strictEqual(suppressedMessages2.length, 0);
8028 it("should pass parser as context.languageOptions.parser to all rules when default parser is used", () => {
8030 // references to Espree get messed up in a browser context, so wrap it
8031 const fakeParser = {
8035 const spy = sinon.spy(context => {
8036 assert.strictEqual(context.languageOptions.parser, fakeParser);
8044 "test-rule": { create: spy }
8056 linter.verify("0", config, filename);
8057 assert.isTrue(spy.calledOnce);
8061 describe("Custom Parsers", () => {
8063 const errorPrefix = "Parsing error: ";
8065 it("should have file path passed to it", () => {
8066 const code = "/* this is code */";
8067 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
8070 parser: testParsers.stubParser
8074 linter.verify(code, config, filename, true);
8076 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
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} />;";
8090 const messages = linter.verify(code, config, filename);
8091 const suppressedMessages = linter.getSuppressedMessages();
8093 assert.strictEqual(messages.length, 0);
8094 assert.strictEqual(suppressedMessages.length, 0);
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'";
8101 parser: testParsers.unknownLogicalOperator
8105 // This shouldn't throw
8106 const messages = linter.verify(code, config, filename);
8107 const suppressedMessages = linter.getSuppressedMessages();
8109 assert.strictEqual(messages.length, 0);
8110 assert.strictEqual(suppressedMessages.length, 0);
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";
8117 parser: testParsers.unknownLogicalOperatorNested
8121 // This shouldn't throw
8122 const messages = linter.verify(code, config, filename);
8123 const suppressedMessages = linter.getSuppressedMessages();
8125 assert.strictEqual(messages.length, 0);
8126 assert.strictEqual(suppressedMessages.length, 0);
8129 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
8130 const code = "foo && bar %% baz";
8136 "collect-node-types": {
8139 nodes.push(node.type);
8147 parser: testParsers.nonJSParser
8150 "test/collect-node-types": "error"
8154 const messages = linter.verify(code, config, filename, true);
8155 const suppressedMessages = linter.getSuppressedMessages();
8157 assert.strictEqual(messages.length, 0);
8158 assert.isTrue(nodes.length > 0);
8159 assert.strictEqual(suppressedMessages.length, 0);
8162 it("should strip leading line: prefix from parser error", () => {
8163 const messages = linter.verify(";", {
8165 parser: testParsers.lineError
8168 const suppressedMessages = linter.getSuppressedMessages();
8170 assert.strictEqual(messages.length, 1);
8171 assert.strictEqual(messages[0].severity, 2);
8172 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
8174 assert.strictEqual(suppressedMessages.length, 0);
8177 it("should not modify a parser error message without a leading line: prefix", () => {
8178 const messages = linter.verify(";", {
8180 parser: testParsers.noLineError
8183 const suppressedMessages = linter.getSuppressedMessages();
8185 assert.strictEqual(messages.length, 1);
8186 assert.strictEqual(messages[0].severity, 2);
8187 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
8189 assert.strictEqual(suppressedMessages.length, 0);
8192 describe("if a parser provides 'visitorKeys'", () => {
8196 let firstChildNodes = [];
8200 firstChildNodes = [];
8205 "collect-node-types": {
8208 types.push(node.type);
8212 "save-scope-manager": {
8214 scopeManager = context.sourceCode.scopeManager;
8221 ":first-child"(node) {
8222 firstChildNodes.push(node);
8230 parser: testParsers.enhancedParser2
8233 "test/collect-node-types": "error",
8234 "test/save-scope-manager": "error",
8235 "test/esquery-option": "error"
8239 linter.verify("@foo class A {}", config);
8241 sourceCode = linter.getSourceCode();
8244 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
8245 assert.deepStrictEqual(
8247 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
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"]
8258 it("should use the same visitorKeys if the source code object is reused", () => {
8264 "collect-node-types": {
8267 types2.push(node.type);
8275 "test/collect-node-types": "error"
8279 linter.verify(sourceCode, config);
8281 assert.deepStrictEqual(
8283 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
8287 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
8288 assert.deepStrictEqual(
8290 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
8295 describe("if a parser provides 'scope'", () => {
8297 let sourceCode = null;
8305 create: context => ({
8307 scope = context.getScope();
8315 parser: testParsers.enhancedParser3
8318 "test/save-scope1": "error"
8322 linter.verify("@foo class A {}", config);
8324 sourceCode = linter.getSourceCode();
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,
8335 it("should use the same scope if the source code object is reused", () => {
8342 create: context => ({
8344 scope2 = context.getScope();
8352 "test/save-scope2": "error"
8356 linter.verify(sourceCode, config, "test.js");
8358 assert(scope2 !== null);
8359 assert(scope2 === scope);
8363 it("should pass default languageOptions to the parser", () => {
8365 const spy = sinon.spy((code, options) => espree.parse(code, options));
8367 linter.verify(";", {
8375 assert(spy.calledWithMatch(";", {
8376 ecmaVersion: espree.latestEcmaVersion + 2009,
8377 sourceType: "module"
8385 describe("parseOptions", () => {
8387 it("should pass ecmaFeatures to all rules when provided on config", () => {
8389 const parserOptions = {
8400 create: sinon.mock().withArgs(
8401 sinon.match({ languageOptions: { parserOptions } })
8415 linter.verify("0", config, filename);
8418 it("should switch globalReturn to false if sourceType is module", () => {
8425 create: sinon.mock().withArgs(
8441 sourceType: "module",
8453 linter.verify("0", config, filename);
8456 it("should not parse sloppy-mode code when impliedStrict is true", () => {
8458 const messages = linter.verify("var private;", {
8467 const suppressedMessages = linter.getSuppressedMessages();
8469 assert.strictEqual(messages.length, 1);
8470 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
8472 assert.strictEqual(suppressedMessages.length, 0);
8475 it("should properly parse valid code when impliedStrict is true", () => {
8477 const messages = linter.verify("var foo;", {
8486 const suppressedMessages = linter.getSuppressedMessages();
8488 assert.strictEqual(messages.length, 0);
8489 assert.strictEqual(suppressedMessages.length, 0);
8492 it("should properly parse JSX when passed ecmaFeatures", () => {
8494 const messages = linter.verify("var x = <div/>;", {
8503 const suppressedMessages = linter.getSuppressedMessages();
8505 assert.strictEqual(messages.length, 0);
8506 assert.strictEqual(suppressedMessages.length, 0);
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();
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 <");
8519 assert.strictEqual(suppressedMessages.length, 0);
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, {
8533 const suppressedMessages = linter.getSuppressedMessages();
8535 assert.strictEqual(messages.length, 0);
8536 assert.strictEqual(suppressedMessages.length, 0);
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, {
8552 const suppressedMessages = linter.getSuppressedMessages();
8554 assert.strictEqual(messages.length, 0);
8555 assert.strictEqual(suppressedMessages.length, 0);
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, {
8563 sourceType: "script"
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);
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, {
8578 sourceType: "script"
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);
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, {
8593 sourceType: "script"
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);
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, {
8608 sourceType: "script",
8615 assert.strictEqual(messages.length, 0);
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)];
8621 ecmaVersions.forEach(ecmaVersion => {
8622 const code = "var enum;";
8623 const messages = linter.verify(code, {
8625 ...(ecmaVersion ? { ecmaVersion } : {}),
8626 sourceType: "script"
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);
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)];
8640 ecmaVersions.forEach(ecmaVersion => {
8641 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
8642 const messages = linter.verify(code, {
8644 ...(ecmaVersion ? { ecmaVersion } : {}),
8645 sourceType: "script"
8649 assert.strictEqual(messages.length, 0);
8653 it("should not allow `allowReserved
: true` in ES > 3", () => {
8654 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8656 ecmaVersions.forEach(ecmaVersion => {
8658 const messages = linter.verify(code, {
8660 ...(ecmaVersion ? { ecmaVersion } : {}),
8661 sourceType: "script",
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);
8677 describe("settings", () => {
8678 const ruleId = "test-rule";
8680 it("should pass settings to all rules", () => {
8687 create: context => ({
8689 context.report(node, context.settings.info);
8700 [`test
/${ruleId}
`]: 1
8704 const messages = linter.verify("0", config, filename);
8705 const suppressedMessages = linter.getSuppressedMessages();
8707 assert.strictEqual(messages.length, 1);
8708 assert.strictEqual(messages[0].message, "Hello");
8710 assert.strictEqual(suppressedMessages.length, 0);
8713 it("should not have any settings if they were not passed in", () => {
8720 create: context => ({
8722 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
8723 context.report(node, "Settings should be empty");
8734 [`test
/${ruleId}
`]: 1
8738 const messages = linter.verify("0", config, filename);
8739 const suppressedMessages = linter.getSuppressedMessages();
8741 assert.strictEqual(messages.length, 0);
8742 assert.strictEqual(suppressedMessages.length, 0);
8746 describe("rules", () => {
8747 const code = "var answer = 6 * 7";
8749 it("should be configurable by only setting the integer value", () => {
8750 const rule = "semi",
8751 config = { rules: {} };
8753 config.rules[rule] = 1;
8755 const messages = linter.verify(code, config, filename, true);
8756 const suppressedMessages = linter.getSuppressedMessages();
8758 assert.strictEqual(messages.length, 1);
8759 assert.strictEqual(messages[0].ruleId, rule);
8761 assert.strictEqual(suppressedMessages.length, 0);
8764 it("should be configurable by only setting the string value", () => {
8765 const rule = "semi",
8766 config = { rules: {} };
8768 config.rules[rule] = "warn";
8770 const messages = linter.verify(code, config, filename, true);
8771 const suppressedMessages = linter.getSuppressedMessages();
8773 assert.strictEqual(messages.length, 1);
8774 assert.strictEqual(messages[0].severity, 1);
8775 assert.strictEqual(messages[0].ruleId, rule);
8777 assert.strictEqual(suppressedMessages.length, 0);
8780 it("should be configurable by passing in values as an array", () => {
8781 const rule = "semi",
8782 config = { rules: {} };
8784 config.rules[rule] = [1];
8786 const messages = linter.verify(code, config, filename, true);
8787 const suppressedMessages = linter.getSuppressedMessages();
8789 assert.strictEqual(messages.length, 1);
8790 assert.strictEqual(messages[0].ruleId, rule);
8792 assert.strictEqual(suppressedMessages.length, 0);
8795 it("should be configurable by passing in string value as an array", () => {
8796 const rule = "semi",
8797 config = { rules: {} };
8799 config.rules[rule] = ["warn"];
8801 const messages = linter.verify(code, config, filename, true);
8802 const suppressedMessages = linter.getSuppressedMessages();
8804 assert.strictEqual(messages.length, 1);
8805 assert.strictEqual(messages[0].severity, 1);
8806 assert.strictEqual(messages[0].ruleId, rule);
8808 assert.strictEqual(suppressedMessages.length, 0);
8811 it("should not be configurable by setting other value", () => {
8812 const rule = "semi",
8813 config = { rules: {} };
8815 config.rules[rule] = "1";
8817 assert.throws(() => {
8818 linter.verify(code, config, filename, true);
8819 }, /Key "rules": Key "semi": Expected severity/u);
8822 it("should process empty config", () => {
8824 const messages = linter.verify(code, config, filename, true);
8825 const suppressedMessages = linter.getSuppressedMessages();
8827 assert.strictEqual(messages.length, 0);
8828 assert.strictEqual(suppressedMessages.length, 0);
8834 describe("verify()", () => {
8836 it("should report warnings in order by line and column when called", () => {
8838 const code = "foo()\n alert('test')";
8839 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8841 const messages = linter.verify(code, config, filename);
8842 const suppressedMessages = linter.getSuppressedMessages();
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);
8852 assert.strictEqual(suppressedMessages.length, 0);
8855 it("should report ignored file when filename isn't matched in the config array", () => {
8857 const code = "foo()\n alert('test')";
8858 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8860 const messages = linter.verify(code, config, "filename.ts");
8862 assert.strictEqual(messages.length, 1);
8863 assert.deepStrictEqual(messages[0], {
8866 message: "No matching configuration found for filename.ts.",
8873 describe("Plugins", () => {
8875 it("should not load rule definition when rule isn't used", () => {
8877 const spy = sinon.spy();
8883 checker: { create: spy }
8889 linter.verify("code", config, filename);
8890 assert.isTrue(spy.notCalled, "Rule should not have been called");
8894 describe("Rule Internals", () => {
8896 const code = TEST_CODE;
8898 it("should throw an error when an error occurs inside of a rule visitor", () => {
8906 throw new Error("Intentional error.");
8914 rules: { "test/checker": "error" }
8917 assert.throws(() => {
8918 linter.verify(code, config, filename);
8919 }, `Intentional error
.\nOccurred while linting ${filename}
:1\nRule: "test/checker"`);
8922 it("should not call rule visitor with a `this` value", () => {
8923 const spy = sinon.spy();
8936 rules: { "test/checker": "error" }
8939 linter.verify("foo", config);
8940 assert(spy.calledOnce);
8941 assert.strictEqual(spy.firstCall.thisValue, void 0);
8944 it("should not call unrecognized rule visitor when present in a rule", () => {
8945 const spy = sinon.spy();
8959 "test/checker": "error",
8964 linter.verify("foo", config);
8965 assert(spy.notCalled);
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;
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);
8985 checker: { create: spy }
8989 rules: { "test/checker": "error" }
8992 linter.verify("foo + bar", config);
8993 assert(spy.calledOnce);
8996 it("events for each node type should fire", () => {
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();
9012 Literal: spyLiteral,
9013 VariableDeclarator: spyVariableDeclarator,
9014 VariableDeclaration: spyVariableDeclaration,
9015 Identifier: spyIdentifier,
9016 BinaryExpression: spyBinaryExpression
9023 rules: { "test/checker": "error" }
9026 const messages = linter.verify(code, config, filename, true);
9027 const suppressedMessages = linter.getSuppressedMessages();
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);
9036 assert.strictEqual(suppressedMessages.length, 0);
9039 it("should throw an error if a rule reports a problem without a message", () => {
9046 create: context => ({
9048 context.report({ node });
9055 rules: { "test/invalid-report": "error" }
9059 () => linter.verify("foo", config),
9061 "Missing `message
` property in report() call; add a message that describes the linting problem."
9068 describe("Rule Context", () => {
9070 describe("context.getFilename()", () => {
9071 const ruleId = "filename-rule";
9073 it("has access to the filename", () => {
9080 create: context => ({
9082 context.report(node, context.getFilename());
9090 [`test
/${ruleId}
`]: 1
9094 const messages = linter.verify("0", config, filename);
9095 const suppressedMessages = linter.getSuppressedMessages();
9097 assert.strictEqual(messages[0].message, filename);
9098 assert.strictEqual(suppressedMessages.length, 0);
9101 it("defaults filename to '<input>'", () => {
9108 create: context => ({
9110 context.report(node, context.getFilename());
9118 [`test
/${ruleId}
`]: 1
9123 const messages = linter.verify("0", config);
9124 const suppressedMessages = linter.getSuppressedMessages();
9126 assert.strictEqual(messages[0].message, "<input>");
9127 assert.strictEqual(suppressedMessages.length, 0);
9131 describe("context.filename", () => {
9132 const ruleId = "filename-rule";
9134 it("has access to the filename", () => {
9141 create: context => ({
9143 assert.strictEqual(context.getFilename(), context.filename);
9144 context.report(node, context.filename);
9152 [`test
/${ruleId}
`]: 1
9156 const messages = linter.verify("0", config, filename);
9157 const suppressedMessages = linter.getSuppressedMessages();
9159 assert.strictEqual(messages[0].message, filename);
9160 assert.strictEqual(suppressedMessages.length, 0);
9163 it("defaults filename to '<input>'", () => {
9170 create: context => ({
9172 assert.strictEqual(context.getFilename(), context.filename);
9173 context.report(node, context.filename);
9181 [`test
/${ruleId}
`]: 1
9186 const messages = linter.verify("0", config);
9187 const suppressedMessages = linter.getSuppressedMessages();
9189 assert.strictEqual(messages[0].message, "<input>");
9190 assert.strictEqual(suppressedMessages.length, 0);
9194 describe("context.getPhysicalFilename()", () => {
9196 const ruleId = "filename-rule";
9198 it("has access to the physicalFilename", () => {
9205 create: context => ({
9207 context.report(node, context.getPhysicalFilename());
9215 [`test
/${ruleId}
`]: 1
9219 const messages = linter.verify("0", config, filename);
9220 const suppressedMessages = linter.getSuppressedMessages();
9222 assert.strictEqual(messages[0].message, filename);
9223 assert.strictEqual(suppressedMessages.length, 0);
9228 describe("context.physicalFilename", () => {
9230 const ruleId = "filename-rule";
9232 it("has access to the physicalFilename", () => {
9239 create: context => ({
9241 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
9242 context.report(node, context.physicalFilename);
9250 [`test
/${ruleId}
`]: 1
9254 const messages = linter.verify("0", config, filename);
9255 const suppressedMessages = linter.getSuppressedMessages();
9257 assert.strictEqual(messages[0].message, filename);
9258 assert.strictEqual(suppressedMessages.length, 0);
9263 describe("context.getSourceLines()", () => {
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;"]);
9276 checker: { create: spy }
9280 rules: { "test/checker": "error" }
9283 linter.verify(code, config);
9284 assert(spy.calledOnce);
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;"]);
9298 checker: { create: spy }
9302 rules: { "test/checker": "error" }
9305 linter.verify(code, config);
9306 assert(spy.calledOnce);
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;"]);
9320 checker: { create: spy }
9324 rules: { "test/checker": "error" }
9327 linter.verify(code, config);
9328 assert(spy.calledOnce);
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;"]);
9342 checker: { create: spy }
9346 rules: { "test/checker": "error" }
9349 linter.verify(code, config);
9350 assert(spy.calledOnce);
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;"]);
9364 checker: { create: spy }
9368 rules: { "test/checker": "error" }
9371 linter.verify(code, config);
9372 assert(spy.calledOnce);
9377 describe("context.getSource()", () => {
9378 const code = TEST_CODE;
9380 it("should retrieve all text when used without parameters", () => {
9390 spy = sinon.spy(() => {
9391 assert.strictEqual(context.getSource(), TEST_CODE);
9393 return { Program: spy };
9399 rules: { "test/checker": "error" }
9402 linter.verify(code, config);
9403 assert(spy && spy.calledOnce);
9406 it("should retrieve all text for root node", () => {
9416 spy = sinon.spy(node => {
9417 assert.strictEqual(context.getSource(node), TEST_CODE);
9419 return { Program: spy };
9425 rules: { "test/checker": "error" }
9428 linter.verify(code, config);
9429 assert(spy && spy.calledOnce);
9432 it("should clamp to valid range when retrieving characters before start of source", () => {
9441 spy = sinon.spy(node => {
9442 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
9444 return { Program: spy };
9450 rules: { "test/checker": "error" }
9453 linter.verify(code, config);
9454 assert(spy && spy.calledOnce);
9457 it("should retrieve all text for binary expression", () => {
9466 spy = sinon.spy(node => {
9467 assert.strictEqual(context.getSource(node), "6 * 7");
9469 return { BinaryExpression: spy };
9475 rules: { "test/checker": "error" }
9478 linter.verify(code, config);
9479 assert(spy && spy.calledOnce);
9482 it("should retrieve all text plus two characters before for binary expression", () => {
9491 spy = sinon.spy(node => {
9492 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
9494 return { BinaryExpression: spy };
9500 rules: { "test/checker": "error" }
9503 linter.verify(code, config);
9504 assert(spy && spy.calledOnce);
9507 it("should retrieve all text plus one character after for binary expression", () => {
9516 spy = sinon.spy(node => {
9517 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
9519 return { BinaryExpression: spy };
9525 rules: { "test/checker": "error" }
9528 linter.verify(code, config);
9529 assert(spy && spy.calledOnce);
9532 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
9541 spy = sinon.spy(node => {
9542 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
9544 return { BinaryExpression: spy };
9550 rules: { "test/checker": "error" }
9553 linter.verify(code, config);
9554 assert(spy && spy.calledOnce);
9559 describe("context.getAncestors()", () => {
9560 const code = TEST_CODE;
9562 it("should retrieve all ancestors when used", () => {
9572 spy = sinon.spy(() => {
9573 const ancestors = context.getAncestors();
9575 assert.strictEqual(ancestors.length, 3);
9577 return { BinaryExpression: spy };
9583 rules: { "test/checker": "error" }
9586 linter.verify(code, config, filename, true);
9587 assert(spy && spy.calledOnce);
9590 it("should retrieve empty ancestors for root node", () => {
9599 spy = sinon.spy(() => {
9600 const ancestors = context.getAncestors();
9602 assert.strictEqual(ancestors.length, 0);
9605 return { Program: spy };
9611 rules: { "test/checker": "error" }
9614 linter.verify(code, config);
9615 assert(spy && spy.calledOnce);
9619 describe("context.getNodeByRangeIndex()", () => {
9620 const code = TEST_CODE;
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");
9632 checker: { create: spy }
9636 rules: { "test/checker": "error" }
9639 linter.verify(code, config);
9640 assert(spy.calledOnce);
9643 it("should retrieve a node containing the given index", () => {
9644 const spy = sinon.spy(context => {
9645 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
9653 checker: { create: spy }
9657 rules: { "test/checker": "error" }
9660 linter.verify(code, config);
9661 assert(spy.calledOnce);
9664 it("should retrieve a node that is exactly the given index", () => {
9665 const spy = sinon.spy(context => {
9666 const node = context.getNodeByRangeIndex(13);
9668 assert.strictEqual(node.type, "Literal");
9669 assert.strictEqual(node.value, 6);
9677 checker: { create: spy }
9681 rules: { "test/checker": "error" }
9684 linter.verify(code, config);
9685 assert(spy.calledOnce);
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");
9698 checker: { create: spy }
9702 rules: { "test/checker": "error" }
9705 linter.verify(code, config);
9706 assert(spy.calledOnce);
9709 it("should retrieve the deepest node containing the given index", () => {
9710 const spy = sinon.spy(context => {
9711 const node1 = context.getNodeByRangeIndex(14);
9713 assert.strictEqual(node1.type, "BinaryExpression");
9715 const node2 = context.getNodeByRangeIndex(3);
9717 assert.strictEqual(node2.type, "VariableDeclaration");
9725 checker: { create: spy }
9729 rules: { "test/checker": "error" }
9732 linter.verify(code, config);
9733 assert(spy.calledOnce);
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);
9740 assert.isNull(node1);
9742 const node2 = context.getNodeByRangeIndex(-99);
9744 assert.isNull(node2);
9752 checker: { create: spy }
9756 rules: { "test/checker": "error" }
9759 linter.verify(code, config);
9760 assert(spy.calledOnce);
9764 describe("context.getScope()", () => {
9765 const codeToTestScope = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
9767 it("should retrieve the global scope correctly from a Program", () => {
9776 spy = sinon.spy(() => {
9777 const scope = context.getScope();
9779 assert.strictEqual(scope.type, "global");
9781 return { Program: spy };
9790 rules: { "test/checker": "error" }
9793 linter.verify(codeToTestScope, config);
9794 assert(spy && spy.calledOnce);
9797 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
9806 spy = sinon.spy(() => {
9807 const scope = context.getScope();
9809 assert.strictEqual(scope.type, "function");
9811 return { FunctionDeclaration: spy };
9820 rules: { "test/checker": "error" }
9823 linter.verify(codeToTestScope, config);
9824 assert(spy && spy.calledTwice);
9827 it("should retrieve the function scope correctly from a LabeledStatement", () => {
9836 spy = sinon.spy(() => {
9837 const scope = context.getScope();
9839 assert.strictEqual(scope.type, "function");
9840 assert.strictEqual(scope.block.id.name, "foo");
9842 return { LabeledStatement: spy };
9851 rules: { "test/checker": "error" }
9855 linter.verify(codeToTestScope, config);
9856 assert(spy && spy.calledOnce);
9859 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
9868 spy = sinon.spy(() => {
9869 const scope = context.getScope();
9871 assert.strictEqual(scope.type, "function");
9872 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
9875 return { ReturnStatement: spy };
9884 rules: { "test/checker": "error" }
9888 linter.verify(codeToTestScope, config);
9889 assert(spy && spy.calledOnce);
9892 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
9901 spy = sinon.spy(() => {
9902 const scope = context.getScope();
9904 assert.strictEqual(scope.type, "switch");
9905 assert.strictEqual(scope.block.type, "SwitchStatement");
9908 return { SwitchStatement: spy };
9917 rules: { "test/checker": "error" }
9920 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
9921 assert(spy && spy.calledOnce);
9924 it("should retrieve the function scope correctly from within a BlockStatement", () => {
9933 spy = sinon.spy(() => {
9934 const scope = context.getScope();
9936 assert.strictEqual(scope.type, "block");
9937 assert.strictEqual(scope.block.type, "BlockStatement");
9940 return { BlockStatement: spy };
9949 rules: { "test/checker": "error" }
9953 linter.verify("var x; {let y = 1}", config);
9954 assert(spy && spy.calledOnce);
9957 it("should retrieve the function scope correctly from within a nested block statement", () => {
9966 spy = sinon.spy(() => {
9967 const scope = context.getScope();
9969 assert.strictEqual(scope.type, "block");
9970 assert.strictEqual(scope.block.type, "BlockStatement");
9973 return { BlockStatement: spy };
9982 rules: { "test/checker": "error" }
9986 linter.verify("if (true) { let x = 1 }", config);
9987 assert(spy && spy.calledOnce);
9990 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
9999 spy = sinon.spy(() => {
10000 const scope = context.getScope();
10002 assert.strictEqual(scope.type, "function");
10003 assert.strictEqual(scope.block.type, "FunctionDeclaration");
10006 return { FunctionDeclaration: spy };
10015 rules: { "test/checker": "error" }
10019 linter.verify("function foo() {}", config);
10020 assert(spy && spy.calledOnce);
10023 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
10032 spy = sinon.spy(() => {
10033 const scope = context.getScope();
10035 assert.strictEqual(scope.type, "function");
10036 assert.strictEqual(scope.block.type, "FunctionExpression");
10039 return { FunctionExpression: spy };
10048 rules: { "test/checker": "error" }
10052 linter.verify("(function foo() {})();", config);
10053 assert(spy && spy.calledOnce);
10056 it("should retrieve the catch scope correctly from within a CatchClause", () => {
10065 spy = sinon.spy(() => {
10066 const scope = context.getScope();
10068 assert.strictEqual(scope.type, "catch");
10069 assert.strictEqual(scope.block.type, "CatchClause");
10072 return { CatchClause: spy };
10081 rules: { "test/checker": "error" }
10084 linter.verify("try {} catch (err) {}", config);
10085 assert(spy && spy.calledOnce);
10088 it("should retrieve module scope correctly from an ES6 module", () => {
10097 spy = sinon.spy(() => {
10098 const scope = context.getScope();
10100 assert.strictEqual(scope.type, "module");
10103 return { AssignmentExpression: spy };
10111 sourceType: "module"
10113 rules: { "test/checker": "error" }
10117 linter.verify("var foo = {}; foo.bar = 1;", config);
10118 assert(spy && spy.calledOnce);
10121 it("should retrieve function scope correctly when sourceType is commonjs", () => {
10130 spy = sinon.spy(() => {
10131 const scope = context.getScope();
10133 assert.strictEqual(scope.type, "function");
10136 return { AssignmentExpression: spy };
10144 sourceType: "commonjs"
10146 rules: { "test/checker": "error" }
10149 linter.verify("var foo = {}; foo.bar = 1;", config);
10150 assert(spy && spy.calledOnce);
10153 describe("Scope Internals", () => {
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.
10162 function getScope(codeToEvaluate, astSelector, ecmaVersion = 5) {
10170 create: context => ({
10171 [astSelector](node0) {
10173 scope = context.getScope();
10182 sourceType: "script"
10184 rules: { "test/get-scope": "error" }
10192 return { node, scope };
10195 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
10196 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
10198 assert.strictEqual(scope.type, "function");
10199 assert.strictEqual(scope.block, node);
10202 it("should return 'function' scope on FunctionExpression (ES5)", () => {
10203 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
10205 assert.strictEqual(scope.type, "function");
10206 assert.strictEqual(scope.block, node);
10209 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
10210 const { node, scope } = getScope("function f() {}", "BlockStatement");
10212 assert.strictEqual(scope.type, "function");
10213 assert.strictEqual(scope.block, node.parent);
10216 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
10217 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
10219 assert.strictEqual(scope.type, "function");
10220 assert.strictEqual(scope.block, node.parent);
10223 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
10224 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
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"]);
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);
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"]);
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);
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"]);
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");
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"]);
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);
10264 assert.strictEqual(scope.type, "switch");
10265 assert.strictEqual(scope.block, node);
10266 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
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");
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"]);
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);
10280 assert.strictEqual(scope.type, "switch");
10281 assert.strictEqual(scope.block, node.parent);
10282 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
10285 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
10286 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
10288 assert.strictEqual(scope.type, "catch");
10289 assert.strictEqual(scope.block, node);
10290 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
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);
10296 assert.strictEqual(scope.type, "catch");
10297 assert.strictEqual(scope.block, node);
10298 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
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");
10304 assert.strictEqual(scope.type, "catch");
10305 assert.strictEqual(scope.block, node.parent);
10306 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
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);
10312 assert.strictEqual(scope.type, "block");
10313 assert.strictEqual(scope.block, node);
10314 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
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");
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"]);
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);
10328 assert.strictEqual(scope.type, "for");
10329 assert.strictEqual(scope.block, node);
10330 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
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");
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"]);
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);
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"]);
10351 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
10352 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
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"]);
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);
10362 assert.strictEqual(scope.type, "for");
10363 assert.strictEqual(scope.block, node);
10364 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
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");
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"]);
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);
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"]);
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);
10388 assert.strictEqual(scope.type, "for");
10389 assert.strictEqual(scope.block, node);
10390 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
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);
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"]);
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);
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]);
10416 describe("Variables and references", () => {
10419 "function foo() { b; }",
10439 create: context => ({
10441 scope = context.getScope();
10450 globals: { e: true, f: false },
10451 sourceType: "script",
10459 linter.verify(code, config);
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);
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"));
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"));
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"));
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"));
10533 describe("context.getDeclaredVariables(node)", () => {
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.
10542 function verify(code, type, expectedNamesList) {
10552 * Assert `context
.getDeclaredVariables(node
)` is empty.
10553 * @param {ASTNode} node A node to check.
10556 function checkEmpty(node) {
10557 assert.strictEqual(0, context.getDeclaredVariables(node).length);
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
10609 rule[type] = function(node) {
10610 const expectedNames = expectedNamesList.shift();
10611 const variables = context.getDeclaredVariables(node);
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);
10630 sourceType: "module"
10637 linter.verify(code, config);
10639 // Check all expected names are asserted.
10640 assert.strictEqual(0, expectedNamesList.length);
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 = [
10648 ["g", "h", "i", "j", "k"],
10652 verify(code, "VariableDeclaration", namesList);
10655 it("VariableDeclaration (on for-in/of loop)", () => {
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 = [
10666 verify(code, "VariableDeclaration", namesList);
10669 it("VariableDeclarator", () => {
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 = [
10681 verify(code, "VariableDeclarator", namesList);
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"]
10691 verify(code, "FunctionDeclaration", namesList);
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"],
10702 verify(code, "FunctionExpression", namesList);
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"]
10712 verify(code, "ArrowFunctionExpression", namesList);
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.
10722 verify(code, "ClassDeclaration", namesList);
10725 it("ClassExpression", () => {
10726 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
10727 const namesList = [
10732 verify(code, "ClassExpression", namesList);
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 = [
10742 verify(code, "CatchClause", namesList);
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 = [
10753 verify(code, "ImportDeclaration", namesList);
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 = [
10763 verify(code, "ImportSpecifier", namesList);
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 = [
10772 verify(code, "ImportDefaultSpecifier", namesList);
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 = [
10781 verify(code, "ImportNamespaceSpecifier", namesList);
10785 describe("context.markVariableAsUsed()", () => {
10787 it("should mark variables in current scope as used", () => {
10788 const code = "var a = 1, b = 2;";
10797 spy = sinon.spy(() => {
10798 assert.isTrue(context.markVariableAsUsed("a"));
10800 const scope = context.getScope();
10802 assert.isTrue(getVariable(scope, "a").eslintUsed);
10803 assert.notOk(getVariable(scope, "b").eslintUsed);
10806 return { "Program:exit": spy };
10813 sourceType: "script"
10815 rules: { "test/checker": "error" }
10818 linter.verify(code, config);
10819 assert(spy && spy.calledOnce);
10822 it("should mark variables in function args as used", () => {
10823 const code = "function abc(a, b) { return 1; }";
10832 spy = sinon.spy(() => {
10833 assert.isTrue(context.markVariableAsUsed("a"));
10835 const scope = context.getScope();
10837 assert.isTrue(getVariable(scope, "a").eslintUsed);
10838 assert.notOk(getVariable(scope, "b").eslintUsed);
10841 return { ReturnStatement: spy };
10847 rules: { "test/checker": "error" }
10850 linter.verify(code, config);
10851 assert(spy && spy.calledOnce);
10854 it("should mark variables in higher scopes as used", () => {
10855 const code = "var a, b; function abc() { return 1; }";
10856 let returnSpy, exitSpy;
10864 returnSpy = sinon.spy(() => {
10865 assert.isTrue(context.markVariableAsUsed("a"));
10867 exitSpy = sinon.spy(() => {
10868 const scope = context.getScope();
10870 assert.isTrue(getVariable(scope, "a").eslintUsed);
10871 assert.notOk(getVariable(scope, "b").eslintUsed);
10874 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
10881 sourceType: "script"
10883 rules: { "test/checker": "error" }
10886 linter.verify(code, config);
10887 assert(returnSpy && returnSpy.calledOnce);
10888 assert(exitSpy && exitSpy.calledOnce);
10891 it("should mark variables as used when sourceType is commonjs", () => {
10892 const code = "var a = 1, b = 2;";
10901 spy = sinon.spy(() => {
10902 const globalScope = context.getScope(),
10903 childScope = globalScope.childScopes[0];
10905 assert.isTrue(context.markVariableAsUsed("a"), "Call to markVariableAsUsed should return true");
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.");
10911 return { "Program:exit": spy };
10918 sourceType: "commonjs"
10920 rules: { "test/checker": "error" }
10923 linter.verify(code, config);
10924 assert(spy && spy.calledOnce, "Spy wasn't called.");
10927 it("should mark variables in modules as used", () => {
10928 const code = "var a = 1, b = 2;";
10937 spy = sinon.spy(() => {
10938 const globalScope = context.getScope(),
10939 childScope = globalScope.childScopes[0];
10941 assert.isTrue(context.markVariableAsUsed("a"));
10943 assert.isTrue(getVariable(childScope, "a").eslintUsed);
10944 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
10947 return { "Program:exit": spy };
10955 sourceType: "module"
10957 rules: { "test/checker": "error" }
10960 linter.verify(code, config);
10961 assert(spy && spy.calledOnce);
10964 it("should return false if the given variable is not found", () => {
10965 const code = "var a = 1, b = 2;";
10974 spy = sinon.spy(() => {
10975 assert.isFalse(context.markVariableAsUsed("c"));
10978 return { "Program:exit": spy };
10984 rules: { "test/checker": "error" }
10987 linter.verify(code, config);
10988 assert(spy && spy.calledOnce);
10992 describe("context.getCwd()", () => {
10993 const code = "a;\nb;";
10994 const baseConfig = { rules: { "test/checker": "error" } };
10996 it("should get cwd correctly in the context", () => {
10998 const linterWithOption = new Linter({ cwd, configType: "flat" });
11006 spy = sinon.spy(() => {
11007 assert.strictEqual(context.getCwd(), cwd);
11009 return { Program: spy };
11018 linterWithOption.verify(code, config);
11019 assert(spy && spy.calledOnce);
11022 it("should assign process.cwd() to it if cwd is undefined", () => {
11024 const linterWithOption = new Linter({ configType: "flat" });
11033 spy = sinon.spy(() => {
11034 assert.strictEqual(context.getCwd(), process.cwd());
11036 return { Program: spy };
11045 linterWithOption.verify(code, config);
11046 assert(spy && spy.calledOnce);
11049 it("should assign process.cwd() to it if the option is undefined", () => {
11058 spy = sinon.spy(() => {
11059 assert.strictEqual(context.getCwd(), process.cwd());
11061 return { Program: spy };
11070 linter.verify(code, config);
11071 assert(spy && spy.calledOnce);
11075 describe("context.cwd", () => {
11076 const code = "a;\nb;";
11077 const baseConfig = { rules: { "test/checker": "error" } };
11079 it("should get cwd correctly in the context", () => {
11081 const linterWithOption = new Linter({ cwd, configType: "flat" });
11089 spy = sinon.spy(() => {
11090 assert.strictEqual(context.cwd, cwd);
11092 return { Program: spy };
11101 linterWithOption.verify(code, config);
11102 assert(spy && spy.calledOnce);
11105 it("should assign process.cwd() to it if cwd is undefined", () => {
11107 const linterWithOption = new Linter({ configType: "flat" });
11116 spy = sinon.spy(() => {
11117 assert.strictEqual(context.getCwd(), context.cwd);
11118 assert.strictEqual(context.cwd, process.cwd());
11120 return { Program: spy };
11129 linterWithOption.verify(code, config);
11130 assert(spy && spy.calledOnce);
11133 it("should assign process.cwd() to it if the option is undefined", () => {
11142 spy = sinon.spy(() => {
11143 assert.strictEqual(context.getCwd(), context.cwd);
11144 assert.strictEqual(context.cwd, process.cwd());
11146 return { Program: spy };
11155 linter.verify(code, config);
11156 assert(spy && spy.calledOnce);
11162 describe("Rule Severity", () => {
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"],
11173 configs.normalizeSync();
11174 const messages = linter.verify("foo", configs, filename, true);
11175 const suppressedMessages = linter.getSuppressedMessages();
11177 assert.strictEqual(messages.length, 1, "Message length is wrong");
11178 assert.strictEqual(messages[0].ruleId, ruleId);
11180 assert.strictEqual(suppressedMessages.length, 0);
11183 it("rule should run as warning when set to 1 with a plain array", () => {
11184 const ruleId = "semi",
11186 files: ["**/*.js"],
11192 const messages = linter.verify("foo", configs, filename, true);
11193 const suppressedMessages = linter.getSuppressedMessages();
11195 assert.strictEqual(messages.length, 1, "Message length is wrong");
11196 assert.strictEqual(messages[0].ruleId, ruleId);
11198 assert.strictEqual(suppressedMessages.length, 0);
11201 it("rule should run as warning when set to 1 with an object", () => {
11202 const ruleId = "semi",
11204 files: ["**/*.js"],
11210 const messages = linter.verify("foo", config, filename, true);
11211 const suppressedMessages = linter.getSuppressedMessages();
11213 assert.strictEqual(messages.length, 1, "Message length is wrong");
11214 assert.strictEqual(messages[0].ruleId, ruleId);
11216 assert.strictEqual(suppressedMessages.length, 0);
11220 describe("Code with a hashbang comment", () => {
11221 const code = "#!bin/program\n\nvar foo;;";
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();
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);
11233 assert.strictEqual(suppressedMessages.length, 0);
11236 it("should have a comment with the hashbang in it", () => {
11237 const spy = sinon.spy(context => {
11238 const comments = context.getAllComments();
11240 assert.strictEqual(comments.length, 1);
11241 assert.strictEqual(comments[0].type, "Shebang");
11249 checker: { create: spy }
11254 "test/checker": "error"
11258 linter.verify(code, config);
11259 assert(spy.calledOnce);
11263 describe("Options", () => {
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");
11276 checker: { create: filenameChecker }
11281 "test/checker": "error"
11285 linter.verify("foo;", config, { filename: "foo.js" });
11286 assert(filenameChecker.calledOnce);
11289 it("should allow filename to be passed as third argument", () => {
11290 const filenameChecker = sinon.spy(context => {
11291 assert.strictEqual(context.filename, "bar.js");
11299 checker: { create: filenameChecker }
11304 "test/checker": "error"
11308 linter.verify("foo;", config, "bar.js");
11309 assert(filenameChecker.calledOnce);
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>");
11322 checker: { create: filenameChecker }
11327 "test/checker": "error"
11331 linter.verify("foo;", config, {});
11332 assert(filenameChecker.calledOnce);
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>");
11345 checker: { create: filenameChecker }
11350 "test/checker": "error"
11354 linter.verify("foo;", config);
11355 assert(filenameChecker.calledOnce);
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");
11371 checker: { create: physicalFilenameChecker }
11376 "test/checker": "error"
11380 linter.verify("foo;", config, { filename: "foo.js" });
11381 assert(physicalFilenameChecker.calledOnce);
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>");
11395 checker: { create: physicalFilenameChecker }
11400 "test/checker": "error"
11404 linter.verify("foo;", config, {});
11405 assert(physicalFilenameChecker.calledOnce);
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>");
11419 checker: { create: physicalFilenameChecker }
11424 "test/checker": "error"
11428 linter.verify("foo;", config);
11429 assert(physicalFilenameChecker.calledOnce);
11435 describe("Inline Directives", () => {
11437 describe("/*global*/ Comments", () => {
11439 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
11441 it("variables should be available in global scope", () => {
11443 /*global a b:true c:false d:readable e:writeable Math:off */
11446 /* global ConfigGlobal : readable */
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");
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);
11486 return { Program: spy };
11492 rules: { "test/checker": "error" },
11494 globals: { Array: "off", ConfigGlobal: "writeable" }
11498 linter.verify(code, config);
11499 assert(spy && spy.calledOnce);
11503 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
11504 const code = "/* global a b : true c: false*/";
11506 it("variables should be available in global scope", () => {
11515 spy = sinon.spy(() => {
11516 const scope = context.getScope(),
11517 a = getVariable(scope, "a"),
11518 b = getVariable(scope, "b"),
11519 c = getVariable(scope, "c");
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);
11529 return { Program: spy };
11535 rules: { "test/checker": "error" }
11538 linter.verify(code, config);
11539 assert(spy && spy.calledOnce);
11543 describe("when evaluating code containing a line comment", () => {
11544 const code = "//global a \n function f() {}";
11546 it("should not introduce a global variable", () => {
11555 spy = sinon.spy(() => {
11556 const scope = context.getScope();
11558 assert.strictEqual(getVariable(scope, "a"), null);
11561 return { Program: spy };
11567 rules: { "test/checker": "error" }
11571 linter.verify(code, config);
11572 assert(spy && spy.calledOnce);
11576 describe("when evaluating code containing normal block comments", () => {
11577 const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
11579 it("should not introduce a global variable", () => {
11588 spy = sinon.spy(() => {
11589 const scope = context.getScope();
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);
11597 return { Program: spy };
11603 rules: { "test/checker": "error" }
11607 linter.verify(code, config);
11608 assert(spy && spy.calledOnce);
11612 it("should attach a \"/*global\" comment node to declared variables", () => {
11613 const code = "/* global foo */\n/* global bar, baz */";
11620 create: context => ({
11622 const scope = context.getScope();
11623 const sourceCode = context.sourceCode;
11624 const comments = sourceCode.getAllComments();
11626 assert.strictEqual(context.getSourceCode(), sourceCode);
11627 assert.strictEqual(2, comments.length);
11629 const foo = getVariable(scope, "foo");
11631 assert.strictEqual(foo.eslintExplicitGlobal, true);
11632 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
11634 const bar = getVariable(scope, "bar");
11636 assert.strictEqual(bar.eslintExplicitGlobal, true);
11637 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
11639 const baz = getVariable(scope, "baz");
11641 assert.strictEqual(baz.eslintExplicitGlobal, true);
11642 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
11651 rules: { "test/test": "error" }
11655 linter.verify(code, config);
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();
11663 assert.deepStrictEqual(results, [
11667 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
11675 ruleId: "no-undef",
11676 messageId: "undef",
11678 message: "'foo' is not defined.",
11683 nodeType: "Identifier"
11687 assert.strictEqual(suppressedMessages.length, 0);
11692 describe("/*exported*/ Comments", () => {
11694 it("we should behave nicely when no matching variable is found", () => {
11695 const code = "/* exported horse */";
11696 const config = { rules: {} };
11698 linter.verify(code, config, filename, true);
11701 it("variables should be exported", () => {
11702 const code = "/* exported horse */\n\nvar horse = 'circus'";
11711 spy = sinon.spy(() => {
11712 const scope = context.getScope(),
11713 horse = getVariable(scope, "horse");
11715 assert.strictEqual(horse.eslintUsed, true);
11718 return { Program: spy };
11725 sourceType: "script"
11727 rules: { "test/checker": "error" }
11730 linter.verify(code, config);
11731 assert(spy && spy.calledOnce);
11734 it("undefined variables should not be exported", () => {
11735 const code = "/* exported horse */\n\nhorse = 'circus'";
11743 spy = sinon.spy(() => {
11744 const scope = context.getScope(),
11745 horse = getVariable(scope, "horse");
11747 assert.strictEqual(horse, null);
11750 return { Program: spy };
11757 sourceType: "script"
11759 rules: { "test/checker": "error" }
11762 linter.verify(code, config);
11763 assert(spy && spy.calledOnce);
11766 it("variables should be exported in strict mode", () => {
11767 const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'";
11775 spy = sinon.spy(() => {
11776 const scope = context.getScope(),
11777 horse = getVariable(scope, "horse");
11779 assert.strictEqual(horse.eslintUsed, true);
11782 return { Program: spy };
11789 sourceType: "script"
11791 rules: { "test/checker": "error" }
11794 linter.verify(code, config);
11795 assert(spy && spy.calledOnce);
11798 it("variables should not be exported in the es6 module environment", () => {
11799 const code = "/* exported horse */\nvar horse = 'circus'";
11807 spy = sinon.spy(() => {
11808 const scope = context.getScope(),
11809 horse = getVariable(scope, "horse");
11811 assert.strictEqual(horse, null); // there is no global scope at all
11814 return { Program: spy };
11822 sourceType: "module"
11824 rules: { "test/checker": "error" }
11827 linter.verify(code, config);
11828 assert(spy && spy.calledOnce);
11831 it("variables should not be exported when in a commonjs file", () => {
11832 const code = "/* exported horse */\nvar horse = 'circus'";
11840 spy = sinon.spy(() => {
11841 const scope = context.getScope(),
11842 horse = getVariable(scope, "horse");
11844 assert.strictEqual(horse, null); // there is no global scope at all
11847 return { Program: spy };
11854 sourceType: "commonjs"
11856 rules: { "test/checker": "error" }
11859 linter.verify(code, config);
11860 assert(spy && spy.calledOnce);
11864 describe("/*eslint*/ Comments", () => {
11865 describe("when evaluating code with comments to enable rules", () => {
11867 it("should report a violation", () => {
11868 const code = "/*eslint no-alert:1*/ alert('test');";
11869 const config = { rules: {} };
11871 const messages = linter.verify(code, config, filename);
11872 const suppressedMessages = linter.getSuppressedMessages();
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");
11879 assert.strictEqual(suppressedMessages.length, 0);
11882 it("rules should not change initial config", () => {
11885 sourceType: "script"
11887 rules: { strict: 2 }
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();
11894 assert.strictEqual(messages.length, 0);
11895 assert.strictEqual(suppressedMessages.length, 0);
11897 messages = linter.verify(codeB, config, filename, false);
11898 suppressedMessages = linter.getSuppressedMessages();
11900 assert.strictEqual(messages.length, 1);
11901 assert.strictEqual(suppressedMessages.length, 0);
11904 it("rules should not change initial config", () => {
11907 sourceType: "script"
11909 rules: { quotes: [2, "double"] }
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();
11916 assert.strictEqual(messages.length, 0);
11917 assert.strictEqual(suppressedMessages.length, 0);
11919 messages = linter.verify(codeB, config, filename, false);
11920 suppressedMessages = linter.getSuppressedMessages();
11922 assert.strictEqual(messages.length, 1);
11923 assert.strictEqual(suppressedMessages.length, 0);
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();
11933 assert.strictEqual(messages.length, 0);
11934 assert.strictEqual(suppressedMessages.length, 0);
11936 messages = linter.verify(codeB, config, filename, false);
11937 suppressedMessages = linter.getSuppressedMessages();
11939 assert.strictEqual(messages.length, 1);
11940 assert.strictEqual(suppressedMessages.length, 0);
11943 it("rules should not change initial config", () => {
11946 sourceType: "script"
11948 rules: { "no-unused-vars": [2, { vars: "all" }] }
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();
11955 assert.strictEqual(messages.length, 0);
11956 assert.strictEqual(suppressedMessages.length, 0);
11958 messages = linter.verify(codeB, config, filename, false);
11959 suppressedMessages = linter.getSuppressedMessages();
11961 assert.strictEqual(messages.length, 1);
11962 assert.strictEqual(suppressedMessages.length, 0);
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();
11971 assert.deepStrictEqual(
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",
11987 assert.strictEqual(suppressedMessages.length, 0);
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();
11994 assert.deepStrictEqual(
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",
12010 assert.strictEqual(suppressedMessages.length, 0);
12014 describe("when evaluating code with comments to disable rules", () => {
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();
12021 assert.strictEqual(messages.length, 0);
12022 assert.strictEqual(suppressedMessages.length, 0);
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();
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);
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);
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);
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);
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();
12060 assert.strictEqual(messages.length, 0);
12061 assert.strictEqual(suppressedMessages.length, 0);
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);
12069 assert.throws(() => {
12070 linter.verify("", { rules: { foo: 2 } }, filename);
12071 }, /Key "rules": Key "foo":/u);
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');";
12079 it("should report a violation", () => {
12080 const config = { rules: {} };
12082 const messages = linter.verify(code, config, filename);
12083 const suppressedMessages = linter.getSuppressedMessages();
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");
12091 assert.strictEqual(suppressedMessages.length, 0);
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');";
12098 it("should report a violation", () => {
12099 const config = { rules: { "no-console": 1, "no-alert": 0 } };
12101 const messages = linter.verify(code, config, filename);
12102 const suppressedMessages = linter.getSuppressedMessages();
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");
12109 assert.strictEqual(suppressedMessages.length, 0);
12113 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
12123 create: context => ({
12125 if (node.value === "trigger violation") {
12126 context.report(node, "Reporting violation.");
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\";";
12142 const messages = linter.verify(code, config, filename);
12143 const suppressedMessages = linter.getSuppressedMessages();
12145 assert.strictEqual(messages.length, 0);
12146 assert.strictEqual(suppressedMessages.length, 0);
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 } };
12153 const messages = linter.verify(code, config, filename);
12154 const suppressedMessages = linter.getSuppressedMessages();
12156 assert.strictEqual(messages.length, 0);
12157 assert.strictEqual(suppressedMessages.length, 0);
12160 it("should report a violation when the report is right before the comment", () => {
12161 const code = " /* eslint-disable */ ";
12168 create: context => ({
12170 context.report({ loc: { line: 1, column: 0 }, message: "foo" });
12178 "test/checker": "error"
12182 const problems = linter.verify(code, config);
12183 const suppressedMessages = linter.getSuppressedMessages();
12185 assert.strictEqual(problems.length, 1);
12186 assert.strictEqual(problems[0].message, "foo");
12188 assert.strictEqual(suppressedMessages.length, 0);
12191 it("should not report a violation when the report is right at the start of the comment", () => {
12192 const code = " /* eslint-disable */ ";
12199 create: context => ({
12201 context.report({ loc: { line: 1, column: 1 }, message: "foo" });
12209 "test/checker": "error"
12213 const problems = linter.verify(code, config);
12214 const suppressedMessages = linter.getSuppressedMessages();
12216 assert.strictEqual(problems.length, 0);
12218 assert.strictEqual(suppressedMessages.length, 1);
12219 assert.strictEqual(suppressedMessages[0].message, "foo");
12220 assert.deepStrictEqual(suppressedMessages[0].suppressions, [{ kind: "directive", justification: "" }]);
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();
12230 assert.strictEqual(messages.length, 0);
12231 assert.strictEqual(suppressedMessages.length, 0);
12233 messages = linter.verify(codeB, config, filename, false);
12234 suppressedMessages = linter.getSuppressedMessages();
12236 assert.strictEqual(messages.length, 1);
12237 assert.strictEqual(suppressedMessages.length, 0);
12241 describe("when evaluating code with comments to enable and disable all reporting", () => {
12242 it("should report a violation", () => {
12245 "/*eslint-disable */",
12247 "/*eslint-enable */",
12250 const config = { rules: { "no-alert": 1 } };
12252 const messages = linter.verify(code, config, filename);
12253 const suppressedMessages = linter.getSuppressedMessages();
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);
12261 assert.strictEqual(suppressedMessages.length, 1);
12262 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12263 assert.strictEqual(suppressedMessages[0].line, 2);
12266 it("should not report a violation", () => {
12268 "/*eslint-disable */",
12272 const config = { rules: { "no-alert": 1 } };
12274 const messages = linter.verify(code, config, filename);
12275 const suppressedMessages = linter.getSuppressedMessages();
12277 assert.strictEqual(messages.length, 0);
12278 assert.strictEqual(suppressedMessages.length, 2);
12281 it("should not report a violation", () => {
12283 " alert('test1');/*eslint-disable */\n",
12285 " alert('test');\n",
12286 "/*eslint-enable */alert('test2');"
12288 const config = { rules: { "no-alert": 1 } };
12290 const messages = linter.verify(code, config, filename);
12291 const suppressedMessages = linter.getSuppressedMessages();
12293 assert.strictEqual(messages.length, 2);
12294 assert.strictEqual(messages[0].column, 21);
12295 assert.strictEqual(messages[1].column, 19);
12297 assert.strictEqual(suppressedMessages.length, 2);
12298 assert.strictEqual(suppressedMessages[0].column, 1);
12299 assert.strictEqual(suppressedMessages[1].column, 56);
12302 it("should report a violation", () => {
12305 "/*eslint-disable */",
12307 "/*eslint-disable */",
12309 "/*eslint-enable*/",
12311 "/*eslint-enable*/"
12314 const config = { rules: { "no-alert": 1 } };
12316 const messages = linter.verify(code, config, filename);
12317 const suppressedMessages = linter.getSuppressedMessages();
12319 assert.strictEqual(messages.length, 1);
12320 assert.strictEqual(suppressedMessages.length, 2);
12324 it("should not report a violation", () => {
12326 "/*eslint-disable */",
12327 "(function(){ var b = 44;})()",
12328 "/*eslint-enable */;any();"
12331 const config = { rules: { "no-unused-vars": 1 } };
12333 const messages = linter.verify(code, config, filename);
12334 const suppressedMessages = linter.getSuppressedMessages();
12336 assert.strictEqual(messages.length, 0);
12338 assert.strictEqual(suppressedMessages.length, 1);
12339 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
12342 it("should not report a violation", () => {
12344 "(function(){ /*eslint-disable */ var b = 44;})()",
12345 "/*eslint-enable */;any();"
12348 const config = { rules: { "no-unused-vars": 1 } };
12350 const messages = linter.verify(code, config, filename);
12351 const suppressedMessages = linter.getSuppressedMessages();
12353 assert.strictEqual(messages.length, 0);
12355 assert.strictEqual(suppressedMessages.length, 1);
12356 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
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');";
12363 it("should report a violation", () => {
12364 const config = { rules: { "no-console": 1, "no-alert": 0 } };
12366 const messages = linter.verify(code, config, filename);
12367 const suppressedMessages = linter.getSuppressedMessages();
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");
12374 assert.strictEqual(suppressedMessages.length, 0);
12378 describe("when evaluating code with comments to enable configurable rule", () => {
12379 const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');";
12381 it("should report a violation", () => {
12382 const config = { rules: { quotes: [2, "single"] } };
12384 const messages = linter.verify(code, config, filename);
12385 const suppressedMessages = linter.getSuppressedMessages();
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");
12392 assert.strictEqual(suppressedMessages.length, 0);
12396 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
12397 const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
12399 it("should report a violation", () => {
12400 const config = { rules: { quotes: [2, "single"] } };
12402 const messages = linter.verify(code, config, filename);
12403 const suppressedMessages = linter.getSuppressedMessages();
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");
12410 assert.strictEqual(suppressedMessages.length, 0);
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');";
12418 const config = { rules: { "no-alert": 1 } };
12420 const messages = linter.verify(code, config, filename);
12421 const suppressedMessages = linter.getSuppressedMessages();
12423 assert.strictEqual(messages.length, 2);
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
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);
12436 assert.strictEqual(messages[1].ruleId, "no-alert");
12437 assert.strictEqual(messages[1].message, "Unexpected alert.");
12438 assert.include(messages[1].nodeType, "CallExpression");
12440 assert.strictEqual(suppressedMessages.length, 0);
12443 it("should report a violation", () => {
12444 const code = "/*eslint no-alert:abc*/ alert('test');";
12446 const config = { rules: { "no-alert": 1 } };
12448 const messages = linter.verify(code, config, filename);
12449 const suppressedMessages = linter.getSuppressedMessages();
12451 assert.strictEqual(messages.length, 2);
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
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);
12464 assert.strictEqual(messages[1].ruleId, "no-alert");
12465 assert.strictEqual(messages[1].message, "Unexpected alert.");
12466 assert.include(messages[1].nodeType, "CallExpression");
12468 assert.strictEqual(suppressedMessages.length, 0);
12471 it("should report a violation", () => {
12472 const code = "/*eslint no-alert:0 2*/ alert('test');";
12474 const config = { rules: { "no-alert": 1 } };
12476 const messages = linter.verify(code, config, filename);
12477 const suppressedMessages = linter.getSuppressedMessages();
12479 assert.strictEqual(messages.length, 2);
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
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);
12492 assert.strictEqual(messages[1].ruleId, "no-alert");
12493 assert.strictEqual(messages[1].message, "Unexpected alert.");
12494 assert.include(messages[1].nodeType, "CallExpression");
12496 assert.strictEqual(suppressedMessages.length, 0);
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|-\\*-"}] */
12506 it("should not parse errors, should report a violation", () => {
12507 const messages = linter.verify(code, {}, filename);
12508 const suppressedMessages = linter.getSuppressedMessages();
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");
12515 assert.strictEqual(suppressedMessages.length, 0);
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");
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();
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");
12545 assert.strictEqual(suppressedMessages.length, 0);
12551 describe("/*eslint-disable*/ and /*eslint-enable*/", () => {
12552 it("should report a violation", () => {
12554 "/*eslint-disable no-alert */",
12556 "console.log('test');" // here
12558 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12560 const messages = linter.verify(code, config, filename);
12561 const suppressedMessages = linter.getSuppressedMessages();
12563 assert.strictEqual(messages.length, 1);
12564 assert.strictEqual(messages[0].ruleId, "no-console");
12566 assert.strictEqual(suppressedMessages.length, 1);
12567 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12570 it("should report no violation", () => {
12572 "/* eslint-disable quotes */",
12573 "console.log(\"foo\");",
12574 "/* eslint-enable quotes */"
12576 const config = { rules: { quotes: 2 } };
12578 const messages = linter.verify(code, config, filename);
12579 const suppressedMessages = linter.getSuppressedMessages();
12581 assert.strictEqual(messages.length, 0);
12582 assert.strictEqual(suppressedMessages.length, 0);
12585 it("should report a violation", () => {
12587 "/*eslint-disable no-alert, no-console */",
12589 "console.log('test');",
12590 "/*eslint-enable*/",
12592 "alert('test');", // here
12593 "console.log('test');" // here
12595 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12597 const messages = linter.verify(code, config, filename);
12598 const suppressedMessages = linter.getSuppressedMessages();
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);
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);
12613 it("should report a violation", () => {
12615 "/*eslint-disable no-alert */",
12617 "console.log('test');", // here
12618 "/*eslint-enable no-console */",
12621 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12623 const messages = linter.verify(code, config, filename);
12624 const suppressedMessages = linter.getSuppressedMessages();
12626 assert.strictEqual(messages.length, 1);
12627 assert.strictEqual(messages[0].ruleId, "no-console");
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);
12637 it("should report a violation", () => {
12639 "/*eslint-disable no-alert, no-console */",
12641 "console.log('test');",
12642 "/*eslint-enable no-alert*/",
12644 "alert('test');", // here
12645 "console.log('test');"
12647 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12649 const messages = linter.verify(code, config, filename);
12650 const suppressedMessages = linter.getSuppressedMessages();
12652 assert.strictEqual(messages.length, 1);
12653 assert.strictEqual(messages[0].ruleId, "no-alert");
12654 assert.strictEqual(messages[0].line, 5);
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);
12666 it("should report a violation", () => {
12668 "/*eslint-disable no-alert */",
12670 "/*eslint-disable no-console */",
12672 "console.log('test');",
12673 "/*eslint-enable */",
12675 "alert('test');", // here
12676 "console.log('test');", // here
12678 "/*eslint-enable */",
12680 "alert('test');", // here
12681 "console.log('test');", // here
12683 "/*eslint-enable*/"
12685 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12687 const messages = linter.verify(code, config, filename);
12688 const suppressedMessages = linter.getSuppressedMessages();
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);
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);
12707 it("should report a violation", () => {
12709 "/*eslint-disable no-alert, no-console */",
12711 "console.log('test');",
12713 "/*eslint-enable no-alert */",
12715 "alert('test');", // here
12716 "console.log('test');",
12718 "/*eslint-enable no-console */",
12720 "alert('test');", // here
12721 "console.log('test');", // here
12722 "/*eslint-enable no-console */"
12724 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12726 const messages = linter.verify(code, config, filename);
12727 const suppressedMessages = linter.getSuppressedMessages();
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);
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);
12746 it("should report a violation when severity is warn", () => {
12748 "/*eslint-disable no-alert, no-console */",
12750 "console.log('test');",
12752 "/*eslint-enable no-alert */",
12754 "alert('test');", // here
12755 "console.log('test');",
12757 "/*eslint-enable no-console */",
12759 "alert('test');", // here
12760 "console.log('test');", // here
12761 "/*eslint-enable no-console */"
12763 const config = { rules: { "no-alert": "warn", "no-console": "warn" } };
12765 const messages = linter.verify(code, config, filename);
12766 const suppressedMessages = linter.getSuppressedMessages();
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);
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);
12785 it("should report no violation", () => {
12787 "/*eslint-disable no-unused-vars */",
12788 "var foo; // eslint-disable-line no-unused-vars",
12790 "/* eslint-enable no-unused-vars */" // here
12792 const config = { rules: { "no-unused-vars": 2 } };
12794 const messages = linter.verify(code, config, filename);
12795 const suppressedMessages = linter.getSuppressedMessages();
12797 assert.strictEqual(messages.length, 0);
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);
12808 describe("/*eslint-disable-line*/", () => {
12810 it("should report a violation", () => {
12812 "alert('test'); // eslint-disable-line no-alert",
12813 "console.log('test');" // here
12822 const messages = linter.verify(code, config, filename);
12823 const suppressedMessages = linter.getSuppressedMessages();
12825 assert.strictEqual(messages.length, 1);
12826 assert.strictEqual(messages[0].ruleId, "no-console");
12827 assert.strictEqual(messages[0].line, 2);
12829 assert.strictEqual(suppressedMessages.length, 1);
12830 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12831 assert.strictEqual(suppressedMessages[0].line, 1);
12834 it("should report a violation", () => {
12836 "alert('test'); // eslint-disable-line no-alert",
12837 "console.log('test'); // eslint-disable-line no-console",
12838 "alert('test');" // here
12847 const messages = linter.verify(code, config, filename);
12848 const suppressedMessages = linter.getSuppressedMessages();
12850 assert.strictEqual(messages.length, 1);
12851 assert.strictEqual(messages[0].ruleId, "no-alert");
12852 assert.strictEqual(messages[0].line, 3);
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);
12861 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
12863 "/* eslint-disable-line",
12865 "*/ console.log('test');" // here
12873 const messages = linter.verify(code, config, filename);
12874 const suppressedMessages = linter.getSuppressedMessages();
12876 assert.strictEqual(messages.length, 2);
12877 assert.strictEqual(messages[1].ruleId, "no-console");
12879 assert.strictEqual(suppressedMessages.length, 0);
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", () => {
12884 "alert('test'); /* eslint-disable-line ",
12893 const messages = linter.verify(code, config);
12894 const suppressedMessages = linter.getSuppressedMessages();
12896 assert.deepStrictEqual(messages, [
12898 ruleId: "no-alert",
12904 message: "Unexpected alert.",
12905 messageId: "unexpected",
12906 nodeType: "CallExpression"
12911 message: "eslint-disable-line comment should not span multiple lines.",
12920 assert.strictEqual(suppressedMessages.length, 0);
12923 it("should not report a violation for eslint-disable-line in block comment", () => {
12925 "alert('test'); // eslint-disable-line no-alert",
12926 "alert('test'); /*eslint-disable-line no-alert*/"
12934 const messages = linter.verify(code, config, filename);
12935 const suppressedMessages = linter.getSuppressedMessages();
12937 assert.strictEqual(messages.length, 0);
12938 assert.strictEqual(suppressedMessages.length, 2);
12941 it("should not report a violation", () => {
12943 "alert('test'); // eslint-disable-line no-alert",
12944 "console.log('test'); // eslint-disable-line no-console"
12953 const messages = linter.verify(code, config, filename);
12954 const suppressedMessages = linter.getSuppressedMessages();
12956 assert.strictEqual(messages.length, 0);
12957 assert.strictEqual(suppressedMessages.length, 2);
12960 it("should not report a violation", () => {
12962 "alert('test') // eslint-disable-line no-alert, quotes, semi",
12963 "console.log('test'); // eslint-disable-line"
12968 quotes: [1, "double"],
12969 semi: [1, "always"],
12974 const messages = linter.verify(code, config, filename);
12975 const suppressedMessages = linter.getSuppressedMessages();
12977 assert.strictEqual(messages.length, 0);
12978 assert.strictEqual(suppressedMessages.length, 5);
12981 it("should not report a violation", () => {
12983 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
12984 "console.log('test'); /* eslint-disable-line */"
12989 quotes: [1, "double"],
12990 semi: [1, "always"],
12995 const messages = linter.verify(code, config, filename);
12996 const suppressedMessages = linter.getSuppressedMessages();
12998 assert.strictEqual(messages.length, 0);
12999 assert.strictEqual(suppressedMessages.length, 5);
13002 it("should ignore violations of multiple rules when specified in mixed comments", () => {
13004 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
13009 quotes: [1, "single"]
13012 const messages = linter.verify(code, config, filename);
13013 const suppressedMessages = linter.getSuppressedMessages();
13015 assert.strictEqual(messages.length, 0);
13017 assert.strictEqual(suppressedMessages.length, 2);
13018 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13019 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
13022 it("should report no violation", () => {
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"
13030 const config = { rules: { "no-unused-vars": 2 } };
13032 const messages = linter.verify(code, config, filename);
13033 const suppressedMessages = linter.getSuppressedMessages();
13035 assert.strictEqual(messages.length, 0);
13036 assert.strictEqual(suppressedMessages.length, 5);
13041 describe("/*eslint-disable-next-line*/", () => {
13042 it("should ignore violation of specified rule on next line", () => {
13044 "// eslint-disable-next-line no-alert",
13046 "console.log('test');"
13054 const messages = linter.verify(code, config, filename);
13055 const suppressedMessages = linter.getSuppressedMessages();
13057 assert.strictEqual(messages.length, 1);
13058 assert.strictEqual(messages[0].ruleId, "no-console");
13060 assert.strictEqual(suppressedMessages.length, 1);
13061 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13064 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
13066 "/* eslint-disable-next-line no-alert */",
13068 "console.log('test');"
13076 const messages = linter.verify(code, config, filename);
13077 const suppressedMessages = linter.getSuppressedMessages();
13079 assert.strictEqual(messages.length, 1);
13080 assert.strictEqual(messages[0].ruleId, "no-console");
13082 assert.strictEqual(suppressedMessages.length, 1);
13083 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13085 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
13087 "/* eslint-disable-next-line no-alert */",
13095 const messages = linter.verify(code, config, filename);
13096 const suppressedMessages = linter.getSuppressedMessages();
13098 assert.strictEqual(messages.length, 0);
13100 assert.strictEqual(suppressedMessages.length, 1);
13101 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13104 it("should not ignore violation if code is not on next line", () => {
13106 "/* eslint-disable-next-line",
13107 "no-alert */alert('test');"
13114 const messages = linter.verify(code, config, filename);
13115 const suppressedMessages = linter.getSuppressedMessages();
13117 assert.strictEqual(messages.length, 1);
13118 assert.strictEqual(messages[0].ruleId, "no-alert");
13120 assert.strictEqual(suppressedMessages.length, 0);
13123 it("should ignore violation if block comment span multiple lines", () => {
13125 "/* eslint-disable-next-line",
13134 const messages = linter.verify(code, config, filename);
13135 const suppressedMessages = linter.getSuppressedMessages();
13137 assert.strictEqual(messages.length, 0);
13139 assert.strictEqual(suppressedMessages.length, 1);
13140 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13143 // For https://github.com/eslint/eslint/issues/14284
13144 it("should ignore violation if block comment span multiple lines with description", () => {
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
13157 const messages = linter.verify(code, config, filename);
13158 const suppressedMessages = linter.getSuppressedMessages();
13160 assert.strictEqual(messages.length, 0);
13162 assert.strictEqual(suppressedMessages.length, 1);
13163 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13166 it("should ignore violations only of specified rule", () => {
13168 "// eslint-disable-next-line no-console",
13170 "console.log('test');"
13178 const messages = linter.verify(code, config, filename);
13179 const suppressedMessages = linter.getSuppressedMessages();
13181 assert.strictEqual(messages.length, 2);
13182 assert.strictEqual(messages[0].ruleId, "no-alert");
13183 assert.strictEqual(messages[1].ruleId, "no-console");
13185 assert.strictEqual(suppressedMessages.length, 0);
13188 it("should ignore violations only of specified rule when block comment span multiple lines", () => {
13190 "/* eslint-disable-next-line",
13193 "console.log('test');"
13201 const messages = linter.verify(code, config, filename);
13203 assert.strictEqual(messages.length, 2);
13204 assert.strictEqual(messages[0].ruleId, "no-alert");
13205 assert.strictEqual(messages[1].ruleId, "no-console");
13208 it("should ignore violations of multiple rules when specified", () => {
13210 "// eslint-disable-next-line no-alert, quotes",
13211 "alert(\"test\");",
13212 "console.log('test');"
13217 quotes: [1, "single"],
13221 const messages = linter.verify(code, config, filename);
13222 const suppressedMessages = linter.getSuppressedMessages();
13224 assert.strictEqual(messages.length, 1);
13225 assert.strictEqual(messages[0].ruleId, "no-console");
13227 assert.strictEqual(suppressedMessages.length, 2);
13228 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13229 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
13232 it("should ignore violations of multiple rules when specified in multiple lines", () => {
13234 "/* eslint-disable-next-line",
13238 "alert(\"test\");",
13239 "console.log('test');"
13244 quotes: [1, "single"],
13248 const messages = linter.verify(code, config, filename);
13250 assert.strictEqual(messages.length, 1);
13251 assert.strictEqual(messages[0].ruleId, "no-console");
13254 it("should ignore violations of multiple rules when specified in mixed comments", () => {
13256 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
13262 quotes: [1, "single"]
13265 const messages = linter.verify(code, config, filename);
13266 const suppressedMessages = linter.getSuppressedMessages();
13268 assert.strictEqual(messages.length, 0);
13270 assert.strictEqual(suppressedMessages.length, 2);
13271 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13272 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
13275 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
13277 "/* eslint-disable-next-line",
13279 "*/ // eslint-disable-next-line quotes",
13285 quotes: [1, "single"]
13288 const messages = linter.verify(code, config, filename);
13290 assert.strictEqual(messages.length, 0);
13293 it("should ignore violations of only the specified rule on next line", () => {
13295 "// eslint-disable-next-line quotes",
13296 "alert(\"test\");",
13297 "console.log('test');"
13302 quotes: [1, "single"],
13306 const messages = linter.verify(code, config, filename);
13307 const suppressedMessages = linter.getSuppressedMessages();
13309 assert.strictEqual(messages.length, 2);
13310 assert.strictEqual(messages[0].ruleId, "no-alert");
13311 assert.strictEqual(messages[1].ruleId, "no-console");
13313 assert.strictEqual(suppressedMessages.length, 1);
13314 assert.strictEqual(suppressedMessages[0].ruleId, "quotes");
13317 it("should ignore violations of specified rule on next line only", () => {
13320 "// eslint-disable-next-line no-alert",
13322 "console.log('test');"
13330 const messages = linter.verify(code, config, filename);
13331 const suppressedMessages = linter.getSuppressedMessages();
13333 assert.strictEqual(messages.length, 2);
13334 assert.strictEqual(messages[0].ruleId, "no-alert");
13335 assert.strictEqual(messages[1].ruleId, "no-console");
13337 assert.strictEqual(suppressedMessages.length, 1);
13338 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13341 it("should ignore all rule violations on next line if none specified", () => {
13343 "// eslint-disable-next-line",
13344 "alert(\"test\");",
13345 "console.log('test')"
13349 semi: [1, "never"],
13350 quotes: [1, "single"],
13355 const messages = linter.verify(code, config, filename);
13356 const suppressedMessages = linter.getSuppressedMessages();
13358 assert.strictEqual(messages.length, 1);
13359 assert.strictEqual(messages[0].ruleId, "no-console");
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");
13367 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
13370 "/* eslint-disable-next-line no-alert */",
13372 "console.log('test');"
13380 const messages = linter.verify(code, config, filename);
13381 const suppressedMessages = linter.getSuppressedMessages();
13383 assert.strictEqual(messages.length, 2);
13384 assert.strictEqual(messages[0].ruleId, "no-alert");
13385 assert.strictEqual(messages[1].ruleId, "no-console");
13387 assert.strictEqual(suppressedMessages.length, 1);
13388 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
13391 it("should report a violation", () => {
13393 "/* eslint-disable-next-line",
13396 "console.log('test');" // here
13405 const messages = linter.verify(code, config, filename);
13406 const suppressedMessages = linter.getSuppressedMessages();
13408 assert.strictEqual(messages.length, 2);
13409 assert.strictEqual(messages[1].ruleId, "no-console");
13411 assert.strictEqual(suppressedMessages.length, 0);
13414 it("should not ignore violations if comment is of the type hashbang", () => {
13416 "#! eslint-disable-next-line no-alert",
13418 "console.log('test');"
13426 const messages = linter.verify(code, config, filename);
13427 const suppressedMessages = linter.getSuppressedMessages();
13429 assert.strictEqual(messages.length, 2);
13430 assert.strictEqual(messages[0].ruleId, "no-alert");
13431 assert.strictEqual(messages[1].ruleId, "no-console");
13433 assert.strictEqual(suppressedMessages.length, 0);
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({});
13445 aaa: { create: aaa },
13446 bbb: { create: bbb }
13452 const messages = linter.verify(`
13453 /*eslint test/aaa:error -- test/bbb:error */
13454 console
.log("hello")
13456 const suppressedMessages = linter.getSuppressedMessages();
13458 // Don't include syntax error of the comment.
13459 assert.deepStrictEqual(messages, []);
13462 assert.strictEqual(aaa.callCount, 1);
13463 assert.strictEqual(bbb.callCount, 0);
13465 assert.strictEqual(suppressedMessages.length, 0);
13468 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
13469 const messages = linter.verify(`
13470 /*globals aaa -- bbb */
13475 sourceType: "script"
13477 rules: { "no-redeclare": "error" }
13479 const suppressedMessages = linter.getSuppressedMessages();
13481 // Don't include `bbb
`
13482 assert.deepStrictEqual(
13489 message: "'aaa' is already defined by a variable declaration.",
13490 messageId: "redeclaredBySyntax",
13492 ruleId: "no-redeclare",
13497 assert.strictEqual(suppressedMessages.length, 0);
13500 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
13501 const messages = linter.verify(`
13502 /*exported aaa -- bbb */
13507 sourceType: "script"
13509 rules: { "no-unused-vars": "error" }
13511 const suppressedMessages = linter.getSuppressedMessages();
13513 // Don't include `aaa
`
13514 assert.deepStrictEqual(
13521 message: "'bbb' is assigned a value but never used.",
13522 messageId: "unusedVar",
13523 nodeType: "Identifier",
13524 ruleId: "no-unused-vars",
13529 assert.strictEqual(suppressedMessages.length, 0);
13532 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
13533 const messages = linter.verify(`
13534 /*eslint-disable no-redeclare -- no-unused-vars */
13537 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13538 const suppressedMessages = linter.getSuppressedMessages();
13540 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13541 assert.deepStrictEqual(
13548 message: "'aaa' is assigned a value but never used.",
13549 messageId: "unusedVar",
13550 nodeType: "Identifier",
13551 ruleId: "no-unused-vars",
13556 assert.deepStrictEqual(
13557 suppressedMessages,
13563 message: "'aaa' is already defined.",
13564 messageId: "redeclared",
13565 nodeType: "Identifier",
13566 ruleId: "no-redeclare",
13568 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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 */
13579 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13580 const suppressedMessages = linter.getSuppressedMessages();
13582 // Do include `no
-redeclare
` but not `no
-unused
-vars
`
13583 assert.deepStrictEqual(
13590 message: "'aaa' is already defined.",
13591 messageId: "redeclared",
13592 nodeType: "Identifier",
13593 ruleId: "no-redeclare",
13598 assert.deepStrictEqual(
13599 suppressedMessages,
13605 message: "'aaa' is assigned a value but never used.",
13606 messageId: "unusedVar",
13607 nodeType: "Identifier",
13608 ruleId: "no-unused-vars",
13610 suppressions: [{ kind: "directive", justification: "" }]
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();
13622 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13623 assert.deepStrictEqual(
13630 message: "'aaa' is assigned a value but never used.",
13631 messageId: "unusedVar",
13632 nodeType: "Identifier",
13633 ruleId: "no-unused-vars",
13638 assert.deepStrictEqual(
13639 suppressedMessages,
13645 message: "'aaa' is already defined.",
13646 messageId: "redeclared",
13647 nodeType: "Identifier",
13648 ruleId: "no-redeclare",
13650 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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();
13662 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13663 assert.deepStrictEqual(
13670 message: "'aaa' is assigned a value but never used.",
13671 messageId: "unusedVar",
13672 nodeType: "Identifier",
13673 ruleId: "no-unused-vars",
13678 assert.deepStrictEqual(
13679 suppressedMessages,
13685 message: "'aaa' is already defined.",
13686 messageId: "redeclared",
13687 nodeType: "Identifier",
13688 ruleId: "no-redeclare",
13690 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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
13699 //eslint-disable-next-line no-redeclare -- no-unused-vars
13701 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13702 const suppressedMessages = linter.getSuppressedMessages();
13704 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13705 assert.deepStrictEqual(
13712 message: "'aaa' is assigned a value but never used.",
13713 messageId: "unusedVar",
13714 nodeType: "Identifier",
13715 ruleId: "no-unused-vars",
13720 assert.deepStrictEqual(
13721 suppressedMessages,
13727 message: "'aaa' is already defined.",
13728 messageId: "redeclared",
13729 nodeType: "Identifier",
13730 ruleId: "no-redeclare",
13732 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
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 */
13741 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
13743 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13744 const suppressedMessages = linter.getSuppressedMessages();
13746 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13747 assert.deepStrictEqual(
13754 message: "'aaa' is assigned a value but never used.",
13755 messageId: "unusedVar",
13756 nodeType: "Identifier",
13757 ruleId: "no-unused-vars",
13762 assert.deepStrictEqual(
13763 suppressedMessages,
13769 message: "'aaa' is already defined.",
13770 messageId: "redeclared",
13771 nodeType: "Identifier",
13772 ruleId: "no-redeclare",
13774 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13779 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
13780 const rule = sinon.stub().returns({});
13785 "a--rule": { create: rule }
13791 const messages = linter.verify(`
13792 /*eslint test/a--rule:error */
13793 console.log("hello")
13795 const suppressedMessages = linter.getSuppressedMessages();
13797 // Don't include syntax error of the comment.
13798 assert.deepStrictEqual(messages, []);
13801 assert.strictEqual(rule.callCount, 1);
13803 assert.strictEqual(suppressedMessages.length, 0);
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({});
13813 aaa: { create: aaa },
13814 bbb: { create: bbb }
13820 const messages = linter.verify(`
13821 /*eslint test/aaa:error -------- test/bbb:error */
13822 console
.log("hello")
13824 const suppressedMessages = linter.getSuppressedMessages();
13826 // Don't include syntax error of the comment.
13827 assert.deepStrictEqual(messages, []);
13830 assert.strictEqual(aaa.callCount, 1);
13831 assert.strictEqual(bbb.callCount, 0);
13833 assert.strictEqual(suppressedMessages.length, 0);
13836 it("should ignore the part preceded by '--' with line breaks.", () => {
13837 const aaa = sinon.stub().returns({});
13838 const bbb = sinon.stub().returns({});
13843 aaa: { create: aaa },
13844 bbb: { create: bbb }
13850 const messages = linter.verify(`
13851 /*eslint test/aaa:error
13854 console.log("hello")
13856 const suppressedMessages = linter.getSuppressedMessages();
13858 // Don't include syntax error of the comment.
13859 assert.deepStrictEqual(messages, []);
13862 assert.strictEqual(aaa.callCount, 1);
13863 assert.strictEqual(bbb.callCount, 0);
13865 assert.strictEqual(suppressedMessages.length, 0);
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", () => {
13873 "alert('test'); // eslint-disable-line no-alert"
13881 const messages = linter.verify(code, config, {
13883 allowInlineConfig: false
13885 const suppressedMessages = linter.getSuppressedMessages();
13887 assert.strictEqual(messages.length, 1);
13888 assert.strictEqual(messages[0].ruleId, "no-alert");
13890 assert.strictEqual(suppressedMessages.length, 0);
13893 it("should report a violation for global variable declarations", () => {
13903 create: context => ({
13905 const scope = context.getScope();
13906 const sourceCode = context.sourceCode;
13907 const comments = sourceCode.getAllComments();
13909 assert.strictEqual(context.getSourceCode(), sourceCode);
13910 assert.strictEqual(1, comments.length);
13912 const foo = getVariable(scope, "foo
");
13928 linter.verify(code, config, { allowInlineConfig: false });
13932 it("should report a violation
for eslint
-disable
", () => {
13934 "/* eslint-disable */",
13943 const messages = linter.verify(code, config, {
13945 allowInlineConfig: false
13947 const suppressedMessages = linter.getSuppressedMessages();
13949 assert.strictEqual(messages.length, 1);
13950 assert.strictEqual(messages[0].ruleId, "no
-alert
");
13952 assert.strictEqual(suppressedMessages.length, 0);
13955 it("should not report a violation
for rule changes
", () => {
13957 "/*eslint no-alert:2*/",
13966 const messages = linter.verify(code, config, {
13968 allowInlineConfig: false
13970 const suppressedMessages = linter.getSuppressedMessages();
13972 assert.strictEqual(messages.length, 0);
13973 assert.strictEqual(suppressedMessages.length, 0);
13976 it("should report a violation
for disable
-line
", () => {
13978 "alert('test'); // eslint-disable-line"
13986 const messages
= linter
.verify(code
, config
, {
13988 allowInlineConfig
: false
13990 const suppressedMessages
= linter
.getSuppressedMessages();
13992 assert
.strictEqual(messages
.length
, 1);
13993 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
13995 assert
.strictEqual(suppressedMessages
.length
, 0);
14000 describe("when evaluating code with 'noInlineConfig'", () => {
14001 for (const directive
of [
14005 "eslint eqeqeq: error",
14006 "eslint-disable eqeqeq",
14007 "eslint-disable-line eqeqeq",
14008 "eslint-disable-next-line eqeqeq",
14009 "eslint-enable eqeqeq"
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} */`, {
14015 noInlineConfig
: true
14018 const suppressedMessages
= linter
.getSuppressedMessages();
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.`);
14026 assert
.strictEqual(suppressedMessages
.length
, 0);
14030 for (const directive
of [
14031 "eslint-disable-line eqeqeq",
14032 "eslint-disable-next-line eqeqeq"
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}`, {
14038 noInlineConfig
: true
14041 const suppressedMessages
= linter
.getSuppressedMessages();
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.`);
14049 assert
.strictEqual(suppressedMessages
.length
, 0);
14053 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
14054 const messages
= linter
.verify("/* globals foo */", {
14056 noInlineConfig
: true
14058 }, { allowInlineConfig
: false });
14059 const suppressedMessages
= linter
.getSuppressedMessages();
14061 assert
.deepStrictEqual(messages
.length
, 0);
14062 assert
.strictEqual(suppressedMessages
.length
, 0);
14066 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
14067 it("should not report a violation", () => {
14069 "alert('test'); // eslint-disable-line no-alert"
14077 const messages
= linter
.verify(code
, config
, {
14079 allowInlineConfig
: true
14081 const suppressedMessages
= linter
.getSuppressedMessages();
14083 assert
.strictEqual(messages
.length
, 0);
14085 assert
.strictEqual(suppressedMessages
.length
, 1);
14086 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
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();
14097 assert
.deepStrictEqual(
14102 message
: "Unused eslint-disable directive (no problems were reported).",
14115 assert
.strictEqual(suppressedMessages
.length
, 0);
14118 it("reports problems for unused eslint-disable comments (error)", () => {
14119 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "error" });
14120 const suppressedMessages
= linter
.getSuppressedMessages();
14122 assert
.deepStrictEqual(
14127 message
: "Unused eslint-disable directive (no problems were reported).",
14140 assert
.strictEqual(suppressedMessages
.length
, 0);
14143 it("reports problems for unused eslint-disable comments (warn)", () => {
14144 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "warn" });
14145 const suppressedMessages
= linter
.getSuppressedMessages();
14147 assert
.deepStrictEqual(
14152 message
: "Unused eslint-disable directive (no problems were reported).",
14165 assert
.strictEqual(suppressedMessages
.length
, 0);
14168 it("reports problems for unused eslint-disable comments (in config)", () => {
14169 const messages
= linter
.verify("/* eslint-disable */", {
14171 reportUnusedDisableDirectives
: true
14174 const suppressedMessages
= linter
.getSuppressedMessages();
14176 assert
.deepStrictEqual(
14181 message
: "Unused eslint-disable directive (no problems were reported).",
14194 assert
.strictEqual(suppressedMessages
.length
, 0);
14197 it("reports problems for partially unused eslint-disable comments (in config)", () => {
14198 const code
= "alert('test'); // eslint-disable-line no-alert, no-redeclare";
14201 reportUnusedDisableDirectives
: true
14209 const messages
= linter
.verify(code
, config
, {
14211 allowInlineConfig
: true
14213 const suppressedMessages
= linter
.getSuppressedMessages();
14215 assert
.deepStrictEqual(
14220 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
14233 assert
.strictEqual(suppressedMessages
.length
, 1);
14234 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
14237 it("reports problems for unused eslint-disable-next-line comments (in config)", () => {
14238 assert
.deepStrictEqual(
14239 linter
.verify("// eslint-disable-next-line", {
14241 reportUnusedDisableDirectives
: true
14247 message
: "Unused eslint-disable directive (no problems were reported).",
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 */", {
14265 reportUnusedDisableDirectives
: true
14271 message
: "Unused eslint-disable directive (no problems were reported).",
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');";
14289 reportUnusedDisableDirectives
: true
14297 const messages
= linter
.verify(code
, config
, {
14299 allowInlineConfig
: true
14302 assert
.deepStrictEqual(
14307 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
14321 it("reports problems for partially unused multiline eslint-disable-next-line comments (in config)", () => {
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
14331 reportUnusedDisableDirectives
: true
14339 const messages
= linter
.verify(code
, config
, {
14341 allowInlineConfig
: true
14344 assert
.deepStrictEqual(
14349 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
14363 describe("autofix", () => {
14364 const alwaysReportsRule
= {
14368 context
.report({ message
: "bad code", loc
: node
.loc
.end
});
14374 const neverReportsRule
= {
14380 const ruleCount
= 3;
14381 const usedRules
= Array
.from(
14382 { length
: ruleCount
},
14383 (_
, index
) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
14385 const unusedRules = usedRules.map(name => `un${name}
`); // "unused", "unused-1", "unused-2"
14394 reportUnusedDisableDirectives: true
14397 ...Object.fromEntries(usedRules.map(name => [`test
/${name}
`, "error"])),
14398 ...Object.fromEntries(unusedRules.map(name => [`test
/${name}
`, "error"]))
14403 config.plugins.test.rules = {
14404 ...Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])),
14405 ...Object.fromEntries(unusedRules.map(name => [name, neverReportsRule]))
14411 //-----------------------------------------------
14412 // Removing the entire comment
14413 //-----------------------------------------------
14416 code: "// eslint-disable-line test/unused",
14420 code: "foo// eslint-disable-line test/unused",
14424 code: "// eslint-disable-line ,test/unused,",
14428 code: "// eslint-disable-line test/unused-1, test/unused-2",
14432 code: "// eslint-disable-line ,test/unused-1,, test/unused-2,, -- comment",
14436 code: "// eslint-disable-next-line test/unused\n",
14440 code: "// eslint-disable-next-line test/unused\nfoo",
14444 code: "/* eslint-disable \ntest/unused\n*/",
14448 //-----------------------------------------------
14449 // Removing only individual rules
14450 //-----------------------------------------------
14452 // content before the first rule should not be changed
14454 code: "//eslint-disable-line test/unused, test/used",
14455 output: "//eslint-disable-line test/used"
14458 code: "// eslint-disable-line test/unused, test/used",
14459 output: "// eslint-disable-line test/used"
14462 code: "// eslint-disable-line test/unused, test/used",
14463 output: "// eslint-disable-line test/used"
14466 code: "/*\neslint-disable test/unused, test/used*/",
14467 output: "/*\neslint-disable test/used*/"
14470 code: "/*\n eslint-disable test/unused, test/used*/",
14471 output: "/*\n eslint-disable test/used*/"
14474 code: "/*\r\neslint-disable test/unused, test/used*/",
14475 output: "/*\r\neslint-disable test/used*/"
14478 code: "/*\u2028eslint-disable test/unused, test/used*/",
14479 output: "/*\u2028eslint-disable test/used*/"
14482 code: "/*\u00A0eslint-disable test/unused, test/used*/",
14483 output: "/*\u00A0eslint-disable test/used*/"
14486 code: "// eslint-disable-line test/unused, test/used",
14487 output: "// eslint-disable-line test/used"
14490 code: "/* eslint-disable\ntest/unused, test/used*/",
14491 output: "/* eslint-disable\ntest/used*/"
14494 code: "/* eslint-disable\n test/unused, test/used*/",
14495 output: "/* eslint-disable\n test/used*/"
14498 code: "/* eslint-disable\r\ntest/unused, test/used*/",
14499 output: "/* eslint-disable\r\ntest/used*/"
14502 code: "/* eslint-disable\u2028test/unused, test/used*/",
14503 output: "/* eslint-disable\u2028test/used*/"
14506 code: "/* eslint-disable\u00A0test/unused, test/used*/",
14507 output: "/* eslint-disable\u00A0test/used*/"
14510 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
14512 code: "// eslint-disable-line test/unused,test/used",
14513 output: "// eslint-disable-line test/used"
14516 code: "// eslint-disable-line test/unused, test/used",
14517 output: "// eslint-disable-line test/used"
14520 code: "// eslint-disable-line test/unused , test/used",
14521 output: "// eslint-disable-line test/used"
14524 code: "// eslint-disable-line test/unused, test/used",
14525 output: "// eslint-disable-line test/used"
14528 code: "// eslint-disable-line test/unused ,test/used",
14529 output: "// eslint-disable-line test/used"
14532 code: "/* eslint-disable test/unused\n,\ntest/used */",
14533 output: "/* eslint-disable test/used */"
14536 code: "/* eslint-disable test/unused \n \n,\n\n test/used */",
14537 output: "/* eslint-disable test/used */"
14540 code: "/* eslint-disable test/unused\u2028,\u2028test/used */",
14541 output: "/* eslint-disable test/used */"
14544 code: "// eslint-disable-line test/unused\u00A0,\u00A0test/used",
14545 output: "// eslint-disable-line test/used"
14548 code: "// eslint-disable-line test/unused,,test/used",
14549 output: "// eslint-disable-line ,test/used"
14552 code: "// eslint-disable-line test/unused, ,test/used",
14553 output: "// eslint-disable-line ,test/used"
14556 code: "// eslint-disable-line test/unused,, test/used",
14557 output: "// eslint-disable-line , test/used"
14560 code: "// eslint-disable-line test/unused,test/used ",
14561 output: "// eslint-disable-line test/used "
14564 code: "// eslint-disable-next-line test/unused,test/used\n",
14565 output: "// eslint-disable-next-line test/used\n"
14568 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
14570 code: "// eslint-disable-line test/used-1,test/unused,test/used-2",
14571 output: "// eslint-disable-line test/used-1,test/used-2"
14574 code: "// eslint-disable-line test/used-1, test/unused,test/used-2",
14575 output: "// eslint-disable-line test/used-1,test/used-2"
14578 code: "// eslint-disable-line test/used-1,test/unused ,test/used-2",
14579 output: "// eslint-disable-line test/used-1,test/used-2"
14582 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14583 output: "// eslint-disable-line test/used-1,test/used-2"
14586 code: "/* eslint-disable test/used-1,\ntest/unused\n,test/used-2 */",
14587 output: "/* eslint-disable test/used-1,test/used-2 */"
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 */"
14594 code: "/* eslint-disable test/used-1,\u2028test/unused\u2028,test/used-2 */",
14595 output: "/* eslint-disable test/used-1,test/used-2 */"
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"
14602 // when removing a rule in the middle, content around commas should not be changed
14604 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14605 output: "// eslint-disable-line test/used-1,test/used-2"
14608 code: "// eslint-disable-line test/used-1,test/unused, test/used-2",
14609 output: "// eslint-disable-line test/used-1, test/used-2"
14612 code: "// eslint-disable-line test/used-1 ,test/unused,test/used-2",
14613 output: "// eslint-disable-line test/used-1 ,test/used-2"
14616 code: "// eslint-disable-line test/used-1 ,test/unused, test/used-2",
14617 output: "// eslint-disable-line test/used-1 , test/used-2"
14620 code: "// eslint-disable-line test/used-1 , test/unused , test/used-2",
14621 output: "// eslint-disable-line test/used-1 , test/used-2"
14624 code: "/* eslint-disable test/used-1\n,test/unused,\ntest/used-2 */",
14625 output: "/* eslint-disable test/used-1\n,\ntest/used-2 */"
14628 code: "/* eslint-disable test/used-1\u2028,test/unused,\u2028test/used-2 */",
14629 output: "/* eslint-disable test/used-1\u2028,\u2028test/used-2 */"
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"
14636 code: "// eslint-disable-line , test/unused ,test/used",
14637 output: "// eslint-disable-line ,test/used"
14640 code: "/* eslint-disable\n, test/unused ,test/used */",
14641 output: "/* eslint-disable\n,test/used */"
14644 code: "/* eslint-disable test/used-1,\n,test/unused,test/used-2 */",
14645 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
14648 code: "/* eslint-disable test/used-1,test/unused,\n,test/used-2 */",
14649 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
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 */"
14656 code: "// eslint-disable-line test/used, test/unused,",
14657 output: "// eslint-disable-line test/used,"
14660 code: "// eslint-disable-next-line test/used, test/unused,\n",
14661 output: "// eslint-disable-next-line test/used,\n"
14664 code: "// eslint-disable-line test/used, test/unused, ",
14665 output: "// eslint-disable-line test/used, "
14668 code: "// eslint-disable-line test/used, test/unused, -- comment",
14669 output: "// eslint-disable-line test/used, -- comment"
14672 code: "/* eslint-disable test/used, test/unused,\n*/",
14673 output: "/* eslint-disable test/used,\n*/"
14676 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
14678 code: "// eslint-disable-line test/used,test/unused",
14679 output: "// eslint-disable-line test/used"
14682 code: "// eslint-disable-line test/used, test/unused",
14683 output: "// eslint-disable-line test/used"
14686 code: "// eslint-disable-line test/used ,test/unused",
14687 output: "// eslint-disable-line test/used"
14690 code: "// eslint-disable-line test/used , test/unused",
14691 output: "// eslint-disable-line test/used"
14694 code: "// eslint-disable-line test/used, test/unused",
14695 output: "// eslint-disable-line test/used"
14698 code: "// eslint-disable-line test/used ,test/unused",
14699 output: "// eslint-disable-line test/used"
14702 code: "/* eslint-disable test/used\n,\ntest/unused */",
14703 output: "/* eslint-disable test/used */"
14706 code: "/* eslint-disable test/used \n \n,\n\n test/unused */",
14707 output: "/* eslint-disable test/used */"
14710 code: "/* eslint-disable test/used\u2028,\u2028test/unused */",
14711 output: "/* eslint-disable test/used */"
14714 code: "// eslint-disable-line test/used\u00A0,\u00A0test/unused",
14715 output: "// eslint-disable-line test/used"
14718 code: "// eslint-disable-line test/used,,test/unused",
14719 output: "// eslint-disable-line test/used,"
14722 code: "// eslint-disable-line test/used, ,test/unused",
14723 output: "// eslint-disable-line test/used,"
14726 code: "/* eslint-disable test/used,\n,test/unused */",
14727 output: "/* eslint-disable test/used, */"
14730 code: "/* eslint-disable test/used\n, ,test/unused */",
14731 output: "/* eslint-disable test/used\n, */"
14734 // content after the last rule should not be changed
14736 code: "// eslint-disable-line test/used,test/unused",
14737 output: "// eslint-disable-line test/used"
14740 code: "// eslint-disable-line test/used,test/unused ",
14741 output: "// eslint-disable-line test/used "
14744 code: "// eslint-disable-line test/used,test/unused ",
14745 output: "// eslint-disable-line test/used "
14748 code: "// eslint-disable-line test/used,test/unused -- comment",
14749 output: "// eslint-disable-line test/used -- comment"
14752 code: "// eslint-disable-next-line test/used,test/unused\n",
14753 output: "// eslint-disable-next-line test/used\n"
14756 code: "// eslint-disable-next-line test/used,test/unused \n",
14757 output: "// eslint-disable-next-line test/used \n"
14760 code: "/* eslint-disable test/used,test/unused\u2028*/",
14761 output: "/* eslint-disable test/used\u2028*/"
14764 code: "// eslint-disable-line test/used,test/unused\u00A0",
14765 output: "// eslint-disable-line test/used\u00A0"
14768 // multiply rules to remove
14770 code: "// eslint-disable-line test/used, test/unused-1, test/unused-2",
14771 output: "// eslint-disable-line test/used"
14774 code: "// eslint-disable-line test/unused-1, test/used, test/unused-2",
14775 output: "// eslint-disable-line test/used"
14778 code: "// eslint-disable-line test/unused-1, test/unused-2, test/used",
14779 output: "// eslint-disable-line test/used"
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"
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"
14791 /* eslint-disable test/unused-1,
14798 /* eslint-disable test/used-1,
14904 // duplicates in the list
14906 code: "// eslint-disable-line test/unused, test/unused, test/used",
14907 output: "// eslint-disable-line test/used"
14910 code: "// eslint-disable-line test/unused, test/used, test/unused",
14911 output: "// eslint-disable-line test/used"
14914 code: "// eslint-disable-line test/used, test/unused, test/unused, test/used",
14915 output: "// eslint-disable-line test/used, test/used"
14919 for (const { code, output } of tests) {
14920 // eslint-disable-next-line no-loop-func -- `linter
` is getting updated in beforeEach()
14922 assert.strictEqual(
14923 linter.verifyAndFix(code, config).output,
14933 describe("Default Global Variables", () => {
14936 it("builtin global variables should be available in the global scope", () => {
14944 spy = sinon.spy(() => {
14945 const scope = context.getScope();
14947 assert.notStrictEqual(getVariable(scope, "Object"), null);
14948 assert.notStrictEqual(getVariable(scope, "Array"), null);
14949 assert.notStrictEqual(getVariable(scope, "undefined"), null);
14952 return { Program: spy };
14960 sourceType: "script"
14963 "test/checker": "error"
14967 linter.verify(code, config);
14968 assert(spy && spy.calledOnce, "Rule should have been called.");
14971 it("ES6 global variables should be available by default", () => {
14979 spy = sinon.spy(() => {
14980 const scope = context.getScope();
14982 assert.notStrictEqual(getVariable(scope, "Promise"), null);
14983 assert.notStrictEqual(getVariable(scope, "Symbol"), null);
14984 assert.notStrictEqual(getVariable(scope, "WeakMap"), null);
14987 return { Program: spy };
14994 sourceType: "script"
14997 "test/checker": "error"
15001 linter.verify(code, config);
15002 assert(spy && spy.calledOnce);
15007 describe("Suggestions", () => {
15008 it("provides suggestion information for tools to use", () => {
15014 "rule-with-suggestions": {
15015 meta: { hasSuggestions: true },
15016 create: context => ({
15020 message: "Incorrect spacing",
15022 desc: "Insert space at the beginning",
15023 fix: fixer => fixer.insertTextBefore(node, " ")
15025 desc: "Insert space at the end",
15026 fix: fixer => fixer.insertTextAfter(node, " ")
15036 "test/rule-with-suggestions": "error"
15040 const messages = linter.verify("var a = 1;", config);
15041 const suppressedMessages = linter.getSuppressedMessages();
15043 assert.deepStrictEqual(messages[0].suggestions, [{
15044 desc: "Insert space at the beginning",
15050 desc: "Insert space at the end",
15057 assert.strictEqual(suppressedMessages.length, 0);
15060 it("supports messageIds for suggestions", () => {
15066 "rule-with-suggestions": {
15069 suggestion1: "Insert space at the beginning",
15070 suggestion2: "Insert space at the end"
15072 hasSuggestions: true
15074 create: context => ({
15078 message: "Incorrect spacing",
15080 messageId: "suggestion1",
15081 fix: fixer => fixer.insertTextBefore(node, " ")
15083 messageId: "suggestion2",
15084 fix: fixer => fixer.insertTextAfter(node, " ")
15094 "test/rule-with-suggestions": "error"
15098 const messages = linter.verify("var a = 1;", config);
15099 const suppressedMessages = linter.getSuppressedMessages();
15101 assert.deepStrictEqual(messages[0].suggestions, [{
15102 messageId: "suggestion1",
15103 desc: "Insert space at the beginning",
15109 messageId: "suggestion2",
15110 desc: "Insert space at the end",
15117 assert.strictEqual(suppressedMessages.length, 0);
15120 it("should throw an error if suggestion is passed but `meta
.hasSuggestions
` property is not enabled", () => {
15126 "rule-with-suggestions": {
15127 meta: { docs: {}, schema: [] },
15128 create: context => ({
15132 message: "hello world",
15133 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
15142 "test/rule-with-suggestions": "error"
15146 assert.throws(() => {
15147 linter.verify("0", config);
15148 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`.");
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", () => {
15157 "rule-with-meta-docs-suggestion": {
15158 meta: { docs: { suggestion: true }, schema: [] },
15159 create: context => ({
15163 message: "hello world",
15164 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
15173 "test/rule-with-meta-docs-suggestion": "error"
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.");
15184 describe("Error Conditions", () => {
15185 describe("when evaluating broken code", () => {
15186 const code = BROKEN_TEST_CODE;
15188 it("should report a violation with a useful parse error prefix", () => {
15189 const messages = linter.verify(code);
15190 const suppressedMessages = linter.getSuppressedMessages();
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);
15200 assert.strictEqual(suppressedMessages.length, 0);
15203 it("should report source code where the issue is present", () => {
15204 const inValidCode = [
15210 const messages = linter.verify(inValidCode.join("\n"));
15211 const suppressedMessages = linter.getSuppressedMessages();
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);
15218 assert.strictEqual(suppressedMessages.length, 0);
15222 describe("when using a rule which has been replaced", () => {
15223 const code = TEST_CODE;
15225 it("should report the new rule", () => {
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);
15237 describe("getSourceCode()", () => {
15238 const code = TEST_CODE;
15240 it("should retrieve SourceCode object after reset", () => {
15241 linter.verify(code, {}, filename, true);
15243 const sourceCode = linter.getSourceCode();
15245 assert.isObject(sourceCode);
15246 assert.strictEqual(sourceCode.text, code);
15247 assert.isObject(sourceCode.ast);
15250 it("should retrieve SourceCode object without reset", () => {
15251 linter.verify(code, {}, filename);
15253 const sourceCode = linter.getSourceCode();
15255 assert.isObject(sourceCode);
15256 assert.strictEqual(sourceCode.text, code);
15257 assert.isObject(sourceCode.ast);
15262 describe("getSuppressedMessages()", () => {
15263 it("should have no suppressed messages", () => {
15264 const suppressedMessages = linter.getSuppressedMessages();
15266 assert.strictEqual(suppressedMessages.length, 0);
15269 it("should have a suppressed message", () => {
15270 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test\");";
15272 rules: { "no-alert": 1 }
15274 const messages = linter.verify(code, config);
15275 const suppressedMessages = linter.getSuppressedMessages();
15277 assert.strictEqual(messages.length, 0);
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" }]
15287 it("should have a suppressed message", () => {
15289 "/* eslint-disable no-alert -- j1",
15295 rules: { "no-alert": 1 }
15297 const messages = linter.verify(code, config);
15298 const suppressedMessages = linter.getSuppressedMessages();
15300 assert.strictEqual(messages.length, 0);
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" }]
15311 describe("defineRule()", () => {
15312 it("should throw an error when called in flat config mode", () => {
15313 assert.throws(() => {
15314 linter.defineRule("foo", {
15317 }, /This method cannot be used with flat config/u);
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);
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);
15337 describe("getRules()", () => {
15338 it("should throw an error when called in flat config mode", () => {
15339 assert.throws(() => {
15341 }, /This method cannot be used with flat config/u);
15345 describe("version", () => {
15346 it("should return current version number", () => {
15347 const version = linter.version;
15349 assert.isString(version);
15350 assert.isTrue(parseInt(version[0], 10) >= 3);
15354 describe("verifyAndFix()", () => {
15355 it("Fixes the code", () => {
15356 const messages = linter.verifyAndFix("var a", {
15360 }, { filename: "test.js" });
15361 const suppressedMessages = linter.getSuppressedMessages();
15363 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
15364 assert.isTrue(messages.fixed);
15366 assert.strictEqual(suppressedMessages.length, 0);
15369 it("does not require a third argument", () => {
15370 const fixResult = linter.verifyAndFix("var a", {
15375 const suppressedMessages = linter.getSuppressedMessages();
15377 assert.deepStrictEqual(fixResult, {
15383 assert.strictEqual(suppressedMessages.length, 0);
15386 it("does not include suggestions in autofix results", () => {
15387 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
15390 "no-useless-escape": 2
15393 const suppressedMessages = linter.getSuppressedMessages();
15395 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
15396 assert.strictEqual(fixResult.fixed, true);
15397 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
15399 assert.strictEqual(suppressedMessages.length, 0);
15402 it("does not apply autofixes when fix argument is `false`", () => {
15403 const fixResult = linter.verifyAndFix("var a", {
15407 }, { fix: false });
15408 const suppressedMessages = linter.getSuppressedMessages();
15410 assert.strictEqual(fixResult.fixed, false);
15411 assert.strictEqual(suppressedMessages.length, 0);
15414 it("stops fixing after 10 passes", () => {
15422 fixable: "whitespace"
15429 message: "Add a space before this node.",
15430 fix: fixer => fixer.insertTextBefore(node, " ")
15440 "test/add-spaces": "error"
15444 const fixResult = linter.verifyAndFix("a", config);
15445 const suppressedMessages = linter.getSuppressedMessages();
15447 assert.strictEqual(fixResult.fixed, true);
15448 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a
`);
15449 assert.strictEqual(fixResult.messages.length, 1);
15451 assert.strictEqual(suppressedMessages.length, 0);
15454 it("should throw an error if fix is passed but meta has no `fixable
` property", () => {
15465 create: context => ({
15467 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
15475 "test/test-rule": "error"
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);
15485 it("should throw an error if fix is passed and there is no metadata", () => {
15492 create: context => ({
15494 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
15502 "test/test-rule": "error"
15506 assert.throws(() => {
15507 linter.verify("0", config);
15508 }, /Fixable rules must set the `meta
\.fixable
` property/u);
15511 it("should throw an error if fix is passed from a legacy-format rule", () => {
15518 create: context => ({
15520 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
15528 "test/test-rule": "error"
15533 assert.throws(() => {
15534 linter.verify("0", config);
15535 }, /Fixable rules must set the `meta
\.fixable
` property/u);
15539 describe("Mutability", () => {
15540 let linter1 = null;
15541 let linter2 = null;
15544 linter1 = new Linter();
15545 linter2 = new Linter();
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()));
15553 it("loading rule in one doesn't change the other", () => {
15554 linter1.defineRule("mock-rule", {
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");
15565 describe("processors", () => {
15566 let receivedFilenames = [];
15567 let receivedPhysicalFilenames = [];
15568 const extraConfig = {
15572 "report-original-text": {
15579 assert.strictEqual(context.getFilename(), context.filename);
15580 assert.strictEqual(context.getPhysicalFilename(), context.physicalFilename);
15582 receivedFilenames.push(context.filename);
15583 receivedPhysicalFilenames.push(context.physicalFilename);
15585 context.report({ node: ast, message: context.sourceCode.text });
15596 receivedFilenames = [];
15597 receivedPhysicalFilenames = [];
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({});
15606 configs.normalizeSync();
15608 linter.verify(code, configs, { filename, preprocess });
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");
15614 it("should run preprocess only once", () => {
15619 preprocess(text, filenameForText) {
15622 filename: filenameForText
15625 return [{ text: "bar", filename: "0.js" }];
15633 linter.verify("foo", config, "a.md");
15634 assert.strictEqual(logs.length, 1, "preprocess() should only be called once.");
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([
15641 { rules: { "test/report-original-text": "error" } }
15644 configs.normalizeSync();
15646 const problems = linter.verify(
15652 // Apply a preprocessor that splits the source text into spaces and lints each word individually
15653 preprocess(input) {
15654 return input.split(" ");
15658 const suppressedMessages = linter.getSuppressedMessages();
15660 assert.strictEqual(problems.length, 3);
15661 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15663 assert.strictEqual(suppressedMessages.length, 0);
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([
15670 { rules: { "test/report-original-text": "error" } }
15673 configs.normalizeSync();
15675 const problems = linter.verify(
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",
15690 const suppressedMessages = linter.getSuppressedMessages();
15692 assert.strictEqual(problems.length, 3);
15693 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15695 assert.strictEqual(suppressedMessages.length, 0);
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]));
15703 // physical filename
15704 assert.strictEqual(receivedPhysicalFilenames.length, 3);
15705 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
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([
15715 configs.normalizeSync();
15717 linter.verify(code, configs);
15718 const sourceCode = linter.getSourceCode();
15720 linter.verify(sourceCode, configs, { filename, preprocess });
15722 assert.strictEqual(preprocess.calledOnce, true);
15723 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
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([
15733 configs.normalizeSync();
15735 linter.verify(code, configs);
15736 const sourceCode = linter.getSourceCode();
15738 linter.verify(sourceCode, configs, { filename, preprocess });
15740 assert.strictEqual(preprocess.calledOnce, true);
15741 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15744 it("should catch preprocess error.", () => {
15745 const code = "foo";
15746 const preprocess = sinon.spy(() => {
15747 throw Object.assign(new SyntaxError("Invalid syntax"), {
15753 const configs = createFlatConfigArray([
15757 configs.normalizeSync();
15759 const messages = linter.verify(code, configs, { filename, preprocess });
15761 assert.strictEqual(preprocess.calledOnce, true);
15762 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15763 assert.deepStrictEqual(messages, [
15768 message: "Preprocessing error: Invalid syntax",
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([
15786 configs.normalizeSync();
15788 linter.verify(code, configs, { filename, postprocess, preprocess });
15790 assert.strictEqual(postprocess.calledOnce, true);
15791 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
15794 it("should apply a postprocessor to the reported messages", () => {
15795 const code = "foo bar baz";
15796 const configs = createFlatConfigArray([
15798 { rules: { "test/report-original-text": "error" } }
15801 configs.normalizeSync();
15803 const problems = linter.verify(
15807 preprocess: input => input.split(" "),
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.
15813 postprocess(problemLists) {
15814 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
15815 return problemLists.reduce(
15816 (combinedList, problemList, index) =>
15817 combinedList.concat(
15824 message: problem.message.toUpperCase(),
15825 column: problem.column + index * 4
15835 const suppressedMessages = linter.getSuppressedMessages();
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]);
15841 assert.strictEqual(suppressedMessages.length, 0);
15844 it("should use postprocessed problem ranges when applying autofixes", () => {
15845 const code = "foo bar baz";
15846 const configs = createFlatConfigArray([
15852 "capitalize-identifiers": {
15859 if (node.name !== node.name.toUpperCase()) {
15862 message: "Capitalize this identifier",
15863 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
15874 { rules: { "test2/capitalize-identifiers": "error" } }
15877 configs.normalizeSync();
15879 const fixResult = linter.verifyAndFix(
15885 * Apply a postprocessor that updates the locations of autofixes
15886 * to make sure they correspond to locations in the original text.
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, {
15896 text: problem.fix.text,
15897 range: problem.fix.range.map(
15898 rangeIndex => rangeIndex + blockIndex * 4
15908 const suppressedMessages = linter.getSuppressedMessages();
15910 assert.strictEqual(fixResult.fixed, true);
15911 assert.strictEqual(fixResult.messages.length, 0);
15912 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
15914 assert.strictEqual(suppressedMessages.length, 0);
15919 describe("Edge cases", () => {
15921 describe("Modules", () => {
15922 const moduleConfig = {
15924 sourceType: "module",
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();
15934 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15935 assert.strictEqual(suppressedMessages.length, 0);
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();
15943 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15944 assert.strictEqual(suppressedMessages.length, 0);
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();
15952 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15953 assert.strictEqual(suppressedMessages.length, 0);
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 } });
15963 assert.deepStrictEqual(messages.length, 1);
15964 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
15966 assert.throws(() => {
15967 linter.verify("", { languageOptions: { sourceType: "foo" } });
15968 }, /Expected "script", "module", or "commonjs"./u);
15971 messages = linter.verify("", { languageOptions: { ecmaVersion: 5, sourceType: "module" } });
15972 const suppressedMessages = linter.getSuppressedMessages();
15974 assert.deepStrictEqual(messages.length, 1);
15975 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
15977 assert.strictEqual(suppressedMessages.length, 0);
15980 it("should not crash when invalid parentheses syntax is encountered", () => {
15981 linter.verify("left = (aSize.width/2) - ()");
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 } });
15988 it("should not crash when parsing destructured assignment", () => {
15989 linter.verify("var { a='a' } = {};", { languageOptions: { ecmaVersion: 6 } });
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();
15996 assert.strictEqual(messages.length, 1);
15997 assert.strictEqual(messages[0].fatal, true);
15999 assert.strictEqual(suppressedMessages.length, 0);
16002 it("should not crash when we reuse the SourceCode object", () => {
16007 ecmaFeatures: { jsx: true }
16012 linter.verify("function render() { return <div className='test'>{hello}</div> }", config);
16013 linter.verify(linter.getSourceCode(), config);
16016 it("should reuse the SourceCode object", () => {
16046 ecmaFeatures: { jsx: true }
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" } });
16055 assert(ast1 !== null);
16056 assert(ast2 !== null);
16057 assert(ast1 === ast2);
16060 it("should not modify config object passed as argument", () => {
16063 Object.freeze(config);
16064 linter.verify("var", config);
16067 it("should pass 'id' to rule contexts with the rule id", () => {
16069 const spy = sinon.spy(context => {
16070 assert.strictEqual(context.id, "test/foo-bar-baz");
16078 "foo-bar-baz": { create: spy }
16083 "test/foo-bar-baz": "error"
16088 linter.verify("x", config);
16089 assert(spy.calledOnce);
16093 describe("when evaluating an empty string", () => {
16094 it("runs rules", () => {
16104 context.report({ node, message: "No programs allowed." });
16113 "test/no-programs": "error"
16117 assert.strictEqual(
16118 linter.verify("", config).length,