Fix post-auth session handoff flow

This commit is contained in:
2026-03-14 19:12:35 -04:00
parent b735b864d2
commit ac3b036c93
5 changed files with 256 additions and 25 deletions

View File

@@ -16,11 +16,49 @@ function createDeferred() {
};
}
function uniqueEmail(prefix: string) {
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}@example.com`;
}
async function gotoAuthPage(page: Page, path: string) {
await page.goto(path, { waitUntil: 'domcontentloaded' });
await page.waitForLoadState('networkidle');
}
async function signUp(page: Page, email: string, path = '/auth/signup') {
await gotoAuthPage(page, path);
await page.locator('input[autocomplete="name"]').fill('Playwright User');
await page.locator('input[autocomplete="email"]').fill(email);
await page.locator('input[autocomplete="new-password"]').first().fill(PASSWORD);
await page.locator('input[autocomplete="new-password"]').nth(1).fill(PASSWORD);
await page.getByRole('button', { name: 'Create account' }).click();
}
async function signIn(page: Page, email: string, path = '/auth/signin') {
await gotoAuthPage(page, path);
await page.locator('input[autocomplete="email"]').fill(email);
await page.locator('input[autocomplete="current-password"]').fill(PASSWORD);
await page.getByRole('button', { name: 'Sign in with password' }).click();
}
async function expectStableDashboard(page: Page) {
await expect(page.getByRole('heading', { name: 'Command Center' })).toBeVisible({ timeout: 30_000 });
await expect(page).toHaveURL(/\/$/, { timeout: 30_000 });
await page.waitForTimeout(1_000);
expect(page.url()).not.toContain('/auth/signin');
}
async function expectStableProtectedRoute(page: Page, pattern: RegExp) {
await expect(page).toHaveURL(pattern, { timeout: 30_000 });
await page.waitForTimeout(1_000);
expect(page.url()).not.toContain('/auth/signin');
}
async function signOut(page: Page) {
await page.getByRole('button', { name: 'Sign out' }).first().click();
await expect(page).toHaveURL(/\/auth\/signin/, { timeout: 30_000 });
}
test('preserves the return path while switching between auth screens and shows the expected controls', async ({ page }) => {
await gotoAuthPage(page, '/auth/signin?next=%2Fanalysis%3Fticker%3DNVDA');
@@ -99,7 +137,7 @@ test('shows loading affordances while sign-up is in flight', async ({ page }) =>
await gotoAuthPage(page, '/auth/signup');
await page.locator('input[autocomplete="name"]').fill('Playwright User');
await page.locator('input[autocomplete="email"]').fill(`playwright-${Date.now()}@example.com`);
await page.locator('input[autocomplete="email"]').fill(uniqueEmail('playwright-loading'));
await page.locator('input[autocomplete="new-password"]').first().fill(PASSWORD);
await page.locator('input[autocomplete="new-password"]').nth(1).fill(PASSWORD);
await page.getByRole('button', { name: 'Create account' }).click();
@@ -110,3 +148,96 @@ test('shows loading affordances while sign-up is in flight', async ({ page }) =>
await expect(page.getByText('Email already exists')).toBeVisible();
});
test('successful signup reaches the authenticated shell and stays there', async ({ page }) => {
await signUp(page, uniqueEmail('playwright-signup-success'));
await expectStableDashboard(page);
});
test('successful signup preserves the requested next path', async ({ page }) => {
await signUp(page, uniqueEmail('playwright-signup-next'), '/auth/signup?next=%2Fanalysis%3Fticker%3DNVDA');
await expectStableProtectedRoute(page, /\/analysis\?ticker=NVDA$/);
});
test('successful sign-in reaches the authenticated shell and stays there', async ({ page }) => {
const email = uniqueEmail('playwright-signin-success');
await signUp(page, email);
await expectStableDashboard(page);
await signOut(page);
await signIn(page, email);
await expectStableDashboard(page);
});
test('authenticated users are redirected away from auth pages with hard navigation', async ({ page }) => {
await signUp(page, uniqueEmail('playwright-authenticated-redirect'));
await expectStableDashboard(page);
await page.goto('/auth/signin', { waitUntil: 'domcontentloaded' });
await expectStableDashboard(page);
await page.goto('/auth/signup?next=%2Fanalysis%3Fticker%3DNVDA', { waitUntil: 'domcontentloaded' });
await expectStableProtectedRoute(page, /\/analysis\?ticker=NVDA$/);
});
test('shows the handoff state while waiting for the session to become visible', async ({ page }) => {
const gate = createDeferred();
let holdSession = false;
await page.route('**/api/auth/get-session**', async (route) => {
if (holdSession) {
await gate.promise;
}
await route.continue();
});
await gotoAuthPage(page, '/auth/signup');
holdSession = true;
await page.locator('input[autocomplete="name"]').fill('Playwright User');
await page.locator('input[autocomplete="email"]').fill(uniqueEmail('playwright-handoff'));
await page.locator('input[autocomplete="new-password"]').first().fill(PASSWORD);
await page.locator('input[autocomplete="new-password"]').nth(1).fill(PASSWORD);
await page.getByRole('button', { name: 'Create account' }).click();
await expect(page.getByRole('button', { name: 'Finishing sign-in...' })).toBeDisabled();
await expect(page.getByText('Establishing your session and opening the workspace...')).toBeVisible();
gate.resolve();
await expectStableDashboard(page);
});
test('shows recovery guidance if session establishment never completes', async ({ page }) => {
let forceMissingSession = false;
await page.route('**/api/auth/get-session**', async (route) => {
if (!forceMissingSession) {
await route.continue();
return;
}
await route.fulfill({
status: 200,
contentType: 'application/json',
body: 'null'
});
});
await gotoAuthPage(page, '/auth/signup');
forceMissingSession = true;
await page.locator('input[autocomplete="name"]').fill('Playwright User');
await page.locator('input[autocomplete="email"]').fill(uniqueEmail('playwright-timeout'));
await page.locator('input[autocomplete="new-password"]').first().fill(PASSWORD);
await page.locator('input[autocomplete="new-password"]').nth(1).fill(PASSWORD);
await page.getByRole('button', { name: 'Create account' }).click();
await expect(page.getByRole('button', { name: 'Finishing sign-in...' })).toBeDisabled();
await expect(page.getByText('Establishing your session and opening the workspace...')).toBeVisible();
await expect(page.getByText('Authentication completed, but the session was not established on this device. Please sign in again.')).toBeVisible({ timeout: 15_000 });
await expect(page).toHaveURL(/\/auth\/signup$/);
await expect(page.getByRole('button', { name: 'Create account' })).toBeEnabled();
});