Initial Commit
This commit is contained in:
86
apps/demo/e2e/smoke.test.ts
Normal file
86
apps/demo/e2e/smoke.test.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Smoke test — boots the built app and asserts:
|
||||
*
|
||||
* 1. `/api/health` returns 200 JSON with `ok: true`.
|
||||
* 2. `/` redirects to `/<locale>/home`.
|
||||
* 3. Each public page (`/en/home`, `/en/about-us`,
|
||||
* `/en/contact-us`, `/en/privacy-policy`, `/en/terms-of-service`)
|
||||
* returns 200 with an HTML body containing the SPA root element.
|
||||
*
|
||||
* Run with `bun run e2e/smoke.test.ts`. Requires `bun run build`
|
||||
* to have produced `dist/client/index.html` first.
|
||||
*/
|
||||
import { spawn } from "node:child_process";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
|
||||
const PORT = 3399;
|
||||
const BASE = `http://localhost:${PORT}`;
|
||||
|
||||
async function waitForReady(): Promise<void> {
|
||||
for (let i = 0; i < 30; i++) {
|
||||
try {
|
||||
const res = await fetch(`${BASE}/api/health`);
|
||||
if (res.ok) return;
|
||||
} catch {
|
||||
// not up yet
|
||||
}
|
||||
await delay(500);
|
||||
}
|
||||
throw new Error("server failed to start within 15s");
|
||||
}
|
||||
|
||||
function assert(condition: unknown, message: string): asserts condition {
|
||||
if (!condition) throw new Error(`SMOKE FAIL: ${message}`);
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const child = spawn("bun", ["run", "src/server/index.ts"], {
|
||||
env: { ...process.env, PORT: String(PORT), NODE_ENV: "test" },
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
try {
|
||||
await waitForReady();
|
||||
|
||||
const health = await (await fetch(`${BASE}/api/health`)).json();
|
||||
// `@suaveplan/api`'s `registerHealthRoutes` emits
|
||||
// `{ status, version, state, timestamp }` — accept either the new
|
||||
// shape (`status: "healthy"`) or the legacy `{ ok: true }` shape so
|
||||
// the smoke remains forward-compatible if the route is rewritten.
|
||||
const healthy = health.status === "healthy" || health.ok === true;
|
||||
assert(healthy, `health probe not healthy: ${JSON.stringify(health)}`);
|
||||
|
||||
const root = await fetch(`${BASE}/`, { redirect: "manual" });
|
||||
// Server sends the SPA shell, client-side router handles the redirect.
|
||||
// We accept either a 200 (shell) or a 3xx redirect.
|
||||
assert(root.status === 200 || (root.status >= 300 && root.status < 400),
|
||||
`/ expected 200 or 3xx, got ${root.status}`);
|
||||
|
||||
const paths = [
|
||||
"/en/home",
|
||||
"/en/about-us",
|
||||
"/en/contact-us",
|
||||
"/en/privacy-policy",
|
||||
"/en/terms-of-service",
|
||||
];
|
||||
for (const path of paths) {
|
||||
const res = await fetch(`${BASE}${path}`);
|
||||
assert(res.ok, `${path} returned ${res.status}`);
|
||||
const body = await res.text();
|
||||
assert(body.includes('id="root"'), `${path} body missing #root`);
|
||||
}
|
||||
|
||||
// Auth endpoint reachable.
|
||||
const session = await fetch(`${BASE}/api/auth/get-session`);
|
||||
assert(session.status === 200, `/api/auth/get-session status ${session.status}`);
|
||||
|
||||
console.log("SMOKE OK");
|
||||
} finally {
|
||||
child.kill("SIGTERM");
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user