]> git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/lib/cli-engine/file-enumerator.js
a1de4cd2161f3bc734d1b75feb3e4ef265c60c33
[pve-eslint.git] / eslint / tests / lib / cli-engine / file-enumerator.js
1 /**
2 * @fileoverview Tests for FileEnumerator class.
3 * @author Toru Nagashima <https://github.com/mysticatea>
4 */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const fs = require("fs");
12 const path = require("path");
13 const os = require("os");
14 const { assert } = require("chai");
15 const sh = require("shelljs");
16 const {
17 Legacy: {
18 CascadingConfigArrayFactory
19 }
20 } = require("@eslint/eslintrc");
21 const { createCustomTeardown } = require("../../_utils");
22 const { FileEnumerator } = require("../../../lib/cli-engine/file-enumerator");
23
24 //------------------------------------------------------------------------------
25 // Tests
26 //------------------------------------------------------------------------------
27
28 describe("FileEnumerator", () => {
29 describe("'iterateFiles(patterns)' method should iterate files and configs.", () => {
30 describe("with three directories ('lib', 'lib/nested', 'test') that contains 'one.js' and 'two.js'", () => {
31 const root = path.join(os.tmpdir(), "eslint/file-enumerator");
32 const files = {
33 "lib/nested/one.js": "",
34 "lib/nested/two.js": "",
35 "lib/nested/parser.js": "",
36 "lib/nested/.eslintrc.yml": "parser: './parser'",
37 "lib/one.js": "",
38 "lib/two.js": "",
39 "test/one.js": "",
40 "test/two.js": "",
41 "test/.eslintrc.yml": "env: { mocha: true }",
42 ".eslintignore": "/lib/nested/parser.js",
43 ".eslintrc.json": JSON.stringify({
44 rules: {
45 "no-undef": "error",
46 "no-unused-vars": "error"
47 }
48 })
49 };
50 const { prepare, cleanup, getPath } = createCustomTeardown({ cwd: root, files });
51
52 /** @type {FileEnumerator} */
53 let enumerator;
54
55 beforeEach(async () => {
56 await prepare();
57 enumerator = new FileEnumerator({ cwd: getPath() });
58 });
59
60 afterEach(cleanup);
61
62 it("should ignore empty strings.", () => {
63 Array.from(enumerator.iterateFiles(["lib/*.js", ""])); // don't throw "file not found" error.
64 });
65
66 describe("if 'lib/*.js' was given,", () => {
67
68 /** @type {Array<{config:(typeof import('../../../lib/cli-engine')).ConfigArray, filePath:string, ignored:boolean}>} */
69 let list;
70
71 beforeEach(() => {
72 list = [...enumerator.iterateFiles("lib/*.js")];
73 });
74
75 it("should list two files.", () => {
76 assert.strictEqual(list.length, 2);
77 });
78
79 it("should list 'lib/one.js' and 'lib/two.js'.", () => {
80 assert.deepStrictEqual(
81 list.map(entry => entry.filePath),
82 [
83 path.join(root, "lib/one.js"),
84 path.join(root, "lib/two.js")
85 ]
86 );
87 });
88
89 it("should use the config '.eslintrc.json' for both files.", () => {
90 assert.strictEqual(list[0].config, list[1].config);
91 assert.strictEqual(list[0].config.length, 3);
92 assert.strictEqual(list[0].config[0].name, "DefaultIgnorePattern");
93 assert.strictEqual(list[0].config[1].filePath, path.join(root, ".eslintrc.json"));
94 assert.strictEqual(list[0].config[2].filePath, path.join(root, ".eslintignore"));
95 });
96 });
97
98 describe("if 'lib/**/*.js' was given,", () => {
99
100 /** @type {Array<{config:(typeof import('../../../lib/cli-engine')).ConfigArray, filePath:string, ignored:boolean}>} */
101 let list;
102
103 beforeEach(() => {
104 list = [...enumerator.iterateFiles("lib/**/*.js")];
105 });
106
107 it("should list four files.", () => {
108 assert.strictEqual(list.length, 4);
109 });
110
111 it("should list 'lib/nested/one.js', 'lib/nested/two.js', 'lib/one.js', 'lib/two.js'.", () => {
112 assert.deepStrictEqual(
113 list.map(entry => entry.filePath),
114 [
115 path.join(root, "lib/nested/one.js"),
116 path.join(root, "lib/nested/two.js"),
117 path.join(root, "lib/one.js"),
118 path.join(root, "lib/two.js")
119 ]
120 );
121 });
122
123 it("should use the merged config of '.eslintrc.json' and 'lib/nested/.eslintrc.yml' for 'lib/nested/one.js' and 'lib/nested/two.js'.", () => {
124 assert.strictEqual(list[0].config, list[1].config);
125 assert.strictEqual(list[0].config.length, 4);
126 assert.strictEqual(list[0].config[0].name, "DefaultIgnorePattern");
127 assert.strictEqual(list[0].config[1].filePath, path.join(root, ".eslintrc.json"));
128 assert.strictEqual(list[0].config[2].filePath, path.join(root, "lib/nested/.eslintrc.yml"));
129 assert.strictEqual(list[0].config[3].filePath, path.join(root, ".eslintignore"));
130 });
131
132 it("should use the config '.eslintrc.json' for 'lib/one.js' and 'lib/two.js'.", () => {
133 assert.strictEqual(list[2].config, list[3].config);
134 assert.strictEqual(list[2].config.length, 3);
135 assert.strictEqual(list[2].config[0].name, "DefaultIgnorePattern");
136 assert.strictEqual(list[2].config[1].filePath, path.join(root, ".eslintrc.json"));
137 assert.strictEqual(list[2].config[2].filePath, path.join(root, ".eslintignore"));
138 });
139 });
140
141 describe("if 'lib/*.js' and 'test/*.js' were given,", () => {
142
143 /** @type {Array<{config:(typeof import('../../../lib/cli-engine')).ConfigArray, filePath:string, ignored:boolean}>} */
144 let list;
145
146 beforeEach(() => {
147 list = [...enumerator.iterateFiles(["lib/*.js", "test/*.js"])];
148 });
149
150 it("should list four files.", () => {
151 assert.strictEqual(list.length, 4);
152 });
153
154 it("should list 'lib/one.js', 'lib/two.js', 'test/one.js', 'test/two.js'.", () => {
155 assert.deepStrictEqual(
156 list.map(entry => entry.filePath),
157 [
158 path.join(root, "lib/one.js"),
159 path.join(root, "lib/two.js"),
160 path.join(root, "test/one.js"),
161 path.join(root, "test/two.js")
162 ]
163 );
164 });
165
166 it("should use the config '.eslintrc.json' for 'lib/one.js' and 'lib/two.js'.", () => {
167 assert.strictEqual(list[0].config, list[1].config);
168 assert.strictEqual(list[0].config.length, 3);
169 assert.strictEqual(list[0].config[0].name, "DefaultIgnorePattern");
170 assert.strictEqual(list[0].config[1].filePath, path.join(root, ".eslintrc.json"));
171 assert.strictEqual(list[0].config[2].filePath, path.join(root, ".eslintignore"));
172 });
173
174 it("should use the merged config of '.eslintrc.json' and 'test/.eslintrc.yml' for 'test/one.js' and 'test/two.js'.", () => {
175 assert.strictEqual(list[2].config, list[3].config);
176 assert.strictEqual(list[2].config.length, 4);
177 assert.strictEqual(list[2].config[0].name, "DefaultIgnorePattern");
178 assert.strictEqual(list[2].config[1].filePath, path.join(root, ".eslintrc.json"));
179 assert.strictEqual(list[2].config[2].filePath, path.join(root, "test/.eslintrc.yml"));
180 assert.strictEqual(list[2].config[3].filePath, path.join(root, ".eslintignore"));
181 });
182 });
183 });
184
185 // This group moved from 'tests/lib/util/glob-utils.js' when refactoring to keep the cumulated test cases.
186 describe("with 'tests/fixtures/glob-utils' files", () => {
187 let fixtureDir;
188
189 /**
190 * Returns the path inside of the fixture directory.
191 * @param {...string} args file path segments.
192 * @returns {string} The path inside the fixture directory.
193 * @private
194 */
195 function getFixturePath(...args) {
196 return path.join(fs.realpathSync(fixtureDir), ...args);
197 }
198
199 /**
200 * List files as a compatible shape with glob-utils.
201 * @param {string|string[]} patterns The patterns to list files.
202 * @param {Object} options The option for FileEnumerator.
203 * @returns {{filename:string,ignored:boolean}[]} The listed files.
204 */
205 function listFiles(patterns, options) {
206 return Array.from(
207 new FileEnumerator({
208 ...options,
209 configArrayFactory: new CascadingConfigArrayFactory({
210 ...options,
211
212 // Disable "No Configuration Found" error.
213 useEslintrc: false
214 })
215 }).iterateFiles(patterns),
216 ({ filePath, ignored }) => ({ filename: filePath, ignored })
217 );
218 }
219
220 before(function() {
221
222 /*
223 * GitHub Actions Windows and macOS runners occasionally
224 * exhibit extremely slow filesystem operations, during which
225 * copying fixtures exceeds the default test timeout, so raise
226 * it just for this hook. Mocha uses `this` to set timeouts on
227 * an individual hook level.
228 */
229 this.timeout(60 * 1000); // eslint-disable-line no-invalid-this -- Mocha API
230 fixtureDir = `${os.tmpdir()}/eslint/tests/fixtures/`;
231 sh.mkdir("-p", fixtureDir);
232 sh.cp("-r", "./tests/fixtures/*", fixtureDir);
233 });
234
235 after(() => {
236 sh.rm("-r", fixtureDir);
237 });
238
239 describe("listFilesToProcess()", () => {
240 it("should return an array with a resolved (absolute) filename", () => {
241 const patterns = [getFixturePath("glob-util", "one-js-file", "**/*.js")];
242 const result = listFiles(patterns, {
243 cwd: getFixturePath()
244 });
245
246 const file1 = getFixturePath("glob-util", "one-js-file", "baz.js");
247
248 assert.isArray(result);
249 assert.deepStrictEqual(result, [{ filename: file1, ignored: false }]);
250 });
251
252 it("should return all files matching a glob pattern", () => {
253 const patterns = [getFixturePath("glob-util", "two-js-files", "**/*.js")];
254 const result = listFiles(patterns, {
255 cwd: getFixturePath()
256 });
257
258 const file1 = getFixturePath("glob-util", "two-js-files", "bar.js");
259 const file2 = getFixturePath("glob-util", "two-js-files", "foo.js");
260
261 assert.strictEqual(result.length, 2);
262 assert.deepStrictEqual(result, [
263 { filename: file1, ignored: false },
264 { filename: file2, ignored: false }
265 ]);
266 });
267
268 it("should return all files matching multiple glob patterns", () => {
269 const patterns = [
270 getFixturePath("glob-util", "two-js-files", "**/*.js"),
271 getFixturePath("glob-util", "one-js-file", "**/*.js")
272 ];
273 const result = listFiles(patterns, {
274 cwd: getFixturePath()
275 });
276
277 const file1 = getFixturePath("glob-util", "two-js-files", "bar.js");
278 const file2 = getFixturePath("glob-util", "two-js-files", "foo.js");
279 const file3 = getFixturePath("glob-util", "one-js-file", "baz.js");
280
281 assert.strictEqual(result.length, 3);
282 assert.deepStrictEqual(result, [
283 { filename: file1, ignored: false },
284 { filename: file2, ignored: false },
285 { filename: file3, ignored: false }
286 ]);
287 });
288
289 it("should ignore hidden files for standard glob patterns", () => {
290 const patterns = [getFixturePath("glob-util", "hidden", "**/*.js")];
291
292 assert.throws(() => {
293 listFiles(patterns, {
294 cwd: getFixturePath()
295 });
296 }, `All files matched by '${patterns[0]}' are ignored.`);
297 });
298
299 it("should return hidden files if included in glob pattern", () => {
300 const patterns = [getFixturePath("glob-util", "hidden", "**/.*.js")];
301 const result = listFiles(patterns, {
302 cwd: getFixturePath()
303 });
304
305 const file1 = getFixturePath("glob-util", "hidden", ".foo.js");
306
307 assert.strictEqual(result.length, 1);
308 assert.deepStrictEqual(result, [
309 { filename: file1, ignored: false }
310 ]);
311 });
312
313 it("should ignore default ignored files if not passed explicitly", () => {
314 const directory = getFixturePath("glob-util", "hidden");
315 const patterns = [directory];
316
317 assert.throws(() => {
318 listFiles(patterns, {
319 cwd: getFixturePath()
320 });
321 }, `All files matched by '${directory}' are ignored.`);
322 });
323
324 it("should ignore and warn for default ignored files when passed explicitly", () => {
325 const filename = getFixturePath("glob-util", "hidden", ".foo.js");
326 const patterns = [filename];
327 const result = listFiles(patterns, {
328 cwd: getFixturePath()
329 });
330
331 assert.strictEqual(result.length, 1);
332 assert.deepStrictEqual(result[0], { filename, ignored: true });
333 });
334
335 it("should ignore default ignored files if not passed explicitly even if ignore is false", () => {
336 const directory = getFixturePath("glob-util", "hidden");
337 const patterns = [directory];
338
339 assert.throws(() => {
340 listFiles(patterns, {
341 cwd: getFixturePath(),
342 ignore: false
343 });
344 }, `All files matched by '${directory}' are ignored.`);
345 });
346
347 it("should not ignore default ignored files when passed explicitly if ignore is false", () => {
348 const filename = getFixturePath("glob-util", "hidden", ".foo.js");
349 const patterns = [filename];
350 const result = listFiles(patterns, {
351 cwd: getFixturePath(),
352 ignore: false
353 });
354
355 assert.strictEqual(result.length, 1);
356 assert.deepStrictEqual(result[0], { filename, ignored: false });
357 });
358
359 it("should throw an error for a file which does not exist", () => {
360 const filename = getFixturePath("glob-util", "hidden", "bar.js");
361 const patterns = [filename];
362
363 assert.throws(() => {
364 listFiles(patterns, {
365 cwd: getFixturePath(),
366 allowMissingGlobs: true
367 });
368 }, `No files matching '${filename}' were found.`);
369 });
370
371 it("should throw if a folder that does not have any applicable files is linted", () => {
372 const filename = getFixturePath("glob-util", "empty");
373 const patterns = [filename];
374
375 assert.throws(() => {
376 listFiles(patterns, {
377 cwd: getFixturePath()
378 });
379 }, `No files matching '${filename}' were found.`);
380 });
381
382 it("should throw if only ignored files match a glob", () => {
383 const pattern = getFixturePath("glob-util", "ignored");
384 const options = { ignore: true, ignorePath: getFixturePath("glob-util", "ignored", ".eslintignore") };
385
386 assert.throws(() => {
387 listFiles([pattern], options);
388 }, `All files matched by '${pattern}' are ignored.`);
389 });
390
391 it("should throw an error if no files match a glob", () => {
392
393 // Relying here on the .eslintignore from the repo root
394 const patterns = ["tests/fixtures/glob-util/ignored/**/*.js"];
395
396 assert.throws(() => {
397 listFiles(patterns);
398 }, `All files matched by '${patterns[0]}' are ignored.`);
399 });
400
401 it("should return an ignored file, if ignore option is turned off", () => {
402 const options = { ignore: false };
403 const patterns = [getFixturePath("glob-util", "ignored", "**/*.js")];
404 const result = listFiles(patterns, options);
405
406 assert.strictEqual(result.length, 1);
407 });
408
409 it("should ignore a file from a glob if it matches a pattern in an ignore file", () => {
410 const options = { ignore: true, ignorePath: getFixturePath("glob-util", "ignored", ".eslintignore") };
411 const patterns = [getFixturePath("glob-util", "ignored", "**/*.js")];
412
413 assert.throws(() => {
414 listFiles(patterns, options);
415 }, `All files matched by '${patterns[0]}' are ignored.`);
416 });
417
418 it("should ignore a file from a glob if matching a specified ignore pattern", () => {
419 const options = { ignore: true, cliConfig: { ignorePatterns: ["foo.js"] }, cwd: getFixturePath() };
420 const patterns = [getFixturePath("glob-util", "ignored", "**/*.js")];
421
422 assert.throws(() => {
423 listFiles(patterns, options);
424 }, `All files matched by '${patterns[0]}' are ignored.`);
425 });
426
427 it("should return a file only once if listed in more than 1 pattern", () => {
428 const patterns = [
429 getFixturePath("glob-util", "one-js-file", "**/*.js"),
430 getFixturePath("glob-util", "one-js-file", "baz.js")
431 ];
432 const result = listFiles(patterns, {
433 cwd: path.join(fixtureDir, "..")
434 });
435
436 const file1 = getFixturePath("glob-util", "one-js-file", "baz.js");
437
438 assert.isArray(result);
439 assert.deepStrictEqual(result, [
440 { filename: file1, ignored: false }
441 ]);
442 });
443
444 it("should set 'ignored: true' for files that are explicitly specified but ignored", () => {
445 const options = { ignore: true, cliConfig: { ignorePatterns: ["foo.js"] }, cwd: getFixturePath() };
446 const filename = getFixturePath("glob-util", "ignored", "foo.js");
447 const patterns = [filename];
448 const result = listFiles(patterns, options);
449
450 assert.strictEqual(result.length, 1);
451 assert.deepStrictEqual(result, [
452 { filename, ignored: true }
453 ]);
454 });
455
456 it("should not return files from default ignored folders", () => {
457 const options = { cwd: getFixturePath("glob-util") };
458 const glob = getFixturePath("glob-util", "**/*.js");
459 const patterns = [glob];
460 const result = listFiles(patterns, options);
461 const resultFilenames = result.map(resultObj => resultObj.filename);
462
463 assert.notInclude(resultFilenames, getFixturePath("glob-util", "node_modules", "dependency.js"));
464 });
465
466 it("should return unignored files from default ignored folders", () => {
467 const options = { cliConfig: { ignorePatterns: ["!/node_modules/dependency.js"] }, cwd: getFixturePath("glob-util") };
468 const glob = getFixturePath("glob-util", "**/*.js");
469 const patterns = [glob];
470 const result = listFiles(patterns, options);
471 const unignoredFilename = getFixturePath("glob-util", "node_modules", "dependency.js");
472
473 assert.includeDeepMembers(result, [{ filename: unignoredFilename, ignored: false }]);
474 });
475
476 it("should return unignored files from folders unignored in .eslintignore", () => {
477 const options = { cwd: getFixturePath("glob-util", "unignored"), ignore: true };
478 const glob = getFixturePath("glob-util", "unignored", "**/*.js");
479 const patterns = [glob];
480 const result = listFiles(patterns, options);
481
482 const filename = getFixturePath("glob-util", "unignored", "dir", "foo.js");
483
484 assert.strictEqual(result.length, 1);
485 assert.deepStrictEqual(result, [{ filename, ignored: false }]);
486 });
487
488 it("should return unignored files from folders unignored in .eslintignore for explicitly specified folder", () => {
489 const options = { cwd: getFixturePath("glob-util", "unignored"), ignore: true };
490 const dir = getFixturePath("glob-util", "unignored", "dir");
491 const patterns = [dir];
492 const result = listFiles(patterns, options);
493
494 const filename = getFixturePath("glob-util", "unignored", "dir", "foo.js");
495
496 assert.strictEqual(result.length, 1);
497 assert.deepStrictEqual(result, [{ filename, ignored: false }]);
498 });
499 });
500 });
501
502 describe("if contains symbolic links", async () => {
503 const root = path.join(os.tmpdir(), "eslint/file-enumerator");
504 const files = {
505 "dir1/1.js": "",
506 "dir1/2.js": "",
507 "top-level.js": "",
508 ".eslintrc.json": JSON.stringify({ rules: {} })
509 };
510 const dir2 = path.join(root, "dir2");
511 const { prepare, cleanup } = createCustomTeardown({ cwd: root, files });
512
513 beforeEach(async () => {
514 await prepare();
515 fs.mkdirSync(dir2);
516 fs.symlinkSync(path.join(root, "top-level.js"), path.join(dir2, "top.js"), "file");
517 fs.symlinkSync(path.join(root, "dir1"), path.join(dir2, "nested"), "dir");
518 });
519
520 afterEach(cleanup);
521
522 it("should resolve", () => {
523 const enumerator = new FileEnumerator({ cwd: root });
524 const list = Array.from(enumerator.iterateFiles(["dir2/**/*.js"])).map(({ filePath }) => filePath);
525
526 assert.deepStrictEqual(list, [
527 path.join(dir2, "nested", "1.js"),
528 path.join(dir2, "nested", "2.js"),
529 path.join(dir2, "top.js")
530 ]);
531 });
532
533 it("should ignore broken links", () => {
534 fs.unlinkSync(path.join(root, "top-level.js"));
535
536 const enumerator = new FileEnumerator({ cwd: root });
537 const list = Array.from(enumerator.iterateFiles(["dir2/**/*.js"])).map(({ filePath }) => filePath);
538
539 assert.deepStrictEqual(list, [
540 path.join(dir2, "nested", "1.js"),
541 path.join(dir2, "nested", "2.js")
542 ]);
543 });
544 });
545 });
546
547 // https://github.com/eslint/eslint/issues/13789
548 describe("constructor default values when config extends eslint:recommended", () => {
549 const root = path.join(os.tmpdir(), "eslint/file-enumerator");
550 const files = {
551 "file.js": "",
552 ".eslintrc.json": JSON.stringify({
553 extends: ["eslint:recommended"]
554 })
555 };
556 const { prepare, cleanup, getPath } = createCustomTeardown({ cwd: root, files });
557
558
559 /** @type {FileEnumerator} */
560 let enumerator;
561
562 beforeEach(async () => {
563 await prepare();
564 enumerator = new FileEnumerator({ cwd: getPath() });
565 });
566
567 afterEach(cleanup);
568
569 it("should not throw an exception iterating files", () => {
570 Array.from(enumerator.iterateFiles(["."]));
571 });
572 });
573 });