// 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 };