Every earlier camera proof stood in for the browser with a werift peer. The question that decides whether the feature works is whether a person looking at the fleet page sees a picture, and the web console had never been opened. BROWSER=1 on scripts/e2e-camera.sh now starts the real Nuxt app and drives it in real Chrome (puppeteer-core): sign in, open the fleet page, wait for the camera player, and check the result. Measured against the H2C: the app's own Supabase client signs in, the header reports Live (the telemetry socket authenticated with the real token), the player reports connected, Chrome decodes 1680x1080 frames, playback time advances, the frame has picture content (mean luma 88, stdev 62.8), and the browser's own ICE stats show the selected candidate is a TURN relay. No uncaught page errors. The login page only offers a magic link by email, which a script cannot follow, so the driver signs in with the app's $supabase.client and a password just set on a throwaway user. That means AUTH=supabase: the backend verifies against the real project's JWKS rather than the local dev issuer, with e2e-supabase-user.ts creating or reusing continuum-e2e@example.invalid (a reserved .invalid address) and resetting its password each run. The JSON it prints carries a password and a live token, so the shell captures it and never echoes it. The frame is judged by pixel statistics and no screenshot is kept: it is the inside of someone's workshop, and "is there a picture" does not need one stored. The relay is read from getStats() through a wrapped RTCPeerConnection rather than inferred from configuration. A browser run with fewer than its nine results fails, so a driver that dies quietly cannot read as a pass. puppeteer-core is a devDependency for these scripts alone. Its 22 resolved packages are all Apache-2.0, BSD-3-Clause, ISC or MIT.
55 lines
2.3 KiB
TypeScript
55 lines
2.3 KiB
TypeScript
/**
|
|
* Provisions the real Supabase test user for scripts/e2e-camera.sh and prints
|
|
* one JSON object: { id, email, password, accessToken }.
|
|
*
|
|
* The output carries a password and a live access token, so the caller must
|
|
* capture it into a variable and never echo it. The account is reused rather
|
|
* than piling up one per run: `.invalid` is reserved by RFC 2606, so the
|
|
* address can never belong to a real person, and its password is reset every
|
|
* time to a fresh random one that exists only for this run.
|
|
*
|
|
* Environment: SUPABASE_URL, SUPABASE_SECRET_KEY, SUPABASE_PUBLISHABLE_KEY.
|
|
*/
|
|
const url = process.env.SUPABASE_URL!;
|
|
const secret = process.env.SUPABASE_SECRET_KEY!;
|
|
const publishable = process.env.SUPABASE_PUBLISHABLE_KEY!;
|
|
const email = "continuum-e2e@example.invalid";
|
|
const password = `e2e-${crypto.randomUUID()}`;
|
|
const admin = { apikey: secret, Authorization: `Bearer ${secret}`, "Content-Type": "application/json" };
|
|
|
|
const die = (message: string): never => {
|
|
console.error(`e2e-supabase-user: ${message}`);
|
|
process.exit(1);
|
|
};
|
|
|
|
let id: string | undefined;
|
|
const made = await fetch(`${url}/auth/v1/admin/users`, {
|
|
method: "POST",
|
|
headers: admin,
|
|
body: JSON.stringify({ email, password, email_confirm: true }),
|
|
});
|
|
if (made.ok) {
|
|
id = ((await made.json()) as { id?: string }).id;
|
|
} else {
|
|
const list = (await fetch(`${url}/auth/v1/admin/users?per_page=200`, { headers: admin }).then((r) => r.json())) as {
|
|
users?: { id: string; email: string }[];
|
|
};
|
|
id = list.users?.find((u) => u.email === email)?.id;
|
|
if (!id) die(`could not create or find the test user (HTTP ${made.status})`);
|
|
const reset = await fetch(`${url}/auth/v1/admin/users/${id}`, {
|
|
method: "PUT",
|
|
headers: admin,
|
|
body: JSON.stringify({ password, email_confirm: true }),
|
|
});
|
|
if (!reset.ok) die(`could not reset the test user's password (HTTP ${reset.status})`);
|
|
}
|
|
|
|
const session = (await fetch(`${url}/auth/v1/token?grant_type=password`, {
|
|
method: "POST",
|
|
headers: { apikey: publishable, "Content-Type": "application/json" },
|
|
body: JSON.stringify({ email, password }),
|
|
}).then((r) => r.json())) as { access_token?: string };
|
|
if (!session.access_token || !id) die("the test user could not sign in");
|
|
|
|
console.log(JSON.stringify({ id, email, password, accessToken: session.access_token }));
|