2 * @fileoverview Used for creating a suggested configuration based on project code.
3 * @author Ian VanSchooten
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const assert
= require("chai").assert
,
13 autoconfig
= require("../../../lib/init/autoconfig"),
14 sourceCodeUtils
= require("../../../lib/init/source-code-utils"),
15 baseDefaultOptions
= require("../../../conf/default-cli-options"),
16 recommendedConfig
= require("../../conf/eslint-recommended");
18 const defaultOptions
= Object
.assign({}, baseDefaultOptions
, { cwd
: process
.cwd() });
20 //------------------------------------------------------------------------------
22 //------------------------------------------------------------------------------
24 const SOURCE_CODE_FIXTURE_FILENAME
= "./tests/fixtures/autoconfig/source.js";
25 const CONFIG_COMMENTS_FILENAME
= "./tests/fixtures/autoconfig/source-with-comments.js";
28 //------------------------------------------------------------------------------
30 //------------------------------------------------------------------------------
33 semi
: [SEVERITY
, [SEVERITY
, "always"], [SEVERITY
, "never"]],
34 "semi-spacing": [SEVERITY
,
35 [SEVERITY
, { before
: true, after
: true }],
36 [SEVERITY
, { before
: true, after
: false }],
37 [SEVERITY
, { before
: false, after
: true }],
38 [SEVERITY
, { before
: false, after
: false }]
43 [SEVERITY
, "backtick"],
44 [SEVERITY
, "single", "avoid-escape"],
45 [SEVERITY
, "double", "avoid-escape"],
46 [SEVERITY
, "backtick", "avoid-escape"]]
49 const errorRulesConfig
= {
50 "no-unused-vars": [SEVERITY
],
51 "semi-spacing": [SEVERITY
,
52 [SEVERITY
, { before
: true, after
: true }],
53 [SEVERITY
, { before
: true, after
: false }],
54 [SEVERITY
, { before
: false, after
: true }],
55 [SEVERITY
, { before
: false, after
: false }]
59 describe("autoconfig", () => {
61 describe("Registry", () => {
63 it("should set up a registry for rules in a provided rulesConfig", () => {
64 const expectedRules
= Object
.keys(rulesConfig
);
65 const registry
= new autoconfig
.Registry(rulesConfig
);
67 assert
.strictEqual(Object
.keys(registry
.rules
).length
, 3);
68 assert
.sameMembers(Object
.keys(registry
.rules
), expectedRules
);
69 assert
.isArray(registry
.rules
.semi
);
70 assert
.isArray(registry
.rules
["semi-spacing"]);
71 assert
.isArray(registry
.rules
.quotes
);
72 assert
.lengthOf(registry
.rules
.semi
, 3);
73 assert
.lengthOf(registry
.rules
["semi-spacing"], 5);
74 assert
.lengthOf(registry
.rules
.quotes
, 7);
77 it("should not have any rules if constructed without a config argument", () => {
78 const registry
= new autoconfig
.Registry();
80 assert
.isObject(registry
.rules
);
81 assert
.lengthOf(Object
.keys(registry
.rules
), 0);
84 it("should create registryItems for each rule with the proper keys", () => {
85 const registry
= new autoconfig
.Registry(rulesConfig
);
87 assert
.isObject(registry
.rules
.semi
[0]);
88 assert
.isObject(registry
.rules
["semi-spacing"][0]);
89 assert
.isObject(registry
.rules
.quotes
[0]);
90 assert
.property(registry
.rules
.semi
[0], "config");
91 assert
.property(registry
.rules
.semi
[0], "specificity");
92 assert
.property(registry
.rules
.semi
[0], "errorCount");
95 it("should populate the config property correctly", () => {
96 const registry
= new autoconfig
.Registry(rulesConfig
);
98 assert
.strictEqual(registry
.rules
.quotes
[0].config
, SEVERITY
);
99 assert
.deepStrictEqual(registry
.rules
.quotes
[1].config
, [SEVERITY
, "single"]);
100 assert
.deepStrictEqual(registry
.rules
.quotes
[2].config
, [SEVERITY
, "double"]);
101 assert
.deepStrictEqual(registry
.rules
.quotes
[3].config
, [SEVERITY
, "backtick"]);
102 assert
.deepStrictEqual(registry
.rules
.quotes
[4].config
, [SEVERITY
, "single", "avoid-escape"]);
103 assert
.deepStrictEqual(registry
.rules
.quotes
[5].config
, [SEVERITY
, "double", "avoid-escape"]);
104 assert
.deepStrictEqual(registry
.rules
.quotes
[6].config
, [SEVERITY
, "backtick", "avoid-escape"]);
107 it("should assign the correct specificity", () => {
108 const registry
= new autoconfig
.Registry(rulesConfig
);
110 assert
.strictEqual(registry
.rules
.quotes
[0].specificity
, 1);
111 assert
.strictEqual(registry
.rules
.quotes
[1].specificity
, 2);
112 assert
.strictEqual(registry
.rules
.quotes
[6].specificity
, 3);
115 it("should initially leave the errorCount as undefined", () => {
116 const registry
= new autoconfig
.Registry(rulesConfig
);
118 assert
.isUndefined(registry
.rules
.quotes
[0].errorCount
);
119 assert
.isUndefined(registry
.rules
.quotes
[1].errorCount
);
120 assert
.isUndefined(registry
.rules
.quotes
[6].errorCount
);
123 describe("populateFromCoreRules()", () => {
125 it("should add core rules to registry", () => {
126 const registry
= new autoconfig
.Registry();
128 registry
.populateFromCoreRules();
129 const finalRuleCount
= Object
.keys(registry
.rules
).length
;
131 assert(finalRuleCount
> 0);
132 assert
.include(Object
.keys(registry
.rules
), "eqeqeq");
135 it("should not add deprecated rules", () => {
136 const registry
= new autoconfig
.Registry();
138 registry
.populateFromCoreRules();
140 const { rules
} = registry
;
142 assert
.notProperty(rules
, "id-blacklist");
143 assert
.notProperty(rules
, "no-negated-in-lhs");
144 assert
.notProperty(rules
, "no-process-exit");
145 assert
.notProperty(rules
, "no-spaced-func");
146 assert
.notProperty(rules
, "prefer-reflect");
149 it("should not add duplicate rules", () => {
150 const registry
= new autoconfig
.Registry(rulesConfig
);
152 registry
.populateFromCoreRules();
153 const semiCount
= Object
.keys(registry
.rules
).filter(ruleId
=> ruleId
=== "semi").length
;
155 assert
.strictEqual(semiCount
, 1);
159 describe("buildRuleSets()", () => {
163 const registry
= new autoconfig
.Registry(rulesConfig
);
165 ruleSets
= registry
.buildRuleSets();
168 it("should create an array of rule configuration sets", () => {
169 assert
.isArray(ruleSets
);
172 it("should include configs for each rule (at least for the first set)", () => {
173 assert
.sameMembers(Object
.keys(ruleSets
[0]), ["semi", "semi-spacing", "quotes"]);
176 it("should create the first set from default rule configs (severity only)", () => {
177 assert
.deepStrictEqual(ruleSets
[0], { semi
: SEVERITY
, "semi-spacing": SEVERITY
, quotes
: SEVERITY
});
180 it("should create as many ruleSets as the highest number of configs in a rule", () => {
182 // `quotes` has 7 possible configurations
183 assert
.lengthOf(ruleSets
, 7);
187 describe("lintSourceCode()", () => {
191 const config
= { ignore
: false };
192 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
194 registry
= new autoconfig
.Registry(rulesConfig
);
195 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
198 it("should populate the errorCount of all registryItems", () => {
199 const expectedRules
= ["semi", "semi-spacing", "quotes"];
201 assert
.sameMembers(Object
.keys(registry
.rules
), expectedRules
);
202 expectedRules
.forEach(ruleId
=> {
203 assert(registry
.rules
[ruleId
].length
> 0);
204 registry
.rules
[ruleId
].forEach(conf
=> {
205 assert
.isNumber(conf
.errorCount
);
210 it("should correctly set the error count of configurations", () => {
211 assert
.strictEqual(registry
.rules
.semi
[0].config
, SEVERITY
);
212 assert
.strictEqual(registry
.rules
.semi
[0].errorCount
, 0);
213 assert
.deepStrictEqual(registry
.rules
.semi
[2].config
, [SEVERITY
, "never"]);
214 assert
.strictEqual(registry
.rules
.semi
[2].errorCount
, 3);
217 it("should respect inline eslint config comments (and not crash when they make linting errors)", () => {
218 const config
= { ignore
: false };
219 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(CONFIG_COMMENTS_FILENAME
, config
);
220 const expectedRegistry
= [
221 { config
: 2, specificity
: 1, errorCount
: 3 },
222 { config
: [2, "always"], specificity
: 2, errorCount
: 3 },
223 { config
: [2, "never"], specificity
: 2, errorCount
: 3 }
226 registry
= new autoconfig
.Registry(rulesConfig
);
227 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
229 assert
.deepStrictEqual(registry
.rules
.semi
, expectedRegistry
);
233 describe("stripFailingConfigs()", () => {
237 const config
= { ignore
: false };
238 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
240 registry
= new autoconfig
.Registry(rulesConfig
);
241 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
242 registry
= registry
.stripFailingConfigs();
245 it("should remove all registryItems with a non-zero errorCount", () => {
246 assert
.lengthOf(registry
.rules
.semi
, 2);
247 assert
.lengthOf(registry
.rules
["semi-spacing"], 3);
248 assert
.lengthOf(registry
.rules
.quotes
, 1);
249 registry
.rules
.semi
.forEach(registryItem
=> {
250 assert
.strictEqual(registryItem
.errorCount
, 0);
252 registry
.rules
["semi-spacing"].forEach(registryItem
=> {
253 assert
.strictEqual(registryItem
.errorCount
, 0);
255 registry
.rules
.quotes
.forEach(registryItem
=> {
256 assert
.strictEqual(registryItem
.errorCount
, 0);
261 describe("getFailingRulesRegistry()", () => {
265 const config
= { ignore
: false };
266 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
267 let registry
= new autoconfig
.Registry(errorRulesConfig
);
269 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
270 failingRegistry
= registry
.getFailingRulesRegistry();
273 it("should return a registry with no registryItems with an errorCount of zero", () => {
274 const failingRules
= Object
.keys(failingRegistry
.rules
);
276 assert
.deepStrictEqual(failingRules
, ["no-unused-vars"]);
277 assert
.lengthOf(failingRegistry
.rules
["no-unused-vars"], 1);
278 assert(failingRegistry
.rules
["no-unused-vars"][0].errorCount
> 0);
282 describe("createConfig()", () => {
286 const config
= { ignore
: false };
287 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
288 let registry
= new autoconfig
.Registry(rulesConfig
);
290 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
291 registry
= registry
.stripFailingConfigs();
292 createdConfig
= registry
.createConfig();
295 it("should create a config with a rules property", () => {
296 assert
.property(createdConfig
, "rules");
299 it("should add rules which have only one registryItem to the config", () => {
300 const configuredRules
= Object
.keys(createdConfig
.rules
);
302 assert
.deepStrictEqual(configuredRules
, ["quotes"]);
305 it("should set the configuration of the rule to the registryItem's `config` value", () => {
306 assert
.deepStrictEqual(createdConfig
.rules
.quotes
, [2, "double", "avoid-escape"]);
309 it("should not care how many errors the config has", () => {
310 const config
= { ignore
: false };
311 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
312 let registry
= new autoconfig
.Registry(errorRulesConfig
);
314 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
315 const failingRegistry
= registry
.getFailingRulesRegistry();
317 createdConfig
= failingRegistry
.createConfig();
318 const configuredRules
= Object
.keys(createdConfig
.rules
);
320 assert
.deepStrictEqual(configuredRules
, ["no-unused-vars"]);
324 describe("filterBySpecificity()", () => {
328 registry
= new autoconfig
.Registry(rulesConfig
);
331 it("should return a registry where all configs have a desired specificity", () => {
332 const filteredRegistry1
= registry
.filterBySpecificity(1);
333 const filteredRegistry2
= registry
.filterBySpecificity(2);
334 const filteredRegistry3
= registry
.filterBySpecificity(3);
336 assert
.lengthOf(filteredRegistry1
.rules
.semi
, 1);
337 assert
.lengthOf(filteredRegistry1
.rules
["semi-spacing"], 1);
338 assert
.lengthOf(filteredRegistry1
.rules
.quotes
, 1);
339 assert
.lengthOf(filteredRegistry2
.rules
.semi
, 2);
340 assert
.lengthOf(filteredRegistry2
.rules
["semi-spacing"], 4);
341 assert
.lengthOf(filteredRegistry2
.rules
.quotes
, 3);
342 assert
.lengthOf(filteredRegistry3
.rules
.quotes
, 3);
347 describe("extendFromRecommended()", () => {
348 it("should return a configuration which has `extends` key with Array type value", () => {
349 const oldConfig
= { extends: [], rules
: {} };
350 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
352 assert
.exists(newConfig
.extends);
353 assert
.isArray(newConfig
.extends);
356 it("should return a configuration which has array property `extends`", () => {
357 const oldConfig
= { extends: [], rules
: {} };
358 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
360 assert
.include(newConfig
.extends, "eslint:recommended");
363 it("should return a configuration which preserves the previous extending configurations", () => {
364 const oldConfig
= { extends: ["previous:configuration1", "previous:configuration2"], rules
: {} };
365 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
367 assert
.includeMembers(newConfig
.extends, oldConfig
.extends);
370 it("should return a configuration which has `eslint:recommended` at the first of `extends`", () => {
371 const oldConfig
= { extends: ["previous:configuration1", "previous:configuration2"], rules
: {} };
372 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
373 const [firstExtendInNewConfig
] = newConfig
.extends;
375 assert
.strictEqual(firstExtendInNewConfig
, "eslint:recommended");
378 it("should return a configuration which not includes rules configured in `eslint:recommended`", () => {
379 const oldConfig
= { extends: [], rules
: { ...recommendedConfig
.rules
} };
380 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
382 assert
.notInclude(newConfig
.rules
, oldConfig
.rules
);