// spec: e2e/plans/parent-notifications.plan.md §7 — approve reward import { test, expect, type APIRequestContext } from '@playwright/test' const CHILD_NAME = 'DigestApproveRewardChild' const REWARD_NAME = 'DigestApproveRewardReward' const REWARD_COST = 15 const INITIAL_POINTS = 50 async function createChild( request: APIRequestContext, name: string, points: number, ): Promise { const pre = await request.get('/api/child/list') for (const c of (await pre.json()).children ?? []) { if (c.name === name) await request.delete(`/api/child/${c.id}`) } await request.put('/api/child/add', { data: { name, age: 8 } }) const list = await request.get('/api/child/list') const id = (await list.json()).children?.find((c: any) => c.name === name)?.id ?? '' if (points > 0) await request.put(`/api/child/${id}/edit`, { data: { points } }) return id } async function createReward( request: APIRequestContext, name: string, cost: number, ): Promise { const pre = await request.get('/api/reward/list') for (const r of (await pre.json()).rewards ?? []) { if (r.name === name) await request.delete(`/api/reward/${r.id}`) } await request.put('/api/reward/add', { data: { name, description: 'digest approve test', cost } }) const list = await request.get('/api/reward/list') return (await list.json()).rewards?.find((r: any) => r.name === name)?.id ?? '' } test.describe('Digest Action Token — Approve Reward', () => { test.describe.configure({ mode: 'serial' }) let childId = '' let rewardId = '' let approveToken = '' test.beforeAll(async ({ request }) => { childId = await createChild(request, CHILD_NAME, INITIAL_POINTS) rewardId = await createReward(request, REWARD_NAME, REWARD_COST) await request.put(`/api/child/${childId}/set-rewards`, { data: { reward_ids: [rewardId] }, }) await request.post(`/api/child/${childId}/request-reward`, { data: { reward_id: rewardId }, }) const tokenRes = await request.post('/api/admin/test/digest-token', { data: { child_id: childId, entity_id: rewardId, entity_type: 'reward', action: 'approve', }, }) expect(tokenRes.status()).toBe(200) approveToken = (await tokenRes.json()).token }) test.afterAll(async ({ request }) => { if (childId) await request.delete(`/api/child/${childId}`) if (rewardId) await request.delete(`/api/reward/${rewardId}`) }) // 7.5 Approve-reward token returns 302 redirect to correct ParentView URL test('Approve-reward token returns 302 redirect to correct ParentView URL', async ({ playwright, }) => { const unauthCtx = await playwright.request.newContext({ ignoreHTTPSErrors: true, baseURL: 'https://localhost:5173', }) const res = await unauthCtx.get(`/api/digest-action/${approveToken}`, { maxRedirects: 0, }) expect(res.status()).toBe(302) const location = res.headers()['location'] ?? '' expect(location).toContain(`/parent/${childId}`) expect(location).toContain(`scrollTo=${rewardId}`) expect(location).toContain('entityType=reward') await unauthCtx.dispose() }) // 7.6 Approve-reward action: reward triggered, child points deducted test('Approve-reward action: reward triggered and child points deducted', async ({ request }) => { await new Promise((r) => setTimeout(r, 300)) const childRes = await request.get('/api/child/list') const children = (await childRes.json()).children ?? [] const child = children.find((c: any) => c.id === childId) expect(child).toBeTruthy() expect(child.points).toBe(INITIAL_POINTS - REWARD_COST) const pendingRes = await request.get('/api/pending-confirmations') const confirmations = (await pendingRes.json()).confirmations ?? [] const pending = confirmations.filter( (c: any) => c.child_id === childId && c.entity_id === rewardId && c.status === 'pending', ) expect(pending).toHaveLength(0) }) })