tts-voice-creator-clone-and.../tests/archetype-gallery.spec.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

56 lines
3.0 KiB
JavaScript

// @ts-check
// Voice archetype gallery; every /api route is mocked (no live writes, no generation).
const { test, expect } = require('./_guard');
const items = Array.from({ length: 24 }, (_, i) => ({
id: 'v' + i, name_de: 'Stimme ' + i, name_en: 'Voice ' + i, instruct: 'Tiefe Stimme ' + i,
category: 'Narration & Story', language: 'German',
facets: { gender: i % 2 ? 'female' : 'male', age: 'elderly', pitch: 'low', accent: 'none', tone: 'warm' },
}));
test('gallery lazy-renders, filters, previews, uses and opens in design', async ({ page }) => {
const calls = [];
await page.route('**/api/archetypes**', route => {
const req = route.request(), url = new URL(req.url());
calls.push(req.method() + ' ' + url.pathname + url.search);
if (req.method() === 'GET') {
const g = url.searchParams.get('gender');
const rows = g ? items.filter(i => i.facets.gender === g) : items;
return route.fulfill({ json: { total: 30, offset: 0, limit: 24, items: rows, categories: ['Narration & Story'], cached: [] } });
}
if (url.pathname.endsWith('/preview')) return route.fulfill({ status: 502, json: { detail: 'mocked engine down' } });
return route.fulfill({ json: { voice_id: 'archetype_v0', created: true, instruct: 'Tiefe Stimme 0', language: 'German' } });
});
await page.route(/\/api\/(?!archetypes)/, route => route.request().method() === 'GET'
? route.fulfill({ json: [] }) : route.fulfill({ json: { ok: true } }));
await page.route('**/static/dist/main.min.js*', r => r.abort());
await page.goto('/', { waitUntil: 'domcontentloaded' });
await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 });
await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 });
await page.evaluate(() => window.navTo('s-design'));
await expect(page.locator('.arch-card')).toHaveCount(0); // lazy: nothing before opening
expect(calls).toEqual([]);
await page.locator('#archetype-gallery > summary').click();
await expect(page.locator('.arch-card')).toHaveCount(24);
await page.locator('#arch-chips [data-k="gender"][data-v="female"]').click();
await expect(page.locator('.arch-card')).toHaveCount(12);
await expect(page.locator('#arch-chips [data-v="female"]')).toHaveAttribute('aria-pressed', 'true');
const play = page.locator('.arch-play').first();
const box = await play.boundingBox();
expect(box && box.height).toBeGreaterThanOrEqual(44);
await play.click();
await expect(page.locator('#arch-status')).toContainText('mocked engine down');
await page.locator('.arch-fav').first().click();
const stored = await page.evaluate(() => localStorage.getItem('ttsvc-archetype-favs'));
expect(JSON.parse(stored || '[]')).toHaveLength(1);
await page.locator('.arch-use').first().click();
await expect(page.locator('.arch-open').first()).toBeVisible();
await page.locator('.arch-open').first().click();
await expect(page.locator('#design-instruct')).toHaveValue(/Tiefe Stimme/);
});