tts-voice-creator-clone-and.../tests/smoke.spec.js
mARTin-B78 40e42590cc Release v1.6.0: a11y (WCAG AA), i18n (DE), PWA, perf, tests, Cast UX
Cast: card/list views, sort & filter, online voice picker, "Hear a line"
sample button, AI character notes, import auto-save.

Platform: WCAG 2.1 AA accessibility pass; German UI translation + language
picker; installable PWA with offline shell; GZip + content-visibility
virtualization + lazy images + Rehearser PCM memory cap (mobile stability);
Playwright suite (desktop + iPhone); opt-in minified bundle build.

Fixes: screenplay parser false characters; Fish-Speech inline-tag tones;
narrator/voice pickers list full library; clone GUI rework; fish.audio
import dedup; voice-ID rename; bulk-delete modal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:23:35 +02:00

62 lines
2.6 KiB
JavaScript

// @ts-check
const { test, expect } = require('@playwright/test');
// Console/page errors that are environmental (offline backends, missing engines) and
// must NOT fail the smoke test. Real JS exceptions (ReferenceError, etc.) still fail.
const IGNORE = [/Failed to load resource/i, /favicon/i, /net::ERR/i, /AbortError/i];
function watchForErrors(page) {
const errors = [];
page.on('pageerror', (e) => errors.push(String(e)));
page.on('console', (m) => { if (m.type() === 'error' && !IGNORE.some(r => r.test(m.text()))) errors.push(m.text()); });
return errors;
}
test('app shell loads without JS errors', async ({ page }) => {
const errors = watchForErrors(page);
await page.goto('/', { waitUntil: 'domcontentloaded' });
await expect(page.locator('#sidebar')).toBeVisible();
await expect(page.locator('.nav-item').first()).toBeVisible();
// Let dynamic sections + module scripts settle
await page.waitForTimeout(1500);
const real = errors.filter(e => /ReferenceError|TypeError|SyntaxError|is not defined|is not a function/.test(e));
expect(real, real.join('\n')).toHaveLength(0);
});
// Navigate via the app's global router (sidebar is hidden behind a menu on mobile,
// so clicking sidebar pixels is unreliable across viewports).
async function navTo(page, section) {
await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 });
await page.evaluate((s) => window.navTo(s), section);
}
test('core sections render', async ({ page }) => {
await page.goto('/', { waitUntil: 'domcontentloaded' });
const sections = [
['s-clone', /Clone a Voice/i],
['s-studio', /Get Voices Online/i],
['s-rehearser', /Script Rehearser/i],
];
for (const [nav, heading] of sections) {
await navTo(page, nav);
await expect(page.getByText(heading).first()).toBeVisible({ timeout: 10_000 });
}
});
test('clone tabs switch and show one panel at a time', async ({ page }) => {
await page.goto('/', { waitUntil: 'domcontentloaded' });
await navTo(page, 's-clone');
await expect(page.locator('.clone-src-tab[data-src="url"]')).toBeVisible({ timeout: 10_000 });
await page.locator('.clone-src-tab[data-src="url"]').click();
await expect(page.locator('#clone-src-url')).toBeVisible();
await expect(page.locator('#clone-src-mic')).toBeHidden();
});
test('PWA manifest and service worker are served', async ({ page, request }) => {
await page.goto('/', { waitUntil: 'domcontentloaded' });
const mani = await request.get('/manifest.webmanifest');
expect(mani.ok()).toBeTruthy();
const sw = await request.get('/sw.js');
expect(sw.ok()).toBeTruthy();
});