]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/rule-tester/rule-tester.js
upgrade to v7.0.0
[pve-eslint.git] / eslint / tests / lib / rule-tester / rule-tester.js
1 /**
2 * @fileoverview Tests for ESLint Tester
3 * @author Nicholas C. Zakas
4 */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
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 { noop } = require("lodash");
16
17 const NODE_ASSERT_STRICT_EQUAL_OPERATOR = (() => {
18 try {
19 nodeAssert.strictEqual(1, 2);
20 } catch (err) {
21 return err.operator;
22 }
23 throw new Error("unexpected successful assertion");
24 })();
25
26 //------------------------------------------------------------------------------
27 // Rewire Things
28 //------------------------------------------------------------------------------
29
30 /*
31 * So here's the situation. Because RuleTester uses it() and describe() from
32 * Mocha, any failures would show up in the output of this test file. That means
33 * when we tested that a failure is thrown, that would also count as a failure
34 * in the testing for RuleTester. In order to remove those results from the
35 * results of this file, we need to overwrite it() and describe() just in
36 * RuleTester to do nothing but run code. Effectively, it() and describe()
37 * just become regular functions inside of index.js, not at all related to Mocha.
38 * That allows the results of this file to be untainted and therefore accurate.
39 *
40 * To assert that the right arguments are passed to RuleTester.describe/it, an
41 * event emitter is used which emits the arguments.
42 */
43
44 const ruleTesterTestEmitter = new EventEmitter();
45
46 //------------------------------------------------------------------------------
47 // Tests
48 //------------------------------------------------------------------------------
49
50 describe("RuleTester", () => {
51
52 // Stub `describe()` and `it()` while this test suite.
53 before(() => {
54 RuleTester.describe = function(text, method) {
55 ruleTesterTestEmitter.emit("describe", text, method);
56 return method.call(this);
57 };
58 RuleTester.it = function(text, method) {
59 ruleTesterTestEmitter.emit("it", text, method);
60 return method.call(this);
61 };
62 });
63 after(() => {
64 RuleTester.describe = null;
65 RuleTester.it = null;
66 });
67
68 let ruleTester;
69
70 /**
71 * A helper function to verify Node.js core error messages.
72 * @param {string} actual The actual input
73 * @param {string} expected The expected input
74 * @returns {Function} Error callback to verify that the message is correct
75 * for the actual and expected input.
76 */
77 function assertErrorMatches(actual, expected) {
78 const err = new nodeAssert.AssertionError({
79 actual,
80 expected,
81 operator: NODE_ASSERT_STRICT_EQUAL_OPERATOR
82 });
83
84 return err.message;
85 }
86
87 beforeEach(() => {
88 RuleTester.resetDefaultConfig();
89 ruleTester = new RuleTester();
90 });
91
92 it("should not throw an error when everything passes", () => {
93 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
94 valid: [
95 "Eval(foo)"
96 ],
97 invalid: [
98 { code: "eval(foo)", errors: [{ message: "eval sucks.", type: "CallExpression" }] }
99 ]
100 });
101 });
102
103 it("should throw an error when valid code is invalid", () => {
104
105 assert.throws(() => {
106 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
107 valid: [
108 "eval(foo)"
109 ],
110 invalid: [
111 { code: "eval(foo)", errors: [{ message: "eval sucks.", type: "CallExpression" }] }
112 ]
113 });
114 }, /Should have no errors but had 1/u);
115 });
116
117 it("should throw an error when valid code is invalid", () => {
118
119 assert.throws(() => {
120 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
121 valid: [
122 { code: "eval(foo)" }
123 ],
124 invalid: [
125 { code: "eval(foo)", errors: [{ message: "eval sucks.", type: "CallExpression" }] }
126 ]
127 });
128 }, /Should have no errors but had 1/u);
129 });
130
131 it("should throw an error if invalid code is valid", () => {
132
133 assert.throws(() => {
134 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
135 valid: [
136 "Eval(foo)"
137 ],
138 invalid: [
139 { code: "Eval(foo)", errors: [{ message: "eval sucks.", type: "CallExpression" }] }
140 ]
141 });
142 }, /Should have 1 error but had 0/u);
143 });
144
145 it("should throw an error when the error message is wrong", () => {
146 assert.throws(() => {
147 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
148
149 // Only the invalid test matters here
150 valid: [
151 "bar = baz;"
152 ],
153 invalid: [
154 { code: "var foo = bar;", errors: [{ message: "Bad error message." }] }
155 ]
156 });
157 }, assertErrorMatches("Bad var.", "Bad error message."));
158 });
159
160 it("should throw an error when the error message regex does not match", () => {
161 assert.throws(() => {
162 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
163 valid: [],
164 invalid: [
165 { code: "var foo = bar;", errors: [{ message: /Bad error message/u }] }
166 ]
167 });
168 }, /Expected 'Bad var.' to match \/Bad error message\//u);
169 });
170
171 it("should throw an error when the error is not a supported type", () => {
172 assert.throws(() => {
173 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
174
175 // Only the invalid test matters here
176 valid: [
177 "bar = baz;"
178 ],
179 invalid: [
180 { code: "var foo = bar;", errors: [42] }
181 ]
182 });
183 }, /Error should be a string, object, or RegExp/u);
184 });
185
186 it("should throw an error when any of the errors is not a supported type", () => {
187 assert.throws(() => {
188 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
189
190 // Only the invalid test matters here
191 valid: [
192 "bar = baz;"
193 ],
194 invalid: [
195 { code: "var foo = bar; var baz = quux", errors: [{ type: "VariableDeclaration" }, null] }
196 ]
197 });
198 }, /Error should be a string, object, or RegExp/u);
199 });
200
201 it("should throw an error when the error is a string and it does not match error message", () => {
202 assert.throws(() => {
203 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
204
205 // Only the invalid test matters here
206 valid: [
207 "bar = baz;"
208 ],
209 invalid: [
210 { code: "var foo = bar;", errors: ["Bad error message."] }
211 ]
212 });
213 }, assertErrorMatches("Bad var.", "Bad error message."));
214 });
215
216 it("should throw an error when the error is a string and it does not match error message", () => {
217 assert.throws(() => {
218 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
219
220 valid: [
221 ],
222 invalid: [
223 { code: "var foo = bar;", errors: [/Bad error message/u] }
224 ]
225 });
226 }, /Expected 'Bad var.' to match \/Bad error message\//u);
227 });
228
229 it("should not throw an error when the error is a string and it matches error message", () => {
230 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
231
232 // Only the invalid test matters here
233 valid: [
234 "bar = baz;"
235 ],
236 invalid: [
237 { code: "var foo = bar;", output: " foo = bar;", errors: ["Bad var."] }
238 ]
239 });
240 });
241
242 it("should not throw an error when the error is a regex and it matches error message", () => {
243 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
244 valid: [],
245 invalid: [
246 { code: "var foo = bar;", output: " foo = bar;", errors: [/^Bad var/u] }
247 ]
248 });
249 });
250
251 it("should throw an error when the error is an object with an unknown property name", () => {
252 assert.throws(() => {
253 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
254 valid: [
255 "bar = baz;"
256 ],
257 invalid: [
258 { code: "var foo = bar;", errors: [{ Message: "Bad var." }] }
259 ]
260 });
261 }, /Invalid error property name 'Message'/u);
262 });
263
264 it("should throw an error when any of the errors is an object with an unknown property name", () => {
265 assert.throws(() => {
266 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
267 valid: [
268 "bar = baz;"
269 ],
270 invalid: [
271 {
272 code: "var foo = bar; var baz = quux",
273 errors: [
274 { message: "Bad var.", type: "VariableDeclaration" },
275 { message: "Bad var.", typo: "VariableDeclaration" }
276 ]
277 }
278 ]
279 });
280 }, /Invalid error property name 'typo'/u);
281 });
282
283 it("should not throw an error when the error is a regex in an object and it matches error message", () => {
284 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
285 valid: [],
286 invalid: [
287 { code: "var foo = bar;", output: " foo = bar;", errors: [{ message: /^Bad var/u }] }
288 ]
289 });
290 });
291
292 it("should throw an error when the expected output doesn't match", () => {
293 assert.throws(() => {
294 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
295 valid: [
296 "bar = baz;"
297 ],
298 invalid: [
299 { code: "var foo = bar;", output: "foo = bar", errors: [{ message: "Bad var.", type: "VariableDeclaration" }] }
300 ]
301 });
302 }, /Output is incorrect/u);
303 });
304
305 it("should use strict equality to compare output", () => {
306 const replaceProgramWith5Rule = {
307 create: context => ({
308 Program(node) {
309 context.report({ node, message: "bad", fix: fixer => fixer.replaceText(node, "5") });
310 }
311 })
312 };
313
314 // Should not throw.
315 ruleTester.run("foo", replaceProgramWith5Rule, {
316 valid: [],
317 invalid: [
318 { code: "var foo = bar;", output: "5", errors: 1 }
319 ]
320 });
321
322 assert.throws(() => {
323 ruleTester.run("foo", replaceProgramWith5Rule, {
324 valid: [],
325 invalid: [
326 { code: "var foo = bar;", output: 5, errors: 1 }
327 ]
328 });
329 }, /Output is incorrect/u);
330 });
331
332 it("should throw an error when the expected output doesn't match and errors is just a number", () => {
333 assert.throws(() => {
334 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
335 valid: [
336 "bar = baz;"
337 ],
338 invalid: [
339 { code: "var foo = bar;", output: "foo = bar", errors: 1 }
340 ]
341 });
342 }, /Output is incorrect/u);
343 });
344
345 it("should not throw an error when the expected output is null and no errors produce output", () => {
346 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
347 valid: [
348 "bar = baz;"
349 ],
350 invalid: [
351 { code: "eval(x)", errors: 1, output: null },
352 { code: "eval(x); eval(y);", errors: 2, output: null }
353 ]
354 });
355 });
356
357 it("should throw an error when the expected output is null and problems produce output", () => {
358 assert.throws(() => {
359 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
360 valid: [
361 "bar = baz;"
362 ],
363 invalid: [
364 { code: "var foo = bar;", output: null, errors: 1 }
365 ]
366 });
367 }, /Expected no autofixes to be suggested/u);
368
369 assert.throws(() => {
370 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
371 valid: [
372 "bar = baz;"
373 ],
374 invalid: [
375 {
376 code: "var foo = bar; var qux = boop;",
377 output: null,
378 errors: 2
379 }
380 ]
381 });
382 }, /Expected no autofixes to be suggested/u);
383 });
384
385 it("should throw an error when the expected output is null and only some problems produce output", () => {
386 assert.throws(() => {
387 ruleTester.run("fixes-one-problem", require("../../fixtures/testers/rule-tester/fixes-one-problem"), {
388 valid: [],
389 invalid: [
390 { code: "foo", output: null, errors: 2 }
391 ]
392 });
393 }, /Expected no autofixes to be suggested/u);
394 });
395
396 it("should throw an error when the expected output isn't specified and problems produce output", () => {
397 assert.throws(() => {
398 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
399 valid: [
400 "bar = baz;"
401 ],
402 invalid: [
403 { code: "var foo = bar;", errors: 1 }
404 ]
405 });
406 }, "The rule fixed the code. Please add 'output' property.");
407 });
408
409 it("should throw an error if invalid code specifies wrong type", () => {
410 assert.throws(() => {
411 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
412 valid: [
413 "Eval(foo)"
414 ],
415 invalid: [
416 { code: "eval(foo)", errors: [{ message: "eval sucks.", type: "CallExpression2" }] }
417 ]
418 });
419 }, /Error type should be CallExpression2, found CallExpression/u);
420 });
421
422 it("should throw an error if invalid code specifies wrong line", () => {
423 assert.throws(() => {
424 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
425 valid: [
426 "Eval(foo)"
427 ],
428 invalid: [
429 { code: "eval(foo)", errors: [{ message: "eval sucks.", type: "CallExpression", line: 5 }] }
430 ]
431 });
432 }, /Error line should be 5/u);
433 });
434
435 it("should not skip line assertion if line is a falsy value", () => {
436 assert.throws(() => {
437 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
438 valid: [
439 "Eval(foo)"
440 ],
441 invalid: [
442 { code: "\neval(foo)", errors: [{ message: "eval sucks.", type: "CallExpression", line: 0 }] }
443 ]
444 });
445 }, /Error line should be 0/u);
446 });
447
448 it("should throw an error if invalid code specifies wrong column", () => {
449 const wrongColumn = 10,
450 expectedErrorMessage = "Error column should be 1";
451
452 assert.throws(() => {
453 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
454 valid: ["Eval(foo)"],
455 invalid: [{
456 code: "eval(foo)",
457 errors: [{
458 message: "eval sucks.",
459 column: wrongColumn
460 }]
461 }]
462 });
463 }, expectedErrorMessage);
464 });
465
466 it("should not skip column assertion if column is a falsy value", () => {
467 assert.throws(() => {
468 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
469 valid: ["Eval(foo)"],
470 invalid: [{
471 code: "var foo; eval(foo)",
472 errors: [{ message: "eval sucks.", column: 0 }]
473 }]
474 });
475 }, /Error column should be 0/u);
476 });
477
478 it("should throw an error if invalid code specifies wrong endLine", () => {
479 assert.throws(() => {
480 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
481 valid: [
482 "bar = baz;"
483 ],
484 invalid: [
485 { code: "var foo = bar;", output: "foo = bar", errors: [{ message: "Bad var.", type: "VariableDeclaration", endLine: 10 }] }
486 ]
487 });
488 }, "Error endLine should be 10");
489 });
490
491 it("should throw an error if invalid code specifies wrong endColumn", () => {
492 assert.throws(() => {
493 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
494 valid: [
495 "bar = baz;"
496 ],
497 invalid: [
498 { code: "var foo = bar;", output: "foo = bar", errors: [{ message: "Bad var.", type: "VariableDeclaration", endColumn: 10 }] }
499 ]
500 });
501 }, "Error endColumn should be 10");
502 });
503
504 it("should throw an error if invalid code has the wrong number of errors", () => {
505 assert.throws(() => {
506 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
507 valid: [
508 "Eval(foo)"
509 ],
510 invalid: [
511 {
512 code: "eval(foo)",
513 errors: [
514 { message: "eval sucks.", type: "CallExpression" },
515 { message: "eval sucks.", type: "CallExpression" }
516 ]
517 }
518 ]
519 });
520 }, /Should have 2 errors but had 1/u);
521 });
522
523 it("should throw an error if invalid code does not have errors", () => {
524 assert.throws(() => {
525 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
526 valid: [
527 "Eval(foo)"
528 ],
529 invalid: [
530 { code: "eval(foo)" }
531 ]
532 });
533 }, /Did not specify errors for an invalid test of no-eval/u);
534 });
535
536 it("should throw an error if invalid code has the wrong explicit number of errors", () => {
537 assert.throws(() => {
538 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
539 valid: [
540 "Eval(foo)"
541 ],
542 invalid: [
543 { code: "eval(foo)", errors: 2 }
544 ]
545 });
546 }, /Should have 2 errors but had 1/u);
547 });
548
549 it("should throw an error if there's a parsing error in a valid test", () => {
550 assert.throws(() => {
551 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
552 valid: [
553 "1eval('foo')"
554 ],
555 invalid: [
556 { code: "eval('foo')", errors: [{}] }
557 ]
558 });
559 }, /fatal parsing error/iu);
560 });
561
562 it("should throw an error if there's a parsing error in an invalid test", () => {
563 assert.throws(() => {
564 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
565 valid: [
566 "noeval('foo')"
567 ],
568 invalid: [
569 { code: "1eval('foo')", errors: [{}] }
570 ]
571 });
572 }, /fatal parsing error/iu);
573 });
574
575 it("should throw an error if there's a parsing error in an invalid test and errors is just a number", () => {
576 assert.throws(() => {
577 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
578 valid: [
579 "noeval('foo')"
580 ],
581 invalid: [
582 { code: "1eval('foo')", errors: 1 }
583 ]
584 });
585 }, /fatal parsing error/iu);
586 });
587
588 // https://github.com/eslint/eslint/issues/4779
589 it("should throw an error if there's a parsing error and output doesn't match", () => {
590 assert.throws(() => {
591 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
592 valid: [],
593 invalid: [
594 { code: "eval(`foo`)", output: "eval(`foo`);", errors: [{}] }
595 ]
596 });
597 }, /fatal parsing error/iu);
598 });
599
600 it("should not throw an error if invalid code has at least an expected empty error object", () => {
601 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
602 valid: ["Eval(foo)"],
603 invalid: [{
604 code: "eval(foo)",
605 errors: [{}]
606 }]
607 });
608 });
609
610 it("should pass-through the globals config of valid tests to the to rule", () => {
611 ruleTester.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
612 valid: [
613 "var test = 'foo'",
614 {
615 code: "var test2 = 'bar'",
616 globals: { test: true }
617 }
618 ],
619 invalid: [{ code: "bar", errors: 1 }]
620 });
621 });
622
623 it("should pass-through the globals config of invalid tests to the to rule", () => {
624 ruleTester.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
625 valid: ["var test = 'foo'"],
626 invalid: [
627 {
628 code: "var test = 'foo'; var foo = 'bar'",
629 errors: 1
630 },
631 {
632 code: "var test = 'foo'",
633 globals: { foo: true },
634 errors: [{ message: "Global variable foo should not be used." }]
635 }
636 ]
637 });
638 });
639
640 it("should pass-through the settings config to rules", () => {
641 ruleTester.run("no-test-settings", require("../../fixtures/testers/rule-tester/no-test-settings"), {
642 valid: [
643 {
644 code: "var test = 'bar'", settings: { test: 1 }
645 }
646 ],
647 invalid: [
648 {
649 code: "var test = 'bar'", settings: { "no-test": 22 }, errors: 1
650 }
651 ]
652 });
653 });
654
655 it("should pass-through the filename to the rule", () => {
656 (function() {
657 ruleTester.run("", require("../../fixtures/testers/rule-tester/no-test-filename"), {
658 valid: [
659 {
660 code: "var foo = 'bar'",
661 filename: "somefile.js"
662 }
663 ],
664 invalid: [
665 {
666 code: "var foo = 'bar'",
667 errors: [
668 { message: "Filename test was not defined." }
669 ]
670 }
671 ]
672 });
673 }());
674 });
675
676 it("should pass-through the options to the rule", () => {
677 ruleTester.run("no-invalid-args", require("../../fixtures/testers/rule-tester/no-invalid-args"), {
678 valid: [
679 {
680 code: "var foo = 'bar'",
681 options: [false]
682 }
683 ],
684 invalid: [
685 {
686 code: "var foo = 'bar'",
687 options: [true],
688 errors: [{ message: "Invalid args" }]
689 }
690 ]
691 });
692 });
693
694 it("should throw an error if the options are an object", () => {
695 assert.throws(() => {
696 ruleTester.run("no-invalid-args", require("../../fixtures/testers/rule-tester/no-invalid-args"), {
697 valid: [
698 {
699 code: "foo",
700 options: { ok: true }
701 }
702 ],
703 invalid: []
704 });
705 }, /options must be an array/u);
706 });
707
708 it("should throw an error if the options are a number", () => {
709 assert.throws(() => {
710 ruleTester.run("no-invalid-args", require("../../fixtures/testers/rule-tester/no-invalid-args"), {
711 valid: [
712 {
713 code: "foo",
714 options: 0
715 }
716 ],
717 invalid: []
718 });
719 }, /options must be an array/u);
720 });
721
722 it("should pass-through the parser to the rule", () => {
723 const spy = sinon.spy(ruleTester.linter, "verify");
724
725 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
726 valid: [
727 {
728 code: "Eval(foo)"
729 }
730 ],
731 invalid: [
732 {
733 code: "eval(foo)",
734 parser: require.resolve("esprima"),
735 errors: [{}]
736 }
737 ]
738 });
739 assert.strictEqual(spy.args[1][1].parser, require.resolve("esprima"));
740 });
741
742 it("should pass-through services from parseForESLint to the rule", () => {
743 const enhancedParserPath = require.resolve("../../fixtures/parsers/enhanced-parser");
744 const disallowHiRule = {
745 create: context => ({
746 Literal(node) {
747 const disallowed = context.parserServices.test.getMessage(); // returns "Hi!"
748
749 if (node.value === disallowed) {
750 context.report({ node, message: `Don't use '${disallowed}'` });
751 }
752 }
753 })
754 };
755
756 ruleTester.run("no-hi", disallowHiRule, {
757 valid: [
758 {
759 code: "'Hello!'",
760 parser: enhancedParserPath
761 }
762 ],
763 invalid: [
764 {
765 code: "'Hi!'",
766 parser: enhancedParserPath,
767 errors: [{ message: "Don't use 'Hi!'" }]
768 }
769 ]
770 });
771 });
772
773 it("should prevent invalid options schemas", () => {
774 assert.throws(() => {
775 ruleTester.run("no-invalid-schema", require("../../fixtures/testers/rule-tester/no-invalid-schema"), {
776 valid: [
777 "var answer = 6 * 7;",
778 { code: "var answer = 6 * 7;", options: [] }
779 ],
780 invalid: [
781 { code: "var answer = 6 * 7;", options: ["bar"], errors: [{ message: "Expected nothing." }] }
782 ]
783 });
784 }, "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");
785
786 });
787
788 it("should prevent schema violations in options", () => {
789 assert.throws(() => {
790 ruleTester.run("no-schema-violation", require("../../fixtures/testers/rule-tester/no-schema-violation"), {
791 valid: [
792 "var answer = 6 * 7;",
793 { code: "var answer = 6 * 7;", options: ["foo"] }
794 ],
795 invalid: [
796 { code: "var answer = 6 * 7;", options: ["bar"], errors: [{ message: "Expected foo." }] }
797 ]
798 });
799 }, /Value "bar" should be equal to one of the allowed values./u);
800
801 });
802
803 it("should disallow invalid defaults in rules", () => {
804 const ruleWithInvalidDefaults = {
805 meta: {
806 schema: [
807 {
808 oneOf: [
809 { enum: ["foo"] },
810 {
811 type: "object",
812 properties: {
813 foo: {
814 enum: ["foo", "bar"],
815 default: "foo"
816 }
817 },
818 additionalProperties: false
819 }
820 ]
821 }
822 ]
823 },
824 create: () => ({})
825 };
826
827 assert.throws(() => {
828 ruleTester.run("invalid-defaults", ruleWithInvalidDefaults, {
829 valid: [
830 {
831 code: "foo",
832 options: [{}]
833 }
834 ],
835 invalid: []
836 });
837 }, /Schema for rule invalid-defaults is invalid: default is ignored for: data1\.foo/u);
838 });
839
840 it("throw an error when an unknown config option is included", () => {
841 assert.throws(() => {
842 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
843 valid: [
844 { code: "Eval(foo)", foo: "bar" }
845 ],
846 invalid: []
847 });
848 }, /ESLint configuration in rule-tester is invalid./u);
849 });
850
851 it("throw an error when an invalid config value is included", () => {
852 assert.throws(() => {
853 ruleTester.run("no-eval", require("../../fixtures/testers/rule-tester/no-eval"), {
854 valid: [
855 { code: "Eval(foo)", env: ["es6"] }
856 ],
857 invalid: []
858 });
859 }, /Property "env" is the wrong type./u);
860 });
861
862 it("should pass-through the tester config to the rule", () => {
863 ruleTester = new RuleTester({
864 globals: { test: true }
865 });
866
867 ruleTester.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
868 valid: [
869 "var test = 'foo'",
870 "var test2 = test"
871 ],
872 invalid: [{ code: "bar", errors: 1, globals: { foo: true } }]
873 });
874 });
875
876 it("should correctly set the globals configuration", () => {
877 const config = { globals: { test: true } };
878
879 RuleTester.setDefaultConfig(config);
880 assert(
881 RuleTester.getDefaultConfig().globals.test,
882 "The default config object is incorrect"
883 );
884 });
885
886 it("should correctly reset the global configuration", () => {
887 const config = { globals: { test: true } };
888
889 RuleTester.setDefaultConfig(config);
890 RuleTester.resetDefaultConfig();
891 assert.deepStrictEqual(
892 RuleTester.getDefaultConfig(),
893 { rules: {} },
894 "The default configuration has not reset correctly"
895 );
896 });
897
898 it("should enforce the global configuration to be an object", () => {
899
900 /**
901 * Set the default config for the rules tester
902 * @param {Object} config configuration object
903 * @returns {Function} Function to be executed
904 * @private
905 */
906 function setConfig(config) {
907 return function() {
908 RuleTester.setDefaultConfig(config);
909 };
910 }
911 assert.throw(setConfig());
912 assert.throw(setConfig(1));
913 assert.throw(setConfig(3.14));
914 assert.throw(setConfig("foo"));
915 assert.throw(setConfig(null));
916 assert.throw(setConfig(true));
917 });
918
919 it("should pass-through the globals config to the tester then to the to rule", () => {
920 const config = { globals: { test: true } };
921
922 RuleTester.setDefaultConfig(config);
923 ruleTester = new RuleTester();
924
925 ruleTester.run("no-test-global", require("../../fixtures/testers/rule-tester/no-test-global"), {
926 valid: [
927 "var test = 'foo'",
928 "var test2 = test"
929 ],
930 invalid: [{ code: "bar", errors: 1, globals: { foo: true } }]
931 });
932 });
933
934 it("should throw an error if AST was modified", () => {
935 assert.throws(() => {
936 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast"), {
937 valid: [
938 "var foo = 0;"
939 ],
940 invalid: []
941 });
942 }, "Rule should not modify AST.");
943 assert.throws(() => {
944 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast"), {
945 valid: [],
946 invalid: [
947 { code: "var bar = 0;", errors: ["error"] }
948 ]
949 });
950 }, "Rule should not modify AST.");
951 });
952
953 it("should throw an error if AST was modified (at Program)", () => {
954 assert.throws(() => {
955 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-first"), {
956 valid: [
957 "var foo = 0;"
958 ],
959 invalid: []
960 });
961 }, "Rule should not modify AST.");
962 assert.throws(() => {
963 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-first"), {
964 valid: [],
965 invalid: [
966 { code: "var bar = 0;", errors: ["error"] }
967 ]
968 });
969 }, "Rule should not modify AST.");
970 });
971
972 it("should throw an error if AST was modified (at Program:exit)", () => {
973 assert.throws(() => {
974 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
975 valid: [
976 "var foo = 0;"
977 ],
978 invalid: []
979 });
980 }, "Rule should not modify AST.");
981 assert.throws(() => {
982 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
983 valid: [],
984 invalid: [
985 { code: "var bar = 0;", errors: ["error"] }
986 ]
987 });
988 }, "Rule should not modify AST.");
989 });
990
991 it("should throw an error if rule uses start and end properties on nodes, tokens or comments", () => {
992 const usesStartEndRule = {
993 create(context) {
994
995 const sourceCode = context.getSourceCode();
996
997 return {
998 CallExpression(node) {
999 noop(node.arguments[1].start);
1000 },
1001 "BinaryExpression[operator='+']"(node) {
1002 noop(node.end);
1003 },
1004 "UnaryExpression[operator='-']"(node) {
1005 noop(sourceCode.getFirstToken(node).start);
1006 },
1007 ConditionalExpression(node) {
1008 noop(sourceCode.getFirstToken(node).end);
1009 },
1010 BlockStatement(node) {
1011 noop(sourceCode.getCommentsInside(node)[0].start);
1012 },
1013 ObjectExpression(node) {
1014 noop(sourceCode.getCommentsInside(node)[0].end);
1015 },
1016 Decorator(node) {
1017 noop(node.start);
1018 }
1019 };
1020 }
1021 };
1022
1023 assert.throws(() => {
1024 ruleTester.run("uses-start-end", usesStartEndRule, {
1025 valid: ["foo(a, b)"],
1026 invalid: []
1027 });
1028 }, "Use node.range[0] instead of node.start");
1029 assert.throws(() => {
1030 ruleTester.run("uses-start-end", usesStartEndRule, {
1031 valid: [],
1032 invalid: [{ code: "var a = b * (c + d) / e;", errors: 1 }]
1033 });
1034 }, "Use node.range[1] instead of node.end");
1035 assert.throws(() => {
1036 ruleTester.run("uses-start-end", usesStartEndRule, {
1037 valid: [],
1038 invalid: [{ code: "var a = -b * c;", errors: 1 }]
1039 });
1040 }, "Use token.range[0] instead of token.start");
1041 assert.throws(() => {
1042 ruleTester.run("uses-start-end", usesStartEndRule, {
1043 valid: ["var a = b ? c : d;"],
1044 invalid: []
1045 });
1046 }, "Use token.range[1] instead of token.end");
1047 assert.throws(() => {
1048 ruleTester.run("uses-start-end", usesStartEndRule, {
1049 valid: ["function f() { /* comment */ }"],
1050 invalid: []
1051 });
1052 }, "Use token.range[0] instead of token.start");
1053 assert.throws(() => {
1054 ruleTester.run("uses-start-end", usesStartEndRule, {
1055 valid: [],
1056 invalid: [{ code: "var x = //\n {\n //comment\n //\n}", errors: 1 }]
1057 });
1058 }, "Use token.range[1] instead of token.end");
1059
1060 const enhancedParserPath = require.resolve("../../fixtures/parsers/enhanced-parser");
1061
1062 assert.throws(() => {
1063 ruleTester.run("uses-start-end", usesStartEndRule, {
1064 valid: [{ code: "foo(a, b)", parser: enhancedParserPath }],
1065 invalid: []
1066 });
1067 }, "Use node.range[0] instead of node.start");
1068 assert.throws(() => {
1069 ruleTester.run("uses-start-end", usesStartEndRule, {
1070 valid: [],
1071 invalid: [{ code: "var a = b * (c + d) / e;", parser: enhancedParserPath, errors: 1 }]
1072 });
1073 }, "Use node.range[1] instead of node.end");
1074 assert.throws(() => {
1075 ruleTester.run("uses-start-end", usesStartEndRule, {
1076 valid: [],
1077 invalid: [{ code: "var a = -b * c;", parser: enhancedParserPath, errors: 1 }]
1078 });
1079 }, "Use token.range[0] instead of token.start");
1080 assert.throws(() => {
1081 ruleTester.run("uses-start-end", usesStartEndRule, {
1082 valid: [{ code: "var a = b ? c : d;", parser: enhancedParserPath }],
1083 invalid: []
1084 });
1085 }, "Use token.range[1] instead of token.end");
1086 assert.throws(() => {
1087 ruleTester.run("uses-start-end", usesStartEndRule, {
1088 valid: [{ code: "function f() { /* comment */ }", parser: enhancedParserPath }],
1089 invalid: []
1090 });
1091 }, "Use token.range[0] instead of token.start");
1092 assert.throws(() => {
1093 ruleTester.run("uses-start-end", usesStartEndRule, {
1094 valid: [],
1095 invalid: [{ code: "var x = //\n {\n //comment\n //\n}", parser: enhancedParserPath, errors: 1 }]
1096 });
1097 }, "Use token.range[1] instead of token.end");
1098
1099 assert.throws(() => {
1100 ruleTester.run("uses-start-end", usesStartEndRule, {
1101 valid: [{ code: "@foo class A {}", parser: require.resolve("../../fixtures/parsers/enhanced-parser2") }],
1102 invalid: []
1103 });
1104 }, "Use node.range[0] instead of node.start");
1105 });
1106
1107 it("should throw an error if no test scenarios given", () => {
1108 assert.throws(() => {
1109 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"));
1110 }, "Test Scenarios for rule foo : Could not find test scenario object");
1111 });
1112
1113 it("should throw an error if no acceptable test scenario object is given", () => {
1114 assert.throws(() => {
1115 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), []);
1116 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios\nCould not find any invalid test scenarios");
1117 assert.throws(() => {
1118 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), "");
1119 }, "Test Scenarios for rule foo : Could not find test scenario object");
1120 assert.throws(() => {
1121 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), 2);
1122 }, "Test Scenarios for rule foo : Could not find test scenario object");
1123 assert.throws(() => {
1124 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {});
1125 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios\nCould not find any invalid test scenarios");
1126 assert.throws(() => {
1127 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1128 valid: []
1129 });
1130 }, "Test Scenarios for rule foo is invalid:\nCould not find any invalid test scenarios");
1131 assert.throws(() => {
1132 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/modify-ast-at-last"), {
1133 invalid: []
1134 });
1135 }, "Test Scenarios for rule foo is invalid:\nCould not find any valid test scenarios");
1136 });
1137
1138 // Nominal message/messageId use cases
1139 it("should assert match if message provided in both test and result.", () => {
1140 assert.throws(() => {
1141 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly, {
1142 valid: [],
1143 invalid: [{ code: "foo", errors: [{ message: "something" }] }]
1144 });
1145 }, /Avoid using variables named/u);
1146
1147 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly, {
1148 valid: [],
1149 invalid: [{ code: "foo", errors: [{ message: "Avoid using variables named 'foo'." }] }]
1150 });
1151 });
1152
1153 it("should assert match between messageId if provided in both test and result.", () => {
1154 assert.throws(() => {
1155 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData, {
1156 valid: [],
1157 invalid: [{ code: "foo", errors: [{ messageId: "unused" }] }]
1158 });
1159 }, "messageId 'avoidFoo' does not match expected messageId 'unused'.");
1160
1161 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData, {
1162 valid: [],
1163 invalid: [{ code: "foo", errors: [{ messageId: "avoidFoo" }] }]
1164 });
1165 });
1166 it("should assert match between resulting message output if messageId and data provided in both test and result", () => {
1167 assert.throws(() => {
1168 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData, {
1169 valid: [],
1170 invalid: [{ code: "foo", errors: [{ messageId: "avoidFoo", data: { name: "notFoo" } }] }]
1171 });
1172 }, "Hydrated message \"Avoid using variables named 'notFoo'.\" does not match \"Avoid using variables named 'foo'.\"");
1173 });
1174
1175 // messageId/message misconfiguration cases
1176 it("should throw if user tests for both message and messageId", () => {
1177 assert.throws(() => {
1178 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData, {
1179 valid: [],
1180 invalid: [{ code: "foo", errors: [{ message: "something", messageId: "avoidFoo" }] }]
1181 });
1182 }, "Error should not specify both 'message' and a 'messageId'.");
1183 });
1184 it("should throw if user tests for messageId but the rule doesn't use the messageId meta syntax.", () => {
1185 assert.throws(() => {
1186 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMessageOnly, {
1187 valid: [],
1188 invalid: [{ code: "foo", errors: [{ messageId: "avoidFoo" }] }]
1189 });
1190 }, "Error can not use 'messageId' if rule under test doesn't define 'meta.messages'");
1191 });
1192 it("should throw if user tests for messageId not listed in the rule's meta syntax.", () => {
1193 assert.throws(() => {
1194 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData, {
1195 valid: [],
1196 invalid: [{ code: "foo", errors: [{ messageId: "useFoo" }] }]
1197 });
1198 }, /Invalid messageId 'useFoo'/u);
1199 });
1200 it("should throw if data provided without messageId.", () => {
1201 assert.throws(() => {
1202 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/messageId").withMetaWithData, {
1203 valid: [],
1204 invalid: [{ code: "foo", errors: [{ data: "something" }] }]
1205 });
1206 }, "Error must specify 'messageId' if 'data' is used.");
1207 });
1208
1209 describe("suggestions", () => {
1210 it("should pass with valid suggestions (tested using desc)", () => {
1211 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1212 valid: [
1213 "var boo;"
1214 ],
1215 invalid: [{
1216 code: "var foo;",
1217 errors: [{
1218 suggestions: [{
1219 desc: "Rename identifier 'foo' to 'bar'",
1220 output: "var bar;"
1221 }]
1222 }]
1223 }]
1224 });
1225 });
1226
1227 it("should pass with suggestions on multiple lines", () => {
1228 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1229 valid: [],
1230 invalid: [
1231 {
1232 code: "function foo() {\n var foo = 1;\n}",
1233 errors: [{
1234 suggestions: [{
1235 desc: "Rename identifier 'foo' to 'bar'",
1236 output: "function bar() {\n var foo = 1;\n}"
1237 }]
1238 }, {
1239 suggestions: [{
1240 desc: "Rename identifier 'foo' to 'bar'",
1241 output: "function foo() {\n var bar = 1;\n}"
1242 }]
1243 }]
1244 }
1245 ]
1246 });
1247 });
1248
1249 it("should pass with valid suggestions (tested using messageIds)", () => {
1250 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1251 valid: [],
1252 invalid: [{
1253 code: "var foo;",
1254 errors: [{
1255 suggestions: [{
1256 messageId: "renameFoo",
1257 output: "var bar;"
1258 }, {
1259 messageId: "renameFoo",
1260 output: "var baz;"
1261 }]
1262 }]
1263 }]
1264 });
1265 });
1266
1267 it("should pass with valid suggestions (one tested using messageIds, the other using desc)", () => {
1268 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1269 valid: [],
1270 invalid: [{
1271 code: "var foo;",
1272 errors: [{
1273 suggestions: [{
1274 messageId: "renameFoo",
1275 output: "var bar;"
1276 }, {
1277 desc: "Rename identifier 'foo' to 'baz'",
1278 output: "var baz;"
1279 }]
1280 }]
1281 }]
1282 });
1283 });
1284
1285 it("should pass with valid suggestions (tested using both desc and messageIds for the same suggestion)", () => {
1286 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1287 valid: [],
1288 invalid: [{
1289 code: "var foo;",
1290 errors: [{
1291 suggestions: [{
1292 desc: "Rename identifier 'foo' to 'bar'",
1293 messageId: "renameFoo",
1294 output: "var bar;"
1295 }, {
1296 desc: "Rename identifier 'foo' to 'baz'",
1297 messageId: "renameFoo",
1298 output: "var baz;"
1299 }]
1300 }]
1301 }]
1302 });
1303 });
1304
1305 it("should pass with valid suggestions (tested using only desc on a rule that utilizes meta.messages)", () => {
1306 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1307 valid: [],
1308 invalid: [{
1309 code: "var foo;",
1310 errors: [{
1311 suggestions: [{
1312 desc: "Rename identifier 'foo' to 'bar'",
1313 output: "var bar;"
1314 }, {
1315 desc: "Rename identifier 'foo' to 'baz'",
1316 output: "var baz;"
1317 }]
1318 }]
1319 }]
1320 });
1321 });
1322
1323 it("should pass with valid suggestions (tested using messageIds and data)", () => {
1324 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1325 valid: [],
1326 invalid: [{
1327 code: "var foo;",
1328 errors: [{
1329 suggestions: [{
1330 messageId: "renameFoo",
1331 data: { newName: "bar" },
1332 output: "var bar;"
1333 }, {
1334 messageId: "renameFoo",
1335 data: { newName: "baz" },
1336 output: "var baz;"
1337 }]
1338 }]
1339 }]
1340 });
1341 });
1342
1343
1344 it("should pass when tested using empty suggestion test objects if the array length is correct", () => {
1345 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1346 valid: [],
1347 invalid: [{
1348 code: "var foo;",
1349 errors: [{
1350 suggestions: [{}, {}]
1351 }]
1352 }]
1353 });
1354 });
1355
1356 it("should support explicitly expecting no suggestions", () => {
1357 [void 0, null, false, []].forEach(suggestions => {
1358 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/no-eval"), {
1359 valid: [],
1360 invalid: [{
1361 code: "eval('var foo');",
1362 errors: [{
1363 suggestions
1364 }]
1365 }]
1366 });
1367 });
1368 });
1369
1370 it("should fail when expecting no suggestions and there are suggestions", () => {
1371 [void 0, null, false, []].forEach(suggestions => {
1372 assert.throws(() => {
1373 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1374 valid: [],
1375 invalid: [{
1376 code: "var foo;",
1377 errors: [{
1378 suggestions
1379 }]
1380 }]
1381 });
1382 }, "Error should have no suggestions on error with message: \"Avoid using identifiers named 'foo'.\"");
1383 });
1384 });
1385
1386 it("should fail when testing for suggestions that don't exist", () => {
1387 assert.throws(() => {
1388 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
1389 valid: [],
1390 invalid: [{
1391 code: "var foo;",
1392 errors: [{
1393 suggestions: [{
1394 messageId: "this-does-not-exist"
1395 }]
1396 }]
1397 }]
1398 });
1399 }, "Error should have an array of suggestions. Instead received \"undefined\" on error with message: \"Bad var.\"");
1400 });
1401
1402 it("should fail when there are a different number of suggestions", () => {
1403 assert.throws(() => {
1404 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1405 valid: [],
1406 invalid: [{
1407 code: "var foo;",
1408 errors: [{
1409 suggestions: [{
1410 desc: "Rename identifier 'foo' to 'bar'",
1411 output: "var bar;"
1412 }, {
1413 desc: "Rename identifier 'foo' to 'baz'",
1414 output: "var baz;"
1415 }]
1416 }]
1417 }]
1418 });
1419 }, "Error should have 2 suggestions. Instead found 1 suggestions");
1420 });
1421
1422 it("should throw if the suggestion description doesn't match", () => {
1423 assert.throws(() => {
1424 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1425 valid: [],
1426 invalid: [{
1427 code: "var foo;",
1428 errors: [{
1429 suggestions: [{
1430 desc: "not right",
1431 output: "var baz;"
1432 }]
1433 }]
1434 }]
1435 });
1436 }, "Error Suggestion at index 0 : desc should be \"not right\" but got \"Rename identifier 'foo' to 'bar'\" instead.");
1437 });
1438
1439 it("should throw if the suggestion description doesn't match (although messageIds match)", () => {
1440 assert.throws(() => {
1441 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1442 valid: [],
1443 invalid: [{
1444 code: "var foo;",
1445 errors: [{
1446 suggestions: [{
1447 desc: "Rename identifier 'foo' to 'bar'",
1448 messageId: "renameFoo",
1449 output: "var bar;"
1450 }, {
1451 desc: "Rename id 'foo' to 'baz'",
1452 messageId: "renameFoo",
1453 output: "var baz;"
1454 }]
1455 }]
1456 }]
1457 });
1458 }, "Error Suggestion at index 1 : desc should be \"Rename id 'foo' to 'baz'\" but got \"Rename identifier 'foo' to 'baz'\" instead.");
1459 });
1460
1461 it("should throw if the suggestion messageId doesn't match", () => {
1462 assert.throws(() => {
1463 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1464 valid: [],
1465 invalid: [{
1466 code: "var foo;",
1467 errors: [{
1468 suggestions: [{
1469 messageId: "unused",
1470 output: "var bar;"
1471 }, {
1472 messageId: "renameFoo",
1473 output: "var baz;"
1474 }]
1475 }]
1476 }]
1477 });
1478 }, "Error Suggestion at index 0 : messageId should be 'unused' but got 'renameFoo' instead.");
1479 });
1480
1481 it("should throw if the suggestion messageId doesn't match (although descriptions match)", () => {
1482 assert.throws(() => {
1483 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1484 valid: [],
1485 invalid: [{
1486 code: "var foo;",
1487 errors: [{
1488 suggestions: [{
1489 desc: "Rename identifier 'foo' to 'bar'",
1490 messageId: "renameFoo",
1491 output: "var bar;"
1492 }, {
1493 desc: "Rename identifier 'foo' to 'baz'",
1494 messageId: "avoidFoo",
1495 output: "var baz;"
1496 }]
1497 }]
1498 }]
1499 });
1500 }, "Error Suggestion at index 1 : messageId should be 'avoidFoo' but got 'renameFoo' instead.");
1501 });
1502
1503 it("should throw if test specifies messageId for a rule that doesn't have meta.messages", () => {
1504 assert.throws(() => {
1505 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1506 valid: [],
1507 invalid: [{
1508 code: "var foo;",
1509 errors: [{
1510 suggestions: [{
1511 messageId: "renameFoo",
1512 output: "var bar;"
1513 }]
1514 }]
1515 }]
1516 });
1517 }, "Error Suggestion at index 0 : Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.");
1518 });
1519
1520 it("should throw if test specifies messageId that doesn't exist in the rule's meta.messages", () => {
1521 assert.throws(() => {
1522 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1523 valid: [],
1524 invalid: [{
1525 code: "var foo;",
1526 errors: [{
1527 suggestions: [{
1528 messageId: "renameFoo",
1529 output: "var bar;"
1530 }, {
1531 messageId: "removeFoo",
1532 output: "var baz;"
1533 }]
1534 }]
1535 }]
1536 });
1537 }, "Error Suggestion at index 1 : Test has invalid messageId 'removeFoo', the rule under test allows only one of ['avoidFoo', 'unused', 'renameFoo'].");
1538 });
1539
1540 it("should throw if hydrated desc doesn't match (wrong data value)", () => {
1541 assert.throws(() => {
1542 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1543 valid: [],
1544 invalid: [{
1545 code: "var foo;",
1546 errors: [{
1547 suggestions: [{
1548 messageId: "renameFoo",
1549 data: { newName: "car" },
1550 output: "var bar;"
1551 }, {
1552 messageId: "renameFoo",
1553 data: { newName: "baz" },
1554 output: "var baz;"
1555 }]
1556 }]
1557 }]
1558 });
1559 }, "Error Suggestion at index 0 : Hydrated test desc \"Rename identifier 'foo' to 'car'\" does not match received desc \"Rename identifier 'foo' to 'bar'\".");
1560 });
1561
1562 it("should throw if hydrated desc doesn't match (wrong data key)", () => {
1563 assert.throws(() => {
1564 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1565 valid: [],
1566 invalid: [{
1567 code: "var foo;",
1568 errors: [{
1569 suggestions: [{
1570 messageId: "renameFoo",
1571 data: { newName: "bar" },
1572 output: "var bar;"
1573 }, {
1574 messageId: "renameFoo",
1575 data: { name: "baz" },
1576 output: "var baz;"
1577 }]
1578 }]
1579 }]
1580 });
1581 }, "Error Suggestion at index 1 : Hydrated test desc \"Rename identifier 'foo' to '{{ newName }}'\" does not match received desc \"Rename identifier 'foo' to 'baz'\".");
1582 });
1583
1584 it("should throw if test specifies both desc and data", () => {
1585 assert.throws(() => {
1586 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1587 valid: [],
1588 invalid: [{
1589 code: "var foo;",
1590 errors: [{
1591 suggestions: [{
1592 desc: "Rename identifier 'foo' to 'bar'",
1593 messageId: "renameFoo",
1594 data: { newName: "bar" },
1595 output: "var bar;"
1596 }, {
1597 messageId: "renameFoo",
1598 data: { newName: "baz" },
1599 output: "var baz;"
1600 }]
1601 }]
1602 }]
1603 });
1604 }, "Error Suggestion at index 0 : Test should not specify both 'desc' and 'data'.");
1605 });
1606
1607 it("should throw if test uses data but doesn't specify messageId", () => {
1608 assert.throws(() => {
1609 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1610 valid: [],
1611 invalid: [{
1612 code: "var foo;",
1613 errors: [{
1614 suggestions: [{
1615 messageId: "renameFoo",
1616 data: { newName: "bar" },
1617 output: "var bar;"
1618 }, {
1619 data: { newName: "baz" },
1620 output: "var baz;"
1621 }]
1622 }]
1623 }]
1624 });
1625 }, "Error Suggestion at index 1 : Test must specify 'messageId' if 'data' is used.");
1626 });
1627
1628 it("should throw if the resulting suggestion output doesn't match", () => {
1629 assert.throws(() => {
1630 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1631 valid: [],
1632 invalid: [{
1633 code: "var foo;",
1634 errors: [{
1635 suggestions: [{
1636 desc: "Rename identifier 'foo' to 'bar'",
1637 output: "var baz;"
1638 }]
1639 }]
1640 }]
1641 });
1642 }, "Expected the applied suggestion fix to match the test suggestion output");
1643 });
1644
1645 it("should fail when specified suggestion isn't an object", () => {
1646 assert.throws(() => {
1647 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1648 valid: [],
1649 invalid: [{
1650 code: "var foo;",
1651 errors: [{
1652 suggestions: [null]
1653 }]
1654 }]
1655 });
1656 }, "Test suggestion in 'suggestions' array must be an object.");
1657
1658 assert.throws(() => {
1659 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1660 valid: [],
1661 invalid: [{
1662 code: "var foo;",
1663 errors: [{
1664 suggestions: [
1665 {
1666 messageId: "renameFoo",
1667 output: "var bar;"
1668 },
1669 "Rename identifier 'foo' to 'baz'"
1670 ]
1671 }]
1672 }]
1673 });
1674 }, "Test suggestion in 'suggestions' array must be an object.");
1675 });
1676
1677 it("should fail when the suggestion is an object with an unknown property name", () => {
1678 assert.throws(() => {
1679 ruleTester.run("suggestions-basic", require("../../fixtures/testers/rule-tester/suggestions").basic, {
1680 valid: [
1681 "var boo;"
1682 ],
1683 invalid: [{
1684 code: "var foo;",
1685 errors: [{
1686 suggestions: [{
1687 message: "Rename identifier 'foo' to 'bar'"
1688 }]
1689 }]
1690 }]
1691 });
1692 }, /Invalid suggestion property name 'message'/u);
1693 });
1694
1695 it("should fail when any of the suggestions is an object with an unknown property name", () => {
1696 assert.throws(() => {
1697 ruleTester.run("suggestions-messageIds", require("../../fixtures/testers/rule-tester/suggestions").withMessageIds, {
1698 valid: [],
1699 invalid: [{
1700 code: "var foo;",
1701 errors: [{
1702 suggestions: [{
1703 messageId: "renameFoo",
1704 output: "var bar;"
1705 }, {
1706 messageId: "renameFoo",
1707 outpt: "var baz;"
1708 }]
1709 }]
1710 }]
1711 });
1712 }, /Invalid suggestion property name 'outpt'/u);
1713 });
1714 });
1715
1716 describe("naming test cases", () => {
1717
1718 /**
1719 * Asserts that a particular value will be emitted from an EventEmitter.
1720 * @param {EventEmitter} emitter The emitter that should emit a value
1721 * @param {string} emitType The type of emission to listen for
1722 * @param {*} expectedValue The value that should be emitted
1723 * @returns {Promise} A Promise that fulfills if the value is emitted, and rejects if something else is emitted.
1724 * The Promise will be indefinitely pending if no value is emitted.
1725 */
1726 function assertEmitted(emitter, emitType, expectedValue) {
1727 return new Promise((resolve, reject) => {
1728 emitter.once(emitType, emittedValue => {
1729 if (emittedValue === expectedValue) {
1730 resolve();
1731 } else {
1732 reject(new Error(`Expected ${expectedValue} to be emitted but ${emittedValue} was emitted instead.`));
1733 }
1734 });
1735 });
1736 }
1737
1738 it("should use the first argument as the name of the test suite", () => {
1739 const assertion = assertEmitted(ruleTesterTestEmitter, "describe", "this-is-a-rule-name");
1740
1741 ruleTester.run("this-is-a-rule-name", require("../../fixtures/testers/rule-tester/no-var"), {
1742 valid: [],
1743 invalid: []
1744 });
1745
1746 return assertion;
1747 });
1748
1749 it("should use the test code as the name of the tests for valid code (string form)", () => {
1750 const assertion = assertEmitted(ruleTesterTestEmitter, "it", "valid(code);");
1751
1752 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
1753 valid: [
1754 "valid(code);"
1755 ],
1756 invalid: []
1757 });
1758
1759 return assertion;
1760 });
1761
1762 it("should use the test code as the name of the tests for valid code (object form)", () => {
1763 const assertion = assertEmitted(ruleTesterTestEmitter, "it", "valid(code);");
1764
1765 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
1766 valid: [
1767 {
1768 code: "valid(code);"
1769 }
1770 ],
1771 invalid: []
1772 });
1773
1774 return assertion;
1775 });
1776
1777 it("should use the test code as the name of the tests for invalid code", () => {
1778 const assertion = assertEmitted(ruleTesterTestEmitter, "it", "var x = invalid(code);");
1779
1780 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
1781 valid: [],
1782 invalid: [
1783 {
1784 code: "var x = invalid(code);",
1785 output: " x = invalid(code);",
1786 errors: 1
1787 }
1788 ]
1789 });
1790
1791 return assertion;
1792 });
1793
1794 // https://github.com/eslint/eslint/issues/8142
1795 it("should use the empty string as the name of the test if the test case is an empty string", () => {
1796 const assertion = assertEmitted(ruleTesterTestEmitter, "it", "");
1797
1798 ruleTester.run("foo", require("../../fixtures/testers/rule-tester/no-var"), {
1799 valid: [
1800 {
1801 code: ""
1802 }
1803 ],
1804 invalid: []
1805 });
1806
1807 return assertion;
1808 });
1809 });
1810
1811 // https://github.com/eslint/eslint/issues/11615
1812 it("should fail the case if autofix made a syntax error.", () => {
1813 assert.throw(() => {
1814 ruleTester.run(
1815 "foo",
1816 context => ({
1817 Identifier(node) {
1818 context.report({
1819 node,
1820 message: "make a syntax error",
1821 fix(fixer) {
1822 return fixer.replaceText(node, "one two");
1823 }
1824 });
1825 }
1826 }),
1827 {
1828 valid: ["one()"],
1829 invalid: []
1830 }
1831 );
1832 }, /A fatal parsing error occurred in autofix.\nError: .+\nAutofix output:\n.+/u);
1833 });
1834
1835 describe("sanitize test cases", () => {
1836 let originalRuleTesterIt;
1837 let spyRuleTesterIt;
1838
1839 before(() => {
1840 originalRuleTesterIt = RuleTester.it;
1841 spyRuleTesterIt = sinon.spy();
1842 RuleTester.it = spyRuleTesterIt;
1843 });
1844 after(() => {
1845 RuleTester.it = originalRuleTesterIt;
1846 });
1847 beforeEach(() => {
1848 spyRuleTesterIt.resetHistory();
1849 ruleTester = new RuleTester();
1850 });
1851 it("should present newline when using back-tick as new line", () => {
1852 const code = `
1853 var foo = bar;`;
1854
1855 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
1856 valid: [],
1857 invalid: [
1858 {
1859 code,
1860 errors: [/^Bad var/u]
1861 }
1862 ]
1863 });
1864 sinon.assert.calledWith(spyRuleTesterIt, code);
1865 });
1866 it("should present \\u0000 as a string", () => {
1867 const code = "\u0000";
1868
1869 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
1870 valid: [],
1871 invalid: [
1872 {
1873 code,
1874 errors: [/^Bad var/u]
1875 }
1876 ]
1877 });
1878 sinon.assert.calledWith(spyRuleTesterIt, "\\u0000");
1879 });
1880 it("should present the pipe character correctly", () => {
1881 const code = "var foo = bar || baz;";
1882
1883 ruleTester.run("no-var", require("../../fixtures/testers/rule-tester/no-var"), {
1884 valid: [],
1885 invalid: [
1886 {
1887 code,
1888 errors: [/^Bad var/u]
1889 }
1890 ]
1891 });
1892 sinon.assert.calledWith(spyRuleTesterIt, code);
1893 });
1894
1895 });
1896 });