// spec: e2e/plans/parent-notifications.plan.md §1 import { test, expect, type Page } from '@playwright/test' function pushToggle(page: Page) { return page .locator('.toggle-field-row') .filter({ has: page.locator('.toggle-field-label', { hasText: 'Push Notifications' }) }) .locator('button.toggle-btn') } test.describe('Push subscription registration', () => { // ------------------------------------------------------------------------- // 1.3 No subscription POST when notification permission not granted // ------------------------------------------------------------------------- test('No subscription POST is sent when notification permission is not granted', async ({ page, }) => { // Do NOT grant notifications permission let postCaptured = false await page.route('**/api/push-subscription', async (route) => { if (route.request().method() === 'POST') { postCaptured = true } await route.continue() }) await page.goto('/parent/profile') const toggle = pushToggle(page) await expect(toggle).toBeVisible({ timeout: 5000 }) const isDisabled = await toggle.isDisabled().catch(() => false) if (!isDisabled) { // Try to click the toggle — it may prompt for permission but deny it await toggle.click().catch(() => {}) await page .getByRole('button', { name: 'Save' }) .click() .catch(() => {}) await page.waitForTimeout(1000) } expect(postCaptured).toBe(false) }) // ------------------------------------------------------------------------- // 1.4 Backend rejects unauthenticated subscription POST (401) // ------------------------------------------------------------------------- test('Backend rejects unauthenticated subscription POST with 401', async ({ page }) => { // Navigate first, then use credentials:'omit' to strip cookies and verify // the backend enforces auth. await page.goto('/parent/profile') const status = await page.evaluate(async () => { const res = await fetch('/api/push-subscription', { method: 'POST', credentials: 'omit', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ endpoint: 'https://fcm.googleapis.com/fcm/send/fake-endpoint', keys: { p256dh: 'fake-key', auth: 'fake-auth' }, }), }) return res.status }) expect(status).toBe(401) }) // ------------------------------------------------------------------------- // 1.5 Backend rejects unauthenticated subscription DELETE (401) // ------------------------------------------------------------------------- test('Backend rejects unauthenticated subscription DELETE with 401', async ({ page }) => { await page.goto('/parent/profile') const status = await page.evaluate(async () => { const res = await fetch('/api/push-subscription', { method: 'DELETE', credentials: 'omit', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ endpoint: 'https://fcm.googleapis.com/fcm/send/fake-endpoint', }), }) return res.status }) expect(status).toBe(401) }) })