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", () => ({
85 throw new Error("Intentional error.");
90 linter.verify(code, config, filename);
91 }, `Intentional error.\nOccurred while linting ${filename}:1\nRule: "checker"`);
94 it("does not call rule listeners with a `this` value", () => {
95 const spy = sinon.spy();
97 linter.defineRule("checker", () => ({ Program: spy }));
98 linter.verify("foo", { rules: { checker: "error" } });
99 assert(spy.calledOnce, "Rule should have been called");
100 assert.strictEqual(spy.firstCall.thisValue, void 0, "this value should be undefined");
103 it("does not allow listeners to use special EventEmitter values", () => {
104 const spy = sinon.spy();
106 linter.defineRule("checker", () => ({ newListener: spy }));
107 linter.verify("foo", { rules: { checker: "error", "no-undef": "error" } });
108 assert(spy.notCalled);
111 it("has all the `parent` properties on nodes when the rule listeners are created", () => {
112 const spy = sinon.spy(context => {
113 const ast = context.getSourceCode().ast;
115 assert.strictEqual(ast.body[0].parent, ast);
116 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
117 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
118 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
123 linter.defineRule("checker", spy);
125 linter.verify("foo + bar", { rules: { checker: "error" } });
126 assert(spy.calledOnce);
130 describe("context.getSourceLines()", () => {
132 it("should get proper lines when using \\n as a line break", () => {
133 const code = "a;\nb;";
134 const spy = sinon.spy(context => {
135 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
139 linter.defineRule("checker", spy);
140 linter.verify(code, { rules: { checker: "error" } });
141 assert(spy.calledOnce);
144 it("should get proper lines when using \\r\\n as a line break", () => {
145 const code = "a;\r\nb;";
146 const spy = sinon.spy(context => {
147 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
151 linter.defineRule("checker", spy);
152 linter.verify(code, { rules: { checker: "error" } });
153 assert(spy.calledOnce);
156 it("should get proper lines when using \\r as a line break", () => {
157 const code = "a;\rb;";
158 const spy = sinon.spy(context => {
159 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
163 linter.defineRule("checker", spy);
164 linter.verify(code, { rules: { checker: "error" } });
165 assert(spy.calledOnce);
168 it("should get proper lines when using \\u2028 as a line break", () => {
169 const code = "a;\u2028b;";
170 const spy = sinon.spy(context => {
171 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
175 linter.defineRule("checker", spy);
176 linter.verify(code, { rules: { checker: "error" } });
177 assert(spy.calledOnce);
180 it("should get proper lines when using \\u2029 as a line break", () => {
181 const code = "a;\u2029b;";
182 const spy = sinon.spy(context => {
183 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
187 linter.defineRule("checker", spy);
188 linter.verify(code, { rules: { checker: "error" } });
189 assert(spy.calledOnce);
195 describe("getSourceCode()", () => {
196 const code = TEST_CODE;
198 it("should retrieve SourceCode object after reset", () => {
199 linter.verify(code, {}, filename, true);
201 const sourceCode = linter.getSourceCode();
203 assert.isObject(sourceCode);
204 assert.strictEqual(sourceCode.text, code);
205 assert.isObject(sourceCode.ast);
208 it("should retrieve SourceCode object without reset", () => {
209 linter.verify(code, {}, filename);
211 const sourceCode = linter.getSourceCode();
213 assert.isObject(sourceCode);
214 assert.strictEqual(sourceCode.text, code);
215 assert.isObject(sourceCode.ast);
220 describe("getSuppressedMessages()", () => {
221 it("should have no suppressed messages", () => {
222 const suppressedMessages = linter.getSuppressedMessages();
224 assert.strictEqual(suppressedMessages.length, 0);
227 it("should have a suppressed message", () => {
228 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test
\");";
230 rules: { "no
-alert
": 1 }
232 const messages = linter.verify(code, config);
233 const suppressedMessages = linter.getSuppressedMessages();
235 assert.strictEqual(messages.length, 0);
237 assert.strictEqual(suppressedMessages.length, 1);
238 assert.strictEqual(suppressedMessages[0].ruleId, "no
-alert
");
239 assert.deepStrictEqual(
240 suppressedMessages[0].suppressions,
241 [{ kind: "directive
", justification: "justification
" }]
245 it("should have a suppressed message
", () => {
247 "/* eslint-disable no-alert --- j1",
253 rules: { "no
-alert
": 1 }
255 const messages = linter.verify(code, config);
256 const suppressedMessages = linter.getSuppressedMessages();
258 assert.strictEqual(messages.length, 0);
260 assert.strictEqual(suppressedMessages.length, 1);
261 assert.strictEqual(suppressedMessages[0].ruleId, "no
-alert
");
262 assert.deepStrictEqual(
263 suppressedMessages[0].suppressions,
264 [{ kind: "directive
", justification: "j1
\n * --- j2
" }]
268 it("should not report a lint message
", () => {
270 "/* eslint-disable -- j1 */",
271 "// eslint-disable-next-line -- j2",
275 rules
: { "no-alert": 1 }
277 const messages
= linter
.verify(code
, config
);
278 const suppressedMessages
= linter
.getSuppressedMessages();
280 assert
.strictEqual(messages
.length
, 0);
282 assert
.strictEqual(suppressedMessages
.length
, 1);
283 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
284 assert
.deepStrictEqual(
285 suppressedMessages
[0].suppressions
,
287 { kind
: "directive", justification
: "j1" },
288 { kind
: "directive", justification
: "j2" }
293 it("should not report a lint message", () => {
295 "/* eslint-disable -- j1 */",
296 "alert(\"test\"); // eslint-disable-line -- j2"
299 rules
: { "no-alert": 1 }
301 const messages
= linter
.verify(code
, config
);
302 const suppressedMessages
= linter
.getSuppressedMessages();
304 assert
.strictEqual(messages
.length
, 0);
306 assert
.strictEqual(suppressedMessages
.length
, 1);
307 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
308 assert
.deepStrictEqual(
309 suppressedMessages
[0].suppressions
,
311 { kind
: "directive", justification
: "j1" },
312 { kind
: "directive", justification
: "j2" }
317 it("should have a suppressed message with multiple suppressions", () => {
319 "/* eslint-disable no-alert -- j1 */",
320 "/* eslint-disable no-console -- unused */",
321 "/* eslint-disable-next-line no-alert -- j2 */",
322 "alert(\"test\"); // eslint-disable-line no-alert -- j3"
325 rules
: { "no-alert": 1 }
327 const messages
= linter
.verify(code
, config
);
328 const suppressedMessages
= linter
.getSuppressedMessages();
330 assert
.strictEqual(messages
.length
, 0);
332 assert
.strictEqual(suppressedMessages
.length
, 1);
333 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
334 assert
.deepStrictEqual(
335 suppressedMessages
[0].suppressions
,
337 { kind
: "directive", justification
: "j1" },
338 { kind
: "directive", justification
: "j2" },
339 { kind
: "directive", justification
: "j3" }
345 describe("context.getSource()", () => {
346 const code
= TEST_CODE
;
348 it("should retrieve all text when used without parameters", () => {
350 const config
= { rules
: { checker
: "error" } };
353 linter
.defineRule("checker", context
=> {
354 spy
= sinon
.spy(() => {
355 assert
.strictEqual(context
.getSource(), TEST_CODE
);
357 return { Program
: spy
};
360 linter
.verify(code
, config
);
361 assert(spy
&& spy
.calledOnce
);
364 it("should retrieve all text for root node", () => {
365 const config
= { rules
: { checker
: "error" } };
368 linter
.defineRule("checker", context
=> {
369 spy
= sinon
.spy(node
=> {
370 assert
.strictEqual(context
.getSource(node
), TEST_CODE
);
372 return { Program
: spy
};
375 linter
.verify(code
, config
);
376 assert(spy
&& spy
.calledOnce
);
379 it("should clamp to valid range when retrieving characters before start of source", () => {
380 const config
= { rules
: { checker
: "error" } };
383 linter
.defineRule("checker", context
=> {
384 spy
= sinon
.spy(node
=> {
385 assert
.strictEqual(context
.getSource(node
, 2, 0), TEST_CODE
);
387 return { Program
: spy
};
390 linter
.verify(code
, config
);
391 assert(spy
&& spy
.calledOnce
);
394 it("should retrieve all text for binary expression", () => {
395 const config
= { rules
: { checker
: "error" } };
398 linter
.defineRule("checker", context
=> {
399 spy
= sinon
.spy(node
=> {
400 assert
.strictEqual(context
.getSource(node
), "6 * 7");
402 return { BinaryExpression
: spy
};
405 linter
.verify(code
, config
);
406 assert(spy
&& spy
.calledOnce
);
409 it("should retrieve all text plus two characters before for binary expression", () => {
410 const config
= { rules
: { checker
: "error" } };
413 linter
.defineRule("checker", context
=> {
414 spy
= sinon
.spy(node
=> {
415 assert
.strictEqual(context
.getSource(node
, 2), "= 6 * 7");
417 return { BinaryExpression
: spy
};
420 linter
.verify(code
, config
);
421 assert(spy
&& spy
.calledOnce
);
424 it("should retrieve all text plus one character after for binary expression", () => {
425 const config
= { rules
: { checker
: "error" } };
428 linter
.defineRule("checker", context
=> {
429 spy
= sinon
.spy(node
=> {
430 assert
.strictEqual(context
.getSource(node
, 0, 1), "6 * 7;");
432 return { BinaryExpression
: spy
};
435 linter
.verify(code
, config
);
436 assert(spy
&& spy
.calledOnce
);
439 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
440 const config
= { rules
: { checker
: "error" } };
443 linter
.defineRule("checker", context
=> {
444 spy
= sinon
.spy(node
=> {
445 assert
.strictEqual(context
.getSource(node
, 2, 1), "= 6 * 7;");
447 return { BinaryExpression
: spy
};
450 linter
.verify(code
, config
);
451 assert(spy
&& spy
.calledOnce
);
456 describe("when calling context.getAncestors", () => {
457 const code
= TEST_CODE
;
459 it("should retrieve all ancestors when used", () => {
461 const config
= { rules
: { checker
: "error" } };
464 linter
.defineRule("checker", context
=> {
465 spy
= sinon
.spy(() => {
466 const ancestors
= context
.getAncestors();
468 assert
.strictEqual(ancestors
.length
, 3);
470 return { BinaryExpression
: spy
};
473 linter
.verify(code
, config
, filename
, true);
474 assert(spy
&& spy
.calledOnce
);
477 it("should retrieve empty ancestors for root node", () => {
478 const config
= { rules
: { checker
: "error" } };
481 linter
.defineRule("checker", context
=> {
482 spy
= sinon
.spy(() => {
483 const ancestors
= context
.getAncestors();
485 assert
.strictEqual(ancestors
.length
, 0);
488 return { Program
: spy
};
491 linter
.verify(code
, config
);
492 assert(spy
&& spy
.calledOnce
);
496 describe("when calling context.getNodeByRangeIndex", () => {
497 const code
= TEST_CODE
;
499 it("should retrieve a node starting at the given index", () => {
500 const config
= { rules
: { checker
: "error" } };
501 const spy
= sinon
.spy(context
=> {
502 assert
.strictEqual(context
.getNodeByRangeIndex(4).type
, "Identifier");
506 linter
.defineRule("checker", spy
);
507 linter
.verify(code
, config
);
508 assert(spy
.calledOnce
);
511 it("should retrieve a node containing the given index", () => {
512 const config
= { rules
: { checker
: "error" } };
513 const spy
= sinon
.spy(context
=> {
514 assert
.strictEqual(context
.getNodeByRangeIndex(6).type
, "Identifier");
518 linter
.defineRule("checker", spy
);
519 linter
.verify(code
, config
);
520 assert(spy
.calledOnce
);
523 it("should retrieve a node that is exactly the given index", () => {
524 const config
= { rules
: { checker
: "error" } };
525 const spy
= sinon
.spy(context
=> {
526 const node
= context
.getNodeByRangeIndex(13);
528 assert
.strictEqual(node
.type
, "Literal");
529 assert
.strictEqual(node
.value
, 6);
533 linter
.defineRule("checker", spy
);
534 linter
.verify(code
, config
);
535 assert(spy
.calledOnce
);
538 it("should retrieve a node ending with the given index", () => {
539 const config
= { rules
: { checker
: "error" } };
540 const spy
= sinon
.spy(context
=> {
541 assert
.strictEqual(context
.getNodeByRangeIndex(9).type
, "Identifier");
545 linter
.defineRule("checker", spy
);
546 linter
.verify(code
, config
);
547 assert(spy
.calledOnce
);
550 it("should retrieve the deepest node containing the given index", () => {
551 const config
= { rules
: { checker
: "error" } };
552 const spy
= sinon
.spy(context
=> {
553 const node1
= context
.getNodeByRangeIndex(14);
555 assert
.strictEqual(node1
.type
, "BinaryExpression");
557 const node2
= context
.getNodeByRangeIndex(3);
559 assert
.strictEqual(node2
.type
, "VariableDeclaration");
563 linter
.defineRule("checker", spy
);
564 linter
.verify(code
, config
);
565 assert(spy
.calledOnce
);
568 it("should return null if the index is outside the range of any node", () => {
569 const config
= { rules
: { checker
: "error" } };
570 const spy
= sinon
.spy(context
=> {
571 const node1
= context
.getNodeByRangeIndex(-1);
573 assert
.isNull(node1
);
575 const node2
= context
.getNodeByRangeIndex(-99);
577 assert
.isNull(node2
);
581 linter
.defineRule("checker", spy
);
582 linter
.verify(code
, config
);
583 assert(spy
.calledOnce
);
588 describe("when calling context.getScope", () => {
589 const code
= "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
591 it("should retrieve the global scope correctly from a Program", () => {
592 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
595 linter
.defineRule("checker", context
=> {
596 spy
= sinon
.spy(() => {
597 const scope
= context
.getScope();
599 assert
.strictEqual(scope
.type
, "global");
601 return { Program
: spy
};
604 linter
.verify(code
, config
);
605 assert(spy
&& spy
.calledOnce
);
608 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
609 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
612 linter
.defineRule("checker", context
=> {
613 spy
= sinon
.spy(() => {
614 const scope
= context
.getScope();
616 assert
.strictEqual(scope
.type
, "function");
618 return { FunctionDeclaration
: spy
};
621 linter
.verify(code
, config
);
622 assert(spy
&& spy
.calledTwice
);
625 it("should retrieve the function scope correctly from a LabeledStatement", () => {
626 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
629 linter
.defineRule("checker", context
=> {
630 spy
= sinon
.spy(() => {
631 const scope
= context
.getScope();
633 assert
.strictEqual(scope
.type
, "function");
634 assert
.strictEqual(scope
.block
.id
.name
, "foo");
636 return { LabeledStatement
: spy
};
639 linter
.verify(code
, config
);
640 assert(spy
&& spy
.calledOnce
);
643 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
644 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
647 linter
.defineRule("checker", context
=> {
648 spy
= sinon
.spy(() => {
649 const scope
= context
.getScope();
651 assert
.strictEqual(scope
.type
, "function");
652 assert
.strictEqual(scope
.block
.type
, "ArrowFunctionExpression");
655 return { ReturnStatement
: spy
};
658 linter
.verify(code
, config
);
659 assert(spy
&& spy
.calledOnce
);
662 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
663 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
666 linter
.defineRule("checker", context
=> {
667 spy
= sinon
.spy(() => {
668 const scope
= context
.getScope();
670 assert
.strictEqual(scope
.type
, "switch");
671 assert
.strictEqual(scope
.block
.type
, "SwitchStatement");
674 return { SwitchStatement
: spy
};
677 linter
.verify("switch(foo){ case 'a': var b = 'foo'; }", config
);
678 assert(spy
&& spy
.calledOnce
);
681 it("should retrieve the function scope correctly from within a BlockStatement", () => {
682 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
685 linter
.defineRule("checker", context
=> {
686 spy
= sinon
.spy(() => {
687 const scope
= context
.getScope();
689 assert
.strictEqual(scope
.type
, "block");
690 assert
.strictEqual(scope
.block
.type
, "BlockStatement");
693 return { BlockStatement
: spy
};
696 linter
.verify("var x; {let y = 1}", config
);
697 assert(spy
&& spy
.calledOnce
);
700 it("should retrieve the function scope correctly from within a nested block statement", () => {
701 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
704 linter
.defineRule("checker", context
=> {
705 spy
= sinon
.spy(() => {
706 const scope
= context
.getScope();
708 assert
.strictEqual(scope
.type
, "block");
709 assert
.strictEqual(scope
.block
.type
, "BlockStatement");
712 return { BlockStatement
: spy
};
715 linter
.verify("if (true) { let x = 1 }", config
);
716 assert(spy
&& spy
.calledOnce
);
719 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
720 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
723 linter
.defineRule("checker", context
=> {
724 spy
= sinon
.spy(() => {
725 const scope
= context
.getScope();
727 assert
.strictEqual(scope
.type
, "function");
728 assert
.strictEqual(scope
.block
.type
, "FunctionDeclaration");
731 return { FunctionDeclaration
: spy
};
734 linter
.verify("function foo() {}", config
);
735 assert(spy
&& spy
.calledOnce
);
738 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
739 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
742 linter
.defineRule("checker", context
=> {
743 spy
= sinon
.spy(() => {
744 const scope
= context
.getScope();
746 assert
.strictEqual(scope
.type
, "function");
747 assert
.strictEqual(scope
.block
.type
, "FunctionExpression");
750 return { FunctionExpression
: spy
};
753 linter
.verify("(function foo() {})();", config
);
754 assert(spy
&& spy
.calledOnce
);
757 it("should retrieve the catch scope correctly from within a CatchClause", () => {
758 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6 } };
761 linter
.defineRule("checker", context
=> {
762 spy
= sinon
.spy(() => {
763 const scope
= context
.getScope();
765 assert
.strictEqual(scope
.type
, "catch");
766 assert
.strictEqual(scope
.block
.type
, "CatchClause");
769 return { CatchClause
: spy
};
772 linter
.verify("try {} catch (err) {}", config
);
773 assert(spy
&& spy
.calledOnce
);
776 it("should retrieve module scope correctly from an ES6 module", () => {
777 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } };
780 linter
.defineRule("checker", context
=> {
781 spy
= sinon
.spy(() => {
782 const scope
= context
.getScope();
784 assert
.strictEqual(scope
.type
, "module");
787 return { AssignmentExpression
: spy
};
790 linter
.verify("var foo = {}; foo.bar = 1;", config
);
791 assert(spy
&& spy
.calledOnce
);
794 it("should retrieve function scope correctly when globalReturn is true", () => {
795 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6, ecmaFeatures
: { globalReturn
: true } } };
798 linter
.defineRule("checker", context
=> {
799 spy
= sinon
.spy(() => {
800 const scope
= context
.getScope();
802 assert
.strictEqual(scope
.type
, "function");
805 return { AssignmentExpression
: spy
};
808 linter
.verify("var foo = {}; foo.bar = 1;", config
);
809 assert(spy
&& spy
.calledOnce
);
813 describe("marking variables as used", () => {
814 it("should mark variables in current scope as used", () => {
815 const code
= "var a = 1, b = 2;";
818 linter
.defineRule("checker", context
=> {
819 spy
= sinon
.spy(() => {
820 assert
.isTrue(context
.markVariableAsUsed("a"));
822 const scope
= context
.getScope();
824 assert
.isTrue(getVariable(scope
, "a").eslintUsed
);
825 assert
.notOk(getVariable(scope
, "b").eslintUsed
);
828 return { "Program:exit": spy
};
831 linter
.verify(code
, { rules
: { checker
: "error" } });
832 assert(spy
&& spy
.calledOnce
);
834 it("should mark variables in function args as used", () => {
835 const code
= "function abc(a, b) { return 1; }";
838 linter
.defineRule("checker", context
=> {
839 spy
= sinon
.spy(() => {
840 assert
.isTrue(context
.markVariableAsUsed("a"));
842 const scope
= context
.getScope();
844 assert
.isTrue(getVariable(scope
, "a").eslintUsed
);
845 assert
.notOk(getVariable(scope
, "b").eslintUsed
);
848 return { ReturnStatement
: spy
};
851 linter
.verify(code
, { rules
: { checker
: "error" } });
852 assert(spy
&& spy
.calledOnce
);
854 it("should mark variables in higher scopes as used", () => {
855 const code
= "var a, b; function abc() { return 1; }";
856 let returnSpy
, exitSpy
;
858 linter
.defineRule("checker", context
=> {
859 returnSpy
= sinon
.spy(() => {
860 assert
.isTrue(context
.markVariableAsUsed("a"));
862 exitSpy
= sinon
.spy(() => {
863 const scope
= context
.getScope();
865 assert
.isTrue(getVariable(scope
, "a").eslintUsed
);
866 assert
.notOk(getVariable(scope
, "b").eslintUsed
);
869 return { ReturnStatement
: returnSpy
, "Program:exit": exitSpy
};
872 linter
.verify(code
, { rules
: { checker
: "error" } });
873 assert(returnSpy
&& returnSpy
.calledOnce
);
874 assert(exitSpy
&& exitSpy
.calledOnce
);
877 it("should mark variables in Node.js environment as used", () => {
878 const code
= "var a = 1, b = 2;";
881 linter
.defineRule("checker", context
=> {
882 spy
= sinon
.spy(() => {
883 const globalScope
= context
.getScope(),
884 childScope
= globalScope
.childScopes
[0];
886 assert
.isTrue(context
.markVariableAsUsed("a"));
888 assert
.isTrue(getVariable(childScope
, "a").eslintUsed
);
889 assert
.isUndefined(getVariable(childScope
, "b").eslintUsed
);
892 return { "Program:exit": spy
};
895 linter
.verify(code
, { rules
: { checker
: "error" }, env
: { node
: true } });
896 assert(spy
&& spy
.calledOnce
);
899 it("should mark variables in modules as used", () => {
900 const code
= "var a = 1, b = 2;";
903 linter
.defineRule("checker", context
=> {
904 spy
= sinon
.spy(() => {
905 const globalScope
= context
.getScope(),
906 childScope
= globalScope
.childScopes
[0];
908 assert
.isTrue(context
.markVariableAsUsed("a"));
910 assert
.isTrue(getVariable(childScope
, "a").eslintUsed
);
911 assert
.isUndefined(getVariable(childScope
, "b").eslintUsed
);
914 return { "Program:exit": spy
};
917 linter
.verify(code
, { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } }, filename
, true);
918 assert(spy
&& spy
.calledOnce
);
921 it("should return false if the given variable is not found", () => {
922 const code
= "var a = 1, b = 2;";
925 linter
.defineRule("checker", context
=> {
926 spy
= sinon
.spy(() => {
927 assert
.isFalse(context
.markVariableAsUsed("c"));
930 return { "Program:exit": spy
};
933 linter
.verify(code
, { rules
: { checker
: "error" } });
934 assert(spy
&& spy
.calledOnce
);
938 describe("when evaluating code", () => {
939 const code
= TEST_CODE
;
941 it("events for each node type should fire", () => {
942 const config
= { rules
: { checker
: "error" } };
944 // spies for various AST node types
945 const spyLiteral
= sinon
.spy(),
946 spyVariableDeclarator
= sinon
.spy(),
947 spyVariableDeclaration
= sinon
.spy(),
948 spyIdentifier
= sinon
.spy(),
949 spyBinaryExpression
= sinon
.spy();
951 linter
.defineRule("checker", () => ({
953 VariableDeclarator
: spyVariableDeclarator
,
954 VariableDeclaration
: spyVariableDeclaration
,
955 Identifier
: spyIdentifier
,
956 BinaryExpression
: spyBinaryExpression
959 const messages
= linter
.verify(code
, config
, filename
, true);
960 const suppressedMessages
= linter
.getSuppressedMessages();
962 assert
.strictEqual(messages
.length
, 0);
963 assert
.strictEqual(suppressedMessages
.length
, 0);
964 sinon
.assert
.calledOnce(spyVariableDeclaration
);
965 sinon
.assert
.calledOnce(spyVariableDeclarator
);
966 sinon
.assert
.calledOnce(spyIdentifier
);
967 sinon
.assert
.calledTwice(spyLiteral
);
968 sinon
.assert
.calledOnce(spyBinaryExpression
);
971 it("should throw an error if a rule reports a problem without a message", () => {
972 linter
.defineRule("invalid-report", context
=> ({
974 context
.report({ node
});
979 () => linter
.verify("foo", { rules
: { "invalid-report": "error" } }),
981 "Missing `message` property in report() call; add a message that describes the linting problem."
986 describe("when config has shared settings for rules", () => {
987 const code
= "test-rule";
989 it("should pass settings to all rules", () => {
990 linter
.defineRule(code
, context
=> ({
992 context
.report(node
, context
.settings
.info
);
996 const config
= { rules
: {}, settings
: { info
: "Hello" } };
998 config
.rules
[code
] = 1;
1000 const messages
= linter
.verify("0", config
, filename
);
1001 const suppressedMessages
= linter
.getSuppressedMessages();
1003 assert
.strictEqual(messages
.length
, 1);
1004 assert
.strictEqual(messages
[0].message
, "Hello");
1005 assert
.strictEqual(suppressedMessages
.length
, 0);
1008 it("should not have any settings if they were not passed in", () => {
1009 linter
.defineRule(code
, context
=> ({
1011 if (Object
.getOwnPropertyNames(context
.settings
).length
!== 0) {
1012 context
.report(node
, "Settings should be empty");
1017 const config
= { rules
: {} };
1019 config
.rules
[code
] = 1;
1021 const messages
= linter
.verify("0", config
, filename
);
1022 const suppressedMessages
= linter
.getSuppressedMessages();
1024 assert
.strictEqual(messages
.length
, 0);
1025 assert
.strictEqual(suppressedMessages
.length
, 0);
1029 describe("when config has parseOptions", () => {
1031 it("should pass ecmaFeatures to all rules when provided on config", () => {
1033 const parserOptions
= {
1040 linter
.defineRule("test-rule", sinon
.mock().withArgs(
1041 sinon
.match({ parserOptions
})
1044 const config
= { rules
: { "test-rule": 2 }, parserOptions
};
1046 linter
.verify("0", config
, filename
);
1049 it("should pass parserOptions to all rules when default parserOptions is used", () => {
1051 const parserOptions
= {};
1053 linter
.defineRule("test-rule", sinon
.mock().withArgs(
1054 sinon
.match({ parserOptions
})
1057 const config
= { rules
: { "test-rule": 2 } };
1059 linter
.verify("0", config
, filename
);
1064 describe("when a custom parser is defined using defineParser", () => {
1066 it("should be able to define a custom parser", () => {
1068 parseForESLint
: function parse(code
, options
) {
1070 ast
: esprima
.parse(code
, options
),
1082 linter
.defineParser("test-parser", parser
);
1083 const config
= { rules
: {}, parser
: "test-parser" };
1084 const messages
= linter
.verify("0", config
, filename
);
1085 const suppressedMessages
= linter
.getSuppressedMessages();
1087 assert
.strictEqual(messages
.length
, 0);
1088 assert
.strictEqual(suppressedMessages
.length
, 0);
1093 describe("when config has parser", () => {
1095 it("should pass parser as parserPath to all rules when provided on config", () => {
1097 const alternateParser
= "esprima";
1099 linter
.defineParser("esprima", esprima
);
1100 linter
.defineRule("test-rule", sinon
.mock().withArgs(
1101 sinon
.match({ parserPath
: alternateParser
})
1104 const config
= { rules
: { "test-rule": 2 }, parser
: alternateParser
};
1106 linter
.verify("0", config
, filename
);
1109 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
1110 const config
= { rules
: {}, parser
: "enhanced-parser" };
1112 linter
.defineParser("enhanced-parser", testParsers
.enhancedParser
);
1113 const messages
= linter
.verify("0", config
, filename
);
1114 const suppressedMessages
= linter
.getSuppressedMessages();
1116 assert
.strictEqual(messages
.length
, 0);
1117 assert
.strictEqual(suppressedMessages
.length
, 0);
1120 it("should expose parser services when using parseForESLint() and services are specified", () => {
1121 linter
.defineParser("enhanced-parser", testParsers
.enhancedParser
);
1122 linter
.defineRule("test-service-rule", context
=> ({
1126 message
: context
.parserServices
.test
.getMessage()
1131 const config
= { rules
: { "test-service-rule": 2 }, parser
: "enhanced-parser" };
1132 const messages
= linter
.verify("0", config
, filename
);
1133 const suppressedMessages
= linter
.getSuppressedMessages();
1135 assert
.strictEqual(messages
.length
, 1);
1136 assert
.strictEqual(messages
[0].message
, "Hi!");
1137 assert
.strictEqual(suppressedMessages
.length
, 0);
1140 it("should use the same parserServices if source code object is reused", () => {
1141 linter
.defineParser("enhanced-parser", testParsers
.enhancedParser
);
1142 linter
.defineRule("test-service-rule", context
=> ({
1146 message
: context
.parserServices
.test
.getMessage()
1151 const config
= { rules
: { "test-service-rule": 2 }, parser
: "enhanced-parser" };
1152 const messages
= linter
.verify("0", config
, filename
);
1153 const suppressedMessages
= linter
.getSuppressedMessages();
1155 assert
.strictEqual(messages
.length
, 1);
1156 assert
.strictEqual(messages
[0].message
, "Hi!");
1157 assert
.strictEqual(suppressedMessages
.length
, 0);
1159 const messages2
= linter
.verify(linter
.getSourceCode(), config
, filename
);
1160 const suppressedMessages2
= linter
.getSuppressedMessages();
1162 assert
.strictEqual(messages2
.length
, 1);
1163 assert
.strictEqual(messages2
[0].message
, "Hi!");
1164 assert
.strictEqual(suppressedMessages2
.length
, 0);
1167 it("should pass parser as parserPath to all rules when default parser is used", () => {
1168 linter
.defineRule("test-rule", sinon
.mock().withArgs(
1169 sinon
.match({ parserPath
: "espree" })
1172 const config
= { rules
: { "test-rule": 2 } };
1174 linter
.verify("0", config
, filename
);
1180 describe("when passing in configuration values for rules", () => {
1181 const code
= "var answer = 6 * 7";
1183 it("should be configurable by only setting the integer value", () => {
1184 const rule
= "semi",
1185 config
= { rules
: {} };
1187 config
.rules
[rule
] = 1;
1189 const messages
= linter
.verify(code
, config
, filename
, true);
1190 const suppressedMessages
= linter
.getSuppressedMessages();
1192 assert
.strictEqual(messages
.length
, 1);
1193 assert
.strictEqual(messages
[0].ruleId
, rule
);
1195 assert
.strictEqual(suppressedMessages
.length
, 0);
1198 it("should be configurable by only setting the string value", () => {
1199 const rule
= "semi",
1200 config
= { rules
: {} };
1202 config
.rules
[rule
] = "warn";
1204 const messages
= linter
.verify(code
, config
, filename
, true);
1205 const suppressedMessages
= linter
.getSuppressedMessages();
1207 assert
.strictEqual(messages
.length
, 1);
1208 assert
.strictEqual(messages
[0].severity
, 1);
1209 assert
.strictEqual(messages
[0].ruleId
, rule
);
1211 assert
.strictEqual(suppressedMessages
.length
, 0);
1214 it("should be configurable by passing in values as an array", () => {
1215 const rule
= "semi",
1216 config
= { rules
: {} };
1218 config
.rules
[rule
] = [1];
1220 const messages
= linter
.verify(code
, config
, filename
, true);
1221 const suppressedMessages
= linter
.getSuppressedMessages();
1223 assert
.strictEqual(messages
.length
, 1);
1224 assert
.strictEqual(messages
[0].ruleId
, rule
);
1226 assert
.strictEqual(suppressedMessages
.length
, 0);
1229 it("should be configurable by passing in string value as an array", () => {
1230 const rule
= "semi",
1231 config
= { rules
: {} };
1233 config
.rules
[rule
] = ["warn"];
1235 const messages
= linter
.verify(code
, config
, filename
, true);
1236 const suppressedMessages
= linter
.getSuppressedMessages();
1238 assert
.strictEqual(messages
.length
, 1);
1239 assert
.strictEqual(messages
[0].severity
, 1);
1240 assert
.strictEqual(messages
[0].ruleId
, rule
);
1242 assert
.strictEqual(suppressedMessages
.length
, 0);
1245 it("should not be configurable by setting other value", () => {
1246 const rule
= "semi",
1247 config
= { rules
: {} };
1249 config
.rules
[rule
] = "1";
1251 const messages
= linter
.verify(code
, config
, filename
, true);
1252 const suppressedMessages
= linter
.getSuppressedMessages();
1254 assert
.strictEqual(messages
.length
, 0);
1255 assert
.strictEqual(suppressedMessages
.length
, 0);
1258 it("should process empty config", () => {
1260 const messages
= linter
.verify(code
, config
, filename
, true);
1261 const suppressedMessages
= linter
.getSuppressedMessages();
1263 assert
.strictEqual(messages
.length
, 0);
1264 assert
.strictEqual(suppressedMessages
.length
, 0);
1268 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
1270 it("variables should be available in global scope", () => {
1271 const config
= { rules
: { checker
: "error" }, globals
: { Array
: "off", ConfigGlobal
: "writeable" } };
1273 /*global a b:true c:false d:readable e:writeable Math:off */
1276 /* global ConfigGlobal : readable */
1280 linter
.defineRule("checker", context
=> {
1281 spy
= sinon
.spy(() => {
1282 const scope
= context
.getScope();
1283 const a
= getVariable(scope
, "a"),
1284 b
= getVariable(scope
, "b"),
1285 c
= getVariable(scope
, "c"),
1286 d
= getVariable(scope
, "d"),
1287 e
= getVariable(scope
, "e"),
1288 f
= getVariable(scope
, "f"),
1289 mathGlobal
= getVariable(scope
, "Math"),
1290 arrayGlobal
= getVariable(scope
, "Array"),
1291 configGlobal
= getVariable(scope
, "ConfigGlobal");
1293 assert
.strictEqual(a
.name
, "a");
1294 assert
.strictEqual(a
.writeable
, false);
1295 assert
.strictEqual(b
.name
, "b");
1296 assert
.strictEqual(b
.writeable
, true);
1297 assert
.strictEqual(c
.name
, "c");
1298 assert
.strictEqual(c
.writeable
, false);
1299 assert
.strictEqual(d
.name
, "d");
1300 assert
.strictEqual(d
.writeable
, false);
1301 assert
.strictEqual(e
.name
, "e");
1302 assert
.strictEqual(e
.writeable
, true);
1303 assert
.strictEqual(f
.name
, "f");
1304 assert
.strictEqual(f
.writeable
, true);
1305 assert
.strictEqual(mathGlobal
, null);
1306 assert
.strictEqual(arrayGlobal
, null);
1307 assert
.strictEqual(configGlobal
.name
, "ConfigGlobal");
1308 assert
.strictEqual(configGlobal
.writeable
, false);
1311 return { Program
: spy
};
1314 linter
.verify(code
, config
);
1315 assert(spy
&& spy
.calledOnce
);
1319 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
1320 const code
= "/* global a b : true c: false*/";
1322 it("variables should be available in global scope", () => {
1323 const config
= { rules
: { checker
: "error" } };
1326 linter
.defineRule("checker", context
=> {
1327 spy
= sinon
.spy(() => {
1328 const scope
= context
.getScope(),
1329 a
= getVariable(scope
, "a"),
1330 b
= getVariable(scope
, "b"),
1331 c
= getVariable(scope
, "c");
1333 assert
.strictEqual(a
.name
, "a");
1334 assert
.strictEqual(a
.writeable
, false);
1335 assert
.strictEqual(b
.name
, "b");
1336 assert
.strictEqual(b
.writeable
, true);
1337 assert
.strictEqual(c
.name
, "c");
1338 assert
.strictEqual(c
.writeable
, false);
1341 return { Program
: spy
};
1344 linter
.verify(code
, config
);
1345 assert(spy
&& spy
.calledOnce
);
1349 describe("when evaluating code containing a /*global */ block with specific variables", () => {
1350 const code
= "/* global toString hasOwnProperty valueOf: true */";
1352 it("should not throw an error if comment block has global variables which are Object.prototype contains", () => {
1353 const config
= { rules
: { checker
: "error" } };
1355 linter
.verify(code
, config
);
1359 describe("when evaluating code containing /*eslint-env */ block", () => {
1360 it("variables should be available in global scope", () => {
1361 const code
= `/*${ESLINT_ENV} node*/ function f() {} /*${ESLINT_ENV} browser, foo*/`;
1362 const config
= { rules
: { checker
: "error" } };
1365 linter
.defineRule("checker", context
=> {
1366 spy
= sinon
.spy(() => {
1367 const scope
= context
.getScope(),
1368 exports
= getVariable(scope
, "exports"),
1369 window
= getVariable(scope
, "window");
1371 assert
.strictEqual(exports
.writeable
, true);
1372 assert
.strictEqual(window
.writeable
, false);
1375 return { Program
: spy
};
1378 linter
.verify(code
, config
);
1379 assert(spy
&& spy
.calledOnce
);
1383 describe("when evaluating code containing /*eslint-env */ block with sloppy whitespace", () => {
1384 const code
= `/* ${ESLINT_ENV} ,, node , no-browser ,, */`;
1386 it("variables should be available in global scope", () => {
1387 const config
= { rules
: { checker
: "error" } };
1390 linter
.defineRule("checker", context
=> {
1391 spy
= sinon
.spy(() => {
1392 const scope
= context
.getScope(),
1393 exports
= getVariable(scope
, "exports"),
1394 window
= getVariable(scope
, "window");
1396 assert
.strictEqual(exports
.writeable
, true);
1397 assert
.strictEqual(window
, null);
1400 return { Program
: spy
};
1403 linter
.verify(code
, config
);
1404 assert(spy
&& spy
.calledOnce
);
1408 describe("when evaluating code containing /*exported */ block", () => {
1410 it("we should behave nicely when no matching variable is found", () => {
1411 const code
= "/* exported horse */";
1412 const config
= { rules
: {} };
1414 linter
.verify(code
, config
, filename
, true);
1417 it("variables should be exported", () => {
1418 const code
= "/* exported horse */\n\nvar horse = 'circus'";
1419 const config
= { rules
: { checker
: "error" } };
1422 linter
.defineRule("checker", context
=> {
1423 spy
= sinon
.spy(() => {
1424 const scope
= context
.getScope(),
1425 horse
= getVariable(scope
, "horse");
1427 assert
.strictEqual(horse
.eslintUsed
, true);
1430 return { Program
: spy
};
1433 linter
.verify(code
, config
);
1434 assert(spy
&& spy
.calledOnce
);
1437 it("undefined variables should not be exported", () => {
1438 const code
= "/* exported horse */\n\nhorse = 'circus'";
1439 const config
= { rules
: { checker
: "error" } };
1442 linter
.defineRule("checker", context
=> {
1443 spy
= sinon
.spy(() => {
1444 const scope
= context
.getScope(),
1445 horse
= getVariable(scope
, "horse");
1447 assert
.strictEqual(horse
, null);
1450 return { Program
: spy
};
1453 linter
.verify(code
, config
);
1454 assert(spy
&& spy
.calledOnce
);
1457 it("variables should be exported in strict mode", () => {
1458 const code
= "/* exported horse */\n'use strict';\nvar horse = 'circus'";
1459 const config
= { rules
: { checker
: "error" } };
1462 linter
.defineRule("checker", context
=> {
1463 spy
= sinon
.spy(() => {
1464 const scope
= context
.getScope(),
1465 horse
= getVariable(scope
, "horse");
1467 assert
.strictEqual(horse
.eslintUsed
, true);
1470 return { Program
: spy
};
1473 linter
.verify(code
, config
);
1474 assert(spy
&& spy
.calledOnce
);
1477 it("variables should not be exported in the es6 module environment", () => {
1478 const code
= "/* exported horse */\nvar horse = 'circus'";
1479 const config
= { rules
: { checker
: "error" }, parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } };
1482 linter
.defineRule("checker", context
=> {
1483 spy
= sinon
.spy(() => {
1484 const scope
= context
.getScope(),
1485 horse
= getVariable(scope
, "horse");
1487 assert
.strictEqual(horse
, null); // there is no global scope at all
1490 return { Program
: spy
};
1493 linter
.verify(code
, config
);
1494 assert(spy
&& spy
.calledOnce
);
1497 it("variables should not be exported when in the node environment", () => {
1498 const code
= "/* exported horse */\nvar horse = 'circus'";
1499 const config
= { rules
: { checker
: "error" }, env
: { node
: true } };
1502 linter
.defineRule("checker", context
=> {
1503 spy
= sinon
.spy(() => {
1504 const scope
= context
.getScope(),
1505 horse
= getVariable(scope
, "horse");
1507 assert
.strictEqual(horse
, null); // there is no global scope at all
1510 return { Program
: spy
};
1513 linter
.verify(code
, config
);
1514 assert(spy
&& spy
.calledOnce
);
1518 describe("when evaluating code containing a line comment", () => {
1519 const code
= "//global a \n function f() {}";
1521 it("should not introduce a global variable", () => {
1522 const config
= { rules
: { checker
: "error" } };
1525 linter
.defineRule("checker", context
=> {
1526 spy
= sinon
.spy(() => {
1527 const scope
= context
.getScope();
1529 assert
.strictEqual(getVariable(scope
, "a"), null);
1532 return { Program
: spy
};
1535 linter
.verify(code
, config
);
1536 assert(spy
&& spy
.calledOnce
);
1540 describe("when evaluating code containing normal block comments", () => {
1541 const code
= "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
1543 it("should not introduce a global variable", () => {
1544 const config
= { rules
: { checker
: "error" } };
1547 linter
.defineRule("checker", context
=> {
1548 spy
= sinon
.spy(() => {
1549 const scope
= context
.getScope();
1551 assert
.strictEqual(getVariable(scope
, "a"), null);
1552 assert
.strictEqual(getVariable(scope
, "b"), null);
1553 assert
.strictEqual(getVariable(scope
, "foo"), null);
1554 assert
.strictEqual(getVariable(scope
, "c"), null);
1557 return { Program
: spy
};
1560 linter
.verify(code
, config
);
1561 assert(spy
&& spy
.calledOnce
);
1565 describe("when evaluating any code", () => {
1568 it("builtin global variables should be available in the global scope", () => {
1569 const config
= { rules
: { checker
: "error" } };
1572 linter
.defineRule("checker", context
=> {
1573 spy
= sinon
.spy(() => {
1574 const scope
= context
.getScope();
1576 assert
.notStrictEqual(getVariable(scope
, "Object"), null);
1577 assert
.notStrictEqual(getVariable(scope
, "Array"), null);
1578 assert
.notStrictEqual(getVariable(scope
, "undefined"), null);
1581 return { Program
: spy
};
1584 linter
.verify(code
, config
);
1585 assert(spy
&& spy
.calledOnce
);
1588 it("ES6 global variables should not be available by default", () => {
1589 const config
= { rules
: { checker
: "error" } };
1592 linter
.defineRule("checker", context
=> {
1593 spy
= sinon
.spy(() => {
1594 const scope
= context
.getScope();
1596 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1597 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1598 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1601 return { Program
: spy
};
1604 linter
.verify(code
, config
);
1605 assert(spy
&& spy
.calledOnce
);
1608 it("ES6 global variables should be available in the es6 environment", () => {
1609 const config
= { rules
: { checker
: "error" }, env
: { es6
: true } };
1612 linter
.defineRule("checker", context
=> {
1613 spy
= sinon
.spy(() => {
1614 const scope
= context
.getScope();
1616 assert
.notStrictEqual(getVariable(scope
, "Promise"), null);
1617 assert
.notStrictEqual(getVariable(scope
, "Symbol"), null);
1618 assert
.notStrictEqual(getVariable(scope
, "WeakMap"), null);
1621 return { Program
: spy
};
1624 linter
.verify(code
, config
);
1625 assert(spy
&& spy
.calledOnce
);
1628 it("ES6 global variables can be disabled when the es6 environment is enabled", () => {
1629 const config
= { rules
: { checker
: "error" }, globals
: { Promise
: "off", Symbol
: "off", WeakMap
: "off" }, env
: { es6
: true } };
1632 linter
.defineRule("checker", context
=> {
1633 spy
= sinon
.spy(() => {
1634 const scope
= context
.getScope();
1636 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1637 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1638 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1641 return { Program
: spy
};
1644 linter
.verify(code
, config
);
1645 assert(spy
&& spy
.calledOnce
);
1649 describe("at any time", () => {
1650 const code
= "new-rule";
1652 it("can add a rule dynamically", () => {
1653 linter
.defineRule(code
, context
=> ({
1655 context
.report(node
, "message");
1659 const config
= { rules
: {} };
1661 config
.rules
[code
] = 1;
1663 const messages
= linter
.verify("0", config
, filename
);
1664 const suppressedMessages
= linter
.getSuppressedMessages();
1666 assert
.strictEqual(messages
.length
, 1);
1667 assert
.strictEqual(messages
[0].ruleId
, code
);
1668 assert
.strictEqual(messages
[0].nodeType
, "Literal");
1670 assert
.strictEqual(suppressedMessages
.length
, 0);
1674 describe("at any time", () => {
1675 const code
= ["new-rule-0", "new-rule-1"];
1677 it("can add multiple rules dynamically", () => {
1678 const config
= { rules
: {} };
1679 const newRules
= {};
1681 code
.forEach(item
=> {
1682 config
.rules
[item
] = 1;
1683 newRules
[item
] = function(context
) {
1686 context
.report(node
, "message");
1691 linter
.defineRules(newRules
);
1693 const messages
= linter
.verify("0", config
, filename
);
1694 const suppressedMessages
= linter
.getSuppressedMessages();
1696 assert
.strictEqual(messages
.length
, code
.length
);
1697 code
.forEach(item
=> {
1698 assert
.ok(messages
.some(message
=> message
.ruleId
=== item
));
1700 messages
.forEach(message
=> {
1701 assert
.strictEqual(message
.nodeType
, "Literal");
1704 assert
.strictEqual(suppressedMessages
.length
, 0);
1708 describe("at any time", () => {
1709 const code
= "filename-rule";
1711 it("has access to the filename", () => {
1712 linter
.defineRule(code
, context
=> ({
1714 context
.report(node
, context
.getFilename());
1718 const config
= { rules
: {} };
1720 config
.rules
[code
] = 1;
1722 const messages
= linter
.verify("0", config
, filename
);
1723 const suppressedMessages
= linter
.getSuppressedMessages();
1725 assert
.strictEqual(messages
[0].message
, filename
);
1726 assert
.strictEqual(suppressedMessages
.length
, 0);
1729 it("has access to the physicalFilename", () => {
1730 linter
.defineRule(code
, context
=> ({
1732 context
.report(node
, context
.getPhysicalFilename());
1736 const config
= { rules
: {} };
1738 config
.rules
[code
] = 1;
1740 const messages
= linter
.verify("0", config
, filename
);
1741 const suppressedMessages
= linter
.getSuppressedMessages();
1743 assert
.strictEqual(messages
[0].message
, filename
);
1744 assert
.strictEqual(suppressedMessages
.length
, 0);
1747 it("defaults filename to '<input>'", () => {
1748 linter
.defineRule(code
, context
=> ({
1750 context
.report(node
, context
.getFilename());
1754 const config
= { rules
: {} };
1756 config
.rules
[code
] = 1;
1758 const messages
= linter
.verify("0", config
);
1759 const suppressedMessages
= linter
.getSuppressedMessages();
1761 assert
.strictEqual(messages
[0].message
, "<input>");
1762 assert
.strictEqual(suppressedMessages
.length
, 0);
1766 describe("when evaluating code with comments to enable rules", () => {
1768 it("should report a violation", () => {
1769 const code
= "/*eslint no-alert:1*/ alert('test');";
1770 const config
= { rules
: {} };
1772 const messages
= linter
.verify(code
, config
, filename
);
1773 const suppressedMessages
= linter
.getSuppressedMessages();
1775 assert
.strictEqual(messages
.length
, 1);
1776 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1777 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1778 assert
.include(messages
[0].nodeType
, "CallExpression");
1780 assert
.strictEqual(suppressedMessages
.length
, 0);
1783 it("rules should not change initial config", () => {
1784 const config
= { rules
: { strict
: 2 } };
1785 const codeA
= "/*eslint strict: 0*/ function bar() { return 2; }";
1786 const codeB
= "function foo() { return 1; }";
1787 let messages
= linter
.verify(codeA
, config
, filename
, false);
1788 let suppressedMessages
= linter
.getSuppressedMessages();
1790 assert
.strictEqual(messages
.length
, 0);
1791 assert
.strictEqual(suppressedMessages
.length
, 0);
1793 messages
= linter
.verify(codeB
, config
, filename
, false);
1794 suppressedMessages
= linter
.getSuppressedMessages();
1795 assert
.strictEqual(messages
.length
, 1);
1797 assert
.strictEqual(suppressedMessages
.length
, 0);
1800 it("rules should not change initial config", () => {
1801 const config
= { rules
: { quotes
: [2, "double"] } };
1802 const codeA
= "/*eslint quotes: 0*/ function bar() { return '2'; }";
1803 const codeB
= "function foo() { return '1'; }";
1804 let messages
= linter
.verify(codeA
, config
, filename
, false);
1805 let suppressedMessages
= linter
.getSuppressedMessages();
1807 assert
.strictEqual(messages
.length
, 0);
1808 assert
.strictEqual(suppressedMessages
.length
, 0);
1810 messages
= linter
.verify(codeB
, config
, filename
, false);
1811 suppressedMessages
= linter
.getSuppressedMessages();
1812 assert
.strictEqual(messages
.length
, 1);
1814 assert
.strictEqual(suppressedMessages
.length
, 0);
1817 it("rules should not change initial config", () => {
1818 const config
= { rules
: { quotes
: [2, "double"] } };
1819 const codeA
= "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
1820 const codeB
= "function foo() { return '1'; }";
1822 let messages
= linter
.verify(codeA
, config
, filename
, false);
1823 let suppressedMessages
= linter
.getSuppressedMessages();
1825 assert
.strictEqual(messages
.length
, 0);
1826 assert
.strictEqual(suppressedMessages
.length
, 0);
1828 messages
= linter
.verify(codeB
, config
, filename
, false);
1829 suppressedMessages
= linter
.getSuppressedMessages();
1831 assert
.strictEqual(messages
.length
, 1);
1832 assert
.strictEqual(suppressedMessages
.length
, 0);
1835 it("rules should not change initial config", () => {
1836 const config
= { rules
: { "no-unused-vars": [2, { vars
: "all" }] } };
1837 const codeA
= "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
1838 const codeB
= "var b = 55;";
1840 let messages
= linter
.verify(codeA
, config
, filename
, false);
1841 let suppressedMessages
= linter
.getSuppressedMessages();
1843 assert
.strictEqual(messages
.length
, 0);
1844 assert
.strictEqual(suppressedMessages
.length
, 0);
1846 messages
= linter
.verify(codeB
, config
, filename
, false);
1847 suppressedMessages
= linter
.getSuppressedMessages();
1849 assert
.strictEqual(messages
.length
, 1);
1850 assert
.strictEqual(suppressedMessages
.length
, 0);
1854 describe("when evaluating code with invalid comments to enable rules", () => {
1855 it("should report a violation when the config is not a valid rule configuration", () => {
1856 const messages
= linter
.verify("/*eslint no-alert:true*/ alert('test');", {});
1857 const suppressedMessages
= linter
.getSuppressedMessages();
1859 assert
.deepStrictEqual(
1865 message
: "Configuration for rule \"no-alert\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed 'true').\n",
1875 assert
.strictEqual(suppressedMessages
.length
, 0);
1878 it("should report a violation when the config violates a rule's schema", () => {
1879 const messages
= linter
.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {});
1880 const suppressedMessages
= linter
.getSuppressedMessages();
1882 assert
.deepStrictEqual(
1888 message
: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
1898 assert
.strictEqual(suppressedMessages
.length
, 0);
1902 describe("when evaluating code with comments to disable rules", () => {
1903 const code
= "/*eslint no-alert:0*/ alert('test');";
1905 it("should not report a violation", () => {
1906 const config
= { rules
: { "no-alert": 1 } };
1908 const messages
= linter
.verify(code
, config
, filename
);
1909 const suppressedMessages
= linter
.getSuppressedMessages();
1911 assert
.strictEqual(messages
.length
, 0);
1912 assert
.strictEqual(suppressedMessages
.length
, 0);
1916 describe("when evaluating code with comments to disable rules", () => {
1917 let code
, messages
, suppressedMessages
;
1919 it("should report an error when disabling a non-existent rule in inline comment", () => {
1920 code
= "/*eslint foo:0*/ ;";
1921 messages
= linter
.verify(code
, {}, filename
);
1922 suppressedMessages
= linter
.getSuppressedMessages();
1923 assert
.strictEqual(messages
.length
, 1);
1924 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1925 assert
.strictEqual(suppressedMessages
.length
, 0);
1927 code
= "/*eslint-disable foo*/ ;";
1928 messages
= linter
.verify(code
, {}, filename
);
1929 suppressedMessages
= linter
.getSuppressedMessages();
1930 assert
.strictEqual(messages
.length
, 1);
1931 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1932 assert
.strictEqual(suppressedMessages
.length
, 0);
1934 code
= "/*eslint-disable-line foo*/ ;";
1935 messages
= linter
.verify(code
, {}, filename
);
1936 suppressedMessages
= linter
.getSuppressedMessages();
1937 assert
.strictEqual(messages
.length
, 1);
1938 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1939 assert
.strictEqual(suppressedMessages
.length
, 0);
1941 code
= "/*eslint-disable-next-line foo*/ ;";
1942 messages
= linter
.verify(code
, {}, filename
);
1943 suppressedMessages
= linter
.getSuppressedMessages();
1944 assert
.strictEqual(messages
.length
, 1);
1945 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1946 assert
.strictEqual(suppressedMessages
.length
, 0);
1949 it("should not report an error, when disabling a non-existent rule in config", () => {
1950 messages
= linter
.verify("", { rules
: { foo
: 0 } }, filename
);
1951 suppressedMessages
= linter
.getSuppressedMessages();
1953 assert
.strictEqual(messages
.length
, 0);
1954 assert
.strictEqual(suppressedMessages
.length
, 0);
1957 it("should report an error, when config a non-existent rule in config", () => {
1958 messages
= linter
.verify("", { rules
: { foo
: 1 } }, filename
);
1959 suppressedMessages
= linter
.getSuppressedMessages();
1960 assert
.strictEqual(messages
.length
, 1);
1961 assert
.strictEqual(messages
[0].severity
, 2);
1962 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1963 assert
.strictEqual(suppressedMessages
.length
, 0);
1965 messages
= linter
.verify("", { rules
: { foo
: 2 } }, filename
);
1966 suppressedMessages
= linter
.getSuppressedMessages();
1967 assert
.strictEqual(messages
.length
, 1);
1968 assert
.strictEqual(messages
[0].severity
, 2);
1969 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1970 assert
.strictEqual(suppressedMessages
.length
, 0);
1974 describe("when evaluating code with comments to enable multiple rules", () => {
1975 const code
= "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
1977 it("should report a violation", () => {
1978 const config
= { rules
: {} };
1980 const messages
= linter
.verify(code
, config
, filename
);
1981 const suppressedMessages
= linter
.getSuppressedMessages();
1983 assert
.strictEqual(messages
.length
, 2);
1984 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1985 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1986 assert
.include(messages
[0].nodeType
, "CallExpression");
1987 assert
.strictEqual(messages
[1].ruleId
, "no-console");
1989 assert
.strictEqual(suppressedMessages
.length
, 0);
1993 describe("when evaluating code with comments to enable and disable multiple rules", () => {
1994 const code
= "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
1996 it("should report a violation", () => {
1997 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
1999 const messages
= linter
.verify(code
, config
, filename
);
2000 const suppressedMessages
= linter
.getSuppressedMessages();
2002 assert
.strictEqual(messages
.length
, 1);
2003 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2004 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
2005 assert
.include(messages
[0].nodeType
, "CallExpression");
2007 assert
.strictEqual(suppressedMessages
.length
, 0);
2011 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
2014 linter
.defineRule("test-plugin/test-rule", context
=> ({
2016 if (node
.value
=== "trigger violation") {
2017 context
.report(node
, "Reporting violation.");
2023 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
2024 const config
= { rules
: {} };
2025 const code
= "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
2027 const messages
= linter
.verify(code
, config
, filename
);
2028 const suppressedMessages
= linter
.getSuppressedMessages();
2030 assert
.strictEqual(messages
.length
, 0);
2031 assert
.strictEqual(suppressedMessages
.length
, 0);
2034 it("should not report a violation when inline comment disables plugin rule", () => {
2035 const code
= "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
2036 const config
= { rules
: { "test-plugin/test-rule": 1 } };
2038 const messages
= linter
.verify(code
, config
, filename
);
2039 const suppressedMessages
= linter
.getSuppressedMessages();
2041 assert
.strictEqual(messages
.length
, 0);
2042 assert
.strictEqual(suppressedMessages
.length
, 0);
2045 it("should report a violation when the report is right before the comment", () => {
2046 const code
= " /* eslint-disable */ ";
2048 linter
.defineRule("checker", context
=> ({
2050 context
.report({ loc
: { line
: 1, column
: 0 }, message
: "foo" });
2053 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
2054 const suppressedMessages
= linter
.getSuppressedMessages();
2056 assert
.strictEqual(problems
.length
, 1);
2057 assert
.strictEqual(problems
[0].message
, "foo");
2058 assert
.strictEqual(suppressedMessages
.length
, 0);
2061 it("should not report a violation when the report is right at the start of the comment", () => {
2062 const code
= " /* eslint-disable */ ";
2064 linter
.defineRule("checker", context
=> ({
2066 context
.report({ loc
: { line
: 1, column
: 1 }, message
: "foo" });
2069 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
2070 const suppressedMessages
= linter
.getSuppressedMessages();
2072 assert
.strictEqual(problems
.length
, 0);
2074 assert
.strictEqual(suppressedMessages
.length
, 1);
2075 assert
.strictEqual(suppressedMessages
[0].message
, "foo");
2076 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 1);
2077 assert
.strictEqual(suppressedMessages
[0].suppressions
[0].justification
, "");
2080 it("rules should not change initial config", () => {
2081 const config
= { rules
: { "test-plugin/test-rule": 2 } };
2082 const codeA
= "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
2083 const codeB
= "var a = \"trigger violation\";";
2085 let messages
= linter
.verify(codeA
, config
, filename
, false);
2086 let suppressedMessages
= linter
.getSuppressedMessages();
2088 assert
.strictEqual(messages
.length
, 0);
2089 assert
.strictEqual(suppressedMessages
.length
, 0);
2091 messages
= linter
.verify(codeB
, config
, filename
, false);
2092 suppressedMessages
= linter
.getSuppressedMessages();
2094 assert
.strictEqual(messages
.length
, 1);
2095 assert
.strictEqual(suppressedMessages
.length
, 0);
2099 describe("when evaluating code with comments to enable and disable all reporting", () => {
2100 it("should report a violation", () => {
2103 "/*eslint-disable */",
2105 "/*eslint-enable */",
2108 const config
= { rules
: { "no-alert": 1 } };
2110 const messages
= linter
.verify(code
, config
, filename
);
2111 const suppressedMessages
= linter
.getSuppressedMessages();
2113 assert
.strictEqual(messages
.length
, 1);
2114 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2115 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
2116 assert
.include(messages
[0].nodeType
, "CallExpression");
2117 assert
.strictEqual(messages
[0].line
, 4);
2119 assert
.strictEqual(suppressedMessages
.length
, 1);
2120 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2121 assert
.strictEqual(suppressedMessages
[0].message
, "Unexpected alert.");
2122 assert
.strictEqual(suppressedMessages
[0].line
, 2);
2123 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 1);
2124 assert
.strictEqual(suppressedMessages
[0].suppressions
[0].justification
, "");
2127 it("should not report a violation", () => {
2129 "/*eslint-disable */",
2133 const config
= { rules
: { "no-alert": 1 } };
2135 const messages
= linter
.verify(code
, config
, filename
);
2136 const suppressedMessages
= linter
.getSuppressedMessages();
2138 assert
.strictEqual(messages
.length
, 0);
2140 assert
.strictEqual(suppressedMessages
.length
, 2);
2141 assert
.strictEqual(suppressedMessages
[0].line
, 2);
2142 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 1);
2143 assert
.strictEqual(suppressedMessages
[1].line
, 3);
2144 assert
.strictEqual(suppressedMessages
[1].suppressions
.length
, 1);
2147 it("should not report a violation", () => {
2149 " alert('test1');/*eslint-disable */\n",
2151 " alert('test');\n",
2152 "/*eslint-enable */alert('test2');"
2154 const config
= { rules
: { "no-alert": 1 } };
2156 const messages
= linter
.verify(code
, config
, filename
);
2157 const suppressedMessages
= linter
.getSuppressedMessages();
2159 assert
.strictEqual(messages
.length
, 2);
2160 assert
.strictEqual(messages
[0].column
, 21);
2161 assert
.strictEqual(messages
[1].column
, 19);
2163 assert
.strictEqual(suppressedMessages
.length
, 2);
2164 assert
.strictEqual(suppressedMessages
[0].column
, 1);
2165 assert
.strictEqual(suppressedMessages
[1].column
, 56);
2168 it("should report a violation", () => {
2171 "/*eslint-disable */",
2173 "/*eslint-disable */",
2175 "/*eslint-enable*/",
2180 const config
= { rules
: { "no-alert": 1 } };
2182 const messages
= linter
.verify(code
, config
, filename
);
2183 const suppressedMessages
= linter
.getSuppressedMessages();
2185 assert
.strictEqual(messages
.length
, 1);
2186 assert
.strictEqual(suppressedMessages
.length
, 2);
2190 it("should not report a violation", () => {
2192 "/*eslint-disable */",
2193 "(function(){ var b = 44;})()",
2194 "/*eslint-enable */;any();"
2197 const config
= { rules
: { "no-unused-vars": 1 } };
2199 const messages
= linter
.verify(code
, config
, filename
);
2200 const suppressedMessages
= linter
.getSuppressedMessages();
2202 assert
.strictEqual(messages
.length
, 0);
2203 assert
.strictEqual(suppressedMessages
.length
, 1);
2206 it("should not report a violation", () => {
2208 "(function(){ /*eslint-disable */ var b = 44;})()",
2209 "/*eslint-enable */;any();"
2212 const config
= { rules
: { "no-unused-vars": 1 } };
2214 const messages
= linter
.verify(code
, config
, filename
);
2215 const suppressedMessages
= linter
.getSuppressedMessages();
2217 assert
.strictEqual(messages
.length
, 0);
2218 assert
.strictEqual(suppressedMessages
.length
, 1);
2222 describe("when evaluating code with comments to ignore reporting on specific rules on a specific line", () => {
2224 describe("eslint-disable-line", () => {
2225 it("should report a violation", () => {
2227 "alert('test'); // eslint-disable-line no-alert",
2228 "console.log('test');" // here
2237 const messages
= linter
.verify(code
, config
, filename
);
2238 const suppressedMessages
= linter
.getSuppressedMessages();
2240 assert
.strictEqual(messages
.length
, 1);
2241 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2243 assert
.strictEqual(suppressedMessages
.length
, 1);
2244 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2247 it("should report a violation", () => {
2249 "alert('test'); // eslint-disable-line no-alert",
2250 "console.log('test'); // eslint-disable-line no-console",
2251 "alert('test');" // here
2260 const messages
= linter
.verify(code
, config
, filename
);
2261 const suppressedMessages
= linter
.getSuppressedMessages();
2263 assert
.strictEqual(messages
.length
, 1);
2264 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2266 assert
.strictEqual(suppressedMessages
.length
, 2);
2267 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2268 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
2271 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
2273 "/* eslint-disable-line",
2275 "*/ console.log('test');" // here
2283 const messages
= linter
.verify(code
, config
, filename
);
2284 const suppressedMessages
= linter
.getSuppressedMessages();
2286 assert
.strictEqual(messages
.length
, 2);
2287 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2288 assert
.strictEqual(suppressedMessages
.length
, 0);
2291 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
2293 "alert('test'); /* eslint-disable-line ",
2302 const messages
= linter
.verify(code
, config
);
2303 const suppressedMessages
= linter
.getSuppressedMessages();
2305 assert
.deepStrictEqual(messages
, [
2313 message
: "Unexpected alert.",
2314 messageId
: "unexpected",
2315 nodeType
: "CallExpression"
2320 message
: "eslint-disable-line comment should not span multiple lines.",
2329 assert
.strictEqual(suppressedMessages
.length
, 0);
2332 it("should not report a violation for eslint-disable-line in block comment", () => {
2334 "alert('test'); // eslint-disable-line no-alert",
2335 "alert('test'); /*eslint-disable-line no-alert*/"
2343 const messages
= linter
.verify(code
, config
, filename
);
2344 const suppressedMessages
= linter
.getSuppressedMessages();
2346 assert
.strictEqual(messages
.length
, 0);
2348 assert
.strictEqual(suppressedMessages
.length
, 2);
2349 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2350 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-alert");
2353 it("should not report a violation", () => {
2355 "alert('test'); // eslint-disable-line no-alert",
2356 "console.log('test'); // eslint-disable-line no-console"
2365 const messages
= linter
.verify(code
, config
, filename
);
2366 const suppressedMessages
= linter
.getSuppressedMessages();
2368 assert
.strictEqual(messages
.length
, 0);
2370 assert
.strictEqual(suppressedMessages
.length
, 2);
2371 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2372 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
2375 it("should not report a violation", () => {
2377 "alert('test') // eslint-disable-line no-alert, quotes, semi",
2378 "console.log('test'); // eslint-disable-line"
2383 quotes
: [1, "double"],
2384 semi
: [1, "always"],
2389 const messages
= linter
.verify(code
, config
, filename
);
2390 const suppressedMessages
= linter
.getSuppressedMessages();
2392 assert
.strictEqual(messages
.length
, 0);
2393 assert
.strictEqual(suppressedMessages
.length
, 5);
2396 it("should not report a violation", () => {
2398 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
2399 "console.log('test'); /* eslint-disable-line */"
2404 quotes
: [1, "double"],
2405 semi
: [1, "always"],
2410 const messages
= linter
.verify(code
, config
, filename
);
2411 const suppressedMessages
= linter
.getSuppressedMessages();
2413 assert
.strictEqual(messages
.length
, 0);
2414 assert
.strictEqual(suppressedMessages
.length
, 5);
2417 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2419 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
2424 quotes
: [1, "single"]
2427 const messages
= linter
.verify(code
, config
, filename
);
2428 const suppressedMessages
= linter
.getSuppressedMessages();
2430 assert
.strictEqual(messages
.length
, 0);
2432 assert
.strictEqual(suppressedMessages
.length
, 2);
2433 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2437 describe("eslint-disable-next-line", () => {
2438 it("should ignore violation of specified rule on next line", () => {
2440 "// eslint-disable-next-line no-alert",
2442 "console.log('test');"
2450 const messages
= linter
.verify(code
, config
, filename
);
2451 const suppressedMessages
= linter
.getSuppressedMessages();
2453 assert
.strictEqual(messages
.length
, 1);
2454 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2456 assert
.strictEqual(suppressedMessages
.length
, 1);
2457 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2460 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2462 "/* eslint-disable-next-line no-alert */",
2464 "console.log('test');"
2472 const messages
= linter
.verify(code
, config
, filename
);
2473 const suppressedMessages
= linter
.getSuppressedMessages();
2475 assert
.strictEqual(messages
.length
, 1);
2476 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2478 assert
.strictEqual(suppressedMessages
.length
, 1);
2479 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2481 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2483 "/* eslint-disable-next-line no-alert */",
2491 const messages
= linter
.verify(code
, config
, filename
);
2492 const suppressedMessages
= linter
.getSuppressedMessages();
2494 assert
.strictEqual(messages
.length
, 0);
2496 assert
.strictEqual(suppressedMessages
.length
, 1);
2497 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2500 it("should not ignore violation if code is not on next line", () => {
2502 "/* eslint-disable-next-line",
2503 "no-alert */alert('test');"
2510 const messages
= linter
.verify(code
, config
, filename
);
2511 const suppressedMessages
= linter
.getSuppressedMessages();
2513 assert
.strictEqual(messages
.length
, 1);
2514 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2516 assert
.strictEqual(suppressedMessages
.length
, 0);
2519 it("should ignore violation if block comment span multiple lines", () => {
2521 "/* eslint-disable-next-line",
2530 const messages
= linter
.verify(code
, config
, filename
);
2531 const suppressedMessages
= linter
.getSuppressedMessages();
2533 assert
.strictEqual(messages
.length
, 0);
2535 assert
.strictEqual(suppressedMessages
.length
, 1);
2536 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2539 it("should ignore violations only of specified rule", () => {
2541 "// eslint-disable-next-line no-console",
2543 "console.log('test');"
2551 const messages
= linter
.verify(code
, config
, filename
);
2552 const suppressedMessages
= linter
.getSuppressedMessages();
2554 assert
.strictEqual(messages
.length
, 2);
2555 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2556 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2558 assert
.strictEqual(suppressedMessages
.length
, 0);
2561 it("should ignore violations of multiple rules when specified", () => {
2563 "// eslint-disable-next-line no-alert, quotes",
2565 "console.log('test');"
2570 quotes
: [1, "single"],
2574 const messages
= linter
.verify(code
, config
, filename
);
2575 const suppressedMessages
= linter
.getSuppressedMessages();
2577 assert
.strictEqual(messages
.length
, 1);
2578 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2580 assert
.strictEqual(suppressedMessages
.length
, 2);
2581 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2582 assert
.strictEqual(suppressedMessages
[1].ruleId
, "quotes");
2585 it("should ignore violations of multiple rules when specified in multiple lines", () => {
2587 "/* eslint-disable-next-line",
2592 "console.log('test');"
2597 quotes
: [1, "single"],
2601 const messages
= linter
.verify(code
, config
, filename
);
2603 assert
.strictEqual(messages
.length
, 1);
2604 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2607 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2609 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
2615 quotes
: [1, "single"]
2618 const messages
= linter
.verify(code
, config
, filename
);
2619 const suppressedMessages
= linter
.getSuppressedMessages();
2621 assert
.strictEqual(messages
.length
, 0);
2623 assert
.strictEqual(suppressedMessages
.length
, 2);
2624 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2625 assert
.strictEqual(suppressedMessages
[1].ruleId
, "quotes");
2628 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
2630 "/* eslint-disable-next-line",
2632 "*/ // eslint-disable-next-line quotes",
2638 quotes
: [1, "single"]
2641 const messages
= linter
.verify(code
, config
, filename
);
2643 assert
.strictEqual(messages
.length
, 0);
2646 it("should ignore violations of only the specified rule on next line", () => {
2648 "// eslint-disable-next-line quotes",
2650 "console.log('test');"
2655 quotes
: [1, "single"],
2659 const messages
= linter
.verify(code
, config
, filename
);
2660 const suppressedMessages
= linter
.getSuppressedMessages();
2662 assert
.strictEqual(messages
.length
, 2);
2663 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2664 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2666 assert
.strictEqual(suppressedMessages
.length
, 1);
2667 assert
.strictEqual(suppressedMessages
[0].ruleId
, "quotes");
2670 it("should ignore violations of specified rule on next line only", () => {
2673 "// eslint-disable-next-line no-alert",
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
, 1);
2691 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2694 it("should ignore all rule violations on next line if none specified", () => {
2696 "// eslint-disable-next-line",
2698 "console.log('test')"
2703 quotes
: [1, "single"],
2708 const messages
= linter
.verify(code
, config
, filename
);
2709 const suppressedMessages
= linter
.getSuppressedMessages();
2711 assert
.strictEqual(messages
.length
, 1);
2712 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2714 assert
.strictEqual(suppressedMessages
.length
, 3);
2715 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2716 assert
.strictEqual(suppressedMessages
[1].ruleId
, "quotes");
2717 assert
.strictEqual(suppressedMessages
[2].ruleId
, "semi");
2720 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
2723 "/* eslint-disable-next-line no-alert */",
2725 "console.log('test');"
2733 const messages
= linter
.verify(code
, config
, filename
);
2734 const suppressedMessages
= linter
.getSuppressedMessages();
2736 assert
.strictEqual(messages
.length
, 2);
2737 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2738 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2740 assert
.strictEqual(suppressedMessages
.length
, 1);
2741 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2744 it("should report a violation", () => {
2746 "/* eslint-disable-next-line",
2749 "console.log('test');" // here
2758 const messages
= linter
.verify(code
, config
, filename
);
2759 const suppressedMessages
= linter
.getSuppressedMessages();
2761 assert
.strictEqual(messages
.length
, 2);
2762 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2764 assert
.strictEqual(suppressedMessages
.length
, 0);
2767 it("should not ignore violations if comment is of the type hashbang", () => {
2769 "#! eslint-disable-next-line no-alert",
2771 "console.log('test');"
2779 const messages
= linter
.verify(code
, config
, filename
);
2780 const suppressedMessages
= linter
.getSuppressedMessages();
2782 assert
.strictEqual(messages
.length
, 2);
2783 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2784 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2786 assert
.strictEqual(suppressedMessages
.length
, 0);
2791 describe("when evaluating code with comments to enable and disable reporting of specific rules", () => {
2793 it("should report a violation", () => {
2795 "/*eslint-disable no-alert */",
2797 "console.log('test');" // here
2799 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2801 const messages
= linter
.verify(code
, config
, filename
);
2802 const suppressedMessages
= linter
.getSuppressedMessages();
2804 assert
.strictEqual(messages
.length
, 1);
2805 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2807 assert
.strictEqual(suppressedMessages
.length
, 1);
2808 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2811 it("should report no violation", () => {
2813 "/*eslint-disable no-unused-vars */",
2814 "var foo; // eslint-disable-line no-unused-vars",
2816 "/* eslint-enable no-unused-vars */" // here
2818 const config
= { rules
: { "no-unused-vars": 2 } };
2820 const messages
= linter
.verify(code
, config
, filename
);
2821 const suppressedMessages
= linter
.getSuppressedMessages();
2823 assert
.strictEqual(messages
.length
, 0);
2825 assert
.strictEqual(suppressedMessages
.length
, 2);
2826 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-unused-vars");
2827 assert
.strictEqual(suppressedMessages
[0].line
, 2);
2828 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-unused-vars");
2829 assert
.strictEqual(suppressedMessages
[1].line
, 3);
2832 it("should report no violation", () => {
2834 "var foo1; // eslint-disable-line no-unused-vars",
2835 "var foo2; // eslint-disable-line no-unused-vars",
2836 "var foo3; // eslint-disable-line no-unused-vars",
2837 "var foo4; // eslint-disable-line no-unused-vars",
2838 "var foo5; // eslint-disable-line no-unused-vars"
2840 const config
= { rules
: { "no-unused-vars": 2 } };
2842 const messages
= linter
.verify(code
, config
, filename
);
2843 const suppressedMessages
= linter
.getSuppressedMessages();
2845 assert
.strictEqual(messages
.length
, 0);
2846 assert
.strictEqual(suppressedMessages
.length
, 5);
2849 it("should report no violation", () => {
2851 "/* eslint-disable quotes */",
2852 "console.log(\"foo\");",
2853 "/* eslint-enable quotes */"
2855 const config
= { rules
: { quotes
: 2 } };
2857 const messages
= linter
.verify(code
, config
, filename
);
2858 const suppressedMessages
= linter
.getSuppressedMessages();
2860 assert
.strictEqual(messages
.length
, 0);
2861 assert
.strictEqual(suppressedMessages
.length
, 0);
2864 it("should report a violation", () => {
2866 "/*eslint-disable no-alert, no-console */",
2868 "console.log('test');",
2869 "/*eslint-enable*/",
2871 "alert('test');", // here
2872 "console.log('test');" // here
2874 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2876 const messages
= linter
.verify(code
, config
, filename
);
2877 const suppressedMessages
= linter
.getSuppressedMessages();
2879 assert
.strictEqual(messages
.length
, 2);
2880 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2881 assert
.strictEqual(messages
[0].line
, 5);
2882 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2883 assert
.strictEqual(messages
[1].line
, 6);
2885 assert
.strictEqual(suppressedMessages
.length
, 2);
2886 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2887 assert
.strictEqual(suppressedMessages
[0].line
, 2);
2888 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
2889 assert
.strictEqual(suppressedMessages
[1].line
, 3);
2892 it("should report a violation", () => {
2894 "/*eslint-disable no-alert */",
2896 "console.log('test');",
2897 "/*eslint-enable no-console */",
2899 "alert('test');" // here
2901 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2903 const messages
= linter
.verify(code
, config
, filename
);
2904 const suppressedMessages
= linter
.getSuppressedMessages();
2906 assert
.strictEqual(messages
.length
, 1);
2907 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2909 assert
.strictEqual(suppressedMessages
.length
, 2);
2910 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2911 assert
.strictEqual(suppressedMessages
[0].line
, 2);
2912 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-alert");
2913 assert
.strictEqual(suppressedMessages
[1].line
, 5);
2917 it("should report a violation", () => {
2919 "/*eslint-disable no-alert, no-console */",
2921 "console.log('test');",
2922 "/*eslint-enable no-alert*/",
2924 "alert('test');", // here
2925 "console.log('test');"
2927 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2929 const messages
= linter
.verify(code
, config
, filename
);
2930 const suppressedMessages
= linter
.getSuppressedMessages();
2932 assert
.strictEqual(messages
.length
, 1);
2933 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2934 assert
.strictEqual(messages
[0].line
, 5);
2936 assert
.strictEqual(suppressedMessages
.length
, 3);
2937 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2938 assert
.strictEqual(suppressedMessages
[0].line
, 2);
2939 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
2940 assert
.strictEqual(suppressedMessages
[1].line
, 3);
2941 assert
.strictEqual(suppressedMessages
[2].ruleId
, "no-console");
2942 assert
.strictEqual(suppressedMessages
[2].line
, 6);
2946 it("should report a violation", () => {
2948 "/*eslint-disable no-alert */",
2950 "/*eslint-disable no-console */",
2952 "console.log('test');",
2953 "/*eslint-enable */",
2955 "alert('test');", // here
2956 "console.log('test');", // here
2958 "/*eslint-enable */",
2960 "alert('test');", // here
2961 "console.log('test');", // here
2965 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2967 const messages
= linter
.verify(code
, config
, filename
);
2968 const suppressedMessages
= linter
.getSuppressedMessages();
2970 assert
.strictEqual(messages
.length
, 4);
2971 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2972 assert
.strictEqual(messages
[0].line
, 6);
2973 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2974 assert
.strictEqual(messages
[1].line
, 7);
2975 assert
.strictEqual(messages
[2].ruleId
, "no-alert");
2976 assert
.strictEqual(messages
[2].line
, 9);
2977 assert
.strictEqual(messages
[3].ruleId
, "no-console");
2978 assert
.strictEqual(messages
[3].line
, 10);
2980 assert
.strictEqual(suppressedMessages
.length
, 2);
2981 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
2982 assert
.strictEqual(suppressedMessages
[0].line
, 3);
2983 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
2984 assert
.strictEqual(suppressedMessages
[1].line
, 4);
2987 it("should report a violation", () => {
2989 "/*eslint-disable no-alert, no-console */",
2991 "console.log('test');",
2993 "/*eslint-enable no-alert */",
2995 "alert('test');", // here
2996 "console.log('test');",
2998 "/*eslint-enable no-console */",
3000 "alert('test');", // here
3001 "console.log('test');", // here
3002 "/*eslint-enable no-console */"
3004 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
3006 const messages
= linter
.verify(code
, config
, filename
);
3007 const suppressedMessages
= linter
.getSuppressedMessages();
3009 assert
.strictEqual(messages
.length
, 3);
3010 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3011 assert
.strictEqual(messages
[0].line
, 5);
3012 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3013 assert
.strictEqual(messages
[1].line
, 8);
3014 assert
.strictEqual(messages
[2].ruleId
, "no-console");
3015 assert
.strictEqual(messages
[2].line
, 9);
3017 assert
.strictEqual(suppressedMessages
.length
, 3);
3018 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
3019 assert
.strictEqual(suppressedMessages
[0].line
, 2);
3020 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
3021 assert
.strictEqual(suppressedMessages
[1].line
, 3);
3022 assert
.strictEqual(suppressedMessages
[2].ruleId
, "no-console");
3023 assert
.strictEqual(suppressedMessages
[2].line
, 6);
3026 it("should report a violation when severity is warn", () => {
3028 "/*eslint-disable no-alert, no-console */",
3030 "console.log('test');",
3032 "/*eslint-enable no-alert */",
3034 "alert('test');", // here
3035 "console.log('test');",
3037 "/*eslint-enable no-console */",
3039 "alert('test');", // here
3040 "console.log('test');", // here
3041 "/*eslint-enable no-console */"
3043 const config
= { rules
: { "no-alert": "warn", "no-console": "warn" } };
3045 const messages
= linter
.verify(code
, config
, filename
);
3046 const suppressedMessages
= linter
.getSuppressedMessages();
3048 assert
.strictEqual(messages
.length
, 3);
3049 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3050 assert
.strictEqual(messages
[0].line
, 5);
3051 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3052 assert
.strictEqual(messages
[1].line
, 8);
3053 assert
.strictEqual(messages
[2].ruleId
, "no-console");
3054 assert
.strictEqual(messages
[2].line
, 9);
3056 assert
.strictEqual(suppressedMessages
.length
, 3);
3057 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
3058 assert
.strictEqual(suppressedMessages
[0].line
, 2);
3059 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-console");
3060 assert
.strictEqual(suppressedMessages
[1].line
, 3);
3061 assert
.strictEqual(suppressedMessages
[2].ruleId
, "no-console");
3062 assert
.strictEqual(suppressedMessages
[2].line
, 6);
3066 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
3067 const code
= "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
3069 it("should report a violation", () => {
3070 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
3072 const messages
= linter
.verify(code
, config
, filename
);
3073 const suppressedMessages
= linter
.getSuppressedMessages();
3075 assert
.strictEqual(messages
.length
, 1);
3076 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3077 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
3078 assert
.include(messages
[0].nodeType
, "CallExpression");
3080 assert
.strictEqual(suppressedMessages
.length
, 0);
3084 describe("when evaluating code with comments to enable configurable rule", () => {
3085 const code
= "/*eslint quotes:[2, \"double\"]*/ alert('test');";
3087 it("should report a violation", () => {
3088 const config
= { rules
: { quotes
: [2, "single"] } };
3090 const messages
= linter
.verify(code
, config
, filename
);
3091 const suppressedMessages
= linter
.getSuppressedMessages();
3093 assert
.strictEqual(messages
.length
, 1);
3094 assert
.strictEqual(messages
[0].ruleId
, "quotes");
3095 assert
.strictEqual(messages
[0].message
, "Strings must use doublequote.");
3096 assert
.include(messages
[0].nodeType
, "Literal");
3098 assert
.strictEqual(suppressedMessages
.length
, 0);
3102 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
3103 const code
= "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
3105 it("should report a violation", () => {
3106 const config
= { rules
: { quotes
: [2, "single"] } };
3108 const messages
= linter
.verify(code
, config
, filename
);
3109 const suppressedMessages
= linter
.getSuppressedMessages();
3111 assert
.strictEqual(messages
.length
, 1);
3112 assert
.strictEqual(messages
[0].ruleId
, "quotes");
3113 assert
.strictEqual(messages
[0].message
, "Strings must use doublequote.");
3114 assert
.include(messages
[0].nodeType
, "Literal");
3116 assert
.strictEqual(suppressedMessages
.length
, 0);
3120 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
3121 it("should report a violation", () => {
3122 const code
= "/*eslint no-alert:'1'*/ alert('test');";
3124 const config
= { rules
: { "no-alert": 1 } };
3126 const messages
= linter
.verify(code
, config
, filename
);
3127 const suppressedMessages
= linter
.getSuppressedMessages();
3129 assert
.strictEqual(messages
.length
, 2);
3132 * Incorrectly formatted comment threw error;
3133 * message from caught exception
3134 * may differ amongst UAs, so verifying
3135 * first part only as defined in the
3136 * parseJsonConfig function in lib/eslint.js
3138 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":'1'':/u);
3139 assert
.strictEqual(messages
[0].line
, 1);
3140 assert
.strictEqual(messages
[0].column
, 1);
3142 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3143 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
3144 assert
.include(messages
[1].nodeType
, "CallExpression");
3146 assert
.strictEqual(suppressedMessages
.length
, 0);
3149 it("should report a violation", () => {
3150 const code
= "/*eslint no-alert:abc*/ alert('test');";
3152 const config
= { rules
: { "no-alert": 1 } };
3154 const messages
= linter
.verify(code
, config
, filename
);
3155 const suppressedMessages
= linter
.getSuppressedMessages();
3157 assert
.strictEqual(messages
.length
, 2);
3160 * Incorrectly formatted comment threw error;
3161 * message from caught exception
3162 * may differ amongst UAs, so verifying
3163 * first part only as defined in the
3164 * parseJsonConfig function in lib/eslint.js
3166 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":abc':/u);
3167 assert
.strictEqual(messages
[0].line
, 1);
3168 assert
.strictEqual(messages
[0].column
, 1);
3170 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3171 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
3172 assert
.include(messages
[1].nodeType
, "CallExpression");
3174 assert
.strictEqual(suppressedMessages
.length
, 0);
3177 it("should report a violation", () => {
3178 const code
= "/*eslint no-alert:0 2*/ alert('test');";
3180 const config
= { rules
: { "no-alert": 1 } };
3182 const messages
= linter
.verify(code
, config
, filename
);
3183 const suppressedMessages
= linter
.getSuppressedMessages();
3185 assert
.strictEqual(messages
.length
, 2);
3188 * Incorrectly formatted comment threw error;
3189 * message from caught exception
3190 * may differ amongst UAs, so verifying
3191 * first part only as defined in the
3192 * parseJsonConfig function in lib/eslint.js
3194 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":0 2':/u);
3195 assert
.strictEqual(messages
[0].line
, 1);
3196 assert
.strictEqual(messages
[0].column
, 1);
3198 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
3199 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
3200 assert
.include(messages
[1].nodeType
, "CallExpression");
3202 assert
.strictEqual(suppressedMessages
.length
, 0);
3206 describe("when evaluating code with comments which have colon in its value", () => {
3207 const code
= String
.raw
`
3208 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
3212 it("should not parse errors, should report a violation", () => {
3213 const messages
= linter
.verify(code
, {}, filename
);
3214 const suppressedMessages
= linter
.getSuppressedMessages();
3216 assert
.strictEqual(messages
.length
, 1);
3217 assert
.strictEqual(messages
[0].ruleId
, "max-len");
3218 assert
.strictEqual(messages
[0].message
, "This line has a length of 129. Maximum allowed is 100.");
3219 assert
.include(messages
[0].nodeType
, "Program");
3221 assert
.strictEqual(suppressedMessages
.length
, 0);
3225 describe("when evaluating code with comments that contain escape sequences", () => {
3226 const code
= String
.raw
`
3227 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
3228 console.log("test");
3229 consolexlog("test2");
3233 it("should validate correctly", () => {
3234 const config
= { rules
: {} };
3235 const messages
= linter
.verify(code
, config
, filename
);
3236 const suppressedMessages
= linter
.getSuppressedMessages();
3237 const [message1
, message2
] = messages
;
3239 assert
.strictEqual(messages
.length
, 2);
3240 assert
.strictEqual(message1
.ruleId
, "max-len");
3241 assert
.strictEqual(message1
.message
, "This line has a length of 21. Maximum allowed is 1.");
3242 assert
.strictEqual(message1
.line
, 4);
3243 assert
.strictEqual(message1
.column
, 1);
3244 assert
.include(message1
.nodeType
, "Program");
3245 assert
.strictEqual(message2
.ruleId
, "max-len");
3246 assert
.strictEqual(message2
.message
, "This line has a length of 16. Maximum allowed is 1.");
3247 assert
.strictEqual(message2
.line
, 5);
3248 assert
.strictEqual(message2
.column
, 1);
3249 assert
.include(message2
.nodeType
, "Program");
3251 assert
.strictEqual(suppressedMessages
.length
, 0);
3255 describe("when evaluating a file with a hashbang", () => {
3257 it("should preserve line numbers", () => {
3258 const code
= "#!bin/program\n\nvar foo;;";
3259 const config
= { rules
: { "no-extra-semi": 1 } };
3260 const messages
= linter
.verify(code
, config
);
3261 const suppressedMessages
= linter
.getSuppressedMessages();
3263 assert
.strictEqual(messages
.length
, 1);
3264 assert
.strictEqual(messages
[0].ruleId
, "no-extra-semi");
3265 assert
.strictEqual(messages
[0].nodeType
, "EmptyStatement");
3266 assert
.strictEqual(messages
[0].line
, 3);
3268 assert
.strictEqual(suppressedMessages
.length
, 0);
3271 it("should have a comment with the hashbang in it", () => {
3272 const code
= "#!bin/program\n\nvar foo;;";
3273 const config
= { rules
: { checker
: "error" } };
3274 const spy
= sinon
.spy(context
=> {
3275 const comments
= context
.getAllComments();
3277 assert
.strictEqual(comments
.length
, 1);
3278 assert
.strictEqual(comments
[0].type
, "Shebang");
3282 linter
.defineRule("checker", spy
);
3283 linter
.verify(code
, config
);
3284 assert(spy
.calledOnce
);
3287 it("should comment hashbang without breaking offset", () => {
3288 const code
= "#!/usr/bin/env node\n'123';";
3289 const config
= { rules
: { checker
: "error" } };
3292 linter
.defineRule("checker", context
=> {
3293 spy
= sinon
.spy(node
=> {
3294 assert
.strictEqual(context
.getSource(node
), "'123';");
3296 return { ExpressionStatement
: spy
};
3299 linter
.verify(code
, config
);
3300 assert(spy
&& spy
.calledOnce
);
3305 describe("when evaluating broken code", () => {
3306 const code
= BROKEN_TEST_CODE
;
3308 it("should report a violation with a useful parse error prefix", () => {
3309 const messages
= linter
.verify(code
);
3310 const suppressedMessages
= linter
.getSuppressedMessages();
3312 assert
.strictEqual(messages
.length
, 1);
3313 assert
.strictEqual(messages
[0].severity
, 2);
3314 assert
.isNull(messages
[0].ruleId
);
3315 assert
.strictEqual(messages
[0].line
, 1);
3316 assert
.strictEqual(messages
[0].column
, 4);
3317 assert
.isTrue(messages
[0].fatal
);
3318 assert
.match(messages
[0].message
, /^Parsing error:/u);
3320 assert
.strictEqual(suppressedMessages
.length
, 0);
3323 it("should report source code where the issue is present", () => {
3324 const inValidCode
= [
3330 const messages
= linter
.verify(inValidCode
.join("\n"));
3331 const suppressedMessages
= linter
.getSuppressedMessages();
3333 assert
.strictEqual(messages
.length
, 1);
3334 assert
.strictEqual(messages
[0].severity
, 2);
3335 assert
.isTrue(messages
[0].fatal
);
3336 assert
.match(messages
[0].message
, /^Parsing error:/u);
3338 assert
.strictEqual(suppressedMessages
.length
, 0);
3342 describe("when using an invalid (undefined) rule", () => {
3343 linter
= new Linter();
3345 const code
= TEST_CODE
;
3346 let results
, result
, warningResult
, arrayOptionResults
, objectOptionResults
, resultsMultiple
;
3349 results
= linter
.verify(code
, { rules
: { foobar
: 2 } });
3350 result
= results
[0];
3351 warningResult
= linter
.verify(code
, { rules
: { foobar
: 1 } })[0];
3352 arrayOptionResults
= linter
.verify(code
, { rules
: { foobar
: [2, "always"] } });
3353 objectOptionResults
= linter
.verify(code
, { rules
: { foobar
: [1, { bar
: false }] } });
3354 resultsMultiple
= linter
.verify(code
, { rules
: { foobar
: 2, barfoo
: 1 } });
3357 it("should report a problem", () => {
3358 assert
.isNotNull(result
);
3359 assert
.isArray(results
);
3360 assert
.isObject(result
);
3361 assert
.property(result
, "ruleId");
3362 assert
.strictEqual(result
.ruleId
, "foobar");
3365 it("should report that the rule does not exist", () => {
3366 assert
.property(result
, "message");
3367 assert
.strictEqual(result
.message
, "Definition for rule 'foobar' was not found.");
3370 it("should report at the correct severity", () => {
3371 assert
.property(result
, "severity");
3372 assert
.strictEqual(result
.severity
, 2);
3373 assert
.strictEqual(warningResult
.severity
, 2); // this is 2, since the rulename is very likely to be wrong
3376 it("should accept any valid rule configuration", () => {
3377 assert
.isObject(arrayOptionResults
[0]);
3378 assert
.isObject(objectOptionResults
[0]);
3381 it("should report multiple missing rules", () => {
3382 assert
.isArray(resultsMultiple
);
3384 assert
.deepStrictEqual(
3388 message
: "Definition for rule 'barfoo' was not found.",
3400 describe("when using a rule which has been replaced", () => {
3401 const code
= TEST_CODE
;
3403 it("should report the new rule", () => {
3404 const results
= linter
.verify(code
, { rules
: { "no-comma-dangle": 2 } });
3405 const suppressedMessages
= linter
.getSuppressedMessages();
3407 assert
.strictEqual(results
[0].ruleId
, "no-comma-dangle");
3408 assert
.strictEqual(results
[0].message
, "Rule 'no-comma-dangle' was removed and replaced by: comma-dangle");
3410 assert
.strictEqual(suppressedMessages
.length
, 0);
3414 describe("when calling getRules", () => {
3415 it("should return all loaded rules", () => {
3416 const rules
= linter
.getRules();
3418 assert
.isAbove(rules
.size
, 230);
3419 assert
.isObject(rules
.get("no-alert"));
3423 describe("when calling version", () => {
3424 it("should return current version number", () => {
3425 const version
= linter
.version
;
3427 assert
.isString(version
);
3428 assert
.isTrue(parseInt(version
[0], 10) >= 3);
3432 describe("when evaluating an empty string", () => {
3433 it("runs rules", () => {
3434 linter
.defineRule("no-programs", context
=> ({
3436 context
.report({ node
, message
: "No programs allowed." });
3441 linter
.verify("", { rules
: { "no-programs": "error" } }).length
,
3447 describe("when evaluating code without comments to environment", () => {
3448 it("should report a violation when using typed array", () => {
3449 const code
= "var array = new Uint8Array();";
3451 const config
= { rules
: { "no-undef": 1 } };
3453 const messages
= linter
.verify(code
, config
, filename
);
3454 const suppressedMessages
= linter
.getSuppressedMessages();
3456 assert
.strictEqual(messages
.length
, 1);
3457 assert
.strictEqual(suppressedMessages
.length
, 0);
3460 it("should report a violation when using Promise", () => {
3461 const code
= "new Promise();";
3463 const config
= { rules
: { "no-undef": 1 } };
3465 const messages
= linter
.verify(code
, config
, filename
);
3466 const suppressedMessages
= linter
.getSuppressedMessages();
3468 assert
.strictEqual(messages
.length
, 1);
3469 assert
.strictEqual(suppressedMessages
.length
, 0);
3473 describe("when evaluating code with comments to environment", () => {
3474 it("should not support legacy config", () => {
3475 const code
= "/*jshint mocha:true */ describe();";
3477 const config
= { rules
: { "no-undef": 1 } };
3479 const messages
= linter
.verify(code
, config
, filename
);
3480 const suppressedMessages
= linter
.getSuppressedMessages();
3482 assert
.strictEqual(messages
.length
, 1);
3483 assert
.strictEqual(messages
[0].ruleId
, "no-undef");
3484 assert
.strictEqual(messages
[0].nodeType
, "Identifier");
3485 assert
.strictEqual(messages
[0].line
, 1);
3487 assert
.strictEqual(suppressedMessages
.length
, 0);
3490 it("should not report a violation", () => {
3491 const code
= "/*eslint-env es6 */ new Promise();";
3493 const config
= { rules
: { "no-undef": 1 } };
3495 const messages
= linter
.verify(code
, config
, filename
);
3496 const suppressedMessages
= linter
.getSuppressedMessages();
3498 assert
.strictEqual(messages
.length
, 0);
3499 assert
.strictEqual(suppressedMessages
.length
, 0);
3502 // https://github.com/eslint/eslint/issues/14652
3503 it("should not report a violation", () => {
3505 "/*eslint-env es6\n */ new Promise();",
3506 "/*eslint-env browser,\nes6 */ window;Promise;",
3507 "/*eslint-env\nbrowser,es6 */ window;Promise;"
3509 const config
= { rules
: { "no-undef": 1 } };
3511 for (const code
of codes
) {
3512 const messages
= linter
.verify(code
, config
, filename
);
3513 const suppressedMessages
= linter
.getSuppressedMessages();
3515 assert
.strictEqual(messages
.length
, 0);
3516 assert
.strictEqual(suppressedMessages
.length
, 0);
3521 it("should not report a violation", () => {
3522 const code
= `/*${ESLINT_ENV} mocha,node */ require();describe();`;
3524 const config
= { rules
: { "no-undef": 1 } };
3526 const messages
= linter
.verify(code
, config
, filename
);
3527 const suppressedMessages
= linter
.getSuppressedMessages();
3529 assert
.strictEqual(messages
.length
, 0);
3530 assert
.strictEqual(suppressedMessages
.length
, 0);
3533 it("should not report a violation", () => {
3534 const code
= "/*eslint-env mocha */ suite();test();";
3536 const config
= { rules
: { "no-undef": 1 } };
3538 const messages
= linter
.verify(code
, config
, filename
);
3539 const suppressedMessages
= linter
.getSuppressedMessages();
3541 assert
.strictEqual(messages
.length
, 0);
3542 assert
.strictEqual(suppressedMessages
.length
, 0);
3545 it("should not report a violation", () => {
3546 const code
= `/*${ESLINT_ENV} amd */ define();require();`;
3548 const config
= { rules
: { "no-undef": 1 } };
3550 const messages
= linter
.verify(code
, config
, filename
);
3551 const suppressedMessages
= linter
.getSuppressedMessages();
3553 assert
.strictEqual(messages
.length
, 0);
3554 assert
.strictEqual(suppressedMessages
.length
, 0);
3557 it("should not report a violation", () => {
3558 const code
= `/*${ESLINT_ENV} jasmine */ expect();spyOn();`;
3560 const config
= { rules
: { "no-undef": 1 } };
3562 const messages
= linter
.verify(code
, config
, filename
);
3563 const suppressedMessages
= linter
.getSuppressedMessages();
3565 assert
.strictEqual(messages
.length
, 0);
3566 assert
.strictEqual(suppressedMessages
.length
, 0);
3569 it("should not report a violation", () => {
3570 const code
= `/*globals require: true */ /*${ESLINT_ENV} node */ require = 1;`;
3572 const config
= { rules
: { "no-undef": 1 } };
3574 const messages
= linter
.verify(code
, config
, filename
);
3575 const suppressedMessages
= linter
.getSuppressedMessages();
3577 assert
.strictEqual(messages
.length
, 0);
3578 assert
.strictEqual(suppressedMessages
.length
, 0);
3581 it("should not report a violation", () => {
3582 const code
= `/*${ESLINT_ENV} node */ process.exit();`;
3584 const config
= { rules
: {} };
3586 const messages
= linter
.verify(code
, config
, filename
);
3587 const suppressedMessages
= linter
.getSuppressedMessages();
3589 assert
.strictEqual(messages
.length
, 0);
3590 assert
.strictEqual(suppressedMessages
.length
, 0);
3593 it("should not report a violation", () => {
3594 const code
= `/*eslint no-process-exit: 0 */ /*${ESLINT_ENV} node */ process.exit();`;
3596 const config
= { rules
: { "no-undef": 1 } };
3598 const messages
= linter
.verify(code
, config
, filename
);
3599 const suppressedMessages
= linter
.getSuppressedMessages();
3601 assert
.strictEqual(messages
.length
, 0);
3602 assert
.strictEqual(suppressedMessages
.length
, 0);
3606 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
3607 it("should report a violation for disabling rules", () => {
3609 "alert('test'); // eslint-disable-line no-alert"
3617 const messages
= linter
.verify(code
, config
, {
3619 allowInlineConfig
: false
3621 const suppressedMessages
= linter
.getSuppressedMessages();
3623 assert
.strictEqual(messages
.length
, 1);
3624 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3626 assert
.strictEqual(suppressedMessages
.length
, 0);
3629 it("should report a violation for global variable declarations", () => {
3640 linter
.defineRules({
3644 const scope
= context
.getScope();
3645 const sourceCode
= context
.getSourceCode();
3646 const comments
= sourceCode
.getAllComments();
3648 assert
.strictEqual(1, comments
.length
);
3650 const foo
= getVariable(scope
, "foo");
3660 linter
.verify(code
, config
, { allowInlineConfig
: false });
3664 it("should report a violation for eslint-disable", () => {
3666 "/* eslint-disable */",
3675 const messages
= linter
.verify(code
, config
, {
3677 allowInlineConfig
: false
3679 const suppressedMessages
= linter
.getSuppressedMessages();
3681 assert
.strictEqual(messages
.length
, 1);
3682 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3684 assert
.strictEqual(suppressedMessages
.length
, 0);
3687 it("should not report a violation for rule changes", () => {
3689 "/*eslint no-alert:2*/",
3698 const messages
= linter
.verify(code
, config
, {
3700 allowInlineConfig
: false
3702 const suppressedMessages
= linter
.getSuppressedMessages();
3704 assert
.strictEqual(messages
.length
, 0);
3705 assert
.strictEqual(suppressedMessages
.length
, 0);
3708 it("should report a violation for disable-line", () => {
3710 "alert('test'); // eslint-disable-line"
3718 const messages
= linter
.verify(code
, config
, {
3720 allowInlineConfig
: false
3722 const suppressedMessages
= linter
.getSuppressedMessages();
3724 assert
.strictEqual(messages
.length
, 1);
3725 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3727 assert
.strictEqual(suppressedMessages
.length
, 0);
3730 it("should report a violation for env changes", () => {
3732 `/*${ESLINT_ENV} browser*/ window`
3739 const messages
= linter
.verify(code
, config
, { allowInlineConfig
: false });
3740 const suppressedMessages
= linter
.getSuppressedMessages();
3742 assert
.strictEqual(messages
.length
, 1);
3743 assert
.strictEqual(messages
[0].ruleId
, "no-undef");
3745 assert
.strictEqual(suppressedMessages
.length
, 0);
3749 describe("when evaluating code with 'noInlineComment'", () => {
3750 for (const directive
of [
3754 "eslint eqeqeq: error",
3755 "eslint-disable eqeqeq",
3756 "eslint-disable-line eqeqeq",
3757 "eslint-disable-next-line eqeqeq",
3758 "eslint-enable eqeqeq",
3761 // eslint-disable-next-line no-loop-func -- No closures
3762 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
3763 const messages
= linter
.verify(`/* ${directive} */`, { noInlineConfig
: true });
3764 const suppressedMessages
= linter
.getSuppressedMessages();
3766 assert
.deepStrictEqual(messages
.length
, 1);
3767 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
3768 assert
.deepStrictEqual(messages
[0].ruleId
, null);
3769 assert
.deepStrictEqual(messages
[0].severity
, 1);
3770 assert
.deepStrictEqual(messages
[0].message
, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
3772 assert
.strictEqual(suppressedMessages
.length
, 0);
3776 for (const directive
of [
3777 "eslint-disable-line eqeqeq",
3778 "eslint-disable-next-line eqeqeq"
3780 // eslint-disable-next-line no-loop-func -- No closures
3781 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
3782 const messages
= linter
.verify(`// ${directive}`, { noInlineConfig
: true });
3783 const suppressedMessages
= linter
.getSuppressedMessages();
3785 assert
.deepStrictEqual(messages
.length
, 1);
3786 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
3787 assert
.deepStrictEqual(messages
[0].ruleId
, null);
3788 assert
.deepStrictEqual(messages
[0].severity
, 1);
3789 assert
.deepStrictEqual(messages
[0].message
, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
3791 assert
.strictEqual(suppressedMessages
.length
, 0);
3795 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
3796 const messages
= linter
.verify("/* globals foo */", { noInlineConfig
: true }, { allowInlineConfig
: false });
3797 const suppressedMessages
= linter
.getSuppressedMessages();
3799 assert
.deepStrictEqual(messages
.length
, 0);
3800 assert
.strictEqual(suppressedMessages
.length
, 0);
3804 describe("when receiving cwd in options during instantiation", () => {
3805 const code
= "a;\nb;";
3806 const config
= { rules
: { checker
: "error" } };
3808 it("should get cwd correctly in the context", () => {
3810 const linterWithOption
= new Linter({ cwd
});
3813 linterWithOption
.defineRule("checker", context
=> {
3814 spy
= sinon
.spy(() => {
3815 assert
.strictEqual(context
.getCwd(), cwd
);
3817 return { Program
: spy
};
3820 linterWithOption
.verify(code
, config
);
3821 assert(spy
&& spy
.calledOnce
);
3824 it("should assign process.cwd() to it if cwd is undefined", () => {
3826 const linterWithOption
= new Linter({ });
3828 linterWithOption
.defineRule("checker", context
=> {
3830 spy
= sinon
.spy(() => {
3831 assert
.strictEqual(context
.getCwd(), process
.cwd());
3833 return { Program
: spy
};
3836 linterWithOption
.verify(code
, config
);
3837 assert(spy
&& spy
.calledOnce
);
3840 it("should assign process.cwd() to it if the option is undefined", () => {
3843 linter
.defineRule("checker", context
=> {
3845 spy
= sinon
.spy(() => {
3846 assert
.strictEqual(context
.getCwd(), process
.cwd());
3848 return { Program
: spy
};
3851 linter
.verify(code
, config
);
3852 assert(spy
&& spy
.calledOnce
);
3856 describe("reportUnusedDisable option", () => {
3857 it("reports problems for unused eslint-disable comments", () => {
3858 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: true });
3859 const suppressedMessages
= linter
.getSuppressedMessages();
3861 assert
.deepStrictEqual(
3866 message
: "Unused eslint-disable directive (no problems were reported).",
3879 assert
.strictEqual(suppressedMessages
.length
, 0);
3882 it("reports problems for multiple eslint-disable comments, including unused ones", () => {
3884 "/* eslint-disable no-alert -- j1 */",
3885 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
3892 const messages
= linter
.verify(code
, config
, { reportUnusedDisableDirectives
: true });
3893 const suppressedMessages
= linter
.getSuppressedMessages();
3895 assert
.strictEqual(messages
.length
, 1);
3896 assert
.strictEqual(suppressedMessages
.length
, 1);
3897 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 2);
3900 it("reports problems for eslint-disable-line and eslint-disable-next-line comments, including unused ones", () => {
3902 "// eslint-disable-next-line no-alert -- j1 */",
3903 "alert(\"test\"); //eslint-disable-line no-alert -- j2"
3910 const messages
= linter
.verify(code
, config
, { reportUnusedDisableDirectives
: true });
3911 const suppressedMessages
= linter
.getSuppressedMessages();
3913 assert
.strictEqual(messages
.length
, 1);
3914 assert
.strictEqual(suppressedMessages
.length
, 1);
3915 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 2);
3918 it("reports problems for multiple unused eslint-disable comments with multiple ruleIds", () => {
3920 "/* eslint no-undef: 2, no-void: 2 */",
3921 "/* eslint-disable no-undef -- j1 */",
3922 "void foo; //eslint-disable-line no-undef, no-void -- j2"
3930 const messages
= linter
.verify(code
, config
, { reportUnusedDisableDirectives
: true });
3931 const suppressedMessages
= linter
.getSuppressedMessages();
3933 assert
.strictEqual(messages
.length
, 1);
3934 assert
.strictEqual(suppressedMessages
.length
, 2);
3935 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-void");
3936 assert
.strictEqual(suppressedMessages
[0].suppressions
.length
, 1);
3937 assert
.strictEqual(suppressedMessages
[1].ruleId
, "no-undef");
3938 assert
.strictEqual(suppressedMessages
[1].suppressions
.length
, 2);
3941 it("reports problems for unused eslint-disable comments (error)", () => {
3942 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "error" });
3943 const suppressedMessages
= linter
.getSuppressedMessages();
3945 assert
.deepStrictEqual(
3950 message
: "Unused eslint-disable directive (no problems were reported).",
3963 assert
.strictEqual(suppressedMessages
.length
, 0);
3966 it("reports problems for unused eslint-disable comments (warn)", () => {
3967 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "warn" });
3968 const suppressedMessages
= linter
.getSuppressedMessages();
3970 assert
.deepStrictEqual(
3975 message
: "Unused eslint-disable directive (no problems were reported).",
3988 assert
.strictEqual(suppressedMessages
.length
, 0);
3991 it("reports problems for unused eslint-disable comments (in config)", () => {
3992 const messages
= linter
.verify("/* eslint-disable */", { reportUnusedDisableDirectives
: true });
3993 const suppressedMessages
= linter
.getSuppressedMessages();
3995 assert
.deepStrictEqual(
4000 message
: "Unused eslint-disable directive (no problems were reported).",
4013 assert
.strictEqual(suppressedMessages
.length
, 0);
4016 it("reports problems for partially unused eslint-disable comments (in config)", () => {
4017 const code
= "alert('test'); // eslint-disable-line no-alert, no-redeclare";
4019 reportUnusedDisableDirectives
: true,
4026 const messages
= linter
.verify(code
, config
, {
4028 allowInlineConfig
: true
4030 const suppressedMessages
= linter
.getSuppressedMessages();
4032 assert
.deepStrictEqual(
4037 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
4050 assert
.strictEqual(suppressedMessages
.length
, 1);
4051 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
4054 describe("autofix", () => {
4055 const alwaysReportsRule
= {
4059 context
.report({ message
: "bad code", loc
: node
.loc
.end
});
4065 const neverReportsRule
= {
4071 const ruleCount
= 3;
4072 const usedRules
= Array
.from(
4073 { length
: ruleCount
},
4074 (_
, index
) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
4076 const unusedRules = usedRules.map(name => `un${name}
`); // "unused", "unused-1", "unused-2"
4079 reportUnusedDisableDirectives: true,
4081 ...Object.fromEntries(usedRules.map(name => [name, "error"])),
4082 ...Object.fromEntries(unusedRules.map(name => [name, "error"]))
4087 linter.defineRules(Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])));
4088 linter.defineRules(Object.fromEntries(unusedRules.map(name => [name, neverReportsRule])));
4093 //-----------------------------------------------
4094 // Removing the entire comment
4095 //-----------------------------------------------
4098 code: "// eslint-disable-line unused",
4102 code: "foo// eslint-disable-line unused",
4106 code: "// eslint-disable-line ,unused,",
4110 code: "// eslint-disable-line unused-1, unused-2",
4114 code: "// eslint-disable-line ,unused-1,, unused-2,, -- comment",
4118 code: "// eslint-disable-next-line unused\n",
4122 code: "// eslint-disable-next-line unused\nfoo",
4126 code: "/* eslint-disable \nunused\n*/",
4130 //-----------------------------------------------
4131 // Removing only individual rules
4132 //-----------------------------------------------
4134 // content before the first rule should not be changed
4136 code: "//eslint-disable-line unused, used",
4137 output: "//eslint-disable-line used"
4140 code: "// eslint-disable-line unused, used",
4141 output: "// eslint-disable-line used"
4144 code: "// eslint-disable-line unused, used",
4145 output: "// eslint-disable-line used"
4148 code: "/*\neslint-disable unused, used*/",
4149 output: "/*\neslint-disable used*/"
4152 code: "/*\n eslint-disable unused, used*/",
4153 output: "/*\n eslint-disable used*/"
4156 code: "/*\r\neslint-disable unused, used*/",
4157 output: "/*\r\neslint-disable used*/"
4160 code: "/*\u2028eslint-disable unused, used*/",
4161 output: "/*\u2028eslint-disable used*/"
4164 code: "/*\u00A0eslint-disable unused, used*/",
4165 output: "/*\u00A0eslint-disable used*/"
4168 code: "// eslint-disable-line unused, used",
4169 output: "// eslint-disable-line used"
4172 code: "/* eslint-disable\nunused, used*/",
4173 output: "/* eslint-disable\nused*/"
4176 code: "/* eslint-disable\n unused, used*/",
4177 output: "/* eslint-disable\n used*/"
4180 code: "/* eslint-disable\r\nunused, used*/",
4181 output: "/* eslint-disable\r\nused*/"
4184 code: "/* eslint-disable\u2028unused, used*/",
4185 output: "/* eslint-disable\u2028used*/"
4188 code: "/* eslint-disable\u00A0unused, used*/",
4189 output: "/* eslint-disable\u00A0used*/"
4192 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
4194 code: "// eslint-disable-line unused,used",
4195 output: "// eslint-disable-line used"
4198 code: "// eslint-disable-line unused, used",
4199 output: "// eslint-disable-line used"
4202 code: "// eslint-disable-line unused , used",
4203 output: "// eslint-disable-line used"
4206 code: "// eslint-disable-line unused, used",
4207 output: "// eslint-disable-line used"
4210 code: "// eslint-disable-line unused ,used",
4211 output: "// eslint-disable-line used"
4214 code: "/* eslint-disable unused\n,\nused */",
4215 output: "/* eslint-disable used */"
4218 code: "/* eslint-disable unused \n \n,\n\n used */",
4219 output: "/* eslint-disable used */"
4222 code: "/* eslint-disable unused\u2028,\u2028used */",
4223 output: "/* eslint-disable used */"
4226 code: "// eslint-disable-line unused\u00A0,\u00A0used",
4227 output: "// eslint-disable-line used"
4230 code: "// eslint-disable-line unused,,used",
4231 output: "// eslint-disable-line ,used"
4234 code: "// eslint-disable-line unused, ,used",
4235 output: "// eslint-disable-line ,used"
4238 code: "// eslint-disable-line unused,, used",
4239 output: "// eslint-disable-line , used"
4242 code: "// eslint-disable-line unused,used ",
4243 output: "// eslint-disable-line used "
4246 code: "// eslint-disable-next-line unused,used\n",
4247 output: "// eslint-disable-next-line used\n"
4250 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
4252 code: "// eslint-disable-line used-1,unused,used-2",
4253 output: "// eslint-disable-line used-1,used-2"
4256 code: "// eslint-disable-line used-1, unused,used-2",
4257 output: "// eslint-disable-line used-1,used-2"
4260 code: "// eslint-disable-line used-1,unused ,used-2",
4261 output: "// eslint-disable-line used-1,used-2"
4264 code: "// eslint-disable-line used-1, unused ,used-2",
4265 output: "// eslint-disable-line used-1,used-2"
4268 code: "/* eslint-disable used-1,\nunused\n,used-2 */",
4269 output: "/* eslint-disable used-1,used-2 */"
4272 code: "/* eslint-disable used-1,\n\n unused \n \n ,used-2 */",
4273 output: "/* eslint-disable used-1,used-2 */"
4276 code: "/* eslint-disable used-1,\u2028unused\u2028,used-2 */",
4277 output: "/* eslint-disable used-1,used-2 */"
4280 code: "// eslint-disable-line used-1,\u00A0unused\u00A0,used-2",
4281 output: "// eslint-disable-line used-1,used-2"
4284 // when removing a rule in the middle, content around commas should not be changed
4286 code: "// eslint-disable-line used-1, unused ,used-2",
4287 output: "// eslint-disable-line used-1,used-2"
4290 code: "// eslint-disable-line used-1,unused, used-2",
4291 output: "// eslint-disable-line used-1, used-2"
4294 code: "// eslint-disable-line used-1 ,unused,used-2",
4295 output: "// eslint-disable-line used-1 ,used-2"
4298 code: "// eslint-disable-line used-1 ,unused, used-2",
4299 output: "// eslint-disable-line used-1 , used-2"
4302 code: "// eslint-disable-line used-1 , unused , used-2",
4303 output: "// eslint-disable-line used-1 , used-2"
4306 code: "/* eslint-disable used-1\n,unused,\nused-2 */",
4307 output: "/* eslint-disable used-1\n,\nused-2 */"
4310 code: "/* eslint-disable used-1\u2028,unused,\u2028used-2 */",
4311 output: "/* eslint-disable used-1\u2028,\u2028used-2 */"
4314 code: "// eslint-disable-line used-1\u00A0,unused,\u00A0used-2",
4315 output: "// eslint-disable-line used-1\u00A0,\u00A0used-2"
4318 code: "// eslint-disable-line , unused ,used",
4319 output: "// eslint-disable-line ,used"
4322 code: "/* eslint-disable\n, unused ,used */",
4323 output: "/* eslint-disable\n,used */"
4326 code: "/* eslint-disable used-1,\n,unused,used-2 */",
4327 output: "/* eslint-disable used-1,\n,used-2 */"
4330 code: "/* eslint-disable used-1,unused,\n,used-2 */",
4331 output: "/* eslint-disable used-1,\n,used-2 */"
4334 code: "/* eslint-disable used-1,\n,unused,\n,used-2 */",
4335 output: "/* eslint-disable used-1,\n,\n,used-2 */"
4338 code: "// eslint-disable-line used, unused,",
4339 output: "// eslint-disable-line used,"
4342 code: "// eslint-disable-next-line used, unused,\n",
4343 output: "// eslint-disable-next-line used,\n"
4346 code: "// eslint-disable-line used, unused, ",
4347 output: "// eslint-disable-line used, "
4350 code: "// eslint-disable-line used, unused, -- comment",
4351 output: "// eslint-disable-line used, -- comment"
4354 code: "/* eslint-disable used, unused,\n*/",
4355 output: "/* eslint-disable used,\n*/"
4358 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
4360 code: "// eslint-disable-line used,unused",
4361 output: "// eslint-disable-line used"
4364 code: "// eslint-disable-line used, unused",
4365 output: "// eslint-disable-line used"
4368 code: "// eslint-disable-line used ,unused",
4369 output: "// eslint-disable-line used"
4372 code: "// eslint-disable-line used , unused",
4373 output: "// eslint-disable-line used"
4376 code: "// eslint-disable-line used, unused",
4377 output: "// eslint-disable-line used"
4380 code: "// eslint-disable-line used ,unused",
4381 output: "// eslint-disable-line used"
4384 code: "/* eslint-disable used\n,\nunused */",
4385 output: "/* eslint-disable used */"
4388 code: "/* eslint-disable used \n \n,\n\n unused */",
4389 output: "/* eslint-disable used */"
4392 code: "/* eslint-disable used\u2028,\u2028unused */",
4393 output: "/* eslint-disable used */"
4396 code: "// eslint-disable-line used\u00A0,\u00A0unused",
4397 output: "// eslint-disable-line used"
4400 code: "// eslint-disable-line used,,unused",
4401 output: "// eslint-disable-line used,"
4404 code: "// eslint-disable-line used, ,unused",
4405 output: "// eslint-disable-line used,"
4408 code: "/* eslint-disable used,\n,unused */",
4409 output: "/* eslint-disable used, */"
4412 code: "/* eslint-disable used\n, ,unused */",
4413 output: "/* eslint-disable used\n, */"
4416 // content after the last rule should not be changed
4418 code: "// eslint-disable-line used,unused",
4419 output: "// eslint-disable-line used"
4422 code: "// eslint-disable-line used,unused ",
4423 output: "// eslint-disable-line used "
4426 code: "// eslint-disable-line used,unused ",
4427 output: "// eslint-disable-line used "
4430 code: "// eslint-disable-line used,unused -- comment",
4431 output: "// eslint-disable-line used -- comment"
4434 code: "// eslint-disable-next-line used,unused\n",
4435 output: "// eslint-disable-next-line used\n"
4438 code: "// eslint-disable-next-line used,unused \n",
4439 output: "// eslint-disable-next-line used \n"
4442 code: "/* eslint-disable used,unused\u2028*/",
4443 output: "/* eslint-disable used\u2028*/"
4446 code: "// eslint-disable-line used,unused\u00A0",
4447 output: "// eslint-disable-line used\u00A0"
4450 // multiply rules to remove
4452 code: "// eslint-disable-line used, unused-1, unused-2",
4453 output: "// eslint-disable-line used"
4456 code: "// eslint-disable-line unused-1, used, unused-2",
4457 output: "// eslint-disable-line used"
4460 code: "// eslint-disable-line unused-1, unused-2, used",
4461 output: "// eslint-disable-line used"
4464 code: "// eslint-disable-line used-1, unused-1, used-2, unused-2",
4465 output: "// eslint-disable-line used-1, used-2"
4468 code: "// eslint-disable-line unused-1, used-1, unused-2, used-2",
4469 output: "// eslint-disable-line used-1, used-2"
4473 /* eslint-disable unused-1,
4480 /* eslint-disable used-1,
4586 // duplicates in the list
4588 code: "// eslint-disable-line unused, unused, used",
4589 output: "// eslint-disable-line used"
4592 code: "// eslint-disable-line unused, used, unused",
4593 output: "// eslint-disable-line used"
4596 code: "// eslint-disable-line used, unused, unused, used",
4597 output: "// eslint-disable-line used, used"
4601 for (const { code, output } of tests) {
4602 // eslint-disable-next-line no-loop-func -- `linter
` is getting updated in beforeEach()
4605 linter.verifyAndFix(code, config).output,
4613 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
4614 it("should not report a violation", () => {
4616 "alert('test'); // eslint-disable-line no-alert"
4624 const messages = linter.verify(code, config, {
4626 allowInlineConfig: true
4628 const suppressedMessages = linter.getSuppressedMessages();
4630 assert.strictEqual(messages.length, 0);
4632 assert.strictEqual(suppressedMessages.length, 1);
4633 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
4637 describe("when evaluating code with hashbang", () => {
4638 it("should comment hashbang without breaking offset", () => {
4639 const code = "#!/usr/bin/env node\n'123';";
4640 const config = { rules: { checker: "error" } };
4643 linter.defineRule("checker", context => {
4644 spy = sinon.spy(node => {
4645 assert.strictEqual(context.getSource(node), "'123';");
4647 return { ExpressionStatement: spy };
4650 linter.verify(code, config);
4651 assert(spy && spy.calledOnce);
4655 describe("verify()", () => {
4656 describe("filenames", () => {
4657 it("should allow filename to be passed on options object", () => {
4658 const filenameChecker = sinon.spy(context => {
4659 assert.strictEqual(context.getFilename(), "foo.js");
4663 linter.defineRule("checker", filenameChecker);
4664 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4665 assert(filenameChecker.calledOnce);
4668 it("should allow filename to be passed as third argument", () => {
4669 const filenameChecker = sinon.spy(context => {
4670 assert.strictEqual(context.getFilename(), "bar.js");
4674 linter.defineRule("checker", filenameChecker);
4675 linter.verify("foo;", { rules: { checker: "error" } }, "bar.js");
4676 assert(filenameChecker.calledOnce);
4679 it("should default filename to <input> when options object doesn't have filename", () => {
4680 const filenameChecker = sinon.spy(context => {
4681 assert.strictEqual(context.getFilename(), "<input>");
4685 linter.defineRule("checker", filenameChecker);
4686 linter.verify("foo;", { rules: { checker: "error" } }, {});
4687 assert(filenameChecker.calledOnce);
4690 it("should default filename to <input> when only two arguments are passed", () => {
4691 const filenameChecker = sinon.spy(context => {
4692 assert.strictEqual(context.getFilename(), "<input>");
4696 linter.defineRule("checker", filenameChecker);
4697 linter.verify("foo;", { rules: { checker: "error" } });
4698 assert(filenameChecker.calledOnce);
4702 describe("physicalFilenames", () => {
4703 it("should be same as `filename
` passed on options object, if no processors are used", () => {
4704 const physicalFilenameChecker = sinon.spy(context => {
4705 assert.strictEqual(context.getPhysicalFilename(), "foo.js");
4709 linter.defineRule("checker", physicalFilenameChecker);
4710 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4711 assert(physicalFilenameChecker.calledOnce);
4714 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
4715 const physicalFilenameChecker = sinon.spy(context => {
4716 assert.strictEqual(context.getPhysicalFilename(), "<input>");
4720 linter.defineRule("checker", physicalFilenameChecker);
4721 linter.verify("foo;", { rules: { checker: "error" } }, {});
4722 assert(physicalFilenameChecker.calledOnce);
4725 it("should default physicalFilename to <input> when only two arguments are passed", () => {
4726 const physicalFilenameChecker = sinon.spy(context => {
4727 assert.strictEqual(context.getPhysicalFilename(), "<input>");
4731 linter.defineRule("checker", physicalFilenameChecker);
4732 linter.verify("foo;", { rules: { checker: "error" } });
4733 assert(physicalFilenameChecker.calledOnce);
4737 it("should report warnings in order by line and column when called", () => {
4739 const code = "foo()\n alert('test')";
4740 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
4742 const messages = linter.verify(code, config, filename);
4743 const suppressedMessages = linter.getSuppressedMessages();
4745 assert.strictEqual(messages.length, 3);
4746 assert.strictEqual(messages[0].line, 1);
4747 assert.strictEqual(messages[0].column, 6);
4748 assert.strictEqual(messages[1].line, 2);
4749 assert.strictEqual(messages[1].column, 18);
4750 assert.strictEqual(messages[2].line, 2);
4751 assert.strictEqual(messages[2].column, 18);
4753 assert.strictEqual(suppressedMessages.length, 0);
4756 describe("ecmaVersion", () => {
4758 it("should not support ES6 when no ecmaVersion provided", () => {
4759 const messages = linter.verify("let x = 0;");
4760 const suppressedMessages = linter.getSuppressedMessages();
4762 assert.strictEqual(messages.length, 1);
4763 assert.strictEqual(suppressedMessages.length, 0);
4766 it("supports ECMAScript version 'latest'", () => {
4767 const messages = linter.verify("let x = 5 ** 7;", {
4768 parserOptions: { ecmaVersion: "latest" }
4770 const suppressedMessages = linter.getSuppressedMessages();
4772 assert.strictEqual(messages.length, 0);
4773 assert.strictEqual(suppressedMessages.length, 0);
4776 it("the 'latest' is equal to espree.latestEcmaVersion", () => {
4777 let ecmaVersion = null;
4778 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4780 linter.defineRule("ecma-version", context => ({
4782 ecmaVersion = context.parserOptions.ecmaVersion;
4785 linter.verify("", config);
4786 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion, "ecmaVersion should be 13");
4789 it("the 'latest' is not normalized for custom parsers", () => {
4790 let ecmaVersion = null;
4791 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "latest" } };
4793 linter.defineParser("custom-parser", testParsers.enhancedParser);
4794 linter.defineRule("ecma-version", context => ({
4796 ecmaVersion = context.parserOptions.ecmaVersion;
4799 linter.verify("", config);
4800 assert.strictEqual(ecmaVersion, "latest", "ecmaVersion should be latest");
4803 it("the 'latest' is equal to espree.latestEcmaVersion on languageOptions", () => {
4804 let ecmaVersion = null;
4805 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4807 linter.defineRule("ecma-version", context => ({
4809 ecmaVersion = context.languageOptions.ecmaVersion;
4812 linter.verify("", config);
4813 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
4816 it("the 'next' is equal to espree.latestEcmaVersion on languageOptions with custom parser", () => {
4817 let ecmaVersion = null;
4818 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "next" } };
4820 linter.defineParser("custom-parser", testParsers.stubParser);
4821 linter.defineRule("ecma-version", context => ({
4823 ecmaVersion = context.languageOptions.ecmaVersion;
4826 linter.verify("", config);
4827 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
4830 it("missing ecmaVersion is equal to 5 on languageOptions with custom parser", () => {
4831 let ecmaVersion = null;
4832 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser" };
4834 linter.defineParser("custom-parser", testParsers.enhancedParser);
4835 linter.defineRule("ecma-version", context => ({
4837 ecmaVersion = context.languageOptions.ecmaVersion;
4840 linter.verify("", config);
4841 assert.strictEqual(ecmaVersion, 5, "ecmaVersion should be 5");
4844 it("should pass normalized ecmaVersion to eslint-scope", () => {
4845 let blockScope = null;
4847 linter.defineRule("block-scope", context => ({
4849 blockScope = context.getScope();
4852 linter.defineParser("custom-parser", {
4853 parse: (...args) => espree.parse(...args)
4856 // Use standard parser
4857 linter.verify("{}", {
4858 rules: { "block-scope": 2 },
4859 parserOptions: { ecmaVersion: "latest" }
4862 assert.strictEqual(blockScope.type, "block");
4864 linter.verify("{}", {
4865 rules: { "block-scope": 2 },
4866 parserOptions: {} // ecmaVersion defaults to 5
4868 assert.strictEqual(blockScope.type, "global");
4870 // Use custom parser
4871 linter.verify("{}", {
4872 rules: { "block-scope": 2 },
4873 parser: "custom-parser",
4874 parserOptions: { ecmaVersion: "latest" }
4877 assert.strictEqual(blockScope.type, "block");
4879 linter.verify("{}", {
4880 rules: { "block-scope": 2 },
4881 parser: "custom-parser",
4882 parserOptions: {} // ecmaVersion defaults to 5
4884 assert.strictEqual(blockScope.type, "global");
4887 describe("it should properly parse let declaration when", () => {
4888 it("the ECMAScript version number is 6", () => {
4889 const messages = linter.verify("let x = 5;", {
4894 const suppressedMessages = linter.getSuppressedMessages();
4896 assert.strictEqual(messages.length, 0);
4897 assert.strictEqual(suppressedMessages.length, 0);
4900 it("the ECMAScript version number is 2015", () => {
4901 const messages = linter.verify("let x = 5;", {
4906 const suppressedMessages = linter.getSuppressedMessages();
4908 assert.strictEqual(messages.length, 0);
4909 assert.strictEqual(suppressedMessages.length, 0);
4913 it("should fail to parse exponentiation operator when the ECMAScript version number is 2015", () => {
4914 const messages = linter.verify("x ** y;", {
4919 const suppressedMessages = linter.getSuppressedMessages();
4921 assert.strictEqual(messages.length, 1);
4922 assert.strictEqual(suppressedMessages.length, 0);
4925 describe("should properly parse exponentiation operator when", () => {
4926 it("the ECMAScript version number is 7", () => {
4927 const messages = linter.verify("x ** y;", {
4932 const suppressedMessages = linter.getSuppressedMessages();
4934 assert.strictEqual(messages.length, 0);
4935 assert.strictEqual(suppressedMessages.length, 0);
4938 it("the ECMAScript version number is 2016", () => {
4939 const messages = linter.verify("x ** y;", {
4944 const suppressedMessages = linter.getSuppressedMessages();
4946 assert.strictEqual(messages.length, 0);
4947 assert.strictEqual(suppressedMessages.length, 0);
4952 it("should properly parse object spread when ecmaVersion is 2018", () => {
4954 const messages = linter.verify("var x = { ...y };", {
4959 const suppressedMessages = linter.getSuppressedMessages();
4961 assert.strictEqual(messages.length, 0);
4962 assert.strictEqual(suppressedMessages.length, 0);
4965 it("should properly parse global return when passed ecmaFeatures", () => {
4967 const messages = linter.verify("return;", {
4974 const suppressedMessages = linter.getSuppressedMessages();
4976 assert.strictEqual(messages.length, 0);
4977 assert.strictEqual(suppressedMessages.length, 0);
4980 it("should properly parse global return when in Node.js environment", () => {
4982 const messages = linter.verify("return;", {
4987 const suppressedMessages = linter.getSuppressedMessages();
4989 assert.strictEqual(messages.length, 0);
4990 assert.strictEqual(suppressedMessages.length, 0);
4993 it("should not parse global return when in Node.js environment with globalReturn explicitly off", () => {
4995 const messages = linter.verify("return;", {
5005 const suppressedMessages = linter.getSuppressedMessages();
5007 assert.strictEqual(messages.length, 1);
5008 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5010 assert.strictEqual(suppressedMessages.length, 0);
5013 it("should not parse global return when Node.js environment is false", () => {
5015 const messages = linter.verify("return;", {}, filename);
5016 const suppressedMessages = linter.getSuppressedMessages();
5018 assert.strictEqual(messages.length, 1);
5019 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
5021 assert.strictEqual(suppressedMessages.length, 0);
5024 it("should properly parse sloppy-mode code when impliedStrict is false", () => {
5026 const messages = linter.verify("var private;", {}, filename);
5027 const suppressedMessages = linter.getSuppressedMessages();
5029 assert.strictEqual(messages.length, 0);
5030 assert.strictEqual(suppressedMessages.length, 0);
5033 it("should not parse sloppy-mode code when impliedStrict is true", () => {
5035 const messages = linter.verify("var private;", {
5042 const suppressedMessages = linter.getSuppressedMessages();
5044 assert.strictEqual(messages.length, 1);
5045 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
5047 assert.strictEqual(suppressedMessages.length, 0);
5050 it("should properly parse valid code when impliedStrict is true", () => {
5052 const messages = linter.verify("var foo;", {
5059 const suppressedMessages = linter.getSuppressedMessages();
5061 assert.strictEqual(messages.length, 0);
5062 assert.strictEqual(suppressedMessages.length, 0);
5065 it("should properly parse JSX when passed ecmaFeatures", () => {
5067 const messages = linter.verify("var x = <div/>;", {
5074 const suppressedMessages = linter.getSuppressedMessages();
5076 assert.strictEqual(messages.length, 0);
5077 assert.strictEqual(suppressedMessages.length, 0);
5080 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
5081 const code = "var myDivElement = <div className=\"foo\" />;";
5082 const messages = linter.verify(code, {}, "filename");
5083 const suppressedMessages = linter.getSuppressedMessages();
5085 assert.strictEqual(messages.length, 1);
5086 assert.strictEqual(messages[0].line, 1);
5087 assert.strictEqual(messages[0].column, 20);
5088 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
5090 assert.strictEqual(suppressedMessages.length, 0);
5093 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
5094 const code = "var myDivElement = <div className=\"foo\" />;";
5095 const messages = linter.verify(code, { parserOptions: { ecmaFeatures: { jsx: true } } }, "filename");
5096 const suppressedMessages = linter.getSuppressedMessages();
5098 assert.strictEqual(messages.length, 0);
5099 assert.strictEqual(suppressedMessages.length, 0);
5102 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
5103 const code = "var myDivElement = <div {...this.props} />;";
5104 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }, "filename");
5105 const suppressedMessages = linter.getSuppressedMessages();
5107 assert.strictEqual(messages.length, 0);
5108 assert.strictEqual(suppressedMessages.length, 0);
5111 it("should not allow the use of reserved words as variable names in ES3", () => {
5112 const code = "var char;";
5113 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5114 const suppressedMessages = linter.getSuppressedMessages();
5116 assert.strictEqual(messages.length, 1);
5117 assert.strictEqual(messages[0].severity, 2);
5118 assert.isTrue(messages[0].fatal);
5119 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5121 assert.strictEqual(suppressedMessages.length, 0);
5124 it("should not allow the use of reserved words as property names in member expressions in ES3", () => {
5125 const code = "obj.char;";
5126 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5127 const suppressedMessages = linter.getSuppressedMessages();
5129 assert.strictEqual(messages.length, 1);
5130 assert.strictEqual(messages[0].severity, 2);
5131 assert.isTrue(messages[0].fatal);
5132 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5134 assert.strictEqual(suppressedMessages.length, 0);
5137 it("should not allow the use of reserved words as property names in object literals in ES3", () => {
5138 const code = "var obj = { char: 1 };";
5139 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
5140 const suppressedMessages = linter.getSuppressedMessages();
5142 assert.strictEqual(messages.length, 1);
5143 assert.strictEqual(messages[0].severity, 2);
5144 assert.isTrue(messages[0].fatal);
5145 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
5147 assert.strictEqual(suppressedMessages.length, 0);
5150 it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => {
5151 const code = "var char; obj.char; var obj = { char: 1 };";
5152 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3, allowReserved: true } }, filename);
5153 const suppressedMessages = linter.getSuppressedMessages();
5155 assert.strictEqual(messages.length, 0);
5156 assert.strictEqual(suppressedMessages.length, 0);
5159 it("should not allow the use of reserved words as variable names in ES > 3", () => {
5160 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5162 ecmaVersions.forEach(ecmaVersion => {
5163 const code = "var enum;";
5164 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
5165 const suppressedMessages = linter.getSuppressedMessages();
5167 assert.strictEqual(messages.length, 1);
5168 assert.strictEqual(messages[0].severity, 2);
5169 assert.isTrue(messages[0].fatal);
5170 assert.match(messages[0].message, /^Parsing error:.*'enum'/u);
5172 assert.strictEqual(suppressedMessages.length, 0);
5176 it("should allow the use of reserved words as property names in ES > 3", () => {
5177 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5179 ecmaVersions.forEach(ecmaVersion => {
5180 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
5181 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
5182 const suppressedMessages = linter.getSuppressedMessages();
5184 assert.strictEqual(messages.length, 0);
5185 assert.strictEqual(suppressedMessages.length, 0);
5189 it("should not allow `allowReserved
: true` in ES > 3", () => {
5190 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
5192 ecmaVersions.forEach(ecmaVersion => {
5194 const messages = linter.verify(code, { parserOptions: { ecmaVersion, allowReserved: true } }, filename);
5195 const suppressedMessages = linter.getSuppressedMessages();
5197 assert.strictEqual(messages.length, 1);
5198 assert.strictEqual(messages[0].severity, 2);
5199 assert.isTrue(messages[0].fatal);
5200 assert.match(messages[0].message, /^Parsing error:.*allowReserved/u);
5202 assert.strictEqual(suppressedMessages.length, 0);
5206 it("should be able to use es6 features if there is a comment which has \"eslint-env es6\"", () => {
5208 "/* eslint-env es6 */",
5209 "var arrow = () => 0;",
5210 "var binary = 0b1010;",
5211 "{ let a = 0; const b = 1; }",
5213 "function defaultParams(a = 0) {}",
5214 "var {a = 1, b = 2} = {};",
5215 "for (var a of []) {}",
5216 "function* generator() { yield 0; }",
5217 "var computed = {[a]: 0};",
5218 "var duplicate = {dup: 0, dup: 1};",
5219 "var method = {foo() {}};",
5220 "var property = {a, b};",
5221 "var octal = 0o755;",
5222 "var u = /^.$/u.test('ð ®·');",
5223 "var y = /hello/y.test('hello');",
5224 "function restParam(a, ...rest) {}",
5225 "class B { superInFunc() { super.foo(); } }",
5226 "var template = `hello
, ${a}
`;",
5227 "var unicode = '\\u{20BB7}';"
5230 const messages = linter.verify(code, null, "eslint-env es6");
5231 const suppressedMessages = linter.getSuppressedMessages();
5233 assert.strictEqual(messages.length, 0);
5234 assert.strictEqual(suppressedMessages.length, 0);
5237 it("should be able to return in global if there is a comment which enables the node environment with a comment", () => {
5238 const messages = linter.verify(`/* ${ESLINT_ENV} node */ return;`, null, "node environment");
5239 const suppressedMessages = linter.getSuppressedMessages();
5241 assert.strictEqual(messages.length, 0);
5242 assert.strictEqual(suppressedMessages.length, 0);
5245 it("should attach a \"/*global\" comment node to declared variables", () => {
5246 const code = "/* global foo */\n/* global bar, baz */";
5249 linter.defineRules({
5253 const scope = context.getScope();
5254 const sourceCode = context.getSourceCode();
5255 const comments = sourceCode.getAllComments();
5257 assert.strictEqual(2, comments.length);
5259 const foo = getVariable(scope, "foo");
5261 assert.strictEqual(foo.eslintExplicitGlobal, true);
5262 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
5264 const bar = getVariable(scope, "bar");
5266 assert.strictEqual(bar.eslintExplicitGlobal, true);
5267 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
5269 const baz = getVariable(scope, "baz");
5271 assert.strictEqual(baz.eslintExplicitGlobal, true);
5272 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
5280 linter.verify(code, { rules: { test: 2 } });
5284 it("should report a linting error when a global is set to an invalid value", () => {
5285 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
5286 const suppressedMessages = linter.getSuppressedMessages();
5288 assert.deepStrictEqual(results, [
5292 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
5303 message: "'foo' is not defined.",
5308 nodeType: "Identifier"
5312 assert.strictEqual(suppressedMessages.length, 0);
5315 it("should not crash when we reuse the SourceCode object", () => {
5316 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
5317 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
5320 it("should reuse the SourceCode object", () => {
5324 linter.defineRule("save-ast1", () => ({
5329 linter.defineRule("save-ast2", () => ({
5335 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast1": 2 } });
5336 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast2": 2 } });
5338 assert(ast1 !== null);
5339 assert(ast2 !== null);
5340 assert(ast1 === ast2);
5343 it("should allow 'await' as a property name in modules", () => {
5344 const result = linter.verify(
5346 { parserOptions: { ecmaVersion: 6, sourceType: "module" } }
5348 const suppressedMessages = linter.getSuppressedMessages();
5350 assert(result.length === 0);
5351 assert.strictEqual(suppressedMessages.length, 0);
5355 it("should not modify config object passed as argument", () => {
5358 Object.freeze(config);
5359 linter.verify("var", config);
5362 it("should pass 'id' to rule contexts with the rule id", () => {
5363 const spy = sinon.spy(context => {
5364 assert.strictEqual(context.id, "foo-bar-baz");
5368 linter.defineRule("foo-bar-baz", spy);
5369 linter.verify("x", { rules: { "foo-bar-baz": "error" } });
5370 assert(spy.calledOnce);
5373 describe("descriptions in directive comments", () => {
5374 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
5375 const aaa = sinon.stub().returns({});
5376 const bbb = sinon.stub().returns({});
5378 linter.defineRule("aaa", { create: aaa });
5379 linter.defineRule("bbb", { create: bbb });
5380 const messages = linter.verify(`
5381 /*eslint aaa:error -- bbb:error */
5382 console
.log("hello")
5384 const suppressedMessages = linter.getSuppressedMessages();
5386 // Don't include syntax error of the comment.
5387 assert.deepStrictEqual(messages, []);
5390 assert.strictEqual(aaa.callCount, 1);
5391 assert.strictEqual(bbb.callCount, 0);
5393 assert.strictEqual(suppressedMessages.length, 0);
5396 it("should ignore the part preceded by '--' in '/*eslint-env*/'.", () => {
5397 const messages = linter.verify(`
5398 /*eslint-env es2015 -- es2017 */
5401 `, { rules: { "no-redeclare": "error" } });
5402 const suppressedMessages = linter.getSuppressedMessages();
5404 // Don't include `Atomics
`
5405 assert.deepStrictEqual(
5412 message: "'Promise' is already defined as a built-in global variable.",
5413 messageId: "redeclaredAsBuiltin",
5414 nodeType: "Identifier",
5415 ruleId: "no-redeclare",
5420 assert.strictEqual(suppressedMessages.length, 0);
5423 it("should ignore the part preceded by '--' in '/*global*/'.", () => {
5424 const messages = linter.verify(`
5425 /*global aaa -- bbb */
5428 `, { rules: { "no-redeclare": "error" } });
5429 const suppressedMessages = linter.getSuppressedMessages();
5431 // Don't include `bbb
`
5432 assert.deepStrictEqual(
5439 message: "'aaa' is already defined by a variable declaration.",
5440 messageId: "redeclaredBySyntax",
5442 ruleId: "no-redeclare",
5447 assert.strictEqual(suppressedMessages.length, 0);
5450 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
5451 const messages = linter.verify(`
5452 /*globals aaa -- bbb */
5455 `, { rules: { "no-redeclare": "error" } });
5456 const suppressedMessages = linter.getSuppressedMessages();
5458 // Don't include `bbb
`
5459 assert.deepStrictEqual(
5466 message: "'aaa' is already defined by a variable declaration.",
5467 messageId: "redeclaredBySyntax",
5469 ruleId: "no-redeclare",
5474 assert.strictEqual(suppressedMessages.length, 0);
5477 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
5478 const messages = linter.verify(`
5479 /*exported aaa -- bbb */
5482 `, { rules: { "no-unused-vars": "error" } });
5483 const suppressedMessages = linter.getSuppressedMessages();
5485 // Don't include `aaa
`
5486 assert.deepStrictEqual(
5493 message: "'bbb' is assigned a value but never used.",
5494 messageId: "unusedVar",
5495 nodeType: "Identifier",
5496 ruleId: "no-unused-vars",
5501 assert.strictEqual(suppressedMessages.length, 0);
5504 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
5505 const messages = linter.verify(`
5506 /*eslint-disable no-redeclare -- no-unused-vars */
5509 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5510 const suppressedMessages = linter.getSuppressedMessages();
5512 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5513 assert.deepStrictEqual(
5520 message: "'aaa' is assigned a value but never used.",
5521 messageId: "unusedVar",
5522 nodeType: "Identifier",
5523 ruleId: "no-unused-vars",
5528 assert.deepStrictEqual(
5535 message: "'aaa' is already defined.",
5536 messageId: "redeclared",
5537 nodeType: "Identifier",
5538 ruleId: "no-redeclare",
5540 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5545 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
5546 const messages = linter.verify(`
5547 /*eslint-disable no-redeclare, no-unused-vars */
5548 /*eslint-enable no-redeclare -- no-unused-vars */
5551 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5552 const suppressedMessages = linter.getSuppressedMessages();
5554 // Do include `no
-redeclare
` but not `no
-unused
-vars
`
5555 assert.deepStrictEqual(
5562 message: "'aaa' is already defined.",
5563 messageId: "redeclared",
5564 nodeType: "Identifier",
5565 ruleId: "no-redeclare",
5570 assert.deepStrictEqual(
5577 message: "'aaa' is assigned a value but never used.",
5578 messageId: "unusedVar",
5579 nodeType: "Identifier",
5580 ruleId: "no-unused-vars",
5582 suppressions: [{ kind: "directive", justification: "" }]
5587 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
5588 const messages = linter.verify(`
5589 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
5590 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
5591 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5592 const suppressedMessages = linter.getSuppressedMessages();
5594 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5595 assert.deepStrictEqual(
5602 message: "'aaa' is assigned a value but never used.",
5603 messageId: "unusedVar",
5604 nodeType: "Identifier",
5605 ruleId: "no-unused-vars",
5610 assert.deepStrictEqual(
5617 message: "'aaa' is already defined.",
5618 messageId: "redeclared",
5619 nodeType: "Identifier",
5620 ruleId: "no-redeclare",
5622 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5627 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
5628 const messages = linter.verify(`
5629 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
5630 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
5631 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5632 const suppressedMessages = linter.getSuppressedMessages();
5634 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5635 assert.deepStrictEqual(
5642 message: "'aaa' is assigned a value but never used.",
5643 messageId: "unusedVar",
5644 nodeType: "Identifier",
5645 ruleId: "no-unused-vars",
5650 assert.deepStrictEqual(
5657 message: "'aaa' is already defined.",
5658 messageId: "redeclared",
5659 nodeType: "Identifier",
5660 ruleId: "no-redeclare",
5662 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5667 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
5668 const messages = linter.verify(`
5669 //eslint-disable-next-line no-redeclare -- no-unused-vars
5671 //eslint-disable-next-line no-redeclare -- no-unused-vars
5673 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5674 const suppressedMessages = linter.getSuppressedMessages();
5676 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5677 assert.deepStrictEqual(
5684 message: "'aaa' is assigned a value but never used.",
5685 messageId: "unusedVar",
5686 nodeType: "Identifier",
5687 ruleId: "no-unused-vars",
5692 assert.deepStrictEqual(
5699 message: "'aaa' is already defined.",
5700 messageId: "redeclared",
5701 nodeType: "Identifier",
5702 ruleId: "no-redeclare",
5704 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5709 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
5710 const messages = linter.verify(`
5711 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
5713 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
5715 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
5716 const suppressedMessages = linter.getSuppressedMessages();
5718 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
5719 assert.deepStrictEqual(
5726 message: "'aaa' is assigned a value but never used.",
5727 messageId: "unusedVar",
5728 nodeType: "Identifier",
5729 ruleId: "no-unused-vars",
5734 assert.deepStrictEqual(
5741 message: "'aaa' is already defined.",
5742 messageId: "redeclared",
5743 nodeType: "Identifier",
5744 ruleId: "no-redeclare",
5746 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
5751 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
5752 const rule = sinon.stub().returns({});
5754 linter.defineRule("a--rule", { create: rule });
5755 const messages = linter.verify(`
5756 /*eslint a--rule:error */
5757 console
.log("hello")
5759 const suppressedMessages = linter.getSuppressedMessages();
5761 // Don't include syntax error of the comment.
5762 assert.deepStrictEqual(messages, []);
5765 assert.strictEqual(rule.callCount, 1);
5767 assert.strictEqual(suppressedMessages.length, 0);
5770 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
5771 const aaa = sinon.stub().returns({});
5772 const bbb = sinon.stub().returns({});
5774 linter.defineRule("aaa", { create: aaa });
5775 linter.defineRule("bbb", { create: bbb });
5776 const messages = linter.verify(`
5777 /*eslint aaa:error -------- bbb:error */
5778 console
.log("hello")
5780 const suppressedMessages = linter.getSuppressedMessages();
5782 // Don't include syntax error of the comment.
5783 assert.deepStrictEqual(messages, []);
5786 assert.strictEqual(aaa.callCount, 1);
5787 assert.strictEqual(bbb.callCount, 0);
5789 assert.strictEqual(suppressedMessages.length, 0);
5792 it("should ignore the part preceded by '--' with line breaks.", () => {
5793 const aaa = sinon.stub().returns({});
5794 const bbb = sinon.stub().returns({});
5796 linter.defineRule("aaa", { create: aaa });
5797 linter.defineRule("bbb", { create: bbb });
5798 const messages = linter.verify(`
5802 console
.log("hello")
5804 const suppressedMessages = linter.getSuppressedMessages();
5806 // Don't include syntax error of the comment.
5807 assert.deepStrictEqual(messages, []);
5810 assert.strictEqual(aaa.callCount, 1);
5811 assert.strictEqual(bbb.callCount, 0);
5813 assert.strictEqual(suppressedMessages.length, 0);
5818 describe("context.getScope()", () => {
5821 * Get the scope on the node `astSelector
` specified.
5822 * @param {string} code The source code to verify.
5823 * @param {string} astSelector The AST selector to get scope.
5824 * @param {number} [ecmaVersion=5] The ECMAScript version.
5825 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
5827 function getScope(code, astSelector, ecmaVersion = 5) {
5830 linter.defineRule("get-scope", context => ({
5831 [astSelector](node0) {
5833 scope = context.getScope();
5839 parserOptions: { ecmaVersion },
5840 rules: { "get-scope": 2 }
5844 return { node, scope };
5847 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
5848 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
5850 assert.strictEqual(scope.type, "function");
5851 assert.strictEqual(scope.block, node);
5854 it("should return 'function' scope on FunctionExpression (ES5)", () => {
5855 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
5857 assert.strictEqual(scope.type, "function");
5858 assert.strictEqual(scope.block, node);
5861 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
5862 const { node, scope } = getScope("function f() {}", "BlockStatement");
5864 assert.strictEqual(scope.type, "function");
5865 assert.strictEqual(scope.block, node.parent);
5868 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
5869 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
5871 assert.strictEqual(scope.type, "function");
5872 assert.strictEqual(scope.block, node.parent);
5875 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
5876 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
5878 assert.strictEqual(scope.type, "function");
5879 assert.strictEqual(scope.block, node.parent.parent);
5880 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5883 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
5884 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
5886 assert.strictEqual(scope.type, "block");
5887 assert.strictEqual(scope.upper.type, "function");
5888 assert.strictEqual(scope.block, node);
5889 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
5890 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
5893 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
5894 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
5896 assert.strictEqual(scope.type, "block");
5897 assert.strictEqual(scope.upper.type, "block");
5898 assert.strictEqual(scope.upper.upper.type, "function");
5899 assert.strictEqual(scope.block, node);
5900 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5901 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
5902 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
5905 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
5906 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
5908 assert.strictEqual(scope.type, "function");
5909 assert.strictEqual(scope.block, node.parent.parent);
5910 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5913 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
5914 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
5916 assert.strictEqual(scope.type, "switch");
5917 assert.strictEqual(scope.block, node);
5918 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5921 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
5922 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
5924 assert.strictEqual(scope.type, "function");
5925 assert.strictEqual(scope.block, node.parent.parent.parent);
5926 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5929 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
5930 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
5932 assert.strictEqual(scope.type, "switch");
5933 assert.strictEqual(scope.block, node.parent);
5934 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5937 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
5938 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
5940 assert.strictEqual(scope.type, "catch");
5941 assert.strictEqual(scope.block, node);
5942 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5945 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
5946 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
5948 assert.strictEqual(scope.type, "catch");
5949 assert.strictEqual(scope.block, node);
5950 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5953 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
5954 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
5956 assert.strictEqual(scope.type, "catch");
5957 assert.strictEqual(scope.block, node.parent);
5958 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5961 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
5962 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
5964 assert.strictEqual(scope.type, "block");
5965 assert.strictEqual(scope.block, node);
5966 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
5969 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
5970 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
5972 assert.strictEqual(scope.type, "function");
5973 assert.strictEqual(scope.block, node.parent.parent);
5974 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
5977 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
5978 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
5980 assert.strictEqual(scope.type, "for");
5981 assert.strictEqual(scope.block, node);
5982 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
5985 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
5986 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
5988 assert.strictEqual(scope.type, "function");
5989 assert.strictEqual(scope.block, node.parent.parent.parent);
5990 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
5993 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
5994 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
5996 assert.strictEqual(scope.type, "block");
5997 assert.strictEqual(scope.upper.type, "for");
5998 assert.strictEqual(scope.block, node);
5999 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6000 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
6003 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
6004 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
6006 assert.strictEqual(scope.type, "function");
6007 assert.strictEqual(scope.block, node.parent.parent);
6008 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
6011 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
6012 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
6014 assert.strictEqual(scope.type, "for");
6015 assert.strictEqual(scope.block, node);
6016 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
6019 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
6020 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
6022 assert.strictEqual(scope.type, "function");
6023 assert.strictEqual(scope.block, node.parent.parent.parent);
6024 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
6027 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
6028 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
6030 assert.strictEqual(scope.type, "block");
6031 assert.strictEqual(scope.upper.type, "for");
6032 assert.strictEqual(scope.block, node);
6033 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6034 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
6037 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
6038 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
6040 assert.strictEqual(scope.type, "for");
6041 assert.strictEqual(scope.block, node);
6042 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
6045 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
6046 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
6048 assert.strictEqual(scope.type, "block");
6049 assert.strictEqual(scope.upper.type, "for");
6050 assert.strictEqual(scope.block, node);
6051 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
6052 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
6055 it("should shadow the same name variable by the iteration variable.", () => {
6056 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
6058 assert.strictEqual(scope.type, "for");
6059 assert.strictEqual(scope.upper.type, "global");
6060 assert.strictEqual(scope.block, node);
6061 assert.strictEqual(scope.upper.variables[0].references.length, 0);
6062 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
6063 assert.strictEqual(scope.references[1].identifier, node.right);
6064 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
6068 describe("Variables and references", () => {
6071 "function foo() { b; }",
6086 linter.defineRules({
6090 scope = context.getScope();
6096 linter.verify(code, { rules: { test: 2 }, globals: { e: true, f: false } });
6104 it("Scope#through should contain references of undefined variables", () => {
6105 assert.strictEqual(scope.through.length, 2);
6106 assert.strictEqual(scope.through[0].identifier.name, "a");
6107 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
6108 assert.strictEqual(scope.through[0].resolved, null);
6109 assert.strictEqual(scope.through[1].identifier.name, "b");
6110 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
6111 assert.strictEqual(scope.through[1].resolved, null);
6114 it("Scope#variables should contain global variables", () => {
6115 assert(scope.variables.some(v => v.name === "Object"));
6116 assert(scope.variables.some(v => v.name === "foo"));
6117 assert(scope.variables.some(v => v.name === "c"));
6118 assert(scope.variables.some(v => v.name === "d"));
6119 assert(scope.variables.some(v => v.name === "e"));
6120 assert(scope.variables.some(v => v.name === "f"));
6123 it("Scope#set should contain global variables", () => {
6124 assert(scope.set.get("Object"));
6125 assert(scope.set.get("foo"));
6126 assert(scope.set.get("c"));
6127 assert(scope.set.get("d"));
6128 assert(scope.set.get("e"));
6129 assert(scope.set.get("f"));
6132 it("Variables#references should contain their references", () => {
6133 assert.strictEqual(scope.set.get("Object").references.length, 1);
6134 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
6135 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
6136 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
6137 assert.strictEqual(scope.set.get("foo").references.length, 1);
6138 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
6139 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
6140 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
6141 assert.strictEqual(scope.set.get("c").references.length, 1);
6142 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
6143 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
6144 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
6145 assert.strictEqual(scope.set.get("d").references.length, 1);
6146 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
6147 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
6148 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
6149 assert.strictEqual(scope.set.get("e").references.length, 1);
6150 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
6151 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
6152 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
6153 assert.strictEqual(scope.set.get("f").references.length, 1);
6154 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
6155 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
6156 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
6159 it("Reference#resolved should be their variable", () => {
6160 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
6161 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
6162 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
6163 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
6164 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
6165 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
6169 describe("context.getDeclaredVariables(node)", () => {
6172 * Assert `context
.getDeclaredVariables(node
)` is valid.
6173 * @param {string} code A code to check.
6174 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
6175 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
6178 function verify(code, type, expectedNamesList) {
6179 linter.defineRules({
6183 * Assert `context
.getDeclaredVariables(node
)` is empty.
6184 * @param {ASTNode} node A node to check.
6187 function checkEmpty(node) {
6188 assert.strictEqual(0, context.getDeclaredVariables(node).length);
6191 Program: checkEmpty,
6192 EmptyStatement: checkEmpty,
6193 BlockStatement: checkEmpty,
6194 ExpressionStatement: checkEmpty,
6195 LabeledStatement: checkEmpty,
6196 BreakStatement: checkEmpty,
6197 ContinueStatement: checkEmpty,
6198 WithStatement: checkEmpty,
6199 SwitchStatement: checkEmpty,
6200 ReturnStatement: checkEmpty,
6201 ThrowStatement: checkEmpty,
6202 TryStatement: checkEmpty,
6203 WhileStatement: checkEmpty,
6204 DoWhileStatement: checkEmpty,
6205 ForStatement: checkEmpty,
6206 ForInStatement: checkEmpty,
6207 DebuggerStatement: checkEmpty,
6208 ThisExpression: checkEmpty,
6209 ArrayExpression: checkEmpty,
6210 ObjectExpression: checkEmpty,
6211 Property: checkEmpty,
6212 SequenceExpression: checkEmpty,
6213 UnaryExpression: checkEmpty,
6214 BinaryExpression: checkEmpty,
6215 AssignmentExpression: checkEmpty,
6216 UpdateExpression: checkEmpty,
6217 LogicalExpression: checkEmpty,
6218 ConditionalExpression: checkEmpty,
6219 CallExpression: checkEmpty,
6220 NewExpression: checkEmpty,
6221 MemberExpression: checkEmpty,
6222 SwitchCase: checkEmpty,
6223 Identifier: checkEmpty,
6224 Literal: checkEmpty,
6225 ForOfStatement: checkEmpty,
6226 ArrowFunctionExpression: checkEmpty,
6227 YieldExpression: checkEmpty,
6228 TemplateLiteral: checkEmpty,
6229 TaggedTemplateExpression: checkEmpty,
6230 TemplateElement: checkEmpty,
6231 ObjectPattern: checkEmpty,
6232 ArrayPattern: checkEmpty,
6233 RestElement: checkEmpty,
6234 AssignmentPattern: checkEmpty,
6235 ClassBody: checkEmpty,
6236 MethodDefinition: checkEmpty,
6237 MetaProperty: checkEmpty
6240 rule[type] = function(node) {
6241 const expectedNames = expectedNamesList.shift();
6242 const variables = context.getDeclaredVariables(node);
6244 assert(Array.isArray(expectedNames));
6245 assert(Array.isArray(variables));
6246 assert.strictEqual(expectedNames.length, variables.length);
6247 for (let i = variables.length - 1; i >= 0; i--) {
6248 assert.strictEqual(expectedNames[i], variables[i].name);
6254 linter.verify(code, {
6258 sourceType: "module"
6262 // Check all expected names are asserted.
6263 assert.strictEqual(0, expectedNamesList.length);
6266 it("VariableDeclaration", () => {
6267 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
6271 ["g", "h", "i", "j", "k"],
6275 verify(code, "VariableDeclaration", namesList);
6278 it("VariableDeclaration (on for-in/of loop)", () => {
6280 // TDZ scope is created here, so tests to exclude those.
6281 const code = "\n for (var {a, x: [b], y: {c = 0}} in foo) {\n let g;\n }\n for (let {d, x: [e], y: {f = 0}} of foo) {\n let h;\n }\n ";
6289 verify(code, "VariableDeclaration", namesList);
6292 it("VariableDeclarator", () => {
6294 // TDZ scope is created here, so tests to exclude those.
6295 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
6304 verify(code, "VariableDeclarator", namesList);
6307 it("FunctionDeclaration", () => {
6308 const code = "\n function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n }\n function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n }\n ";
6310 ["foo", "a", "b", "c", "d", "e"],
6311 ["bar", "f", "g", "h", "i", "j"]
6314 verify(code, "FunctionDeclaration", namesList);
6317 it("FunctionExpression", () => {
6318 const code = "\n (function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n });\n (function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n });\n ";
6320 ["foo", "a", "b", "c", "d", "e"],
6321 ["bar", "f", "g", "h", "i", "j"],
6325 verify(code, "FunctionExpression", namesList);
6328 it("ArrowFunctionExpression", () => {
6329 const code = "\n (({a, x: [b], y: {c = 0}}, [d, e]) => {\n let z;\n });\n (({f, x: [g], y: {h = 0}}, [i, j]) => {\n let z;\n });\n ";
6331 ["a", "b", "c", "d", "e"],
6332 ["f", "g", "h", "i", "j"]
6335 verify(code, "ArrowFunctionExpression", namesList);
6338 it("ClassDeclaration", () => {
6339 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
6341 ["A", "A"], // outer scope's and inner scope's.
6345 verify(code, "ClassDeclaration", namesList);
6348 it("ClassExpression", () => {
6349 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
6355 verify(code, "ClassExpression", namesList);
6358 it("CatchClause", () => {
6359 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
6365 verify(code, "CatchClause", namesList);
6368 it("ImportDeclaration", () => {
6369 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6376 verify(code, "ImportDeclaration", namesList);
6379 it("ImportSpecifier", () => {
6380 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6386 verify(code, "ImportSpecifier", namesList);
6389 it("ImportDefaultSpecifier", () => {
6390 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6395 verify(code, "ImportDefaultSpecifier", namesList);
6398 it("ImportNamespaceSpecifier", () => {
6399 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
6404 verify(code, "ImportNamespaceSpecifier", namesList);
6408 describe("suggestions", () => {
6409 it("provides suggestion information for tools to use", () => {
6410 linter.defineRule("rule-with-suggestions", {
6411 meta: { hasSuggestions: true },
6412 create: context => ({
6416 message: "Incorrect spacing",
6418 desc: "Insert space at the beginning",
6419 fix: fixer => fixer.insertTextBefore(node, " ")
6421 desc: "Insert space at the end",
6422 fix: fixer => fixer.insertTextAfter(node, " ")
6429 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6430 const suppressedMessages = linter.getSuppressedMessages();
6432 assert.deepStrictEqual(messages[0].suggestions, [{
6433 desc: "Insert space at the beginning",
6439 desc: "Insert space at the end",
6446 assert.strictEqual(suppressedMessages.length, 0);
6449 it("supports messageIds for suggestions", () => {
6450 linter.defineRule("rule-with-suggestions", {
6453 suggestion1: "Insert space at the beginning",
6454 suggestion2: "Insert space at the end"
6456 hasSuggestions: true
6458 create: context => ({
6462 message: "Incorrect spacing",
6464 messageId: "suggestion1",
6465 fix: fixer => fixer.insertTextBefore(node, " ")
6467 messageId: "suggestion2",
6468 fix: fixer => fixer.insertTextAfter(node, " ")
6475 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
6476 const suppressedMessages = linter.getSuppressedMessages();
6478 assert.deepStrictEqual(messages[0].suggestions, [{
6479 messageId: "suggestion1",
6480 desc: "Insert space at the beginning",
6486 messageId: "suggestion2",
6487 desc: "Insert space at the end",
6494 assert.strictEqual(suppressedMessages.length, 0);
6497 it("should throw an error if suggestion is passed but `meta
.hasSuggestions
` property is not enabled", () => {
6498 linter.defineRule("rule-with-suggestions", {
6499 meta: { docs: {}, schema: [] },
6500 create: context => ({
6504 message: "hello world",
6505 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
6511 assert.throws(() => {
6512 linter.verify("0", { rules: { "rule-with-suggestions": "error" } });
6513 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`.");
6516 it("should throw an error if suggestion is passed but `meta
.hasSuggestions
` property is not enabled and the rule has the obsolete `meta
.docs
.suggestion
` property", () => {
6517 linter.defineRule("rule-with-meta-docs-suggestion", {
6518 meta: { docs: { suggestion: true }, schema: [] },
6519 create: context => ({
6523 message: "hello world",
6524 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
6530 assert.throws(() => {
6531 linter.verify("0", { rules: { "rule-with-meta-docs-suggestion": "error" } });
6532 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`. `meta
.docs
.suggestion
` is ignored by ESLint.");
6536 describe("mutability", () => {
6541 linter1 = new Linter();
6542 linter2 = new Linter();
6545 describe("rules", () => {
6546 it("with no changes, same rules are loaded", () => {
6547 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
6550 it("loading rule in one doesn't change the other", () => {
6551 linter1.defineRule("mock-rule", () => ({}));
6553 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
6554 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
6559 describe("processors", () => {
6560 let receivedFilenames = [];
6561 let receivedPhysicalFilenames = [];
6564 receivedFilenames = [];
6565 receivedPhysicalFilenames = [];
6567 // A rule that always reports the AST with a message equal to the source text
6568 linter.defineRule("report-original-text", context => ({
6570 receivedFilenames.push(context.getFilename());
6571 receivedPhysicalFilenames.push(context.getPhysicalFilename());
6572 context.report({ node: ast, message: context.getSourceCode().text });
6577 describe("preprocessors", () => {
6578 it("should receive text and filename.", () => {
6579 const code = "foo bar baz";
6580 const preprocess = sinon.spy(text => text.split(" "));
6582 linter.verify(code, {}, { filename, preprocess });
6584 assert.strictEqual(preprocess.calledOnce, true);
6585 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6588 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
6589 const code = "foo bar baz";
6590 const problems = linter.verify(
6592 { rules: { "report-original-text": "error" } },
6595 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6597 return input.split(" ");
6602 assert.strictEqual(problems.length, 3);
6603 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
6606 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
6607 const code = "foo bar baz";
6608 const problems = linter.verify(
6610 { rules: { "report-original-text": "error" } },
6614 // Apply a preprocessor that splits the source text into spaces and lints each word individually
6616 return input.split(" ").map(text => ({
6617 filename: "block.js",
6624 assert.strictEqual(problems.length, 3);
6625 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
6628 assert.strictEqual(receivedFilenames.length, 3);
6629 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
6630 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
6631 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
6633 // physical filename
6634 assert.strictEqual(receivedPhysicalFilenames.length, 3);
6635 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
6638 it("should receive text even if a SourceCode object was given.", () => {
6640 const preprocess = sinon.spy(text => text.split(" "));
6642 linter.verify(code, {});
6643 const sourceCode = linter.getSourceCode();
6645 linter.verify(sourceCode, {}, { filename, preprocess });
6647 assert.strictEqual(preprocess.calledOnce, true);
6648 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6651 it("should receive text even if a SourceCode object was given (with BOM).", () => {
6652 const code = "\uFEFFfoo";
6653 const preprocess = sinon.spy(text => text.split(" "));
6655 linter.verify(code, {});
6656 const sourceCode = linter.getSourceCode();
6658 linter.verify(sourceCode, {}, { filename, preprocess });
6660 assert.strictEqual(preprocess.calledOnce, true);
6661 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6664 it("should catch preprocess error.", () => {
6666 const preprocess = sinon.spy(() => {
6667 throw Object.assign(new SyntaxError("Invalid syntax"), {
6673 const messages = linter.verify(code, {}, { filename, preprocess });
6675 assert.strictEqual(preprocess.calledOnce, true);
6676 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
6677 assert.deepStrictEqual(messages, [
6682 message: "Preprocessing error: Invalid syntax",
6690 describe("postprocessors", () => {
6691 it("should receive result and filename.", () => {
6692 const code = "foo bar baz";
6693 const preprocess = sinon.spy(text => text.split(" "));
6694 const postprocess = sinon.spy(text => [text]);
6696 linter.verify(code, {}, { filename, postprocess, preprocess });
6698 assert.strictEqual(postprocess.calledOnce, true);
6699 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
6702 it("should apply a postprocessor to the reported messages", () => {
6703 const code = "foo bar baz";
6705 const problems = linter.verify(
6707 { rules: { "report-original-text": "error" } },
6709 preprocess: input => input.split(" "),
6712 * Apply a postprocessor that updates the locations of the reported problems
6713 * to make sure they correspond to the locations in the original text.
6715 postprocess(problemLists) {
6716 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
6717 return problemLists.reduce(
6718 (combinedList, problemList, index) =>
6719 combinedList.concat(
6726 message: problem.message.toUpperCase(),
6727 column: problem.column + index * 4
6738 assert.strictEqual(problems.length, 3);
6739 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
6740 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
6743 it("should use postprocessed problem ranges when applying autofixes", () => {
6744 const code = "foo bar baz";
6746 linter.defineRule("capitalize-identifiers", {
6753 if (node.name !== node.name.toUpperCase()) {
6756 message: "Capitalize this identifier",
6757 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
6765 const fixResult = linter.verifyAndFix(
6767 { rules: { "capitalize-identifiers": "error" } },
6771 * Apply a postprocessor that updates the locations of autofixes
6772 * to make sure they correspond to locations in the original text.
6774 preprocess: input => input.split(" "),
6775 postprocess(problemLists) {
6776 return problemLists.reduce(
6777 (combinedProblems, problemList, blockIndex) =>
6778 combinedProblems.concat(
6779 problemList.map(problem =>
6780 Object.assign(problem, {
6782 text: problem.fix.text,
6783 range: problem.fix.range.map(
6784 rangeIndex => rangeIndex + blockIndex * 4
6795 assert.strictEqual(fixResult.fixed, true);
6796 assert.strictEqual(fixResult.messages.length, 0);
6797 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
6802 describe("verifyAndFix", () => {
6803 it("Fixes the code", () => {
6804 const messages = linter.verifyAndFix("var a", {
6808 }, { filename: "test.js" });
6810 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
6811 assert.isTrue(messages.fixed);
6814 it("does not require a third argument", () => {
6815 const fixResult = linter.verifyAndFix("var a", {
6821 assert.deepStrictEqual(fixResult, {
6828 it("does not include suggestions in autofix results", () => {
6829 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
6832 "no-useless-escape": 2
6836 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
6837 assert.strictEqual(fixResult.fixed, true);
6838 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
6841 it("does not apply autofixes when fix argument is `false`", () => {
6842 const fixResult = linter.verifyAndFix("var a", {
6848 assert.strictEqual(fixResult.fixed, false);
6851 it("stops fixing after 10 passes", () => {
6853 linter.defineRule("add-spaces", {
6855 fixable: "whitespace"
6862 message: "Add a space before this node.",
6863 fix: fixer => fixer.insertTextBefore(node, " ")
6870 const fixResult = linter.verifyAndFix("a", { rules: { "add-spaces": "error" } });
6872 assert.strictEqual(fixResult.fixed, true);
6873 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a
`);
6874 assert.strictEqual(fixResult.messages.length, 1);
6877 it("should throw an error if fix is passed but meta has no `fixable
` property", () => {
6878 linter.defineRule("test-rule", {
6883 create: context => ({
6885 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6890 assert.throws(() => {
6891 linter.verify("0", { rules: { "test-rule": "error" } });
6892 }, /Fixable rules must set the `meta
\.fixable
` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test-rule"$/u);
6895 it("should throw an error if fix is passed and there is no metadata", () => {
6896 linter.defineRule("test-rule", {
6897 create: context => ({
6899 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6904 assert.throws(() => {
6905 linter.verify("0", { rules: { "test-rule": "error" } });
6906 }, /Fixable rules must set the `meta
\.fixable
` property/u);
6909 it("should throw an error if fix is passed from a legacy-format rule", () => {
6910 linter.defineRule("test-rule", context => ({
6912 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6916 assert.throws(() => {
6917 linter.verify("0", { rules: { "test-rule": "error" } });
6918 }, /Fixable rules must set the `meta
\.fixable
` property/u);
6922 describe("Edge cases", () => {
6924 it("should properly parse import statements when sourceType is module", () => {
6925 const code = "import foo from 'foo';";
6926 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6927 const suppressedMessages = linter.getSuppressedMessages();
6929 assert.strictEqual(messages.length, 0);
6930 assert.strictEqual(suppressedMessages.length, 0);
6933 it("should properly parse import all statements when sourceType is module", () => {
6934 const code = "import * as foo from 'foo';";
6935 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6936 const suppressedMessages = linter.getSuppressedMessages();
6938 assert.strictEqual(messages.length, 0);
6939 assert.strictEqual(suppressedMessages.length, 0);
6942 it("should properly parse default export statements when sourceType is module", () => {
6943 const code = "export default function initialize() {}";
6944 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6945 const suppressedMessages = linter.getSuppressedMessages();
6947 assert.strictEqual(messages.length, 0);
6948 assert.strictEqual(suppressedMessages.length, 0);
6951 // https://github.com/eslint/eslint/issues/9687
6952 it("should report an error when invalid parserOptions found", () => {
6953 let messages = linter.verify("", { parserOptions: { ecmaVersion: 222 } });
6954 let suppressedMessages = linter.getSuppressedMessages();
6956 assert.deepStrictEqual(messages.length, 1);
6957 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
6958 assert.strictEqual(suppressedMessages.length, 0);
6960 messages = linter.verify("", { parserOptions: { sourceType: "foo" } });
6961 suppressedMessages = linter.getSuppressedMessages();
6963 assert.deepStrictEqual(messages.length, 1);
6964 assert.ok(messages[0].message.includes("Invalid sourceType"));
6965 assert.strictEqual(suppressedMessages.length, 0);
6967 messages = linter.verify("", { parserOptions: { ecmaVersion: 5, sourceType: "module" } });
6968 suppressedMessages = linter.getSuppressedMessages();
6970 assert.deepStrictEqual(messages.length, 1);
6971 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
6972 assert.strictEqual(suppressedMessages.length, 0);
6975 it("should not crash when invalid parentheses syntax is encountered", () => {
6976 linter.verify("left = (aSize.width/2) - ()");
6979 it("should not crash when let is used inside of switch case", () => {
6980 linter.verify("switch(foo) { case 1: let bar=2; }", { parserOptions: { ecmaVersion: 6 } });
6983 it("should not crash when parsing destructured assignment", () => {
6984 linter.verify("var { a='a' } = {};", { parserOptions: { ecmaVersion: 6 } });
6987 it("should report syntax error when a keyword exists in object property shorthand", () => {
6988 const messages = linter.verify("let a = {this}", { parserOptions: { ecmaVersion: 6 } });
6989 const suppressedMessages = linter.getSuppressedMessages();
6991 assert.strictEqual(messages.length, 1);
6992 assert.strictEqual(messages[0].fatal, true);
6994 assert.strictEqual(suppressedMessages.length, 0);
6997 it("should not rewrite env setting in core (https://github.com/eslint/eslint/issues/4814)", () => {
7000 * This test focuses on the instance of https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/conf/environments.js#L26-L28
7001 * This `verify()` takes the instance and runs https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/lib/eslint.js#L416
7003 linter.defineRule("test", () => ({}));
7004 linter.verify("var a = 0;", {
7005 env: { node: true },
7006 parserOptions: { ecmaVersion: 6, sourceType: "module" },
7010 // This `verify()` takes the instance and tests that the instance was not modified.
7013 linter.defineRule("test", context => {
7015 context.parserOptions.ecmaFeatures.globalReturn,
7016 "`ecmaFeatures
.globalReturn
` of the node environment should not be modified."
7021 linter.verify("var a = 0;", {
7022 env: { node: true },
7029 it("should throw when rule's create() function does not return an object", () => {
7030 const config = { rules: { checker: "error" } };
7032 linter.defineRule("checker", () => null); // returns null
7034 assert.throws(() => {
7035 linter.verify("abc", config, filename);
7036 }, "The create() function for rule 'checker' did not return an object.");
7038 linter.defineRule("checker", () => {}); // returns undefined
7040 assert.throws(() => {
7041 linter.verify("abc", config, filename);
7042 }, "The create() function for rule 'checker' did not return an object.");
7046 describe("Custom parser", () => {
7048 const errorPrefix = "Parsing error: ";
7050 it("should have file path passed to it", () => {
7051 const code = "/* this is code */";
7052 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
7054 linter.defineParser("stub-parser", testParsers.stubParser);
7055 linter.verify(code, { parser: "stub-parser" }, filename, true);
7057 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
7060 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
7061 const code = "var myDivElement = <div {...this.props} />;";
7063 linter.defineParser("esprima", esprima);
7064 const messages = linter.verify(code, { parser: "esprima", parserOptions: { jsx: true } }, "filename");
7065 const suppressedMessages = linter.getSuppressedMessages();
7067 assert.strictEqual(messages.length, 0);
7068 assert.strictEqual(suppressedMessages.length, 0);
7071 it("should return an error when the custom parser can't be found", () => {
7072 const code = "var myDivElement = <div {...this.props} />;";
7073 const messages = linter.verify(code, { parser: "esprima-xyz" }, "filename");
7074 const suppressedMessages = linter.getSuppressedMessages();
7076 assert.strictEqual(messages.length, 1);
7077 assert.strictEqual(messages[0].severity, 2);
7078 assert.strictEqual(messages[0].message, "Configured parser 'esprima-xyz' was not found.");
7080 assert.strictEqual(suppressedMessages.length, 0);
7083 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
7084 const code = "null %% 'foo'";
7086 linter.defineParser("unknown-logical-operator", testParsers.unknownLogicalOperator);
7088 // This shouldn't throw
7089 const messages = linter.verify(code, { parser: "unknown-logical-operator" }, filename, true);
7090 const suppressedMessages = linter.getSuppressedMessages();
7092 assert.strictEqual(messages.length, 0);
7093 assert.strictEqual(suppressedMessages.length, 0);
7096 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
7097 const code = "foo && bar %% baz";
7099 linter.defineParser("unknown-logical-operator-nested", testParsers.unknownLogicalOperatorNested);
7101 // This shouldn't throw
7102 const messages = linter.verify(code, { parser: "unknown-logical-operator-nested" }, filename, true);
7103 const suppressedMessages = linter.getSuppressedMessages();
7105 assert.strictEqual(messages.length, 0);
7106 assert.strictEqual(suppressedMessages.length, 0);
7109 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
7110 const code = "foo && bar %% baz";
7114 linter.defineRule("collect-node-types", () => ({
7116 nodes.push(node.type);
7120 linter.defineParser("non-js-parser", testParsers.nonJSParser);
7122 const messages = linter.verify(code, {
7123 parser: "non-js-parser",
7125 "collect-node-types": "error"
7128 const suppressedMessages = linter.getSuppressedMessages();
7130 assert.strictEqual(messages.length, 0);
7131 assert.isTrue(nodes.length > 0);
7133 assert.strictEqual(suppressedMessages.length, 0);
7136 it("should strip leading line: prefix from parser error", () => {
7137 linter.defineParser("line-error", testParsers.lineError);
7138 const messages = linter.verify(";", { parser: "line-error" }, "filename");
7139 const suppressedMessages = linter.getSuppressedMessages();
7141 assert.strictEqual(messages.length, 1);
7142 assert.strictEqual(messages[0].severity, 2);
7143 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
7145 assert.strictEqual(suppressedMessages.length, 0);
7148 it("should not modify a parser error message without a leading line: prefix", () => {
7149 linter.defineParser("no-line-error", testParsers.noLineError);
7150 const messages = linter.verify(";", { parser: "no-line-error" }, filename);
7151 const suppressedMessages = linter.getSuppressedMessages();
7153 assert.strictEqual(messages.length, 1);
7154 assert.strictEqual(messages[0].severity, 2);
7155 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
7157 assert.strictEqual(suppressedMessages.length, 0);
7160 describe("if a parser provides 'visitorKeys'", () => {
7164 let firstChildNodes = [];
7168 firstChildNodes = [];
7169 linter.defineRule("collect-node-types", () => ({
7171 types.push(node.type);
7174 linter.defineRule("save-scope-manager", context => {
7175 scopeManager = context.getSourceCode().scopeManager;
7179 linter.defineRule("esquery-option", () => ({
7180 ":first-child"(node) {
7181 firstChildNodes.push(node);
7184 linter.defineParser("enhanced-parser2", testParsers.enhancedParser2);
7185 linter.verify("@foo class A {}", {
7186 parser: "enhanced-parser2",
7188 "collect-node-types": "error",
7189 "save-scope-manager": "error",
7190 "esquery-option": "error"
7194 sourceCode = linter.getSourceCode();
7197 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
7198 assert.deepStrictEqual(
7200 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7204 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7205 assert.deepStrictEqual(
7206 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
7207 ["experimentalDecorators", "id", "superClass", "body"]
7211 it("should use the same visitorKeys if the source code object is reused", () => {
7214 linter.defineRule("collect-node-types", () => ({
7216 types2.push(node.type);
7219 linter.verify(sourceCode, {
7221 "collect-node-types": "error"
7225 assert.deepStrictEqual(
7227 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7231 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7232 assert.deepStrictEqual(
7234 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
7239 describe("if a parser provides 'scope'", () => {
7241 let sourceCode = null;
7244 linter.defineParser("enhanced-parser3", testParsers.enhancedParser3);
7245 linter.defineRule("save-scope1", context => ({
7247 scope = context.getScope();
7250 linter.verify("@foo class A {}", { parser: "enhanced-parser3", rules: { "save-scope1": 2 } });
7252 sourceCode = linter.getSourceCode();
7255 it("should use the scope (so the global scope has the reference of '@foo')", () => {
7256 assert.strictEqual(scope.references.length, 1);
7257 assert.deepStrictEqual(
7258 scope.references[0].identifier.name,
7263 it("should use the same scope if the source code object is reused", () => {
7266 linter.defineRule("save-scope2", context => ({
7268 scope2 = context.getScope();
7271 linter.verify(sourceCode, { rules: { "save-scope2": 2 } }, "test.js");
7273 assert(scope2 !== null);
7274 assert(scope2 === scope);
7278 it("should not pass any default parserOptions to the parser", () => {
7279 linter.defineParser("throws-with-options", testParsers.throwsWithOptions);
7280 const messages = linter.verify(";", { parser: "throws-with-options" }, "filename");
7281 const suppressedMessages = linter.getSuppressedMessages();
7283 assert.strictEqual(messages.length, 0);
7284 assert.strictEqual(suppressedMessages.length, 0);
7288 describe("merging 'parserOptions'", () => {
7289 it("should deeply merge 'parserOptions' from an environment with 'parserOptions' from the provided config", () => {
7290 const code = "return <div/>";
7293 node: true // ecmaFeatures: { globalReturn: true }
7302 const messages = linter.verify(code, config);
7303 const suppressedMessages = linter.getSuppressedMessages();
7305 // no parsing errors
7306 assert.strictEqual(messages.length, 0);
7307 assert.strictEqual(suppressedMessages.length, 0);
7312 describe("Linter with FlatConfigArray", () => {
7315 const filename = "filename.js";
7318 * Creates a config array with some default properties.
7319 * @param {FlatConfig|FlatConfig[]} value The value to base the
7321 * @returns {FlatConfigArray} The created config array.
7323 function createFlatConfigArray(value) {
7324 return new FlatConfigArray(value, { basePath: "" });
7328 linter = new Linter({ configType: "flat" });
7331 describe("Static Members", () => {
7332 describe("version", () => {
7333 it("should return same version as instance property", () => {
7334 assert.strictEqual(Linter.version, linter.version);
7339 describe("Config Options", () => {
7341 describe("languageOptions", () => {
7343 describe("ecmaVersion", () => {
7345 it("should error when accessing a global that isn't available in ecmaVersion 5", () => {
7346 const messages = linter.verify("new Map()", {
7349 sourceType: "script"
7355 const suppressedMessages = linter.getSuppressedMessages();
7357 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7358 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7360 assert.strictEqual(suppressedMessages.length, 0);
7363 it("should error when accessing a global that isn't available in ecmaVersion 3", () => {
7364 const messages = linter.verify("JSON.stringify({})", {
7367 sourceType: "script"
7373 const suppressedMessages = linter.getSuppressedMessages();
7375 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7376 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7378 assert.strictEqual(suppressedMessages.length, 0);
7381 it("should add globals for ES6 when ecmaVersion is 6", () => {
7382 const messages = linter.verify("new Map()", {
7390 const suppressedMessages = linter.getSuppressedMessages();
7392 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7393 assert.strictEqual(suppressedMessages.length, 0);
7396 it("should allow destructuring when ecmaVersion is 6", () => {
7397 const messages = linter.verify("let {a} = b", {
7402 const suppressedMessages = linter.getSuppressedMessages();
7404 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7405 assert.strictEqual(suppressedMessages.length, 0);
7408 it("ecmaVersion should be normalized to year name for ES 6", () => {
7416 assert.strictEqual(context.languageOptions.ecmaVersion, 2015);
7426 rules: { "test/checker": "error" }
7429 linter.verify("foo", config, filename);
7432 it("ecmaVersion should be normalized to latest year by default", () => {
7440 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7447 rules: { "test/checker": "error" }
7450 linter.verify("foo", config, filename);
7453 it("ecmaVersion should not be normalized to year name for ES 5", () => {
7461 assert.strictEqual(context.languageOptions.ecmaVersion, 5);
7471 rules: { "test/checker": "error" }
7474 linter.verify("foo", config, filename);
7477 it("ecmaVersion should be normalized to year name for 'latest'", () => {
7485 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
7493 ecmaVersion: "latest"
7495 rules: { "test/checker": "error" }
7498 linter.verify("foo", config, filename);
7504 describe("sourceType", () => {
7506 it("should be module by default", () => {
7514 assert.strictEqual(context.languageOptions.sourceType, "module");
7521 rules: { "test/checker": "error" }
7524 linter.verify("import foo from 'bar'", config, filename);
7527 it("should default to commonjs when passed a .cjs filename", () => {
7535 assert.strictEqual(context.languageOptions.sourceType, "commonjs");
7542 rules: { "test/checker": "error" }
7545 linter.verify("import foo from 'bar'", config, `${filename}
.cjs
`);
7549 it("should error when import is used in a script", () => {
7550 const messages = linter.verify("import foo from 'bar';", {
7553 sourceType: "script"
7557 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
7558 assert.strictEqual(messages[0].message, "Parsing error: 'import' and 'export' may appear only with 'sourceType: module'");
7561 it("should not error when import is used in a module", () => {
7562 const messages = linter.verify("import foo from 'bar';", {
7565 sourceType: "module"
7568 const suppressedMessages = linter.getSuppressedMessages();
7570 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7571 assert.strictEqual(suppressedMessages.length, 0);
7574 it("should error when return is used at the top-level outside of commonjs", () => {
7575 const messages = linter.verify("return", {
7578 sourceType: "script"
7581 const suppressedMessages = linter.getSuppressedMessages();
7583 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
7584 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
7586 assert.strictEqual(suppressedMessages.length, 0);
7589 it("should not error when top-level return is used in commonjs", () => {
7590 const messages = linter.verify("return", {
7593 sourceType: "commonjs"
7596 const suppressedMessages = linter.getSuppressedMessages();
7598 assert.strictEqual(messages.length, 0, "There should no linting errors.");
7599 assert.strictEqual(suppressedMessages.length, 0);
7602 it("should error when accessing a Node.js global outside of commonjs", () => {
7603 const messages = linter.verify("require()", {
7611 const suppressedMessages = linter.getSuppressedMessages();
7613 assert.strictEqual(messages.length, 1, "There should be one linting error.");
7614 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
7616 assert.strictEqual(suppressedMessages.length, 0);
7619 it("should add globals for Node.js when sourceType is commonjs", () => {
7620 const messages = linter.verify("require()", {
7623 sourceType: "commonjs"
7629 const suppressedMessages = linter.getSuppressedMessages();
7631 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
7632 assert.strictEqual(suppressedMessages.length, 0);
7635 it("should allow 'await' as a property name in modules", () => {
7636 const result = linter.verify(
7641 sourceType: "module"
7645 const suppressedMessages = linter.getSuppressedMessages();
7647 assert(result.length === 0);
7648 assert.strictEqual(suppressedMessages.length, 0);
7653 describe("parser", () => {
7655 it("should be able to define a custom parser", () => {
7657 parseForESLint: function parse(code, options) {
7659 ast: esprima.parse(code, options),
7675 "test-parser": parser
7680 parser: "test/test-parser"
7685 const messages = linter.verify("0", config, filename);
7686 const suppressedMessages = linter.getSuppressedMessages();
7688 assert.strictEqual(messages.length, 0);
7689 assert.strictEqual(suppressedMessages.length, 0);
7692 it("should pass parser as context.languageOptions.parser to all rules when provided on config", () => {
7698 "test-rule": sinon.mock().withArgs(
7699 sinon.match({ languageOptions: { parser: esprima } })
7712 linter.verify("0", config, filename);
7715 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
7720 "enhanced-parser": testParsers.enhancedParser
7725 parser: "test/enhanced-parser"
7729 const messages = linter.verify("0", config, filename);
7730 const suppressedMessages = linter.getSuppressedMessages();
7732 assert.strictEqual(messages.length, 0);
7733 assert.strictEqual(suppressedMessages.length, 0);
7736 it("should expose parser services when using parseForESLint() and services are specified", () => {
7742 "enhanced-parser": testParsers.enhancedParser
7745 "test-service-rule": context => ({
7749 message: context.parserServices.test.getMessage()
7757 parser: "test/enhanced-parser"
7760 "test/test-service-rule": 2
7764 const messages = linter.verify("0", config, filename);
7765 const suppressedMessages = linter.getSuppressedMessages();
7767 assert.strictEqual(messages.length, 1);
7768 assert.strictEqual(messages[0].message, "Hi!");
7770 assert.strictEqual(suppressedMessages.length, 0);
7773 it("should use the same parserServices if source code object is reused", () => {
7779 "enhanced-parser": testParsers.enhancedParser
7782 "test-service-rule": context => ({
7786 message: context.parserServices.test.getMessage()
7794 parser: "test/enhanced-parser"
7797 "test/test-service-rule": 2
7801 const messages = linter.verify("0", config, filename);
7802 const suppressedMessages = linter.getSuppressedMessages();
7804 assert.strictEqual(messages.length, 1);
7805 assert.strictEqual(messages[0].message, "Hi!");
7806 assert.strictEqual(suppressedMessages.length, 0);
7808 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
7809 const suppressedMessages2 = linter.getSuppressedMessages();
7811 assert.strictEqual(messages2.length, 1);
7812 assert.strictEqual(messages2[0].message, "Hi!");
7813 assert.strictEqual(suppressedMessages2.length, 0);
7816 it("should pass parser as context.languageOptions.parser to all rules when default parser is used", () => {
7818 // references to Espree get messed up in a browser context, so wrap it
7819 const fakeParser = {
7823 const spy = sinon.spy(context => {
7824 assert.strictEqual(context.languageOptions.parser, fakeParser);
7844 linter.verify("0", config, filename);
7845 assert.isTrue(spy.calledOnce);
7849 describe("Custom Parsers", () => {
7851 const errorPrefix = "Parsing error: ";
7853 it("should have file path passed to it", () => {
7854 const code = "/* this is code */";
7855 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
7858 parser: testParsers.stubParser
7862 linter.verify(code, config, filename, true);
7864 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
7867 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
7868 const code = "var myDivElement = <div {...this.props} />;";
7878 const messages = linter.verify(code, config, filename);
7879 const suppressedMessages = linter.getSuppressedMessages();
7881 assert.strictEqual(messages.length, 0);
7882 assert.strictEqual(suppressedMessages.length, 0);
7885 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
7886 const code = "null %% 'foo'";
7889 parser: testParsers.unknownLogicalOperator
7893 // This shouldn't throw
7894 const messages = linter.verify(code, config, filename);
7895 const suppressedMessages = linter.getSuppressedMessages();
7897 assert.strictEqual(messages.length, 0);
7898 assert.strictEqual(suppressedMessages.length, 0);
7901 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
7902 const code = "foo && bar %% baz";
7905 parser: testParsers.unknownLogicalOperatorNested
7909 // This shouldn't throw
7910 const messages = linter.verify(code, config, filename);
7911 const suppressedMessages = linter.getSuppressedMessages();
7913 assert.strictEqual(messages.length, 0);
7914 assert.strictEqual(suppressedMessages.length, 0);
7917 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
7918 const code = "foo && bar %% baz";
7924 "collect-node-types": () => ({
7926 nodes.push(node.type);
7933 parser: testParsers.nonJSParser
7936 "test/collect-node-types": "error"
7940 const messages = linter.verify(code, config, filename, true);
7941 const suppressedMessages = linter.getSuppressedMessages();
7943 assert.strictEqual(messages.length, 0);
7944 assert.isTrue(nodes.length > 0);
7945 assert.strictEqual(suppressedMessages.length, 0);
7948 it("should strip leading line: prefix from parser error", () => {
7949 const messages = linter.verify(";", {
7951 parser: testParsers.lineError
7954 const suppressedMessages = linter.getSuppressedMessages();
7956 assert.strictEqual(messages.length, 1);
7957 assert.strictEqual(messages[0].severity, 2);
7958 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
7960 assert.strictEqual(suppressedMessages.length, 0);
7963 it("should not modify a parser error message without a leading line: prefix", () => {
7964 const messages = linter.verify(";", {
7966 parser: testParsers.noLineError
7969 const suppressedMessages = linter.getSuppressedMessages();
7971 assert.strictEqual(messages.length, 1);
7972 assert.strictEqual(messages[0].severity, 2);
7973 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
7975 assert.strictEqual(suppressedMessages.length, 0);
7978 describe("if a parser provides 'visitorKeys'", () => {
7982 let firstChildNodes = [];
7986 firstChildNodes = [];
7991 "collect-node-types": () => ({
7993 types.push(node.type);
7996 "save-scope-manager": context => {
7997 scopeManager = context.getSourceCode().scopeManager;
8001 "esquery-option": () => ({
8002 ":first-child"(node) {
8003 firstChildNodes.push(node);
8010 parser: testParsers.enhancedParser2
8013 "test/collect-node-types": "error",
8014 "test/save-scope-manager": "error",
8015 "test/esquery-option": "error"
8019 linter.verify("@foo class A {}", config);
8021 sourceCode = linter.getSourceCode();
8024 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
8025 assert.deepStrictEqual(
8027 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
8031 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
8032 assert.deepStrictEqual(
8033 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
8034 ["experimentalDecorators", "id", "superClass", "body"]
8038 it("should use the same visitorKeys if the source code object is reused", () => {
8044 "collect-node-types": () => ({
8046 types2.push(node.type);
8053 "test/collect-node-types": "error"
8057 linter.verify(sourceCode, config);
8059 assert.deepStrictEqual(
8061 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
8065 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
8066 assert.deepStrictEqual(
8068 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
8073 describe("if a parser provides 'scope'", () => {
8075 let sourceCode = null;
8082 "save-scope1": context => ({
8084 scope = context.getScope();
8091 parser: testParsers.enhancedParser3
8094 "test/save-scope1": "error"
8098 linter.verify("@foo class A {}", config);
8100 sourceCode = linter.getSourceCode();
8103 it("should use the scope (so the global scope has the reference of '@foo')", () => {
8104 assert.strictEqual(scope.references.length, 1);
8105 assert.deepStrictEqual(
8106 scope.references[0].identifier.name,
8111 it("should use the same scope if the source code object is reused", () => {
8117 "save-scope2": context => ({
8119 scope2 = context.getScope();
8126 "test/save-scope2": "error"
8130 linter.verify(sourceCode, config, "test.js");
8132 assert(scope2 !== null);
8133 assert(scope2 === scope);
8137 it("should pass default languageOptions to the parser", () => {
8139 const spy = sinon.spy((code, options) => espree.parse(code, options));
8141 linter.verify(";", {
8149 assert(spy.calledWithMatch(";", {
8150 ecmaVersion: espree.latestEcmaVersion + 2009,
8151 sourceType: "module"
8159 describe("parseOptions", () => {
8161 it("should pass ecmaFeatures to all rules when provided on config", () => {
8163 const parserOptions = {
8173 "test-rule": sinon.mock().withArgs(
8174 sinon.match({ languageOptions: { parserOptions } })
8187 linter.verify("0", config, filename);
8190 it("should switch globalReturn to false if sourceType is module", () => {
8196 "test-rule": sinon.mock().withArgs(
8211 sourceType: "module",
8223 linter.verify("0", config, filename);
8226 it("should not parse sloppy-mode code when impliedStrict is true", () => {
8228 const messages = linter.verify("var private;", {
8237 const suppressedMessages = linter.getSuppressedMessages();
8239 assert.strictEqual(messages.length, 1);
8240 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
8242 assert.strictEqual(suppressedMessages.length, 0);
8245 it("should properly parse valid code when impliedStrict is true", () => {
8247 const messages = linter.verify("var foo;", {
8256 const suppressedMessages = linter.getSuppressedMessages();
8258 assert.strictEqual(messages.length, 0);
8259 assert.strictEqual(suppressedMessages.length, 0);
8262 it("should properly parse JSX when passed ecmaFeatures", () => {
8264 const messages = linter.verify("var x = <div/>;", {
8273 const suppressedMessages = linter.getSuppressedMessages();
8275 assert.strictEqual(messages.length, 0);
8276 assert.strictEqual(suppressedMessages.length, 0);
8279 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
8280 const code = "var myDivElement = <div className=\"foo\" />;";
8281 const messages = linter.verify(code, {}, filename);
8282 const suppressedMessages = linter.getSuppressedMessages();
8284 assert.strictEqual(messages.length, 1);
8285 assert.strictEqual(messages[0].line, 1);
8286 assert.strictEqual(messages[0].column, 20);
8287 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
8289 assert.strictEqual(suppressedMessages.length, 0);
8292 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
8293 const code = "var myDivElement = <div className=\"foo\" />;";
8294 const messages = linter.verify(code, {
8303 const suppressedMessages = linter.getSuppressedMessages();
8305 assert.strictEqual(messages.length, 0);
8306 assert.strictEqual(suppressedMessages.length, 0);
8309 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
8310 const code = "var myDivElement = <div {...this.props} />;";
8311 const messages = linter.verify(code, {
8322 const suppressedMessages = linter.getSuppressedMessages();
8324 assert.strictEqual(messages.length, 0);
8325 assert.strictEqual(suppressedMessages.length, 0);
8328 it("should not allow the use of reserved words as variable names in ES3", () => {
8329 const code = "var char;";
8330 const messages = linter.verify(code, {
8333 sourceType: "script"
8337 assert.strictEqual(messages.length, 1);
8338 assert.strictEqual(messages[0].severity, 2);
8339 assert.isTrue(messages[0].fatal);
8340 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8343 it("should not allow the use of reserved words as property names in member expressions in ES3", () => {
8344 const code = "obj.char;";
8345 const messages = linter.verify(code, {
8348 sourceType: "script"
8352 assert.strictEqual(messages.length, 1);
8353 assert.strictEqual(messages[0].severity, 2);
8354 assert.isTrue(messages[0].fatal);
8355 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8358 it("should not allow the use of reserved words as property names in object literals in ES3", () => {
8359 const code = "var obj = { char: 1 };";
8360 const messages = linter.verify(code, {
8363 sourceType: "script"
8367 assert.strictEqual(messages.length, 1);
8368 assert.strictEqual(messages[0].severity, 2);
8369 assert.isTrue(messages[0].fatal);
8370 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
8373 it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => {
8374 const code = "var char; obj.char; var obj = { char: 1 };";
8375 const messages = linter.verify(code, {
8378 sourceType: "script",
8385 assert.strictEqual(messages.length, 0);
8388 it("should not allow the use of reserved words as variable names in ES > 3", () => {
8389 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8391 ecmaVersions.forEach(ecmaVersion => {
8392 const code = "var enum;";
8393 const messages = linter.verify(code, {
8395 ...ecmaVersion ? { ecmaVersion } : {},
8396 sourceType: "script"
8400 assert.strictEqual(messages.length, 1);
8401 assert.strictEqual(messages[0].severity, 2);
8402 assert.isTrue(messages[0].fatal);
8403 assert.match(messages[0].message, /^Parsing error:.*'enum'/u);
8407 it("should allow the use of reserved words as property names in ES > 3", () => {
8408 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8410 ecmaVersions.forEach(ecmaVersion => {
8411 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
8412 const messages = linter.verify(code, {
8414 ...ecmaVersion ? { ecmaVersion } : {},
8415 sourceType: "script"
8419 assert.strictEqual(messages.length, 0);
8423 it("should not allow `allowReserved
: true` in ES > 3", () => {
8424 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
8426 ecmaVersions.forEach(ecmaVersion => {
8428 const messages = linter.verify(code, {
8430 ...ecmaVersion ? { ecmaVersion } : {},
8431 sourceType: "script",
8438 assert.strictEqual(messages.length, 1);
8439 assert.strictEqual(messages[0].severity, 2);
8440 assert.isTrue(messages[0].fatal);
8441 assert.match(messages[0].message, /^Parsing error:.*allowReserved/u);
8447 describe("settings", () => {
8448 const ruleId = "test-rule";
8450 it("should pass settings to all rules", () => {
8456 [ruleId]: context => ({
8458 context.report(node, context.settings.info);
8468 [`test
/${ruleId}
`]: 1
8472 const messages = linter.verify("0", config, filename);
8473 const suppressedMessages = linter.getSuppressedMessages();
8475 assert.strictEqual(messages.length, 1);
8476 assert.strictEqual(messages[0].message, "Hello");
8478 assert.strictEqual(suppressedMessages.length, 0);
8481 it("should not have any settings if they were not passed in", () => {
8487 [ruleId]: context => ({
8489 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
8490 context.report(node, "Settings should be empty");
8500 [`test
/${ruleId}
`]: 1
8504 const messages = linter.verify("0", config, filename);
8505 const suppressedMessages = linter.getSuppressedMessages();
8507 assert.strictEqual(messages.length, 0);
8508 assert.strictEqual(suppressedMessages.length, 0);
8512 describe("rules", () => {
8513 const code = "var answer = 6 * 7";
8515 it("should be configurable by only setting the integer value", () => {
8516 const rule = "semi",
8517 config = { rules: {} };
8519 config.rules[rule] = 1;
8521 const messages = linter.verify(code, config, filename, true);
8522 const suppressedMessages = linter.getSuppressedMessages();
8524 assert.strictEqual(messages.length, 1);
8525 assert.strictEqual(messages[0].ruleId, rule);
8527 assert.strictEqual(suppressedMessages.length, 0);
8530 it("should be configurable by only setting the string value", () => {
8531 const rule = "semi",
8532 config = { rules: {} };
8534 config.rules[rule] = "warn";
8536 const messages = linter.verify(code, config, filename, true);
8537 const suppressedMessages = linter.getSuppressedMessages();
8539 assert.strictEqual(messages.length, 1);
8540 assert.strictEqual(messages[0].severity, 1);
8541 assert.strictEqual(messages[0].ruleId, rule);
8543 assert.strictEqual(suppressedMessages.length, 0);
8546 it("should be configurable by passing in values as an array", () => {
8547 const rule = "semi",
8548 config = { rules: {} };
8550 config.rules[rule] = [1];
8552 const messages = linter.verify(code, config, filename, true);
8553 const suppressedMessages = linter.getSuppressedMessages();
8555 assert.strictEqual(messages.length, 1);
8556 assert.strictEqual(messages[0].ruleId, rule);
8558 assert.strictEqual(suppressedMessages.length, 0);
8561 it("should be configurable by passing in string value as an array", () => {
8562 const rule = "semi",
8563 config = { rules: {} };
8565 config.rules[rule] = ["warn"];
8567 const messages = linter.verify(code, config, filename, true);
8568 const suppressedMessages = linter.getSuppressedMessages();
8570 assert.strictEqual(messages.length, 1);
8571 assert.strictEqual(messages[0].severity, 1);
8572 assert.strictEqual(messages[0].ruleId, rule);
8574 assert.strictEqual(suppressedMessages.length, 0);
8577 it("should not be configurable by setting other value", () => {
8578 const rule = "semi",
8579 config = { rules: {} };
8581 config.rules[rule] = "1";
8583 assert.throws(() => {
8584 linter.verify(code, config, filename, true);
8585 }, /Key "rules": Key "semi"/u);
8588 it("should process empty config", () => {
8590 const messages = linter.verify(code, config, filename, true);
8591 const suppressedMessages = linter.getSuppressedMessages();
8593 assert.strictEqual(messages.length, 0);
8594 assert.strictEqual(suppressedMessages.length, 0);
8600 describe("verify()", () => {
8602 it("should report warnings in order by line and column when called", () => {
8604 const code = "foo()\n alert('test')";
8605 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8607 const messages = linter.verify(code, config, filename);
8608 const suppressedMessages = linter.getSuppressedMessages();
8610 assert.strictEqual(messages.length, 3);
8611 assert.strictEqual(messages[0].line, 1);
8612 assert.strictEqual(messages[0].column, 6);
8613 assert.strictEqual(messages[1].line, 2);
8614 assert.strictEqual(messages[1].column, 18);
8615 assert.strictEqual(messages[2].line, 2);
8616 assert.strictEqual(messages[2].column, 18);
8618 assert.strictEqual(suppressedMessages.length, 0);
8621 it("should report ignored file when filename isn't matched in the config array", () => {
8623 const code = "foo()\n alert('test')";
8624 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
8626 const messages = linter.verify(code, config, "filename.ts");
8628 assert.strictEqual(messages.length, 1);
8629 assert.deepStrictEqual(messages[0], {
8632 message: "No matching configuration found for filename.ts.",
8638 describe("Plugins", () => {
8640 it("should not load rule definition when rule isn't used", () => {
8642 const spy = sinon.spy();
8654 linter.verify("code", config, filename);
8655 assert.isTrue(spy.notCalled, "Rule should not have been called");
8659 describe("Rule Internals", () => {
8661 const code = TEST_CODE;
8663 it("should throw an error when an error occurs inside of a rule visitor", () => {
8670 throw new Error("Intentional error.");
8676 rules: { "test/checker": "error" }
8679 assert.throws(() => {
8680 linter.verify(code, config, filename);
8681 }, `Intentional error
.\nOccurred while linting ${filename}
:1\nRule: "test/checker"`);
8684 it("should not call rule visitor with a `this` value", () => {
8685 const spy = sinon.spy();
8696 rules: { "test/checker": "error" }
8699 linter.verify("foo", config);
8700 assert(spy.calledOnce);
8701 assert.strictEqual(spy.firstCall.thisValue, void 0);
8704 it("should not call unrecognized rule visitor when present in a rule", () => {
8705 const spy = sinon.spy();
8717 "test/checker": "error",
8722 linter.verify("foo", config);
8723 assert(spy.notCalled);
8726 it("should have all the `parent
` properties on nodes when the rule visitors are created", () => {
8727 const spy = sinon.spy(context => {
8728 const ast = context.getSourceCode().ast;
8730 assert.strictEqual(ast.body[0].parent, ast);
8731 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
8732 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
8733 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
8746 rules: { "test/checker": "error" }
8749 linter.verify("foo + bar", config);
8750 assert(spy.calledOnce);
8753 it("events for each node type should fire", () => {
8755 // spies for various AST node types
8756 const spyLiteral = sinon.spy(),
8757 spyVariableDeclarator = sinon.spy(),
8758 spyVariableDeclaration = sinon.spy(),
8759 spyIdentifier = sinon.spy(),
8760 spyBinaryExpression = sinon.spy();
8768 Literal: spyLiteral,
8769 VariableDeclarator: spyVariableDeclarator,
8770 VariableDeclaration: spyVariableDeclaration,
8771 Identifier: spyIdentifier,
8772 BinaryExpression: spyBinaryExpression
8778 rules: { "test/checker": "error" }
8781 const messages = linter.verify(code, config, filename, true);
8782 const suppressedMessages = linter.getSuppressedMessages();
8784 assert.strictEqual(messages.length, 0);
8785 sinon.assert.calledOnce(spyVariableDeclaration);
8786 sinon.assert.calledOnce(spyVariableDeclarator);
8787 sinon.assert.calledOnce(spyIdentifier);
8788 sinon.assert.calledTwice(spyLiteral);
8789 sinon.assert.calledOnce(spyBinaryExpression);
8791 assert.strictEqual(suppressedMessages.length, 0);
8794 it("should throw an error if a rule reports a problem without a message", () => {
8800 "invalid-report"(context) {
8803 context.report({ node });
8810 rules: { "test/invalid-report": "error" }
8814 () => linter.verify("foo", config),
8816 "Missing `message
` property in report() call; add a message that describes the linting problem."
8823 describe("Rule Context", () => {
8825 describe("context.getFilename()", () => {
8826 const ruleId = "filename-rule";
8828 it("has access to the filename", () => {
8834 [ruleId]: context => ({
8836 context.report(node, context.getFilename());
8843 [`test
/${ruleId}
`]: 1
8847 const messages = linter.verify("0", config, filename);
8848 const suppressedMessages = linter.getSuppressedMessages();
8850 assert.strictEqual(messages[0].message, filename);
8851 assert.strictEqual(suppressedMessages.length, 0);
8854 it("defaults filename to '<input>'", () => {
8860 [ruleId]: context => ({
8862 context.report(node, context.getFilename());
8869 [`test
/${ruleId}
`]: 1
8874 const messages = linter.verify("0", config);
8875 const suppressedMessages = linter.getSuppressedMessages();
8877 assert.strictEqual(messages[0].message, "<input>");
8878 assert.strictEqual(suppressedMessages.length, 0);
8882 describe("context.getPhysicalFilename()", () => {
8884 const ruleId = "filename-rule";
8886 it("has access to the physicalFilename", () => {
8892 [ruleId]: context => ({
8894 context.report(node, context.getPhysicalFilename());
8901 [`test
/${ruleId}
`]: 1
8905 const messages = linter.verify("0", config, filename);
8906 const suppressedMessages = linter.getSuppressedMessages();
8908 assert.strictEqual(messages[0].message, filename);
8909 assert.strictEqual(suppressedMessages.length, 0);
8914 describe("context.getSourceLines()", () => {
8916 it("should get proper lines when using \\n as a line break", () => {
8917 const code = "a;\nb;";
8918 const spy = sinon.spy(context => {
8919 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8931 rules: { "test/checker": "error" }
8934 linter.verify(code, config);
8935 assert(spy.calledOnce);
8938 it("should get proper lines when using \\r\\n as a line break", () => {
8939 const code = "a;\r\nb;";
8940 const spy = sinon.spy(context => {
8941 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8953 rules: { "test/checker": "error" }
8956 linter.verify(code, config);
8957 assert(spy.calledOnce);
8960 it("should get proper lines when using \\r as a line break", () => {
8961 const code = "a;\rb;";
8962 const spy = sinon.spy(context => {
8963 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8975 rules: { "test/checker": "error" }
8978 linter.verify(code, config);
8979 assert(spy.calledOnce);
8982 it("should get proper lines when using \\u2028 as a line break", () => {
8983 const code = "a;\u2028b;";
8984 const spy = sinon.spy(context => {
8985 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
8997 rules: { "test/checker": "error" }
9000 linter.verify(code, config);
9001 assert(spy.calledOnce);
9004 it("should get proper lines when using \\u2029 as a line break", () => {
9005 const code = "a;\u2029b;";
9006 const spy = sinon.spy(context => {
9007 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
9019 rules: { "test/checker": "error" }
9022 linter.verify(code, config);
9023 assert(spy.calledOnce);
9028 describe("context.getSource()", () => {
9029 const code = TEST_CODE;
9031 it("should retrieve all text when used without parameters", () => {
9039 checker: context => {
9040 spy = sinon.spy(() => {
9041 assert.strictEqual(context.getSource(), TEST_CODE);
9043 return { Program: spy };
9048 rules: { "test/checker": "error" }
9051 linter.verify(code, config);
9052 assert(spy && spy.calledOnce);
9055 it("should retrieve all text for root node", () => {
9063 checker: context => {
9064 spy = sinon.spy(node => {
9065 assert.strictEqual(context.getSource(node), TEST_CODE);
9067 return { Program: spy };
9072 rules: { "test/checker": "error" }
9075 linter.verify(code, config);
9076 assert(spy && spy.calledOnce);
9079 it("should clamp to valid range when retrieving characters before start of source", () => {
9086 checker: context => {
9087 spy = sinon.spy(node => {
9088 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
9090 return { Program: spy };
9095 rules: { "test/checker": "error" }
9098 linter.verify(code, config);
9099 assert(spy && spy.calledOnce);
9102 it("should retrieve all text for binary expression", () => {
9109 checker: context => {
9110 spy = sinon.spy(node => {
9111 assert.strictEqual(context.getSource(node), "6 * 7");
9113 return { BinaryExpression: spy };
9118 rules: { "test/checker": "error" }
9121 linter.verify(code, config);
9122 assert(spy && spy.calledOnce);
9125 it("should retrieve all text plus two characters before for binary expression", () => {
9132 checker: context => {
9133 spy = sinon.spy(node => {
9134 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
9136 return { BinaryExpression: spy };
9141 rules: { "test/checker": "error" }
9144 linter.verify(code, config);
9145 assert(spy && spy.calledOnce);
9148 it("should retrieve all text plus one character after for binary expression", () => {
9155 checker: context => {
9156 spy = sinon.spy(node => {
9157 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
9159 return { BinaryExpression: spy };
9164 rules: { "test/checker": "error" }
9167 linter.verify(code, config);
9168 assert(spy && spy.calledOnce);
9171 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
9178 checker: context => {
9179 spy = sinon.spy(node => {
9180 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
9182 return { BinaryExpression: spy };
9187 rules: { "test/checker": "error" }
9190 linter.verify(code, config);
9191 assert(spy && spy.calledOnce);
9196 describe("context.getAncestors()", () => {
9197 const code = TEST_CODE;
9199 it("should retrieve all ancestors when used", () => {
9207 checker: context => {
9208 spy = sinon.spy(() => {
9209 const ancestors = context.getAncestors();
9211 assert.strictEqual(ancestors.length, 3);
9213 return { BinaryExpression: spy };
9218 rules: { "test/checker": "error" }
9221 linter.verify(code, config, filename, true);
9222 assert(spy && spy.calledOnce);
9225 it("should retrieve empty ancestors for root node", () => {
9232 checker: context => {
9233 spy = sinon.spy(() => {
9234 const ancestors = context.getAncestors();
9236 assert.strictEqual(ancestors.length, 0);
9239 return { Program: spy };
9244 rules: { "test/checker": "error" }
9247 linter.verify(code, config);
9248 assert(spy && spy.calledOnce);
9252 describe("context.getNodeByRangeIndex()", () => {
9253 const code = TEST_CODE;
9255 it("should retrieve a node starting at the given index", () => {
9256 const spy = sinon.spy(context => {
9257 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
9269 rules: { "test/checker": "error" }
9272 linter.verify(code, config);
9273 assert(spy.calledOnce);
9276 it("should retrieve a node containing the given index", () => {
9277 const spy = sinon.spy(context => {
9278 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
9290 rules: { "test/checker": "error" }
9293 linter.verify(code, config);
9294 assert(spy.calledOnce);
9297 it("should retrieve a node that is exactly the given index", () => {
9298 const spy = sinon.spy(context => {
9299 const node = context.getNodeByRangeIndex(13);
9301 assert.strictEqual(node.type, "Literal");
9302 assert.strictEqual(node.value, 6);
9314 rules: { "test/checker": "error" }
9317 linter.verify(code, config);
9318 assert(spy.calledOnce);
9321 it("should retrieve a node ending with the given index", () => {
9322 const spy = sinon.spy(context => {
9323 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
9335 rules: { "test/checker": "error" }
9338 linter.verify(code, config);
9339 assert(spy.calledOnce);
9342 it("should retrieve the deepest node containing the given index", () => {
9343 const spy = sinon.spy(context => {
9344 const node1 = context.getNodeByRangeIndex(14);
9346 assert.strictEqual(node1.type, "BinaryExpression");
9348 const node2 = context.getNodeByRangeIndex(3);
9350 assert.strictEqual(node2.type, "VariableDeclaration");
9362 rules: { "test/checker": "error" }
9365 linter.verify(code, config);
9366 assert(spy.calledOnce);
9369 it("should return null if the index is outside the range of any node", () => {
9370 const spy = sinon.spy(context => {
9371 const node1 = context.getNodeByRangeIndex(-1);
9373 assert.isNull(node1);
9375 const node2 = context.getNodeByRangeIndex(-99);
9377 assert.isNull(node2);
9389 rules: { "test/checker": "error" }
9392 linter.verify(code, config);
9393 assert(spy.calledOnce);
9397 describe("context.getScope()", () => {
9398 const codeToTestScope = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
9400 it("should retrieve the global scope correctly from a Program", () => {
9407 checker: context => {
9408 spy = sinon.spy(() => {
9409 const scope = context.getScope();
9411 assert.strictEqual(scope.type, "global");
9413 return { Program: spy };
9421 rules: { "test/checker": "error" }
9424 linter.verify(codeToTestScope, config);
9425 assert(spy && spy.calledOnce);
9428 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
9435 checker: context => {
9436 spy = sinon.spy(() => {
9437 const scope = context.getScope();
9439 assert.strictEqual(scope.type, "function");
9441 return { FunctionDeclaration: spy };
9449 rules: { "test/checker": "error" }
9452 linter.verify(codeToTestScope, config);
9453 assert(spy && spy.calledTwice);
9456 it("should retrieve the function scope correctly from a LabeledStatement", () => {
9463 checker: context => {
9464 spy = sinon.spy(() => {
9465 const scope = context.getScope();
9467 assert.strictEqual(scope.type, "function");
9468 assert.strictEqual(scope.block.id.name, "foo");
9470 return { LabeledStatement: spy };
9478 rules: { "test/checker": "error" }
9482 linter.verify(codeToTestScope, config);
9483 assert(spy && spy.calledOnce);
9486 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
9493 checker: context => {
9494 spy = sinon.spy(() => {
9495 const scope = context.getScope();
9497 assert.strictEqual(scope.type, "function");
9498 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
9501 return { ReturnStatement: spy };
9509 rules: { "test/checker": "error" }
9513 linter.verify(codeToTestScope, config);
9514 assert(spy && spy.calledOnce);
9517 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
9524 checker: context => {
9525 spy = sinon.spy(() => {
9526 const scope = context.getScope();
9528 assert.strictEqual(scope.type, "switch");
9529 assert.strictEqual(scope.block.type, "SwitchStatement");
9532 return { SwitchStatement: spy };
9540 rules: { "test/checker": "error" }
9543 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
9544 assert(spy && spy.calledOnce);
9547 it("should retrieve the function scope correctly from within a BlockStatement", () => {
9554 checker: context => {
9555 spy = sinon.spy(() => {
9556 const scope = context.getScope();
9558 assert.strictEqual(scope.type, "block");
9559 assert.strictEqual(scope.block.type, "BlockStatement");
9562 return { BlockStatement: spy };
9570 rules: { "test/checker": "error" }
9574 linter.verify("var x; {let y = 1}", config);
9575 assert(spy && spy.calledOnce);
9578 it("should retrieve the function scope correctly from within a nested block statement", () => {
9585 checker: context => {
9586 spy = sinon.spy(() => {
9587 const scope = context.getScope();
9589 assert.strictEqual(scope.type, "block");
9590 assert.strictEqual(scope.block.type, "BlockStatement");
9593 return { BlockStatement: spy };
9601 rules: { "test/checker": "error" }
9605 linter.verify("if (true) { let x = 1 }", config);
9606 assert(spy && spy.calledOnce);
9609 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
9616 checker: context => {
9617 spy = sinon.spy(() => {
9618 const scope = context.getScope();
9620 assert.strictEqual(scope.type, "function");
9621 assert.strictEqual(scope.block.type, "FunctionDeclaration");
9624 return { FunctionDeclaration: spy };
9632 rules: { "test/checker": "error" }
9636 linter.verify("function foo() {}", config);
9637 assert(spy && spy.calledOnce);
9640 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
9647 checker: context => {
9648 spy = sinon.spy(() => {
9649 const scope = context.getScope();
9651 assert.strictEqual(scope.type, "function");
9652 assert.strictEqual(scope.block.type, "FunctionExpression");
9655 return { FunctionExpression: spy };
9663 rules: { "test/checker": "error" }
9667 linter.verify("(function foo() {})();", config);
9668 assert(spy && spy.calledOnce);
9671 it("should retrieve the catch scope correctly from within a CatchClause", () => {
9678 checker: context => {
9679 spy = sinon.spy(() => {
9680 const scope = context.getScope();
9682 assert.strictEqual(scope.type, "catch");
9683 assert.strictEqual(scope.block.type, "CatchClause");
9686 return { CatchClause: spy };
9694 rules: { "test/checker": "error" }
9697 linter.verify("try {} catch (err) {}", config);
9698 assert(spy && spy.calledOnce);
9701 it("should retrieve module scope correctly from an ES6 module", () => {
9708 checker: context => {
9709 spy = sinon.spy(() => {
9710 const scope = context.getScope();
9712 assert.strictEqual(scope.type, "module");
9715 return { AssignmentExpression: spy };
9722 sourceType: "module"
9724 rules: { "test/checker": "error" }
9728 linter.verify("var foo = {}; foo.bar = 1;", config);
9729 assert(spy && spy.calledOnce);
9732 it("should retrieve function scope correctly when sourceType is commonjs", () => {
9739 checker: context => {
9740 spy = sinon.spy(() => {
9741 const scope = context.getScope();
9743 assert.strictEqual(scope.type, "function");
9746 return { AssignmentExpression: spy };
9753 sourceType: "commonjs"
9755 rules: { "test/checker": "error" }
9758 linter.verify("var foo = {}; foo.bar = 1;", config);
9759 assert(spy && spy.calledOnce);
9762 describe("Scope Internals", () => {
9765 * Get the scope on the node `astSelector
` specified.
9766 * @param {string} codeToEvaluate The source code to verify.
9767 * @param {string} astSelector The AST selector to get scope.
9768 * @param {number} [ecmaVersion=5] The ECMAScript version.
9769 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
9771 function getScope(codeToEvaluate, astSelector, ecmaVersion = 5) {
9778 "get-scope": context => ({
9779 [astSelector](node0) {
9781 scope = context.getScope();
9789 sourceType: "script"
9791 rules: { "test/get-scope": "error" }
9799 return { node, scope };
9802 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
9803 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
9805 assert.strictEqual(scope.type, "function");
9806 assert.strictEqual(scope.block, node);
9809 it("should return 'function' scope on FunctionExpression (ES5)", () => {
9810 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
9812 assert.strictEqual(scope.type, "function");
9813 assert.strictEqual(scope.block, node);
9816 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
9817 const { node, scope } = getScope("function f() {}", "BlockStatement");
9819 assert.strictEqual(scope.type, "function");
9820 assert.strictEqual(scope.block, node.parent);
9823 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
9824 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
9826 assert.strictEqual(scope.type, "function");
9827 assert.strictEqual(scope.block, node.parent);
9830 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
9831 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
9833 assert.strictEqual(scope.type, "function");
9834 assert.strictEqual(scope.block, node.parent.parent);
9835 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
9838 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
9839 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
9841 assert.strictEqual(scope.type, "block");
9842 assert.strictEqual(scope.upper.type, "function");
9843 assert.strictEqual(scope.block, node);
9844 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
9845 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
9848 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
9849 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
9851 assert.strictEqual(scope.type, "block");
9852 assert.strictEqual(scope.upper.type, "block");
9853 assert.strictEqual(scope.upper.upper.type, "function");
9854 assert.strictEqual(scope.block, node);
9855 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
9856 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
9857 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
9860 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
9861 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
9863 assert.strictEqual(scope.type, "function");
9864 assert.strictEqual(scope.block, node.parent.parent);
9865 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
9868 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
9869 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
9871 assert.strictEqual(scope.type, "switch");
9872 assert.strictEqual(scope.block, node);
9873 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
9876 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
9877 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
9879 assert.strictEqual(scope.type, "function");
9880 assert.strictEqual(scope.block, node.parent.parent.parent);
9881 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
9884 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
9885 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
9887 assert.strictEqual(scope.type, "switch");
9888 assert.strictEqual(scope.block, node.parent);
9889 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
9892 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
9893 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
9895 assert.strictEqual(scope.type, "catch");
9896 assert.strictEqual(scope.block, node);
9897 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
9900 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
9901 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
9903 assert.strictEqual(scope.type, "catch");
9904 assert.strictEqual(scope.block, node);
9905 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
9908 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
9909 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
9911 assert.strictEqual(scope.type, "catch");
9912 assert.strictEqual(scope.block, node.parent);
9913 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
9916 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
9917 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
9919 assert.strictEqual(scope.type, "block");
9920 assert.strictEqual(scope.block, node);
9921 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
9924 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
9925 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
9927 assert.strictEqual(scope.type, "function");
9928 assert.strictEqual(scope.block, node.parent.parent);
9929 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
9932 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
9933 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
9935 assert.strictEqual(scope.type, "for");
9936 assert.strictEqual(scope.block, node);
9937 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
9940 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
9941 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
9943 assert.strictEqual(scope.type, "function");
9944 assert.strictEqual(scope.block, node.parent.parent.parent);
9945 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
9948 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
9949 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
9951 assert.strictEqual(scope.type, "block");
9952 assert.strictEqual(scope.upper.type, "for");
9953 assert.strictEqual(scope.block, node);
9954 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
9955 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
9958 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
9959 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
9961 assert.strictEqual(scope.type, "function");
9962 assert.strictEqual(scope.block, node.parent.parent);
9963 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
9966 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
9967 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
9969 assert.strictEqual(scope.type, "for");
9970 assert.strictEqual(scope.block, node);
9971 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
9974 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
9975 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
9977 assert.strictEqual(scope.type, "function");
9978 assert.strictEqual(scope.block, node.parent.parent.parent);
9979 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
9982 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
9983 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
9985 assert.strictEqual(scope.type, "block");
9986 assert.strictEqual(scope.upper.type, "for");
9987 assert.strictEqual(scope.block, node);
9988 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
9989 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
9992 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
9993 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
9995 assert.strictEqual(scope.type, "for");
9996 assert.strictEqual(scope.block, node);
9997 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
10000 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
10001 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
10003 assert.strictEqual(scope.type, "block");
10004 assert.strictEqual(scope.upper.type, "for");
10005 assert.strictEqual(scope.block, node);
10006 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
10007 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
10010 it("should shadow the same name variable by the iteration variable.", () => {
10011 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
10013 assert.strictEqual(scope.type, "for");
10014 assert.strictEqual(scope.upper.type, "global");
10015 assert.strictEqual(scope.block, node);
10016 assert.strictEqual(scope.upper.variables[0].references.length, 0);
10017 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
10018 assert.strictEqual(scope.references[1].identifier, node.right);
10019 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
10023 describe("Variables and references", () => {
10026 "function foo() { b; }",
10048 scope = context.getScope();
10057 globals: { e: true, f: false },
10058 sourceType: "script",
10066 linter.verify(code, config);
10074 it("Scope#through should contain references of undefined variables", () => {
10075 assert.strictEqual(scope.through.length, 2);
10076 assert.strictEqual(scope.through[0].identifier.name, "a");
10077 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
10078 assert.strictEqual(scope.through[0].resolved, null);
10079 assert.strictEqual(scope.through[1].identifier.name, "b");
10080 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
10081 assert.strictEqual(scope.through[1].resolved, null);
10084 it("Scope#variables should contain global variables", () => {
10085 assert(scope.variables.some(v => v.name === "Object"));
10086 assert(scope.variables.some(v => v.name === "foo"));
10087 assert(scope.variables.some(v => v.name === "c"));
10088 assert(scope.variables.some(v => v.name === "d"));
10089 assert(scope.variables.some(v => v.name === "e"));
10090 assert(scope.variables.some(v => v.name === "f"));
10093 it("Scope#set should contain global variables", () => {
10094 assert(scope.set.get("Object"));
10095 assert(scope.set.get("foo"));
10096 assert(scope.set.get("c"));
10097 assert(scope.set.get("d"));
10098 assert(scope.set.get("e"));
10099 assert(scope.set.get("f"));
10102 it("Variables#references should contain their references", () => {
10103 assert.strictEqual(scope.set.get("Object").references.length, 1);
10104 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
10105 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
10106 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
10107 assert.strictEqual(scope.set.get("foo").references.length, 1);
10108 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
10109 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
10110 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
10111 assert.strictEqual(scope.set.get("c").references.length, 1);
10112 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
10113 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
10114 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
10115 assert.strictEqual(scope.set.get("d").references.length, 1);
10116 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
10117 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
10118 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
10119 assert.strictEqual(scope.set.get("e").references.length, 1);
10120 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
10121 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
10122 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
10123 assert.strictEqual(scope.set.get("f").references.length, 1);
10124 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
10125 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
10126 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
10129 it("Reference#resolved should be their variable", () => {
10130 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
10131 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
10132 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
10133 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
10134 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
10135 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
10140 describe("context.getDeclaredVariables(node)", () => {
10143 * Assert `context
.getDeclaredVariables(node
)` is valid.
10144 * @param {string} code A code to check.
10145 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
10146 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
10149 function verify(code, type, expectedNamesList) {
10158 * Assert `context
.getDeclaredVariables(node
)` is empty.
10159 * @param {ASTNode} node A node to check.
10162 function checkEmpty(node) {
10163 assert.strictEqual(0, context.getDeclaredVariables(node).length);
10166 Program: checkEmpty,
10167 EmptyStatement: checkEmpty,
10168 BlockStatement: checkEmpty,
10169 ExpressionStatement: checkEmpty,
10170 LabeledStatement: checkEmpty,
10171 BreakStatement: checkEmpty,
10172 ContinueStatement: checkEmpty,
10173 WithStatement: checkEmpty,
10174 SwitchStatement: checkEmpty,
10175 ReturnStatement: checkEmpty,
10176 ThrowStatement: checkEmpty,
10177 TryStatement: checkEmpty,
10178 WhileStatement: checkEmpty,
10179 DoWhileStatement: checkEmpty,
10180 ForStatement: checkEmpty,
10181 ForInStatement: checkEmpty,
10182 DebuggerStatement: checkEmpty,
10183 ThisExpression: checkEmpty,
10184 ArrayExpression: checkEmpty,
10185 ObjectExpression: checkEmpty,
10186 Property: checkEmpty,
10187 SequenceExpression: checkEmpty,
10188 UnaryExpression: checkEmpty,
10189 BinaryExpression: checkEmpty,
10190 AssignmentExpression: checkEmpty,
10191 UpdateExpression: checkEmpty,
10192 LogicalExpression: checkEmpty,
10193 ConditionalExpression: checkEmpty,
10194 CallExpression: checkEmpty,
10195 NewExpression: checkEmpty,
10196 MemberExpression: checkEmpty,
10197 SwitchCase: checkEmpty,
10198 Identifier: checkEmpty,
10199 Literal: checkEmpty,
10200 ForOfStatement: checkEmpty,
10201 ArrowFunctionExpression: checkEmpty,
10202 YieldExpression: checkEmpty,
10203 TemplateLiteral: checkEmpty,
10204 TaggedTemplateExpression: checkEmpty,
10205 TemplateElement: checkEmpty,
10206 ObjectPattern: checkEmpty,
10207 ArrayPattern: checkEmpty,
10208 RestElement: checkEmpty,
10209 AssignmentPattern: checkEmpty,
10210 ClassBody: checkEmpty,
10211 MethodDefinition: checkEmpty,
10212 MetaProperty: checkEmpty
10215 rule[type] = function(node) {
10216 const expectedNames = expectedNamesList.shift();
10217 const variables = context.getDeclaredVariables(node);
10219 assert(Array.isArray(expectedNames));
10220 assert(Array.isArray(variables));
10221 assert.strictEqual(expectedNames.length, variables.length);
10222 for (let i = variables.length - 1; i >= 0; i--) {
10223 assert.strictEqual(expectedNames[i], variables[i].name);
10234 sourceType: "module"
10241 linter.verify(code, config);
10243 // Check all expected names are asserted.
10244 assert.strictEqual(0, expectedNamesList.length);
10247 it("VariableDeclaration", () => {
10248 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
10249 const namesList = [
10252 ["g", "h", "i", "j", "k"],
10256 verify(code, "VariableDeclaration", namesList);
10259 it("VariableDeclaration (on for-in/of loop)", () => {
10261 // TDZ scope is created here, so tests to exclude those.
10262 const code = "\n for (var {a, x: [b], y: {c = 0}} in foo) {\n let g;\n }\n for (let {d, x: [e], y: {f = 0}} of foo) {\n let h;\n }\n ";
10263 const namesList = [
10270 verify(code, "VariableDeclaration", namesList);
10273 it("VariableDeclarator", () => {
10275 // TDZ scope is created here, so tests to exclude those.
10276 const code = "\n var {a, x: [b], y: {c = 0}} = foo;\n let {d, x: [e], y: {f = 0}} = foo;\n const {g, x: [h], y: {i = 0}} = foo, {j, k = function(z) { let l; }} = bar;\n ";
10277 const namesList = [
10285 verify(code, "VariableDeclarator", namesList);
10288 it("FunctionDeclaration", () => {
10289 const code = "\n function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n }\n function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n }\n ";
10290 const namesList = [
10291 ["foo", "a", "b", "c", "d", "e"],
10292 ["bar", "f", "g", "h", "i", "j"]
10295 verify(code, "FunctionDeclaration", namesList);
10298 it("FunctionExpression", () => {
10299 const code = "\n (function foo({a, x: [b], y: {c = 0}}, [d, e]) {\n let z;\n });\n (function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) {\n let z;\n });\n ";
10300 const namesList = [
10301 ["foo", "a", "b", "c", "d", "e"],
10302 ["bar", "f", "g", "h", "i", "j"],
10306 verify(code, "FunctionExpression", namesList);
10309 it("ArrowFunctionExpression", () => {
10310 const code = "\n (({a, x: [b], y: {c = 0}}, [d, e]) => {\n let z;\n });\n (({f, x: [g], y: {h = 0}}, [i, j]) => {\n let z;\n });\n ";
10311 const namesList = [
10312 ["a", "b", "c", "d", "e"],
10313 ["f", "g", "h", "i", "j"]
10316 verify(code, "ArrowFunctionExpression", namesList);
10319 it("ClassDeclaration", () => {
10320 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
10321 const namesList = [
10322 ["A", "A"], // outer scope's and inner scope's.
10326 verify(code, "ClassDeclaration", namesList);
10329 it("ClassExpression", () => {
10330 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
10331 const namesList = [
10336 verify(code, "ClassExpression", namesList);
10339 it("CatchClause", () => {
10340 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
10341 const namesList = [
10346 verify(code, "CatchClause", namesList);
10349 it("ImportDeclaration", () => {
10350 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10351 const namesList = [
10357 verify(code, "ImportDeclaration", namesList);
10360 it("ImportSpecifier", () => {
10361 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10362 const namesList = [
10367 verify(code, "ImportSpecifier", namesList);
10370 it("ImportDefaultSpecifier", () => {
10371 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10372 const namesList = [
10376 verify(code, "ImportDefaultSpecifier", namesList);
10379 it("ImportNamespaceSpecifier", () => {
10380 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
10381 const namesList = [
10385 verify(code, "ImportNamespaceSpecifier", namesList);
10389 describe("context.markVariableAsUsed()", () => {
10391 it("should mark variables in current scope as used", () => {
10392 const code = "var a = 1, b = 2;";
10399 checker: context => {
10400 spy = sinon.spy(() => {
10401 assert.isTrue(context.markVariableAsUsed("a"));
10403 const scope = context.getScope();
10405 assert.isTrue(getVariable(scope, "a").eslintUsed);
10406 assert.notOk(getVariable(scope, "b").eslintUsed);
10409 return { "Program:exit": spy };
10415 sourceType: "script"
10417 rules: { "test/checker": "error" }
10420 linter.verify(code, config);
10421 assert(spy && spy.calledOnce);
10424 it("should mark variables in function args as used", () => {
10425 const code = "function abc(a, b) { return 1; }";
10432 checker: context => {
10433 spy = sinon.spy(() => {
10434 assert.isTrue(context.markVariableAsUsed("a"));
10436 const scope = context.getScope();
10438 assert.isTrue(getVariable(scope, "a").eslintUsed);
10439 assert.notOk(getVariable(scope, "b").eslintUsed);
10442 return { ReturnStatement: spy };
10447 rules: { "test/checker": "error" }
10450 linter.verify(code, config);
10451 assert(spy && spy.calledOnce);
10454 it("should mark variables in higher scopes as used", () => {
10455 const code = "var a, b; function abc() { return 1; }";
10456 let returnSpy, exitSpy;
10462 checker: context => {
10463 returnSpy = sinon.spy(() => {
10464 assert.isTrue(context.markVariableAsUsed("a"));
10466 exitSpy = sinon.spy(() => {
10467 const scope = context.getScope();
10469 assert.isTrue(getVariable(scope, "a").eslintUsed);
10470 assert.notOk(getVariable(scope, "b").eslintUsed);
10473 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
10479 sourceType: "script"
10481 rules: { "test/checker": "error" }
10484 linter.verify(code, config);
10485 assert(returnSpy && returnSpy.calledOnce);
10486 assert(exitSpy && exitSpy.calledOnce);
10489 it("should mark variables as used when sourceType is commonjs", () => {
10490 const code = "var a = 1, b = 2;";
10497 checker: context => {
10498 spy = sinon.spy(() => {
10499 const globalScope = context.getScope(),
10500 childScope = globalScope.childScopes[0];
10502 assert.isTrue(context.markVariableAsUsed("a"), "Call to markVariableAsUsed should return true");
10504 assert.isTrue(getVariable(childScope, "a").eslintUsed, "'a' should be marked as used.");
10505 assert.isUndefined(getVariable(childScope, "b").eslintUsed, "'b' should be marked as used.");
10508 return { "Program:exit": spy };
10514 sourceType: "commonjs"
10516 rules: { "test/checker": "error" }
10519 linter.verify(code, config);
10520 assert(spy && spy.calledOnce, "Spy wasn't called.");
10523 it("should mark variables in modules as used", () => {
10524 const code = "var a = 1, b = 2;";
10531 checker: context => {
10532 spy = sinon.spy(() => {
10533 const globalScope = context.getScope(),
10534 childScope = globalScope.childScopes[0];
10536 assert.isTrue(context.markVariableAsUsed("a"));
10538 assert.isTrue(getVariable(childScope, "a").eslintUsed);
10539 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
10542 return { "Program:exit": spy };
10549 sourceType: "module"
10551 rules: { "test/checker": "error" }
10554 linter.verify(code, config);
10555 assert(spy && spy.calledOnce);
10558 it("should return false if the given variable is not found", () => {
10559 const code = "var a = 1, b = 2;";
10566 checker: context => {
10567 spy = sinon.spy(() => {
10568 assert.isFalse(context.markVariableAsUsed("c"));
10571 return { "Program:exit": spy };
10576 rules: { "test/checker": "error" }
10579 linter.verify(code, config);
10580 assert(spy && spy.calledOnce);
10584 describe("context.getCwd()", () => {
10585 const code = "a;\nb;";
10586 const baseConfig = { rules: { "test/checker": "error" } };
10588 it("should get cwd correctly in the context", () => {
10590 const linterWithOption = new Linter({ cwd, configType: "flat" });
10596 checker: context => {
10597 spy = sinon.spy(() => {
10598 assert.strictEqual(context.getCwd(), cwd);
10600 return { Program: spy };
10608 linterWithOption.verify(code, config);
10609 assert(spy && spy.calledOnce);
10612 it("should assign process.cwd() to it if cwd is undefined", () => {
10614 const linterWithOption = new Linter({ configType: "flat" });
10620 checker: context => {
10622 spy = sinon.spy(() => {
10623 assert.strictEqual(context.getCwd(), process.cwd());
10625 return { Program: spy };
10633 linterWithOption.verify(code, config);
10634 assert(spy && spy.calledOnce);
10637 it("should assign process.cwd() to it if the option is undefined", () => {
10643 checker: context => {
10645 spy = sinon.spy(() => {
10646 assert.strictEqual(context.getCwd(), process.cwd());
10648 return { Program: spy };
10656 linter.verify(code, config);
10657 assert(spy && spy.calledOnce);
10663 describe("Rule Severity", () => {
10665 it("rule should run as warning when set to 1 with a config array", () => {
10666 const ruleId = "semi",
10667 configs = createFlatConfigArray({
10668 files: ["**/*.js"],
10674 configs.normalizeSync();
10675 const messages = linter.verify("foo", configs, filename, true);
10676 const suppressedMessages = linter.getSuppressedMessages();
10678 assert.strictEqual(messages.length, 1, "Message length is wrong");
10679 assert.strictEqual(messages[0].ruleId, ruleId);
10681 assert.strictEqual(suppressedMessages.length, 0);
10684 it("rule should run as warning when set to 1 with a plain array", () => {
10685 const ruleId = "semi",
10687 files: ["**/*.js"],
10693 const messages = linter.verify("foo", configs, filename, true);
10694 const suppressedMessages = linter.getSuppressedMessages();
10696 assert.strictEqual(messages.length, 1, "Message length is wrong");
10697 assert.strictEqual(messages[0].ruleId, ruleId);
10699 assert.strictEqual(suppressedMessages.length, 0);
10702 it("rule should run as warning when set to 1 with an object", () => {
10703 const ruleId = "semi",
10705 files: ["**/*.js"],
10711 const messages = linter.verify("foo", config, filename, true);
10712 const suppressedMessages = linter.getSuppressedMessages();
10714 assert.strictEqual(messages.length, 1, "Message length is wrong");
10715 assert.strictEqual(messages[0].ruleId, ruleId);
10717 assert.strictEqual(suppressedMessages.length, 0);
10721 describe("Code with a hashbang comment", () => {
10722 const code = "#!bin/program\n\nvar foo;;";
10724 it("should preserve line numbers", () => {
10725 const config = { rules: { "no-extra-semi": 1 } };
10726 const messages = linter.verify(code, config);
10727 const suppressedMessages = linter.getSuppressedMessages();
10729 assert.strictEqual(messages.length, 1);
10730 assert.strictEqual(messages[0].ruleId, "no-extra-semi");
10731 assert.strictEqual(messages[0].nodeType, "EmptyStatement");
10732 assert.strictEqual(messages[0].line, 3);
10734 assert.strictEqual(suppressedMessages.length, 0);
10737 it("should have a comment with the hashbang in it", () => {
10738 const spy = sinon.spy(context => {
10739 const comments = context.getAllComments();
10741 assert.strictEqual(comments.length, 1);
10742 assert.strictEqual(comments[0].type, "Shebang");
10755 "test/checker": "error"
10759 linter.verify(code, config);
10760 assert(spy.calledOnce);
10764 describe("Options", () => {
10766 describe("filename", () => {
10767 it("should allow filename to be passed on options object", () => {
10768 const filenameChecker = sinon.spy(context => {
10769 assert.strictEqual(context.getFilename(), "foo.js");
10777 checker: filenameChecker
10782 "test/checker": "error"
10786 linter.verify("foo;", config, { filename: "foo.js" });
10787 assert(filenameChecker.calledOnce);
10790 it("should allow filename to be passed as third argument", () => {
10791 const filenameChecker = sinon.spy(context => {
10792 assert.strictEqual(context.getFilename(), "bar.js");
10800 checker: filenameChecker
10805 "test/checker": "error"
10809 linter.verify("foo;", config, "bar.js");
10810 assert(filenameChecker.calledOnce);
10813 it("should default filename to <input> when options object doesn't have filename", () => {
10814 const filenameChecker = sinon.spy(context => {
10815 assert.strictEqual(context.getFilename(), "<input>");
10823 checker: filenameChecker
10828 "test/checker": "error"
10832 linter.verify("foo;", config, {});
10833 assert(filenameChecker.calledOnce);
10836 it("should default filename to <input> when only two arguments are passed", () => {
10837 const filenameChecker = sinon.spy(context => {
10838 assert.strictEqual(context.getFilename(), "<input>");
10846 checker: filenameChecker
10851 "test/checker": "error"
10855 linter.verify("foo;", config);
10856 assert(filenameChecker.calledOnce);
10860 describe("physicalFilename", () => {
10861 it("should be same as `filename
` passed on options object, if no processors are used", () => {
10862 const physicalFilenameChecker = sinon.spy(context => {
10863 assert.strictEqual(context.getPhysicalFilename(), "foo.js");
10871 checker: physicalFilenameChecker
10876 "test/checker": "error"
10880 linter.verify("foo;", config, { filename: "foo.js" });
10881 assert(physicalFilenameChecker.calledOnce);
10884 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
10885 const physicalFilenameChecker = sinon.spy(context => {
10886 assert.strictEqual(context.getPhysicalFilename(), "<input>");
10894 checker: physicalFilenameChecker
10899 "test/checker": "error"
10903 linter.verify("foo;", config, {});
10904 assert(physicalFilenameChecker.calledOnce);
10907 it("should default physicalFilename to <input> when only two arguments are passed", () => {
10908 const physicalFilenameChecker = sinon.spy(context => {
10909 assert.strictEqual(context.getPhysicalFilename(), "<input>");
10917 checker: physicalFilenameChecker
10922 "test/checker": "error"
10926 linter.verify("foo;", config);
10927 assert(physicalFilenameChecker.calledOnce);
10933 describe("Inline Directives", () => {
10935 describe("/*global*/ Comments", () => {
10937 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
10939 it("variables should be available in global scope", () => {
10941 /*global a b:true c:false d:readable e:writeable Math:off */
10944 /* global ConfigGlobal : readable */
10952 checker: context => {
10953 spy = sinon.spy(() => {
10954 const scope = context.getScope();
10955 const a = getVariable(scope, "a"),
10956 b = getVariable(scope, "b"),
10957 c = getVariable(scope, "c"),
10958 d = getVariable(scope, "d"),
10959 e = getVariable(scope, "e"),
10960 f = getVariable(scope, "f"),
10961 mathGlobal = getVariable(scope, "Math"),
10962 arrayGlobal = getVariable(scope, "Array"),
10963 configGlobal = getVariable(scope, "ConfigGlobal");
10965 assert.strictEqual(a.name, "a");
10966 assert.strictEqual(a.writeable, false);
10967 assert.strictEqual(b.name, "b");
10968 assert.strictEqual(b.writeable, true);
10969 assert.strictEqual(c.name, "c");
10970 assert.strictEqual(c.writeable, false);
10971 assert.strictEqual(d.name, "d");
10972 assert.strictEqual(d.writeable, false);
10973 assert.strictEqual(e.name, "e");
10974 assert.strictEqual(e.writeable, true);
10975 assert.strictEqual(f.name, "f");
10976 assert.strictEqual(f.writeable, true);
10977 assert.strictEqual(mathGlobal, null);
10978 assert.strictEqual(arrayGlobal, null);
10979 assert.strictEqual(configGlobal.name, "ConfigGlobal");
10980 assert.strictEqual(configGlobal.writeable, false);
10983 return { Program: spy };
10988 rules: { "test/checker": "error" },
10990 globals: { Array: "off", ConfigGlobal: "writeable" }
10994 linter.verify(code, config);
10995 assert(spy && spy.calledOnce);
10999 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
11000 const code = "/* global a b : true c: false*/";
11002 it("variables should be available in global scope", () => {
11009 checker: context => {
11010 spy = sinon.spy(() => {
11011 const scope = context.getScope(),
11012 a = getVariable(scope, "a"),
11013 b = getVariable(scope, "b"),
11014 c = getVariable(scope, "c");
11016 assert.strictEqual(a.name, "a");
11017 assert.strictEqual(a.writeable, false);
11018 assert.strictEqual(b.name, "b");
11019 assert.strictEqual(b.writeable, true);
11020 assert.strictEqual(c.name, "c");
11021 assert.strictEqual(c.writeable, false);
11024 return { Program: spy };
11029 rules: { "test/checker": "error" }
11032 linter.verify(code, config);
11033 assert(spy && spy.calledOnce);
11037 describe("when evaluating code containing a line comment", () => {
11038 const code = "//global a \n function f() {}";
11040 it("should not introduce a global variable", () => {
11047 checker: context => {
11048 spy = sinon.spy(() => {
11049 const scope = context.getScope();
11051 assert.strictEqual(getVariable(scope, "a"), null);
11054 return { Program: spy };
11059 rules: { "test/checker": "error" }
11063 linter.verify(code, config);
11064 assert(spy && spy.calledOnce);
11068 describe("when evaluating code containing normal block comments", () => {
11069 const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
11071 it("should not introduce a global variable", () => {
11078 checker: context => {
11079 spy = sinon.spy(() => {
11080 const scope = context.getScope();
11082 assert.strictEqual(getVariable(scope, "a"), null);
11083 assert.strictEqual(getVariable(scope, "b"), null);
11084 assert.strictEqual(getVariable(scope, "foo"), null);
11085 assert.strictEqual(getVariable(scope, "c"), null);
11088 return { Program: spy };
11093 rules: { "test/checker": "error" }
11097 linter.verify(code, config);
11098 assert(spy && spy.calledOnce);
11102 it("should attach a \"/*global\" comment node to declared variables", () => {
11103 const code = "/* global foo */\n/* global bar, baz */";
11112 const scope = context.getScope();
11113 const sourceCode = context.getSourceCode();
11114 const comments = sourceCode.getAllComments();
11116 assert.strictEqual(2, comments.length);
11118 const foo = getVariable(scope, "foo");
11120 assert.strictEqual(foo.eslintExplicitGlobal, true);
11121 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
11123 const bar = getVariable(scope, "bar");
11125 assert.strictEqual(bar.eslintExplicitGlobal, true);
11126 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
11128 const baz = getVariable(scope, "baz");
11130 assert.strictEqual(baz.eslintExplicitGlobal, true);
11131 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
11140 rules: { "test/test": "error" }
11144 linter.verify(code, config);
11148 it("should report a linting error when a global is set to an invalid value", () => {
11149 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
11150 const suppressedMessages = linter.getSuppressedMessages();
11152 assert.deepStrictEqual(results, [
11156 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
11164 ruleId: "no-undef",
11165 messageId: "undef",
11167 message: "'foo' is not defined.",
11172 nodeType: "Identifier"
11176 assert.strictEqual(suppressedMessages.length, 0);
11181 describe("/*exported*/ Comments", () => {
11183 it("we should behave nicely when no matching variable is found", () => {
11184 const code = "/* exported horse */";
11185 const config = { rules: {} };
11187 linter.verify(code, config, filename, true);
11190 it("variables should be exported", () => {
11191 const code = "/* exported horse */\n\nvar horse = 'circus'";
11198 checker: context => {
11199 spy = sinon.spy(() => {
11200 const scope = context.getScope(),
11201 horse = getVariable(scope, "horse");
11203 assert.strictEqual(horse.eslintUsed, true);
11206 return { Program: spy };
11212 sourceType: "script"
11214 rules: { "test/checker": "error" }
11217 linter.verify(code, config);
11218 assert(spy && spy.calledOnce);
11221 it("undefined variables should not be exported", () => {
11222 const code = "/* exported horse */\n\nhorse = 'circus'";
11228 checker: context => {
11229 spy = sinon.spy(() => {
11230 const scope = context.getScope(),
11231 horse = getVariable(scope, "horse");
11233 assert.strictEqual(horse, null);
11236 return { Program: spy };
11242 sourceType: "script"
11244 rules: { "test/checker": "error" }
11247 linter.verify(code, config);
11248 assert(spy && spy.calledOnce);
11251 it("variables should be exported in strict mode", () => {
11252 const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'";
11258 checker: context => {
11259 spy = sinon.spy(() => {
11260 const scope = context.getScope(),
11261 horse = getVariable(scope, "horse");
11263 assert.strictEqual(horse.eslintUsed, true);
11266 return { Program: spy };
11272 sourceType: "script"
11274 rules: { "test/checker": "error" }
11277 linter.verify(code, config);
11278 assert(spy && spy.calledOnce);
11281 it("variables should not be exported in the es6 module environment", () => {
11282 const code = "/* exported horse */\nvar horse = 'circus'";
11288 checker: context => {
11289 spy = sinon.spy(() => {
11290 const scope = context.getScope(),
11291 horse = getVariable(scope, "horse");
11293 assert.strictEqual(horse, null); // there is no global scope at all
11296 return { Program: spy };
11303 sourceType: "module"
11305 rules: { "test/checker": "error" }
11308 linter.verify(code, config);
11309 assert(spy && spy.calledOnce);
11312 it("variables should not be exported when in a commonjs file", () => {
11313 const code = "/* exported horse */\nvar horse = 'circus'";
11319 checker: context => {
11320 spy = sinon.spy(() => {
11321 const scope = context.getScope(),
11322 horse = getVariable(scope, "horse");
11324 assert.strictEqual(horse, null); // there is no global scope at all
11327 return { Program: spy };
11333 sourceType: "commonjs"
11335 rules: { "test/checker": "error" }
11338 linter.verify(code, config);
11339 assert(spy && spy.calledOnce);
11343 describe("/*eslint*/ Comments", () => {
11344 describe("when evaluating code with comments to enable rules", () => {
11346 it("should report a violation", () => {
11347 const code = "/*eslint no-alert:1*/ alert('test');";
11348 const config = { rules: {} };
11350 const messages = linter.verify(code, config, filename);
11351 const suppressedMessages = linter.getSuppressedMessages();
11353 assert.strictEqual(messages.length, 1);
11354 assert.strictEqual(messages[0].ruleId, "no-alert");
11355 assert.strictEqual(messages[0].message, "Unexpected alert.");
11356 assert.include(messages[0].nodeType, "CallExpression");
11358 assert.strictEqual(suppressedMessages.length, 0);
11361 it("rules should not change initial config", () => {
11364 sourceType: "script"
11366 rules: { strict: 2 }
11368 const codeA = "/*eslint strict: 0*/ function bar() { return 2; }";
11369 const codeB = "function foo() { return 1; }";
11370 let messages = linter.verify(codeA, config, filename, false);
11371 let suppressedMessages = linter.getSuppressedMessages();
11373 assert.strictEqual(messages.length, 0);
11374 assert.strictEqual(suppressedMessages.length, 0);
11376 messages = linter.verify(codeB, config, filename, false);
11377 suppressedMessages = linter.getSuppressedMessages();
11379 assert.strictEqual(messages.length, 1);
11380 assert.strictEqual(suppressedMessages.length, 0);
11383 it("rules should not change initial config", () => {
11386 sourceType: "script"
11388 rules: { quotes: [2, "double"] }
11390 const codeA = "/*eslint quotes: 0*/ function bar() { return '2'; }";
11391 const codeB = "function foo() { return '1'; }";
11392 let messages = linter.verify(codeA, config, filename, false);
11393 let suppressedMessages = linter.getSuppressedMessages();
11395 assert.strictEqual(messages.length, 0);
11396 assert.strictEqual(suppressedMessages.length, 0);
11398 messages = linter.verify(codeB, config, filename, false);
11399 suppressedMessages = linter.getSuppressedMessages();
11401 assert.strictEqual(messages.length, 1);
11402 assert.strictEqual(suppressedMessages.length, 0);
11405 it("rules should not change initial config", () => {
11406 const config = { rules: { quotes: [2, "double"] } };
11407 const codeA = "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
11408 const codeB = "function foo() { return '1'; }";
11409 let messages = linter.verify(codeA, config, filename, false);
11410 let suppressedMessages = linter.getSuppressedMessages();
11412 assert.strictEqual(messages.length, 0);
11413 assert.strictEqual(suppressedMessages.length, 0);
11415 messages = linter.verify(codeB, config, filename, false);
11416 suppressedMessages = linter.getSuppressedMessages();
11418 assert.strictEqual(messages.length, 1);
11419 assert.strictEqual(suppressedMessages.length, 0);
11422 it("rules should not change initial config", () => {
11425 sourceType: "script"
11427 rules: { "no-unused-vars": [2, { vars: "all" }] }
11429 const codeA = "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
11430 const codeB = "var b = 55;";
11431 let messages = linter.verify(codeA, config, filename, false);
11432 let suppressedMessages = linter.getSuppressedMessages();
11434 assert.strictEqual(messages.length, 0);
11435 assert.strictEqual(suppressedMessages.length, 0);
11437 messages = linter.verify(codeB, config, filename, false);
11438 suppressedMessages = linter.getSuppressedMessages();
11440 assert.strictEqual(messages.length, 1);
11441 assert.strictEqual(suppressedMessages.length, 0);
11445 describe("when evaluating code with invalid comments to enable rules", () => {
11446 it("should report a violation when the config is not a valid rule configuration", () => {
11447 const messages = linter.verify("/*eslint no-alert:true*/ alert('test');", {});
11448 const suppressedMessages = linter.getSuppressedMessages();
11450 assert.deepStrictEqual(
11455 ruleId: "no-alert",
11456 message: "Configuration for rule \"no-alert\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed 'true').\n",
11466 assert.strictEqual(suppressedMessages.length, 0);
11469 it("should report a violation when the config violates a rule's schema", () => {
11470 const messages = linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {});
11471 const suppressedMessages = linter.getSuppressedMessages();
11473 assert.deepStrictEqual(
11478 ruleId: "no-alert",
11479 message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
11489 assert.strictEqual(suppressedMessages.length, 0);
11493 describe("when evaluating code with comments to disable rules", () => {
11495 it("should not report a violation", () => {
11496 const config = { rules: { "no-alert": 1 } };
11497 const messages = linter.verify("/*eslint no-alert:0*/ alert('test');", config, filename);
11498 const suppressedMessages = linter.getSuppressedMessages();
11500 assert.strictEqual(messages.length, 0);
11501 assert.strictEqual(suppressedMessages.length, 0);
11504 it("should report an error when disabling a non-existent rule in inline comment", () => {
11505 let code = "/*eslint foo:0*/ ;";
11506 let messages = linter.verify(code, {}, filename);
11507 let suppressedMessages = linter.getSuppressedMessages();
11509 assert.strictEqual(messages.length, 1, "/*eslint*/ comment should report problem.");
11510 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11511 assert.strictEqual(suppressedMessages.length, 0);
11513 code = "/*eslint-disable foo*/ ;";
11514 messages = linter.verify(code, {}, filename);
11515 suppressedMessages = linter.getSuppressedMessages();
11516 assert.strictEqual(messages.length, 1, "/*eslint-disable*/ comment should report problem.");
11517 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11518 assert.strictEqual(suppressedMessages.length, 0);
11520 code = "/*eslint-disable-line foo*/ ;";
11521 messages = linter.verify(code, {}, filename);
11522 suppressedMessages = linter.getSuppressedMessages();
11523 assert.strictEqual(messages.length, 1, "/*eslint-disable-line*/ comment should report problem.");
11524 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11525 assert.strictEqual(suppressedMessages.length, 0);
11527 code = "/*eslint-disable-next-line foo*/ ;";
11528 messages = linter.verify(code, {}, filename);
11529 suppressedMessages = linter.getSuppressedMessages();
11530 assert.strictEqual(messages.length, 1, "/*eslint-disable-next-line*/ comment should report problem.");
11531 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
11532 assert.strictEqual(suppressedMessages.length, 0);
11535 it("should not report an error, when disabling a non-existent rule in config", () => {
11536 const messages = linter.verify("", { rules: { foo: 0 } }, filename);
11537 const suppressedMessages = linter.getSuppressedMessages();
11539 assert.strictEqual(messages.length, 0);
11540 assert.strictEqual(suppressedMessages.length, 0);
11543 it("should throw an error when a non-existent rule in config", () => {
11544 assert.throws(() => {
11545 linter.verify("", { rules: { foo: 1 } }, filename);
11546 }, /Key "rules": Key "foo":/u);
11548 assert.throws(() => {
11549 linter.verify("", { rules: { foo: 2 } }, filename);
11550 }, /Key "rules": Key "foo":/u);
11555 describe("when evaluating code with comments to enable multiple rules", () => {
11556 const code = "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
11558 it("should report a violation", () => {
11559 const config = { rules: {} };
11561 const messages = linter.verify(code, config, filename);
11562 const suppressedMessages = linter.getSuppressedMessages();
11564 assert.strictEqual(messages.length, 2);
11565 assert.strictEqual(messages[0].ruleId, "no-alert");
11566 assert.strictEqual(messages[0].message, "Unexpected alert.");
11567 assert.include(messages[0].nodeType, "CallExpression");
11568 assert.strictEqual(messages[1].ruleId, "no-console");
11570 assert.strictEqual(suppressedMessages.length, 0);
11574 describe("when evaluating code with comments to enable and disable multiple rules", () => {
11575 const code = "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
11577 it("should report a violation", () => {
11578 const config = { rules: { "no-console": 1, "no-alert": 0 } };
11580 const messages = linter.verify(code, config, filename);
11581 const suppressedMessages = linter.getSuppressedMessages();
11583 assert.strictEqual(messages.length, 1);
11584 assert.strictEqual(messages[0].ruleId, "no-alert");
11585 assert.strictEqual(messages[0].message, "Unexpected alert.");
11586 assert.include(messages[0].nodeType, "CallExpression");
11588 assert.strictEqual(suppressedMessages.length, 0);
11592 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
11601 "test-rule"(context) {
11604 if (node.value === "trigger violation") {
11605 context.report(node, "Reporting violation.");
11617 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
11618 const config = { ...baseConfig, rules: {} };
11619 const code = "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
11621 const messages = linter.verify(code, config, filename);
11622 const suppressedMessages = linter.getSuppressedMessages();
11624 assert.strictEqual(messages.length, 0);
11625 assert.strictEqual(suppressedMessages.length, 0);
11628 it("should not report a violation when inline comment disables plugin rule", () => {
11629 const code = "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
11630 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 1 } };
11632 const messages = linter.verify(code, config, filename);
11633 const suppressedMessages = linter.getSuppressedMessages();
11635 assert.strictEqual(messages.length, 0);
11636 assert.strictEqual(suppressedMessages.length, 0);
11639 it("should report a violation when the report is right before the comment", () => {
11640 const code = " /* eslint-disable */ ";
11646 checker: context => ({
11648 context.report({ loc: { line: 1, column: 0 }, message: "foo" });
11655 "test/checker": "error"
11659 const problems = linter.verify(code, config);
11660 const suppressedMessages = linter.getSuppressedMessages();
11662 assert.strictEqual(problems.length, 1);
11663 assert.strictEqual(problems[0].message, "foo");
11665 assert.strictEqual(suppressedMessages.length, 0);
11668 it("should not report a violation when the report is right at the start of the comment", () => {
11669 const code = " /* eslint-disable */ ";
11675 checker: context => ({
11677 context.report({ loc: { line: 1, column: 1 }, message: "foo" });
11684 "test/checker": "error"
11688 const problems = linter.verify(code, config);
11689 const suppressedMessages = linter.getSuppressedMessages();
11691 assert.strictEqual(problems.length, 0);
11693 assert.strictEqual(suppressedMessages.length, 1);
11694 assert.strictEqual(suppressedMessages[0].message, "foo");
11695 assert.deepStrictEqual(suppressedMessages[0].suppressions, [{ kind: "directive", justification: "" }]);
11698 it("rules should not change initial config", () => {
11699 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 2 } };
11700 const codeA = "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
11701 const codeB = "var a = \"trigger violation\";";
11702 let messages = linter.verify(codeA, config, filename, false);
11703 let suppressedMessages = linter.getSuppressedMessages();
11705 assert.strictEqual(messages.length, 0);
11706 assert.strictEqual(suppressedMessages.length, 0);
11708 messages = linter.verify(codeB, config, filename, false);
11709 suppressedMessages = linter.getSuppressedMessages();
11711 assert.strictEqual(messages.length, 1);
11712 assert.strictEqual(suppressedMessages.length, 0);
11716 describe("when evaluating code with comments to enable and disable all reporting", () => {
11717 it("should report a violation", () => {
11720 "/*eslint-disable */",
11722 "/*eslint-enable */",
11725 const config = { rules: { "no-alert": 1 } };
11727 const messages = linter.verify(code, config, filename);
11728 const suppressedMessages = linter.getSuppressedMessages();
11730 assert.strictEqual(messages.length, 1);
11731 assert.strictEqual(messages[0].ruleId, "no-alert");
11732 assert.strictEqual(messages[0].message, "Unexpected alert.");
11733 assert.include(messages[0].nodeType, "CallExpression");
11734 assert.strictEqual(messages[0].line, 4);
11736 assert.strictEqual(suppressedMessages.length, 1);
11737 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
11738 assert.strictEqual(suppressedMessages[0].line, 2);
11741 it("should not report a violation", () => {
11743 "/*eslint-disable */",
11747 const config = { rules: { "no-alert": 1 } };
11749 const messages = linter.verify(code, config, filename);
11750 const suppressedMessages = linter.getSuppressedMessages();
11752 assert.strictEqual(messages.length, 0);
11753 assert.strictEqual(suppressedMessages.length, 2);
11756 it("should not report a violation", () => {
11758 " alert('test1');/*eslint-disable */\n",
11760 " alert('test');\n",
11761 "/*eslint-enable */alert('test2');"
11763 const config = { rules: { "no-alert": 1 } };
11765 const messages = linter.verify(code, config, filename);
11766 const suppressedMessages = linter.getSuppressedMessages();
11768 assert.strictEqual(messages.length, 2);
11769 assert.strictEqual(messages[0].column, 21);
11770 assert.strictEqual(messages[1].column, 19);
11772 assert.strictEqual(suppressedMessages.length, 2);
11773 assert.strictEqual(suppressedMessages[0].column, 1);
11774 assert.strictEqual(suppressedMessages[1].column, 56);
11777 it("should report a violation", () => {
11780 "/*eslint-disable */",
11782 "/*eslint-disable */",
11784 "/*eslint-enable*/",
11786 "/*eslint-enable*/"
11789 const config = { rules: { "no-alert": 1 } };
11791 const messages = linter.verify(code, config, filename);
11792 const suppressedMessages = linter.getSuppressedMessages();
11794 assert.strictEqual(messages.length, 1);
11795 assert.strictEqual(suppressedMessages.length, 2);
11799 it("should not report a violation", () => {
11801 "/*eslint-disable */",
11802 "(function(){ var b = 44;})()",
11803 "/*eslint-enable */;any();"
11806 const config = { rules: { "no-unused-vars": 1 } };
11808 const messages = linter.verify(code, config, filename);
11809 const suppressedMessages = linter.getSuppressedMessages();
11811 assert.strictEqual(messages.length, 0);
11813 assert.strictEqual(suppressedMessages.length, 1);
11814 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
11817 it("should not report a violation", () => {
11819 "(function(){ /*eslint-disable */ var b = 44;})()",
11820 "/*eslint-enable */;any();"
11823 const config = { rules: { "no-unused-vars": 1 } };
11825 const messages = linter.verify(code, config, filename);
11826 const suppressedMessages = linter.getSuppressedMessages();
11828 assert.strictEqual(messages.length, 0);
11830 assert.strictEqual(suppressedMessages.length, 1);
11831 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
11835 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
11836 const code = "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
11838 it("should report a violation", () => {
11839 const config = { rules: { "no-console": 1, "no-alert": 0 } };
11841 const messages = linter.verify(code, config, filename);
11842 const suppressedMessages = linter.getSuppressedMessages();
11844 assert.strictEqual(messages.length, 1);
11845 assert.strictEqual(messages[0].ruleId, "no-alert");
11846 assert.strictEqual(messages[0].message, "Unexpected alert.");
11847 assert.include(messages[0].nodeType, "CallExpression");
11849 assert.strictEqual(suppressedMessages.length, 0);
11853 describe("when evaluating code with comments to enable configurable rule", () => {
11854 const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');";
11856 it("should report a violation", () => {
11857 const config = { rules: { quotes: [2, "single"] } };
11859 const messages = linter.verify(code, config, filename);
11860 const suppressedMessages = linter.getSuppressedMessages();
11862 assert.strictEqual(messages.length, 1);
11863 assert.strictEqual(messages[0].ruleId, "quotes");
11864 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
11865 assert.include(messages[0].nodeType, "Literal");
11867 assert.strictEqual(suppressedMessages.length, 0);
11871 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
11872 const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
11874 it("should report a violation", () => {
11875 const config = { rules: { quotes: [2, "single"] } };
11877 const messages = linter.verify(code, config, filename);
11878 const suppressedMessages = linter.getSuppressedMessages();
11880 assert.strictEqual(messages.length, 1);
11881 assert.strictEqual(messages[0].ruleId, "quotes");
11882 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
11883 assert.include(messages[0].nodeType, "Literal");
11885 assert.strictEqual(suppressedMessages.length, 0);
11889 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
11890 it("should report a violation", () => {
11891 const code = "/*eslint no-alert:'1'*/ alert('test');";
11893 const config = { rules: { "no-alert": 1 } };
11895 const messages = linter.verify(code, config, filename);
11896 const suppressedMessages = linter.getSuppressedMessages();
11898 assert.strictEqual(messages.length, 2);
11901 * Incorrectly formatted comment threw error;
11902 * message from caught exception
11903 * may differ amongst UAs, so verifying
11904 * first part only as defined in the
11905 * parseJsonConfig function in lib/eslint.js
11907 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":'1'':/u);
11908 assert.strictEqual(messages[0].line, 1);
11909 assert.strictEqual(messages[0].column, 1);
11911 assert.strictEqual(messages[1].ruleId, "no-alert");
11912 assert.strictEqual(messages[1].message, "Unexpected alert.");
11913 assert.include(messages[1].nodeType, "CallExpression");
11915 assert.strictEqual(suppressedMessages.length, 0);
11918 it("should report a violation", () => {
11919 const code = "/*eslint no-alert:abc*/ alert('test');";
11921 const config = { rules: { "no-alert": 1 } };
11923 const messages = linter.verify(code, config, filename);
11924 const suppressedMessages = linter.getSuppressedMessages();
11926 assert.strictEqual(messages.length, 2);
11929 * Incorrectly formatted comment threw error;
11930 * message from caught exception
11931 * may differ amongst UAs, so verifying
11932 * first part only as defined in the
11933 * parseJsonConfig function in lib/eslint.js
11935 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":abc':/u);
11936 assert.strictEqual(messages[0].line, 1);
11937 assert.strictEqual(messages[0].column, 1);
11939 assert.strictEqual(messages[1].ruleId, "no-alert");
11940 assert.strictEqual(messages[1].message, "Unexpected alert.");
11941 assert.include(messages[1].nodeType, "CallExpression");
11943 assert.strictEqual(suppressedMessages.length, 0);
11946 it("should report a violation", () => {
11947 const code = "/*eslint no-alert:0 2*/ alert('test');";
11949 const config = { rules: { "no-alert": 1 } };
11951 const messages = linter.verify(code, config, filename);
11952 const suppressedMessages = linter.getSuppressedMessages();
11954 assert.strictEqual(messages.length, 2);
11957 * Incorrectly formatted comment threw error;
11958 * message from caught exception
11959 * may differ amongst UAs, so verifying
11960 * first part only as defined in the
11961 * parseJsonConfig function in lib/eslint.js
11963 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":0 2':/u);
11964 assert.strictEqual(messages[0].line, 1);
11965 assert.strictEqual(messages[0].column, 1);
11967 assert.strictEqual(messages[1].ruleId, "no-alert");
11968 assert.strictEqual(messages[1].message, "Unexpected alert.");
11969 assert.include(messages[1].nodeType, "CallExpression");
11971 assert.strictEqual(suppressedMessages.length, 0);
11975 describe("when evaluating code with comments which have colon in its value", () => {
11976 const code = String.raw`
11977 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
11981 it("should not parse errors, should report a violation", () => {
11982 const messages = linter.verify(code, {}, filename);
11983 const suppressedMessages = linter.getSuppressedMessages();
11985 assert.strictEqual(messages.length, 1);
11986 assert.strictEqual(messages[0].ruleId, "max-len");
11987 assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100.");
11988 assert.include(messages[0].nodeType, "Program");
11990 assert.strictEqual(suppressedMessages.length, 0);
11994 describe("when evaluating code with comments that contain escape sequences", () => {
11995 const code = String.raw`
11996 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
11997 console
.log("test");
11998 consolexlog("test2");
12002 it("should validate correctly", () => {
12003 const config = { rules: {} };
12004 const messages = linter.verify(code, config, filename);
12005 const [message1, message2] = messages;
12006 const suppressedMessages = linter.getSuppressedMessages();
12008 assert.strictEqual(messages.length, 2);
12009 assert.strictEqual(message1.ruleId, "max-len");
12010 assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1.");
12011 assert.strictEqual(message1.line, 4);
12012 assert.strictEqual(message1.column, 1);
12013 assert.include(message1.nodeType, "Program");
12014 assert.strictEqual(message2.ruleId, "max-len");
12015 assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1.");
12016 assert.strictEqual(message2.line, 5);
12017 assert.strictEqual(message2.column, 1);
12018 assert.include(message2.nodeType, "Program");
12020 assert.strictEqual(suppressedMessages.length, 0);
12026 describe("/*eslint-disable*/ and /*eslint-enable*/", () => {
12027 it("should report a violation", () => {
12029 "/*eslint-disable no-alert */",
12031 "console.log('test');" // here
12033 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12035 const messages = linter.verify(code, config, filename);
12036 const suppressedMessages = linter.getSuppressedMessages();
12038 assert.strictEqual(messages.length, 1);
12039 assert.strictEqual(messages[0].ruleId, "no-console");
12041 assert.strictEqual(suppressedMessages.length, 1);
12042 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12045 it("should report no violation", () => {
12047 "/* eslint-disable quotes */",
12048 "console.log(\"foo\");",
12049 "/* eslint-enable quotes */"
12051 const config = { rules: { quotes: 2 } };
12053 const messages = linter.verify(code, config, filename);
12054 const suppressedMessages = linter.getSuppressedMessages();
12056 assert.strictEqual(messages.length, 0);
12057 assert.strictEqual(suppressedMessages.length, 0);
12060 it("should report a violation", () => {
12062 "/*eslint-disable no-alert, no-console */",
12064 "console.log('test');",
12065 "/*eslint-enable*/",
12067 "alert('test');", // here
12068 "console.log('test');" // here
12070 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12072 const messages = linter.verify(code, config, filename);
12073 const suppressedMessages = linter.getSuppressedMessages();
12075 assert.strictEqual(messages.length, 2);
12076 assert.strictEqual(messages[0].ruleId, "no-alert");
12077 assert.strictEqual(messages[0].line, 5);
12078 assert.strictEqual(messages[1].ruleId, "no-console");
12079 assert.strictEqual(messages[1].line, 6);
12081 assert.strictEqual(suppressedMessages.length, 2);
12082 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12083 assert.strictEqual(suppressedMessages[0].line, 2);
12084 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12085 assert.strictEqual(suppressedMessages[1].line, 3);
12088 it("should report a violation", () => {
12090 "/*eslint-disable no-alert */",
12092 "console.log('test');", // here
12093 "/*eslint-enable no-console */",
12096 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12098 const messages = linter.verify(code, config, filename);
12099 const suppressedMessages = linter.getSuppressedMessages();
12101 assert.strictEqual(messages.length, 1);
12102 assert.strictEqual(messages[0].ruleId, "no-console");
12104 assert.strictEqual(suppressedMessages.length, 2);
12105 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12106 assert.strictEqual(suppressedMessages[0].line, 2);
12107 assert.strictEqual(suppressedMessages[1].ruleId, "no-alert");
12108 assert.strictEqual(suppressedMessages[1].line, 5);
12112 it("should report a violation", () => {
12114 "/*eslint-disable no-alert, no-console */",
12116 "console.log('test');",
12117 "/*eslint-enable no-alert*/",
12119 "alert('test');", // here
12120 "console.log('test');"
12122 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12124 const messages = linter.verify(code, config, filename);
12125 const suppressedMessages = linter.getSuppressedMessages();
12127 assert.strictEqual(messages.length, 1);
12128 assert.strictEqual(messages[0].ruleId, "no-alert");
12129 assert.strictEqual(messages[0].line, 5);
12131 assert.strictEqual(suppressedMessages.length, 3);
12132 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12133 assert.strictEqual(suppressedMessages[0].line, 2);
12134 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12135 assert.strictEqual(suppressedMessages[1].line, 3);
12136 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12137 assert.strictEqual(suppressedMessages[2].line, 6);
12141 it("should report a violation", () => {
12143 "/*eslint-disable no-alert */",
12145 "/*eslint-disable no-console */",
12147 "console.log('test');",
12148 "/*eslint-enable */",
12150 "alert('test');", // here
12151 "console.log('test');", // here
12153 "/*eslint-enable */",
12155 "alert('test');", // here
12156 "console.log('test');", // here
12158 "/*eslint-enable*/"
12160 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12162 const messages = linter.verify(code, config, filename);
12163 const suppressedMessages = linter.getSuppressedMessages();
12165 assert.strictEqual(messages.length, 4);
12166 assert.strictEqual(messages[0].ruleId, "no-alert");
12167 assert.strictEqual(messages[0].line, 6);
12168 assert.strictEqual(messages[1].ruleId, "no-console");
12169 assert.strictEqual(messages[1].line, 7);
12170 assert.strictEqual(messages[2].ruleId, "no-alert");
12171 assert.strictEqual(messages[2].line, 9);
12172 assert.strictEqual(messages[3].ruleId, "no-console");
12173 assert.strictEqual(messages[3].line, 10);
12175 assert.strictEqual(suppressedMessages.length, 2);
12176 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12177 assert.strictEqual(suppressedMessages[0].line, 3);
12178 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12179 assert.strictEqual(suppressedMessages[1].line, 4);
12182 it("should report a violation", () => {
12184 "/*eslint-disable no-alert, no-console */",
12186 "console.log('test');",
12188 "/*eslint-enable no-alert */",
12190 "alert('test');", // here
12191 "console.log('test');",
12193 "/*eslint-enable no-console */",
12195 "alert('test');", // here
12196 "console.log('test');", // here
12197 "/*eslint-enable no-console */"
12199 const config = { rules: { "no-alert": 1, "no-console": 1 } };
12201 const messages = linter.verify(code, config, filename);
12202 const suppressedMessages = linter.getSuppressedMessages();
12204 assert.strictEqual(messages.length, 3);
12205 assert.strictEqual(messages[0].ruleId, "no-alert");
12206 assert.strictEqual(messages[0].line, 5);
12207 assert.strictEqual(messages[1].ruleId, "no-alert");
12208 assert.strictEqual(messages[1].line, 8);
12209 assert.strictEqual(messages[2].ruleId, "no-console");
12210 assert.strictEqual(messages[2].line, 9);
12212 assert.strictEqual(suppressedMessages.length, 3);
12213 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12214 assert.strictEqual(suppressedMessages[0].line, 2);
12215 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12216 assert.strictEqual(suppressedMessages[1].line, 3);
12217 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12218 assert.strictEqual(suppressedMessages[2].line, 6);
12221 it("should report a violation when severity is warn", () => {
12223 "/*eslint-disable no-alert, no-console */",
12225 "console.log('test');",
12227 "/*eslint-enable no-alert */",
12229 "alert('test');", // here
12230 "console.log('test');",
12232 "/*eslint-enable no-console */",
12234 "alert('test');", // here
12235 "console.log('test');", // here
12236 "/*eslint-enable no-console */"
12238 const config = { rules: { "no-alert": "warn", "no-console": "warn" } };
12240 const messages = linter.verify(code, config, filename);
12241 const suppressedMessages = linter.getSuppressedMessages();
12243 assert.strictEqual(messages.length, 3);
12244 assert.strictEqual(messages[0].ruleId, "no-alert");
12245 assert.strictEqual(messages[0].line, 5);
12246 assert.strictEqual(messages[1].ruleId, "no-alert");
12247 assert.strictEqual(messages[1].line, 8);
12248 assert.strictEqual(messages[2].ruleId, "no-console");
12249 assert.strictEqual(messages[2].line, 9);
12251 assert.strictEqual(suppressedMessages.length, 3);
12252 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12253 assert.strictEqual(suppressedMessages[0].line, 2);
12254 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12255 assert.strictEqual(suppressedMessages[1].line, 3);
12256 assert.strictEqual(suppressedMessages[2].ruleId, "no-console");
12257 assert.strictEqual(suppressedMessages[2].line, 6);
12260 it("should report no violation", () => {
12262 "/*eslint-disable no-unused-vars */",
12263 "var foo; // eslint-disable-line no-unused-vars",
12265 "/* eslint-enable no-unused-vars */" // here
12267 const config = { rules: { "no-unused-vars": 2 } };
12269 const messages = linter.verify(code, config, filename);
12270 const suppressedMessages = linter.getSuppressedMessages();
12272 assert.strictEqual(messages.length, 0);
12274 assert.strictEqual(suppressedMessages.length, 2);
12275 assert.strictEqual(suppressedMessages[0].ruleId, "no-unused-vars");
12276 assert.strictEqual(suppressedMessages[0].line, 2);
12277 assert.strictEqual(suppressedMessages[1].ruleId, "no-unused-vars");
12278 assert.strictEqual(suppressedMessages[1].line, 3);
12283 describe("/*eslint-disable-line*/", () => {
12285 it("should report a violation", () => {
12287 "alert('test'); // eslint-disable-line no-alert",
12288 "console.log('test');" // here
12297 const messages = linter.verify(code, config, filename);
12298 const suppressedMessages = linter.getSuppressedMessages();
12300 assert.strictEqual(messages.length, 1);
12301 assert.strictEqual(messages[0].ruleId, "no-console");
12302 assert.strictEqual(messages[0].line, 2);
12304 assert.strictEqual(suppressedMessages.length, 1);
12305 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12306 assert.strictEqual(suppressedMessages[0].line, 1);
12309 it("should report a violation", () => {
12311 "alert('test'); // eslint-disable-line no-alert",
12312 "console.log('test'); // eslint-disable-line no-console",
12313 "alert('test');" // here
12322 const messages = linter.verify(code, config, filename);
12323 const suppressedMessages = linter.getSuppressedMessages();
12325 assert.strictEqual(messages.length, 1);
12326 assert.strictEqual(messages[0].ruleId, "no-alert");
12327 assert.strictEqual(messages[0].line, 3);
12329 assert.strictEqual(suppressedMessages.length, 2);
12330 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12331 assert.strictEqual(suppressedMessages[0].line, 1);
12332 assert.strictEqual(suppressedMessages[1].ruleId, "no-console");
12333 assert.strictEqual(suppressedMessages[1].line, 2);
12336 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
12338 "/* eslint-disable-line",
12340 "*/ console.log('test');" // here
12348 const messages = linter.verify(code, config, filename);
12349 const suppressedMessages = linter.getSuppressedMessages();
12351 assert.strictEqual(messages.length, 2);
12352 assert.strictEqual(messages[1].ruleId, "no-console");
12354 assert.strictEqual(suppressedMessages.length, 0);
12357 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
12359 "alert('test'); /* eslint-disable-line ",
12368 const messages = linter.verify(code, config);
12369 const suppressedMessages = linter.getSuppressedMessages();
12371 assert.deepStrictEqual(messages, [
12373 ruleId: "no-alert",
12379 message: "Unexpected alert.",
12380 messageId: "unexpected",
12381 nodeType: "CallExpression"
12386 message: "eslint-disable-line comment should not span multiple lines.",
12395 assert.strictEqual(suppressedMessages.length, 0);
12398 it("should not report a violation for eslint-disable-line in block comment", () => {
12400 "alert('test'); // eslint-disable-line no-alert",
12401 "alert('test'); /*eslint-disable-line no-alert*/"
12409 const messages = linter.verify(code, config, filename);
12410 const suppressedMessages = linter.getSuppressedMessages();
12412 assert.strictEqual(messages.length, 0);
12413 assert.strictEqual(suppressedMessages.length, 2);
12416 it("should not report a violation", () => {
12418 "alert('test'); // eslint-disable-line no-alert",
12419 "console.log('test'); // eslint-disable-line no-console"
12428 const messages = linter.verify(code, config, filename);
12429 const suppressedMessages = linter.getSuppressedMessages();
12431 assert.strictEqual(messages.length, 0);
12432 assert.strictEqual(suppressedMessages.length, 2);
12435 it("should not report a violation", () => {
12437 "alert('test') // eslint-disable-line no-alert, quotes, semi",
12438 "console.log('test'); // eslint-disable-line"
12443 quotes: [1, "double"],
12444 semi: [1, "always"],
12449 const messages = linter.verify(code, config, filename);
12450 const suppressedMessages = linter.getSuppressedMessages();
12452 assert.strictEqual(messages.length, 0);
12453 assert.strictEqual(suppressedMessages.length, 5);
12456 it("should not report a violation", () => {
12458 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
12459 "console.log('test'); /* eslint-disable-line */"
12464 quotes: [1, "double"],
12465 semi: [1, "always"],
12470 const messages = linter.verify(code, config, filename);
12471 const suppressedMessages = linter.getSuppressedMessages();
12473 assert.strictEqual(messages.length, 0);
12474 assert.strictEqual(suppressedMessages.length, 5);
12477 it("should ignore violations of multiple rules when specified in mixed comments", () => {
12479 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
12484 quotes: [1, "single"]
12487 const messages = linter.verify(code, config, filename);
12488 const suppressedMessages = linter.getSuppressedMessages();
12490 assert.strictEqual(messages.length, 0);
12492 assert.strictEqual(suppressedMessages.length, 2);
12493 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12494 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12497 it("should report no violation", () => {
12499 "var foo1; // eslint-disable-line no-unused-vars",
12500 "var foo2; // eslint-disable-line no-unused-vars",
12501 "var foo3; // eslint-disable-line no-unused-vars",
12502 "var foo4; // eslint-disable-line no-unused-vars",
12503 "var foo5; // eslint-disable-line no-unused-vars"
12505 const config = { rules: { "no-unused-vars": 2 } };
12507 const messages = linter.verify(code, config, filename);
12508 const suppressedMessages = linter.getSuppressedMessages();
12510 assert.strictEqual(messages.length, 0);
12511 assert.strictEqual(suppressedMessages.length, 5);
12516 describe("/*eslint-disable-next-line*/", () => {
12517 it("should ignore violation of specified rule on next line", () => {
12519 "// eslint-disable-next-line no-alert",
12521 "console.log('test');"
12529 const messages = linter.verify(code, config, filename);
12530 const suppressedMessages = linter.getSuppressedMessages();
12532 assert.strictEqual(messages.length, 1);
12533 assert.strictEqual(messages[0].ruleId, "no-console");
12535 assert.strictEqual(suppressedMessages.length, 1);
12536 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12539 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
12541 "/* eslint-disable-next-line no-alert */",
12543 "console.log('test');"
12551 const messages = linter.verify(code, config, filename);
12552 const suppressedMessages = linter.getSuppressedMessages();
12554 assert.strictEqual(messages.length, 1);
12555 assert.strictEqual(messages[0].ruleId, "no-console");
12557 assert.strictEqual(suppressedMessages.length, 1);
12558 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12560 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
12562 "/* eslint-disable-next-line no-alert */",
12570 const messages = linter.verify(code, config, filename);
12571 const suppressedMessages = linter.getSuppressedMessages();
12573 assert.strictEqual(messages.length, 0);
12575 assert.strictEqual(suppressedMessages.length, 1);
12576 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12579 it("should not ignore violation if code is not on next line", () => {
12581 "/* eslint-disable-next-line",
12582 "no-alert */alert('test');"
12589 const messages = linter.verify(code, config, filename);
12590 const suppressedMessages = linter.getSuppressedMessages();
12592 assert.strictEqual(messages.length, 1);
12593 assert.strictEqual(messages[0].ruleId, "no-alert");
12595 assert.strictEqual(suppressedMessages.length, 0);
12598 it("should ignore violation if block comment span multiple lines", () => {
12600 "/* eslint-disable-next-line",
12609 const messages = linter.verify(code, config, filename);
12610 const suppressedMessages = linter.getSuppressedMessages();
12612 assert.strictEqual(messages.length, 0);
12614 assert.strictEqual(suppressedMessages.length, 1);
12615 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12618 // For https://github.com/eslint/eslint/issues/14284
12619 it("should ignore violation if block comment span multiple lines with description", () => {
12621 /* eslint-disable-next-line no-alert --
12622 description on why this exception is seen as appropriate but past a
12623 comfortable reading line length
12632 const messages = linter.verify(code, config, filename);
12633 const suppressedMessages = linter.getSuppressedMessages();
12635 assert.strictEqual(messages.length, 0);
12637 assert.strictEqual(suppressedMessages.length, 1);
12638 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12641 it("should ignore violations only of specified rule", () => {
12643 "// eslint-disable-next-line no-console",
12645 "console.log('test');"
12653 const messages = linter.verify(code, config, filename);
12654 const suppressedMessages = linter.getSuppressedMessages();
12656 assert.strictEqual(messages.length, 2);
12657 assert.strictEqual(messages[0].ruleId, "no-alert");
12658 assert.strictEqual(messages[1].ruleId, "no-console");
12660 assert.strictEqual(suppressedMessages.length, 0);
12663 it("should ignore violations only of specified rule when block comment span multiple lines", () => {
12665 "/* eslint-disable-next-line",
12668 "console.log('test');"
12676 const messages = linter.verify(code, config, filename);
12678 assert.strictEqual(messages.length, 2);
12679 assert.strictEqual(messages[0].ruleId, "no-alert");
12680 assert.strictEqual(messages[1].ruleId, "no-console");
12683 it("should ignore violations of multiple rules when specified", () => {
12685 "// eslint-disable-next-line no-alert, quotes",
12686 "alert(\"test\");",
12687 "console.log('test');"
12692 quotes: [1, "single"],
12696 const messages = linter.verify(code, config, filename);
12697 const suppressedMessages = linter.getSuppressedMessages();
12699 assert.strictEqual(messages.length, 1);
12700 assert.strictEqual(messages[0].ruleId, "no-console");
12702 assert.strictEqual(suppressedMessages.length, 2);
12703 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12704 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12707 it("should ignore violations of multiple rules when specified in multiple lines", () => {
12709 "/* eslint-disable-next-line",
12713 "alert(\"test\");",
12714 "console.log('test');"
12719 quotes: [1, "single"],
12723 const messages = linter.verify(code, config, filename);
12725 assert.strictEqual(messages.length, 1);
12726 assert.strictEqual(messages[0].ruleId, "no-console");
12729 it("should ignore violations of multiple rules when specified in mixed comments", () => {
12731 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
12737 quotes: [1, "single"]
12740 const messages = linter.verify(code, config, filename);
12741 const suppressedMessages = linter.getSuppressedMessages();
12743 assert.strictEqual(messages.length, 0);
12745 assert.strictEqual(suppressedMessages.length, 2);
12746 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12747 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12750 it("should ignore violations of multiple rules when specified in mixed single line and multi line comments", () => {
12752 "/* eslint-disable-next-line",
12754 "*/ // eslint-disable-next-line quotes",
12760 quotes: [1, "single"]
12763 const messages = linter.verify(code, config, filename);
12765 assert.strictEqual(messages.length, 0);
12768 it("should ignore violations of only the specified rule on next line", () => {
12770 "// eslint-disable-next-line quotes",
12771 "alert(\"test\");",
12772 "console.log('test');"
12777 quotes: [1, "single"],
12781 const messages = linter.verify(code, config, filename);
12782 const suppressedMessages = linter.getSuppressedMessages();
12784 assert.strictEqual(messages.length, 2);
12785 assert.strictEqual(messages[0].ruleId, "no-alert");
12786 assert.strictEqual(messages[1].ruleId, "no-console");
12788 assert.strictEqual(suppressedMessages.length, 1);
12789 assert.strictEqual(suppressedMessages[0].ruleId, "quotes");
12792 it("should ignore violations of specified rule on next line only", () => {
12795 "// eslint-disable-next-line no-alert",
12797 "console.log('test');"
12805 const messages = linter.verify(code, config, filename);
12806 const suppressedMessages = linter.getSuppressedMessages();
12808 assert.strictEqual(messages.length, 2);
12809 assert.strictEqual(messages[0].ruleId, "no-alert");
12810 assert.strictEqual(messages[1].ruleId, "no-console");
12812 assert.strictEqual(suppressedMessages.length, 1);
12813 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12816 it("should ignore all rule violations on next line if none specified", () => {
12818 "// eslint-disable-next-line",
12819 "alert(\"test\");",
12820 "console.log('test')"
12824 semi: [1, "never"],
12825 quotes: [1, "single"],
12830 const messages = linter.verify(code, config, filename);
12831 const suppressedMessages = linter.getSuppressedMessages();
12833 assert.strictEqual(messages.length, 1);
12834 assert.strictEqual(messages[0].ruleId, "no-console");
12836 assert.strictEqual(suppressedMessages.length, 3);
12837 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12838 assert.strictEqual(suppressedMessages[1].ruleId, "quotes");
12839 assert.strictEqual(suppressedMessages[2].ruleId, "semi");
12842 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
12845 "/* eslint-disable-next-line no-alert */",
12847 "console.log('test');"
12855 const messages = linter.verify(code, config, filename);
12856 const suppressedMessages = linter.getSuppressedMessages();
12858 assert.strictEqual(messages.length, 2);
12859 assert.strictEqual(messages[0].ruleId, "no-alert");
12860 assert.strictEqual(messages[1].ruleId, "no-console");
12862 assert.strictEqual(suppressedMessages.length, 1);
12863 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
12866 it("should report a violation", () => {
12868 "/* eslint-disable-next-line",
12871 "console.log('test');" // here
12880 const messages = linter.verify(code, config, filename);
12881 const suppressedMessages = linter.getSuppressedMessages();
12883 assert.strictEqual(messages.length, 2);
12884 assert.strictEqual(messages[1].ruleId, "no-console");
12886 assert.strictEqual(suppressedMessages.length, 0);
12889 it("should not ignore violations if comment is of the type hashbang", () => {
12891 "#! eslint-disable-next-line no-alert",
12893 "console.log('test');"
12901 const messages = linter.verify(code, config, filename);
12902 const suppressedMessages = linter.getSuppressedMessages();
12904 assert.strictEqual(messages.length, 2);
12905 assert.strictEqual(messages[0].ruleId, "no-alert");
12906 assert.strictEqual(messages[1].ruleId, "no-console");
12908 assert.strictEqual(suppressedMessages.length, 0);
12912 describe("descriptions in directive comments", () => {
12913 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
12914 const aaa = sinon.stub().returns({});
12915 const bbb = sinon.stub().returns({});
12920 aaa: { create: aaa },
12921 bbb: { create: bbb }
12927 const messages = linter.verify(`
12928 /*eslint test/aaa:error -- test/bbb:error */
12929 console
.log("hello")
12931 const suppressedMessages = linter.getSuppressedMessages();
12933 // Don't include syntax error of the comment.
12934 assert.deepStrictEqual(messages, []);
12937 assert.strictEqual(aaa.callCount, 1);
12938 assert.strictEqual(bbb.callCount, 0);
12940 assert.strictEqual(suppressedMessages.length, 0);
12943 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
12944 const messages = linter.verify(`
12945 /*globals aaa -- bbb */
12950 sourceType: "script"
12952 rules: { "no-redeclare": "error" }
12954 const suppressedMessages = linter.getSuppressedMessages();
12956 // Don't include `bbb
`
12957 assert.deepStrictEqual(
12964 message: "'aaa' is already defined by a variable declaration.",
12965 messageId: "redeclaredBySyntax",
12967 ruleId: "no-redeclare",
12972 assert.strictEqual(suppressedMessages.length, 0);
12975 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
12976 const messages = linter.verify(`
12977 /*exported aaa -- bbb */
12982 sourceType: "script"
12984 rules: { "no-unused-vars": "error" }
12986 const suppressedMessages = linter.getSuppressedMessages();
12988 // Don't include `aaa
`
12989 assert.deepStrictEqual(
12996 message: "'bbb' is assigned a value but never used.",
12997 messageId: "unusedVar",
12998 nodeType: "Identifier",
12999 ruleId: "no-unused-vars",
13004 assert.strictEqual(suppressedMessages.length, 0);
13007 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
13008 const messages = linter.verify(`
13009 /*eslint-disable no-redeclare -- no-unused-vars */
13012 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13013 const suppressedMessages = linter.getSuppressedMessages();
13015 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13016 assert.deepStrictEqual(
13023 message: "'aaa' is assigned a value but never used.",
13024 messageId: "unusedVar",
13025 nodeType: "Identifier",
13026 ruleId: "no-unused-vars",
13031 assert.deepStrictEqual(
13032 suppressedMessages,
13038 message: "'aaa' is already defined.",
13039 messageId: "redeclared",
13040 nodeType: "Identifier",
13041 ruleId: "no-redeclare",
13043 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13048 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
13049 const messages = linter.verify(`
13050 /*eslint-disable no-redeclare, no-unused-vars */
13051 /*eslint-enable no-redeclare -- no-unused-vars */
13054 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13055 const suppressedMessages = linter.getSuppressedMessages();
13057 // Do include `no
-redeclare
` but not `no
-unused
-vars
`
13058 assert.deepStrictEqual(
13065 message: "'aaa' is already defined.",
13066 messageId: "redeclared",
13067 nodeType: "Identifier",
13068 ruleId: "no-redeclare",
13073 assert.deepStrictEqual(
13074 suppressedMessages,
13080 message: "'aaa' is assigned a value but never used.",
13081 messageId: "unusedVar",
13082 nodeType: "Identifier",
13083 ruleId: "no-unused-vars",
13085 suppressions: [{ kind: "directive", justification: "" }]
13090 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
13091 const messages = linter.verify(`
13092 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
13093 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
13094 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13095 const suppressedMessages = linter.getSuppressedMessages();
13097 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13098 assert.deepStrictEqual(
13105 message: "'aaa' is assigned a value but never used.",
13106 messageId: "unusedVar",
13107 nodeType: "Identifier",
13108 ruleId: "no-unused-vars",
13113 assert.deepStrictEqual(
13114 suppressedMessages,
13120 message: "'aaa' is already defined.",
13121 messageId: "redeclared",
13122 nodeType: "Identifier",
13123 ruleId: "no-redeclare",
13125 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13130 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
13131 const messages = linter.verify(`
13132 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
13133 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
13134 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13135 const suppressedMessages = linter.getSuppressedMessages();
13137 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13138 assert.deepStrictEqual(
13145 message: "'aaa' is assigned a value but never used.",
13146 messageId: "unusedVar",
13147 nodeType: "Identifier",
13148 ruleId: "no-unused-vars",
13153 assert.deepStrictEqual(
13154 suppressedMessages,
13160 message: "'aaa' is already defined.",
13161 messageId: "redeclared",
13162 nodeType: "Identifier",
13163 ruleId: "no-redeclare",
13165 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13170 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
13171 const messages = linter.verify(`
13172 //eslint-disable-next-line no-redeclare -- no-unused-vars
13174 //eslint-disable-next-line no-redeclare -- no-unused-vars
13176 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13177 const suppressedMessages = linter.getSuppressedMessages();
13179 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13180 assert.deepStrictEqual(
13187 message: "'aaa' is assigned a value but never used.",
13188 messageId: "unusedVar",
13189 nodeType: "Identifier",
13190 ruleId: "no-unused-vars",
13195 assert.deepStrictEqual(
13196 suppressedMessages,
13202 message: "'aaa' is already defined.",
13203 messageId: "redeclared",
13204 nodeType: "Identifier",
13205 ruleId: "no-redeclare",
13207 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13212 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
13213 const messages = linter.verify(`
13214 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
13216 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
13218 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
13219 const suppressedMessages = linter.getSuppressedMessages();
13221 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
13222 assert.deepStrictEqual(
13229 message: "'aaa' is assigned a value but never used.",
13230 messageId: "unusedVar",
13231 nodeType: "Identifier",
13232 ruleId: "no-unused-vars",
13237 assert.deepStrictEqual(
13238 suppressedMessages,
13244 message: "'aaa' is already defined.",
13245 messageId: "redeclared",
13246 nodeType: "Identifier",
13247 ruleId: "no-redeclare",
13249 suppressions: [{ kind: "directive", justification: "no-unused-vars" }]
13254 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
13255 const rule = sinon.stub().returns({});
13260 "a--rule": { create: rule }
13266 const messages = linter.verify(`
13267 /*eslint test/a--rule:error */
13268 console.log("hello")
13270 const suppressedMessages = linter.getSuppressedMessages();
13272 // Don't include syntax error of the comment.
13273 assert.deepStrictEqual(messages, []);
13276 assert.strictEqual(rule.callCount, 1);
13278 assert.strictEqual(suppressedMessages.length, 0);
13281 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
13282 const aaa = sinon.stub().returns({});
13283 const bbb = sinon.stub().returns({});
13288 aaa: { create: aaa },
13289 bbb: { create: bbb }
13295 const messages = linter.verify(`
13296 /*eslint test/aaa:error -------- test/bbb:error */
13297 console
.log("hello")
13299 const suppressedMessages = linter.getSuppressedMessages();
13301 // Don't include syntax error of the comment.
13302 assert.deepStrictEqual(messages, []);
13305 assert.strictEqual(aaa.callCount, 1);
13306 assert.strictEqual(bbb.callCount, 0);
13308 assert.strictEqual(suppressedMessages.length, 0);
13311 it("should ignore the part preceded by '--' with line breaks.", () => {
13312 const aaa = sinon.stub().returns({});
13313 const bbb = sinon.stub().returns({});
13318 aaa: { create: aaa },
13319 bbb: { create: bbb }
13325 const messages = linter.verify(`
13326 /*eslint test/aaa:error
13329 console.log("hello")
13331 const suppressedMessages = linter.getSuppressedMessages();
13333 // Don't include syntax error of the comment.
13334 assert.deepStrictEqual(messages, []);
13337 assert.strictEqual(aaa.callCount, 1);
13338 assert.strictEqual(bbb.callCount, 0);
13340 assert.strictEqual(suppressedMessages.length, 0);
13344 describe("allowInlineConfig option", () => {
13345 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
13346 it("should report a violation for disabling rules", () => {
13348 "alert('test'); // eslint-disable-line no-alert"
13356 const messages = linter.verify(code, config, {
13358 allowInlineConfig: false
13360 const suppressedMessages = linter.getSuppressedMessages();
13362 assert.strictEqual(messages.length, 1);
13363 assert.strictEqual(messages[0].ruleId, "no-alert");
13365 assert.strictEqual(suppressedMessages.length, 0);
13368 it("should report a violation for global variable declarations", () => {
13380 const scope = context.getScope();
13381 const sourceCode = context.getSourceCode();
13382 const comments = sourceCode.getAllComments();
13384 assert.strictEqual(1, comments.length);
13386 const foo = getVariable(scope, "foo
");
13402 linter.verify(code, config, { allowInlineConfig: false });
13406 it("should report a violation
for eslint
-disable
", () => {
13408 "/* eslint-disable */",
13417 const messages = linter.verify(code, config, {
13419 allowInlineConfig: false
13421 const suppressedMessages = linter.getSuppressedMessages();
13423 assert.strictEqual(messages.length, 1);
13424 assert.strictEqual(messages[0].ruleId, "no
-alert
");
13426 assert.strictEqual(suppressedMessages.length, 0);
13429 it("should not report a violation
for rule changes
", () => {
13431 "/*eslint no-alert:2*/",
13440 const messages = linter.verify(code, config, {
13442 allowInlineConfig: false
13444 const suppressedMessages = linter.getSuppressedMessages();
13446 assert.strictEqual(messages.length, 0);
13447 assert.strictEqual(suppressedMessages.length, 0);
13450 it("should report a violation
for disable
-line
", () => {
13452 "alert('test'); // eslint-disable-line"
13460 const messages
= linter
.verify(code
, config
, {
13462 allowInlineConfig
: false
13464 const suppressedMessages
= linter
.getSuppressedMessages();
13466 assert
.strictEqual(messages
.length
, 1);
13467 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
13469 assert
.strictEqual(suppressedMessages
.length
, 0);
13474 describe("when evaluating code with 'noInlineConfig'", () => {
13475 for (const directive
of [
13479 "eslint eqeqeq: error",
13480 "eslint-disable eqeqeq",
13481 "eslint-disable-line eqeqeq",
13482 "eslint-disable-next-line eqeqeq",
13483 "eslint-enable eqeqeq"
13485 // eslint-disable-next-line no-loop-func -- No closures
13486 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
13487 const messages
= linter
.verify(`/* ${directive} */`, {
13489 noInlineConfig
: true
13492 const suppressedMessages
= linter
.getSuppressedMessages();
13494 assert
.deepStrictEqual(messages
.length
, 1);
13495 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
13496 assert
.deepStrictEqual(messages
[0].ruleId
, null);
13497 assert
.deepStrictEqual(messages
[0].severity
, 1);
13498 assert
.deepStrictEqual(messages
[0].message
, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
13500 assert
.strictEqual(suppressedMessages
.length
, 0);
13504 for (const directive
of [
13505 "eslint-disable-line eqeqeq",
13506 "eslint-disable-next-line eqeqeq"
13508 // eslint-disable-next-line no-loop-func -- No closures
13509 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
13510 const messages
= linter
.verify(`// ${directive}`, {
13512 noInlineConfig
: true
13515 const suppressedMessages
= linter
.getSuppressedMessages();
13517 assert
.deepStrictEqual(messages
.length
, 1);
13518 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
13519 assert
.deepStrictEqual(messages
[0].ruleId
, null);
13520 assert
.deepStrictEqual(messages
[0].severity
, 1);
13521 assert
.deepStrictEqual(messages
[0].message
, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
13523 assert
.strictEqual(suppressedMessages
.length
, 0);
13527 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
13528 const messages
= linter
.verify("/* globals foo */", {
13530 noInlineConfig
: true
13532 }, { allowInlineConfig
: false });
13533 const suppressedMessages
= linter
.getSuppressedMessages();
13535 assert
.deepStrictEqual(messages
.length
, 0);
13536 assert
.strictEqual(suppressedMessages
.length
, 0);
13540 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
13541 it("should not report a violation", () => {
13543 "alert('test'); // eslint-disable-line no-alert"
13551 const messages
= linter
.verify(code
, config
, {
13553 allowInlineConfig
: true
13555 const suppressedMessages
= linter
.getSuppressedMessages();
13557 assert
.strictEqual(messages
.length
, 0);
13559 assert
.strictEqual(suppressedMessages
.length
, 1);
13560 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
13566 describe("reportUnusedDisableDirectives option", () => {
13567 it("reports problems for unused eslint-disable comments", () => {
13568 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: true });
13569 const suppressedMessages
= linter
.getSuppressedMessages();
13571 assert
.deepStrictEqual(
13576 message
: "Unused eslint-disable directive (no problems were reported).",
13589 assert
.strictEqual(suppressedMessages
.length
, 0);
13592 it("reports problems for unused eslint-disable comments (error)", () => {
13593 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "error" });
13594 const suppressedMessages
= linter
.getSuppressedMessages();
13596 assert
.deepStrictEqual(
13601 message
: "Unused eslint-disable directive (no problems were reported).",
13614 assert
.strictEqual(suppressedMessages
.length
, 0);
13617 it("reports problems for unused eslint-disable comments (warn)", () => {
13618 const messages
= linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "warn" });
13619 const suppressedMessages
= linter
.getSuppressedMessages();
13621 assert
.deepStrictEqual(
13626 message
: "Unused eslint-disable directive (no problems were reported).",
13639 assert
.strictEqual(suppressedMessages
.length
, 0);
13642 it("reports problems for unused eslint-disable comments (in config)", () => {
13643 const messages
= linter
.verify("/* eslint-disable */", {
13645 reportUnusedDisableDirectives
: true
13648 const suppressedMessages
= linter
.getSuppressedMessages();
13650 assert
.deepStrictEqual(
13655 message
: "Unused eslint-disable directive (no problems were reported).",
13668 assert
.strictEqual(suppressedMessages
.length
, 0);
13671 it("reports problems for partially unused eslint-disable comments (in config)", () => {
13672 const code
= "alert('test'); // eslint-disable-line no-alert, no-redeclare";
13675 reportUnusedDisableDirectives
: true
13683 const messages
= linter
.verify(code
, config
, {
13685 allowInlineConfig
: true
13687 const suppressedMessages
= linter
.getSuppressedMessages();
13689 assert
.deepStrictEqual(
13694 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
13707 assert
.strictEqual(suppressedMessages
.length
, 1);
13708 assert
.strictEqual(suppressedMessages
[0].ruleId
, "no-alert");
13711 it("reports problems for unused eslint-disable-next-line comments (in config)", () => {
13712 assert
.deepStrictEqual(
13713 linter
.verify("// eslint-disable-next-line", {
13715 reportUnusedDisableDirectives
: true
13721 message
: "Unused eslint-disable directive (no problems were reported).",
13735 it("reports problems for unused multiline eslint-disable-next-line comments (in config)", () => {
13736 assert
.deepStrictEqual(
13737 linter
.verify("/* \neslint-disable-next-line\n */", {
13739 reportUnusedDisableDirectives
: true
13745 message
: "Unused eslint-disable directive (no problems were reported).",
13759 it("reports problems for partially unused eslint-disable-next-line comments (in config)", () => {
13760 const code
= "// eslint-disable-next-line no-alert, no-redeclare \nalert('test');";
13763 reportUnusedDisableDirectives
: true
13771 const messages
= linter
.verify(code
, config
, {
13773 allowInlineConfig
: true
13776 assert
.deepStrictEqual(
13781 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
13795 it("reports problems for partially unused multiline eslint-disable-next-line comments (in config)", () => {
13797 /* eslint-disable-next-line no-alert, no-redeclare --
13798 * Here's a very long description about why this configuration is necessary
13799 * along with some additional information
13805 reportUnusedDisableDirectives
: true
13813 const messages
= linter
.verify(code
, config
, {
13815 allowInlineConfig
: true
13818 assert
.deepStrictEqual(
13823 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
13837 describe("autofix", () => {
13838 const alwaysReportsRule
= {
13842 context
.report({ message
: "bad code", loc
: node
.loc
.end
});
13848 const neverReportsRule
= {
13854 const ruleCount
= 3;
13855 const usedRules
= Array
.from(
13856 { length
: ruleCount
},
13857 (_
, index
) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
13859 const unusedRules = usedRules.map(name => `un${name}
`); // "unused", "unused-1", "unused-2"
13868 reportUnusedDisableDirectives: true
13871 ...Object.fromEntries(usedRules.map(name => [`test
/${name}
`, "error"])),
13872 ...Object.fromEntries(unusedRules.map(name => [`test
/${name}
`, "error"]))
13877 config.plugins.test.rules = {
13878 ...Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])),
13879 ...Object.fromEntries(unusedRules.map(name => [name, neverReportsRule]))
13885 //-----------------------------------------------
13886 // Removing the entire comment
13887 //-----------------------------------------------
13890 code: "// eslint-disable-line test/unused",
13894 code: "foo// eslint-disable-line test/unused",
13898 code: "// eslint-disable-line ,test/unused,",
13902 code: "// eslint-disable-line test/unused-1, test/unused-2",
13906 code: "// eslint-disable-line ,test/unused-1,, test/unused-2,, -- comment",
13910 code: "// eslint-disable-next-line test/unused\n",
13914 code: "// eslint-disable-next-line test/unused\nfoo",
13918 code: "/* eslint-disable \ntest/unused\n*/",
13922 //-----------------------------------------------
13923 // Removing only individual rules
13924 //-----------------------------------------------
13926 // content before the first rule should not be changed
13928 code: "//eslint-disable-line test/unused, test/used",
13929 output: "//eslint-disable-line test/used"
13932 code: "// eslint-disable-line test/unused, test/used",
13933 output: "// eslint-disable-line test/used"
13936 code: "// eslint-disable-line test/unused, test/used",
13937 output: "// eslint-disable-line test/used"
13940 code: "/*\neslint-disable test/unused, test/used*/",
13941 output: "/*\neslint-disable test/used*/"
13944 code: "/*\n eslint-disable test/unused, test/used*/",
13945 output: "/*\n eslint-disable test/used*/"
13948 code: "/*\r\neslint-disable test/unused, test/used*/",
13949 output: "/*\r\neslint-disable test/used*/"
13952 code: "/*\u2028eslint-disable test/unused, test/used*/",
13953 output: "/*\u2028eslint-disable test/used*/"
13956 code: "/*\u00A0eslint-disable test/unused, test/used*/",
13957 output: "/*\u00A0eslint-disable test/used*/"
13960 code: "// eslint-disable-line test/unused, test/used",
13961 output: "// eslint-disable-line test/used"
13964 code: "/* eslint-disable\ntest/unused, test/used*/",
13965 output: "/* eslint-disable\ntest/used*/"
13968 code: "/* eslint-disable\n test/unused, test/used*/",
13969 output: "/* eslint-disable\n test/used*/"
13972 code: "/* eslint-disable\r\ntest/unused, test/used*/",
13973 output: "/* eslint-disable\r\ntest/used*/"
13976 code: "/* eslint-disable\u2028test/unused, test/used*/",
13977 output: "/* eslint-disable\u2028test/used*/"
13980 code: "/* eslint-disable\u00A0test/unused, test/used*/",
13981 output: "/* eslint-disable\u00A0test/used*/"
13984 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
13986 code: "// eslint-disable-line test/unused,test/used",
13987 output: "// eslint-disable-line test/used"
13990 code: "// eslint-disable-line test/unused, test/used",
13991 output: "// eslint-disable-line test/used"
13994 code: "// eslint-disable-line test/unused , test/used",
13995 output: "// eslint-disable-line test/used"
13998 code: "// eslint-disable-line test/unused, test/used",
13999 output: "// eslint-disable-line test/used"
14002 code: "// eslint-disable-line test/unused ,test/used",
14003 output: "// eslint-disable-line test/used"
14006 code: "/* eslint-disable test/unused\n,\ntest/used */",
14007 output: "/* eslint-disable test/used */"
14010 code: "/* eslint-disable test/unused \n \n,\n\n test/used */",
14011 output: "/* eslint-disable test/used */"
14014 code: "/* eslint-disable test/unused\u2028,\u2028test/used */",
14015 output: "/* eslint-disable test/used */"
14018 code: "// eslint-disable-line test/unused\u00A0,\u00A0test/used",
14019 output: "// eslint-disable-line test/used"
14022 code: "// eslint-disable-line test/unused,,test/used",
14023 output: "// eslint-disable-line ,test/used"
14026 code: "// eslint-disable-line test/unused, ,test/used",
14027 output: "// eslint-disable-line ,test/used"
14030 code: "// eslint-disable-line test/unused,, test/used",
14031 output: "// eslint-disable-line , test/used"
14034 code: "// eslint-disable-line test/unused,test/used ",
14035 output: "// eslint-disable-line test/used "
14038 code: "// eslint-disable-next-line test/unused,test/used\n",
14039 output: "// eslint-disable-next-line test/used\n"
14042 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
14044 code: "// eslint-disable-line test/used-1,test/unused,test/used-2",
14045 output: "// eslint-disable-line test/used-1,test/used-2"
14048 code: "// eslint-disable-line test/used-1, test/unused,test/used-2",
14049 output: "// eslint-disable-line test/used-1,test/used-2"
14052 code: "// eslint-disable-line test/used-1,test/unused ,test/used-2",
14053 output: "// eslint-disable-line test/used-1,test/used-2"
14056 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14057 output: "// eslint-disable-line test/used-1,test/used-2"
14060 code: "/* eslint-disable test/used-1,\ntest/unused\n,test/used-2 */",
14061 output: "/* eslint-disable test/used-1,test/used-2 */"
14064 code: "/* eslint-disable test/used-1,\n\n test/unused \n \n ,test/used-2 */",
14065 output: "/* eslint-disable test/used-1,test/used-2 */"
14068 code: "/* eslint-disable test/used-1,\u2028test/unused\u2028,test/used-2 */",
14069 output: "/* eslint-disable test/used-1,test/used-2 */"
14072 code: "// eslint-disable-line test/used-1,\u00A0test/unused\u00A0,test/used-2",
14073 output: "// eslint-disable-line test/used-1,test/used-2"
14076 // when removing a rule in the middle, content around commas should not be changed
14078 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
14079 output: "// eslint-disable-line test/used-1,test/used-2"
14082 code: "// eslint-disable-line test/used-1,test/unused, test/used-2",
14083 output: "// eslint-disable-line test/used-1, test/used-2"
14086 code: "// eslint-disable-line test/used-1 ,test/unused,test/used-2",
14087 output: "// eslint-disable-line test/used-1 ,test/used-2"
14090 code: "// eslint-disable-line test/used-1 ,test/unused, test/used-2",
14091 output: "// eslint-disable-line test/used-1 , test/used-2"
14094 code: "// eslint-disable-line test/used-1 , test/unused , test/used-2",
14095 output: "// eslint-disable-line test/used-1 , test/used-2"
14098 code: "/* eslint-disable test/used-1\n,test/unused,\ntest/used-2 */",
14099 output: "/* eslint-disable test/used-1\n,\ntest/used-2 */"
14102 code: "/* eslint-disable test/used-1\u2028,test/unused,\u2028test/used-2 */",
14103 output: "/* eslint-disable test/used-1\u2028,\u2028test/used-2 */"
14106 code: "// eslint-disable-line test/used-1\u00A0,test/unused,\u00A0test/used-2",
14107 output: "// eslint-disable-line test/used-1\u00A0,\u00A0test/used-2"
14110 code: "// eslint-disable-line , test/unused ,test/used",
14111 output: "// eslint-disable-line ,test/used"
14114 code: "/* eslint-disable\n, test/unused ,test/used */",
14115 output: "/* eslint-disable\n,test/used */"
14118 code: "/* eslint-disable test/used-1,\n,test/unused,test/used-2 */",
14119 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
14122 code: "/* eslint-disable test/used-1,test/unused,\n,test/used-2 */",
14123 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
14126 code: "/* eslint-disable test/used-1,\n,test/unused,\n,test/used-2 */",
14127 output: "/* eslint-disable test/used-1,\n,\n,test/used-2 */"
14130 code: "// eslint-disable-line test/used, test/unused,",
14131 output: "// eslint-disable-line test/used,"
14134 code: "// eslint-disable-next-line test/used, test/unused,\n",
14135 output: "// eslint-disable-next-line test/used,\n"
14138 code: "// eslint-disable-line test/used, test/unused, ",
14139 output: "// eslint-disable-line test/used, "
14142 code: "// eslint-disable-line test/used, test/unused, -- comment",
14143 output: "// eslint-disable-line test/used, -- comment"
14146 code: "/* eslint-disable test/used, test/unused,\n*/",
14147 output: "/* eslint-disable test/used,\n*/"
14150 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
14152 code: "// eslint-disable-line test/used,test/unused",
14153 output: "// eslint-disable-line test/used"
14156 code: "// eslint-disable-line test/used, test/unused",
14157 output: "// eslint-disable-line test/used"
14160 code: "// eslint-disable-line test/used ,test/unused",
14161 output: "// eslint-disable-line test/used"
14164 code: "// eslint-disable-line test/used , test/unused",
14165 output: "// eslint-disable-line test/used"
14168 code: "// eslint-disable-line test/used, test/unused",
14169 output: "// eslint-disable-line test/used"
14172 code: "// eslint-disable-line test/used ,test/unused",
14173 output: "// eslint-disable-line test/used"
14176 code: "/* eslint-disable test/used\n,\ntest/unused */",
14177 output: "/* eslint-disable test/used */"
14180 code: "/* eslint-disable test/used \n \n,\n\n test/unused */",
14181 output: "/* eslint-disable test/used */"
14184 code: "/* eslint-disable test/used\u2028,\u2028test/unused */",
14185 output: "/* eslint-disable test/used */"
14188 code: "// eslint-disable-line test/used\u00A0,\u00A0test/unused",
14189 output: "// eslint-disable-line test/used"
14192 code: "// eslint-disable-line test/used,,test/unused",
14193 output: "// eslint-disable-line test/used,"
14196 code: "// eslint-disable-line test/used, ,test/unused",
14197 output: "// eslint-disable-line test/used,"
14200 code: "/* eslint-disable test/used,\n,test/unused */",
14201 output: "/* eslint-disable test/used, */"
14204 code: "/* eslint-disable test/used\n, ,test/unused */",
14205 output: "/* eslint-disable test/used\n, */"
14208 // content after the last rule should not be changed
14210 code: "// eslint-disable-line test/used,test/unused",
14211 output: "// eslint-disable-line test/used"
14214 code: "// eslint-disable-line test/used,test/unused ",
14215 output: "// eslint-disable-line test/used "
14218 code: "// eslint-disable-line test/used,test/unused ",
14219 output: "// eslint-disable-line test/used "
14222 code: "// eslint-disable-line test/used,test/unused -- comment",
14223 output: "// eslint-disable-line test/used -- comment"
14226 code: "// eslint-disable-next-line test/used,test/unused\n",
14227 output: "// eslint-disable-next-line test/used\n"
14230 code: "// eslint-disable-next-line test/used,test/unused \n",
14231 output: "// eslint-disable-next-line test/used \n"
14234 code: "/* eslint-disable test/used,test/unused\u2028*/",
14235 output: "/* eslint-disable test/used\u2028*/"
14238 code: "// eslint-disable-line test/used,test/unused\u00A0",
14239 output: "// eslint-disable-line test/used\u00A0"
14242 // multiply rules to remove
14244 code: "// eslint-disable-line test/used, test/unused-1, test/unused-2",
14245 output: "// eslint-disable-line test/used"
14248 code: "// eslint-disable-line test/unused-1, test/used, test/unused-2",
14249 output: "// eslint-disable-line test/used"
14252 code: "// eslint-disable-line test/unused-1, test/unused-2, test/used",
14253 output: "// eslint-disable-line test/used"
14256 code: "// eslint-disable-line test/used-1, test/unused-1, test/used-2, test/unused-2",
14257 output: "// eslint-disable-line test/used-1, test/used-2"
14260 code: "// eslint-disable-line test/unused-1, test/used-1, test/unused-2, test/used-2",
14261 output: "// eslint-disable-line test/used-1, test/used-2"
14265 /* eslint-disable test/unused-1,
14272 /* eslint-disable test/used-1,
14378 // duplicates in the list
14380 code: "// eslint-disable-line test/unused, test/unused, test/used",
14381 output: "// eslint-disable-line test/used"
14384 code: "// eslint-disable-line test/unused, test/used, test/unused",
14385 output: "// eslint-disable-line test/used"
14388 code: "// eslint-disable-line test/used, test/unused, test/unused, test/used",
14389 output: "// eslint-disable-line test/used, test/used"
14393 for (const { code, output } of tests) {
14394 // eslint-disable-next-line no-loop-func -- `linter
` is getting updated in beforeEach()
14396 assert.strictEqual(
14397 linter.verifyAndFix(code, config).output,
14407 describe("Default Global Variables", () => {
14410 it("builtin global variables should be available in the global scope", () => {
14416 checker: context => {
14417 spy = sinon.spy(() => {
14418 const scope = context.getScope();
14420 assert.notStrictEqual(getVariable(scope, "Object"), null);
14421 assert.notStrictEqual(getVariable(scope, "Array"), null);
14422 assert.notStrictEqual(getVariable(scope, "undefined"), null);
14425 return { Program: spy };
14432 sourceType: "script"
14435 "test/checker": "error"
14439 linter.verify(code, config);
14440 assert(spy && spy.calledOnce, "Rule should have been called.");
14443 it("ES6 global variables should be available by default", () => {
14449 checker: context => {
14450 spy = sinon.spy(() => {
14451 const scope = context.getScope();
14453 assert.notStrictEqual(getVariable(scope, "Promise"), null);
14454 assert.notStrictEqual(getVariable(scope, "Symbol"), null);
14455 assert.notStrictEqual(getVariable(scope, "WeakMap"), null);
14458 return { Program: spy };
14464 sourceType: "script"
14467 "test/checker": "error"
14471 linter.verify(code, config);
14472 assert(spy && spy.calledOnce);
14477 describe("Suggestions", () => {
14478 it("provides suggestion information for tools to use", () => {
14484 "rule-with-suggestions": {
14485 meta: { hasSuggestions: true },
14486 create: context => ({
14490 message: "Incorrect spacing",
14492 desc: "Insert space at the beginning",
14493 fix: fixer => fixer.insertTextBefore(node, " ")
14495 desc: "Insert space at the end",
14496 fix: fixer => fixer.insertTextAfter(node, " ")
14506 "test/rule-with-suggestions": "error"
14510 const messages = linter.verify("var a = 1;", config);
14511 const suppressedMessages = linter.getSuppressedMessages();
14513 assert.deepStrictEqual(messages[0].suggestions, [{
14514 desc: "Insert space at the beginning",
14520 desc: "Insert space at the end",
14527 assert.strictEqual(suppressedMessages.length, 0);
14530 it("supports messageIds for suggestions", () => {
14536 "rule-with-suggestions": {
14539 suggestion1: "Insert space at the beginning",
14540 suggestion2: "Insert space at the end"
14542 hasSuggestions: true
14544 create: context => ({
14548 message: "Incorrect spacing",
14550 messageId: "suggestion1",
14551 fix: fixer => fixer.insertTextBefore(node, " ")
14553 messageId: "suggestion2",
14554 fix: fixer => fixer.insertTextAfter(node, " ")
14564 "test/rule-with-suggestions": "error"
14568 const messages = linter.verify("var a = 1;", config);
14569 const suppressedMessages = linter.getSuppressedMessages();
14571 assert.deepStrictEqual(messages[0].suggestions, [{
14572 messageId: "suggestion1",
14573 desc: "Insert space at the beginning",
14579 messageId: "suggestion2",
14580 desc: "Insert space at the end",
14587 assert.strictEqual(suppressedMessages.length, 0);
14590 it("should throw an error if suggestion is passed but `meta
.hasSuggestions
` property is not enabled", () => {
14596 "rule-with-suggestions": {
14597 meta: { docs: {}, schema: [] },
14598 create: context => ({
14602 message: "hello world",
14603 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
14612 "test/rule-with-suggestions": "error"
14616 assert.throws(() => {
14617 linter.verify("0", config);
14618 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`.");
14621 it("should throw an error if suggestion is passed but `meta
.hasSuggestions
` property is not enabled and the rule has the obsolete `meta
.docs
.suggestion
` property", () => {
14627 "rule-with-meta-docs-suggestion": {
14628 meta: { docs: { suggestion: true }, schema: [] },
14629 create: context => ({
14633 message: "hello world",
14634 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
14643 "test/rule-with-meta-docs-suggestion": "error"
14647 assert.throws(() => {
14648 linter.verify("0", config);
14649 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`. `meta
.docs
.suggestion
` is ignored by ESLint.");
14654 describe("Error Conditions", () => {
14655 describe("when evaluating broken code", () => {
14656 const code = BROKEN_TEST_CODE;
14658 it("should report a violation with a useful parse error prefix", () => {
14659 const messages = linter.verify(code);
14660 const suppressedMessages = linter.getSuppressedMessages();
14662 assert.strictEqual(messages.length, 1);
14663 assert.strictEqual(messages[0].severity, 2);
14664 assert.isNull(messages[0].ruleId);
14665 assert.strictEqual(messages[0].line, 1);
14666 assert.strictEqual(messages[0].column, 4);
14667 assert.isTrue(messages[0].fatal);
14668 assert.match(messages[0].message, /^Parsing error:/u);
14670 assert.strictEqual(suppressedMessages.length, 0);
14673 it("should report source code where the issue is present", () => {
14674 const inValidCode = [
14680 const messages = linter.verify(inValidCode.join("\n"));
14681 const suppressedMessages = linter.getSuppressedMessages();
14683 assert.strictEqual(messages.length, 1);
14684 assert.strictEqual(messages[0].severity, 2);
14685 assert.isTrue(messages[0].fatal);
14686 assert.match(messages[0].message, /^Parsing error:/u);
14688 assert.strictEqual(suppressedMessages.length, 0);
14692 describe("when using a rule which has been replaced", () => {
14693 const code = TEST_CODE;
14695 it("should report the new rule", () => {
14697 assert.throws(() => {
14698 linter.verify(code, { rules: { "no-comma-dangle": 2 } });
14699 }, /Key "rules": Key "no-comma-dangle": Rule "no-comma-dangle" was removed and replaced by "comma-dangle"/u);
14707 describe("getSourceCode()", () => {
14708 const code = TEST_CODE;
14710 it("should retrieve SourceCode object after reset", () => {
14711 linter.verify(code, {}, filename, true);
14713 const sourceCode = linter.getSourceCode();
14715 assert.isObject(sourceCode);
14716 assert.strictEqual(sourceCode.text, code);
14717 assert.isObject(sourceCode.ast);
14720 it("should retrieve SourceCode object without reset", () => {
14721 linter.verify(code, {}, filename);
14723 const sourceCode = linter.getSourceCode();
14725 assert.isObject(sourceCode);
14726 assert.strictEqual(sourceCode.text, code);
14727 assert.isObject(sourceCode.ast);
14732 describe("getSuppressedMessages()", () => {
14733 it("should have no suppressed messages", () => {
14734 const suppressedMessages = linter.getSuppressedMessages();
14736 assert.strictEqual(suppressedMessages.length, 0);
14739 it("should have a suppressed message", () => {
14740 const code = "/* eslint-disable no-alert -- justification */\nalert(\"test\");";
14742 rules: { "no-alert": 1 }
14744 const messages = linter.verify(code, config);
14745 const suppressedMessages = linter.getSuppressedMessages();
14747 assert.strictEqual(messages.length, 0);
14749 assert.strictEqual(suppressedMessages.length, 1);
14750 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
14751 assert.deepStrictEqual(
14752 suppressedMessages[0].suppressions,
14753 [{ kind: "directive", justification: "justification" }]
14757 it("should have a suppressed message", () => {
14759 "/* eslint-disable no-alert -- j1",
14765 rules: { "no-alert": 1 }
14767 const messages = linter.verify(code, config);
14768 const suppressedMessages = linter.getSuppressedMessages();
14770 assert.strictEqual(messages.length, 0);
14772 assert.strictEqual(suppressedMessages.length, 1);
14773 assert.strictEqual(suppressedMessages[0].ruleId, "no-alert");
14774 assert.deepStrictEqual(
14775 suppressedMessages[0].suppressions,
14776 [{ kind: "directive", justification: "j1\n * j2" }]
14781 describe("defineRule()", () => {
14782 it("should throw an error when called in flat config mode", () => {
14783 assert.throws(() => {
14784 linter.defineRule("foo", () => {});
14785 }, /This method cannot be used with flat config/u);
14789 describe("defineRules()", () => {
14790 it("should throw an error when called in flat config mode", () => {
14791 assert.throws(() => {
14792 linter.defineRules({});
14793 }, /This method cannot be used with flat config/u);
14797 describe("defineParser()", () => {
14798 it("should throw an error when called in flat config mode", () => {
14799 assert.throws(() => {
14800 linter.defineParser("foo", {});
14801 }, /This method cannot be used with flat config/u);
14805 describe("getRules()", () => {
14806 it("should throw an error when called in flat config mode", () => {
14807 assert.throws(() => {
14809 }, /This method cannot be used with flat config/u);
14813 describe("version", () => {
14814 it("should return current version number", () => {
14815 const version = linter.version;
14817 assert.isString(version);
14818 assert.isTrue(parseInt(version[0], 10) >= 3);
14822 describe("verifyAndFix()", () => {
14823 it("Fixes the code", () => {
14824 const messages = linter.verifyAndFix("var a", {
14828 }, { filename: "test.js" });
14829 const suppressedMessages = linter.getSuppressedMessages();
14831 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
14832 assert.isTrue(messages.fixed);
14834 assert.strictEqual(suppressedMessages.length, 0);
14837 it("does not require a third argument", () => {
14838 const fixResult = linter.verifyAndFix("var a", {
14843 const suppressedMessages = linter.getSuppressedMessages();
14845 assert.deepStrictEqual(fixResult, {
14851 assert.strictEqual(suppressedMessages.length, 0);
14854 it("does not include suggestions in autofix results", () => {
14855 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
14858 "no-useless-escape": 2
14861 const suppressedMessages = linter.getSuppressedMessages();
14863 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
14864 assert.strictEqual(fixResult.fixed, true);
14865 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
14867 assert.strictEqual(suppressedMessages.length, 0);
14870 it("does not apply autofixes when fix argument is `false`", () => {
14871 const fixResult = linter.verifyAndFix("var a", {
14875 }, { fix: false });
14876 const suppressedMessages = linter.getSuppressedMessages();
14878 assert.strictEqual(fixResult.fixed, false);
14879 assert.strictEqual(suppressedMessages.length, 0);
14882 it("stops fixing after 10 passes", () => {
14890 fixable: "whitespace"
14897 message: "Add a space before this node.",
14898 fix: fixer => fixer.insertTextBefore(node, " ")
14908 "test/add-spaces": "error"
14912 const fixResult = linter.verifyAndFix("a", config);
14913 const suppressedMessages = linter.getSuppressedMessages();
14915 assert.strictEqual(fixResult.fixed, true);
14916 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a
`);
14917 assert.strictEqual(fixResult.messages.length, 1);
14919 assert.strictEqual(suppressedMessages.length, 0);
14922 it("should throw an error if fix is passed but meta has no `fixable
` property", () => {
14933 create: context => ({
14935 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
14943 "test/test-rule": "error"
14948 assert.throws(() => {
14949 linter.verify("0", config);
14950 }, /Fixable rules must set the `meta
\.fixable
` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test\/test-rule"$/u);
14953 it("should throw an error if fix is passed and there is no metadata", () => {
14960 create: context => ({
14962 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
14970 "test/test-rule": "error"
14974 assert.throws(() => {
14975 linter.verify("0", config);
14976 }, /Fixable rules must set the `meta
\.fixable
` property/u);
14979 it("should throw an error if fix is passed from a legacy-format rule", () => {
14985 "test-rule": context => ({
14987 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
14994 "test/test-rule": "error"
14999 assert.throws(() => {
15000 linter.verify("0", config);
15001 }, /Fixable rules must set the `meta
\.fixable
` property/u);
15005 describe("Mutability", () => {
15006 let linter1 = null;
15007 let linter2 = null;
15010 linter1 = new Linter();
15011 linter2 = new Linter();
15014 describe("rules", () => {
15015 it("with no changes, same rules are loaded", () => {
15016 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
15019 it("loading rule in one doesn't change the other", () => {
15020 linter1.defineRule("mock-rule", () => ({}));
15022 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
15023 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
15029 describe("processors", () => {
15030 let receivedFilenames = [];
15031 let receivedPhysicalFilenames = [];
15032 const extraConfig = {
15036 "report-original-text": {
15043 receivedFilenames.push(context.getFilename());
15044 receivedPhysicalFilenames.push(context.getPhysicalFilename());
15045 context.report({ node: ast, message: context.getSourceCode().text });
15056 receivedFilenames = [];
15057 receivedPhysicalFilenames = [];
15060 describe("preprocessors", () => {
15061 it("should receive text and filename.", () => {
15062 const code = "foo bar baz";
15063 const preprocess = sinon.spy(text => text.split(" "));
15064 const configs = createFlatConfigArray({});
15066 configs.normalizeSync();
15068 linter.verify(code, configs, { filename, preprocess });
15070 assert.strictEqual(preprocess.calledOnce, true, "preprocess wasn't called");
15071 assert.deepStrictEqual(preprocess.args[0], [code, filename], "preprocess was called with the wrong arguments");
15074 it("should run preprocess only once", () => {
15079 preprocess(text, filenameForText) {
15082 filename: filenameForText
15085 return [{ text: "bar", filename: "0.js" }];
15093 linter.verify("foo", config, "a.md");
15094 assert.strictEqual(logs.length, 1, "preprocess() should only be called once.");
15097 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
15098 const code = "foo bar baz";
15099 const configs = createFlatConfigArray([
15101 { rules: { "test/report-original-text": "error" } }
15104 configs.normalizeSync();
15106 const problems = linter.verify(
15112 // Apply a preprocessor that splits the source text into spaces and lints each word individually
15113 preprocess(input) {
15114 return input.split(" ");
15118 const suppressedMessages = linter.getSuppressedMessages();
15120 assert.strictEqual(problems.length, 3);
15121 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15123 assert.strictEqual(suppressedMessages.length, 0);
15126 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
15127 const code = "foo bar baz";
15128 const configs = createFlatConfigArray([
15130 { rules: { "test/report-original-text": "error" } }
15133 configs.normalizeSync();
15135 const problems = linter.verify(
15141 // Apply a preprocessor that splits the source text into spaces and lints each word individually
15142 preprocess(input) {
15143 return input.split(" ").map(text => ({
15144 filename: "block.js",
15150 const suppressedMessages = linter.getSuppressedMessages();
15152 assert.strictEqual(problems.length, 3);
15153 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
15155 assert.strictEqual(suppressedMessages.length, 0);
15158 assert.strictEqual(receivedFilenames.length, 3);
15159 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
15160 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
15161 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
15163 // physical filename
15164 assert.strictEqual(receivedPhysicalFilenames.length, 3);
15165 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
15168 it("should receive text even if a SourceCode object was given.", () => {
15169 const code = "foo";
15170 const preprocess = sinon.spy(text => text.split(" "));
15171 const configs = createFlatConfigArray([
15175 configs.normalizeSync();
15177 linter.verify(code, configs);
15178 const sourceCode = linter.getSourceCode();
15180 linter.verify(sourceCode, configs, { filename, preprocess });
15182 assert.strictEqual(preprocess.calledOnce, true);
15183 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15186 it("should receive text even if a SourceCode object was given (with BOM).", () => {
15187 const code = "\uFEFFfoo";
15188 const preprocess = sinon.spy(text => text.split(" "));
15189 const configs = createFlatConfigArray([
15193 configs.normalizeSync();
15195 linter.verify(code, configs);
15196 const sourceCode = linter.getSourceCode();
15198 linter.verify(sourceCode, configs, { filename, preprocess });
15200 assert.strictEqual(preprocess.calledOnce, true);
15201 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15204 it("should catch preprocess error.", () => {
15205 const code = "foo";
15206 const preprocess = sinon.spy(() => {
15207 throw Object.assign(new SyntaxError("Invalid syntax"), {
15213 const configs = createFlatConfigArray([
15217 configs.normalizeSync();
15219 const messages = linter.verify(code, configs, { filename, preprocess });
15221 assert.strictEqual(preprocess.calledOnce, true);
15222 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
15223 assert.deepStrictEqual(messages, [
15228 message: "Preprocessing error: Invalid syntax",
15236 describe("postprocessors", () => {
15237 it("should receive result and filename.", () => {
15238 const code = "foo bar baz";
15239 const preprocess = sinon.spy(text => text.split(" "));
15240 const postprocess = sinon.spy(text => [text]);
15241 const configs = createFlatConfigArray([
15245 configs.normalizeSync();
15247 linter.verify(code, configs, { filename, postprocess, preprocess });
15249 assert.strictEqual(postprocess.calledOnce, true);
15250 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
15253 it("should apply a postprocessor to the reported messages", () => {
15254 const code = "foo bar baz";
15255 const configs = createFlatConfigArray([
15257 { rules: { "test/report-original-text": "error" } }
15260 configs.normalizeSync();
15262 const problems = linter.verify(
15266 preprocess: input => input.split(" "),
15269 * Apply a postprocessor that updates the locations of the reported problems
15270 * to make sure they correspond to the locations in the original text.
15272 postprocess(problemLists) {
15273 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
15274 return problemLists.reduce(
15275 (combinedList, problemList, index) =>
15276 combinedList.concat(
15283 message: problem.message.toUpperCase(),
15284 column: problem.column + index * 4
15294 const suppressedMessages = linter.getSuppressedMessages();
15296 assert.strictEqual(problems.length, 3);
15297 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
15298 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
15300 assert.strictEqual(suppressedMessages.length, 0);
15303 it("should use postprocessed problem ranges when applying autofixes", () => {
15304 const code = "foo bar baz";
15305 const configs = createFlatConfigArray([
15311 "capitalize-identifiers": {
15318 if (node.name !== node.name.toUpperCase()) {
15321 message: "Capitalize this identifier",
15322 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
15333 { rules: { "test2/capitalize-identifiers": "error" } }
15336 configs.normalizeSync();
15338 const fixResult = linter.verifyAndFix(
15344 * Apply a postprocessor that updates the locations of autofixes
15345 * to make sure they correspond to locations in the original text.
15347 preprocess: input => input.split(" "),
15348 postprocess(problemLists) {
15349 return problemLists.reduce(
15350 (combinedProblems, problemList, blockIndex) =>
15351 combinedProblems.concat(
15352 problemList.map(problem =>
15353 Object.assign(problem, {
15355 text: problem.fix.text,
15356 range: problem.fix.range.map(
15357 rangeIndex => rangeIndex + blockIndex * 4
15367 const suppressedMessages = linter.getSuppressedMessages();
15369 assert.strictEqual(fixResult.fixed, true);
15370 assert.strictEqual(fixResult.messages.length, 0);
15371 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
15373 assert.strictEqual(suppressedMessages.length, 0);
15378 describe("Edge cases", () => {
15380 describe("Modules", () => {
15381 const moduleConfig = {
15383 sourceType: "module",
15388 it("should properly parse import statements when sourceType is module", () => {
15389 const code = "import foo from 'foo';";
15390 const messages = linter.verify(code, moduleConfig);
15391 const suppressedMessages = linter.getSuppressedMessages();
15393 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15394 assert.strictEqual(suppressedMessages.length, 0);
15397 it("should properly parse import all statements when sourceType is module", () => {
15398 const code = "import * as foo from 'foo';";
15399 const messages = linter.verify(code, moduleConfig);
15400 const suppressedMessages = linter.getSuppressedMessages();
15402 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15403 assert.strictEqual(suppressedMessages.length, 0);
15406 it("should properly parse default export statements when sourceType is module", () => {
15407 const code = "export default function initialize() {}";
15408 const messages = linter.verify(code, moduleConfig);
15409 const suppressedMessages = linter.getSuppressedMessages();
15411 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
15412 assert.strictEqual(suppressedMessages.length, 0);
15418 // https://github.com/eslint/eslint/issues/9687
15419 it("should report an error when invalid languageOptions found", () => {
15420 let messages = linter.verify("", { languageOptions: { ecmaVersion: 222 } });
15422 assert.deepStrictEqual(messages.length, 1);
15423 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
15425 assert.throws(() => {
15426 linter.verify("", { languageOptions: { sourceType: "foo" } });
15427 }, /Expected "script", "module", or "commonjs"./u);
15430 messages = linter.verify("", { languageOptions: { ecmaVersion: 5, sourceType: "module" } });
15431 const suppressedMessages = linter.getSuppressedMessages();
15433 assert.deepStrictEqual(messages.length, 1);
15434 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
15436 assert.strictEqual(suppressedMessages.length, 0);
15439 it("should not crash when invalid parentheses syntax is encountered", () => {
15440 linter.verify("left = (aSize.width/2) - ()");
15443 it("should not crash when let is used inside of switch case", () => {
15444 linter.verify("switch(foo) { case 1: let bar=2; }", { languageOptions: { ecmaVersion: 6 } });
15447 it("should not crash when parsing destructured assignment", () => {
15448 linter.verify("var { a='a' } = {};", { languageOptions: { ecmaVersion: 6 } });
15451 it("should report syntax error when a keyword exists in object property shorthand", () => {
15452 const messages = linter.verify("let a = {this}", { languageOptions: { ecmaVersion: 6 } });
15453 const suppressedMessages = linter.getSuppressedMessages();
15455 assert.strictEqual(messages.length, 1);
15456 assert.strictEqual(messages[0].fatal, true);
15458 assert.strictEqual(suppressedMessages.length, 0);
15461 it("should not crash when we reuse the SourceCode object", () => {
15466 ecmaFeatures: { jsx: true }
15471 linter.verify("function render() { return <div className='test'>{hello}</div> }", config);
15472 linter.verify(linter.getSourceCode(), config);
15475 it("should reuse the SourceCode object", () => {
15483 "save-ast1": () => ({
15489 "save-ast2": () => ({
15501 ecmaFeatures: { jsx: true }
15507 linter.verify("function render() { return <div className='test'>{hello}</div> }", { ...config, rules: { "test/save-ast1": "error" } });
15508 linter.verify(linter.getSourceCode(), { ...config, rules: { "test/save-ast2": "error" } });
15510 assert(ast1 !== null);
15511 assert(ast2 !== null);
15512 assert(ast1 === ast2);
15515 it("should not modify config object passed as argument", () => {
15518 Object.freeze(config);
15519 linter.verify("var", config);
15522 it("should pass 'id' to rule contexts with the rule id", () => {
15524 const spy = sinon.spy(context => {
15525 assert.strictEqual(context.id, "test/foo-bar-baz");
15538 "test/foo-bar-baz": "error"
15543 linter.verify("x", config);
15544 assert(spy.calledOnce);
15548 describe("when evaluating an empty string", () => {
15549 it("runs rules", () => {
15555 "no-programs": context => ({
15557 context.report({ node, message: "No programs allowed." });
15564 "test/no-programs": "error"
15568 assert.strictEqual(
15569 linter.verify("", config).length,