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
This commit is contained in:
parent
92e86527c3
commit
23de70958a
3 changed files with 39 additions and 19 deletions
|
|
@ -6,6 +6,7 @@ import {
|
||||||
normalizeShellPath,
|
normalizeShellPath,
|
||||||
reactShellRoutes,
|
reactShellRoutes,
|
||||||
resolveLegacyShellPageFromPath,
|
resolveLegacyShellPageFromPath,
|
||||||
|
resolveShellNavPage,
|
||||||
resolveShellPageFromPath,
|
resolveShellPageFromPath,
|
||||||
shellRouteManifest,
|
shellRouteManifest,
|
||||||
} from './route-manifest';
|
} from './route-manifest';
|
||||||
|
|
@ -41,8 +42,9 @@ describe('shellRouteManifest', () => {
|
||||||
|
|
||||||
it('tracks whether a route is rendered by React or the legacy shell', () => {
|
it('tracks whether a route is rendered by React or the legacy shell', () => {
|
||||||
expect(getShellRouteByPageId('issues')?.kind).toBe('react');
|
expect(getShellRouteByPageId('issues')?.kind).toBe('react');
|
||||||
|
expect(getShellRouteByPageId('stats')?.kind).toBe('react');
|
||||||
expect(getShellRouteByPageId('discover')?.kind).toBe('legacy');
|
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);
|
expect(legacyShellRoutes.some((route) => route.pageId === 'dashboard')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -55,4 +57,9 @@ describe('shellRouteManifest', () => {
|
||||||
expect(resolveLegacyShellPageFromPath('/issues')).toBeNull();
|
expect(resolveLegacyShellPageFromPath('/issues')).toBeNull();
|
||||||
expect(resolveLegacyShellPageFromPath('/does-not-exist')).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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -92,3 +92,8 @@ export function resolveLegacyShellPageFromPath(pathname: string): ShellPageId |
|
||||||
const route = getShellRouteByPath(pathname);
|
const route = getShellRouteByPath(pathname);
|
||||||
return route?.kind === 'legacy' ? route.pageId : null;
|
return route?.kind === 'legacy' ? route.pageId : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveShellNavPage(pageId: ShellPageId): ShellPageId | '' {
|
||||||
|
if (pageId === 'artist-detail') return 'library';
|
||||||
|
return pageId;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
import { expect, test, type Page } from '@playwright/test';
|
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) {
|
async function selectProfile(page: Page, baseURL: string, profileId = 1) {
|
||||||
const response = await page.request.post(new URL('/api/profiles/select', baseURL).toString(), {
|
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) {
|
async function waitForShellRoute(page: Page, pageId: string) {
|
||||||
if (pageId === 'issues') {
|
const route = getShellRouteByPageId(pageId as ShellPageId);
|
||||||
|
|
||||||
|
if (route?.kind === 'react') {
|
||||||
await expect
|
await expect
|
||||||
.poll(async () => page.evaluate(() => document.querySelector('.page.active')?.id ?? ''), {
|
.poll(async () => page.evaluate(() => document.querySelector('.page.active')?.id ?? ''), {
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
})
|
})
|
||||||
.toBe('webui-react-root');
|
.toBe('webui-react-root');
|
||||||
await expect(page.getByTestId('issues-board')).toBeVisible({ timeout: 15000 });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect
|
await expect
|
||||||
.poll(async () =>
|
.poll(async () => page.evaluate(() => document.querySelector('.page.active')?.id ?? ''))
|
||||||
page.evaluate(() => document.querySelector('.page.active')?.id ?? ''),
|
|
||||||
)
|
|
||||||
.toBe(`${pageId}-page`);
|
.toBe(`${pageId}-page`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExpectedNavPage(pageId: ShellPageId): string {
|
function getExpectedNavPage(pageId: ShellPageId): string {
|
||||||
if (pageId === 'artist-detail') {
|
return resolveShellNavPage(pageId);
|
||||||
return 'library';
|
|
||||||
}
|
|
||||||
|
|
||||||
return pageId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function expectNavHighlight(page: Page, pageId: ShellPageId) {
|
async function expectNavHighlight(page: Page, pageId: ShellPageId) {
|
||||||
|
|
@ -51,15 +51,19 @@ async function verifyIssuesRoute(page: Page) {
|
||||||
await expect(page.getByTestId('issues-board')).toContainText('Issues');
|
await expect(page.getByTestId('issues-board')).toContainText('Issues');
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectedUrlPattern(path: string): RegExp {
|
function expectedUrlPattern(path: string, pageId: ShellPageId): RegExp {
|
||||||
if (path === '/issues') {
|
if (pageId === 'issues') {
|
||||||
return /\/issues(?:\?status=open&category=all)?$/;
|
return /\/issues(?:\?status=open&category=all)?$/;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pageId === 'stats') {
|
||||||
|
return /\/stats(?:\?range=7d)?$/;
|
||||||
|
}
|
||||||
|
|
||||||
return new RegExp(`${path.replace('/', '\\/')}$`);
|
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) {
|
if (!baseURL) {
|
||||||
test.skip();
|
test.skip();
|
||||||
return;
|
return;
|
||||||
|
|
@ -70,9 +74,11 @@ test('direct load activates all known top-level routes', async ({ page, baseURL
|
||||||
for (const route of shellRouteManifest) {
|
for (const route of shellRouteManifest) {
|
||||||
const routePage = await page.context().newPage();
|
const routePage = await page.context().newPage();
|
||||||
try {
|
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 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);
|
await expectNavHighlight(routePage, route.pageId);
|
||||||
|
|
||||||
if (route.pageId === 'issues') {
|
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) {
|
if (!baseURL) {
|
||||||
test.skip();
|
test.skip();
|
||||||
return;
|
return;
|
||||||
|
|
@ -101,7 +107,9 @@ test('browser history restores top-level routes', async ({ page, baseURL }) => {
|
||||||
await page.getByRole('link', { name: 'Issues' }).click();
|
await page.getByRole('link', { name: 'Issues' }).click();
|
||||||
await expect
|
await expect
|
||||||
.poll(async () =>
|
.poll(async () =>
|
||||||
page.evaluate(() => (window as typeof window & { __spaNavMarker?: string }).__spaNavMarker ?? null),
|
page.evaluate(
|
||||||
|
() => (window as typeof window & { __spaNavMarker?: string }).__spaNavMarker ?? null,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.toBe('persist');
|
.toBe('persist');
|
||||||
await waitForShellRoute(page, 'issues');
|
await waitForShellRoute(page, 'issues');
|
||||||
Loading…
Reference in a new issue