Skip to content

Commit 9d2c754

Browse files
authored
feat(test): add test utils (#12)
* feat(test-utils): add test utils * chore: add changeset
1 parent e27f1ba commit 9d2c754

File tree

4 files changed

+92
-0
lines changed

4 files changed

+92
-0
lines changed

.changeset/old-mugs-stare.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@bomb.sh/tools": patch
3+
---
4+
5+
Adds `@bomb.sh/tools/test-utils` export with `createFixture()`

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
".": {
2727
"import": "./dist/bin.mjs"
2828
},
29+
"./test-utils": {
30+
"types": "./dist/test-utils.d.mts",
31+
"import": "./dist/test-utils.mjs"
32+
},
2933
"./*": "./dist/*",
3034
"./package.json": "./package.json",
3135
"./tsconfig.json": "./tsconfig.json"

src/test-utils.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, it, expect } from "vitest";
2+
import { existsSync } from "node:fs";
3+
import { readFile, writeFile } from "node:fs/promises";
4+
import { createFixture } from "./test-utils.ts";
5+
6+
describe("createFixture", () => {
7+
it("creates files on disk from inline tree", async () => {
8+
const fixture = await createFixture({
9+
"hello.txt": "hello world",
10+
});
11+
const content = await readFile(fixture.resolve("hello.txt"), "utf8");
12+
expect(content).toBe("hello world");
13+
});
14+
15+
it("creates nested directories from slash-separated keys", async () => {
16+
const fixture = await createFixture({
17+
"src/index.ts": "export const x = 1",
18+
"src/utils/helpers.ts": "export function help() {}",
19+
});
20+
expect(existsSync(fixture.resolve("src/index.ts"))).toBe(true);
21+
expect(existsSync(fixture.resolve("src/utils/helpers.ts"))).toBe(true);
22+
});
23+
24+
it("resolve returns absolute path within fixture root", async () => {
25+
const fixture = await createFixture({ "a.txt": "" });
26+
expect(fixture.resolve("a.txt").toString()).toContain(fixture.root.toString());
27+
});
28+
29+
it("readFile reads the actual file", async () => {
30+
const fixture = await createFixture({ "a.txt": "Empty" });
31+
expect(await fixture.readFile("a.txt")).toEqual("Empty");
32+
await writeFile(fixture.resolve("a.txt"), "Hello world!", { encoding: "utf-8" });
33+
expect(await fixture.readFile("a.txt")).toEqual("Hello world!");
34+
});
35+
36+
it("cleanup removes the temp directory", async () => {
37+
const fixture = await createFixture({ "a.txt": "" });
38+
const path = fixture.root;
39+
expect(existsSync(path)).toBe(true);
40+
await fixture.cleanup();
41+
expect(existsSync(path)).toBe(false);
42+
});
43+
});

src/test-utils.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { PathLike } from "node:fs";
2+
import { mkdtemp, mkdir, writeFile, rm, readFile as fsReadFile } from "node:fs/promises";
3+
import { tmpdir } from "node:os";
4+
import { sep } from "node:path";
5+
import { fileURLToPath, pathToFileURL } from "node:url";
6+
import { onTestFinished } from "vitest";
7+
8+
export interface Fixture {
9+
root: URL;
10+
resolve: (...segments: string[]) => URL;
11+
readFile: (file: PathLike) => Promise<string>;
12+
cleanup: () => Promise<void>;
13+
}
14+
15+
export async function createFixture(files: Record<string, string>): Promise<Fixture> {
16+
const root = new URL(`bsh-`, `file://${tmpdir()}/`);
17+
const path = await mkdtemp(fileURLToPath(root));
18+
const base = pathToFileURL(path + sep);
19+
20+
for (const [name, content] of Object.entries(files)) {
21+
const url = new URL(name, base);
22+
const dir = new URL("./", url);
23+
await mkdir(dir, { recursive: true });
24+
await writeFile(url, content, "utf8");
25+
}
26+
27+
const cleanup = () => rm(path, { recursive: true, force: true });
28+
onTestFinished(cleanup);
29+
30+
const resolve = (...segments: string[]) => new URL(`./${segments.join("/")}`, base);
31+
const readFile = (file: PathLike) =>
32+
fsReadFile(new URL(`./${file}`, base), { encoding: "utf-8" });
33+
34+
return {
35+
root: pathToFileURL(path),
36+
resolve,
37+
readFile,
38+
cleanup,
39+
};
40+
}

0 commit comments

Comments
 (0)