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("context.getSource()", () => {
221 const code = TEST_CODE;
223 it("should retrieve all text when used without parameters", () => {
225 const config = { rules: { checker: "error" } };
228 linter.defineRule("checker", context => {
229 spy = sinon.spy(() => {
230 assert.strictEqual(context.getSource(), TEST_CODE);
232 return { Program: spy };
235 linter.verify(code, config);
236 assert(spy && spy.calledOnce);
239 it("should retrieve all text for root node", () => {
240 const config = { rules: { checker: "error" } };
243 linter.defineRule("checker", context => {
244 spy = sinon.spy(node => {
245 assert.strictEqual(context.getSource(node), TEST_CODE);
247 return { Program: spy };
250 linter.verify(code, config);
251 assert(spy && spy.calledOnce);
254 it("should clamp to valid range when retrieving characters before start of source", () => {
255 const config = { rules: { checker: "error" } };
258 linter.defineRule("checker", context => {
259 spy = sinon.spy(node => {
260 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
262 return { Program: spy };
265 linter.verify(code, config);
266 assert(spy && spy.calledOnce);
269 it("should retrieve all text for binary expression", () => {
270 const config = { rules: { checker: "error" } };
273 linter.defineRule("checker", context => {
274 spy = sinon.spy(node => {
275 assert.strictEqual(context.getSource(node), "6 * 7");
277 return { BinaryExpression: spy };
280 linter.verify(code, config);
281 assert(spy && spy.calledOnce);
284 it("should retrieve all text plus two characters before for binary expression", () => {
285 const config = { rules: { checker: "error" } };
288 linter.defineRule("checker", context => {
289 spy = sinon.spy(node => {
290 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
292 return { BinaryExpression: spy };
295 linter.verify(code, config);
296 assert(spy && spy.calledOnce);
299 it("should retrieve all text plus one character after for binary expression", () => {
300 const config = { rules: { checker: "error" } };
303 linter.defineRule("checker", context => {
304 spy = sinon.spy(node => {
305 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
307 return { BinaryExpression: spy };
310 linter.verify(code, config);
311 assert(spy && spy.calledOnce);
314 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
315 const config = { rules: { checker: "error" } };
318 linter.defineRule("checker", context => {
319 spy = sinon.spy(node => {
320 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
322 return { BinaryExpression: spy };
325 linter.verify(code, config);
326 assert(spy && spy.calledOnce);
331 describe("when calling context.getAncestors", () => {
332 const code = TEST_CODE;
334 it("should retrieve all ancestors when used", () => {
336 const config = { rules: { checker: "error" } };
339 linter.defineRule("checker", context => {
340 spy = sinon.spy(() => {
341 const ancestors = context.getAncestors();
343 assert.strictEqual(ancestors.length, 3);
345 return { BinaryExpression: spy };
348 linter.verify(code, config, filename, true);
349 assert(spy && spy.calledOnce);
352 it("should retrieve empty ancestors for root node", () => {
353 const config = { rules: { checker: "error" } };
356 linter.defineRule("checker", context => {
357 spy = sinon.spy(() => {
358 const ancestors = context.getAncestors();
360 assert.strictEqual(ancestors.length, 0);
363 return { Program: spy };
366 linter.verify(code, config);
367 assert(spy && spy.calledOnce);
371 describe("when calling context.getNodeByRangeIndex", () => {
372 const code = TEST_CODE;
374 it("should retrieve a node starting at the given index", () => {
375 const config = { rules: { checker: "error" } };
376 const spy = sinon.spy(context => {
377 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
381 linter.defineRule("checker", spy);
382 linter.verify(code, config);
383 assert(spy.calledOnce);
386 it("should retrieve a node containing the given index", () => {
387 const config = { rules: { checker: "error" } };
388 const spy = sinon.spy(context => {
389 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
393 linter.defineRule("checker", spy);
394 linter.verify(code, config);
395 assert(spy.calledOnce);
398 it("should retrieve a node that is exactly the given index", () => {
399 const config = { rules: { checker: "error" } };
400 const spy = sinon.spy(context => {
401 const node = context.getNodeByRangeIndex(13);
403 assert.strictEqual(node.type, "Literal");
404 assert.strictEqual(node.value, 6);
408 linter.defineRule("checker", spy);
409 linter.verify(code, config);
410 assert(spy.calledOnce);
413 it("should retrieve a node ending with the given index", () => {
414 const config = { rules: { checker: "error" } };
415 const spy = sinon.spy(context => {
416 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
420 linter.defineRule("checker", spy);
421 linter.verify(code, config);
422 assert(spy.calledOnce);
425 it("should retrieve the deepest node containing the given index", () => {
426 const config = { rules: { checker: "error" } };
427 const spy = sinon.spy(context => {
428 const node1 = context.getNodeByRangeIndex(14);
430 assert.strictEqual(node1.type, "BinaryExpression");
432 const node2 = context.getNodeByRangeIndex(3);
434 assert.strictEqual(node2.type, "VariableDeclaration");
438 linter.defineRule("checker", spy);
439 linter.verify(code, config);
440 assert(spy.calledOnce);
443 it("should return null if the index is outside the range of any node", () => {
444 const config = { rules: { checker: "error" } };
445 const spy = sinon.spy(context => {
446 const node1 = context.getNodeByRangeIndex(-1);
448 assert.isNull(node1);
450 const node2 = context.getNodeByRangeIndex(-99);
452 assert.isNull(node2);
456 linter.defineRule("checker", spy);
457 linter.verify(code, config);
458 assert(spy.calledOnce);
463 describe("when calling context.getScope", () => {
464 const code = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
466 it("should retrieve the global scope correctly from a Program", () => {
467 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
470 linter.defineRule("checker", context => {
471 spy = sinon.spy(() => {
472 const scope = context.getScope();
474 assert.strictEqual(scope.type, "global");
476 return { Program: spy };
479 linter.verify(code, config);
480 assert(spy && spy.calledOnce);
483 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
484 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
487 linter.defineRule("checker", context => {
488 spy = sinon.spy(() => {
489 const scope = context.getScope();
491 assert.strictEqual(scope.type, "function");
493 return { FunctionDeclaration: spy };
496 linter.verify(code, config);
497 assert(spy && spy.calledTwice);
500 it("should retrieve the function scope correctly from a LabeledStatement", () => {
501 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
504 linter.defineRule("checker", context => {
505 spy = sinon.spy(() => {
506 const scope = context.getScope();
508 assert.strictEqual(scope.type, "function");
509 assert.strictEqual(scope.block.id.name, "foo");
511 return { LabeledStatement: spy };
514 linter.verify(code, config);
515 assert(spy && spy.calledOnce);
518 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
519 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
522 linter.defineRule("checker", context => {
523 spy = sinon.spy(() => {
524 const scope = context.getScope();
526 assert.strictEqual(scope.type, "function");
527 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
530 return { ReturnStatement: spy };
533 linter.verify(code, config);
534 assert(spy && spy.calledOnce);
537 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
538 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
541 linter.defineRule("checker", context => {
542 spy = sinon.spy(() => {
543 const scope = context.getScope();
545 assert.strictEqual(scope.type, "switch");
546 assert.strictEqual(scope.block.type, "SwitchStatement");
549 return { SwitchStatement: spy };
552 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
553 assert(spy && spy.calledOnce);
556 it("should retrieve the function scope correctly from within a BlockStatement", () => {
557 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
560 linter.defineRule("checker", context => {
561 spy = sinon.spy(() => {
562 const scope = context.getScope();
564 assert.strictEqual(scope.type, "block");
565 assert.strictEqual(scope.block.type, "BlockStatement");
568 return { BlockStatement: spy };
571 linter.verify("var x; {let y = 1}", config);
572 assert(spy && spy.calledOnce);
575 it("should retrieve the function scope correctly from within a nested block statement", () => {
576 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
579 linter.defineRule("checker", context => {
580 spy = sinon.spy(() => {
581 const scope = context.getScope();
583 assert.strictEqual(scope.type, "block");
584 assert.strictEqual(scope.block.type, "BlockStatement");
587 return { BlockStatement: spy };
590 linter.verify("if (true) { let x = 1 }", config);
591 assert(spy && spy.calledOnce);
594 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
595 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
598 linter.defineRule("checker", context => {
599 spy = sinon.spy(() => {
600 const scope = context.getScope();
602 assert.strictEqual(scope.type, "function");
603 assert.strictEqual(scope.block.type, "FunctionDeclaration");
606 return { FunctionDeclaration: spy };
609 linter.verify("function foo() {}", config);
610 assert(spy && spy.calledOnce);
613 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
614 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
617 linter.defineRule("checker", context => {
618 spy = sinon.spy(() => {
619 const scope = context.getScope();
621 assert.strictEqual(scope.type, "function");
622 assert.strictEqual(scope.block.type, "FunctionExpression");
625 return { FunctionExpression: spy };
628 linter.verify("(function foo() {})();", config);
629 assert(spy && spy.calledOnce);
632 it("should retrieve the catch scope correctly from within a CatchClause", () => {
633 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
636 linter.defineRule("checker", context => {
637 spy = sinon.spy(() => {
638 const scope = context.getScope();
640 assert.strictEqual(scope.type, "catch");
641 assert.strictEqual(scope.block.type, "CatchClause");
644 return { CatchClause: spy };
647 linter.verify("try {} catch (err) {}", config);
648 assert(spy && spy.calledOnce);
651 it("should retrieve module scope correctly from an ES6 module", () => {
652 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } };
655 linter.defineRule("checker", context => {
656 spy = sinon.spy(() => {
657 const scope = context.getScope();
659 assert.strictEqual(scope.type, "module");
662 return { AssignmentExpression: spy };
665 linter.verify("var foo = {}; foo.bar = 1;", config);
666 assert(spy && spy.calledOnce);
669 it("should retrieve function scope correctly when globalReturn is true", () => {
670 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, ecmaFeatures: { globalReturn: true } } };
673 linter.defineRule("checker", context => {
674 spy = sinon.spy(() => {
675 const scope = context.getScope();
677 assert.strictEqual(scope.type, "function");
680 return { AssignmentExpression: spy };
683 linter.verify("var foo = {}; foo.bar = 1;", config);
684 assert(spy && spy.calledOnce);
688 describe("marking variables as used", () => {
689 it("should mark variables in current scope as used", () => {
690 const code = "var a = 1, b = 2;";
693 linter.defineRule("checker", context => {
694 spy = sinon.spy(() => {
695 assert.isTrue(context.markVariableAsUsed("a"));
697 const scope = context.getScope();
699 assert.isTrue(getVariable(scope, "a").eslintUsed);
700 assert.notOk(getVariable(scope, "b").eslintUsed);
703 return { "Program:exit": spy };
706 linter.verify(code, { rules: { checker: "error" } });
707 assert(spy && spy.calledOnce);
709 it("should mark variables in function args as used", () => {
710 const code = "function abc(a, b) { return 1; }";
713 linter.defineRule("checker", context => {
714 spy = sinon.spy(() => {
715 assert.isTrue(context.markVariableAsUsed("a"));
717 const scope = context.getScope();
719 assert.isTrue(getVariable(scope, "a").eslintUsed);
720 assert.notOk(getVariable(scope, "b").eslintUsed);
723 return { ReturnStatement: spy };
726 linter.verify(code, { rules: { checker: "error" } });
727 assert(spy && spy.calledOnce);
729 it("should mark variables in higher scopes as used", () => {
730 const code = "var a, b; function abc() { return 1; }";
731 let returnSpy, exitSpy;
733 linter.defineRule("checker", context => {
734 returnSpy = sinon.spy(() => {
735 assert.isTrue(context.markVariableAsUsed("a"));
737 exitSpy = sinon.spy(() => {
738 const scope = context.getScope();
740 assert.isTrue(getVariable(scope, "a").eslintUsed);
741 assert.notOk(getVariable(scope, "b").eslintUsed);
744 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
747 linter.verify(code, { rules: { checker: "error" } });
748 assert(returnSpy && returnSpy.calledOnce);
749 assert(exitSpy && exitSpy.calledOnce);
752 it("should mark variables in Node.js environment as used", () => {
753 const code = "var a = 1, b = 2;";
756 linter.defineRule("checker", context => {
757 spy = sinon.spy(() => {
758 const globalScope = context.getScope(),
759 childScope = globalScope.childScopes[0];
761 assert.isTrue(context.markVariableAsUsed("a"));
763 assert.isTrue(getVariable(childScope, "a").eslintUsed);
764 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
767 return { "Program:exit": spy };
770 linter.verify(code, { rules: { checker: "error" }, env: { node: true } });
771 assert(spy && spy.calledOnce);
774 it("should mark variables in modules as used", () => {
775 const code = "var a = 1, b = 2;";
778 linter.defineRule("checker", context => {
779 spy = sinon.spy(() => {
780 const globalScope = context.getScope(),
781 childScope = globalScope.childScopes[0];
783 assert.isTrue(context.markVariableAsUsed("a"));
785 assert.isTrue(getVariable(childScope, "a").eslintUsed);
786 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
789 return { "Program:exit": spy };
792 linter.verify(code, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } }, filename, true);
793 assert(spy && spy.calledOnce);
796 it("should return false if the given variable is not found", () => {
797 const code = "var a = 1, b = 2;";
800 linter.defineRule("checker", context => {
801 spy = sinon.spy(() => {
802 assert.isFalse(context.markVariableAsUsed("c"));
805 return { "Program:exit": spy };
808 linter.verify(code, { rules: { checker: "error" } });
809 assert(spy && spy.calledOnce);
813 describe("when evaluating code", () => {
814 const code = TEST_CODE;
816 it("events for each node type should fire", () => {
817 const config = { rules: { checker: "error" } };
819 // spies for various AST node types
820 const spyLiteral = sinon.spy(),
821 spyVariableDeclarator = sinon.spy(),
822 spyVariableDeclaration = sinon.spy(),
823 spyIdentifier = sinon.spy(),
824 spyBinaryExpression = sinon.spy();
826 linter.defineRule("checker", () => ({
828 VariableDeclarator: spyVariableDeclarator,
829 VariableDeclaration: spyVariableDeclaration,
830 Identifier: spyIdentifier,
831 BinaryExpression: spyBinaryExpression
834 const messages = linter.verify(code, config, filename, true);
836 assert.strictEqual(messages.length, 0);
837 sinon.assert.calledOnce(spyVariableDeclaration);
838 sinon.assert.calledOnce(spyVariableDeclarator);
839 sinon.assert.calledOnce(spyIdentifier);
840 sinon.assert.calledTwice(spyLiteral);
841 sinon.assert.calledOnce(spyBinaryExpression);
844 it("should throw an error if a rule reports a problem without a message", () => {
845 linter.defineRule("invalid-report", context => ({
847 context.report({ node });
852 () => linter.verify("foo", { rules: { "invalid-report": "error" } }),
854 "Missing `message` property in report() call; add a message that describes the linting problem."
859 describe("when config has shared settings for rules", () => {
860 const code = "test-rule";
862 it("should pass settings to all rules", () => {
863 linter.defineRule(code, context => ({
865 context.report(node, context.settings.info);
869 const config = { rules: {}, settings: { info: "Hello" } };
871 config.rules[code] = 1;
873 const messages = linter.verify("0", config, filename);
875 assert.strictEqual(messages.length, 1);
876 assert.strictEqual(messages[0].message, "Hello");
879 it("should not have any settings if they were not passed in", () => {
880 linter.defineRule(code, context => ({
882 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
883 context.report(node, "Settings should be empty");
888 const config = { rules: {} };
890 config.rules[code] = 1;
892 const messages = linter.verify("0", config, filename);
894 assert.strictEqual(messages.length, 0);
898 describe("when config has parseOptions", () => {
900 it("should pass ecmaFeatures to all rules when provided on config", () => {
902 const parserOptions = {
909 linter.defineRule("test-rule", sinon.mock().withArgs(
910 sinon.match({ parserOptions })
913 const config = { rules: { "test-rule": 2 }, parserOptions };
915 linter.verify("0", config, filename);
918 it("should pass parserOptions to all rules when default parserOptions is used", () => {
920 const parserOptions = {};
922 linter.defineRule("test-rule", sinon.mock().withArgs(
923 sinon.match({ parserOptions })
926 const config = { rules: { "test-rule": 2 } };
928 linter.verify("0", config, filename);
933 describe("when a custom parser is defined using defineParser", () => {
935 it("should be able to define a custom parser", () => {
937 parseForESLint: function parse(code, options) {
939 ast: esprima.parse(code, options),
951 linter.defineParser("test-parser", parser);
952 const config = { rules: {}, parser: "test-parser" };
953 const messages = linter.verify("0", config, filename);
955 assert.strictEqual(messages.length, 0);
960 describe("when config has parser", () => {
962 it("should pass parser as parserPath to all rules when provided on config", () => {
964 const alternateParser = "esprima";
966 linter.defineParser("esprima", esprima);
967 linter.defineRule("test-rule", sinon.mock().withArgs(
968 sinon.match({ parserPath: alternateParser })
971 const config = { rules: { "test-rule": 2 }, parser: alternateParser };
973 linter.verify("0", config, filename);
976 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
977 const config = { rules: {}, parser: "enhanced-parser" };
979 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
980 const messages = linter.verify("0", config, filename);
982 assert.strictEqual(messages.length, 0);
985 it("should expose parser services when using parseForESLint() and services are specified", () => {
986 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
987 linter.defineRule("test-service-rule", context => ({
991 message: context.parserServices.test.getMessage()
996 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
997 const messages = linter.verify("0", config, filename);
999 assert.strictEqual(messages.length, 1);
1000 assert.strictEqual(messages[0].message, "Hi!");
1003 it("should use the same parserServices if source code object is reused", () => {
1004 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1005 linter.defineRule("test-service-rule", context => ({
1009 message: context.parserServices.test.getMessage()
1014 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
1015 const messages = linter.verify("0", config, filename);
1017 assert.strictEqual(messages.length, 1);
1018 assert.strictEqual(messages[0].message, "Hi!");
1020 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
1022 assert.strictEqual(messages2.length, 1);
1023 assert.strictEqual(messages2[0].message, "Hi!");
1026 it("should pass parser as parserPath to all rules when default parser is used", () => {
1027 linter.defineRule("test-rule", sinon.mock().withArgs(
1028 sinon.match({ parserPath: "espree" })
1031 const config = { rules: { "test-rule": 2 } };
1033 linter.verify("0", config, filename);
1039 describe("when passing in configuration values for rules", () => {
1040 const code = "var answer = 6 * 7";
1042 it("should be configurable by only setting the integer value", () => {
1043 const rule = "semi",
1044 config = { rules: {} };
1046 config.rules[rule] = 1;
1048 const messages = linter.verify(code, config, filename, true);
1050 assert.strictEqual(messages.length, 1);
1051 assert.strictEqual(messages[0].ruleId, rule);
1054 it("should be configurable by only setting the string value", () => {
1055 const rule = "semi",
1056 config = { rules: {} };
1058 config.rules[rule] = "warn";
1060 const messages = linter.verify(code, config, filename, true);
1062 assert.strictEqual(messages.length, 1);
1063 assert.strictEqual(messages[0].severity, 1);
1064 assert.strictEqual(messages[0].ruleId, rule);
1067 it("should be configurable by passing in values as an array", () => {
1068 const rule = "semi",
1069 config = { rules: {} };
1071 config.rules[rule] = [1];
1073 const messages = linter.verify(code, config, filename, true);
1075 assert.strictEqual(messages.length, 1);
1076 assert.strictEqual(messages[0].ruleId, rule);
1079 it("should be configurable by passing in string value as an array", () => {
1080 const rule = "semi",
1081 config = { rules: {} };
1083 config.rules[rule] = ["warn"];
1085 const messages = linter.verify(code, config, filename, true);
1087 assert.strictEqual(messages.length, 1);
1088 assert.strictEqual(messages[0].severity, 1);
1089 assert.strictEqual(messages[0].ruleId, rule);
1092 it("should not be configurable by setting other value", () => {
1093 const rule = "semi",
1094 config = { rules: {} };
1096 config.rules[rule] = "1";
1098 const messages = linter.verify(code, config, filename, true);
1100 assert.strictEqual(messages.length, 0);
1103 it("should process empty config", () => {
1105 const messages = linter.verify(code, config, filename, true);
1107 assert.strictEqual(messages.length, 0);
1111 describe("when evaluating code containing /*global */ and
/*globals */ blocks
", () => {
1113 it("variables should be available
in global scope
", () => {
1114 const config = { rules: { checker: "error
" }, globals: { Array: "off
", ConfigGlobal: "writeable
" } };
1116 /*global a b:true c:false d:readable e:writeable Math:off */
1119 /* global ConfigGlobal : readable */
1123 linter.defineRule("checker
", context => {
1124 spy = sinon.spy(() => {
1125 const scope = context.getScope();
1126 const a = getVariable(scope, "a
"),
1127 b = getVariable(scope, "b
"),
1128 c = getVariable(scope, "c
"),
1129 d = getVariable(scope, "d
"),
1130 e = getVariable(scope, "e
"),
1131 f = getVariable(scope, "f
"),
1132 mathGlobal = getVariable(scope, "Math
"),
1133 arrayGlobal = getVariable(scope, "Array
"),
1134 configGlobal = getVariable(scope, "ConfigGlobal
");
1136 assert.strictEqual(a.name, "a
");
1137 assert.strictEqual(a.writeable, false);
1138 assert.strictEqual(b.name, "b
");
1139 assert.strictEqual(b.writeable, true);
1140 assert.strictEqual(c.name, "c
");
1141 assert.strictEqual(c.writeable, false);
1142 assert.strictEqual(d.name, "d
");
1143 assert.strictEqual(d.writeable, false);
1144 assert.strictEqual(e.name, "e
");
1145 assert.strictEqual(e.writeable, true);
1146 assert.strictEqual(f.name, "f
");
1147 assert.strictEqual(f.writeable, true);
1148 assert.strictEqual(mathGlobal, null);
1149 assert.strictEqual(arrayGlobal, null);
1150 assert.strictEqual(configGlobal.name, "ConfigGlobal
");
1151 assert.strictEqual(configGlobal.writeable, false);
1154 return { Program: spy };
1157 linter.verify(code, config);
1158 assert(spy && spy.calledOnce);
1162 describe("when evaluating code containing a
/*global */ block
with sloppy whitespace
", () => {
1163 const code = "/* global a b : true c: false*/";
1165 it("variables should be available
in global scope
", () => {
1166 const config = { rules: { checker: "error
" } };
1169 linter.defineRule("checker
", context => {
1170 spy = sinon.spy(() => {
1171 const scope = context.getScope(),
1172 a = getVariable(scope, "a
"),
1173 b = getVariable(scope, "b
"),
1174 c = getVariable(scope, "c
");
1176 assert.strictEqual(a.name, "a
");
1177 assert.strictEqual(a.writeable, false);
1178 assert.strictEqual(b.name, "b
");
1179 assert.strictEqual(b.writeable, true);
1180 assert.strictEqual(c.name, "c
");
1181 assert.strictEqual(c.writeable, false);
1184 return { Program: spy };
1187 linter.verify(code, config);
1188 assert(spy && spy.calledOnce);
1192 describe("when evaluating code containing a
/*global */ block
with specific variables
", () => {
1193 const code = "/* global toString hasOwnProperty valueOf: true */";
1195 it("should not
throw an error
if comment block has global variables which are Object
.prototype contains
", () => {
1196 const config = { rules: { checker: "error
" } };
1198 linter.verify(code, config);
1202 describe("when evaluating code containing
/*eslint-env */ block
", () => {
1203 it("variables should be available
in global scope
", () => {
1204 const code = `/*${ESLINT_ENV} node*/ function f() {} /*${ESLINT_ENV} browser, foo*/`;
1205 const config = { rules: { checker: "error
" } };
1208 linter.defineRule("checker
", context => {
1209 spy = sinon.spy(() => {
1210 const scope = context.getScope(),
1211 exports = getVariable(scope, "exports
"),
1212 window = getVariable(scope, "window
");
1214 assert.strictEqual(exports.writeable, true);
1215 assert.strictEqual(window.writeable, false);
1218 return { Program: spy };
1221 linter.verify(code, config);
1222 assert(spy && spy.calledOnce);
1226 describe("when evaluating code containing
/*eslint-env */ block
with sloppy whitespace
", () => {
1227 const code = `/* ${ESLINT_ENV} ,, node , no-browser ,, */`;
1229 it("variables should be available
in global scope
", () => {
1230 const config = { rules: { checker: "error
" } };
1233 linter.defineRule("checker
", context => {
1234 spy = sinon.spy(() => {
1235 const scope = context.getScope(),
1236 exports = getVariable(scope, "exports
"),
1237 window = getVariable(scope, "window
");
1239 assert.strictEqual(exports.writeable, true);
1240 assert.strictEqual(window, null);
1243 return { Program: spy };
1246 linter.verify(code, config);
1247 assert(spy && spy.calledOnce);
1251 describe("when evaluating code containing
/*exported */ block
", () => {
1253 it("we should behave nicely when no matching variable is found
", () => {
1254 const code = "/* exported horse */";
1255 const config = { rules: {} };
1257 linter.verify(code, config, filename, true);
1260 it("variables should be exported
", () => {
1261 const code = "/* exported horse */\n\nvar horse
= 'circus'";
1262 const config = { rules: { checker: "error
" } };
1265 linter.defineRule("checker
", context => {
1266 spy = sinon.spy(() => {
1267 const scope = context.getScope(),
1268 horse = getVariable(scope, "horse
");
1270 assert.strictEqual(horse.eslintUsed, true);
1273 return { Program: spy };
1276 linter.verify(code, config);
1277 assert(spy && spy.calledOnce);
1280 it("undefined variables should not be exported
", () => {
1281 const code = "/* exported horse */\n\nhorse = 'circus'";
1282 const config = { rules: { checker: "error
" } };
1285 linter.defineRule("checker
", context => {
1286 spy = sinon.spy(() => {
1287 const scope = context.getScope(),
1288 horse = getVariable(scope, "horse
");
1290 assert.strictEqual(horse, null);
1293 return { Program: spy };
1296 linter.verify(code, config);
1297 assert(spy && spy.calledOnce);
1300 it("variables should be exported
in strict mode
", () => {
1301 const code = "/* exported horse */\n'use strict';\nvar horse
= 'circus'";
1302 const config = { rules: { checker: "error
" } };
1305 linter.defineRule("checker
", context => {
1306 spy = sinon.spy(() => {
1307 const scope = context.getScope(),
1308 horse = getVariable(scope, "horse
");
1310 assert.strictEqual(horse.eslintUsed, true);
1313 return { Program: spy };
1316 linter.verify(code, config);
1317 assert(spy && spy.calledOnce);
1320 it("variables should not be exported
in the es6 module environment
", () => {
1321 const code = "/* exported horse */\nvar horse
= 'circus'";
1322 const config = { rules: { checker: "error
" }, parserOptions: { ecmaVersion: 6, sourceType: "module
" } };
1325 linter.defineRule("checker
", context => {
1326 spy = sinon.spy(() => {
1327 const scope = context.getScope(),
1328 horse = getVariable(scope, "horse
");
1330 assert.strictEqual(horse, null); // there is no global scope at all
1333 return { Program: spy };
1336 linter.verify(code, config);
1337 assert(spy && spy.calledOnce);
1340 it("variables should not be exported when
in the node environment
", () => {
1341 const code = "/* exported horse */\nvar horse
= 'circus'";
1342 const config = { rules: { checker: "error
" }, env: { node: true } };
1345 linter.defineRule("checker
", context => {
1346 spy = sinon.spy(() => {
1347 const scope = context.getScope(),
1348 horse = getVariable(scope, "horse
");
1350 assert.strictEqual(horse, null); // there is no global scope at all
1353 return { Program: spy };
1356 linter.verify(code, config);
1357 assert(spy && spy.calledOnce);
1361 describe("when evaluating code containing a line comment
", () => {
1362 const code = "//global a \n function f() {}";
1364 it("should not introduce a global variable", () => {
1365 const config
= { rules
: { checker
: "error" } };
1368 linter
.defineRule("checker", context
=> {
1369 spy
= sinon
.spy(() => {
1370 const scope
= context
.getScope();
1372 assert
.strictEqual(getVariable(scope
, "a"), null);
1375 return { Program
: spy
};
1378 linter
.verify(code
, config
);
1379 assert(spy
&& spy
.calledOnce
);
1383 describe("when evaluating code containing normal block comments", () => {
1384 const code
= "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
1386 it("should not introduce a global variable", () => {
1387 const config
= { rules
: { checker
: "error" } };
1390 linter
.defineRule("checker", context
=> {
1391 spy
= sinon
.spy(() => {
1392 const scope
= context
.getScope();
1394 assert
.strictEqual(getVariable(scope
, "a"), null);
1395 assert
.strictEqual(getVariable(scope
, "b"), null);
1396 assert
.strictEqual(getVariable(scope
, "foo"), null);
1397 assert
.strictEqual(getVariable(scope
, "c"), null);
1400 return { Program
: spy
};
1403 linter
.verify(code
, config
);
1404 assert(spy
&& spy
.calledOnce
);
1408 describe("when evaluating any code", () => {
1411 it("builtin global variables should be available in the global scope", () => {
1412 const config
= { rules
: { checker
: "error" } };
1415 linter
.defineRule("checker", context
=> {
1416 spy
= sinon
.spy(() => {
1417 const scope
= context
.getScope();
1419 assert
.notStrictEqual(getVariable(scope
, "Object"), null);
1420 assert
.notStrictEqual(getVariable(scope
, "Array"), null);
1421 assert
.notStrictEqual(getVariable(scope
, "undefined"), null);
1424 return { Program
: spy
};
1427 linter
.verify(code
, config
);
1428 assert(spy
&& spy
.calledOnce
);
1431 it("ES6 global variables should not be available by default", () => {
1432 const config
= { rules
: { checker
: "error" } };
1435 linter
.defineRule("checker", context
=> {
1436 spy
= sinon
.spy(() => {
1437 const scope
= context
.getScope();
1439 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1440 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1441 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1444 return { Program
: spy
};
1447 linter
.verify(code
, config
);
1448 assert(spy
&& spy
.calledOnce
);
1451 it("ES6 global variables should be available in the es6 environment", () => {
1452 const config
= { rules
: { checker
: "error" }, env
: { es6
: true } };
1455 linter
.defineRule("checker", context
=> {
1456 spy
= sinon
.spy(() => {
1457 const scope
= context
.getScope();
1459 assert
.notStrictEqual(getVariable(scope
, "Promise"), null);
1460 assert
.notStrictEqual(getVariable(scope
, "Symbol"), null);
1461 assert
.notStrictEqual(getVariable(scope
, "WeakMap"), null);
1464 return { Program
: spy
};
1467 linter
.verify(code
, config
);
1468 assert(spy
&& spy
.calledOnce
);
1471 it("ES6 global variables can be disabled when the es6 environment is enabled", () => {
1472 const config
= { rules
: { checker
: "error" }, globals
: { Promise
: "off", Symbol
: "off", WeakMap
: "off" }, env
: { es6
: true } };
1475 linter
.defineRule("checker", context
=> {
1476 spy
= sinon
.spy(() => {
1477 const scope
= context
.getScope();
1479 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1480 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1481 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1484 return { Program
: spy
};
1487 linter
.verify(code
, config
);
1488 assert(spy
&& spy
.calledOnce
);
1492 describe("at any time", () => {
1493 const code
= "new-rule";
1495 it("can add a rule dynamically", () => {
1496 linter
.defineRule(code
, context
=> ({
1498 context
.report(node
, "message");
1502 const config
= { rules
: {} };
1504 config
.rules
[code
] = 1;
1506 const messages
= linter
.verify("0", config
, filename
);
1508 assert
.strictEqual(messages
.length
, 1);
1509 assert
.strictEqual(messages
[0].ruleId
, code
);
1510 assert
.strictEqual(messages
[0].nodeType
, "Literal");
1514 describe("at any time", () => {
1515 const code
= ["new-rule-0", "new-rule-1"];
1517 it("can add multiple rules dynamically", () => {
1518 const config
= { rules
: {} };
1519 const newRules
= {};
1521 code
.forEach(item
=> {
1522 config
.rules
[item
] = 1;
1523 newRules
[item
] = function(context
) {
1526 context
.report(node
, "message");
1531 linter
.defineRules(newRules
);
1533 const messages
= linter
.verify("0", config
, filename
);
1535 assert
.strictEqual(messages
.length
, code
.length
);
1536 code
.forEach(item
=> {
1537 assert
.ok(messages
.some(message
=> message
.ruleId
=== item
));
1539 messages
.forEach(message
=> {
1540 assert
.strictEqual(message
.nodeType
, "Literal");
1545 describe("at any time", () => {
1546 const code
= "filename-rule";
1548 it("has access to the filename", () => {
1549 linter
.defineRule(code
, context
=> ({
1551 context
.report(node
, context
.getFilename());
1555 const config
= { rules
: {} };
1557 config
.rules
[code
] = 1;
1559 const messages
= linter
.verify("0", config
, filename
);
1561 assert
.strictEqual(messages
[0].message
, filename
);
1564 it("has access to the physicalFilename", () => {
1565 linter
.defineRule(code
, context
=> ({
1567 context
.report(node
, context
.getPhysicalFilename());
1571 const config
= { rules
: {} };
1573 config
.rules
[code
] = 1;
1575 const messages
= linter
.verify("0", config
, filename
);
1577 assert
.strictEqual(messages
[0].message
, filename
);
1580 it("defaults filename to '<input>'", () => {
1581 linter
.defineRule(code
, context
=> ({
1583 context
.report(node
, context
.getFilename());
1587 const config
= { rules
: {} };
1589 config
.rules
[code
] = 1;
1591 const messages
= linter
.verify("0", config
);
1593 assert
.strictEqual(messages
[0].message
, "<input>");
1597 describe("when evaluating code with comments to enable rules", () => {
1599 it("should report a violation", () => {
1600 const code
= "/*eslint no-alert:1*/ alert('test');";
1601 const config
= { rules
: {} };
1603 const messages
= linter
.verify(code
, config
, filename
);
1605 assert
.strictEqual(messages
.length
, 1);
1606 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1607 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1608 assert
.include(messages
[0].nodeType
, "CallExpression");
1611 it("rules should not change initial config", () => {
1612 const config
= { rules
: { strict
: 2 } };
1613 const codeA
= "/*eslint strict: 0*/ function bar() { return 2; }";
1614 const codeB
= "function foo() { return 1; }";
1615 let messages
= linter
.verify(codeA
, config
, filename
, false);
1617 assert
.strictEqual(messages
.length
, 0);
1619 messages
= linter
.verify(codeB
, config
, filename
, false);
1620 assert
.strictEqual(messages
.length
, 1);
1623 it("rules should not change initial config", () => {
1624 const config
= { rules
: { quotes
: [2, "double"] } };
1625 const codeA
= "/*eslint quotes: 0*/ function bar() { return '2'; }";
1626 const codeB
= "function foo() { return '1'; }";
1627 let messages
= linter
.verify(codeA
, config
, filename
, false);
1629 assert
.strictEqual(messages
.length
, 0);
1631 messages
= linter
.verify(codeB
, config
, filename
, false);
1632 assert
.strictEqual(messages
.length
, 1);
1635 it("rules should not change initial config", () => {
1636 const config
= { rules
: { quotes
: [2, "double"] } };
1637 const codeA
= "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
1638 const codeB
= "function foo() { return '1'; }";
1639 let messages
= linter
.verify(codeA
, config
, filename
, false);
1641 assert
.strictEqual(messages
.length
, 0);
1643 messages
= linter
.verify(codeB
, config
, filename
, false);
1644 assert
.strictEqual(messages
.length
, 1);
1647 it("rules should not change initial config", () => {
1648 const config
= { rules
: { "no-unused-vars": [2, { vars
: "all" }] } };
1649 const codeA
= "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
1650 const codeB
= "var b = 55;";
1651 let messages
= linter
.verify(codeA
, config
, filename
, false);
1653 assert
.strictEqual(messages
.length
, 0);
1655 messages
= linter
.verify(codeB
, config
, filename
, false);
1656 assert
.strictEqual(messages
.length
, 1);
1660 describe("when evaluating code with invalid comments to enable rules", () => {
1661 it("should report a violation when the config is not a valid rule configuration", () => {
1662 assert
.deepStrictEqual(
1663 linter
.verify("/*eslint no-alert:true*/ alert('test');", {}),
1668 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",
1679 it("should report a violation when the config violates a rule's schema", () => {
1680 assert
.deepStrictEqual(
1681 linter
.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {}),
1686 message
: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
1698 describe("when evaluating code with comments to disable rules", () => {
1699 const code
= "/*eslint no-alert:0*/ alert('test');";
1701 it("should not report a violation", () => {
1702 const config
= { rules
: { "no-alert": 1 } };
1704 const messages
= linter
.verify(code
, config
, filename
);
1706 assert
.strictEqual(messages
.length
, 0);
1710 describe("when evaluating code with comments to disable rules", () => {
1713 it("should report an error when disabling a non-existent rule in inline comment", () => {
1714 code
= "/*eslint foo:0*/ ;";
1715 messages
= linter
.verify(code
, {}, filename
);
1716 assert
.strictEqual(messages
.length
, 1);
1717 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1719 code
= "/*eslint-disable foo*/ ;";
1720 messages
= linter
.verify(code
, {}, filename
);
1721 assert
.strictEqual(messages
.length
, 1);
1722 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1724 code
= "/*eslint-disable-line foo*/ ;";
1725 messages
= linter
.verify(code
, {}, filename
);
1726 assert
.strictEqual(messages
.length
, 1);
1727 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1729 code
= "/*eslint-disable-next-line foo*/ ;";
1730 messages
= linter
.verify(code
, {}, filename
);
1731 assert
.strictEqual(messages
.length
, 1);
1732 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1735 it("should not report an error, when disabling a non-existent rule in config", () => {
1736 messages
= linter
.verify("", { rules
: { foo
: 0 } }, filename
);
1738 assert
.strictEqual(messages
.length
, 0);
1741 it("should report an error, when config a non-existent rule in config", () => {
1742 messages
= linter
.verify("", { rules
: { foo
: 1 } }, filename
);
1743 assert
.strictEqual(messages
.length
, 1);
1744 assert
.strictEqual(messages
[0].severity
, 2);
1745 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1747 messages
= linter
.verify("", { rules
: { foo
: 2 } }, filename
);
1748 assert
.strictEqual(messages
.length
, 1);
1749 assert
.strictEqual(messages
[0].severity
, 2);
1750 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1754 describe("when evaluating code with comments to enable multiple rules", () => {
1755 const code
= "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
1757 it("should report a violation", () => {
1758 const config
= { rules
: {} };
1760 const messages
= linter
.verify(code
, config
, filename
);
1762 assert
.strictEqual(messages
.length
, 2);
1763 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1764 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1765 assert
.include(messages
[0].nodeType
, "CallExpression");
1766 assert
.strictEqual(messages
[1].ruleId
, "no-console");
1770 describe("when evaluating code with comments to enable and disable multiple rules", () => {
1771 const code
= "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
1773 it("should report a violation", () => {
1774 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
1776 const messages
= linter
.verify(code
, config
, filename
);
1778 assert
.strictEqual(messages
.length
, 1);
1779 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1780 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1781 assert
.include(messages
[0].nodeType
, "CallExpression");
1785 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
1788 linter
.defineRule("test-plugin/test-rule", context
=> ({
1790 if (node
.value
=== "trigger violation") {
1791 context
.report(node
, "Reporting violation.");
1797 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
1798 const config
= { rules
: {} };
1799 const code
= "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
1801 const messages
= linter
.verify(code
, config
, filename
);
1803 assert
.strictEqual(messages
.length
, 0);
1806 it("should not report a violation when inline comment disables plugin rule", () => {
1807 const code
= "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
1808 const config
= { rules
: { "test-plugin/test-rule": 1 } };
1810 const messages
= linter
.verify(code
, config
, filename
);
1812 assert
.strictEqual(messages
.length
, 0);
1815 it("should report a violation when the report is right before the comment", () => {
1816 const code
= " /* eslint-disable */ ";
1818 linter
.defineRule("checker", context
=> ({
1820 context
.report({ loc
: { line
: 1, column
: 0 }, message
: "foo" });
1823 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
1825 assert
.strictEqual(problems
.length
, 1);
1826 assert
.strictEqual(problems
[0].message
, "foo");
1829 it("should not report a violation when the report is right at the start of the comment", () => {
1830 const code
= " /* eslint-disable */ ";
1832 linter
.defineRule("checker", context
=> ({
1834 context
.report({ loc
: { line
: 1, column
: 1 }, message
: "foo" });
1837 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
1839 assert
.strictEqual(problems
.length
, 0);
1842 it("rules should not change initial config", () => {
1843 const config
= { rules
: { "test-plugin/test-rule": 2 } };
1844 const codeA
= "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
1845 const codeB
= "var a = \"trigger violation\";";
1846 let messages
= linter
.verify(codeA
, config
, filename
, false);
1848 assert
.strictEqual(messages
.length
, 0);
1850 messages
= linter
.verify(codeB
, config
, filename
, false);
1851 assert
.strictEqual(messages
.length
, 1);
1855 describe("when evaluating code with comments to enable and disable all reporting", () => {
1856 it("should report a violation", () => {
1859 "/*eslint-disable */",
1861 "/*eslint-enable */",
1864 const config
= { rules
: { "no-alert": 1 } };
1866 const messages
= linter
.verify(code
, config
, filename
);
1868 assert
.strictEqual(messages
.length
, 1);
1869 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1870 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1871 assert
.include(messages
[0].nodeType
, "CallExpression");
1872 assert
.strictEqual(messages
[0].line
, 4);
1875 it("should not report a violation", () => {
1877 "/*eslint-disable */",
1881 const config
= { rules
: { "no-alert": 1 } };
1883 const messages
= linter
.verify(code
, config
, filename
);
1885 assert
.strictEqual(messages
.length
, 0);
1888 it("should not report a violation", () => {
1890 " alert('test1');/*eslint-disable */\n",
1892 " alert('test');\n",
1893 "/*eslint-enable */alert('test2');"
1895 const config
= { rules
: { "no-alert": 1 } };
1897 const messages
= linter
.verify(code
, config
, filename
);
1899 assert
.strictEqual(messages
.length
, 2);
1900 assert
.strictEqual(messages
[0].column
, 21);
1901 assert
.strictEqual(messages
[1].column
, 19);
1904 it("should report a violation", () => {
1907 "/*eslint-disable */",
1909 "/*eslint-disable */",
1911 "/*eslint-enable*/",
1916 const config
= { rules
: { "no-alert": 1 } };
1918 const messages
= linter
.verify(code
, config
, filename
);
1920 assert
.strictEqual(messages
.length
, 1);
1924 it("should not report a violation", () => {
1926 "/*eslint-disable */",
1927 "(function(){ var b = 44;})()",
1928 "/*eslint-enable */;any();"
1931 const config
= { rules
: { "no-unused-vars": 1 } };
1933 const messages
= linter
.verify(code
, config
, filename
);
1935 assert
.strictEqual(messages
.length
, 0);
1938 it("should not report a violation", () => {
1940 "(function(){ /*eslint-disable */ var b = 44;})()",
1941 "/*eslint-enable */;any();"
1944 const config
= { rules
: { "no-unused-vars": 1 } };
1946 const messages
= linter
.verify(code
, config
, filename
);
1948 assert
.strictEqual(messages
.length
, 0);
1952 describe("when evaluating code with comments to ignore reporting on specific rules on a specific line", () => {
1954 describe("eslint-disable-line", () => {
1955 it("should report a violation", () => {
1957 "alert('test'); // eslint-disable-line no-alert",
1958 "console.log('test');" // here
1967 const messages
= linter
.verify(code
, config
, filename
);
1969 assert
.strictEqual(messages
.length
, 1);
1971 assert
.strictEqual(messages
[0].ruleId
, "no-console");
1974 it("should report a violation", () => {
1976 "alert('test'); // eslint-disable-line no-alert",
1977 "console.log('test'); // eslint-disable-line no-console",
1978 "alert('test');" // here
1987 const messages
= linter
.verify(code
, config
, filename
);
1989 assert
.strictEqual(messages
.length
, 1);
1991 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1994 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
1996 "/* eslint-disable-line",
1998 "*/ console.log('test');" // here
2006 const messages
= linter
.verify(code
, config
, filename
);
2008 assert
.strictEqual(messages
.length
, 2);
2010 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2013 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
2015 "alert('test'); /* eslint-disable-line ",
2024 const messages
= linter
.verify(code
, config
);
2026 assert
.deepStrictEqual(messages
, [
2034 message
: "Unexpected alert.",
2035 messageId
: "unexpected",
2036 nodeType
: "CallExpression"
2041 message
: "eslint-disable-line comment should not span multiple lines.",
2051 it("should not report a violation for eslint-disable-line in block comment", () => {
2053 "alert('test'); // eslint-disable-line no-alert",
2054 "alert('test'); /*eslint-disable-line no-alert*/"
2062 const messages
= linter
.verify(code
, config
, filename
);
2064 assert
.strictEqual(messages
.length
, 0);
2067 it("should not report a violation", () => {
2069 "alert('test'); // eslint-disable-line no-alert",
2070 "console.log('test'); // eslint-disable-line no-console"
2079 const messages
= linter
.verify(code
, config
, filename
);
2081 assert
.strictEqual(messages
.length
, 0);
2084 it("should not report a violation", () => {
2086 "alert('test') // eslint-disable-line no-alert, quotes, semi",
2087 "console.log('test'); // eslint-disable-line"
2092 quotes
: [1, "double"],
2093 semi
: [1, "always"],
2098 const messages
= linter
.verify(code
, config
, filename
);
2100 assert
.strictEqual(messages
.length
, 0);
2103 it("should not report a violation", () => {
2105 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
2106 "console.log('test'); /* eslint-disable-line */"
2111 quotes
: [1, "double"],
2112 semi
: [1, "always"],
2117 const messages
= linter
.verify(code
, config
, filename
);
2119 assert
.strictEqual(messages
.length
, 0);
2122 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2124 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
2129 quotes
: [1, "single"]
2132 const messages
= linter
.verify(code
, config
, filename
);
2134 assert
.strictEqual(messages
.length
, 0);
2138 describe("eslint-disable-next-line", () => {
2139 it("should ignore violation of specified rule on next line", () => {
2141 "// eslint-disable-next-line no-alert",
2143 "console.log('test');"
2151 const messages
= linter
.verify(code
, config
, filename
);
2153 assert
.strictEqual(messages
.length
, 1);
2154 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2157 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2159 "/* eslint-disable-next-line no-alert */",
2161 "console.log('test');"
2169 const messages
= linter
.verify(code
, config
, filename
);
2171 assert
.strictEqual(messages
.length
, 1);
2172 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2174 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2176 "/* eslint-disable-next-line no-alert */",
2184 const messages
= linter
.verify(code
, config
, filename
);
2186 assert
.strictEqual(messages
.length
, 0);
2189 it("should not ignore violation if block comment is not on a single line", () => {
2191 "/* eslint-disable-next-line",
2192 "no-alert */alert('test');"
2199 const messages
= linter
.verify(code
, config
, filename
);
2201 assert
.strictEqual(messages
.length
, 2);
2202 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2205 it("should ignore violations only of specified rule", () => {
2207 "// eslint-disable-next-line no-console",
2209 "console.log('test');"
2217 const messages
= linter
.verify(code
, config
, filename
);
2219 assert
.strictEqual(messages
.length
, 2);
2220 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2221 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2224 it("should ignore violations of multiple rules when specified", () => {
2226 "// eslint-disable-next-line no-alert, quotes",
2228 "console.log('test');"
2233 quotes
: [1, "single"],
2237 const messages
= linter
.verify(code
, config
, filename
);
2239 assert
.strictEqual(messages
.length
, 1);
2240 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2243 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2245 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
2251 quotes
: [1, "single"]
2254 const messages
= linter
.verify(code
, config
, filename
);
2256 assert
.strictEqual(messages
.length
, 0);
2259 it("should ignore violations of only the specified rule on next line", () => {
2261 "// eslint-disable-next-line quotes",
2263 "console.log('test');"
2268 quotes
: [1, "single"],
2272 const messages
= linter
.verify(code
, config
, filename
);
2274 assert
.strictEqual(messages
.length
, 2);
2275 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2276 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2279 it("should ignore violations of specified rule on next line only", () => {
2282 "// eslint-disable-next-line no-alert",
2284 "console.log('test');"
2292 const messages
= linter
.verify(code
, config
, filename
);
2294 assert
.strictEqual(messages
.length
, 2);
2295 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2296 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2299 it("should ignore all rule violations on next line if none specified", () => {
2301 "// eslint-disable-next-line",
2303 "console.log('test')"
2308 quotes
: [1, "single"],
2313 const messages
= linter
.verify(code
, config
, filename
);
2315 assert
.strictEqual(messages
.length
, 1);
2316 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2319 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
2322 "/* eslint-disable-next-line no-alert */",
2324 "console.log('test');"
2332 const messages
= linter
.verify(code
, config
, filename
);
2334 assert
.strictEqual(messages
.length
, 2);
2335 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2336 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2339 it("should report a violation", () => {
2341 "/* eslint-disable-next-line",
2344 "console.log('test');" // here
2353 const messages
= linter
.verify(code
, config
, filename
);
2355 assert
.strictEqual(messages
.length
, 2);
2357 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2360 it("should not ignore violations if comment is of the type hashbang", () => {
2362 "#! eslint-disable-next-line no-alert",
2364 "console.log('test');"
2372 const messages
= linter
.verify(code
, config
, filename
);
2374 assert
.strictEqual(messages
.length
, 2);
2375 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2376 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2381 describe("when evaluating code with comments to enable and disable reporting of specific rules", () => {
2383 it("should report a violation", () => {
2385 "/*eslint-disable no-alert */",
2387 "console.log('test');" // here
2389 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2391 const messages
= linter
.verify(code
, config
, filename
);
2393 assert
.strictEqual(messages
.length
, 1);
2395 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2398 it("should report no violation", () => {
2400 "/*eslint-disable no-unused-vars */",
2401 "var foo; // eslint-disable-line no-unused-vars",
2403 "/* eslint-enable no-unused-vars */" // here
2405 const config
= { rules
: { "no-unused-vars": 2 } };
2407 const messages
= linter
.verify(code
, config
, filename
);
2409 assert
.strictEqual(messages
.length
, 0);
2412 it("should report no violation", () => {
2414 "var foo1; // eslint-disable-line no-unused-vars",
2415 "var foo2; // eslint-disable-line no-unused-vars",
2416 "var foo3; // eslint-disable-line no-unused-vars",
2417 "var foo4; // eslint-disable-line no-unused-vars",
2418 "var foo5; // eslint-disable-line no-unused-vars"
2420 const config
= { rules
: { "no-unused-vars": 2 } };
2422 const messages
= linter
.verify(code
, config
, filename
);
2424 assert
.strictEqual(messages
.length
, 0);
2427 it("should report no violation", () => {
2429 "/* eslint-disable quotes */",
2430 "console.log(\"foo\");",
2431 "/* eslint-enable quotes */"
2433 const config
= { rules
: { quotes
: 2 } };
2435 const messages
= linter
.verify(code
, config
, filename
);
2437 assert
.strictEqual(messages
.length
, 0);
2440 it("should report a violation", () => {
2442 "/*eslint-disable no-alert, no-console */",
2444 "console.log('test');",
2445 "/*eslint-enable*/",
2447 "alert('test');", // here
2448 "console.log('test');" // here
2450 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2452 const messages
= linter
.verify(code
, config
, filename
);
2454 assert
.strictEqual(messages
.length
, 2);
2456 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2457 assert
.strictEqual(messages
[0].line
, 5);
2458 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2459 assert
.strictEqual(messages
[1].line
, 6);
2462 it("should report a violation", () => {
2464 "/*eslint-disable no-alert */",
2466 "console.log('test');",
2467 "/*eslint-enable no-console */",
2469 "alert('test');" // here
2471 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2473 const messages
= linter
.verify(code
, config
, filename
);
2475 assert
.strictEqual(messages
.length
, 1);
2477 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2481 it("should report a violation", () => {
2483 "/*eslint-disable no-alert, no-console */",
2485 "console.log('test');",
2486 "/*eslint-enable no-alert*/",
2488 "alert('test');", // here
2489 "console.log('test');"
2491 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2493 const messages
= linter
.verify(code
, config
, filename
);
2495 assert
.strictEqual(messages
.length
, 1);
2497 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2498 assert
.strictEqual(messages
[0].line
, 5);
2502 it("should report a violation", () => {
2504 "/*eslint-disable no-alert */",
2506 "/*eslint-disable no-console */",
2508 "console.log('test');",
2509 "/*eslint-enable */",
2511 "alert('test');", // here
2512 "console.log('test');", // here
2514 "/*eslint-enable */",
2516 "alert('test');", // here
2517 "console.log('test');", // here
2521 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2523 const messages
= linter
.verify(code
, config
, filename
);
2525 assert
.strictEqual(messages
.length
, 4);
2527 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2528 assert
.strictEqual(messages
[0].line
, 6);
2530 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2531 assert
.strictEqual(messages
[1].line
, 7);
2533 assert
.strictEqual(messages
[2].ruleId
, "no-alert");
2534 assert
.strictEqual(messages
[2].line
, 9);
2536 assert
.strictEqual(messages
[3].ruleId
, "no-console");
2537 assert
.strictEqual(messages
[3].line
, 10);
2541 it("should report a violation", () => {
2543 "/*eslint-disable no-alert, no-console */",
2545 "console.log('test');",
2547 "/*eslint-enable no-alert */",
2549 "alert('test');", // here
2550 "console.log('test');",
2552 "/*eslint-enable no-console */",
2554 "alert('test');", // here
2555 "console.log('test');", // here
2556 "/*eslint-enable no-console */"
2558 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2560 const messages
= linter
.verify(code
, config
, filename
);
2562 assert
.strictEqual(messages
.length
, 3);
2564 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2565 assert
.strictEqual(messages
[0].line
, 5);
2567 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2568 assert
.strictEqual(messages
[1].line
, 8);
2570 assert
.strictEqual(messages
[2].ruleId
, "no-console");
2571 assert
.strictEqual(messages
[2].line
, 9);
2575 it("should report a violation when severity is warn", () => {
2577 "/*eslint-disable no-alert, no-console */",
2579 "console.log('test');",
2581 "/*eslint-enable no-alert */",
2583 "alert('test');", // here
2584 "console.log('test');",
2586 "/*eslint-enable no-console */",
2588 "alert('test');", // here
2589 "console.log('test');", // here
2590 "/*eslint-enable no-console */"
2592 const config
= { rules
: { "no-alert": "warn", "no-console": "warn" } };
2594 const messages
= linter
.verify(code
, config
, filename
);
2596 assert
.strictEqual(messages
.length
, 3);
2598 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2599 assert
.strictEqual(messages
[0].line
, 5);
2601 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2602 assert
.strictEqual(messages
[1].line
, 8);
2604 assert
.strictEqual(messages
[2].ruleId
, "no-console");
2605 assert
.strictEqual(messages
[2].line
, 9);
2610 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
2611 const code
= "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
2613 it("should report a violation", () => {
2614 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
2616 const messages
= linter
.verify(code
, config
, filename
);
2618 assert
.strictEqual(messages
.length
, 1);
2619 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2620 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
2621 assert
.include(messages
[0].nodeType
, "CallExpression");
2625 describe("when evaluating code with comments to enable configurable rule", () => {
2626 const code
= "/*eslint quotes:[2, \"double\"]*/ alert('test');";
2628 it("should report a violation", () => {
2629 const config
= { rules
: { quotes
: [2, "single"] } };
2631 const messages
= linter
.verify(code
, config
, filename
);
2633 assert
.strictEqual(messages
.length
, 1);
2634 assert
.strictEqual(messages
[0].ruleId
, "quotes");
2635 assert
.strictEqual(messages
[0].message
, "Strings must use doublequote.");
2636 assert
.include(messages
[0].nodeType
, "Literal");
2640 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
2641 const code
= "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
2643 it("should report a violation", () => {
2644 const config
= { rules
: { quotes
: [2, "single"] } };
2646 const messages
= linter
.verify(code
, config
, filename
);
2648 assert
.strictEqual(messages
.length
, 1);
2649 assert
.strictEqual(messages
[0].ruleId
, "quotes");
2650 assert
.strictEqual(messages
[0].message
, "Strings must use doublequote.");
2651 assert
.include(messages
[0].nodeType
, "Literal");
2655 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
2656 it("should report a violation", () => {
2657 const code
= "/*eslint no-alert:'1'*/ alert('test');";
2659 const config
= { rules
: { "no-alert": 1 } };
2661 const messages
= linter
.verify(code
, config
, filename
);
2663 assert
.strictEqual(messages
.length
, 2);
2666 * Incorrectly formatted comment threw error;
2667 * message from caught exception
2668 * may differ amongst UAs, so verifying
2669 * first part only as defined in the
2670 * parseJsonConfig function in lib/eslint.js
2672 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":'1'':/u);
2673 assert
.strictEqual(messages
[0].line
, 1);
2674 assert
.strictEqual(messages
[0].column
, 1);
2676 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2677 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
2678 assert
.include(messages
[1].nodeType
, "CallExpression");
2681 it("should report a violation", () => {
2682 const code
= "/*eslint no-alert:abc*/ alert('test');";
2684 const config
= { rules
: { "no-alert": 1 } };
2686 const messages
= linter
.verify(code
, config
, filename
);
2688 assert
.strictEqual(messages
.length
, 2);
2691 * Incorrectly formatted comment threw error;
2692 * message from caught exception
2693 * may differ amongst UAs, so verifying
2694 * first part only as defined in the
2695 * parseJsonConfig function in lib/eslint.js
2697 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":abc':/u);
2698 assert
.strictEqual(messages
[0].line
, 1);
2699 assert
.strictEqual(messages
[0].column
, 1);
2701 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2702 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
2703 assert
.include(messages
[1].nodeType
, "CallExpression");
2706 it("should report a violation", () => {
2707 const code
= "/*eslint no-alert:0 2*/ alert('test');";
2709 const config
= { rules
: { "no-alert": 1 } };
2711 const messages
= linter
.verify(code
, config
, filename
);
2713 assert
.strictEqual(messages
.length
, 2);
2716 * Incorrectly formatted comment threw error;
2717 * message from caught exception
2718 * may differ amongst UAs, so verifying
2719 * first part only as defined in the
2720 * parseJsonConfig function in lib/eslint.js
2722 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":0 2':/u);
2723 assert
.strictEqual(messages
[0].line
, 1);
2724 assert
.strictEqual(messages
[0].column
, 1);
2726 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2727 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
2728 assert
.include(messages
[1].nodeType
, "CallExpression");
2732 describe("when evaluating code with comments which have colon in its value", () => {
2733 const code
= String
.raw
`
2734 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
2738 it("should not parse errors, should report a violation", () => {
2739 const messages
= linter
.verify(code
, {}, filename
);
2741 assert
.strictEqual(messages
.length
, 1);
2742 assert
.strictEqual(messages
[0].ruleId
, "max-len");
2743 assert
.strictEqual(messages
[0].message
, "This line has a length of 129. Maximum allowed is 100.");
2744 assert
.include(messages
[0].nodeType
, "Program");
2748 describe("when evaluating code with comments that contain escape sequences", () => {
2749 const code
= String
.raw
`
2750 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
2751 console.log("test");
2752 consolexlog("test2");
2756 it("should validate correctly", () => {
2757 const config
= { rules
: {} };
2758 const messages
= linter
.verify(code
, config
, filename
);
2759 const [message1
, message2
] = messages
;
2761 assert
.strictEqual(messages
.length
, 2);
2762 assert
.strictEqual(message1
.ruleId
, "max-len");
2763 assert
.strictEqual(message1
.message
, "This line has a length of 21. Maximum allowed is 1.");
2764 assert
.strictEqual(message1
.line
, 4);
2765 assert
.strictEqual(message1
.column
, 1);
2766 assert
.include(message1
.nodeType
, "Program");
2767 assert
.strictEqual(message2
.ruleId
, "max-len");
2768 assert
.strictEqual(message2
.message
, "This line has a length of 16. Maximum allowed is 1.");
2769 assert
.strictEqual(message2
.line
, 5);
2770 assert
.strictEqual(message2
.column
, 1);
2771 assert
.include(message2
.nodeType
, "Program");
2775 describe("when evaluating a file with a hashbang", () => {
2777 it("should preserve line numbers", () => {
2778 const code
= "#!bin/program\n\nvar foo;;";
2779 const config
= { rules
: { "no-extra-semi": 1 } };
2780 const messages
= linter
.verify(code
, config
);
2782 assert
.strictEqual(messages
.length
, 1);
2783 assert
.strictEqual(messages
[0].ruleId
, "no-extra-semi");
2784 assert
.strictEqual(messages
[0].nodeType
, "EmptyStatement");
2785 assert
.strictEqual(messages
[0].line
, 3);
2788 it("should have a comment with the hashbang in it", () => {
2789 const code
= "#!bin/program\n\nvar foo;;";
2790 const config
= { rules
: { checker
: "error" } };
2791 const spy
= sinon
.spy(context
=> {
2792 const comments
= context
.getAllComments();
2794 assert
.strictEqual(comments
.length
, 1);
2795 assert
.strictEqual(comments
[0].type
, "Shebang");
2799 linter
.defineRule("checker", spy
);
2800 linter
.verify(code
, config
);
2801 assert(spy
.calledOnce
);
2804 it("should comment hashbang without breaking offset", () => {
2805 const code
= "#!/usr/bin/env node\n'123';";
2806 const config
= { rules
: { checker
: "error" } };
2809 linter
.defineRule("checker", context
=> {
2810 spy
= sinon
.spy(node
=> {
2811 assert
.strictEqual(context
.getSource(node
), "'123';");
2813 return { ExpressionStatement
: spy
};
2816 linter
.verify(code
, config
);
2817 assert(spy
&& spy
.calledOnce
);
2822 describe("when evaluating broken code", () => {
2823 const code
= BROKEN_TEST_CODE
;
2825 it("should report a violation with a useful parse error prefix", () => {
2826 const messages
= linter
.verify(code
);
2828 assert
.strictEqual(messages
.length
, 1);
2829 assert
.strictEqual(messages
[0].severity
, 2);
2830 assert
.isNull(messages
[0].ruleId
);
2831 assert
.strictEqual(messages
[0].line
, 1);
2832 assert
.strictEqual(messages
[0].column
, 4);
2833 assert
.isTrue(messages
[0].fatal
);
2834 assert
.match(messages
[0].message
, /^Parsing error:/u);
2837 it("should report source code where the issue is present", () => {
2838 const inValidCode
= [
2844 const messages
= linter
.verify(inValidCode
.join("\n"));
2846 assert
.strictEqual(messages
.length
, 1);
2847 assert
.strictEqual(messages
[0].severity
, 2);
2848 assert
.isTrue(messages
[0].fatal
);
2849 assert
.match(messages
[0].message
, /^Parsing error:/u);
2853 describe("when using an invalid (undefined) rule", () => {
2854 linter
= new Linter();
2856 const code
= TEST_CODE
;
2857 let results
, result
, warningResult
, arrayOptionResults
, objectOptionResults
, resultsMultiple
;
2860 results
= linter
.verify(code
, { rules
: { foobar
: 2 } });
2861 result
= results
[0];
2862 warningResult
= linter
.verify(code
, { rules
: { foobar
: 1 } })[0];
2863 arrayOptionResults
= linter
.verify(code
, { rules
: { foobar
: [2, "always"] } });
2864 objectOptionResults
= linter
.verify(code
, { rules
: { foobar
: [1, { bar
: false }] } });
2865 resultsMultiple
= linter
.verify(code
, { rules
: { foobar
: 2, barfoo
: 1 } });
2868 it("should report a problem", () => {
2869 assert
.isNotNull(result
);
2870 assert
.isArray(results
);
2871 assert
.isObject(result
);
2872 assert
.property(result
, "ruleId");
2873 assert
.strictEqual(result
.ruleId
, "foobar");
2876 it("should report that the rule does not exist", () => {
2877 assert
.property(result
, "message");
2878 assert
.strictEqual(result
.message
, "Definition for rule 'foobar' was not found.");
2881 it("should report at the correct severity", () => {
2882 assert
.property(result
, "severity");
2883 assert
.strictEqual(result
.severity
, 2);
2884 assert
.strictEqual(warningResult
.severity
, 2); // this is 2, since the rulename is very likely to be wrong
2887 it("should accept any valid rule configuration", () => {
2888 assert
.isObject(arrayOptionResults
[0]);
2889 assert
.isObject(objectOptionResults
[0]);
2892 it("should report multiple missing rules", () => {
2893 assert
.isArray(resultsMultiple
);
2895 assert
.deepStrictEqual(
2899 message
: "Definition for rule 'barfoo' was not found.",
2911 describe("when using a rule which has been replaced", () => {
2912 const code
= TEST_CODE
;
2914 it("should report the new rule", () => {
2915 const results
= linter
.verify(code
, { rules
: { "no-comma-dangle": 2 } });
2917 assert
.strictEqual(results
[0].ruleId
, "no-comma-dangle");
2918 assert
.strictEqual(results
[0].message
, "Rule 'no-comma-dangle' was removed and replaced by: comma-dangle");
2922 describe("when calling getRules", () => {
2923 it("should return all loaded rules", () => {
2924 const rules
= linter
.getRules();
2926 assert
.isAbove(rules
.size
, 230);
2927 assert
.isObject(rules
.get("no-alert"));
2931 describe("when calling version", () => {
2932 it("should return current version number", () => {
2933 const version
= linter
.version
;
2935 assert
.isString(version
);
2936 assert
.isTrue(parseInt(version
[0], 10) >= 3);
2940 describe("when evaluating an empty string", () => {
2941 it("runs rules", () => {
2942 linter
.defineRule("no-programs", context
=> ({
2944 context
.report({ node
, message
: "No programs allowed." });
2949 linter
.verify("", { rules
: { "no-programs": "error" } }).length
,
2955 describe("when evaluating code without comments to environment", () => {
2956 it("should report a violation when using typed array", () => {
2957 const code
= "var array = new Uint8Array();";
2959 const config
= { rules
: { "no-undef": 1 } };
2961 const messages
= linter
.verify(code
, config
, filename
);
2963 assert
.strictEqual(messages
.length
, 1);
2966 it("should report a violation when using Promise", () => {
2967 const code
= "new Promise();";
2969 const config
= { rules
: { "no-undef": 1 } };
2971 const messages
= linter
.verify(code
, config
, filename
);
2973 assert
.strictEqual(messages
.length
, 1);
2977 describe("when evaluating code with comments to environment", () => {
2978 it("should not support legacy config", () => {
2979 const code
= "/*jshint mocha:true */ describe();";
2981 const config
= { rules
: { "no-undef": 1 } };
2983 const messages
= linter
.verify(code
, config
, filename
);
2985 assert
.strictEqual(messages
.length
, 1);
2986 assert
.strictEqual(messages
[0].ruleId
, "no-undef");
2987 assert
.strictEqual(messages
[0].nodeType
, "Identifier");
2988 assert
.strictEqual(messages
[0].line
, 1);
2991 it("should not report a violation", () => {
2992 const code
= "/*eslint-env es6 */ new Promise();";
2994 const config
= { rules
: { "no-undef": 1 } };
2996 const messages
= linter
.verify(code
, config
, filename
);
2998 assert
.strictEqual(messages
.length
, 0);
3001 // https://github.com/eslint/eslint/issues/14652
3002 it("should not report a violation", () => {
3004 "/*eslint-env es6\n */ new Promise();",
3005 "/*eslint-env browser,\nes6 */ window;Promise;",
3006 "/*eslint-env\nbrowser,es6 */ window;Promise;"
3008 const config
= { rules
: { "no-undef": 1 } };
3010 for (const code
of codes
) {
3011 const messages
= linter
.verify(code
, config
, filename
);
3013 assert
.strictEqual(messages
.length
, 0);
3018 it("should not report a violation", () => {
3019 const code
= `/*${ESLINT_ENV} mocha,node */ require();describe();`;
3021 const config
= { rules
: { "no-undef": 1 } };
3023 const messages
= linter
.verify(code
, config
, filename
);
3025 assert
.strictEqual(messages
.length
, 0);
3028 it("should not report a violation", () => {
3029 const code
= "/*eslint-env mocha */ suite();test();";
3031 const config
= { rules
: { "no-undef": 1 } };
3033 const messages
= linter
.verify(code
, config
, filename
);
3035 assert
.strictEqual(messages
.length
, 0);
3038 it("should not report a violation", () => {
3039 const code
= `/*${ESLINT_ENV} amd */ define();require();`;
3041 const config
= { rules
: { "no-undef": 1 } };
3043 const messages
= linter
.verify(code
, config
, filename
);
3045 assert
.strictEqual(messages
.length
, 0);
3048 it("should not report a violation", () => {
3049 const code
= `/*${ESLINT_ENV} jasmine */ expect();spyOn();`;
3051 const config
= { rules
: { "no-undef": 1 } };
3053 const messages
= linter
.verify(code
, config
, filename
);
3055 assert
.strictEqual(messages
.length
, 0);
3058 it("should not report a violation", () => {
3059 const code
= `/*globals require: true */ /*${ESLINT_ENV} node */ require = 1;`;
3061 const config
= { rules
: { "no-undef": 1 } };
3063 const messages
= linter
.verify(code
, config
, filename
);
3065 assert
.strictEqual(messages
.length
, 0);
3068 it("should not report a violation", () => {
3069 const code
= `/*${ESLINT_ENV} node */ process.exit();`;
3071 const config
= { rules
: {} };
3073 const messages
= linter
.verify(code
, config
, filename
);
3075 assert
.strictEqual(messages
.length
, 0);
3078 it("should not report a violation", () => {
3079 const code
= `/*eslint no-process-exit: 0 */ /*${ESLINT_ENV} node */ process.exit();`;
3081 const config
= { rules
: { "no-undef": 1 } };
3083 const messages
= linter
.verify(code
, config
, filename
);
3085 assert
.strictEqual(messages
.length
, 0);
3089 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
3090 it("should report a violation for disabling rules", () => {
3092 "alert('test'); // eslint-disable-line no-alert"
3100 const messages
= linter
.verify(code
, config
, {
3102 allowInlineConfig
: false
3105 assert
.strictEqual(messages
.length
, 1);
3106 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3109 it("should report a violation for global variable declarations", () => {
3120 linter
.defineRules({
3124 const scope
= context
.getScope();
3125 const sourceCode
= context
.getSourceCode();
3126 const comments
= sourceCode
.getAllComments();
3128 assert
.strictEqual(1, comments
.length
);
3130 const foo
= getVariable(scope
, "foo");
3140 linter
.verify(code
, config
, { allowInlineConfig
: false });
3144 it("should report a violation for eslint-disable", () => {
3146 "/* eslint-disable */",
3155 const messages
= linter
.verify(code
, config
, {
3157 allowInlineConfig
: false
3160 assert
.strictEqual(messages
.length
, 1);
3161 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3164 it("should not report a violation for rule changes", () => {
3166 "/*eslint no-alert:2*/",
3175 const messages
= linter
.verify(code
, config
, {
3177 allowInlineConfig
: false
3180 assert
.strictEqual(messages
.length
, 0);
3183 it("should report a violation for disable-line", () => {
3185 "alert('test'); // eslint-disable-line"
3193 const messages
= linter
.verify(code
, config
, {
3195 allowInlineConfig
: false
3198 assert
.strictEqual(messages
.length
, 1);
3199 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3202 it("should report a violation for env changes", () => {
3204 `/*${ESLINT_ENV} browser*/ window`
3211 const messages
= linter
.verify(code
, config
, { allowInlineConfig
: false });
3213 assert
.strictEqual(messages
.length
, 1);
3214 assert
.strictEqual(messages
[0].ruleId
, "no-undef");
3218 describe("when evaluating code with 'noInlineComment'", () => {
3219 for (const directive
of [
3223 "eslint eqeqeq: error",
3224 "eslint-disable eqeqeq",
3225 "eslint-disable-line eqeqeq",
3226 "eslint-disable-next-line eqeqeq",
3227 "eslint-enable eqeqeq",
3230 // eslint-disable-next-line no-loop-func -- No closures
3231 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
3232 const messages
= linter
.verify(`/* ${directive} */`, { noInlineConfig
: true });
3234 assert
.deepStrictEqual(messages
.length
, 1);
3235 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
3236 assert
.deepStrictEqual(messages
[0].ruleId
, null);
3237 assert
.deepStrictEqual(messages
[0].severity
, 1);
3238 assert
.deepStrictEqual(messages
[0].message
, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
3242 for (const directive
of [
3243 "eslint-disable-line eqeqeq",
3244 "eslint-disable-next-line eqeqeq"
3246 // eslint-disable-next-line no-loop-func -- No closures
3247 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
3248 const messages
= linter
.verify(`// ${directive}`, { noInlineConfig
: true });
3250 assert
.deepStrictEqual(messages
.length
, 1);
3251 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
3252 assert
.deepStrictEqual(messages
[0].ruleId
, null);
3253 assert
.deepStrictEqual(messages
[0].severity
, 1);
3254 assert
.deepStrictEqual(messages
[0].message
, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
3258 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
3259 const messages
= linter
.verify("/* globals foo */", { noInlineConfig
: true }, { allowInlineConfig
: false });
3261 assert
.deepStrictEqual(messages
.length
, 0);
3265 describe("when receiving cwd in options during instantiation", () => {
3266 const code
= "a;\nb;";
3267 const config
= { rules
: { checker
: "error" } };
3269 it("should get cwd correctly in the context", () => {
3271 const linterWithOption
= new Linter({ cwd
});
3274 linterWithOption
.defineRule("checker", context
=> {
3275 spy
= sinon
.spy(() => {
3276 assert
.strictEqual(context
.getCwd(), cwd
);
3278 return { Program
: spy
};
3281 linterWithOption
.verify(code
, config
);
3282 assert(spy
&& spy
.calledOnce
);
3285 it("should assign process.cwd() to it if cwd is undefined", () => {
3287 const linterWithOption
= new Linter({ });
3289 linterWithOption
.defineRule("checker", context
=> {
3291 spy
= sinon
.spy(() => {
3292 assert
.strictEqual(context
.getCwd(), process
.cwd());
3294 return { Program
: spy
};
3297 linterWithOption
.verify(code
, config
);
3298 assert(spy
&& spy
.calledOnce
);
3301 it("should assign process.cwd() to it if the option is undefined", () => {
3304 linter
.defineRule("checker", context
=> {
3306 spy
= sinon
.spy(() => {
3307 assert
.strictEqual(context
.getCwd(), process
.cwd());
3309 return { Program
: spy
};
3312 linter
.verify(code
, config
);
3313 assert(spy
&& spy
.calledOnce
);
3317 describe("reportUnusedDisable option", () => {
3318 it("reports problems for unused eslint-disable comments", () => {
3319 assert
.deepStrictEqual(
3320 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: true }),
3324 message
: "Unused eslint-disable directive (no problems were reported).",
3338 it("reports problems for unused eslint-disable comments (error)", () => {
3339 assert
.deepStrictEqual(
3340 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "error" }),
3344 message
: "Unused eslint-disable directive (no problems were reported).",
3358 it("reports problems for unused eslint-disable comments (warn)", () => {
3359 assert
.deepStrictEqual(
3360 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "warn" }),
3364 message
: "Unused eslint-disable directive (no problems were reported).",
3378 it("reports problems for unused eslint-disable comments (in config)", () => {
3379 assert
.deepStrictEqual(
3380 linter
.verify("/* eslint-disable */", { reportUnusedDisableDirectives
: true }),
3384 message
: "Unused eslint-disable directive (no problems were reported).",
3398 it("reports problems for partially unused eslint-disable comments (in config)", () => {
3399 const code
= "alert('test'); // eslint-disable-line no-alert, no-redeclare";
3401 reportUnusedDisableDirectives
: true,
3408 const messages
= linter
.verify(code
, config
, {
3410 allowInlineConfig
: true
3413 assert
.deepStrictEqual(
3418 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
3432 describe("autofix", () => {
3433 const alwaysReportsRule
= {
3437 context
.report({ message
: "bad code", loc
: node
.loc
.end
});
3443 const neverReportsRule
= {
3449 const ruleCount
= 3;
3450 const usedRules
= Array
.from(
3451 { length
: ruleCount
},
3452 (_
, index
) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
3454 const unusedRules = usedRules.map(name => `un${name}
`); // "unused", "unused-1", "unused-2"
3457 reportUnusedDisableDirectives: true,
3459 ...Object.fromEntries(usedRules.map(name => [name, "error"])),
3460 ...Object.fromEntries(unusedRules.map(name => [name, "error"]))
3465 linter.defineRules(Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])));
3466 linter.defineRules(Object.fromEntries(unusedRules.map(name => [name, neverReportsRule])));
3471 //-----------------------------------------------
3472 // Removing the entire comment
3473 //-----------------------------------------------
3476 code: "// eslint-disable-line unused",
3480 code: "foo// eslint-disable-line unused",
3484 code: "// eslint-disable-line ,unused,",
3488 code: "// eslint-disable-line unused-1, unused-2",
3492 code: "// eslint-disable-line ,unused-1,, unused-2,, -- comment",
3496 code: "// eslint-disable-next-line unused\n",
3500 code: "// eslint-disable-next-line unused\nfoo",
3504 code: "/* eslint-disable \nunused\n*/",
3508 //-----------------------------------------------
3509 // Removing only individual rules
3510 //-----------------------------------------------
3512 // content before the first rule should not be changed
3514 code: "//eslint-disable-line unused, used",
3515 output: "//eslint-disable-line used"
3518 code: "// eslint-disable-line unused, used",
3519 output: "// eslint-disable-line used"
3522 code: "// eslint-disable-line unused, used",
3523 output: "// eslint-disable-line used"
3526 code: "/*\neslint-disable unused, used*/",
3527 output: "/*\neslint-disable used*/"
3530 code: "/*\n eslint-disable unused, used*/",
3531 output: "/*\n eslint-disable used*/"
3534 code: "/*\r\neslint-disable unused, used*/",
3535 output: "/*\r\neslint-disable used*/"
3538 code: "/*\u2028eslint-disable unused, used*/",
3539 output: "/*\u2028eslint-disable used*/"
3542 code: "/*\u00A0eslint-disable unused, used*/",
3543 output: "/*\u00A0eslint-disable used*/"
3546 code: "// eslint-disable-line unused, used",
3547 output: "// eslint-disable-line used"
3550 code: "/* eslint-disable\nunused, used*/",
3551 output: "/* eslint-disable\nused*/"
3554 code: "/* eslint-disable\n unused, used*/",
3555 output: "/* eslint-disable\n used*/"
3558 code: "/* eslint-disable\r\nunused, used*/",
3559 output: "/* eslint-disable\r\nused*/"
3562 code: "/* eslint-disable\u2028unused, used*/",
3563 output: "/* eslint-disable\u2028used*/"
3566 code: "/* eslint-disable\u00A0unused, used*/",
3567 output: "/* eslint-disable\u00A0used*/"
3570 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
3572 code: "// eslint-disable-line unused,used",
3573 output: "// eslint-disable-line used"
3576 code: "// eslint-disable-line unused, used",
3577 output: "// eslint-disable-line used"
3580 code: "// eslint-disable-line unused , used",
3581 output: "// eslint-disable-line used"
3584 code: "// eslint-disable-line unused, used",
3585 output: "// eslint-disable-line used"
3588 code: "// eslint-disable-line unused ,used",
3589 output: "// eslint-disable-line used"
3592 code: "/* eslint-disable unused\n,\nused */",
3593 output: "/* eslint-disable used */"
3596 code: "/* eslint-disable unused \n \n,\n\n used */",
3597 output: "/* eslint-disable used */"
3600 code: "/* eslint-disable unused\u2028,\u2028used */",
3601 output: "/* eslint-disable used */"
3604 code: "// eslint-disable-line unused\u00A0,\u00A0used",
3605 output: "// eslint-disable-line used"
3608 code: "// eslint-disable-line unused,,used",
3609 output: "// eslint-disable-line ,used"
3612 code: "// eslint-disable-line unused, ,used",
3613 output: "// eslint-disable-line ,used"
3616 code: "// eslint-disable-line unused,, used",
3617 output: "// eslint-disable-line , used"
3620 code: "// eslint-disable-line unused,used ",
3621 output: "// eslint-disable-line used "
3624 code: "// eslint-disable-next-line unused,used\n",
3625 output: "// eslint-disable-next-line used\n"
3628 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
3630 code: "// eslint-disable-line used-1,unused,used-2",
3631 output: "// eslint-disable-line used-1,used-2"
3634 code: "// eslint-disable-line used-1, unused,used-2",
3635 output: "// eslint-disable-line used-1,used-2"
3638 code: "// eslint-disable-line used-1,unused ,used-2",
3639 output: "// eslint-disable-line used-1,used-2"
3642 code: "// eslint-disable-line used-1, unused ,used-2",
3643 output: "// eslint-disable-line used-1,used-2"
3646 code: "/* eslint-disable used-1,\nunused\n,used-2 */",
3647 output: "/* eslint-disable used-1,used-2 */"
3650 code: "/* eslint-disable used-1,\n\n unused \n \n ,used-2 */",
3651 output: "/* eslint-disable used-1,used-2 */"
3654 code: "/* eslint-disable used-1,\u2028unused\u2028,used-2 */",
3655 output: "/* eslint-disable used-1,used-2 */"
3658 code: "// eslint-disable-line used-1,\u00A0unused\u00A0,used-2",
3659 output: "// eslint-disable-line used-1,used-2"
3662 // when removing a rule in the middle, content around commas should not be changed
3664 code: "// eslint-disable-line used-1, unused ,used-2",
3665 output: "// eslint-disable-line used-1,used-2"
3668 code: "// eslint-disable-line used-1,unused, used-2",
3669 output: "// eslint-disable-line used-1, used-2"
3672 code: "// eslint-disable-line used-1 ,unused,used-2",
3673 output: "// eslint-disable-line used-1 ,used-2"
3676 code: "// eslint-disable-line used-1 ,unused, used-2",
3677 output: "// eslint-disable-line used-1 , used-2"
3680 code: "// eslint-disable-line used-1 , unused , used-2",
3681 output: "// eslint-disable-line used-1 , used-2"
3684 code: "/* eslint-disable used-1\n,unused,\nused-2 */",
3685 output: "/* eslint-disable used-1\n,\nused-2 */"
3688 code: "/* eslint-disable used-1\u2028,unused,\u2028used-2 */",
3689 output: "/* eslint-disable used-1\u2028,\u2028used-2 */"
3692 code: "// eslint-disable-line used-1\u00A0,unused,\u00A0used-2",
3693 output: "// eslint-disable-line used-1\u00A0,\u00A0used-2"
3696 code: "// eslint-disable-line , unused ,used",
3697 output: "// eslint-disable-line ,used"
3700 code: "/* eslint-disable\n, unused ,used */",
3701 output: "/* eslint-disable\n,used */"
3704 code: "/* eslint-disable used-1,\n,unused,used-2 */",
3705 output: "/* eslint-disable used-1,\n,used-2 */"
3708 code: "/* eslint-disable used-1,unused,\n,used-2 */",
3709 output: "/* eslint-disable used-1,\n,used-2 */"
3712 code: "/* eslint-disable used-1,\n,unused,\n,used-2 */",
3713 output: "/* eslint-disable used-1,\n,\n,used-2 */"
3716 code: "// eslint-disable-line used, unused,",
3717 output: "// eslint-disable-line used,"
3720 code: "// eslint-disable-next-line used, unused,\n",
3721 output: "// eslint-disable-next-line used,\n"
3724 code: "// eslint-disable-line used, unused, ",
3725 output: "// eslint-disable-line used, "
3728 code: "// eslint-disable-line used, unused, -- comment",
3729 output: "// eslint-disable-line used, -- comment"
3732 code: "/* eslint-disable used, unused,\n*/",
3733 output: "/* eslint-disable used,\n*/"
3736 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
3738 code: "// eslint-disable-line used,unused",
3739 output: "// eslint-disable-line used"
3742 code: "// eslint-disable-line used, unused",
3743 output: "// eslint-disable-line used"
3746 code: "// eslint-disable-line used ,unused",
3747 output: "// eslint-disable-line used"
3750 code: "// eslint-disable-line used , unused",
3751 output: "// eslint-disable-line used"
3754 code: "// eslint-disable-line used, unused",
3755 output: "// eslint-disable-line used"
3758 code: "// eslint-disable-line used ,unused",
3759 output: "// eslint-disable-line used"
3762 code: "/* eslint-disable used\n,\nunused */",
3763 output: "/* eslint-disable used */"
3766 code: "/* eslint-disable used \n \n,\n\n unused */",
3767 output: "/* eslint-disable used */"
3770 code: "/* eslint-disable used\u2028,\u2028unused */",
3771 output: "/* eslint-disable used */"
3774 code: "// eslint-disable-line used\u00A0,\u00A0unused",
3775 output: "// eslint-disable-line used"
3778 code: "// eslint-disable-line used,,unused",
3779 output: "// eslint-disable-line used,"
3782 code: "// eslint-disable-line used, ,unused",
3783 output: "// eslint-disable-line used,"
3786 code: "/* eslint-disable used,\n,unused */",
3787 output: "/* eslint-disable used, */"
3790 code: "/* eslint-disable used\n, ,unused */",
3791 output: "/* eslint-disable used\n, */"
3794 // content after the last rule should not be changed
3796 code: "// eslint-disable-line used,unused",
3797 output: "// eslint-disable-line used"
3800 code: "// eslint-disable-line used,unused ",
3801 output: "// eslint-disable-line used "
3804 code: "// eslint-disable-line used,unused ",
3805 output: "// eslint-disable-line used "
3808 code: "// eslint-disable-line used,unused -- comment",
3809 output: "// eslint-disable-line used -- comment"
3812 code: "// eslint-disable-next-line used,unused\n",
3813 output: "// eslint-disable-next-line used\n"
3816 code: "// eslint-disable-next-line used,unused \n",
3817 output: "// eslint-disable-next-line used \n"
3820 code: "/* eslint-disable used,unused\u2028*/",
3821 output: "/* eslint-disable used\u2028*/"
3824 code: "// eslint-disable-line used,unused\u00A0",
3825 output: "// eslint-disable-line used\u00A0"
3828 // multiply rules to remove
3830 code: "// eslint-disable-line used, unused-1, unused-2",
3831 output: "// eslint-disable-line used"
3834 code: "// eslint-disable-line unused-1, used, unused-2",
3835 output: "// eslint-disable-line used"
3838 code: "// eslint-disable-line unused-1, unused-2, used",
3839 output: "// eslint-disable-line used"
3842 code: "// eslint-disable-line used-1, unused-1, used-2, unused-2",
3843 output: "// eslint-disable-line used-1, used-2"
3846 code: "// eslint-disable-line unused-1, used-1, unused-2, used-2",
3847 output: "// eslint-disable-line used-1, used-2"
3851 /* eslint-disable unused-1,
3858 /* eslint-disable used-1,
3964 // duplicates in the list
3966 code: "// eslint-disable-line unused, unused, used",
3967 output: "// eslint-disable-line used"
3970 code: "// eslint-disable-line unused, used, unused",
3971 output: "// eslint-disable-line used"
3974 code: "// eslint-disable-line used, unused, unused, used",
3975 output: "// eslint-disable-line used, used"
3979 for (const { code, output } of tests) {
3980 // eslint-disable-next-line no-loop-func -- `linter
` is getting updated in beforeEach()
3983 linter.verifyAndFix(code, config).output,
3991 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
3992 it("should not report a violation", () => {
3994 "alert('test'); // eslint-disable-line no-alert"
4002 const messages = linter.verify(code, config, {
4004 allowInlineConfig: true
4007 assert.strictEqual(messages.length, 0);
4011 describe("when evaluating code with hashbang", () => {
4012 it("should comment hashbang without breaking offset", () => {
4013 const code = "#!/usr/bin/env node\n'123';";
4014 const config = { rules: { checker: "error" } };
4017 linter.defineRule("checker", context => {
4018 spy = sinon.spy(node => {
4019 assert.strictEqual(context.getSource(node), "'123';");
4021 return { ExpressionStatement: spy };
4024 linter.verify(code, config);
4025 assert(spy && spy.calledOnce);
4029 describe("verify()", () => {
4030 describe("filenames", () => {
4031 it("should allow filename to be passed on options object", () => {
4032 const filenameChecker = sinon.spy(context => {
4033 assert.strictEqual(context.getFilename(), "foo.js");
4037 linter.defineRule("checker", filenameChecker);
4038 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4039 assert(filenameChecker.calledOnce);
4042 it("should allow filename to be passed as third argument", () => {
4043 const filenameChecker = sinon.spy(context => {
4044 assert.strictEqual(context.getFilename(), "bar.js");
4048 linter.defineRule("checker", filenameChecker);
4049 linter.verify("foo;", { rules: { checker: "error" } }, "bar.js");
4050 assert(filenameChecker.calledOnce);
4053 it("should default filename to <input> when options object doesn't have filename", () => {
4054 const filenameChecker = sinon.spy(context => {
4055 assert.strictEqual(context.getFilename(), "<input>");
4059 linter.defineRule("checker", filenameChecker);
4060 linter.verify("foo;", { rules: { checker: "error" } }, {});
4061 assert(filenameChecker.calledOnce);
4064 it("should default filename to <input> when only two arguments are passed", () => {
4065 const filenameChecker = sinon.spy(context => {
4066 assert.strictEqual(context.getFilename(), "<input>");
4070 linter.defineRule("checker", filenameChecker);
4071 linter.verify("foo;", { rules: { checker: "error" } });
4072 assert(filenameChecker.calledOnce);
4076 describe("physicalFilenames", () => {
4077 it("should be same as `filename
` passed on options object, if no processors are used", () => {
4078 const physicalFilenameChecker = sinon.spy(context => {
4079 assert.strictEqual(context.getPhysicalFilename(), "foo.js");
4083 linter.defineRule("checker", physicalFilenameChecker);
4084 linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
4085 assert(physicalFilenameChecker.calledOnce);
4088 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
4089 const physicalFilenameChecker = sinon.spy(context => {
4090 assert.strictEqual(context.getPhysicalFilename(), "<input>");
4094 linter.defineRule("checker", physicalFilenameChecker);
4095 linter.verify("foo;", { rules: { checker: "error" } }, {});
4096 assert(physicalFilenameChecker.calledOnce);
4099 it("should default physicalFilename to <input> when only two arguments are passed", () => {
4100 const physicalFilenameChecker = sinon.spy(context => {
4101 assert.strictEqual(context.getPhysicalFilename(), "<input>");
4105 linter.defineRule("checker", physicalFilenameChecker);
4106 linter.verify("foo;", { rules: { checker: "error" } });
4107 assert(physicalFilenameChecker.calledOnce);
4111 it("should report warnings in order by line and column when called", () => {
4113 const code = "foo()\n alert('test')";
4114 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
4116 const messages = linter.verify(code, config, filename);
4118 assert.strictEqual(messages.length, 3);
4119 assert.strictEqual(messages[0].line, 1);
4120 assert.strictEqual(messages[0].column, 6);
4121 assert.strictEqual(messages[1].line, 2);
4122 assert.strictEqual(messages[1].column, 18);
4123 assert.strictEqual(messages[2].line, 2);
4124 assert.strictEqual(messages[2].column, 18);
4127 describe("ecmaVersion", () => {
4129 it("should not support ES6 when no ecmaVersion provided", () => {
4130 const messages = linter.verify("let x = 0;");
4132 assert.strictEqual(messages.length, 1);
4135 it("supports ECMAScript version 'latest'", () => {
4136 const messages = linter.verify("let x = 5 ** 7;", {
4137 parserOptions: { ecmaVersion: "latest" }
4140 assert.strictEqual(messages.length, 0);
4143 it("the 'latest' is equal to espree.latestEcmaVersion", () => {
4144 let ecmaVersion = null;
4145 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4147 linter.defineRule("ecma-version", context => ({
4149 ecmaVersion = context.parserOptions.ecmaVersion;
4152 linter.verify("", config);
4153 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion, "ecmaVersion should be 13");
4156 it("the 'latest' is not normalized for custom parsers", () => {
4157 let ecmaVersion = null;
4158 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "latest" } };
4160 linter.defineParser("custom-parser", testParsers.enhancedParser);
4161 linter.defineRule("ecma-version", context => ({
4163 ecmaVersion = context.parserOptions.ecmaVersion;
4166 linter.verify("", config);
4167 assert.strictEqual(ecmaVersion, "latest", "ecmaVersion should be latest");
4170 it("the 'latest' is equal to espree.latestEcmaVersion on languageOptions", () => {
4171 let ecmaVersion = null;
4172 const config = { rules: { "ecma-version": 2 }, parserOptions: { ecmaVersion: "latest" } };
4174 linter.defineRule("ecma-version", context => ({
4176 ecmaVersion = context.languageOptions.ecmaVersion;
4179 linter.verify("", config);
4180 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
4183 it("the 'next' is equal to espree.latestEcmaVersion on languageOptions with custom parser", () => {
4184 let ecmaVersion = null;
4185 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser", parserOptions: { ecmaVersion: "next" } };
4187 linter.defineParser("custom-parser", testParsers.stubParser);
4188 linter.defineRule("ecma-version", context => ({
4190 ecmaVersion = context.languageOptions.ecmaVersion;
4193 linter.verify("", config);
4194 assert.strictEqual(ecmaVersion, espree.latestEcmaVersion + 2009, "ecmaVersion should be 2022");
4197 it("missing ecmaVersion is equal to 5 on languageOptions with custom parser", () => {
4198 let ecmaVersion = null;
4199 const config = { rules: { "ecma-version": 2 }, parser: "custom-parser" };
4201 linter.defineParser("custom-parser", testParsers.enhancedParser);
4202 linter.defineRule("ecma-version", context => ({
4204 ecmaVersion = context.languageOptions.ecmaVersion;
4207 linter.verify("", config);
4208 assert.strictEqual(ecmaVersion, 5, "ecmaVersion should be 5");
4211 it("should pass normalized ecmaVersion to eslint-scope", () => {
4212 let blockScope = null;
4214 linter.defineRule("block-scope", context => ({
4216 blockScope = context.getScope();
4219 linter.defineParser("custom-parser", {
4220 parse: (...args) => espree.parse(...args)
4223 // Use standard parser
4224 linter.verify("{}", {
4225 rules: { "block-scope": 2 },
4226 parserOptions: { ecmaVersion: "latest" }
4229 assert.strictEqual(blockScope.type, "block");
4231 linter.verify("{}", {
4232 rules: { "block-scope": 2 },
4233 parserOptions: {} // ecmaVersion defaults to 5
4235 assert.strictEqual(blockScope.type, "global");
4237 // Use custom parser
4238 linter.verify("{}", {
4239 rules: { "block-scope": 2 },
4240 parser: "custom-parser",
4241 parserOptions: { ecmaVersion: "latest" }
4244 assert.strictEqual(blockScope.type, "block");
4246 linter.verify("{}", {
4247 rules: { "block-scope": 2 },
4248 parser: "custom-parser",
4249 parserOptions: {} // ecmaVersion defaults to 5
4251 assert.strictEqual(blockScope.type, "global");
4254 describe("it should properly parse let declaration when", () => {
4255 it("the ECMAScript version number is 6", () => {
4256 const messages = linter.verify("let x = 5;", {
4262 assert.strictEqual(messages.length, 0);
4265 it("the ECMAScript version number is 2015", () => {
4266 const messages = linter.verify("let x = 5;", {
4272 assert.strictEqual(messages.length, 0);
4276 it("should fail to parse exponentiation operator when the ECMAScript version number is 2015", () => {
4277 const messages = linter.verify("x ** y;", {
4283 assert.strictEqual(messages.length, 1);
4286 describe("should properly parse exponentiation operator when", () => {
4287 it("the ECMAScript version number is 7", () => {
4288 const messages = linter.verify("x ** y;", {
4294 assert.strictEqual(messages.length, 0);
4297 it("the ECMAScript version number is 2016", () => {
4298 const messages = linter.verify("x ** y;", {
4304 assert.strictEqual(messages.length, 0);
4309 it("should properly parse object spread when ecmaVersion is 2018", () => {
4311 const messages = linter.verify("var x = { ...y };", {
4317 assert.strictEqual(messages.length, 0);
4320 it("should properly parse global return when passed ecmaFeatures", () => {
4322 const messages = linter.verify("return;", {
4330 assert.strictEqual(messages.length, 0);
4333 it("should properly parse global return when in Node.js environment", () => {
4335 const messages = linter.verify("return;", {
4341 assert.strictEqual(messages.length, 0);
4344 it("should not parse global return when in Node.js environment with globalReturn explicitly off", () => {
4346 const messages = linter.verify("return;", {
4357 assert.strictEqual(messages.length, 1);
4358 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
4361 it("should not parse global return when Node.js environment is false", () => {
4363 const messages = linter.verify("return;", {}, filename);
4365 assert.strictEqual(messages.length, 1);
4366 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
4369 it("should properly parse sloppy-mode code when impliedStrict is false", () => {
4371 const messages = linter.verify("var private;", {}, filename);
4373 assert.strictEqual(messages.length, 0);
4376 it("should not parse sloppy-mode code when impliedStrict is true", () => {
4378 const messages = linter.verify("var private;", {
4386 assert.strictEqual(messages.length, 1);
4387 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
4390 it("should properly parse valid code when impliedStrict is true", () => {
4392 const messages = linter.verify("var foo;", {
4400 assert.strictEqual(messages.length, 0);
4403 it("should properly parse JSX when passed ecmaFeatures", () => {
4405 const messages = linter.verify("var x = <div/>;", {
4413 assert.strictEqual(messages.length, 0);
4416 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
4417 const code = "var myDivElement = <div className=\"foo\" />;";
4418 const messages = linter.verify(code, {}, "filename");
4420 assert.strictEqual(messages.length, 1);
4421 assert.strictEqual(messages[0].line, 1);
4422 assert.strictEqual(messages[0].column, 20);
4423 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
4426 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
4427 const code = "var myDivElement = <div className=\"foo\" />;";
4428 const messages = linter.verify(code, { parserOptions: { ecmaFeatures: { jsx: true } } }, "filename");
4430 assert.strictEqual(messages.length, 0);
4433 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
4434 const code = "var myDivElement = <div {...this.props} />;";
4435 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }, "filename");
4437 assert.strictEqual(messages.length, 0);
4440 it("should not allow the use of reserved words as variable names in ES3", () => {
4441 const code = "var char;";
4442 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
4444 assert.strictEqual(messages.length, 1);
4445 assert.strictEqual(messages[0].severity, 2);
4446 assert.isTrue(messages[0].fatal);
4447 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
4450 it("should not allow the use of reserved words as property names in member expressions in ES3", () => {
4451 const code = "obj.char;";
4452 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
4454 assert.strictEqual(messages.length, 1);
4455 assert.strictEqual(messages[0].severity, 2);
4456 assert.isTrue(messages[0].fatal);
4457 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
4460 it("should not allow the use of reserved words as property names in object literals in ES3", () => {
4461 const code = "var obj = { char: 1 };";
4462 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3 } }, filename);
4464 assert.strictEqual(messages.length, 1);
4465 assert.strictEqual(messages[0].severity, 2);
4466 assert.isTrue(messages[0].fatal);
4467 assert.match(messages[0].message, /^Parsing error:.*'char'/u);
4470 it("should allow the use of reserved words as variable and property names in ES3 when allowReserved is true", () => {
4471 const code = "var char; obj.char; var obj = { char: 1 };";
4472 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 3, allowReserved: true } }, filename);
4474 assert.strictEqual(messages.length, 0);
4477 it("should not allow the use of reserved words as variable names in ES > 3", () => {
4478 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
4480 ecmaVersions.forEach(ecmaVersion => {
4481 const code = "var enum;";
4482 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
4484 assert.strictEqual(messages.length, 1);
4485 assert.strictEqual(messages[0].severity, 2);
4486 assert.isTrue(messages[0].fatal);
4487 assert.match(messages[0].message, /^Parsing error:.*'enum'/u);
4491 it("should allow the use of reserved words as property names in ES > 3", () => {
4492 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
4494 ecmaVersions.forEach(ecmaVersion => {
4495 const code = "obj.enum; obj.function; var obj = { enum: 1, function: 2 };";
4496 const messages = linter.verify(code, { parserOptions: { ecmaVersion } }, filename);
4498 assert.strictEqual(messages.length, 0);
4502 it("should not allow `allowReserved
: true` in ES > 3", () => {
4503 const ecmaVersions = [void 0, ...espree.supportedEcmaVersions.filter(ecmaVersion => ecmaVersion > 3)];
4505 ecmaVersions.forEach(ecmaVersion => {
4507 const messages = linter.verify(code, { parserOptions: { ecmaVersion, allowReserved: true } }, filename);
4509 assert.strictEqual(messages.length, 1);
4510 assert.strictEqual(messages[0].severity, 2);
4511 assert.isTrue(messages[0].fatal);
4512 assert.match(messages[0].message, /^Parsing error:.*allowReserved/u);
4516 it("should be able to use es6 features if there is a comment which has \"eslint-env es6\"", () => {
4518 "/* eslint-env es6 */",
4519 "var arrow = () => 0;",
4520 "var binary = 0b1010;",
4521 "{ let a = 0; const b = 1; }",
4523 "function defaultParams(a = 0) {}",
4524 "var {a = 1, b = 2} = {};",
4525 "for (var a of []) {}",
4526 "function* generator() { yield 0; }",
4527 "var computed = {[a]: 0};",
4528 "var duplicate = {dup: 0, dup: 1};",
4529 "var method = {foo() {}};",
4530 "var property = {a, b};",
4531 "var octal = 0o755;",
4532 "var u = /^.$/u.test('ð ®·');",
4533 "var y = /hello/y.test('hello');",
4534 "function restParam(a, ...rest) {}",
4535 "class B { superInFunc() { super.foo(); } }",
4536 "var template = `hello
, ${a}
`;",
4537 "var unicode = '\\u{20BB7}';"
4540 const messages = linter.verify(code, null, "eslint-env es6");
4542 assert.strictEqual(messages.length, 0);
4545 it("should be able to return in global if there is a comment which enables the node environment with a comment", () => {
4546 const messages = linter.verify(`/* ${ESLINT_ENV} node */ return;`, null, "node environment");
4548 assert.strictEqual(messages.length, 0);
4551 it("should attach a \"/*global\" comment node to declared variables", () => {
4552 const code = "/* global foo */\n/* global bar, baz */";
4555 linter.defineRules({
4559 const scope = context.getScope();
4560 const sourceCode = context.getSourceCode();
4561 const comments = sourceCode.getAllComments();
4563 assert.strictEqual(2, comments.length);
4565 const foo = getVariable(scope, "foo");
4567 assert.strictEqual(foo.eslintExplicitGlobal, true);
4568 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
4570 const bar = getVariable(scope, "bar");
4572 assert.strictEqual(bar.eslintExplicitGlobal, true);
4573 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
4575 const baz = getVariable(scope, "baz");
4577 assert.strictEqual(baz.eslintExplicitGlobal, true);
4578 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
4586 linter.verify(code, { rules: { test: 2 } });
4590 it("should report a linting error when a global is set to an invalid value", () => {
4591 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
4593 assert.deepStrictEqual(results, [
4597 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
4608 message: "'foo' is not defined.",
4613 nodeType: "Identifier"
4618 it("should not crash when we reuse the SourceCode object", () => {
4619 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
4620 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } });
4623 it("should reuse the SourceCode object", () => {
4627 linter.defineRule("save-ast1", () => ({
4632 linter.defineRule("save-ast2", () => ({
4638 linter.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast1": 2 } });
4639 linter.verify(linter.getSourceCode(), { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, rules: { "save-ast2": 2 } });
4641 assert(ast1 !== null);
4642 assert(ast2 !== null);
4643 assert(ast1 === ast2);
4646 it("should allow 'await' as a property name in modules", () => {
4647 const result = linter.verify(
4649 { parserOptions: { ecmaVersion: 6, sourceType: "module" } }
4652 assert(result.length === 0);
4656 it("should not modify config object passed as argument", () => {
4659 Object.freeze(config);
4660 linter.verify("var", config);
4663 it("should pass 'id' to rule contexts with the rule id", () => {
4664 const spy = sinon.spy(context => {
4665 assert.strictEqual(context.id, "foo-bar-baz");
4669 linter.defineRule("foo-bar-baz", spy);
4670 linter.verify("x", { rules: { "foo-bar-baz": "error" } });
4671 assert(spy.calledOnce);
4674 describe("descriptions in directive comments", () => {
4675 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
4676 const aaa = sinon.stub().returns({});
4677 const bbb = sinon.stub().returns({});
4679 linter.defineRule("aaa", { create: aaa });
4680 linter.defineRule("bbb", { create: bbb });
4681 const messages = linter.verify(`
4682 /*eslint aaa:error -- bbb:error */
4683 console
.log("hello")
4686 // Don't include syntax error of the comment.
4687 assert.deepStrictEqual(messages, []);
4690 assert.strictEqual(aaa.callCount, 1);
4691 assert.strictEqual(bbb.callCount, 0);
4694 it("should ignore the part preceded by '--' in '/*eslint-env*/'.", () => {
4695 const messages = linter.verify(`
4696 /*eslint-env es2015 -- es2017 */
4699 `, { rules: { "no-redeclare": "error" } });
4701 // Don't include `Atomics
`
4702 assert.deepStrictEqual(
4709 message: "'Promise' is already defined as a built-in global variable.",
4710 messageId: "redeclaredAsBuiltin",
4711 nodeType: "Identifier",
4712 ruleId: "no-redeclare",
4718 it("should ignore the part preceded by '--' in '/*global*/'.", () => {
4719 const messages = linter.verify(`
4720 /*global aaa -- bbb */
4723 `, { rules: { "no-redeclare": "error" } });
4725 // Don't include `bbb
`
4726 assert.deepStrictEqual(
4733 message: "'aaa' is already defined by a variable declaration.",
4734 messageId: "redeclaredBySyntax",
4736 ruleId: "no-redeclare",
4742 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
4743 const messages = linter.verify(`
4744 /*globals aaa -- bbb */
4747 `, { rules: { "no-redeclare": "error" } });
4749 // Don't include `bbb
`
4750 assert.deepStrictEqual(
4757 message: "'aaa' is already defined by a variable declaration.",
4758 messageId: "redeclaredBySyntax",
4760 ruleId: "no-redeclare",
4766 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
4767 const messages = linter.verify(`
4768 /*exported aaa -- bbb */
4771 `, { rules: { "no-unused-vars": "error" } });
4773 // Don't include `aaa
`
4774 assert.deepStrictEqual(
4781 message: "'bbb' is assigned a value but never used.",
4782 messageId: "unusedVar",
4783 nodeType: "Identifier",
4784 ruleId: "no-unused-vars",
4790 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
4791 const messages = linter.verify(`
4792 /*eslint-disable no-redeclare -- no-unused-vars */
4795 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
4797 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
4798 assert.deepStrictEqual(
4805 message: "'aaa' is assigned a value but never used.",
4806 messageId: "unusedVar",
4807 nodeType: "Identifier",
4808 ruleId: "no-unused-vars",
4814 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
4815 const messages = linter.verify(`
4816 /*eslint-disable no-redeclare, no-unused-vars */
4817 /*eslint-enable no-redeclare -- no-unused-vars */
4820 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
4822 // Do include `no
-redeclare
` but not `no
-unused
-vars
`
4823 assert.deepStrictEqual(
4830 message: "'aaa' is already defined.",
4831 messageId: "redeclared",
4832 nodeType: "Identifier",
4833 ruleId: "no-redeclare",
4839 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
4840 const messages = linter.verify(`
4841 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
4842 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
4843 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
4845 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
4846 assert.deepStrictEqual(
4853 message: "'aaa' is assigned a value but never used.",
4854 messageId: "unusedVar",
4855 nodeType: "Identifier",
4856 ruleId: "no-unused-vars",
4862 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
4863 const messages = linter.verify(`
4864 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
4865 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
4866 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
4868 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
4869 assert.deepStrictEqual(
4876 message: "'aaa' is assigned a value but never used.",
4877 messageId: "unusedVar",
4878 nodeType: "Identifier",
4879 ruleId: "no-unused-vars",
4885 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
4886 const messages = linter.verify(`
4887 //eslint-disable-next-line no-redeclare -- no-unused-vars
4889 //eslint-disable-next-line no-redeclare -- no-unused-vars
4891 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
4893 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
4894 assert.deepStrictEqual(
4901 message: "'aaa' is assigned a value but never used.",
4902 messageId: "unusedVar",
4903 nodeType: "Identifier",
4904 ruleId: "no-unused-vars",
4910 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
4911 const messages = linter.verify(`
4912 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
4914 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
4916 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
4918 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
4919 assert.deepStrictEqual(
4926 message: "'aaa' is assigned a value but never used.",
4927 messageId: "unusedVar",
4928 nodeType: "Identifier",
4929 ruleId: "no-unused-vars",
4935 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
4936 const rule = sinon.stub().returns({});
4938 linter.defineRule("a--rule", { create: rule });
4939 const messages = linter.verify(`
4940 /*eslint a--rule:error */
4941 console
.log("hello")
4944 // Don't include syntax error of the comment.
4945 assert.deepStrictEqual(messages, []);
4948 assert.strictEqual(rule.callCount, 1);
4951 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
4952 const aaa = sinon.stub().returns({});
4953 const bbb = sinon.stub().returns({});
4955 linter.defineRule("aaa", { create: aaa });
4956 linter.defineRule("bbb", { create: bbb });
4957 const messages = linter.verify(`
4958 /*eslint aaa:error -------- bbb:error */
4959 console
.log("hello")
4962 // Don't include syntax error of the comment.
4963 assert.deepStrictEqual(messages, []);
4966 assert.strictEqual(aaa.callCount, 1);
4967 assert.strictEqual(bbb.callCount, 0);
4970 it("should ignore the part preceded by '--' with line breaks.", () => {
4971 const aaa = sinon.stub().returns({});
4972 const bbb = sinon.stub().returns({});
4974 linter.defineRule("aaa", { create: aaa });
4975 linter.defineRule("bbb", { create: bbb });
4976 const messages = linter.verify(`
4980 console
.log("hello")
4983 // Don't include syntax error of the comment.
4984 assert.deepStrictEqual(messages, []);
4987 assert.strictEqual(aaa.callCount, 1);
4988 assert.strictEqual(bbb.callCount, 0);
4993 describe("context.getScope()", () => {
4996 * Get the scope on the node `astSelector
` specified.
4997 * @param {string} code The source code to verify.
4998 * @param {string} astSelector The AST selector to get scope.
4999 * @param {number} [ecmaVersion=5] The ECMAScript version.
5000 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
5002 function getScope(code, astSelector, ecmaVersion = 5) {
5005 linter.defineRule("get-scope", context => ({
5006 [astSelector](node0) {
5008 scope = context.getScope();
5014 parserOptions: { ecmaVersion },
5015 rules: { "get-scope": 2 }
5019 return { node, scope };
5022 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
5023 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
5025 assert.strictEqual(scope.type, "function");
5026 assert.strictEqual(scope.block, node);
5029 it("should return 'function' scope on FunctionExpression (ES5)", () => {
5030 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
5032 assert.strictEqual(scope.type, "function");
5033 assert.strictEqual(scope.block, node);
5036 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
5037 const { node, scope } = getScope("function f() {}", "BlockStatement");
5039 assert.strictEqual(scope.type, "function");
5040 assert.strictEqual(scope.block, node.parent);
5043 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
5044 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
5046 assert.strictEqual(scope.type, "function");
5047 assert.strictEqual(scope.block, node.parent);
5050 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
5051 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
5053 assert.strictEqual(scope.type, "function");
5054 assert.strictEqual(scope.block, node.parent.parent);
5055 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5058 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
5059 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
5061 assert.strictEqual(scope.type, "block");
5062 assert.strictEqual(scope.upper.type, "function");
5063 assert.strictEqual(scope.block, node);
5064 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
5065 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
5068 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
5069 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
5071 assert.strictEqual(scope.type, "block");
5072 assert.strictEqual(scope.upper.type, "block");
5073 assert.strictEqual(scope.upper.upper.type, "function");
5074 assert.strictEqual(scope.block, node);
5075 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5076 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
5077 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
5080 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
5081 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
5083 assert.strictEqual(scope.type, "function");
5084 assert.strictEqual(scope.block, node.parent.parent);
5085 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5088 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
5089 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
5091 assert.strictEqual(scope.type, "switch");
5092 assert.strictEqual(scope.block, node);
5093 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5096 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
5097 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
5099 assert.strictEqual(scope.type, "function");
5100 assert.strictEqual(scope.block, node.parent.parent.parent);
5101 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
5104 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
5105 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
5107 assert.strictEqual(scope.type, "switch");
5108 assert.strictEqual(scope.block, node.parent);
5109 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
5112 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
5113 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
5115 assert.strictEqual(scope.type, "catch");
5116 assert.strictEqual(scope.block, node);
5117 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5120 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
5121 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
5123 assert.strictEqual(scope.type, "catch");
5124 assert.strictEqual(scope.block, node);
5125 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5128 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
5129 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
5131 assert.strictEqual(scope.type, "catch");
5132 assert.strictEqual(scope.block, node.parent);
5133 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
5136 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
5137 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
5139 assert.strictEqual(scope.type, "block");
5140 assert.strictEqual(scope.block, node);
5141 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
5144 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
5145 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
5147 assert.strictEqual(scope.type, "function");
5148 assert.strictEqual(scope.block, node.parent.parent);
5149 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
5152 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
5153 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
5155 assert.strictEqual(scope.type, "for");
5156 assert.strictEqual(scope.block, node);
5157 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
5160 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
5161 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
5163 assert.strictEqual(scope.type, "function");
5164 assert.strictEqual(scope.block, node.parent.parent.parent);
5165 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
5168 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
5169 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
5171 assert.strictEqual(scope.type, "block");
5172 assert.strictEqual(scope.upper.type, "for");
5173 assert.strictEqual(scope.block, node);
5174 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
5175 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
5178 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
5179 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
5181 assert.strictEqual(scope.type, "function");
5182 assert.strictEqual(scope.block, node.parent.parent);
5183 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
5186 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
5187 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
5189 assert.strictEqual(scope.type, "for");
5190 assert.strictEqual(scope.block, node);
5191 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
5194 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
5195 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
5197 assert.strictEqual(scope.type, "function");
5198 assert.strictEqual(scope.block, node.parent.parent.parent);
5199 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
5202 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
5203 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
5205 assert.strictEqual(scope.type, "block");
5206 assert.strictEqual(scope.upper.type, "for");
5207 assert.strictEqual(scope.block, node);
5208 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
5209 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
5212 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
5213 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
5215 assert.strictEqual(scope.type, "for");
5216 assert.strictEqual(scope.block, node);
5217 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
5220 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
5221 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
5223 assert.strictEqual(scope.type, "block");
5224 assert.strictEqual(scope.upper.type, "for");
5225 assert.strictEqual(scope.block, node);
5226 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
5227 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
5230 it("should shadow the same name variable by the iteration variable.", () => {
5231 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
5233 assert.strictEqual(scope.type, "for");
5234 assert.strictEqual(scope.upper.type, "global");
5235 assert.strictEqual(scope.block, node);
5236 assert.strictEqual(scope.upper.variables[0].references.length, 0);
5237 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
5238 assert.strictEqual(scope.references[1].identifier, node.right);
5239 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
5243 describe("Variables and references", () => {
5246 "function foo() { b; }",
5261 linter.defineRules({
5265 scope = context.getScope();
5271 linter.verify(code, { rules: { test: 2 }, globals: { e: true, f: false } });
5279 it("Scope#through should contain references of undefined variables", () => {
5280 assert.strictEqual(scope.through.length, 2);
5281 assert.strictEqual(scope.through[0].identifier.name, "a");
5282 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
5283 assert.strictEqual(scope.through[0].resolved, null);
5284 assert.strictEqual(scope.through[1].identifier.name, "b");
5285 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
5286 assert.strictEqual(scope.through[1].resolved, null);
5289 it("Scope#variables should contain global variables", () => {
5290 assert(scope.variables.some(v => v.name === "Object"));
5291 assert(scope.variables.some(v => v.name === "foo"));
5292 assert(scope.variables.some(v => v.name === "c"));
5293 assert(scope.variables.some(v => v.name === "d"));
5294 assert(scope.variables.some(v => v.name === "e"));
5295 assert(scope.variables.some(v => v.name === "f"));
5298 it("Scope#set should contain global variables", () => {
5299 assert(scope.set.get("Object"));
5300 assert(scope.set.get("foo"));
5301 assert(scope.set.get("c"));
5302 assert(scope.set.get("d"));
5303 assert(scope.set.get("e"));
5304 assert(scope.set.get("f"));
5307 it("Variables#references should contain their references", () => {
5308 assert.strictEqual(scope.set.get("Object").references.length, 1);
5309 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
5310 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
5311 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
5312 assert.strictEqual(scope.set.get("foo").references.length, 1);
5313 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
5314 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
5315 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
5316 assert.strictEqual(scope.set.get("c").references.length, 1);
5317 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
5318 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
5319 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
5320 assert.strictEqual(scope.set.get("d").references.length, 1);
5321 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
5322 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
5323 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
5324 assert.strictEqual(scope.set.get("e").references.length, 1);
5325 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
5326 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
5327 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
5328 assert.strictEqual(scope.set.get("f").references.length, 1);
5329 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
5330 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
5331 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
5334 it("Reference#resolved should be their variable", () => {
5335 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
5336 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
5337 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
5338 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
5339 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
5340 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
5344 describe("context.getDeclaredVariables(node)", () => {
5347 * Assert `context
.getDeclaredVariables(node
)` is valid.
5348 * @param {string} code A code to check.
5349 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
5350 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
5353 function verify(code, type, expectedNamesList) {
5354 linter.defineRules({
5358 * Assert `context
.getDeclaredVariables(node
)` is empty.
5359 * @param {ASTNode} node A node to check.
5362 function checkEmpty(node) {
5363 assert.strictEqual(0, context.getDeclaredVariables(node).length);
5366 Program: checkEmpty,
5367 EmptyStatement: checkEmpty,
5368 BlockStatement: checkEmpty,
5369 ExpressionStatement: checkEmpty,
5370 LabeledStatement: checkEmpty,
5371 BreakStatement: checkEmpty,
5372 ContinueStatement: checkEmpty,
5373 WithStatement: checkEmpty,
5374 SwitchStatement: checkEmpty,
5375 ReturnStatement: checkEmpty,
5376 ThrowStatement: checkEmpty,
5377 TryStatement: checkEmpty,
5378 WhileStatement: checkEmpty,
5379 DoWhileStatement: checkEmpty,
5380 ForStatement: checkEmpty,
5381 ForInStatement: checkEmpty,
5382 DebuggerStatement: checkEmpty,
5383 ThisExpression: checkEmpty,
5384 ArrayExpression: checkEmpty,
5385 ObjectExpression: checkEmpty,
5386 Property: checkEmpty,
5387 SequenceExpression: checkEmpty,
5388 UnaryExpression: checkEmpty,
5389 BinaryExpression: checkEmpty,
5390 AssignmentExpression: checkEmpty,
5391 UpdateExpression: checkEmpty,
5392 LogicalExpression: checkEmpty,
5393 ConditionalExpression: checkEmpty,
5394 CallExpression: checkEmpty,
5395 NewExpression: checkEmpty,
5396 MemberExpression: checkEmpty,
5397 SwitchCase: checkEmpty,
5398 Identifier: checkEmpty,
5399 Literal: checkEmpty,
5400 ForOfStatement: checkEmpty,
5401 ArrowFunctionExpression: checkEmpty,
5402 YieldExpression: checkEmpty,
5403 TemplateLiteral: checkEmpty,
5404 TaggedTemplateExpression: checkEmpty,
5405 TemplateElement: checkEmpty,
5406 ObjectPattern: checkEmpty,
5407 ArrayPattern: checkEmpty,
5408 RestElement: checkEmpty,
5409 AssignmentPattern: checkEmpty,
5410 ClassBody: checkEmpty,
5411 MethodDefinition: checkEmpty,
5412 MetaProperty: checkEmpty
5415 rule[type] = function(node) {
5416 const expectedNames = expectedNamesList.shift();
5417 const variables = context.getDeclaredVariables(node);
5419 assert(Array.isArray(expectedNames));
5420 assert(Array.isArray(variables));
5421 assert.strictEqual(expectedNames.length, variables.length);
5422 for (let i = variables.length - 1; i >= 0; i--) {
5423 assert.strictEqual(expectedNames[i], variables[i].name);
5429 linter.verify(code, {
5433 sourceType: "module"
5437 // Check all expected names are asserted.
5438 assert.strictEqual(0, expectedNamesList.length);
5441 it("VariableDeclaration", () => {
5442 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 ";
5446 ["g", "h", "i", "j", "k"],
5450 verify(code, "VariableDeclaration", namesList);
5453 it("VariableDeclaration (on for-in/of loop)", () => {
5455 // TDZ scope is created here, so tests to exclude those.
5456 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 ";
5464 verify(code, "VariableDeclaration", namesList);
5467 it("VariableDeclarator", () => {
5469 // TDZ scope is created here, so tests to exclude those.
5470 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 ";
5479 verify(code, "VariableDeclarator", namesList);
5482 it("FunctionDeclaration", () => {
5483 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 ";
5485 ["foo", "a", "b", "c", "d", "e"],
5486 ["bar", "f", "g", "h", "i", "j"]
5489 verify(code, "FunctionDeclaration", namesList);
5492 it("FunctionExpression", () => {
5493 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 ";
5495 ["foo", "a", "b", "c", "d", "e"],
5496 ["bar", "f", "g", "h", "i", "j"],
5500 verify(code, "FunctionExpression", namesList);
5503 it("ArrowFunctionExpression", () => {
5504 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 ";
5506 ["a", "b", "c", "d", "e"],
5507 ["f", "g", "h", "i", "j"]
5510 verify(code, "ArrowFunctionExpression", namesList);
5513 it("ClassDeclaration", () => {
5514 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
5516 ["A", "A"], // outer scope's and inner scope's.
5520 verify(code, "ClassDeclaration", namesList);
5523 it("ClassExpression", () => {
5524 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
5530 verify(code, "ClassExpression", namesList);
5533 it("CatchClause", () => {
5534 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
5540 verify(code, "CatchClause", namesList);
5543 it("ImportDeclaration", () => {
5544 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
5551 verify(code, "ImportDeclaration", namesList);
5554 it("ImportSpecifier", () => {
5555 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
5561 verify(code, "ImportSpecifier", namesList);
5564 it("ImportDefaultSpecifier", () => {
5565 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
5570 verify(code, "ImportDefaultSpecifier", namesList);
5573 it("ImportNamespaceSpecifier", () => {
5574 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
5579 verify(code, "ImportNamespaceSpecifier", namesList);
5583 describe("suggestions", () => {
5584 it("provides suggestion information for tools to use", () => {
5585 linter.defineRule("rule-with-suggestions", {
5586 meta: { hasSuggestions: true },
5587 create: context => ({
5591 message: "Incorrect spacing",
5593 desc: "Insert space at the beginning",
5594 fix: fixer => fixer.insertTextBefore(node, " ")
5596 desc: "Insert space at the end",
5597 fix: fixer => fixer.insertTextAfter(node, " ")
5604 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
5606 assert.deepStrictEqual(messages[0].suggestions, [{
5607 desc: "Insert space at the beginning",
5613 desc: "Insert space at the end",
5621 it("supports messageIds for suggestions", () => {
5622 linter.defineRule("rule-with-suggestions", {
5625 suggestion1: "Insert space at the beginning",
5626 suggestion2: "Insert space at the end"
5628 hasSuggestions: true
5630 create: context => ({
5634 message: "Incorrect spacing",
5636 messageId: "suggestion1",
5637 fix: fixer => fixer.insertTextBefore(node, " ")
5639 messageId: "suggestion2",
5640 fix: fixer => fixer.insertTextAfter(node, " ")
5647 const messages = linter.verify("var a = 1;", { rules: { "rule-with-suggestions": "error" } });
5649 assert.deepStrictEqual(messages[0].suggestions, [{
5650 messageId: "suggestion1",
5651 desc: "Insert space at the beginning",
5657 messageId: "suggestion2",
5658 desc: "Insert space at the end",
5666 it("should throw an error if suggestion is passed but `meta
.hasSuggestions
` property is not enabled", () => {
5667 linter.defineRule("rule-with-suggestions", {
5668 meta: { docs: {}, schema: [] },
5669 create: context => ({
5673 message: "hello world",
5674 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
5680 assert.throws(() => {
5681 linter.verify("0", { rules: { "rule-with-suggestions": "error" } });
5682 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`.");
5685 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", () => {
5686 linter.defineRule("rule-with-meta-docs-suggestion", {
5687 meta: { docs: { suggestion: true }, schema: [] },
5688 create: context => ({
5692 message: "hello world",
5693 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
5699 assert.throws(() => {
5700 linter.verify("0", { rules: { "rule-with-meta-docs-suggestion": "error" } });
5701 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`. `meta
.docs
.suggestion
` is ignored by ESLint.");
5705 describe("mutability", () => {
5710 linter1 = new Linter();
5711 linter2 = new Linter();
5714 describe("rules", () => {
5715 it("with no changes, same rules are loaded", () => {
5716 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
5719 it("loading rule in one doesn't change the other", () => {
5720 linter1.defineRule("mock-rule", () => ({}));
5722 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
5723 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
5728 describe("processors", () => {
5729 let receivedFilenames = [];
5730 let receivedPhysicalFilenames = [];
5733 receivedFilenames = [];
5734 receivedPhysicalFilenames = [];
5736 // A rule that always reports the AST with a message equal to the source text
5737 linter.defineRule("report-original-text", context => ({
5739 receivedFilenames.push(context.getFilename());
5740 receivedPhysicalFilenames.push(context.getPhysicalFilename());
5741 context.report({ node: ast, message: context.getSourceCode().text });
5746 describe("preprocessors", () => {
5747 it("should receive text and filename.", () => {
5748 const code = "foo bar baz";
5749 const preprocess = sinon.spy(text => text.split(" "));
5751 linter.verify(code, {}, { filename, preprocess });
5753 assert.strictEqual(preprocess.calledOnce, true);
5754 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
5757 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
5758 const code = "foo bar baz";
5759 const problems = linter.verify(
5761 { rules: { "report-original-text": "error" } },
5764 // Apply a preprocessor that splits the source text into spaces and lints each word individually
5766 return input.split(" ");
5771 assert.strictEqual(problems.length, 3);
5772 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
5775 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
5776 const code = "foo bar baz";
5777 const problems = linter.verify(
5779 { rules: { "report-original-text": "error" } },
5783 // Apply a preprocessor that splits the source text into spaces and lints each word individually
5785 return input.split(" ").map(text => ({
5786 filename: "block.js",
5793 assert.strictEqual(problems.length, 3);
5794 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
5797 assert.strictEqual(receivedFilenames.length, 3);
5798 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
5799 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
5800 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
5802 // physical filename
5803 assert.strictEqual(receivedPhysicalFilenames.length, 3);
5804 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
5807 it("should receive text even if a SourceCode object was given.", () => {
5809 const preprocess = sinon.spy(text => text.split(" "));
5811 linter.verify(code, {});
5812 const sourceCode = linter.getSourceCode();
5814 linter.verify(sourceCode, {}, { filename, preprocess });
5816 assert.strictEqual(preprocess.calledOnce, true);
5817 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
5820 it("should receive text even if a SourceCode object was given (with BOM).", () => {
5821 const code = "\uFEFFfoo";
5822 const preprocess = sinon.spy(text => text.split(" "));
5824 linter.verify(code, {});
5825 const sourceCode = linter.getSourceCode();
5827 linter.verify(sourceCode, {}, { filename, preprocess });
5829 assert.strictEqual(preprocess.calledOnce, true);
5830 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
5834 describe("postprocessors", () => {
5835 it("should receive result and filename.", () => {
5836 const code = "foo bar baz";
5837 const preprocess = sinon.spy(text => text.split(" "));
5838 const postprocess = sinon.spy(text => [text]);
5840 linter.verify(code, {}, { filename, postprocess, preprocess });
5842 assert.strictEqual(postprocess.calledOnce, true);
5843 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
5846 it("should apply a postprocessor to the reported messages", () => {
5847 const code = "foo bar baz";
5849 const problems = linter.verify(
5851 { rules: { "report-original-text": "error" } },
5853 preprocess: input => input.split(" "),
5856 * Apply a postprocessor that updates the locations of the reported problems
5857 * to make sure they correspond to the locations in the original text.
5859 postprocess(problemLists) {
5860 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
5861 return problemLists.reduce(
5862 (combinedList, problemList, index) =>
5863 combinedList.concat(
5870 message: problem.message.toUpperCase(),
5871 column: problem.column + index * 4
5882 assert.strictEqual(problems.length, 3);
5883 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
5884 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
5887 it("should use postprocessed problem ranges when applying autofixes", () => {
5888 const code = "foo bar baz";
5890 linter.defineRule("capitalize-identifiers", {
5897 if (node.name !== node.name.toUpperCase()) {
5900 message: "Capitalize this identifier",
5901 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
5909 const fixResult = linter.verifyAndFix(
5911 { rules: { "capitalize-identifiers": "error" } },
5915 * Apply a postprocessor that updates the locations of autofixes
5916 * to make sure they correspond to locations in the original text.
5918 preprocess: input => input.split(" "),
5919 postprocess(problemLists) {
5920 return problemLists.reduce(
5921 (combinedProblems, problemList, blockIndex) =>
5922 combinedProblems.concat(
5923 problemList.map(problem =>
5924 Object.assign(problem, {
5926 text: problem.fix.text,
5927 range: problem.fix.range.map(
5928 rangeIndex => rangeIndex + blockIndex * 4
5939 assert.strictEqual(fixResult.fixed, true);
5940 assert.strictEqual(fixResult.messages.length, 0);
5941 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
5946 describe("verifyAndFix", () => {
5947 it("Fixes the code", () => {
5948 const messages = linter.verifyAndFix("var a", {
5952 }, { filename: "test.js" });
5954 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
5955 assert.isTrue(messages.fixed);
5958 it("does not require a third argument", () => {
5959 const fixResult = linter.verifyAndFix("var a", {
5965 assert.deepStrictEqual(fixResult, {
5972 it("does not include suggestions in autofix results", () => {
5973 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
5976 "no-useless-escape": 2
5980 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
5981 assert.strictEqual(fixResult.fixed, true);
5982 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
5985 it("does not apply autofixes when fix argument is `false`", () => {
5986 const fixResult = linter.verifyAndFix("var a", {
5992 assert.strictEqual(fixResult.fixed, false);
5995 it("stops fixing after 10 passes", () => {
5997 linter.defineRule("add-spaces", {
5999 fixable: "whitespace"
6006 message: "Add a space before this node.",
6007 fix: fixer => fixer.insertTextBefore(node, " ")
6014 const fixResult = linter.verifyAndFix("a", { rules: { "add-spaces": "error" } });
6016 assert.strictEqual(fixResult.fixed, true);
6017 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a
`);
6018 assert.strictEqual(fixResult.messages.length, 1);
6021 it("should throw an error if fix is passed but meta has no `fixable
` property", () => {
6022 linter.defineRule("test-rule", {
6027 create: context => ({
6029 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6034 assert.throws(() => {
6035 linter.verify("0", { rules: { "test-rule": "error" } });
6036 }, /Fixable rules must set the `meta
\.fixable
` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test-rule"$/u);
6039 it("should throw an error if fix is passed and there is no metadata", () => {
6040 linter.defineRule("test-rule", {
6041 create: context => ({
6043 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6048 assert.throws(() => {
6049 linter.verify("0", { rules: { "test-rule": "error" } });
6050 }, /Fixable rules must set the `meta
\.fixable
` property/u);
6053 it("should throw an error if fix is passed from a legacy-format rule", () => {
6054 linter.defineRule("test-rule", context => ({
6056 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
6060 assert.throws(() => {
6061 linter.verify("0", { rules: { "test-rule": "error" } });
6062 }, /Fixable rules must set the `meta
\.fixable
` property/u);
6066 describe("Edge cases", () => {
6068 it("should properly parse import statements when sourceType is module", () => {
6069 const code = "import foo from 'foo';";
6070 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6072 assert.strictEqual(messages.length, 0);
6075 it("should properly parse import all statements when sourceType is module", () => {
6076 const code = "import * as foo from 'foo';";
6077 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6079 assert.strictEqual(messages.length, 0);
6082 it("should properly parse default export statements when sourceType is module", () => {
6083 const code = "export default function initialize() {}";
6084 const messages = linter.verify(code, { parserOptions: { ecmaVersion: 6, sourceType: "module" } });
6086 assert.strictEqual(messages.length, 0);
6089 // https://github.com/eslint/eslint/issues/9687
6090 it("should report an error when invalid parserOptions found", () => {
6091 let messages = linter.verify("", { parserOptions: { ecmaVersion: 222 } });
6093 assert.deepStrictEqual(messages.length, 1);
6094 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
6096 messages = linter.verify("", { parserOptions: { sourceType: "foo" } });
6097 assert.deepStrictEqual(messages.length, 1);
6098 assert.ok(messages[0].message.includes("Invalid sourceType"));
6100 messages = linter.verify("", { parserOptions: { ecmaVersion: 5, sourceType: "module" } });
6101 assert.deepStrictEqual(messages.length, 1);
6102 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
6105 it("should not crash when invalid parentheses syntax is encountered", () => {
6106 linter.verify("left = (aSize.width/2) - ()");
6109 it("should not crash when let is used inside of switch case", () => {
6110 linter.verify("switch(foo) { case 1: let bar=2; }", { parserOptions: { ecmaVersion: 6 } });
6113 it("should not crash when parsing destructured assignment", () => {
6114 linter.verify("var { a='a' } = {};", { parserOptions: { ecmaVersion: 6 } });
6117 it("should report syntax error when a keyword exists in object property shorthand", () => {
6118 const messages = linter.verify("let a = {this}", { parserOptions: { ecmaVersion: 6 } });
6120 assert.strictEqual(messages.length, 1);
6121 assert.strictEqual(messages[0].fatal, true);
6124 it("should not rewrite env setting in core (https://github.com/eslint/eslint/issues/4814)", () => {
6127 * This test focuses on the instance of https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/conf/environments.js#L26-L28
6128 * This `verify()` takes the instance and runs https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/lib/eslint.js#L416
6130 linter.defineRule("test", () => ({}));
6131 linter.verify("var a = 0;", {
6132 env: { node: true },
6133 parserOptions: { ecmaVersion: 6, sourceType: "module" },
6137 // This `verify()` takes the instance and tests that the instance was not modified.
6140 linter.defineRule("test", context => {
6142 context.parserOptions.ecmaFeatures.globalReturn,
6143 "`ecmaFeatures
.globalReturn
` of the node environment should not be modified."
6148 linter.verify("var a = 0;", {
6149 env: { node: true },
6157 describe("Custom parser", () => {
6159 const errorPrefix = "Parsing error: ";
6161 it("should have file path passed to it", () => {
6162 const code = "/* this is code */";
6163 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
6165 linter.defineParser("stub-parser", testParsers.stubParser);
6166 linter.verify(code, { parser: "stub-parser" }, filename, true);
6168 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
6171 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
6172 const code = "var myDivElement = <div {...this.props} />;";
6174 linter.defineParser("esprima", esprima);
6175 const messages = linter.verify(code, { parser: "esprima", parserOptions: { jsx: true } }, "filename");
6177 assert.strictEqual(messages.length, 0);
6180 it("should return an error when the custom parser can't be found", () => {
6181 const code = "var myDivElement = <div {...this.props} />;";
6182 const messages = linter.verify(code, { parser: "esprima-xyz" }, "filename");
6184 assert.strictEqual(messages.length, 1);
6185 assert.strictEqual(messages[0].severity, 2);
6186 assert.strictEqual(messages[0].message, "Configured parser 'esprima-xyz' was not found.");
6189 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
6190 const code = "null %% 'foo'";
6192 linter.defineParser("unknown-logical-operator", testParsers.unknownLogicalOperator);
6194 // This shouldn't throw
6195 const messages = linter.verify(code, { parser: "unknown-logical-operator" }, filename, true);
6197 assert.strictEqual(messages.length, 0);
6200 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
6201 const code = "foo && bar %% baz";
6203 linter.defineParser("unknown-logical-operator-nested", testParsers.unknownLogicalOperatorNested);
6205 // This shouldn't throw
6206 const messages = linter.verify(code, { parser: "unknown-logical-operator-nested" }, filename, true);
6208 assert.strictEqual(messages.length, 0);
6211 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
6212 const code = "foo && bar %% baz";
6216 linter.defineRule("collect-node-types", () => ({
6218 nodes.push(node.type);
6222 linter.defineParser("non-js-parser", testParsers.nonJSParser);
6224 const messages = linter.verify(code, {
6225 parser: "non-js-parser",
6227 "collect-node-types": "error"
6231 assert.strictEqual(messages.length, 0);
6232 assert.isTrue(nodes.length > 0);
6235 it("should strip leading line: prefix from parser error", () => {
6236 linter.defineParser("line-error", testParsers.lineError);
6237 const messages = linter.verify(";", { parser: "line-error" }, "filename");
6239 assert.strictEqual(messages.length, 1);
6240 assert.strictEqual(messages[0].severity, 2);
6241 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
6244 it("should not modify a parser error message without a leading line: prefix", () => {
6245 linter.defineParser("no-line-error", testParsers.noLineError);
6246 const messages = linter.verify(";", { parser: "no-line-error" }, "filename");
6248 assert.strictEqual(messages.length, 1);
6249 assert.strictEqual(messages[0].severity, 2);
6250 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
6253 describe("if a parser provides 'visitorKeys'", () => {
6257 let firstChildNodes = [];
6261 firstChildNodes = [];
6262 linter.defineRule("collect-node-types", () => ({
6264 types.push(node.type);
6267 linter.defineRule("save-scope-manager", context => {
6268 scopeManager = context.getSourceCode().scopeManager;
6272 linter.defineRule("esquery-option", () => ({
6273 ":first-child"(node) {
6274 firstChildNodes.push(node);
6277 linter.defineParser("enhanced-parser2", testParsers.enhancedParser2);
6278 linter.verify("@foo class A {}", {
6279 parser: "enhanced-parser2",
6281 "collect-node-types": "error",
6282 "save-scope-manager": "error",
6283 "esquery-option": "error"
6287 sourceCode = linter.getSourceCode();
6290 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
6291 assert.deepStrictEqual(
6293 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
6297 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
6298 assert.deepStrictEqual(
6299 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
6300 ["experimentalDecorators", "id", "superClass", "body"]
6304 it("should use the same visitorKeys if the source code object is reused", () => {
6307 linter.defineRule("collect-node-types", () => ({
6309 types2.push(node.type);
6312 linter.verify(sourceCode, {
6314 "collect-node-types": "error"
6318 assert.deepStrictEqual(
6320 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
6324 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
6325 assert.deepStrictEqual(
6327 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
6332 describe("if a parser provides 'scope'", () => {
6334 let sourceCode = null;
6337 linter.defineParser("enhanced-parser3", testParsers.enhancedParser3);
6338 linter.defineRule("save-scope1", context => ({
6340 scope = context.getScope();
6343 linter.verify("@foo class A {}", { parser: "enhanced-parser3", rules: { "save-scope1": 2 } });
6345 sourceCode = linter.getSourceCode();
6348 it("should use the scope (so the global scope has the reference of '@foo')", () => {
6349 assert.strictEqual(scope.references.length, 1);
6350 assert.deepStrictEqual(
6351 scope.references[0].identifier.name,
6356 it("should use the same scope if the source code object is reused", () => {
6359 linter.defineRule("save-scope2", context => ({
6361 scope2 = context.getScope();
6364 linter.verify(sourceCode, { rules: { "save-scope2": 2 } }, "test.js");
6366 assert(scope2 !== null);
6367 assert(scope2 === scope);
6371 it("should not pass any default parserOptions to the parser", () => {
6372 linter.defineParser("throws-with-options", testParsers.throwsWithOptions);
6373 const messages = linter.verify(";", { parser: "throws-with-options" }, "filename");
6375 assert.strictEqual(messages.length, 0);
6379 describe("merging 'parserOptions'", () => {
6380 it("should deeply merge 'parserOptions' from an environment with 'parserOptions' from the provided config", () => {
6381 const code = "return <div/>";
6384 node: true // ecmaFeatures: { globalReturn: true }
6393 const messages = linter.verify(code, config);
6395 // no parsing errors
6396 assert.strictEqual(messages.length, 0);
6401 describe("Linter with FlatConfigArray", () => {
6404 const filename = "filename.js";
6407 * Creates a config array with some default properties.
6408 * @param {FlatConfig|FlatConfig[]} value The value to base the
6410 * @returns {FlatConfigArray} The created config array.
6412 function createFlatConfigArray(value) {
6413 return new FlatConfigArray(value, { basePath: "" });
6417 linter = new Linter({ configType: "flat" });
6420 describe("Static Members", () => {
6421 describe("version", () => {
6422 it("should return same version as instance property", () => {
6423 assert.strictEqual(Linter.version, linter.version);
6428 describe("Config Options", () => {
6430 describe("languageOptions", () => {
6432 describe("ecmaVersion", () => {
6434 it("should error when accessing a global that isn't available in ecmaVersion 5", () => {
6435 const messages = linter.verify("new Map()", {
6438 sourceType: "script"
6445 assert.strictEqual(messages.length, 1, "There should be one linting error.");
6446 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
6449 it("should error when accessing a global that isn't available in ecmaVersion 3", () => {
6450 const messages = linter.verify("JSON.stringify({})", {
6453 sourceType: "script"
6460 assert.strictEqual(messages.length, 1, "There should be one linting error.");
6461 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
6464 it("should add globals for ES6 when ecmaVersion is 6", () => {
6465 const messages = linter.verify("new Map()", {
6474 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
6477 it("should allow destructuring when ecmaVersion is 6", () => {
6478 const messages = linter.verify("let {a} = b", {
6484 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
6487 it("ecmaVersion should be normalized to year name for ES 6", () => {
6495 assert.strictEqual(context.languageOptions.ecmaVersion, 2015);
6505 rules: { "test/checker": "error" }
6508 linter.verify("foo", config, filename);
6511 it("ecmaVersion should be normalized to latest year by default", () => {
6519 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
6526 rules: { "test/checker": "error" }
6529 linter.verify("foo", config, filename);
6532 it("ecmaVersion should not be normalized to year name for ES 5", () => {
6540 assert.strictEqual(context.languageOptions.ecmaVersion, 5);
6550 rules: { "test/checker": "error" }
6553 linter.verify("foo", config, filename);
6556 it("ecmaVersion should be normalized to year name for 'latest'", () => {
6564 assert.strictEqual(context.languageOptions.ecmaVersion, espree.latestEcmaVersion + 2009);
6572 ecmaVersion: "latest"
6574 rules: { "test/checker": "error" }
6577 linter.verify("foo", config, filename);
6583 describe("sourceType", () => {
6585 it("should be module by default", () => {
6593 assert.strictEqual(context.languageOptions.sourceType, "module");
6600 rules: { "test/checker": "error" }
6603 linter.verify("import foo from 'bar'", config, filename);
6606 it("should default to commonjs when passed a .cjs filename", () => {
6614 assert.strictEqual(context.languageOptions.sourceType, "commonjs");
6621 rules: { "test/checker": "error" }
6624 linter.verify("import foo from 'bar'", config, `${filename}
.cjs
`);
6628 it("should error when import is used in a script", () => {
6629 const messages = linter.verify("import foo from 'bar';", {
6632 sourceType: "script"
6636 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
6637 assert.strictEqual(messages[0].message, "Parsing error: 'import' and 'export' may appear only with 'sourceType: module'");
6640 it("should not error when import is used in a module", () => {
6641 const messages = linter.verify("import foo from 'bar';", {
6644 sourceType: "module"
6648 assert.strictEqual(messages.length, 0, "There should no linting errors.");
6651 it("should error when return is used at the top-level outside of commonjs", () => {
6652 const messages = linter.verify("return", {
6655 sourceType: "script"
6659 assert.strictEqual(messages.length, 1, "There should be one parsing error.");
6660 assert.strictEqual(messages[0].message, "Parsing error: 'return' outside of function");
6663 it("should not error when top-level return is used in commonjs", () => {
6664 const messages = linter.verify("return", {
6667 sourceType: "commonjs"
6671 assert.strictEqual(messages.length, 0, "There should no linting errors.");
6674 it("should error when accessing a Node.js global outside of commonjs", () => {
6675 const messages = linter.verify("require()", {
6684 assert.strictEqual(messages.length, 1, "There should be one linting error.");
6685 assert.strictEqual(messages[0].ruleId, "no-undef", "The linting error should be no-undef.");
6688 it("should add globals for Node.js when sourceType is commonjs", () => {
6689 const messages = linter.verify("require()", {
6692 sourceType: "commonjs"
6699 assert.strictEqual(messages.length, 0, "There should be no linting errors.");
6702 it("should allow 'await' as a property name in modules", () => {
6703 const result = linter.verify(
6708 sourceType: "module"
6713 assert(result.length === 0);
6718 describe("parser", () => {
6720 it("should be able to define a custom parser", () => {
6722 parseForESLint: function parse(code, options) {
6724 ast: esprima.parse(code, options),
6740 "test-parser": parser
6745 parser: "test/test-parser"
6750 const messages = linter.verify("0", config, filename);
6752 assert.strictEqual(messages.length, 0);
6755 it("should pass parser as context.languageOptions.parser to all rules when provided on config", () => {
6761 "test-rule": sinon.mock().withArgs(
6762 sinon.match({ languageOptions: { parser: esprima } })
6775 linter.verify("0", config, filename);
6778 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
6783 "enhanced-parser": testParsers.enhancedParser
6788 parser: "test/enhanced-parser"
6792 const messages = linter.verify("0", config, filename);
6794 assert.strictEqual(messages.length, 0);
6797 it("should expose parser services when using parseForESLint() and services are specified", () => {
6803 "enhanced-parser": testParsers.enhancedParser
6806 "test-service-rule": context => ({
6810 message: context.parserServices.test.getMessage()
6818 parser: "test/enhanced-parser"
6821 "test/test-service-rule": 2
6825 const messages = linter.verify("0", config, filename);
6827 assert.strictEqual(messages.length, 1);
6828 assert.strictEqual(messages[0].message, "Hi!");
6831 it("should use the same parserServices if source code object is reused", () => {
6837 "enhanced-parser": testParsers.enhancedParser
6840 "test-service-rule": context => ({
6844 message: context.parserServices.test.getMessage()
6852 parser: "test/enhanced-parser"
6855 "test/test-service-rule": 2
6859 const messages = linter.verify("0", config, filename);
6861 assert.strictEqual(messages.length, 1);
6862 assert.strictEqual(messages[0].message, "Hi!");
6864 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
6866 assert.strictEqual(messages2.length, 1);
6867 assert.strictEqual(messages2[0].message, "Hi!");
6870 it("should pass parser as context.languageOptions.parser to all rules when default parser is used", () => {
6872 // references to Espree get messed up in a browser context, so wrap it
6873 const fakeParser = {
6877 const spy = sinon.spy(context => {
6878 assert.strictEqual(context.languageOptions.parser, fakeParser);
6898 linter.verify("0", config, filename);
6899 assert.isTrue(spy.calledOnce);
6903 describe("Custom Parsers", () => {
6905 const errorPrefix = "Parsing error: ";
6907 it("should have file path passed to it", () => {
6908 const code = "/* this is code */";
6909 const parseSpy = sinon.spy(testParsers.stubParser, "parse");
6912 parser: testParsers.stubParser
6916 linter.verify(code, config, filename, true);
6918 sinon.assert.calledWithMatch(parseSpy, "", { filePath: filename });
6921 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
6922 const code = "var myDivElement = <div {...this.props} />;";
6932 const messages = linter.verify(code, config, filename);
6934 assert.strictEqual(messages.length, 0);
6937 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
6938 const code = "null %% 'foo'";
6941 parser: testParsers.unknownLogicalOperator
6945 // This shouldn't throw
6946 const messages = linter.verify(code, config, filename);
6948 assert.strictEqual(messages.length, 0);
6951 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
6952 const code = "foo && bar %% baz";
6955 parser: testParsers.unknownLogicalOperatorNested
6959 // This shouldn't throw
6960 const messages = linter.verify(code, config, filename);
6962 assert.strictEqual(messages.length, 0);
6965 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
6966 const code = "foo && bar %% baz";
6972 "collect-node-types": () => ({
6974 nodes.push(node.type);
6981 parser: testParsers.nonJSParser
6984 "test/collect-node-types": "error"
6988 const messages = linter.verify(code, config, filename, true);
6990 assert.strictEqual(messages.length, 0);
6991 assert.isTrue(nodes.length > 0);
6994 it("should strip leading line: prefix from parser error", () => {
6995 const messages = linter.verify(";", {
6997 parser: testParsers.lineError
7001 assert.strictEqual(messages.length, 1);
7002 assert.strictEqual(messages[0].severity, 2);
7003 assert.strictEqual(messages[0].message, errorPrefix + testParsers.lineError.expectedError);
7006 it("should not modify a parser error message without a leading line: prefix", () => {
7007 const messages = linter.verify(";", {
7009 parser: testParsers.noLineError
7013 assert.strictEqual(messages.length, 1);
7014 assert.strictEqual(messages[0].severity, 2);
7015 assert.strictEqual(messages[0].message, errorPrefix + testParsers.noLineError.expectedError);
7018 describe("if a parser provides 'visitorKeys'", () => {
7022 let firstChildNodes = [];
7026 firstChildNodes = [];
7031 "collect-node-types": () => ({
7033 types.push(node.type);
7036 "save-scope-manager": context => {
7037 scopeManager = context.getSourceCode().scopeManager;
7041 "esquery-option": () => ({
7042 ":first-child"(node) {
7043 firstChildNodes.push(node);
7050 parser: testParsers.enhancedParser2
7053 "test/collect-node-types": "error",
7054 "test/save-scope-manager": "error",
7055 "test/esquery-option": "error"
7059 linter.verify("@foo class A {}", config);
7061 sourceCode = linter.getSourceCode();
7064 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
7065 assert.deepStrictEqual(
7067 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7071 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7072 assert.deepStrictEqual(
7073 scopeManager.__options.childVisitorKeys.ClassDeclaration, // eslint-disable-line no-underscore-dangle -- ScopeManager API
7074 ["experimentalDecorators", "id", "superClass", "body"]
7078 it("should use the same visitorKeys if the source code object is reused", () => {
7084 "collect-node-types": () => ({
7086 types2.push(node.type);
7093 "test/collect-node-types": "error"
7097 linter.verify(sourceCode, config);
7099 assert.deepStrictEqual(
7101 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
7105 it("esquery should use the visitorKeys (so 'visitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
7106 assert.deepStrictEqual(
7108 [sourceCode.ast.body[0], sourceCode.ast.body[0].experimentalDecorators[0]]
7113 describe("if a parser provides 'scope'", () => {
7115 let sourceCode = null;
7122 "save-scope1": context => ({
7124 scope = context.getScope();
7131 parser: testParsers.enhancedParser3
7134 "test/save-scope1": "error"
7138 linter.verify("@foo class A {}", config);
7140 sourceCode = linter.getSourceCode();
7143 it("should use the scope (so the global scope has the reference of '@foo')", () => {
7144 assert.strictEqual(scope.references.length, 1);
7145 assert.deepStrictEqual(
7146 scope.references[0].identifier.name,
7151 it("should use the same scope if the source code object is reused", () => {
7157 "save-scope2": context => ({
7159 scope2 = context.getScope();
7166 "test/save-scope2": "error"
7170 linter.verify(sourceCode, config, "test.js");
7172 assert(scope2 !== null);
7173 assert(scope2 === scope);
7177 it("should pass default languageOptions to the parser", () => {
7179 const spy = sinon.spy((code, options) => espree.parse(code, options));
7181 linter.verify(";", {
7189 assert(spy.calledWithMatch(";", {
7190 ecmaVersion: espree.latestEcmaVersion + 2009,
7191 sourceType: "module"
7199 describe("parseOptions", () => {
7201 it("should pass ecmaFeatures to all rules when provided on config", () => {
7203 const parserOptions = {
7213 "test-rule": sinon.mock().withArgs(
7214 sinon.match({ languageOptions: { parserOptions } })
7227 linter.verify("0", config, filename);
7230 it("should switch globalReturn to false if sourceType is module", () => {
7236 "test-rule": sinon.mock().withArgs(
7251 sourceType: "module",
7263 linter.verify("0", config, filename);
7266 it("should not parse sloppy-mode code when impliedStrict is true", () => {
7268 const messages = linter.verify("var private;", {
7278 assert.strictEqual(messages.length, 1);
7279 assert.strictEqual(messages[0].message, "Parsing error: The keyword 'private' is reserved");
7282 it("should properly parse valid code when impliedStrict is true", () => {
7284 const messages = linter.verify("var foo;", {
7294 assert.strictEqual(messages.length, 0);
7297 it("should properly parse JSX when passed ecmaFeatures", () => {
7299 const messages = linter.verify("var x = <div/>;", {
7309 assert.strictEqual(messages.length, 0);
7312 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
7313 const code = "var myDivElement = <div className=\"foo\" />;";
7314 const messages = linter.verify(code, {}, "filename");
7316 assert.strictEqual(messages.length, 1);
7317 assert.strictEqual(messages[0].line, 1);
7318 assert.strictEqual(messages[0].column, 20);
7319 assert.strictEqual(messages[0].message, "Parsing error: Unexpected token <");
7322 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
7323 const code = "var myDivElement = <div className=\"foo\" />;";
7324 const messages = linter.verify(code, {
7335 assert.strictEqual(messages.length, 0);
7338 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
7339 const code = "var myDivElement = <div {...this.props} />;";
7340 const messages = linter.verify(code, {
7352 assert.strictEqual(messages.length, 0);
7359 describe("settings", () => {
7360 const ruleId = "test-rule";
7362 it("should pass settings to all rules", () => {
7368 [ruleId]: context => ({
7370 context.report(node, context.settings.info);
7380 [`test
/${ruleId}
`]: 1
7384 const messages = linter.verify("0", config, filename);
7386 assert.strictEqual(messages.length, 1);
7387 assert.strictEqual(messages[0].message, "Hello");
7390 it("should not have any settings if they were not passed in", () => {
7396 [ruleId]: context => ({
7398 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
7399 context.report(node, "Settings should be empty");
7409 [`test
/${ruleId}
`]: 1
7413 const messages = linter.verify("0", config, filename);
7415 assert.strictEqual(messages.length, 0);
7419 describe("rules", () => {
7420 const code = "var answer = 6 * 7";
7422 it("should be configurable by only setting the integer value", () => {
7423 const rule = "semi",
7424 config = { rules: {} };
7426 config.rules[rule] = 1;
7428 const messages = linter.verify(code, config, filename, true);
7430 assert.strictEqual(messages.length, 1);
7431 assert.strictEqual(messages[0].ruleId, rule);
7434 it("should be configurable by only setting the string value", () => {
7435 const rule = "semi",
7436 config = { rules: {} };
7438 config.rules[rule] = "warn";
7440 const messages = linter.verify(code, config, filename, true);
7442 assert.strictEqual(messages.length, 1);
7443 assert.strictEqual(messages[0].severity, 1);
7444 assert.strictEqual(messages[0].ruleId, rule);
7447 it("should be configurable by passing in values as an array", () => {
7448 const rule = "semi",
7449 config = { rules: {} };
7451 config.rules[rule] = [1];
7453 const messages = linter.verify(code, config, filename, true);
7455 assert.strictEqual(messages.length, 1);
7456 assert.strictEqual(messages[0].ruleId, rule);
7459 it("should be configurable by passing in string value as an array", () => {
7460 const rule = "semi",
7461 config = { rules: {} };
7463 config.rules[rule] = ["warn"];
7465 const messages = linter.verify(code, config, filename, true);
7467 assert.strictEqual(messages.length, 1);
7468 assert.strictEqual(messages[0].severity, 1);
7469 assert.strictEqual(messages[0].ruleId, rule);
7472 it("should not be configurable by setting other value", () => {
7473 const rule = "semi",
7474 config = { rules: {} };
7476 config.rules[rule] = "1";
7478 assert.throws(() => {
7479 linter.verify(code, config, filename, true);
7480 }, /Key "rules": Key "semi"/u);
7483 it("should process empty config", () => {
7485 const messages = linter.verify(code, config, filename, true);
7487 assert.strictEqual(messages.length, 0);
7493 describe("verify()", () => {
7495 it("should report warnings in order by line and column when called", () => {
7497 const code = "foo()\n alert('test')";
7498 const config = { rules: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi: [1, "always"] } };
7500 const messages = linter.verify(code, config, filename);
7502 assert.strictEqual(messages.length, 3);
7503 assert.strictEqual(messages[0].line, 1);
7504 assert.strictEqual(messages[0].column, 6);
7505 assert.strictEqual(messages[1].line, 2);
7506 assert.strictEqual(messages[1].column, 18);
7507 assert.strictEqual(messages[2].line, 2);
7508 assert.strictEqual(messages[2].column, 18);
7511 describe("Plugins", () => {
7513 it("should not load rule definition when rule isn't used", () => {
7515 const spy = sinon.spy();
7527 linter.verify("code", config, filename);
7528 assert.isTrue(spy.notCalled, "Rule should not have been called");
7532 describe("Rule Internals", () => {
7534 const code = TEST_CODE;
7536 it("should throw an error when an error occurs inside of a rule visitor", () => {
7543 throw new Error("Intentional error.");
7549 rules: { "test/checker": "error" }
7552 assert.throws(() => {
7553 linter.verify(code, config, filename);
7554 }, `Intentional error
.\nOccurred while linting ${filename}
:1\nRule: "test/checker"`);
7557 it("should not call rule visitor with a `this` value", () => {
7558 const spy = sinon.spy();
7569 rules: { "test/checker": "error" }
7572 linter.verify("foo", config);
7573 assert(spy.calledOnce);
7574 assert.strictEqual(spy.firstCall.thisValue, void 0);
7577 it("should not call unrecognized rule visitor when present in a rule", () => {
7578 const spy = sinon.spy();
7590 "test/checker": "error",
7595 linter.verify("foo", config);
7596 assert(spy.notCalled);
7599 it("should have all the `parent
` properties on nodes when the rule visitors are created", () => {
7600 const spy = sinon.spy(context => {
7601 const ast = context.getSourceCode().ast;
7603 assert.strictEqual(ast.body[0].parent, ast);
7604 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
7605 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
7606 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
7619 rules: { "test/checker": "error" }
7622 linter.verify("foo + bar", config);
7623 assert(spy.calledOnce);
7626 it("events for each node type should fire", () => {
7628 // spies for various AST node types
7629 const spyLiteral = sinon.spy(),
7630 spyVariableDeclarator = sinon.spy(),
7631 spyVariableDeclaration = sinon.spy(),
7632 spyIdentifier = sinon.spy(),
7633 spyBinaryExpression = sinon.spy();
7641 Literal: spyLiteral,
7642 VariableDeclarator: spyVariableDeclarator,
7643 VariableDeclaration: spyVariableDeclaration,
7644 Identifier: spyIdentifier,
7645 BinaryExpression: spyBinaryExpression
7651 rules: { "test/checker": "error" }
7654 const messages = linter.verify(code, config, filename, true);
7656 assert.strictEqual(messages.length, 0);
7657 sinon.assert.calledOnce(spyVariableDeclaration);
7658 sinon.assert.calledOnce(spyVariableDeclarator);
7659 sinon.assert.calledOnce(spyIdentifier);
7660 sinon.assert.calledTwice(spyLiteral);
7661 sinon.assert.calledOnce(spyBinaryExpression);
7664 it("should throw an error if a rule reports a problem without a message", () => {
7670 "invalid-report"(context) {
7673 context.report({ node });
7680 rules: { "test/invalid-report": "error" }
7684 () => linter.verify("foo", config),
7686 "Missing `message
` property in report() call; add a message that describes the linting problem."
7693 describe("Rule Context", () => {
7695 describe("context.getFilename()", () => {
7696 const ruleId = "filename-rule";
7698 it("has access to the filename", () => {
7704 [ruleId]: context => ({
7706 context.report(node, context.getFilename());
7713 [`test
/${ruleId}
`]: 1
7717 const messages = linter.verify("0", config, filename);
7719 assert.strictEqual(messages[0].message, filename);
7722 it("defaults filename to '<input>'", () => {
7728 [ruleId]: context => ({
7730 context.report(node, context.getFilename());
7737 [`test
/${ruleId}
`]: 1
7742 const messages = linter.verify("0", config);
7744 assert.strictEqual(messages[0].message, "<input>");
7748 describe("context.getPhysicalFilename()", () => {
7750 const ruleId = "filename-rule";
7752 it("has access to the physicalFilename", () => {
7758 [ruleId]: context => ({
7760 context.report(node, context.getPhysicalFilename());
7767 [`test
/${ruleId}
`]: 1
7771 const messages = linter.verify("0", config, filename);
7773 assert.strictEqual(messages[0].message, filename);
7778 describe("context.getSourceLines()", () => {
7780 it("should get proper lines when using \\n as a line break", () => {
7781 const code = "a;\nb;";
7782 const spy = sinon.spy(context => {
7783 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
7795 rules: { "test/checker": "error" }
7798 linter.verify(code, config);
7799 assert(spy.calledOnce);
7802 it("should get proper lines when using \\r\\n as a line break", () => {
7803 const code = "a;\r\nb;";
7804 const spy = sinon.spy(context => {
7805 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
7817 rules: { "test/checker": "error" }
7820 linter.verify(code, config);
7821 assert(spy.calledOnce);
7824 it("should get proper lines when using \\r as a line break", () => {
7825 const code = "a;\rb;";
7826 const spy = sinon.spy(context => {
7827 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
7839 rules: { "test/checker": "error" }
7842 linter.verify(code, config);
7843 assert(spy.calledOnce);
7846 it("should get proper lines when using \\u2028 as a line break", () => {
7847 const code = "a;\u2028b;";
7848 const spy = sinon.spy(context => {
7849 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
7861 rules: { "test/checker": "error" }
7864 linter.verify(code, config);
7865 assert(spy.calledOnce);
7868 it("should get proper lines when using \\u2029 as a line break", () => {
7869 const code = "a;\u2029b;";
7870 const spy = sinon.spy(context => {
7871 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
7883 rules: { "test/checker": "error" }
7886 linter.verify(code, config);
7887 assert(spy.calledOnce);
7892 describe("context.getSource()", () => {
7893 const code = TEST_CODE;
7895 it("should retrieve all text when used without parameters", () => {
7903 checker: context => {
7904 spy = sinon.spy(() => {
7905 assert.strictEqual(context.getSource(), TEST_CODE);
7907 return { Program: spy };
7912 rules: { "test/checker": "error" }
7915 linter.verify(code, config);
7916 assert(spy && spy.calledOnce);
7919 it("should retrieve all text for root node", () => {
7927 checker: context => {
7928 spy = sinon.spy(node => {
7929 assert.strictEqual(context.getSource(node), TEST_CODE);
7931 return { Program: spy };
7936 rules: { "test/checker": "error" }
7939 linter.verify(code, config);
7940 assert(spy && spy.calledOnce);
7943 it("should clamp to valid range when retrieving characters before start of source", () => {
7950 checker: context => {
7951 spy = sinon.spy(node => {
7952 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
7954 return { Program: spy };
7959 rules: { "test/checker": "error" }
7962 linter.verify(code, config);
7963 assert(spy && spy.calledOnce);
7966 it("should retrieve all text for binary expression", () => {
7973 checker: context => {
7974 spy = sinon.spy(node => {
7975 assert.strictEqual(context.getSource(node), "6 * 7");
7977 return { BinaryExpression: spy };
7982 rules: { "test/checker": "error" }
7985 linter.verify(code, config);
7986 assert(spy && spy.calledOnce);
7989 it("should retrieve all text plus two characters before for binary expression", () => {
7996 checker: context => {
7997 spy = sinon.spy(node => {
7998 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
8000 return { BinaryExpression: spy };
8005 rules: { "test/checker": "error" }
8008 linter.verify(code, config);
8009 assert(spy && spy.calledOnce);
8012 it("should retrieve all text plus one character after for binary expression", () => {
8019 checker: context => {
8020 spy = sinon.spy(node => {
8021 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
8023 return { BinaryExpression: spy };
8028 rules: { "test/checker": "error" }
8031 linter.verify(code, config);
8032 assert(spy && spy.calledOnce);
8035 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
8042 checker: context => {
8043 spy = sinon.spy(node => {
8044 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
8046 return { BinaryExpression: spy };
8051 rules: { "test/checker": "error" }
8054 linter.verify(code, config);
8055 assert(spy && spy.calledOnce);
8060 describe("context.getAncestors()", () => {
8061 const code = TEST_CODE;
8063 it("should retrieve all ancestors when used", () => {
8071 checker: context => {
8072 spy = sinon.spy(() => {
8073 const ancestors = context.getAncestors();
8075 assert.strictEqual(ancestors.length, 3);
8077 return { BinaryExpression: spy };
8082 rules: { "test/checker": "error" }
8085 linter.verify(code, config, filename, true);
8086 assert(spy && spy.calledOnce);
8089 it("should retrieve empty ancestors for root node", () => {
8096 checker: context => {
8097 spy = sinon.spy(() => {
8098 const ancestors = context.getAncestors();
8100 assert.strictEqual(ancestors.length, 0);
8103 return { Program: spy };
8108 rules: { "test/checker": "error" }
8111 linter.verify(code, config);
8112 assert(spy && spy.calledOnce);
8116 describe("context.getNodeByRangeIndex()", () => {
8117 const code = TEST_CODE;
8119 it("should retrieve a node starting at the given index", () => {
8120 const spy = sinon.spy(context => {
8121 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
8133 rules: { "test/checker": "error" }
8136 linter.verify(code, config);
8137 assert(spy.calledOnce);
8140 it("should retrieve a node containing the given index", () => {
8141 const spy = sinon.spy(context => {
8142 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
8154 rules: { "test/checker": "error" }
8157 linter.verify(code, config);
8158 assert(spy.calledOnce);
8161 it("should retrieve a node that is exactly the given index", () => {
8162 const spy = sinon.spy(context => {
8163 const node = context.getNodeByRangeIndex(13);
8165 assert.strictEqual(node.type, "Literal");
8166 assert.strictEqual(node.value, 6);
8178 rules: { "test/checker": "error" }
8181 linter.verify(code, config);
8182 assert(spy.calledOnce);
8185 it("should retrieve a node ending with the given index", () => {
8186 const spy = sinon.spy(context => {
8187 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
8199 rules: { "test/checker": "error" }
8202 linter.verify(code, config);
8203 assert(spy.calledOnce);
8206 it("should retrieve the deepest node containing the given index", () => {
8207 const spy = sinon.spy(context => {
8208 const node1 = context.getNodeByRangeIndex(14);
8210 assert.strictEqual(node1.type, "BinaryExpression");
8212 const node2 = context.getNodeByRangeIndex(3);
8214 assert.strictEqual(node2.type, "VariableDeclaration");
8226 rules: { "test/checker": "error" }
8229 linter.verify(code, config);
8230 assert(spy.calledOnce);
8233 it("should return null if the index is outside the range of any node", () => {
8234 const spy = sinon.spy(context => {
8235 const node1 = context.getNodeByRangeIndex(-1);
8237 assert.isNull(node1);
8239 const node2 = context.getNodeByRangeIndex(-99);
8241 assert.isNull(node2);
8253 rules: { "test/checker": "error" }
8256 linter.verify(code, config);
8257 assert(spy.calledOnce);
8261 describe("context.getScope()", () => {
8262 const codeToTestScope = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
8264 it("should retrieve the global scope correctly from a Program", () => {
8271 checker: context => {
8272 spy = sinon.spy(() => {
8273 const scope = context.getScope();
8275 assert.strictEqual(scope.type, "global");
8277 return { Program: spy };
8285 rules: { "test/checker": "error" }
8288 linter.verify(codeToTestScope, config);
8289 assert(spy && spy.calledOnce);
8292 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
8299 checker: context => {
8300 spy = sinon.spy(() => {
8301 const scope = context.getScope();
8303 assert.strictEqual(scope.type, "function");
8305 return { FunctionDeclaration: spy };
8313 rules: { "test/checker": "error" }
8316 linter.verify(codeToTestScope, config);
8317 assert(spy && spy.calledTwice);
8320 it("should retrieve the function scope correctly from a LabeledStatement", () => {
8327 checker: context => {
8328 spy = sinon.spy(() => {
8329 const scope = context.getScope();
8331 assert.strictEqual(scope.type, "function");
8332 assert.strictEqual(scope.block.id.name, "foo");
8334 return { LabeledStatement: spy };
8342 rules: { "test/checker": "error" }
8346 linter.verify(codeToTestScope, config);
8347 assert(spy && spy.calledOnce);
8350 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
8357 checker: context => {
8358 spy = sinon.spy(() => {
8359 const scope = context.getScope();
8361 assert.strictEqual(scope.type, "function");
8362 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
8365 return { ReturnStatement: spy };
8373 rules: { "test/checker": "error" }
8377 linter.verify(codeToTestScope, config);
8378 assert(spy && spy.calledOnce);
8381 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
8388 checker: context => {
8389 spy = sinon.spy(() => {
8390 const scope = context.getScope();
8392 assert.strictEqual(scope.type, "switch");
8393 assert.strictEqual(scope.block.type, "SwitchStatement");
8396 return { SwitchStatement: spy };
8404 rules: { "test/checker": "error" }
8407 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
8408 assert(spy && spy.calledOnce);
8411 it("should retrieve the function scope correctly from within a BlockStatement", () => {
8418 checker: context => {
8419 spy = sinon.spy(() => {
8420 const scope = context.getScope();
8422 assert.strictEqual(scope.type, "block");
8423 assert.strictEqual(scope.block.type, "BlockStatement");
8426 return { BlockStatement: spy };
8434 rules: { "test/checker": "error" }
8438 linter.verify("var x; {let y = 1}", config);
8439 assert(spy && spy.calledOnce);
8442 it("should retrieve the function scope correctly from within a nested block statement", () => {
8449 checker: context => {
8450 spy = sinon.spy(() => {
8451 const scope = context.getScope();
8453 assert.strictEqual(scope.type, "block");
8454 assert.strictEqual(scope.block.type, "BlockStatement");
8457 return { BlockStatement: spy };
8465 rules: { "test/checker": "error" }
8469 linter.verify("if (true) { let x = 1 }", config);
8470 assert(spy && spy.calledOnce);
8473 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
8480 checker: context => {
8481 spy = sinon.spy(() => {
8482 const scope = context.getScope();
8484 assert.strictEqual(scope.type, "function");
8485 assert.strictEqual(scope.block.type, "FunctionDeclaration");
8488 return { FunctionDeclaration: spy };
8496 rules: { "test/checker": "error" }
8500 linter.verify("function foo() {}", config);
8501 assert(spy && spy.calledOnce);
8504 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
8511 checker: context => {
8512 spy = sinon.spy(() => {
8513 const scope = context.getScope();
8515 assert.strictEqual(scope.type, "function");
8516 assert.strictEqual(scope.block.type, "FunctionExpression");
8519 return { FunctionExpression: spy };
8527 rules: { "test/checker": "error" }
8531 linter.verify("(function foo() {})();", config);
8532 assert(spy && spy.calledOnce);
8535 it("should retrieve the catch scope correctly from within a CatchClause", () => {
8542 checker: context => {
8543 spy = sinon.spy(() => {
8544 const scope = context.getScope();
8546 assert.strictEqual(scope.type, "catch");
8547 assert.strictEqual(scope.block.type, "CatchClause");
8550 return { CatchClause: spy };
8558 rules: { "test/checker": "error" }
8561 linter.verify("try {} catch (err) {}", config);
8562 assert(spy && spy.calledOnce);
8565 it("should retrieve module scope correctly from an ES6 module", () => {
8572 checker: context => {
8573 spy = sinon.spy(() => {
8574 const scope = context.getScope();
8576 assert.strictEqual(scope.type, "module");
8579 return { AssignmentExpression: spy };
8586 sourceType: "module"
8588 rules: { "test/checker": "error" }
8592 linter.verify("var foo = {}; foo.bar = 1;", config);
8593 assert(spy && spy.calledOnce);
8596 it("should retrieve function scope correctly when sourceType is commonjs", () => {
8603 checker: context => {
8604 spy = sinon.spy(() => {
8605 const scope = context.getScope();
8607 assert.strictEqual(scope.type, "function");
8610 return { AssignmentExpression: spy };
8617 sourceType: "commonjs"
8619 rules: { "test/checker": "error" }
8622 linter.verify("var foo = {}; foo.bar = 1;", config);
8623 assert(spy && spy.calledOnce);
8626 describe("Scope Internals", () => {
8629 * Get the scope on the node `astSelector
` specified.
8630 * @param {string} codeToEvaluate The source code to verify.
8631 * @param {string} astSelector The AST selector to get scope.
8632 * @param {number} [ecmaVersion=5] The ECMAScript version.
8633 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
8635 function getScope(codeToEvaluate, astSelector, ecmaVersion = 5) {
8642 "get-scope": context => ({
8643 [astSelector](node0) {
8645 scope = context.getScope();
8653 sourceType: "script"
8655 rules: { "test/get-scope": "error" }
8663 return { node, scope };
8666 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
8667 const { node, scope } = getScope("function f() {}", "FunctionDeclaration");
8669 assert.strictEqual(scope.type, "function");
8670 assert.strictEqual(scope.block, node);
8673 it("should return 'function' scope on FunctionExpression (ES5)", () => {
8674 const { node, scope } = getScope("!function f() {}", "FunctionExpression");
8676 assert.strictEqual(scope.type, "function");
8677 assert.strictEqual(scope.block, node);
8680 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
8681 const { node, scope } = getScope("function f() {}", "BlockStatement");
8683 assert.strictEqual(scope.type, "function");
8684 assert.strictEqual(scope.block, node.parent);
8687 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
8688 const { node, scope } = getScope("function f() {}", "BlockStatement", 2015);
8690 assert.strictEqual(scope.type, "function");
8691 assert.strictEqual(scope.block, node.parent);
8694 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
8695 const { node, scope } = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
8697 assert.strictEqual(scope.type, "function");
8698 assert.strictEqual(scope.block, node.parent.parent);
8699 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
8702 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
8703 const { node, scope } = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
8705 assert.strictEqual(scope.type, "block");
8706 assert.strictEqual(scope.upper.type, "function");
8707 assert.strictEqual(scope.block, node);
8708 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
8709 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "b"]);
8712 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
8713 const { node, scope } = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
8715 assert.strictEqual(scope.type, "block");
8716 assert.strictEqual(scope.upper.type, "block");
8717 assert.strictEqual(scope.upper.upper.type, "function");
8718 assert.strictEqual(scope.block, node);
8719 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
8720 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["a"]);
8721 assert.deepStrictEqual(scope.variableScope.variables.map(v => v.name), ["arguments", "c"]);
8724 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
8725 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
8727 assert.strictEqual(scope.type, "function");
8728 assert.strictEqual(scope.block, node.parent.parent);
8729 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
8732 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
8733 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
8735 assert.strictEqual(scope.type, "switch");
8736 assert.strictEqual(scope.block, node);
8737 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
8740 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
8741 const { node, scope } = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
8743 assert.strictEqual(scope.type, "function");
8744 assert.strictEqual(scope.block, node.parent.parent.parent);
8745 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "b"]);
8748 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
8749 const { node, scope } = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
8751 assert.strictEqual(scope.type, "switch");
8752 assert.strictEqual(scope.block, node.parent);
8753 assert.deepStrictEqual(scope.variables.map(v => v.name), ["b"]);
8756 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
8757 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
8759 assert.strictEqual(scope.type, "catch");
8760 assert.strictEqual(scope.block, node);
8761 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
8764 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
8765 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
8767 assert.strictEqual(scope.type, "catch");
8768 assert.strictEqual(scope.block, node);
8769 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
8772 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
8773 const { node, scope } = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
8775 assert.strictEqual(scope.type, "catch");
8776 assert.strictEqual(scope.block, node.parent);
8777 assert.deepStrictEqual(scope.variables.map(v => v.name), ["e"]);
8780 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
8781 const { node, scope } = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
8783 assert.strictEqual(scope.type, "block");
8784 assert.strictEqual(scope.block, node);
8785 assert.deepStrictEqual(scope.variables.map(v => v.name), ["a"]);
8788 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
8789 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
8791 assert.strictEqual(scope.type, "function");
8792 assert.strictEqual(scope.block, node.parent.parent);
8793 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
8796 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
8797 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
8799 assert.strictEqual(scope.type, "for");
8800 assert.strictEqual(scope.block, node);
8801 assert.deepStrictEqual(scope.variables.map(v => v.name), ["i"]);
8804 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
8805 const { node, scope } = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
8807 assert.strictEqual(scope.type, "function");
8808 assert.strictEqual(scope.block, node.parent.parent.parent);
8809 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "i"]);
8812 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
8813 const { node, scope } = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
8815 assert.strictEqual(scope.type, "block");
8816 assert.strictEqual(scope.upper.type, "for");
8817 assert.strictEqual(scope.block, node);
8818 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
8819 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["i"]);
8822 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
8823 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
8825 assert.strictEqual(scope.type, "function");
8826 assert.strictEqual(scope.block, node.parent.parent);
8827 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
8830 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
8831 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
8833 assert.strictEqual(scope.type, "for");
8834 assert.strictEqual(scope.block, node);
8835 assert.deepStrictEqual(scope.variables.map(v => v.name), ["key"]);
8838 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
8839 const { node, scope } = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
8841 assert.strictEqual(scope.type, "function");
8842 assert.strictEqual(scope.block, node.parent.parent.parent);
8843 assert.deepStrictEqual(scope.variables.map(v => v.name), ["arguments", "key"]);
8846 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
8847 const { node, scope } = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
8849 assert.strictEqual(scope.type, "block");
8850 assert.strictEqual(scope.upper.type, "for");
8851 assert.strictEqual(scope.block, node);
8852 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
8853 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["key"]);
8856 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
8857 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
8859 assert.strictEqual(scope.type, "for");
8860 assert.strictEqual(scope.block, node);
8861 assert.deepStrictEqual(scope.variables.map(v => v.name), ["x"]);
8864 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
8865 const { node, scope } = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
8867 assert.strictEqual(scope.type, "block");
8868 assert.strictEqual(scope.upper.type, "for");
8869 assert.strictEqual(scope.block, node);
8870 assert.deepStrictEqual(scope.variables.map(v => v.name), []);
8871 assert.deepStrictEqual(scope.upper.variables.map(v => v.name), ["x"]);
8874 it("should shadow the same name variable by the iteration variable.", () => {
8875 const { node, scope } = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
8877 assert.strictEqual(scope.type, "for");
8878 assert.strictEqual(scope.upper.type, "global");
8879 assert.strictEqual(scope.block, node);
8880 assert.strictEqual(scope.upper.variables[0].references.length, 0);
8881 assert.strictEqual(scope.references[0].identifier, node.left.declarations[0].id);
8882 assert.strictEqual(scope.references[1].identifier, node.right);
8883 assert.strictEqual(scope.references[1].resolved, scope.variables[0]);
8887 describe("Variables and references", () => {
8890 "function foo() { b; }",
8912 scope = context.getScope();
8921 globals: { e: true, f: false },
8922 sourceType: "script",
8930 linter.verify(code, config);
8938 it("Scope#through should contain references of undefined variables", () => {
8939 assert.strictEqual(scope.through.length, 2);
8940 assert.strictEqual(scope.through[0].identifier.name, "a");
8941 assert.strictEqual(scope.through[0].identifier.loc.start.line, 1);
8942 assert.strictEqual(scope.through[0].resolved, null);
8943 assert.strictEqual(scope.through[1].identifier.name, "b");
8944 assert.strictEqual(scope.through[1].identifier.loc.start.line, 2);
8945 assert.strictEqual(scope.through[1].resolved, null);
8948 it("Scope#variables should contain global variables", () => {
8949 assert(scope.variables.some(v => v.name === "Object"));
8950 assert(scope.variables.some(v => v.name === "foo"));
8951 assert(scope.variables.some(v => v.name === "c"));
8952 assert(scope.variables.some(v => v.name === "d"));
8953 assert(scope.variables.some(v => v.name === "e"));
8954 assert(scope.variables.some(v => v.name === "f"));
8957 it("Scope#set should contain global variables", () => {
8958 assert(scope.set.get("Object"));
8959 assert(scope.set.get("foo"));
8960 assert(scope.set.get("c"));
8961 assert(scope.set.get("d"));
8962 assert(scope.set.get("e"));
8963 assert(scope.set.get("f"));
8966 it("Variables#references should contain their references", () => {
8967 assert.strictEqual(scope.set.get("Object").references.length, 1);
8968 assert.strictEqual(scope.set.get("Object").references[0].identifier.name, "Object");
8969 assert.strictEqual(scope.set.get("Object").references[0].identifier.loc.start.line, 3);
8970 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
8971 assert.strictEqual(scope.set.get("foo").references.length, 1);
8972 assert.strictEqual(scope.set.get("foo").references[0].identifier.name, "foo");
8973 assert.strictEqual(scope.set.get("foo").references[0].identifier.loc.start.line, 4);
8974 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
8975 assert.strictEqual(scope.set.get("c").references.length, 1);
8976 assert.strictEqual(scope.set.get("c").references[0].identifier.name, "c");
8977 assert.strictEqual(scope.set.get("c").references[0].identifier.loc.start.line, 6);
8978 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
8979 assert.strictEqual(scope.set.get("d").references.length, 1);
8980 assert.strictEqual(scope.set.get("d").references[0].identifier.name, "d");
8981 assert.strictEqual(scope.set.get("d").references[0].identifier.loc.start.line, 8);
8982 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
8983 assert.strictEqual(scope.set.get("e").references.length, 1);
8984 assert.strictEqual(scope.set.get("e").references[0].identifier.name, "e");
8985 assert.strictEqual(scope.set.get("e").references[0].identifier.loc.start.line, 9);
8986 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
8987 assert.strictEqual(scope.set.get("f").references.length, 1);
8988 assert.strictEqual(scope.set.get("f").references[0].identifier.name, "f");
8989 assert.strictEqual(scope.set.get("f").references[0].identifier.loc.start.line, 10);
8990 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
8993 it("Reference#resolved should be their variable", () => {
8994 assert.strictEqual(scope.set.get("Object").references[0].resolved, scope.set.get("Object"));
8995 assert.strictEqual(scope.set.get("foo").references[0].resolved, scope.set.get("foo"));
8996 assert.strictEqual(scope.set.get("c").references[0].resolved, scope.set.get("c"));
8997 assert.strictEqual(scope.set.get("d").references[0].resolved, scope.set.get("d"));
8998 assert.strictEqual(scope.set.get("e").references[0].resolved, scope.set.get("e"));
8999 assert.strictEqual(scope.set.get("f").references[0].resolved, scope.set.get("f"));
9004 describe("context.getDeclaredVariables(node)", () => {
9007 * Assert `context
.getDeclaredVariables(node
)` is valid.
9008 * @param {string} code A code to check.
9009 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
9010 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
9013 function verify(code, type, expectedNamesList) {
9022 * Assert `context
.getDeclaredVariables(node
)` is empty.
9023 * @param {ASTNode} node A node to check.
9026 function checkEmpty(node) {
9027 assert.strictEqual(0, context.getDeclaredVariables(node).length);
9030 Program: checkEmpty,
9031 EmptyStatement: checkEmpty,
9032 BlockStatement: checkEmpty,
9033 ExpressionStatement: checkEmpty,
9034 LabeledStatement: checkEmpty,
9035 BreakStatement: checkEmpty,
9036 ContinueStatement: checkEmpty,
9037 WithStatement: checkEmpty,
9038 SwitchStatement: checkEmpty,
9039 ReturnStatement: checkEmpty,
9040 ThrowStatement: checkEmpty,
9041 TryStatement: checkEmpty,
9042 WhileStatement: checkEmpty,
9043 DoWhileStatement: checkEmpty,
9044 ForStatement: checkEmpty,
9045 ForInStatement: checkEmpty,
9046 DebuggerStatement: checkEmpty,
9047 ThisExpression: checkEmpty,
9048 ArrayExpression: checkEmpty,
9049 ObjectExpression: checkEmpty,
9050 Property: checkEmpty,
9051 SequenceExpression: checkEmpty,
9052 UnaryExpression: checkEmpty,
9053 BinaryExpression: checkEmpty,
9054 AssignmentExpression: checkEmpty,
9055 UpdateExpression: checkEmpty,
9056 LogicalExpression: checkEmpty,
9057 ConditionalExpression: checkEmpty,
9058 CallExpression: checkEmpty,
9059 NewExpression: checkEmpty,
9060 MemberExpression: checkEmpty,
9061 SwitchCase: checkEmpty,
9062 Identifier: checkEmpty,
9063 Literal: checkEmpty,
9064 ForOfStatement: checkEmpty,
9065 ArrowFunctionExpression: checkEmpty,
9066 YieldExpression: checkEmpty,
9067 TemplateLiteral: checkEmpty,
9068 TaggedTemplateExpression: checkEmpty,
9069 TemplateElement: checkEmpty,
9070 ObjectPattern: checkEmpty,
9071 ArrayPattern: checkEmpty,
9072 RestElement: checkEmpty,
9073 AssignmentPattern: checkEmpty,
9074 ClassBody: checkEmpty,
9075 MethodDefinition: checkEmpty,
9076 MetaProperty: checkEmpty
9079 rule[type] = function(node) {
9080 const expectedNames = expectedNamesList.shift();
9081 const variables = context.getDeclaredVariables(node);
9083 assert(Array.isArray(expectedNames));
9084 assert(Array.isArray(variables));
9085 assert.strictEqual(expectedNames.length, variables.length);
9086 for (let i = variables.length - 1; i >= 0; i--) {
9087 assert.strictEqual(expectedNames[i], variables[i].name);
9098 sourceType: "module"
9105 linter.verify(code, config);
9107 // Check all expected names are asserted.
9108 assert.strictEqual(0, expectedNamesList.length);
9111 it("VariableDeclaration", () => {
9112 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 ";
9116 ["g", "h", "i", "j", "k"],
9120 verify(code, "VariableDeclaration", namesList);
9123 it("VariableDeclaration (on for-in/of loop)", () => {
9125 // TDZ scope is created here, so tests to exclude those.
9126 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 ";
9134 verify(code, "VariableDeclaration", namesList);
9137 it("VariableDeclarator", () => {
9139 // TDZ scope is created here, so tests to exclude those.
9140 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 ";
9149 verify(code, "VariableDeclarator", namesList);
9152 it("FunctionDeclaration", () => {
9153 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 ";
9155 ["foo", "a", "b", "c", "d", "e"],
9156 ["bar", "f", "g", "h", "i", "j"]
9159 verify(code, "FunctionDeclaration", namesList);
9162 it("FunctionExpression", () => {
9163 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 ";
9165 ["foo", "a", "b", "c", "d", "e"],
9166 ["bar", "f", "g", "h", "i", "j"],
9170 verify(code, "FunctionExpression", namesList);
9173 it("ArrowFunctionExpression", () => {
9174 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 ";
9176 ["a", "b", "c", "d", "e"],
9177 ["f", "g", "h", "i", "j"]
9180 verify(code, "ArrowFunctionExpression", namesList);
9183 it("ClassDeclaration", () => {
9184 const code = "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
9186 ["A", "A"], // outer scope's and inner scope's.
9190 verify(code, "ClassDeclaration", namesList);
9193 it("ClassExpression", () => {
9194 const code = "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
9200 verify(code, "ClassExpression", namesList);
9203 it("CatchClause", () => {
9204 const code = "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
9210 verify(code, "CatchClause", namesList);
9213 it("ImportDeclaration", () => {
9214 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
9221 verify(code, "ImportDeclaration", namesList);
9224 it("ImportSpecifier", () => {
9225 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
9231 verify(code, "ImportSpecifier", namesList);
9234 it("ImportDefaultSpecifier", () => {
9235 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
9240 verify(code, "ImportDefaultSpecifier", namesList);
9243 it("ImportNamespaceSpecifier", () => {
9244 const code = "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
9249 verify(code, "ImportNamespaceSpecifier", namesList);
9253 describe("context.markVariableAsUsed()", () => {
9255 it("should mark variables in current scope as used", () => {
9256 const code = "var a = 1, b = 2;";
9263 checker: context => {
9264 spy = sinon.spy(() => {
9265 assert.isTrue(context.markVariableAsUsed("a"));
9267 const scope = context.getScope();
9269 assert.isTrue(getVariable(scope, "a").eslintUsed);
9270 assert.notOk(getVariable(scope, "b").eslintUsed);
9273 return { "Program:exit": spy };
9279 sourceType: "script"
9281 rules: { "test/checker": "error" }
9284 linter.verify(code, config);
9285 assert(spy && spy.calledOnce);
9288 it("should mark variables in function args as used", () => {
9289 const code = "function abc(a, b) { return 1; }";
9296 checker: context => {
9297 spy = sinon.spy(() => {
9298 assert.isTrue(context.markVariableAsUsed("a"));
9300 const scope = context.getScope();
9302 assert.isTrue(getVariable(scope, "a").eslintUsed);
9303 assert.notOk(getVariable(scope, "b").eslintUsed);
9306 return { ReturnStatement: spy };
9311 rules: { "test/checker": "error" }
9314 linter.verify(code, config);
9315 assert(spy && spy.calledOnce);
9318 it("should mark variables in higher scopes as used", () => {
9319 const code = "var a, b; function abc() { return 1; }";
9320 let returnSpy, exitSpy;
9326 checker: context => {
9327 returnSpy = sinon.spy(() => {
9328 assert.isTrue(context.markVariableAsUsed("a"));
9330 exitSpy = sinon.spy(() => {
9331 const scope = context.getScope();
9333 assert.isTrue(getVariable(scope, "a").eslintUsed);
9334 assert.notOk(getVariable(scope, "b").eslintUsed);
9337 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
9343 sourceType: "script"
9345 rules: { "test/checker": "error" }
9348 linter.verify(code, config);
9349 assert(returnSpy && returnSpy.calledOnce);
9350 assert(exitSpy && exitSpy.calledOnce);
9353 it("should mark variables as used when sourceType is commonjs", () => {
9354 const code = "var a = 1, b = 2;";
9361 checker: context => {
9362 spy = sinon.spy(() => {
9363 const globalScope = context.getScope(),
9364 childScope = globalScope.childScopes[0];
9366 assert.isTrue(context.markVariableAsUsed("a"), "Call to markVariableAsUsed should return true");
9368 assert.isTrue(getVariable(childScope, "a").eslintUsed, "'a' should be marked as used.");
9369 assert.isUndefined(getVariable(childScope, "b").eslintUsed, "'b' should be marked as used.");
9372 return { "Program:exit": spy };
9378 sourceType: "commonjs"
9380 rules: { "test/checker": "error" }
9383 linter.verify(code, config);
9384 assert(spy && spy.calledOnce, "Spy wasn't called.");
9387 it("should mark variables in modules as used", () => {
9388 const code = "var a = 1, b = 2;";
9395 checker: context => {
9396 spy = sinon.spy(() => {
9397 const globalScope = context.getScope(),
9398 childScope = globalScope.childScopes[0];
9400 assert.isTrue(context.markVariableAsUsed("a"));
9402 assert.isTrue(getVariable(childScope, "a").eslintUsed);
9403 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
9406 return { "Program:exit": spy };
9413 sourceType: "module"
9415 rules: { "test/checker": "error" }
9418 linter.verify(code, config);
9419 assert(spy && spy.calledOnce);
9422 it("should return false if the given variable is not found", () => {
9423 const code = "var a = 1, b = 2;";
9430 checker: context => {
9431 spy = sinon.spy(() => {
9432 assert.isFalse(context.markVariableAsUsed("c"));
9435 return { "Program:exit": spy };
9440 rules: { "test/checker": "error" }
9443 linter.verify(code, config);
9444 assert(spy && spy.calledOnce);
9448 describe("context.getCwd()", () => {
9449 const code = "a;\nb;";
9450 const baseConfig = { rules: { "test/checker": "error" } };
9452 it("should get cwd correctly in the context", () => {
9454 const linterWithOption = new Linter({ cwd, configType: "flat" });
9460 checker: context => {
9461 spy = sinon.spy(() => {
9462 assert.strictEqual(context.getCwd(), cwd);
9464 return { Program: spy };
9472 linterWithOption.verify(code, config);
9473 assert(spy && spy.calledOnce);
9476 it("should assign process.cwd() to it if cwd is undefined", () => {
9478 const linterWithOption = new Linter({ configType: "flat" });
9484 checker: context => {
9486 spy = sinon.spy(() => {
9487 assert.strictEqual(context.getCwd(), process.cwd());
9489 return { Program: spy };
9497 linterWithOption.verify(code, config);
9498 assert(spy && spy.calledOnce);
9501 it("should assign process.cwd() to it if the option is undefined", () => {
9507 checker: context => {
9509 spy = sinon.spy(() => {
9510 assert.strictEqual(context.getCwd(), process.cwd());
9512 return { Program: spy };
9520 linter.verify(code, config);
9521 assert(spy && spy.calledOnce);
9527 describe("Rule Severity", () => {
9529 it("rule should run as warning when set to 1 with a config array", () => {
9530 const ruleId = "semi",
9531 configs = createFlatConfigArray({
9538 configs.normalizeSync();
9539 const messages = linter.verify("foo", configs, filename, true);
9541 assert.strictEqual(messages.length, 1, "Message length is wrong");
9542 assert.strictEqual(messages[0].ruleId, ruleId);
9545 it("rule should run as warning when set to 1 with a plain array", () => {
9546 const ruleId = "semi",
9554 const messages = linter.verify("foo", configs, filename, true);
9556 assert.strictEqual(messages.length, 1, "Message length is wrong");
9557 assert.strictEqual(messages[0].ruleId, ruleId);
9560 it("rule should run as warning when set to 1 with an object", () => {
9561 const ruleId = "semi",
9569 const messages = linter.verify("foo", config, filename, true);
9571 assert.strictEqual(messages.length, 1, "Message length is wrong");
9572 assert.strictEqual(messages[0].ruleId, ruleId);
9576 describe("Code with a hashbang comment", () => {
9577 const code = "#!bin/program\n\nvar foo;;";
9579 it("should preserve line numbers", () => {
9580 const config = { rules: { "no-extra-semi": 1 } };
9581 const messages = linter.verify(code, config);
9583 assert.strictEqual(messages.length, 1);
9584 assert.strictEqual(messages[0].ruleId, "no-extra-semi");
9585 assert.strictEqual(messages[0].nodeType, "EmptyStatement");
9586 assert.strictEqual(messages[0].line, 3);
9589 it("should have a comment with the hashbang in it", () => {
9590 const spy = sinon.spy(context => {
9591 const comments = context.getAllComments();
9593 assert.strictEqual(comments.length, 1);
9594 assert.strictEqual(comments[0].type, "Shebang");
9607 "test/checker": "error"
9611 linter.verify(code, config);
9612 assert(spy.calledOnce);
9616 describe("Options", () => {
9618 describe("filename", () => {
9619 it("should allow filename to be passed on options object", () => {
9620 const filenameChecker = sinon.spy(context => {
9621 assert.strictEqual(context.getFilename(), "foo.js");
9629 checker: filenameChecker
9634 "test/checker": "error"
9638 linter.verify("foo;", config, { filename: "foo.js" });
9639 assert(filenameChecker.calledOnce);
9642 it("should allow filename to be passed as third argument", () => {
9643 const filenameChecker = sinon.spy(context => {
9644 assert.strictEqual(context.getFilename(), "bar.js");
9652 checker: filenameChecker
9657 "test/checker": "error"
9661 linter.verify("foo;", config, "bar.js");
9662 assert(filenameChecker.calledOnce);
9665 it("should default filename to <input> when options object doesn't have filename", () => {
9666 const filenameChecker = sinon.spy(context => {
9667 assert.strictEqual(context.getFilename(), "<input>");
9675 checker: filenameChecker
9680 "test/checker": "error"
9684 linter.verify("foo;", config, {});
9685 assert(filenameChecker.calledOnce);
9688 it("should default filename to <input> when only two arguments are passed", () => {
9689 const filenameChecker = sinon.spy(context => {
9690 assert.strictEqual(context.getFilename(), "<input>");
9698 checker: filenameChecker
9703 "test/checker": "error"
9707 linter.verify("foo;", config);
9708 assert(filenameChecker.calledOnce);
9712 describe("physicalFilename", () => {
9713 it("should be same as `filename
` passed on options object, if no processors are used", () => {
9714 const physicalFilenameChecker = sinon.spy(context => {
9715 assert.strictEqual(context.getPhysicalFilename(), "foo.js");
9723 checker: physicalFilenameChecker
9728 "test/checker": "error"
9732 linter.verify("foo;", config, { filename: "foo.js" });
9733 assert(physicalFilenameChecker.calledOnce);
9736 it("should default physicalFilename to <input> when options object doesn't have filename", () => {
9737 const physicalFilenameChecker = sinon.spy(context => {
9738 assert.strictEqual(context.getPhysicalFilename(), "<input>");
9746 checker: physicalFilenameChecker
9751 "test/checker": "error"
9755 linter.verify("foo;", config, {});
9756 assert(physicalFilenameChecker.calledOnce);
9759 it("should default physicalFilename to <input> when only two arguments are passed", () => {
9760 const physicalFilenameChecker = sinon.spy(context => {
9761 assert.strictEqual(context.getPhysicalFilename(), "<input>");
9769 checker: physicalFilenameChecker
9774 "test/checker": "error"
9778 linter.verify("foo;", config);
9779 assert(physicalFilenameChecker.calledOnce);
9785 describe("Inline Directives", () => {
9787 describe("/*global*/ Comments", () => {
9789 describe("when evaluating code containing /*global */ and /*globals */ blocks", () => {
9791 it("variables should be available in global scope", () => {
9793 /*global a b:true c:false d:readable e:writeable Math:off */
9796 /* global ConfigGlobal : readable */
9804 checker: context => {
9805 spy = sinon.spy(() => {
9806 const scope = context.getScope();
9807 const a = getVariable(scope, "a"),
9808 b = getVariable(scope, "b"),
9809 c = getVariable(scope, "c"),
9810 d = getVariable(scope, "d"),
9811 e = getVariable(scope, "e"),
9812 f = getVariable(scope, "f"),
9813 mathGlobal = getVariable(scope, "Math"),
9814 arrayGlobal = getVariable(scope, "Array"),
9815 configGlobal = getVariable(scope, "ConfigGlobal");
9817 assert.strictEqual(a.name, "a");
9818 assert.strictEqual(a.writeable, false);
9819 assert.strictEqual(b.name, "b");
9820 assert.strictEqual(b.writeable, true);
9821 assert.strictEqual(c.name, "c");
9822 assert.strictEqual(c.writeable, false);
9823 assert.strictEqual(d.name, "d");
9824 assert.strictEqual(d.writeable, false);
9825 assert.strictEqual(e.name, "e");
9826 assert.strictEqual(e.writeable, true);
9827 assert.strictEqual(f.name, "f");
9828 assert.strictEqual(f.writeable, true);
9829 assert.strictEqual(mathGlobal, null);
9830 assert.strictEqual(arrayGlobal, null);
9831 assert.strictEqual(configGlobal.name, "ConfigGlobal");
9832 assert.strictEqual(configGlobal.writeable, false);
9835 return { Program: spy };
9840 rules: { "test/checker": "error" },
9842 globals: { Array: "off", ConfigGlobal: "writeable" }
9846 linter.verify(code, config);
9847 assert(spy && spy.calledOnce);
9851 describe("when evaluating code containing a /*global */ block with sloppy whitespace", () => {
9852 const code = "/* global a b : true c: false*/";
9854 it("variables should be available in global scope", () => {
9861 checker: context => {
9862 spy = sinon.spy(() => {
9863 const scope = context.getScope(),
9864 a = getVariable(scope, "a"),
9865 b = getVariable(scope, "b"),
9866 c = getVariable(scope, "c");
9868 assert.strictEqual(a.name, "a");
9869 assert.strictEqual(a.writeable, false);
9870 assert.strictEqual(b.name, "b");
9871 assert.strictEqual(b.writeable, true);
9872 assert.strictEqual(c.name, "c");
9873 assert.strictEqual(c.writeable, false);
9876 return { Program: spy };
9881 rules: { "test/checker": "error" }
9884 linter.verify(code, config);
9885 assert(spy && spy.calledOnce);
9889 describe("when evaluating code containing a line comment", () => {
9890 const code = "//global a \n function f() {}";
9892 it("should not introduce a global variable", () => {
9899 checker: context => {
9900 spy = sinon.spy(() => {
9901 const scope = context.getScope();
9903 assert.strictEqual(getVariable(scope, "a"), null);
9906 return { Program: spy };
9911 rules: { "test/checker": "error" }
9915 linter.verify(code, config);
9916 assert(spy && spy.calledOnce);
9920 describe("when evaluating code containing normal block comments", () => {
9921 const code = "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
9923 it("should not introduce a global variable", () => {
9930 checker: context => {
9931 spy = sinon.spy(() => {
9932 const scope = context.getScope();
9934 assert.strictEqual(getVariable(scope, "a"), null);
9935 assert.strictEqual(getVariable(scope, "b"), null);
9936 assert.strictEqual(getVariable(scope, "foo"), null);
9937 assert.strictEqual(getVariable(scope, "c"), null);
9940 return { Program: spy };
9945 rules: { "test/checker": "error" }
9949 linter.verify(code, config);
9950 assert(spy && spy.calledOnce);
9954 it("should attach a \"/*global\" comment node to declared variables", () => {
9955 const code = "/* global foo */\n/* global bar, baz */";
9964 const scope = context.getScope();
9965 const sourceCode = context.getSourceCode();
9966 const comments = sourceCode.getAllComments();
9968 assert.strictEqual(2, comments.length);
9970 const foo = getVariable(scope, "foo");
9972 assert.strictEqual(foo.eslintExplicitGlobal, true);
9973 assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]);
9975 const bar = getVariable(scope, "bar");
9977 assert.strictEqual(bar.eslintExplicitGlobal, true);
9978 assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]);
9980 const baz = getVariable(scope, "baz");
9982 assert.strictEqual(baz.eslintExplicitGlobal, true);
9983 assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]);
9992 rules: { "test/test": "error" }
9996 linter.verify(code, config);
10000 it("should report a linting error when a global is set to an invalid value", () => {
10001 const results = linter.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules: { "no-undef": "error" } });
10003 assert.deepStrictEqual(results, [
10007 message: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
10015 ruleId: "no-undef",
10016 messageId: "undef",
10018 message: "'foo' is not defined.",
10023 nodeType: "Identifier"
10030 describe("/*exported*/ Comments", () => {
10032 it("we should behave nicely when no matching variable is found", () => {
10033 const code = "/* exported horse */";
10034 const config = { rules: {} };
10036 linter.verify(code, config, filename, true);
10039 it("variables should be exported", () => {
10040 const code = "/* exported horse */\n\nvar horse = 'circus'";
10047 checker: context => {
10048 spy = sinon.spy(() => {
10049 const scope = context.getScope(),
10050 horse = getVariable(scope, "horse");
10052 assert.strictEqual(horse.eslintUsed, true);
10055 return { Program: spy };
10061 sourceType: "script"
10063 rules: { "test/checker": "error" }
10066 linter.verify(code, config);
10067 assert(spy && spy.calledOnce);
10070 it("undefined variables should not be exported", () => {
10071 const code = "/* exported horse */\n\nhorse = 'circus'";
10077 checker: context => {
10078 spy = sinon.spy(() => {
10079 const scope = context.getScope(),
10080 horse = getVariable(scope, "horse");
10082 assert.strictEqual(horse, null);
10085 return { Program: spy };
10091 sourceType: "script"
10093 rules: { "test/checker": "error" }
10096 linter.verify(code, config);
10097 assert(spy && spy.calledOnce);
10100 it("variables should be exported in strict mode", () => {
10101 const code = "/* exported horse */\n'use strict';\nvar horse = 'circus'";
10107 checker: context => {
10108 spy = sinon.spy(() => {
10109 const scope = context.getScope(),
10110 horse = getVariable(scope, "horse");
10112 assert.strictEqual(horse.eslintUsed, true);
10115 return { Program: spy };
10121 sourceType: "script"
10123 rules: { "test/checker": "error" }
10126 linter.verify(code, config);
10127 assert(spy && spy.calledOnce);
10130 it("variables should not be exported in the es6 module environment", () => {
10131 const code = "/* exported horse */\nvar horse = 'circus'";
10137 checker: context => {
10138 spy = sinon.spy(() => {
10139 const scope = context.getScope(),
10140 horse = getVariable(scope, "horse");
10142 assert.strictEqual(horse, null); // there is no global scope at all
10145 return { Program: spy };
10152 sourceType: "module"
10154 rules: { "test/checker": "error" }
10157 linter.verify(code, config);
10158 assert(spy && spy.calledOnce);
10161 it("variables should not be exported when in a commonjs file", () => {
10162 const code = "/* exported horse */\nvar horse = 'circus'";
10168 checker: context => {
10169 spy = sinon.spy(() => {
10170 const scope = context.getScope(),
10171 horse = getVariable(scope, "horse");
10173 assert.strictEqual(horse, null); // there is no global scope at all
10176 return { Program: spy };
10182 sourceType: "commonjs"
10184 rules: { "test/checker": "error" }
10187 linter.verify(code, config);
10188 assert(spy && spy.calledOnce);
10192 describe("/*eslint*/ Comments", () => {
10193 describe("when evaluating code with comments to enable rules", () => {
10195 it("should report a violation", () => {
10196 const code = "/*eslint no-alert:1*/ alert('test');";
10197 const config = { rules: {} };
10199 const messages = linter.verify(code, config, filename);
10201 assert.strictEqual(messages.length, 1);
10202 assert.strictEqual(messages[0].ruleId, "no-alert");
10203 assert.strictEqual(messages[0].message, "Unexpected alert.");
10204 assert.include(messages[0].nodeType, "CallExpression");
10207 it("rules should not change initial config", () => {
10210 sourceType: "script"
10212 rules: { strict: 2 }
10214 const codeA = "/*eslint strict: 0*/ function bar() { return 2; }";
10215 const codeB = "function foo() { return 1; }";
10216 let messages = linter.verify(codeA, config, filename, false);
10218 assert.strictEqual(messages.length, 0);
10220 messages = linter.verify(codeB, config, filename, false);
10221 assert.strictEqual(messages.length, 1);
10224 it("rules should not change initial config", () => {
10227 sourceType: "script"
10229 rules: { quotes: [2, "double"] }
10231 const codeA = "/*eslint quotes: 0*/ function bar() { return '2'; }";
10232 const codeB = "function foo() { return '1'; }";
10233 let messages = linter.verify(codeA, config, filename, false);
10235 assert.strictEqual(messages.length, 0);
10237 messages = linter.verify(codeB, config, filename, false);
10238 assert.strictEqual(messages.length, 1);
10241 it("rules should not change initial config", () => {
10242 const config = { rules: { quotes: [2, "double"] } };
10243 const codeA = "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
10244 const codeB = "function foo() { return '1'; }";
10245 let messages = linter.verify(codeA, config, filename, false);
10247 assert.strictEqual(messages.length, 0);
10249 messages = linter.verify(codeB, config, filename, false);
10250 assert.strictEqual(messages.length, 1);
10253 it("rules should not change initial config", () => {
10256 sourceType: "script"
10258 rules: { "no-unused-vars": [2, { vars: "all" }] }
10260 const codeA = "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
10261 const codeB = "var b = 55;";
10262 let messages = linter.verify(codeA, config, filename, false);
10264 assert.strictEqual(messages.length, 0);
10266 messages = linter.verify(codeB, config, filename, false);
10267 assert.strictEqual(messages.length, 1);
10271 describe("when evaluating code with invalid comments to enable rules", () => {
10272 it("should report a violation when the config is not a valid rule configuration", () => {
10273 assert.deepStrictEqual(
10274 linter.verify("/*eslint no-alert:true*/ alert('test');", {}),
10278 ruleId: "no-alert",
10279 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",
10290 it("should report a violation when the config violates a rule's schema", () => {
10291 assert.deepStrictEqual(
10292 linter.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {}),
10296 ruleId: "no-alert",
10297 message: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
10309 describe("when evaluating code with comments to disable rules", () => {
10311 it("should not report a violation", () => {
10312 const config = { rules: { "no-alert": 1 } };
10313 const messages = linter.verify("/*eslint no-alert:0*/ alert('test');", config, filename);
10315 assert.strictEqual(messages.length, 0);
10318 it("should report an error when disabling a non-existent rule in inline comment", () => {
10319 let code = "/*eslint foo:0*/ ;";
10320 let messages = linter.verify(code, {}, filename);
10322 assert.strictEqual(messages.length, 1, "/*eslint*/ comment should report problem.");
10323 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
10325 code = "/*eslint-disable foo*/ ;";
10326 messages = linter.verify(code, {}, filename);
10327 assert.strictEqual(messages.length, 1, "/*eslint-disable*/ comment should report problem.");
10328 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
10330 code = "/*eslint-disable-line foo*/ ;";
10331 messages = linter.verify(code, {}, filename);
10332 assert.strictEqual(messages.length, 1, "/*eslint-disable-line*/ comment should report problem.");
10333 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
10335 code = "/*eslint-disable-next-line foo*/ ;";
10336 messages = linter.verify(code, {}, filename);
10337 assert.strictEqual(messages.length, 1, "/*eslint-disable-next-line*/ comment should report problem.");
10338 assert.strictEqual(messages[0].message, "Definition for rule 'foo' was not found.");
10341 it("should not report an error, when disabling a non-existent rule in config", () => {
10342 const messages = linter.verify("", { rules: { foo: 0 } }, filename);
10344 assert.strictEqual(messages.length, 0);
10347 it("should throw an error when a non-existent rule in config", () => {
10348 assert.throws(() => {
10349 linter.verify("", { rules: { foo: 1 } }, filename);
10350 }, /Key "rules": Key "foo":/u);
10352 assert.throws(() => {
10353 linter.verify("", { rules: { foo: 2 } }, filename);
10354 }, /Key "rules": Key "foo":/u);
10359 describe("when evaluating code with comments to enable multiple rules", () => {
10360 const code = "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
10362 it("should report a violation", () => {
10363 const config = { rules: {} };
10365 const messages = linter.verify(code, config, filename);
10367 assert.strictEqual(messages.length, 2);
10368 assert.strictEqual(messages[0].ruleId, "no-alert");
10369 assert.strictEqual(messages[0].message, "Unexpected alert.");
10370 assert.include(messages[0].nodeType, "CallExpression");
10371 assert.strictEqual(messages[1].ruleId, "no-console");
10375 describe("when evaluating code with comments to enable and disable multiple rules", () => {
10376 const code = "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
10378 it("should report a violation", () => {
10379 const config = { rules: { "no-console": 1, "no-alert": 0 } };
10381 const messages = linter.verify(code, config, filename);
10383 assert.strictEqual(messages.length, 1);
10384 assert.strictEqual(messages[0].ruleId, "no-alert");
10385 assert.strictEqual(messages[0].message, "Unexpected alert.");
10386 assert.include(messages[0].nodeType, "CallExpression");
10390 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
10399 "test-rule"(context) {
10402 if (node.value === "trigger violation") {
10403 context.report(node, "Reporting violation.");
10415 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
10416 const config = { ...baseConfig, rules: {} };
10417 const code = "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
10419 const messages = linter.verify(code, config, filename);
10421 assert.strictEqual(messages.length, 0);
10424 it("should not report a violation when inline comment disables plugin rule", () => {
10425 const code = "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
10426 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 1 } };
10428 const messages = linter.verify(code, config, filename);
10430 assert.strictEqual(messages.length, 0);
10433 it("should report a violation when the report is right before the comment", () => {
10434 const code = " /* eslint-disable */ ";
10440 checker: context => ({
10442 context.report({ loc: { line: 1, column: 0 }, message: "foo" });
10449 "test/checker": "error"
10453 const problems = linter.verify(code, config);
10455 assert.strictEqual(problems.length, 1);
10456 assert.strictEqual(problems[0].message, "foo");
10459 it("should not report a violation when the report is right at the start of the comment", () => {
10460 const code = " /* eslint-disable */ ";
10466 checker: context => ({
10468 context.report({ loc: { line: 1, column: 1 }, message: "foo" });
10475 "test/checker": "error"
10479 const problems = linter.verify(code, config);
10481 assert.strictEqual(problems.length, 0);
10484 it("rules should not change initial config", () => {
10485 const config = { ...baseConfig, rules: { "test-plugin/test-rule": 2 } };
10486 const codeA = "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
10487 const codeB = "var a = \"trigger violation\";";
10488 let messages = linter.verify(codeA, config, filename, false);
10490 assert.strictEqual(messages.length, 0);
10492 messages = linter.verify(codeB, config, filename, false);
10493 assert.strictEqual(messages.length, 1);
10497 describe("when evaluating code with comments to enable and disable all reporting", () => {
10498 it("should report a violation", () => {
10501 "/*eslint-disable */",
10503 "/*eslint-enable */",
10506 const config = { rules: { "no-alert": 1 } };
10508 const messages = linter.verify(code, config, filename);
10510 assert.strictEqual(messages.length, 1);
10511 assert.strictEqual(messages[0].ruleId, "no-alert");
10512 assert.strictEqual(messages[0].message, "Unexpected alert.");
10513 assert.include(messages[0].nodeType, "CallExpression");
10514 assert.strictEqual(messages[0].line, 4);
10517 it("should not report a violation", () => {
10519 "/*eslint-disable */",
10523 const config = { rules: { "no-alert": 1 } };
10525 const messages = linter.verify(code, config, filename);
10527 assert.strictEqual(messages.length, 0);
10530 it("should not report a violation", () => {
10532 " alert('test1');/*eslint-disable */\n",
10534 " alert('test');\n",
10535 "/*eslint-enable */alert('test2');"
10537 const config = { rules: { "no-alert": 1 } };
10539 const messages = linter.verify(code, config, filename);
10541 assert.strictEqual(messages.length, 2);
10542 assert.strictEqual(messages[0].column, 21);
10543 assert.strictEqual(messages[1].column, 19);
10546 it("should report a violation", () => {
10549 "/*eslint-disable */",
10551 "/*eslint-disable */",
10553 "/*eslint-enable*/",
10555 "/*eslint-enable*/"
10558 const config = { rules: { "no-alert": 1 } };
10560 const messages = linter.verify(code, config, filename);
10562 assert.strictEqual(messages.length, 1);
10566 it("should not report a violation", () => {
10568 "/*eslint-disable */",
10569 "(function(){ var b = 44;})()",
10570 "/*eslint-enable */;any();"
10573 const config = { rules: { "no-unused-vars": 1 } };
10575 const messages = linter.verify(code, config, filename);
10577 assert.strictEqual(messages.length, 0);
10580 it("should not report a violation", () => {
10582 "(function(){ /*eslint-disable */ var b = 44;})()",
10583 "/*eslint-enable */;any();"
10586 const config = { rules: { "no-unused-vars": 1 } };
10588 const messages = linter.verify(code, config, filename);
10590 assert.strictEqual(messages.length, 0);
10594 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
10595 const code = "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
10597 it("should report a violation", () => {
10598 const config = { rules: { "no-console": 1, "no-alert": 0 } };
10600 const messages = linter.verify(code, config, filename);
10602 assert.strictEqual(messages.length, 1);
10603 assert.strictEqual(messages[0].ruleId, "no-alert");
10604 assert.strictEqual(messages[0].message, "Unexpected alert.");
10605 assert.include(messages[0].nodeType, "CallExpression");
10609 describe("when evaluating code with comments to enable configurable rule", () => {
10610 const code = "/*eslint quotes:[2, \"double\"]*/ alert('test');";
10612 it("should report a violation", () => {
10613 const config = { rules: { quotes: [2, "single"] } };
10615 const messages = linter.verify(code, config, filename);
10617 assert.strictEqual(messages.length, 1);
10618 assert.strictEqual(messages[0].ruleId, "quotes");
10619 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
10620 assert.include(messages[0].nodeType, "Literal");
10624 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
10625 const code = "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
10627 it("should report a violation", () => {
10628 const config = { rules: { quotes: [2, "single"] } };
10630 const messages = linter.verify(code, config, filename);
10632 assert.strictEqual(messages.length, 1);
10633 assert.strictEqual(messages[0].ruleId, "quotes");
10634 assert.strictEqual(messages[0].message, "Strings must use doublequote.");
10635 assert.include(messages[0].nodeType, "Literal");
10639 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
10640 it("should report a violation", () => {
10641 const code = "/*eslint no-alert:'1'*/ alert('test');";
10643 const config = { rules: { "no-alert": 1 } };
10645 const messages = linter.verify(code, config, filename);
10647 assert.strictEqual(messages.length, 2);
10650 * Incorrectly formatted comment threw error;
10651 * message from caught exception
10652 * may differ amongst UAs, so verifying
10653 * first part only as defined in the
10654 * parseJsonConfig function in lib/eslint.js
10656 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":'1'':/u);
10657 assert.strictEqual(messages[0].line, 1);
10658 assert.strictEqual(messages[0].column, 1);
10660 assert.strictEqual(messages[1].ruleId, "no-alert");
10661 assert.strictEqual(messages[1].message, "Unexpected alert.");
10662 assert.include(messages[1].nodeType, "CallExpression");
10665 it("should report a violation", () => {
10666 const code = "/*eslint no-alert:abc*/ alert('test');";
10668 const config = { rules: { "no-alert": 1 } };
10670 const messages = linter.verify(code, config, filename);
10672 assert.strictEqual(messages.length, 2);
10675 * Incorrectly formatted comment threw error;
10676 * message from caught exception
10677 * may differ amongst UAs, so verifying
10678 * first part only as defined in the
10679 * parseJsonConfig function in lib/eslint.js
10681 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":abc':/u);
10682 assert.strictEqual(messages[0].line, 1);
10683 assert.strictEqual(messages[0].column, 1);
10685 assert.strictEqual(messages[1].ruleId, "no-alert");
10686 assert.strictEqual(messages[1].message, "Unexpected alert.");
10687 assert.include(messages[1].nodeType, "CallExpression");
10690 it("should report a violation", () => {
10691 const code = "/*eslint no-alert:0 2*/ alert('test');";
10693 const config = { rules: { "no-alert": 1 } };
10695 const messages = linter.verify(code, config, filename);
10697 assert.strictEqual(messages.length, 2);
10700 * Incorrectly formatted comment threw error;
10701 * message from caught exception
10702 * may differ amongst UAs, so verifying
10703 * first part only as defined in the
10704 * parseJsonConfig function in lib/eslint.js
10706 assert.match(messages[0].message, /^Failed to parse JSON from ' "no-alert":0 2':/u);
10707 assert.strictEqual(messages[0].line, 1);
10708 assert.strictEqual(messages[0].column, 1);
10710 assert.strictEqual(messages[1].ruleId, "no-alert");
10711 assert.strictEqual(messages[1].message, "Unexpected alert.");
10712 assert.include(messages[1].nodeType, "CallExpression");
10716 describe("when evaluating code with comments which have colon in its value", () => {
10717 const code = String.raw`
10718 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
10722 it("should not parse errors, should report a violation", () => {
10723 const messages = linter.verify(code, {}, filename);
10725 assert.strictEqual(messages.length, 1);
10726 assert.strictEqual(messages[0].ruleId, "max-len");
10727 assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100.");
10728 assert.include(messages[0].nodeType, "Program");
10732 describe("when evaluating code with comments that contain escape sequences", () => {
10733 const code = String.raw`
10734 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
10735 console
.log("test");
10736 consolexlog("test2");
10740 it("should validate correctly", () => {
10741 const config = { rules: {} };
10742 const messages = linter.verify(code, config, filename);
10743 const [message1, message2] = messages;
10745 assert.strictEqual(messages.length, 2);
10746 assert.strictEqual(message1.ruleId, "max-len");
10747 assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1.");
10748 assert.strictEqual(message1.line, 4);
10749 assert.strictEqual(message1.column, 1);
10750 assert.include(message1.nodeType, "Program");
10751 assert.strictEqual(message2.ruleId, "max-len");
10752 assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1.");
10753 assert.strictEqual(message2.line, 5);
10754 assert.strictEqual(message2.column, 1);
10755 assert.include(message2.nodeType, "Program");
10761 describe("/*eslint-disable*/ and /*eslint-enable*/", () => {
10762 it("should report a violation", () => {
10764 "/*eslint-disable no-alert */",
10766 "console.log('test');" // here
10768 const config = { rules: { "no-alert": 1, "no-console": 1 } };
10770 const messages = linter.verify(code, config, filename);
10772 assert.strictEqual(messages.length, 1);
10774 assert.strictEqual(messages[0].ruleId, "no-console");
10777 it("should report no violation", () => {
10779 "/* eslint-disable quotes */",
10780 "console.log(\"foo\");",
10781 "/* eslint-enable quotes */"
10783 const config = { rules: { quotes: 2 } };
10785 const messages = linter.verify(code, config, filename);
10787 assert.strictEqual(messages.length, 0);
10790 it("should report a violation", () => {
10792 "/*eslint-disable no-alert, no-console */",
10794 "console.log('test');",
10795 "/*eslint-enable*/",
10797 "alert('test');", // here
10798 "console.log('test');" // here
10800 const config = { rules: { "no-alert": 1, "no-console": 1 } };
10802 const messages = linter.verify(code, config, filename);
10804 assert.strictEqual(messages.length, 2);
10806 assert.strictEqual(messages[0].ruleId, "no-alert");
10807 assert.strictEqual(messages[0].line, 5);
10808 assert.strictEqual(messages[1].ruleId, "no-console");
10809 assert.strictEqual(messages[1].line, 6);
10812 it("should report a violation", () => {
10814 "/*eslint-disable no-alert */",
10816 "console.log('test');",
10817 "/*eslint-enable no-console */",
10819 "alert('test');" // here
10821 const config = { rules: { "no-alert": 1, "no-console": 1 } };
10823 const messages = linter.verify(code, config, filename);
10825 assert.strictEqual(messages.length, 1);
10827 assert.strictEqual(messages[0].ruleId, "no-console");
10831 it("should report a violation", () => {
10833 "/*eslint-disable no-alert, no-console */",
10835 "console.log('test');",
10836 "/*eslint-enable no-alert*/",
10838 "alert('test');", // here
10839 "console.log('test');"
10841 const config = { rules: { "no-alert": 1, "no-console": 1 } };
10843 const messages = linter.verify(code, config, filename);
10845 assert.strictEqual(messages.length, 1);
10847 assert.strictEqual(messages[0].ruleId, "no-alert");
10848 assert.strictEqual(messages[0].line, 5);
10852 it("should report a violation", () => {
10854 "/*eslint-disable no-alert */",
10856 "/*eslint-disable no-console */",
10858 "console.log('test');",
10859 "/*eslint-enable */",
10861 "alert('test');", // here
10862 "console.log('test');", // here
10864 "/*eslint-enable */",
10866 "alert('test');", // here
10867 "console.log('test');", // here
10869 "/*eslint-enable*/"
10871 const config = { rules: { "no-alert": 1, "no-console": 1 } };
10873 const messages = linter.verify(code, config, filename);
10875 assert.strictEqual(messages.length, 4);
10877 assert.strictEqual(messages[0].ruleId, "no-alert");
10878 assert.strictEqual(messages[0].line, 6);
10880 assert.strictEqual(messages[1].ruleId, "no-console");
10881 assert.strictEqual(messages[1].line, 7);
10883 assert.strictEqual(messages[2].ruleId, "no-alert");
10884 assert.strictEqual(messages[2].line, 9);
10886 assert.strictEqual(messages[3].ruleId, "no-console");
10887 assert.strictEqual(messages[3].line, 10);
10891 it("should report a violation", () => {
10893 "/*eslint-disable no-alert, no-console */",
10895 "console.log('test');",
10897 "/*eslint-enable no-alert */",
10899 "alert('test');", // here
10900 "console.log('test');",
10902 "/*eslint-enable no-console */",
10904 "alert('test');", // here
10905 "console.log('test');", // here
10906 "/*eslint-enable no-console */"
10908 const config = { rules: { "no-alert": 1, "no-console": 1 } };
10910 const messages = linter.verify(code, config, filename);
10912 assert.strictEqual(messages.length, 3);
10914 assert.strictEqual(messages[0].ruleId, "no-alert");
10915 assert.strictEqual(messages[0].line, 5);
10917 assert.strictEqual(messages[1].ruleId, "no-alert");
10918 assert.strictEqual(messages[1].line, 8);
10920 assert.strictEqual(messages[2].ruleId, "no-console");
10921 assert.strictEqual(messages[2].line, 9);
10925 it("should report a violation when severity is warn", () => {
10927 "/*eslint-disable no-alert, no-console */",
10929 "console.log('test');",
10931 "/*eslint-enable no-alert */",
10933 "alert('test');", // here
10934 "console.log('test');",
10936 "/*eslint-enable no-console */",
10938 "alert('test');", // here
10939 "console.log('test');", // here
10940 "/*eslint-enable no-console */"
10942 const config = { rules: { "no-alert": "warn", "no-console": "warn" } };
10944 const messages = linter.verify(code, config, filename);
10946 assert.strictEqual(messages.length, 3);
10948 assert.strictEqual(messages[0].ruleId, "no-alert");
10949 assert.strictEqual(messages[0].line, 5);
10951 assert.strictEqual(messages[1].ruleId, "no-alert");
10952 assert.strictEqual(messages[1].line, 8);
10954 assert.strictEqual(messages[2].ruleId, "no-console");
10955 assert.strictEqual(messages[2].line, 9);
10959 it("should report no violation", () => {
10961 "/*eslint-disable no-unused-vars */",
10962 "var foo; // eslint-disable-line no-unused-vars",
10964 "/* eslint-enable no-unused-vars */" // here
10966 const config = { rules: { "no-unused-vars": 2 } };
10968 const messages = linter.verify(code, config, filename);
10970 assert.strictEqual(messages.length, 0);
10975 describe("/*eslint-disable-line*/", () => {
10977 it("should report a violation", () => {
10979 "alert('test'); // eslint-disable-line no-alert",
10980 "console.log('test');" // here
10989 const messages = linter.verify(code, config, filename);
10991 assert.strictEqual(messages.length, 1);
10993 assert.strictEqual(messages[0].ruleId, "no-console");
10996 it("should report a violation", () => {
10998 "alert('test'); // eslint-disable-line no-alert",
10999 "console.log('test'); // eslint-disable-line no-console",
11000 "alert('test');" // here
11009 const messages = linter.verify(code, config, filename);
11011 assert.strictEqual(messages.length, 1);
11013 assert.strictEqual(messages[0].ruleId, "no-alert");
11016 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
11018 "/* eslint-disable-line",
11020 "*/ console.log('test');" // here
11028 const messages = linter.verify(code, config, filename);
11030 assert.strictEqual(messages.length, 2);
11032 assert.strictEqual(messages[1].ruleId, "no-console");
11035 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
11037 "alert('test'); /* eslint-disable-line ",
11046 const messages = linter.verify(code, config);
11048 assert.deepStrictEqual(messages, [
11050 ruleId: "no-alert",
11056 message: "Unexpected alert.",
11057 messageId: "unexpected",
11058 nodeType: "CallExpression"
11063 message: "eslint-disable-line comment should not span multiple lines.",
11073 it("should not report a violation for eslint-disable-line in block comment", () => {
11075 "alert('test'); // eslint-disable-line no-alert",
11076 "alert('test'); /*eslint-disable-line no-alert*/"
11084 const messages = linter.verify(code, config, filename);
11086 assert.strictEqual(messages.length, 0);
11089 it("should not report a violation", () => {
11091 "alert('test'); // eslint-disable-line no-alert",
11092 "console.log('test'); // eslint-disable-line no-console"
11101 const messages = linter.verify(code, config, filename);
11103 assert.strictEqual(messages.length, 0);
11106 it("should not report a violation", () => {
11108 "alert('test') // eslint-disable-line no-alert, quotes, semi",
11109 "console.log('test'); // eslint-disable-line"
11114 quotes: [1, "double"],
11115 semi: [1, "always"],
11120 const messages = linter.verify(code, config, filename);
11122 assert.strictEqual(messages.length, 0);
11125 it("should not report a violation", () => {
11127 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
11128 "console.log('test'); /* eslint-disable-line */"
11133 quotes: [1, "double"],
11134 semi: [1, "always"],
11139 const messages = linter.verify(code, config, filename);
11141 assert.strictEqual(messages.length, 0);
11144 it("should ignore violations of multiple rules when specified in mixed comments", () => {
11146 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
11151 quotes: [1, "single"]
11154 const messages = linter.verify(code, config, filename);
11156 assert.strictEqual(messages.length, 0);
11159 it("should report no violation", () => {
11161 "var foo1; // eslint-disable-line no-unused-vars",
11162 "var foo2; // eslint-disable-line no-unused-vars",
11163 "var foo3; // eslint-disable-line no-unused-vars",
11164 "var foo4; // eslint-disable-line no-unused-vars",
11165 "var foo5; // eslint-disable-line no-unused-vars"
11167 const config = { rules: { "no-unused-vars": 2 } };
11169 const messages = linter.verify(code, config, filename);
11171 assert.strictEqual(messages.length, 0);
11176 describe("/*eslint-disable-next-line*/", () => {
11177 it("should ignore violation of specified rule on next line", () => {
11179 "// eslint-disable-next-line no-alert",
11181 "console.log('test');"
11189 const messages = linter.verify(code, config, filename);
11191 assert.strictEqual(messages.length, 1);
11192 assert.strictEqual(messages[0].ruleId, "no-console");
11195 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
11197 "/* eslint-disable-next-line no-alert */",
11199 "console.log('test');"
11207 const messages = linter.verify(code, config, filename);
11209 assert.strictEqual(messages.length, 1);
11210 assert.strictEqual(messages[0].ruleId, "no-console");
11212 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
11214 "/* eslint-disable-next-line no-alert */",
11222 const messages = linter.verify(code, config, filename);
11224 assert.strictEqual(messages.length, 0);
11227 it("should not ignore violation if block comment is not on a single line", () => {
11229 "/* eslint-disable-next-line",
11230 "no-alert */alert('test');"
11237 const messages = linter.verify(code, config, filename);
11239 assert.strictEqual(messages.length, 2);
11240 assert.strictEqual(messages[1].ruleId, "no-alert");
11243 it("should ignore violations only of specified rule", () => {
11245 "// eslint-disable-next-line no-console",
11247 "console.log('test');"
11255 const messages = linter.verify(code, config, filename);
11257 assert.strictEqual(messages.length, 2);
11258 assert.strictEqual(messages[0].ruleId, "no-alert");
11259 assert.strictEqual(messages[1].ruleId, "no-console");
11262 it("should ignore violations of multiple rules when specified", () => {
11264 "// eslint-disable-next-line no-alert, quotes",
11265 "alert(\"test\");",
11266 "console.log('test');"
11271 quotes: [1, "single"],
11275 const messages = linter.verify(code, config, filename);
11277 assert.strictEqual(messages.length, 1);
11278 assert.strictEqual(messages[0].ruleId, "no-console");
11281 it("should ignore violations of multiple rules when specified in mixed comments", () => {
11283 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
11289 quotes: [1, "single"]
11292 const messages = linter.verify(code, config, filename);
11294 assert.strictEqual(messages.length, 0);
11297 it("should ignore violations of only the specified rule on next line", () => {
11299 "// eslint-disable-next-line quotes",
11300 "alert(\"test\");",
11301 "console.log('test');"
11306 quotes: [1, "single"],
11310 const messages = linter.verify(code, config, filename);
11312 assert.strictEqual(messages.length, 2);
11313 assert.strictEqual(messages[0].ruleId, "no-alert");
11314 assert.strictEqual(messages[1].ruleId, "no-console");
11317 it("should ignore violations of specified rule on next line only", () => {
11320 "// eslint-disable-next-line no-alert",
11322 "console.log('test');"
11330 const messages = linter.verify(code, config, filename);
11332 assert.strictEqual(messages.length, 2);
11333 assert.strictEqual(messages[0].ruleId, "no-alert");
11334 assert.strictEqual(messages[1].ruleId, "no-console");
11337 it("should ignore all rule violations on next line if none specified", () => {
11339 "// eslint-disable-next-line",
11340 "alert(\"test\");",
11341 "console.log('test')"
11345 semi: [1, "never"],
11346 quotes: [1, "single"],
11351 const messages = linter.verify(code, config, filename);
11353 assert.strictEqual(messages.length, 1);
11354 assert.strictEqual(messages[0].ruleId, "no-console");
11357 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
11360 "/* eslint-disable-next-line no-alert */",
11362 "console.log('test');"
11370 const messages = linter.verify(code, config, filename);
11372 assert.strictEqual(messages.length, 2);
11373 assert.strictEqual(messages[0].ruleId, "no-alert");
11374 assert.strictEqual(messages[1].ruleId, "no-console");
11377 it("should report a violation", () => {
11379 "/* eslint-disable-next-line",
11382 "console.log('test');" // here
11391 const messages = linter.verify(code, config, filename);
11393 assert.strictEqual(messages.length, 2);
11395 assert.strictEqual(messages[1].ruleId, "no-console");
11398 it("should not ignore violations if comment is of the type hashbang", () => {
11400 "#! eslint-disable-next-line no-alert",
11402 "console.log('test');"
11410 const messages = linter.verify(code, config, filename);
11412 assert.strictEqual(messages.length, 2);
11413 assert.strictEqual(messages[0].ruleId, "no-alert");
11414 assert.strictEqual(messages[1].ruleId, "no-console");
11418 describe("descriptions in directive comments", () => {
11419 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
11420 const aaa = sinon.stub().returns({});
11421 const bbb = sinon.stub().returns({});
11426 aaa: { create: aaa },
11427 bbb: { create: bbb }
11433 const messages = linter.verify(`
11434 /*eslint test/aaa:error -- test/bbb:error */
11435 console
.log("hello")
11438 // Don't include syntax error of the comment.
11439 assert.deepStrictEqual(messages, []);
11442 assert.strictEqual(aaa.callCount, 1);
11443 assert.strictEqual(bbb.callCount, 0);
11446 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
11447 const messages = linter.verify(`
11448 /*globals aaa -- bbb */
11453 sourceType: "script"
11455 rules: { "no-redeclare": "error" }
11458 // Don't include `bbb
`
11459 assert.deepStrictEqual(
11466 message: "'aaa' is already defined by a variable declaration.",
11467 messageId: "redeclaredBySyntax",
11469 ruleId: "no-redeclare",
11475 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
11476 const messages = linter.verify(`
11477 /*exported aaa -- bbb */
11482 sourceType: "script"
11484 rules: { "no-unused-vars": "error" }
11487 // Don't include `aaa
`
11488 assert.deepStrictEqual(
11495 message: "'bbb' is assigned a value but never used.",
11496 messageId: "unusedVar",
11497 nodeType: "Identifier",
11498 ruleId: "no-unused-vars",
11504 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
11505 const messages = linter.verify(`
11506 /*eslint-disable no-redeclare -- no-unused-vars */
11509 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
11511 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
11512 assert.deepStrictEqual(
11519 message: "'aaa' is assigned a value but never used.",
11520 messageId: "unusedVar",
11521 nodeType: "Identifier",
11522 ruleId: "no-unused-vars",
11528 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
11529 const messages = linter.verify(`
11530 /*eslint-disable no-redeclare, no-unused-vars */
11531 /*eslint-enable no-redeclare -- no-unused-vars */
11534 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
11536 // Do include `no
-redeclare
` but not `no
-unused
-vars
`
11537 assert.deepStrictEqual(
11544 message: "'aaa' is already defined.",
11545 messageId: "redeclared",
11546 nodeType: "Identifier",
11547 ruleId: "no-redeclare",
11553 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
11554 const messages = linter.verify(`
11555 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
11556 var aaa
= {} //eslint-disable-line no-redeclare -- no-unused-vars
11557 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
11559 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
11560 assert.deepStrictEqual(
11567 message: "'aaa' is assigned a value but never used.",
11568 messageId: "unusedVar",
11569 nodeType: "Identifier",
11570 ruleId: "no-unused-vars",
11576 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
11577 const messages = linter.verify(`
11578 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
11579 var aaa
= {} /*eslint-disable-line no-redeclare -- no-unused-vars */
11580 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
11582 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
11583 assert.deepStrictEqual(
11590 message: "'aaa' is assigned a value but never used.",
11591 messageId: "unusedVar",
11592 nodeType: "Identifier",
11593 ruleId: "no-unused-vars",
11599 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
11600 const messages = linter.verify(`
11601 //eslint-disable-next-line no-redeclare -- no-unused-vars
11603 //eslint-disable-next-line no-redeclare -- no-unused-vars
11605 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
11607 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
11608 assert.deepStrictEqual(
11615 message: "'aaa' is assigned a value but never used.",
11616 messageId: "unusedVar",
11617 nodeType: "Identifier",
11618 ruleId: "no-unused-vars",
11624 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
11625 const messages = linter.verify(`
11626 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
11628 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
11630 `, { rules: { "no-redeclare": "error", "no-unused-vars": "error" } });
11632 // Do include `no
-unused
-vars
` but not `no
-redeclare
`
11633 assert.deepStrictEqual(
11640 message: "'aaa' is assigned a value but never used.",
11641 messageId: "unusedVar",
11642 nodeType: "Identifier",
11643 ruleId: "no-unused-vars",
11649 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
11650 const rule = sinon.stub().returns({});
11655 "a--rule": { create: rule }
11661 const messages = linter.verify(`
11662 /*eslint test/a--rule:error */
11663 console.log("hello")
11666 // Don't include syntax error of the comment.
11667 assert.deepStrictEqual(messages, []);
11670 assert.strictEqual(rule.callCount, 1);
11673 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
11674 const aaa = sinon.stub().returns({});
11675 const bbb = sinon.stub().returns({});
11680 aaa: { create: aaa },
11681 bbb: { create: bbb }
11687 const messages = linter.verify(`
11688 /*eslint test/aaa:error -------- test/bbb:error */
11689 console
.log("hello")
11692 // Don't include syntax error of the comment.
11693 assert.deepStrictEqual(messages, []);
11696 assert.strictEqual(aaa.callCount, 1);
11697 assert.strictEqual(bbb.callCount, 0);
11700 it("should ignore the part preceded by '--' with line breaks.", () => {
11701 const aaa = sinon.stub().returns({});
11702 const bbb = sinon.stub().returns({});
11707 aaa: { create: aaa },
11708 bbb: { create: bbb }
11714 const messages = linter.verify(`
11715 /*eslint test/aaa:error
11718 console.log("hello")
11721 // Don't include syntax error of the comment.
11722 assert.deepStrictEqual(messages, []);
11725 assert.strictEqual(aaa.callCount, 1);
11726 assert.strictEqual(bbb.callCount, 0);
11730 describe("allowInlineConfig option", () => {
11731 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
11732 it("should report a violation for disabling rules", () => {
11734 "alert('test'); // eslint-disable-line no-alert"
11742 const messages = linter.verify(code, config, {
11744 allowInlineConfig: false
11747 assert.strictEqual(messages.length, 1);
11748 assert.strictEqual(messages[0].ruleId, "no-alert");
11751 it("should report a violation for global variable declarations", () => {
11763 const scope = context.getScope();
11764 const sourceCode = context.getSourceCode();
11765 const comments = sourceCode.getAllComments();
11767 assert.strictEqual(1, comments.length);
11769 const foo = getVariable(scope, "foo
");
11785 linter.verify(code, config, { allowInlineConfig: false });
11789 it("should report a violation
for eslint
-disable
", () => {
11791 "/* eslint-disable */",
11800 const messages = linter.verify(code, config, {
11802 allowInlineConfig: false
11805 assert.strictEqual(messages.length, 1);
11806 assert.strictEqual(messages[0].ruleId, "no
-alert
");
11809 it("should not report a violation
for rule changes
", () => {
11811 "/*eslint no-alert:2*/",
11820 const messages = linter.verify(code, config, {
11822 allowInlineConfig: false
11825 assert.strictEqual(messages.length, 0);
11828 it("should report a violation
for disable
-line
", () => {
11830 "alert('test'); // eslint-disable-line"
11838 const messages
= linter
.verify(code
, config
, {
11840 allowInlineConfig
: false
11843 assert
.strictEqual(messages
.length
, 1);
11844 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
11849 describe("when evaluating code with 'noInlineConfig'", () => {
11850 for (const directive
of [
11854 "eslint eqeqeq: error",
11855 "eslint-disable eqeqeq",
11856 "eslint-disable-line eqeqeq",
11857 "eslint-disable-next-line eqeqeq",
11858 "eslint-enable eqeqeq"
11860 // eslint-disable-next-line no-loop-func -- No closures
11861 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
11862 const messages
= linter
.verify(`/* ${directive} */`, {
11864 noInlineConfig
: true
11868 assert
.deepStrictEqual(messages
.length
, 1);
11869 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
11870 assert
.deepStrictEqual(messages
[0].ruleId
, null);
11871 assert
.deepStrictEqual(messages
[0].severity
, 1);
11872 assert
.deepStrictEqual(messages
[0].message
, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
11876 for (const directive
of [
11877 "eslint-disable-line eqeqeq",
11878 "eslint-disable-next-line eqeqeq"
11880 // eslint-disable-next-line no-loop-func -- No closures
11881 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
11882 const messages
= linter
.verify(`// ${directive}`, {
11884 noInlineConfig
: true
11888 assert
.deepStrictEqual(messages
.length
, 1);
11889 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
11890 assert
.deepStrictEqual(messages
[0].ruleId
, null);
11891 assert
.deepStrictEqual(messages
[0].severity
, 1);
11892 assert
.deepStrictEqual(messages
[0].message
, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
11896 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
11897 const messages
= linter
.verify("/* globals foo */", {
11899 noInlineConfig
: true
11901 }, { allowInlineConfig
: false });
11903 assert
.deepStrictEqual(messages
.length
, 0);
11907 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
11908 it("should not report a violation", () => {
11910 "alert('test'); // eslint-disable-line no-alert"
11918 const messages
= linter
.verify(code
, config
, {
11920 allowInlineConfig
: true
11923 assert
.strictEqual(messages
.length
, 0);
11929 describe("reportUnusedDisableDirectives option", () => {
11930 it("reports problems for unused eslint-disable comments", () => {
11931 assert
.deepStrictEqual(
11932 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: true }),
11936 message
: "Unused eslint-disable directive (no problems were reported).",
11950 it("reports problems for unused eslint-disable comments (error)", () => {
11951 assert
.deepStrictEqual(
11952 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "error" }),
11956 message
: "Unused eslint-disable directive (no problems were reported).",
11970 it("reports problems for unused eslint-disable comments (warn)", () => {
11971 assert
.deepStrictEqual(
11972 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "warn" }),
11976 message
: "Unused eslint-disable directive (no problems were reported).",
11990 it("reports problems for unused eslint-disable comments (in config)", () => {
11991 assert
.deepStrictEqual(
11992 linter
.verify("/* eslint-disable */", {
11994 reportUnusedDisableDirectives
: true
12000 message
: "Unused eslint-disable directive (no problems were reported).",
12014 it("reports problems for partially unused eslint-disable comments (in config)", () => {
12015 const code
= "alert('test'); // eslint-disable-line no-alert, no-redeclare";
12018 reportUnusedDisableDirectives
: true
12026 const messages
= linter
.verify(code
, config
, {
12028 allowInlineConfig
: true
12031 assert
.deepStrictEqual(
12036 message
: "Unused eslint-disable directive (no problems were reported from 'no-redeclare').",
12050 describe("autofix", () => {
12051 const alwaysReportsRule
= {
12055 context
.report({ message
: "bad code", loc
: node
.loc
.end
});
12061 const neverReportsRule
= {
12067 const ruleCount
= 3;
12068 const usedRules
= Array
.from(
12069 { length
: ruleCount
},
12070 (_
, index
) => `used${index ? `-${index}` : ""}` // "used", "used-1", "used-2"
12072 const unusedRules = usedRules.map(name => `un${name}
`); // "unused", "unused-1", "unused-2"
12081 reportUnusedDisableDirectives: true
12084 ...Object.fromEntries(usedRules.map(name => [`test
/${name}
`, "error"])),
12085 ...Object.fromEntries(unusedRules.map(name => [`test
/${name}
`, "error"]))
12090 config.plugins.test.rules = {
12091 ...Object.fromEntries(usedRules.map(name => [name, alwaysReportsRule])),
12092 ...Object.fromEntries(unusedRules.map(name => [name, neverReportsRule]))
12098 //-----------------------------------------------
12099 // Removing the entire comment
12100 //-----------------------------------------------
12103 code: "// eslint-disable-line test/unused",
12107 code: "foo// eslint-disable-line test/unused",
12111 code: "// eslint-disable-line ,test/unused,",
12115 code: "// eslint-disable-line test/unused-1, test/unused-2",
12119 code: "// eslint-disable-line ,test/unused-1,, test/unused-2,, -- comment",
12123 code: "// eslint-disable-next-line test/unused\n",
12127 code: "// eslint-disable-next-line test/unused\nfoo",
12131 code: "/* eslint-disable \ntest/unused\n*/",
12135 //-----------------------------------------------
12136 // Removing only individual rules
12137 //-----------------------------------------------
12139 // content before the first rule should not be changed
12141 code: "//eslint-disable-line test/unused, test/used",
12142 output: "//eslint-disable-line test/used"
12145 code: "// eslint-disable-line test/unused, test/used",
12146 output: "// eslint-disable-line test/used"
12149 code: "// eslint-disable-line test/unused, test/used",
12150 output: "// eslint-disable-line test/used"
12153 code: "/*\neslint-disable test/unused, test/used*/",
12154 output: "/*\neslint-disable test/used*/"
12157 code: "/*\n eslint-disable test/unused, test/used*/",
12158 output: "/*\n eslint-disable test/used*/"
12161 code: "/*\r\neslint-disable test/unused, test/used*/",
12162 output: "/*\r\neslint-disable test/used*/"
12165 code: "/*\u2028eslint-disable test/unused, test/used*/",
12166 output: "/*\u2028eslint-disable test/used*/"
12169 code: "/*\u00A0eslint-disable test/unused, test/used*/",
12170 output: "/*\u00A0eslint-disable test/used*/"
12173 code: "// eslint-disable-line test/unused, test/used",
12174 output: "// eslint-disable-line test/used"
12177 code: "/* eslint-disable\ntest/unused, test/used*/",
12178 output: "/* eslint-disable\ntest/used*/"
12181 code: "/* eslint-disable\n test/unused, test/used*/",
12182 output: "/* eslint-disable\n test/used*/"
12185 code: "/* eslint-disable\r\ntest/unused, test/used*/",
12186 output: "/* eslint-disable\r\ntest/used*/"
12189 code: "/* eslint-disable\u2028test/unused, test/used*/",
12190 output: "/* eslint-disable\u2028test/used*/"
12193 code: "/* eslint-disable\u00A0test/unused, test/used*/",
12194 output: "/* eslint-disable\u00A0test/used*/"
12197 // when removing the first rule, the comma and all whitespace up to the next rule (or next lone comma) should also be removed
12199 code: "// eslint-disable-line test/unused,test/used",
12200 output: "// eslint-disable-line test/used"
12203 code: "// eslint-disable-line test/unused, test/used",
12204 output: "// eslint-disable-line test/used"
12207 code: "// eslint-disable-line test/unused , test/used",
12208 output: "// eslint-disable-line test/used"
12211 code: "// eslint-disable-line test/unused, test/used",
12212 output: "// eslint-disable-line test/used"
12215 code: "// eslint-disable-line test/unused ,test/used",
12216 output: "// eslint-disable-line test/used"
12219 code: "/* eslint-disable test/unused\n,\ntest/used */",
12220 output: "/* eslint-disable test/used */"
12223 code: "/* eslint-disable test/unused \n \n,\n\n test/used */",
12224 output: "/* eslint-disable test/used */"
12227 code: "/* eslint-disable test/unused\u2028,\u2028test/used */",
12228 output: "/* eslint-disable test/used */"
12231 code: "// eslint-disable-line test/unused\u00A0,\u00A0test/used",
12232 output: "// eslint-disable-line test/used"
12235 code: "// eslint-disable-line test/unused,,test/used",
12236 output: "// eslint-disable-line ,test/used"
12239 code: "// eslint-disable-line test/unused, ,test/used",
12240 output: "// eslint-disable-line ,test/used"
12243 code: "// eslint-disable-line test/unused,, test/used",
12244 output: "// eslint-disable-line , test/used"
12247 code: "// eslint-disable-line test/unused,test/used ",
12248 output: "// eslint-disable-line test/used "
12251 code: "// eslint-disable-next-line test/unused,test/used\n",
12252 output: "// eslint-disable-next-line test/used\n"
12255 // when removing a rule in the middle, one comma and all whitespace between commas should also be removed
12257 code: "// eslint-disable-line test/used-1,test/unused,test/used-2",
12258 output: "// eslint-disable-line test/used-1,test/used-2"
12261 code: "// eslint-disable-line test/used-1, test/unused,test/used-2",
12262 output: "// eslint-disable-line test/used-1,test/used-2"
12265 code: "// eslint-disable-line test/used-1,test/unused ,test/used-2",
12266 output: "// eslint-disable-line test/used-1,test/used-2"
12269 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
12270 output: "// eslint-disable-line test/used-1,test/used-2"
12273 code: "/* eslint-disable test/used-1,\ntest/unused\n,test/used-2 */",
12274 output: "/* eslint-disable test/used-1,test/used-2 */"
12277 code: "/* eslint-disable test/used-1,\n\n test/unused \n \n ,test/used-2 */",
12278 output: "/* eslint-disable test/used-1,test/used-2 */"
12281 code: "/* eslint-disable test/used-1,\u2028test/unused\u2028,test/used-2 */",
12282 output: "/* eslint-disable test/used-1,test/used-2 */"
12285 code: "// eslint-disable-line test/used-1,\u00A0test/unused\u00A0,test/used-2",
12286 output: "// eslint-disable-line test/used-1,test/used-2"
12289 // when removing a rule in the middle, content around commas should not be changed
12291 code: "// eslint-disable-line test/used-1, test/unused ,test/used-2",
12292 output: "// eslint-disable-line test/used-1,test/used-2"
12295 code: "// eslint-disable-line test/used-1,test/unused, test/used-2",
12296 output: "// eslint-disable-line test/used-1, test/used-2"
12299 code: "// eslint-disable-line test/used-1 ,test/unused,test/used-2",
12300 output: "// eslint-disable-line test/used-1 ,test/used-2"
12303 code: "// eslint-disable-line test/used-1 ,test/unused, test/used-2",
12304 output: "// eslint-disable-line test/used-1 , test/used-2"
12307 code: "// eslint-disable-line test/used-1 , test/unused , test/used-2",
12308 output: "// eslint-disable-line test/used-1 , test/used-2"
12311 code: "/* eslint-disable test/used-1\n,test/unused,\ntest/used-2 */",
12312 output: "/* eslint-disable test/used-1\n,\ntest/used-2 */"
12315 code: "/* eslint-disable test/used-1\u2028,test/unused,\u2028test/used-2 */",
12316 output: "/* eslint-disable test/used-1\u2028,\u2028test/used-2 */"
12319 code: "// eslint-disable-line test/used-1\u00A0,test/unused,\u00A0test/used-2",
12320 output: "// eslint-disable-line test/used-1\u00A0,\u00A0test/used-2"
12323 code: "// eslint-disable-line , test/unused ,test/used",
12324 output: "// eslint-disable-line ,test/used"
12327 code: "/* eslint-disable\n, test/unused ,test/used */",
12328 output: "/* eslint-disable\n,test/used */"
12331 code: "/* eslint-disable test/used-1,\n,test/unused,test/used-2 */",
12332 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
12335 code: "/* eslint-disable test/used-1,test/unused,\n,test/used-2 */",
12336 output: "/* eslint-disable test/used-1,\n,test/used-2 */"
12339 code: "/* eslint-disable test/used-1,\n,test/unused,\n,test/used-2 */",
12340 output: "/* eslint-disable test/used-1,\n,\n,test/used-2 */"
12343 code: "// eslint-disable-line test/used, test/unused,",
12344 output: "// eslint-disable-line test/used,"
12347 code: "// eslint-disable-next-line test/used, test/unused,\n",
12348 output: "// eslint-disable-next-line test/used,\n"
12351 code: "// eslint-disable-line test/used, test/unused, ",
12352 output: "// eslint-disable-line test/used, "
12355 code: "// eslint-disable-line test/used, test/unused, -- comment",
12356 output: "// eslint-disable-line test/used, -- comment"
12359 code: "/* eslint-disable test/used, test/unused,\n*/",
12360 output: "/* eslint-disable test/used,\n*/"
12363 // when removing the last rule, the comma and all whitespace up to the previous rule (or previous lone comma) should also be removed
12365 code: "// eslint-disable-line test/used,test/unused",
12366 output: "// eslint-disable-line test/used"
12369 code: "// eslint-disable-line test/used, test/unused",
12370 output: "// eslint-disable-line test/used"
12373 code: "// eslint-disable-line test/used ,test/unused",
12374 output: "// eslint-disable-line test/used"
12377 code: "// eslint-disable-line test/used , test/unused",
12378 output: "// eslint-disable-line test/used"
12381 code: "// eslint-disable-line test/used, test/unused",
12382 output: "// eslint-disable-line test/used"
12385 code: "// eslint-disable-line test/used ,test/unused",
12386 output: "// eslint-disable-line test/used"
12389 code: "/* eslint-disable test/used\n,\ntest/unused */",
12390 output: "/* eslint-disable test/used */"
12393 code: "/* eslint-disable test/used \n \n,\n\n test/unused */",
12394 output: "/* eslint-disable test/used */"
12397 code: "/* eslint-disable test/used\u2028,\u2028test/unused */",
12398 output: "/* eslint-disable test/used */"
12401 code: "// eslint-disable-line test/used\u00A0,\u00A0test/unused",
12402 output: "// eslint-disable-line test/used"
12405 code: "// eslint-disable-line test/used,,test/unused",
12406 output: "// eslint-disable-line test/used,"
12409 code: "// eslint-disable-line test/used, ,test/unused",
12410 output: "// eslint-disable-line test/used,"
12413 code: "/* eslint-disable test/used,\n,test/unused */",
12414 output: "/* eslint-disable test/used, */"
12417 code: "/* eslint-disable test/used\n, ,test/unused */",
12418 output: "/* eslint-disable test/used\n, */"
12421 // content after the last rule should not be changed
12423 code: "// eslint-disable-line test/used,test/unused",
12424 output: "// eslint-disable-line test/used"
12427 code: "// eslint-disable-line test/used,test/unused ",
12428 output: "// eslint-disable-line test/used "
12431 code: "// eslint-disable-line test/used,test/unused ",
12432 output: "// eslint-disable-line test/used "
12435 code: "// eslint-disable-line test/used,test/unused -- comment",
12436 output: "// eslint-disable-line test/used -- comment"
12439 code: "// eslint-disable-next-line test/used,test/unused\n",
12440 output: "// eslint-disable-next-line test/used\n"
12443 code: "// eslint-disable-next-line test/used,test/unused \n",
12444 output: "// eslint-disable-next-line test/used \n"
12447 code: "/* eslint-disable test/used,test/unused\u2028*/",
12448 output: "/* eslint-disable test/used\u2028*/"
12451 code: "// eslint-disable-line test/used,test/unused\u00A0",
12452 output: "// eslint-disable-line test/used\u00A0"
12455 // multiply rules to remove
12457 code: "// eslint-disable-line test/used, test/unused-1, test/unused-2",
12458 output: "// eslint-disable-line test/used"
12461 code: "// eslint-disable-line test/unused-1, test/used, test/unused-2",
12462 output: "// eslint-disable-line test/used"
12465 code: "// eslint-disable-line test/unused-1, test/unused-2, test/used",
12466 output: "// eslint-disable-line test/used"
12469 code: "// eslint-disable-line test/used-1, test/unused-1, test/used-2, test/unused-2",
12470 output: "// eslint-disable-line test/used-1, test/used-2"
12473 code: "// eslint-disable-line test/unused-1, test/used-1, test/unused-2, test/used-2",
12474 output: "// eslint-disable-line test/used-1, test/used-2"
12478 /* eslint-disable test/unused-1,
12485 /* eslint-disable test/used-1,
12591 // duplicates in the list
12593 code: "// eslint-disable-line test/unused, test/unused, test/used",
12594 output: "// eslint-disable-line test/used"
12597 code: "// eslint-disable-line test/unused, test/used, test/unused",
12598 output: "// eslint-disable-line test/used"
12601 code: "// eslint-disable-line test/used, test/unused, test/unused, test/used",
12602 output: "// eslint-disable-line test/used, test/used"
12606 for (const { code, output } of tests) {
12607 // eslint-disable-next-line no-loop-func -- `linter
` is getting updated in beforeEach()
12609 assert.strictEqual(
12610 linter.verifyAndFix(code, config).output,
12620 describe("Default Global Variables", () => {
12623 it("builtin global variables should be available in the global scope", () => {
12629 checker: context => {
12630 spy = sinon.spy(() => {
12631 const scope = context.getScope();
12633 assert.notStrictEqual(getVariable(scope, "Object"), null);
12634 assert.notStrictEqual(getVariable(scope, "Array"), null);
12635 assert.notStrictEqual(getVariable(scope, "undefined"), null);
12638 return { Program: spy };
12645 sourceType: "script"
12648 "test/checker": "error"
12652 linter.verify(code, config);
12653 assert(spy && spy.calledOnce, "Rule should have been called.");
12656 it("ES6 global variables should be available by default", () => {
12662 checker: context => {
12663 spy = sinon.spy(() => {
12664 const scope = context.getScope();
12666 assert.notStrictEqual(getVariable(scope, "Promise"), null);
12667 assert.notStrictEqual(getVariable(scope, "Symbol"), null);
12668 assert.notStrictEqual(getVariable(scope, "WeakMap"), null);
12671 return { Program: spy };
12677 sourceType: "script"
12680 "test/checker": "error"
12684 linter.verify(code, config);
12685 assert(spy && spy.calledOnce);
12690 describe("Suggestions", () => {
12691 it("provides suggestion information for tools to use", () => {
12697 "rule-with-suggestions": {
12698 meta: { hasSuggestions: true },
12699 create: context => ({
12703 message: "Incorrect spacing",
12705 desc: "Insert space at the beginning",
12706 fix: fixer => fixer.insertTextBefore(node, " ")
12708 desc: "Insert space at the end",
12709 fix: fixer => fixer.insertTextAfter(node, " ")
12719 "test/rule-with-suggestions": "error"
12723 const messages = linter.verify("var a = 1;", config);
12725 assert.deepStrictEqual(messages[0].suggestions, [{
12726 desc: "Insert space at the beginning",
12732 desc: "Insert space at the end",
12740 it("supports messageIds for suggestions", () => {
12746 "rule-with-suggestions": {
12749 suggestion1: "Insert space at the beginning",
12750 suggestion2: "Insert space at the end"
12752 hasSuggestions: true
12754 create: context => ({
12758 message: "Incorrect spacing",
12760 messageId: "suggestion1",
12761 fix: fixer => fixer.insertTextBefore(node, " ")
12763 messageId: "suggestion2",
12764 fix: fixer => fixer.insertTextAfter(node, " ")
12774 "test/rule-with-suggestions": "error"
12778 const messages = linter.verify("var a = 1;", config);
12780 assert.deepStrictEqual(messages[0].suggestions, [{
12781 messageId: "suggestion1",
12782 desc: "Insert space at the beginning",
12788 messageId: "suggestion2",
12789 desc: "Insert space at the end",
12797 it("should throw an error if suggestion is passed but `meta
.hasSuggestions
` property is not enabled", () => {
12803 "rule-with-suggestions": {
12804 meta: { docs: {}, schema: [] },
12805 create: context => ({
12809 message: "hello world",
12810 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
12819 "test/rule-with-suggestions": "error"
12823 assert.throws(() => {
12824 linter.verify("0", config);
12825 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`.");
12828 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", () => {
12834 "rule-with-meta-docs-suggestion": {
12835 meta: { docs: { suggestion: true }, schema: [] },
12836 create: context => ({
12840 message: "hello world",
12841 suggest: [{ desc: "convert to foo", fix: fixer => fixer.insertTextBefore(node, " ") }]
12850 "test/rule-with-meta-docs-suggestion": "error"
12854 assert.throws(() => {
12855 linter.verify("0", config);
12856 }, "Rules with suggestions must set the `meta
.hasSuggestions
` property to `true`. `meta
.docs
.suggestion
` is ignored by ESLint.");
12861 describe("Error Conditions", () => {
12862 describe("when evaluating broken code", () => {
12863 const code = BROKEN_TEST_CODE;
12865 it("should report a violation with a useful parse error prefix", () => {
12866 const messages = linter.verify(code);
12868 assert.strictEqual(messages.length, 1);
12869 assert.strictEqual(messages[0].severity, 2);
12870 assert.isNull(messages[0].ruleId);
12871 assert.strictEqual(messages[0].line, 1);
12872 assert.strictEqual(messages[0].column, 4);
12873 assert.isTrue(messages[0].fatal);
12874 assert.match(messages[0].message, /^Parsing error:/u);
12877 it("should report source code where the issue is present", () => {
12878 const inValidCode = [
12884 const messages = linter.verify(inValidCode.join("\n"));
12886 assert.strictEqual(messages.length, 1);
12887 assert.strictEqual(messages[0].severity, 2);
12888 assert.isTrue(messages[0].fatal);
12889 assert.match(messages[0].message, /^Parsing error:/u);
12893 describe("when using a rule which has been replaced", () => {
12894 const code = TEST_CODE;
12896 it("should report the new rule", () => {
12898 assert.throws(() => {
12899 linter.verify(code, { rules: { "no-comma-dangle": 2 } });
12900 }, /Key "rules": Key "no-comma-dangle": Rule "no-comma-dangle" was removed and replaced by "comma-dangle"/u);
12908 describe("getSourceCode()", () => {
12909 const code = TEST_CODE;
12911 it("should retrieve SourceCode object after reset", () => {
12912 linter.verify(code, {}, filename, true);
12914 const sourceCode = linter.getSourceCode();
12916 assert.isObject(sourceCode);
12917 assert.strictEqual(sourceCode.text, code);
12918 assert.isObject(sourceCode.ast);
12921 it("should retrieve SourceCode object without reset", () => {
12922 linter.verify(code, {}, filename);
12924 const sourceCode = linter.getSourceCode();
12926 assert.isObject(sourceCode);
12927 assert.strictEqual(sourceCode.text, code);
12928 assert.isObject(sourceCode.ast);
12933 describe("defineRule()", () => {
12934 it("should throw an error when called in flat config mode", () => {
12935 assert.throws(() => {
12936 linter.defineRule("foo", () => {});
12937 }, /This method cannot be used with flat config/u);
12941 describe("defineRules()", () => {
12942 it("should throw an error when called in flat config mode", () => {
12943 assert.throws(() => {
12944 linter.defineRules({});
12945 }, /This method cannot be used with flat config/u);
12949 describe("defineParser()", () => {
12950 it("should throw an error when called in flat config mode", () => {
12951 assert.throws(() => {
12952 linter.defineParser("foo", {});
12953 }, /This method cannot be used with flat config/u);
12957 describe("getRules()", () => {
12958 it("should throw an error when called in flat config mode", () => {
12959 assert.throws(() => {
12961 }, /This method cannot be used with flat config/u);
12965 describe("version", () => {
12966 it("should return current version number", () => {
12967 const version = linter.version;
12969 assert.isString(version);
12970 assert.isTrue(parseInt(version[0], 10) >= 3);
12974 describe("verifyAndFix()", () => {
12975 it("Fixes the code", () => {
12976 const messages = linter.verifyAndFix("var a", {
12980 }, { filename: "test.js" });
12982 assert.strictEqual(messages.output, "var a;", "Fixes were applied correctly");
12983 assert.isTrue(messages.fixed);
12986 it("does not require a third argument", () => {
12987 const fixResult = linter.verifyAndFix("var a", {
12993 assert.deepStrictEqual(fixResult, {
13000 it("does not include suggestions in autofix results", () => {
13001 const fixResult = linter.verifyAndFix("var foo = /\\#/", {
13004 "no-useless-escape": 2
13008 assert.strictEqual(fixResult.output, "var foo = /\\#/;");
13009 assert.strictEqual(fixResult.fixed, true);
13010 assert.strictEqual(fixResult.messages[0].suggestions.length > 0, true);
13013 it("does not apply autofixes when fix argument is `false`", () => {
13014 const fixResult = linter.verifyAndFix("var a", {
13018 }, { fix: false });
13020 assert.strictEqual(fixResult.fixed, false);
13023 it("stops fixing after 10 passes", () => {
13031 fixable: "whitespace"
13038 message: "Add a space before this node.",
13039 fix: fixer => fixer.insertTextBefore(node, " ")
13049 "test/add-spaces": "error"
13053 const fixResult = linter.verifyAndFix("a", config);
13055 assert.strictEqual(fixResult.fixed, true);
13056 assert.strictEqual(fixResult.output, `${" ".repeat(10)}a
`);
13057 assert.strictEqual(fixResult.messages.length, 1);
13060 it("should throw an error if fix is passed but meta has no `fixable
` property", () => {
13071 create: context => ({
13073 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
13081 "test/test-rule": "error"
13086 assert.throws(() => {
13087 linter.verify("0", config);
13088 }, /Fixable rules must set the `meta
\.fixable
` property to "code" or "whitespace".\nOccurred while linting <input>:1\nRule: "test\/test-rule"$/u);
13091 it("should throw an error if fix is passed and there is no metadata", () => {
13098 create: context => ({
13100 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
13108 "test/test-rule": "error"
13112 assert.throws(() => {
13113 linter.verify("0", config);
13114 }, /Fixable rules must set the `meta
\.fixable
` property/u);
13117 it("should throw an error if fix is passed from a legacy-format rule", () => {
13123 "test-rule": context => ({
13125 context.report(node, "hello world", {}, () => ({ range: [1, 1], text: "" }));
13132 "test/test-rule": "error"
13137 assert.throws(() => {
13138 linter.verify("0", config);
13139 }, /Fixable rules must set the `meta
\.fixable
` property/u);
13143 describe("Mutability", () => {
13144 let linter1 = null;
13145 let linter2 = null;
13148 linter1 = new Linter();
13149 linter2 = new Linter();
13152 describe("rules", () => {
13153 it("with no changes, same rules are loaded", () => {
13154 assert.sameDeepMembers(Array.from(linter1.getRules().keys()), Array.from(linter2.getRules().keys()));
13157 it("loading rule in one doesn't change the other", () => {
13158 linter1.defineRule("mock-rule", () => ({}));
13160 assert.isTrue(linter1.getRules().has("mock-rule"), "mock rule is present");
13161 assert.isFalse(linter2.getRules().has("mock-rule"), "mock rule is not present");
13167 describe("processors", () => {
13168 let receivedFilenames = [];
13169 let receivedPhysicalFilenames = [];
13170 const extraConfig = {
13174 "report-original-text": {
13181 receivedFilenames.push(context.getFilename());
13182 receivedPhysicalFilenames.push(context.getPhysicalFilename());
13183 context.report({ node: ast, message: context.getSourceCode().text });
13194 receivedFilenames = [];
13195 receivedPhysicalFilenames = [];
13198 describe("preprocessors", () => {
13199 it("should receive text and filename.", () => {
13200 const code = "foo bar baz";
13201 const preprocess = sinon.spy(text => text.split(" "));
13202 const configs = createFlatConfigArray({});
13204 configs.normalizeSync();
13206 linter.verify(code, configs, { filename, preprocess });
13208 assert.strictEqual(preprocess.calledOnce, true, "preprocess wasn't called");
13209 assert.deepStrictEqual(preprocess.args[0], [code, filename], "preprocess was called with the wrong arguments");
13212 it("should run preprocess only once", () => {
13217 preprocess(text, filenameForText) {
13220 filename: filenameForText
13223 return [{ text: "bar", filename: "0.js" }];
13231 linter.verify("foo", config, "a.md");
13232 assert.strictEqual(logs.length, 1, "preprocess() should only be called once.");
13235 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
13236 const code = "foo bar baz";
13237 const configs = createFlatConfigArray([
13239 { rules: { "test/report-original-text": "error" } }
13242 configs.normalizeSync();
13244 const problems = linter.verify(
13250 // Apply a preprocessor that splits the source text into spaces and lints each word individually
13251 preprocess(input) {
13252 return input.split(" ");
13257 assert.strictEqual(problems.length, 3);
13258 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
13261 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
13262 const code = "foo bar baz";
13263 const configs = createFlatConfigArray([
13265 { rules: { "test/report-original-text": "error" } }
13268 configs.normalizeSync();
13270 const problems = linter.verify(
13276 // Apply a preprocessor that splits the source text into spaces and lints each word individually
13277 preprocess(input) {
13278 return input.split(" ").map(text => ({
13279 filename: "block.js",
13286 assert.strictEqual(problems.length, 3);
13287 assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
13290 assert.strictEqual(receivedFilenames.length, 3);
13291 assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
13292 assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
13293 assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
13295 // physical filename
13296 assert.strictEqual(receivedPhysicalFilenames.length, 3);
13297 assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
13300 it("should receive text even if a SourceCode object was given.", () => {
13301 const code = "foo";
13302 const preprocess = sinon.spy(text => text.split(" "));
13303 const configs = createFlatConfigArray([
13307 configs.normalizeSync();
13309 linter.verify(code, configs);
13310 const sourceCode = linter.getSourceCode();
13312 linter.verify(sourceCode, configs, { filename, preprocess });
13314 assert.strictEqual(preprocess.calledOnce, true);
13315 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
13318 it("should receive text even if a SourceCode object was given (with BOM).", () => {
13319 const code = "\uFEFFfoo";
13320 const preprocess = sinon.spy(text => text.split(" "));
13321 const configs = createFlatConfigArray([
13325 configs.normalizeSync();
13327 linter.verify(code, configs);
13328 const sourceCode = linter.getSourceCode();
13330 linter.verify(sourceCode, configs, { filename, preprocess });
13332 assert.strictEqual(preprocess.calledOnce, true);
13333 assert.deepStrictEqual(preprocess.args[0], [code, filename]);
13337 describe("postprocessors", () => {
13338 it("should receive result and filename.", () => {
13339 const code = "foo bar baz";
13340 const preprocess = sinon.spy(text => text.split(" "));
13341 const postprocess = sinon.spy(text => [text]);
13342 const configs = createFlatConfigArray([
13346 configs.normalizeSync();
13348 linter.verify(code, configs, { filename, postprocess, preprocess });
13350 assert.strictEqual(postprocess.calledOnce, true);
13351 assert.deepStrictEqual(postprocess.args[0], [[[], [], []], filename]);
13354 it("should apply a postprocessor to the reported messages", () => {
13355 const code = "foo bar baz";
13356 const configs = createFlatConfigArray([
13358 { rules: { "test/report-original-text": "error" } }
13361 configs.normalizeSync();
13363 const problems = linter.verify(
13367 preprocess: input => input.split(" "),
13370 * Apply a postprocessor that updates the locations of the reported problems
13371 * to make sure they correspond to the locations in the original text.
13373 postprocess(problemLists) {
13374 problemLists.forEach(problemList => assert.strictEqual(problemList.length, 1));
13375 return problemLists.reduce(
13376 (combinedList, problemList, index) =>
13377 combinedList.concat(
13384 message: problem.message.toUpperCase(),
13385 column: problem.column + index * 4
13396 assert.strictEqual(problems.length, 3);
13397 assert.deepStrictEqual(problems.map(problem => problem.message), ["FOO", "BAR", "BAZ"]);
13398 assert.deepStrictEqual(problems.map(problem => problem.column), [1, 5, 9]);
13401 it("should use postprocessed problem ranges when applying autofixes", () => {
13402 const code = "foo bar baz";
13403 const configs = createFlatConfigArray([
13409 "capitalize-identifiers": {
13416 if (node.name !== node.name.toUpperCase()) {
13419 message: "Capitalize this identifier",
13420 fix: fixer => fixer.replaceText(node, node.name.toUpperCase())
13431 { rules: { "test2/capitalize-identifiers": "error" } }
13434 configs.normalizeSync();
13436 const fixResult = linter.verifyAndFix(
13442 * Apply a postprocessor that updates the locations of autofixes
13443 * to make sure they correspond to locations in the original text.
13445 preprocess: input => input.split(" "),
13446 postprocess(problemLists) {
13447 return problemLists.reduce(
13448 (combinedProblems, problemList, blockIndex) =>
13449 combinedProblems.concat(
13450 problemList.map(problem =>
13451 Object.assign(problem, {
13453 text: problem.fix.text,
13454 range: problem.fix.range.map(
13455 rangeIndex => rangeIndex + blockIndex * 4
13466 assert.strictEqual(fixResult.fixed, true);
13467 assert.strictEqual(fixResult.messages.length, 0);
13468 assert.strictEqual(fixResult.output, "FOO BAR BAZ");
13473 describe("Edge cases", () => {
13475 describe("Modules", () => {
13476 const moduleConfig = {
13478 sourceType: "module",
13483 it("should properly parse import statements when sourceType is module", () => {
13484 const code = "import foo from 'foo';";
13485 const messages = linter.verify(code, moduleConfig);
13487 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
13490 it("should properly parse import all statements when sourceType is module", () => {
13491 const code = "import * as foo from 'foo';";
13492 const messages = linter.verify(code, moduleConfig);
13494 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
13497 it("should properly parse default export statements when sourceType is module", () => {
13498 const code = "export default function initialize() {}";
13499 const messages = linter.verify(code, moduleConfig);
13501 assert.strictEqual(messages.length, 0, "Unexpected linting error.");
13507 // https://github.com/eslint/eslint/issues/9687
13508 it("should report an error when invalid languageOptions found", () => {
13509 let messages = linter.verify("", { languageOptions: { ecmaVersion: 222 } });
13511 assert.deepStrictEqual(messages.length, 1);
13512 assert.ok(messages[0].message.includes("Invalid ecmaVersion"));
13514 assert.throws(() => {
13515 linter.verify("", { languageOptions: { sourceType: "foo" } });
13516 }, /Expected "script", "module", or "commonjs"./u);
13519 messages = linter.verify("", { languageOptions: { ecmaVersion: 5, sourceType: "module" } });
13521 assert.deepStrictEqual(messages.length, 1);
13522 assert.ok(messages[0].message.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
13525 it("should not crash when invalid parentheses syntax is encountered", () => {
13526 linter.verify("left = (aSize.width/2) - ()");
13529 it("should not crash when let is used inside of switch case", () => {
13530 linter.verify("switch(foo) { case 1: let bar=2; }", { languageOptions: { ecmaVersion: 6 } });
13533 it("should not crash when parsing destructured assignment", () => {
13534 linter.verify("var { a='a' } = {};", { languageOptions: { ecmaVersion: 6 } });
13537 it("should report syntax error when a keyword exists in object property shorthand", () => {
13538 const messages = linter.verify("let a = {this}", { languageOptions: { ecmaVersion: 6 } });
13540 assert.strictEqual(messages.length, 1);
13541 assert.strictEqual(messages[0].fatal, true);
13544 it("should not crash when we reuse the SourceCode object", () => {
13549 ecmaFeatures: { jsx: true }
13554 linter.verify("function render() { return <div className='test'>{hello}</div> }", config);
13555 linter.verify(linter.getSourceCode(), config);
13558 it("should reuse the SourceCode object", () => {
13566 "save-ast1": () => ({
13572 "save-ast2": () => ({
13584 ecmaFeatures: { jsx: true }
13590 linter.verify("function render() { return <div className='test'>{hello}</div> }", { ...config, rules: { "test/save-ast1": "error" } });
13591 linter.verify(linter.getSourceCode(), { ...config, rules: { "test/save-ast2": "error" } });
13593 assert(ast1 !== null);
13594 assert(ast2 !== null);
13595 assert(ast1 === ast2);
13598 it("should not modify config object passed as argument", () => {
13601 Object.freeze(config);
13602 linter.verify("var", config);
13605 it("should pass 'id' to rule contexts with the rule id", () => {
13607 const spy = sinon.spy(context => {
13608 assert.strictEqual(context.id, "test/foo-bar-baz");
13621 "test/foo-bar-baz": "error"
13626 linter.verify("x", config);
13627 assert(spy.calledOnce);
13631 describe("when evaluating an empty string", () => {
13632 it("runs rules", () => {
13638 "no-programs": context => ({
13640 context.report({ node, message: "No programs allowed." });
13647 "test/no-programs": "error"
13651 assert.strictEqual(
13652 linter.verify("", config).length,