From 29f5243f6baa08f0cb32ce2924efa5fa820a6b23 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Fri, 4 Sep 2026 15:31:45 +0200 Subject: [PATCH] test: preserve dialogue-only recast merges --- CHANGELOG.md | 2 ++ TODO.md | 2 +- VERSION | 2 +- docs/ARCHITECTURE.md | 1 + static/index.html | 16 ++++++++-------- tests/lazy-loading.spec.js | 28 ++++++++++++++++++++++++++++ 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6802704..ca45757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi ## [Unreleased] ### Changed +- **Dialogue-only merge regression coverage.** Recast/verify normalization now has explicit browser coverage proving that + only adjacent known-speaker dialogue merges; narration and unknown-speaker lines remain separate. - **Visual identity regression coverage.** Portrait and concept-sheet prompt tests now verify shared identity anchors; completed pairs centralize prompt-version, identity-fingerprint, and asset-digest metadata. - **Frontend namespace migration completed.** The remaining utility, library, cast-compatibility, i18n, and settings/log diff --git a/TODO.md b/TODO.md index f878266..ccdfd16 100644 --- a/TODO.md +++ b/TODO.md @@ -268,7 +268,7 @@ codebase; benefit is relative user/reliability impact. - [x] **P1 / M — High benefit — Identity-anchor regression tests.** Add automated coverage proving avatar and concept-sheet requests share canonical identity anchors and persist the prompt version used for each generated asset. -- [ ] **P1 / S–M — Medium benefit — Preserve dialogue-only merging.** Keep the dialogue-only merge for recast/verify +- [x] **P1 / S–M — Medium benefit — Preserve dialogue-only merging.** Keep the dialogue-only merge for recast/verify outputs where adjacent pairs still occur, with regression coverage for the non-dialogue boundaries. - [ ] **P1 / L–XL — Very high benefit — Rework segmentation from Run 3.** Inspect the earlier dialogue split/fusion stages and correct the dominant unmatched-segment class using the measured Run 3 artifacts. diff --git a/VERSION b/VERSION index 9e35561..ad21919 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.24.99 +1.25.0 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6c711e7..b1df27d 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -88,6 +88,7 @@ Done incrementally as global-scope ordered files (no ES-module change needed) - ✅ All remaining application frontend modules are either privately scoped or intentionally stateless bootstrap/cache IIFEs; legacy globals are explicit compatibility exports and new integrations use `window.TTVC` namespaces. - ✅ Visual pair generation centralizes prompt-version, identity-fingerprint, and asset-digest metadata with regression coverage for shared anchors. +- ✅ Recast/verify dialogue merging has regression coverage for known-speaker-only behavior and non-dialogue boundaries. - ✅ `static/css/resource-diagnostics.css` — isolated Performance/resource diagnostic panel styles. - ✅ `static/css/background-jobs.css` — isolated background-job history and retry-panel styles. - ✅ `static/css/voice-design.css` — isolated Voice Design form, samples, and preview-match styles. diff --git a/static/index.html b/static/index.html index 372f26b..1d775b9 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@ - + @@ -27,12 +27,12 @@ - - - - - - + + + + + + @@ -397,7 +397,7 @@ window.toggleNavTree = function(treeId, chevronId) { - + diff --git a/tests/lazy-loading.spec.js b/tests/lazy-loading.spec.js index 801e6b3..4d4faee 100644 --- a/tests/lazy-loading.spec.js +++ b/tests/lazy-loading.spec.js @@ -41,6 +41,34 @@ test('Audiobook lazy modules publish their namespace APIs after loading', async }))).toEqual({ castView: 'function', stream: 'function', startCast: 'function', pipelineCast: 'function', core: 'function', preview: 'function', exportAudio: 'function' }); }); +test('Audiobook recast merge preserves narration and merges only known dialogue pairs', async ({ page }) => { + 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-reader')); + await page.waitForFunction(() => typeof window.TTVC?.audiobook?.cast?.mergeAdjacentDialogue === 'function', null, { timeout: 15_000 }); + await expect.poll(() => page.evaluate(() => { + const merge = window.TTVC.audiobook.cast.mergeAdjacentDialogue; + return merge([ + { type: 'narration', speaker: 'Narrator', text: 'A.' }, + { type: 'narration', speaker: 'Narrator', text: 'B.' }, + { type: 'dialogue', speaker: 'Mira', text: 'C.' }, + { type: 'dialogue', speaker: 'Mira', text: 'D.' }, + { type: 'dialogue', speaker: 'Unknown', text: 'E.' }, + { type: 'dialogue', speaker: 'Unknown', text: 'F.' }, + ]); + })).toEqual({ + segments: [ + { type: 'narration', speaker: 'Narrator', text: 'A.' }, + { type: 'narration', speaker: 'Narrator', text: 'B.' }, + { type: 'dialogue', speaker: 'Mira', text: 'C.\n\nD.' }, + { type: 'dialogue', speaker: 'Unknown', text: 'E.' }, + { type: 'dialogue', speaker: 'Unknown', text: 'F.' }, + ], + merged: 1, + }); +}); + test('Rehearser playback module publishes transport APIs after loading', async ({ page }) => { await page.goto('/', { waitUntil: 'domcontentloaded' }); await page.waitForFunction(() => typeof window.navTo === 'function', null, { timeout: 15_000 });