// spec: e2e/plans/user-profile.plan.md import { test, expect, type APIRequestContext } from '@playwright/test' import { E2E_PIN } from '../../e2e-constants' const BACKEND = 'http://localhost:5000' const NEW_PIN = '5678' async function setPinDirectly(request: APIRequestContext, pin: string): Promise { // Request a new code, retrieve it via test endpoint, verify it, then set the pin await request.post(`${BACKEND}/user/request-pin-setup`) const codeRes = await request.get(`${BACKEND}/user/e2e-get-pin-code`) const { code } = await codeRes.json() await request.post(`${BACKEND}/user/verify-pin-setup`, { data: { code } }) await request.post(`${BACKEND}/user/set-pin`, { data: { pin } }) } test.describe('User Profile – Change Parent PIN', () => { test.describe.configure({ mode: 'serial' }) test.afterAll(async ({ request }) => { // Restore original PIN so subsequent test runs start fresh await setPinDirectly(request, E2E_PIN) }) test('Change Parent PIN link navigates to PIN setup page', async ({ page }) => { await page.goto('/parent/profile') await page.getByRole('button', { name: 'Change Parent PIN' }).click() await expect(page).toHaveURL(/\/parent\/pin-setup/) await expect(page.getByRole('heading', { name: 'Set up your Parent PIN' })).toBeVisible() }) test('Back from PIN setup page returns to profile', async ({ page }) => { // Navigate from the profile page so browser history exists await page.goto('/parent/profile') await page.getByRole('button', { name: 'Change Parent PIN' }).click() await expect(page).toHaveURL(/\/parent\/pin-setup/) await page.goBack() await expect(page).toHaveURL(/\/parent\/profile/) }) test('Send Verification Code advances to step 2', async ({ page }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() const codeInput = page.getByPlaceholder('6-digit code') await expect(codeInput).toBeVisible() // Verify Code is disabled while input is empty await expect(page.getByRole('button', { name: 'Verify Code' })).toBeDisabled() }) test('Resend Code button appears after delay', async ({ page }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() // According to the component, resend appears after 10 seconds await expect(page.getByRole('button', { name: 'Resend Code' })).toBeVisible({ timeout: 15000 }) }) test('Invalid code shows error', async ({ page }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() await page.getByPlaceholder('6-digit code').fill('ZZZZZZ') await page.getByRole('button', { name: 'Verify Code' }).click() await expect(page.getByText(/invalid code/i)).toBeVisible() }) test('Valid code advances to step 3 (Create PIN)', async ({ page, request }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() // Retrieve the code via the test-only endpoint const codeRes = await request.get(`${BACKEND}/user/e2e-get-pin-code`) const { code } = await codeRes.json() await page.getByPlaceholder('6-digit code').fill(code) await page.getByRole('button', { name: 'Verify Code' }).click() await expect(page.getByRole('heading', { name: 'Create Parent PIN' })).toBeVisible() await expect(page.getByPlaceholder('New PIN')).toBeVisible() await expect(page.getByPlaceholder('Confirm PIN')).toBeVisible() await expect(page.getByRole('button', { name: 'Set PIN' })).toBeDisabled() }) test('Set PIN disabled when PINs do not match', async ({ page, request }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() const codeRes = await request.get(`${BACKEND}/user/e2e-get-pin-code`) const { code } = await codeRes.json() await page.getByPlaceholder('6-digit code').fill(code) await page.getByRole('button', { name: 'Verify Code' }).click() await expect(page.getByRole('heading', { name: 'Create Parent PIN' })).toBeVisible() await page.getByPlaceholder('New PIN').fill('1111') await page.getByPlaceholder('Confirm PIN').fill('2222') await expect(page.getByRole('button', { name: 'Set PIN' })).toBeDisabled() }) test('Set PIN disabled for fewer than 4 digits', async ({ page, request }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() const codeRes = await request.get(`${BACKEND}/user/e2e-get-pin-code`) const { code } = await codeRes.json() await page.getByPlaceholder('6-digit code').fill(code) await page.getByRole('button', { name: 'Verify Code' }).click() await expect(page.getByRole('heading', { name: 'Create Parent PIN' })).toBeVisible() await page.getByPlaceholder('New PIN').fill('12') await page.getByPlaceholder('Confirm PIN').fill('12') await expect(page.getByRole('button', { name: 'Set PIN' })).toBeDisabled() }) test('Set PIN succeeds with matching 4-digit PIN – shows success step', async ({ page, request, }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() const codeRes = await request.get(`${BACKEND}/user/e2e-get-pin-code`) const { code } = await codeRes.json() await page.getByPlaceholder('6-digit code').fill(code) await page.getByRole('button', { name: 'Verify Code' }).click() await expect(page.getByRole('heading', { name: 'Create Parent PIN' })).toBeVisible() await page.getByPlaceholder('New PIN').fill(NEW_PIN) await page.getByPlaceholder('Confirm PIN').fill(NEW_PIN) await expect(page.getByRole('button', { name: 'Set PIN' })).toBeEnabled() await page.getByRole('button', { name: 'Set PIN' }).click() await expect(page.getByRole('heading', { name: 'Parent PIN Set!' })).toBeVisible() await expect(page.getByRole('button', { name: 'Back', exact: true })).toBeVisible() }) test('Back from success step exits parent mode and goes to child selector', async ({ page, request, }) => { await page.goto('/parent/pin-setup') await page.getByRole('button', { name: 'Send Verification Code' }).click() await expect(page.getByRole('heading', { name: 'Enter Verification Code' })).toBeVisible() const codeRes = await request.get(`${BACKEND}/user/e2e-get-pin-code`) const { code } = await codeRes.json() await page.getByPlaceholder('6-digit code').fill(code) await page.getByRole('button', { name: 'Verify Code' }).click() await expect(page.getByRole('heading', { name: 'Create Parent PIN' })).toBeVisible() await page.getByPlaceholder('New PIN').fill(NEW_PIN) await page.getByPlaceholder('Confirm PIN').fill(NEW_PIN) await page.getByRole('button', { name: 'Set PIN' }).click() await expect(page.getByRole('heading', { name: 'Parent PIN Set!' })).toBeVisible() await page.getByRole('button', { name: 'Back', exact: true }).click() await expect(page).toHaveURL(/\/child(\/|$)/) // Parent mode exited – "Parent login" button visible instead of parent menu await expect(page.getByRole('button', { name: 'Parent login' })).toBeVisible() }) })