@wrnexus/test
WRNexusJS-aware component, route, and browser testing utilities.
Private registry access required
This package is not available from the public npm registry. After WorkRoot approves access and supplies private registry instructions, install the release-aligned package:
bun add @wrnexus/test@0.2.59Request preview access. Never put registry tokens in source control.
Testing utilities for WRNexusJS apps — component rendering, reactive-DOM mounting, route handler calls, and a full in-process app harness, plus a one-import re-export of bun:test.
Part of the WRNexusJS framework — an SSR-first, Bun-native full-stack web framework.
Overview
@wrnexus/test is the server-side test toolkit you reach for when writing tests for a WRNexusJS app. It runs under bun test (invoked via wrnexus test) and gives you a single import surface: the bun:test primitives (test, expect, mock, …) re-exported alongside WRNexusJS-aware helpers that compile .wrn components, hydrate server HTML in a DOM, invoke API route handlers, and boot the real app on an ephemeral port for integration tests.
bun add @wrnexus/test
Private package — the machine must be authenticated to the wrnexus npm org
(a read token in ~/.npmrc). Requires Bun (Node is not supported).
API
Re-exported test primitives
For one-import DX, the following are re-exported straight from bun:test:
test, expect, describe, it, beforeEach, afterEach, beforeAll, afterAll, mock, spyOn.
createContext is also re-exported from @wrnexus/core.
renderComponent(source, props?)
function renderComponent(source: string, props?: Record<string, unknown>): Promise<string>;
Compiles a .wrn component source string (via @wrnexus/compiler) and renders it to an HTML string with the given props. Throws if the compiled module has no render export.
mountHtml(html)
function mountHtml(html: string): {
document: Document;
window: unknown;
querySelector: (sel: string) => Element | null;
querySelectorAll: (sel: string) => Element[];
};
Mounts server-rendered html in a happy-dom window with the reactive runtime hydrated, so you can test data-scope / data-text / data-for / data-show behaviour. Returns the window plus document and query helpers; assert on those.
happy-domis loaded lazily (viarequire), so importing this package never
requires it unless you actually call mountHtml.
callRoute(handler, request)
function callRoute(
handler: (ctx: Context) => Response | Promise<Response>,
request: Request,
): Promise<Response>;
Calls an API route handler with a Context built from a Request (using createContext). Returns the handler's Response.
createHarness(projectRoot, options?)
function createHarness(projectRoot: string, options?: HarnessOptions): Promise<Harness>;
interface HarnessOptions {
/** Config/env profile. Default "test". */
profile?: string;
}
interface Harness {
/** Base URL of the ephemeral test server. */
url: string;
/** Fetch a path on the app (relative to `url`). */
fetch(path: string, init?: RequestInit): Promise<Response>;
/** The scanned router (pages/api/realtime/components). */
router: unknown;
/** Stop the server. */
close(): void;
}
Boots the app at projectRoot on an ephemeral port (port: 0) for integration tests covering pages, API routes, middleware, and the full request pipeline. Loads env and app config for the given profile (default "test") so it picks up your test database/env. The server runs in development mode with HMR disabled. Remember to await app.close() when done.
Usage
import { test, expect, renderComponent, mountHtml, createHarness } from "@wrnexus/test";
test("counter renders its label", async () => {
const html = await renderComponent(SRC, { start: 3, label: "Hits" });
expect(html).toContain("Hits");
});
test("reactive scope hydrates", () => {
const { querySelector } = mountHtml(serverHtml);
expect(querySelector("[data-text]")?.textContent).toBe("3");
});
test("home page responds", async () => {
const app = await createHarness("examples/basic-app");
const res = await app.fetch("/");
expect(res.status).toBe(200);
await app.close();
});
Calling an API route handler directly:
import { test, expect, callRoute } from "@wrnexus/test";
import { GET } from "../app/api/health.ts";
test("health endpoint", async () => {
const res = await callRoute(GET, new Request("http://test/api/health"));
expect(res.status).toBe(200);
});
Requirements / Notes
- Bun-only. Runs under
bun test(viawrnexus test); uses Bun's module mountHtmlrequireshappy-domto be available in the workspace (loaded- Works with the rest of the WRNexusJS toolchain:
loading and the bun:test runtime.
lazily; it's a dev dependency, not a runtime dependency of this package).
[@wrnexus/compiler](../compiler) (compiles .wrn sources), [@wrnexus/core](../core) (Context / createContext), [@wrnexus/csr](../csr) (reactive runtime for mountHtml), [@wrnexus/dev-server](../dev-server) (startServer behind createHarness), and [@wrnexus/styles](../styles) (config/env/profile loading for the harness).
Complete TypeScript API
This declaration comes from the exact installed package and lists its exported functions, classes, interfaces, and types.
import { Context } from '@wrnexus/core';
export { createContext } from '@wrnexus/core';
export { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock, spyOn, test } from 'bun:test';
/**
* @wrnexus/test — testing utilities for WRNexusJS apps. Runs on `bun test` (via
* `wrnexus test`). Import everything from one place:
*
* import { test, expect, renderComponent, mountHtml, createHarness } from "@wrnexus/test";
*
* test("counter renders its label", async () => {
* const html = await renderComponent(SRC, { start: 3, label: "Hits" });
* expect(html).toContain("Hits");
* });
*
* test("home page responds", async () => {
* const app = await createHarness("examples/basic-app");
* const res = await app.fetch("/");
* expect(res.status).toBe(200);
* await app.close();
* });
*/
/** Compile a `.wrn` component source + render it to HTML with the given props. */
declare function renderComponent(source: string, props?: Record<string, unknown>): Promise<string>;
/**
* Mount server-rendered HTML in a happy-dom window with the reactive runtime
* hydrated, so you can test `data-scope`/`data-text`/`data-for`/`data-show`
* behaviour. Returns the window; assert on `win.document`.
*/
declare function mountHtml(html: string): {
document: Document;
window: unknown;
querySelector: (sel: string) => Element | null;
querySelectorAll: (sel: string) => Element[];
};
/** Call an API route handler with a `Context` built from a Request. */
declare function callRoute(handler: (ctx: Context) => Response | Promise<Response>, request: Request): Promise<Response>;
interface Harness {
/** Base URL of the ephemeral test server. */
url: string;
/** Fetch a path on the app (relative to `url`). */
fetch(path: string, init?: RequestInit): Promise<Response>;
/** The scanned router (pages/api/realtime/components). */
router: unknown;
/** Stop the server. */
close(): void;
}
interface HarnessOptions {
/** Config/env profile. Default "test". */
profile?: string;
}
/**
* Boot the app on an ephemeral port for integration tests (pages, API routes,
* middleware, the full pipeline). Uses the "test" profile by default so it picks
* up your test database/env. Remember to `await app.close()`.
*/
declare function createHarness(projectRoot: string, options?: HarnessOptions): Promise<Harness>;
export { type Harness, type HarnessOptions, callRoute, createHarness, mountHtml, renderComponent };
Examples
Examples are taken from this package's installed documentation and must be evaluated with its requirements and stability notes.
Typical usage
import { test, expect, renderComponent, mountHtml, createHarness } from "@wrnexus/test";
test("counter renders its label", async () => {
const html = await renderComponent(SRC, { start: 3, label: "Hits" });
expect(html).toContain("Hits");
});
test("reactive scope hydrates", () => {
const { querySelector } = mountHtml(serverHtml);
expect(querySelector("[data-text]")?.textContent).toBe("3");
});
test("home page responds", async () => {
const app = await createHarness("examples/basic-app");
const res = await app.fetch("/");
expect(res.status).toBe(200);
await app.close();
});Calling an API route handler directly
import { test, expect, callRoute } from "@wrnexus/test";
import { GET } from "../app/api/health.ts";
test("health endpoint", async () => {
const res = await callRoute(GET, new Request("http://test/api/health"));
expect(res.status).toBe(200);
});