diff --git a/frontend/e2e/README.md b/frontend/e2e/README.md index 5c7c6bb3..ea763eed 100644 --- a/frontend/e2e/README.md +++ b/frontend/e2e/README.md @@ -17,6 +17,16 @@ siftlode e2e-seed # (re)seed the test account only, no tests The localdev stack must be up (`siftlode dev`). `global.setup.ts` reseeds the account and logs in once, sharing the session via `e2e/.auth/state.json` (git-ignored). +**Browsers** — every flow runs in three projects: `chromium` (Desktop Chrome), `webkit` (Desktop +Safari) and `ipad` (iPad gen 7 landscape: 1080×810, touch, mobile). Install the engines once per +machine with `npx playwright install chromium webkit`; run one project with +`npx playwright test --project=ipad`. WebKit catches what every iOS browser inherits from the engine +(JS, CSS/layout, event order). It does **not** reproduce iPad media refusals: Playwright's WebKit +decodes through the host OS (Media Foundation on Windows, GStreamer on Linux), and measured on +2026-09-14 it played an mp3-in-mp4 and an AV1 mp4 that a real iPad refuses. Check a media format +on a real iPad against the dev stack over the LAN (`http://:8080`, e.g. a +login-free `/watch/` share link). + **Target** — `PW_BASE_URL` (default `http://127.0.0.1:8080`): - **`:8080`** (baked SPA) is authoritative and same-origin. After a **frontend change you must diff --git a/frontend/e2e/global.setup.ts b/frontend/e2e/global.setup.ts index 5c40de1a..9dfa571b 100644 --- a/frontend/e2e/global.setup.ts +++ b/frontend/e2e/global.setup.ts @@ -67,13 +67,17 @@ setup("seed + authenticate", async ({ page }) => { const me = await (await page.request.get("/api/me")).json(); expect(me.authenticated, "session not established after login").toBeTruthy(); - // 4. Pin the UI locale and suppress the first-login onboarding wizard (a full-screen overlay + // 4. Pin the UI locale, suppress the first-login onboarding wizard (a full-screen overlay // that would intercept every click — the test account has no YouTube connection, so it - // auto-opens). The dismissed flag is per-account: `siftlode.onboarding.dismissed.`. + // auto-opens), and opt the account out of starting on Home. Since E24 a bare URL lands on + // Home, and the feed, channel and player flows this net locks all open "/" expecting the + // feed; the opt-out is the one Settings → Appearance → Startup writes. Both flags are + // per-account: `siftlode.onboarding.dismissed.`, `siftlode.startOnHome.`. await page.goto("/"); await page.evaluate((id: number) => { localStorage.setItem("siftlode.lang", "en"); localStorage.setItem(`siftlode.onboarding.dismissed.${id}`, "1"); + localStorage.setItem(`siftlode.startOnHome.${id}`, "0"); }, me.id); mkdirSync(dirname(STORAGE), { recursive: true }); await page.context().storageState({ path: STORAGE }); diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index 5aa27308..45b26084 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -47,5 +47,19 @@ export default defineConfig({ use: { ...devices["Desktop Chrome"], storageState: STORAGE_STATE }, dependencies: ["setup"], }, + // WebKit catches engine-level differences (JS, CSS/layout, event order) that every iOS browser + // inherits. It does NOT reproduce iPad MEDIA refusals: Playwright's WebKit decodes through the + // host OS (Media Foundation on Windows, GStreamer on Linux), and measured on 2026-09-14 it played + // an mp3-in-mp4 and an AV1 mp4 that iPad refuses. Media checks need a real iPad. + { + name: "webkit", + use: { ...devices["Desktop Safari"], storageState: STORAGE_STATE }, + dependencies: ["setup"], + }, + { + name: "ipad", + use: { ...devices["iPad (gen 7) landscape"], storageState: STORAGE_STATE }, + dependencies: ["setup"], + }, ], });