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