1 | import * as fse from "fs-extra";
|
2 | import { expect } from "chai";
|
3 | import path from "./assert_path";
|
4 | import helper from "./helper";
|
5 | import * as jetpack from "..";
|
6 |
|
7 | describe("find", () => {
|
8 | beforeEach(helper.setCleanTestCwd);
|
9 | afterEach(helper.switchBackToCorrectCwd);
|
10 |
|
11 | describe("returns list of relative paths anchored to CWD", () => {
|
12 | const preparations = () => {
|
13 | fse.outputFileSync("a/b/file.txt", "abc");
|
14 | };
|
15 |
|
16 | const expectations = (found: string[]) => {
|
17 | const normalizedPaths = helper.osSep(["a/b/file.txt"]);
|
18 | expect(found).to.eql(normalizedPaths);
|
19 | };
|
20 |
|
21 | it("sync", () => {
|
22 | preparations();
|
23 | expectations(jetpack.find("a", { matching: "*.txt" }));
|
24 | });
|
25 |
|
26 | it("async", done => {
|
27 | preparations();
|
28 | jetpack.findAsync("a", { matching: "*.txt" }).then(found => {
|
29 | expectations(found);
|
30 | done();
|
31 | });
|
32 | });
|
33 | });
|
34 |
|
35 | describe("if recursive=false will exclude subfolders from search", () => {
|
36 | const preparations = () => {
|
37 | fse.outputFileSync("x/file.txt", "abc");
|
38 | fse.outputFileSync("x/y/file.txt", "123");
|
39 | fse.outputFileSync("x/y/b/file.txt", "456");
|
40 | };
|
41 |
|
42 | const expectations = (found: string[]) => {
|
43 | const normalizedPaths = helper.osSep(["x/file.txt"]);
|
44 | expect(found).to.eql(normalizedPaths);
|
45 | };
|
46 |
|
47 | it("sync", () => {
|
48 | preparations();
|
49 | expectations(jetpack.find("x", { matching: "*.txt", recursive: false }));
|
50 | });
|
51 |
|
52 | it("async", done => {
|
53 | preparations();
|
54 | jetpack
|
55 | .findAsync("x", { matching: "*.txt", recursive: false })
|
56 | .then(found => {
|
57 | expectations(found);
|
58 | done();
|
59 | });
|
60 | });
|
61 | });
|
62 |
|
63 | describe("defaults to CWD if no path provided", () => {
|
64 | const preparations = () => {
|
65 | fse.outputFileSync("a/b/file.txt", "abc");
|
66 | };
|
67 |
|
68 | const expectations = (found: string[]) => {
|
69 | const normalizedPaths = helper.osSep(["a/b/file.txt"]);
|
70 | expect(found).to.eql(normalizedPaths);
|
71 | };
|
72 |
|
73 | it("sync", () => {
|
74 | preparations();
|
75 | expectations(jetpack.find({ matching: "*.txt" }));
|
76 | });
|
77 |
|
78 | it("async", done => {
|
79 | preparations();
|
80 | jetpack.findAsync({ matching: "*.txt" }).then(found => {
|
81 | expectations(found);
|
82 | done();
|
83 | });
|
84 | });
|
85 | });
|
86 |
|
87 | describe("returns empty list if nothing found", () => {
|
88 | const preparations = () => {
|
89 | fse.outputFileSync("a/b/c.md", "abc");
|
90 | };
|
91 |
|
92 | const expectations = (found: string[]) => {
|
93 | expect(found).to.eql([]);
|
94 | };
|
95 |
|
96 | it("sync", () => {
|
97 | preparations();
|
98 | expectations(jetpack.find("a", { matching: "*.txt" }));
|
99 | });
|
100 |
|
101 | it("async", done => {
|
102 | preparations();
|
103 | jetpack.findAsync("a", { matching: "*.txt" }).then(found => {
|
104 | expectations(found);
|
105 | done();
|
106 | });
|
107 | });
|
108 | });
|
109 |
|
110 | describe("finds all paths which match globs", () => {
|
111 | const preparations = () => {
|
112 | fse.outputFileSync("a/b/file.txt", "1");
|
113 | fse.outputFileSync("a/b/c/file.txt", "2");
|
114 | fse.outputFileSync("a/b/c/file.md", "3");
|
115 | fse.outputFileSync("a/x/y/z", "Zzzzz...");
|
116 | };
|
117 |
|
118 | const expectations = (found: string[]) => {
|
119 | const normalizedPaths = helper.osSep([
|
120 | "a/b/c/file.txt",
|
121 | "a/b/file.txt",
|
122 | "a/x/y/z"
|
123 | ]);
|
124 | found.sort();
|
125 | expect(found).to.eql(normalizedPaths);
|
126 | };
|
127 |
|
128 | it("sync", () => {
|
129 | preparations();
|
130 | expectations(jetpack.find("a", { matching: ["*.txt", "z"] }));
|
131 | });
|
132 |
|
133 | it("async", done => {
|
134 | preparations();
|
135 | jetpack.findAsync("a", { matching: ["*.txt", "z"] }).then(found => {
|
136 | expectations(found);
|
137 | done();
|
138 | });
|
139 | });
|
140 | });
|
141 |
|
142 | describe("anchors globs to directory you're finding in", () => {
|
143 | const preparations = () => {
|
144 | fse.outputFileSync("x/y/a/b/file.txt", "123");
|
145 | fse.outputFileSync("x/y/a/b/c/file.txt", "456");
|
146 | };
|
147 |
|
148 | const expectations = (found: string[]) => {
|
149 | const normalizedPaths = helper.osSep(["x/y/a/b/file.txt"]);
|
150 | expect(found).to.eql(normalizedPaths);
|
151 | };
|
152 |
|
153 | it("sync", () => {
|
154 | preparations();
|
155 | expectations(jetpack.find("x/y/a", { matching: "b/*.txt" }));
|
156 | });
|
157 |
|
158 | it("async", done => {
|
159 | preparations();
|
160 | jetpack.findAsync("x/y/a", { matching: "b/*.txt" }).then(found => {
|
161 | expectations(found);
|
162 | done();
|
163 | });
|
164 | });
|
165 | });
|
166 |
|
167 | describe("can use ./ as indication of anchor directory", () => {
|
168 | const preparations = () => {
|
169 | fse.outputFileSync("x/y/file.txt", "123");
|
170 | fse.outputFileSync("x/y/b/file.txt", "456");
|
171 | };
|
172 |
|
173 | const expectations = (found: string[]) => {
|
174 | const normalizedPaths = helper.osSep(["x/y/file.txt"]);
|
175 | expect(found).to.eql(normalizedPaths);
|
176 | };
|
177 |
|
178 | it("sync", () => {
|
179 | preparations();
|
180 | expectations(jetpack.find("x/y", { matching: "./file.txt" }));
|
181 | });
|
182 |
|
183 | it("async", done => {
|
184 | preparations();
|
185 | jetpack.findAsync("x/y", { matching: "./file.txt" }).then(found => {
|
186 | expectations(found);
|
187 | done();
|
188 | });
|
189 | });
|
190 | });
|
191 |
|
192 | describe("deals with negation globs", () => {
|
193 | const preparations = () => {
|
194 | fse.outputFileSync("x/y/a/b", "bbb");
|
195 | fse.outputFileSync("x/y/a/x", "xxx");
|
196 | fse.outputFileSync("x/y/a/y", "yyy");
|
197 | fse.outputFileSync("x/y/a/z", "zzz");
|
198 | };
|
199 |
|
200 | const expectations = (found: string[]) => {
|
201 | const normalizedPaths = helper.osSep(["x/y/a/b"]);
|
202 | expect(found).to.eql(normalizedPaths);
|
203 | };
|
204 |
|
205 | it("sync", () => {
|
206 | preparations();
|
207 | expectations(
|
208 | jetpack.find("x/y", {
|
209 | matching: [
|
210 | "a/*",
|
211 |
|
212 | "!x",
|
213 | "!a/y",
|
214 | "!./a/z"
|
215 | ]
|
216 | })
|
217 | );
|
218 | });
|
219 |
|
220 | it("async", done => {
|
221 | preparations();
|
222 | jetpack
|
223 | .findAsync("x/y", {
|
224 | matching: [
|
225 | "a/*",
|
226 |
|
227 | "!x",
|
228 | "!a/y",
|
229 | "!./a/z"
|
230 | ]
|
231 | })
|
232 | .then(found => {
|
233 | expectations(found);
|
234 | done();
|
235 | });
|
236 | });
|
237 | });
|
238 |
|
239 | describe("doesn't look for directories by default", () => {
|
240 | const preparations = () => {
|
241 | fse.outputFileSync("a/b/foo1", "abc");
|
242 | fse.mkdirsSync("a/b/foo2");
|
243 | };
|
244 |
|
245 | const expectations = (found: string[]) => {
|
246 | const normalizedPaths = helper.osSep(["a/b/foo1"]);
|
247 | expect(found).to.eql(normalizedPaths);
|
248 | };
|
249 |
|
250 | it("sync", () => {
|
251 | preparations();
|
252 | expectations(jetpack.find("a", { matching: "foo*" }));
|
253 | });
|
254 |
|
255 | it("async", done => {
|
256 | preparations();
|
257 | jetpack.findAsync("a", { matching: "foo*" }).then(found => {
|
258 | expectations(found);
|
259 | done();
|
260 | });
|
261 | });
|
262 | });
|
263 |
|
264 | describe("treats symlinks like real files", () => {
|
265 | const preparations = () => {
|
266 | fse.mkdirsSync("dir");
|
267 | fse.outputFileSync("file", "abc");
|
268 | jetpack.symlink("dir", "symdir");
|
269 | jetpack.symlink("file", "symfile");
|
270 | };
|
271 |
|
272 | const expectations = (found: string[]) => {
|
273 | expect(found).to.eql(["file", "symfile"]);
|
274 | };
|
275 |
|
276 | it("sync", () => {
|
277 | preparations();
|
278 | expectations(jetpack.find({ matching: "*" }));
|
279 | });
|
280 |
|
281 | it("async", done => {
|
282 | preparations();
|
283 | jetpack.findAsync({ matching: "*" }).then(found => {
|
284 | expectations(found);
|
285 | done();
|
286 | });
|
287 | });
|
288 | });
|
289 |
|
290 | describe("follows to symlinked directories", () => {
|
291 | const preparations = () => {
|
292 | fse.outputFileSync("dir1/dir2/file.txt", "abc");
|
293 | jetpack.symlink("../dir1", "foo/symlink_to_dir1");
|
294 | expect(jetpack.read("foo/symlink_to_dir1/dir2/file.txt")).to.eql("abc");
|
295 | };
|
296 |
|
297 | const expectations = (found: string[]) => {
|
298 | const normalizedPaths = helper.osSep([
|
299 | "foo/symlink_to_dir1/dir2/file.txt"
|
300 | ]);
|
301 | expect(found).to.eql(normalizedPaths);
|
302 | };
|
303 |
|
304 | it("sync", () => {
|
305 | preparations();
|
306 | expectations(jetpack.find("foo", { matching: "file*" }));
|
307 | });
|
308 |
|
309 | it("async", done => {
|
310 | preparations();
|
311 | jetpack.findAsync("foo", { matching: "file*" }).then(found => {
|
312 | expectations(found);
|
313 | done();
|
314 | });
|
315 | });
|
316 | });
|
317 |
|
318 | describe("can look for files and directories", () => {
|
319 | const preparations = () => {
|
320 | fse.outputFileSync("a/b/foo1", "abc");
|
321 | fse.mkdirsSync("a/b/foo2");
|
322 | };
|
323 |
|
324 | const expectations = (found: string[]) => {
|
325 | const normalizedPaths = helper.osSep(["a/b/foo1", "a/b/foo2"]);
|
326 | expect(found).to.eql(normalizedPaths);
|
327 | };
|
328 |
|
329 | it("sync", () => {
|
330 | preparations();
|
331 | expectations(
|
332 | jetpack.find("a", {
|
333 | matching: "foo*",
|
334 | directories: true
|
335 | })
|
336 | );
|
337 | });
|
338 |
|
339 | it("async", done => {
|
340 | preparations();
|
341 | jetpack
|
342 | .findAsync("a", {
|
343 | matching: "foo*",
|
344 | directories: true
|
345 | })
|
346 | .then(found => {
|
347 | expectations(found);
|
348 | done();
|
349 | })
|
350 | .catch(done);
|
351 | });
|
352 | });
|
353 |
|
354 | describe("can look for only directories", () => {
|
355 | const preparations = () => {
|
356 | fse.outputFileSync("a/b/foo1", "abc");
|
357 | fse.mkdirsSync("a/b/foo2");
|
358 | };
|
359 |
|
360 | const expectations = (found: string[]) => {
|
361 | const normalizedPaths = helper.osSep(["a/b/foo2"]);
|
362 | expect(found).to.eql(normalizedPaths);
|
363 | };
|
364 |
|
365 | it("sync", () => {
|
366 | preparations();
|
367 | expectations(
|
368 | jetpack.find("a", {
|
369 | matching: "foo*",
|
370 | files: false,
|
371 | directories: true
|
372 | })
|
373 | );
|
374 | });
|
375 |
|
376 | it("async", done => {
|
377 | preparations();
|
378 | jetpack
|
379 | .findAsync("a", {
|
380 | matching: "foo*",
|
381 | files: false,
|
382 | directories: true
|
383 | })
|
384 | .then(found => {
|
385 | expectations(found);
|
386 | done();
|
387 | })
|
388 | .catch(done);
|
389 | });
|
390 | });
|
391 |
|
392 | describe("looking for directories works ok with only negation globs in set", () => {
|
393 | const preparations = () => {
|
394 | fse.outputFileSync("a/x", "123");
|
395 | fse.outputFileSync("a/y", "789");
|
396 | };
|
397 |
|
398 | const expectations = (found: string[]) => {
|
399 | const normalizedPaths = helper.osSep(["a/x"]);
|
400 | expect(found).to.eql(normalizedPaths);
|
401 | };
|
402 |
|
403 | it("sync", () => {
|
404 | preparations();
|
405 | expectations(
|
406 | jetpack.find("a", {
|
407 | matching: ["!y"],
|
408 | directories: true
|
409 | })
|
410 | );
|
411 | });
|
412 |
|
413 | it("async", done => {
|
414 | preparations();
|
415 | jetpack
|
416 | .findAsync("a", {
|
417 | matching: ["!y"],
|
418 | directories: true
|
419 | })
|
420 | .then(found => {
|
421 | expectations(found);
|
422 | done();
|
423 | })
|
424 | .catch(done);
|
425 | });
|
426 | });
|
427 |
|
428 | describe("when you turn off files and directoies returns empty list", () => {
|
429 | const preparations = () => {
|
430 | fse.outputFileSync("a/b/foo1", "abc");
|
431 | fse.mkdirsSync("a/b/foo2");
|
432 | };
|
433 |
|
434 | const expectations = (found: string[]) => {
|
435 | expect(found).to.eql([]);
|
436 | };
|
437 |
|
438 | it("sync", () => {
|
439 | preparations();
|
440 | expectations(
|
441 | jetpack.find("a", {
|
442 | matching: "foo*",
|
443 | files: false,
|
444 | directories: false
|
445 | })
|
446 | );
|
447 | });
|
448 |
|
449 | it("async", done => {
|
450 | preparations();
|
451 | jetpack
|
452 | .findAsync("a", {
|
453 | matching: "foo*",
|
454 | files: false,
|
455 | directories: false
|
456 | })
|
457 | .then(found => {
|
458 | expectations(found);
|
459 | done();
|
460 | });
|
461 | });
|
462 | });
|
463 |
|
464 | describe("throws if path doesn't exist", () => {
|
465 | const expectations = (err: any) => {
|
466 | expect(err.code).to.equal("ENOENT");
|
467 | expect(err.message).to.have.string(
|
468 | "Path you want to find stuff in doesn't exist"
|
469 | );
|
470 | };
|
471 |
|
472 | it("sync", () => {
|
473 | try {
|
474 | jetpack.find("a", { matching: "*.txt" });
|
475 | throw new Error("Expected error to be thrown");
|
476 | } catch (err) {
|
477 | expectations(err);
|
478 | }
|
479 | });
|
480 |
|
481 | it("async", done => {
|
482 | jetpack.findAsync("a", { matching: "*.txt" }).catch(err => {
|
483 | expectations(err);
|
484 | done();
|
485 | });
|
486 | });
|
487 | });
|
488 |
|
489 | describe("throws if path is a file, not a directory", () => {
|
490 | const preparations = () => {
|
491 | fse.outputFileSync("a/b", "abc");
|
492 | };
|
493 |
|
494 | const expectations = (err: any) => {
|
495 | expect(err.code).to.equal("ENOTDIR");
|
496 | expect(err.message).to.have.string(
|
497 | "Path you want to find stuff in must be a directory"
|
498 | );
|
499 | };
|
500 |
|
501 | it("sync", () => {
|
502 | preparations();
|
503 | try {
|
504 | jetpack.find("a/b", { matching: "*.txt" });
|
505 | throw new Error("Expected error to be thrown");
|
506 | } catch (err) {
|
507 | expectations(err);
|
508 | }
|
509 | });
|
510 |
|
511 | it("async", done => {
|
512 | preparations();
|
513 | jetpack.findAsync("a/b", { matching: "*.txt" }).catch(err => {
|
514 | expectations(err);
|
515 | done();
|
516 | });
|
517 | });
|
518 | });
|
519 |
|
520 | describe("respects internal CWD of jetpack instance", () => {
|
521 | const preparations = () => {
|
522 | fse.outputFileSync("a/b/c/d.txt", "abc");
|
523 | };
|
524 |
|
525 | const expectations = (found: string[]) => {
|
526 | const normalizedPaths = helper.osSep(["b/c/d.txt"]);
|
527 | expect(found).to.eql(normalizedPaths);
|
528 | };
|
529 |
|
530 | it("sync", () => {
|
531 | const jetContext = jetpack.cwd("a");
|
532 | preparations();
|
533 | expectations(jetContext.find("b", { matching: "*.txt" }));
|
534 | });
|
535 |
|
536 | it("async", done => {
|
537 | const jetContext = jetpack.cwd("a");
|
538 | preparations();
|
539 | jetContext.findAsync("b", { matching: "*.txt" }).then(found => {
|
540 | expectations(found);
|
541 | done();
|
542 | });
|
543 | });
|
544 | });
|
545 |
|
546 | describe("finds dot-dirs and dot-files", () => {
|
547 | const preparations = () => {
|
548 | fse.outputFileSync(".dir/file", "a");
|
549 | fse.outputFileSync(".dir/.file", "b");
|
550 | fse.outputFileSync(".foo/.file", "c");
|
551 | };
|
552 |
|
553 | const expectations = (found: string[]) => {
|
554 | const normalizedPaths = helper.osSep([".dir", ".dir/.file"]);
|
555 | expect(found).to.eql(normalizedPaths);
|
556 | };
|
557 |
|
558 | it("sync", () => {
|
559 | preparations();
|
560 | expectations(
|
561 | jetpack.find({
|
562 | matching: [".dir", ".file", "!.foo/**"],
|
563 | directories: true
|
564 | })
|
565 | );
|
566 | });
|
567 |
|
568 | it("async", done => {
|
569 | preparations();
|
570 | jetpack
|
571 | .findAsync({
|
572 | matching: [".dir", ".file", "!.foo/**"],
|
573 | directories: true
|
574 | })
|
575 | .then(found => {
|
576 | expectations(found);
|
577 | done();
|
578 | });
|
579 | });
|
580 | });
|
581 |
|
582 | describe("input validation", () => {
|
583 | const tests = [
|
584 | { type: "sync", method: jetpack.find as any, methodName: "find" },
|
585 | {
|
586 | type: "async",
|
587 | method: jetpack.findAsync as any,
|
588 | methodName: "findAsync"
|
589 | }
|
590 | ];
|
591 |
|
592 | describe('"path" argument', () => {
|
593 | tests.forEach(test => {
|
594 | it(test.type, () => {
|
595 | expect(() => {
|
596 | test.method(undefined, {});
|
597 | }).to.throw(
|
598 | `Argument "path" passed to ${
|
599 | test.methodName
|
600 | }([path], options) must be a string. Received undefined`
|
601 | );
|
602 | });
|
603 | });
|
604 | });
|
605 |
|
606 | describe('"options" object', () => {
|
607 | describe('"matching" argument', () => {
|
608 | tests.forEach(test => {
|
609 | it(test.type, () => {
|
610 | expect(() => {
|
611 | test.method({ matching: 1 });
|
612 | }).to.throw(
|
613 | `Argument "options.matching" passed to ${
|
614 | test.methodName
|
615 | }([path], options) must be a string or an array of string. Received number`
|
616 | );
|
617 | });
|
618 | });
|
619 | });
|
620 | describe('"files" argument', () => {
|
621 | tests.forEach(test => {
|
622 | it(test.type, () => {
|
623 | expect(() => {
|
624 | test.method("abc", { files: 1 });
|
625 | }).to.throw(
|
626 | `Argument "options.files" passed to ${
|
627 | test.methodName
|
628 | }([path], options) must be a boolean. Received number`
|
629 | );
|
630 | });
|
631 | });
|
632 | });
|
633 | describe('"directories" argument', () => {
|
634 | tests.forEach(test => {
|
635 | it(test.type, () => {
|
636 | expect(() => {
|
637 | test.method("abc", { directories: 1 });
|
638 | }).to.throw(
|
639 | `Argument "options.directories" passed to ${
|
640 | test.methodName
|
641 | }([path], options) must be a boolean. Received number`
|
642 | );
|
643 | });
|
644 | });
|
645 | });
|
646 | describe('"recursive" argument', () => {
|
647 | tests.forEach(test => {
|
648 | it(test.type, () => {
|
649 | expect(() => {
|
650 | test.method("abc", { recursive: 1 });
|
651 | }).to.throw(
|
652 | `Argument "options.recursive" passed to ${
|
653 | test.methodName
|
654 | }([path], options) must be a boolean. Received number`
|
655 | );
|
656 | });
|
657 | });
|
658 | });
|
659 | });
|
660 | });
|
661 | });
|