/** * The P1/A1 camera in the real web console, in a real Chrome, for * scripts/e2e-camera-snapshot.sh (BROWSER=1). Signs in the same way as * e2e-camera-browser.ts. Reads pixel statistics and keeps no screenshot: the * frame is the inside of someone's workshop. * * Results are `oklabel` / `FAILEDlabel`. * Environment: APP_URL, SUPABASE_E2E_EMAIL, SUPABASE_E2E_PASSWORD, CHROME_BIN. */ import puppeteer from "puppeteer-core"; const appUrl = process.env.APP_URL!; const email = process.env.SUPABASE_E2E_EMAIL!; const password = process.env.SUPABASE_E2E_PASSWORD!; const chromeBin = process.env.CHROME_BIN ?? "/usr/bin/google-chrome"; let failed = false; const say = (good: boolean, label: string, detail = "") => { if (!good) failed = true; console.log(`${good ? "ok" : "FAILED"}\t${label}${detail ? ` (${detail})` : ""}`); }; const browser = await puppeteer.launch({ executablePath: chromeBin, headless: true, args: ["--no-sandbox", "--disable-dev-shm-usage"], }); try { const page = await browser.newPage(); const pageErrors: string[] = []; page.on("pageerror", (err) => pageErrors.push(String((err as Error).message ?? err).slice(0, 160))); await page.goto(`${appUrl}/login`, { waitUntil: "domcontentloaded", timeout: 120_000 }); await page.waitForFunction( () => typeof (window as any).useNuxtApp === "function" && Boolean((window as any).useNuxtApp().$supabase), { timeout: 120_000 }, ); const signInError = await page.evaluate( async (e: string, p: string) => { const { client } = (window as any).useNuxtApp().$supabase; const { error } = await client.auth.signInWithPassword({ email: e, password: p }); return error ? String(error.message) : null; }, email, password, ); say(signInError === null, "the app's own Supabase client signs the user in", signInError ?? ""); if (signInError) process.exit(1); await page.goto(`${appUrl}/`, { waitUntil: "domcontentloaded", timeout: 60_000 }); const stayed = await page.waitForSelector(".fleet", { timeout: 60_000 }).then(() => true).catch(() => false); say(stayed && !page.url().includes("/login"), "the fleet page loads and does not bounce back to /login"); const live = await page .waitForFunction(() => document.querySelector(".app-shell__status.is-online") !== null, { timeout: 30_000 }) .then(() => true) .catch(() => false); say(live, "the header reports Live"); const shown = await page .waitForFunction(() => document.querySelector(".snapshot-view__status.is-connected") !== null, { timeout: 60_000 }) .then(() => true) .catch(() => false); const status = await page.evaluate( () => document.querySelector(".snapshot-view__status")?.textContent?.trim() ?? "no snapshot view rendered", ); say(shown, "the snapshot view reports connected", `status: ${status}`); say( (await page.$(".webrtc-view")) === null, "no WebRTC player is rendered for a snapshot camera", ); if (shown) { // Two different frames, a few seconds apart: the picture is refreshing. const result = await page.evaluate(async () => { const img = document.querySelector(".snapshot-view img")!; const first = img.src; const t0 = Date.now(); while (img.src === first && Date.now() - t0 < 15_000) await new Promise((r) => setTimeout(r, 250)); const refreshed = img.src !== first; const canvas = document.createElement("canvas"); canvas.width = 64; canvas.height = 36; const ctx = canvas.getContext("2d")!; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); const px = ctx.getImageData(0, 0, canvas.width, canvas.height).data; let sum = 0; let sumSq = 0; const n = px.length / 4; for (let i = 0; i < px.length; i += 4) { const y = 0.299 * px[i] + 0.587 * px[i + 1] + 0.114 * px[i + 2]; sum += y; sumSq += y * y; } const mean = sum / n; return { refreshed, width: img.naturalWidth, height: img.naturalHeight, mean, stdev: Math.sqrt(Math.max(0, sumSq / n - mean * mean)), }; }); say(result.width > 0, "Chrome decoded the JPEG", `${result.width}x${result.height}`); say(result.refreshed, "the picture refreshes by itself"); say(result.stdev > 2, "the frame has picture content, not black or one flat colour", `mean luma ${result.mean.toFixed(0)}, stdev ${result.stdev.toFixed(1)}`); } say(pageErrors.length === 0, "the page raised no uncaught errors", pageErrors.slice(0, 2).join(" | ")); } finally { await browser.close(); } process.exit(failed ? 1 : 0);