diff --git a/static/nav.js b/static/nav.js index df9efc5..99a5ab7 100644 --- a/static/nav.js +++ b/static/nav.js @@ -211,6 +211,11 @@ } window.navTo = function (sectionId) { + // nav.js is loaded as a classic script; callers can observe navTo as soon + // as it is assigned, before this file reaches its startup restore below. + // Preserve that early intent so startup cannot immediately switch back to + // the previously stored section. + if (SECTIONS.includes(sectionId)) window._navRequestedSection = sectionId; showSection(sectionId); var tabName = Object.keys(TAB_SECTION_MAP).find(function (k) { return TAB_SECTION_MAP[k] === sectionId; @@ -425,7 +430,7 @@ } window._settingsSidebarCat = localStorage.getItem('ttsvc_settings_cat') || 'connections'; window._enginesSidebarCat = localStorage.getItem('ttsvc_engines_cat') || 'llm'; - var _startSection = hashSection() || ((_savedSection && SECTIONS.includes(_savedSection)) ? _savedSection : 's-voices'); + var _startSection = window._navRequestedSection || hashSection() || ((_savedSection && SECTIONS.includes(_savedSection)) ? _savedSection : 's-voices'); showSection(_startSection); window.addEventListener('hashchange', function () { diff --git a/tests/characters.spec.js b/tests/characters.spec.js index dc5e7d0..24ec05e 100644 --- a/tests/characters.spec.js +++ b/tests/characters.spec.js @@ -25,7 +25,8 @@ test('Characters / Cast supports profile sorting, column visibility, and voice r // the Library panel exists before switching its sub-view. await page.waitForSelector('#lib-chars-list', { state: 'attached', timeout: 15_000 }); await page.waitForFunction(() => typeof window.libraryRenderCharacters === 'function', null, { timeout: 15_000 }); - await page.locator('#nav-library-head').evaluate(el => el.click()); + await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 }); + await page.evaluate(() => window.navTo('s-library')); await expect(page.locator('#s-library')).toHaveClass(/is-active/, { timeout: 15_000 }); await page.locator('.lib-tab[data-library-view="characters"]').evaluate(el => el.click()); await expect(page.locator('#s-library')).toHaveClass(/is-active/, { timeout: 15_000 }); diff --git a/tests/rehearser.spec.js b/tests/rehearser.spec.js index 2f37dfe..b6d3d7f 100644 --- a/tests/rehearser.spec.js +++ b/tests/rehearser.spec.js @@ -28,6 +28,7 @@ test('parses a screenplay and builds the cast', async ({ page }) => { json: [{ id: 'smoke_voice', name: 'Smoke voice', enabled: true }], })); await page.goto('/', { waitUntil: 'domcontentloaded' }); + await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 }); await navTo(page, 's-rehearser'); await page.waitForFunction(() => typeof window.navRehearserPhase === 'function', null, { timeout: 15_000 }); await page.evaluate(() => window.navRehearserPhase(1)); diff --git a/tests/smoke.spec.js b/tests/smoke.spec.js index 32ebedf..1ede737 100644 --- a/tests/smoke.spec.js +++ b/tests/smoke.spec.js @@ -32,6 +32,7 @@ async function navTo(page, section) { test('core sections render', async ({ page }) => { await page.goto('/', { waitUntil: 'domcontentloaded' }); + await page.waitForSelector('#app-skeleton', { state: 'detached', timeout: 15_000 }); const sections = [ ['s-clone', /Clone a Voice/i], ['s-studio', /Get Voices Online/i], @@ -39,7 +40,7 @@ test('core sections render', async ({ page }) => { ]; for (const [nav, heading] of sections) { await navTo(page, nav); - await expect(page.getByText(heading).first()).toBeVisible({ timeout: 10_000 }); + await expect(page.locator('#' + nav).getByText(heading).first()).toBeVisible({ timeout: 10_000 }); } });