import { test, expect, type APIRequestContext } from '@playwright/test' const CHILD_NAME = 'AssignChoreChild' const CHORE1_NAME = 'AssignTestChore1' const CHORE2_NAME = 'AssignTestChore2' const CHORE3_NAME = 'AssignTestChore3' async function createTestChild(request: APIRequestContext, name: string): Promise { await request.put('/api/child/add', { data: { name, age: 8 } }) const res = await request.get('/api/child/list') const data = (await res.json()) as { children?: { name: string; id: string }[] } return data.children?.find((c) => c.name === name)?.id ?? '' } async function createTestTask( request: APIRequestContext, name: string, type: 'chore' | 'kindness' | 'penalty', ): Promise { await request.put('/api/task/add', { data: { name, points: 5, type } }) const res = await request.get('/api/task/list') const data = (await res.json()) as { tasks?: { name: string; id: string }[] } return data.tasks?.find((t) => t.name === name)?.id ?? '' } test.describe('Chore assignment', () => { test.describe.configure({ mode: 'serial' }) let childId = '' let chore1Id = '' let chore2Id = '' let chore3Id = '' test.beforeAll(async ({ request }) => { // Clean up any stale child from a previous run const listRes = await request.get('/api/child/list') const listData = await listRes.json() for (const c of listData.children ?? []) { if (c.name === CHILD_NAME) await request.delete(`/api/child/${c.id}`) } // Clean up any stale test tasks from a previous run const taskRes = await request.get('/api/task/list') const taskData = await taskRes.json() for (const t of taskData.tasks ?? []) { if ([CHORE1_NAME, CHORE2_NAME, CHORE3_NAME].includes(t.name)) { await request.delete(`/api/task/${t.id}`) } } childId = await createTestChild(request, CHILD_NAME) chore1Id = await createTestTask(request, CHORE1_NAME, 'chore') chore2Id = await createTestTask(request, CHORE2_NAME, 'chore') chore3Id = await createTestTask(request, CHORE3_NAME, 'chore') }) test.afterAll(async ({ request }) => { if (childId) await request.delete(`/api/child/${childId}`) if (chore1Id) await request.delete(`/api/task/${chore1Id}`) if (chore2Id) await request.delete(`/api/task/${chore2Id}`) if (chore3Id) await request.delete(`/api/task/${chore3Id}`) }) test('"Assign Chores" button navigates to the chore assign view', async ({ page }) => { await page.goto(`/parent/${childId}`) await expect(page.getByRole('button', { name: 'Assign Chores' })).toBeVisible() await page.getByRole('button', { name: 'Assign Chores' }).click() await page.waitForURL(new RegExp(`/parent/${childId}/assign-chores(\\?|$)`)) await expect(page.getByRole('heading', { name: 'Assign Chores' })).toBeVisible() // Test-specific chores are listed await expect(page.getByText(CHORE1_NAME, { exact: true })).toBeVisible() await expect(page.getByText(CHORE2_NAME, { exact: true })).toBeVisible() // Fresh child — nothing pre-checked await expect( page.locator('.list-row').filter({ hasText: CHORE1_NAME }).locator('.list-checkbox'), ).not.toBeChecked() }) test('Select chores and submit — items appear in ParentView chores section', async ({ page }) => { await page.goto(`/parent/${childId}`) await page.getByRole('button', { name: 'Assign Chores' }).click() await page.waitForURL(new RegExp(`/parent/${childId}/assign-chores(\\?|$)`)) await expect(page.getByRole('heading', { name: 'Assign Chores' })).toBeVisible() // Select two chores await page.getByText(CHORE1_NAME, { exact: true }).click() await page.getByText(CHORE2_NAME, { exact: true }).click() await expect( page.locator('.list-row').filter({ hasText: CHORE1_NAME }).locator('.list-checkbox'), ).toBeChecked() await expect( page.locator('.list-row').filter({ hasText: CHORE2_NAME }).locator('.list-checkbox'), ).toBeChecked() await page.getByRole('button', { name: 'Submit' }).click() await page.waitForURL(new RegExp(`/parent/${childId}$`)) await expect(page.getByRole('button', { name: 'Assign Chores' })).toBeVisible() await expect(page.getByText(CHORE1_NAME)).toBeVisible() await expect(page.getByText(CHORE2_NAME)).toBeVisible() }) test('Re-opening assign view shows assigned items as pre-checked', async ({ page }) => { await page.goto(`/parent/${childId}/assign-chores`) await expect(page.getByRole('heading', { name: 'Assign Chores' })).toBeVisible() // Previously assigned items must be pre-checked await expect( page.locator('.list-row').filter({ hasText: CHORE1_NAME }).locator('.list-checkbox'), ).toBeChecked() await expect( page.locator('.list-row').filter({ hasText: CHORE2_NAME }).locator('.list-checkbox'), ).toBeChecked() // An unassigned item must not be checked await expect( page.locator('.list-row').filter({ hasText: CHORE3_NAME }).locator('.list-checkbox'), ).not.toBeChecked() }) test('Uncheck all chores and submit — items are removed from ParentView', async ({ page }) => { await page.goto(`/parent/${childId}`) await page.getByRole('button', { name: 'Assign Chores' }).click() await page.waitForURL(new RegExp(`/parent/${childId}/assign-chores(\\?|$)`)) await expect(page.getByRole('heading', { name: 'Assign Chores' })).toBeVisible() // Uncheck the two previously assigned chores await page.getByText(CHORE1_NAME, { exact: true }).click() await page.getByText(CHORE2_NAME, { exact: true }).click() await expect( page.locator('.list-row').filter({ hasText: CHORE1_NAME }).locator('.list-checkbox'), ).not.toBeChecked() await expect( page.locator('.list-row').filter({ hasText: CHORE2_NAME }).locator('.list-checkbox'), ).not.toBeChecked() await page.getByRole('button', { name: 'Submit' }).click() await page.waitForURL(new RegExp(`/parent/${childId}$`)) await expect(page.getByRole('button', { name: 'Assign Chores' })).toBeVisible() await expect(page.getByText(CHORE1_NAME)).not.toBeVisible() await expect(page.getByText(CHORE2_NAME)).not.toBeVisible() }) test('Cancel does not persist selection changes', async ({ page }) => { await page.goto(`/parent/${childId}`) await page.getByRole('button', { name: 'Assign Chores' }).click() await page.waitForURL(new RegExp(`/parent/${childId}/assign-chores(\\?|$)`)) await expect(page.getByRole('heading', { name: 'Assign Chores' })).toBeVisible() // Check an item await page.getByText(CHORE3_NAME, { exact: true }).click() await expect( page.locator('.list-row').filter({ hasText: CHORE3_NAME }).locator('.list-checkbox'), ).toBeChecked() // Cancel instead of submitting await page.getByRole('button', { name: 'Cancel' }).click() await page.waitForURL(new RegExp(`/parent/${childId}$`)) // Navigate back to the assign view and verify the selection was not saved await page.goto(`/parent/${childId}/assign-chores`) await expect(page.getByRole('heading', { name: 'Assign Chores' })).toBeVisible() await expect( page.locator('.list-row').filter({ hasText: CHORE3_NAME }).locator('.list-checkbox'), ).not.toBeChecked() }) })