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