Files
MealQuest/docs/lighthouse/2026-10-11/installability-check.mjs
T
dev_agentandClaude Opus 5.5 b19dad7b40
CI / ci (push) Successful in 11m4s
CI / ci (pull_request) Successful in 11m16s
Add launch evidence: Lighthouse and installability on production (Maestro 2c8f8887)
Record the 2026-10-11 Lighthouse 13.5.0 mobile run against production
mealquest:743a155 (signed in): accessibility 100 on Home, Plan, Discover
and Dinner detail, meeting the >= 95 gate; performance recorded, not gated.
Record the Playwright installability probe (PASS on all four pages), used
because Lighthouse 10+ removed the PWA category.

The session cookie Lighthouse echoed into configSettings.extraHeaders is
redacted from the committed reports.

Maestro task: 2c8f8887-85e5-4734-b90f-9678f17d3778

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:12:55 -04:00

32 lines
2.4 KiB
JavaScript

import { chromium } from 'playwright';
const ck = process.env.CK;
const pages = [['home','/'],['plan','/plan'],['discover','/discover'],['dinner','/dinner/01M4PKT9QYA6C4JCF67XE7RA6R']];
const browser = await chromium.launch({ executablePath: '/usr/bin/google-chrome', args: ['--no-sandbox'] });
const ctx = await browser.newContext({ userAgent: 'Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Mobile Safari/537.36' });
await ctx.addCookies([{ name: '__Host-mealquest_session', value: ck, url: 'https://mealquest.cubecraftlabs.com/' }]);
let fail = 0;
for (const [name, path] of pages) {
const page = await ctx.newPage();
page.on('console', m => { if (/sw|service|error/i.test(m.text())) console.log(' [console]', m.text().slice(0,120)); });
page.on('pageerror', e => console.log(' [pageerror]', String(e).slice(0,120)));
await page.goto('https://mealquest.cubecraftlabs.com' + path, { waitUntil: 'networkidle', timeout: 30000 });
const href = await page.evaluate(() => document.querySelector('link[rel="manifest"]')?.href || null);
const sw = await page.evaluate(async () => {
try { await navigator.serviceWorker.ready; } catch (e) { return { err: String(e) }; }
const regs = await navigator.serviceWorker.getRegistrations();
return { registrations: regs.length, scope: regs[0]?.scope || null, state: regs[0]?.active?.state || null, controlling: !!navigator.serviceWorker.controller };
});
const manifest = href ? await page.evaluate(async (h) => { const r = await fetch(h); return { status: r.status, body: await r.json() }; }, href) : { status: 'no link' };
const m = manifest.body || {};
const icons = (m.icons || []).map(i => i.sizes + (i.purpose ? ':' + i.purpose : ''));
const okSw = sw.registrations > 0;
const okIcons = icons.some(s => s.startsWith('192')) && icons.some(s => s.startsWith('512')) && icons.some(s => s.includes('maskable'));
const okManifest = m.name === 'MealQuest' && m.display === 'standalone' && !!m.start_url && okIcons;
const pass = okSw && okManifest;
if (!pass) fail++;
console.log(`${name}: url=${page.url().replace('https://mealquest.cubecraftlabs.com','')} manifest=${manifest.status} name=${m.name} display=${m.display} icons=[${icons.join(',')}] SW=${JSON.stringify(sw)} => ${pass ? 'INSTALLABLE' : 'NOT INSTALLABLE'}`);
await page.close();
}
await browser.close();
process.exit(fail ? 1 : 0);