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>
53 lines
1.9 KiB
JavaScript
53 lines
1.9 KiB
JavaScript
// @ts-check
|
|
// Functional safety net for the Script Rehearser — guards the parser/cast pipeline
|
|
// while rehearser.js is split into smaller modules (#8).
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
const SCRIPT = `FADE IN
|
|
|
|
EXT. HARBOR - NIGHT
|
|
|
|
A ship rocks in the dark.
|
|
|
|
JACK
|
|
We need to find the compass.
|
|
|
|
GIBBS
|
|
Aye, Captain. But the tide is against us.
|
|
|
|
JACK
|
|
Then we sail against the tide.`;
|
|
|
|
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('parses a screenplay and builds the cast', async ({ page }) => {
|
|
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
|
await navTo(page, 's-rehearser');
|
|
|
|
// Go to the Cast tab area: paste a script and parse
|
|
await page.waitForSelector('#reh-script-text', { timeout: 10_000 });
|
|
await page.fill('#reh-script-text', SCRIPT);
|
|
await page.click('#reh-parse-btn');
|
|
|
|
// Cast cards should appear: Narrator + JACK + GIBBS (title page / FADE IN filtered out)
|
|
const cards = page.locator('#reh-cast-list .reh-cast-card');
|
|
await expect(cards).toHaveCount(3, { timeout: 10_000 });
|
|
await expect(page.locator('#reh-cast-list')).toContainText('JACK');
|
|
await expect(page.locator('#reh-cast-list')).toContainText('GIBBS');
|
|
// Scene heading / FADE IN must NOT have become characters
|
|
await expect(page.locator('#reh-cast-list')).not.toContainText('FADE IN');
|
|
await expect(page.locator('#reh-cast-list')).not.toContainText('HARBOR');
|
|
|
|
// A "Hear a line" sample button appears once a character has a voice assigned
|
|
await expect(page.locator('.reh-cc-sample-btn')).toHaveCount(0);
|
|
await page.evaluate(() => {
|
|
rehState.cast['JACK'].voice = 'EN_Test_Voice';
|
|
renderCastList();
|
|
});
|
|
await expect(page.locator('.reh-cc-sample-btn').first()).toBeVisible();
|
|
await expect(page.locator('.reh-cc-sample-btn').first()).toContainText('Hear a line');
|
|
});
|