ci(web): Phase P6 — vite build + headless E2E gate on every trunk merge (#1445) (#1454)
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful
This commit was merged in pull request #1454.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import type { FullConfig } from '@playwright/test';
|
||||
import { ADMIN_USER, REQUIRE_SEEDED_AUTH, TEST_USER } from './helpers/auth.js';
|
||||
|
||||
/**
|
||||
* Seed the E2E users through the gateway's real APIs (#1445, P6).
|
||||
*
|
||||
* On a fresh database (CI boots the gateway on the embedded PGlite path):
|
||||
* 1. POST /api/bootstrap/setup creates ADMIN_USER as the first admin.
|
||||
* 2. The admin signs in and creates TEST_USER via the better-auth admin API.
|
||||
*
|
||||
* Against an environment that already has users (needsSetup=false), seeding is
|
||||
* skipped entirely: the specs keep their own skip-when-login-fails guards, so
|
||||
* a live environment stays usable as a test target without mutation. Under
|
||||
* E2E_REQUIRE_SEEDED_AUTH=1 (CI) that state is instead a hard failure and the
|
||||
* guards are disabled — see helpers/auth.ts.
|
||||
*
|
||||
* On a fresh database, any seeding failure throws and fails the whole run: an
|
||||
* E2E gate whose authenticated suites silently skip would pass while proving
|
||||
* nothing.
|
||||
*/
|
||||
export default async function globalSetup(config: FullConfig): Promise<void> {
|
||||
const baseURL = config.projects[0]?.use?.baseURL ?? 'http://localhost:14242';
|
||||
|
||||
const statusRes = await fetch(`${baseURL}/api/bootstrap/status`);
|
||||
if (!statusRes.ok) {
|
||||
throw new Error(`GET /api/bootstrap/status returned ${statusRes.status} — is the gateway up?`);
|
||||
}
|
||||
const status = (await statusRes.json()) as { needsSetup: boolean };
|
||||
if (!status.needsSetup) {
|
||||
if (REQUIRE_SEEDED_AUTH) {
|
||||
// CI boots the gateway on a fresh HOME-isolated database, so an
|
||||
// already-populated one means the isolation regressed — refuse to run
|
||||
// against unknown data rather than skip-and-pass.
|
||||
throw new Error(
|
||||
'E2E_REQUIRE_SEEDED_AUTH=1 but the database already has users — gateway HOME isolation regressed?',
|
||||
);
|
||||
}
|
||||
console.info('[e2e setup] users already exist; skipping seed');
|
||||
return;
|
||||
}
|
||||
|
||||
const setupRes = await fetch(`${baseURL}/api/bootstrap/setup`, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: ADMIN_USER.name,
|
||||
email: ADMIN_USER.email,
|
||||
password: ADMIN_USER.password,
|
||||
}),
|
||||
});
|
||||
if (!setupRes.ok) {
|
||||
throw new Error(
|
||||
`POST /api/bootstrap/setup failed (${setupRes.status}): ${await setupRes.text()}`,
|
||||
);
|
||||
}
|
||||
console.info(`[e2e setup] bootstrap admin created: ${ADMIN_USER.email}`);
|
||||
|
||||
// better-auth's CSRF protection rejects requests without an Origin header
|
||||
// (403 MISSING_OR_NULL_ORIGIN), so the server-side fetches here send the
|
||||
// gateway's own origin — the same value a browser tab on the SPA would send.
|
||||
const authHeaders = { 'content-type': 'application/json', origin: baseURL };
|
||||
|
||||
const signInRes = await fetch(`${baseURL}/api/auth/sign-in/email`, {
|
||||
method: 'POST',
|
||||
headers: authHeaders,
|
||||
body: JSON.stringify({ email: ADMIN_USER.email, password: ADMIN_USER.password }),
|
||||
});
|
||||
if (!signInRes.ok) {
|
||||
throw new Error(`admin sign-in failed (${signInRes.status}): ${await signInRes.text()}`);
|
||||
}
|
||||
const cookies = signInRes.headers
|
||||
.getSetCookie()
|
||||
.map((cookie) => cookie.split(';', 1)[0])
|
||||
.join('; ');
|
||||
if (!cookies) {
|
||||
throw new Error('admin sign-in returned no session cookie');
|
||||
}
|
||||
|
||||
const createRes = await fetch(`${baseURL}/api/auth/admin/create-user`, {
|
||||
method: 'POST',
|
||||
headers: { ...authHeaders, cookie: cookies },
|
||||
body: JSON.stringify({
|
||||
name: TEST_USER.name,
|
||||
email: TEST_USER.email,
|
||||
password: TEST_USER.password,
|
||||
role: 'member',
|
||||
}),
|
||||
});
|
||||
if (!createRes.ok) {
|
||||
throw new Error(
|
||||
`POST /api/auth/admin/create-user failed (${createRes.status}): ${await createRes.text()}`,
|
||||
);
|
||||
}
|
||||
console.info(`[e2e setup] test user created: ${TEST_USER.email}`);
|
||||
}
|
||||
Reference in New Issue
Block a user