// spec: frontend/vue-app/e2e/plans/parent-item-management.plan.md // seed: e2e/seed.spec.ts import { test, expect } from '@playwright/test' test.describe('Chore management', () => { test.beforeEach(async ({ page }, testInfo) => { test.skip(testInfo.project.name === 'chromium-no-pin', 'Requires parent-authenticated mode') }) test.describe.configure({ mode: 'serial' }) test('Create a new chore (parent mode)', async ({ page }) => { const suffix = Date.now() const name = `Wash dishes ${suffix}` // 1. Navigate to /parent/tasks/chores await page.goto('/parent/tasks/chores') // expect: The parent dashboard loads and shows the chores list await expect(page.getByText('Clean your mess', { exact: true })).toBeVisible() // 2. Click the 'Create Chore' FAB await page.getByRole('button', { name: 'Create Chore' }).click() // expect: The Create Chore form is displayed with fields for name and points await expect(page.locator('text=Chore Name')).toBeVisible() // 3. Enter the chore name and points using DOM events await page.evaluate((name) => { const setVal = (sel, val) => { const el = document.querySelector(sel) if (el) { el.value = val el.dispatchEvent(new Event('input', { bubbles: true })) el.dispatchEvent(new Event('change', { bubbles: true })) } } setVal('#name', name) setVal('#points', '10') }, name) // expect: Submit/Create button becomes enabled await expect(page.getByRole('button', { name: 'Create' })).toBeEnabled() // 5. Click the Create button await page.click('button:has-text("Create")') // expect: No validation errors are shown await expect(page.locator('.error')).toHaveCount(0) // expect: Navigation returns to /parent/chores (or dashboard) await expect(page).toHaveURL(/\/parent/) // expect: The new chore appears in the chores list await expect(page.locator(`text=${name}`)).toBeVisible() }) test('Edit an existing chore', async ({ page }) => { const suffix = Date.now() const original = `Wash dishes ${suffix}` const updated = `Wash car ${suffix}` // 1. Ensure there is at least one chore in the list (create one if necessary) await page.goto('/parent/tasks/chores') if (!(await page.locator(`text=${original}`).count())) { await page.getByRole('button', { name: 'Create Chore' }).click() await page.evaluate((name) => { const setVal = (sel, val) => { const el = document.querySelector(sel) if (el) { el.value = val el.dispatchEvent(new Event('input', { bubbles: true })) el.dispatchEvent(new Event('change', { bubbles: true })) } } setVal('#name', name) setVal('#points', '10') }, original) await page.getByRole('button', { name: 'Create' }).click() } // expect: The chore appears in the list await expect(page.locator(`text=${original}`)).toBeVisible() // 2. Click the chore row to edit await page.click(`text=${original}`) // expect: Edit Chore form appears (loaded with current values) await expect(page.locator('text=Chore Name')).toBeVisible() // 3. Change the Name to 'Wash car' and Points to '15' via DOM events await page.evaluate((name) => { const setVal = (sel, val) => { const el = document.querySelector(sel) if (el) { el.value = val el.dispatchEvent(new Event('input', { bubbles: true })) el.dispatchEvent(new Event('change', { bubbles: true })) } } setVal('#name', name) setVal('#points', '15') }, updated) // expect: Fields are updated with the new values // 4. Click Save/Update await expect(page.getByRole('button', { name: 'Save' })).toBeEnabled() await page.click('button:has-text("Save")') // expect: No errors are displayed await expect(page.locator('.error')).toHaveCount(0) // expect: Navigation returns to chores list await expect(page).toHaveURL(/\/parent/) // expect: The chore now reads updated name with 15 points await expect(page.locator(`text=${updated}`)).toBeVisible() }) test('Delete a chore', async ({ page }) => { const suffix = Date.now() const name = `Wash car ${suffix}` await page.goto('/parent/tasks/chores') await page.getByRole('button', { name: 'Create Chore' }).click() await page.evaluate((name) => { const setVal = (sel, val) => { const el = document.querySelector(sel) if (el) { el.value = val el.dispatchEvent(new Event('input', { bubbles: true })) el.dispatchEvent(new Event('change', { bubbles: true })) } } setVal('#name', name) setVal('#points', '15') }, name) await page.getByRole('button', { name: 'Create' }).click() await expect(page.locator(`text=${name}`)).toBeVisible() // delete using row-local button then confirm via modal await page .locator(`text=${name}`) .first() .locator('..') .getByRole('button', { name: 'Delete' }) .click() // modal appears with a warning message await expect(page.locator('text=Are you sure you want to delete')).toBeVisible() // click the red danger button inside the modal (labelled Delete) await page.locator('button.btn-danger:has-text("Delete")').click() await expect(page.locator(`text=${name}`)).toHaveCount(0) }) })