Skip to content

Commit 583ba67

Browse files
test(e2e): settings watchdog boots on a hung settings DB
Tier-3 Playwright spec for the restore watchdog. Stubs indexedDB.open for the settings DB (via addInitScript) to return a request that never settles, simulating an open blocked by another tab's versionchange. Asserts the app shell stays interactive (load-model button visible/enabled) and that the watchdog fires and logs its timeout, which is emitted in the same branch that calls setSettingsLoaded(true), so it proves the app booted on defaults rather than hanging on the dead DB. Needs no model weights, so it runs in ~10s. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 54003a1 commit 583ba67

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

test/e2e/settings-watchdog.spec.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Tier-3 E2E: startup settings restore must not hang the app when the settings
2+
// IndexedDB never opens. This happens for real when another tab holds a
3+
// versionchange that blocks our open request (the open just sits there, firing
4+
// neither success nor error), which would otherwise leave the app wedged on an
5+
// unconfigured state forever. The restore watchdog (App.jsx,
6+
// SETTINGS_LOAD_TIMEOUT_MS) must give up, log it, and boot on defaults.
7+
//
8+
// We simulate the blocked DB by stubbing indexedDB.open for the settings DB so
9+
// its open request never settles. Unlike transcription.spec.js this needs no
10+
// model weights, so it is quick. Built with Claude Code.
11+
12+
import { test, expect } from '@playwright/test';
13+
14+
const SETTINGS_DB = 'parakeetweb-settings-db';
15+
16+
test('boots on defaults when the settings DB open hangs', async ({ page }) => {
17+
// Before any app code runs, make the settings DB open hang forever: return a
18+
// request object whose onsuccess/onerror/onupgradeneeded are never fired (a
19+
// plain object accepts the assignments openIdb makes but invokes nothing), so
20+
// the open() promise never settles. Other DBs open normally.
21+
await page.addInitScript((dbName) => {
22+
const realOpen = indexedDB.open.bind(indexedDB);
23+
indexedDB.open = function (name, version) {
24+
if (name === dbName) return {};
25+
return realOpen(name, version);
26+
};
27+
}, SETTINGS_DB);
28+
29+
const warnings = [];
30+
page.on('console', (m) => { if (m.type() === 'warning') warnings.push(m.text()); });
31+
32+
await page.goto('/');
33+
34+
// The app shell renders immediately; confirm it is interactive (the model-load
35+
// button is present and clickable) rather than stuck on a blank screen.
36+
const loadBtn = page.locator('[data-umami-event="load_model_button"]');
37+
await expect(loadBtn).toBeVisible({ timeout: 15000 });
38+
await expect(loadBtn).toBeEnabled();
39+
40+
// The watchdog must fire and boot on defaults. Its warning is emitted in the
41+
// same branch that calls setSettingsLoaded(true), so seeing it is proof the
42+
// timeout path ran and the app booted instead of hanging on the dead DB.
43+
await expect
44+
.poll(() => warnings.some((w) => w.includes('Settings restore timed out')), {
45+
timeout: 20000,
46+
message: 'expected the settings-restore watchdog to fire and log a timeout',
47+
})
48+
.toBe(true);
49+
});

0 commit comments

Comments
 (0)