Add end-to-end tests for task modification and assignment features
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m33s

- Implemented tests for editing penalty points and reward costs in `penalty-edit-points.spec.ts` and `reward-edit-cost.spec.ts`.
- Created detailed plans for task activation and assignment scenarios in `task-activated.plan.md` and `task-assignment.plan.md`.
- Added comprehensive test cases for modifying tasks, including editing points for chores, kindness acts, penalties, and rewards in `task-modified.plan.md`.
- Ensured all tests are isolated and run in serial mode to maintain state integrity.
This commit is contained in:
2026-03-17 22:46:27 -04:00
parent b2115ceb57
commit a9131242a7
28 changed files with 3724 additions and 106 deletions

View File

@@ -0,0 +1,174 @@
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<string> {
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<string> {
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()
})
})

View File

@@ -0,0 +1,143 @@
import { test, expect, type APIRequestContext } from '@playwright/test'
const CHILD_NAME = 'AssignKindnessChild'
const KIND1_NAME = 'AssignTestKindness1'
async function createTestChild(request: APIRequestContext, name: string): Promise<string> {
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<string> {
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('Kindness act assignment', () => {
test.describe.configure({ mode: 'serial' })
let childId = ''
let kind1Id = ''
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 task from a previous run
const taskRes = await request.get('/api/task/list')
const taskData = await taskRes.json()
for (const t of taskData.tasks ?? []) {
if (t.name === KIND1_NAME) await request.delete(`/api/task/${t.id}`)
}
childId = await createTestChild(request, CHILD_NAME)
kind1Id = await createTestTask(request, KIND1_NAME, 'kindness')
})
test.afterAll(async ({ request }) => {
if (childId) await request.delete(`/api/child/${childId}`)
if (kind1Id) await request.delete(`/api/task/${kind1Id}`)
})
test('"Assign Kindness Acts" button navigates to the kindness assign view', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await expect(page.getByRole('button', { name: 'Assign Kindness Acts' })).toBeVisible()
await page.getByRole('button', { name: 'Assign Kindness Acts' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-kindness$`))
await expect(page.getByRole('heading', { name: 'Assign Kindness Acts' })).toBeVisible()
// Test-specific kindness act is listed
await expect(page.getByText(KIND1_NAME, { exact: true })).toBeVisible()
// Fresh child — nothing pre-checked
await expect(
page.locator('.list-row').filter({ hasText: KIND1_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
test('Select a kindness act and submit — item appears in ParentView', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Kindness Acts' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-kindness$`))
await expect(page.getByRole('heading', { name: 'Assign Kindness Acts' })).toBeVisible()
await page.getByText(KIND1_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: KIND1_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 Kindness Acts' })).toBeVisible()
await expect(page.getByText(KIND1_NAME)).toBeVisible()
})
test('Re-opening assign view shows the assigned item as pre-checked', async ({ page }) => {
await page.goto(`/parent/${childId}/assign-kindness`)
await expect(page.getByRole('heading', { name: 'Assign Kindness Acts' })).toBeVisible()
await expect(
page.locator('.list-row').filter({ hasText: KIND1_NAME }).locator('.list-checkbox'),
).toBeChecked()
})
test('Uncheck the kindness act and submit — item is removed from ParentView', async ({
page,
}) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Kindness Acts' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-kindness$`))
await expect(page.getByRole('heading', { name: 'Assign Kindness Acts' })).toBeVisible()
// Uncheck the previously assigned item
await page.getByText(KIND1_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: KIND1_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 Kindness Acts' })).toBeVisible()
await expect(page.getByText(KIND1_NAME)).not.toBeVisible()
})
test('Cancel does not persist selection changes', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Kindness Acts' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-kindness$`))
await expect(page.getByRole('heading', { name: 'Assign Kindness Acts' })).toBeVisible()
// Check the item
await page.getByText(KIND1_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: KIND1_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 and verify the selection was not saved
await page.goto(`/parent/${childId}/assign-kindness`)
await expect(page.getByRole('heading', { name: 'Assign Kindness Acts' })).toBeVisible()
await expect(
page.locator('.list-row').filter({ hasText: KIND1_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
})

View File

@@ -0,0 +1,176 @@
import { test, expect, type APIRequestContext } from '@playwright/test'
const CHILD_NAME = 'AssignPenaltyChild'
const PEN1_NAME = 'AssignTestPenalty1'
const PEN2_NAME = 'AssignTestPenalty2'
const PEN3_NAME = 'AssignTestPenalty3'
async function createTestChild(request: APIRequestContext, name: string): Promise<string> {
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<string> {
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('Penalty assignment', () => {
test.describe.configure({ mode: 'serial' })
let childId = ''
let pen1Id = ''
let pen2Id = ''
let pen3Id = ''
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 ([PEN1_NAME, PEN2_NAME, PEN3_NAME].includes(t.name)) {
await request.delete(`/api/task/${t.id}`)
}
}
childId = await createTestChild(request, CHILD_NAME)
pen1Id = await createTestTask(request, PEN1_NAME, 'penalty')
pen2Id = await createTestTask(request, PEN2_NAME, 'penalty')
pen3Id = await createTestTask(request, PEN3_NAME, 'penalty')
})
test.afterAll(async ({ request }) => {
if (childId) await request.delete(`/api/child/${childId}`)
if (pen1Id) await request.delete(`/api/task/${pen1Id}`)
if (pen2Id) await request.delete(`/api/task/${pen2Id}`)
if (pen3Id) await request.delete(`/api/task/${pen3Id}`)
})
test('"Assign Penalties" button navigates to the penalty assign view', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await expect(page.getByRole('button', { name: 'Assign Penalties' })).toBeVisible()
await page.getByRole('button', { name: 'Assign Penalties' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-penalties$`))
await expect(page.getByRole('heading', { name: 'Assign Penalties' })).toBeVisible()
// Test-specific penalty items are listed
await expect(page.getByText(PEN1_NAME, { exact: true })).toBeVisible()
await expect(page.getByText(PEN2_NAME, { exact: true })).toBeVisible()
// Fresh child — nothing pre-checked
await expect(
page.locator('.list-row').filter({ hasText: PEN1_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
test('Select penalties and submit — items appear in ParentView penalties section', async ({
page,
}) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Penalties' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-penalties$`))
await expect(page.getByRole('heading', { name: 'Assign Penalties' })).toBeVisible()
// Select two penalties
await page.getByText(PEN1_NAME, { exact: true }).click()
await page.getByText(PEN2_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: PEN1_NAME }).locator('.list-checkbox'),
).toBeChecked()
await expect(
page.locator('.list-row').filter({ hasText: PEN2_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 Penalties' })).toBeVisible()
await expect(page.getByText(PEN1_NAME)).toBeVisible()
await expect(page.getByText(PEN2_NAME)).toBeVisible()
})
test('Re-opening assign view shows assigned penalties as pre-checked', async ({ page }) => {
await page.goto(`/parent/${childId}/assign-penalties`)
await expect(page.getByRole('heading', { name: 'Assign Penalties' })).toBeVisible()
// Previously assigned items should be pre-checked
await expect(
page.locator('.list-row').filter({ hasText: PEN1_NAME }).locator('.list-checkbox'),
).toBeChecked()
await expect(
page.locator('.list-row').filter({ hasText: PEN2_NAME }).locator('.list-checkbox'),
).toBeChecked()
// An unassigned item should not be checked
await expect(
page.locator('.list-row').filter({ hasText: PEN3_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
test('Uncheck all penalties and submit — items are removed from ParentView', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Penalties' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-penalties$`))
await expect(page.getByRole('heading', { name: 'Assign Penalties' })).toBeVisible()
// Uncheck the previously assigned penalties
await page.getByText(PEN1_NAME, { exact: true }).click()
await page.getByText(PEN2_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: PEN1_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
await expect(
page.locator('.list-row').filter({ hasText: PEN2_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 Penalties' })).toBeVisible()
await expect(page.getByText(PEN1_NAME)).not.toBeVisible()
await expect(page.getByText(PEN2_NAME)).not.toBeVisible()
})
test('Cancel does not persist selection changes', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Penalties' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-penalties$`))
await expect(page.getByRole('heading', { name: 'Assign Penalties' })).toBeVisible()
// Check an item
await page.getByText(PEN3_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: PEN3_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 and verify the selection was not saved
await page.goto(`/parent/${childId}/assign-penalties`)
await expect(page.getByRole('heading', { name: 'Assign Penalties' })).toBeVisible()
await expect(
page.locator('.list-row').filter({ hasText: PEN3_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
})

View File

@@ -0,0 +1,172 @@
import { test, expect, type APIRequestContext } from '@playwright/test'
const CHILD_NAME = 'AssignRewardChild'
const REW1_NAME = 'AssignTestReward1'
const REW2_NAME = 'AssignTestReward2'
const REW3_NAME = 'AssignTestReward3'
async function createTestChild(request: APIRequestContext, name: string): Promise<string> {
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 createTestReward(request: APIRequestContext, name: string): Promise<string> {
await request.put('/api/reward/add', { data: { name, description: 'Test reward', cost: 10 } })
const res = await request.get('/api/reward/list')
const data = (await res.json()) as { rewards?: { name: string; id: string }[] }
return data.rewards?.find((r) => r.name === name)?.id ?? ''
}
test.describe('Reward assignment', () => {
test.describe.configure({ mode: 'serial' })
let childId = ''
let rew1Id = ''
let rew2Id = ''
let rew3Id = ''
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 rewards from a previous run
const rewardRes = await request.get('/api/reward/list')
const rewardData = await rewardRes.json()
for (const r of rewardData.rewards ?? []) {
if ([REW1_NAME, REW2_NAME, REW3_NAME].includes(r.name)) {
await request.delete(`/api/reward/${r.id}`)
}
}
childId = await createTestChild(request, CHILD_NAME)
rew1Id = await createTestReward(request, REW1_NAME)
rew2Id = await createTestReward(request, REW2_NAME)
rew3Id = await createTestReward(request, REW3_NAME)
})
test.afterAll(async ({ request }) => {
if (childId) await request.delete(`/api/child/${childId}`)
if (rew1Id) await request.delete(`/api/reward/${rew1Id}`)
if (rew2Id) await request.delete(`/api/reward/${rew2Id}`)
if (rew3Id) await request.delete(`/api/reward/${rew3Id}`)
})
test('"Assign Rewards" button navigates to the reward assign view', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await expect(page.getByRole('button', { name: 'Assign Rewards' })).toBeVisible()
await page.getByRole('button', { name: 'Assign Rewards' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-rewards$`))
await expect(page.getByRole('heading', { name: 'Assign Rewards' })).toBeVisible()
// Test-specific rewards are listed
await expect(page.getByText(REW1_NAME, { exact: true })).toBeVisible()
await expect(page.getByText(REW2_NAME, { exact: true })).toBeVisible()
// Fresh child — nothing pre-checked
await expect(
page.locator('.list-row').filter({ hasText: REW1_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
test('Select rewards and submit — items appear in ParentView rewards section', async ({
page,
}) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Rewards' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-rewards$`))
await expect(page.getByRole('heading', { name: 'Assign Rewards' })).toBeVisible()
// Select two rewards
await page.getByText(REW1_NAME, { exact: true }).click()
await page.getByText(REW2_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: REW1_NAME }).locator('.list-checkbox'),
).toBeChecked()
await expect(
page.locator('.list-row').filter({ hasText: REW2_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 Rewards' })).toBeVisible()
await expect(page.getByText(REW1_NAME)).toBeVisible()
await expect(page.getByText(REW2_NAME)).toBeVisible()
})
test('Re-opening assign view shows assigned rewards as pre-checked', async ({ page }) => {
await page.goto(`/parent/${childId}/assign-rewards`)
await expect(page.getByRole('heading', { name: 'Assign Rewards' })).toBeVisible()
// Previously assigned items should be pre-checked
await expect(
page.locator('.list-row').filter({ hasText: REW1_NAME }).locator('.list-checkbox'),
).toBeChecked()
await expect(
page.locator('.list-row').filter({ hasText: REW2_NAME }).locator('.list-checkbox'),
).toBeChecked()
// An unassigned item should not be checked
await expect(
page.locator('.list-row').filter({ hasText: REW3_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
test('Uncheck all rewards and submit — items are removed from ParentView', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Rewards' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-rewards$`))
await expect(page.getByRole('heading', { name: 'Assign Rewards' })).toBeVisible()
// Uncheck the previously assigned rewards
await page.getByText(REW1_NAME, { exact: true }).click()
await page.getByText(REW2_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: REW1_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
await expect(
page.locator('.list-row').filter({ hasText: REW2_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 Rewards' })).toBeVisible()
await expect(page.getByText(REW1_NAME)).not.toBeVisible()
await expect(page.getByText(REW2_NAME)).not.toBeVisible()
})
test('Cancel does not persist selection changes', async ({ page }) => {
await page.goto(`/parent/${childId}`)
await page.getByRole('button', { name: 'Assign Rewards' }).click()
await page.waitForURL(new RegExp(`/parent/${childId}/assign-rewards$`))
await expect(page.getByRole('heading', { name: 'Assign Rewards' })).toBeVisible()
// Check an item
await page.getByText(REW3_NAME, { exact: true }).click()
await expect(
page.locator('.list-row').filter({ hasText: REW3_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 and verify the selection was not saved
await page.goto(`/parent/${childId}/assign-rewards`)
await expect(page.getByRole('heading', { name: 'Assign Rewards' })).toBeVisible()
await expect(
page.locator('.list-row').filter({ hasText: REW3_NAME }).locator('.list-checkbox'),
).not.toBeChecked()
})
})