2 * @fileoverview Tests for eslint object.
3 * @author Nicholas C. Zakas
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
13 * To make sure this works in both browsers and Node.js
14 * @param {string} name Name of the module to require
15 * @param {Object} windowName name of the window
16 * @returns {Object} Required object
19 function compatRequire(name
, windowName
) {
20 if (typeof window
=== "object") { // eslint-disable-line no-undef
21 return window
[windowName
|| name
]; // eslint-disable-line no-undef
23 if (typeof require
=== "function") {
26 throw new Error(`Cannot find object '${name}'.`);
29 //------------------------------------------------------------------------------
31 //------------------------------------------------------------------------------
33 const assert
= require("chai").assert
,
34 sinon
= require("sinon"),
35 esprima
= require("esprima"),
36 testParsers
= require("../../fixtures/parsers/linter-test-parsers");
38 const { Linter
} = compatRequire("../../../lib/linter", "eslint");
40 //------------------------------------------------------------------------------
42 //------------------------------------------------------------------------------
44 const TEST_CODE
= "var answer = 6 * 7;",
45 BROKEN_TEST_CODE
= "var;";
47 //------------------------------------------------------------------------------
49 //------------------------------------------------------------------------------
52 * Get variables in the current scope
53 * @param {Object} scope current scope
54 * @param {string} name name of the variable to look for
55 * @returns {ASTNode|null} The variable object
58 function getVariable(scope
, name
) {
59 return scope
.variables
.find(v
=> v
.name
=== name
) || null;
63 * `eslint-env` comments are processed by doing a full source text match before parsing.
64 * As a result, if this source file contains `eslint- env` followed by an environment in a string,
65 * it will actually enable the given envs for this source file. This variable is used to avoid having a string
66 * like that appear in the code.
68 const ESLINT_ENV
= "eslint-env";
70 //------------------------------------------------------------------------------
72 //------------------------------------------------------------------------------
74 describe("Linter", () => {
75 const filename
= "filename.js";
77 /** @type {InstanceType<import("../../../lib/linter/linter.js")["Linter"]>} */
81 linter = new Linter();
85 sinon.verifyAndRestore();
88 describe("Static Members", () => {
89 describe("version", () => {
90 it("should return same version as instance property", () => {
91 assert.strictEqual(Linter.version, linter.version);
96 describe("when using events", () => {
97 const code = TEST_CODE;
99 it("an error should be thrown when an error occurs inside of an event handler", () => {
100 const config = { rules: { checker: "error" } };
102 linter.defineRule("checker", () => ({
104 throw new Error("Intentional error.");
108 assert.throws(() => {
109 linter.verify(code, config, filename);
110 }, `Intentional error.\nOccurred while linting ${filename}:1`);
113 it("does not call rule listeners with a `this` value", () => {
114 const spy = sinon.spy();
116 linter.defineRule("checker", () => ({ Program: spy }));
117 linter.verify("foo", { rules: { checker: "error" } });
118 assert(spy.calledOnce);
119 assert.strictEqual(spy.firstCall.thisValue, void 0);
122 it("does not allow listeners to use special EventEmitter values", () => {
123 const spy = sinon.spy();
125 linter.defineRule("checker", () => ({ newListener: spy }));
126 linter.verify("foo", { rules: { checker: "error", "no-undef": "error" } });
127 assert(spy.notCalled);
130 it("has all the `parent` properties on nodes when the rule listeners are created", () => {
131 const spy = sinon.spy(context => {
132 const ast = context.getSourceCode().ast;
134 assert.strictEqual(ast.body[0].parent, ast);
135 assert.strictEqual(ast.body[0].expression.parent, ast.body[0]);
136 assert.strictEqual(ast.body[0].expression.left.parent, ast.body[0].expression);
137 assert.strictEqual(ast.body[0].expression.right.parent, ast.body[0].expression);
142 linter.defineRule("checker", spy);
144 linter.verify("foo + bar", { rules: { checker: "error" } });
145 assert(spy.calledOnce);
149 describe("context.getSourceLines()", () => {
151 it("should get proper lines when using \\n as a line break", () => {
152 const code = "a;\nb;";
153 const spy = sinon.spy(context => {
154 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
158 linter.defineRule("checker", spy);
159 linter.verify(code, { rules: { checker: "error" } });
160 assert(spy.calledOnce);
163 it("should get proper lines when using \\r\\n as a line break", () => {
164 const code = "a;\r\nb;";
165 const spy = sinon.spy(context => {
166 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
170 linter.defineRule("checker", spy);
171 linter.verify(code, { rules: { checker: "error" } });
172 assert(spy.calledOnce);
175 it("should get proper lines when using \\r as a line break", () => {
176 const code = "a;\rb;";
177 const spy = sinon.spy(context => {
178 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
182 linter.defineRule("checker", spy);
183 linter.verify(code, { rules: { checker: "error" } });
184 assert(spy.calledOnce);
187 it("should get proper lines when using \\u2028 as a line break", () => {
188 const code = "a;\u2028b;";
189 const spy = sinon.spy(context => {
190 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
194 linter.defineRule("checker", spy);
195 linter.verify(code, { rules: { checker: "error" } });
196 assert(spy.calledOnce);
199 it("should get proper lines when using \\u2029 as a line break", () => {
200 const code = "a;\u2029b;";
201 const spy = sinon.spy(context => {
202 assert.deepStrictEqual(context.getSourceLines(), ["a;", "b;"]);
206 linter.defineRule("checker", spy);
207 linter.verify(code, { rules: { checker: "error" } });
208 assert(spy.calledOnce);
214 describe("getSourceCode()", () => {
215 const code = TEST_CODE;
217 it("should retrieve SourceCode object after reset", () => {
218 linter.verify(code, {}, filename, true);
220 const sourceCode = linter.getSourceCode();
222 assert.isObject(sourceCode);
223 assert.strictEqual(sourceCode.text, code);
224 assert.isObject(sourceCode.ast);
227 it("should retrieve SourceCode object without reset", () => {
228 linter.verify(code, {}, filename);
230 const sourceCode = linter.getSourceCode();
232 assert.isObject(sourceCode);
233 assert.strictEqual(sourceCode.text, code);
234 assert.isObject(sourceCode.ast);
239 describe("context.getSource()", () => {
240 const code = TEST_CODE;
242 it("should retrieve all text when used without parameters", () => {
244 const config = { rules: { checker: "error" } };
247 linter.defineRule("checker", context => {
248 spy = sinon.spy(() => {
249 assert.strictEqual(context.getSource(), TEST_CODE);
251 return { Program: spy };
254 linter.verify(code, config);
255 assert(spy && spy.calledOnce);
258 it("should retrieve all text for root node", () => {
259 const config = { rules: { checker: "error" } };
262 linter.defineRule("checker", context => {
263 spy = sinon.spy(node => {
264 assert.strictEqual(context.getSource(node), TEST_CODE);
266 return { Program: spy };
269 linter.verify(code, config);
270 assert(spy && spy.calledOnce);
273 it("should clamp to valid range when retrieving characters before start of source", () => {
274 const config = { rules: { checker: "error" } };
277 linter.defineRule("checker", context => {
278 spy = sinon.spy(node => {
279 assert.strictEqual(context.getSource(node, 2, 0), TEST_CODE);
281 return { Program: spy };
284 linter.verify(code, config);
285 assert(spy && spy.calledOnce);
288 it("should retrieve all text for binary expression", () => {
289 const config = { rules: { checker: "error" } };
292 linter.defineRule("checker", context => {
293 spy = sinon.spy(node => {
294 assert.strictEqual(context.getSource(node), "6 * 7");
296 return { BinaryExpression: spy };
299 linter.verify(code, config);
300 assert(spy && spy.calledOnce);
303 it("should retrieve all text plus two characters before for binary expression", () => {
304 const config = { rules: { checker: "error" } };
307 linter.defineRule("checker", context => {
308 spy = sinon.spy(node => {
309 assert.strictEqual(context.getSource(node, 2), "= 6 * 7");
311 return { BinaryExpression: spy };
314 linter.verify(code, config);
315 assert(spy && spy.calledOnce);
318 it("should retrieve all text plus one character after for binary expression", () => {
319 const config = { rules: { checker: "error" } };
322 linter.defineRule("checker", context => {
323 spy = sinon.spy(node => {
324 assert.strictEqual(context.getSource(node, 0, 1), "6 * 7;");
326 return { BinaryExpression: spy };
329 linter.verify(code, config);
330 assert(spy && spy.calledOnce);
333 it("should retrieve all text plus two characters before and one character after for binary expression", () => {
334 const config = { rules: { checker: "error" } };
337 linter.defineRule("checker", context => {
338 spy = sinon.spy(node => {
339 assert.strictEqual(context.getSource(node, 2, 1), "= 6 * 7;");
341 return { BinaryExpression: spy };
344 linter.verify(code, config);
345 assert(spy && spy.calledOnce);
350 describe("when calling context.getAncestors", () => {
351 const code = TEST_CODE;
353 it("should retrieve all ancestors when used", () => {
355 const config = { rules: { checker: "error" } };
358 linter.defineRule("checker", context => {
359 spy = sinon.spy(() => {
360 const ancestors = context.getAncestors();
362 assert.strictEqual(ancestors.length, 3);
364 return { BinaryExpression: spy };
367 linter.verify(code, config, filename, true);
368 assert(spy && spy.calledOnce);
371 it("should retrieve empty ancestors for root node", () => {
372 const config = { rules: { checker: "error" } };
375 linter.defineRule("checker", context => {
376 spy = sinon.spy(() => {
377 const ancestors = context.getAncestors();
379 assert.strictEqual(ancestors.length, 0);
382 return { Program: spy };
385 linter.verify(code, config);
386 assert(spy && spy.calledOnce);
390 describe("when calling context.getNodeByRangeIndex", () => {
391 const code = TEST_CODE;
393 it("should retrieve a node starting at the given index", () => {
394 const config = { rules: { checker: "error" } };
395 const spy = sinon.spy(context => {
396 assert.strictEqual(context.getNodeByRangeIndex(4).type, "Identifier");
400 linter.defineRule("checker", spy);
401 linter.verify(code, config);
402 assert(spy.calledOnce);
405 it("should retrieve a node containing the given index", () => {
406 const config = { rules: { checker: "error" } };
407 const spy = sinon.spy(context => {
408 assert.strictEqual(context.getNodeByRangeIndex(6).type, "Identifier");
412 linter.defineRule("checker", spy);
413 linter.verify(code, config);
414 assert(spy.calledOnce);
417 it("should retrieve a node that is exactly the given index", () => {
418 const config = { rules: { checker: "error" } };
419 const spy = sinon.spy(context => {
420 const node = context.getNodeByRangeIndex(13);
422 assert.strictEqual(node.type, "Literal");
423 assert.strictEqual(node.value, 6);
427 linter.defineRule("checker", spy);
428 linter.verify(code, config);
429 assert(spy.calledOnce);
432 it("should retrieve a node ending with the given index", () => {
433 const config = { rules: { checker: "error" } };
434 const spy = sinon.spy(context => {
435 assert.strictEqual(context.getNodeByRangeIndex(9).type, "Identifier");
439 linter.defineRule("checker", spy);
440 linter.verify(code, config);
441 assert(spy.calledOnce);
444 it("should retrieve the deepest node containing the given index", () => {
445 const config = { rules: { checker: "error" } };
446 const spy = sinon.spy(context => {
447 const node1 = context.getNodeByRangeIndex(14);
449 assert.strictEqual(node1.type, "BinaryExpression");
451 const node2 = context.getNodeByRangeIndex(3);
453 assert.strictEqual(node2.type, "VariableDeclaration");
457 linter.defineRule("checker", spy);
458 linter.verify(code, config);
459 assert(spy.calledOnce);
462 it("should return null if the index is outside the range of any node", () => {
463 const config = { rules: { checker: "error" } };
464 const spy = sinon.spy(context => {
465 const node1 = context.getNodeByRangeIndex(-1);
467 assert.isNull(node1);
469 const node2 = context.getNodeByRangeIndex(-99);
471 assert.isNull(node2);
475 linter.defineRule("checker", spy);
476 linter.verify(code, config);
477 assert(spy.calledOnce);
482 describe("when calling context.getScope", () => {
483 const code = "function foo() { q: for(;;) { break q; } } function bar () { var q = t; } var baz = (() => { return 1; });";
485 it("should retrieve the global scope correctly from a Program", () => {
486 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
489 linter.defineRule("checker", context => {
490 spy = sinon.spy(() => {
491 const scope = context.getScope();
493 assert.strictEqual(scope.type, "global");
495 return { Program: spy };
498 linter.verify(code, config);
499 assert(spy && spy.calledOnce);
502 it("should retrieve the function scope correctly from a FunctionDeclaration", () => {
503 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
506 linter.defineRule("checker", context => {
507 spy = sinon.spy(() => {
508 const scope = context.getScope();
510 assert.strictEqual(scope.type, "function");
512 return { FunctionDeclaration: spy };
515 linter.verify(code, config);
516 assert(spy && spy.calledTwice);
519 it("should retrieve the function scope correctly from a LabeledStatement", () => {
520 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
523 linter.defineRule("checker", context => {
524 spy = sinon.spy(() => {
525 const scope = context.getScope();
527 assert.strictEqual(scope.type, "function");
528 assert.strictEqual(scope.block.id.name, "foo");
530 return { LabeledStatement: spy };
533 linter.verify(code, config);
534 assert(spy && spy.calledOnce);
537 it("should retrieve the function scope correctly from within an ArrowFunctionExpression", () => {
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, "function");
546 assert.strictEqual(scope.block.type, "ArrowFunctionExpression");
549 return { ReturnStatement: spy };
552 linter.verify(code, config);
553 assert(spy && spy.calledOnce);
556 it("should retrieve the function scope correctly from within an SwitchStatement", () => {
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, "switch");
565 assert.strictEqual(scope.block.type, "SwitchStatement");
568 return { SwitchStatement: spy };
571 linter.verify("switch(foo){ case 'a': var b = 'foo'; }", config);
572 assert(spy && spy.calledOnce);
575 it("should retrieve the function scope correctly from within a BlockStatement", () => {
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("var x; {let y = 1}", config);
591 assert(spy && spy.calledOnce);
594 it("should retrieve the function scope correctly from within a nested block statement", () => {
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, "block");
603 assert.strictEqual(scope.block.type, "BlockStatement");
606 return { BlockStatement: spy };
609 linter.verify("if (true) { let x = 1 }", config);
610 assert(spy && spy.calledOnce);
613 it("should retrieve the function scope correctly from within a FunctionDeclaration", () => {
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, "FunctionDeclaration");
625 return { FunctionDeclaration: spy };
628 linter.verify("function foo() {}", config);
629 assert(spy && spy.calledOnce);
632 it("should retrieve the function scope correctly from within a FunctionExpression", () => {
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, "function");
641 assert.strictEqual(scope.block.type, "FunctionExpression");
644 return { FunctionExpression: spy };
647 linter.verify("(function foo() {})();", config);
648 assert(spy && spy.calledOnce);
651 it("should retrieve the catch scope correctly from within a CatchClause", () => {
652 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6 } };
655 linter.defineRule("checker", context => {
656 spy = sinon.spy(() => {
657 const scope = context.getScope();
659 assert.strictEqual(scope.type, "catch");
660 assert.strictEqual(scope.block.type, "CatchClause");
663 return { CatchClause: spy };
666 linter.verify("try {} catch (err) {}", config);
667 assert(spy && spy.calledOnce);
670 it("should retrieve module scope correctly from an ES6 module", () => {
671 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } };
674 linter.defineRule("checker", context => {
675 spy = sinon.spy(() => {
676 const scope = context.getScope();
678 assert.strictEqual(scope.type, "module");
681 return { AssignmentExpression: spy };
684 linter.verify("var foo = {}; foo.bar = 1;", config);
685 assert(spy && spy.calledOnce);
688 it("should retrieve function scope correctly when globalReturn is true", () => {
689 const config = { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, ecmaFeatures: { globalReturn: true } } };
692 linter.defineRule("checker", context => {
693 spy = sinon.spy(() => {
694 const scope = context.getScope();
696 assert.strictEqual(scope.type, "function");
699 return { AssignmentExpression: spy };
702 linter.verify("var foo = {}; foo.bar = 1;", config);
703 assert(spy && spy.calledOnce);
707 describe("marking variables as used", () => {
708 it("should mark variables in current scope as used", () => {
709 const code = "var a = 1, b = 2;";
712 linter.defineRule("checker", context => {
713 spy = sinon.spy(() => {
714 assert.isTrue(context.markVariableAsUsed("a"));
716 const scope = context.getScope();
718 assert.isTrue(getVariable(scope, "a").eslintUsed);
719 assert.notOk(getVariable(scope, "b").eslintUsed);
722 return { "Program:exit": spy };
725 linter.verify(code, { rules: { checker: "error" } });
726 assert(spy && spy.calledOnce);
728 it("should mark variables in function args as used", () => {
729 const code = "function abc(a, b) { return 1; }";
732 linter.defineRule("checker", context => {
733 spy = sinon.spy(() => {
734 assert.isTrue(context.markVariableAsUsed("a"));
736 const scope = context.getScope();
738 assert.isTrue(getVariable(scope, "a").eslintUsed);
739 assert.notOk(getVariable(scope, "b").eslintUsed);
742 return { ReturnStatement: spy };
745 linter.verify(code, { rules: { checker: "error" } });
746 assert(spy && spy.calledOnce);
748 it("should mark variables in higher scopes as used", () => {
749 const code = "var a, b; function abc() { return 1; }";
750 let returnSpy, exitSpy;
752 linter.defineRule("checker", context => {
753 returnSpy = sinon.spy(() => {
754 assert.isTrue(context.markVariableAsUsed("a"));
756 exitSpy = sinon.spy(() => {
757 const scope = context.getScope();
759 assert.isTrue(getVariable(scope, "a").eslintUsed);
760 assert.notOk(getVariable(scope, "b").eslintUsed);
763 return { ReturnStatement: returnSpy, "Program:exit": exitSpy };
766 linter.verify(code, { rules: { checker: "error" } });
767 assert(returnSpy && returnSpy.calledOnce);
768 assert(exitSpy && exitSpy.calledOnce);
771 it("should mark variables in Node.js environment as used", () => {
772 const code = "var a = 1, b = 2;";
775 linter.defineRule("checker", context => {
776 spy = sinon.spy(() => {
777 const globalScope = context.getScope(),
778 childScope = globalScope.childScopes[0];
780 assert.isTrue(context.markVariableAsUsed("a"));
782 assert.isTrue(getVariable(childScope, "a").eslintUsed);
783 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
786 return { "Program:exit": spy };
789 linter.verify(code, { rules: { checker: "error" }, env: { node: true } });
790 assert(spy && spy.calledOnce);
793 it("should mark variables in modules as used", () => {
794 const code = "var a = 1, b = 2;";
797 linter.defineRule("checker", context => {
798 spy = sinon.spy(() => {
799 const globalScope = context.getScope(),
800 childScope = globalScope.childScopes[0];
802 assert.isTrue(context.markVariableAsUsed("a"));
804 assert.isTrue(getVariable(childScope, "a").eslintUsed);
805 assert.isUndefined(getVariable(childScope, "b").eslintUsed);
808 return { "Program:exit": spy };
811 linter.verify(code, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 6, sourceType: "module" } }, filename, true);
812 assert(spy && spy.calledOnce);
815 it("should return false if the given variable is not found", () => {
816 const code = "var a = 1, b = 2;";
819 linter.defineRule("checker", context => {
820 spy = sinon.spy(() => {
821 assert.isFalse(context.markVariableAsUsed("c"));
824 return { "Program:exit": spy };
827 linter.verify(code, { rules: { checker: "error" } });
828 assert(spy && spy.calledOnce);
832 describe("when evaluating code", () => {
833 const code = TEST_CODE;
835 it("events for each node type should fire", () => {
836 const config = { rules: { checker: "error" } };
838 // spies for various AST node types
839 const spyLiteral = sinon.spy(),
840 spyVariableDeclarator = sinon.spy(),
841 spyVariableDeclaration = sinon.spy(),
842 spyIdentifier = sinon.spy(),
843 spyBinaryExpression = sinon.spy();
845 linter.defineRule("checker", () => ({
847 VariableDeclarator: spyVariableDeclarator,
848 VariableDeclaration: spyVariableDeclaration,
849 Identifier: spyIdentifier,
850 BinaryExpression: spyBinaryExpression
853 const messages = linter.verify(code, config, filename, true);
855 assert.strictEqual(messages.length, 0);
856 sinon.assert.calledOnce(spyVariableDeclaration);
857 sinon.assert.calledOnce(spyVariableDeclarator);
858 sinon.assert.calledOnce(spyIdentifier);
859 sinon.assert.calledTwice(spyLiteral);
860 sinon.assert.calledOnce(spyBinaryExpression);
863 it("should throw an error if a rule reports a problem without a message", () => {
864 linter.defineRule("invalid-report", context => ({
866 context.report({ node });
871 () => linter.verify("foo", { rules: { "invalid-report": "error" } }),
873 "Missing `message` property in report() call; add a message that describes the linting problem."
878 describe("when config has shared settings for rules", () => {
879 const code = "test-rule";
881 it("should pass settings to all rules", () => {
882 linter.defineRule(code, context => ({
884 context.report(node, context.settings.info);
888 const config = { rules: {}, settings: { info: "Hello" } };
890 config.rules[code] = 1;
892 const messages = linter.verify("0", config, filename);
894 assert.strictEqual(messages.length, 1);
895 assert.strictEqual(messages[0].message, "Hello");
898 it("should not have any settings if they were not passed in", () => {
899 linter.defineRule(code, context => ({
901 if (Object.getOwnPropertyNames(context.settings).length !== 0) {
902 context.report(node, "Settings should be empty");
907 const config = { rules: {} };
909 config.rules[code] = 1;
911 const messages = linter.verify("0", config, filename);
913 assert.strictEqual(messages.length, 0);
917 describe("when config has parseOptions", () => {
919 it("should pass ecmaFeatures to all rules when provided on config", () => {
921 const parserOptions = {
928 linter.defineRule("test-rule", sinon.mock().withArgs(
929 sinon.match({ parserOptions })
932 const config = { rules: { "test-rule": 2 }, parserOptions };
934 linter.verify("0", config, filename);
937 it("should pass parserOptions to all rules when default parserOptions is used", () => {
939 const parserOptions = {};
941 linter.defineRule("test-rule", sinon.mock().withArgs(
942 sinon.match({ parserOptions })
945 const config = { rules: { "test-rule": 2 } };
947 linter.verify("0", config, filename);
952 describe("when a custom parser is defined using defineParser", () => {
954 it("should be able to define a custom parser", () => {
956 parseForESLint: function parse(code, options) {
958 ast: esprima.parse(code, options),
970 linter.defineParser("test-parser", parser);
971 const config = { rules: {}, parser: "test-parser" };
972 const messages = linter.verify("0", config, filename);
974 assert.strictEqual(messages.length, 0);
979 describe("when config has parser", () => {
981 it("should pass parser as parserPath to all rules when provided on config", () => {
983 const alternateParser = "esprima";
985 linter.defineParser("esprima", esprima);
986 linter.defineRule("test-rule", sinon.mock().withArgs(
987 sinon.match({ parserPath: alternateParser })
990 const config = { rules: { "test-rule": 2 }, parser: alternateParser };
992 linter.verify("0", config, filename);
995 it("should use parseForESLint() in custom parser when custom parser is specified", () => {
996 const config = { rules: {}, parser: "enhanced-parser" };
998 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
999 const messages = linter.verify("0", config, filename);
1001 assert.strictEqual(messages.length, 0);
1004 it("should expose parser services when using parseForESLint() and services are specified", () => {
1005 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1006 linter.defineRule("test-service-rule", context => ({
1010 message: context.parserServices.test.getMessage()
1015 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
1016 const messages = linter.verify("0", config, filename);
1018 assert.strictEqual(messages.length, 1);
1019 assert.strictEqual(messages[0].message, "Hi!");
1022 it("should use the same parserServices if source code object is reused", () => {
1023 linter.defineParser("enhanced-parser", testParsers.enhancedParser);
1024 linter.defineRule("test-service-rule", context => ({
1028 message: context.parserServices.test.getMessage()
1033 const config = { rules: { "test-service-rule": 2 }, parser: "enhanced-parser" };
1034 const messages = linter.verify("0", config, filename);
1036 assert.strictEqual(messages.length, 1);
1037 assert.strictEqual(messages[0].message, "Hi!");
1039 const messages2 = linter.verify(linter.getSourceCode(), config, filename);
1041 assert.strictEqual(messages2.length, 1);
1042 assert.strictEqual(messages2[0].message, "Hi!");
1045 it("should pass parser as parserPath to all rules when default parser is used", () => {
1046 linter.defineRule("test-rule", sinon.mock().withArgs(
1047 sinon.match({ parserPath: "espree" })
1050 const config = { rules: { "test-rule": 2 } };
1052 linter.verify("0", config, filename);
1058 describe("when passing in configuration values for rules", () => {
1059 const code = "var answer = 6 * 7";
1061 it("should be configurable by only setting the integer value", () => {
1062 const rule = "semi",
1063 config = { rules: {} };
1065 config.rules[rule] = 1;
1067 const messages = linter.verify(code, config, filename, true);
1069 assert.strictEqual(messages.length, 1);
1070 assert.strictEqual(messages[0].ruleId, rule);
1073 it("should be configurable by only setting the string value", () => {
1074 const rule = "semi",
1075 config = { rules: {} };
1077 config.rules[rule] = "warn";
1079 const messages = linter.verify(code, config, filename, true);
1081 assert.strictEqual(messages.length, 1);
1082 assert.strictEqual(messages[0].severity, 1);
1083 assert.strictEqual(messages[0].ruleId, rule);
1086 it("should be configurable by passing in values as an array", () => {
1087 const rule = "semi",
1088 config = { rules: {} };
1090 config.rules[rule] = [1];
1092 const messages = linter.verify(code, config, filename, true);
1094 assert.strictEqual(messages.length, 1);
1095 assert.strictEqual(messages[0].ruleId, rule);
1098 it("should be configurable by passing in string value as an array", () => {
1099 const rule = "semi",
1100 config = { rules: {} };
1102 config.rules[rule] = ["warn"];
1104 const messages = linter.verify(code, config, filename, true);
1106 assert.strictEqual(messages.length, 1);
1107 assert.strictEqual(messages[0].severity, 1);
1108 assert.strictEqual(messages[0].ruleId, rule);
1111 it("should not be configurable by setting other value", () => {
1112 const rule = "semi",
1113 config = { rules: {} };
1115 config.rules[rule] = "1";
1117 const messages = linter.verify(code, config, filename, true);
1119 assert.strictEqual(messages.length, 0);
1122 it("should process empty config", () => {
1124 const messages = linter.verify(code, config, filename, true);
1126 assert.strictEqual(messages.length, 0);
1130 describe("when evaluating code containing /*global */ and
/*globals */ blocks
", () => {
1132 it("variables should be available
in global scope
", () => {
1133 const config = { rules: { checker: "error
" }, globals: { Array: "off
", ConfigGlobal: "writeable
" } };
1135 /*global a b:true c:false d:readable e:writeable Math:off */
1138 /* global ConfigGlobal : readable */
1142 linter.defineRule("checker
", context => {
1143 spy = sinon.spy(() => {
1144 const scope = context.getScope();
1145 const a = getVariable(scope, "a
"),
1146 b = getVariable(scope, "b
"),
1147 c = getVariable(scope, "c
"),
1148 d = getVariable(scope, "d
"),
1149 e = getVariable(scope, "e
"),
1150 f = getVariable(scope, "f
"),
1151 mathGlobal = getVariable(scope, "Math
"),
1152 arrayGlobal = getVariable(scope, "Array
"),
1153 configGlobal = getVariable(scope, "ConfigGlobal
");
1155 assert.strictEqual(a.name, "a
");
1156 assert.strictEqual(a.writeable, false);
1157 assert.strictEqual(b.name, "b
");
1158 assert.strictEqual(b.writeable, true);
1159 assert.strictEqual(c.name, "c
");
1160 assert.strictEqual(c.writeable, false);
1161 assert.strictEqual(d.name, "d
");
1162 assert.strictEqual(d.writeable, false);
1163 assert.strictEqual(e.name, "e
");
1164 assert.strictEqual(e.writeable, true);
1165 assert.strictEqual(f.name, "f
");
1166 assert.strictEqual(f.writeable, true);
1167 assert.strictEqual(mathGlobal, null);
1168 assert.strictEqual(arrayGlobal, null);
1169 assert.strictEqual(configGlobal.name, "ConfigGlobal
");
1170 assert.strictEqual(configGlobal.writeable, false);
1173 return { Program: spy };
1176 linter.verify(code, config);
1177 assert(spy && spy.calledOnce);
1181 describe("when evaluating code containing a
/*global */ block
with sloppy whitespace
", () => {
1182 const code = "/* global a b : true c: false*/";
1184 it("variables should be available
in global scope
", () => {
1185 const config = { rules: { checker: "error
" } };
1188 linter.defineRule("checker
", context => {
1189 spy = sinon.spy(() => {
1190 const scope = context.getScope(),
1191 a = getVariable(scope, "a
"),
1192 b = getVariable(scope, "b
"),
1193 c = getVariable(scope, "c
");
1195 assert.strictEqual(a.name, "a
");
1196 assert.strictEqual(a.writeable, false);
1197 assert.strictEqual(b.name, "b
");
1198 assert.strictEqual(b.writeable, true);
1199 assert.strictEqual(c.name, "c
");
1200 assert.strictEqual(c.writeable, false);
1203 return { Program: spy };
1206 linter.verify(code, config);
1207 assert(spy && spy.calledOnce);
1211 describe("when evaluating code containing a
/*global */ block
with specific variables
", () => {
1212 const code = "/* global toString hasOwnProperty valueOf: true */";
1214 it("should not
throw an error
if comment block has global variables which are Object
.prototype contains
", () => {
1215 const config = { rules: { checker: "error
" } };
1217 linter.verify(code, config);
1221 describe("when evaluating code containing
/*eslint-env */ block
", () => {
1222 it("variables should be available
in global scope
", () => {
1223 const code = `/*${ESLINT_ENV} node*/ function f() {} /*${ESLINT_ENV} browser, foo*/`;
1224 const config = { rules: { checker: "error
" } };
1227 linter.defineRule("checker
", context => {
1228 spy = sinon.spy(() => {
1229 const scope = context.getScope(),
1230 exports = getVariable(scope, "exports
"),
1231 window = getVariable(scope, "window
");
1233 assert.strictEqual(exports.writeable, true);
1234 assert.strictEqual(window.writeable, false);
1237 return { Program: spy };
1240 linter.verify(code, config);
1241 assert(spy && spy.calledOnce);
1245 describe("when evaluating code containing
/*eslint-env */ block
with sloppy whitespace
", () => {
1246 const code = `/* ${ESLINT_ENV} ,, node , no-browser ,, */`;
1248 it("variables should be available
in global scope
", () => {
1249 const config = { rules: { checker: "error
" } };
1252 linter.defineRule("checker
", context => {
1253 spy = sinon.spy(() => {
1254 const scope = context.getScope(),
1255 exports = getVariable(scope, "exports
"),
1256 window = getVariable(scope, "window
");
1258 assert.strictEqual(exports.writeable, true);
1259 assert.strictEqual(window, null);
1262 return { Program: spy };
1265 linter.verify(code, config);
1266 assert(spy && spy.calledOnce);
1270 describe("when evaluating code containing
/*exported */ block
", () => {
1272 it("we should behave nicely when no matching variable is found
", () => {
1273 const code = "/* exported horse */";
1274 const config = { rules: {} };
1276 linter.verify(code, config, filename, true);
1279 it("variables should be exported
", () => {
1280 const code = "/* exported horse */\n\nvar horse
= 'circus'";
1281 const config = { rules: { checker: "error
" } };
1284 linter.defineRule("checker
", context => {
1285 spy = sinon.spy(() => {
1286 const scope = context.getScope(),
1287 horse = getVariable(scope, "horse
");
1289 assert.strictEqual(horse.eslintUsed, true);
1292 return { Program: spy };
1295 linter.verify(code, config);
1296 assert(spy && spy.calledOnce);
1299 it("undefined variables should not be exported
", () => {
1300 const code = "/* exported horse */\n\nhorse = 'circus'";
1301 const config = { rules: { checker: "error
" } };
1304 linter.defineRule("checker
", context => {
1305 spy = sinon.spy(() => {
1306 const scope = context.getScope(),
1307 horse = getVariable(scope, "horse
");
1309 assert.strictEqual(horse, null);
1312 return { Program: spy };
1315 linter.verify(code, config);
1316 assert(spy && spy.calledOnce);
1319 it("variables should be exported
in strict mode
", () => {
1320 const code = "/* exported horse */\n'use strict';\nvar horse
= 'circus'";
1321 const config = { rules: { checker: "error
" } };
1324 linter.defineRule("checker
", context => {
1325 spy = sinon.spy(() => {
1326 const scope = context.getScope(),
1327 horse = getVariable(scope, "horse
");
1329 assert.strictEqual(horse.eslintUsed, true);
1332 return { Program: spy };
1335 linter.verify(code, config);
1336 assert(spy && spy.calledOnce);
1339 it("variables should not be exported
in the es6 module environment
", () => {
1340 const code = "/* exported horse */\nvar horse
= 'circus'";
1341 const config = { rules: { checker: "error
" }, parserOptions: { ecmaVersion: 6, sourceType: "module
" } };
1344 linter.defineRule("checker
", context => {
1345 spy = sinon.spy(() => {
1346 const scope = context.getScope(),
1347 horse = getVariable(scope, "horse
");
1349 assert.strictEqual(horse, null); // there is no global scope at all
1352 return { Program: spy };
1355 linter.verify(code, config);
1356 assert(spy && spy.calledOnce);
1359 it("variables should not be exported when
in the node environment
", () => {
1360 const code = "/* exported horse */\nvar horse
= 'circus'";
1361 const config = { rules: { checker: "error
" }, env: { node: true } };
1364 linter.defineRule("checker
", context => {
1365 spy = sinon.spy(() => {
1366 const scope = context.getScope(),
1367 horse = getVariable(scope, "horse
");
1369 assert.strictEqual(horse, null); // there is no global scope at all
1372 return { Program: spy };
1375 linter.verify(code, config);
1376 assert(spy && spy.calledOnce);
1380 describe("when evaluating code containing a line comment
", () => {
1381 const code = "//global a \n function f() {}";
1383 it("should not introduce a global variable", () => {
1384 const config
= { rules
: { checker
: "error" } };
1387 linter
.defineRule("checker", context
=> {
1388 spy
= sinon
.spy(() => {
1389 const scope
= context
.getScope();
1391 assert
.strictEqual(getVariable(scope
, "a"), null);
1394 return { Program
: spy
};
1397 linter
.verify(code
, config
);
1398 assert(spy
&& spy
.calledOnce
);
1402 describe("when evaluating code containing normal block comments", () => {
1403 const code
= "/**/ /*a*/ /*b:true*/ /*foo c:false*/";
1405 it("should not introduce a global variable", () => {
1406 const config
= { rules
: { checker
: "error" } };
1409 linter
.defineRule("checker", context
=> {
1410 spy
= sinon
.spy(() => {
1411 const scope
= context
.getScope();
1413 assert
.strictEqual(getVariable(scope
, "a"), null);
1414 assert
.strictEqual(getVariable(scope
, "b"), null);
1415 assert
.strictEqual(getVariable(scope
, "foo"), null);
1416 assert
.strictEqual(getVariable(scope
, "c"), null);
1419 return { Program
: spy
};
1422 linter
.verify(code
, config
);
1423 assert(spy
&& spy
.calledOnce
);
1427 describe("when evaluating any code", () => {
1430 it("builtin global variables should be available in the global scope", () => {
1431 const config
= { rules
: { checker
: "error" } };
1434 linter
.defineRule("checker", context
=> {
1435 spy
= sinon
.spy(() => {
1436 const scope
= context
.getScope();
1438 assert
.notStrictEqual(getVariable(scope
, "Object"), null);
1439 assert
.notStrictEqual(getVariable(scope
, "Array"), null);
1440 assert
.notStrictEqual(getVariable(scope
, "undefined"), null);
1443 return { Program
: spy
};
1446 linter
.verify(code
, config
);
1447 assert(spy
&& spy
.calledOnce
);
1450 it("ES6 global variables should not be available by default", () => {
1451 const config
= { rules
: { checker
: "error" } };
1454 linter
.defineRule("checker", context
=> {
1455 spy
= sinon
.spy(() => {
1456 const scope
= context
.getScope();
1458 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1459 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1460 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1463 return { Program
: spy
};
1466 linter
.verify(code
, config
);
1467 assert(spy
&& spy
.calledOnce
);
1470 it("ES6 global variables should be available in the es6 environment", () => {
1471 const config
= { rules
: { checker
: "error" }, env
: { es6
: true } };
1474 linter
.defineRule("checker", context
=> {
1475 spy
= sinon
.spy(() => {
1476 const scope
= context
.getScope();
1478 assert
.notStrictEqual(getVariable(scope
, "Promise"), null);
1479 assert
.notStrictEqual(getVariable(scope
, "Symbol"), null);
1480 assert
.notStrictEqual(getVariable(scope
, "WeakMap"), null);
1483 return { Program
: spy
};
1486 linter
.verify(code
, config
);
1487 assert(spy
&& spy
.calledOnce
);
1490 it("ES6 global variables can be disabled when the es6 environment is enabled", () => {
1491 const config
= { rules
: { checker
: "error" }, globals
: { Promise
: "off", Symbol
: "off", WeakMap
: "off" }, env
: { es6
: true } };
1494 linter
.defineRule("checker", context
=> {
1495 spy
= sinon
.spy(() => {
1496 const scope
= context
.getScope();
1498 assert
.strictEqual(getVariable(scope
, "Promise"), null);
1499 assert
.strictEqual(getVariable(scope
, "Symbol"), null);
1500 assert
.strictEqual(getVariable(scope
, "WeakMap"), null);
1503 return { Program
: spy
};
1506 linter
.verify(code
, config
);
1507 assert(spy
&& spy
.calledOnce
);
1511 describe("at any time", () => {
1512 const code
= "new-rule";
1514 it("can add a rule dynamically", () => {
1515 linter
.defineRule(code
, context
=> ({
1517 context
.report(node
, "message");
1521 const config
= { rules
: {} };
1523 config
.rules
[code
] = 1;
1525 const messages
= linter
.verify("0", config
, filename
);
1527 assert
.strictEqual(messages
.length
, 1);
1528 assert
.strictEqual(messages
[0].ruleId
, code
);
1529 assert
.strictEqual(messages
[0].nodeType
, "Literal");
1533 describe("at any time", () => {
1534 const code
= ["new-rule-0", "new-rule-1"];
1536 it("can add multiple rules dynamically", () => {
1537 const config
= { rules
: {} };
1538 const newRules
= {};
1540 code
.forEach(item
=> {
1541 config
.rules
[item
] = 1;
1542 newRules
[item
] = function(context
) {
1545 context
.report(node
, "message");
1550 linter
.defineRules(newRules
);
1552 const messages
= linter
.verify("0", config
, filename
);
1554 assert
.strictEqual(messages
.length
, code
.length
);
1555 code
.forEach(item
=> {
1556 assert
.ok(messages
.some(message
=> message
.ruleId
=== item
));
1558 messages
.forEach(message
=> {
1559 assert
.strictEqual(message
.nodeType
, "Literal");
1564 describe("at any time", () => {
1565 const code
= "filename-rule";
1567 it("has access to the filename", () => {
1568 linter
.defineRule(code
, context
=> ({
1570 context
.report(node
, context
.getFilename());
1574 const config
= { rules
: {} };
1576 config
.rules
[code
] = 1;
1578 const messages
= linter
.verify("0", config
, filename
);
1580 assert
.strictEqual(messages
[0].message
, filename
);
1583 it("defaults filename to '<input>'", () => {
1584 linter
.defineRule(code
, context
=> ({
1586 context
.report(node
, context
.getFilename());
1590 const config
= { rules
: {} };
1592 config
.rules
[code
] = 1;
1594 const messages
= linter
.verify("0", config
);
1596 assert
.strictEqual(messages
[0].message
, "<input>");
1600 describe("when evaluating code with comments to enable rules", () => {
1602 it("should report a violation", () => {
1603 const code
= "/*eslint no-alert:1*/ alert('test');";
1604 const config
= { rules
: {} };
1606 const messages
= linter
.verify(code
, config
, filename
);
1608 assert
.strictEqual(messages
.length
, 1);
1609 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1610 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1611 assert
.include(messages
[0].nodeType
, "CallExpression");
1614 it("rules should not change initial config", () => {
1615 const config
= { rules
: { strict
: 2 } };
1616 const codeA
= "/*eslint strict: 0*/ function bar() { return 2; }";
1617 const codeB
= "function foo() { return 1; }";
1618 let messages
= linter
.verify(codeA
, config
, filename
, false);
1620 assert
.strictEqual(messages
.length
, 0);
1622 messages
= linter
.verify(codeB
, config
, filename
, false);
1623 assert
.strictEqual(messages
.length
, 1);
1626 it("rules should not change initial config", () => {
1627 const config
= { rules
: { quotes
: [2, "double"] } };
1628 const codeA
= "/*eslint quotes: 0*/ function bar() { return '2'; }";
1629 const codeB
= "function foo() { return '1'; }";
1630 let messages
= linter
.verify(codeA
, config
, filename
, false);
1632 assert
.strictEqual(messages
.length
, 0);
1634 messages
= linter
.verify(codeB
, config
, filename
, false);
1635 assert
.strictEqual(messages
.length
, 1);
1638 it("rules should not change initial config", () => {
1639 const config
= { rules
: { quotes
: [2, "double"] } };
1640 const codeA
= "/*eslint quotes: [0, \"single\"]*/ function bar() { return '2'; }";
1641 const codeB
= "function foo() { return '1'; }";
1642 let messages
= linter
.verify(codeA
, config
, filename
, false);
1644 assert
.strictEqual(messages
.length
, 0);
1646 messages
= linter
.verify(codeB
, config
, filename
, false);
1647 assert
.strictEqual(messages
.length
, 1);
1650 it("rules should not change initial config", () => {
1651 const config
= { rules
: { "no-unused-vars": [2, { vars
: "all" }] } };
1652 const codeA
= "/*eslint no-unused-vars: [0, {\"vars\": \"local\"}]*/ var a = 44;";
1653 const codeB
= "var b = 55;";
1654 let messages
= linter
.verify(codeA
, config
, filename
, false);
1656 assert
.strictEqual(messages
.length
, 0);
1658 messages
= linter
.verify(codeB
, config
, filename
, false);
1659 assert
.strictEqual(messages
.length
, 1);
1663 describe("when evaluating code with invalid comments to enable rules", () => {
1664 it("should report a violation when the config is not a valid rule configuration", () => {
1665 assert
.deepStrictEqual(
1666 linter
.verify("/*eslint no-alert:true*/ alert('test');", {}),
1671 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",
1682 it("should report a violation when the config violates a rule's schema", () => {
1683 assert
.deepStrictEqual(
1684 linter
.verify("/* eslint no-alert: [error, {nonExistentPropertyName: true}]*/", {}),
1689 message
: "Configuration for rule \"no-alert\" is invalid:\n\tValue [{\"nonExistentPropertyName\":true}] should NOT have more than 0 items.\n",
1701 describe("when evaluating code with comments to disable rules", () => {
1702 const code
= "/*eslint no-alert:0*/ alert('test');";
1704 it("should not report a violation", () => {
1705 const config
= { rules
: { "no-alert": 1 } };
1707 const messages
= linter
.verify(code
, config
, filename
);
1709 assert
.strictEqual(messages
.length
, 0);
1713 describe("when evaluating code with comments to disable rules", () => {
1716 it("should report an error when disabling a non-existent rule in inline comment", () => {
1717 code
= "/*eslint foo:0*/ ;";
1718 messages
= linter
.verify(code
, {}, filename
);
1719 assert
.strictEqual(messages
.length
, 1);
1720 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1722 code
= "/*eslint-disable foo*/ ;";
1723 messages
= linter
.verify(code
, {}, filename
);
1724 assert
.strictEqual(messages
.length
, 1);
1725 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1727 code
= "/*eslint-disable-line foo*/ ;";
1728 messages
= linter
.verify(code
, {}, filename
);
1729 assert
.strictEqual(messages
.length
, 1);
1730 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1732 code
= "/*eslint-disable-next-line foo*/ ;";
1733 messages
= linter
.verify(code
, {}, filename
);
1734 assert
.strictEqual(messages
.length
, 1);
1735 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1738 it("should not report an error, when disabling a non-existent rule in config", () => {
1739 messages
= linter
.verify("", { rules
: { foo
: 0 } }, filename
);
1741 assert
.strictEqual(messages
.length
, 0);
1744 it("should report an error, when config a non-existent rule in config", () => {
1745 messages
= linter
.verify("", { rules
: { foo
: 1 } }, filename
);
1746 assert
.strictEqual(messages
.length
, 1);
1747 assert
.strictEqual(messages
[0].severity
, 2);
1748 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1750 messages
= linter
.verify("", { rules
: { foo
: 2 } }, filename
);
1751 assert
.strictEqual(messages
.length
, 1);
1752 assert
.strictEqual(messages
[0].severity
, 2);
1753 assert
.strictEqual(messages
[0].message
, "Definition for rule 'foo' was not found.");
1757 describe("when evaluating code with comments to enable multiple rules", () => {
1758 const code
= "/*eslint no-alert:1 no-console:1*/ alert('test'); console.log('test');";
1760 it("should report a violation", () => {
1761 const config
= { rules
: {} };
1763 const messages
= linter
.verify(code
, config
, filename
);
1765 assert
.strictEqual(messages
.length
, 2);
1766 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1767 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1768 assert
.include(messages
[0].nodeType
, "CallExpression");
1769 assert
.strictEqual(messages
[1].ruleId
, "no-console");
1773 describe("when evaluating code with comments to enable and disable multiple rules", () => {
1774 const code
= "/*eslint no-alert:1 no-console:0*/ alert('test'); console.log('test');";
1776 it("should report a violation", () => {
1777 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
1779 const messages
= linter
.verify(code
, config
, filename
);
1781 assert
.strictEqual(messages
.length
, 1);
1782 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1783 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1784 assert
.include(messages
[0].nodeType
, "CallExpression");
1788 describe("when evaluating code with comments to disable and enable configurable rule as part of plugin", () => {
1791 linter
.defineRule("test-plugin/test-rule", context
=> ({
1793 if (node
.value
=== "trigger violation") {
1794 context
.report(node
, "Reporting violation.");
1800 it("should not report a violation when inline comment enables plugin rule and there's no violation", () => {
1801 const config
= { rules
: {} };
1802 const code
= "/*eslint test-plugin/test-rule: 2*/ var a = \"no violation\";";
1804 const messages
= linter
.verify(code
, config
, filename
);
1806 assert
.strictEqual(messages
.length
, 0);
1809 it("should not report a violation when inline comment disables plugin rule", () => {
1810 const code
= "/*eslint test-plugin/test-rule:0*/ var a = \"trigger violation\"";
1811 const config
= { rules
: { "test-plugin/test-rule": 1 } };
1813 const messages
= linter
.verify(code
, config
, filename
);
1815 assert
.strictEqual(messages
.length
, 0);
1818 it("should report a violation when the report is right before the comment", () => {
1819 const code
= " /* eslint-disable */ ";
1821 linter
.defineRule("checker", context
=> ({
1823 context
.report({ loc
: { line
: 1, column
: 0 }, message
: "foo" });
1826 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
1828 assert
.strictEqual(problems
.length
, 1);
1829 assert
.strictEqual(problems
[0].message
, "foo");
1832 it("should not report a violation when the report is right at the start of the comment", () => {
1833 const code
= " /* eslint-disable */ ";
1835 linter
.defineRule("checker", context
=> ({
1837 context
.report({ loc
: { line
: 1, column
: 1 }, message
: "foo" });
1840 const problems
= linter
.verify(code
, { rules
: { checker
: "error" } });
1842 assert
.strictEqual(problems
.length
, 0);
1845 it("rules should not change initial config", () => {
1846 const config
= { rules
: { "test-plugin/test-rule": 2 } };
1847 const codeA
= "/*eslint test-plugin/test-rule: 0*/ var a = \"trigger violation\";";
1848 const codeB
= "var a = \"trigger violation\";";
1849 let messages
= linter
.verify(codeA
, config
, filename
, false);
1851 assert
.strictEqual(messages
.length
, 0);
1853 messages
= linter
.verify(codeB
, config
, filename
, false);
1854 assert
.strictEqual(messages
.length
, 1);
1858 describe("when evaluating code with comments to enable and disable all reporting", () => {
1859 it("should report a violation", () => {
1862 "/*eslint-disable */",
1864 "/*eslint-enable */",
1867 const config
= { rules
: { "no-alert": 1 } };
1869 const messages
= linter
.verify(code
, config
, filename
);
1871 assert
.strictEqual(messages
.length
, 1);
1872 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1873 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
1874 assert
.include(messages
[0].nodeType
, "CallExpression");
1875 assert
.strictEqual(messages
[0].line
, 4);
1878 it("should not report a violation", () => {
1880 "/*eslint-disable */",
1884 const config
= { rules
: { "no-alert": 1 } };
1886 const messages
= linter
.verify(code
, config
, filename
);
1888 assert
.strictEqual(messages
.length
, 0);
1891 it("should not report a violation", () => {
1893 " alert('test1');/*eslint-disable */\n",
1895 " alert('test');\n",
1896 "/*eslint-enable */alert('test2');"
1898 const config
= { rules
: { "no-alert": 1 } };
1900 const messages
= linter
.verify(code
, config
, filename
);
1902 assert
.strictEqual(messages
.length
, 2);
1903 assert
.strictEqual(messages
[0].column
, 21);
1904 assert
.strictEqual(messages
[1].column
, 19);
1907 it("should report a violation", () => {
1910 "/*eslint-disable */",
1912 "/*eslint-disable */",
1914 "/*eslint-enable*/",
1919 const config
= { rules
: { "no-alert": 1 } };
1921 const messages
= linter
.verify(code
, config
, filename
);
1923 assert
.strictEqual(messages
.length
, 1);
1927 it("should not report a violation", () => {
1929 "/*eslint-disable */",
1930 "(function(){ var b = 44;})()",
1931 "/*eslint-enable */;any();"
1934 const config
= { rules
: { "no-unused-vars": 1 } };
1936 const messages
= linter
.verify(code
, config
, filename
);
1938 assert
.strictEqual(messages
.length
, 0);
1941 it("should not report a violation", () => {
1943 "(function(){ /*eslint-disable */ var b = 44;})()",
1944 "/*eslint-enable */;any();"
1947 const config
= { rules
: { "no-unused-vars": 1 } };
1949 const messages
= linter
.verify(code
, config
, filename
);
1951 assert
.strictEqual(messages
.length
, 0);
1955 describe("when evaluating code with comments to ignore reporting on specific rules on a specific line", () => {
1957 describe("eslint-disable-line", () => {
1958 it("should report a violation", () => {
1960 "alert('test'); // eslint-disable-line no-alert",
1961 "console.log('test');" // here
1970 const messages
= linter
.verify(code
, config
, filename
);
1972 assert
.strictEqual(messages
.length
, 1);
1974 assert
.strictEqual(messages
[0].ruleId
, "no-console");
1977 it("should report a violation", () => {
1979 "alert('test'); // eslint-disable-line no-alert",
1980 "console.log('test'); // eslint-disable-line no-console",
1981 "alert('test');" // here
1990 const messages
= linter
.verify(code
, config
, filename
);
1992 assert
.strictEqual(messages
.length
, 1);
1994 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
1997 it("should report a violation if eslint-disable-line in a block comment is not on a single line", () => {
1999 "/* eslint-disable-line",
2001 "*/ console.log('test');" // here
2009 const messages
= linter
.verify(code
, config
, filename
);
2011 assert
.strictEqual(messages
.length
, 2);
2013 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2016 it("should not disable rule and add an extra report if eslint-disable-line in a block comment is not on a single line", () => {
2018 "alert('test'); /* eslint-disable-line ",
2027 const messages
= linter
.verify(code
, config
);
2029 assert
.deepStrictEqual(messages
, [
2037 message
: "Unexpected alert.",
2038 messageId
: "unexpected",
2039 nodeType
: "CallExpression"
2044 message
: "eslint-disable-line comment should not span multiple lines.",
2054 it("should not report a violation for eslint-disable-line in block comment", () => {
2056 "alert('test'); // eslint-disable-line no-alert",
2057 "alert('test'); /*eslint-disable-line no-alert*/"
2065 const messages
= linter
.verify(code
, config
, filename
);
2067 assert
.strictEqual(messages
.length
, 0);
2070 it("should not report a violation", () => {
2072 "alert('test'); // eslint-disable-line no-alert",
2073 "console.log('test'); // eslint-disable-line no-console"
2082 const messages
= linter
.verify(code
, config
, filename
);
2084 assert
.strictEqual(messages
.length
, 0);
2087 it("should not report a violation", () => {
2089 "alert('test') // eslint-disable-line no-alert, quotes, semi",
2090 "console.log('test'); // eslint-disable-line"
2095 quotes
: [1, "double"],
2096 semi
: [1, "always"],
2101 const messages
= linter
.verify(code
, config
, filename
);
2103 assert
.strictEqual(messages
.length
, 0);
2106 it("should not report a violation", () => {
2108 "alert('test') /* eslint-disable-line no-alert, quotes, semi */",
2109 "console.log('test'); /* eslint-disable-line */"
2114 quotes
: [1, "double"],
2115 semi
: [1, "always"],
2120 const messages
= linter
.verify(code
, config
, filename
);
2122 assert
.strictEqual(messages
.length
, 0);
2125 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2127 " alert(\"test\"); /* eslint-disable-line no-alert */ // eslint-disable-line quotes"
2132 quotes
: [1, "single"]
2135 const messages
= linter
.verify(code
, config
, filename
);
2137 assert
.strictEqual(messages
.length
, 0);
2141 describe("eslint-disable-next-line", () => {
2142 it("should ignore violation of specified rule on next line", () => {
2144 "// eslint-disable-next-line no-alert",
2146 "console.log('test');"
2154 const messages
= linter
.verify(code
, config
, filename
);
2156 assert
.strictEqual(messages
.length
, 1);
2157 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2160 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2162 "/* eslint-disable-next-line no-alert */",
2164 "console.log('test');"
2172 const messages
= linter
.verify(code
, config
, filename
);
2174 assert
.strictEqual(messages
.length
, 1);
2175 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2177 it("should ignore violation of specified rule if eslint-disable-next-line is a block comment", () => {
2179 "/* eslint-disable-next-line no-alert */",
2187 const messages
= linter
.verify(code
, config
, filename
);
2189 assert
.strictEqual(messages
.length
, 0);
2192 it("should not ignore violation if block comment is not on a single line", () => {
2194 "/* eslint-disable-next-line",
2195 "no-alert */alert('test');"
2202 const messages
= linter
.verify(code
, config
, filename
);
2204 assert
.strictEqual(messages
.length
, 2);
2205 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2208 it("should ignore violations only of specified rule", () => {
2210 "// eslint-disable-next-line no-console",
2212 "console.log('test');"
2220 const messages
= linter
.verify(code
, config
, filename
);
2222 assert
.strictEqual(messages
.length
, 2);
2223 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2224 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2227 it("should ignore violations of multiple rules when specified", () => {
2229 "// eslint-disable-next-line no-alert, quotes",
2231 "console.log('test');"
2236 quotes
: [1, "single"],
2240 const messages
= linter
.verify(code
, config
, filename
);
2242 assert
.strictEqual(messages
.length
, 1);
2243 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2246 it("should ignore violations of multiple rules when specified in mixed comments", () => {
2248 "/* eslint-disable-next-line no-alert */ // eslint-disable-next-line quotes",
2254 quotes
: [1, "single"]
2257 const messages
= linter
.verify(code
, config
, filename
);
2259 assert
.strictEqual(messages
.length
, 0);
2262 it("should ignore violations of only the specified rule on next line", () => {
2264 "// eslint-disable-next-line quotes",
2266 "console.log('test');"
2271 quotes
: [1, "single"],
2275 const messages
= linter
.verify(code
, config
, filename
);
2277 assert
.strictEqual(messages
.length
, 2);
2278 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2279 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2282 it("should ignore violations of specified rule on next line only", () => {
2285 "// eslint-disable-next-line no-alert",
2287 "console.log('test');"
2295 const messages
= linter
.verify(code
, config
, filename
);
2297 assert
.strictEqual(messages
.length
, 2);
2298 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2299 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2302 it("should ignore all rule violations on next line if none specified", () => {
2304 "// eslint-disable-next-line",
2306 "console.log('test')"
2311 quotes
: [1, "single"],
2316 const messages
= linter
.verify(code
, config
, filename
);
2318 assert
.strictEqual(messages
.length
, 1);
2319 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2322 it("should ignore violations if eslint-disable-next-line is a block comment", () => {
2325 "/* eslint-disable-next-line no-alert */",
2327 "console.log('test');"
2335 const messages
= linter
.verify(code
, config
, filename
);
2337 assert
.strictEqual(messages
.length
, 2);
2338 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2339 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2342 it("should report a violation", () => {
2344 "/* eslint-disable-next-line",
2347 "console.log('test');" // here
2356 const messages
= linter
.verify(code
, config
, filename
);
2358 assert
.strictEqual(messages
.length
, 2);
2360 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2363 it("should not ignore violations if comment is of the type Shebang", () => {
2365 "#! eslint-disable-next-line no-alert",
2367 "console.log('test');"
2375 const messages
= linter
.verify(code
, config
, filename
);
2377 assert
.strictEqual(messages
.length
, 2);
2378 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2379 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2384 describe("when evaluating code with comments to enable and disable reporting of specific rules", () => {
2386 it("should report a violation", () => {
2388 "/*eslint-disable no-alert */",
2390 "console.log('test');" // here
2392 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2394 const messages
= linter
.verify(code
, config
, filename
);
2396 assert
.strictEqual(messages
.length
, 1);
2398 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2401 it("should report no violation", () => {
2403 "/*eslint-disable no-unused-vars */",
2404 "var foo; // eslint-disable-line no-unused-vars",
2406 "/* eslint-enable no-unused-vars */" // here
2408 const config
= { rules
: { "no-unused-vars": 2 } };
2410 const messages
= linter
.verify(code
, config
, filename
);
2412 assert
.strictEqual(messages
.length
, 0);
2415 it("should report no violation", () => {
2417 "var foo1; // eslint-disable-line no-unused-vars",
2418 "var foo2; // eslint-disable-line no-unused-vars",
2419 "var foo3; // eslint-disable-line no-unused-vars",
2420 "var foo4; // eslint-disable-line no-unused-vars",
2421 "var foo5; // eslint-disable-line no-unused-vars"
2423 const config
= { rules
: { "no-unused-vars": 2 } };
2425 const messages
= linter
.verify(code
, config
, filename
);
2427 assert
.strictEqual(messages
.length
, 0);
2430 it("should report no violation", () => {
2432 "/* eslint-disable quotes */",
2433 "console.log(\"foo\");",
2434 "/* eslint-enable quotes */"
2436 const config
= { rules
: { quotes
: 2 } };
2438 const messages
= linter
.verify(code
, config
, filename
);
2440 assert
.strictEqual(messages
.length
, 0);
2443 it("should report a violation", () => {
2445 "/*eslint-disable no-alert, no-console */",
2447 "console.log('test');",
2448 "/*eslint-enable*/",
2450 "alert('test');", // here
2451 "console.log('test');" // here
2453 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2455 const messages
= linter
.verify(code
, config
, filename
);
2457 assert
.strictEqual(messages
.length
, 2);
2459 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2460 assert
.strictEqual(messages
[0].line
, 5);
2461 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2462 assert
.strictEqual(messages
[1].line
, 6);
2465 it("should report a violation", () => {
2467 "/*eslint-disable no-alert */",
2469 "console.log('test');",
2470 "/*eslint-enable no-console */",
2472 "alert('test');" // here
2474 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2476 const messages
= linter
.verify(code
, config
, filename
);
2478 assert
.strictEqual(messages
.length
, 1);
2480 assert
.strictEqual(messages
[0].ruleId
, "no-console");
2484 it("should report a violation", () => {
2486 "/*eslint-disable no-alert, no-console */",
2488 "console.log('test');",
2489 "/*eslint-enable no-alert*/",
2491 "alert('test');", // here
2492 "console.log('test');"
2494 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2496 const messages
= linter
.verify(code
, config
, filename
);
2498 assert
.strictEqual(messages
.length
, 1);
2500 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2501 assert
.strictEqual(messages
[0].line
, 5);
2505 it("should report a violation", () => {
2507 "/*eslint-disable no-alert */",
2509 "/*eslint-disable no-console */",
2511 "console.log('test');",
2512 "/*eslint-enable */",
2514 "alert('test');", // here
2515 "console.log('test');", // here
2517 "/*eslint-enable */",
2519 "alert('test');", // here
2520 "console.log('test');", // here
2524 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2526 const messages
= linter
.verify(code
, config
, filename
);
2528 assert
.strictEqual(messages
.length
, 4);
2530 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2531 assert
.strictEqual(messages
[0].line
, 6);
2533 assert
.strictEqual(messages
[1].ruleId
, "no-console");
2534 assert
.strictEqual(messages
[1].line
, 7);
2536 assert
.strictEqual(messages
[2].ruleId
, "no-alert");
2537 assert
.strictEqual(messages
[2].line
, 9);
2539 assert
.strictEqual(messages
[3].ruleId
, "no-console");
2540 assert
.strictEqual(messages
[3].line
, 10);
2544 it("should report a violation", () => {
2546 "/*eslint-disable no-alert, no-console */",
2548 "console.log('test');",
2550 "/*eslint-enable no-alert */",
2552 "alert('test');", // here
2553 "console.log('test');",
2555 "/*eslint-enable no-console */",
2557 "alert('test');", // here
2558 "console.log('test');", // here
2559 "/*eslint-enable no-console */"
2561 const config
= { rules
: { "no-alert": 1, "no-console": 1 } };
2563 const messages
= linter
.verify(code
, config
, filename
);
2565 assert
.strictEqual(messages
.length
, 3);
2567 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2568 assert
.strictEqual(messages
[0].line
, 5);
2570 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2571 assert
.strictEqual(messages
[1].line
, 8);
2573 assert
.strictEqual(messages
[2].ruleId
, "no-console");
2574 assert
.strictEqual(messages
[2].line
, 9);
2578 it("should report a violation when severity is warn", () => {
2580 "/*eslint-disable no-alert, no-console */",
2582 "console.log('test');",
2584 "/*eslint-enable no-alert */",
2586 "alert('test');", // here
2587 "console.log('test');",
2589 "/*eslint-enable no-console */",
2591 "alert('test');", // here
2592 "console.log('test');", // here
2593 "/*eslint-enable no-console */"
2595 const config
= { rules
: { "no-alert": "warn", "no-console": "warn" } };
2597 const messages
= linter
.verify(code
, config
, filename
);
2599 assert
.strictEqual(messages
.length
, 3);
2601 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2602 assert
.strictEqual(messages
[0].line
, 5);
2604 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2605 assert
.strictEqual(messages
[1].line
, 8);
2607 assert
.strictEqual(messages
[2].ruleId
, "no-console");
2608 assert
.strictEqual(messages
[2].line
, 9);
2613 describe("when evaluating code with comments to enable and disable multiple comma separated rules", () => {
2614 const code
= "/*eslint no-alert:1, no-console:0*/ alert('test'); console.log('test');";
2616 it("should report a violation", () => {
2617 const config
= { rules
: { "no-console": 1, "no-alert": 0 } };
2619 const messages
= linter
.verify(code
, config
, filename
);
2621 assert
.strictEqual(messages
.length
, 1);
2622 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
2623 assert
.strictEqual(messages
[0].message
, "Unexpected alert.");
2624 assert
.include(messages
[0].nodeType
, "CallExpression");
2628 describe("when evaluating code with comments to enable configurable rule", () => {
2629 const code
= "/*eslint quotes:[2, \"double\"]*/ alert('test');";
2631 it("should report a violation", () => {
2632 const config
= { rules
: { quotes
: [2, "single"] } };
2634 const messages
= linter
.verify(code
, config
, filename
);
2636 assert
.strictEqual(messages
.length
, 1);
2637 assert
.strictEqual(messages
[0].ruleId
, "quotes");
2638 assert
.strictEqual(messages
[0].message
, "Strings must use doublequote.");
2639 assert
.include(messages
[0].nodeType
, "Literal");
2643 describe("when evaluating code with comments to enable configurable rule using string severity", () => {
2644 const code
= "/*eslint quotes:[\"error\", \"double\"]*/ alert('test');";
2646 it("should report a violation", () => {
2647 const config
= { rules
: { quotes
: [2, "single"] } };
2649 const messages
= linter
.verify(code
, config
, filename
);
2651 assert
.strictEqual(messages
.length
, 1);
2652 assert
.strictEqual(messages
[0].ruleId
, "quotes");
2653 assert
.strictEqual(messages
[0].message
, "Strings must use doublequote.");
2654 assert
.include(messages
[0].nodeType
, "Literal");
2658 describe("when evaluating code with incorrectly formatted comments to disable rule", () => {
2659 it("should report a violation", () => {
2660 const code
= "/*eslint no-alert:'1'*/ alert('test');";
2662 const config
= { rules
: { "no-alert": 1 } };
2664 const messages
= linter
.verify(code
, config
, filename
);
2666 assert
.strictEqual(messages
.length
, 2);
2669 * Incorrectly formatted comment threw error;
2670 * message from caught exception
2671 * may differ amongst UAs, so verifying
2672 * first part only as defined in the
2673 * parseJsonConfig function in lib/eslint.js
2675 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":'1'':/u);
2676 assert
.strictEqual(messages
[0].line
, 1);
2677 assert
.strictEqual(messages
[0].column
, 1);
2679 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2680 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
2681 assert
.include(messages
[1].nodeType
, "CallExpression");
2684 it("should report a violation", () => {
2685 const code
= "/*eslint no-alert:abc*/ alert('test');";
2687 const config
= { rules
: { "no-alert": 1 } };
2689 const messages
= linter
.verify(code
, config
, filename
);
2691 assert
.strictEqual(messages
.length
, 2);
2694 * Incorrectly formatted comment threw error;
2695 * message from caught exception
2696 * may differ amongst UAs, so verifying
2697 * first part only as defined in the
2698 * parseJsonConfig function in lib/eslint.js
2700 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":abc':/u);
2701 assert
.strictEqual(messages
[0].line
, 1);
2702 assert
.strictEqual(messages
[0].column
, 1);
2704 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2705 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
2706 assert
.include(messages
[1].nodeType
, "CallExpression");
2709 it("should report a violation", () => {
2710 const code
= "/*eslint no-alert:0 2*/ alert('test');";
2712 const config
= { rules
: { "no-alert": 1 } };
2714 const messages
= linter
.verify(code
, config
, filename
);
2716 assert
.strictEqual(messages
.length
, 2);
2719 * Incorrectly formatted comment threw error;
2720 * message from caught exception
2721 * may differ amongst UAs, so verifying
2722 * first part only as defined in the
2723 * parseJsonConfig function in lib/eslint.js
2725 assert
.match(messages
[0].message
, /^Failed to parse JSON from ' "no-alert":0 2':/u);
2726 assert
.strictEqual(messages
[0].line
, 1);
2727 assert
.strictEqual(messages
[0].column
, 1);
2729 assert
.strictEqual(messages
[1].ruleId
, "no-alert");
2730 assert
.strictEqual(messages
[1].message
, "Unexpected alert.");
2731 assert
.include(messages
[1].nodeType
, "CallExpression");
2735 describe("when evaluating code with comments which have colon in its value", () => {
2736 const code
= String
.raw
`
2737 /* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */
2741 it("should not parse errors, should report a violation", () => {
2742 const messages
= linter
.verify(code
, {}, filename
);
2744 assert
.strictEqual(messages
.length
, 1);
2745 assert
.strictEqual(messages
[0].ruleId
, "max-len");
2746 assert
.strictEqual(messages
[0].message
, "This line has a length of 129. Maximum allowed is 100.");
2747 assert
.include(messages
[0].nodeType
, "Program");
2751 describe("when evaluating code with comments that contain escape sequences", () => {
2752 const code
= String
.raw
`
2753 /* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */
2754 console.log("test");
2755 consolexlog("test2");
2759 it("should validate correctly", () => {
2760 const config
= { rules
: {} };
2761 const messages
= linter
.verify(code
, config
, filename
);
2762 const [message1
, message2
] = messages
;
2764 assert
.strictEqual(messages
.length
, 2);
2765 assert
.strictEqual(message1
.ruleId
, "max-len");
2766 assert
.strictEqual(message1
.message
, "This line has a length of 21. Maximum allowed is 1.");
2767 assert
.strictEqual(message1
.line
, 4);
2768 assert
.strictEqual(message1
.column
, 1);
2769 assert
.include(message1
.nodeType
, "Program");
2770 assert
.strictEqual(message2
.ruleId
, "max-len");
2771 assert
.strictEqual(message2
.message
, "This line has a length of 16. Maximum allowed is 1.");
2772 assert
.strictEqual(message2
.line
, 5);
2773 assert
.strictEqual(message2
.column
, 1);
2774 assert
.include(message2
.nodeType
, "Program");
2778 describe("when evaluating a file with a shebang", () => {
2779 const code
= "#!bin/program\n\nvar foo;;";
2781 it("should preserve line numbers", () => {
2782 const config
= { rules
: { "no-extra-semi": 1 } };
2783 const messages
= linter
.verify(code
, config
);
2785 assert
.strictEqual(messages
.length
, 1);
2786 assert
.strictEqual(messages
[0].ruleId
, "no-extra-semi");
2787 assert
.strictEqual(messages
[0].nodeType
, "EmptyStatement");
2788 assert
.strictEqual(messages
[0].line
, 3);
2791 it("should have a comment with the shebang in it", () => {
2792 const config
= { rules
: { checker
: "error" } };
2793 const spy
= sinon
.spy(context
=> {
2794 const comments
= context
.getAllComments();
2796 assert
.strictEqual(comments
.length
, 1);
2797 assert
.strictEqual(comments
[0].type
, "Shebang");
2801 linter
.defineRule("checker", spy
);
2802 linter
.verify(code
, config
);
2803 assert(spy
.calledOnce
);
2807 describe("when evaluating broken code", () => {
2808 const code
= BROKEN_TEST_CODE
;
2810 it("should report a violation with a useful parse error prefix", () => {
2811 const messages
= linter
.verify(code
);
2813 assert
.strictEqual(messages
.length
, 1);
2814 assert
.strictEqual(messages
[0].severity
, 2);
2815 assert
.isNull(messages
[0].ruleId
);
2816 assert
.strictEqual(messages
[0].line
, 1);
2817 assert
.strictEqual(messages
[0].column
, 4);
2818 assert
.isTrue(messages
[0].fatal
);
2819 assert
.match(messages
[0].message
, /^Parsing error:/u);
2822 it("should report source code where the issue is present", () => {
2823 const inValidCode
= [
2829 const messages
= linter
.verify(inValidCode
.join("\n"));
2831 assert
.strictEqual(messages
.length
, 1);
2832 assert
.strictEqual(messages
[0].severity
, 2);
2833 assert
.isTrue(messages
[0].fatal
);
2834 assert
.match(messages
[0].message
, /^Parsing error:/u);
2838 describe("when using an invalid (undefined) rule", () => {
2839 linter
= new Linter();
2841 const code
= TEST_CODE
;
2842 const results
= linter
.verify(code
, { rules
: { foobar
: 2 } });
2843 const result
= results
[0];
2844 const warningResult
= linter
.verify(code
, { rules
: { foobar
: 1 } })[0];
2845 const arrayOptionResults
= linter
.verify(code
, { rules
: { foobar
: [2, "always"] } });
2846 const objectOptionResults
= linter
.verify(code
, { rules
: { foobar
: [1, { bar
: false }] } });
2847 const resultsMultiple
= linter
.verify(code
, { rules
: { foobar
: 2, barfoo
: 1 } });
2849 it("should report a problem", () => {
2850 assert
.isNotNull(result
);
2851 assert
.isArray(results
);
2852 assert
.isObject(result
);
2853 assert
.property(result
, "ruleId");
2854 assert
.strictEqual(result
.ruleId
, "foobar");
2857 it("should report that the rule does not exist", () => {
2858 assert
.property(result
, "message");
2859 assert
.strictEqual(result
.message
, "Definition for rule 'foobar' was not found.");
2862 it("should report at the correct severity", () => {
2863 assert
.property(result
, "severity");
2864 assert
.strictEqual(result
.severity
, 2);
2865 assert
.strictEqual(warningResult
.severity
, 2); // this is 2, since the rulename is very likely to be wrong
2868 it("should accept any valid rule configuration", () => {
2869 assert
.isObject(arrayOptionResults
[0]);
2870 assert
.isObject(objectOptionResults
[0]);
2873 it("should report multiple missing rules", () => {
2874 assert
.isArray(resultsMultiple
);
2876 assert
.deepStrictEqual(
2880 message
: "Definition for rule 'barfoo' was not found.",
2892 describe("when using a rule which has been replaced", () => {
2893 const code
= TEST_CODE
;
2894 const results
= linter
.verify(code
, { rules
: { "no-comma-dangle": 2 } });
2896 it("should report the new rule", () => {
2897 assert
.strictEqual(results
[0].ruleId
, "no-comma-dangle");
2898 assert
.strictEqual(results
[0].message
, "Rule 'no-comma-dangle' was removed and replaced by: comma-dangle");
2902 describe("when calling getRules", () => {
2903 it("should return all loaded rules", () => {
2904 const rules
= linter
.getRules();
2906 assert
.isAbove(rules
.size
, 230);
2907 assert
.isObject(rules
.get("no-alert"));
2911 describe("when calling version", () => {
2912 it("should return current version number", () => {
2913 const version
= linter
.version
;
2915 assert
.isString(version
);
2916 assert
.isTrue(parseInt(version
[0], 10) >= 3);
2920 describe("when evaluating an empty string", () => {
2921 it("runs rules", () => {
2922 linter
.defineRule("no-programs", context
=> ({
2924 context
.report({ node
, message
: "No programs allowed." });
2929 linter
.verify("", { rules
: { "no-programs": "error" } }).length
,
2935 describe("when evaluating code without comments to environment", () => {
2936 it("should report a violation when using typed array", () => {
2937 const code
= "var array = new Uint8Array();";
2939 const config
= { rules
: { "no-undef": 1 } };
2941 const messages
= linter
.verify(code
, config
, filename
);
2943 assert
.strictEqual(messages
.length
, 1);
2946 it("should report a violation when using Promise", () => {
2947 const code
= "new Promise();";
2949 const config
= { rules
: { "no-undef": 1 } };
2951 const messages
= linter
.verify(code
, config
, filename
);
2953 assert
.strictEqual(messages
.length
, 1);
2957 describe("when evaluating code with comments to environment", () => {
2958 it("should not support legacy config", () => {
2959 const code
= "/*jshint mocha:true */ describe();";
2961 const config
= { rules
: { "no-undef": 1 } };
2963 const messages
= linter
.verify(code
, config
, filename
);
2965 assert
.strictEqual(messages
.length
, 1);
2966 assert
.strictEqual(messages
[0].ruleId
, "no-undef");
2967 assert
.strictEqual(messages
[0].nodeType
, "Identifier");
2968 assert
.strictEqual(messages
[0].line
, 1);
2971 it("should not report a violation", () => {
2972 const code
= "/*eslint-env es6 */ new Promise();";
2974 const config
= { rules
: { "no-undef": 1 } };
2976 const messages
= linter
.verify(code
, config
, filename
);
2978 assert
.strictEqual(messages
.length
, 0);
2981 it("should not report a violation", () => {
2982 const code
= `/*${ESLINT_ENV} mocha,node */ require();describe();`;
2984 const config
= { rules
: { "no-undef": 1 } };
2986 const messages
= linter
.verify(code
, config
, filename
);
2988 assert
.strictEqual(messages
.length
, 0);
2991 it("should not report a violation", () => {
2992 const code
= "/*eslint-env mocha */ suite();test();";
2994 const config
= { rules
: { "no-undef": 1 } };
2996 const messages
= linter
.verify(code
, config
, filename
);
2998 assert
.strictEqual(messages
.length
, 0);
3001 it("should not report a violation", () => {
3002 const code
= `/*${ESLINT_ENV} amd */ define();require();`;
3004 const config
= { rules
: { "no-undef": 1 } };
3006 const messages
= linter
.verify(code
, config
, filename
);
3008 assert
.strictEqual(messages
.length
, 0);
3011 it("should not report a violation", () => {
3012 const code
= `/*${ESLINT_ENV} jasmine */ expect();spyOn();`;
3014 const config
= { rules
: { "no-undef": 1 } };
3016 const messages
= linter
.verify(code
, config
, filename
);
3018 assert
.strictEqual(messages
.length
, 0);
3021 it("should not report a violation", () => {
3022 const code
= `/*globals require: true */ /*${ESLINT_ENV} node */ require = 1;`;
3024 const config
= { rules
: { "no-undef": 1 } };
3026 const messages
= linter
.verify(code
, config
, filename
);
3028 assert
.strictEqual(messages
.length
, 0);
3031 it("should not report a violation", () => {
3032 const code
= `/*${ESLINT_ENV} node */ process.exit();`;
3034 const config
= { rules
: {} };
3036 const messages
= linter
.verify(code
, config
, filename
);
3038 assert
.strictEqual(messages
.length
, 0);
3041 it("should not report a violation", () => {
3042 const code
= `/*eslint no-process-exit: 0 */ /*${ESLINT_ENV} node */ process.exit();`;
3044 const config
= { rules
: { "no-undef": 1 } };
3046 const messages
= linter
.verify(code
, config
, filename
);
3048 assert
.strictEqual(messages
.length
, 0);
3052 describe("when evaluating code with comments to change config when allowInlineConfig is enabled", () => {
3053 it("should report a violation for disabling rules", () => {
3055 "alert('test'); // eslint-disable-line no-alert"
3063 const messages
= linter
.verify(code
, config
, {
3065 allowInlineConfig
: false
3068 assert
.strictEqual(messages
.length
, 1);
3069 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3072 it("should report a violation for global variable declarations", () => {
3083 linter
.defineRules({
3087 const scope
= context
.getScope();
3088 const sourceCode
= context
.getSourceCode();
3089 const comments
= sourceCode
.getAllComments();
3091 assert
.strictEqual(1, comments
.length
);
3093 const foo
= getVariable(scope
, "foo");
3103 linter
.verify(code
, config
, { allowInlineConfig
: false });
3107 it("should report a violation for eslint-disable", () => {
3109 "/* eslint-disable */",
3118 const messages
= linter
.verify(code
, config
, {
3120 allowInlineConfig
: false
3123 assert
.strictEqual(messages
.length
, 1);
3124 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3127 it("should not report a violation for rule changes", () => {
3129 "/*eslint no-alert:2*/",
3138 const messages
= linter
.verify(code
, config
, {
3140 allowInlineConfig
: false
3143 assert
.strictEqual(messages
.length
, 0);
3146 it("should report a violation for disable-line", () => {
3148 "alert('test'); // eslint-disable-line"
3156 const messages
= linter
.verify(code
, config
, {
3158 allowInlineConfig
: false
3161 assert
.strictEqual(messages
.length
, 1);
3162 assert
.strictEqual(messages
[0].ruleId
, "no-alert");
3165 it("should report a violation for env changes", () => {
3167 `/*${ESLINT_ENV} browser*/ window`
3174 const messages
= linter
.verify(code
, config
, { allowInlineConfig
: false });
3176 assert
.strictEqual(messages
.length
, 1);
3177 assert
.strictEqual(messages
[0].ruleId
, "no-undef");
3181 describe("when evaluating code with 'noInlineComment'", () => {
3182 for (const directive
of [
3186 "eslint eqeqeq: error",
3187 "eslint-disable eqeqeq",
3188 "eslint-disable-line eqeqeq",
3189 "eslint-disable-next-line eqeqeq",
3190 "eslint-enable eqeqeq",
3193 // eslint-disable-next-line no-loop-func
3194 it(`should warn '/* ${directive} */' if 'noInlineConfig' was given.`, () => {
3195 const messages
= linter
.verify(`/* ${directive} */`, { noInlineConfig
: true });
3197 assert
.deepStrictEqual(messages
.length
, 1);
3198 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
3199 assert
.deepStrictEqual(messages
[0].ruleId
, null);
3200 assert
.deepStrictEqual(messages
[0].severity
, 1);
3201 assert
.deepStrictEqual(messages
[0].message
, `'/*${directive.split(" ")[0]}*/' has no effect because you have 'noInlineConfig' setting in your config.`);
3205 for (const directive
of [
3206 "eslint-disable-line eqeqeq",
3207 "eslint-disable-next-line eqeqeq"
3209 // eslint-disable-next-line no-loop-func
3210 it(`should warn '// ${directive}' if 'noInlineConfig' was given.`, () => {
3211 const messages
= linter
.verify(`// ${directive}`, { noInlineConfig
: true });
3213 assert
.deepStrictEqual(messages
.length
, 1);
3214 assert
.deepStrictEqual(messages
[0].fatal
, void 0);
3215 assert
.deepStrictEqual(messages
[0].ruleId
, null);
3216 assert
.deepStrictEqual(messages
[0].severity
, 1);
3217 assert
.deepStrictEqual(messages
[0].message
, `'//${directive.split(" ")[0]}' has no effect because you have 'noInlineConfig' setting in your config.`);
3221 it("should not warn if 'noInlineConfig' and '--no-inline-config' were given.", () => {
3222 const messages
= linter
.verify("/* globals foo */", { noInlineConfig
: true }, { allowInlineConfig
: false });
3224 assert
.deepStrictEqual(messages
.length
, 0);
3228 describe("when receiving cwd in options during instantiation", () => {
3229 const code
= "a;\nb;";
3230 const config
= { rules
: { checker
: "error" } };
3232 it("should get cwd correctly in the context", () => {
3234 const linterWithOption
= new Linter({ cwd
});
3237 linterWithOption
.defineRule("checker", context
=> {
3238 spy
= sinon
.spy(() => {
3239 assert
.strictEqual(context
.getCwd(), cwd
);
3241 return { Program
: spy
};
3244 linterWithOption
.verify(code
, config
);
3245 assert(spy
&& spy
.calledOnce
);
3248 it("should assign process.cwd() to it if cwd is undefined", () => {
3250 const linterWithOption
= new Linter({ });
3252 linterWithOption
.defineRule("checker", context
=> {
3254 spy
= sinon
.spy(() => {
3255 assert
.strictEqual(context
.getCwd(), process
.cwd());
3257 return { Program
: spy
};
3260 linterWithOption
.verify(code
, config
);
3261 assert(spy
&& spy
.calledOnce
);
3264 it("should assign process.cwd() to it if the option is undefined", () => {
3267 linter
.defineRule("checker", context
=> {
3269 spy
= sinon
.spy(() => {
3270 assert
.strictEqual(context
.getCwd(), process
.cwd());
3272 return { Program
: spy
};
3275 linter
.verify(code
, config
);
3276 assert(spy
&& spy
.calledOnce
);
3280 describe("reportUnusedDisable option", () => {
3281 it("reports problems for unused eslint-disable comments", () => {
3282 assert
.deepStrictEqual(
3283 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: true }),
3287 message
: "Unused eslint-disable directive (no problems were reported).",
3297 it("reports problems for unused eslint-disable comments (error)", () => {
3298 assert
.deepStrictEqual(
3299 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "error" }),
3303 message
: "Unused eslint-disable directive (no problems were reported).",
3313 it("reports problems for unused eslint-disable comments (warn)", () => {
3314 assert
.deepStrictEqual(
3315 linter
.verify("/* eslint-disable */", {}, { reportUnusedDisableDirectives
: "warn" }),
3319 message
: "Unused eslint-disable directive (no problems were reported).",
3329 it("reports problems for unused eslint-disable comments (in config)", () => {
3330 assert
.deepStrictEqual(
3331 linter
.verify("/* eslint-disable */", { reportUnusedDisableDirectives
: true }),
3335 message
: "Unused eslint-disable directive (no problems were reported).",
3346 describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => {
3347 it("should not report a violation", () => {
3349 "alert('test'); // eslint-disable-line no-alert"
3357 const messages
= linter
.verify(code
, config
, {
3359 allowInlineConfig
: true
3362 assert
.strictEqual(messages
.length
, 0);
3366 describe("when evaluating code with hashbang", () => {
3367 it("should comment hashbang without breaking offset", () => {
3368 const code
= "#!/usr/bin/env node\n'123';";
3369 const config
= { rules
: { checker
: "error" } };
3372 linter
.defineRule("checker", context
=> {
3373 spy
= sinon
.spy(node
=> {
3374 assert
.strictEqual(context
.getSource(node
), "'123';");
3376 return { ExpressionStatement
: spy
};
3379 linter
.verify(code
, config
);
3380 assert(spy
&& spy
.calledOnce
);
3384 describe("verify()", () => {
3385 describe("filenames", () => {
3386 it("should allow filename to be passed on options object", () => {
3387 const filenameChecker
= sinon
.spy(context
=> {
3388 assert
.strictEqual(context
.getFilename(), "foo.js");
3392 linter
.defineRule("checker", filenameChecker
);
3393 linter
.defineRule("checker", filenameChecker
);
3394 linter
.verify("foo;", { rules
: { checker
: "error" } }, { filename
: "foo.js" });
3395 assert(filenameChecker
.calledOnce
);
3398 it("should allow filename to be passed as third argument", () => {
3399 const filenameChecker
= sinon
.spy(context
=> {
3400 assert
.strictEqual(context
.getFilename(), "bar.js");
3404 linter
.defineRule("checker", filenameChecker
);
3405 linter
.verify("foo;", { rules
: { checker
: "error" } }, "bar.js");
3406 assert(filenameChecker
.calledOnce
);
3409 it("should default filename to <input> when options object doesn't have filename", () => {
3410 const filenameChecker
= sinon
.spy(context
=> {
3411 assert
.strictEqual(context
.getFilename(), "<input>");
3415 linter
.defineRule("checker", filenameChecker
);
3416 linter
.verify("foo;", { rules
: { checker
: "error" } }, {});
3417 assert(filenameChecker
.calledOnce
);
3420 it("should default filename to <input> when only two arguments are passed", () => {
3421 const filenameChecker
= sinon
.spy(context
=> {
3422 assert
.strictEqual(context
.getFilename(), "<input>");
3426 linter
.defineRule("checker", filenameChecker
);
3427 linter
.verify("foo;", { rules
: { checker
: "error" } });
3428 assert(filenameChecker
.calledOnce
);
3432 it("should report warnings in order by line and column when called", () => {
3434 const code
= "foo()\n alert('test')";
3435 const config
= { rules
: { "no-mixed-spaces-and-tabs": 1, "eol-last": 1, semi
: [1, "always"] } };
3437 const messages
= linter
.verify(code
, config
, filename
);
3439 assert
.strictEqual(messages
.length
, 3);
3440 assert
.strictEqual(messages
[0].line
, 1);
3441 assert
.strictEqual(messages
[0].column
, 6);
3442 assert
.strictEqual(messages
[1].line
, 2);
3443 assert
.strictEqual(messages
[1].column
, 18);
3444 assert
.strictEqual(messages
[2].line
, 2);
3445 assert
.strictEqual(messages
[2].column
, 18);
3448 describe("ecmaVersion", () => {
3449 describe("it should properly parse let declaration when", () => {
3450 it("the ECMAScript version number is 6", () => {
3451 const messages
= linter
.verify("let x = 5;", {
3457 assert
.strictEqual(messages
.length
, 0);
3460 it("the ECMAScript version number is 2015", () => {
3461 const messages
= linter
.verify("let x = 5;", {
3467 assert
.strictEqual(messages
.length
, 0);
3471 it("should fail to parse exponentiation operator when the ECMAScript version number is 2015", () => {
3472 const messages
= linter
.verify("x ** y;", {
3478 assert
.strictEqual(messages
.length
, 1);
3481 describe("should properly parse exponentiation operator when", () => {
3482 it("the ECMAScript version number is 7", () => {
3483 const messages
= linter
.verify("x ** y;", {
3489 assert
.strictEqual(messages
.length
, 0);
3492 it("the ECMAScript version number is 2016", () => {
3493 const messages
= linter
.verify("x ** y;", {
3499 assert
.strictEqual(messages
.length
, 0);
3504 it("should properly parse object spread when ecmaVersion is 2018", () => {
3506 const messages
= linter
.verify("var x = { ...y };", {
3512 assert
.strictEqual(messages
.length
, 0);
3515 it("should properly parse global return when passed ecmaFeatures", () => {
3517 const messages
= linter
.verify("return;", {
3525 assert
.strictEqual(messages
.length
, 0);
3528 it("should properly parse global return when in Node.js environment", () => {
3530 const messages
= linter
.verify("return;", {
3536 assert
.strictEqual(messages
.length
, 0);
3539 it("should not parse global return when in Node.js environment with globalReturn explicitly off", () => {
3541 const messages
= linter
.verify("return;", {
3552 assert
.strictEqual(messages
.length
, 1);
3553 assert
.strictEqual(messages
[0].message
, "Parsing error: 'return' outside of function");
3556 it("should not parse global return when Node.js environment is false", () => {
3558 const messages
= linter
.verify("return;", {}, filename
);
3560 assert
.strictEqual(messages
.length
, 1);
3561 assert
.strictEqual(messages
[0].message
, "Parsing error: 'return' outside of function");
3564 it("should properly parse sloppy-mode code when impliedStrict is false", () => {
3566 const messages
= linter
.verify("var private;", {}, filename
);
3568 assert
.strictEqual(messages
.length
, 0);
3571 it("should not parse sloppy-mode code when impliedStrict is true", () => {
3573 const messages
= linter
.verify("var private;", {
3581 assert
.strictEqual(messages
.length
, 1);
3582 assert
.strictEqual(messages
[0].message
, "Parsing error: The keyword 'private' is reserved");
3585 it("should properly parse valid code when impliedStrict is true", () => {
3587 const messages
= linter
.verify("var foo;", {
3595 assert
.strictEqual(messages
.length
, 0);
3598 it("should properly parse JSX when passed ecmaFeatures", () => {
3600 const messages
= linter
.verify("var x = <div/>;", {
3608 assert
.strictEqual(messages
.length
, 0);
3611 it("should report an error when JSX code is encountered and JSX is not enabled", () => {
3612 const code
= "var myDivElement = <div className=\"foo\" />;";
3613 const messages
= linter
.verify(code
, {}, "filename");
3615 assert
.strictEqual(messages
.length
, 1);
3616 assert
.strictEqual(messages
[0].line
, 1);
3617 assert
.strictEqual(messages
[0].column
, 20);
3618 assert
.strictEqual(messages
[0].message
, "Parsing error: Unexpected token <");
3621 it("should not report an error when JSX code is encountered and JSX is enabled", () => {
3622 const code
= "var myDivElement = <div className=\"foo\" />;";
3623 const messages
= linter
.verify(code
, { parserOptions
: { ecmaFeatures
: { jsx
: true } } }, "filename");
3625 assert
.strictEqual(messages
.length
, 0);
3628 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
3629 const code
= "var myDivElement = <div {...this.props} />;";
3630 const messages
= linter
.verify(code
, { parserOptions
: { ecmaVersion
: 6, ecmaFeatures
: { jsx
: true } } }, "filename");
3632 assert
.strictEqual(messages
.length
, 0);
3635 it("should be able to use es6 features if there is a comment which has \"eslint-env es6\"", () => {
3637 "/* eslint-env es6 */",
3638 "var arrow = () => 0;",
3639 "var binary = 0b1010;",
3640 "{ let a = 0; const b = 1; }",
3642 "function defaultParams(a = 0) {}",
3643 "var {a = 1, b = 2} = {};",
3644 "for (var a of []) {}",
3645 "function* generator() { yield 0; }",
3646 "var computed = {[a]: 0};",
3647 "var duplicate = {dup: 0, dup: 1};",
3648 "var method = {foo() {}};",
3649 "var property = {a, b};",
3650 "var octal = 0o755;",
3651 "var u = /^.$/u.test('ð ®·');",
3652 "var y = /hello/y.test('hello');",
3653 "function restParam(a, ...rest) {}",
3654 "class B { superInFunc() { super.foo(); } }",
3655 "var template = `hello, ${a}`;",
3656 "var unicode = '\\u{20BB7}';"
3659 const messages
= linter
.verify(code
, null, "eslint-env es6");
3661 assert
.strictEqual(messages
.length
, 0);
3664 it("should be able to return in global if there is a comment which enables the node environment with a comment", () => {
3665 const messages
= linter
.verify(`/* ${ESLINT_ENV} node */ return;`, null, "node environment");
3667 assert
.strictEqual(messages
.length
, 0);
3670 it("should attach a \"/*global\" comment node to declared variables", () => {
3671 const code
= "/* global foo */\n/* global bar, baz */";
3674 linter
.defineRules({
3678 const scope
= context
.getScope();
3679 const sourceCode
= context
.getSourceCode();
3680 const comments
= sourceCode
.getAllComments();
3682 assert
.strictEqual(2, comments
.length
);
3684 const foo
= getVariable(scope
, "foo");
3686 assert
.strictEqual(foo
.eslintExplicitGlobal
, true);
3687 assert
.strictEqual(foo
.eslintExplicitGlobalComments
[0], comments
[0]);
3689 const bar
= getVariable(scope
, "bar");
3691 assert
.strictEqual(bar
.eslintExplicitGlobal
, true);
3692 assert
.strictEqual(bar
.eslintExplicitGlobalComments
[0], comments
[1]);
3694 const baz
= getVariable(scope
, "baz");
3696 assert
.strictEqual(baz
.eslintExplicitGlobal
, true);
3697 assert
.strictEqual(baz
.eslintExplicitGlobalComments
[0], comments
[1]);
3705 linter
.verify(code
, { rules
: { test
: 2 } });
3709 it("should report a linting error when a global is set to an invalid value", () => {
3710 const results
= linter
.verify("/* global foo: AAAAA, bar: readonly */\nfoo;\nbar;", { rules
: { "no-undef": "error" } });
3712 assert
.deepStrictEqual(results
, [
3716 message
: "'AAAAA' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')",
3727 message
: "'foo' is not defined.",
3732 nodeType
: "Identifier"
3737 it("should not crash when we reuse the SourceCode object", () => {
3738 linter
.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions
: { ecmaVersion
: 6, ecmaFeatures
: { jsx
: true } } });
3739 linter
.verify(linter
.getSourceCode(), { parserOptions
: { ecmaVersion
: 6, ecmaFeatures
: { jsx
: true } } });
3742 it("should reuse the SourceCode object", () => {
3746 linter
.defineRule("save-ast1", () => ({
3751 linter
.defineRule("save-ast2", () => ({
3757 linter
.verify("function render() { return <div className='test'>{hello}</div> }", { parserOptions
: { ecmaVersion
: 6, ecmaFeatures
: { jsx
: true } }, rules
: { "save-ast1": 2 } });
3758 linter
.verify(linter
.getSourceCode(), { parserOptions
: { ecmaVersion
: 6, ecmaFeatures
: { jsx
: true } }, rules
: { "save-ast2": 2 } });
3760 assert(ast1
!== null);
3761 assert(ast2
!== null);
3762 assert(ast1
=== ast2
);
3765 it("should allow 'await' as a property name in modules", () => {
3766 const result
= linter
.verify(
3768 { parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } }
3771 assert(result
.length
=== 0);
3775 it("should not modify config object passed as argument", () => {
3778 Object
.freeze(config
);
3779 linter
.verify("var", config
);
3782 it("should pass 'id' to rule contexts with the rule id", () => {
3783 const spy
= sinon
.spy(context
=> {
3784 assert
.strictEqual(context
.id
, "foo-bar-baz");
3788 linter
.defineRule("foo-bar-baz", spy
);
3789 linter
.verify("x", { rules
: { "foo-bar-baz": "error" } });
3790 assert(spy
.calledOnce
);
3793 describe("descriptions in directive comments", () => {
3794 it("should ignore the part preceded by '--' in '/*eslint*/'.", () => {
3795 const aaa
= sinon
.stub().returns({});
3796 const bbb
= sinon
.stub().returns({});
3798 linter
.defineRule("aaa", { create
: aaa
});
3799 linter
.defineRule("bbb", { create
: bbb
});
3800 const messages
= linter
.verify(`
3801 /*eslint aaa:error -- bbb:error */
3802 console.log("hello")
3805 // Don't include syntax error of the comment.
3806 assert
.deepStrictEqual(messages
, []);
3809 assert
.strictEqual(aaa
.callCount
, 1);
3810 assert
.strictEqual(bbb
.callCount
, 0);
3813 it("should ignore the part preceded by '--' in '/*eslint-env*/'.", () => {
3814 const messages
= linter
.verify(`
3815 /*eslint-env es2015 -- es2017 */
3818 `, { rules
: { "no-redeclare": "error" } });
3820 // Don't include `Atomics`
3821 assert
.deepStrictEqual(
3828 message
: "'Promise' is already defined as a built-in global variable.",
3829 messageId
: "redeclaredAsBuiltin",
3830 nodeType
: "Identifier",
3831 ruleId
: "no-redeclare",
3837 it("should ignore the part preceded by '--' in '/*global*/'.", () => {
3838 const messages
= linter
.verify(`
3839 /*global aaa -- bbb */
3842 `, { rules
: { "no-redeclare": "error" } });
3844 // Don't include `bbb`
3845 assert
.deepStrictEqual(
3852 message
: "'aaa' is already defined by a variable declaration.",
3853 messageId
: "redeclaredBySyntax",
3855 ruleId
: "no-redeclare",
3861 it("should ignore the part preceded by '--' in '/*globals*/'.", () => {
3862 const messages
= linter
.verify(`
3863 /*globals aaa -- bbb */
3866 `, { rules
: { "no-redeclare": "error" } });
3868 // Don't include `bbb`
3869 assert
.deepStrictEqual(
3876 message
: "'aaa' is already defined by a variable declaration.",
3877 messageId
: "redeclaredBySyntax",
3879 ruleId
: "no-redeclare",
3885 it("should ignore the part preceded by '--' in '/*exported*/'.", () => {
3886 const messages
= linter
.verify(`
3887 /*exported aaa -- bbb */
3890 `, { rules
: { "no-unused-vars": "error" } });
3892 // Don't include `aaa`
3893 assert
.deepStrictEqual(
3900 message
: "'bbb' is assigned a value but never used.",
3901 messageId
: "unusedVar",
3902 nodeType
: "Identifier",
3903 ruleId
: "no-unused-vars",
3909 it("should ignore the part preceded by '--' in '/*eslint-disable*/'.", () => {
3910 const messages
= linter
.verify(`
3911 /*eslint-disable no-redeclare -- no-unused-vars */
3914 `, { rules
: { "no-redeclare": "error", "no-unused-vars": "error" } });
3916 // Do include `no-unused-vars` but not `no-redeclare`
3917 assert
.deepStrictEqual(
3924 message
: "'aaa' is assigned a value but never used.",
3925 messageId
: "unusedVar",
3926 nodeType
: "Identifier",
3927 ruleId
: "no-unused-vars",
3933 it("should ignore the part preceded by '--' in '/*eslint-enable*/'.", () => {
3934 const messages
= linter
.verify(`
3935 /*eslint-disable no-redeclare, no-unused-vars */
3936 /*eslint-enable no-redeclare -- no-unused-vars */
3939 `, { rules
: { "no-redeclare": "error", "no-unused-vars": "error" } });
3941 // Do include `no-redeclare` but not `no-unused-vars`
3942 assert
.deepStrictEqual(
3949 message
: "'aaa' is already defined.",
3950 messageId
: "redeclared",
3951 nodeType
: "Identifier",
3952 ruleId
: "no-redeclare",
3958 it("should ignore the part preceded by '--' in '//eslint-disable-line'.", () => {
3959 const messages
= linter
.verify(`
3960 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
3961 var aaa = {} //eslint-disable-line no-redeclare -- no-unused-vars
3962 `, { rules
: { "no-redeclare": "error", "no-unused-vars": "error" } });
3964 // Do include `no-unused-vars` but not `no-redeclare`
3965 assert
.deepStrictEqual(
3972 message
: "'aaa' is assigned a value but never used.",
3973 messageId
: "unusedVar",
3974 nodeType
: "Identifier",
3975 ruleId
: "no-unused-vars",
3981 it("should ignore the part preceded by '--' in '/*eslint-disable-line*/'.", () => {
3982 const messages
= linter
.verify(`
3983 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
3984 var aaa = {} /*eslint-disable-line no-redeclare -- no-unused-vars */
3985 `, { rules
: { "no-redeclare": "error", "no-unused-vars": "error" } });
3987 // Do include `no-unused-vars` but not `no-redeclare`
3988 assert
.deepStrictEqual(
3995 message
: "'aaa' is assigned a value but never used.",
3996 messageId
: "unusedVar",
3997 nodeType
: "Identifier",
3998 ruleId
: "no-unused-vars",
4004 it("should ignore the part preceded by '--' in '//eslint-disable-next-line'.", () => {
4005 const messages
= linter
.verify(`
4006 //eslint-disable-next-line no-redeclare -- no-unused-vars
4008 //eslint-disable-next-line no-redeclare -- no-unused-vars
4010 `, { rules
: { "no-redeclare": "error", "no-unused-vars": "error" } });
4012 // Do include `no-unused-vars` but not `no-redeclare`
4013 assert
.deepStrictEqual(
4020 message
: "'aaa' is assigned a value but never used.",
4021 messageId
: "unusedVar",
4022 nodeType
: "Identifier",
4023 ruleId
: "no-unused-vars",
4029 it("should ignore the part preceded by '--' in '/*eslint-disable-next-line*/'.", () => {
4030 const messages
= linter
.verify(`
4031 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
4033 /*eslint-disable-next-line no-redeclare -- no-unused-vars */
4035 `, { rules
: { "no-redeclare": "error", "no-unused-vars": "error" } });
4037 // Do include `no-unused-vars` but not `no-redeclare`
4038 assert
.deepStrictEqual(
4045 message
: "'aaa' is assigned a value but never used.",
4046 messageId
: "unusedVar",
4047 nodeType
: "Identifier",
4048 ruleId
: "no-unused-vars",
4054 it("should not ignore the part preceded by '--' if the '--' is not surrounded by whitespaces.", () => {
4055 const rule
= sinon
.stub().returns({});
4057 linter
.defineRule("a--rule", { create
: rule
});
4058 const messages
= linter
.verify(`
4059 /*eslint a--rule:error */
4060 console.log("hello")
4063 // Don't include syntax error of the comment.
4064 assert
.deepStrictEqual(messages
, []);
4067 assert
.strictEqual(rule
.callCount
, 1);
4070 it("should ignore the part preceded by '--' even if the '--' is longer than 2.", () => {
4071 const aaa
= sinon
.stub().returns({});
4072 const bbb
= sinon
.stub().returns({});
4074 linter
.defineRule("aaa", { create
: aaa
});
4075 linter
.defineRule("bbb", { create
: bbb
});
4076 const messages
= linter
.verify(`
4077 /*eslint aaa:error -------- bbb:error */
4078 console.log("hello")
4081 // Don't include syntax error of the comment.
4082 assert
.deepStrictEqual(messages
, []);
4085 assert
.strictEqual(aaa
.callCount
, 1);
4086 assert
.strictEqual(bbb
.callCount
, 0);
4089 it("should ignore the part preceded by '--' with line breaks.", () => {
4090 const aaa
= sinon
.stub().returns({});
4091 const bbb
= sinon
.stub().returns({});
4093 linter
.defineRule("aaa", { create
: aaa
});
4094 linter
.defineRule("bbb", { create
: bbb
});
4095 const messages
= linter
.verify(`
4099 console.log("hello")
4102 // Don't include syntax error of the comment.
4103 assert
.deepStrictEqual(messages
, []);
4106 assert
.strictEqual(aaa
.callCount
, 1);
4107 assert
.strictEqual(bbb
.callCount
, 0);
4112 describe("context.getScope()", () => {
4115 * Get the scope on the node `astSelector` specified.
4116 * @param {string} code The source code to verify.
4117 * @param {string} astSelector The AST selector to get scope.
4118 * @param {number} [ecmaVersion=5] The ECMAScript version.
4119 * @returns {{node: ASTNode, scope: escope.Scope}} Gotten scope.
4121 function getScope(code
, astSelector
, ecmaVersion
= 5) {
4124 linter
.defineRule("get-scope", context
=> ({
4125 [astSelector
](node0
) {
4127 scope
= context
.getScope();
4133 parserOptions
: { ecmaVersion
},
4134 rules
: { "get-scope": 2 }
4138 return { node
, scope
};
4141 it("should return 'function' scope on FunctionDeclaration (ES5)", () => {
4142 const { node
, scope
} = getScope("function f() {}", "FunctionDeclaration");
4144 assert
.strictEqual(scope
.type
, "function");
4145 assert
.strictEqual(scope
.block
, node
);
4148 it("should return 'function' scope on FunctionExpression (ES5)", () => {
4149 const { node
, scope
} = getScope("!function f() {}", "FunctionExpression");
4151 assert
.strictEqual(scope
.type
, "function");
4152 assert
.strictEqual(scope
.block
, node
);
4155 it("should return 'function' scope on the body of FunctionDeclaration (ES5)", () => {
4156 const { node
, scope
} = getScope("function f() {}", "BlockStatement");
4158 assert
.strictEqual(scope
.type
, "function");
4159 assert
.strictEqual(scope
.block
, node
.parent
);
4162 it("should return 'function' scope on the body of FunctionDeclaration (ES2015)", () => {
4163 const { node
, scope
} = getScope("function f() {}", "BlockStatement", 2015);
4165 assert
.strictEqual(scope
.type
, "function");
4166 assert
.strictEqual(scope
.block
, node
.parent
);
4169 it("should return 'function' scope on BlockStatement in functions (ES5)", () => {
4170 const { node
, scope
} = getScope("function f() { { var b; } }", "BlockStatement > BlockStatement");
4172 assert
.strictEqual(scope
.type
, "function");
4173 assert
.strictEqual(scope
.block
, node
.parent
.parent
);
4174 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["arguments", "b"]);
4177 it("should return 'block' scope on BlockStatement in functions (ES2015)", () => {
4178 const { node
, scope
} = getScope("function f() { { let a; var b; } }", "BlockStatement > BlockStatement", 2015);
4180 assert
.strictEqual(scope
.type
, "block");
4181 assert
.strictEqual(scope
.upper
.type
, "function");
4182 assert
.strictEqual(scope
.block
, node
);
4183 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["a"]);
4184 assert
.deepStrictEqual(scope
.variableScope
.variables
.map(v
=> v
.name
), ["arguments", "b"]);
4187 it("should return 'block' scope on nested BlockStatement in functions (ES2015)", () => {
4188 const { node
, scope
} = getScope("function f() { { let a; { let b; var c; } } }", "BlockStatement > BlockStatement > BlockStatement", 2015);
4190 assert
.strictEqual(scope
.type
, "block");
4191 assert
.strictEqual(scope
.upper
.type
, "block");
4192 assert
.strictEqual(scope
.upper
.upper
.type
, "function");
4193 assert
.strictEqual(scope
.block
, node
);
4194 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["b"]);
4195 assert
.deepStrictEqual(scope
.upper
.variables
.map(v
=> v
.name
), ["a"]);
4196 assert
.deepStrictEqual(scope
.variableScope
.variables
.map(v
=> v
.name
), ["arguments", "c"]);
4199 it("should return 'function' scope on SwitchStatement in functions (ES5)", () => {
4200 const { node
, scope
} = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchStatement");
4202 assert
.strictEqual(scope
.type
, "function");
4203 assert
.strictEqual(scope
.block
, node
.parent
.parent
);
4204 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["arguments", "b"]);
4207 it("should return 'switch' scope on SwitchStatement in functions (ES2015)", () => {
4208 const { node
, scope
} = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchStatement", 2015);
4210 assert
.strictEqual(scope
.type
, "switch");
4211 assert
.strictEqual(scope
.block
, node
);
4212 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["b"]);
4215 it("should return 'function' scope on SwitchCase in functions (ES5)", () => {
4216 const { node
, scope
} = getScope("function f() { switch (a) { case 0: var b; } }", "SwitchCase");
4218 assert
.strictEqual(scope
.type
, "function");
4219 assert
.strictEqual(scope
.block
, node
.parent
.parent
.parent
);
4220 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["arguments", "b"]);
4223 it("should return 'switch' scope on SwitchCase in functions (ES2015)", () => {
4224 const { node
, scope
} = getScope("function f() { switch (a) { case 0: let b; } }", "SwitchCase", 2015);
4226 assert
.strictEqual(scope
.type
, "switch");
4227 assert
.strictEqual(scope
.block
, node
.parent
);
4228 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["b"]);
4231 it("should return 'catch' scope on CatchClause in functions (ES5)", () => {
4232 const { node
, scope
} = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause");
4234 assert
.strictEqual(scope
.type
, "catch");
4235 assert
.strictEqual(scope
.block
, node
);
4236 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["e"]);
4239 it("should return 'catch' scope on CatchClause in functions (ES2015)", () => {
4240 const { node
, scope
} = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause", 2015);
4242 assert
.strictEqual(scope
.type
, "catch");
4243 assert
.strictEqual(scope
.block
, node
);
4244 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["e"]);
4247 it("should return 'catch' scope on the block of CatchClause in functions (ES5)", () => {
4248 const { node
, scope
} = getScope("function f() { try {} catch (e) { var a; } }", "CatchClause > BlockStatement");
4250 assert
.strictEqual(scope
.type
, "catch");
4251 assert
.strictEqual(scope
.block
, node
.parent
);
4252 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["e"]);
4255 it("should return 'block' scope on the block of CatchClause in functions (ES2015)", () => {
4256 const { node
, scope
} = getScope("function f() { try {} catch (e) { let a; } }", "CatchClause > BlockStatement", 2015);
4258 assert
.strictEqual(scope
.type
, "block");
4259 assert
.strictEqual(scope
.block
, node
);
4260 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["a"]);
4263 it("should return 'function' scope on ForStatement in functions (ES5)", () => {
4264 const { node
, scope
} = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement");
4266 assert
.strictEqual(scope
.type
, "function");
4267 assert
.strictEqual(scope
.block
, node
.parent
.parent
);
4268 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["arguments", "i"]);
4271 it("should return 'for' scope on ForStatement in functions (ES2015)", () => {
4272 const { node
, scope
} = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement", 2015);
4274 assert
.strictEqual(scope
.type
, "for");
4275 assert
.strictEqual(scope
.block
, node
);
4276 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["i"]);
4279 it("should return 'function' scope on the block body of ForStatement in functions (ES5)", () => {
4280 const { node
, scope
} = getScope("function f() { for (var i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement");
4282 assert
.strictEqual(scope
.type
, "function");
4283 assert
.strictEqual(scope
.block
, node
.parent
.parent
.parent
);
4284 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["arguments", "i"]);
4287 it("should return 'block' scope on the block body of ForStatement in functions (ES2015)", () => {
4288 const { node
, scope
} = getScope("function f() { for (let i = 0; i < 10; ++i) {} }", "ForStatement > BlockStatement", 2015);
4290 assert
.strictEqual(scope
.type
, "block");
4291 assert
.strictEqual(scope
.upper
.type
, "for");
4292 assert
.strictEqual(scope
.block
, node
);
4293 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), []);
4294 assert
.deepStrictEqual(scope
.upper
.variables
.map(v
=> v
.name
), ["i"]);
4297 it("should return 'function' scope on ForInStatement in functions (ES5)", () => {
4298 const { node
, scope
} = getScope("function f() { for (var key in obj) {} }", "ForInStatement");
4300 assert
.strictEqual(scope
.type
, "function");
4301 assert
.strictEqual(scope
.block
, node
.parent
.parent
);
4302 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["arguments", "key"]);
4305 it("should return 'for' scope on ForInStatement in functions (ES2015)", () => {
4306 const { node
, scope
} = getScope("function f() { for (let key in obj) {} }", "ForInStatement", 2015);
4308 assert
.strictEqual(scope
.type
, "for");
4309 assert
.strictEqual(scope
.block
, node
);
4310 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["key"]);
4313 it("should return 'function' scope on the block body of ForInStatement in functions (ES5)", () => {
4314 const { node
, scope
} = getScope("function f() { for (var key in obj) {} }", "ForInStatement > BlockStatement");
4316 assert
.strictEqual(scope
.type
, "function");
4317 assert
.strictEqual(scope
.block
, node
.parent
.parent
.parent
);
4318 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["arguments", "key"]);
4321 it("should return 'block' scope on the block body of ForInStatement in functions (ES2015)", () => {
4322 const { node
, scope
} = getScope("function f() { for (let key in obj) {} }", "ForInStatement > BlockStatement", 2015);
4324 assert
.strictEqual(scope
.type
, "block");
4325 assert
.strictEqual(scope
.upper
.type
, "for");
4326 assert
.strictEqual(scope
.block
, node
);
4327 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), []);
4328 assert
.deepStrictEqual(scope
.upper
.variables
.map(v
=> v
.name
), ["key"]);
4331 it("should return 'for' scope on ForOfStatement in functions (ES2015)", () => {
4332 const { node
, scope
} = getScope("function f() { for (let x of xs) {} }", "ForOfStatement", 2015);
4334 assert
.strictEqual(scope
.type
, "for");
4335 assert
.strictEqual(scope
.block
, node
);
4336 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), ["x"]);
4339 it("should return 'block' scope on the block body of ForOfStatement in functions (ES2015)", () => {
4340 const { node
, scope
} = getScope("function f() { for (let x of xs) {} }", "ForOfStatement > BlockStatement", 2015);
4342 assert
.strictEqual(scope
.type
, "block");
4343 assert
.strictEqual(scope
.upper
.type
, "for");
4344 assert
.strictEqual(scope
.block
, node
);
4345 assert
.deepStrictEqual(scope
.variables
.map(v
=> v
.name
), []);
4346 assert
.deepStrictEqual(scope
.upper
.variables
.map(v
=> v
.name
), ["x"]);
4349 it("should shadow the same name variable by the iteration variable.", () => {
4350 const { node
, scope
} = getScope("let x; for (let x of x) {}", "ForOfStatement", 2015);
4352 assert
.strictEqual(scope
.type
, "for");
4353 assert
.strictEqual(scope
.upper
.type
, "global");
4354 assert
.strictEqual(scope
.block
, node
);
4355 assert
.strictEqual(scope
.upper
.variables
[0].references
.length
, 0);
4356 assert
.strictEqual(scope
.references
[0].identifier
, node
.left
.declarations
[0].id
);
4357 assert
.strictEqual(scope
.references
[1].identifier
, node
.right
);
4358 assert
.strictEqual(scope
.references
[1].resolved
, scope
.variables
[0]);
4362 describe("Variables and references", () => {
4365 "function foo() { b; }",
4380 linter
.defineRules({
4384 scope
= context
.getScope();
4390 linter
.verify(code
, { rules
: { test
: 2 }, globals
: { e
: true, f
: false } });
4398 it("Scope#through should contain references of undefined variables", () => {
4399 assert
.strictEqual(scope
.through
.length
, 2);
4400 assert
.strictEqual(scope
.through
[0].identifier
.name
, "a");
4401 assert
.strictEqual(scope
.through
[0].identifier
.loc
.start
.line
, 1);
4402 assert
.strictEqual(scope
.through
[0].resolved
, null);
4403 assert
.strictEqual(scope
.through
[1].identifier
.name
, "b");
4404 assert
.strictEqual(scope
.through
[1].identifier
.loc
.start
.line
, 2);
4405 assert
.strictEqual(scope
.through
[1].resolved
, null);
4408 it("Scope#variables should contain global variables", () => {
4409 assert(scope
.variables
.some(v
=> v
.name
=== "Object"));
4410 assert(scope
.variables
.some(v
=> v
.name
=== "foo"));
4411 assert(scope
.variables
.some(v
=> v
.name
=== "c"));
4412 assert(scope
.variables
.some(v
=> v
.name
=== "d"));
4413 assert(scope
.variables
.some(v
=> v
.name
=== "e"));
4414 assert(scope
.variables
.some(v
=> v
.name
=== "f"));
4417 it("Scope#set should contain global variables", () => {
4418 assert(scope
.set.get("Object"));
4419 assert(scope
.set.get("foo"));
4420 assert(scope
.set.get("c"));
4421 assert(scope
.set.get("d"));
4422 assert(scope
.set.get("e"));
4423 assert(scope
.set.get("f"));
4426 it("Variables#references should contain their references", () => {
4427 assert
.strictEqual(scope
.set.get("Object").references
.length
, 1);
4428 assert
.strictEqual(scope
.set.get("Object").references
[0].identifier
.name
, "Object");
4429 assert
.strictEqual(scope
.set.get("Object").references
[0].identifier
.loc
.start
.line
, 3);
4430 assert
.strictEqual(scope
.set.get("Object").references
[0].resolved
, scope
.set.get("Object"));
4431 assert
.strictEqual(scope
.set.get("foo").references
.length
, 1);
4432 assert
.strictEqual(scope
.set.get("foo").references
[0].identifier
.name
, "foo");
4433 assert
.strictEqual(scope
.set.get("foo").references
[0].identifier
.loc
.start
.line
, 4);
4434 assert
.strictEqual(scope
.set.get("foo").references
[0].resolved
, scope
.set.get("foo"));
4435 assert
.strictEqual(scope
.set.get("c").references
.length
, 1);
4436 assert
.strictEqual(scope
.set.get("c").references
[0].identifier
.name
, "c");
4437 assert
.strictEqual(scope
.set.get("c").references
[0].identifier
.loc
.start
.line
, 6);
4438 assert
.strictEqual(scope
.set.get("c").references
[0].resolved
, scope
.set.get("c"));
4439 assert
.strictEqual(scope
.set.get("d").references
.length
, 1);
4440 assert
.strictEqual(scope
.set.get("d").references
[0].identifier
.name
, "d");
4441 assert
.strictEqual(scope
.set.get("d").references
[0].identifier
.loc
.start
.line
, 8);
4442 assert
.strictEqual(scope
.set.get("d").references
[0].resolved
, scope
.set.get("d"));
4443 assert
.strictEqual(scope
.set.get("e").references
.length
, 1);
4444 assert
.strictEqual(scope
.set.get("e").references
[0].identifier
.name
, "e");
4445 assert
.strictEqual(scope
.set.get("e").references
[0].identifier
.loc
.start
.line
, 9);
4446 assert
.strictEqual(scope
.set.get("e").references
[0].resolved
, scope
.set.get("e"));
4447 assert
.strictEqual(scope
.set.get("f").references
.length
, 1);
4448 assert
.strictEqual(scope
.set.get("f").references
[0].identifier
.name
, "f");
4449 assert
.strictEqual(scope
.set.get("f").references
[0].identifier
.loc
.start
.line
, 10);
4450 assert
.strictEqual(scope
.set.get("f").references
[0].resolved
, scope
.set.get("f"));
4453 it("Reference#resolved should be their variable", () => {
4454 assert
.strictEqual(scope
.set.get("Object").references
[0].resolved
, scope
.set.get("Object"));
4455 assert
.strictEqual(scope
.set.get("foo").references
[0].resolved
, scope
.set.get("foo"));
4456 assert
.strictEqual(scope
.set.get("c").references
[0].resolved
, scope
.set.get("c"));
4457 assert
.strictEqual(scope
.set.get("d").references
[0].resolved
, scope
.set.get("d"));
4458 assert
.strictEqual(scope
.set.get("e").references
[0].resolved
, scope
.set.get("e"));
4459 assert
.strictEqual(scope
.set.get("f").references
[0].resolved
, scope
.set.get("f"));
4463 describe("context.getDeclaredVariables(node)", () => {
4466 * Assert `context.getDeclaredVariables(node)` is valid.
4467 * @param {string} code A code to check.
4468 * @param {string} type A type string of ASTNode. This method checks variables on the node of the type.
4469 * @param {Array<Array<string>>} expectedNamesList An array of expected variable names. The expected variable names is an array of string.
4472 function verify(code
, type
, expectedNamesList
) {
4473 linter
.defineRules({
4477 * Assert `context.getDeclaredVariables(node)` is empty.
4478 * @param {ASTNode} node A node to check.
4481 function checkEmpty(node
) {
4482 assert
.strictEqual(0, context
.getDeclaredVariables(node
).length
);
4485 Program
: checkEmpty
,
4486 EmptyStatement
: checkEmpty
,
4487 BlockStatement
: checkEmpty
,
4488 ExpressionStatement
: checkEmpty
,
4489 LabeledStatement
: checkEmpty
,
4490 BreakStatement
: checkEmpty
,
4491 ContinueStatement
: checkEmpty
,
4492 WithStatement
: checkEmpty
,
4493 SwitchStatement
: checkEmpty
,
4494 ReturnStatement
: checkEmpty
,
4495 ThrowStatement
: checkEmpty
,
4496 TryStatement
: checkEmpty
,
4497 WhileStatement
: checkEmpty
,
4498 DoWhileStatement
: checkEmpty
,
4499 ForStatement
: checkEmpty
,
4500 ForInStatement
: checkEmpty
,
4501 DebuggerStatement
: checkEmpty
,
4502 ThisExpression
: checkEmpty
,
4503 ArrayExpression
: checkEmpty
,
4504 ObjectExpression
: checkEmpty
,
4505 Property
: checkEmpty
,
4506 SequenceExpression
: checkEmpty
,
4507 UnaryExpression
: checkEmpty
,
4508 BinaryExpression
: checkEmpty
,
4509 AssignmentExpression
: checkEmpty
,
4510 UpdateExpression
: checkEmpty
,
4511 LogicalExpression
: checkEmpty
,
4512 ConditionalExpression
: checkEmpty
,
4513 CallExpression
: checkEmpty
,
4514 NewExpression
: checkEmpty
,
4515 MemberExpression
: checkEmpty
,
4516 SwitchCase
: checkEmpty
,
4517 Identifier
: checkEmpty
,
4518 Literal
: checkEmpty
,
4519 ForOfStatement
: checkEmpty
,
4520 ArrowFunctionExpression
: checkEmpty
,
4521 YieldExpression
: checkEmpty
,
4522 TemplateLiteral
: checkEmpty
,
4523 TaggedTemplateExpression
: checkEmpty
,
4524 TemplateElement
: checkEmpty
,
4525 ObjectPattern
: checkEmpty
,
4526 ArrayPattern
: checkEmpty
,
4527 RestElement
: checkEmpty
,
4528 AssignmentPattern
: checkEmpty
,
4529 ClassBody
: checkEmpty
,
4530 MethodDefinition
: checkEmpty
,
4531 MetaProperty
: checkEmpty
4534 rule
[type
] = function(node
) {
4535 const expectedNames
= expectedNamesList
.shift();
4536 const variables
= context
.getDeclaredVariables(node
);
4538 assert(Array
.isArray(expectedNames
));
4539 assert(Array
.isArray(variables
));
4540 assert
.strictEqual(expectedNames
.length
, variables
.length
);
4541 for (let i
= variables
.length
- 1; i
>= 0; i
--) {
4542 assert
.strictEqual(expectedNames
[i
], variables
[i
].name
);
4548 linter
.verify(code
, {
4552 sourceType
: "module"
4556 // Check all expected names are asserted.
4557 assert
.strictEqual(0, expectedNamesList
.length
);
4560 it("VariableDeclaration", () => {
4561 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 ";
4565 ["g", "h", "i", "j", "k"],
4569 verify(code
, "VariableDeclaration", namesList
);
4572 it("VariableDeclaration (on for-in/of loop)", () => {
4574 // TDZ scope is created here, so tests to exclude those.
4575 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 ";
4583 verify(code
, "VariableDeclaration", namesList
);
4586 it("VariableDeclarator", () => {
4588 // TDZ scope is created here, so tests to exclude those.
4589 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 ";
4598 verify(code
, "VariableDeclarator", namesList
);
4601 it("FunctionDeclaration", () => {
4602 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 ";
4604 ["foo", "a", "b", "c", "d", "e"],
4605 ["bar", "f", "g", "h", "i", "j"]
4608 verify(code
, "FunctionDeclaration", namesList
);
4611 it("FunctionExpression", () => {
4612 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 ";
4614 ["foo", "a", "b", "c", "d", "e"],
4615 ["bar", "f", "g", "h", "i", "j"],
4619 verify(code
, "FunctionExpression", namesList
);
4622 it("ArrowFunctionExpression", () => {
4623 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 ";
4625 ["a", "b", "c", "d", "e"],
4626 ["f", "g", "h", "i", "j"]
4629 verify(code
, "ArrowFunctionExpression", namesList
);
4632 it("ClassDeclaration", () => {
4633 const code
= "\n class A { foo(x) { let y; } }\n class B { foo(x) { let y; } }\n ";
4635 ["A", "A"], // outer scope's and inner scope's.
4639 verify(code
, "ClassDeclaration", namesList
);
4642 it("ClassExpression", () => {
4643 const code
= "\n (class A { foo(x) { let y; } });\n (class B { foo(x) { let y; } });\n ";
4649 verify(code
, "ClassExpression", namesList
);
4652 it("CatchClause", () => {
4653 const code
= "\n try {} catch ({a, b}) {\n let x;\n try {} catch ({c, d}) {\n let y;\n }\n }\n ";
4659 verify(code
, "CatchClause", namesList
);
4662 it("ImportDeclaration", () => {
4663 const code
= "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
4670 verify(code
, "ImportDeclaration", namesList
);
4673 it("ImportSpecifier", () => {
4674 const code
= "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
4680 verify(code
, "ImportSpecifier", namesList
);
4683 it("ImportDefaultSpecifier", () => {
4684 const code
= "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
4689 verify(code
, "ImportDefaultSpecifier", namesList
);
4692 it("ImportNamespaceSpecifier", () => {
4693 const code
= "\n import \"aaa\";\n import * as a from \"bbb\";\n import b, {c, x as d} from \"ccc\";\n ";
4698 verify(code
, "ImportNamespaceSpecifier", namesList
);
4702 describe("suggestions", () => {
4703 it("provides suggestion information for tools to use", () => {
4704 linter
.defineRule("rule-with-suggestions", context
=> ({
4708 message
: "Incorrect spacing",
4710 desc
: "Insert space at the beginning",
4711 fix
: fixer
=> fixer
.insertTextBefore(node
, " ")
4713 desc
: "Insert space at the end",
4714 fix
: fixer
=> fixer
.insertTextAfter(node
, " ")
4720 const messages
= linter
.verify("var a = 1;", { rules
: { "rule-with-suggestions": "error" } });
4722 assert
.deepStrictEqual(messages
[0].suggestions
, [{
4723 desc
: "Insert space at the beginning",
4729 desc
: "Insert space at the end",
4737 it("supports messageIds for suggestions", () => {
4738 linter
.defineRule("rule-with-suggestions", {
4741 suggestion1
: "Insert space at the beginning",
4742 suggestion2
: "Insert space at the end"
4745 create
: context
=> ({
4749 message
: "Incorrect spacing",
4751 messageId
: "suggestion1",
4752 fix
: fixer
=> fixer
.insertTextBefore(node
, " ")
4754 messageId
: "suggestion2",
4755 fix
: fixer
=> fixer
.insertTextAfter(node
, " ")
4762 const messages
= linter
.verify("var a = 1;", { rules
: { "rule-with-suggestions": "error" } });
4764 assert
.deepStrictEqual(messages
[0].suggestions
, [{
4765 messageId
: "suggestion1",
4766 desc
: "Insert space at the beginning",
4772 messageId
: "suggestion2",
4773 desc
: "Insert space at the end",
4782 describe("mutability", () => {
4787 linter1
= new Linter();
4788 linter2
= new Linter();
4791 describe("rules", () => {
4792 it("with no changes, same rules are loaded", () => {
4793 assert
.sameDeepMembers(Array
.from(linter1
.getRules().keys()), Array
.from(linter2
.getRules().keys()));
4796 it("loading rule in one doesn't change the other", () => {
4797 linter1
.defineRule("mock-rule", () => ({}));
4799 assert
.isTrue(linter1
.getRules().has("mock-rule"), "mock rule is present");
4800 assert
.isFalse(linter2
.getRules().has("mock-rule"), "mock rule is not present");
4805 describe("processors", () => {
4806 let receivedFilenames
= [];
4809 receivedFilenames
= [];
4811 // A rule that always reports the AST with a message equal to the source text
4812 linter
.defineRule("report-original-text", context
=> ({
4814 receivedFilenames
.push(context
.getFilename());
4815 context
.report({ node
: ast
, message
: context
.getSourceCode().text
});
4820 describe("preprocessors", () => {
4821 it("should receive text and filename.", () => {
4822 const code
= "foo bar baz";
4823 const preprocess
= sinon
.spy(text
=> text
.split(" "));
4825 linter
.verify(code
, {}, { filename
, preprocess
});
4827 assert
.strictEqual(preprocess
.calledOnce
, true);
4828 assert
.deepStrictEqual(preprocess
.args
[0], [code
, filename
]);
4831 it("should apply a preprocessor to the code, and lint each code sample separately", () => {
4832 const code
= "foo bar baz";
4833 const problems
= linter
.verify(
4835 { rules
: { "report-original-text": "error" } },
4838 // Apply a preprocessor that splits the source text into spaces and lints each word individually
4840 return input
.split(" ");
4845 assert
.strictEqual(problems
.length
, 3);
4846 assert
.deepStrictEqual(problems
.map(problem
=> problem
.message
), ["foo", "bar", "baz"]);
4849 it("should apply a preprocessor to the code even if the preprocessor returned code block objects.", () => {
4850 const code
= "foo bar baz";
4851 const problems
= linter
.verify(
4853 { rules
: { "report-original-text": "error" } },
4857 // Apply a preprocessor that splits the source text into spaces and lints each word individually
4859 return input
.split(" ").map(text
=> ({
4860 filename
: "block.js",
4867 assert
.strictEqual(problems
.length
, 3);
4868 assert
.deepStrictEqual(problems
.map(problem
=> problem
.message
), ["foo", "bar", "baz"]);
4869 assert
.strictEqual(receivedFilenames
.length
, 3);
4870 assert(/^filename\.js[/\\]0_block
\.js
/u
.test(receivedFilenames
[0]));
4871 assert(/^filename\.js[/\\]1_block
\.js
/u
.test(receivedFilenames
[1]));
4872 assert(/^filename\.js[/\\]2_block
\.js
/u
.test(receivedFilenames
[2]));
4875 it("should receive text even if a SourceCode object was given.", () => {
4877 const preprocess
= sinon
.spy(text
=> text
.split(" "));
4879 linter
.verify(code
, {});
4880 const sourceCode
= linter
.getSourceCode();
4882 linter
.verify(sourceCode
, {}, { filename
, preprocess
});
4884 assert
.strictEqual(preprocess
.calledOnce
, true);
4885 assert
.deepStrictEqual(preprocess
.args
[0], [code
, filename
]);
4888 it("should receive text even if a SourceCode object was given (with BOM).", () => {
4889 const code
= "\uFEFFfoo";
4890 const preprocess
= sinon
.spy(text
=> text
.split(" "));
4892 linter
.verify(code
, {});
4893 const sourceCode
= linter
.getSourceCode();
4895 linter
.verify(sourceCode
, {}, { filename
, preprocess
});
4897 assert
.strictEqual(preprocess
.calledOnce
, true);
4898 assert
.deepStrictEqual(preprocess
.args
[0], [code
, filename
]);
4902 describe("postprocessors", () => {
4903 it("should receive result and filename.", () => {
4904 const code
= "foo bar baz";
4905 const preprocess
= sinon
.spy(text
=> text
.split(" "));
4906 const postprocess
= sinon
.spy(text
=> [text
]);
4908 linter
.verify(code
, {}, { filename
, postprocess
, preprocess
});
4910 assert
.strictEqual(postprocess
.calledOnce
, true);
4911 assert
.deepStrictEqual(postprocess
.args
[0], [[[], [], []], filename
]);
4914 it("should apply a postprocessor to the reported messages", () => {
4915 const code
= "foo bar baz";
4917 const problems
= linter
.verify(
4919 { rules
: { "report-original-text": "error" } },
4921 preprocess
: input
=> input
.split(" "),
4924 * Apply a postprocessor that updates the locations of the reported problems
4925 * to make sure they correspond to the locations in the original text.
4927 postprocess(problemLists
) {
4928 problemLists
.forEach(problemList
=> assert
.strictEqual(problemList
.length
, 1));
4929 return problemLists
.reduce(
4930 (combinedList
, problemList
, index
) =>
4931 combinedList
.concat(
4938 message
: problem
.message
.toUpperCase(),
4939 column
: problem
.column
+ index
* 4
4950 assert
.strictEqual(problems
.length
, 3);
4951 assert
.deepStrictEqual(problems
.map(problem
=> problem
.message
), ["FOO", "BAR", "BAZ"]);
4952 assert
.deepStrictEqual(problems
.map(problem
=> problem
.column
), [1, 5, 9]);
4955 it("should use postprocessed problem ranges when applying autofixes", () => {
4956 const code
= "foo bar baz";
4958 linter
.defineRule("capitalize-identifiers", context
=> ({
4960 if (node
.name
!== node
.name
.toUpperCase()) {
4963 message
: "Capitalize this identifier",
4964 fix
: fixer
=> fixer
.replaceText(node
, node
.name
.toUpperCase())
4970 const fixResult
= linter
.verifyAndFix(
4972 { rules
: { "capitalize-identifiers": "error" } },
4976 * Apply a postprocessor that updates the locations of autofixes
4977 * to make sure they correspond to locations in the original text.
4979 preprocess
: input
=> input
.split(" "),
4980 postprocess(problemLists
) {
4981 return problemLists
.reduce(
4982 (combinedProblems
, problemList
, blockIndex
) =>
4983 combinedProblems
.concat(
4984 problemList
.map(problem
=>
4985 Object
.assign(problem
, {
4987 text
: problem
.fix
.text
,
4988 range
: problem
.fix
.range
.map(
4989 rangeIndex
=> rangeIndex
+ blockIndex
* 4
5000 assert
.strictEqual(fixResult
.fixed
, true);
5001 assert
.strictEqual(fixResult
.messages
.length
, 0);
5002 assert
.strictEqual(fixResult
.output
, "FOO BAR BAZ");
5007 describe("verifyAndFix", () => {
5008 it("Fixes the code", () => {
5009 const messages
= linter
.verifyAndFix("var a", {
5013 }, { filename
: "test.js" });
5015 assert
.strictEqual(messages
.output
, "var a;", "Fixes were applied correctly");
5016 assert
.isTrue(messages
.fixed
);
5019 it("does not require a third argument", () => {
5020 const fixResult
= linter
.verifyAndFix("var a", {
5026 assert
.deepStrictEqual(fixResult
, {
5033 it("does not include suggestions in autofix results", () => {
5034 const fixResult
= linter
.verifyAndFix("var foo = /\\#/", {
5037 "no-useless-escape": 2
5041 assert
.strictEqual(fixResult
.output
, "var foo = /\\#/;");
5042 assert
.strictEqual(fixResult
.fixed
, true);
5043 assert
.strictEqual(fixResult
.messages
[0].suggestions
.length
> 0, true);
5046 it("does not apply autofixes when fix argument is `false`", () => {
5047 const fixResult
= linter
.verifyAndFix("var a", {
5053 assert
.strictEqual(fixResult
.fixed
, false);
5056 it("stops fixing after 10 passes", () => {
5057 linter
.defineRule("add-spaces", context
=> ({
5061 message
: "Add a space before this node.",
5062 fix
: fixer
=> fixer
.insertTextBefore(node
, " ")
5067 const fixResult
= linter
.verifyAndFix("a", { rules
: { "add-spaces": "error" } });
5069 assert
.strictEqual(fixResult
.fixed
, true);
5070 assert
.strictEqual(fixResult
.output
, `${" ".repeat(10)}a`);
5071 assert
.strictEqual(fixResult
.messages
.length
, 1);
5074 it("should throw an error if fix is passed but meta has no `fixable` property", () => {
5075 linter
.defineRule("test-rule", {
5080 create
: context
=> ({
5082 context
.report(node
, "hello world", {}, () => ({ range
: [1, 1], text
: "" }));
5087 assert
.throws(() => {
5088 linter
.verify("0", { rules
: { "test-rule": "error" } });
5089 }, /Fixable rules should export a `meta\.fixable` property.\nOccurred while linting <input>:1$/u);
5092 it("should not throw an error if fix is passed and there is no metadata", () => {
5093 linter
.defineRule("test-rule", {
5094 create
: context
=> ({
5096 context
.report(node
, "hello world", {}, () => ({ range
: [1, 1], text
: "" }));
5101 linter
.verify("0", { rules
: { "test-rule": "error" } });
5105 describe("Edge cases", () => {
5107 it("should properly parse import statements when sourceType is module", () => {
5108 const code
= "import foo from 'foo';";
5109 const messages
= linter
.verify(code
, { parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } });
5111 assert
.strictEqual(messages
.length
, 0);
5114 it("should properly parse import all statements when sourceType is module", () => {
5115 const code
= "import * as foo from 'foo';";
5116 const messages
= linter
.verify(code
, { parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } });
5118 assert
.strictEqual(messages
.length
, 0);
5121 it("should properly parse default export statements when sourceType is module", () => {
5122 const code
= "export default function initialize() {}";
5123 const messages
= linter
.verify(code
, { parserOptions
: { ecmaVersion
: 6, sourceType
: "module" } });
5125 assert
.strictEqual(messages
.length
, 0);
5128 // https://github.com/eslint/eslint/issues/9687
5129 it("should report an error when invalid parserOptions found", () => {
5130 let messages
= linter
.verify("", { parserOptions
: { ecmaVersion
: 222 } });
5132 assert
.deepStrictEqual(messages
.length
, 1);
5133 assert
.ok(messages
[0].message
.includes("Invalid ecmaVersion"));
5135 messages
= linter
.verify("", { parserOptions
: { sourceType
: "foo" } });
5136 assert
.deepStrictEqual(messages
.length
, 1);
5137 assert
.ok(messages
[0].message
.includes("Invalid sourceType"));
5139 messages
= linter
.verify("", { parserOptions
: { ecmaVersion
: 5, sourceType
: "module" } });
5140 assert
.deepStrictEqual(messages
.length
, 1);
5141 assert
.ok(messages
[0].message
.includes("sourceType 'module' is not supported when ecmaVersion < 2015"));
5144 it("should not crash when invalid parentheses syntax is encountered", () => {
5145 linter
.verify("left = (aSize.width/2) - ()");
5148 it("should not crash when let is used inside of switch case", () => {
5149 linter
.verify("switch(foo) { case 1: let bar=2; }", { parserOptions
: { ecmaVersion
: 6 } });
5152 it("should not crash when parsing destructured assignment", () => {
5153 linter
.verify("var { a='a' } = {};", { parserOptions
: { ecmaVersion
: 6 } });
5156 it("should report syntax error when a keyword exists in object property shorthand", () => {
5157 const messages
= linter
.verify("let a = {this}", { parserOptions
: { ecmaVersion
: 6 } });
5159 assert
.strictEqual(messages
.length
, 1);
5160 assert
.strictEqual(messages
[0].fatal
, true);
5163 it("should not rewrite env setting in core (https://github.com/eslint/eslint/issues/4814)", () => {
5166 * This test focuses on the instance of https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/conf/environments.js#L26-L28
5167 * This `verify()` takes the instance and runs https://github.com/eslint/eslint/blob/v2.0.0-alpha-2/lib/eslint.js#L416
5169 linter
.defineRule("test", () => ({}));
5170 linter
.verify("var a = 0;", {
5171 env
: { node
: true },
5172 parserOptions
: { ecmaVersion
: 6, sourceType
: "module" },
5176 // This `verify()` takes the instance and tests that the instance was not modified.
5179 linter
.defineRule("test", context
=> {
5181 context
.parserOptions
.ecmaFeatures
.globalReturn
,
5182 "`ecmaFeatures.globalReturn` of the node environment should not be modified."
5187 linter
.verify("var a = 0;", {
5188 env
: { node
: true },
5196 describe("Custom parser", () => {
5198 const errorPrefix
= "Parsing error: ";
5200 it("should have file path passed to it", () => {
5201 const code
= "/* this is code */";
5202 const parseSpy
= sinon
.spy(testParsers
.stubParser
, "parse");
5204 linter
.defineParser("stub-parser", testParsers
.stubParser
);
5205 linter
.verify(code
, { parser
: "stub-parser" }, filename
, true);
5207 sinon
.assert
.calledWithMatch(parseSpy
, "", { filePath
: filename
});
5210 it("should not report an error when JSX code contains a spread operator and JSX is enabled", () => {
5211 const code
= "var myDivElement = <div {...this.props} />;";
5213 linter
.defineParser("esprima", esprima
);
5214 const messages
= linter
.verify(code
, { parser
: "esprima", parserOptions
: { jsx
: true } }, "filename");
5216 assert
.strictEqual(messages
.length
, 0);
5219 it("should return an error when the custom parser can't be found", () => {
5220 const code
= "var myDivElement = <div {...this.props} />;";
5221 const messages
= linter
.verify(code
, { parser
: "esprima-xyz" }, "filename");
5223 assert
.strictEqual(messages
.length
, 1);
5224 assert
.strictEqual(messages
[0].severity
, 2);
5225 assert
.strictEqual(messages
[0].message
, "Configured parser 'esprima-xyz' was not found.");
5228 it("should not throw or report errors when the custom parser returns unrecognized operators (https://github.com/eslint/eslint/issues/10475)", () => {
5229 const code
= "null %% 'foo'";
5231 linter
.defineParser("unknown-logical-operator", testParsers
.unknownLogicalOperator
);
5233 // This shouldn't throw
5234 const messages
= linter
.verify(code
, { parser
: "unknown-logical-operator" }, filename
, true);
5236 assert
.strictEqual(messages
.length
, 0);
5239 it("should not throw or report errors when the custom parser returns nested unrecognized operators (https://github.com/eslint/eslint/issues/10560)", () => {
5240 const code
= "foo && bar %% baz";
5242 linter
.defineParser("unknown-logical-operator-nested", testParsers
.unknownLogicalOperatorNested
);
5244 // This shouldn't throw
5245 const messages
= linter
.verify(code
, { parser
: "unknown-logical-operator-nested" }, filename
, true);
5247 assert
.strictEqual(messages
.length
, 0);
5250 it("should not throw or return errors when the custom parser returns unknown AST nodes", () => {
5251 const code
= "foo && bar %% baz";
5255 linter
.defineRule("collect-node-types", () => ({
5257 nodes
.push(node
.type
);
5261 linter
.defineParser("non-js-parser", testParsers
.nonJSParser
);
5263 const messages
= linter
.verify(code
, {
5264 parser
: "non-js-parser",
5266 "collect-node-types": "error"
5270 assert
.strictEqual(messages
.length
, 0);
5271 assert
.isTrue(nodes
.length
> 0);
5274 it("should strip leading line: prefix from parser error", () => {
5275 linter
.defineParser("line-error", testParsers
.lineError
);
5276 const messages
= linter
.verify(";", { parser
: "line-error" }, "filename");
5278 assert
.strictEqual(messages
.length
, 1);
5279 assert
.strictEqual(messages
[0].severity
, 2);
5280 assert
.strictEqual(messages
[0].message
, errorPrefix
+ testParsers
.lineError
.expectedError
);
5283 it("should not modify a parser error message without a leading line: prefix", () => {
5284 linter
.defineParser("no-line-error", testParsers
.noLineError
);
5285 const messages
= linter
.verify(";", { parser
: "no-line-error" }, "filename");
5287 assert
.strictEqual(messages
.length
, 1);
5288 assert
.strictEqual(messages
[0].severity
, 2);
5289 assert
.strictEqual(messages
[0].message
, errorPrefix
+ testParsers
.noLineError
.expectedError
);
5292 describe("if a parser provides 'visitorKeys'", () => {
5299 linter
.defineRule("collect-node-types", () => ({
5301 types
.push(node
.type
);
5304 linter
.defineRule("save-scope-manager", context
=> {
5305 scopeManager
= context
.getSourceCode().scopeManager
;
5309 linter
.defineParser("enhanced-parser2", testParsers
.enhancedParser2
);
5310 linter
.verify("@foo class A {}", {
5311 parser
: "enhanced-parser2",
5313 "collect-node-types": "error",
5314 "save-scope-manager": "error"
5318 sourceCode
= linter
.getSourceCode();
5321 it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
5322 assert
.deepStrictEqual(
5324 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
5328 it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
5329 assert
.deepStrictEqual(
5330 scopeManager
.__options
.childVisitorKeys
.ClassDeclaration
, // eslint-disable-line no-underscore-dangle
5331 ["experimentalDecorators", "id", "superClass", "body"]
5335 it("should use the same visitorKeys if the source code object is reused", () => {
5338 linter
.defineRule("collect-node-types", () => ({
5340 types2
.push(node
.type
);
5343 linter
.verify(sourceCode
, {
5345 "collect-node-types": "error"
5349 assert
.deepStrictEqual(
5351 ["Program", "ClassDeclaration", "Decorator", "Identifier", "Identifier", "ClassBody"]
5356 describe("if a parser provides 'scope'", () => {
5358 let sourceCode
= null;
5361 linter
.defineParser("enhanced-parser3", testParsers
.enhancedParser3
);
5362 linter
.defineRule("save-scope1", context
=> ({
5364 scope
= context
.getScope();
5367 linter
.verify("@foo class A {}", { parser
: "enhanced-parser3", rules
: { "save-scope1": 2 } });
5369 sourceCode
= linter
.getSourceCode();
5372 it("should use the scope (so the global scope has the reference of '@foo')", () => {
5373 assert
.strictEqual(scope
.references
.length
, 1);
5374 assert
.deepStrictEqual(
5375 scope
.references
[0].identifier
.name
,
5380 it("should use the same scope if the source code object is reused", () => {
5383 linter
.defineRule("save-scope2", context
=> ({
5385 scope2
= context
.getScope();
5388 linter
.verify(sourceCode
, { rules
: { "save-scope2": 2 } }, "test.js");
5390 assert(scope2
!== null);
5391 assert(scope2
=== scope
);
5395 it("should not pass any default parserOptions to the parser", () => {
5396 linter
.defineParser("throws-with-options", testParsers
.throwsWithOptions
);
5397 const messages
= linter
.verify(";", { parser
: "throws-with-options" }, "filename");
5399 assert
.strictEqual(messages
.length
, 0);