Ignore fetches cancelled by goto in e2e tests

This commit is contained in:
Rūdolfs Ošiņš
2026-10-01 21:58:39 +02:00
parent 8903f853ea
commit 6c9e83c763
3 changed files with 20 additions and 9 deletions
+3 -2
View File
@@ -57,8 +57,9 @@ need an ssh-agent use the `sshAuthSock` fixture and create a peer with a
`passphrase`. When a fixture depends on one peer seeing another's changes, `passphrase`. When a fixture depends on one peer seeing another's changes,
wait for the node event with `waitForEvent` instead of sleeping. wait for the node event with `waitForEvent` instead of sleeping.
Reload pages with `reload(page)` from `@tests/support/fixtures.js`, not Reload pages with `reload(page)` from `@tests/support/fixtures.js`, not
`page.reload()`: the app polls the backend, and the harness would otherwise `page.reload()`, and navigate a page that already shows the app with
fail the test on the request the reload cancels. `goto(page, url)`: the app polls the backend, and the harness would otherwise
fail the test on the request the navigation cancels.
Tests run against a production build in `tests/tmp/build`, served by Tests run against a production build in `tests/tmp/build`, served by
`vite preview`; the build takes a few seconds at startup. Set `vite preview`; the build takes a few seconds at startup. Set
+2 -1
View File
@@ -8,6 +8,7 @@ import {
} from "@tests/support/collaboration.js"; } from "@tests/support/collaboration.js";
import { import {
expect, expect,
goto,
test, test,
useBackend, useBackend,
waitForCommand, waitForCommand,
@@ -124,7 +125,7 @@ test("a contributor can't resolve someone else's review comment", async ({
// Now as eve. // Now as eve.
await eve.startHttpd(); await eve.startHttpd();
await useBackend(page, eve); await useBackend(page, eve);
await page.goto(`/repos/${rid}/patches/${patchId}?view=changes`); await goto(page, `/repos/${rid}/patches/${patchId}?view=changes`);
const thread = page.getByRole("group").filter({ hasText: "Keep it loud" }); const thread = page.getByRole("group").filter({ hasText: "Keep it loud" });
await thread.hover(); await thread.hover();
await expect(thread.getByTitle("Reply")).toBeVisible(); await expect(thread.getByTitle("Reply")).toBeVisible();
+15 -6
View File
@@ -62,7 +62,7 @@ export const test = base.extend<{
if (tearingDown.has(page)) { if (tearingDown.has(page)) {
return; return;
} }
if (reloading.has(page) && isCancelledFetch(msg)) { if (navigating.has(page) && isCancelledFetch(msg)) {
return; return;
} }
// Only says some resize notifications waited for the next frame. // Only says some resize notifications waited for the next frame.
@@ -173,7 +173,7 @@ export const test = base.extend<{
}, },
}); });
const reloading = new WeakSet<Page>(); const navigating = new WeakSet<Page>();
const tearingDown = new WeakSet<Page>(); const tearingDown = new WeakSet<Page>();
// WebKit reports a fetch that was cancelled because the page went away as an // WebKit reports a fetch that was cancelled because the page went away as an
@@ -184,18 +184,27 @@ function isCancelledFetch(error: Error) {
); );
} }
export async function reload(page: Page) { async function navigate(page: Page, action: () => Promise<unknown>) {
reloading.add(page); navigating.add(page);
try { try {
await page.reload(); await action();
// WebKit reports fetches the old page had in flight after the new one // WebKit reports fetches the old page had in flight after the new one
// has loaded, so keep ignoring them until the network settles. // has loaded, so keep ignoring them until the network settles.
await page.waitForLoadState("networkidle"); await page.waitForLoadState("networkidle");
} finally { } finally {
reloading.delete(page); navigating.delete(page);
} }
} }
export async function reload(page: Page) {
await navigate(page, () => page.reload());
}
// Use instead of `page.goto` when the page already shows the app.
export async function goto(page: Page, url: string) {
await navigate(page, () => page.goto(url));
}
// Runs `action` and waits for the backend command it sends to finish. The UI // Runs `action` and waits for the backend command it sends to finish. The UI
// often updates before the write lands, and a reload would cancel it. // often updates before the write lands, and a reload would cancel it.
export async function waitForCommand( export async function waitForCommand(