d67e582c03
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m4s
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m39s
The edge caddy 503 carries X-Scrabble-Maintenance during a deploy window, but a user already in the running SPA only sees their calls start failing — the static caddy page catches only a fresh load. Detect that marker (strictly — not any transient 'unavailable', which the Connecting indicator already covers) on the raw ConnectError at the two transport catch sites (unary exec + the live subscribe stream), before toGatewayError discards the response headers, and raise a non-dismissable dimmed overlay that mirrors the static caddy page. It self-clears: a capped-backoff poll (a cheap read, mirroring connection.svelte.ts) lifts it on the first success, and a manual "retry" button forces an immediate re-check — so it can never get stuck. On detection the read retry loop fails fast, so a window doesn't burn the retry budget on every call. - pure detector maintenance.ts (maintenanceRetryMs / parseRetryAfterMs) + unit tests; the store + self-clearing poll in maintenance.svelte.ts (mirrors connection.svelte.ts) - MaintenanceOverlay.svelte (clones Splash's fixed/inset/dimmed shell, non-dismissable), mounted app-global in App.svelte after Coachmark - transport.ts detects + reports at both catch sites, clears on any successful read - i18n RU "Технические работы" / EN "Under maintenance"; a window.__maint mock hook (the mock can't emit a real 503) + a Playwright spec Prod is same-origin (VITE_GATEWAY_URL empty) so the marker header is readable without a CORS expose-header. Verified: pnpm check (0), unit (402), build, e2e (186, Chromium+WebKit).
23 lines
1.4 KiB
TypeScript
23 lines
1.4 KiB
TypeScript
import { expect, test } from './fixtures';
|
|
|
|
// The maintenance overlay is raised in prod by the edge 503 marker (X-Scrabble-Maintenance);
|
|
// the mock transport never produces one, so — like the offline indicator — the e2e drives it
|
|
// through the window.__maint hook (gateway.ts, mock-only). It is app-global (mounted outside
|
|
// the route blocks in App.svelte), so it shows without a session.
|
|
test('maintenance overlay covers the app and lifts on recovery', async ({ page }) => {
|
|
await page.goto('/');
|
|
await expect(page.getByRole('alertdialog')).toHaveCount(0);
|
|
|
|
// A planned deploy window begins: a non-dismissable dimmed overlay covers the app.
|
|
await page.evaluate(() => (window as unknown as { __maint: { on(): void } }).__maint.on());
|
|
const overlay = page.getByRole('alertdialog');
|
|
await expect(overlay).toBeVisible();
|
|
// The retry button is present (EN "Try again" / RU "Повторить" depending on locale).
|
|
await expect(overlay.getByRole('button', { name: /again|Повторить/i })).toBeVisible();
|
|
|
|
// The window ends — in prod the store's poll lifts it on the first successful read; the mock
|
|
// has no probe, so it clears explicitly. The overlay disappears with no page reload.
|
|
await page.evaluate(() => (window as unknown as { __maint: { off(): void } }).__maint.off());
|
|
await expect(page.getByRole('alertdialog')).toHaveCount(0);
|
|
});
|