Skip to main content
Version: 1.x

WebSocket & Real-Time Testing

Some SAP applications use WebSocket connections for real-time features such as collaborative editing, live notifications, and push-based data updates. This guide covers how to monitor, intercept, and test WebSocket communication alongside Praman's UI5 bridge.

SAP WebSocket Scenarios​

WebSockets appear in several SAP contexts:

  • SAP Fiori Notifications: The notification channel uses a WebSocket or long-polling connection to push alerts to the FLP shell.
  • Collaborative editing: SAP applications like S/4HANA journals or SAP Build may use WebSockets for multi-user editing.
  • OData V4 with server-sent events: Some OData V4 services push change notifications via WebSocket channels.
  • Custom real-time dashboards: Business applications that display live KPIs or process monitoring data.

Monitoring WebSocket Frames​

Playwright provides built-in WebSocket monitoring through the page.on('websocket') event:

import { test, expect } from 'playwright-praman';

test('monitor WebSocket messages', async ({ page, ui5 }) => {
const wsMessages: { type: 'sent' | 'received'; data: string }[] = [];

page.on('websocket', (ws) => {
ws.on('framesent', (frame) => {
wsMessages.push({ type: 'sent', data: frame.payload.toString() });
});
ws.on('framereceived', (frame) => {
wsMessages.push({ type: 'received', data: frame.payload.toString() });
});
});

await page.goto('/app#/dashboard');
await ui5.waitForUI5();

await test.step('Trigger an action that sends a WebSocket message', async () => {
await ui5.click({ id: 'subscribeBtn' });
await ui5.waitForUI5();
});

await test.step('Verify WebSocket communication occurred', async () => {
expect(wsMessages.length).toBeGreaterThan(0);
const sentMessages = wsMessages.filter((m) => m.type === 'sent');
expect(sentMessages.length).toBeGreaterThan(0);
});
});

Waiting for WebSocket Events​

Combine Playwright's WebSocket monitoring with Praman's waitForUI5() to wait for both the WebSocket message and the resulting UI update:

test('data refreshes after WebSocket push', async ({ page, ui5 }) => {
await page.goto('/app#/orders');
await ui5.waitForUI5();

await test.step('Wait for real-time update', async () => {
// Create a promise that resolves when a specific WS message arrives
const wsUpdate = new Promise<string>((resolve) => {
page.on('websocket', (ws) => {
ws.on('framereceived', (frame) => {
const payload = frame.payload.toString();
if (payload.includes('orderStatusChanged')) {
resolve(payload);
}
});
});
});

// Trigger the update from another source (API call, another user action, etc.)
await page.request.post('/api/orders/123/status', {
data: { status: 'Shipped' },
});

// Wait for the WebSocket message
const message = await wsUpdate;
expect(message).toContain('orderStatusChanged');

// Wait for UI5 to process the update
await ui5.waitForUI5();
});

await test.step('Verify UI reflects the update', async () => {
const status = await ui5.getText({ id: 'orderStatusField' });
expect(status).toBe('Shipped');
});
});

Intercepting WebSocket Connections​

Use Playwright's route API to intercept WebSocket upgrade requests for testing error scenarios:

test('app handles WebSocket connection failure gracefully', async ({ page, ui5 }) => {
// Block WebSocket connections
await page.route('**/ws/**', (route) => {
route.abort('connectionfailed');
});

await page.goto('/app#/dashboard');
await ui5.waitForUI5();

await test.step('Verify fallback behavior', async () => {
// App should fall back to polling or show a connection warning
const warning = ui5.control({
controlType: 'sap.m.MessageStrip',
properties: { type: 'Warning' },
});
await expect(warning).toBeDefined();
});
});

Combining WebSocket Testing with ui5.waitForUI5()​

The Praman bridge's waitForUI5() monitors the UI5 framework's internal pending request counter, but WebSocket messages are not tracked as pending requests by default. This means waitForUI5() may resolve before a WebSocket-triggered model update has been processed.

To handle this, add an explicit wait for the expected UI change after waitForUI5():

test('wait for WS-driven model update', async ({ page, ui5 }) => {
await ui5.waitForUI5();

// waitForUI5 ensures HTTP requests are done, but WS updates may still be pending
// Use Playwright's built-in waiting for the expected text change
await expect(page.getByText('Live: Connected')).toBeVisible({ timeout: 10_000 });

// Now the WS-driven update is reflected in the DOM
const status = await ui5.getText({ id: 'connectionStatus' });
expect(status).toBe('Connected');
});

Common Pitfalls​

  • waitForUI5() does not wait for WebSocket updates: The UI5 stability check monitors XHR/fetch requests and setTimeout callbacks, not WebSocket frames. Always add an explicit wait for the expected DOM or model change after a WebSocket event.
  • WebSocket timing in CI: WebSocket connections may take longer to establish in CI environments. Use generous timeouts (10-15 seconds) for WebSocket-related waits.
  • Binary WebSocket frames: Some SAP services send binary WebSocket frames (e.g., for file collaboration). Use frame.payload as a Buffer instead of calling .toString().
  • Multiple WebSocket connections: An SAP FLP page may open several WebSocket connections (notifications, collaboration, analytics). Filter by URL pattern to isolate the one you are testing.