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"));
1045 it("should pass normalized ecmaVersion to the rule", () => {
1046 const reportEcmaVersionRule
= {
1049 ecmaVersionMessage
: "context.parserOptions.ecmaVersion is {{type}} {{ecmaVersion}}."
1052 create
: context
=> ({
1054 const { ecmaVersion
} = context
.parserOptions
;
1058 messageId
: "ecmaVersionMessage",
1059 data
: { type
: typeof ecmaVersion
, ecmaVersion
}
1065 const notEspree
= require
.resolve("../../fixtures/parsers/empty-program-parser");
1067 ruleTester
.run("report-ecma-version", reportEcmaVersionRule
, {
1072 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }]
1076 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1081 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1082 parserOptions
: { ecmaFeatures
: { jsx
: true } }
1086 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1087 parser
: require
.resolve("espree")
1091 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1092 parserOptions
: { ecmaVersion
: 6 }
1096 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1097 parserOptions
: { ecmaVersion
: 2015 }
1101 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1102 env
: { browser
: true }
1106 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1111 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1116 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "8" } }],
1117 env
: { es6
: false, es2017
: true }
1121 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1122 env
: { es6
: "truthy" }
1126 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "8" } }],
1127 env
: { es2017
: true }
1131 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "11" } }],
1132 env
: { es2020
: true }
1136 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "12" } }],
1137 env
: { es2021
: true }
1141 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1142 parserOptions
: { ecmaVersion
: "latest" }
1146 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1147 parser
: require
.resolve("espree"),
1148 parserOptions
: { ecmaVersion
: "latest" }
1152 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1153 parserOptions
: { ecmaVersion
: "latest", ecmaFeatures
: { jsx
: true } }
1156 code
: "import 'foo'",
1157 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1158 parserOptions
: { ecmaVersion
: "latest", sourceType
: "module" }
1162 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1163 parserOptions
: { ecmaVersion
: "latest" },
1168 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: String(espree
.latestEcmaVersion
) } }],
1169 parserOptions
: { ecmaVersion
: "latest" },
1170 env
: { es2020
: true }
1174 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1179 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }],
1185 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "5" } }],
1187 parserOptions
: { ecmaVersion
: 5 }
1191 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1193 parserOptions
: { ecmaVersion
: 6 }
1197 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1199 parserOptions
: { ecmaVersion
: 2015 }
1203 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "string", ecmaVersion
: "latest" } }],
1205 parserOptions
: { ecmaVersion
: "latest" }
1210 [{ parserOptions
: { ecmaVersion
: 6 } }, { env
: { es6
: true } }].forEach(options
=> {
1211 new RuleTester(options
).run("report-ecma-version", reportEcmaVersionRule
, {
1216 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }]
1220 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "number", ecmaVersion
: "6" } }],
1227 new RuleTester({ parser
: notEspree
}).run("report-ecma-version", reportEcmaVersionRule
, {
1232 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "undefined", ecmaVersion
: "undefined" } }]
1236 errors
: [{ messageId
: "ecmaVersionMessage", data
: { type
: "string", ecmaVersion
: "latest" } }],
1237 parserOptions
: { ecmaVersion
: "latest" }
1243 it("should pass-through services from parseForESLint to the rule", () => {
1244 const enhancedParserPath
= require
.resolve("../../fixtures/parsers/enhanced-parser");
1245 const disallowHiRule
= {
1246 create
: context
=> ({
1248 const disallowed
= context
.parserServices
.test
.getMessage(); // returns "Hi!"
1250 if (node
.value
=== disallowed
) {
1251 context
.report({ node
, message
: `Don't use '${disallowed}'` });
1257 ruleTester
.run("no-hi", disallowHiRule
, {
1261 parser
: enhancedParserPath
1267 parser
: enhancedParserPath
,
1268 errors
: [{ message
: "Don't use 'Hi!'" }]
1274 it("should prevent invalid options schemas", () => {
1275 assert
.throws(() => {
1276 ruleTester
.run("no-invalid-schema", require("../../fixtures/testers/rule-tester/no-invalid-schema"), {
1278 "var answer = 6 * 7;",
1279 { code
: "var answer = 6 * 7;", options
: [] }
1282 { code
: "var answer = 6 * 7;", options
: ["bar"], errors
: [{ message
: "Expected nothing." }] }
1285 }, "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");
1289 it("should prevent schema violations in options", () => {
1290 assert
.throws(() => {
1291 ruleTester
.run("no-schema-violation", require("../../fixtures/testers/rule-tester/no-schema-violation"), {
1293 "var answer = 6 * 7;",
1294 { code
: "var answer = 6 * 7;", options
: ["foo"] }
1297 { code
: "var answer = 6 * 7;", options
: ["bar"], errors
: [{ message
: "Expected foo." }] }
1300 }, /Value "bar" should be equal to one of the allowed values./u);
1304 it("should disallow invalid defaults in rules", () => {
1305 const ruleWithInvalidDefaults
= {
1315 enum: ["foo", "bar"],
1319 additionalProperties
: false
1328 assert
.throws(() => {
1329 ruleTester
.run("invalid-defaults", ruleWithInvalidDefaults
, {
1338 }, /Schema for rule invalid-defaults is invalid: default is ignored for: data1\.foo/u);
1341 it("throw an error when an unknown config option is included", () => {
1342 assert
.throws(() => {
1343 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
1345 { code
: "Eval(foo)", foo
: "bar" }
1349 }, /ESLint configuration in rule-tester is invalid./u);
1352 it("throw an error when an invalid config value is included", () => {
1353 assert
.throws(() => {
1354 ruleTester
.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
1356 { code
: "Eval(foo)", env
: ["es6"] }
1360 }, /Property "env" is the wrong type./u);
1363 it("should pass-through the tester config to the rule", () => {
1364 ruleTester
= new RuleTester({
1365 globals
: { test
: true }
1368 ruleTester
.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
1373 invalid
: [{ code
: "bar", errors
: 1, globals
: { foo
: true } }]
1377 it("should correctly set the globals configuration", () => {
1378 const config
= { globals
: { test
: true } };
1380 RuleTester
.setDefaultConfig(config
);
1382 RuleTester
.getDefaultConfig().globals
.test
,
1383 "The default config object is incorrect"
1387 it("should correctly reset the global configuration", () => {
1388 const config
= { globals
: { test
: true } };
1390 RuleTester
.setDefaultConfig(config
);
1391 RuleTester
.resetDefaultConfig();
1392 assert
.deepStrictEqual(
1393 RuleTester
.getDefaultConfig(),
1395 "The default configuration has not reset correctly"
1399 it("should enforce the global configuration to be an object", () => {
1402 * Set the default config for the rules tester
1403 * @param {Object} config configuration object
1404 * @returns {Function} Function to be executed
1407 function setConfig(config
) {
1409 RuleTester
.setDefaultConfig(config
);
1412 assert
.throw(setConfig());
1413 assert
.throw(setConfig(1));
1414 assert
.throw(setConfig(3.14));
1415 assert
.throw(setConfig("foo"));
1416 assert
.throw(setConfig(null));
1417 assert
.throw(setConfig(true));
1420 it("should pass-through the globals config to the tester then to the to rule", () => {
1421 const config
= { globals
: { test
: true } };
1423 RuleTester
.setDefaultConfig(config
);
1424 ruleTester
= new RuleTester();
1426 ruleTester
.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
1431 invalid
: [{ code
: "bar", errors
: 1, globals
: { foo
: true } }]
1435 it("should throw an error if AST was modified", () => {
1436 assert
.throws(() => {
1437 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast"), {
1443 }, "Rule should not modify AST.");
1444 assert
.throws(() => {
1445 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast"), {
1448 { code
: "var bar = 0;", errors
: ["error"] }
1451 }, "Rule should not modify AST.");
1454 it("should throw an error if AST was modified (at Program)", () => {
1455 assert
.throws(() => {
1456 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-first"), {
1462 }, "Rule should not modify AST.");
1463 assert
.throws(() => {
1464 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-first"), {
1467 { code
: "var bar = 0;", errors
: ["error"] }
1470 }, "Rule should not modify AST.");
1473 it("should throw an error if AST was modified (at Program:exit)", () => {
1474 assert
.throws(() => {
1475 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1481 }, "Rule should not modify AST.");
1482 assert
.throws(() => {
1483 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1486 { code
: "var bar = 0;", errors
: ["error"] }
1489 }, "Rule should not modify AST.");
1492 it("should throw an error if rule uses start and end properties on nodes, tokens or comments", () => {
1493 const usesStartEndRule
= {
1496 const sourceCode
= context
.getSourceCode();
1499 CallExpression(node
) {
1500 noop(node
.arguments
[1].start
);
1502 "BinaryExpression[operator='+']"(node
) {
1505 "UnaryExpression[operator='-']"(node
) {
1506 noop(sourceCode
.getFirstToken(node
).start
);
1508 ConditionalExpression(node
) {
1509 noop(sourceCode
.getFirstToken(node
).end
);
1511 BlockStatement(node
) {
1512 noop(sourceCode
.getCommentsInside(node
)[0].start
);
1514 ObjectExpression(node
) {
1515 noop(sourceCode
.getCommentsInside(node
)[0].end
);
1524 assert
.throws(() => {
1525 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1526 valid
: ["foo(a, b)"],
1529 }, "Use node.range[0] instead of node.start");
1530 assert
.throws(() => {
1531 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1533 invalid
: [{ code
: "var a = b * (c + d) / e;", errors
: 1 }]
1535 }, "Use node.range[1] instead of node.end");
1536 assert
.throws(() => {
1537 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1539 invalid
: [{ code
: "var a = -b * c;", errors
: 1 }]
1541 }, "Use token.range[0] instead of token.start");
1542 assert
.throws(() => {
1543 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1544 valid
: ["var a = b ? c : d;"],
1547 }, "Use token.range[1] instead of token.end");
1548 assert
.throws(() => {
1549 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1550 valid
: ["function f() { /* comment */ }"],
1553 }, "Use token.range[0] instead of token.start");
1554 assert
.throws(() => {
1555 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1557 invalid
: [{ code
: "var x = //\n {\n //comment\n //\n}", errors
: 1 }]
1559 }, "Use token.range[1] instead of token.end");
1561 const enhancedParserPath
= require
.resolve("../../fixtures/parsers/enhanced-parser");
1563 assert
.throws(() => {
1564 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1565 valid
: [{ code
: "foo(a, b)", parser
: enhancedParserPath
}],
1568 }, "Use node.range[0] instead of node.start");
1569 assert
.throws(() => {
1570 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1572 invalid
: [{ code
: "var a = b * (c + d) / e;", parser
: enhancedParserPath
, errors
: 1 }]
1574 }, "Use node.range[1] instead of node.end");
1575 assert
.throws(() => {
1576 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1578 invalid
: [{ code
: "var a = -b * c;", parser
: enhancedParserPath
, errors
: 1 }]
1580 }, "Use token.range[0] instead of token.start");
1581 assert
.throws(() => {
1582 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1583 valid
: [{ code
: "var a = b ? c : d;", parser
: enhancedParserPath
}],
1586 }, "Use token.range[1] instead of token.end");
1587 assert
.throws(() => {
1588 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1589 valid
: [{ code
: "function f() { /* comment */ }", parser
: enhancedParserPath
}],
1592 }, "Use token.range[0] instead of token.start");
1593 assert
.throws(() => {
1594 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1596 invalid
: [{ code
: "var x = //\n {\n //comment\n //\n}", parser
: enhancedParserPath
, errors
: 1 }]
1598 }, "Use token.range[1] instead of token.end");
1600 assert
.throws(() => {
1601 ruleTester
.run("uses-start-end", usesStartEndRule
, {
1602 valid
: [{ code
: "@foo class A {}", parser
: require
.resolve("../../fixtures/parsers/enhanced-parser2") }],
1605 }, "Use node.range[0] instead of node.start");
1608 it("should throw an error if no test scenarios given", () => {
1609 assert
.throws(() => {
1610 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"));
1611 }, "Test Scenarios for rule foo : Could not find test scenario object");
1614 it("should throw an error if no acceptable test scenario object is given", () => {
1615 assert
.throws(() => {
1616 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), []);
1617 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios\nCould not find any invalid test scenarios");
1618 assert
.throws(() => {
1619 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), "");
1620 }, "Test Scenarios for rule foo : Could not find test scenario object");
1621 assert
.throws(() => {
1622 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), 2);
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"), {});
1626 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios\nCould not find any invalid test scenarios");
1627 assert
.throws(() => {
1628 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1631 }, "Test Scenarios for rule foo is invalid:\nCould not find any invalid test scenarios");
1632 assert
.throws(() => {
1633 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1636 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios");
1639 // Nominal message/messageId use cases
1640 it("should assert match if message provided in both test and result.", () => {
1641 assert
.throws(() => {
1642 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly
, {
1644 invalid
: [{ code
: "foo", errors
: [{ message
: "something" }] }]
1646 }, /Avoid using variables named/u);
1648 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly
, {
1650 invalid
: [{ code
: "foo", errors
: [{ message
: "Avoid using variables named 'foo'." }] }]
1654 it("should assert match between messageId if provided in both test and result.", () => {
1655 assert
.throws(() => {
1656 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1658 invalid
: [{ code
: "foo", errors
: [{ messageId
: "unused" }] }]
1660 }, "messageId 'avoidFoo' does not match expected messageId 'unused'.");
1662 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1664 invalid
: [{ code
: "foo", errors
: [{ messageId
: "avoidFoo" }] }]
1667 it("should assert match between resulting message output if messageId and data provided in both test and result", () => {
1668 assert
.throws(() => {
1669 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1671 invalid
: [{ code
: "foo", errors
: [{ messageId
: "avoidFoo", data
: { name
: "notFoo" } }] }]
1673 }, "Hydrated message \"Avoid using variables named 'notFoo'.\" does not match \"Avoid using variables named 'foo'.\"");
1676 // messageId/message misconfiguration cases
1677 it("should throw if user tests for both message and messageId", () => {
1678 assert
.throws(() => {
1679 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1681 invalid
: [{ code
: "foo", errors
: [{ message
: "something", messageId
: "avoidFoo" }] }]
1683 }, "Error should not specify both 'message' and a 'messageId'.");
1685 it("should throw if user tests for messageId but the rule doesn't use the messageId meta syntax.", () => {
1686 assert
.throws(() => {
1687 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly
, {
1689 invalid
: [{ code
: "foo", errors
: [{ messageId
: "avoidFoo" }] }]
1691 }, "Error can not use 'messageId' if rule under test doesn't define 'meta.messages'");
1693 it("should throw if user tests for messageId not listed in the rule's meta syntax.", () => {
1694 assert
.throws(() => {
1695 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1697 invalid
: [{ code
: "foo", errors
: [{ messageId
: "useFoo" }] }]
1699 }, /Invalid messageId 'useFoo'/u);
1701 it("should throw if data provided without messageId.", () => {
1702 assert
.throws(() => {
1703 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData
, {
1705 invalid
: [{ code
: "foo", errors
: [{ data
: "something" }] }]
1707 }, "Error must specify 'messageId' if 'data' is used.");
1710 // fixable rules with or without `meta` property
1711 it("should not throw an error if a rule that has `meta.fixable` produces fixes", () => {
1712 const replaceProgramWith5Rule
= {
1719 context
.report({ node
, message
: "bad", fix
: fixer
=> fixer
.replaceText(node
, "5") });
1725 ruleTester
.run("replaceProgramWith5", replaceProgramWith5Rule
, {
1728 { code
: "var foo = bar;", output
: "5", errors
: 1 }
1732 it("should throw an error if a new-format rule that doesn't have `meta` produces fixes", () => {
1733 const replaceProgramWith5Rule
= {
1737 context
.report({ node
, message
: "bad", fix
: fixer
=> fixer
.replaceText(node
, "5") });
1743 assert
.throws(() => {
1744 ruleTester
.run("replaceProgramWith5", replaceProgramWith5Rule
, {
1747 { code
: "var foo = bar;", output
: "5", errors
: 1 }
1750 }, /Fixable rules must set the `meta\.fixable` property/u);
1752 it("should throw an error if a legacy-format rule produces fixes", () => {
1755 * Legacy-format rule (a function instead of an object with `create` method).
1756 * @param {RuleContext} context The ESLint rule context object.
1757 * @returns {Object} Listeners.
1759 function replaceProgramWith5Rule(context
) {
1762 context
.report({ node
, message
: "bad", fix
: fixer
=> fixer
.replaceText(node
, "5") });
1767 assert
.throws(() => {
1768 ruleTester
.run("replaceProgramWith5", replaceProgramWith5Rule
, {
1771 { code
: "var foo = bar;", output
: "5", errors
: 1 }
1774 }, /Fixable rules must set the `meta\.fixable` property/u);
1777 describe("suggestions", () => {
1778 it("should pass with valid suggestions (tested using desc)", () => {
1779 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1787 desc
: "Rename identifier 'foo' to 'bar'",
1795 it("should pass with suggestions on multiple lines", () => {
1796 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1800 code
: "function foo() {\n var foo = 1;\n}",
1803 desc
: "Rename identifier 'foo' to 'bar'",
1804 output
: "function bar() {\n var foo = 1;\n}"
1808 desc
: "Rename identifier 'foo' to 'bar'",
1809 output
: "function foo() {\n var bar = 1;\n}"
1817 it("should pass with valid suggestions (tested using messageIds)", () => {
1818 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1824 messageId
: "renameFoo",
1827 messageId
: "renameFoo",
1835 it("should pass with valid suggestions (one tested using messageIds, the other using desc)", () => {
1836 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1842 messageId
: "renameFoo",
1845 desc
: "Rename identifier 'foo' to 'baz'",
1853 it("should pass with valid suggestions (tested using both desc and messageIds for the same suggestion)", () => {
1854 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1860 desc
: "Rename identifier 'foo' to 'bar'",
1861 messageId
: "renameFoo",
1864 desc
: "Rename identifier 'foo' to 'baz'",
1865 messageId
: "renameFoo",
1873 it("should pass with valid suggestions (tested using only desc on a rule that utilizes meta.messages)", () => {
1874 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1880 desc
: "Rename identifier 'foo' to 'bar'",
1883 desc
: "Rename identifier 'foo' to 'baz'",
1891 it("should pass with valid suggestions (tested using messageIds and data)", () => {
1892 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1898 messageId
: "renameFoo",
1899 data
: { newName
: "bar" },
1902 messageId
: "renameFoo",
1903 data
: { newName
: "baz" },
1912 it("should pass when tested using empty suggestion test objects if the array length is correct", () => {
1913 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
1918 suggestions
: [{}, {}]
1924 it("should support explicitly expecting no suggestions", () => {
1925 [void 0, null, false, []].forEach(suggestions
=> {
1926 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/no-eval"), {
1929 code
: "eval('var foo');",
1938 it("should fail when expecting no suggestions and there are suggestions", () => {
1939 [void 0, null, false, []].forEach(suggestions
=> {
1940 assert
.throws(() => {
1941 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1950 }, "Error should have no suggestions on error with message: \"Avoid using identifiers named 'foo'.\"");
1954 it("should fail when testing for suggestions that don't exist", () => {
1955 assert
.throws(() => {
1956 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
1962 messageId
: "this-does-not-exist"
1967 }, "Error should have an array of suggestions. Instead received \"undefined\" on error with message: \"Bad var.\"");
1970 it("should fail when there are a different number of suggestions", () => {
1971 assert
.throws(() => {
1972 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
1978 desc
: "Rename identifier 'foo' to 'bar'",
1981 desc
: "Rename identifier 'foo' to 'baz'",
1987 }, "Error should have 2 suggestions. Instead found 1 suggestions");
1990 it("should throw if the suggestion description doesn't match", () => {
1991 assert
.throws(() => {
1992 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2004 }, "Error Suggestion at index 0 : desc should be \"not right\" but got \"Rename identifier 'foo' to 'bar'\" instead.");
2007 it("should throw if the suggestion description doesn't match (although messageIds match)", () => {
2008 assert
.throws(() => {
2009 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2015 desc
: "Rename identifier 'foo' to 'bar'",
2016 messageId
: "renameFoo",
2019 desc
: "Rename id 'foo' to 'baz'",
2020 messageId
: "renameFoo",
2026 }, "Error Suggestion at index 1 : desc should be \"Rename id 'foo' to 'baz'\" but got \"Rename identifier 'foo' to 'baz'\" instead.");
2029 it("should throw if the suggestion messageId doesn't match", () => {
2030 assert
.throws(() => {
2031 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2037 messageId
: "unused",
2040 messageId
: "renameFoo",
2046 }, "Error Suggestion at index 0 : messageId should be 'unused' but got 'renameFoo' instead.");
2049 it("should throw if the suggestion messageId doesn't match (although descriptions match)", () => {
2050 assert
.throws(() => {
2051 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2057 desc
: "Rename identifier 'foo' to 'bar'",
2058 messageId
: "renameFoo",
2061 desc
: "Rename identifier 'foo' to 'baz'",
2062 messageId
: "avoidFoo",
2068 }, "Error Suggestion at index 1 : messageId should be 'avoidFoo' but got 'renameFoo' instead.");
2071 it("should throw if test specifies messageId for a rule that doesn't have meta.messages", () => {
2072 assert
.throws(() => {
2073 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2079 messageId
: "renameFoo",
2085 }, "Error Suggestion at index 0 : Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.");
2088 it("should throw if test specifies messageId that doesn't exist in the rule's meta.messages", () => {
2089 assert
.throws(() => {
2090 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2096 messageId
: "renameFoo",
2099 messageId
: "removeFoo",
2105 }, "Error Suggestion at index 1 : Test has invalid messageId 'removeFoo', the rule under test allows only one of ['avoidFoo', 'unused', 'renameFoo'].");
2108 it("should throw if hydrated desc doesn't match (wrong data value)", () => {
2109 assert
.throws(() => {
2110 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2116 messageId
: "renameFoo",
2117 data
: { newName
: "car" },
2120 messageId
: "renameFoo",
2121 data
: { newName
: "baz" },
2127 }, "Error Suggestion at index 0 : Hydrated test desc \"Rename identifier 'foo' to 'car'\" does not match received desc \"Rename identifier 'foo' to 'bar'\".");
2130 it("should throw if hydrated desc doesn't match (wrong data key)", () => {
2131 assert
.throws(() => {
2132 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2138 messageId
: "renameFoo",
2139 data
: { newName
: "bar" },
2142 messageId
: "renameFoo",
2143 data
: { name
: "baz" },
2149 }, "Error Suggestion at index 1 : Hydrated test desc \"Rename identifier 'foo' to '{{ newName }}'\" does not match received desc \"Rename identifier 'foo' to 'baz'\".");
2152 it("should throw if test specifies both desc and data", () => {
2153 assert
.throws(() => {
2154 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2160 desc
: "Rename identifier 'foo' to 'bar'",
2161 messageId
: "renameFoo",
2162 data
: { newName
: "bar" },
2165 messageId
: "renameFoo",
2166 data
: { newName
: "baz" },
2172 }, "Error Suggestion at index 0 : Test should not specify both 'desc' and 'data'.");
2175 it("should throw if test uses data but doesn't specify messageId", () => {
2176 assert
.throws(() => {
2177 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2183 messageId
: "renameFoo",
2184 data
: { newName
: "bar" },
2187 data
: { newName
: "baz" },
2193 }, "Error Suggestion at index 1 : Test must specify 'messageId' if 'data' is used.");
2196 it("should throw if the resulting suggestion output doesn't match", () => {
2197 assert
.throws(() => {
2198 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2204 desc
: "Rename identifier 'foo' to 'bar'",
2210 }, "Expected the applied suggestion fix to match the test suggestion output");
2213 it("should fail when specified suggestion isn't an object", () => {
2214 assert
.throws(() => {
2215 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2224 }, "Test suggestion in 'suggestions' array must be an object.");
2226 assert
.throws(() => {
2227 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2234 messageId
: "renameFoo",
2237 "Rename identifier 'foo' to 'baz'"
2242 }, "Test suggestion in 'suggestions' array must be an object.");
2245 it("should fail when the suggestion is an object with an unknown property name", () => {
2246 assert
.throws(() => {
2247 ruleTester
.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic
, {
2255 message
: "Rename identifier 'foo' to 'bar'"
2260 }, /Invalid suggestion property name 'message'/u);
2263 it("should fail when any of the suggestions is an object with an unknown property name", () => {
2264 assert
.throws(() => {
2265 ruleTester
.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds
, {
2271 messageId
: "renameFoo",
2274 messageId
: "renameFoo",
2280 }, /Invalid suggestion property name 'outpt'/u);
2283 it("should throw an error if a rule that doesn't have `meta.hasSuggestions` enabled produces suggestions", () => {
2284 assert
.throws(() => {
2285 ruleTester
.run("suggestions-missing-hasSuggestions-property", require("../../fixtures/testers/rule-tester/suggestions").withoutHasSuggestionsProperty
, {
2288 { code
: "var foo = bar;", output
: "5", errors
: 1 }
2291 }, "Rules with suggestions must set the `meta.hasSuggestions` property to `true`.");
2295 describe("naming test cases", () => {
2298 * Asserts that a particular value will be emitted from an EventEmitter.
2299 * @param {EventEmitter} emitter The emitter that should emit a value
2300 * @param {string} emitType The type of emission to listen for
2301 * @param {any} expectedValue The value that should be emitted
2302 * @returns {Promise<void>} A Promise that fulfills if the value is emitted, and rejects if something else is emitted.
2303 * The Promise will be indefinitely pending if no value is emitted.
2305 function assertEmitted(emitter
, emitType
, expectedValue
) {
2306 return new Promise((resolve
, reject
) => {
2307 emitter
.once(emitType
, emittedValue
=> {
2308 if (emittedValue
=== expectedValue
) {
2311 reject(new Error(`Expected ${expectedValue} to be emitted but ${emittedValue} was emitted instead.`));
2317 it("should use the first argument as the name of the test suite", () => {
2318 const assertion
= assertEmitted(ruleTesterTestEmitter
, "describe", "this-is-a-rule-name");
2320 ruleTester
.run("this-is-a-rule-name", require("../../fixtures/testers/rule-tester/no-var"), {
2328 it("should use the test code as the name of the tests for valid code (string form)", () => {
2329 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "valid(code);");
2331 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2341 it("should use the test code as the name of the tests for valid code (object form)", () => {
2342 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "valid(code);");
2344 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2347 code
: "valid(code);"
2356 it("should use the test code as the name of the tests for invalid code", () => {
2357 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "var x = invalid(code);");
2359 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2363 code
: "var x = invalid(code);",
2364 output
: " x = invalid(code);",
2373 // https://github.com/eslint/eslint/issues/8142
2374 it("should use the empty string as the name of the test if the test case is an empty string", () => {
2375 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "");
2377 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2389 it('should use the "name" property if set to a non-empty string', () => {
2390 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "my test");
2392 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2397 code
: "var x = invalid(code);",
2398 output
: " x = invalid(code);",
2407 it('should use the "name" property if set to a non-empty string for valid cases too', () => {
2408 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "my test");
2410 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2414 code
: "valid(code);"
2424 it('should use the test code as the name if the "name" property is set to an empty string', () => {
2425 const assertion
= assertEmitted(ruleTesterTestEmitter
, "it", "var x = invalid(code);");
2427 ruleTester
.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
2432 code
: "var x = invalid(code);",
2433 output
: " x = invalid(code);",
2443 // https://github.com/eslint/eslint/issues/11615
2444 it("should fail the case if autofix made a syntax error.", () => {
2445 assert
.throw(() => {
2457 message
: "make a syntax error",
2459 return fixer
.replaceText(node
, "one two");
2471 }, /A fatal parsing error occurred in autofix.\nError: .+\nAutofix output:\n.+/u);
2474 describe("sanitize test cases", () => {
2475 let originalRuleTesterIt
;
2476 let spyRuleTesterIt
;
2479 originalRuleTesterIt
= RuleTester
.it
;
2480 spyRuleTesterIt
= sinon
.spy();
2481 RuleTester
.it
= spyRuleTesterIt
;
2484 RuleTester
.it
= originalRuleTesterIt
;
2487 spyRuleTesterIt
.resetHistory();
2488 ruleTester
= new RuleTester();
2490 it("should present newline when using back-tick as new line", () => {
2494 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
2499 errors
: [/^Bad var/u]
2503 sinon
.assert
.calledWith(spyRuleTesterIt
, code
);
2505 it("should present \\u0000 as a string", () => {
2506 const code
= "\u0000";
2508 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
2513 errors
: [/^Bad var/u]
2517 sinon
.assert
.calledWith(spyRuleTesterIt
, "\\u0000");
2519 it("should present the pipe character correctly", () => {
2520 const code
= "var foo = bar || baz;";
2522 ruleTester
.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
2527 errors
: [/^Bad var/u]
2531 sinon
.assert
.calledWith(spyRuleTesterIt
, code
);
2536 describe("SourceCode#getComments()", () => {
2537 const useGetCommentsRule
= {
2538 create
: context
=> ({
2540 const sourceCode
= context
.getSourceCode();
2542 sourceCode
.getComments(node
);
2547 it("should throw if called from a valid test case", () => {
2548 assert
.throws(() => {
2549 ruleTester
.run("use-get-comments", useGetCommentsRule
, {
2553 }, /`SourceCode#getComments\(\)` is deprecated/u);
2556 it("should throw if called from an invalid test case", () => {
2557 assert
.throws(() => {
2558 ruleTester
.run("use-get-comments", useGetCommentsRule
, {
2565 }, /`SourceCode#getComments\(\)` is deprecated/u);