]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/init/config-initializer.js
fe9120f7de108c0a0a121e697bbd085846e9b3bb
[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
31 describe("configInitializer", () => {
32
33 let fixtureDir,
34 npmCheckStub,
35 npmInstallStub,
36 npmFetchPeerDependenciesStub,
37 init,
38 localESLintVersion = null;
39
40 const log = {
41 info: sinon.spy(),
42 error: sinon.spy()
43 };
44 const requireStubs = {
45 "../shared/logging": log,
46 "../shared/relative-module-resolver": {
47 resolve() {
48 if (localESLintVersion) {
49 return `local-eslint-${localESLintVersion}`;
50 }
51 throw new Error("Cannot find module");
52 }
53 },
54 "local-eslint-3.18.0": { linter: { version: "3.18.0" }, "@noCallThru": true },
55 "local-eslint-3.19.0": { linter: { version: "3.19.0" }, "@noCallThru": true },
56 "local-eslint-4.0.0": { linter: { version: "4.0.0" }, "@noCallThru": true }
57 };
58
59 /**
60 * Returns the path inside of the fixture directory.
61 * @param {...string} args file path segments.
62 * @returns {string} The path inside the fixture directory.
63 * @private
64 */
65 function getFixturePath(...args) {
66 const filepath = path.join(fixtureDir, ...args);
67
68 try {
69 return fs.realpathSync(filepath);
70 } catch (e) {
71 return filepath;
72 }
73 }
74
75 // copy into clean area so as not to get "infected" by this project's .eslintrc files
76 before(() => {
77 fixtureDir = path.join(os.tmpdir(), "eslint/fixtures/config-initializer");
78 sh.mkdir("-p", fixtureDir);
79 sh.cp("-r", "./tests/fixtures/config-initializer/.", fixtureDir);
80 fixtureDir = fs.realpathSync(fixtureDir);
81 });
82
83 beforeEach(() => {
84 npmInstallStub = sinon.stub(npmUtils, "installSyncSaveDev");
85 npmCheckStub = sinon.stub(npmUtils, "checkDevDeps").callsFake(packages => packages.reduce((status, pkg) => {
86 status[pkg] = false;
87 return status;
88 }, {}));
89 npmFetchPeerDependenciesStub = sinon
90 .stub(npmUtils, "fetchPeerDependencies")
91 .returns({
92 eslint: "^3.19.0",
93 "eslint-plugin-jsx-a11y": "^5.0.1",
94 "eslint-plugin-import": "^2.2.0",
95 "eslint-plugin-react": "^7.0.1"
96 });
97 init = proxyquire("../../../lib/init/config-initializer", requireStubs);
98 });
99
100 afterEach(() => {
101 log.info.resetHistory();
102 log.error.resetHistory();
103 npmInstallStub.restore();
104 npmCheckStub.restore();
105 npmFetchPeerDependenciesStub.restore();
106 });
107
108 after(() => {
109 sh.rm("-r", fixtureDir);
110 });
111
112 describe("processAnswers()", () => {
113
114 describe("prompt", () => {
115
116 beforeEach(() => {
117 answers = {
118 purpose: "style",
119 source: "prompt",
120 extendDefault: true,
121 indent: 2,
122 quotes: "single",
123 linebreak: "unix",
124 semi: true,
125 moduleType: "esm",
126 es6Globals: true,
127 env: ["browser"],
128 format: "JSON"
129 };
130 });
131
132 it("should create default config", () => {
133 const config = init.processAnswers(answers);
134
135 assert.deepStrictEqual(config.rules.indent, ["error", 2]);
136 assert.deepStrictEqual(config.rules.quotes, ["error", "single"]);
137 assert.deepStrictEqual(config.rules["linebreak-style"], ["error", "unix"]);
138 assert.deepStrictEqual(config.rules.semi, ["error", "always"]);
139 assert.strictEqual(config.env.es6, true);
140 assert.strictEqual(config.globals.Atomics, "readonly");
141 assert.strictEqual(config.globals.SharedArrayBuffer, "readonly");
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/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/eslint-recommended", "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 install required sharable config", () => {
246 const config = { extends: "google" };
247
248 init.installModules(init.getModulesList(config));
249 assert(npmInstallStub.calledOnce);
250 assert(npmInstallStub.firstCall.args[0].some(name => name.startsWith("eslint-config-google@")));
251 });
252
253 it("should install ESLint if not installed locally", () => {
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@")));
259 });
260
261 it("should install peerDependencies of the sharable config", () => {
262 const config = { extends: "airbnb" };
263
264 init.installModules(init.getModulesList(config));
265
266 assert(npmFetchPeerDependenciesStub.calledOnce);
267 assert(npmFetchPeerDependenciesStub.firstCall.args[0] === "eslint-config-airbnb@latest");
268 assert(npmInstallStub.calledOnce);
269 assert.deepStrictEqual(
270 npmInstallStub.firstCall.args[0],
271 [
272 "eslint-config-airbnb@latest",
273 "eslint@^3.19.0",
274 "eslint-plugin-jsx-a11y@^5.0.1",
275 "eslint-plugin-import@^2.2.0",
276 "eslint-plugin-react@^7.0.1"
277 ]
278 );
279 });
280
281 describe("hasESLintVersionConflict (Note: peerDependencies always `eslint: \"^3.19.0\"` by stubs)", () => {
282 describe("if local ESLint is not found,", () => {
283 before(() => {
284 localESLintVersion = null;
285 });
286
287 it("should return false.", () => {
288 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
289
290 assert.strictEqual(result, false);
291 });
292 });
293
294 describe("if local ESLint is 3.19.0,", () => {
295 before(() => {
296 localESLintVersion = "3.19.0";
297 });
298
299 it("should return false.", () => {
300 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
301
302 assert.strictEqual(result, false);
303 });
304 });
305
306 describe("if local ESLint is 4.0.0,", () => {
307 before(() => {
308 localESLintVersion = "4.0.0";
309 });
310
311 it("should return true.", () => {
312 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
313
314 assert.strictEqual(result, true);
315 });
316 });
317
318 describe("if local ESLint is 3.18.0,", () => {
319 before(() => {
320 localESLintVersion = "3.18.0";
321 });
322
323 it("should return true.", () => {
324 const result = init.hasESLintVersionConflict({ styleguide: "airbnb" });
325
326 assert.strictEqual(result, true);
327 });
328 });
329 });
330
331 it("should support the standard style guide with Vue.js", () => {
332 const config = {
333 plugins: ["vue"],
334 extends: ["plugin:vue/essential", "standard"]
335 };
336 const modules = init.getModulesList(config);
337
338 assert.include(modules, "eslint-plugin-vue@latest");
339 assert.include(modules, "eslint-config-standard@latest");
340 });
341
342 it("should support custom parser", () => {
343 const config = {
344 parser: "@typescript-eslint/parser"
345 };
346 const modules = init.getModulesList(config);
347
348 assert.include(modules, "@typescript-eslint/parser@latest");
349 });
350
351 it("should support custom parser with Vue.js", () => {
352 const config = {
353
354 // We should declare the parser at `parserOptions` when using with `eslint-plugin-vue`.
355 parserOptions: {
356 parser: "@typescript-eslint/parser"
357 }
358 };
359 const modules = init.getModulesList(config);
360
361 assert.include(modules, "@typescript-eslint/parser@latest");
362 });
363 });
364
365 describe("auto", () => {
366 const completeSpy = sinon.spy();
367 let config;
368
369 before(() => {
370 const patterns = [
371 getFixturePath("lib"),
372 getFixturePath("tests")
373 ].join(" ");
374
375 answers = {
376 purpose: "style",
377 source: "auto",
378 patterns,
379 env: ["browser"],
380 format: "JSON"
381 };
382
383 sinon.stub(console, "log"); // necessary to replace, because of progress bar
384
385 process.chdir(fixtureDir);
386 config = init.processAnswers(answers);
387 sinon.restore();
388 });
389
390 after(() => {
391 sinon.restore();
392 });
393
394 afterEach(() => {
395 process.chdir(originalDir);
396 sinon.restore();
397 });
398
399 it("should create a config", () => {
400 assert.isTrue(completeSpy.notCalled);
401 assert.ok(config);
402 });
403
404 it("should create the config based on examined files", () => {
405 assert.deepStrictEqual(config.rules.quotes, ["error", "double"]);
406 assert.strictEqual(config.rules.semi, "off");
407 });
408
409 it("should extend and not disable recommended rules", () => {
410 assert.strictEqual(config.extends, "eslint:recommended");
411 assert.notProperty(config.rules, "no-debugger");
412 });
413
414 it("should support new ES features if using later ES version", () => {
415 const filename = getFixturePath("new-es-features");
416
417 answers.patterns = filename;
418 answers.ecmaVersion = 2017;
419 process.chdir(fixtureDir);
420 config = init.processAnswers(answers);
421 });
422
423 it("should throw on fatal parsing error", () => {
424 const filename = getFixturePath("parse-error");
425
426 sinon.stub(autoconfig, "extendFromRecommended");
427 answers.patterns = filename;
428 process.chdir(fixtureDir);
429 assert.throws(() => {
430 config = init.processAnswers(answers);
431 }, "Parsing error: Unexpected token ;");
432 });
433
434 it("should throw if no files are matched from patterns", () => {
435 sinon.stub(autoconfig, "extendFromRecommended");
436 answers.patterns = "not-a-real-filename";
437 process.chdir(fixtureDir);
438 assert.throws(() => {
439 config = init.processAnswers(answers);
440 }, "No files matching 'not-a-real-filename' were found.");
441 });
442 });
443 });
444 });