Files
chore/frontend/vue-app/e2e/mode_parent/create-child/sse.spec.ts
Ryan Kegel f250c42e5e
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m31s
Add end-to-end tests for parent item management
- Implement tests for creating, editing, and deleting chores, kindness acts, and penalties.
- Add tests to verify conversion of default items to user items and restoration of system defaults upon deletion.
- Ensure proper cancellation of creation and editing actions.
- Create a comprehensive plan document outlining the test scenarios and expected behaviors.
2026-03-12 12:22:37 -04:00

109 lines
4.4 KiB
TypeScript

// spec: e2e/plans/create-child.plan.md
import { test, expect } from '@playwright/test'
import { STORAGE_STATE } from '../../e2e-constants'
test.describe('Create Child', () => {
test.describe.configure({ mode: 'serial' })
test('New child appears in list without page reload', async ({ page, context, request }) => {
// Clean up 'Hannah' before test
const res = await request.get('/api/child/list')
const data = await res.json()
for (const child of data.children ?? []) {
if (child.name === 'Hannah') {
await request.delete(`/api/child/${child.id}`)
}
}
// 1. Open two browser tabs both on /parent (children list)
await page.goto('/')
await expect(page).toHaveURL('/parent')
const tab2 = await context.newPage()
await tab2.goto('/')
await expect(tab2).toHaveURL('/parent')
// 2. In Tab 1, create child 'Hannah' age '4'
// Use a retry loop: SSE events from parallel tests can reset the form or cancel navigation
await page.getByRole('button', { name: 'Add Child' }).click()
await expect(async () => {
if (new URL(page.url()).pathname === '/parent') return
// Clean up any Hannah created in a previous attempt where navigation was cancelled
const lr = await request.get('/api/child/list')
for (const c of (await lr.json()).children ?? []) {
if (c.name === 'Hannah') await request.delete(`/api/child/${c.id}`)
}
await page.getByLabel('Name').fill('Hannah')
await page.getByLabel('Age').fill('4')
await expect(page.getByRole('button', { name: 'Create' })).toBeEnabled()
await page.getByRole('button', { name: 'Create' }).click({ timeout: 2000 })
await expect(page).toHaveURL('/parent', { timeout: 5000 })
}).toPass({ timeout: 20000 })
await expect(page).toHaveURL('/parent')
await expect(page.getByRole('heading', { name: 'Hannah' })).toBeVisible()
// 3. Tab 2 should show 'Hannah' via SSE without a manual refresh
await expect(tab2.getByRole('heading', { name: 'Hannah' })).toBeVisible()
await tab2.close()
})
test('New child appears in child mode list without page reload', async ({
page,
browser,
request,
}) => {
// Clean up 'Hannah' before test
const res = await request.get('/api/child/list')
const data = await res.json()
for (const child of data.children ?? []) {
if (child.name === 'Hannah') {
await request.delete(`/api/child/${child.id}`)
}
}
// 1. Tab 1: parent mode
await page.goto('/')
await expect(page).toHaveURL('/parent')
// 2. Tab 2: isolated browser context so removing parentAuth doesn't affect Tab 1
const childContext = await browser.newContext({ storageState: STORAGE_STATE })
const tab2 = await childContext.newPage()
// Load the app first (to ensure localStorage is seeded from storageState),
// then remove parentAuth so the next navigation boots in child mode.
await tab2.goto('/')
await tab2.evaluate(() => localStorage.removeItem('parentAuth'))
// Full navigation triggers a fresh Vue init — auth store reads no parentAuth
// so the router allows /child
await tab2.goto('/child')
await expect(tab2).toHaveURL('/child')
// 3. In Tab 1, create child 'Hannah' age '4'
// Use a retry loop: SSE events from parallel tests can reset the form or cancel navigation
await page.getByRole('button', { name: 'Add Child' }).click()
await expect(async () => {
if (new URL(page.url()).pathname === '/parent') return
// Clean up any Hannah created in a previous attempt where navigation was cancelled
const lr = await request.get('/api/child/list')
for (const c of (await lr.json()).children ?? []) {
if (c.name === 'Hannah') await request.delete(`/api/child/${c.id}`)
}
await page.getByLabel('Name').fill('Hannah')
await page.getByLabel('Age').fill('4')
await expect(page.getByRole('button', { name: 'Create' })).toBeEnabled()
await page.getByRole('button', { name: 'Create' }).click({ timeout: 2000 })
await expect(page).toHaveURL('/parent', { timeout: 5000 })
}).toPass({ timeout: 20000 })
await expect(page).toHaveURL('/parent')
await expect(page.getByRole('heading', { name: 'Hannah' })).toBeVisible()
// 4. Tab 2 (child mode) should show 'Hannah' via SSE without a manual refresh
await expect(tab2.getByRole('heading', { name: 'Hannah' })).toBeVisible()
await childContext.close()
})
})