0d06e6f7b7
Spec CAPTURE.md §Browsers: Phone iOS Safari / Android Chrome, PC Chrome / Edge / Firefox, others hard stop. - browserGate.js: UA detection + allowlist (pure, DOM-free) - +layout.svelte: renders hard-stop panel when unsupported - iPadOS 13+ reports as Macintosh — added Safari rule (Version/ + Safari/ without Chrome/) so iPad Safari is not blocked - 27 unit tests incl. iPad Safari regression - 107/107 full suite green
110 lines
4.7 KiB
JavaScript
110 lines
4.7 KiB
JavaScript
import { test, expect, devices } from '@playwright/test';
|
|
|
|
// UAT-3: Browser gate (hard stop) — e2e.
|
|
// Spec (CAPTURE.md §Browsers):
|
|
// Phone: iOS Safari, Android Chrome.
|
|
// PC: Chrome, Edge, Firefox.
|
|
// Other clients get a hard stop.
|
|
//
|
|
// Acceptance: opening in an unsupported browser (e.g. Opera, Samsung
|
|
// Internet) shows a hard-stop message, NOT the app.
|
|
//
|
|
// The app itself must not render (no <slot> content, no "Milestone 1
|
|
// — Capture" text) in an unsupported browser; the hard-stop page lists
|
|
// the spec browsers instead.
|
|
|
|
const DESKTOP_CHROME_UA =
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
|
|
const DESKTOP_OPERA_UA =
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 OPR/110.0.0.0';
|
|
const DESKTOP_EDGE_UA =
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0';
|
|
const DESKTOP_FIREFOX_UA =
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0';
|
|
const IOS_SAFARI_UA =
|
|
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/605.1.15';
|
|
const ANDROID_CHROME_UA =
|
|
'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6331.115 Mobile Safari/537.36';
|
|
const SAMSUNG_INTERNET_UA =
|
|
'Mozilla/5.0 (Linux; Android 14; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/24.0 Chrome/124.0.0.0 Mobile Safari/537.36';
|
|
|
|
async function openApp(page, ua) {
|
|
await page.setExtraHTTPHeaders({ 'User-Agent': ua });
|
|
await page.goto('/');
|
|
// The gate runs on client hydration; wait a beat for the DOM to
|
|
// settle on whichever branch rendered.
|
|
await page.waitForTimeout(300);
|
|
}
|
|
|
|
// The app renders when the gate passes: the layout's <main> is present
|
|
// and the capture UI (Add Photo heading) is inside it.
|
|
async function expectApp(page) {
|
|
await expect(page.locator('main')).toBeVisible();
|
|
await expect(page.locator('main')).toContainText('Add Photo');
|
|
// No hard-stop page.
|
|
await expect(page.locator('.gate')).toHaveCount(0);
|
|
}
|
|
|
|
// The hard stop renders when the gate fails: the .gate box is present,
|
|
// the app's <main> is NOT, and the spec browsers are listed.
|
|
async function expectHardStop(page) {
|
|
await expect(page.locator('.gate h1')).toHaveText('Unsupported browser');
|
|
await expect(page.locator('main')).toHaveCount(0);
|
|
const gate = page.locator('.gate');
|
|
await expect(gate).toContainText('iOS Safari');
|
|
await expect(gate).toContainText('Android Chrome');
|
|
await expect(gate).toContainText('Chrome');
|
|
await expect(gate).toContainText('Edge');
|
|
await expect(gate).toContainText('Firefox');
|
|
}
|
|
|
|
test('UAT-3: app renders in desktop Chrome (supported)', async ({ page }) => {
|
|
await openApp(page, DESKTOP_CHROME_UA);
|
|
await expectApp(page);
|
|
});
|
|
|
|
test('UAT-3: app renders in desktop Edge (supported)', async ({ page }) => {
|
|
await openApp(page, DESKTOP_EDGE_UA);
|
|
await expectApp(page);
|
|
});
|
|
|
|
test('UAT-3: app renders in desktop Firefox (supported)', async ({ page }) => {
|
|
await openApp(page, DESKTOP_FIREFOX_UA);
|
|
await expectApp(page);
|
|
});
|
|
|
|
test('UAT-3: app renders in iOS Safari (supported)', async ({ page, context }) => {
|
|
await context.setExtraHTTPHeaders({ 'User-Agent': IOS_SAFARI_UA });
|
|
await page.goto('/');
|
|
await page.waitForTimeout(300);
|
|
await expectApp(page);
|
|
});
|
|
|
|
test('UAT-3: app renders in Android Chrome (supported)', async ({ page, context }) => {
|
|
await context.setExtraHTTPHeaders({ 'User-Agent': ANDROID_CHROME_UA });
|
|
await page.goto('/');
|
|
await page.waitForTimeout(300);
|
|
await expectApp(page);
|
|
});
|
|
|
|
test('UAT-3: hard stop in desktop Opera (unsupported)', async ({ page }) => {
|
|
await openApp(page, DESKTOP_OPERA_UA);
|
|
await expect(page.locator('.gate h1')).toHaveText('Unsupported browser');
|
|
// The app must NOT render.
|
|
await expect(page.locator('main')).toHaveCount(0);
|
|
// The hard-stop page lists the spec browsers.
|
|
await expect(page.locator('.gate')).toContainText('iOS Safari');
|
|
await expect(page.locator('.gate')).toContainText('Android Chrome');
|
|
await expect(page.locator('.gate')).toContainText('Chrome');
|
|
await expect(page.locator('.gate')).toContainText('Edge');
|
|
await expect(page.locator('.gate')).toContainText('Firefox');
|
|
});
|
|
|
|
test('UAT-3: hard stop in Samsung Internet (unsupported)', async ({ page, context }) => {
|
|
await context.setExtraHTTPHeaders({ 'User-Agent': SAMSUNG_INTERNET_UA });
|
|
await page.goto('/');
|
|
await page.waitForTimeout(300);
|
|
await expect(page.locator('.gate h1')).toHaveText('Unsupported browser');
|
|
await expect(page.locator('main')).toHaveCount(0);
|
|
});
|