/** * E2E Tests for Push Notifications * * Tests the complete push notification workflow using Playwright: * - Permission granting * - Subscription creation * - Server registration * - Manual test notifications * - Unsubscribe flow * - localStorage persistence * * Note: These tests require the dev server to be running. */ import { test, expect, type BrowserContext } from '@playwright/test'; test.describe('Push Notifications E2E', () => { let context: BrowserContext; test.beforeEach(async ({ browser }) => { // Create new context with notification permissions granted context = await browser.newContext(); await context.grantPermissions(['notifications']); }); test.afterEach(async () => { await context?.close(); }); test('should subscribe to push notifications', async () => { const page = await context.newPage(); await page.goto('/'); await page.waitForLoadState('networkidle'); // Wait for service worker to be registered await page.waitForFunction(() => 'serviceWorker' in navigator && 'PushManager' in window); // Find the notification toggle button const toggleButton = page.getByRole('button', { name: /enable notifications/i }); await expect(toggleButton).toBeVisible(); // Click to enable notifications await toggleButton.click(); // Wait for subscription to complete await page.waitForTimeout(2000); // Verify subscription was created in browser const subscription = await page.evaluate(async () => { const registration = await navigator.serviceWorker.ready; const sub = await registration.pushManager.getSubscription(); return sub ? { endpoint: sub.endpoint, hasKeys: !!(sub as any).keys } : null; }); expect(subscription).not.toBeNull(); expect(subscription?.endpoint).toBeTruthy(); expect(subscription?.endpoint).toContain('https://'); expect(subscription?.hasKeys).toBe(true); // Verify button text changed to "Disable Notifications" await expect(toggleButton).toHaveText(/disable notifications/i); await page.close(); }); test('should show test notification buttons when subscribed', async () => { const page = await context.newPage(); await page.goto('/'); await page.waitForLoadState('networkidle'); // Wait for service worker await page.waitForFunction(() => 'serviceWorker' in navigator); // Enable notifications first const toggleButton = page.getByRole('button', { name: /enable notifications/i }); await toggleButton.click(); await page.waitForTimeout(2000); // Verify test buttons are visible const testSuccessButton = page.getByRole('button', { name: /test success/i }); const testErrorButton = page.getByRole('button', { name: /test error/i }); const testProgressButton = page.getByRole('button', { name: /test progress/i }); await expect(testSuccessButton).toBeVisible(); await expect(testErrorButton).toBeVisible(); await expect(testProgressButton).toBeVisible(); await page.close(); }); test('should send test notifications', async () => { const page = await context.newPage(); await page.goto('/'); await page.waitForLoadState('networkidle'); // Wait for service worker await page.waitForFunction(() => 'serviceWorker' in navigator); // Enable notifications first const toggleButton = page.getByRole('button', { name: /enable notifications/i }); await toggleButton.click(); await page.waitForTimeout(2000); // Mock the test notification API response await page.route('/api/notifications/test', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ success: true, subscriberCount: 1 }) }); }); // Click test success button const testSuccessButton = page.getByRole('button', { name: /test success/i }); await testSuccessButton.click(); // Wait for and verify success message const successMessage = page.getByText(/✓ test success notification sent/i); await expect(successMessage).toBeVisible({ timeout: 5000 }); // Verify message contains subscriber count await expect(successMessage).toContainText('1 subscriber'); // Wait for auto-dismiss await expect(successMessage).not.toBeVisible({ timeout: 4000 }); await page.close(); }); test('should unsubscribe from push notifications', async () => { const page = await context.newPage(); await page.goto('/'); await page.waitForLoadState('networkidle'); // Wait for service worker await page.waitForFunction(() => 'serviceWorker' in navigator); // First subscribe const toggleButton = page.getByRole('button', { name: /enable notifications/i }); await toggleButton.click(); await page.waitForTimeout(2000); // Verify subscribed await expect(toggleButton).toHaveText(/disable notifications/i); // Now unsubscribe await toggleButton.click(); await page.waitForTimeout(2000); // Verify subscription was removed const subscription = await page.evaluate(async () => { const registration = await navigator.serviceWorker.ready; return await registration.pushManager.getSubscription(); }); expect(subscription).toBeNull(); // Verify button text changed back await expect(toggleButton).toHaveText(/enable notifications/i); // Verify test buttons are no longer visible const testSuccessButton = page.getByRole('button', { name: /test success/i }); await expect(testSuccessButton).not.toBeVisible(); await page.close(); }); test('should persist clientId in localStorage', async () => { const page = await context.newPage(); await page.goto('/'); await page.waitForLoadState('networkidle'); // Wait for service worker await page.waitForFunction(() => 'serviceWorker' in navigator); // Enable notifications const toggleButton = page.getByRole('button', { name: /enable notifications/i }); await toggleButton.click(); await page.waitForTimeout(2000); // Verify clientId is stored in localStorage const clientId = await page.evaluate(() => { return localStorage.getItem('push-client-id'); }); expect(clientId).toBeTruthy(); expect(clientId).toMatch(/^client_[a-f0-9-]+$/); // Reload page and verify clientId persists await page.reload(); await page.waitForLoadState('networkidle'); const persistedClientId = await page.evaluate(() => { return localStorage.getItem('push-client-id'); }); expect(persistedClientId).toBe(clientId); await page.close(); }); });