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