[READ-ONLY] Mirror of https://github.com/bombshell-dev/tools. Internal CLI to standardize tooling across all Bombshell projects
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(test): add test utils (#12)

* feat(test-utils): add test utils

* chore: add changeset

authored by

Nate Moore and committed by
GitHub
(Mar 24, 2026, 9:11 PM EDT) 9d2c754a e27f1ba4

+92
+5
.changeset/old-mugs-stare.md
··· 1 + --- 2 + "@bomb.sh/tools": patch 3 + --- 4 + 5 + Adds `@bomb.sh/tools/test-utils` export with `createFixture()`
+4
package.json
··· 26 26 ".": { 27 27 "import": "./dist/bin.mjs" 28 28 }, 29 + "./test-utils": { 30 + "types": "./dist/test-utils.d.mts", 31 + "import": "./dist/test-utils.mjs" 32 + }, 29 33 "./*": "./dist/*", 30 34 "./package.json": "./package.json", 31 35 "./tsconfig.json": "./tsconfig.json"
+43
src/test-utils.test.ts
··· 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 + });
+40
src/test-utils.ts
··· 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 + }