143 lines
5.2 KiB
TypeScript
143 lines
5.2 KiB
TypeScript
/**
|
|
* Example WrNexus tests. Run with `wrnexus test` (or `bun test`).
|
|
*
|
|
* - renderComponent: compile + render a .wrn component to HTML
|
|
* - callRoute: call an API handler with a fake Request
|
|
* - createHarness: boot the whole app on an ephemeral port and fetch real routes
|
|
*
|
|
* Everything comes from one import: `@wrnexus/test`.
|
|
*/
|
|
import {
|
|
test,
|
|
expect,
|
|
describe,
|
|
beforeAll,
|
|
afterAll,
|
|
renderComponent,
|
|
callRoute,
|
|
createHarness,
|
|
type Harness,
|
|
} from "@wrnexus/test";
|
|
|
|
// --- Component-level (fast, no server) -------------------------------------
|
|
|
|
const COUNTER = `component Counter {
|
|
props {
|
|
start = 0
|
|
label = "Count"
|
|
}
|
|
state count = start
|
|
view { <button @click="count++">{label}: {count}</button> }
|
|
}`;
|
|
|
|
test("Counter renders its label and initial value", async () => {
|
|
const html = await renderComponent(COUNTER, { start: 5, label: "Clicks" });
|
|
expect(html).toContain("Clicks");
|
|
expect(html).toContain("5");
|
|
});
|
|
|
|
// --- Route-level (fast, no server) -----------------------------------------
|
|
|
|
test("POST /api/echo echoes the JSON body", async () => {
|
|
const { POST } = await import("./api/echo.ts");
|
|
const res = await callRoute(
|
|
POST,
|
|
new Request("http://test/api/echo", {
|
|
method: "POST",
|
|
body: JSON.stringify({ hi: "there" }),
|
|
headers: { "content-type": "application/json" },
|
|
}),
|
|
);
|
|
expect(await res.json()).toEqual({ received: { hi: "there" } });
|
|
});
|
|
|
|
// --- App-level integration (real in-process server) ------------------------
|
|
|
|
describe("full app", () => {
|
|
let app: Harness;
|
|
beforeAll(async () => {
|
|
app = await createHarness(import.meta.dir + "/..");
|
|
});
|
|
afterAll(() => app?.close());
|
|
|
|
test("home page server-renders translated page and layout text", async () => {
|
|
const res = await app.fetch("/");
|
|
expect(res.status).toBe(200);
|
|
expect(res.headers.get("content-type")).toContain("text/html");
|
|
const html = await res.text();
|
|
expect(html).toContain('<span data-t="nav.home">Home</span>');
|
|
expect(html).toContain('<span data-t="home.title">Hello from WrNexus</span>');
|
|
expect(html).not.toContain('<span data-t="nav.home"></span>');
|
|
});
|
|
|
|
test("home page ships only its active theme stylesheet", async () => {
|
|
const response = await app.fetch("/");
|
|
const html = await response.text();
|
|
const href = html.match(/data-wrnexus-theme href="([^"]+)"/)?.[1];
|
|
expect(href).toContain("/__wrnexus/theme/dark/");
|
|
expect(html).not.toContain('href="/__wrnexus/theme.css');
|
|
const cssResponse = await app.fetch(String(href));
|
|
expect(cssResponse.status).toBe(200);
|
|
const css = await cssResponse.text();
|
|
expect(css.length).toBeLessThan(10_000);
|
|
expect(css).not.toContain("[data-theme=");
|
|
});
|
|
|
|
test("GET /api/hello returns a translated greeting", async () => {
|
|
const res = await app.fetch("/api/hello");
|
|
expect(res.status).toBe(200);
|
|
const body = (await res.json()) as { message: string; lang: string };
|
|
expect(typeof body.message).toBe("string");
|
|
expect(body.lang).toBeTruthy();
|
|
});
|
|
|
|
test("language-server playground renders its real interactive output", async () => {
|
|
const res = await app.fetch("/language-tools");
|
|
expect(res.status).toBe(200);
|
|
const html = await res.text();
|
|
expect(html).toContain("Language server playground");
|
|
expect(html).toContain("computed double:");
|
|
expect(html).toContain('data-text="doubled">0</span>');
|
|
expect(html).toContain('aria-live="polite"');
|
|
});
|
|
|
|
test("login renders real accessible fields without an accidental Card component", async () => {
|
|
const res = await app.fetch("/login?next=/dashboard");
|
|
expect(res.status).toBe(200);
|
|
const html = await res.text();
|
|
expect(html).toContain('name="email"');
|
|
expect(html).toContain('name="password"');
|
|
expect(html).toContain('autocomplete="current-password"');
|
|
expect(html).not.toContain("Card title");
|
|
});
|
|
|
|
test("shared UI foundations render on demand instead of shipping in ui.css", async () => {
|
|
const fieldResponse = await app.fetch("/modal");
|
|
expect(fieldResponse.status).toBe(200);
|
|
const fieldHtml = await fieldResponse.text();
|
|
expect(fieldHtml).toContain('data-wrnexus-style-owner="FieldStyles"');
|
|
expect(fieldHtml).toContain('data-wrnexus-style-owner="ComponentBaseStyles"');
|
|
const selectResponse = await app.fetch("/test");
|
|
expect(selectResponse.status).toBe(200);
|
|
expect(await selectResponse.text()).toContain('data-wrnexus-style-owner="SelectStyles"');
|
|
});
|
|
|
|
test("platform showcase uses the public layout and styled reactive primitives", async () => {
|
|
const res = await app.fetch("/platform-showcase");
|
|
expect(res.status).toBe(200);
|
|
const html = await res.text();
|
|
expect(html).toContain("WrNexus");
|
|
expect(html).toContain('data-wrn-dynamic-component="Admin"');
|
|
expect(html).toContain('data-wrn-portal="#showcase-modal"');
|
|
expect(html).toContain("platform-showcase");
|
|
});
|
|
|
|
test("async data page renders its server loader without a cache failure", async () => {
|
|
const res = await app.fetch("/async-data");
|
|
expect(res.status).toBe(200);
|
|
const html = await res.text();
|
|
expect(html).toContain("Rendered by the server loader");
|
|
expect(html).toContain("Loading the browser profile");
|
|
});
|
|
});
|