W WRNexusJS
Tooling

@wrnexus/test

WRNexusJS-aware component, route, and browser testing utilities.

bun add @wrnexus/test@0.2.15
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.

Installation

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-dom is loaded lazily (via require), 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 (via wrnexus test); uses Bun's module
  • loading and the bun:test runtime.

  • mountHtml requires happy-dom to be available in the workspace (loaded
  • lazily; it's a dev dependency, not a runtime dependency of this package).

  • Works with the rest of the WRNexusJS toolchain:
  • [@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 is generated from the exact published 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

Copy-ready examples taken from this package's published documentation.

Example 1

bun add @wrnexus/test

Example 2

function renderComponent(source: string, props?: Record<string, unknown>): Promise<string>;

Example 3

function mountHtml(html: string): {
  document: Document;
  window: unknown;
  querySelector: (sel: string) => Element | null;
  querySelectorAll: (sel: string) => Element[];
};

Example 4

function callRoute(
  handler: (ctx: Context) => Response | Promise<Response>,
  request: Request,
): Promise<Response>;