// tests/e2e/02-signin-landing.spec.mjs // @ts-check import { test, expect } from '@playwright/test'; import { loadTestUser } from '../utils/testUser.js'; test.describe('@p0 SignIn → Landing', () => { test.setTimeout(10000); test('signs in with persisted user and reaches SignInLanding', async ({ page }) => { const user = loadTestUser(); await page.context().clearCookies(); await page.goto('/signin', { waitUntil: 'networkidle' }); await expect(page.getByRole('heading', { name: /Sign In/i })).toBeVisible(); await page.getByPlaceholder('Username', { exact: true }).fill(user.username); await page.getByPlaceholder('Password', { exact: true }).fill(user.password); await page.getByRole('button', { name: /^Sign In$/ }).click(); // Wait explicitly for the /api/signin POST and capture the outcome const signinResp = await page.waitForResponse( r => r.url().includes('/api/signin') && r.request().method() === 'POST', { timeout: 15000 } ); const status = signinResp.status(); let bodyText = ''; try { bodyText = await signinResp.text(); } catch {} // If backend rejected signin, fail here with clear diagnostics expect(status, `POST /api/signin → ${status}\n${bodyText}`).toBeLessThan(400); // Authenticated redirect can go to /verify (new gate) OR /signin-landing (legacy) OR journey. await page.waitForLoadState('networkidle'); const url = page.url(); if (url.includes('/verify')) { // Complete email verification via existing API (token exposed by server in non-prod). await expect(page.getByText(/Verify your account/i)).toBeVisible({ timeout: 10000 }); const resp = await page.request.post('/api/auth/verify/email/send', { data: {} }); expect(resp.status()).toBeLessThan(400); const json = await resp.json(); // If server is prod-like and doesn’t expose test_token, fail fast with diagnostics. expect(json.test_token, 'Server did not expose test_token (non-production only)').toBeTruthy(); // Confirm directly via API to avoid timing on auto-redirect. const confirm = await page.request.post('/api/auth/verify/email/confirm', { data: { token: json.test_token } }); expect(confirm.status()).toBeLessThan(400); // Navigate to the authenticated home now that VerificationGate will pass. await page.goto('/signin-landing', { waitUntil: 'networkidle' }); await expect(page.getByText(/Welcome to AptivaAI/i)).toBeVisible({ timeout: 10000 }); await expect(page.getByRole('link', { name: /Go to Exploring/i })).toBeVisible(); await expect(page.getByRole('link', { name: /Go to Preparing/i })).toBeVisible(); await expect(page.getByRole('link', { name: /Go to Enhancing/i })).toBeVisible(); await expect(page.getByRole('link', { name: /Go to Retirement/i })).toBeVisible(); // continue below to cookie + console checks } else if (url.includes('/signin-landing')) { // Greeting is not personalized here; accept any "Welcome to AptivaAI" variant. await expect(page.getByText(/Welcome to AptivaAI/i)).toBeVisible({ timeout: 5000 }); await expect(page.getByRole('link', { name: /Go to Exploring/i })).toBeVisible(); await expect(page.getByRole('link', { name: /Go to Preparing/i })).toBeVisible(); await expect(page.getByRole('link', { name: /Go to Enhancing/i })).toBeVisible(); await expect(page.getByRole('link', { name: /Go to Retirement/i })).toBeVisible(); } else { // Journey-resume path: just prove we’re authenticated and not stuck on /signin. expect(url).not.toMatch(/\/signin($|[?#])/); } const cookies = await page.context().cookies(); expect(cookies.some(c => /jwt|session/i.test(c.name))).toBeTruthy(); /** @type {string[]} */ const consoleErrors = []; page.on('console', m => { if (m.type() === 'error') consoleErrors.push(m.text()); }); expect(consoleErrors).toEqual([]); }); });