WebSocket-based collaboration features often look stable in a demo, then fail under the conditions that real users create by accident, switching tabs, losing Wi-Fi for a few seconds, rejoining from another device, or leaving a document open long enough for auth state to expire. The hard part is not opening a socket. The hard part is keeping the application honest about who is present, what state is current, and which updates should be replayed after a reconnect.

That is why teams that ship collaborative web apps need more than a happy-path smoke test. They need a repeatable way to test websocket reconnect flows, presence indicator testing, and state convergence after interruptions. This checklist is written for frontend engineers, QA teams, and platform engineers who want a practical approach to realtime app regression testing without turning every test into a brittle network simulation project.

For background on the broader testing discipline and the role of automation in maintaining confidence, see software testing, test automation, and continuous integration.

What usually breaks in collaborative realtime apps

Live collaboration features fail in ways that are easy to miss during manual QA because the UI still appears interactive. The document may load, the cursor may move, and the presence list may render, while the underlying session is already drifting out of sync.

Common failure modes include:

  • Reconnects that create duplicate subscriptions or duplicate event handlers
  • Presence indicators that remain green after a disconnect, then correct themselves only after a hard refresh
  • Missed replay of buffered edits after transient network loss
  • Out-of-order messages that overwrite newer state with stale state
  • Tab suspension, background throttling, or focus changes that delay heartbeats
  • Token refresh flows that reconnect the socket but do not restore the correct room or document context
  • Multi-device conflicts where one client silently believes it is authoritative

A realtime UI can be visually responsive while its data model is already incorrect. The test plan should treat connection state, presence state, and document state as separate concerns.

The practical implication is that a checklist should not ask only whether the socket reconnects. It should ask whether the product restores the right session, emits the right presence transitions, and converges on the same document state after interruption.

Define the state model before writing tests

Before writing test cases, teams should agree on the states they expect the app to expose. If the product has no explicit state model, tests become vague and failures become hard to classify.

At minimum, document these states:

Connection state

Examples include connecting, connected, reconnecting, disconnected, and auth_failed. A reconnect test is only useful if the UI and telemetry can distinguish a recoverable disconnect from a terminal one.

Presence state

Examples include online, idle, away, offline, typing, cursor_visible, or editing. Presence is often derived from a combination of heartbeat, tab visibility, and application-level activity, so the rules should be explicit.

Collaboration state

Examples include active room membership, document version, pending optimistic operations, unsaved local changes, acked operations, and conflict resolution status.

Recovery policy

Specify what the client should do after a reconnect, such as resubscribe, re-authenticate, fetch the latest snapshot, replay unacked operations, and reconcile local drafts.

A good test suite checks each of these states independently, then verifies how they interact under failure.

Checklist 1, verify the reconnect lifecycle itself

If the socket reconnects but the client never reattaches to the correct room or stream, the UI may look normal while it is actually detached.

Validate these cases

  • Initial connection succeeds after cold start
  • Temporary disconnect triggers the expected reconnect behavior
  • Reconnect backoff does not spin too fast or too slowly for the product policy
  • Reconnect after auth refresh restores the original session context
  • Reconnect after page navigation or soft reload does not duplicate subscriptions
  • Reconnect from background tab resumes without leaving stale presence behind

What to assert

  • Connection status changes are visible in logs, telemetry, or DOM where intended
  • The client does not create multiple active sockets for one browser session
  • The reconnect path resubscribes to the same channels or document IDs
  • Server acknowledgments after reconnect match the current session, not a previous one
  • Error states are surfaced when reconnect is impossible

Example with Playwright and a network interruption

A simple way to validate client recovery is to interrupt the network and observe whether the app returns to a usable state.

import { test, expect } from '@playwright/test';
test('reconnects after a transient network drop', async ({ page }) => {
  await page.goto('/doc/123');
  await expect(page.getByTestId('connection-status')).toHaveText('connected');

await page.context().setOffline(true); await expect(page.getByTestId(‘connection-status’)).toHaveText(/disconnected|reconnecting/);

await page.context().setOffline(false); await expect(page.getByTestId(‘connection-status’)).toHaveText(‘connected’); await expect(page.getByTestId(‘collaboration-state’)).toHaveText(‘synced’); });

This kind of test is useful because it focuses on observable behavior, not on implementation details of the socket library. The tradeoff is that browser-level offline simulation cannot reproduce every production failure, especially proxy resets, TCP half-open connections, or server-side room eviction. For deeper validation, pair browser tests with protocol-level tests.

Checklist 2, test presence indicator behavior across lifecycle changes

Presence indicator testing is usually where teams discover hidden assumptions. A green avatar often means much less than it appears to mean.

Questions to answer

  • Does presence appear only after the client has authenticated and joined the room?
  • How quickly should presence disappear after disconnect, and is that timeout documented?
  • Does tab blur change presence to away, or only after a longer idle period?
  • Are multiple tabs from the same user shown as one identity or multiple sessions?
  • What happens if one device closes cleanly while another loses connectivity?
  • Does a reconnect preserve typing state, or should typing reset immediately?

Assertions worth automating

  • A user is not shown as online before the server has confirmed membership
  • A clean leave removes presence promptly
  • An unclean disconnect eventually expires presence even if no leave event arrives
  • Presence updates do not oscillate during short network drops
  • The UI does not show stale typing indicators after reconnect

Common edge case, presence lag after reconnect

If a reconnect path restores document content but not the latest heartbeat, other users may see the person as online while the server considers the session stale. That gap can be small and still be meaningful in a chat or cursor-sharing UI.

A useful pattern is to assert both sides, what the local UI shows and what the server reports through an API or test hook. If the product lacks an introspection endpoint, consider adding one for test environments only.

Checklist 3, verify live state convergence, not just event delivery

In collaborative apps, sending an event is not the same as converging on the correct state. A client can receive an edit event, apply it, and still end up wrong if a reconnect race, optimistic update, or ordering issue occurs.

Test these scenarios

  • Two users edit the same region at nearly the same time
  • One client goes offline, makes local changes, then reconnects
  • One client receives delayed events after already fetching a newer snapshot
  • A room membership change arrives before the latest document version
  • A stale update is replayed after reconnect and must be ignored

Assertions to include

  • The latest authoritative snapshot wins over stale local state when appropriate
  • Optimistic changes are either confirmed or cleanly rolled back
  • Duplicate events do not duplicate content
  • Out-of-order updates are rejected or merged according to the conflict policy
  • Replayed events do not reopen closed UI states such as resolved comments or dismissed notifications

Practical test design

The most stable approach is to define a small set of invariants, then verify them after injected disruptions. For example:

  • Document text matches the server version after reconnect
  • Cursor position is restored only if the cursor still belongs in the current document version
  • Unsaved drafts survive a reconnect but do not overwrite newer server data without confirmation
  • Presence list reflects current membership, not cached membership

This is the difference between testing message transport and testing collaborative behavior.

Checklist 4, simulate tab switches, backgrounding, and browser suspension

Many realtime bugs are not network bugs. They are lifecycle bugs.

Browsers can throttle timers, delay heartbeats, or pause a tab that has been backgrounded for long enough. Some app frameworks also pause rendering or de-prioritize state updates when a tab is hidden. That means the test matrix should include focus and visibility changes.

Run tests for

  • Switching tabs and returning after a short delay
  • Leaving the app in the background and resuming later
  • Minimizing and restoring the browser window
  • Locking and unlocking the device where your test environment supports it
  • Opening the same document in two tabs from the same browser profile

What to verify

  • Heartbeats resume correctly after the tab is foregrounded
  • Presence transitions are intentional, not accidental side effects of throttled timers
  • Reconnect logic does not fire repeatedly when the browser is hidden
  • Local edits are preserved across backgrounding
  • No duplicate listeners are attached after focus events

A common failure mode is that the app treats visibilitychange as a soft disconnect, then forgets to clear the state when the tab becomes active again. Another is that it reconnects too aggressively and causes extra server load.

Checklist 5, test latency spikes and packet loss, not just full outages

A fully offline simulation is useful, but many production issues happen under partial degradation. The connection stays up, yet messages arrive late enough to trigger stale UI.

Add these scenarios

  • 200 ms to 1000 ms latency spikes
  • Brief packet loss or dropped frames in a controlled environment
  • Slow authentication refresh before reconnect
  • Long server response times during snapshot fetch
  • Message burst after reconnect, where several queued events arrive together

Why this matters

If the client assumes immediate acks, a short delay can trigger duplicate retries. If the UI assumes ordering without sequence numbers or version checks, a late event can overwrite newer state. If the server resends state after reconnect, the client must be able to ignore duplicates.

Useful assertions

  • Retry logic does not create duplicate writes
  • Timeout UI is visible only when thresholds are actually exceeded
  • Loading and syncing indicators match the real connection and recovery state
  • The app remains usable while it waits for eventual consistency

For teams using browser automation, it is often enough to layer a network proxy or browser-level throttling over a few targeted tests. You do not need to model every packet-level behavior to catch the majority of regressions.

Checklist 6, inspect server-side events and client-side observability

Testing realtime systems without logs is like testing database migrations without query output. The UI might tell part of the story, but it rarely explains the failure.

Instrument these signals

  • Connection opened, closed, reconnect attempt, reconnect success, reconnect failure
  • Subscription joined and left
  • Presence published and expired
  • Snapshot fetched and applied
  • Operation queued, acked, retried, or rejected
  • Conflict detected or resolution rule applied

Useful properties in logs or telemetry

  • Session or connection ID
  • Document or room ID
  • Version number or sequence number
  • Reason for disconnect, if known
  • Retry count and backoff stage
  • Client time versus server time when ordering matters

When a test fails, these fields help determine whether the bug is in transport, auth, sequencing, or UI rendering. This also makes flaky test triage much faster, because the failure can be categorized instead of re-run blindly.

Checklist 7, cover auth expiry and session rotation

Auth is part of the reconnect story, especially for apps that keep sockets open for a long time. A token that expires mid-session can create a confusing split brain, the page still looks live, but the next reconnect fails.

Include these cases

  • Token expires while the app is idle
  • Reconnect requires token refresh before subscription restoration
  • Refresh succeeds but the original room membership is lost and must be rejoined
  • Refresh fails and the app should move to a clear unauthenticated state
  • A second device logs in and invalidates the first session, if that is the product rule

Assertions

  • The client does not loop forever on auth failures
  • The user sees a meaningful state, not just a spinner
  • Sensitive collaboration data is not shown after session invalidation
  • Recovery preserves unsaved local work when policy allows it

This area is often missed because auth failures are less common than disconnects, but when they happen, the user experience tends to be worse.

Checklist 8, test multi-client consistency on the same document

Single-client tests are necessary, but they are not enough. Realtime collaboration problems often appear only when a second or third client joins the same room.

Scenarios to automate

  • Two users join, one disconnects, then rejoins
  • One user edits while another is offline and later reconnects
  • One user closes the tab without leaving cleanly
  • Multiple devices under the same account join the same document
  • One client receives older updates after reconnect while another has already advanced the document

What should remain true

  • All active clients eventually converge on the same document state
  • Presence lists reflect each active participant according to product rules
  • A reconnecting client does not erase newer edits from another client
  • The order of arrivals does not depend on a single machine’s timing quirks

A lot of teams discover here that they have been testing only the happy path where one tab acts like a whole product. That is not enough for live collaboration QA.

A minimal automation pattern that scales

You do not need a giant framework to start. What you need is a small set of reusable helpers that model connection interruption, state assertions, and cleanup.

Example helper structure

typescript

async function waitForSynced(page) {
  await page.getByTestId('connection-status').waitFor({ state: 'visible' });
  await expect(page.getByTestId('collaboration-state')).toHaveText('synced');
}

async function simulateReconnect(page) { await page.context().setOffline(true); await page.context().setOffline(false); await waitForSynced(page); }

The point of helpers like these is not abstraction for its own sake. It is to make failures readable. If a test fails, engineers should be able to tell whether the problem was reconnect detection, session restoration, or state convergence.

How to choose what to automate first

A practical selection order helps teams avoid overinvesting in rare edge cases while missing the common ones.

Automate first

  • Core reconnect flow after brief network interruption
  • Presence appears and disappears correctly
  • Rejoin after tab switch or refresh
  • One multi-client conflict path
  • One auth-expiry recovery path

Automate next

  • Latency and delayed ack scenarios
  • Background tab behavior
  • Multi-document or multi-room switching
  • Reconnect after app idle periods
  • Duplicate subscription detection

Leave for targeted manual testing or lower-level integration tests

  • Protocol-level packet reordering beyond what your app abstraction exposes
  • Browser or OS quirks that only apply to a narrow population
  • Rare compatibility issues with specific proxies or enterprise networks

The tradeoff is simple, automate the behaviors that are expensive to discover late, and keep lower-level transport chaos tests focused so they remain maintainable.

CI advice for realtime regression testing

Realtime tests can be stable in CI if they are designed around deterministic assertions and bounded timing.

Practical rules

  • Use explicit timeouts and retry budgets, not arbitrary sleeps
  • Isolate tests by room, document ID, and test user
  • Clean up sessions after each run
  • Seed test data so reconnect assertions start from a known version
  • Tag slow network and multi-client scenarios separately so they can run on a useful schedule

A sample GitHub Actions workflow can run the core reconnect suite on every pull request and the broader latency or multi-client matrix on a nightly schedule.

name: realtime-tests
on:
  pull_request:
  schedule:
    - cron: '0 2 * * *'
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test -- --grep "reconnect|presence|collaboration"

The important part is not the CI system itself. It is that the test suite reflects the failure modes you actually care about, and that failures are actionable rather than noisy.

A compact checklist you can paste into a test plan

Use this as a starting point for a release checklist or QA matrix:

  • Verify initial socket connection and room subscription
  • Simulate transient disconnect and confirm clean reconnect
  • Confirm no duplicate sockets or duplicate event handlers after reconnect
  • Validate presence appears only after server confirmation
  • Confirm presence clears on clean leave and expires on silent disconnect
  • Test typing, cursor, and online indicators after reconnect
  • Rejoin after tab switch, backgrounding, and page restore
  • Verify document state converges after offline edits
  • Confirm stale events are ignored after newer snapshots arrive
  • Test auth expiry, token refresh, and invalid session handling
  • Run at least one two-client conflict scenario
  • Inspect logs or telemetry for connection, subscription, and replay events

Final takeaway

The best realtime tests do not try to prove that WebSocket reconnects are possible. They prove that the application behaves correctly when reconnects happen at awkward moments. That distinction matters because the visible bug is often not the disconnect itself, it is the stale presence badge, duplicated edit, missing rejoin, or silently diverged document that follows.

If you want a durable test strategy, focus on a small set of invariants, make presence and connection states explicit, and test the same flow under a few realistic disruptions, offline blips, tab switches, delayed messages, and auth rotation. That combination catches the issues that manual QA tends to miss, and it gives engineering teams a shared language for discussing failures in live collaboration systems.