import * as fse from "fs-extra"; import { expect } from "chai"; import path from "./assert_path"; import helper from "./helper"; import * as jetpack from ".."; describe("find", () => { beforeEach(helper.setCleanTestCwd); afterEach(helper.switchBackToCorrectCwd); describe("returns list of relative paths anchored to CWD", () => { const preparations = () => { fse.outputFileSync("a/b/file.txt", "abc"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["a/b/file.txt"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find("a", { matching: "*.txt" })); }); it("async", done => { preparations(); jetpack.findAsync("a", { matching: "*.txt" }).then(found => { expectations(found); done(); }); }); }); describe("if recursive=false will exclude subfolders from search", () => { const preparations = () => { fse.outputFileSync("x/file.txt", "abc"); fse.outputFileSync("x/y/file.txt", "123"); fse.outputFileSync("x/y/b/file.txt", "456"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["x/file.txt"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find("x", { matching: "*.txt", recursive: false })); }); it("async", done => { preparations(); jetpack .findAsync("x", { matching: "*.txt", recursive: false }) .then(found => { expectations(found); done(); }); }); }); describe("defaults to CWD if no path provided", () => { const preparations = () => { fse.outputFileSync("a/b/file.txt", "abc"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["a/b/file.txt"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find({ matching: "*.txt" })); }); it("async", done => { preparations(); jetpack.findAsync({ matching: "*.txt" }).then(found => { expectations(found); done(); }); }); }); describe("returns empty list if nothing found", () => { const preparations = () => { fse.outputFileSync("a/b/c.md", "abc"); }; const expectations = (found: string[]) => { expect(found).to.eql([]); }; it("sync", () => { preparations(); expectations(jetpack.find("a", { matching: "*.txt" })); }); it("async", done => { preparations(); jetpack.findAsync("a", { matching: "*.txt" }).then(found => { expectations(found); done(); }); }); }); describe("finds all paths which match globs", () => { const preparations = () => { fse.outputFileSync("a/b/file.txt", "1"); fse.outputFileSync("a/b/c/file.txt", "2"); fse.outputFileSync("a/b/c/file.md", "3"); fse.outputFileSync("a/x/y/z", "Zzzzz..."); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep([ "a/b/c/file.txt", "a/b/file.txt", "a/x/y/z" ]); found.sort(); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find("a", { matching: ["*.txt", "z"] })); }); it("async", done => { preparations(); jetpack.findAsync("a", { matching: ["*.txt", "z"] }).then(found => { expectations(found); done(); }); }); }); describe("anchors globs to directory you're finding in", () => { const preparations = () => { fse.outputFileSync("x/y/a/b/file.txt", "123"); fse.outputFileSync("x/y/a/b/c/file.txt", "456"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["x/y/a/b/file.txt"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find("x/y/a", { matching: "b/*.txt" })); }); it("async", done => { preparations(); jetpack.findAsync("x/y/a", { matching: "b/*.txt" }).then(found => { expectations(found); done(); }); }); }); describe("can use ./ as indication of anchor directory", () => { const preparations = () => { fse.outputFileSync("x/y/file.txt", "123"); fse.outputFileSync("x/y/b/file.txt", "456"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["x/y/file.txt"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find("x/y", { matching: "./file.txt" })); }); it("async", done => { preparations(); jetpack.findAsync("x/y", { matching: "./file.txt" }).then(found => { expectations(found); done(); }); }); }); describe("deals with negation globs", () => { const preparations = () => { fse.outputFileSync("x/y/a/b", "bbb"); fse.outputFileSync("x/y/a/x", "xxx"); fse.outputFileSync("x/y/a/y", "yyy"); fse.outputFileSync("x/y/a/z", "zzz"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["x/y/a/b"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations( jetpack.find("x/y", { matching: [ "a/*", // Three different pattern types to test: "!x", "!a/y", "!./a/z" ] }) ); }); it("async", done => { preparations(); jetpack .findAsync("x/y", { matching: [ "a/*", // Three different pattern types to test: "!x", "!a/y", "!./a/z" ] }) .then(found => { expectations(found); done(); }); }); }); describe("doesn't look for directories by default", () => { const preparations = () => { fse.outputFileSync("a/b/foo1", "abc"); fse.mkdirsSync("a/b/foo2"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["a/b/foo1"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find("a", { matching: "foo*" })); }); it("async", done => { preparations(); jetpack.findAsync("a", { matching: "foo*" }).then(found => { expectations(found); done(); }); }); }); describe("treats symlinks like real files", () => { const preparations = () => { fse.mkdirsSync("dir"); fse.outputFileSync("file", "abc"); jetpack.symlink("dir", "symdir"); jetpack.symlink("file", "symfile"); }; const expectations = (found: string[]) => { expect(found).to.eql(["file", "symfile"]); }; it("sync", () => { preparations(); expectations(jetpack.find({ matching: "*" })); }); it("async", done => { preparations(); jetpack.findAsync({ matching: "*" }).then(found => { expectations(found); done(); }); }); }); describe("follows to symlinked directories", () => { const preparations = () => { fse.outputFileSync("dir1/dir2/file.txt", "abc"); jetpack.symlink("../dir1", "foo/symlink_to_dir1"); expect(jetpack.read("foo/symlink_to_dir1/dir2/file.txt")).to.eql("abc"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep([ "foo/symlink_to_dir1/dir2/file.txt" ]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations(jetpack.find("foo", { matching: "file*" })); }); it("async", done => { preparations(); jetpack.findAsync("foo", { matching: "file*" }).then(found => { expectations(found); done(); }); }); }); describe("can look for files and directories", () => { const preparations = () => { fse.outputFileSync("a/b/foo1", "abc"); fse.mkdirsSync("a/b/foo2"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["a/b/foo1", "a/b/foo2"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations( jetpack.find("a", { matching: "foo*", directories: true }) ); }); it("async", done => { preparations(); jetpack .findAsync("a", { matching: "foo*", directories: true }) .then(found => { expectations(found); done(); }) .catch(done); }); }); describe("can look for only directories", () => { const preparations = () => { fse.outputFileSync("a/b/foo1", "abc"); fse.mkdirsSync("a/b/foo2"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["a/b/foo2"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations( jetpack.find("a", { matching: "foo*", files: false, directories: true }) ); }); it("async", done => { preparations(); jetpack .findAsync("a", { matching: "foo*", files: false, directories: true }) .then(found => { expectations(found); done(); }) .catch(done); }); }); describe("looking for directories works ok with only negation globs in set", () => { const preparations = () => { fse.outputFileSync("a/x", "123"); fse.outputFileSync("a/y", "789"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["a/x"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations( jetpack.find("a", { matching: ["!y"], directories: true }) ); }); it("async", done => { preparations(); jetpack .findAsync("a", { matching: ["!y"], directories: true }) .then(found => { expectations(found); done(); }) .catch(done); }); }); describe("when you turn off files and directoies returns empty list", () => { const preparations = () => { fse.outputFileSync("a/b/foo1", "abc"); fse.mkdirsSync("a/b/foo2"); }; const expectations = (found: string[]) => { expect(found).to.eql([]); }; it("sync", () => { preparations(); expectations( jetpack.find("a", { matching: "foo*", files: false, directories: false }) ); }); it("async", done => { preparations(); jetpack .findAsync("a", { matching: "foo*", files: false, directories: false }) .then(found => { expectations(found); done(); }); }); }); describe("throws if path doesn't exist", () => { const expectations = (err: any) => { expect(err.code).to.equal("ENOENT"); expect(err.message).to.have.string( "Path you want to find stuff in doesn't exist" ); }; it("sync", () => { try { jetpack.find("a", { matching: "*.txt" }); throw new Error("Expected error to be thrown"); } catch (err) { expectations(err); } }); it("async", done => { jetpack.findAsync("a", { matching: "*.txt" }).catch(err => { expectations(err); done(); }); }); }); describe("throws if path is a file, not a directory", () => { const preparations = () => { fse.outputFileSync("a/b", "abc"); }; const expectations = (err: any) => { expect(err.code).to.equal("ENOTDIR"); expect(err.message).to.have.string( "Path you want to find stuff in must be a directory" ); }; it("sync", () => { preparations(); try { jetpack.find("a/b", { matching: "*.txt" }); throw new Error("Expected error to be thrown"); } catch (err) { expectations(err); } }); it("async", done => { preparations(); jetpack.findAsync("a/b", { matching: "*.txt" }).catch(err => { expectations(err); done(); }); }); }); describe("respects internal CWD of jetpack instance", () => { const preparations = () => { fse.outputFileSync("a/b/c/d.txt", "abc"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep(["b/c/d.txt"]); // NOT a/b/c/d.txt expect(found).to.eql(normalizedPaths); }; it("sync", () => { const jetContext = jetpack.cwd("a"); preparations(); expectations(jetContext.find("b", { matching: "*.txt" })); }); it("async", done => { const jetContext = jetpack.cwd("a"); preparations(); jetContext.findAsync("b", { matching: "*.txt" }).then(found => { expectations(found); done(); }); }); }); describe("finds dot-dirs and dot-files", () => { const preparations = () => { fse.outputFileSync(".dir/file", "a"); fse.outputFileSync(".dir/.file", "b"); fse.outputFileSync(".foo/.file", "c"); }; const expectations = (found: string[]) => { const normalizedPaths = helper.osSep([".dir", ".dir/.file"]); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations( jetpack.find({ matching: [".dir", ".file", "!.foo/**"], directories: true }) ); }); it("async", done => { preparations(); jetpack .findAsync({ matching: [".dir", ".file", "!.foo/**"], directories: true }) .then(found => { expectations(found); done(); }); }); }); describe("if ignoreCase=true it ignores case in patterns", () => { const preparations = () => { fse.outputFileSync("FOO/BAR", "a"); }; const expectations = (found: string[]) => { const paths = ["FOO", "FOO/BAR"]; const normalizedPaths = helper.osSep(paths); expect(found).to.eql(normalizedPaths); }; it("sync", () => { preparations(); expectations( jetpack.find({ matching: ["foo", "bar"], directories: true, ignoreCase: true }) ); }); it("async", done => { preparations(); jetpack .findAsync({ matching: ["foo", "bar"], directories: true, ignoreCase: true }) .then(found => { expectations(found); done(); }) .catch(done); }); }); describe("input validation", () => { const tests = [ { type: "sync", method: jetpack.find as any, methodName: "find" }, { type: "async", method: jetpack.findAsync as any, methodName: "findAsync" } ]; describe('"path" argument', () => { tests.forEach(test => { it(test.type, () => { expect(() => { test.method(undefined, {}); }).to.throw( `Argument "path" passed to ${ test.methodName }([path], options) must be a string. Received undefined` ); }); }); }); describe('"options" object', () => { describe('"matching" argument', () => { tests.forEach(test => { it(test.type, () => { expect(() => { test.method({ matching: 1 }); }).to.throw( `Argument "options.matching" passed to ${ test.methodName }([path], options) must be a string or an array of string. Received number` ); }); }); }); describe('"files" argument', () => { tests.forEach(test => { it(test.type, () => { expect(() => { test.method("abc", { files: 1 }); }).to.throw( `Argument "options.files" passed to ${ test.methodName }([path], options) must be a boolean. Received number` ); }); }); }); describe('"directories" argument', () => { tests.forEach(test => { it(test.type, () => { expect(() => { test.method("abc", { directories: 1 }); }).to.throw( `Argument "options.directories" passed to ${ test.methodName }([path], options) must be a boolean. Received number` ); }); }); }); describe('"recursive" argument', () => { tests.forEach(test => { it(test.type, () => { expect(() => { test.method("abc", { recursive: 1 }); }).to.throw( `Argument "options.recursive" passed to ${ test.methodName }([path], options) must be a boolean. Received number` ); }); }); }); describe('"ignoreCase" argument', () => { tests.forEach(test => { it(test.type, () => { expect(() => { test.method("abc", { ignoreCase: 1 }); }).to.throw( `Argument "options.ignoreCase" passed to ${ test.methodName }([path], options) must be a boolean. Received number` ); }); }); }); }); }); });