]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Tests for cli. | |
3 | * @author Ian Christian Myers | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | /* | |
56c4a2cb DC |
9 | * NOTE: If you are adding new tests for cli.js, use verifyESLintOpts(). The |
10 | * test only needs to verify that ESLint receives the correct opts. | |
eb39fafa DC |
11 | */ |
12 | ||
13 | //------------------------------------------------------------------------------ | |
14 | // Requirements | |
15 | //------------------------------------------------------------------------------ | |
16 | ||
17 | const assert = require("chai").assert, | |
56c4a2cb DC |
18 | stdAssert = require("assert"), |
19 | { ESLint } = require("../../lib/eslint"), | |
20 | BuiltinRules = require("../../lib/rules"), | |
eb39fafa DC |
21 | path = require("path"), |
22 | sinon = require("sinon"), | |
23 | fs = require("fs"), | |
24 | os = require("os"), | |
25 | sh = require("shelljs"); | |
26 | ||
27 | const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); | |
28 | ||
29 | //------------------------------------------------------------------------------ | |
30 | // Tests | |
31 | //------------------------------------------------------------------------------ | |
32 | ||
33 | describe("cli", () => { | |
34 | let fixtureDir; | |
35 | const log = { | |
36 | info: sinon.spy(), | |
37 | error: sinon.spy() | |
38 | }; | |
39 | const RuntimeInfo = { | |
40 | environment: sinon.stub(), | |
41 | version: sinon.stub() | |
42 | }; | |
43 | const cli = proxyquire("../../lib/cli", { | |
44 | "./shared/logging": log, | |
45 | "./shared/runtime-info": RuntimeInfo | |
46 | }); | |
47 | ||
48 | /** | |
56c4a2cb | 49 | * Verify that ESLint class receives correct opts via await cli.execute(). |
eb39fafa | 50 | * @param {string} cmd CLI command. |
56c4a2cb | 51 | * @param {Object} opts Options hash that should match that received by ESLint class. |
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 | }); |