Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 2m46s
188 lines
6.4 KiB
TypeScript
188 lines
6.4 KiB
TypeScript
// spec: e2e/plans/chore-scheduler.plan.md — Section 4: Schedule Enable/Disable Toggle
|
|
|
|
import { test, expect } from '@playwright/test'
|
|
import {
|
|
createChild,
|
|
createChoreTask,
|
|
assignChore,
|
|
deleteSchedule,
|
|
setDaysSchedule,
|
|
choreCard,
|
|
openScheduleModal,
|
|
todayDayIndex,
|
|
nonTodayDayIndex,
|
|
goToChildView,
|
|
} from './helpers'
|
|
|
|
const CHILD_NAME = 'ScheduleToggleChild'
|
|
const CHORE_NAME = 'ScheduleToggleChore'
|
|
|
|
test.describe('Schedule Enable/Disable Toggle', () => {
|
|
test.describe.configure({ mode: 'serial' })
|
|
|
|
let childId = ''
|
|
let choreId = ''
|
|
|
|
test.beforeAll(async ({ request }) => {
|
|
childId = await createChild(request, CHILD_NAME)
|
|
choreId = await createChoreTask(request, CHORE_NAME)
|
|
await assignChore(request, childId, [choreId])
|
|
})
|
|
|
|
test.afterAll(async ({ request }) => {
|
|
if (childId) await request.delete(`/api/child/${childId}`)
|
|
if (choreId) await request.delete(`/api/task/${choreId}`)
|
|
})
|
|
|
|
test.beforeEach(async ({ request }) => {
|
|
await deleteSchedule(request, childId, choreId)
|
|
})
|
|
|
|
test('4.1 Toggle row is visible in the schedule modal', async ({ page, request }) => {
|
|
// Seed a schedule so the modal has something to show
|
|
await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()])
|
|
|
|
await page.goto(`/parent/${childId}`)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
await card.waitFor()
|
|
await openScheduleModal(page, card)
|
|
|
|
await expect(page.locator('.schedule-toggle-row')).toBeVisible()
|
|
await expect(page.getByRole('switch')).toBeVisible()
|
|
await expect(page.locator('.toggle-label')).toBeVisible()
|
|
})
|
|
|
|
test('4.2 Toggling OFF shows "Paused" label and dims the form body', async ({
|
|
page,
|
|
request,
|
|
}) => {
|
|
await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()])
|
|
|
|
await page.goto(`/parent/${childId}`)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
await card.waitFor()
|
|
await openScheduleModal(page, card)
|
|
|
|
// Toggle is ON initially
|
|
await expect(page.getByRole('switch')).toHaveAttribute('aria-checked', 'true')
|
|
|
|
// Toggle OFF
|
|
await page.getByRole('switch').click()
|
|
await expect(page.getByRole('switch')).toHaveAttribute('aria-checked', 'false')
|
|
await expect(page.locator('.toggle-label')).toHaveText('Paused')
|
|
|
|
// Form body dims
|
|
await expect(page.locator('.schedule-body')).toHaveClass(/disabled/)
|
|
|
|
// Cancel/Save buttons remain interactive
|
|
await expect(page.getByRole('button', { name: 'Cancel' })).toBeEnabled()
|
|
})
|
|
|
|
test('4.3 Toggling ON restores "Enabled" label and form body', async ({ page, request }) => {
|
|
await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()])
|
|
|
|
await page.goto(`/parent/${childId}`)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
await card.waitFor()
|
|
await openScheduleModal(page, card)
|
|
|
|
// Toggle OFF then back ON
|
|
await page.getByRole('switch').click()
|
|
await expect(page.locator('.toggle-label')).toHaveText('Paused')
|
|
await page.getByRole('switch').click()
|
|
|
|
await expect(page.getByRole('switch')).toHaveAttribute('aria-checked', 'true')
|
|
await expect(page.locator('.toggle-label')).toHaveText('Enabled')
|
|
await expect(page.locator('.schedule-body')).not.toHaveClass(/disabled/)
|
|
})
|
|
|
|
test('4.4 Save paused state — persists on reopen', async ({ page, request }) => {
|
|
await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()])
|
|
|
|
await page.goto(`/parent/${childId}`)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
await card.waitFor()
|
|
await openScheduleModal(page, card)
|
|
|
|
// Toggle OFF and save
|
|
await page.getByRole('switch').click()
|
|
await expect(page.locator('.toggle-label')).toHaveText('Paused')
|
|
await page.getByRole('button', { name: 'Save' }).click()
|
|
await expect(page.locator('.modal-title')).not.toBeVisible()
|
|
|
|
// Navigate fresh to reset card ready-state, then reopen to verify
|
|
await page.goto(`/parent/${childId}`)
|
|
await card.waitFor()
|
|
await openScheduleModal(page, card)
|
|
await expect(page.getByRole('switch')).toHaveAttribute('aria-checked', 'false')
|
|
await expect(page.locator('.toggle-label')).toHaveText('Paused')
|
|
await expect(page.locator('.schedule-body')).toHaveClass(/disabled/)
|
|
})
|
|
|
|
test('4.5 Dirty detection — toggling enabled state enables/disables Save', async ({
|
|
page,
|
|
request,
|
|
}) => {
|
|
await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()])
|
|
|
|
await page.goto(`/parent/${childId}`)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
await card.waitFor()
|
|
await openScheduleModal(page, card)
|
|
|
|
// Save is disabled initially
|
|
await expect(page.getByRole('button', { name: 'Save' })).toBeDisabled()
|
|
|
|
// Toggle OFF → dirty
|
|
await page.getByRole('switch').click()
|
|
await expect(page.getByRole('button', { name: 'Save' })).toBeEnabled()
|
|
|
|
// Toggle back ON → not dirty again
|
|
await page.getByRole('switch').click()
|
|
await expect(page.getByRole('button', { name: 'Save' })).toBeDisabled()
|
|
})
|
|
|
|
test('4.6 Paused schedule in parent mode — chore is visible and not grayed out', async ({
|
|
page,
|
|
request,
|
|
}) => {
|
|
// Schedule for a non-today weekday, but paused (enabled=false)
|
|
await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()], { enabled: false })
|
|
|
|
await page.goto(`/parent/${childId}`)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
await card.waitFor()
|
|
|
|
// Chore is visible
|
|
await expect(card).toBeVisible()
|
|
// Paused = acts like unscheduled = not grayed out
|
|
await expect(card).not.toHaveClass(/chore-inactive/)
|
|
})
|
|
|
|
test('4.7 Paused schedule in child mode — chore is visible', async ({ page, request }) => {
|
|
// Non-today schedule, paused
|
|
await setDaysSchedule(request, childId, choreId, [nonTodayDayIndex()], { enabled: false })
|
|
|
|
await goToChildView(page, childId)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
// Paused = isScheduledToday returns true → chore is visible in child mode
|
|
await expect(card).toBeVisible()
|
|
})
|
|
|
|
test('4.8 Paused schedule shows no "Due by" label', async ({ page, request }) => {
|
|
// Schedule for today with a deadline, but paused
|
|
await setDaysSchedule(request, childId, choreId, [todayDayIndex()], {
|
|
hour: 23,
|
|
minute: 59,
|
|
enabled: false,
|
|
})
|
|
|
|
await page.goto(`/parent/${childId}`)
|
|
const card = choreCard(page, CHORE_NAME)
|
|
await card.waitFor()
|
|
|
|
// Paused = getDueTimeToday returns null → no "Due by" label
|
|
await expect(card.locator('.due-label')).not.toBeVisible()
|
|
})
|
|
})
|