ci(e2e): run the tagged specs under WebKit as the Safari/iOS proxy (#221)
CI / Build & Quality Checks (push) Successful in 3m11s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 18s
CI / Trigger Desktop Build (push) Successful in 17s
CI / Playwright smoke (e2e) (push) Failing after 8m58s
CI / Build & Quality Checks (push) Successful in 3m11s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 18s
CI / Trigger Desktop Build (push) Successful in 17s
CI / Playwright smoke (e2e) (push) Failing after 8m58s
Two new Playwright projects — 'webkit' (Desktop Safari) for tests tagged @webkit and 'iphone' (iPhone 14 descriptor) for @ios — covering boot, login + send/receive, the thread panel and the gallery lightbox. The CI e2e job installs webkit next to chromium. WebKit reports handled fetch failures (well-known probes, a wasm fetch cut short by our own navigation) as page errors with its own wording, so the benign allowlist now applies to page errors as well. Locally: 8/8 green twice in a row against the local Synapse. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -275,8 +275,11 @@ jobs:
|
||||
sleep $((attempt * 15))
|
||||
done
|
||||
|
||||
- name: Install Playwright Chromium
|
||||
run: npx playwright install --with-deps chromium
|
||||
# [Gitea #221] WebKit too — Playwright's Safari/iOS proxy for the tagged
|
||||
# subset (see playwright.config.ts projects). `--with-deps` pulls the
|
||||
# GTK/GStreamer libraries WebKit needs on the runner.
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps chromium webkit
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
@@ -57,6 +57,8 @@ Browser-level smoke tests under `e2e/` (config: `playwright.config.ts`). They bo
|
||||
| **E2EE composer** (gated) | `e2e/e2ee-composer.spec.ts` | only when `E2E_HOMESERVER`, `E2E_USER`, `E2E_PASSWORD` are all set | password login → `/home/create/` with the End-to-End Encryption switch on (asserts `createRoom` carries `m.room.encryption`) → text message renders → attach a generated JPEG with "Compress image before uploading" ticked, image renders → every `PUT …/rooms/*/send/*` was `m.room.encrypted` with `ciphertext` and no plaintext `body` / `url` / `file` / `mxc://` |
|
||||
| **Local homeserver** (Gitea #220) | `e2e/local-homeserver.spec.ts` | when a Synapse answers at `E2E_LOCAL_HS` (default `http://localhost:8008`); the CI `e2e` job starts one with `scripts/dev-homeserver.sh start` + `dev-seed.py`, locally run the same two commands | registers its own `e2e_alice_*`/`e2e_bob_*` users and rooms over the CS API, then drives the built client: login + send/receive, own message scrolls into view (#212), `/kick` failure toast (#216), upload 413 sentence (#213), forward provenance header, thread panel + drawer at 1400 px (#218), timeline image → gallery lightbox (#219), clock-skew banner via `page.clock` (#158), status save under the presence rate limit (#226), long-press action sheet on a Pixel 7 emulation (#166). Helpers in `e2e/localHs.ts`; add a test here whenever a fix was reproduced with a scratch Playwright script |
|
||||
|
||||
**Browsers** (Gitea #221): everything runs under Chromium; the tests tagged `@webkit` (boot, login + send/receive, thread panel, lightbox) also run under Playwright's WebKit as desktop Safari, and those tagged `@ios` under the `iPhone 14` descriptor — the closest CI gets to Safari/iOS. It catches WebKit-only breakage (CSS, `dvh`, IndexedDB, media decode) but does not emulate the on-screen keyboard or Home-Screen install; a real iPhone pass (#166/#199) stays manual. Locally: `npx playwright install --with-deps webkit` once, then `npx playwright test --project=webkit --project=iphone`. WebKit words handled fetch failures as page errors (`TypeError: Load failed`, `due to access control checks`), so the allowlist in `e2e/helpers.ts` applies to page errors too.
|
||||
|
||||
**CI secrets** (Gitea → repo → Settings → Actions → Secrets; the `e2e` job forwards them via `env:`; until they exist the E2EE tier reports `skipped`, the boot tier still runs):
|
||||
|
||||
- `E2E_HOMESERVER` — server name as typed on the login page (e.g. `matrix.example.org`). Must offer `m.login.password`; a next-gen-auth (MAS/OIDC-issuer) server shows only the OIDC button and the tier will fail at the username field.
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import { collectConsole } from './helpers';
|
||||
// `vite preview` (see playwright.config.ts webServer). No homeserver needed.
|
||||
|
||||
test.describe('boot', () => {
|
||||
test('client boots to the login screen without errors', async ({ page }) => {
|
||||
test('client boots to the login screen without errors @webkit @ios', async ({ page }) => {
|
||||
const consoleLog = collectConsole(page);
|
||||
|
||||
await page.goto('/');
|
||||
@@ -24,7 +24,7 @@ test.describe('boot', () => {
|
||||
expect(consoleLog.unexpected(), 'unexpected console/page errors during boot').toEqual([]);
|
||||
});
|
||||
|
||||
test('service worker script is served and registers', async ({ page }) => {
|
||||
test('service worker script is served and registers @webkit @ios', async ({ page }) => {
|
||||
const swResponse = await page.request.get('/sw.js');
|
||||
expect(swResponse.status(), 'GET /sw.js').toBe(200);
|
||||
expect(swResponse.headers()['content-type'] ?? '').toMatch(/javascript/);
|
||||
|
||||
+10
-1
@@ -18,6 +18,11 @@ const BENIGN_CONSOLE_PATTERNS: RegExp[] = [
|
||||
// autodiscovery probes https://localhost/, which is either not listening
|
||||
// (CI) or a self-signed dev server (local calls stack).
|
||||
/ERR_CONNECTION_REFUSED|ERR_CERT_AUTHORITY_INVALID|ERR_SSL_PROTOCOL_ERROR/i,
|
||||
// WebKit's spellings of the same discovery failures (#221).
|
||||
/Unacceptable TLS certificate|Could not connect to the server|TypeError: Load failed/i,
|
||||
// Also a fetch cut short by our own navigation (e.g. the crypto wasm while
|
||||
// the test moves from /home to a room) — WebKit words that the same way.
|
||||
/due to access control checks/i,
|
||||
// React devtools hint in production bundles.
|
||||
/Download the React DevTools/i,
|
||||
];
|
||||
@@ -45,8 +50,12 @@ export function collectConsole(page: Page): ConsoleCollector {
|
||||
return {
|
||||
errors,
|
||||
pageErrors,
|
||||
// WebKit surfaces handled fetch failures (well-known probes) as page
|
||||
// errors rather than console lines, so the allowlist applies to both.
|
||||
unexpected: () => [
|
||||
...pageErrors.map((m) => `pageerror: ${m}`),
|
||||
...pageErrors
|
||||
.filter((m) => !BENIGN_CONSOLE_PATTERNS.some((re) => re.test(m)))
|
||||
.map((m) => `pageerror: ${m}`),
|
||||
...errors.filter((m) => !BENIGN_CONSOLE_PATTERNS.some((re) => re.test(m))),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ test.describe('local homeserver regression', () => {
|
||||
bob = await ensureUser(uniq('e2e_bob_'));
|
||||
});
|
||||
|
||||
test('logs in, opens a room, sends and receives', async ({ page }) => {
|
||||
test('logs in, opens a room, sends and receives @webkit @ios', async ({ page }) => {
|
||||
const console_ = collectConsole(page);
|
||||
const room = await createRoom(alice, 'Regression Room', { invite: [bob.userId] });
|
||||
await joinRoom(bob, room);
|
||||
@@ -140,7 +140,7 @@ test.describe('local homeserver regression', () => {
|
||||
await expect(page.getByText('the original message')).toBeVisible();
|
||||
});
|
||||
|
||||
test('thread panel: opens from the chip and yields the member drawer at 1400px (#218)', async ({
|
||||
test('thread panel: opens from the chip and yields the member drawer at 1400px (#218) @webkit', async ({
|
||||
browser,
|
||||
}) => {
|
||||
const ctx = await browser.newContext({ viewport: { width: 1400, height: 850 } });
|
||||
@@ -169,7 +169,7 @@ test.describe('local homeserver regression', () => {
|
||||
await ctx.close();
|
||||
});
|
||||
|
||||
test('timeline image opens the gallery lightbox (#219)', async ({ page }) => {
|
||||
test('timeline image opens the gallery lightbox (#219) @webkit', async ({ page }) => {
|
||||
const room = await createRoom(alice, 'Lightbox Room');
|
||||
const png = Buffer.from(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "node --import tsx --test $(find src -name '*.test.ts')",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:install": "playwright install chromium",
|
||||
"test:e2e:install": "playwright install chromium webkit",
|
||||
"prepare": "husky",
|
||||
"commit": "git-cz",
|
||||
"postinstall": "node scripts/patch-folds.mjs",
|
||||
|
||||
+10
-1
@@ -25,7 +25,16 @@ export default defineConfig({
|
||||
trace: 'retain-on-failure',
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
|
||||
projects: [
|
||||
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
||||
// [Gitea #221] WebKit as the Safari/iOS proxy. It runs the subset tagged
|
||||
// @webkit / @ios (boot, composer round-trip, thread panel, lightbox): it
|
||||
// catches WebKit-only breakage (CSS, dvh, IndexedDB, media decode) that
|
||||
// Chromium emulation can't, but does not emulate the iOS keyboard or the
|
||||
// Home-Screen install flow.
|
||||
{ name: 'webkit', use: { ...devices['Desktop Safari'] }, grep: /@webkit/ },
|
||||
{ name: 'iphone', use: { ...devices['iPhone 14'] }, grep: /@ios/ },
|
||||
],
|
||||
webServer: {
|
||||
command: `npx vite preview --port ${PORT} --strictPort`,
|
||||
url: BASE_URL,
|
||||
|
||||
Reference in New Issue
Block a user