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 duplicate rules", () => {
136 const registry
= new autoconfig
.Registry(rulesConfig
);
138 registry
.populateFromCoreRules();
139 const semiCount
= Object
.keys(registry
.rules
).filter(ruleId
=> ruleId
=== "semi").length
;
141 assert
.strictEqual(semiCount
, 1);
145 describe("buildRuleSets()", () => {
149 const registry
= new autoconfig
.Registry(rulesConfig
);
151 ruleSets
= registry
.buildRuleSets();
154 it("should create an array of rule configuration sets", () => {
155 assert
.isArray(ruleSets
);
158 it("should include configs for each rule (at least for the first set)", () => {
159 assert
.sameMembers(Object
.keys(ruleSets
[0]), ["semi", "semi-spacing", "quotes"]);
162 it("should create the first set from default rule configs (severity only)", () => {
163 assert
.deepStrictEqual(ruleSets
[0], { semi
: SEVERITY
, "semi-spacing": SEVERITY
, quotes
: SEVERITY
});
166 it("should create as many ruleSets as the highest number of configs in a rule", () => {
168 // `quotes` has 7 possible configurations
169 assert
.lengthOf(ruleSets
, 7);
173 describe("lintSourceCode()", () => {
177 const config
= { ignore
: false };
178 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
180 registry
= new autoconfig
.Registry(rulesConfig
);
181 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
184 it("should populate the errorCount of all registryItems", () => {
185 const expectedRules
= ["semi", "semi-spacing", "quotes"];
187 assert
.sameMembers(Object
.keys(registry
.rules
), expectedRules
);
188 expectedRules
.forEach(ruleId
=> {
189 assert(registry
.rules
[ruleId
].length
> 0);
190 registry
.rules
[ruleId
].forEach(conf
=> {
191 assert
.isNumber(conf
.errorCount
);
196 it("should correctly set the error count of configurations", () => {
197 assert
.strictEqual(registry
.rules
.semi
[0].config
, SEVERITY
);
198 assert
.strictEqual(registry
.rules
.semi
[0].errorCount
, 0);
199 assert
.deepStrictEqual(registry
.rules
.semi
[2].config
, [SEVERITY
, "never"]);
200 assert
.strictEqual(registry
.rules
.semi
[2].errorCount
, 3);
203 it("should respect inline eslint config comments (and not crash when they make linting errors)", () => {
204 const config
= { ignore
: false };
205 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(CONFIG_COMMENTS_FILENAME
, config
);
206 const expectedRegistry
= [
207 { config
: 2, specificity
: 1, errorCount
: 3 },
208 { config
: [2, "always"], specificity
: 2, errorCount
: 3 },
209 { config
: [2, "never"], specificity
: 2, errorCount
: 3 }
212 registry
= new autoconfig
.Registry(rulesConfig
);
213 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
215 assert
.deepStrictEqual(registry
.rules
.semi
, expectedRegistry
);
219 describe("stripFailingConfigs()", () => {
223 const config
= { ignore
: false };
224 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
226 registry
= new autoconfig
.Registry(rulesConfig
);
227 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
228 registry
= registry
.stripFailingConfigs();
231 it("should remove all registryItems with a non-zero errorCount", () => {
232 assert
.lengthOf(registry
.rules
.semi
, 2);
233 assert
.lengthOf(registry
.rules
["semi-spacing"], 3);
234 assert
.lengthOf(registry
.rules
.quotes
, 1);
235 registry
.rules
.semi
.forEach(registryItem
=> {
236 assert
.strictEqual(registryItem
.errorCount
, 0);
238 registry
.rules
["semi-spacing"].forEach(registryItem
=> {
239 assert
.strictEqual(registryItem
.errorCount
, 0);
241 registry
.rules
.quotes
.forEach(registryItem
=> {
242 assert
.strictEqual(registryItem
.errorCount
, 0);
247 describe("getFailingRulesRegistry()", () => {
251 const config
= { ignore
: false };
252 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
253 let registry
= new autoconfig
.Registry(errorRulesConfig
);
255 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
256 failingRegistry
= registry
.getFailingRulesRegistry();
259 it("should return a registry with no registryItems with an errorCount of zero", () => {
260 const failingRules
= Object
.keys(failingRegistry
.rules
);
262 assert
.deepStrictEqual(failingRules
, ["no-unused-vars"]);
263 assert
.lengthOf(failingRegistry
.rules
["no-unused-vars"], 1);
264 assert(failingRegistry
.rules
["no-unused-vars"][0].errorCount
> 0);
268 describe("createConfig()", () => {
272 const config
= { ignore
: false };
273 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
274 let registry
= new autoconfig
.Registry(rulesConfig
);
276 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
277 registry
= registry
.stripFailingConfigs();
278 createdConfig
= registry
.createConfig();
281 it("should create a config with a rules property", () => {
282 assert
.property(createdConfig
, "rules");
285 it("should add rules which have only one registryItem to the config", () => {
286 const configuredRules
= Object
.keys(createdConfig
.rules
);
288 assert
.deepStrictEqual(configuredRules
, ["quotes"]);
291 it("should set the configuration of the rule to the registryItem's `config` value", () => {
292 assert
.deepStrictEqual(createdConfig
.rules
.quotes
, [2, "double", "avoid-escape"]);
295 it("should not care how many errors the config has", () => {
296 const config
= { ignore
: false };
297 const sourceCode
= sourceCodeUtils
.getSourceCodeOfFiles(SOURCE_CODE_FIXTURE_FILENAME
, config
);
298 let registry
= new autoconfig
.Registry(errorRulesConfig
);
300 registry
= registry
.lintSourceCode(sourceCode
, defaultOptions
);
301 const failingRegistry
= registry
.getFailingRulesRegistry();
303 createdConfig
= failingRegistry
.createConfig();
304 const configuredRules
= Object
.keys(createdConfig
.rules
);
306 assert
.deepStrictEqual(configuredRules
, ["no-unused-vars"]);
310 describe("filterBySpecificity()", () => {
314 registry
= new autoconfig
.Registry(rulesConfig
);
317 it("should return a registry where all configs have a desired specificity", () => {
318 const filteredRegistry1
= registry
.filterBySpecificity(1);
319 const filteredRegistry2
= registry
.filterBySpecificity(2);
320 const filteredRegistry3
= registry
.filterBySpecificity(3);
322 assert
.lengthOf(filteredRegistry1
.rules
.semi
, 1);
323 assert
.lengthOf(filteredRegistry1
.rules
["semi-spacing"], 1);
324 assert
.lengthOf(filteredRegistry1
.rules
.quotes
, 1);
325 assert
.lengthOf(filteredRegistry2
.rules
.semi
, 2);
326 assert
.lengthOf(filteredRegistry2
.rules
["semi-spacing"], 4);
327 assert
.lengthOf(filteredRegistry2
.rules
.quotes
, 3);
328 assert
.lengthOf(filteredRegistry3
.rules
.quotes
, 3);
333 describe("extendFromRecommended()", () => {
334 it("should return a configuration which has `extends` key with Array type value", () => {
335 const oldConfig
= { extends: [], rules
: {} };
336 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
338 assert
.exists(newConfig
.extends);
339 assert
.isArray(newConfig
.extends);
342 it("should return a configuration which has array property `extends`", () => {
343 const oldConfig
= { extends: [], rules
: {} };
344 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
346 assert
.include(newConfig
.extends, "eslint:recommended");
349 it("should return a configuration which preserves the previous extending configurations", () => {
350 const oldConfig
= { extends: ["previous:configuration1", "previous:configuration2"], rules
: {} };
351 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
353 assert
.includeMembers(newConfig
.extends, oldConfig
.extends);
356 it("should return a configuration which has `eslint:recommended` at the first of `extends`", () => {
357 const oldConfig
= { extends: ["previous:configuration1", "previous:configuration2"], rules
: {} };
358 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
359 const [firstExtendInNewConfig
] = newConfig
.extends;
361 assert
.strictEqual(firstExtendInNewConfig
, "eslint:recommended");
364 it("should return a configuration which not includes rules configured in `eslint:recommended`", () => {
365 const oldConfig
= { extends: [], rules
: { ...recommendedConfig
.rules
} };
366 const newConfig
= autoconfig
.extendFromRecommended(oldConfig
);
368 assert
.notInclude(newConfig
.rules
, oldConfig
.rules
);