Skip to main content

How to Test WebSockets and Live UI Updates

Short answer

Live dashboards fail on missed events after reconnect, out-of-order message handling, and stale UI that looks fresh—not on whether one message renders once. Use test hooks or probe fallbacks instead of arbitrary timeouts, simulate disconnect/reconnect, and assert event ordering via probe or monotonic sequence ids.

Part of Testing Guides by UI patterns.

Who this is for

Teams shipping real-time dashboards, chat, collaborative editors, trading blotters, or ops consoles with WebSockets, SSE, or Socket.IO. Any UI that updates without full page reload.

Why testing live updates matters

Stale live UI causes operational and financial harm:

  • Wrong balances — reconnect misses ledger events; trader acts on stale P&L.
  • Duplicate actions — double message delivery creates duplicate orders.
  • Missed alerts — incident banner never appears after WS drop; on-call blind.
  • Collaboration conflicts — two users edit; last-write wins without CRDT merge visibility.
  • Compliance — audit stream gap after reconnect; regulatory replay incomplete.

Assert probe state and sequence monotonicity—not single snapshot after waitForTimeout.

Complexity map

ScenarioEdge caseWhy tests breakApproach
Initial snapshot + streamGap on connectMissing rowsProbe baseline then events
ReconnectMissed messages while downStale countAssert resync banner + probe
Out-of-orderseq 3 before 2Corrupt stateProbe rejects or buffers
Duplicate deliverySame event id twiceDouble incrementIdempotent probe
Heartbeat timeoutSilent dropFrozen UIForce close socket
Auth on WSToken expires mid-session401 on subscribeRefresh token helper
Room/channel joinWrong tenant roomCross-tenant leakProbe isolation
SSE vs WSDifferent client pathUntested fallbackSeparate spec or feature flag
BackpressureBurst 100 eventsUI freezeProbe final aggregate
Optimistic UIRollback on errorWrong displayPoll probe after error event
Binary framesProtobufHard to assertDecode via test hook
CI proxyWS blockedFalse pass locallyws:// test gateway

Wait strategies (no arbitrary sleep)

Pattern A — test hook promise

await page.goto('/dashboard');
await page.evaluate(() => window.__TEST_WS__.ready());
await page.evaluate(() => window.__TEST_WS__.emit('balance_update', { accountId: 'a1', cents: 5000 }));
await expect(page.getByTestId('balance-a1')).toHaveText('$50.00');

Expose __TEST_WS__ only when NODE_ENV=test.

Pattern B — probe poll

await request.post('/api/test/inject-event', {
data: { runId, type: 'balance_update', accountId: 'a1', cents: 5000 },
});
await expect.poll(async () => {
const v = await page.getByTestId('balance-a1').textContent();
return v;
}).toBe('$50.00');

Server injects WS message to connected test client or writes DB that UI polls.

Pattern C — waitForEvent (Playwright)

page.on('websocket', ws => {
ws.on('framereceived', frame => {
if (frame.payload.includes('balance_update')) resolved = true;
});
});

Use when you control WS endpoint and payload format.

Reconnect scenario

await page.evaluate(() => window.__TEST_WS__.disconnect());
await expect(page.getByRole('status')).toContainText(/reconnecting|connection lost/i);
await page.evaluate(() => window.__TEST_WS__.reconnect());
await expect.poll(async () =>
(await request.get(`/api/test/ws-sync-state?runId=${runId}`)).json()
).toMatchObject({ synced: true, lastSeq: expect.any(Number) });

Inject events during disconnect; after reconnect probe must show gap filled or explicit conflict UI.

Requirement slices to cover

Track event_type (balance_update, order_fill, chat_message) and reconnect sync_outcome. If prod shows frequent reconnects but tests never simulate drop, run /testchimp evolve—link data grids scenarios when live rows update admin tables.

CI checklist

  1. Test gateway or inject route—no prod WS in PR CI
  2. Reconnect spec mandatory for financial/ops UIs
  3. Idempotent handling spec for duplicate event ids
  4. Auth refresh spec if WS uses short-lived JWT
  5. Never waitForTimeout(5000) as primary wait
  6. Cross-tenant negative: user B must not receive user A events (probe)

Anti-patterns

Anti-patternWhy it failsBetter approach
Fixed sleep after actionRacepoll probe or hook
Assert DOM onceMiss reconnect gapDisconnect test
Prod WS in CIFlaky externalTest gateway
Ignore orderingCorrupt stateseq id monotonic
UI count onlyMiss duplicatesProbe id set
Skip auth refreshMid-test dropToken refresh Arrange

Example scenario

Situation: Ops dashboard loses WebSocket during incident; reconnects after 30s burst of alerts.

Expected outcome: All alert ids appear once; timeline ordered; no stale 'all clear' banner.

Why UI-only automation breaks: UI shows updated count but missed alert id 8842—on-call never sees critical page.

  1. Arrange: Seed alert stream ids 8840-8850; connect dashboard.
  2. Act: Disconnect WS, inject 8841-8850 server-side, reconnect.
  3. Assert: Probe timeline ids monotonic include 8842; DOM role=alert count matches probe; no duplicate ids.

TestChimp workflow: Instrument ws_reconnect with gap_seconds; expand tests when prod reconnect rate rises.

Same Arrange/Act/Assert pattern as expired-coupon checkout.

Connect scenarios to your QA workflow

Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).

External references

Frequently asked questions

How do I wait for WebSocket messages in Playwright?

Prefer test hook that resolves when handler runs, or poll probe until DB/UI reflects event. Optionally listen to page websocket framereceived for known payload markers—avoid fixed timeouts.

How do I test reconnect after connection drop?

Force disconnect via test hook, inject events while offline, reconnect, assert resync state via probe and ordered event ids in UI timeline.

SSE vs WebSocket in E2E?

If prod uses both via feature flag, cover both or assert probe parity on critical event types. SSE may use EventSource hooks similar to WS test gateway.

How do I prevent duplicate event bugs?

Send same event id twice in inject route; probe and UI must show single logical effect—idempotent handlers.

How do auth expiring tokens affect WS tests?

Seed short-lived token, advance clock or wait to expiry, assert refresh and resubscribe without stale UI—probe subscription active.

Which event_types should TrueCoverage prioritize?

Compare prod ws event_type distribution to test inject coverage—expand scenarios for high-traffic types like balance_update or order_status.

Can I inject WS messages without browser WS?

Yes—server inject route writes DB and pushes to test client, or UI polls—probe remains authoritative for financial UIs.

Apply these patterns in your repo

Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.

Start free on TestChimp · Book a demo