Testing

Components and culets are plain functions, so both can be tested directly. No DOM environment and no rendering library is required.

Setup

The test script runs Vitest with V8 coverage. Exclude the entrypoints, the facets file and the DTO folder from coverage, since they are wiring rather than logic.

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    coverage: {
      reporter: [
        "text",
        "html",
        "lcov"
      ],
      include: ['src/**/*.{ts,tsx}'],
      exclude: [
        'src/client/entrypoint.ts',
        'src/server/entrypoint.ts',
        'src/client/facets.cara.ts',
        'src/infra/**/*',
        'src/dto/**/*',
        '**/*.d.ts',
      ],
    },
  },
});

Testing a Component

Call init once from jjsx to install the transpiler, then call transpile with a JSX element to get the HTML string. Asserting on substrings of that string is usually enough and keeps the test free of a DOM.

// src/client/__tests__/profile-page.test.tsx
import { expect, test } from "vitest";
import { init, transpile } from "jjsx";
import Profile from "../pages/profile";

init();

test("Render profile page", () => {
    const mockUser = {
        id: "1",
        name: "John Doe",
        email: "john@example.com",
        username: "johndoe",
        phone: "123-456-7890",
        website: "johndoe.com",
    };

    const page = transpile(<Profile {...mockUser} />);

    expect(page).toContain('app-header');
    expect(page).toContain("John Doe");
    expect(page).toContain("john@example.com");
    expect(page).toContain("Vault Member");
});

A burnished component accepts its server props as ordinary props, so the same element renders with mock data in a test exactly as it would on the server.

`init()` installs the JJSX transpiler and must run before the first `transpile` call. Because the result is a plain HTML string, there is no DOM to set up and no rendering library to configure.

Testing a Culet

A culet is a function from a request to a value. Pass a plain object with params and call it. There is no server to start, which is the main reason culets are cheap to test.

// src/server/__tests__/trade-api.test.ts
import { describe, expect, test } from "vitest";
import getTradeData from "../culets/trade";

describe("Trade API", () => {
    test("GET /trade/btc", async () => {
        const mockRequest = {
            params: { symbol: "btc" },
            url: "/trade/btc",
            query: {},
            headers: {},
            cookies: {},
            method: "GET",
        };
        const result = await getTradeData(mockRequest);
        expect(result.symbol).toBe("BTC");
        expect(result.price).toBeDefined();
        expect(result.orderBook.bids.length).toBeGreaterThan(0);
    });
});

`@fetch-mock/vitest` is available for a culet that calls a remote API, which keeps the test offline and deterministic.

Coverage

A component that reads `window` or `document` directly in its body cannot be transpiled in a test, because the test environment has no document. That is a useful signal rather than an obstacle: it means the side effect belongs in an `afterMount` or `beforeMount` callback, where the framework already keeps it out of the server render.