]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Tests for cli. | |
3 | * @author Ian Christian Myers | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | /* | |
56c4a2cb DC |
9 | * NOTE: If you are adding new tests for cli.js, use verifyESLintOpts(). The |
10 | * test only needs to verify that ESLint receives the correct opts. | |
eb39fafa DC |
11 | */ |
12 | ||
13 | //------------------------------------------------------------------------------ | |
14 | // Requirements | |
15 | //------------------------------------------------------------------------------ | |
16 | ||
17 | const assert = require("chai").assert, | |
56c4a2cb DC |
18 | stdAssert = require("assert"), |
19 | { ESLint } = require("../../lib/eslint"), | |
20 | BuiltinRules = require("../../lib/rules"), | |
eb39fafa DC |
21 | path = require("path"), |
22 | sinon = require("sinon"), | |
23 | fs = require("fs"), | |
24 | os = require("os"), | |
25 | sh = require("shelljs"); | |
26 | ||
27 | const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); | |
28 | ||
29 | //------------------------------------------------------------------------------ | |
30 | // Tests | |
31 | //------------------------------------------------------------------------------ | |
32 | ||
33 | describe("cli", () => { | |
34 | let fixtureDir; | |
35 | const log = { | |
36 | info: sinon.spy(), | |
37 | error: sinon.spy() | |
38 | }; | |
39 | const RuntimeInfo = { | |
40 | environment: sinon.stub(), | |
41 | version: sinon.stub() | |
42 | }; | |
43 | const cli = proxyquire("../../lib/cli", { | |
44 | "./shared/logging": log, | |
45 | "./shared/runtime-info": RuntimeInfo | |
46 | }); | |
47 | ||
48 | /** | |
56c4a2cb | 49 | * Verify that ESLint class receives correct opts via await cli.execute(). |
eb39fafa | 50 | * @param {string} cmd CLI command. |
56c4a2cb | 51 | * @param {Object} opts Options hash that should match that received by ESLint class. |
eb39fafa DC |
52 | * @returns {void} |
53 | */ | |
56c4a2cb | 54 | async function verifyESLintOpts(cmd, opts) { |
eb39fafa | 55 | |
56c4a2cb DC |
56 | // create a fake ESLint class to test with |
57 | const fakeESLint = sinon.mock().withExactArgs(sinon.match(opts)); | |
eb39fafa | 58 | |
56c4a2cb DC |
59 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
60 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
61 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: sinon.spy() }); | |
eb39fafa DC |
62 | |
63 | const localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 64 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
65 | "./shared/logging": log |
66 | }); | |
67 | ||
56c4a2cb | 68 | await localCLI.execute(cmd); |
eb39fafa DC |
69 | sinon.verifyAndRestore(); |
70 | } | |
71 | ||
56c4a2cb | 72 | // verifyESLintOpts |
eb39fafa DC |
73 | |
74 | /** | |
75 | * Returns the path inside of the fixture directory. | |
76 | * @param {...string} args file path segments. | |
77 | * @returns {string} The path inside the fixture directory. | |
78 | * @private | |
79 | */ | |
80 | function getFixturePath(...args) { | |
81 | return path.join(fixtureDir, ...args); | |
82 | } | |
83 | ||
84 | // copy into clean area so as not to get "infected" by this project's .eslintrc files | |
6f036462 TL |
85 | before(function() { |
86 | ||
87 | /* | |
88 | * GitHub Actions Windows and macOS runners occasionally exhibit | |
89 | * extremely slow filesystem operations, during which copying fixtures | |
90 | * exceeds the default test timeout, so raise it just for this hook. | |
91 | * Mocha uses `this` to set timeouts on an individual hook level. | |
92 | */ | |
93 | this.timeout(60 * 1000); // eslint-disable-line no-invalid-this | |
eb39fafa DC |
94 | fixtureDir = `${os.tmpdir()}/eslint/fixtures`; |
95 | sh.mkdir("-p", fixtureDir); | |
96 | sh.cp("-r", "./tests/fixtures/.", fixtureDir); | |
97 | }); | |
98 | ||
99 | afterEach(() => { | |
100 | log.info.resetHistory(); | |
101 | log.error.resetHistory(); | |
102 | }); | |
103 | ||
104 | after(() => { | |
105 | sh.rm("-r", fixtureDir); | |
106 | }); | |
107 | ||
108 | describe("execute()", () => { | |
56c4a2cb | 109 | it("should return error when text with incorrect quotes is passed as argument", async () => { |
eb39fafa | 110 | const configFile = getFixturePath("configurations", "quotes-error.json"); |
56c4a2cb | 111 | const result = await cli.execute(`-c ${configFile}`, "var foo = 'bar';"); |
eb39fafa DC |
112 | |
113 | assert.strictEqual(result, 1); | |
114 | }); | |
115 | ||
56c4a2cb DC |
116 | it("should not print debug info when passed the empty string as text", async () => { |
117 | const result = await cli.execute(["--stdin", "--no-eslintrc"], ""); | |
eb39fafa DC |
118 | |
119 | assert.strictEqual(result, 0); | |
120 | assert.isTrue(log.info.notCalled); | |
121 | }); | |
122 | ||
56c4a2cb | 123 | it("should return no error when --ext .js2 is specified", async () => { |
eb39fafa | 124 | const filePath = getFixturePath("files"); |
56c4a2cb | 125 | const result = await cli.execute(`--ext .js2 ${filePath}`); |
eb39fafa DC |
126 | |
127 | assert.strictEqual(result, 0); | |
128 | }); | |
129 | ||
56c4a2cb | 130 | it("should exit with console error when passed unsupported arguments", async () => { |
eb39fafa | 131 | const filePath = getFixturePath("files"); |
56c4a2cb | 132 | const result = await cli.execute(`--blah --another ${filePath}`); |
eb39fafa DC |
133 | |
134 | assert.strictEqual(result, 2); | |
135 | }); | |
136 | ||
137 | }); | |
138 | ||
139 | describe("when given a config file", () => { | |
56c4a2cb | 140 | it("should load the specified config file", async () => { |
eb39fafa DC |
141 | const configPath = getFixturePath(".eslintrc"); |
142 | const filePath = getFixturePath("passing.js"); | |
143 | ||
56c4a2cb | 144 | await cli.execute(`--config ${configPath} ${filePath}`); |
eb39fafa DC |
145 | }); |
146 | }); | |
147 | ||
148 | describe("when there is a local config file", () => { | |
149 | const code = "lib/cli.js"; | |
150 | ||
56c4a2cb | 151 | it("should load the local config file", async () => { |
eb39fafa DC |
152 | |
153 | // Mock CWD | |
154 | process.eslintCwd = getFixturePath("configurations", "single-quotes"); | |
155 | ||
56c4a2cb | 156 | await cli.execute(code); |
eb39fafa DC |
157 | |
158 | process.eslintCwd = null; | |
159 | }); | |
160 | }); | |
161 | ||
162 | describe("when given a config with rules with options and severity level set to error", () => { | |
56c4a2cb | 163 | it("should exit with an error status (1)", async () => { |
eb39fafa DC |
164 | const configPath = getFixturePath("configurations", "quotes-error.json"); |
165 | const filePath = getFixturePath("single-quoted.js"); | |
166 | const code = `--no-ignore --config ${configPath} ${filePath}`; | |
167 | ||
56c4a2cb | 168 | const exitStatus = await cli.execute(code); |
eb39fafa DC |
169 | |
170 | assert.strictEqual(exitStatus, 1); | |
171 | }); | |
172 | }); | |
173 | ||
174 | describe("when given a config file and a directory of files", () => { | |
56c4a2cb | 175 | it("should load and execute without error", async () => { |
eb39fafa DC |
176 | const configPath = getFixturePath("configurations", "semi-error.json"); |
177 | const filePath = getFixturePath("formatters"); | |
178 | const code = `--config ${configPath} ${filePath}`; | |
179 | ||
56c4a2cb | 180 | const exitStatus = await cli.execute(code); |
eb39fafa DC |
181 | |
182 | assert.strictEqual(exitStatus, 0); | |
183 | }); | |
184 | }); | |
185 | ||
186 | describe("when given a config with environment set to browser", () => { | |
56c4a2cb | 187 | it("should execute without any errors", async () => { |
eb39fafa DC |
188 | const configPath = getFixturePath("configurations", "env-browser.json"); |
189 | const filePath = getFixturePath("globals-browser.js"); | |
190 | const code = `--config ${configPath} ${filePath}`; | |
191 | ||
56c4a2cb | 192 | const exit = await cli.execute(code); |
eb39fafa DC |
193 | |
194 | assert.strictEqual(exit, 0); | |
195 | }); | |
196 | }); | |
197 | ||
198 | describe("when given a config with environment set to Node.js", () => { | |
56c4a2cb | 199 | it("should execute without any errors", async () => { |
eb39fafa DC |
200 | const configPath = getFixturePath("configurations", "env-node.json"); |
201 | const filePath = getFixturePath("globals-node.js"); | |
202 | const code = `--config ${configPath} ${filePath}`; | |
203 | ||
56c4a2cb | 204 | const exit = await cli.execute(code); |
eb39fafa DC |
205 | |
206 | assert.strictEqual(exit, 0); | |
207 | }); | |
208 | }); | |
209 | ||
210 | describe("when given a config with environment set to Nashorn", () => { | |
56c4a2cb | 211 | it("should execute without any errors", async () => { |
eb39fafa DC |
212 | const configPath = getFixturePath("configurations", "env-nashorn.json"); |
213 | const filePath = getFixturePath("globals-nashorn.js"); | |
214 | const code = `--config ${configPath} ${filePath}`; | |
215 | ||
56c4a2cb | 216 | const exit = await cli.execute(code); |
eb39fafa DC |
217 | |
218 | assert.strictEqual(exit, 0); | |
219 | }); | |
220 | }); | |
221 | ||
222 | describe("when given a config with environment set to WebExtensions", () => { | |
56c4a2cb | 223 | it("should execute without any errors", async () => { |
eb39fafa DC |
224 | const configPath = getFixturePath("configurations", "env-webextensions.json"); |
225 | const filePath = getFixturePath("globals-webextensions.js"); | |
226 | const code = `--config ${configPath} ${filePath}`; | |
227 | ||
56c4a2cb | 228 | const exit = await cli.execute(code); |
eb39fafa DC |
229 | |
230 | assert.strictEqual(exit, 0); | |
231 | }); | |
232 | }); | |
233 | ||
234 | describe("when given a valid built-in formatter name", () => { | |
56c4a2cb | 235 | it("should execute without any errors", async () => { |
eb39fafa | 236 | const filePath = getFixturePath("passing.js"); |
56c4a2cb | 237 | const exit = await cli.execute(`-f checkstyle ${filePath}`); |
eb39fafa DC |
238 | |
239 | assert.strictEqual(exit, 0); | |
240 | }); | |
241 | }); | |
242 | ||
243 | describe("when given a valid built-in formatter name that uses rules meta.", () => { | |
56c4a2cb | 244 | it("should execute without any errors", async () => { |
eb39fafa | 245 | const filePath = getFixturePath("passing.js"); |
56c4a2cb | 246 | const exit = await cli.execute(`-f json-with-metadata ${filePath} --no-eslintrc`); |
eb39fafa DC |
247 | |
248 | assert.strictEqual(exit, 0); | |
249 | ||
250 | // Check metadata. | |
251 | const { metadata } = JSON.parse(log.info.args[0][0]); | |
56c4a2cb | 252 | const expectedMetadata = Array.from(BuiltinRules).reduce((obj, [ruleId, rule]) => { |
eb39fafa DC |
253 | obj.rulesMeta[ruleId] = rule.meta; |
254 | return obj; | |
255 | }, { rulesMeta: {} }); | |
256 | ||
257 | assert.deepStrictEqual(metadata, expectedMetadata); | |
258 | }); | |
259 | }); | |
260 | ||
261 | describe("when given an invalid built-in formatter name", () => { | |
56c4a2cb | 262 | it("should execute with error", async () => { |
eb39fafa | 263 | const filePath = getFixturePath("passing.js"); |
56c4a2cb | 264 | const exit = await cli.execute(`-f fakeformatter ${filePath}`); |
eb39fafa DC |
265 | |
266 | assert.strictEqual(exit, 2); | |
267 | }); | |
268 | }); | |
269 | ||
270 | describe("when given a valid formatter path", () => { | |
56c4a2cb | 271 | it("should execute without any errors", async () => { |
eb39fafa DC |
272 | const formatterPath = getFixturePath("formatters", "simple.js"); |
273 | const filePath = getFixturePath("passing.js"); | |
56c4a2cb | 274 | const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); |
eb39fafa DC |
275 | |
276 | assert.strictEqual(exit, 0); | |
277 | }); | |
278 | }); | |
279 | ||
280 | describe("when given an invalid formatter path", () => { | |
56c4a2cb | 281 | it("should execute with error", async () => { |
eb39fafa DC |
282 | const formatterPath = getFixturePath("formatters", "file-does-not-exist.js"); |
283 | const filePath = getFixturePath("passing.js"); | |
56c4a2cb | 284 | const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); |
eb39fafa DC |
285 | |
286 | assert.strictEqual(exit, 2); | |
287 | }); | |
288 | }); | |
289 | ||
290 | describe("when executing a file with a lint error", () => { | |
56c4a2cb | 291 | it("should exit with error", async () => { |
eb39fafa DC |
292 | const filePath = getFixturePath("undef.js"); |
293 | const code = `--no-ignore --rule no-undef:2 ${filePath}`; | |
294 | ||
56c4a2cb | 295 | const exit = await cli.execute(code); |
eb39fafa DC |
296 | |
297 | assert.strictEqual(exit, 1); | |
298 | }); | |
299 | }); | |
300 | ||
301 | describe("when using --fix-type without --fix or --fix-dry-run", () => { | |
56c4a2cb | 302 | it("should exit with error", async () => { |
eb39fafa DC |
303 | const filePath = getFixturePath("passing.js"); |
304 | const code = `--fix-type suggestion ${filePath}`; | |
305 | ||
56c4a2cb | 306 | const exit = await cli.execute(code); |
eb39fafa DC |
307 | |
308 | assert.strictEqual(exit, 2); | |
309 | }); | |
310 | }); | |
311 | ||
312 | describe("when executing a file with a syntax error", () => { | |
56c4a2cb | 313 | it("should exit with error", async () => { |
eb39fafa | 314 | const filePath = getFixturePath("syntax-error.js"); |
56c4a2cb | 315 | const exit = await cli.execute(`--no-ignore ${filePath}`); |
eb39fafa DC |
316 | |
317 | assert.strictEqual(exit, 1); | |
318 | }); | |
319 | }); | |
320 | ||
321 | describe("when calling execute more than once", () => { | |
56c4a2cb | 322 | it("should not print the results from previous execution", async () => { |
eb39fafa DC |
323 | const filePath = getFixturePath("missing-semicolon.js"); |
324 | const passingPath = getFixturePath("passing.js"); | |
325 | ||
56c4a2cb | 326 | await cli.execute(`--no-ignore --rule semi:2 ${filePath}`); |
eb39fafa DC |
327 | |
328 | assert.isTrue(log.info.called, "Log should have been called."); | |
329 | ||
330 | log.info.resetHistory(); | |
331 | ||
56c4a2cb | 332 | await cli.execute(`--no-ignore --rule semi:2 ${passingPath}`); |
eb39fafa DC |
333 | assert.isTrue(log.info.notCalled); |
334 | ||
335 | }); | |
336 | }); | |
337 | ||
338 | describe("when executing with version flag", () => { | |
56c4a2cb DC |
339 | it("should print out current version", async () => { |
340 | assert.strictEqual(await cli.execute("-v"), 0); | |
eb39fafa DC |
341 | assert.strictEqual(log.info.callCount, 1); |
342 | }); | |
343 | }); | |
344 | ||
345 | describe("when executing with env-info flag", () => { | |
56c4a2cb DC |
346 | it("should print out environment information", async () => { |
347 | assert.strictEqual(await cli.execute("--env-info"), 0); | |
eb39fafa DC |
348 | assert.strictEqual(log.info.callCount, 1); |
349 | }); | |
350 | ||
56c4a2cb | 351 | it("should print error message and return error code", async () => { |
eb39fafa DC |
352 | RuntimeInfo.environment.throws("There was an error!"); |
353 | ||
56c4a2cb | 354 | assert.strictEqual(await cli.execute("--env-info"), 2); |
eb39fafa DC |
355 | assert.strictEqual(log.error.callCount, 1); |
356 | }); | |
357 | }); | |
358 | ||
359 | describe("when executing without no-error-on-unmatched-pattern flag", () => { | |
56c4a2cb | 360 | it("should throw an error on unmatched glob pattern", async () => { |
eb39fafa DC |
361 | const filePath = getFixturePath("unmatched-patterns"); |
362 | const globPattern = "*.js3"; | |
363 | ||
56c4a2cb DC |
364 | await stdAssert.rejects(async () => { |
365 | await cli.execute(`"${filePath}/${globPattern}"`); | |
366 | }, new Error(`No files matching '${filePath}/${globPattern}' were found.`)); | |
eb39fafa DC |
367 | }); |
368 | ||
56c4a2cb | 369 | it("should throw an error on unmatched --ext", async () => { |
eb39fafa DC |
370 | const filePath = getFixturePath("unmatched-patterns"); |
371 | const extension = ".js3"; | |
372 | ||
56c4a2cb DC |
373 | await stdAssert.rejects(async () => { |
374 | await cli.execute(`--ext ${extension} ${filePath}`); | |
eb39fafa DC |
375 | }, `No files matching '${filePath}' were found`); |
376 | }); | |
377 | }); | |
378 | ||
379 | describe("when executing with no-error-on-unmatched-pattern flag", () => { | |
56c4a2cb | 380 | it("should not throw an error on unmatched node glob syntax patterns", async () => { |
eb39fafa | 381 | const filePath = getFixturePath("unmatched-patterns"); |
56c4a2cb | 382 | const exit = await cli.execute(`--no-error-on-unmatched-pattern "${filePath}/*.js3"`); |
eb39fafa DC |
383 | |
384 | assert.strictEqual(exit, 0); | |
385 | }); | |
386 | ||
56c4a2cb | 387 | it("should not throw an error on unmatched --ext", async () => { |
eb39fafa | 388 | const filePath = getFixturePath("unmatched-patterns"); |
56c4a2cb | 389 | const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 ${filePath}`); |
eb39fafa DC |
390 | |
391 | assert.strictEqual(exit, 0); | |
392 | }); | |
393 | }); | |
394 | ||
395 | describe("when executing with no-error-on-unmatched-pattern flag and multiple patterns", () => { | |
56c4a2cb | 396 | it("should not throw an error on multiple unmatched node glob syntax patterns", async () => { |
eb39fafa | 397 | const filePath = getFixturePath("unmatched-patterns"); |
56c4a2cb | 398 | const exit = await cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js4`); |
eb39fafa DC |
399 | |
400 | assert.strictEqual(exit, 0); | |
401 | }); | |
402 | ||
56c4a2cb | 403 | it("should still throw an error on when a matched pattern has lint errors", async () => { |
eb39fafa | 404 | const filePath = getFixturePath("unmatched-patterns"); |
56c4a2cb | 405 | const exit = await cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js`); |
eb39fafa DC |
406 | |
407 | assert.strictEqual(exit, 1); | |
408 | }); | |
409 | }); | |
410 | ||
411 | describe("when executing with no-error-on-unmatched-pattern flag and multiple --ext arguments", () => { | |
56c4a2cb | 412 | it("should not throw an error on multiple unmatched --ext arguments", async () => { |
eb39fafa | 413 | const filePath = getFixturePath("unmatched-patterns"); |
56c4a2cb | 414 | const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js4 ${filePath}`); |
eb39fafa DC |
415 | |
416 | assert.strictEqual(exit, 0); | |
417 | }); | |
418 | ||
56c4a2cb | 419 | it("should still throw an error on when a matched pattern has lint errors", async () => { |
eb39fafa | 420 | const filePath = getFixturePath("unmatched-patterns"); |
56c4a2cb | 421 | const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js ${filePath}`); |
eb39fafa DC |
422 | |
423 | assert.strictEqual(exit, 1); | |
424 | }); | |
425 | }); | |
426 | ||
427 | describe("when executing with help flag", () => { | |
56c4a2cb DC |
428 | it("should print out help", async () => { |
429 | assert.strictEqual(await cli.execute("-h"), 0); | |
eb39fafa DC |
430 | assert.strictEqual(log.info.callCount, 1); |
431 | }); | |
432 | }); | |
433 | ||
434 | describe("when given a directory with eslint excluded files in the directory", () => { | |
56c4a2cb | 435 | it("should throw an error and not process any files", async () => { |
eb39fafa DC |
436 | const ignorePath = getFixturePath(".eslintignore"); |
437 | const filePath = getFixturePath("cli"); | |
438 | ||
56c4a2cb DC |
439 | await stdAssert.rejects(async () => { |
440 | await cli.execute(`--ignore-path ${ignorePath} ${filePath}`); | |
441 | }, new Error(`All files matched by '${filePath}' are ignored.`)); | |
eb39fafa DC |
442 | }); |
443 | }); | |
444 | ||
445 | describe("when given a file in excluded files list", () => { | |
56c4a2cb | 446 | it("should not process the file", async () => { |
eb39fafa DC |
447 | const ignorePath = getFixturePath(".eslintignore"); |
448 | const filePath = getFixturePath("passing.js"); | |
56c4a2cb | 449 | const exit = await cli.execute(`--ignore-path ${ignorePath} ${filePath}`); |
eb39fafa DC |
450 | |
451 | // a warning about the ignored file | |
452 | assert.isTrue(log.info.called); | |
453 | assert.strictEqual(exit, 0); | |
454 | }); | |
455 | ||
56c4a2cb | 456 | it("should process the file when forced", async () => { |
eb39fafa DC |
457 | const ignorePath = getFixturePath(".eslintignore"); |
458 | const filePath = getFixturePath("passing.js"); | |
56c4a2cb | 459 | const exit = await cli.execute(`--ignore-path ${ignorePath} --no-ignore ${filePath}`); |
eb39fafa DC |
460 | |
461 | // no warnings | |
462 | assert.isFalse(log.info.called); | |
463 | assert.strictEqual(exit, 0); | |
464 | }); | |
465 | }); | |
466 | ||
467 | describe("when given a pattern to ignore", () => { | |
56c4a2cb | 468 | it("should not process any files", async () => { |
eb39fafa DC |
469 | const ignoredFile = getFixturePath("cli/syntax-error.js"); |
470 | const filePath = getFixturePath("cli/passing.js"); | |
56c4a2cb | 471 | const exit = await cli.execute(`--ignore-pattern cli/ ${ignoredFile} ${filePath}`); |
eb39fafa DC |
472 | |
473 | // warnings about the ignored files | |
474 | assert.isTrue(log.info.called); | |
475 | assert.strictEqual(exit, 0); | |
476 | }); | |
477 | }); | |
478 | ||
479 | describe("when given patterns to ignore", () => { | |
56c4a2cb | 480 | it("should not process any matching files", async () => { |
eb39fafa DC |
481 | const ignorePaths = ["a", "b"]; |
482 | ||
483 | const cmd = ignorePaths.map(ignorePath => `--ignore-pattern ${ignorePath}`).concat(".").join(" "); | |
484 | ||
485 | const opts = { | |
56c4a2cb DC |
486 | overrideConfig: { |
487 | ignorePatterns: ignorePaths | |
488 | } | |
eb39fafa DC |
489 | }; |
490 | ||
56c4a2cb | 491 | await verifyESLintOpts(cmd, opts); |
eb39fafa DC |
492 | }); |
493 | }); | |
494 | ||
495 | describe("when executing a file with a shebang", () => { | |
56c4a2cb | 496 | it("should execute without error", async () => { |
eb39fafa | 497 | const filePath = getFixturePath("shebang.js"); |
56c4a2cb | 498 | const exit = await cli.execute(`--no-ignore ${filePath}`); |
eb39fafa DC |
499 | |
500 | assert.strictEqual(exit, 0); | |
501 | }); | |
502 | }); | |
503 | ||
504 | describe("when loading a custom rule", () => { | |
56c4a2cb | 505 | it("should return an error when rule isn't found", async () => { |
eb39fafa DC |
506 | const rulesPath = getFixturePath("rules", "wrong"); |
507 | const configPath = getFixturePath("rules", "eslint.json"); | |
508 | const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); | |
509 | const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; | |
510 | ||
56c4a2cb DC |
511 | await stdAssert.rejects(async () => { |
512 | const exit = await cli.execute(code); | |
eb39fafa DC |
513 | |
514 | assert.strictEqual(exit, 2); | |
515 | }, /Error while loading rule 'custom-rule': Cannot read property/u); | |
516 | }); | |
517 | ||
56c4a2cb | 518 | it("should return a warning when rule is matched", async () => { |
eb39fafa DC |
519 | const rulesPath = getFixturePath("rules"); |
520 | const configPath = getFixturePath("rules", "eslint.json"); | |
521 | const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); | |
522 | const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; | |
523 | ||
56c4a2cb | 524 | await cli.execute(code); |
eb39fafa DC |
525 | |
526 | assert.isTrue(log.info.calledOnce); | |
527 | assert.isTrue(log.info.neverCalledWith("")); | |
528 | }); | |
529 | ||
56c4a2cb | 530 | it("should return warnings from multiple rules in different directories", async () => { |
eb39fafa DC |
531 | const rulesPath = getFixturePath("rules", "dir1"); |
532 | const rulesPath2 = getFixturePath("rules", "dir2"); | |
533 | const configPath = getFixturePath("rules", "multi-rulesdirs.json"); | |
534 | const filePath = getFixturePath("rules", "test-multi-rulesdirs.js"); | |
535 | const code = `--rulesdir ${rulesPath} --rulesdir ${rulesPath2} --config ${configPath} --no-ignore ${filePath}`; | |
56c4a2cb | 536 | const exit = await cli.execute(code); |
eb39fafa DC |
537 | |
538 | const call = log.info.getCall(0); | |
539 | ||
540 | assert.isTrue(log.info.calledOnce); | |
541 | assert.isTrue(call.args[0].indexOf("String!") > -1); | |
542 | assert.isTrue(call.args[0].indexOf("Literal!") > -1); | |
543 | assert.isTrue(call.args[0].indexOf("2 problems") > -1); | |
544 | assert.isTrue(log.info.neverCalledWith("")); | |
545 | assert.strictEqual(exit, 1); | |
546 | }); | |
547 | ||
548 | ||
549 | }); | |
550 | ||
551 | describe("when executing with no-eslintrc flag", () => { | |
56c4a2cb | 552 | it("should ignore a local config file", async () => { |
eb39fafa | 553 | const filePath = getFixturePath("eslintrc", "quotes.js"); |
56c4a2cb | 554 | const exit = await cli.execute(`--no-eslintrc --no-ignore ${filePath}`); |
eb39fafa DC |
555 | |
556 | assert.isTrue(log.info.notCalled); | |
557 | assert.strictEqual(exit, 0); | |
558 | }); | |
559 | }); | |
560 | ||
561 | describe("when executing without no-eslintrc flag", () => { | |
56c4a2cb | 562 | it("should load a local config file", async () => { |
eb39fafa | 563 | const filePath = getFixturePath("eslintrc", "quotes.js"); |
56c4a2cb | 564 | const exit = await cli.execute(`--no-ignore ${filePath}`); |
eb39fafa DC |
565 | |
566 | assert.isTrue(log.info.calledOnce); | |
567 | assert.strictEqual(exit, 1); | |
568 | }); | |
569 | }); | |
570 | ||
571 | describe("when executing without env flag", () => { | |
56c4a2cb | 572 | it("should not define environment-specific globals", async () => { |
eb39fafa DC |
573 | const files = [ |
574 | getFixturePath("globals-browser.js"), | |
575 | getFixturePath("globals-node.js") | |
576 | ]; | |
577 | ||
56c4a2cb | 578 | await cli.execute(`--no-eslintrc --config ./conf/eslint-recommended.js --no-ignore ${files.join(" ")}`); |
eb39fafa DC |
579 | |
580 | assert.strictEqual(log.info.args[0][0].split("\n").length, 10); | |
581 | }); | |
582 | }); | |
583 | ||
584 | describe("when executing with global flag", () => { | |
56c4a2cb | 585 | it("should default defined variables to read-only", async () => { |
eb39fafa | 586 | const filePath = getFixturePath("undef.js"); |
56c4a2cb | 587 | const exit = await cli.execute(`--global baz,bat --no-ignore --rule no-global-assign:2 ${filePath}`); |
eb39fafa DC |
588 | |
589 | assert.isTrue(log.info.calledOnce); | |
590 | assert.strictEqual(exit, 1); | |
591 | }); | |
592 | ||
56c4a2cb | 593 | it("should allow defining writable global variables", async () => { |
eb39fafa | 594 | const filePath = getFixturePath("undef.js"); |
56c4a2cb | 595 | const exit = await cli.execute(`--global baz:false,bat:true --no-ignore ${filePath}`); |
eb39fafa DC |
596 | |
597 | assert.isTrue(log.info.notCalled); | |
598 | assert.strictEqual(exit, 0); | |
599 | }); | |
600 | ||
56c4a2cb | 601 | it("should allow defining variables with multiple flags", async () => { |
eb39fafa | 602 | const filePath = getFixturePath("undef.js"); |
56c4a2cb | 603 | const exit = await cli.execute(`--global baz --global bat:true --no-ignore ${filePath}`); |
eb39fafa DC |
604 | |
605 | assert.isTrue(log.info.notCalled); | |
606 | assert.strictEqual(exit, 0); | |
607 | }); | |
608 | }); | |
609 | ||
610 | describe("when supplied with rule flag and severity level set to error", () => { | |
56c4a2cb | 611 | it("should exit with an error status (2)", async () => { |
eb39fafa DC |
612 | const filePath = getFixturePath("single-quoted.js"); |
613 | const code = `--no-ignore --rule 'quotes: [2, double]' ${filePath}`; | |
56c4a2cb | 614 | const exitStatus = await cli.execute(code); |
eb39fafa DC |
615 | |
616 | assert.strictEqual(exitStatus, 1); | |
617 | }); | |
618 | }); | |
619 | ||
620 | describe("when the quiet option is enabled", () => { | |
621 | ||
56c4a2cb | 622 | it("should only print error", async () => { |
eb39fafa DC |
623 | const filePath = getFixturePath("single-quoted.js"); |
624 | const cliArgs = `--no-ignore --quiet -f compact --rule 'quotes: [2, double]' --rule 'no-unused-vars: 1' ${filePath}`; | |
625 | ||
56c4a2cb | 626 | await cli.execute(cliArgs); |
eb39fafa DC |
627 | |
628 | sinon.assert.calledOnce(log.info); | |
629 | ||
630 | const formattedOutput = log.info.firstCall.args[0]; | |
631 | ||
632 | assert.include(formattedOutput, "Error"); | |
633 | assert.notInclude(formattedOutput, "Warning"); | |
634 | }); | |
635 | ||
56c4a2cb | 636 | it("should print nothing if there are no errors", async () => { |
eb39fafa DC |
637 | const filePath = getFixturePath("single-quoted.js"); |
638 | const cliArgs = `--quiet -f compact --rule 'quotes: [1, double]' --rule 'no-unused-vars: 1' ${filePath}`; | |
639 | ||
56c4a2cb | 640 | await cli.execute(cliArgs); |
eb39fafa DC |
641 | |
642 | sinon.assert.notCalled(log.info); | |
643 | }); | |
644 | }); | |
645 | ||
646 | describe("when supplied with report output file path", () => { | |
647 | afterEach(() => { | |
648 | sh.rm("-rf", "tests/output"); | |
649 | }); | |
650 | ||
56c4a2cb | 651 | it("should write the file and create dirs if they don't exist", async () => { |
eb39fafa DC |
652 | const filePath = getFixturePath("single-quoted.js"); |
653 | const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; | |
654 | ||
56c4a2cb | 655 | await cli.execute(code); |
eb39fafa DC |
656 | |
657 | assert.include(fs.readFileSync("tests/output/eslint-output.txt", "utf8"), filePath); | |
658 | assert.isTrue(log.info.notCalled); | |
659 | }); | |
660 | ||
56c4a2cb | 661 | it("should return an error if the path is a directory", async () => { |
eb39fafa DC |
662 | const filePath = getFixturePath("single-quoted.js"); |
663 | const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output ${filePath}`; | |
664 | ||
665 | fs.mkdirSync("tests/output"); | |
666 | ||
56c4a2cb | 667 | const exit = await cli.execute(code); |
eb39fafa DC |
668 | |
669 | assert.strictEqual(exit, 2); | |
670 | assert.isTrue(log.info.notCalled); | |
671 | assert.isTrue(log.error.calledOnce); | |
672 | }); | |
673 | ||
56c4a2cb | 674 | it("should return an error if the path could not be written to", async () => { |
eb39fafa DC |
675 | const filePath = getFixturePath("single-quoted.js"); |
676 | const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; | |
677 | ||
678 | fs.writeFileSync("tests/output", "foo"); | |
679 | ||
56c4a2cb | 680 | const exit = await cli.execute(code); |
eb39fafa DC |
681 | |
682 | assert.strictEqual(exit, 2); | |
683 | assert.isTrue(log.info.notCalled); | |
684 | assert.isTrue(log.error.calledOnce); | |
685 | }); | |
686 | }); | |
687 | ||
688 | describe("when supplied with a plugin", () => { | |
56c4a2cb | 689 | it("should pass plugins to ESLint", async () => { |
eb39fafa DC |
690 | const examplePluginName = "eslint-plugin-example"; |
691 | ||
56c4a2cb DC |
692 | await verifyESLintOpts(`--no-ignore --plugin ${examplePluginName} foo.js`, { |
693 | overrideConfig: { | |
694 | plugins: [examplePluginName] | |
695 | } | |
eb39fafa DC |
696 | }); |
697 | }); | |
698 | ||
699 | }); | |
700 | ||
701 | describe("when supplied with a plugin-loading path", () => { | |
56c4a2cb | 702 | it("should pass the option to ESLint", async () => { |
eb39fafa DC |
703 | const examplePluginDirPath = "foo/bar"; |
704 | ||
56c4a2cb | 705 | await verifyESLintOpts(`--resolve-plugins-relative-to ${examplePluginDirPath} foo.js`, { |
eb39fafa DC |
706 | resolvePluginsRelativeTo: examplePluginDirPath |
707 | }); | |
708 | }); | |
709 | }); | |
710 | ||
711 | describe("when given an parser name", () => { | |
56c4a2cb | 712 | it("should exit with a fatal error if parser is invalid", async () => { |
eb39fafa DC |
713 | const filePath = getFixturePath("passing.js"); |
714 | ||
56c4a2cb | 715 | await stdAssert.rejects(async () => await cli.execute(`--no-ignore --parser test111 ${filePath}`), "Cannot find module 'test111'"); |
eb39fafa DC |
716 | }); |
717 | ||
56c4a2cb | 718 | it("should exit with no error if parser is valid", async () => { |
eb39fafa | 719 | const filePath = getFixturePath("passing.js"); |
56c4a2cb | 720 | const exit = await cli.execute(`--no-ignore --parser espree ${filePath}`); |
eb39fafa DC |
721 | |
722 | assert.strictEqual(exit, 0); | |
723 | }); | |
724 | }); | |
725 | ||
726 | describe("when given parser options", () => { | |
56c4a2cb | 727 | it("should exit with error if parser options are invalid", async () => { |
eb39fafa | 728 | const filePath = getFixturePath("passing.js"); |
56c4a2cb | 729 | const exit = await cli.execute(`--no-ignore --parser-options test111 ${filePath}`); |
eb39fafa DC |
730 | |
731 | assert.strictEqual(exit, 2); | |
732 | }); | |
733 | ||
56c4a2cb | 734 | it("should exit with no error if parser is valid", async () => { |
eb39fafa | 735 | const filePath = getFixturePath("passing.js"); |
56c4a2cb | 736 | const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); |
eb39fafa DC |
737 | |
738 | assert.strictEqual(exit, 0); | |
739 | }); | |
740 | ||
56c4a2cb | 741 | it("should exit with an error on ecmaVersion 7 feature in ecmaVersion 6", async () => { |
eb39fafa | 742 | const filePath = getFixturePath("passing-es7.js"); |
56c4a2cb | 743 | const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); |
eb39fafa DC |
744 | |
745 | assert.strictEqual(exit, 1); | |
746 | }); | |
747 | ||
56c4a2cb | 748 | it("should exit with no error on ecmaVersion 7 feature in ecmaVersion 7", async () => { |
eb39fafa | 749 | const filePath = getFixturePath("passing-es7.js"); |
56c4a2cb | 750 | const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:7 ${filePath}`); |
eb39fafa DC |
751 | |
752 | assert.strictEqual(exit, 0); | |
753 | }); | |
754 | ||
56c4a2cb | 755 | it("should exit with no error on ecmaVersion 7 feature with config ecmaVersion 6 and command line ecmaVersion 7", async () => { |
eb39fafa DC |
756 | const configPath = getFixturePath("configurations", "es6.json"); |
757 | const filePath = getFixturePath("passing-es7.js"); | |
56c4a2cb | 758 | const exit = await cli.execute(`--no-ignore --config ${configPath} --parser-options=ecmaVersion:7 ${filePath}`); |
eb39fafa DC |
759 | |
760 | assert.strictEqual(exit, 0); | |
761 | }); | |
762 | }); | |
763 | ||
764 | describe("when given the max-warnings flag", () => { | |
56c4a2cb | 765 | it("should not change exit code if warning count under threshold", async () => { |
eb39fafa | 766 | const filePath = getFixturePath("max-warnings"); |
56c4a2cb | 767 | const exitCode = await cli.execute(`--no-ignore --max-warnings 10 ${filePath}`); |
eb39fafa DC |
768 | |
769 | assert.strictEqual(exitCode, 0); | |
770 | }); | |
771 | ||
56c4a2cb | 772 | it("should exit with exit code 1 if warning count exceeds threshold", async () => { |
eb39fafa | 773 | const filePath = getFixturePath("max-warnings"); |
56c4a2cb | 774 | const exitCode = await cli.execute(`--no-ignore --max-warnings 5 ${filePath}`); |
eb39fafa DC |
775 | |
776 | assert.strictEqual(exitCode, 1); | |
777 | assert.ok(log.error.calledOnce); | |
778 | assert.include(log.error.getCall(0).args[0], "ESLint found too many warnings"); | |
779 | }); | |
780 | ||
56c4a2cb | 781 | it("should not change exit code if warning count equals threshold", async () => { |
eb39fafa | 782 | const filePath = getFixturePath("max-warnings"); |
56c4a2cb | 783 | const exitCode = await cli.execute(`--no-ignore --max-warnings 6 ${filePath}`); |
eb39fafa DC |
784 | |
785 | assert.strictEqual(exitCode, 0); | |
786 | }); | |
787 | ||
56c4a2cb | 788 | it("should not change exit code if flag is not specified and there are warnings", async () => { |
eb39fafa | 789 | const filePath = getFixturePath("max-warnings"); |
56c4a2cb | 790 | const exitCode = await cli.execute(filePath); |
eb39fafa DC |
791 | |
792 | assert.strictEqual(exitCode, 0); | |
793 | }); | |
794 | }); | |
795 | ||
796 | describe("when passed --no-inline-config", () => { | |
797 | let localCLI; | |
798 | ||
799 | afterEach(() => { | |
800 | sinon.verifyAndRestore(); | |
801 | }); | |
802 | ||
56c4a2cb | 803 | it("should pass allowInlineConfig:false to ESLint when --no-inline-config is used", async () => { |
eb39fafa | 804 | |
56c4a2cb DC |
805 | // create a fake ESLint class to test with |
806 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: false })); | |
eb39fafa | 807 | |
56c4a2cb DC |
808 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
809 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([{ | |
810 | filePath: "./foo.js", | |
811 | output: "bar", | |
812 | messages: [ | |
813 | { | |
814 | severity: 2, | |
815 | message: "Fake message" | |
816 | } | |
817 | ], | |
eb39fafa | 818 | errorCount: 1, |
56c4a2cb DC |
819 | warningCount: 0 |
820 | }]); | |
821 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
822 | fakeESLint.outputFixes = sinon.stub(); | |
eb39fafa DC |
823 | |
824 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 825 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
826 | "./shared/logging": log |
827 | }); | |
828 | ||
56c4a2cb | 829 | await localCLI.execute("--no-inline-config ."); |
eb39fafa DC |
830 | }); |
831 | ||
56c4a2cb | 832 | it("should not error and allowInlineConfig should be true by default", async () => { |
eb39fafa | 833 | |
56c4a2cb DC |
834 | // create a fake ESLint class to test with |
835 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: true })); | |
eb39fafa | 836 | |
56c4a2cb DC |
837 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
838 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
839 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
840 | fakeESLint.outputFixes = sinon.stub(); | |
eb39fafa DC |
841 | |
842 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 843 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
844 | "./shared/logging": log |
845 | }); | |
846 | ||
56c4a2cb | 847 | const exitCode = await localCLI.execute("."); |
eb39fafa DC |
848 | |
849 | assert.strictEqual(exitCode, 0); | |
850 | ||
851 | }); | |
852 | ||
853 | }); | |
854 | ||
855 | describe("when passed --fix", () => { | |
856 | let localCLI; | |
857 | ||
858 | afterEach(() => { | |
859 | sinon.verifyAndRestore(); | |
860 | }); | |
861 | ||
56c4a2cb | 862 | it("should pass fix:true to ESLint when executing on files", async () => { |
eb39fafa | 863 | |
56c4a2cb DC |
864 | // create a fake ESLint class to test with |
865 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 866 | |
56c4a2cb DC |
867 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
868 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
869 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
870 | fakeESLint.outputFixes = sinon.mock().once(); | |
eb39fafa DC |
871 | |
872 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 873 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
874 | "./shared/logging": log |
875 | }); | |
876 | ||
56c4a2cb | 877 | const exitCode = await localCLI.execute("--fix ."); |
eb39fafa DC |
878 | |
879 | assert.strictEqual(exitCode, 0); | |
880 | ||
881 | }); | |
882 | ||
883 | ||
56c4a2cb | 884 | it("should rewrite files when in fix mode", async () => { |
eb39fafa | 885 | |
56c4a2cb DC |
886 | const report = [{ |
887 | filePath: "./foo.js", | |
888 | output: "bar", | |
889 | messages: [ | |
890 | { | |
891 | severity: 2, | |
892 | message: "Fake message" | |
893 | } | |
894 | ], | |
eb39fafa | 895 | errorCount: 1, |
56c4a2cb DC |
896 | warningCount: 0 |
897 | }]; | |
eb39fafa | 898 | |
56c4a2cb DC |
899 | // create a fake ESLint class to test with |
900 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 901 | |
56c4a2cb DC |
902 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
903 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
904 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
905 | fakeESLint.outputFixes = sinon.mock().withExactArgs(report); | |
eb39fafa DC |
906 | |
907 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 908 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
909 | "./shared/logging": log |
910 | }); | |
911 | ||
56c4a2cb | 912 | const exitCode = await localCLI.execute("--fix ."); |
eb39fafa DC |
913 | |
914 | assert.strictEqual(exitCode, 1); | |
915 | ||
916 | }); | |
917 | ||
56c4a2cb | 918 | it("should provide fix predicate and rewrite files when in fix mode and quiet mode", async () => { |
eb39fafa | 919 | |
56c4a2cb DC |
920 | const report = [{ |
921 | filePath: "./foo.js", | |
922 | output: "bar", | |
923 | messages: [ | |
924 | { | |
925 | severity: 1, | |
926 | message: "Fake message" | |
927 | } | |
928 | ], | |
eb39fafa | 929 | errorCount: 0, |
56c4a2cb DC |
930 | warningCount: 1 |
931 | }]; | |
eb39fafa | 932 | |
56c4a2cb DC |
933 | // create a fake ESLint class to test with |
934 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); | |
eb39fafa | 935 | |
56c4a2cb DC |
936 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
937 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
938 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
939 | fakeESLint.getErrorResults = sinon.stub().returns([]); | |
940 | fakeESLint.outputFixes = sinon.mock().withExactArgs(report); | |
eb39fafa DC |
941 | |
942 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 943 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
944 | "./shared/logging": log |
945 | }); | |
946 | ||
56c4a2cb | 947 | const exitCode = await localCLI.execute("--fix --quiet ."); |
eb39fafa DC |
948 | |
949 | assert.strictEqual(exitCode, 0); | |
950 | ||
951 | }); | |
952 | ||
56c4a2cb | 953 | it("should not call ESLint and return 2 when executing on text", async () => { |
eb39fafa | 954 | |
56c4a2cb DC |
955 | // create a fake ESLint class to test with |
956 | const fakeESLint = sinon.mock().never(); | |
eb39fafa DC |
957 | |
958 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 959 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
960 | "./shared/logging": log |
961 | }); | |
962 | ||
56c4a2cb | 963 | const exitCode = await localCLI.execute("--fix .", "foo = bar;"); |
eb39fafa DC |
964 | |
965 | assert.strictEqual(exitCode, 2); | |
966 | }); | |
967 | ||
968 | }); | |
969 | ||
970 | describe("when passed --fix-dry-run", () => { | |
971 | let localCLI; | |
972 | ||
973 | afterEach(() => { | |
974 | sinon.verifyAndRestore(); | |
975 | }); | |
976 | ||
56c4a2cb | 977 | it("should pass fix:true to ESLint when executing on files", async () => { |
eb39fafa | 978 | |
56c4a2cb DC |
979 | // create a fake ESLint class to test with |
980 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 981 | |
56c4a2cb DC |
982 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
983 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
984 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
985 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
986 | |
987 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 988 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
989 | "./shared/logging": log |
990 | }); | |
991 | ||
56c4a2cb | 992 | const exitCode = await localCLI.execute("--fix-dry-run ."); |
eb39fafa DC |
993 | |
994 | assert.strictEqual(exitCode, 0); | |
995 | ||
996 | }); | |
997 | ||
56c4a2cb | 998 | it("should pass fixTypes to ESLint when --fix-type is passed", async () => { |
eb39fafa | 999 | |
56c4a2cb | 1000 | const expectedESLintOptions = { |
eb39fafa DC |
1001 | fix: true, |
1002 | fixTypes: ["suggestion"] | |
1003 | }; | |
1004 | ||
56c4a2cb DC |
1005 | // create a fake ESLint class to test with |
1006 | const fakeESLint = sinon.mock().withExactArgs(sinon.match(expectedESLintOptions)); | |
eb39fafa | 1007 | |
56c4a2cb DC |
1008 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1009 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
1010 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1011 | fakeESLint.outputFixes = sinon.stub(); | |
eb39fafa DC |
1012 | |
1013 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1014 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1015 | "./shared/logging": log |
1016 | }); | |
1017 | ||
56c4a2cb | 1018 | const exitCode = await localCLI.execute("--fix-dry-run --fix-type suggestion ."); |
eb39fafa DC |
1019 | |
1020 | assert.strictEqual(exitCode, 0); | |
1021 | }); | |
1022 | ||
56c4a2cb | 1023 | it("should not rewrite files when in fix-dry-run mode", async () => { |
eb39fafa | 1024 | |
56c4a2cb DC |
1025 | const report = [{ |
1026 | filePath: "./foo.js", | |
1027 | output: "bar", | |
1028 | messages: [ | |
1029 | { | |
1030 | severity: 2, | |
1031 | message: "Fake message" | |
1032 | } | |
1033 | ], | |
eb39fafa | 1034 | errorCount: 1, |
56c4a2cb DC |
1035 | warningCount: 0 |
1036 | }]; | |
eb39fafa | 1037 | |
56c4a2cb DC |
1038 | // create a fake ESLint class to test with |
1039 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 1040 | |
56c4a2cb DC |
1041 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1042 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
1043 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1044 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
1045 | |
1046 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1047 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1048 | "./shared/logging": log |
1049 | }); | |
1050 | ||
56c4a2cb | 1051 | const exitCode = await localCLI.execute("--fix-dry-run ."); |
eb39fafa DC |
1052 | |
1053 | assert.strictEqual(exitCode, 1); | |
1054 | ||
1055 | }); | |
1056 | ||
56c4a2cb | 1057 | it("should provide fix predicate when in fix-dry-run mode and quiet mode", async () => { |
eb39fafa | 1058 | |
56c4a2cb DC |
1059 | const report = [{ |
1060 | filePath: "./foo.js", | |
1061 | output: "bar", | |
1062 | messages: [ | |
1063 | { | |
1064 | severity: 1, | |
1065 | message: "Fake message" | |
1066 | } | |
1067 | ], | |
eb39fafa | 1068 | errorCount: 0, |
56c4a2cb DC |
1069 | warningCount: 1 |
1070 | }]; | |
eb39fafa | 1071 | |
56c4a2cb DC |
1072 | // create a fake ESLint class to test with |
1073 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); | |
eb39fafa | 1074 | |
56c4a2cb DC |
1075 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1076 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
1077 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1078 | fakeESLint.getErrorResults = sinon.stub().returns([]); | |
1079 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
1080 | |
1081 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1082 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1083 | "./shared/logging": log |
1084 | }); | |
1085 | ||
56c4a2cb | 1086 | const exitCode = await localCLI.execute("--fix-dry-run --quiet ."); |
eb39fafa DC |
1087 | |
1088 | assert.strictEqual(exitCode, 0); | |
1089 | ||
1090 | }); | |
1091 | ||
56c4a2cb | 1092 | it("should allow executing on text", async () => { |
eb39fafa | 1093 | |
56c4a2cb DC |
1094 | const report = [{ |
1095 | filePath: "./foo.js", | |
1096 | output: "bar", | |
1097 | messages: [ | |
1098 | { | |
1099 | severity: 2, | |
1100 | message: "Fake message" | |
1101 | } | |
1102 | ], | |
eb39fafa | 1103 | errorCount: 1, |
56c4a2cb DC |
1104 | warningCount: 0 |
1105 | }]; | |
eb39fafa | 1106 | |
56c4a2cb DC |
1107 | // create a fake ESLint class to test with |
1108 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 1109 | |
56c4a2cb DC |
1110 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1111 | sinon.stub(fakeESLint.prototype, "lintText").returns(report); | |
1112 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1113 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
1114 | |
1115 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1116 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1117 | "./shared/logging": log |
1118 | }); | |
1119 | ||
56c4a2cb | 1120 | const exitCode = await localCLI.execute("--fix-dry-run .", "foo = bar;"); |
eb39fafa DC |
1121 | |
1122 | assert.strictEqual(exitCode, 1); | |
1123 | }); | |
1124 | ||
56c4a2cb | 1125 | it("should not call ESLint and return 2 when used with --fix", async () => { |
eb39fafa | 1126 | |
56c4a2cb DC |
1127 | // create a fake ESLint class to test with |
1128 | const fakeESLint = sinon.mock().never(); | |
eb39fafa DC |
1129 | |
1130 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1131 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1132 | "./shared/logging": log |
1133 | }); | |
1134 | ||
56c4a2cb | 1135 | const exitCode = await localCLI.execute("--fix --fix-dry-run .", "foo = bar;"); |
eb39fafa DC |
1136 | |
1137 | assert.strictEqual(exitCode, 2); | |
1138 | }); | |
1139 | }); | |
1140 | ||
1141 | describe("when passing --print-config", () => { | |
56c4a2cb | 1142 | it("should print out the configuration", async () => { |
eb39fafa DC |
1143 | const filePath = getFixturePath("xxxx"); |
1144 | ||
56c4a2cb | 1145 | const exitCode = await cli.execute(`--print-config ${filePath}`); |
eb39fafa DC |
1146 | |
1147 | assert.isTrue(log.info.calledOnce); | |
1148 | assert.strictEqual(exitCode, 0); | |
1149 | }); | |
1150 | ||
56c4a2cb | 1151 | it("should error if any positional file arguments are passed", async () => { |
eb39fafa DC |
1152 | const filePath1 = getFixturePath("files", "bar.js"); |
1153 | const filePath2 = getFixturePath("files", "foo.js"); | |
1154 | ||
56c4a2cb | 1155 | const exitCode = await cli.execute(`--print-config ${filePath1} ${filePath2}`); |
eb39fafa DC |
1156 | |
1157 | assert.isTrue(log.info.notCalled); | |
1158 | assert.isTrue(log.error.calledOnce); | |
1159 | assert.strictEqual(exitCode, 2); | |
1160 | }); | |
1161 | ||
56c4a2cb DC |
1162 | it("should error out when executing on text", async () => { |
1163 | const exitCode = await cli.execute("--print-config=myFile.js", "foo = bar;"); | |
eb39fafa DC |
1164 | |
1165 | assert.isTrue(log.info.notCalled); | |
1166 | assert.isTrue(log.error.calledOnce); | |
1167 | assert.strictEqual(exitCode, 2); | |
1168 | }); | |
1169 | }); | |
1170 | ||
1171 | }); |