Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(site/e2e): fix login helper race condition causing navigation flake - #27107

Merged
jeremyruppel merged 9 commits into
mainfrom
jeremy/devex-538-fix-login-race
Jul 13, 2026
Merged

fix(site/e2e): fix login helper race condition causing navigation flake#27107
jeremyruppel merged 9 commits into
mainfrom
jeremy/devex-538-fix-login-race

Conversation

@jeremyruppel

@jeremyruppel jeremyruppel commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Fixes flake reported in DEVEX-538.

Problem

The login() e2e helper had a race condition causing intermittent navigation failures:

page.goto: Navigation to "/deployment/users" is interrupted by
another navigation to "/"

After clicking Sign In, LoginPage.tsx does a hard navigation via location.href = sanitizeRedirect(redirectTo). With no ?redirect= param, retrieveRedirect defaults to "/", so login navigates to /. The browser loads /, fires the load event, then React boots and the router does a client-side redirect from / to /workspaces (via <Navigate to="/workspaces" replace />).

The old helper waited with expectUrl(page).toHavePathName("/workspaces"), which polls page.url() and resolves the moment the pathname matches. It has no awareness of page load state. So it resolved after the client-side redirect changed the URL, but before the /workspaces page components had mounted. When a test immediately called page.goto() afterward, pending React rendering could trigger a competing navigation.

Fix

Replace the URL polling with two Playwright-idiomatic waits:

  1. page.waitForURL(/\/workspaces/) hooks into the browser's navigation lifecycle: it waits for the URL to match AND for the page to reach a load state ("load" by default), unlike expectUrl which is purely a string poll.

  2. await expect(page).toHaveTitle(/Workspaces/) waits for the page title, which is set by the WorkspacesPage component. This proves React booted, auth resolved, and the page fully rendered, closing the window where pending React work could interfere with the next navigation.

Also adds { waitUntil: "domcontentloaded" } to page.goto("/login") for consistency with every other navigation helper in the file.

🤖 Generated by Coder Agents on behalf of @jeremyruppel

The login() helper waited for the post-login URL to match /workspaces
via polling, but did not wait for the page to finish loading. When a
test immediately navigated elsewhere after login(), the still-loading
page could trigger a competing navigation, causing:

  page.goto: Navigation to "/deployment/users" is interrupted by
  another navigation to "/"

Replace the URL polling with waitForLoadState("domcontentloaded"),
which waits for the hard navigation (location.href assignment in
LoginPage.tsx) to complete. This is the correct completion signal
since login uses a full page reload, not SPA routing.

This also removes the coupling between login() and the /workspaces
landing page. A dedicated test in login.spec.ts now covers the
post-login redirect separately.
@linear-code

linear-code Bot commented Jul 8, 2026

Copy link
Copy Markdown

DEVEX-538

waitForLoadState("domcontentloaded") resolves immediately on the
current page's DOM (the login page) before the hard navigation
triggered by location.href even starts. This caused login() to return
before the session cookie was set, breaking all downstream tests with
"session token not found".

Use page.waitForURL(/\/workspaces/) instead. This waits for the
actual hard navigation to complete and the new page to fully load
(default waitUntil: "load"), which guarantees the session cookie
is present.
The login() helper already waits for /workspaces via waitForURL,
so a separate test asserting the same thing adds no coverage.
…race

waitForURL resolves once the URL matches and the document's load
event fires. But login triggers a hard nav to "/" (the default
redirect), and React Router then does a client-side redirect to
"/workspaces". The load event fires for the "/" page load, not the
client-side redirect, so waitForURL resolves before the /workspaces
page components have mounted.

Wait for the page title to match /Workspaces/ after waitForURL.
The title is set by the WorkspacesPage component, so it proves
React booted, auth resolved, and the page rendered. This closes
the window where a subsequent page.goto could be interrupted by
pending React rendering.
@jeremyruppel
jeremyruppel marked this pull request as ready for review July 9, 2026 16:25
@jakehwll

Copy link
Copy Markdown
Contributor

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-07-13 00:51 UTC by @jakehwll
Spend: $7.84 / $100.00

Review history
  • R1 (2026-07-13): 12 reviewers, 2 Nit, 1 P3, COMMENT. Review

deep-review v0.9.0 | Round 1 | 990f0a5..dde31a8

Last posted: Round 1, 3 findings (1 P3, 2 Nit), COMMENT. Review

Finding inventory

Finding inventory - PR #27107

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P3 Open site/e2e/helpers.ts:84 Load-bearing title check has no comment; reads as duplicate of waitForURL and invites deletion R1 Leorio P3, Meruem Note, Mafuuu Note Yes
CRF-2 Nit Open site/e2e/helpers.ts:84 Loose title regex /Workspaces/ diverges from file's exact-string convention R1 Mafuuu Nit, Meruem Nit Yes
CRF-3 Nit Open site/e2e/helpers.ts:83 Unanchored URL regex /\/workspaces/ looser than replaced exact pathname R1 Komugi Nit, Bisky/Hisoka/Mafu-san/Mafuuu/Pariston/Chopper/Kite Note Yes

Round log

Round 1

Netero clean (no findings). Panel: bisky, hisoka, mafu-san, mafuuu, pariston, komugi, ging-ts, gon, leorio, chopper, kite, meruem (+meruem wildcard). Reviewed against 990f0a5..dde31a8.
1 P3, 2 Nit. Event COMMENT. Process observations (description precision, commit subject length, PR title stutter) folded into review body, not inline findings.
ging-ts and gon: no findings.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, tightly scoped flake fix: one helper, three lines, one concern. The panel tried hard to break it and could not. The diagnosis in the PR body is accurate at every link the reviewers traced (LoginPage's hard location.href nav to /, the router's client-side <Navigate to="/workspaces" replace />, and WorkspacesPage setting the Workspaces - Coder title), and the switch from a bare page.url() poll to a rendered-signal wait is the durable fix, not a widened timeout. Pariston: "I tried to build a case against this and could not."

Severity count: 0 P0-P2, 1 P3, 2 Nit.

One substantive point worth acting on before merge (P3): the toHaveTitle line is load-bearing for the fix but looks like a duplicate of the waitForURL above it, so a future reader is likely to delete it and reinstate the flake. Three reviewers independently flagged this deletion risk. A one-line comment fixes it.

Two process observations, no action required but worth knowing:

  • The description credits waitForURL(/\/workspaces/) with waiting for a load state. The / to /workspaces transition is a same-document History navigation that fires no load event, so waitForURL resolves on the URL change alone; the actual synchronization is toHaveTitle, which retries until WorkspacesPage mounts. The fix is correct; the stated mechanism is imprecise, and this reinforces the P3 (do not trim waitForURL or the title check believing the other carries the guarantee). "Fully rendered" also slightly overstates what the title wait proves: it proves mount, not that the workspaces query settled.
  • Commit subject is 73 chars (one over the 72 ceiling), and the PR title stutters fix(site/e2e): fix .... Minor; the commit body itself is exemplary.

🤖 This review was automatically generated with Coder Agents.

Comment thread site/e2e/helpers.ts Outdated
Comment thread site/e2e/helpers.ts Outdated
Comment thread site/e2e/helpers.ts Outdated
The toHaveTitle check looks redundant next to waitForURL but is the
actual synchronization point, since the client-side redirect from /
to /workspaces fires no load event. Document this so the check is
not removed as an apparent duplicate.
The regex /\/workspaces/ is an unanchored partial match, so any URL
containing /workspaces anywhere in it would satisfy the wait. A plain
string without wildcards resolves against baseURL and requires exact
equality, which matches the post-login redirect precisely.
The exact-string form of waitForURL compares the full URL including
any query string, which is stricter than intended. Use the predicate
form to compare only the pathname, which precisely matches the
post-login redirect regardless of query parameters.
Matches the file's exact-string toHaveTitle convention and catches
title drift that a substring regex would miss.
@jeremyruppel
jeremyruppel merged commit 580ba19 into main Jul 13, 2026
44 of 48 checks passed
@jeremyruppel
jeremyruppel deleted the jeremy/devex-538-fix-login-race branch July 13, 2026 13:47
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants