2 * @fileoverview Tests for ESLint Tester
3 * @author Nicholas C. Zakas
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
10 const sinon
= require("sinon"),
11 EventEmitter
= require("events"),
12 { RuleTester
} = require("../../../lib/rule-tester"),
13 assert
= require("chai").assert
,
14 nodeAssert
= require("assert"),
15 espree
= require("espree");
17 const NODE_ASSERT_STRICT_EQUAL_OPERATOR
= (() => {
19 nodeAssert
.strictEqual(1, 2);
23 throw new Error("unexpected successful assertion");
35 //------------------------------------------------------------------------------
37 //------------------------------------------------------------------------------
40 * So here's the situation. Because RuleTester uses it() and describe() from
41 * Mocha, any failures would show up in the output of this test file. That means
42 * when we tested that a failure is thrown, that would also count as a failure
43 * in the testing for RuleTester. In order to remove those results from the
44 * results of this file, we need to overwrite it() and describe() just in
45 * RuleTester to do nothing but run code. Effectively, it() and describe()
46 * just become regular functions inside of index.js, not at all related to Mocha.
47 * That allows the results of this file to be untainted and therefore accurate.
49 * To assert that the right arguments are passed to RuleTester.describe/it, an
50 * event emitter is used which emits the arguments.
53 const ruleTesterTestEmitter
= new EventEmitter();
55 //------------------------------------------------------------------------------
57 //------------------------------------------------------------------------------
59 describe("RuleTester", () => {
61 // Stub `describe()` and `it()` while this test suite.
63 RuleTester
.describe = function(text
, method
) {
64 ruleTesterTestEmitter
.emit("describe", text
, method
);
65 return method
.call(this);
67 RuleTester
.it = function(text
, method
) {
68 ruleTesterTestEmitter
.emit("it", text
, method
);
69 return method
.call(this);
73 RuleTester
.describe
= null;
80 * A helper function to verify Node.js core error messages.
81 * @param {string} actual The actual input
82 * @param {string} expected The expected input
83 * @returns {Function} Error callback to verify that the message is correct
84 * for the actual and expected input.
86 function assertErrorMatches(actual
, expected
) {
87 const err
= new nodeAssert
.AssertionError({
90 operator
: NODE_ASSERT_STRICT_EQUAL_OPERATOR
97 RuleTester
.resetDefaultConfig();
98 ruleTester
= new RuleTester();
101 describe("only", () => {
102 describe("`itOnly` accessor", () => {
103 describe("when `itOnly` is set", () => {
105 RuleTester
.itOnly
= sinon
.spy();
108 RuleTester
.itOnly
= void 0;
111 RuleTester
.itOnly
.resetHistory();
112 ruleTester
= new RuleTester();
115 it("is called by exclusive tests", () => {
116 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
118 code
: "const notVar = 42;",
124 sinon
.assert
.calledWith(RuleTester
.itOnly
, "const notVar = 42;");
128 describe("when `it` is set and has an `only()` method", () => {
130 RuleTester
.it
.only
= () => {};
131 sinon
.spy(RuleTester
.it
, "only");
134 RuleTester
.it
.only
= void 0;
137 RuleTester
.it
.only
.resetHistory();
138 ruleTester
= new RuleTester();
141 it("is called by tests with `only` set", () => {
142 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
144 code
: "const notVar = 42;",
150 sinon
.assert
.calledWith(RuleTester
.it
.only
, "const notVar = 42;");
154 describe("when global `it` is a function that has an `only()` method", () => {
155 let originalGlobalItOnly
;
160 * We run tests with `--forbid-only`, so we have to override
161 * `it.only` to prevent the real one from being called.
163 originalGlobalItOnly
= it
.only
;
165 sinon
.spy(it
, "only");
168 it
.only
= originalGlobalItOnly
;
171 it
.only
.resetHistory();
172 ruleTester
= new RuleTester();
175 it("is called by tests with `only` set", () => {
176 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
178 code
: "const notVar = 42;",
184 sinon
.assert
.calledWith(it
.only
, "const notVar = 42;");
188 describe("when `describe` and `it` are overridden without `itOnly`", () => {
189 let originalGlobalItOnly
;
194 * These tests override `describe` and `it` already, so we
195 * don't need to override them here. We do, however, need to
196 * remove `only` from the global `it` to prevent it from
197 * being used instead.
199 originalGlobalItOnly
= it
.only
;
203 it
.only
= originalGlobalItOnly
;
206 ruleTester
= new RuleTester();
209 it("throws an error recommending overriding `itOnly`", () => {
210 assert
.throws(() => {
211 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
213 code
: "const notVar = 42;",
218 }, "Set `RuleTester.itOnly` to use `only` with a custom test framework.");
222 describe("when global `it` is a function that does not have an `only()` method", () => {
223 let originalGlobalIt
;
224 let originalRuleTesterDescribe
;
225 let originalRuleTesterIt
;
228 originalGlobalIt
= global
.it
;
230 // eslint-disable-next-line no-global-assign -- Temporarily override Mocha global
234 * These tests override `describe` and `it`, so we need to
235 * un-override them here so they won't interfere.
237 originalRuleTesterDescribe
= RuleTester
.describe
;
238 RuleTester
.describe
= void 0;
239 originalRuleTesterIt
= RuleTester
.it
;
240 RuleTester
.it
= void 0;
244 // eslint-disable-next-line no-global-assign -- Restore Mocha global
245 it
= originalGlobalIt
;
246 RuleTester
.describe
= originalRuleTesterDescribe
;
247 RuleTester
.it
= originalRuleTesterIt
;
250 ruleTester
= new RuleTester();
253 it("throws an error explaining that the current test framework does not support `only`", () => {
254 assert
.throws(() => {
255 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
257 code
: "const notVar = 42;",
262 }, "The current test framework does not support exclusive tests with `only`.");
267 describe("test cases", () => {
268 const ruleName
= "no-var";
269 const rule
= require("../../fixtures/testers/rule-tester/no-var");
271 let originalRuleTesterIt
;
273 let originalRuleTesterItOnly
;
274 let spyRuleTesterItOnly
;
277 originalRuleTesterIt
= RuleTester
.it
;
278 spyRuleTesterIt
= sinon
.spy();
279 RuleTester
.it
= spyRuleTesterIt
;
280 originalRuleTesterItOnly
= RuleTester
.itOnly
;
281 spyRuleTesterItOnly
= sinon
.spy();
282 RuleTester
.itOnly
= spyRuleTesterItOnly
;
285 RuleTester
.it
= originalRuleTesterIt
;
286 RuleTester
.itOnly
= originalRuleTesterItOnly
;
289 spyRuleTesterIt
.resetHistory();
290 spyRuleTesterItOnly
.resetHistory();
291 ruleTester
= new RuleTester();
294 it("isn't called for normal tests", () => {
295 ruleTester
.run(ruleName
, rule
, {
296 valid
: ["const notVar = 42;"],
299 sinon
.assert
.calledWith(spyRuleTesterIt
, "const notVar = 42;");
300 sinon
.assert
.notCalled(spyRuleTesterItOnly
);
303 it("calls it or itOnly for every test case", () => {
306 * `RuleTester` doesn't implement test case exclusivity itself.
307 * Setting `only: true` just causes `RuleTester` to call
308 * whatever `only()` function is provided by the test framework
309 * instead of the regular `it()` function.
312 ruleTester
.run(ruleName
, rule
, {
316 code
: "const onlyValid = 42;",
322 code
: "var invalid = 42;",
323 errors
: [/^Bad var/u]
326 code
: "var onlyInvalid = 42;",
327 errors
: [/^Bad var/u],
333 sinon
.assert
.calledWith(spyRuleTesterIt
, "const valid = 42;");
334 sinon
.assert
.calledWith(spyRuleTesterItOnly
, "const onlyValid = 42;");
335 sinon
.assert
.calledWith(spyRuleTesterIt
, "var invalid = 42;");
336 sinon
.assert
.calledWith(spyRuleTesterItOnly
, "var onlyInvalid = 42;");
340 describe("static helper wrapper", () => {
341 it("adds `only` to string test cases", () => {
342 const test
= RuleTester
.only("const valid = 42;");
344 assert
.deepStrictEqual(test
, {
345 code
: "const valid = 42;",
350 it("adds `only` to object test cases", () => {
351 const test
= RuleTester
.only({ code
: "const valid = 42;" });
353 assert
.deepStrictEqual(test
, {
354 code
: "const valid = 42;",
361 it("should not throw an error when everything passes", () => {
362 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
367 { code
: "eval(foo)", errors
: [{ message
: "eval sucks.", type
: "CallExpression" }] }
372 it("should throw an error when valid code is invalid", () => {
374 assert
.throws(() => {
375 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
380 { code
: "eval(foo)", errors
: [{ message
: "eval sucks.", type
: "CallExpression" }] }
383 }, /Should have no errors but had 1/u);
386 it("should throw an error when valid code is invalid", () => {
388 assert
.throws(() => {
389 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
391 { code
: "eval(foo)" }
394 { code
: "eval(foo)", errors
: [{ message
: "eval sucks.", type
: "CallExpression" }] }
397 }, /Should have no errors but had 1/u);
400 it("should throw an error if invalid code is valid", () => {
402 assert
.throws(() => {
403 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
408 { code
: "Eval(foo)", errors
: [{ message
: "eval sucks.", type
: "CallExpression" }] }
411 }, /Should have 1 error but had 0/u);
414 it("should throw an error when the error message is wrong", () => {
415 assert
.throws(() => {
416 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
418 // Only the invalid test matters here
423 { code
: "var foo = bar;", errors
: [{ message
: "Bad error message." }] }
426 }, assertErrorMatches("Bad var.", "Bad error message."));
429 it("should throw an error when the error message regex does not match", () => {
430 assert
.throws(() => {
431 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
434 { code
: "var foo = bar;", errors
: [{ message
: /Bad error message/u }] }
437 }, /Expected 'Bad var.' to match \/Bad error message\//u);
440 it("should throw an error when the error is not a supported type", () => {
441 assert
.throws(() => {
442 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
444 // Only the invalid test matters here
449 { code
: "var foo = bar;", errors
: [42] }
452 }, /Error should be a string, object, or RegExp/u);
455 it("should throw an error when any of the errors is not a supported type", () => {
456 assert
.throws(() => {
457 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
459 // Only the invalid test matters here
464 { code
: "var foo = bar; var baz = quux", errors
: [{ type
: "VariableDeclaration" }, null] }
467 }, /Error should be a string, object, or RegExp/u);
470 it("should throw an error when the error is a string and it does not match error message", () => {
471 assert
.throws(() => {
472 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
474 // Only the invalid test matters here
479 { code
: "var foo = bar;", errors
: ["Bad error message."] }
482 }, assertErrorMatches("Bad var.", "Bad error message."));
485 it("should throw an error when the error is a string and it does not match error message", () => {
486 assert
.throws(() => {
487 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
492 { code
: "var foo = bar;", errors
: [/Bad error message/u] }
495 }, /Expected 'Bad var.' to match \/Bad error message\//u);
498 it("should not throw an error when the error is a string and it matches error message", () => {
499 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
501 // Only the invalid test matters here
506 { code
: "var foo = bar;", output
: " foo = bar;", errors
: ["Bad var."] }
511 it("should not throw an error when the error is a regex and it matches error message", () => {
512 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
515 { code
: "var foo = bar;", output
: " foo = bar;", errors
: [/^Bad var/u] }
520 it("should throw an error when the error is an object with an unknown property name", () => {
521 assert
.throws(() => {
522 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
527 { code
: "var foo = bar;", errors
: [{ Message
: "Bad var." }] }
530 }, /Invalid error property name 'Message'/u);
533 it("should throw an error when any of the errors is an object with an unknown property name", () => {
534 assert
.throws(() => {
535 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
541 code
: "var foo = bar; var baz = quux",
543 { message
: "Bad var.", type
: "VariableDeclaration" },
544 { message
: "Bad var.", typo
: "VariableDeclaration" }
549 }, /Invalid error property name 'typo'/u);
552 it("should not throw an error when the error is a regex in an object and it matches error message", () => {
553 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
556 { code
: "var foo = bar;", output
: " foo = bar;", errors
: [{ message
: /^Bad var/u }] }
561 it("should throw an error when the expected output doesn't match", () => {
562 assert
.throws(() => {
563 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
568 { code
: "var foo = bar;", output
: "foo = bar", errors
: [{ message
: "Bad var.", type
: "VariableDeclaration" }] }
571 }, /Output is incorrect/u);
574 it("should use strict equality to compare output", () => {
575 const replaceProgramWith5Rule
= {
580 create
: context
=> ({
582 context
.report({ node
, message
: "bad", fix
: fixer
=> fixer
.replaceText(node
, "5") });
588 ruleTester
.run("foo", replaceProgramWith5Rule
, {
591 { code
: "var foo = bar;", output
: "5", errors
: 1 }
595 assert
.throws(() => {
596 ruleTester
.run("foo", replaceProgramWith5Rule
, {
599 { code
: "var foo = bar;", output
: 5, errors
: 1 }
602 }, /Output is incorrect/u);
605 it("should throw an error when the expected output doesn't match and errors is just a number", () => {
606 assert
.throws(() => {
607 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
612 { code
: "var foo = bar;", output
: "foo = bar", errors
: 1 }
615 }, /Output is incorrect/u);
618 it("should not throw an error when the expected output is null and no errors produce output", () => {
619 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
624 { code
: "eval(x)", errors
: 1, output
: null },
625 { code
: "eval(x); eval(y);", errors
: 2, output
: null }
630 it("should throw an error when the expected output is null and problems produce output", () => {
631 assert
.throws(() => {
632 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
637 { code
: "var foo = bar;", output
: null, errors
: 1 }
640 }, /Expected no autofixes to be suggested/u);
642 assert
.throws(() => {
643 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
649 code
: "var foo = bar; var qux = boop;",
655 }, /Expected no autofixes to be suggested/u);
658 it("should throw an error when the expected output is null and only some problems produce output", () => {
659 assert
.throws(() => {
660 ruleTester
.run("fixes-one-problem", require("../../fixtures/testers/rule-tester/fixes-one-problem"), {
663 { code
: "foo", output
: null, errors
: 2 }
666 }, /Expected no autofixes to be suggested/u);
669 it("should throw an error when the expected output isn't specified and problems produce output", () => {
670 assert
.throws(() => {
671 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
676 { code
: "var foo = bar;", errors
: 1 }
679 }, "The rule fixed the code. Please add 'output' property.");
682 it("should throw an error if invalid code specifies wrong type", () => {
683 assert
.throws(() => {
684 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
689 { code
: "eval(foo)", errors
: [{ message
: "eval sucks.", type
: "CallExpression2" }] }
692 }, /Error type should be CallExpression2, found CallExpression/u);
695 it("should throw an error if invalid code specifies wrong line", () => {
696 assert
.throws(() => {
697 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
702 { code
: "eval(foo)", errors
: [{ message
: "eval sucks.", type
: "CallExpression", line
: 5 }] }
705 }, /Error line should be 5/u);
708 it("should not skip line assertion if line is a falsy value", () => {
709 assert
.throws(() => {
710 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
715 { code
: "\neval(foo)", errors
: [{ message
: "eval sucks.", type
: "CallExpression", line
: 0 }] }
718 }, /Error line should be 0/u);
721 it("should throw an error if invalid code specifies wrong column", () => {
722 const wrongColumn
= 10,
723 expectedErrorMessage
= "Error column should be 1";
725 assert
.throws(() => {
726 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
727 valid
: ["Eval(foo)"],
731 message
: "eval sucks.",
736 }, expectedErrorMessage
);
739 it("should throw error for empty error array", () => {
740 assert
.throws(() => {
741 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
748 }, /Invalid cases must have at least one error/u);
751 it("should throw error for errors : 0", () => {
752 assert
.throws(() => {
754 "suggestions-messageIds",
755 require("../../fixtures/testers/rule-tester/suggestions")
767 }, /Invalid cases must have 'error' value greater than 0/u);
770 it("should not skip column assertion if column is a falsy value", () => {
771 assert
.throws(() => {
772 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
773 valid
: ["Eval(foo)"],
775 code
: "var foo; eval(foo)",
776 errors
: [{ message
: "eval sucks.", column
: 0 }]
779 }, /Error column should be 0/u);
782 it("should throw an error if invalid code specifies wrong endLine", () => {
783 assert
.throws(() => {
784 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
789 { code
: "var foo = bar;", output
: "foo = bar", errors
: [{ message
: "Bad var.", type
: "VariableDeclaration", endLine
: 10 }] }
792 }, "Error endLine should be 10");
795 it("should throw an error if invalid code specifies wrong endColumn", () => {
796 assert
.throws(() => {
797 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
802 { code
: "var foo = bar;", output
: "foo = bar", errors
: [{ message
: "Bad var.", type
: "VariableDeclaration", endColumn
: 10 }] }
805 }, "Error endColumn should be 10");
808 it("should throw an error if invalid code has the wrong number of errors", () => {
809 assert
.throws(() => {
810 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
818 { message
: "eval sucks.", type
: "CallExpression" },
819 { message
: "eval sucks.", type
: "CallExpression" }
824 }, /Should have 2 errors but had 1/u);
827 it("should throw an error if invalid code does not have errors", () => {
828 assert
.throws(() => {
829 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
834 { code
: "eval(foo)" }
837 }, /Did not specify errors for an invalid test of no-eval/u);
840 it("should throw an error if invalid code has the wrong explicit number of errors", () => {
841 assert
.throws(() => {
842 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
847 { code
: "eval(foo)", errors
: 2 }
850 }, /Should have 2 errors but had 1/u);
853 it("should throw an error if there's a parsing error in a valid test", () => {
854 assert
.throws(() => {
855 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
860 { code
: "eval('foo')", errors
: [{}] }
863 }, /fatal parsing error/iu);
866 it("should throw an error if there's a parsing error in an invalid test", () => {
867 assert
.throws(() => {
868 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
873 { code
: "1eval('foo')", errors
: [{}] }
876 }, /fatal parsing error/iu);
879 it("should throw an error if there's a parsing error in an invalid test and errors is just a number", () => {
880 assert
.throws(() => {
881 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
886 { code
: "1eval('foo')", errors
: 1 }
889 }, /fatal parsing error/iu);
892 // https://github.com/eslint/eslint/issues/4779
893 it("should throw an error if there's a parsing error and output doesn't match", () => {
894 assert
.throws(() => {
895 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
898 { code
: "eval(`foo`)", output
: "eval(`foo`);", errors
: [{}] }
901 }, /fatal parsing error/iu);
904 it("should not throw an error if invalid code has at least an expected empty error object", () => {
905 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
906 valid
: ["Eval(foo)"],
914 it("should pass-through the globals config of valid tests to the to rule", () => {
915 ruleTester
.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
919 code
: "var test2 = 'bar'",
920 globals
: { test
: true }
923 invalid
: [{ code
: "bar", errors
: 1 }]
927 it("should pass-through the globals config of invalid tests to the to rule", () => {
928 ruleTester
.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
929 valid
: ["var test = 'foo'"],
932 code
: "var test = 'foo'; var foo = 'bar'",
936 code
: "var test = 'foo'",
937 globals
: { foo
: true },
938 errors
: [{ message
: "Global variable foo should not be used." }]
944 it("should pass-through the settings config to rules", () => {
945 ruleTester
.run("no-test-settings", require("../../fixtures/testers/rule-tester/no-test-settings"), {
948 code
: "var test = 'bar'", settings
: { test
: 1 }
953 code
: "var test = 'bar'", settings
: { "no-test": 22 }, errors
: 1
959 it("should pass-through the filename to the rule", () => {
961 ruleTester
.run("", require("../../fixtures/testers/rule-tester/no-test-filename"), {
964 code
: "var foo = 'bar'",
965 filename
: "somefile.js"
970 code
: "var foo = 'bar'",
972 { message
: "Filename test was not defined." }
980 it("should pass-through the options to the rule", () => {
981 ruleTester
.run("no-invalid-args", require("../../fixtures/testers/rule-tester/no-invalid-args"), {
984 code
: "var foo = 'bar'",
990 code
: "var foo = 'bar'",
992 errors
: [{ message
: "Invalid args" }]
998 it("should throw an error if the options are an object", () => {
999 assert
.throws(() => {
1000 ruleTester
.run("no-invalid-args", require("../../fixtures/testers/rule-tester/no-invalid-args"), {
1004 options
: { ok
: true }
1009 }, /options must be an array/u);
1012 it("should throw an error if the options are a number", () => {
1013 assert
.throws(() => {
1014 ruleTester
.run("no-invalid-args", require("../../fixtures/testers/rule-tester/no-invalid-args"), {
1023 }, /options must be an array/u);
1026 it("should pass-through the parser to the rule", () => {
1027 const spy
= sinon
.spy(ruleTester
.linter
, "verify");
1029 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
1038 parser
: require
.resolve("esprima"),
1039 errors
: [{ line
: 1 }]
1043 assert
.strictEqual(spy
.args
[1][1].parser
, require
.resolve("esprima"));
1046 it("should pass normalized ecmaVersion to the rule", () => {
1047 const reportEcmaVersionRule
= {
1050 ecmaVersionMessage
: "context.parserOptions.ecmaVersion is {{type}} {{ecmaVersion}}."
1053 create
: context
=> ({
1055 const { ecmaVersion
} = context
.parserOptions
;
1059 messageId
: "ecmaVersionMessage",
1060 data
: { type
: typeof ecmaVersion
, ecmaVersion
}
1066 const notEspree
= require
.resolve("../../fixtures/parsers/empty-program-parser");
1068 ruleTester
.run("report-ecma-version", reportEcmaVersionRule
, {
1073 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }]
1077 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1082 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1083 parserOptions
: { ecmaFeatures
: { jsx
: true } }
1087 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1088 parser
: require
.resolve("espree")
1092 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1093 parserOptions
: { ecmaVersion
: 6 }
1097 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1098 parserOptions
: { ecmaVersion
: 2015 }
1102 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1103 env
: { browser
: true }
1107 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1112 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1117 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "8" } }],
1118 env
: { es6
: false, es2017
: true }
1122 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1123 env
: { es6
: "truthy" }
1127 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "8" } }],
1128 env
: { es2017
: true }
1132 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "11" } }],
1133 env
: { es2020
: true }
1137 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "12" } }],
1138 env
: { es2021
: true }
1142 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1143 parserOptions
: { ecmaVersion
: "latest" }
1147 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1148 parser
: require
.resolve("espree"),
1149 parserOptions
: { ecmaVersion
: "latest" }
1153 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1154 parserOptions
: { ecmaVersion
: "latest", ecmaFeatures
: { jsx
: true } }
1157 code
: "import 'foo'",
1158 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1159 parserOptions
: { ecmaVersion
: "latest", sourceType
: "module" }
1163 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1164 parserOptions
: { ecmaVersion
: "latest" },
1169 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1170 parserOptions
: { ecmaVersion
: "latest" },
1171 env
: { es2020
: true }
1174 // Non-Espree parsers normalize ecmaVersion if it's not "latest"
1177 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1182 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1188 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "5" } }],
1190 parserOptions
: { ecmaVersion
: 5 }
1194 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1196 parserOptions
: { ecmaVersion
: 6 }
1200 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: 6 } }],
1202 parserOptions
: { ecmaVersion
: 2015 }
1206 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "string", ecmaVersion
: "latest" } }],
1208 parserOptions
: { ecmaVersion
: "latest" }
1213 [{ parserOptions
: { ecmaVersion
: 6 } }, { env
: { es6
: true } }].forEach(options
=> {
1214 new RuleTester(options
).run("report-ecma-version", reportEcmaVersionRule
, {
1219 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }]
1223 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1230 new RuleTester({ parser
: notEspree
}).run("report-ecma-version", reportEcmaVersionRule
, {
1235 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }]
1239 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "string", ecmaVersion
: "latest" } }],
1240 parserOptions
: { ecmaVersion
: "latest" }
1246 it("should pass-through services from parseForESLint to the rule", () => {
1247 const enhancedParserPath
= require
.resolve("../../fixtures/parsers/enhanced-parser");
1248 const disallowHiRule
= {
1249 create
: context
=> ({
1251 const disallowed
= context
.parserServices
.test
.getMessage(); // returns "Hi!"
1253 if (node
.value
=== disallowed
) {
1254 context
.report({ node
, message
: `Don't use '${disallowed}'` });
1260 ruleTester
.run("no-hi", disallowHiRule
, {
1264 parser
: enhancedParserPath
1270 parser
: enhancedParserPath
,
1271 errors
: [{ message
: "Don't use 'Hi!'" }]
1277 it("should prevent invalid options schemas", () => {
1278 assert
.throws(() => {
1279 ruleTester
.run("no-invalid-schema", require("../../fixtures/testers/rule-tester/no-invalid-schema"), {
1281 "var answer = 6 * 7;",
1282 { code
: "var answer = 6 * 7;", options
: [] }
1285 { code
: "var answer = 6 * 7;", options
: ["bar"], errors
: [{ message
: "Expected nothing." }] }
1288 }, "Schema for rule no-invalid-schema is invalid:,\titems: should be object\n\titems[0].enum: should NOT have fewer than 1 items\n\titems: should match some schema in anyOf");
1292 it("should prevent schema violations in options", () => {
1293 assert
.throws(() => {
1294 ruleTester
.run("no-schema-violation", require("../../fixtures/testers/rule-tester/no-schema-violation"), {
1296 "var answer = 6 * 7;",
1297 { code
: "var answer = 6 * 7;", options
: ["foo"] }
1300 { code
: "var answer = 6 * 7;", options
: ["bar"], errors
: [{ message
: "Expected foo." }] }
1303 }, /Value "bar" should be equal to one of the allowed values./u);
1307 it("should disallow invalid defaults in rules", () => {
1308 const ruleWithInvalidDefaults
= {
1318 enum: ["foo", "bar"],
1322 additionalProperties
: false
1331 assert
.throws(() => {
1332 ruleTester
.run("invalid-defaults", ruleWithInvalidDefaults
, {
1341 }, /Schema for rule invalid-defaults is invalid: default is ignored for: data1\.foo/u);
1344 it("throw an error when an unknown config option is included", () => {
1345 assert
.throws(() => {
1346 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
1348 { code
: "Eval(foo)", foo
: "bar" }
1352 }, /ESLint configuration in rule-tester is invalid./u);
1355 it("throw an error when an invalid config value is included", () => {
1356 assert
.throws(() => {
1357 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
1359 { code
: "Eval(foo)", env
: ["es6"] }
1363 }, /Property "env" is the wrong type./u);
1366 it("should pass-through the tester config to the rule", () => {
1367 ruleTester
= new RuleTester({
1368 globals
: { test
: true }
1371 ruleTester
.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
1376 invalid
: [{ code
: "bar", errors
: 1, globals
: { foo
: true } }]
1380 it("should correctly set the globals configuration", () => {
1381 const config
= { globals
: { test
: true } };
1383 RuleTester
.setDefaultConfig(config
);
1385 RuleTester
.getDefaultConfig().globals
.test
,
1386 "The default config object is incorrect"
1390 it("should correctly reset the global configuration", () => {
1391 const config
= { globals
: { test
: true } };
1393 RuleTester
.setDefaultConfig(config
);
1394 RuleTester
.resetDefaultConfig();
1395 assert
.deepStrictEqual(
1396 RuleTester
.getDefaultConfig(),
1398 "The default configuration has not reset correctly"
1402 it("should enforce the global configuration to be an object", () => {
1405 * Set the default config for the rules tester
1406 * @param {Object} config configuration object
1407 * @returns {Function} Function to be executed
1410 function setConfig(config
) {
1412 RuleTester
.setDefaultConfig(config
);
1415 assert
.throw(setConfig());
1416 assert
.throw(setConfig(1));
1417 assert
.throw(setConfig(3.14));
1418 assert
.throw(setConfig("foo"));
1419 assert
.throw(setConfig(null));
1420 assert
.throw(setConfig(true));
1423 it("should pass-through the globals config to the tester then to the to rule", () => {
1424 const config
= { globals
: { test
: true } };
1426 RuleTester
.setDefaultConfig(config
);
1427 ruleTester
= new RuleTester();
1429 ruleTester
.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
1434 invalid
: [{ code
: "bar", errors
: 1, globals
: { foo
: true } }]
1438 it("should throw an error if AST was modified", () => {
1439 assert
.throws(() => {
1440 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast"), {
1446 }, "Rule should not modify AST.");
1447 assert
.throws(() => {
1448 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast"), {
1451 { code
: "var bar = 0;", errors
: ["error"] }
1454 }, "Rule should not modify AST.");
1457 it("should throw an error if AST was modified (at Program)", () => {
1458 assert
.throws(() => {
1459 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-first"), {
1465 }, "Rule should not modify AST.");
1466 assert
.throws(() => {
1467 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-first"), {
1470 { code
: "var bar = 0;", errors
: ["error"] }
1473 }, "Rule should not modify AST.");
1476 it("should throw an error if AST was modified (at Program:exit)", () => {
1477 assert
.throws(() => {
1478 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1484 }, "Rule should not modify AST.");
1485 assert
.throws(() => {
1486 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1489 { code
: "var bar = 0;", errors
: ["error"] }
1492 }, "Rule should not modify AST.");
1495 it("should throw an error if rule uses start and end properties on nodes, tokens or comments", () => {
1496 const usesStartEndRule
= {
1499 const sourceCode
= context
.getSourceCode();
1502 CallExpression(node
) {
1503 noop(node
.arguments
[1].start
);
1505 "BinaryExpression[operator='+']"(node
) {
1508 "UnaryExpression[operator='-']"(node
) {
1509 noop(sourceCode
.getFirstToken(node
).start
);
1511 ConditionalExpression(node
) {
1512 noop(sourceCode
.getFirstToken(node
).end
);
1514 BlockStatement(node
) {
1515 noop(sourceCode
.getCommentsInside(node
)[0].start
);
1517 ObjectExpression(node
) {
1518 noop(sourceCode
.getCommentsInside(node
)[0].end
);
1527 assert
.throws(() => {
1528 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1529 valid
: ["foo(a, b)"],
1532 }, "Use node.range[0] instead of node.start");
1533 assert
.throws(() => {
1534 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1536 invalid
: [{ code
: "var a = b * (c + d) / e;", errors
: 1 }]
1538 }, "Use node.range[1] instead of node.end");
1539 assert
.throws(() => {
1540 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1542 invalid
: [{ code
: "var a = -b * c;", errors
: 1 }]
1544 }, "Use token.range[0] instead of token.start");
1545 assert
.throws(() => {
1546 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1547 valid
: ["var a = b ? c : d;"],
1550 }, "Use token.range[1] instead of token.end");
1551 assert
.throws(() => {
1552 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1553 valid
: ["function f() { /* comment */ }"],
1556 }, "Use token.range[0] instead of token.start");
1557 assert
.throws(() => {
1558 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1560 invalid
: [{ code
: "var x = //\n {\n //comment\n //\n}", errors
: 1 }]
1562 }, "Use token.range[1] instead of token.end");
1564 const enhancedParserPath
= require
.resolve("../../fixtures/parsers/enhanced-parser");
1566 assert
.throws(() => {
1567 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1568 valid
: [{ code
: "foo(a, b)", parser
: enhancedParserPath
}],
1571 }, "Use node.range[0] instead of node.start");
1572 assert
.throws(() => {
1573 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1575 invalid
: [{ code
: "var a = b * (c + d) / e;", parser
: enhancedParserPath
, errors
: 1 }]
1577 }, "Use node.range[1] instead of node.end");
1578 assert
.throws(() => {
1579 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1581 invalid
: [{ code
: "var a = -b * c;", parser
: enhancedParserPath
, errors
: 1 }]
1583 }, "Use token.range[0] instead of token.start");
1584 assert
.throws(() => {
1585 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1586 valid
: [{ code
: "var a = b ? c : d;", parser
: enhancedParserPath
}],
1589 }, "Use token.range[1] instead of token.end");
1590 assert
.throws(() => {
1591 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1592 valid
: [{ code
: "function f() { /* comment */ }", parser
: enhancedParserPath
}],
1595 }, "Use token.range[0] instead of token.start");
1596 assert
.throws(() => {
1597 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1599 invalid
: [{ code
: "var x = //\n {\n //comment\n //\n}", parser
: enhancedParserPath
, errors
: 1 }]
1601 }, "Use token.range[1] instead of token.end");
1603 assert
.throws(() => {
1604 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1605 valid
: [{ code
: "@foo class A {}", parser
: require
.resolve("../../fixtures/parsers/enhanced-parser2") }],
1608 }, "Use node.range[0] instead of node.start");
1611 it("should throw an error if no test scenarios given", () => {
1612 assert
.throws(() => {
1613 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"));
1614 }, "Test Scenarios for rule foo : Could not find test scenario object");
1617 it("should throw an error if no acceptable test scenario object is given", () => {
1618 assert
.throws(() => {
1619 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), []);
1620 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios\nCould not find any invalid test scenarios");
1621 assert
.throws(() => {
1622 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), "");
1623 }, "Test Scenarios for rule foo : Could not find test scenario object");
1624 assert
.throws(() => {
1625 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), 2);
1626 }, "Test Scenarios for rule foo : Could not find test scenario object");
1627 assert
.throws(() => {
1628 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {});
1629 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios\nCould not find any invalid test scenarios");
1630 assert
.throws(() => {
1631 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1634 }, "Test Scenarios for rule foo is invalid:\nCould not find any invalid test scenarios");
1635 assert
.throws(() => {
1636 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1639 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios");
1642 // Nominal message/messageId use cases
1643 it("should assert match if message provided in both test and result.", () => {
1644 assert
.throws(() => {
1645 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly
, {
1647 invalid
: [{ code
: "foo", errors
: [{ message
: "something" }] }]
1649 }, /Avoid using variables named/u);
1651 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly
, {
1653 invalid
: [{ code
: "foo", errors
: [{ message
: "Avoid using variables named 'foo'." }] }]
1657 it("should assert match between messageId if provided in both test and result.", () => {
1658 assert
.throws(() => {
1659 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1661 invalid
: [{ code
: "foo", errors
: [{ messageId
: "unused" }] }]
1663 }, "messageId 'avoidFoo' does not match expected messageId 'unused'.");
1665 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1667 invalid
: [{ code
: "foo", errors
: [{ messageId
: "avoidFoo" }] }]
1670 it("should assert match between resulting message output if messageId and data provided in both test and result", () => {
1671 assert
.throws(() => {
1672 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1674 invalid
: [{ code
: "foo", errors
: [{ messageId
: "avoidFoo", data
: { name
: "notFoo" } }] }]
1676 }, "Hydrated message \"Avoid using variables named 'notFoo'.\" does not match \"Avoid using variables named 'foo'.\"");
1679 // messageId/message misconfiguration cases
1680 it("should throw if user tests for both message and messageId", () => {
1681 assert
.throws(() => {
1682 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1684 invalid
: [{ code
: "foo", errors
: [{ message
: "something", messageId
: "avoidFoo" }] }]
1686 }, "Error should not specify both 'message' and a 'messageId'.");
1688 it("should throw if user tests for messageId but the rule doesn't use the messageId meta syntax.", () => {
1689 assert
.throws(() => {
1690 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly
, {
1692 invalid
: [{ code
: "foo", errors
: [{ messageId
: "avoidFoo" }] }]
1694 }, "Error can not use 'messageId' if rule under test doesn't define 'meta.messages'");
1696 it("should throw if user tests for messageId not listed in the rule's meta syntax.", () => {
1697 assert
.throws(() => {
1698 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1700 invalid
: [{ code
: "foo", errors
: [{ messageId
: "useFoo" }] }]
1702 }, /Invalid messageId 'useFoo'/u);
1704 it("should throw if data provided without messageId.", () => {
1705 assert
.throws(() => {
1706 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1708 invalid
: [{ code
: "foo", errors
: [{ data
: "something" }] }]
1710 }, "Error must specify 'messageId' if 'data' is used.");
1713 // fixable rules with or without `meta` property
1714 it("should not throw an error if a rule that has `meta.fixable` produces fixes", () => {
1715 const replaceProgramWith5Rule
= {
1722 context
.report({ node
, message
: "bad", fix
: fixer
=> fixer
.replaceText(node
, "5") });
1728 ruleTester
.run("replaceProgramWith5", replaceProgramWith5Rule
, {
1731 { code
: "var foo = bar;", output
: "5", errors
: 1 }
1735 it("should throw an error if a new-format rule that doesn't have `meta` produces fixes", () => {
1736 const replaceProgramWith5Rule
= {
1740 context
.report({ node
, message
: "bad", fix
: fixer
=> fixer
.replaceText(node
, "5") });
1746 assert
.throws(() => {
1747 ruleTester
.run("replaceProgramWith5", replaceProgramWith5Rule
, {
1750 { code
: "var foo = bar;", output
: "5", errors
: 1 }
1753 }, /Fixable rules must set the `meta\.fixable` property/u);
1755 it("should throw an error if a legacy-format rule produces fixes", () => {
1758 * Legacy-format rule (a function instead of an object with `create` method).
1759 * @param {RuleContext} context The ESLint rule context object.
1760 * @returns {Object} Listeners.
1762 function replaceProgramWith5Rule(context
) {
1765 context
.report({ node
, message
: "bad", fix
: fixer
=> fixer
.replaceText(node
, "5") });
1770 assert
.throws(() => {
1771 ruleTester
.run("replaceProgramWith5", replaceProgramWith5Rule
, {
1774 { code
: "var foo = bar;", output
: "5", errors
: 1 }
1777 }, /Fixable rules must set the `meta\.fixable` property/u);
1780 describe("suggestions", () => {
1781 it("should pass with valid suggestions (tested using desc)", () => {
1782 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1790 desc
: "Rename identifier 'foo' to 'bar'",
1798 it("should pass with suggestions on multiple lines", () => {
1799 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1803 code
: "function foo() {\n var foo = 1;\n}",
1806 desc
: "Rename identifier 'foo' to 'bar'",
1807 output
: "function bar() {\n var foo = 1;\n}"
1811 desc
: "Rename identifier 'foo' to 'bar'",
1812 output
: "function foo() {\n var bar = 1;\n}"
1820 it("should pass with valid suggestions (tested using messageIds)", () => {
1821 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1827 messageId
: "renameFoo",
1830 messageId
: "renameFoo",
1838 it("should pass with valid suggestions (one tested using messageIds, the other using desc)", () => {
1839 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1845 messageId
: "renameFoo",
1848 desc
: "Rename identifier 'foo' to 'baz'",
1856 it("should pass with valid suggestions (tested using both desc and messageIds for the same suggestion)", () => {
1857 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1863 desc
: "Rename identifier 'foo' to 'bar'",
1864 messageId
: "renameFoo",
1867 desc
: "Rename identifier 'foo' to 'baz'",
1868 messageId
: "renameFoo",
1876 it("should pass with valid suggestions (tested using only desc on a rule that utilizes meta.messages)", () => {
1877 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1883 desc
: "Rename identifier 'foo' to 'bar'",
1886 desc
: "Rename identifier 'foo' to 'baz'",
1894 it("should pass with valid suggestions (tested using messageIds and data)", () => {
1895 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1901 messageId
: "renameFoo",
1902 data
: { newName
: "bar" },
1905 messageId
: "renameFoo",
1906 data
: { newName
: "baz" },
1915 it("should pass when tested using empty suggestion test objects if the array length is correct", () => {
1916 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1921 suggestions
: [{}, {}]
1927 it("should support explicitly expecting no suggestions", () => {
1928 [void 0, null, false, []].forEach(suggestions
=> {
1929 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/no-eval"), {
1932 code
: "eval('var foo');",
1941 it("should fail when expecting no suggestions and there are suggestions", () => {
1942 [void 0, null, false, []].forEach(suggestions
=> {
1943 assert
.throws(() => {
1944 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1953 }, "Error should have no suggestions on error with message: \"Avoid using identifiers named 'foo'.\"");
1957 it("should fail when testing for suggestions that don't exist", () => {
1958 assert
.throws(() => {
1959 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
1965 messageId
: "this-does-not-exist"
1970 }, "Error should have an array of suggestions. Instead received \"undefined\" on error with message: \"Bad var.\"");
1973 it("should fail when there are a different number of suggestions", () => {
1974 assert
.throws(() => {
1975 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1981 desc
: "Rename identifier 'foo' to 'bar'",
1984 desc
: "Rename identifier 'foo' to 'baz'",
1990 }, "Error should have 2 suggestions. Instead found 1 suggestions");
1993 it("should throw if the suggestion description doesn't match", () => {
1994 assert
.throws(() => {
1995 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2007 }, "Error Suggestion at index 0 : desc should be \"not right\" but got \"Rename identifier 'foo' to 'bar'\" instead.");
2010 it("should throw if the suggestion description doesn't match (although messageIds match)", () => {
2011 assert
.throws(() => {
2012 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2018 desc
: "Rename identifier 'foo' to 'bar'",
2019 messageId
: "renameFoo",
2022 desc
: "Rename id 'foo' to 'baz'",
2023 messageId
: "renameFoo",
2029 }, "Error Suggestion at index 1 : desc should be \"Rename id 'foo' to 'baz'\" but got \"Rename identifier 'foo' to 'baz'\" instead.");
2032 it("should throw if the suggestion messageId doesn't match", () => {
2033 assert
.throws(() => {
2034 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2040 messageId
: "unused",
2043 messageId
: "renameFoo",
2049 }, "Error Suggestion at index 0 : messageId should be 'unused' but got 'renameFoo' instead.");
2052 it("should throw if the suggestion messageId doesn't match (although descriptions match)", () => {
2053 assert
.throws(() => {
2054 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2060 desc
: "Rename identifier 'foo' to 'bar'",
2061 messageId
: "renameFoo",
2064 desc
: "Rename identifier 'foo' to 'baz'",
2065 messageId
: "avoidFoo",
2071 }, "Error Suggestion at index 1 : messageId should be 'avoidFoo' but got 'renameFoo' instead.");
2074 it("should throw if test specifies messageId for a rule that doesn't have meta.messages", () => {
2075 assert
.throws(() => {
2076 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2082 messageId
: "renameFoo",
2088 }, "Error Suggestion at index 0 : Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.");
2091 it("should throw if test specifies messageId that doesn't exist in the rule's meta.messages", () => {
2092 assert
.throws(() => {
2093 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2099 messageId
: "renameFoo",
2102 messageId
: "removeFoo",
2108 }, "Error Suggestion at index 1 : Test has invalid messageId 'removeFoo', the rule under test allows only one of ['avoidFoo', 'unused', 'renameFoo'].");
2111 it("should throw if hydrated desc doesn't match (wrong data value)", () => {
2112 assert
.throws(() => {
2113 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2119 messageId
: "renameFoo",
2120 data
: { newName
: "car" },
2123 messageId
: "renameFoo",
2124 data
: { newName
: "baz" },
2130 }, "Error Suggestion at index 0 : Hydrated test desc \"Rename identifier 'foo' to 'car'\" does not match received desc \"Rename identifier 'foo' to 'bar'\".");
2133 it("should throw if hydrated desc doesn't match (wrong data key)", () => {
2134 assert
.throws(() => {
2135 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2141 messageId
: "renameFoo",
2142 data
: { newName
: "bar" },
2145 messageId
: "renameFoo",
2146 data
: { name
: "baz" },
2152 }, "Error Suggestion at index 1 : Hydrated test desc \"Rename identifier 'foo' to '{{ newName }}'\" does not match received desc \"Rename identifier 'foo' to 'baz'\".");
2155 it("should throw if test specifies both desc and data", () => {
2156 assert
.throws(() => {
2157 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2163 desc
: "Rename identifier 'foo' to 'bar'",
2164 messageId
: "renameFoo",
2165 data
: { newName
: "bar" },
2168 messageId
: "renameFoo",
2169 data
: { newName
: "baz" },
2175 }, "Error Suggestion at index 0 : Test should not specify both 'desc' and 'data'.");
2178 it("should throw if test uses data but doesn't specify messageId", () => {
2179 assert
.throws(() => {
2180 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2186 messageId
: "renameFoo",
2187 data
: { newName
: "bar" },
2190 data
: { newName
: "baz" },
2196 }, "Error Suggestion at index 1 : Test must specify 'messageId' if 'data' is used.");
2199 it("should throw if the resulting suggestion output doesn't match", () => {
2200 assert
.throws(() => {
2201 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2207 desc
: "Rename identifier 'foo' to 'bar'",
2213 }, "Expected the applied suggestion fix to match the test suggestion output");
2216 it("should fail when specified suggestion isn't an object", () => {
2217 assert
.throws(() => {
2218 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2227 }, "Test suggestion in 'suggestions' array must be an object.");
2229 assert
.throws(() => {
2230 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2237 messageId
: "renameFoo",
2240 "Rename identifier 'foo' to 'baz'"
2245 }, "Test suggestion in 'suggestions' array must be an object.");
2248 it("should fail when the suggestion is an object with an unknown property name", () => {
2249 assert
.throws(() => {
2250 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2258 message
: "Rename identifier 'foo' to 'bar'"
2263 }, /Invalid suggestion property name 'message'/u);
2266 it("should fail when any of the suggestions is an object with an unknown property name", () => {
2267 assert
.throws(() => {
2268 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2274 messageId
: "renameFoo",
2277 messageId
: "renameFoo",
2283 }, /Invalid suggestion property name 'outpt'/u);
2286 it("should throw an error if a rule that doesn't have `meta.hasSuggestions` enabled produces suggestions", () => {
2287 assert
.throws(() => {
2288 ruleTester
.run("suggestions-missing-hasSuggestions-property", require("../../fixtures/testers/rule-tester/suggestions").withoutHasSuggestionsProperty
, {
2291 { code
: "var foo = bar;", output
: "5", errors
: 1 }
2294 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
2298 describe("naming test cases", () => {
2301 * Asserts that a particular value will be emitted from an EventEmitter.
2302 * @param {EventEmitter} emitter The emitter that should emit a value
2303 * @param {string} emitType The type of emission to listen for
2304 * @param {any} expectedValue The value that should be emitted
2305 * @returns {Promise<void>} A Promise that fulfills if the value is emitted, and rejects if something else is emitted.
2306 * The Promise will be indefinitely pending if no value is emitted.
2308 function assertEmitted(emitter
, emitType
, expectedValue
) {
2309 return new Promise((resolve
, reject
) => {
2310 emitter
.once(emitType
, emittedValue
=> {
2311 if (emittedValue
=== expectedValue
) {
2314 reject(new Error(`Expected ${expectedValue} to be emitted but ${emittedValue} was emitted instead.`));
2320 it("should use the first argument as the name of the test suite", () => {
2321 const assertion
= assertEmitted(ruleTesterTestEmitter
, "describe", "this-is-a-rule-name");
2323 ruleTester
.run("this-is-a-rule-name", require("../../fixtures/testers/rule-tester/no-var"), {
2331 it("should use the test code as the name of the tests for valid code (string form)", () => {
2332 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "valid(code);");
2334 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2344 it("should use the test code as the name of the tests for valid code (object form)", () => {
2345 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "valid(code);");
2347 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2350 code
: "valid(code);"
2359 it("should use the test code as the name of the tests for invalid code", () => {
2360 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "var x = invalid(code);");
2362 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2366 code
: "var x = invalid(code);",
2367 output
: " x = invalid(code);",
2376 // https://github.com/eslint/eslint/issues/8142
2377 it("should use the empty string as the name of the test if the test case is an empty string", () => {
2378 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "");
2380 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2392 it('should use the "name" property if set to a non-empty string', () => {
2393 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "my test");
2395 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2400 code
: "var x = invalid(code);",
2401 output
: " x = invalid(code);",
2410 it('should use the "name" property if set to a non-empty string for valid cases too', () => {
2411 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "my test");
2413 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2417 code
: "valid(code);"
2427 it('should use the test code as the name if the "name" property is set to an empty string', () => {
2428 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "var x = invalid(code);");
2430 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2435 code
: "var x = invalid(code);",
2436 output
: " x = invalid(code);",
2446 // https://github.com/eslint/eslint/issues/11615
2447 it("should fail the case if autofix made a syntax error.", () => {
2448 assert
.throw(() => {
2460 message
: "make a syntax error",
2462 return fixer
.replaceText(node
, "one two");
2474 }, /A fatal parsing error occurred in autofix.\nError: .+\nAutofix output:\n.+/u);
2477 describe("sanitize test cases", () => {
2478 let originalRuleTesterIt
;
2479 let spyRuleTesterIt
;
2482 originalRuleTesterIt
= RuleTester
.it
;
2483 spyRuleTesterIt
= sinon
.spy();
2484 RuleTester
.it
= spyRuleTesterIt
;
2487 RuleTester
.it
= originalRuleTesterIt
;
2490 spyRuleTesterIt
.resetHistory();
2491 ruleTester
= new RuleTester();
2493 it("should present newline when using back-tick as new line", () => {
2497 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
2502 errors
: [/^Bad var/u]
2506 sinon
.assert
.calledWith(spyRuleTesterIt
, code
);
2508 it("should present \\u0000 as a string", () => {
2509 const code
= "\u0000";
2511 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
2516 errors
: [/^Bad var/u]
2520 sinon
.assert
.calledWith(spyRuleTesterIt
, "\\u0000");
2522 it("should present the pipe character correctly", () => {
2523 const code
= "var foo = bar || baz;";
2525 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
2530 errors
: [/^Bad var/u]
2534 sinon
.assert
.calledWith(spyRuleTesterIt
, code
);
2539 describe("SourceCode#getComments()", () => {
2540 const useGetCommentsRule
= {
2541 create
: context
=> ({
2543 const sourceCode
= context
.getSourceCode();
2545 sourceCode
.getComments(node
);
2550 it("should throw if called from a valid test case", () => {
2551 assert
.throws(() => {
2552 ruleTester
.run("use-get-comments", useGetCommentsRule
, {
2556 }, /`SourceCode#getComments\(\)` is deprecated/u);
2559 it("should throw if called from an invalid test case", () => {
2560 assert
.throws(() => {
2561 ruleTester
.run("use-get-comments", useGetCommentsRule
, {
2568 }, /`SourceCode#getComments\(\)` is deprecated/u);