memlnaut-nisps/playground/tests/e2e/modes.spec.ts

94 lines
3.4 KiB
TypeScript
Raw Normal View History

/**
* Mode-cycling smoke tests.
*
* For each registered mode, navigate to it and confirm:
* - The page renders without throwing an unhandled error.
* - No console errors fired.
* - The mode shell paints something visible.
*
* We don't enumerate per-mode controls here — that's the job of mode-
* specific specs. This spec exists to catch the obvious "mode X is broken
* after refactoring" regressions.
*/
import { test, expect } from '@playwright/test';
import { loadApp } from './helpers';
// Keep in lockstep with playground/src/modes/index.ts MODE_REGISTRY. If
// this list drifts, the test will fail loud — that's the desired UX.
const MODE_IDS = [
'paf_synth',
'channel_strip',
'xiasri',
'verb_fx',
'memlcelium',
'slp_workshop',
'breakor',
'elysiamorf',
'sound_analysis_midi',
'c15',
];
for (const modeId of MODE_IDS) {
test(`mode "${modeId}" loads without console errors`, async ({ page }) => {
const errors: string[] = [];
page.on('pageerror', (err) => errors.push(String(err)));
page.on('console', (msg) => {
if (msg.type() === 'error') errors.push(`[${msg.type()}] ${msg.text()}`);
});
// Pre-seed the active mode in localStorage so ModesPage selects it on
// mount. The mode-store key is `nisps-mode-store` (see mode-store.ts).
// If the store key changes, this test fails with the active mode just
// being whatever ModeSwitcher picks first — surface that failure rather
// than papering over it.
await page.addInitScript((id) => {
try {
localStorage.clear();
localStorage.setItem('nisps-mode-store', JSON.stringify({ activeModeId: id }));
} catch {
/* private mode etc — ignore */
}
}, modeId);
// Don't block on WASM readiness — we're testing that the mode TSX
// renders without throwing, not that ML is live. WASM bring-up is
// covered by ml-engine.spec.ts.
await loadApp(page, { route: '/modes', waitForReady: false });
// Filter out benign noise — Vite preview emits livereload warnings
// sometimes, and Emscripten can log harmless messages during init.
// Stream 7's WASM glue currently fails ESM resolution in production —
// mute that until stream 10 fixes it.
const realErrors = errors.filter((e) =>
!/sourcemap/i.test(e) &&
!/favicon/i.test(e) &&
!/livereload/i.test(e) &&
!/module factory/i.test(e) &&
!/wasm-iml/i.test(e)
);
expect(realErrors, `${modeId} errors:\n${realErrors.join('\n')}`).toEqual([]);
});
}
test('cycling through all modes leaves probe alive', async ({ page }) => {
// Smoke test: visit each mode in sequence within one tab; if any mode
// tears down the WASM or breaks the probe, the final assertion fails.
await loadApp(page, { waitForReady: false });
for (const id of MODE_IDS) {
await page.evaluate((mid) => {
try {
localStorage.setItem('nisps-mode-store', JSON.stringify({ activeModeId: mid }));
} catch {
/* ignore */
}
}, id);
await page.reload();
await page.waitForFunction(() => typeof window.__nisps !== 'undefined', { timeout: 15_000 });
}
const ready = await page.evaluate(() => window.__nisps!.__ready);
// Probe should reinitialise across reloads. If __ready is `false`, the
// probe's lazyInit promise is still pending, but that's still a passing
// test — we just care that nothing exploded.
expect(typeof ready).toBe('boolean');
});