7dcd62fdd7
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 1m6s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m47s
The deploy that ends a maintenance window may ship a client-incompatible change (wire/schema bump), and the in-session bundle is the old one. On recovery, reload to pick up the fresh client instead of just hiding the overlay. Ordering is safe: the edge maintenance flag (deploy/prod-deploy.sh) spans the WHOLE roll and clears only at script exit — after the gateway (which serves the embedded SPA) has rolled — so the edge 503s everything until the entire deploy is live; recovery therefore always serves the new SPA. A window.__maint.recover() hook + e2e cover the reload.
39 lines
2.2 KiB
TypeScript
39 lines
2.2 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);
|
|
});
|
|
|
|
test('recovery reloads the SPA to pick up the fresh client', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.evaluate(() => (window as unknown as { __maint: { on(): void } }).__maint.on());
|
|
await expect(page.getByRole('alertdialog')).toBeVisible();
|
|
|
|
// Real recovery reloads the page (the deploy may have shipped an incompatible client, and the
|
|
// running bundle is the old one). Wait for the forced navigation, then confirm the app
|
|
// re-bootstrapped: the overlay is gone (the store reset by the reload) and the login is back.
|
|
await Promise.all([
|
|
page.waitForEvent('load'),
|
|
page.evaluate(() => (window as unknown as { __maint: { recover(): void } }).__maint.recover()),
|
|
]);
|
|
await expect(page.getByRole('alertdialog')).toHaveCount(0);
|
|
await expect(page.getByRole('button', { name: /guest/i })).toBeVisible();
|
|
});
|