2 * @fileoverview Tests for configInitializer.
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const assert
= require("chai").assert
,
14 path
= require("path"),
16 sinon
= require("sinon"),
17 sh
= require("shelljs"),
18 espree
= require("espree"),
19 autoconfig
= require("../../../lib/init/autoconfig"),
20 npmUtils
= require("../../../lib/init/npm-utils");
22 const originalDir
= process
.cwd();
23 const proxyquire
= require("proxyquire").noPreserveCache();
25 //------------------------------------------------------------------------------
27 //------------------------------------------------------------------------------
30 let pkgJSONContents
= {};
33 describe("configInitializer", () => {
38 npmFetchPeerDependenciesStub
,
40 localESLintVersion
= null;
46 const requireStubs
= {
47 "../shared/logging": log
,
48 "../shared/relative-module-resolver": {
50 if (localESLintVersion
) {
51 return `local-eslint-${localESLintVersion}`;
53 throw new Error("Cannot find module");
56 "local-eslint-3.18.0": { linter
: { version
: "3.18.0" }, "@noCallThru": true },
57 "local-eslint-3.19.0": { linter
: { version
: "3.19.0" }, "@noCallThru": true },
58 "local-eslint-4.0.0": { linter
: { version
: "4.0.0" }, "@noCallThru": true }
62 * Returns the path inside of the fixture directory.
63 * @param {...string} args file path segments.
64 * @returns {string} The path inside the fixture directory.
67 function getFixturePath(...args
) {
68 const filepath
= path
.join(fixtureDir
, ...args
);
71 return fs
.realpathSync(filepath
);
77 // copy into clean area so as not to get "infected" by this project's .eslintrc files
79 fixtureDir
= path
.join(os
.tmpdir(), "eslint/fixtures/config-initializer");
80 sh
.mkdir("-p", fixtureDir
);
81 sh
.cp("-r", "./tests/fixtures/config-initializer/.", fixtureDir
);
82 fixtureDir
= fs
.realpathSync(fixtureDir
);
86 npmInstallStub
= sinon
.stub(npmUtils
, "installSyncSaveDev");
87 npmCheckStub
= sinon
.stub(npmUtils
, "checkDevDeps").callsFake(packages
=> packages
.reduce((status
, pkg
) => {
91 npmFetchPeerDependenciesStub
= sinon
92 .stub(npmUtils
, "fetchPeerDependencies")
95 "eslint-plugin-jsx-a11y": "^5.0.1",
96 "eslint-plugin-import": "^2.2.0",
97 "eslint-plugin-react": "^7.0.1"
99 init
= proxyquire("../../../lib/init/config-initializer", requireStubs
);
103 log
.info
.resetHistory();
104 log
.error
.resetHistory();
105 npmInstallStub
.restore();
106 npmCheckStub
.restore();
107 npmFetchPeerDependenciesStub
.restore();
111 sh
.rm("-r", fixtureDir
);
114 describe("processAnswers()", () => {
116 describe("prompt", () => {
134 it("should create default config", () => {
135 const config
= init
.processAnswers(answers
);
137 assert
.deepStrictEqual(config
.rules
.indent
, ["error", 2]);
138 assert
.deepStrictEqual(config
.rules
.quotes
, ["error", "single"]);
139 assert
.deepStrictEqual(config
.rules
["linebreak-style"], ["error", "unix"]);
140 assert
.deepStrictEqual(config
.rules
.semi
, ["error", "always"]);
141 assert
.strictEqual(config
.env
.es2021
, true);
142 assert
.strictEqual(config
.parserOptions
.ecmaVersion
, espree
.latestEcmaVersion
);
143 assert
.strictEqual(config
.parserOptions
.sourceType
, "module");
144 assert
.strictEqual(config
.env
.browser
, true);
145 assert
.strictEqual(config
.extends, "eslint:recommended");
148 it("should disable semi", () => {
149 answers
.semi
= false;
150 const config
= init
.processAnswers(answers
);
152 assert
.deepStrictEqual(config
.rules
.semi
, ["error", "never"]);
155 it("should enable react plugin", () => {
156 answers
.framework
= "react";
157 const config
= init
.processAnswers(answers
);
159 assert
.strictEqual(config
.parserOptions
.ecmaFeatures
.jsx
, true);
160 assert
.strictEqual(config
.parserOptions
.ecmaVersion
, espree
.latestEcmaVersion
);
161 assert
.deepStrictEqual(config
.plugins
, ["react"]);
164 it("should enable vue plugin", () => {
165 answers
.framework
= "vue";
166 const config
= init
.processAnswers(answers
);
168 assert
.strictEqual(config
.parserOptions
.ecmaVersion
, espree
.latestEcmaVersion
);
169 assert
.deepStrictEqual(config
.plugins
, ["vue"]);
170 assert
.deepStrictEqual(config
.extends, ["eslint:recommended", "plugin:vue/essential"]);
173 it("should enable typescript parser and plugin", () => {
174 answers
.typescript
= true;
175 const config
= init
.processAnswers(answers
);
177 assert
.strictEqual(config
.parser
, "@typescript-eslint/parser");
178 assert
.deepStrictEqual(config
.plugins
, ["@typescript-eslint"]);
179 assert
.deepStrictEqual(config
.extends, ["eslint:recommended", "plugin:@typescript-eslint/recommended"]);
182 it("should enable typescript parser and plugin with vue", () => {
183 answers
.framework
= "vue";
184 answers
.typescript
= true;
185 const config
= init
.processAnswers(answers
);
187 assert
.deepStrictEqual(config
.extends, ["eslint:recommended", "plugin:vue/essential", "plugin:@typescript-eslint/recommended"]);
188 assert
.strictEqual(config
.parserOptions
.parser
, "@typescript-eslint/parser");
189 assert
.deepStrictEqual(config
.plugins
, ["vue", "@typescript-eslint"]);
192 it("should extend eslint:recommended", () => {
193 const config
= init
.processAnswers(answers
);
195 assert
.strictEqual(config
.extends, "eslint:recommended");
198 it("should not use commonjs by default", () => {
199 const config
= init
.processAnswers(answers
);
201 assert
.isUndefined(config
.env
.commonjs
);
204 it("should use commonjs when set", () => {
205 answers
.moduleType
= "commonjs";
206 const config
= init
.processAnswers(answers
);
208 assert
.isTrue(config
.env
.commonjs
);
212 describe("guide", () => {
213 it("should support the google style guide", () => {
214 const config
= { extends: "google" };
215 const modules
= init
.getModulesList(config
);
217 assert
.deepStrictEqual(config
, { extends: "google", installedESLint
: true });
218 assert
.include(modules
, "eslint-config-google@latest");
221 it("should support the airbnb style guide", () => {
222 const config
= { extends: "airbnb" };
223 const modules
= init
.getModulesList(config
);
225 assert
.deepStrictEqual(config
, { extends: "airbnb", installedESLint
: true });
226 assert
.include(modules
, "eslint-config-airbnb@latest");
229 it("should support the airbnb base style guide", () => {
230 const config
= { extends: "airbnb-base" };
231 const modules
= init
.getModulesList(config
);
233 assert
.deepStrictEqual(config
, { extends: "airbnb-base", installedESLint
: true });
234 assert
.include(modules
, "eslint-config-airbnb-base@latest");
237 it("should support the standard style guide", () => {
238 const config
= { extends: "standard" };
239 const modules
= init
.getModulesList(config
);
241 assert
.deepStrictEqual(config
, { extends: "standard", installedESLint
: true });
242 assert
.include(modules
, "eslint-config-standard@latest");
245 it("should support the xo style guide", () => {
246 const config
= { extends: "xo" };
247 const modules
= init
.getModulesList(config
);
249 assert
.deepStrictEqual(config
, { extends: "xo", installedESLint
: true });
250 assert
.include(modules
, "eslint-config-xo@latest");
253 it("should install required sharable config", () => {
254 const config
= { extends: "google" };
256 init
.installModules(init
.getModulesList(config
));
257 assert(npmInstallStub
.calledOnce
);
258 assert(npmInstallStub
.firstCall
.args
[0].some(name
=> name
.startsWith("eslint-config-google@")));
261 it("should install ESLint if not installed locally", () => {
262 const config
= { extends: "google" };
264 init
.installModules(init
.getModulesList(config
));
265 assert(npmInstallStub
.calledOnce
);
266 assert(npmInstallStub
.firstCall
.args
[0].some(name
=> name
.startsWith("eslint@")));
269 it("should install peerDependencies of the sharable config", () => {
270 const config
= { extends: "airbnb" };
272 init
.installModules(init
.getModulesList(config
));
274 assert(npmFetchPeerDependenciesStub
.calledOnce
);
275 assert(npmFetchPeerDependenciesStub
.firstCall
.args
[0] === "eslint-config-airbnb@latest");
276 assert(npmInstallStub
.calledOnce
);
277 assert
.deepStrictEqual(
278 npmInstallStub
.firstCall
.args
[0],
280 "eslint-config-airbnb@latest",
282 "eslint-plugin-jsx-a11y@^5.0.1",
283 "eslint-plugin-import@^2.2.0",
284 "eslint-plugin-react@^7.0.1"
289 describe("hasESLintVersionConflict (Note: peerDependencies always `eslint: \"^3.19.0\"` by stubs)", () => {
290 describe("if local ESLint is not found,", () => {
292 localESLintVersion
= null;
295 it("should return false.", () => {
296 const result
= init
.hasESLintVersionConflict({ styleguide
: "airbnb" });
298 assert
.strictEqual(result
, false);
302 describe("if local ESLint is 3.19.0,", () => {
304 localESLintVersion
= "3.19.0";
307 it("should return false.", () => {
308 const result
= init
.hasESLintVersionConflict({ styleguide
: "airbnb" });
310 assert
.strictEqual(result
, false);
314 describe("if local ESLint is 4.0.0,", () => {
316 localESLintVersion
= "4.0.0";
319 it("should return true.", () => {
320 const result
= init
.hasESLintVersionConflict({ styleguide
: "airbnb" });
322 assert
.strictEqual(result
, true);
326 describe("if local ESLint is 3.18.0,", () => {
328 localESLintVersion
= "3.18.0";
331 it("should return true.", () => {
332 const result
= init
.hasESLintVersionConflict({ styleguide
: "airbnb" });
334 assert
.strictEqual(result
, true);
339 it("should support the standard style guide with Vue.js", () => {
342 extends: ["plugin:vue/essential", "standard"]
344 const modules
= init
.getModulesList(config
);
346 assert
.include(modules
, "eslint-plugin-vue@latest");
347 assert
.include(modules
, "eslint-config-standard@latest");
350 it("should support custom parser", () => {
352 parser
: "@typescript-eslint/parser"
354 const modules
= init
.getModulesList(config
);
356 assert
.include(modules
, "@typescript-eslint/parser@latest");
359 it("should support custom parser with Vue.js", () => {
362 // We should declare the parser at `parserOptions` when using with `eslint-plugin-vue`.
364 parser
: "@typescript-eslint/parser"
367 const modules
= init
.getModulesList(config
);
369 assert
.include(modules
, "@typescript-eslint/parser@latest");
373 describe("auto", () => {
374 const completeSpy
= sinon
.spy();
379 getFixturePath("lib"),
380 getFixturePath("tests")
391 sinon
.stub(console
, "log"); // necessary to replace, because of progress bar
393 process
.chdir(fixtureDir
);
394 config
= init
.processAnswers(answers
);
403 process
.chdir(originalDir
);
407 it("should create a config", () => {
408 assert
.isTrue(completeSpy
.notCalled
);
412 it("should create the config based on examined files", () => {
413 assert
.deepStrictEqual(config
.rules
.quotes
, ["error", "double"]);
414 assert
.strictEqual(config
.rules
.semi
, "off");
417 it("should extend and not disable recommended rules", () => {
418 assert
.strictEqual(config
.extends, "eslint:recommended");
419 assert
.notProperty(config
.rules
, "no-debugger");
422 it("should not include deprecated rules", () => {
423 assert
.notProperty(config
.rules
, "id-blacklist");
424 assert
.notProperty(config
.rules
, "no-negated-in-lhs");
425 assert
.notProperty(config
.rules
, "no-process-exit");
426 assert
.notProperty(config
.rules
, "no-spaced-func");
427 assert
.notProperty(config
.rules
, "prefer-reflect");
430 it("should support new ES features if using later ES version", () => {
431 const filename
= getFixturePath("new-es-features");
433 answers
.patterns
= filename
;
434 answers
.ecmaVersion
= 2017;
435 process
.chdir(fixtureDir
);
436 config
= init
.processAnswers(answers
);
439 it("should throw on fatal parsing error", () => {
440 const filename
= getFixturePath("parse-error");
442 sinon
.stub(autoconfig
, "extendFromRecommended");
443 answers
.patterns
= filename
;
444 process
.chdir(fixtureDir
);
445 assert
.throws(() => {
446 config
= init
.processAnswers(answers
);
447 }, "Parsing error: Unexpected token ;");
450 it("should throw if no files are matched from patterns", () => {
451 sinon
.stub(autoconfig
, "extendFromRecommended");
452 answers
.patterns
= "not-a-real-filename";
453 process
.chdir(fixtureDir
);
454 assert
.throws(() => {
455 config
= init
.processAnswers(answers
);
456 }, "No files matching 'not-a-real-filename' were found.");
461 describe("writeFile()", () => {
479 name
: "config-initializer",
483 process
.chdir(fixtureDir
);
485 pkgJSONPath
= path
.resolve(fixtureDir
, "package.json");
489 process
.chdir(originalDir
);
492 it("should create .eslintrc.json", () => {
493 const config
= init
.processAnswers(answers
);
494 const filePath
= path
.resolve(fixtureDir
, ".eslintrc.json");
496 fs
.writeFileSync(pkgJSONPath
, JSON
.stringify(pkgJSONContents
));
498 init
.writeFile(config
, answers
.format
);
500 assert
.isTrue(fs
.existsSync(filePath
));
502 fs
.unlinkSync(filePath
);
503 fs
.unlinkSync(pkgJSONPath
);
506 it("should create .eslintrc.js", () => {
507 answers
.format
= "JavaScript";
509 const config
= init
.processAnswers(answers
);
510 const filePath
= path
.resolve(fixtureDir
, ".eslintrc.js");
512 fs
.writeFileSync(pkgJSONPath
, JSON
.stringify(pkgJSONContents
));
514 init
.writeFile(config
, answers
.format
);
516 assert
.isTrue(fs
.existsSync(filePath
));
518 fs
.unlinkSync(filePath
);
519 fs
.unlinkSync(pkgJSONPath
);
522 it("should create .eslintrc.yml", () => {
523 answers
.format
= "YAML";
525 const config
= init
.processAnswers(answers
);
526 const filePath
= path
.resolve(fixtureDir
, ".eslintrc.yml");
528 fs
.writeFileSync(pkgJSONPath
, JSON
.stringify(pkgJSONContents
));
530 init
.writeFile(config
, answers
.format
);
532 assert
.isTrue(fs
.existsSync(filePath
));
534 fs
.unlinkSync(filePath
);
535 fs
.unlinkSync(pkgJSONPath
);
538 // For https://github.com/eslint/eslint/issues/14137
539 it("should create .eslintrc.cjs", () => {
540 answers
.format
= "JavaScript";
542 // create package.json with "type": "module"
543 pkgJSONContents
.type
= "module";
545 fs
.writeFileSync(pkgJSONPath
, JSON
.stringify(pkgJSONContents
));
547 const config
= init
.processAnswers(answers
);
548 const filePath
= path
.resolve(fixtureDir
, ".eslintrc.cjs");
550 init
.writeFile(config
, answers
.format
);
552 assert
.isTrue(fs
.existsSync(filePath
));
554 fs
.unlinkSync(filePath
);
555 fs
.unlinkSync(pkgJSONPath
);
558 it("should create .eslintrc.json even with type: 'module'", () => {
559 answers
.format
= "JSON";
561 // create package.json with "type": "module"
562 pkgJSONContents
.type
= "module";
564 fs
.writeFileSync(pkgJSONPath
, JSON
.stringify(pkgJSONContents
));
566 const config
= init
.processAnswers(answers
);
567 const filePath
= path
.resolve(fixtureDir
, ".eslintrc.json");
569 init
.writeFile(config
, answers
.format
);
571 assert
.isTrue(fs
.existsSync(filePath
));
573 fs
.unlinkSync(filePath
);
574 fs
.unlinkSync(pkgJSONPath
);