Files
chore/frontend/vue-app/e2e/mode_parent/chore-scheduler/schedule-interval-mode.spec.ts
Ryan Kegel ef9cb01d92
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m39s
feat: add enable/disable toggle for chore scheduling in ScheduleModal
- Introduced a toggle button to enable or disable chores in the scheduler.
- The toggle will be available in both types of schedulers.
- Added UI design proposal and considerations for mobile dimensions.
- Included E2E tests to ensure toggle state persistence and correct behavior in child view.
2026-03-20 16:42:13 -04:00

179 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// spec: e2e/plans/chore-scheduler.plan.md — Section 3: Schedule Modal Every X Days Mode
import { test, expect } from '@playwright/test'
import {
createChild,
createChoreTask,
assignChore,
deleteSchedule,
setIntervalSchedule,
choreCard,
openScheduleModal,
setTimePicker,
setDateInput,
todayISO,
futureDateISO,
} from './helpers'
const CHILD_NAME = 'ScheduleIntervalChild'
const CHORE_NAME = 'ScheduleIntervalChore'
test.describe('Schedule Modal — Every X Days mode', () => {
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('3.1 Interval stepper increments and decrements within 17', async ({ page }) => {
await page.goto(`/parent/${childId}`)
const card = choreCard(page, CHORE_NAME)
await card.waitFor()
await openScheduleModal(page, card)
await page.getByRole('button', { name: 'Every X Days' }).click()
// Default value is 1
await expect(page.locator('.stepper-value')).toHaveText('1')
// Decrement is disabled at 1
const decBtn = page.locator('.stepper').getByRole('button').first()
await expect(decBtn).toBeDisabled()
// Increment 6 times → value becomes 7
const incBtn = page.locator('.stepper').getByRole('button').last()
for (let i = 0; i < 6; i++) await incBtn.click()
await expect(page.locator('.stepper-value')).toHaveText('7')
// Increment is disabled at 7
await expect(incBtn).toBeDisabled()
})
test("3.2 Anchor date field shows today's date by default", async ({ page }) => {
await page.goto(`/parent/${childId}`)
const card = choreCard(page, CHORE_NAME)
await card.waitFor()
await openScheduleModal(page, card)
await page.getByRole('button', { name: 'Every X Days' }).click()
await expect(page.locator('input.date-input-hidden')).toHaveValue(todayISO())
})
test('3.3 Next occurrence preview label updates correctly', async ({ page }) => {
await page.goto(`/parent/${childId}`)
const card = choreCard(page, CHORE_NAME)
await card.waitFor()
await openScheduleModal(page, card)
await page.getByRole('button', { name: 'Every X Days' }).click()
// Set interval to 2 (click + once from default 1)
await page.locator('.stepper').getByRole('button').last().click()
await expect(page.locator('.stepper-value')).toHaveText('2')
// Ensure anchor is today
await setDateInput(page, todayISO())
// Next occurrence row should be visible
await expect(page.locator('.next-occurrence-label')).toBeVisible()
await expect(page.locator('.next-occurrence-label')).toContainText('Next occurrence:')
})
test('3.4 Interval deadline "Anytime" toggle works', async ({ page }) => {
await page.goto(`/parent/${childId}`)
const card = choreCard(page, CHORE_NAME)
await card.waitFor()
await openScheduleModal(page, card)
await page.getByRole('button', { name: 'Every X Days' }).click()
// Time picker visible by default
await expect(page.locator('.interval-time-row').locator('.time-picker-popover')).toBeVisible()
// Click "Clear (Anytime)"
await page
.locator('.interval-time-row')
.getByRole('button', { name: 'Clear (Anytime)' })
.click()
await expect(
page.locator('.interval-time-row').locator('.time-picker-popover'),
).not.toBeVisible()
await expect(page.locator('.interval-time-row').getByText('Anytime')).toBeVisible()
// Click "Set deadline" — time picker reappears
await page.locator('.interval-time-row').getByRole('button', { name: 'Set deadline' }).click()
await expect(page.locator('.interval-time-row').locator('.time-picker-popover')).toBeVisible()
})
test('3.5 Save interval schedule — persists on reopen', async ({ page }) => {
const anchor = futureDateISO(60) // 60 days ahead — well past today min constraint
await page.goto(`/parent/${childId}`)
const card = choreCard(page, CHORE_NAME)
await card.waitFor()
await openScheduleModal(page, card)
await page.getByRole('button', { name: 'Every X Days' }).click()
// Set interval to 3 (click + twice from default 1)
const incBtn = page.locator('.stepper').getByRole('button').last()
await incBtn.click()
await incBtn.click()
await expect(page.locator('.stepper-value')).toHaveText('3')
// Set anchor date
await setDateInput(page, anchor)
// Set deadline to 04:30 PM
const timePicker = page.locator('.interval-time-row').locator('.time-picker-popover')
await setTimePicker(page, timePicker, 4, 30, 'PM')
// Save
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('button', { name: 'Every X Days' })).toHaveClass(/active/)
await expect(page.locator('.stepper-value')).toHaveText('3')
await expect(page.locator('input.date-input-hidden')).toHaveValue(anchor)
await expect(page.locator('.interval-time-row').locator('.time-display')).toHaveText('04:30 PM')
})
test('3.6 Dirty detection — changing interval enables Save', async ({ page, request }) => {
await setIntervalSchedule(request, childId, choreId, 3, futureDateISO(30))
await page.goto(`/parent/${childId}`)
const card = choreCard(page, CHORE_NAME)
await card.waitFor()
await openScheduleModal(page, card)
// Save is disabled (no changes yet)
await expect(page.getByRole('button', { name: 'Save' })).toBeDisabled()
// Change interval from 3 to 5 (click + twice)
const incBtn = page.locator('.stepper').getByRole('button').last()
await incBtn.click()
await incBtn.click()
await expect(page.locator('.stepper-value')).toHaveText('5')
// Save is now enabled
await expect(page.getByRole('button', { name: 'Save' })).toBeEnabled()
})
})