]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/config/flat-config-array.js
import 8.41.0 source
[pve-eslint.git] / eslint / tests / lib / config / flat-config-array.js
1 /**
2 * @fileoverview Tests for FlatConfigArray
3 * @author Nicholas C. Zakas
4 */
5
6 "use strict";
7
8 //-----------------------------------------------------------------------------
9 // Requirements
10 //-----------------------------------------------------------------------------
11
12 const { FlatConfigArray } = require("../../../lib/config/flat-config-array");
13 const assert = require("chai").assert;
14 const {
15 all: allConfig,
16 recommended: recommendedConfig
17 } = require("@eslint/js").configs;
18 const stringify = require("json-stable-stringify-without-jsonify");
19 const espree = require("espree");
20
21 //-----------------------------------------------------------------------------
22 // Helpers
23 //-----------------------------------------------------------------------------
24
25 const baseConfig = {
26 files: ["**/*.js"],
27 plugins: {
28 "@": {
29 rules: {
30 foo: {
31 meta: {
32 schema: {
33 type: "array",
34 items: [
35 {
36 enum: ["always", "never"]
37 }
38 ],
39 minItems: 0,
40 maxItems: 1
41 }
42 }
43
44 },
45 bar: {
46
47 },
48 baz: {
49
50 },
51
52 // old-style
53 boom() {},
54
55 foo2: {
56 meta: {
57 schema: {
58 type: "array",
59 items: {
60 type: "string"
61 },
62 uniqueItems: true,
63 minItems: 1
64 }
65 }
66 }
67 }
68 },
69 test1: {
70 rules: {
71 match: {}
72 }
73 },
74 test2: {
75 rules: {
76 nomatch: {}
77 }
78 }
79 }
80 };
81
82 /**
83 * Creates a config array with the correct default options.
84 * @param {*[]} configs An array of configs to use in the config array.
85 * @returns {FlatConfigArray} The config array;
86 */
87 function createFlatConfigArray(configs) {
88 return new FlatConfigArray(configs, {
89 baseConfig: [baseConfig]
90 });
91 }
92
93 /**
94 * Asserts that a given set of configs will be merged into the given
95 * result config.
96 * @param {*[]} values An array of configs to use in the config array.
97 * @param {Object} result The expected merged result of the configs.
98 * @returns {void}
99 * @throws {AssertionError} If the actual result doesn't match the
100 * expected result.
101 */
102 async function assertMergedResult(values, result) {
103 const configs = createFlatConfigArray(values);
104
105 await configs.normalize();
106
107 const config = configs.getConfig("foo.js");
108
109 assert.deepStrictEqual(config, result);
110 }
111
112 /**
113 * Asserts that a given set of configs results in an invalid config.
114 * @param {*[]} values An array of configs to use in the config array.
115 * @param {string|RegExp} message The expected error message.
116 * @returns {void}
117 * @throws {AssertionError} If the config is valid or if the error
118 * has an unexpected message.
119 */
120 async function assertInvalidConfig(values, message) {
121 const configs = createFlatConfigArray(values);
122
123 await configs.normalize();
124
125 assert.throws(() => {
126 configs.getConfig("foo.js");
127 }, message);
128 }
129
130 /**
131 * Normalizes the rule configs to an array with severity to match
132 * how Flat Config merges rule options.
133 * @param {Object} rulesConfig The rules config portion of a config.
134 * @returns {Array} The rules config object.
135 */
136 function normalizeRuleConfig(rulesConfig) {
137 const rulesConfigCopy = {
138 ...rulesConfig
139 };
140
141 for (const ruleId of Object.keys(rulesConfigCopy)) {
142 rulesConfigCopy[ruleId] = [2];
143 }
144
145 return rulesConfigCopy;
146 }
147
148 //-----------------------------------------------------------------------------
149 // Tests
150 //-----------------------------------------------------------------------------
151
152 describe("FlatConfigArray", () => {
153
154 it("should allow noniterable baseConfig objects", () => {
155 const base = {
156 languageOptions: {
157 parserOptions: {
158 foo: true
159 }
160 }
161 };
162
163 const configs = new FlatConfigArray([], {
164 baseConfig: base
165 });
166
167 // should not throw error
168 configs.normalizeSync();
169 });
170
171 it("should not reuse languageOptions.parserOptions across configs", () => {
172 const base = [{
173 files: ["**/*.js"],
174 languageOptions: {
175 parserOptions: {
176 foo: true
177 }
178 }
179 }];
180
181 const configs = new FlatConfigArray([], {
182 baseConfig: base
183 });
184
185 configs.normalizeSync();
186
187 const config = configs.getConfig("foo.js");
188
189 assert.notStrictEqual(base[0].languageOptions, config.languageOptions);
190 assert.notStrictEqual(base[0].languageOptions.parserOptions, config.languageOptions.parserOptions, "parserOptions should be new object");
191 });
192
193 describe("Serialization of configs", () => {
194
195 it("should convert config into normalized JSON object", () => {
196
197 const configs = new FlatConfigArray([{
198 plugins: {
199 a: {},
200 b: {}
201 }
202 }]);
203
204 configs.normalizeSync();
205
206 const config = configs.getConfig("foo.js");
207 const expected = {
208 plugins: ["@", "a", "b"],
209 languageOptions: {
210 ecmaVersion: "latest",
211 sourceType: "module",
212 parser: `espree@${espree.version}`,
213 parserOptions: {}
214 },
215 processor: void 0
216 };
217 const actual = config.toJSON();
218
219 assert.deepStrictEqual(actual, expected);
220
221 assert.strictEqual(stringify(actual), stringify(expected));
222 });
223
224 it("should convert config with plugin name/version into normalized JSON object", () => {
225
226 const configs = new FlatConfigArray([{
227 plugins: {
228 a: {},
229 b: {
230 name: "b-plugin",
231 version: "2.3.1"
232 }
233 }
234 }]);
235
236 configs.normalizeSync();
237
238 const config = configs.getConfig("foo.js");
239 const expected = {
240 plugins: ["@", "a", "b:b-plugin@2.3.1"],
241 languageOptions: {
242 ecmaVersion: "latest",
243 sourceType: "module",
244 parser: `espree@${espree.version}`,
245 parserOptions: {}
246 },
247 processor: void 0
248 };
249 const actual = config.toJSON();
250
251 assert.deepStrictEqual(actual, expected);
252
253 assert.strictEqual(stringify(actual), stringify(expected));
254 });
255
256 it("should convert config with plugin meta into normalized JSON object", () => {
257
258 const configs = new FlatConfigArray([{
259 plugins: {
260 a: {},
261 b: {
262 meta: {
263 name: "b-plugin",
264 version: "2.3.1"
265 }
266 }
267 }
268 }]);
269
270 configs.normalizeSync();
271
272 const config = configs.getConfig("foo.js");
273 const expected = {
274 plugins: ["@", "a", "b:b-plugin@2.3.1"],
275 languageOptions: {
276 ecmaVersion: "latest",
277 sourceType: "module",
278 parser: `espree@${espree.version}`,
279 parserOptions: {}
280 },
281 processor: void 0
282 };
283 const actual = config.toJSON();
284
285 assert.deepStrictEqual(actual, expected);
286
287 assert.strictEqual(stringify(actual), stringify(expected));
288 });
289
290 it("should throw an error when config with unnamed parser object is normalized", () => {
291
292 const configs = new FlatConfigArray([{
293 languageOptions: {
294 parser: {
295 parse() { /* empty */ }
296 }
297 }
298 }]);
299
300 configs.normalizeSync();
301
302 const config = configs.getConfig("foo.js");
303
304 assert.throws(() => {
305 config.toJSON();
306 }, /Could not serialize parser/u);
307
308 });
309
310 it("should throw an error when config with unnamed parser object with empty meta object is normalized", () => {
311
312 const configs = new FlatConfigArray([{
313 languageOptions: {
314 parser: {
315 meta: {},
316 parse() { /* empty */ }
317 }
318 }
319 }]);
320
321 configs.normalizeSync();
322
323 const config = configs.getConfig("foo.js");
324
325 assert.throws(() => {
326 config.toJSON();
327 }, /Could not serialize parser/u);
328
329 });
330
331 it("should throw an error when config with unnamed parser object with only meta version is normalized", () => {
332
333 const configs = new FlatConfigArray([{
334 languageOptions: {
335 parser: {
336 meta: {
337 version: "0.1.1"
338 },
339 parse() { /* empty */ }
340 }
341 }
342 }]);
343
344 configs.normalizeSync();
345
346 const config = configs.getConfig("foo.js");
347
348 assert.throws(() => {
349 config.toJSON();
350 }, /Could not serialize parser/u);
351
352 });
353
354 it("should not throw an error when config with named parser object is normalized", () => {
355
356 const configs = new FlatConfigArray([{
357 languageOptions: {
358 parser: {
359 meta: {
360 name: "custom-parser"
361 },
362 parse() { /* empty */ }
363 }
364 }
365 }]);
366
367 configs.normalizeSync();
368
369 const config = configs.getConfig("foo.js");
370
371 assert.deepStrictEqual(config.toJSON(), {
372 languageOptions: {
373 ecmaVersion: "latest",
374 parser: "custom-parser",
375 parserOptions: {},
376 sourceType: "module"
377 },
378 plugins: ["@"],
379 processor: void 0
380 });
381
382 });
383
384 it("should not throw an error when config with named and versioned parser object is normalized", () => {
385
386 const configs = new FlatConfigArray([{
387 languageOptions: {
388 parser: {
389 meta: {
390 name: "custom-parser",
391 version: "0.1.0"
392 },
393 parse() { /* empty */ }
394 }
395 }
396 }]);
397
398 configs.normalizeSync();
399
400 const config = configs.getConfig("foo.js");
401
402 assert.deepStrictEqual(config.toJSON(), {
403 languageOptions: {
404 ecmaVersion: "latest",
405 parser: "custom-parser@0.1.0",
406 parserOptions: {},
407 sourceType: "module"
408 },
409 plugins: ["@"],
410 processor: void 0
411 });
412
413 });
414
415 it("should not throw an error when config with meta-named and versioned parser object is normalized", () => {
416
417 const configs = new FlatConfigArray([{
418 languageOptions: {
419 parser: {
420 meta: {
421 name: "custom-parser"
422 },
423 version: "0.1.0",
424 parse() { /* empty */ }
425 }
426 }
427 }]);
428
429 configs.normalizeSync();
430
431 const config = configs.getConfig("foo.js");
432
433 assert.deepStrictEqual(config.toJSON(), {
434 languageOptions: {
435 ecmaVersion: "latest",
436 parser: "custom-parser@0.1.0",
437 parserOptions: {},
438 sourceType: "module"
439 },
440 plugins: ["@"],
441 processor: void 0
442 });
443
444 });
445
446 it("should not throw an error when config with named and versioned parser object outside of meta object is normalized", () => {
447
448 const configs = new FlatConfigArray([{
449 languageOptions: {
450 parser: {
451 name: "custom-parser",
452 version: "0.1.0",
453 parse() { /* empty */ }
454 }
455 }
456 }]);
457
458 configs.normalizeSync();
459
460 const config = configs.getConfig("foo.js");
461
462 assert.deepStrictEqual(config.toJSON(), {
463 languageOptions: {
464 ecmaVersion: "latest",
465 parser: "custom-parser@0.1.0",
466 parserOptions: {},
467 sourceType: "module"
468 },
469 plugins: ["@"],
470 processor: void 0
471 });
472
473 });
474
475 it("should throw an error when config with unnamed processor object is normalized", () => {
476
477 const configs = new FlatConfigArray([{
478 processor: {
479 preprocess() { /* empty */ },
480 postprocess() { /* empty */ }
481 }
482 }]);
483
484 configs.normalizeSync();
485
486 const config = configs.getConfig("foo.js");
487
488 assert.throws(() => {
489 config.toJSON();
490 }, /Could not serialize processor/u);
491
492 });
493
494 it("should throw an error when config with processor object with empty meta object is normalized", () => {
495
496 const configs = new FlatConfigArray([{
497 processor: {
498 meta: {},
499 preprocess() { /* empty */ },
500 postprocess() { /* empty */ }
501 }
502 }]);
503
504 configs.normalizeSync();
505
506 const config = configs.getConfig("foo.js");
507
508 assert.throws(() => {
509 config.toJSON();
510 }, /Could not serialize processor/u);
511
512 });
513
514
515 it("should not throw an error when config with named processor object is normalized", () => {
516
517 const configs = new FlatConfigArray([{
518 processor: {
519 meta: {
520 name: "custom-processor"
521 },
522 preprocess() { /* empty */ },
523 postprocess() { /* empty */ }
524 }
525 }]);
526
527 configs.normalizeSync();
528
529 const config = configs.getConfig("foo.js");
530
531 assert.deepStrictEqual(config.toJSON(), {
532 languageOptions: {
533 ecmaVersion: "latest",
534 parser: `espree@${espree.version}`,
535 parserOptions: {},
536 sourceType: "module"
537 },
538 plugins: ["@"],
539 processor: "custom-processor"
540 });
541
542 });
543
544 it("should not throw an error when config with named processor object without meta is normalized", () => {
545
546 const configs = new FlatConfigArray([{
547 processor: {
548 name: "custom-processor",
549 preprocess() { /* empty */ },
550 postprocess() { /* empty */ }
551 }
552 }]);
553
554 configs.normalizeSync();
555
556 const config = configs.getConfig("foo.js");
557
558 assert.deepStrictEqual(config.toJSON(), {
559 languageOptions: {
560 ecmaVersion: "latest",
561 parser: `espree@${espree.version}`,
562 parserOptions: {},
563 sourceType: "module"
564 },
565 plugins: ["@"],
566 processor: "custom-processor"
567 });
568
569 });
570
571 it("should not throw an error when config with named and versioned processor object is normalized", () => {
572
573 const configs = new FlatConfigArray([{
574 processor: {
575 meta: {
576 name: "custom-processor",
577 version: "1.2.3"
578 },
579 preprocess() { /* empty */ },
580 postprocess() { /* empty */ }
581 }
582 }]);
583
584
585 configs.normalizeSync();
586
587 const config = configs.getConfig("foo.js");
588
589 assert.deepStrictEqual(config.toJSON(), {
590 languageOptions: {
591 ecmaVersion: "latest",
592 parser: `espree@${espree.version}`,
593 parserOptions: {},
594 sourceType: "module"
595 },
596 plugins: ["@"],
597 processor: "custom-processor@1.2.3"
598 });
599
600 });
601
602 it("should not throw an error when config with named and versioned processor object without meta is normalized", () => {
603
604 const configs = new FlatConfigArray([{
605 processor: {
606 name: "custom-processor",
607 version: "1.2.3",
608 preprocess() { /* empty */ },
609 postprocess() { /* empty */ }
610 }
611 }]);
612
613
614 configs.normalizeSync();
615
616 const config = configs.getConfig("foo.js");
617
618 assert.deepStrictEqual(config.toJSON(), {
619 languageOptions: {
620 ecmaVersion: "latest",
621 parser: `espree@${espree.version}`,
622 parserOptions: {},
623 sourceType: "module"
624 },
625 plugins: ["@"],
626 processor: "custom-processor@1.2.3"
627 });
628
629 });
630
631 });
632
633 describe("Special configs", () => {
634 it("eslint:recommended is replaced with an actual config", async () => {
635 const configs = new FlatConfigArray(["eslint:recommended"]);
636
637 await configs.normalize();
638 const config = configs.getConfig("foo.js");
639
640 assert.deepStrictEqual(config.rules, normalizeRuleConfig(recommendedConfig.rules));
641 });
642
643 it("eslint:all is replaced with an actual config", async () => {
644 const configs = new FlatConfigArray(["eslint:all"]);
645
646 await configs.normalize();
647 const config = configs.getConfig("foo.js");
648
649 assert.deepStrictEqual(config.rules, normalizeRuleConfig(allConfig.rules));
650 });
651 });
652
653 describe("Config Properties", () => {
654
655 describe("settings", () => {
656
657 it("should merge two objects", () => assertMergedResult([
658 {
659 settings: {
660 a: true,
661 b: false
662 }
663 },
664 {
665 settings: {
666 c: true,
667 d: false
668 }
669 }
670 ], {
671 plugins: baseConfig.plugins,
672
673 settings: {
674 a: true,
675 b: false,
676 c: true,
677 d: false
678 }
679 }));
680
681 it("should merge two objects when second object has overrides", () => assertMergedResult([
682 {
683 settings: {
684 a: true,
685 b: false,
686 d: [1, 2],
687 e: [5, 6]
688 }
689 },
690 {
691 settings: {
692 c: true,
693 a: false,
694 d: [3, 4]
695 }
696 }
697 ], {
698 plugins: baseConfig.plugins,
699
700 settings: {
701 a: false,
702 b: false,
703 c: true,
704 d: [3, 4],
705 e: [5, 6]
706 }
707 }));
708
709 it("should deeply merge two objects when second object has overrides", () => assertMergedResult([
710 {
711 settings: {
712 object: {
713 a: true,
714 b: false
715 }
716 }
717 },
718 {
719 settings: {
720 object: {
721 c: true,
722 a: false
723 }
724 }
725 }
726 ], {
727 plugins: baseConfig.plugins,
728
729 settings: {
730 object: {
731 a: false,
732 b: false,
733 c: true
734 }
735 }
736 }));
737
738 it("should merge an object and undefined into one object", () => assertMergedResult([
739 {
740 settings: {
741 a: true,
742 b: false
743 }
744 },
745 {
746 }
747 ], {
748 plugins: baseConfig.plugins,
749
750 settings: {
751 a: true,
752 b: false
753 }
754 }));
755
756 it("should merge undefined and an object into one object", () => assertMergedResult([
757 {
758 },
759 {
760 settings: {
761 a: true,
762 b: false
763 }
764 }
765 ], {
766 plugins: baseConfig.plugins,
767
768 settings: {
769 a: true,
770 b: false
771 }
772 }));
773
774 });
775
776 describe("plugins", () => {
777
778 const pluginA = {};
779 const pluginB = {};
780 const pluginC = {};
781
782 it("should merge two objects", () => assertMergedResult([
783 {
784 plugins: {
785 a: pluginA,
786 b: pluginB
787 }
788 },
789 {
790 plugins: {
791 c: pluginC
792 }
793 }
794 ], {
795 plugins: {
796 a: pluginA,
797 b: pluginB,
798 c: pluginC,
799 ...baseConfig.plugins
800 }
801 }));
802
803 it("should merge an object and undefined into one object", () => assertMergedResult([
804 {
805 plugins: {
806 a: pluginA,
807 b: pluginB
808 }
809 },
810 {
811 }
812 ], {
813 plugins: {
814 a: pluginA,
815 b: pluginB,
816 ...baseConfig.plugins
817 }
818 }));
819
820 it("should error when attempting to redefine a plugin", async () => {
821
822 await assertInvalidConfig([
823 {
824 plugins: {
825 a: pluginA,
826 b: pluginB
827 }
828 },
829 {
830 plugins: {
831 a: pluginC
832 }
833 }
834 ], "Cannot redefine plugin \"a\".");
835 });
836
837 it("should error when plugin is not an object", async () => {
838
839 await assertInvalidConfig([
840 {
841 plugins: {
842 a: true
843 }
844 }
845 ], "Key \"a\": Expected an object.");
846 });
847
848
849 });
850
851 describe("processor", () => {
852
853 it("should merge two values when second is a string", () => {
854
855 const stubProcessor = {
856 preprocess() {},
857 postprocess() {}
858 };
859
860 return assertMergedResult([
861 {
862 processor: {
863 preprocess() {},
864 postprocess() {}
865 }
866 },
867 {
868 plugins: {
869 markdown: {
870 processors: {
871 markdown: stubProcessor
872 }
873 }
874 },
875 processor: "markdown/markdown"
876 }
877 ], {
878 plugins: {
879 markdown: {
880 processors: {
881 markdown: stubProcessor
882 }
883 },
884 ...baseConfig.plugins
885 },
886 processor: stubProcessor
887 });
888 });
889
890 it("should merge two values when second is an object", () => {
891
892 const processor = {
893 preprocess() { },
894 postprocess() { }
895 };
896
897 return assertMergedResult([
898 {
899 processor: "markdown/markdown"
900 },
901 {
902 processor
903 }
904 ], {
905 plugins: baseConfig.plugins,
906
907 processor
908 });
909 });
910
911 it("should error when an invalid string is used", async () => {
912
913 await assertInvalidConfig([
914 {
915 processor: "foo"
916 }
917 ], "pluginName/objectName");
918 });
919
920 it("should error when an empty string is used", async () => {
921
922 await assertInvalidConfig([
923 {
924 processor: ""
925 }
926 ], "pluginName/objectName");
927 });
928
929 it("should error when an invalid processor is used", async () => {
930 await assertInvalidConfig([
931 {
932 processor: {}
933 }
934 ], "Object must have a preprocess() and a postprocess() method.");
935
936 });
937
938 it("should error when a processor cannot be found in a plugin", async () => {
939 await assertInvalidConfig([
940 {
941 plugins: {
942 foo: {}
943 },
944 processor: "foo/bar"
945 }
946 ], /Could not find "bar" in plugin "foo"/u);
947
948 });
949
950 });
951
952 describe("linterOptions", () => {
953
954 it("should error when an unexpected key is found", async () => {
955
956 await assertInvalidConfig([
957 {
958 linterOptions: {
959 foo: true
960 }
961 }
962 ], "Unexpected key \"foo\" found.");
963
964 });
965
966 describe("noInlineConfig", () => {
967
968 it("should error when an unexpected value is found", async () => {
969
970 await assertInvalidConfig([
971 {
972 linterOptions: {
973 noInlineConfig: "true"
974 }
975 }
976 ], "Expected a Boolean.");
977 });
978
979 it("should merge two objects when second object has overrides", () => assertMergedResult([
980 {
981 linterOptions: {
982 noInlineConfig: true
983 }
984 },
985 {
986 linterOptions: {
987 noInlineConfig: false
988 }
989 }
990 ], {
991 plugins: baseConfig.plugins,
992
993 linterOptions: {
994 noInlineConfig: false
995 }
996 }));
997
998 it("should merge an object and undefined into one object", () => assertMergedResult([
999 {
1000 linterOptions: {
1001 noInlineConfig: false
1002 }
1003 },
1004 {
1005 }
1006 ], {
1007 plugins: baseConfig.plugins,
1008
1009 linterOptions: {
1010 noInlineConfig: false
1011 }
1012 }));
1013
1014 it("should merge undefined and an object into one object", () => assertMergedResult([
1015 {
1016 },
1017 {
1018 linterOptions: {
1019 noInlineConfig: false
1020 }
1021 }
1022 ], {
1023 plugins: baseConfig.plugins,
1024
1025 linterOptions: {
1026 noInlineConfig: false
1027 }
1028 }));
1029
1030
1031 });
1032 describe("reportUnusedDisableDirectives", () => {
1033
1034 it("should error when an unexpected value is found", async () => {
1035
1036 await assertInvalidConfig([
1037 {
1038 linterOptions: {
1039 reportUnusedDisableDirectives: "true"
1040 }
1041 }
1042 ], /Expected a Boolean/u);
1043 });
1044
1045 it("should merge two objects when second object has overrides", () => assertMergedResult([
1046 {
1047 linterOptions: {
1048 reportUnusedDisableDirectives: false
1049 }
1050 },
1051 {
1052 linterOptions: {
1053 reportUnusedDisableDirectives: true
1054 }
1055 }
1056 ], {
1057 plugins: baseConfig.plugins,
1058
1059 linterOptions: {
1060 reportUnusedDisableDirectives: true
1061 }
1062 }));
1063
1064 it("should merge an object and undefined into one object", () => assertMergedResult([
1065 {},
1066 {
1067 linterOptions: {
1068 reportUnusedDisableDirectives: true
1069 }
1070 }
1071 ], {
1072 plugins: baseConfig.plugins,
1073
1074 linterOptions: {
1075 reportUnusedDisableDirectives: true
1076 }
1077 }));
1078
1079
1080 });
1081
1082 });
1083
1084 describe("languageOptions", () => {
1085
1086 it("should error when an unexpected key is found", async () => {
1087
1088 await assertInvalidConfig([
1089 {
1090 languageOptions: {
1091 foo: true
1092 }
1093 }
1094 ], "Unexpected key \"foo\" found.");
1095
1096 });
1097
1098 it("should merge two languageOptions objects with different properties", () => assertMergedResult([
1099 {
1100 languageOptions: {
1101 ecmaVersion: 2019
1102 }
1103 },
1104 {
1105 languageOptions: {
1106 sourceType: "commonjs"
1107 }
1108 }
1109 ], {
1110 plugins: baseConfig.plugins,
1111
1112 languageOptions: {
1113 ecmaVersion: 2019,
1114 sourceType: "commonjs"
1115 }
1116 }));
1117
1118 describe("ecmaVersion", () => {
1119
1120 it("should error when an unexpected value is found", async () => {
1121
1122 await assertInvalidConfig([
1123 {
1124 languageOptions: {
1125 ecmaVersion: "true"
1126 }
1127 }
1128 ], /Key "languageOptions": Key "ecmaVersion": Expected a number or "latest"\./u);
1129 });
1130
1131 it("should merge two objects when second object has overrides", () => assertMergedResult([
1132 {
1133 languageOptions: {
1134 ecmaVersion: 2019
1135 }
1136 },
1137 {
1138 languageOptions: {
1139 ecmaVersion: 2021
1140 }
1141 }
1142 ], {
1143 plugins: baseConfig.plugins,
1144
1145 languageOptions: {
1146 ecmaVersion: 2021
1147 }
1148 }));
1149
1150 it("should merge an object and undefined into one object", () => assertMergedResult([
1151 {
1152 languageOptions: {
1153 ecmaVersion: 2021
1154 }
1155 },
1156 {
1157 }
1158 ], {
1159 plugins: baseConfig.plugins,
1160
1161 languageOptions: {
1162 ecmaVersion: 2021
1163 }
1164 }));
1165
1166
1167 it("should merge undefined and an object into one object", () => assertMergedResult([
1168 {
1169 },
1170 {
1171 languageOptions: {
1172 ecmaVersion: 2021
1173 }
1174 }
1175 ], {
1176 plugins: baseConfig.plugins,
1177
1178 languageOptions: {
1179 ecmaVersion: 2021
1180 }
1181 }));
1182
1183
1184 });
1185
1186 describe("sourceType", () => {
1187
1188 it("should error when an unexpected value is found", async () => {
1189
1190 await assertInvalidConfig([
1191 {
1192 languageOptions: {
1193 sourceType: "true"
1194 }
1195 }
1196 ], "Expected \"script\", \"module\", or \"commonjs\".");
1197 });
1198
1199 it("should merge two objects when second object has overrides", () => assertMergedResult([
1200 {
1201 languageOptions: {
1202 sourceType: "module"
1203 }
1204 },
1205 {
1206 languageOptions: {
1207 sourceType: "script"
1208 }
1209 }
1210 ], {
1211 plugins: baseConfig.plugins,
1212
1213 languageOptions: {
1214 sourceType: "script"
1215 }
1216 }));
1217
1218 it("should merge an object and undefined into one object", () => assertMergedResult([
1219 {
1220 languageOptions: {
1221 sourceType: "script"
1222 }
1223 },
1224 {
1225 }
1226 ], {
1227 plugins: baseConfig.plugins,
1228
1229 languageOptions: {
1230 sourceType: "script"
1231 }
1232 }));
1233
1234
1235 it("should merge undefined and an object into one object", () => assertMergedResult([
1236 {
1237 },
1238 {
1239 languageOptions: {
1240 sourceType: "module"
1241 }
1242 }
1243 ], {
1244 plugins: baseConfig.plugins,
1245
1246 languageOptions: {
1247 sourceType: "module"
1248 }
1249 }));
1250
1251
1252 });
1253
1254 describe("globals", () => {
1255
1256 it("should error when an unexpected value is found", async () => {
1257
1258 await assertInvalidConfig([
1259 {
1260 languageOptions: {
1261 globals: "true"
1262 }
1263 }
1264 ], "Expected an object.");
1265 });
1266
1267 it("should error when an unexpected key value is found", async () => {
1268
1269 await assertInvalidConfig([
1270 {
1271 languageOptions: {
1272 globals: {
1273 foo: "truex"
1274 }
1275 }
1276 }
1277 ], "Key \"foo\": Expected \"readonly\", \"writable\", or \"off\".");
1278 });
1279
1280 it("should error when a global has leading whitespace", async () => {
1281
1282 await assertInvalidConfig([
1283 {
1284 languageOptions: {
1285 globals: {
1286 " foo": "readonly"
1287 }
1288 }
1289 }
1290 ], /Global " foo" has leading or trailing whitespace/u);
1291 });
1292
1293 it("should error when a global has trailing whitespace", async () => {
1294
1295 await assertInvalidConfig([
1296 {
1297 languageOptions: {
1298 globals: {
1299 "foo ": "readonly"
1300 }
1301 }
1302 }
1303 ], /Global "foo " has leading or trailing whitespace/u);
1304 });
1305
1306 it("should merge two objects when second object has different keys", () => assertMergedResult([
1307 {
1308 languageOptions: {
1309 globals: {
1310 foo: "readonly"
1311 }
1312 }
1313 },
1314 {
1315 languageOptions: {
1316 globals: {
1317 bar: "writable"
1318 }
1319 }
1320 }
1321 ], {
1322 plugins: baseConfig.plugins,
1323
1324 languageOptions: {
1325 globals: {
1326 foo: "readonly",
1327 bar: "writable"
1328 }
1329 }
1330 }));
1331
1332 it("should merge two objects when second object has overrides", () => assertMergedResult([
1333 {
1334 languageOptions: {
1335 globals: {
1336 foo: null
1337 }
1338 }
1339 },
1340 {
1341 languageOptions: {
1342 globals: {
1343 foo: "writeable"
1344 }
1345 }
1346 }
1347 ], {
1348 plugins: baseConfig.plugins,
1349
1350 languageOptions: {
1351 globals: {
1352 foo: "writeable"
1353 }
1354 }
1355 }));
1356
1357 it("should merge an object and undefined into one object", () => assertMergedResult([
1358 {
1359 languageOptions: {
1360 globals: {
1361 foo: "readable"
1362 }
1363 }
1364 },
1365 {
1366 }
1367 ], {
1368 plugins: baseConfig.plugins,
1369
1370 languageOptions: {
1371 globals: {
1372 foo: "readable"
1373 }
1374 }
1375 }));
1376
1377
1378 it("should merge undefined and an object into one object", () => assertMergedResult([
1379 {
1380 },
1381 {
1382 languageOptions: {
1383 globals: {
1384 foo: "false"
1385 }
1386 }
1387 }
1388 ], {
1389 plugins: baseConfig.plugins,
1390
1391 languageOptions: {
1392 globals: {
1393 foo: "false"
1394 }
1395 }
1396 }));
1397
1398
1399 });
1400
1401 describe("parser", () => {
1402
1403 it("should error when an unexpected value is found", async () => {
1404
1405 await assertInvalidConfig([
1406 {
1407 languageOptions: {
1408 parser: true
1409 }
1410 }
1411 ], "Key \"languageOptions\": Key \"parser\": Expected object with parse() or parseForESLint() method.");
1412 });
1413
1414 it("should error when a null is found", async () => {
1415
1416 await assertInvalidConfig([
1417 {
1418 languageOptions: {
1419 parser: null
1420 }
1421 }
1422 ], "Key \"languageOptions\": Key \"parser\": Expected object with parse() or parseForESLint() method.");
1423 });
1424
1425 it("should error when a parser is a string", async () => {
1426
1427 await assertInvalidConfig([
1428 {
1429 languageOptions: {
1430 parser: "foo/bar"
1431 }
1432 }
1433 ], "Key \"languageOptions\": Key \"parser\": Expected object with parse() or parseForESLint() method.");
1434 });
1435
1436 it("should error when a value doesn't have a parse() method", async () => {
1437
1438 await assertInvalidConfig([
1439 {
1440 languageOptions: {
1441 parser: {}
1442 }
1443 }
1444 ], "Key \"languageOptions\": Key \"parser\": Expected object with parse() or parseForESLint() method.");
1445 });
1446
1447 it("should merge two objects when second object has overrides", () => {
1448
1449 const parser = { parse() {} };
1450 const stubParser = { parse() { } };
1451
1452 return assertMergedResult([
1453 {
1454 languageOptions: {
1455 parser
1456 }
1457 },
1458 {
1459 languageOptions: {
1460 parser: stubParser
1461 }
1462 }
1463 ], {
1464 plugins: {
1465 ...baseConfig.plugins
1466 },
1467 languageOptions: {
1468 parser: stubParser
1469 }
1470 });
1471 });
1472
1473 it("should merge an object and undefined into one object", () => {
1474
1475 const stubParser = { parse() { } };
1476
1477 return assertMergedResult([
1478 {
1479 languageOptions: {
1480 parser: stubParser
1481 }
1482 },
1483 {
1484 }
1485 ], {
1486 plugins: {
1487 ...baseConfig.plugins
1488 },
1489
1490 languageOptions: {
1491 parser: stubParser
1492 }
1493 });
1494
1495 });
1496
1497
1498 it("should merge undefined and an object into one object", () => {
1499
1500 const stubParser = { parse() {} };
1501
1502 return assertMergedResult([
1503 {
1504 },
1505 {
1506 languageOptions: {
1507 parser: stubParser
1508 }
1509 }
1510 ], {
1511 plugins: {
1512 ...baseConfig.plugins
1513 },
1514
1515 languageOptions: {
1516 parser: stubParser
1517 }
1518 });
1519
1520 });
1521
1522 });
1523
1524
1525 describe("parserOptions", () => {
1526
1527 it("should error when an unexpected value is found", async () => {
1528
1529 await assertInvalidConfig([
1530 {
1531 languageOptions: {
1532 parserOptions: "true"
1533 }
1534 }
1535 ], "Expected an object.");
1536 });
1537
1538 it("should merge two objects when second object has different keys", () => assertMergedResult([
1539 {
1540 languageOptions: {
1541 parserOptions: {
1542 foo: "whatever"
1543 }
1544 }
1545 },
1546 {
1547 languageOptions: {
1548 parserOptions: {
1549 bar: "baz"
1550 }
1551 }
1552 }
1553 ], {
1554 plugins: baseConfig.plugins,
1555
1556 languageOptions: {
1557 parserOptions: {
1558 foo: "whatever",
1559 bar: "baz"
1560 }
1561 }
1562 }));
1563
1564 it("should deeply merge two objects when second object has different keys", () => assertMergedResult([
1565 {
1566 languageOptions: {
1567 parserOptions: {
1568 ecmaFeatures: {
1569 jsx: true
1570 }
1571 }
1572 }
1573 },
1574 {
1575 languageOptions: {
1576 parserOptions: {
1577 ecmaFeatures: {
1578 globalReturn: true
1579 }
1580 }
1581 }
1582 }
1583 ], {
1584 plugins: baseConfig.plugins,
1585
1586 languageOptions: {
1587 parserOptions: {
1588 ecmaFeatures: {
1589 jsx: true,
1590 globalReturn: true
1591 }
1592 }
1593 }
1594 }));
1595
1596 it("should deeply merge two objects when second object has missing key", () => assertMergedResult([
1597 {
1598 languageOptions: {
1599 parserOptions: {
1600 ecmaFeatures: {
1601 jsx: true
1602 }
1603 }
1604 }
1605 },
1606 {
1607 languageOptions: {
1608 ecmaVersion: 2021
1609 }
1610 }
1611 ], {
1612 plugins: baseConfig.plugins,
1613
1614 languageOptions: {
1615 ecmaVersion: 2021,
1616 parserOptions: {
1617 ecmaFeatures: {
1618 jsx: true
1619 }
1620 }
1621 }
1622 }));
1623
1624 it("should merge two objects when second object has overrides", () => assertMergedResult([
1625 {
1626 languageOptions: {
1627 parserOptions: {
1628 foo: "whatever"
1629 }
1630 }
1631 },
1632 {
1633 languageOptions: {
1634 parserOptions: {
1635 foo: "bar"
1636 }
1637 }
1638 }
1639 ], {
1640 plugins: baseConfig.plugins,
1641
1642 languageOptions: {
1643 parserOptions: {
1644 foo: "bar"
1645 }
1646 }
1647 }));
1648
1649 it("should merge an object and undefined into one object", () => assertMergedResult([
1650 {
1651 languageOptions: {
1652 parserOptions: {
1653 foo: "whatever"
1654 }
1655 }
1656 },
1657 {
1658 }
1659 ], {
1660 plugins: baseConfig.plugins,
1661
1662 languageOptions: {
1663 parserOptions: {
1664 foo: "whatever"
1665 }
1666 }
1667 }));
1668
1669
1670 it("should merge undefined and an object into one object", () => assertMergedResult([
1671 {
1672 },
1673 {
1674 languageOptions: {
1675 parserOptions: {
1676 foo: "bar"
1677 }
1678 }
1679 }
1680 ], {
1681 plugins: baseConfig.plugins,
1682
1683 languageOptions: {
1684 parserOptions: {
1685 foo: "bar"
1686 }
1687 }
1688 }));
1689
1690
1691 });
1692
1693
1694 });
1695
1696 describe("rules", () => {
1697
1698 it("should error when an unexpected value is found", async () => {
1699
1700 await assertInvalidConfig([
1701 {
1702 rules: true
1703 }
1704 ], "Expected an object.");
1705 });
1706
1707 it("should error when an invalid rule severity is set", async () => {
1708
1709 await assertInvalidConfig([
1710 {
1711 rules: {
1712 foo: true
1713 }
1714 }
1715 ], "Key \"rules\": Key \"foo\": Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
1716 });
1717
1718 it("should error when an invalid rule severity of the right type is set", async () => {
1719
1720 await assertInvalidConfig([
1721 {
1722 rules: {
1723 foo: 3
1724 }
1725 }
1726 ], "Key \"rules\": Key \"foo\": Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
1727 });
1728
1729 it("should error when an invalid rule severity is set in an array", async () => {
1730
1731 await assertInvalidConfig([
1732 {
1733 rules: {
1734 foo: [true]
1735 }
1736 }
1737 ], "Key \"rules\": Key \"foo\": Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
1738 });
1739
1740 it("should error when rule doesn't exist", async () => {
1741
1742 await assertInvalidConfig([
1743 {
1744 rules: {
1745 foox: [1, "bar"]
1746 }
1747 }
1748 ], /Key "rules": Key "foox": Could not find "foox" in plugin "@"./u);
1749 });
1750
1751 it("should error and suggest alternative when rule doesn't exist", async () => {
1752
1753 await assertInvalidConfig([
1754 {
1755 rules: {
1756 "test2/match": "error"
1757 }
1758 }
1759 ], /Key "rules": Key "test2\/match": Could not find "match" in plugin "test2"\. Did you mean "test1\/match"\?/u);
1760 });
1761
1762 it("should error when plugin for rule doesn't exist", async () => {
1763
1764 await assertInvalidConfig([
1765 {
1766 rules: {
1767 "doesnt-exist/match": "error"
1768 }
1769 }
1770 ], /Key "rules": Key "doesnt-exist\/match": Could not find plugin "doesnt-exist"\./u);
1771 });
1772
1773 it("should error when rule options don't match schema", async () => {
1774
1775 await assertInvalidConfig([
1776 {
1777 rules: {
1778 foo: [1, "bar"]
1779 }
1780 }
1781 ], /Value "bar" should be equal to one of the allowed values/u);
1782 });
1783
1784 it("should error when rule options don't match schema requiring at least one item", async () => {
1785
1786 await assertInvalidConfig([
1787 {
1788 rules: {
1789 foo2: 1
1790 }
1791 }
1792 ], /Value \[\] should NOT have fewer than 1 items/u);
1793 });
1794
1795 it("should merge two objects", () => assertMergedResult([
1796 {
1797 rules: {
1798 foo: 1,
1799 bar: "error"
1800 }
1801 },
1802 {
1803 rules: {
1804 baz: "warn",
1805 boom: 0
1806 }
1807 }
1808 ], {
1809 plugins: baseConfig.plugins,
1810
1811 rules: {
1812 foo: [1],
1813 bar: [2],
1814 baz: [1],
1815 boom: [0]
1816 }
1817 }));
1818
1819 it("should merge two objects when second object has simple overrides", () => assertMergedResult([
1820 {
1821 rules: {
1822 foo: [1, "always"],
1823 bar: "error"
1824 }
1825 },
1826 {
1827 rules: {
1828 foo: "error",
1829 bar: 0
1830 }
1831 }
1832 ], {
1833 plugins: baseConfig.plugins,
1834
1835 rules: {
1836 foo: [2, "always"],
1837 bar: [0]
1838 }
1839 }));
1840
1841 it("should merge two objects when second object has array overrides", () => assertMergedResult([
1842 {
1843 rules: {
1844 foo: 1,
1845 foo2: "error"
1846 }
1847 },
1848 {
1849 rules: {
1850 foo: ["error", "never"],
1851 foo2: ["warn", "foo"]
1852 }
1853 }
1854 ], {
1855 plugins: baseConfig.plugins,
1856 rules: {
1857 foo: [2, "never"],
1858 foo2: [1, "foo"]
1859 }
1860 }));
1861
1862 it("should merge two objects and options when second object overrides without options", () => assertMergedResult([
1863 {
1864 rules: {
1865 foo: [1, "always"],
1866 bar: "error"
1867 }
1868 },
1869 {
1870 plugins: {
1871 "@foo/baz/boom": {
1872 rules: {
1873 bang: {}
1874 }
1875 }
1876 },
1877 rules: {
1878 foo: ["error"],
1879 bar: 0,
1880 "@foo/baz/boom/bang": "error"
1881 }
1882 }
1883 ], {
1884 plugins: {
1885 ...baseConfig.plugins,
1886 "@foo/baz/boom": {
1887 rules: {
1888 bang: {}
1889 }
1890 }
1891 },
1892 rules: {
1893 foo: [2, "always"],
1894 bar: [0],
1895 "@foo/baz/boom/bang": [2]
1896 }
1897 }));
1898
1899 it("should merge an object and undefined into one object", () => assertMergedResult([
1900 {
1901 rules: {
1902 foo: 0,
1903 bar: 1
1904 }
1905 },
1906 {
1907 }
1908 ], {
1909 plugins: baseConfig.plugins,
1910 rules: {
1911 foo: [0],
1912 bar: [1]
1913 }
1914 }));
1915
1916 it("should merge a rule that doesn't exist without error when the rule is off", () => assertMergedResult([
1917 {
1918 rules: {
1919 foo: 0,
1920 bar: 1
1921 }
1922 },
1923 {
1924 rules: {
1925 nonExistentRule: 0,
1926 nonExistentRule2: ["off", "bar"]
1927 }
1928 }
1929 ], {
1930 plugins: baseConfig.plugins,
1931 rules: {
1932 foo: [0],
1933 bar: [1],
1934 nonExistentRule: [0],
1935 nonExistentRule2: [0, "bar"]
1936 }
1937 }));
1938
1939 });
1940
1941 });
1942 });