</>

Technology

Playwright

Difficulty

Advanced

Interview Question

How would you test a real-time notification feature using WebSockets in Playwright?

Answer

Scenario: Testing Real-Time WebSocket Notifications

Strategy 1 — Wait for WebSocket Frame and Verify UI Update

TypeScript
import { test, expect } from '@playwright/test';

test('receive real-time notification via WebSocket', async ({ page }) => {
  await page.goto('/dashboard');

  // Wait for WebSocket connection to be established
  const ws = await page.waitForEvent('websocket');
  console.log('WebSocket URL:', ws.url());

  // Wait for the server to send a notification frame
  const frameSentPromise = ws.waitForEvent('framesent');

  // Or wait for incoming frames
  const frameReceivedPromise = ws.waitForEvent('framereceived');

  // Trigger the action that causes a notification (via another tab, API, etc.)
  const frame = await frameReceivedPromise;
  const data = JSON.parse(frame.payload as string);

  console.log('Received:', data);
  expect(data.type).toBe('notification');
  expect(data.message).toBeTruthy();

  // Verify the notification appears in the UI
  await expect(page.locator('.notification-badge')).toBeVisible();
  await expect(page.locator('.notification-item').first()).toContainText(data.message);
});

Strategy 2 — Trigger Notification via Second Browser Tab

TypeScript
test('admin action triggers user notification', async ({ browser }) => {
  // Create two contexts: user and admin
  const userContext  = await browser.newContext({ storageState: '.auth/user.json' });
  const adminContext = await browser.newContext({ storageState: '.auth/admin.json' });

  const userPage  = await userContext.newPage();
  const adminPage = await adminContext.newPage();

  // User opens their notifications page
  await userPage.goto('/dashboard');
  await expect(userPage.locator('.notification-count')).toHaveText('0');

  // Wait for WebSocket connection on user's page
  const ws = await userPage.waitForEvent('websocket');
  const notificationPromise = userPage.locator('.notification-popup').waitFor({ state: 'visible' });

  // Admin triggers an action that sends a notification
  await adminPage.goto('/admin/users');
  await adminPage.locator(`[data-user-email="user@test.com"]`).getByRole('button', { name: 'Send Alert' }).click();
  await adminPage.getByRole('button', { name: 'Confirm' }).click();

  // User's page should show the notification via WebSocket
  await notificationPromise;

  await expect(userPage.locator('.notification-popup')).toContainText('New alert from admin');
  await expect(userPage.locator('.notification-count')).toHaveText('1');

  await userContext.close();
  await adminContext.close();
});

Strategy 3 — Mock WebSocket Server (Pure Frontend Test)

TypeScript
test('UI updates when WebSocket message arrives', async ({ page }) => {
  await page.goto('/live-feed');

  // Intercept the WebSocket and inject a fake message
  await page.evaluate(() => {
    // Override WebSocket to send a test message immediately
    const OrigWS = WebSocket;
    (window as any).WebSocket = class extends OrigWS {
      constructor(url: string) {
        super(url);
        setTimeout(() => {
          const event = new MessageEvent('message', {
            data: JSON.stringify({
              type: 'order-update',
              orderId: 'ORD-123',
              status: 'shipped',
              message: 'Your order has been shipped!',
            }),
          });
          this.dispatchEvent(event);
        }, 1000);
      }
    };
  });

  // Wait for notification to appear in UI
  await expect(page.locator('.live-notification')).toBeVisible({ timeout: 5000 });
  await expect(page.locator('.live-notification')).toContainText('Your order has been shipped!');
});

Strategy 4 — Trigger via API and Verify WebSocket Delivery

TypeScript
test('order status WebSocket notification flow', async ({ page, request }) => {
  await page.goto('/orders/ORD-123');

  // Assert initial status
  await expect(page.locator('.order-status')).toHaveText('Processing');

  // Trigger status change via API (simulates backend update)
  await request.patch('/api/orders/ORD-123', {
    data: { status: 'shipped' },
    headers: { Authorization: `Bearer ${process.env.ADMIN_TOKEN}` },
  });

  // WebSocket pushes update — verify UI updates without page reload
  await expect(page.locator('.order-status')).toHaveText('Shipped', { timeout: 10000 });
  await expect(page.locator('.status-badge')).toHaveClass(/badge-shipped/);
});

Follow AutomateQA

Related Topics