@forthtilliath/test-kit
v0.4.0
Published
Framework-agnostic test helpers: a `fetch()` mock response builder, a JSON fixture loader, an in-memory file system fake, and readers/writers for Zustand's persisted state — extracted from patterns duplicated across test files in this monorepo, none of th
Readme
@forthtilliath/test-kit
Framework-agnostic test helpers: a fetch() mock response builder, a JSON
fixture loader, an in-memory file system fake, and readers/writers for
Zustand's persisted state — extracted from patterns
duplicated across test files in this monorepo, none of them tied to Expo or
any other framework (see @forthtilliath/expo-test-kit for the
Expo/Drizzle-specific ones: a real SQLite test database and an
expo-file-system fake).
Install
npm install --save-dev @forthtilliath/test-kitOr, from within this monorepo, as a workspace dependency:
{
"devDependencies": {
"@forthtilliath/test-kit": "workspace:*"
}
}Usage
Import everything you need from the package root, or one module at a time:
import { createMockResponse, loadJsonFixture } from "@forthtilliath/test-kit";
// or
import { createMockResponse } from "@forthtilliath/test-kit/createMockResponse";createMockResponse(body, options?)
Builds a real fetch() Response resolving body — full API (clone(),
blob(), arrayBuffer()…), assignable wherever a Response is expected.
A string body is sent as-is; anything else is JSON-serialized, with a
content-type: application/json header unless you pass one. ok follows
the real rule (2xx only) unless forced with { ok }; a 204/205/304 status
has no body. Wrap it in your test runner's own mock function — this package
has no opinion on Jest vs Vitest.
vi.stubGlobal(
"fetch",
vi.fn().mockResolvedValue(createMockResponse({ id: 1 })),
);
// an error case:
vi.fn().mockResolvedValue(createMockResponse({}, { status: 404 }));loadJsonFixture(importMetaUrl, relativePath)
Reads and JSON-parses a fixture file relative to the calling module,
resolved from import.meta.url — the ESM-safe replacement for the
CommonJS __dirname trick.
const release = loadJsonFixture<GithubRelease>(
import.meta.url,
"./fixtures/github-release.json",
);createInMemoryFileSystem(initialFiles?)
A minimal in-memory file system (exists, readFile, writeFile,
deleteFile, reset, and the underlying files map for direct
inspection) — enough to test code that reads/writes files by path (e.g. via
node:fs/promises) without touching the real disk.
const fakeFs = createInMemoryFileSystem({ "/config.json": "{}" });
vi.mock("node:fs/promises", () => fakeFs);readPersisted(storage, key) / writePersisted(storage, key, state, version)
Read and write the { state, version } envelope Zustand's persist
middleware keeps under its storage key — to check what a store saves on the
device, or to plant the data an earlier app version left there and test the
rehydration (and its migrate). storage is anything with getItem /
setItem, sync or async: React Native's AsyncStorage (its Jest mock),
the web's localStorage… readPersisted throws if nothing is stored, or
if the value isn't such an envelope.
import AsyncStorage from "@react-native-async-storage/async-storage";
await writePersisted(AsyncStorage, "settings", { dark: true }, 1);
// …rehydrate the store, then:
await expect(readPersisted(AsyncStorage, "settings")).resolves.toEqual({
state: { theme: "dark" },
version: 2,
});Scripts
pnpm run dev # tsc --watch -> dist/
pnpm run build # tsc -> dist/
pnpm run check-types # tsc --noEmit
pnpm run lint # eslint
pnpm run test # vitest run
pnpm run test:watch # vitestBuilt to dist/ (see the exports field in package.json), so run
pnpm run build (or dev) after source changes for consumers to see them.
