tts-voice-creator-clone-and.../tests/_guard.js
mARTin-B78 e8c0df027e test: fail any spec that would write to the live API
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 21:00:25 +02:00

31 lines
1.8 KiB
JavaScript

// Single import path for all specs: `const { test, expect } = require('./_guard');`
// Auto fixture that makes a spec fail if it would send a write (non-GET/HEAD/OPTIONS)
// to /api/** on the live app. It is registered first, and Playwright consults the
// most recently registered route first, so spec mocks (page.route in the spec/beforeEach)
// win; only requests no spec mock fulfilled (or route.fallback()'d) reach this handler.
const base = require('@playwright/test');
const test = base.test.extend({
// Registered after the guard, before any spec route: the app POSTs its design presets on every
// page load (voice-clone.js syncDesignPresetsToServer); answer it inertly. Spec mocks still win.
initWriteMocks: [async ({ page, liveWriteGuard }, use) => {
await page.route('**/api/voice-design-presets', (r) => (r.request().method() === 'POST' ? r.fulfill({ json: {} }) : r.fallback()));
// POST /api/voice-sources is the scrape triggered on opening the Studio section.
await page.route('**/api/voice-sources', (r) => (r.request().method() === 'POST' ? r.fulfill({ json: { total: 0, errors: [], sources: [], voices: [] } }) : r.fallback()));
await use();
}, { auto: true }],
liveWriteGuard: [async ({ page }, use) => {
const violations = [];
await page.route('**/api/**', (route) => {
const { method, url } = { method: route.request().method(), url: route.request().url() };
if (['GET', 'HEAD', 'OPTIONS'].includes(method)) return route.fallback();
violations.push(`${method} ${url}`);
return route.abort('blockedbyclient');
});
await use();
base.expect(violations, `Unmocked write(s) to the live API blocked; add a page.route mock in the spec:\n${violations.join('\n')}`).toEqual([]);
}, { auto: true }],
});
module.exports = { test, expect: base.expect };