Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 2m46s
182 lines
7.9 KiB
TypeScript
182 lines
7.9 KiB
TypeScript
// 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<void> {
|
||
// 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()
|
||
})
|
||
})
|