From 23de70958a1942346e826f0546900b7c67d653fe Mon Sep 17 00:00:00 2001 From: Antti Kettunen Date: Thu, 14 May 2026 17:36:46 +0300 Subject: [PATCH] test(webui): generalize shell route smoke coverage - rename the Playwright smoke suite to reflect shell-wide route coverage - share nav highlight expectations through the route manifest helpers - cover React route activation and canonical stats URL behavior --- .../src/platform/shell/route-manifest.test.ts | 9 +++- webui/src/platform/shell/route-manifest.ts | 5 +++ ...oke.spec.ts => shell-routes.smoke.spec.ts} | 44 +++++++++++-------- 3 files changed, 39 insertions(+), 19 deletions(-) rename webui/tests/{issues.smoke.spec.ts => shell-routes.smoke.spec.ts} (80%) diff --git a/webui/src/platform/shell/route-manifest.test.ts b/webui/src/platform/shell/route-manifest.test.ts index 369f7b3d..2727c255 100644 --- a/webui/src/platform/shell/route-manifest.test.ts +++ b/webui/src/platform/shell/route-manifest.test.ts @@ -6,6 +6,7 @@ import { normalizeShellPath, reactShellRoutes, resolveLegacyShellPageFromPath, + resolveShellNavPage, resolveShellPageFromPath, shellRouteManifest, } from './route-manifest'; @@ -41,8 +42,9 @@ describe('shellRouteManifest', () => { it('tracks whether a route is rendered by React or the legacy shell', () => { expect(getShellRouteByPageId('issues')?.kind).toBe('react'); + expect(getShellRouteByPageId('stats')?.kind).toBe('react'); expect(getShellRouteByPageId('discover')?.kind).toBe('legacy'); - expect(reactShellRoutes.map((route) => route.pageId)).toEqual(['issues']); + expect(reactShellRoutes.map((route) => route.pageId)).toEqual(['stats', 'issues']); expect(legacyShellRoutes.some((route) => route.pageId === 'dashboard')).toBe(true); }); @@ -55,4 +57,9 @@ describe('shellRouteManifest', () => { expect(resolveLegacyShellPageFromPath('/issues')).toBeNull(); expect(resolveLegacyShellPageFromPath('/does-not-exist')).toBeNull(); }); + + it('maps contextual pages to the nav chrome they belong under', () => { + expect(resolveShellNavPage('artist-detail')).toBe('library'); + expect(resolveShellNavPage('stats')).toBe('stats'); + }); }); diff --git a/webui/src/platform/shell/route-manifest.ts b/webui/src/platform/shell/route-manifest.ts index 39686325..bbcd7d09 100644 --- a/webui/src/platform/shell/route-manifest.ts +++ b/webui/src/platform/shell/route-manifest.ts @@ -92,3 +92,8 @@ export function resolveLegacyShellPageFromPath(pathname: string): ShellPageId | const route = getShellRouteByPath(pathname); return route?.kind === 'legacy' ? route.pageId : null; } + +export function resolveShellNavPage(pageId: ShellPageId): ShellPageId | '' { + if (pageId === 'artist-detail') return 'library'; + return pageId; +} diff --git a/webui/tests/issues.smoke.spec.ts b/webui/tests/shell-routes.smoke.spec.ts similarity index 80% rename from webui/tests/issues.smoke.spec.ts rename to webui/tests/shell-routes.smoke.spec.ts index ebac18f5..f2854c0c 100644 --- a/webui/tests/issues.smoke.spec.ts +++ b/webui/tests/shell-routes.smoke.spec.ts @@ -1,6 +1,11 @@ import { expect, test, type Page } from '@playwright/test'; -import { shellRouteManifest, type ShellPageId } from '../src/platform/shell/route-manifest'; +import { + getShellRouteByPageId, + resolveShellNavPage, + shellRouteManifest, + type ShellPageId, +} from '../src/platform/shell/route-manifest'; async function selectProfile(page: Page, baseURL: string, profileId = 1) { const response = await page.request.post(new URL('/api/profiles/select', baseURL).toString(), { @@ -11,29 +16,24 @@ async function selectProfile(page: Page, baseURL: string, profileId = 1) { } async function waitForShellRoute(page: Page, pageId: string) { - if (pageId === 'issues') { + const route = getShellRouteByPageId(pageId as ShellPageId); + + if (route?.kind === 'react') { await expect .poll(async () => page.evaluate(() => document.querySelector('.page.active')?.id ?? ''), { timeout: 15000, }) .toBe('webui-react-root'); - await expect(page.getByTestId('issues-board')).toBeVisible({ timeout: 15000 }); return; } await expect - .poll(async () => - page.evaluate(() => document.querySelector('.page.active')?.id ?? ''), - ) + .poll(async () => page.evaluate(() => document.querySelector('.page.active')?.id ?? '')) .toBe(`${pageId}-page`); } function getExpectedNavPage(pageId: ShellPageId): string { - if (pageId === 'artist-detail') { - return 'library'; - } - - return pageId; + return resolveShellNavPage(pageId); } async function expectNavHighlight(page: Page, pageId: ShellPageId) { @@ -51,15 +51,19 @@ async function verifyIssuesRoute(page: Page) { await expect(page.getByTestId('issues-board')).toContainText('Issues'); } -function expectedUrlPattern(path: string): RegExp { - if (path === '/issues') { +function expectedUrlPattern(path: string, pageId: ShellPageId): RegExp { + if (pageId === 'issues') { return /\/issues(?:\?status=open&category=all)?$/; } + if (pageId === 'stats') { + return /\/stats(?:\?range=7d)?$/; + } + return new RegExp(`${path.replace('/', '\\/')}$`); } -test('direct load activates all known top-level routes', async ({ page, baseURL }) => { +test('direct load activates all known shell routes', async ({ page, baseURL }) => { if (!baseURL) { test.skip(); return; @@ -70,9 +74,11 @@ test('direct load activates all known top-level routes', async ({ page, baseURL for (const route of shellRouteManifest) { const routePage = await page.context().newPage(); try { - await routePage.goto(new URL(route.path, baseURL).toString(), { waitUntil: 'domcontentloaded' }); + await routePage.goto(new URL(route.path, baseURL).toString(), { + waitUntil: 'domcontentloaded', + }); await waitForShellRoute(routePage, route.pageId); - await expect(routePage).toHaveURL(expectedUrlPattern(route.path)); + await expect(routePage).toHaveURL(expectedUrlPattern(route.path, route.pageId)); await expectNavHighlight(routePage, route.pageId); if (route.pageId === 'issues') { @@ -84,7 +90,7 @@ test('direct load activates all known top-level routes', async ({ page, baseURL } }); -test('browser history restores top-level routes', async ({ page, baseURL }) => { +test('browser history restores shell routes', async ({ page, baseURL }) => { if (!baseURL) { test.skip(); return; @@ -101,7 +107,9 @@ test('browser history restores top-level routes', async ({ page, baseURL }) => { await page.getByRole('link', { name: 'Issues' }).click(); await expect .poll(async () => - page.evaluate(() => (window as typeof window & { __spaNavMarker?: string }).__spaNavMarker ?? null), + page.evaluate( + () => (window as typeof window & { __spaNavMarker?: string }).__spaNavMarker ?? null, + ), ) .toBe('persist'); await waitForShellRoute(page, 'issues');