test(e2e): fix a flake that went red when the app behaved correctly
storage-purge failed roughly one run in three on two unrelated races, both of
which blamed whatever change happened to be in flight.
`page.goto('/admin')` rejected with "interrupted by another navigation" or
ERR_ABORTED when the admin layout redirected to /admin/login first — i.e. the
test went red precisely when the app did the right thing, quickly. The
assertion is the waitForURL that follows, which does not care how the
navigation ended, so the goto is now allowed to reject. (waitUntil: 'commit'
narrows the window but an abort can beat commit too.)
And the PIN test read the page's execution context while the layout's boot
hydration was still in flight, which surfaced as an intermittent "Execution
context was destroyed". Settles the page first.
Verified with 50 consecutive runs, previously ~1 in 3 red.
This commit is contained in:
@@ -81,7 +81,16 @@ test.describe('Browser chaos — storage purge', () => {
|
|||||||
|
|
||||||
await clearAllStorage(page);
|
await clearAllStorage(page);
|
||||||
|
|
||||||
await page.goto('/admin');
|
// The `goto` is deliberately allowed to REJECT. What is under test is a race the app wins:
|
||||||
|
// the admin layout redirects to /admin/login the moment it sees no JWT, and a redirect that
|
||||||
|
// lands first makes this navigation either "interrupted by another navigation" or
|
||||||
|
// ERR_ABORTED. Both mean the app did exactly the right thing, quickly — so failing on them
|
||||||
|
// made the test red precisely when the behaviour was correct, roughly one run in three, and
|
||||||
|
// it read as a regression in whatever change happened to be in flight. (`waitUntil: 'commit'`
|
||||||
|
// narrows the window but does not close it; an abort can beat commit too.)
|
||||||
|
//
|
||||||
|
// `waitForURL` below is the assertion, and it is unaffected by how the navigation ended.
|
||||||
|
await page.goto('/admin').catch(() => {});
|
||||||
// The admin layout should bounce them to /admin/login when the JWT is gone.
|
// The admin layout should bounce them to /admin/login when the JWT is gone.
|
||||||
await page.waitForURL(/admin\/login|join/, { timeout: 5_000 });
|
await page.waitForURL(/admin\/login|join/, { timeout: 5_000 });
|
||||||
});
|
});
|
||||||
@@ -94,6 +103,12 @@ test.describe('Browser chaos — storage purge', () => {
|
|||||||
const g = await guest('PurgePin');
|
const g = await guest('PurgePin');
|
||||||
await signIn(page, g);
|
await signIn(page, g);
|
||||||
await page.goto('/account');
|
await page.goto('/account');
|
||||||
|
// Let the page finish settling before touching its execution context. `goto` resolves at
|
||||||
|
// `load`, but the layout's boot hydration is still in flight and any navigation it triggers
|
||||||
|
// destroys the context out from under the `page.evaluate` below — which surfaced as an
|
||||||
|
// intermittent "Execution context was destroyed" that has nothing to do with what this test
|
||||||
|
// asserts.
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Simulate clearAuth() — clears JWT + user_id but keeps PIN so the user can recover.
|
// Simulate clearAuth() — clears JWT + user_id but keeps PIN so the user can recover.
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user