Skip to content

Commit bd6167e

Browse files
cursoragentjon-bell
andcommitted
Remove E2E login fallback and harden magic-link retry
Co-authored-by: Jonathan Bell <jon@jonbell.net>
1 parent 385aa3f commit bd6167e

File tree

1 file changed

+24
-31
lines changed

1 file changed

+24
-31
lines changed

tests/e2e/TestingUtils.ts

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -359,50 +359,43 @@ async function signInWithMagicLinkAndRetry(page: Page, testingUser: TestingUser,
359359
// Use magic link for login
360360
await page.goto(magicLink);
361361
await page.getByRole("button", { name: "Sign in with magic link" }).click();
362-
await page.waitForLoadState("networkidle");
363362

364-
const currentUrl = page.url();
365-
const isSuccessful = currentUrl.includes("/course");
366-
// Check to see if we got the magic link expired notice
367-
if (!isSuccessful) {
368-
// Magic link expired, retry if we have retries remaining
369-
if (retriesRemaining > 0) {
370-
return await signInWithMagicLinkAndRetry(page, testingUser, retriesRemaining - 1);
371-
} else {
372-
throw new Error("Magic link expired and no retries remaining");
363+
// On slower machines (especially in dev mode), redirect can lag after submit.
364+
// Wait for either successful course navigation or explicit expired-link message.
365+
let outcome: "success" | "expired" | "unknown" = "unknown";
366+
try {
367+
await page.waitForURL(/\/course(\/|$)/, { timeout: 30_000 });
368+
outcome = "success";
369+
} catch {
370+
const expiredMessage = page.getByText(/Email link is invalid or has expired/i);
371+
try {
372+
await expiredMessage.waitFor({ state: "visible", timeout: 2_000 });
373+
outcome = "expired";
374+
} catch {
375+
outcome = "unknown";
373376
}
374377
}
375378

376-
if (!isSuccessful) {
377-
throw new Error("Failed to sign in - neither success nor expired state detected");
379+
if (outcome === "success") {
380+
return;
381+
}
382+
if (retriesRemaining > 0) {
383+
return await signInWithMagicLinkAndRetry(page, testingUser, retriesRemaining - 1);
378384
}
385+
if (outcome === "expired") {
386+
throw new Error("Magic link expired and no retries remaining");
387+
}
388+
throw new Error(`Magic link sign-in did not complete (final URL: ${page.url()})`);
379389
} catch (error) {
380-
if (retriesRemaining > 0 && (error as Error).message.includes("Failed to sign in")) {
381-
console.log(`Sign in failed, retrying... (${retriesRemaining} retries remaining)`);
390+
if (retriesRemaining > 0 && (error as Error).message.includes("did not complete")) {
382391
return await signInWithMagicLinkAndRetry(page, testingUser, retriesRemaining - 1);
383392
}
384393
throw new Error(`Failed to sign in with magic link: ${(error as Error).message}`);
385394
}
386395
}
387396
export async function loginAsUser(page: Page, testingUser: TestingUser, course?: Course) {
388397
await page.goto("/");
389-
try {
390-
await signInWithMagicLinkAndRetry(page, testingUser);
391-
} catch {
392-
// Fallback for local environments where magic-link verification is flaky.
393-
await page.goto(`/sign-in?email=${encodeURIComponent(testingUser.email)}`);
394-
await page.getByLabel("Sign in email").fill(testingUser.email);
395-
await page.getByLabel("Sign in password").fill(testingUser.password);
396-
await page.getByRole("button", { name: "Sign in with email" }).click();
397-
try {
398-
await page.waitForURL(/\/course(\/|$)/, { timeout: 30_000 });
399-
} catch {
400-
// Fall through to explicit URL check below for a clear error.
401-
}
402-
if (!page.url().includes("/course")) {
403-
throw new Error("Failed to sign in with both magic link and email/password fallback");
404-
}
405-
}
398+
await signInWithMagicLinkAndRetry(page, testingUser);
406399

407400
if (course) {
408401
await page.waitForLoadState("networkidle");

0 commit comments

Comments
 (0)