adventure-web/src/app.test.tsx
nholtware ae7bc89a82
All checks were successful
/ checks (push) Successful in 41s
/ deploy (push) Successful in 4s
feat: passport-local authentication, story ownership
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 13:33:51 -06:00

98 lines
3 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import { createMemoryRouter, RouterProvider } from "react-router";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { routes } from "./routes";
// Flags come from the Unleash SDK; tests control them directly.
const flagsOn = vi.hoisted(() => new Set<string>());
vi.mock("@unleash/proxy-client-react", () => ({
FlagProvider: ({ children }: { children: React.ReactNode }) => children,
useFlag: (name: string) => flagsOn.has(name),
useVariant: () => ({ name: "disabled", enabled: false }),
}));
function renderAt(path: string) {
const router = createMemoryRouter(routes, { initialEntries: [path] });
return render(<RouterProvider router={router} />);
}
beforeEach(() => {
flagsOn.clear();
window.__APP_CONFIG__ = { API_BASE_URL: "/api", APP_NAME: "test-app" };
});
afterEach(() => {
vi.unstubAllGlobals();
});
describe("app", () => {
it("library lists published stories and links to play", async () => {
vi.stubGlobal(
"fetch",
vi.fn(
async () =>
new Response(
JSON.stringify({
stories: [
{
id: "s1",
title: "Cave",
description: "Dark",
nodeCount: 3,
published: true,
},
],
}),
),
),
);
renderAt("/");
expect(
await screen.findByRole("heading", { name: "Cave" }),
).toBeInTheDocument();
expect(
screen.getAllByRole("link", { name: "Play" }).at(-1)!,
).toHaveAttribute("href", "/play/s1");
});
it("library shows a builder prompt when empty", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () => new Response(JSON.stringify({ stories: [] }))),
);
renderAt("/");
expect(
await screen.findByText(/No published stories yet/),
).toBeInTheDocument();
});
it("status fetches / and /readyz under API_BASE_URL and renders the JSON", async () => {
const fetchMock = vi.fn(async (url: string) => {
const body =
url === "/api/readyz" ? { status: "ready" } : { service: "svc" };
return new Response(JSON.stringify(body), { status: 200 });
});
vi.stubGlobal("fetch", fetchMock);
renderAt("/status");
expect(await screen.findByText(/GET \/readyz/)).toBeInTheDocument();
expect(screen.getByText(/"status": "ready"/)).toBeInTheDocument();
expect(screen.getByText(/"service": "svc"/)).toBeInTheDocument();
// Layout also asks /auth/me who is signed in; that's not what's under test.
expect(
fetchMock.mock.calls.map((c) => c[0]).filter((u) => u !== "/api/auth/me"),
).toEqual(["/api/", "/api/readyz"]);
});
it("status shows the error page when the backend is unreachable", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async () => {
throw new Error("network down");
}),
);
renderAt("/status");
expect(await screen.findByRole("alert")).toHaveTextContent("network down");
});
});