/** * @note `fs.glob` et. al. are powered by {@link Bun.Glob}, which is extensively * tested elsewhere. These tests check API compatibility with Node.js. */ import { afterAll, beforeAll, describe, expect, it } from "bun:test"; import { isWindows, tempDirWithFiles } from "harness"; import fs from "node:fs"; let tmp: string; beforeAll(() => { tmp = tempDirWithFiles("fs-glob", { "foo.txt": "foo", a: { "bar.txt": "bar", "baz.js": "baz", }, "folder.test": { "file.txt": "content", "another-folder": { "some-file.txt": "content", }, }, }); }); afterAll(() => { return fs.promises.rmdir(tmp, { recursive: true }); }); describe("fs.glob", () => { it("has a length of 3", () => { expect(fs).toHaveProperty("glob"); expect(typeof fs.glob).toEqual("function"); expect(fs.glob).toHaveLength(3); }); it("is named 'glob'", () => { expect(fs.glob.name).toEqual("glob"); }); it("when successful, passes paths to the callback", done => { fs.glob("*.txt", { cwd: tmp }, (err, paths) => { expect(err).toBeNull(); expect(paths.sort()).toStrictEqual(["foo.txt"]); done(); }); }); it("can filter out files", done => { const exclude = (path: string) => path.endsWith(".js"); fs.glob("a/*", { cwd: tmp, exclude }, (err, paths) => { if (err) done(err); if (isWindows) { expect(paths).toStrictEqual(["a\\bar.txt"]); } else { expect(paths).toStrictEqual(["a/bar.txt"]); } done(); }); }); it("can filter out files (2)", done => { const exclude = ["**/*.js"]; fs.glob("a/*", { cwd: tmp, exclude }, (err, paths) => { if (err) done(err); if (isWindows) { expect(paths).toStrictEqual(["a\\bar.txt"]); } else { expect(paths).toStrictEqual(["a/bar.txt"]); } done(); }); }); describe("invalid arguments", () => { it("throws if no callback is provided", () => { expect(() => fs.glob("*.txt")).toThrow(TypeError); expect(() => fs.glob("*.txt", undefined)).toThrow(TypeError); expect(() => fs.glob("*.txt", { cwd: tmp })).toThrow(TypeError); expect(() => fs.glob("*.txt", { cwd: tmp }, undefined)).toThrow(TypeError); }); }); it("matches directories", () => { const paths = fs.globSync("*.test", { cwd: tmp }); expect(paths).toContain("folder.test"); }); it("supports arrays of patterns", () => { const expected = isWindows ? ["a\\bar.txt", "a\\baz.js"] : ["a/bar.txt", "a/baz.js"]; expect(fs.globSync(["a/bar.txt", "a/baz.js"], { cwd: tmp })).toStrictEqual(expected); }); }); // describe("fs.globSync", () => { it("has a length of 2", () => { expect(fs).toHaveProperty("globSync"); expect(typeof fs.globSync).toBe("function"); expect(fs.globSync).toHaveLength(2); }); it("is named 'globSync'", () => { expect(fs.globSync.name).toEqual("globSync"); }); it.each([ ["*.txt", ["foo.txt"]], ["a/**", isWindows ? ["a\\bar.txt", "a\\baz.js"] : ["a/bar.txt", "a/baz.js"]], ])("fs.glob(%p, { cwd: /tmp/fs-glob }) === %p", (pattern, expected) => { expect(fs.globSync(pattern, { cwd: tmp }).sort()).toStrictEqual(expected); }); describe("when process.cwd() is set", () => { let oldProcessCwd: () => string; beforeAll(() => { oldProcessCwd = process.cwd; process.cwd = () => tmp; }); afterAll(() => { process.cwd = oldProcessCwd; }); it("respects the new cwd", () => { expect(fs.globSync("*.txt")).toStrictEqual(["foo.txt"]); }); }); it("can filter out files", () => { const exclude = (path: string) => path.endsWith(".js"); const expected = isWindows ? ["a\\bar.txt"] : ["a/bar.txt"]; expect(fs.globSync("a/*", { cwd: tmp, exclude })).toStrictEqual(expected); }); it("can filter out files (2)", () => { const exclude = ["**/*.js"]; const expected = isWindows ? ["a\\bar.txt"] : ["a/bar.txt"]; expect(fs.globSync("a/*", { cwd: tmp, exclude })).toStrictEqual(expected); }); it("works without providing options", () => { const oldProcessCwd = process.cwd; try { process.cwd = () => tmp; const paths = fs.globSync("*.txt"); expect(paths).toContain("foo.txt"); } finally { process.cwd = oldProcessCwd; } }); it("matches directories", () => { const paths = fs.globSync("*.test", { cwd: tmp }); expect(paths).toContain("folder.test"); }); it("supports arrays of patterns", () => { const expected = isWindows ? ["a\\bar.txt", "a\\baz.js"] : ["a/bar.txt", "a/baz.js"]; expect(fs.globSync(["a/bar.txt", "a/baz.js"], { cwd: tmp })).toStrictEqual(expected); }); }); // describe("fs.promises.glob", () => { it("has a length of 2", () => { expect(fs.promises).toHaveProperty("glob"); expect(typeof fs.promises.glob).toBe("function"); expect(fs.promises.glob).toHaveLength(2); }); it("is named 'glob'", () => { expect(fs.promises.glob.name).toEqual("glob"); }); it("returns an AsyncIterable over matched paths", async () => { const iter = fs.promises.glob("*.txt", { cwd: tmp }); // FIXME: .toHaveProperty does not support symbol keys expect(iter[Symbol.asyncIterator]).toBeDefined(); for await (const path of iter) { expect(path).toMatch(/\.txt$/); } }); it("works without providing options", async () => { const oldProcessCwd = process.cwd; try { process.cwd = () => tmp; const iter = fs.promises.glob("*.txt"); expect(iter[Symbol.asyncIterator]).toBeDefined(); const paths = []; for await (const path of iter) { paths.push(path); } expect(paths).toContain("foo.txt"); } finally { process.cwd = oldProcessCwd; } }); it("matches directories", async () => { const iter = fs.promises.glob("*.test", { cwd: tmp }); expect(iter[Symbol.asyncIterator]).toBeDefined(); let count = 0; for await (const path of iter) { expect(path).toBe("folder.test"); count++; } expect(count).toBe(1); }); it("can filter out files", async () => { const exclude = (path: string) => path.endsWith(".js"); const expected = isWindows ? ["a\\bar.txt"] : ["a/bar.txt"]; expect(Array.fromAsync(fs.promises.glob("a/*", { cwd: tmp, exclude }))).resolves.toStrictEqual(expected); }); it("can filter out files (2)", async () => { const exclude = ["**/*.js"]; const expected = isWindows ? ["a\\bar.txt"] : ["a/bar.txt"]; expect(Array.fromAsync(fs.promises.glob("a/*", { cwd: tmp, exclude }))).resolves.toStrictEqual(expected); const exclude2 = ["folder.test/another-folder"]; const expected2 = isWindows ? ["folder.test\\file.txt"] : ["folder.test/file.txt"]; expect( Array.fromAsync(fs.promises.glob("folder.test/**/*", { cwd: tmp, exclude: exclude2 })), ).resolves.toStrictEqual(expected2); }); it("supports arrays of patterns", async () => { const expected = isWindows ? ["a\\bar.txt", "a\\baz.js"] : ["a/bar.txt", "a/baz.js"]; expect(Array.fromAsync(fs.promises.glob(["a/bar.txt", "a/baz.js"], { cwd: tmp }))).resolves.toStrictEqual(expected); }); }); //