Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 2m46s
87 lines
3.2 KiB
TypeScript
87 lines
3.2 KiB
TypeScript
// 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)
|
|
})
|
|
})
|