React Testing & Parity Scorecard

Parity with the Blazor portal is enforced by tests at three altitudes: the registry (does the React pack cover the Blazor control vocabulary?), the renderer (do the components actually render and bind?), and the portal end-to-end (do real documentation pages render through the React SPA against a live mesh?).

1. The parity ratchet — render/parity.test.ts

clients/react/src/render/parity.test.ts pins the authoritative Blazor vocabulary: the *Control / *Skin types in src/MeshWeaver.Layout (the $type is the class name minus the suffix). When a new control is added to Layout, its $type is added to the list — and the test fails until the React pack covers it, keeping the port at 1:1:

it("the placeholder long-tail only ever shrinks", () => {
  const pinned: string[] = [];
  expect([...placeholderControlTypes].sort()).toEqual(pinned.sort());
});

Adding a new placeholder fails here — every new control must ship a real implementation. A companion test (the un-placeholdered controls are all real, distinct components) further asserts those five are real, distinct React components, so none may regress to a shared badge.

2. The vitest suites

cd clients/react && npm test        # renderer + controls
cd clients/grpc-web && npm test     # transport + thread submission

In clients/react/src:

Suite Covers
area/pointer.test.ts, area/source.test.ts JSON pointer resolution, RFC 7396 merge-patch, the StaticAreaSource contract (optimistic updates)
render/core.test.tsx The renderer core in jsdom: $type dispatch, skin popping, binding resolution, event emission
render/parity.test.ts The ratchet above
render/gallery.test.tsx Rendering the control gallery
controls/dialog.test.tsx, controls/itemTemplate.test.tsx, controls/threadChat.test.tsx Per-control behavior — the chat suite covers thread watching, queued bubbles, composer gating, submission
theme/theme.test.tsx The Blazor-compatible localStorage contract, mode resolution, cross-instance sync
live/grpcSource.test.ts Folding the live wire: Full snapshots, RFC 6902 patch arrays, JSON-encoded instance keys, event posting

In clients/grpc-web/src: envelope.test.ts (delivery JSON build/parse), connection.test.ts (the Connect+Deliver split, ack, demux), mesh.test.ts (the ops surface), threads.test.ts (the startThread / submitMessage wire shapes and guards).

The server side of the split has its own C# round-trip test: MeshGrpcTransportTest.WebSplit_request_round_trips_via_connect_and_deliver (test/MeshWeaver.Hosting.Grpc.Test) proves a request posted through the unary Deliver comes back down the server-streaming Connect.

3. The E2E scorecard — ReactDocViewsTest

test/MeshWeaver.Portal.E2E.Test/ReactDocViewsTest.cs is the doc-views-through-React scorecard: it drives Playwright at {E2E_BASE_URL}/app/#/{docPath} for every documentation page with embedded interactive examples — the same page list the Blazor-side DocExamplesRenderTest covers — and asserts each page reaches real rendered content through the React SPA over live gRPC-web.

One Playwright detail worth knowing when writing similar tests: never wait for NetworkIdle against the SPA — it holds the gRPC-web Connect stream open for its whole life, so the network never goes idle.

E2E_REACT=true dotnet test test/MeshWeaver.Portal.E2E.Test \
  --filter "FullyQualifiedName~ReactDocViewsTest" --no-restore
Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.