FRbext.dev
DocsApplication Toolkit
On this page

Testing

bext gives PRISM apps the two testing ergonomics you know from Laravel and Testing Library, both runnable in plain bun test:

  • @bext-stack/framework/testkit — model factories for seeded test data and a route test client that invokes a route's loader/action with a synthesized request. Pure, no DOM, no server.
  • @bext-stack/framework/testing — component testing: render a "use signals" component, query it, interact, and assert (backed by happy-dom).

Factories#

ts
import { defineFactory, cycle } from "@bext-stack/framework/testkit";

const userFactory = defineFactory((n) => ({
  id: `u${n}`,
  email: `user${n}@example.com`,        // `n` gives unique fields
  role: cycle("admin", "member")(n),    // cycle by index
  active: true,
}));

const users = userFactory.makeMany(3);              // 3 seeded users
const admin = userFactory.make({ role: "admin" });  // one, overridden
const admins = userFactory.state(() => ({ role: "admin" })).makeMany(2); // a variant (states)

make/makeMany advance a sequence so unique fields stay unique; makeMany(n, i => ({...})) takes per-index overrides; state(fn) derives a variant factory (Laravel factory states); reset() zeroes the sequence.

Route test client#

testRoute(module) wraps a route's loader/action exports and calls them with a synthesized Request:

ts
import { testRoute } from "@bext-stack/framework/testkit";
import * as signup from "../app/signup/page";

test("rejects an invalid signup", async () => {
  const route = testRoute(signup);
  const res = await route.post({ email: "nope", age: "12" });   // form-encoded → action
  expect(res.ok).toBe(false);
});

test("loader reads query params", async () => {
  expect(await testRoute(signup).get({ ref: "promo" })).toMatchObject({ ref: "promo" });
});
Member Calls
.get(query?) the loader with a GET request
.post(fields?) the action with a form-encoded POST
.postJson(body) the action with a JSON POST
.request(req) either handler with an explicit Request (custom headers/cookies)

Standalone request builders — formData(obj), formRequest(url, fields), jsonRequest(url, body) — are exported too, and factories feed action inputs directly: route.post(userFactory.make()).

Component testing#

ts
import { render } from "@bext-stack/framework/testing";

const { text, getByTestId, click } = render(Counter, { start: 5 });
expect(text()).toContain("Count: 5");
click(getByTestId("inc"));
expect(text()).toContain("Count: 6");

render SSRs + hydrates the component in a happy-dom container and returns queries (getByText, getByTestId, getAllByTag) and interactions (click, type, fire). Import it only from test files — it pulls in happy-dom.

Try It#

The live testkit demo generates factory data and runs testRoute assertions in the isolate — the same helpers you'd use in bun test. Source in sites/demo/src/app/examples/testkit/page.tsx.

See Also#

Edit this page ↗Need a hand? ↗
Search documentation

Start with a topic, a command, or a question.