mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
test(timeline): add minimal jsdom/testing-library DOM net
Phase 2 (b)-lite, slice 1: establish a thin DOM test lane so the upcoming virtualized scroll rewrite ships with automated coverage, not just manual checks. The repo's test runner is `node --test` over `*.test.mjs` with no jsdom and no JSX transform, so component rendering wasn't testable. This adds a second lane that coexists with the existing pure-logic lane: - esbuild `load` hook transforms .ts/.tsx (TS + JSX, automatic React runtime) - global-jsdom installs DOM globals via `--import` - @testing-library/react + /dom for render/query - `pnpm test` now runs both lanes (test:unit + test:dom) - DOM tests are `*.dom.test.tsx` Deliberately thin: jsdom has no layout engine, so it does not cover react-virtual's real measurement/scroll math — those stay on the manual/visual verification pass (see FEASIBILITY.md). One smoke test proves the net is wired. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
co-authored by
Taylor Ho
parent
606dfac37e
commit
fc2302b5e4
@@ -11,7 +11,9 @@
|
||||
"lint": "biome lint .",
|
||||
"check": "biome check . && pnpm check:file-sizes",
|
||||
"format": "biome format --write .",
|
||||
"test": "node --import ./test-loader.mjs --experimental-strip-types --test 'src/**/*.test.mjs'",
|
||||
"test": "pnpm test:unit && pnpm test:dom",
|
||||
"test:unit": "node --import ./test-loader.mjs --experimental-strip-types --test 'src/**/*.test.mjs'",
|
||||
"test:dom": "node --import ./test-dom-setup.mjs --test 'src/**/*.dom.test.tsx'",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"test:e2e": "pnpm build && playwright test",
|
||||
@@ -83,9 +85,14 @@
|
||||
"@tanstack/router-plugin": "^1.167.12",
|
||||
"@tanstack/virtual-file-routes": "^1.161.7",
|
||||
"@tauri-apps/cli": "~2.11",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@vitejs/plugin-react": "^6.0.0",
|
||||
"esbuild": "^0.28.1",
|
||||
"global-jsdom": "^29.0.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"nostr-tools": "^2.23.3",
|
||||
"postcss": "^8.5.8",
|
||||
"tailwindcss": "^4.3.0",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { afterEach, test } from "node:test";
|
||||
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
// Smoke test that proves the (b)-lite DOM net is wired: jsdom globals exist,
|
||||
// esbuild transforms TSX, React renders into the document, and Testing Library
|
||||
// can query it. The real virtualized-scroll DOM tests build on this lane.
|
||||
function Greeting({ name }: { name: string }) {
|
||||
return <p>Hello, {name}!</p>;
|
||||
}
|
||||
|
||||
test("DOM harness: jsdom + esbuild + testing-library render a component", () => {
|
||||
render(<Greeting name="Springfield" />);
|
||||
assert.ok(screen.getByText("Hello, Springfield!"));
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
// DOM test loader — extends the pure-logic resolver in `test-loader-hooks.mjs`
|
||||
// with a `load` hook so JSX/TSX components can be rendered under jsdom.
|
||||
//
|
||||
// The default `pnpm test` lane (node --test over *.test.mjs) runs pure logic and
|
||||
// imports .ts via `--experimental-strip-types`, which strips types but does NOT
|
||||
// transform JSX. The DOM lane needs real component rendering, so it routes .ts
|
||||
// and .tsx through esbuild (TS + JSX -> JS, automatic React runtime). This keeps
|
||||
// the net thin: one transform dep, no second test runner.
|
||||
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { transform } from "esbuild";
|
||||
|
||||
const srcRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"src",
|
||||
);
|
||||
|
||||
const repoRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
);
|
||||
|
||||
// Extensionless `@/` and relative specifiers resolve .tsx first (components),
|
||||
// then fall back to .ts (logic) — so both lanes of source resolve under jsdom.
|
||||
export function resolve(specifier, context, nextResolve) {
|
||||
if (specifier === "@features-manifest") {
|
||||
const resolved = path.join(repoRoot, "preview-features.json");
|
||||
return nextResolve(resolved, context);
|
||||
}
|
||||
if (specifier.startsWith("@/")) {
|
||||
const stripped = specifier.slice(2);
|
||||
if (path.extname(stripped)) {
|
||||
return nextResolve(`${srcRoot}/${stripped}`, context);
|
||||
}
|
||||
return nextResolve(`${srcRoot}/${stripped}.tsx`, context).catch(() =>
|
||||
nextResolve(`${srcRoot}/${stripped}.ts`, context),
|
||||
);
|
||||
}
|
||||
if (
|
||||
(specifier.startsWith("./") || specifier.startsWith("../")) &&
|
||||
!path.extname(specifier) &&
|
||||
context.parentURL
|
||||
) {
|
||||
const tsx = new URL(`${specifier}.tsx`, context.parentURL).href;
|
||||
return nextResolve(tsx, context).catch(() =>
|
||||
nextResolve(new URL(`${specifier}.ts`, context.parentURL).href, context),
|
||||
);
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
|
||||
export async function load(url, context, nextLoad) {
|
||||
if (url.endsWith(".ts") || url.endsWith(".tsx")) {
|
||||
const filename = fileURLToPath(url);
|
||||
const source = await readFile(filename, "utf8");
|
||||
const { code } = await transform(source, {
|
||||
loader: url.endsWith(".tsx") ? "tsx" : "ts",
|
||||
jsx: "automatic",
|
||||
format: "esm",
|
||||
target: "es2022",
|
||||
sourcemap: "inline",
|
||||
sourcefile: filename,
|
||||
});
|
||||
return { format: "module", source: code, shortCircuit: true };
|
||||
}
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// DOM test bootstrap for the (b)-lite net. Imported via `--import` so it runs
|
||||
// before any test module loads:
|
||||
// 1. install a jsdom `window`/`document`/etc. into globalThis (global-jsdom)
|
||||
// 2. register the JSX/TSX-transforming module loader
|
||||
//
|
||||
// Scope is deliberately thin: just enough DOM to render React components and
|
||||
// exercise the virtualized scroll path. jsdom has no layout engine, so it does
|
||||
// NOT cover react-virtual's real measurement/scroll math — those stay on the
|
||||
// manual/visual verification pass. See FEASIBILITY.md.
|
||||
|
||||
import "global-jsdom/register";
|
||||
|
||||
import { register } from "node:module";
|
||||
|
||||
register("./test-dom-loader-hooks.mjs", import.meta.url);
|
||||
Generated
+726
-11
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user