Skip to content

Commit e8d8c5d

Browse files
committed
Harden cli journey navigation checks
1 parent 6003308 commit e8d8c5d

1 file changed

Lines changed: 40 additions & 7 deletions

File tree

apps/cli-journey-e2e/src/user-journey.spec.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ test.setTimeout(60_000);
66

77
const framework = process.env.JOURNEY_FRAMEWORK ?? "next";
88
const expectsProtectedRouteRedirect = framework === "next" || framework === "nuxt";
9+
const loginUrl = /\/login(?:[/?#]|$)/;
10+
const profileUrl = /\/profile(?:[/?#]|$)/;
911

1012
test(`password-only registration, logout, and password login work in a fresh ${framework} app`, async ({
1113
page,
@@ -50,11 +52,10 @@ if (process.env.JOURNEY_ENABLE_PASSKEY !== "0") {
5052

5153
async function expectProtectedRouteToRedirect(page: Page): Promise<void> {
5254
await page.goto("/profile");
53-
await expect(page).toHaveURL(/\/login(?:\?|$)/);
55+
await expect(page).toHaveURL(loginUrl);
5456
}
5557

5658
async function gotoLogin(page: Page): Promise<void> {
57-
const loginUrl = /\/login(?:\?|$)/;
5859
if (loginUrl.test(page.url())) {
5960
return;
6061
}
@@ -142,10 +143,28 @@ async function skipPasskeyUpsellIfVisible(page: Page): Promise<void> {
142143
}
143144

144145
async function expectSignedIn(page: Page): Promise<void> {
145-
await page.waitForURL("**/profile", { timeout: 30_000 });
146+
if (expectsProtectedRouteRedirect) {
147+
await expect(page).toHaveURL(profileUrl, { timeout: 30_000 });
148+
} else {
149+
await expect(signedInLocator(page).first()).toBeVisible({ timeout: 30_000 });
150+
if (!profileUrl.test(page.url())) {
151+
await gotoProfile(page);
152+
}
153+
}
146154
await expect(signedInLocator(page).first()).toBeVisible({ timeout: 30_000 });
147155
}
148156

157+
async function gotoProfile(page: Page): Promise<void> {
158+
await page.goto("/profile").catch(async (error: unknown) => {
159+
if (!isInterruptedByProfileNavigation(error) && !profileUrl.test(page.url())) {
160+
throw error;
161+
}
162+
if (!profileUrl.test(page.url())) {
163+
await page.waitForURL(profileUrl, { timeout: 5000 });
164+
}
165+
});
166+
}
167+
149168
async function expectSessionCookie(page: Page): Promise<void> {
150169
await expect
151170
.poll(
@@ -187,8 +206,8 @@ async function logout(page: Page): Promise<void> {
187206
await expect(logout.first()).toBeVisible({ timeout: 5000 });
188207
await logout.first().click();
189208
const loggedOutUrl = expectsProtectedRouteRedirect
190-
? /\/login(?:\?|$)/
191-
: /\/(?:login)?(?:\?|$)/;
209+
? loginUrl
210+
: /\/(?:login)?(?:[/?#]|$)/;
192211
await expect(page).toHaveURL(loggedOutUrl);
193212
await expectSessionCleared(page);
194213
}
@@ -296,8 +315,22 @@ function actionLocator(page: Page, actionName: string) {
296315
function isInterruptedByLoginNavigation(error: unknown): boolean {
297316
return (
298317
error instanceof Error &&
299-
error.message.includes('interrupted by another navigation to "') &&
300-
error.message.includes("/login")
318+
isExpectedNavigationRace(error.message, "/login")
319+
);
320+
}
321+
322+
function isInterruptedByProfileNavigation(error: unknown): boolean {
323+
return (
324+
error instanceof Error &&
325+
isExpectedNavigationRace(error.message, "/profile")
326+
);
327+
}
328+
329+
function isExpectedNavigationRace(message: string, path: string): boolean {
330+
return (
331+
(message.includes('interrupted by another navigation to "') ||
332+
message.includes("net::ERR_ABORTED at ")) &&
333+
message.includes(path)
301334
);
302335
}
303336

0 commit comments

Comments
 (0)