soulsync/webui/tests/shell-routes.smoke.spec.ts
Antti Kettunen b78350a3e2
fix(webui): preserve import tab refresh URLs
- stop the legacy shell bootstrap from collapsing /import/auto and /import/singles back to the import root on reload\n- update the shell route smoke test to expect the canonical /import/album redirect for the bare import page
2026-05-24 21:17:20 +03:00

153 lines
4.4 KiB
TypeScript

import { expect, test, type Page } from '@playwright/test';
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(), {
data: { profile_id: profileId },
});
expect(response.ok()).toBe(true);
}
async function waitForShellRoute(page: Page, pageId: string) {
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');
return;
}
await expect
.poll(async () => page.evaluate(() => document.querySelector('.page.active')?.id ?? ''))
.toBe(`${pageId}-page`);
}
function getExpectedNavPage(pageId: ShellPageId): string {
return resolveShellNavPage(pageId);
}
async function expectNavHighlight(page: Page, pageId: ShellPageId) {
const navPage = getExpectedNavPage(pageId);
const activeNavPage = await page.evaluate(() => {
return document.querySelector('.nav-button.active')?.getAttribute('data-page') ?? '';
});
expect(activeNavPage).toBe(navPage);
}
async function verifyIssuesRoute(page: Page) {
const appRoot = page.locator('#webui-react-root');
await expect(appRoot).toBeVisible();
await expect(page.getByTestId('issues-board')).toContainText('Issues');
}
function expectedUrlPattern(path: string, pageId: ShellPageId): RegExp {
if (pageId === 'issues') {
return /\/issues(?:\?status=open&category=all)?$/;
}
if (pageId === 'stats') {
return /\/stats(?:\?range=7d)?$/;
}
if (pageId === 'import') {
return /\/import\/album$/;
}
return new RegExp(`${path.replace('/', '\\/')}$`);
}
test('direct load activates all known shell routes', async ({ page, baseURL }) => {
if (!baseURL) {
test.skip();
return;
}
await selectProfile(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 waitForShellRoute(routePage, route.pageId);
await expect(routePage).toHaveURL(expectedUrlPattern(route.path, route.pageId));
await expectNavHighlight(routePage, route.pageId);
if (route.pageId === 'issues') {
await verifyIssuesRoute(routePage);
}
} finally {
await routePage.close();
}
}
});
test('browser history restores shell routes', async ({ page, baseURL }) => {
if (!baseURL) {
test.skip();
return;
}
await selectProfile(page, baseURL);
await page.goto(new URL('/discover', baseURL).toString(), { waitUntil: 'domcontentloaded' });
await waitForShellRoute(page, 'discover');
await page.evaluate(() => {
(window as typeof window & { __spaNavMarker?: string }).__spaNavMarker = 'persist';
});
await page.getByRole('link', { name: 'Issues' }).click();
await expect
.poll(async () =>
page.evaluate(
() => (window as typeof window & { __spaNavMarker?: string }).__spaNavMarker ?? null,
),
)
.toBe('persist');
await waitForShellRoute(page, 'issues');
await expect(page).toHaveURL(/\/issues(?:\?status=open&category=all)?$/);
await page.goBack();
await waitForShellRoute(page, 'discover');
await expect(page).toHaveURL(/\/discover$/);
await page.goForward();
await waitForShellRoute(page, 'issues');
await expect(page).toHaveURL(/\/issues(?:\?status=open&category=all)?$/);
});
test('browser history leaves artist detail when going back to library', async ({
page,
baseURL,
}) => {
if (!baseURL) {
test.skip();
return;
}
await selectProfile(page, baseURL);
await page.goto(new URL('/library', baseURL).toString(), { waitUntil: 'domcontentloaded' });
await waitForShellRoute(page, 'library');
await expect.poll(async () => page.locator('.library-artist-card').count()).toBeGreaterThan(0);
await page.locator('.library-artist-card').first().click();
await waitForShellRoute(page, 'artist-detail');
await expect(page).toHaveURL(/\/artist-detail\/library\/[^/]+$/);
await page.goBack();
await waitForShellRoute(page, 'library');
await expect(page).toHaveURL(/\/library$/);
});