test(e2e): prove the pager route by its history trail, not by sampling

Sampling window.location on a timer measured the host's speed: on an unloaded
machine the sibling landed before the third sample, so the guard that the
samples spanned the switch failed 3 runs in 4. Weakening it to a length check
made it vacuous instead — the sampling loop always runs its full count.

Record every pushState/replaceState the app makes and assert the exact
sequence, which is what "without passing through the list" claims. Verified by
routing the pager through the list: the trail assert names the detour.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sergey Kozyrenko
2026-07-24 13:00:06 +07:00
co-authored by Claude Opus 4.8
parent c45fbd401f
commit 3b948d68ea
+24 -24
View File
@@ -32,43 +32,43 @@ test.describe('flow pager', { tag: ['@flows', '@smoke'] }, () => {
test('steps to the sibling flow and back without passing through the list', async ({ page, pageErrorLog }) => {
const header = page.locator('header');
await page.addInitScript(() => {
const trail: string[] = [];
(window as unknown as { __routeTrail: string[] }).__routeTrail = trail;
for (const method of ['pushState', 'replaceState'] as const) {
const original = history[method].bind(history);
history[method] = (state: unknown, unused: string, url?: null | string | URL) => {
original(state, unused, url);
trail.push(window.location.pathname);
};
}
});
await page.goto('/flows/5');
await expect(header.getByText('E2E Alpha')).toBeVisible();
await page.route('**/graphql', async (route) => {
await new Promise((resolve) => setTimeout(resolve, 300));
await route.fallback();
// Drop the router's own normalising replaceState from the initial load.
await page.evaluate(() => {
(window as unknown as { __routeTrail: string[] }).__routeTrail.length = 0;
});
await header.getByRole('button', { name: 'Next' }).click();
const samples = await page.evaluate(async () => {
const taken = [];
for (let index = 0; index < 10; index += 1) {
taken.push({
hasPager: !!document.querySelector('header button[aria-label="Next"]'),
isSiblingShown: document.querySelector('header')?.textContent?.includes('E2E Beta') ?? false,
path: window.location.pathname,
});
await new Promise((resolve) => setTimeout(resolve, 30));
}
return taken;
});
await expect(page).toHaveURL(/\/flows\/6$/);
await expect(header.getByText('E2E Beta')).toBeVisible();
// Guards the delay above as much as the pager: without it the sibling lands inside the
// first sample and the loop measures nothing.
expect(samples.filter((sample) => !sample.isSiblingShown).length).toBeGreaterThan(2);
expect(samples.every((sample) => sample.hasPager)).toBe(true);
expect(samples.map((sample) => sample.path)).not.toContain('/flows');
await expect(header.getByRole('button', { name: 'Next' })).toBeVisible();
await header.getByRole('button', { name: 'Previous' }).click();
await expect(page).toHaveURL(/\/flows\/5$/);
await expect(header.getByText('E2E Alpha')).toBeVisible();
const trail = await page.evaluate(() => (window as unknown as { __routeTrail: string[] }).__routeTrail);
expect(trail, 'the pager must step straight between siblings').toEqual(['/flows/6', '/flows/5']);
expectCleanPage(pageErrorLog);
});
});