]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/init/config-initializer.js
bump version to 8.4.0-3
[pve-eslint.git] / eslint / tests / lib / init / config-initializer.js
1 /**
2 * @fileoverview Tests for configInitializer.
3 * @author Ilya Volodin
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const assert = require("chai").assert,
13 fs = require("fs"),
14 path = require("path"),
15 os = require("os"),
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");
21
22 const originalDir = process.cwd();
23 const proxyquire = require("proxyquire").noPreserveCache();
24
25 //------------------------------------------------------------------------------
26 // Tests
27 //------------------------------------------------------------------------------
28
29 let answers = {};
30 let pkgJSONContents = {};
31 let pkgJSONPath = "";
32
33 describe("configInitializer", () => {
34
35 let fixtureDir,
36 npmCheckStub,
37 npmInstallStub,
38 npmFetchPeerDependenciesStub,
39 init,
40 localESLintVersion = null;
41
42 const log = {
43 info: sinon.spy(),
44 error: sinon.spy()
45 };
46 const requireStubs = {
47 "../shared/logging": log,
48 "../shared/relative-module-resolver": {
49 resolve() {
50 if (localESLintVersion) {
51 return `local-eslint-${localESLintVersion}`;
52 }
53 throw new Error("Cannot find module");
54 }
55 },
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 }
59 };
60
61 /**
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.
65 * @private
66 */
67 function getFixturePath(...args) {
68 const filepath = path.join(fixtureDir, ...args);
69
70 try {
71 return fs.realpathSync(filepath);
72 } catch {
73 return filepath;
74 }
75 }
76
77 // copy into clean area so as not to get "infected" by this project's .eslintrc files
78 before(() => {
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);
83 });
84
85 beforeEach(() => {
86 npmInstallStub = sinon.stub(npmUtils, "installSyncSaveDev");
87 npmCheckStub = sinon.stub(npmUtils, "checkDevDeps").callsFake(packages => packages.reduce((status, pkg) => {
88 status[pkg] = false;
89 return status;
90 }, {}));
91 npmFetchPeerDependenciesStub = sinon
92 .stub(npmUtils, "fetchPeerDependencies")
93 .returns({
94 eslint: "^3.19.0",
95 "eslint-plugin-jsx-a11y": "^5.0.1",
96 "eslint-plugin-import": "^2.2.0",
97 "eslint-plugin-react": "^7.0.1"
98 });
99 init = proxyquire("../../../lib/init/config-initializer", requireStubs);
100 });
101
102 afterEach(() => {
103 log.info.resetHistory();
104 log.error.resetHistory();
105 npmInstallStub.restore();
106 npmCheckStub.restore();
107 npmFetchPeerDependenciesStub.restore();
108 });
109
110 after(() => {
111 sh.rm("-r", fixtureDir);
112 });
113
114 describe("processAnswers()", () => {
115
116 describe("prompt", () => {
117
118 beforeEach(() => {
119 answers = {
120 purpose: "style",
121 source: "prompt",
122 extendDefault: true,
123 indent: 2,
124 quotes: "single",
125 linebreak: "unix",
126 semi: true,
127 moduleType: "esm",
128 es6Globals: true,
129 env: ["browser"],
130 format: "JSON"
131 };
132 });
133
134 it("should create default config", () => {
135 const config = init.processAnswers(answers);
136
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");
146 });
147
148 it("should disable semi", () => {
149 answers.semi = false;
150 const config = init.processAnswers(answers);
151
152 assert.deepStrictEqual(config.rules.semi, ["error", "never"]);
153 });
154
155 it("should enable react plugin", () => {
156 answers.framework = "react";
157 const config = init.processAnswers(answers);
158
159 assert.strictEqual(config.parserOptions.ecmaFeatures.jsx, true);
160 assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion);
161 assert.deepStrictEqual(config.plugins, ["react"]);
162 });
163
164 it("should enable vue plugin", () => {
165 answers.framework = "vue";
166 const config = init.processAnswers(answers);
167
168 assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion);
169 assert.deepStrictEqual(config.plugins, ["vue"]);
170 assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:vue/essential"]);
171 });
172
173 it("should enable typescript parser and plugin", () => {
174 answers.typescript = true;
175 const config = init.processAnswers(answers);
176
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"]);
180 });
181
182 it("should enable typescript parser and plugin with vue", () => {
183 answers.framework = "vue";
184 answers.typescript = true;
185 const config = init.processAnswers(answers);
186
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"]);
190 });
191
192 it("should extend eslint:recommended", () => {
193 const config = init.processAnswers(answers);
194
195 assert.strictEqual(config.extends, "eslint:recommended");
196 });
197
198 it("should not use commonjs by default", () => {
199 const config = init.processAnswers(answers);
200
201 assert.isUndefined(config.env.commonjs);
202 });
203
204 it("should use commonjs when set", () => {
205 answers.moduleType = "commonjs";
206 const config = init.processAnswers(answers);
207
208 assert.isTrue(config.env.commonjs);
209 });
210 });
211
212 describe("guide", () => {
213 it("should support the google style guide", () => {
214 const config = { extends: "google" };
215 const modules = init.getModulesList(config);
216
217 assert.deepStrictEqual(config, { extends: "google", installedESLint: true });
218 assert.include(modules, "eslint-config-google@latest");
219 });
220
221 it("should support the airbnb style guide", () => {
222 const config = { extends: "airbnb" };
223 const modules = init.getModulesList(config);
224
225 assert.deepStrictEqual(config, { extends: "airbnb", installedESLint: true });
226 assert.include(modules, "eslint-config-airbnb@latest");
227 });
228
229 it("should support the airbnb base style guide", () => {
230 const config = { extends: "airbnb-base" };
231 const modules = init.getModulesList(config);
232
233 assert.deepStrictEqual(config, { extends: "airbnb-base", installedESLint: true });
234 assert.include(modules, "eslint-config-airbnb-base@latest");
235 });
236
237 it("should support the standard style guide", () => {
238 const config = { extends: "standard" };
239 const modules = init.getModulesList(config);
240
241 assert.deepStrictEqual(config, { extends: "standard", installedESLint: true });
242 assert.include(modules, "eslint-config-standard@latest");
243 });
244
245 it("should support the xo style guide", () => {
246 const config = { extends: "xo" };
247 const modules = init.getModulesList(config);
248
249 assert.deepStrictEqual(config, { extends: "xo", installedESLint: true });
250 assert.include(modules, "eslint-config-xo@latest");
251 });
252
253 it("should install required sharable config", () => {
254 const config = { extends: "google" };
255
256 init.installModules(init.getModulesList(config));
257 assert(npmInstallStub.calledOnce);
258 assert(npmInstallStub.firstCall.args[0].some(name => name.startsWith("eslint-config-google@")));
259 });
260
261 it("should install ESLint if not installed locally", () => {
262 const config = { extends: "google" };
263
264 init.installModules(init.getModulesList(config));
265 assert(npmInstallStub.calledOnce);
266 assert(npmInstallStub.firstCall.args[0].some(name => name.startsWith("eslint@")));
267 });
268
269 it("should install peerDependencies of the sharable config", () => {
270 const config = { extends: "airbnb" };
271
272 init.installModules(init.getModulesList(config));
273
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],
279 [
280 "eslint-config-airbnb@latest",
281 "eslint@^3.19.0",
282 "eslint-plugin-jsx-a11y@^5.0.1",
283 "eslint-plugin-import@^2.2.0",
284 "eslint-plugin-react@^7.0.1"
285 ]
286 );
287 });
288
289 describe("hasESLintVersionConflict (Note: peerDependencies always `eslint: \"^3.19.0\"` by stubs)", () => {
290 describe("if local ESLint is not found,", () => {
291 before(() => {
292 localESLintVersion = null;
293 });
294
295 it("should return false.", () => {
296 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
297
298 assert.strictEqual(result, false);
299 });
300 });
301
302 describe("if local ESLint is 3.19.0,", () => {
303 before(() => {
304 localESLintVersion = "3.19.0";
305 });
306
307 it("should return false.", () => {
308 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
309
310 assert.strictEqual(result, false);
311 });
312 });
313
314 describe("if local ESLint is 4.0.0,", () => {
315 before(() => {
316 localESLintVersion = "4.0.0";
317 });
318
319 it("should return true.", () => {
320 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
321
322 assert.strictEqual(result, true);
323 });
324 });
325
326 describe("if local ESLint is 3.18.0,", () => {
327 before(() => {
328 localESLintVersion = "3.18.0";
329 });
330
331 it("should return true.", () => {
332 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
333
334 assert.strictEqual(result, true);
335 });
336 });
337 });
338
339 it("should support the standard style guide with Vue.js", () => {
340 const config = {
341 plugins: ["vue"],
342 extends: ["plugin:vue/essential", "standard"]
343 };
344 const modules = init.getModulesList(config);
345
346 assert.include(modules, "eslint-plugin-vue@latest");
347 assert.include(modules, "eslint-config-standard@latest");
348 });
349
350 it("should support custom parser", () => {
351 const config = {
352 parser: "@typescript-eslint/parser"
353 };
354 const modules = init.getModulesList(config);
355
356 assert.include(modules, "@typescript-eslint/parser@latest");
357 });
358
359 it("should support custom parser with Vue.js", () => {
360 const config = {
361
362 // We should declare the parser at `parserOptions` when using with `eslint-plugin-vue`.
363 parserOptions: {
364 parser: "@typescript-eslint/parser"
365 }
366 };
367 const modules = init.getModulesList(config);
368
369 assert.include(modules, "@typescript-eslint/parser@latest");
370 });
371 });
372
373 describe("auto", () => {
374 const completeSpy = sinon.spy();
375 let config;
376
377 before(() => {
378 const patterns = [
379 getFixturePath("lib"),
380 getFixturePath("tests")
381 ].join(" ");
382
383 answers = {
384 purpose: "style",
385 source: "auto",
386 patterns,
387 env: ["browser"],
388 format: "JSON"
389 };
390
391 sinon.stub(console, "log"); // necessary to replace, because of progress bar
392
393 process.chdir(fixtureDir);
394 config = init.processAnswers(answers);
395 sinon.restore();
396 });
397
398 after(() => {
399 sinon.restore();
400 });
401
402 afterEach(() => {
403 process.chdir(originalDir);
404 sinon.restore();
405 });
406
407 it("should create a config", () => {
408 assert.isTrue(completeSpy.notCalled);
409 assert.ok(config);
410 });
411
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");
415 });
416
417 it("should extend and not disable recommended rules", () => {
418 assert.strictEqual(config.extends, "eslint:recommended");
419 assert.notProperty(config.rules, "no-debugger");
420 });
421
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");
428 });
429
430 it("should support new ES features if using later ES version", () => {
431 const filename = getFixturePath("new-es-features");
432
433 answers.patterns = filename;
434 answers.ecmaVersion = 2017;
435 process.chdir(fixtureDir);
436 config = init.processAnswers(answers);
437 });
438
439 it("should throw on fatal parsing error", () => {
440 const filename = getFixturePath("parse-error");
441
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 ;");
448 });
449
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.");
457 });
458 });
459 });
460
461 describe("writeFile()", () => {
462
463 beforeEach(() => {
464 answers = {
465 purpose: "style",
466 source: "prompt",
467 extendDefault: true,
468 indent: 2,
469 quotes: "single",
470 linebreak: "unix",
471 semi: true,
472 moduleType: "esm",
473 es6Globals: true,
474 env: ["browser"],
475 format: "JSON"
476 };
477
478 pkgJSONContents = {
479 name: "config-initializer",
480 version: "1.0.0"
481 };
482
483 process.chdir(fixtureDir);
484
485 pkgJSONPath = path.resolve(fixtureDir, "package.json");
486 });
487
488 afterEach(() => {
489 process.chdir(originalDir);
490 });
491
492 it("should create .eslintrc.json", () => {
493 const config = init.processAnswers(answers);
494 const filePath = path.resolve(fixtureDir, ".eslintrc.json");
495
496 fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
497
498 init.writeFile(config, answers.format);
499
500 assert.isTrue(fs.existsSync(filePath));
501
502 fs.unlinkSync(filePath);
503 fs.unlinkSync(pkgJSONPath);
504 });
505
506 it("should create .eslintrc.js", () => {
507 answers.format = "JavaScript";
508
509 const config = init.processAnswers(answers);
510 const filePath = path.resolve(fixtureDir, ".eslintrc.js");
511
512 fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
513
514 init.writeFile(config, answers.format);
515
516 assert.isTrue(fs.existsSync(filePath));
517
518 fs.unlinkSync(filePath);
519 fs.unlinkSync(pkgJSONPath);
520 });
521
522 it("should create .eslintrc.yml", () => {
523 answers.format = "YAML";
524
525 const config = init.processAnswers(answers);
526 const filePath = path.resolve(fixtureDir, ".eslintrc.yml");
527
528 fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
529
530 init.writeFile(config, answers.format);
531
532 assert.isTrue(fs.existsSync(filePath));
533
534 fs.unlinkSync(filePath);
535 fs.unlinkSync(pkgJSONPath);
536 });
537
538 // For https://github.com/eslint/eslint/issues/14137
539 it("should create .eslintrc.cjs", () => {
540 answers.format = "JavaScript";
541
542 // create package.json with "type": "module"
543 pkgJSONContents.type = "module";
544
545 fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
546
547 const config = init.processAnswers(answers);
548 const filePath = path.resolve(fixtureDir, ".eslintrc.cjs");
549
550 init.writeFile(config, answers.format);
551
552 assert.isTrue(fs.existsSync(filePath));
553
554 fs.unlinkSync(filePath);
555 fs.unlinkSync(pkgJSONPath);
556 });
557
558 it("should create .eslintrc.json even with type: 'module'", () => {
559 answers.format = "JSON";
560
561 // create package.json with "type": "module"
562 pkgJSONContents.type = "module";
563
564 fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSONContents));
565
566 const config = init.processAnswers(answers);
567 const filePath = path.resolve(fixtureDir, ".eslintrc.json");
568
569 init.writeFile(config, answers.format);
570
571 assert.isTrue(fs.existsSync(filePath));
572
573 fs.unlinkSync(filePath);
574 fs.unlinkSync(pkgJSONPath);
575 });
576 });
577 });