]>
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 | */ | |
609c276f | 93 | this.timeout(60 * 1000); // eslint-disable-line no-invalid-this -- Mocha API |
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); | |
609c276f | 515 | }, /Error while loading rule 'custom-rule': Boom!/u); |
eb39fafa DC |
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 | ||
5422a9cc TL |
781 | it("should exit with exit code 1 without printing warnings if the quiet option is enabled and warning count exceeds threshold", async () => { |
782 | const filePath = getFixturePath("max-warnings"); | |
783 | const exitCode = await cli.execute(`--no-ignore --quiet --max-warnings 5 ${filePath}`); | |
784 | ||
785 | assert.strictEqual(exitCode, 1); | |
786 | assert.ok(log.error.calledOnce); | |
787 | assert.include(log.error.getCall(0).args[0], "ESLint found too many warnings"); | |
788 | assert.ok(log.info.notCalled); // didn't print warnings | |
789 | }); | |
790 | ||
56c4a2cb | 791 | it("should not change exit code if warning count equals threshold", async () => { |
eb39fafa | 792 | const filePath = getFixturePath("max-warnings"); |
56c4a2cb | 793 | const exitCode = await cli.execute(`--no-ignore --max-warnings 6 ${filePath}`); |
eb39fafa DC |
794 | |
795 | assert.strictEqual(exitCode, 0); | |
796 | }); | |
797 | ||
56c4a2cb | 798 | it("should not change exit code if flag is not specified and there are warnings", async () => { |
eb39fafa | 799 | const filePath = getFixturePath("max-warnings"); |
56c4a2cb | 800 | const exitCode = await cli.execute(filePath); |
eb39fafa DC |
801 | |
802 | assert.strictEqual(exitCode, 0); | |
803 | }); | |
804 | }); | |
805 | ||
609c276f TL |
806 | describe("when given the exit-on-fatal-error flag", () => { |
807 | it("should not change exit code if no fatal errors are reported", async () => { | |
808 | const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error.js"); | |
809 | const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); | |
810 | ||
811 | assert.strictEqual(exitCode, 0); | |
812 | }); | |
813 | ||
814 | it("should exit with exit code 1 if no fatal errors are found, but rule violations are found", async () => { | |
815 | const filePath = getFixturePath("exit-on-fatal-error", "no-fatal-error-rule-violation.js"); | |
816 | const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); | |
817 | ||
818 | assert.strictEqual(exitCode, 1); | |
819 | }); | |
820 | ||
821 | it("should exit with exit code 2 if fatal error is found", async () => { | |
822 | const filePath = getFixturePath("exit-on-fatal-error", "fatal-error.js"); | |
823 | const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); | |
824 | ||
825 | assert.strictEqual(exitCode, 2); | |
826 | }); | |
827 | ||
828 | it("should exit with exit code 2 if fatal error is found in any file", async () => { | |
829 | const filePath = getFixturePath("exit-on-fatal-error"); | |
830 | const exitCode = await cli.execute(`--no-ignore --exit-on-fatal-error ${filePath}`); | |
831 | ||
832 | assert.strictEqual(exitCode, 2); | |
833 | }); | |
834 | ||
835 | ||
836 | }); | |
837 | ||
eb39fafa DC |
838 | describe("when passed --no-inline-config", () => { |
839 | let localCLI; | |
840 | ||
841 | afterEach(() => { | |
842 | sinon.verifyAndRestore(); | |
843 | }); | |
844 | ||
56c4a2cb | 845 | it("should pass allowInlineConfig:false to ESLint when --no-inline-config is used", async () => { |
eb39fafa | 846 | |
56c4a2cb DC |
847 | // create a fake ESLint class to test with |
848 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: false })); | |
eb39fafa | 849 | |
56c4a2cb DC |
850 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
851 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([{ | |
852 | filePath: "./foo.js", | |
853 | output: "bar", | |
854 | messages: [ | |
855 | { | |
856 | severity: 2, | |
857 | message: "Fake message" | |
858 | } | |
859 | ], | |
eb39fafa | 860 | errorCount: 1, |
56c4a2cb DC |
861 | warningCount: 0 |
862 | }]); | |
863 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
864 | fakeESLint.outputFixes = sinon.stub(); | |
eb39fafa DC |
865 | |
866 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 867 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
868 | "./shared/logging": log |
869 | }); | |
870 | ||
56c4a2cb | 871 | await localCLI.execute("--no-inline-config ."); |
eb39fafa DC |
872 | }); |
873 | ||
56c4a2cb | 874 | it("should not error and allowInlineConfig should be true by default", async () => { |
eb39fafa | 875 | |
56c4a2cb DC |
876 | // create a fake ESLint class to test with |
877 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: true })); | |
eb39fafa | 878 | |
56c4a2cb DC |
879 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
880 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
881 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
882 | fakeESLint.outputFixes = sinon.stub(); | |
eb39fafa DC |
883 | |
884 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 885 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
886 | "./shared/logging": log |
887 | }); | |
888 | ||
56c4a2cb | 889 | const exitCode = await localCLI.execute("."); |
eb39fafa DC |
890 | |
891 | assert.strictEqual(exitCode, 0); | |
892 | ||
893 | }); | |
894 | ||
895 | }); | |
896 | ||
897 | describe("when passed --fix", () => { | |
898 | let localCLI; | |
899 | ||
900 | afterEach(() => { | |
901 | sinon.verifyAndRestore(); | |
902 | }); | |
903 | ||
56c4a2cb | 904 | it("should pass fix:true to ESLint when executing on files", async () => { |
eb39fafa | 905 | |
56c4a2cb DC |
906 | // create a fake ESLint class to test with |
907 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 908 | |
56c4a2cb DC |
909 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
910 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
911 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
912 | fakeESLint.outputFixes = sinon.mock().once(); | |
eb39fafa DC |
913 | |
914 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 915 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
916 | "./shared/logging": log |
917 | }); | |
918 | ||
56c4a2cb | 919 | const exitCode = await localCLI.execute("--fix ."); |
eb39fafa DC |
920 | |
921 | assert.strictEqual(exitCode, 0); | |
922 | ||
923 | }); | |
924 | ||
925 | ||
56c4a2cb | 926 | it("should rewrite files when in fix mode", async () => { |
eb39fafa | 927 | |
56c4a2cb DC |
928 | const report = [{ |
929 | filePath: "./foo.js", | |
930 | output: "bar", | |
931 | messages: [ | |
932 | { | |
933 | severity: 2, | |
934 | message: "Fake message" | |
935 | } | |
936 | ], | |
eb39fafa | 937 | errorCount: 1, |
56c4a2cb DC |
938 | warningCount: 0 |
939 | }]; | |
eb39fafa | 940 | |
56c4a2cb DC |
941 | // create a fake ESLint class to test with |
942 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 943 | |
56c4a2cb DC |
944 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
945 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
946 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
947 | fakeESLint.outputFixes = sinon.mock().withExactArgs(report); | |
eb39fafa DC |
948 | |
949 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 950 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
951 | "./shared/logging": log |
952 | }); | |
953 | ||
56c4a2cb | 954 | const exitCode = await localCLI.execute("--fix ."); |
eb39fafa DC |
955 | |
956 | assert.strictEqual(exitCode, 1); | |
957 | ||
958 | }); | |
959 | ||
56c4a2cb | 960 | it("should provide fix predicate and rewrite files when in fix mode and quiet mode", async () => { |
eb39fafa | 961 | |
56c4a2cb DC |
962 | const report = [{ |
963 | filePath: "./foo.js", | |
964 | output: "bar", | |
965 | messages: [ | |
966 | { | |
967 | severity: 1, | |
968 | message: "Fake message" | |
969 | } | |
970 | ], | |
eb39fafa | 971 | errorCount: 0, |
56c4a2cb DC |
972 | warningCount: 1 |
973 | }]; | |
eb39fafa | 974 | |
56c4a2cb DC |
975 | // create a fake ESLint class to test with |
976 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); | |
eb39fafa | 977 | |
56c4a2cb DC |
978 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
979 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
980 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
981 | fakeESLint.getErrorResults = sinon.stub().returns([]); | |
982 | fakeESLint.outputFixes = sinon.mock().withExactArgs(report); | |
eb39fafa DC |
983 | |
984 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 985 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
986 | "./shared/logging": log |
987 | }); | |
988 | ||
56c4a2cb | 989 | const exitCode = await localCLI.execute("--fix --quiet ."); |
eb39fafa DC |
990 | |
991 | assert.strictEqual(exitCode, 0); | |
992 | ||
993 | }); | |
994 | ||
56c4a2cb | 995 | it("should not call ESLint and return 2 when executing on text", async () => { |
eb39fafa | 996 | |
56c4a2cb DC |
997 | // create a fake ESLint class to test with |
998 | const fakeESLint = sinon.mock().never(); | |
eb39fafa DC |
999 | |
1000 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1001 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1002 | "./shared/logging": log |
1003 | }); | |
1004 | ||
56c4a2cb | 1005 | const exitCode = await localCLI.execute("--fix .", "foo = bar;"); |
eb39fafa DC |
1006 | |
1007 | assert.strictEqual(exitCode, 2); | |
1008 | }); | |
1009 | ||
1010 | }); | |
1011 | ||
1012 | describe("when passed --fix-dry-run", () => { | |
1013 | let localCLI; | |
1014 | ||
1015 | afterEach(() => { | |
1016 | sinon.verifyAndRestore(); | |
1017 | }); | |
1018 | ||
56c4a2cb | 1019 | it("should pass fix:true to ESLint when executing on files", async () => { |
eb39fafa | 1020 | |
56c4a2cb DC |
1021 | // create a fake ESLint class to test with |
1022 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 1023 | |
56c4a2cb DC |
1024 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1025 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
1026 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1027 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
1028 | |
1029 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1030 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1031 | "./shared/logging": log |
1032 | }); | |
1033 | ||
56c4a2cb | 1034 | const exitCode = await localCLI.execute("--fix-dry-run ."); |
eb39fafa DC |
1035 | |
1036 | assert.strictEqual(exitCode, 0); | |
1037 | ||
1038 | }); | |
1039 | ||
56c4a2cb | 1040 | it("should pass fixTypes to ESLint when --fix-type is passed", async () => { |
eb39fafa | 1041 | |
56c4a2cb | 1042 | const expectedESLintOptions = { |
eb39fafa DC |
1043 | fix: true, |
1044 | fixTypes: ["suggestion"] | |
1045 | }; | |
1046 | ||
56c4a2cb DC |
1047 | // create a fake ESLint class to test with |
1048 | const fakeESLint = sinon.mock().withExactArgs(sinon.match(expectedESLintOptions)); | |
eb39fafa | 1049 | |
56c4a2cb DC |
1050 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1051 | sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); | |
1052 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1053 | fakeESLint.outputFixes = sinon.stub(); | |
eb39fafa DC |
1054 | |
1055 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1056 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1057 | "./shared/logging": log |
1058 | }); | |
1059 | ||
56c4a2cb | 1060 | const exitCode = await localCLI.execute("--fix-dry-run --fix-type suggestion ."); |
eb39fafa DC |
1061 | |
1062 | assert.strictEqual(exitCode, 0); | |
1063 | }); | |
1064 | ||
56c4a2cb | 1065 | it("should not rewrite files when in fix-dry-run mode", async () => { |
eb39fafa | 1066 | |
56c4a2cb DC |
1067 | const report = [{ |
1068 | filePath: "./foo.js", | |
1069 | output: "bar", | |
1070 | messages: [ | |
1071 | { | |
1072 | severity: 2, | |
1073 | message: "Fake message" | |
1074 | } | |
1075 | ], | |
eb39fafa | 1076 | errorCount: 1, |
56c4a2cb DC |
1077 | warningCount: 0 |
1078 | }]; | |
eb39fafa | 1079 | |
56c4a2cb DC |
1080 | // create a fake ESLint class to test with |
1081 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 1082 | |
56c4a2cb DC |
1083 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1084 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
1085 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1086 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
1087 | |
1088 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1089 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1090 | "./shared/logging": log |
1091 | }); | |
1092 | ||
56c4a2cb | 1093 | const exitCode = await localCLI.execute("--fix-dry-run ."); |
eb39fafa DC |
1094 | |
1095 | assert.strictEqual(exitCode, 1); | |
1096 | ||
1097 | }); | |
1098 | ||
56c4a2cb | 1099 | it("should provide fix predicate when in fix-dry-run mode and quiet mode", async () => { |
eb39fafa | 1100 | |
56c4a2cb DC |
1101 | const report = [{ |
1102 | filePath: "./foo.js", | |
1103 | output: "bar", | |
1104 | messages: [ | |
1105 | { | |
1106 | severity: 1, | |
1107 | message: "Fake message" | |
1108 | } | |
1109 | ], | |
eb39fafa | 1110 | errorCount: 0, |
56c4a2cb DC |
1111 | warningCount: 1 |
1112 | }]; | |
eb39fafa | 1113 | |
56c4a2cb DC |
1114 | // create a fake ESLint class to test with |
1115 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); | |
eb39fafa | 1116 | |
56c4a2cb DC |
1117 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1118 | sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); | |
1119 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1120 | fakeESLint.getErrorResults = sinon.stub().returns([]); | |
1121 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
1122 | |
1123 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1124 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1125 | "./shared/logging": log |
1126 | }); | |
1127 | ||
56c4a2cb | 1128 | const exitCode = await localCLI.execute("--fix-dry-run --quiet ."); |
eb39fafa DC |
1129 | |
1130 | assert.strictEqual(exitCode, 0); | |
1131 | ||
1132 | }); | |
1133 | ||
56c4a2cb | 1134 | it("should allow executing on text", async () => { |
eb39fafa | 1135 | |
56c4a2cb DC |
1136 | const report = [{ |
1137 | filePath: "./foo.js", | |
1138 | output: "bar", | |
1139 | messages: [ | |
1140 | { | |
1141 | severity: 2, | |
1142 | message: "Fake message" | |
1143 | } | |
1144 | ], | |
eb39fafa | 1145 | errorCount: 1, |
56c4a2cb DC |
1146 | warningCount: 0 |
1147 | }]; | |
eb39fafa | 1148 | |
56c4a2cb DC |
1149 | // create a fake ESLint class to test with |
1150 | const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); | |
eb39fafa | 1151 | |
56c4a2cb DC |
1152 | Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); |
1153 | sinon.stub(fakeESLint.prototype, "lintText").returns(report); | |
1154 | sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); | |
1155 | fakeESLint.outputFixes = sinon.mock().never(); | |
eb39fafa DC |
1156 | |
1157 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1158 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1159 | "./shared/logging": log |
1160 | }); | |
1161 | ||
56c4a2cb | 1162 | const exitCode = await localCLI.execute("--fix-dry-run .", "foo = bar;"); |
eb39fafa DC |
1163 | |
1164 | assert.strictEqual(exitCode, 1); | |
1165 | }); | |
1166 | ||
56c4a2cb | 1167 | it("should not call ESLint and return 2 when used with --fix", async () => { |
eb39fafa | 1168 | |
56c4a2cb DC |
1169 | // create a fake ESLint class to test with |
1170 | const fakeESLint = sinon.mock().never(); | |
eb39fafa DC |
1171 | |
1172 | localCLI = proxyquire("../../lib/cli", { | |
56c4a2cb | 1173 | "./eslint": { ESLint: fakeESLint }, |
eb39fafa DC |
1174 | "./shared/logging": log |
1175 | }); | |
1176 | ||
56c4a2cb | 1177 | const exitCode = await localCLI.execute("--fix --fix-dry-run .", "foo = bar;"); |
eb39fafa DC |
1178 | |
1179 | assert.strictEqual(exitCode, 2); | |
1180 | }); | |
1181 | }); | |
1182 | ||
1183 | describe("when passing --print-config", () => { | |
56c4a2cb | 1184 | it("should print out the configuration", async () => { |
eb39fafa DC |
1185 | const filePath = getFixturePath("xxxx"); |
1186 | ||
56c4a2cb | 1187 | const exitCode = await cli.execute(`--print-config ${filePath}`); |
eb39fafa DC |
1188 | |
1189 | assert.isTrue(log.info.calledOnce); | |
1190 | assert.strictEqual(exitCode, 0); | |
1191 | }); | |
1192 | ||
56c4a2cb | 1193 | it("should error if any positional file arguments are passed", async () => { |
eb39fafa DC |
1194 | const filePath1 = getFixturePath("files", "bar.js"); |
1195 | const filePath2 = getFixturePath("files", "foo.js"); | |
1196 | ||
56c4a2cb | 1197 | const exitCode = await cli.execute(`--print-config ${filePath1} ${filePath2}`); |
eb39fafa DC |
1198 | |
1199 | assert.isTrue(log.info.notCalled); | |
1200 | assert.isTrue(log.error.calledOnce); | |
1201 | assert.strictEqual(exitCode, 2); | |
1202 | }); | |
1203 | ||
56c4a2cb DC |
1204 | it("should error out when executing on text", async () => { |
1205 | const exitCode = await cli.execute("--print-config=myFile.js", "foo = bar;"); | |
eb39fafa DC |
1206 | |
1207 | assert.isTrue(log.info.notCalled); | |
1208 | assert.isTrue(log.error.calledOnce); | |
1209 | assert.strictEqual(exitCode, 2); | |
1210 | }); | |
1211 | }); | |
1212 | ||
1213 | }); |