151 lines
6.9 KiB
JavaScript
151 lines
6.9 KiB
JavaScript
import { expect, test } from '../fixtures/auth.js'
|
|
import { API_URL } from '../global-setup.js'
|
|
|
|
test.describe('ChoreEdit', () => {
|
|
// All tests in this suite run as the pre-authenticated E2E user
|
|
test.use({
|
|
storageState: '.auth/state.json',
|
|
})
|
|
|
|
test('blocks save and shows an error when the name is left blank', async ({
|
|
page,
|
|
}) => {
|
|
// ── Navigate to the create chore page, leave the form at defaults ──────
|
|
await page.goto('/chores/create')
|
|
|
|
// ── Attempt to save without a name ──────────────────────────────────────
|
|
await page.getByRole('button', { name: 'Create' }).click()
|
|
|
|
// ── The validation error is shown and we never leave the create page ───
|
|
await expect(page.getByText('Name is required').first()).toBeVisible({
|
|
timeout: 5_000,
|
|
})
|
|
await expect(page).toHaveURL(/\/chores\/create/)
|
|
})
|
|
|
|
test('days-of-the-week repeat: blocks save with no days selected, then succeeds once days are chosen', async ({
|
|
page,
|
|
}) => {
|
|
const choreName = `E2E Weekly Chore ${Date.now()}`
|
|
|
|
// ── Navigate to the create chore page ────────────────────────────────
|
|
await page.goto('/chores/create')
|
|
|
|
// ── Fill in the chore name ────────────────────────────────────────────
|
|
await page.locator('input').first().fill(choreName)
|
|
|
|
// ── Enable recurrence and switch to a custom "days of the week" schedule
|
|
await page.getByLabel('Repeat this task').click()
|
|
await page.getByLabel('Custom').click()
|
|
await page.getByLabel('Days of the Week').click()
|
|
|
|
// ── Attempt to save with zero days selected ─────────────────────────────
|
|
await page.getByRole('button', { name: 'Create' }).click()
|
|
await expect(
|
|
page.getByText('Please select at least one day of the week').first(),
|
|
).toBeVisible({ timeout: 5_000 })
|
|
await expect(page).toHaveURL(/\/chores\/create/)
|
|
|
|
// ── Select Monday and Wednesday, then save successfully ────────────────
|
|
await page.getByLabel('Monday').click()
|
|
await page.getByLabel('Wednesday').click()
|
|
await page.getByRole('button', { name: 'Create' }).click()
|
|
|
|
await page.waitForURL('**/chores', { timeout: 15_000 })
|
|
await expect(page.getByText(choreName)).toBeVisible({ timeout: 10_000 })
|
|
|
|
// ── Verify the chore exists in the API with the selected days ─────────
|
|
const token = await page.evaluate(() => localStorage.getItem('token'))
|
|
const apiRes = await fetch(`${API_URL}/api/v1/chores/`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
expect(apiRes.ok).toBe(true)
|
|
|
|
const { res: chores } = await apiRes.json()
|
|
const created = chores.find(c => c.name === choreName)
|
|
expect(created).toBeDefined()
|
|
expect(created.frequencyType).toBe('days_of_the_week')
|
|
expect([...(created.frequencyMetadata?.days || [])].sort()).toEqual([
|
|
'monday',
|
|
'wednesday',
|
|
])
|
|
})
|
|
|
|
test('creates a chore, edits it after reload, and toggles Anyone assignment', async ({
|
|
page,
|
|
}) => {
|
|
const originalName = `E2E Edit Chore ${Date.now()}`
|
|
const updatedName = `${originalName} Updated`
|
|
const updatedDescription = `Updated by e2e ${Date.now()}`
|
|
// One week out, formatted as YYYY-MM-DD for the native date input
|
|
const updatedDueDate = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
|
.toISOString()
|
|
.slice(0, 10)
|
|
|
|
// ── Create a simple one-off chore with a due date ───────────────────────
|
|
await page.goto('/chores/create')
|
|
await page.locator('input').first().fill(originalName)
|
|
|
|
// Toggle "Anyone" on then off again — confirm it snaps back to the
|
|
// default self-assignment before we submit.
|
|
await page.getByLabel('Anyone').click()
|
|
await expect(page.getByLabel('Anyone')).toBeChecked()
|
|
await page.getByLabel('Anyone').click()
|
|
await expect(page.getByLabel('Anyone')).not.toBeChecked()
|
|
|
|
// Give it a due date so it's a simple one-off chore
|
|
await page.getByLabel('Give this task a due date').click()
|
|
|
|
await page.getByRole('button', { name: 'Create' }).click()
|
|
await page.waitForURL('**/chores', { timeout: 15_000 })
|
|
await expect(page.getByText(originalName)).toBeVisible({ timeout: 10_000 })
|
|
|
|
// ── Look up the created chore via the API (id + self-assignment state) ─
|
|
const token = await page.evaluate(() => localStorage.getItem('token'))
|
|
const listRes = await fetch(`${API_URL}/api/v1/chores/`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
expect(listRes.ok).toBe(true)
|
|
const { res: chores } = await listRes.json()
|
|
const created = chores.find(c => c.name === originalName)
|
|
expect(created).toBeDefined()
|
|
// Default self-assignment: exactly one assignee, matching assignedTo.
|
|
expect(created.assignees?.length).toBe(1)
|
|
expect(created.assignedTo).toBe(created.assignees[0].userId)
|
|
|
|
// ── Edit: change name, description, due date, and switch to Anyone ─────
|
|
await page.goto(`/chores/${created.id}/edit`)
|
|
|
|
await page.locator('input').first().fill(updatedName)
|
|
await page.locator('.ql-editor').fill(updatedDescription)
|
|
await page.locator('input[type="date"]').fill(updatedDueDate)
|
|
await page.getByLabel('Anyone').click()
|
|
|
|
await page.getByRole('button', { name: 'Save' }).click()
|
|
await page.waitForURL('**/chores', { timeout: 15_000 })
|
|
|
|
// ── Reload the edit page and confirm the new values persisted in the UI
|
|
await page.goto(`/chores/${created.id}/edit`)
|
|
await expect(page.locator('input').first()).toHaveValue(updatedName)
|
|
await expect(page.locator('.ql-editor')).toContainText(updatedDescription)
|
|
await expect(page.locator('input[type="date"]')).toHaveValue(updatedDueDate)
|
|
await expect(page.getByLabel('Anyone')).toBeChecked()
|
|
|
|
// ── Confirm persisted via the API too ───────────────────────────────────
|
|
const detailRes = await fetch(`${API_URL}/api/v1/chores/${created.id}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
expect(detailRes.ok).toBe(true)
|
|
const { res: updated } = await detailRes.json()
|
|
expect(updated.name).toBe(updatedName)
|
|
expect(updated.description).toContain(updatedDescription)
|
|
// Due-date persistence is already confirmed via the reloaded date input
|
|
// above; comparing the raw UTC `nextDueDate` string here would be
|
|
// timezone-fragile since the app stores it as an end-of-local-day UTC
|
|
// instant.
|
|
// "Anyone" assignment: no specific assignees, no fixed assignedTo.
|
|
expect(updated.assignees ?? []).toHaveLength(0)
|
|
expect(updated.assignedTo).toBeFalsy()
|
|
})
|
|
})
|