stream-ssr.test.ts
Overview
The stream-ssr.test.ts file contains automated end-to-end (E2E) tests designed to verify and validate the behavior of Server-Side Rendering (SSR) with streaming and partial hydration in a React application environment. Utilizing the Playwright testing framework, it simulates browser interactions to ensure that the SSR pages:
Render expected initial (server-side) markup correctly.
Hydrate in the browser without errors.
Support partial hydration where components hydrate asynchronously after the initial render.
Maintain UI consistency before and after client hydration.
This test suite is critical to guarantee the reliability, correctness, and stability of the streaming SSR and partial hydration features in the application.
Detailed Explanation of Test Suite
Test Framework and Setup
Testing Framework: Playwright is used for browser automation and assertions.
Test Suite: Defined using
test.describeto group related tests under the "Stream SSR" label.Error Monitoring:
Each test exposes a client-side functionconsoleErrorto capture any uncaught errors fromwindow.onerrorduring page load and hydration phases. These errors are collected in a locallogarray and asserted to be empty, ensuring no runtime errors occur during SSR and hydration.
Tests
1. Basic SSR
Purpose:
To verify that the basic server-side rendered page renders placeholders initially and then hydrates with the expected data, without any client-side errors.
Test Steps:
Initialize an empty
logarray for client-side error collection.Expose
consoleErrorfunction on the page to push errors intolog.Override
window.onerrorto forward errors toconsoleError.Navigate to the
./basic-ssrroute.Assert the presence and visibility of the following text nodes:
"result:undefined"— placeholder state before hydration."result:SSR Works"— hydrated state with data."history:[null,"SSR Works"]"— debug history of data state.
Assert that no errors were logged (
log.length === 0).
Usage Example:
// Inside the test suite
await page.goto('./basic-ssr', { waitUntil: 'commit' });
await expect(page.getByText('result:undefined')).toBeVisible();
await expect(page.getByText('result:SSR Works')).toBeVisible();
await expect(page.getByText('history:[null,"SSR Works"]')).toBeVisible();
expect(log).toHaveLength(0);
2. Partially Hydrate
Purpose:
To validate partial hydration behavior where some parts of the UI hydrate immediately while others hydrate with a delay (e.g., via Suspense boundaries). It checks that the page correctly renders placeholders, then updates to hydrated content, and that no client errors occur.
Test Steps:
Initialize an empty
logarray for capturing errors.Expose the
consoleErrorfunction and overridewindow.onerrorsimilarly to the first test.Navigate to the
./partially-hydratepage.Assert visibility of the following text nodes in order:
"first data:undefined"— first component placeholder before hydration."second data (delayed hydration):undefined"— second component placeholder with delayed hydration."first data:SSR Works"— hydrated first component data."second data (delayed hydration):SSR Works"— hydrated second component data after delay."first history:[null,"SSR Works"]"— debug history for first component data."second history:[null,"SSR Works"]"— debug history for second component data.
Confirm no errors logged (
log.length === 0).
Usage Example:
await page.goto('./partially-hydrate', { waitUntil: 'commit' });
await expect(page.getByText('first data:undefined')).toBeVisible();
await expect(page.getByText('second data (delayed hydration):undefined')).toBeVisible();
await expect(page.getByText('first data:SSR Works')).toBeVisible();
await expect(page.getByText('second data (delayed hydration):SSR Works')).toBeVisible();
await expect(page.getByText('first history:[null,"SSR Works"]')).toBeVisible();
await expect(page.getByText('second history:[null,"SSR Works"]')).toBeVisible();
expect(log).toHaveLength(0);
Important Implementation Details
Error Capturing via
window.onerror:
The tests intercept uncaught errors on the page by overridingwindow.onerrorand forwarding those errors to a Playwright-exposed function. This mechanism ensures that any client-side exceptions during hydration or rendering are caught and can cause test failures if present.Use of Playwright's
page.gotowithwaitUntil: 'commit':
The navigation waits until the network response is committed, meaning the server has sent the initial HTML. This timing is crucial to test SSR content before client-side hydration begins.Text Assertions Using
getByText:
The tests use Playwright’sgetByTextto assert that specific text snippets are visible. These snippets represent key UI states (placeholders vs hydrated data) and debug information (history), enabling verification of SSR and hydration correctness.No Explicit Waits for Hydration:
The presence of hydrated text after placeholders implies the test waits implicitly for React hydration to complete and update the DOM, showcasing that hydration happens naturally during test execution.
Interaction with Other System Components
SSR Pages (
./basic-ssr&./partially-hydrate):
These tests directly interact with SSR pages that implement server-side rendering and partial hydration strategies. The pages are expected to produce initial HTML with placeholders and then hydrate with actual data asynchronously.React Suspense and Data Fetching Hooks:
The tested pages utilize React Suspense boundaries and data fetching hooks that throw promises to suspend rendering until data is ready. This behavior manifests in the test assertions of placeholder (undefined) and hydrated (SSR Works) states.Debugging Utilities:
The tests verify debug output such ashistory:[null,"SSR Works"]which is generated by hooks that track data state changes during SSR and hydration, helping ensure data consistency.Playwright Test Runner:
Drives the tests by automating browser interactions, capturing errors, and asserting UI states.
Visual Diagram: Test Structure and Flow
flowchart TD
A[Start Test Suite: Stream SSR]
A --> B[Setup: Expose consoleError, Override window.onerror]
B --> C{Test Case?}
C -->|Basic SSR| D[Navigate to ./basic-ssr]
C -->|Partially Hydrate| E[Navigate to ./partially-hydrate]
D --> F[Check placeholder texts visible]
F --> G[Check hydrated texts visible]
G --> H[Assert no errors logged]
E --> I[Check placeholders for first and second data]
I --> J[Check hydrated data for first and second data]
J --> K[Check debug history texts]
K --> L[Assert no errors logged]
H --> M[End Basic SSR Test]
L --> N[End Partial Hydrate Test]
M --> O[End Test Suite]
N --> O
Summary
The stream-ssr.test.ts file serves as a crucial quality assurance layer for the application's streaming SSR and partial hydration features. By programmatically navigating to SSR pages, intercepting errors, and asserting the presence of expected UI states across hydration phases, it ensures that:
Server-rendered content is correct and visible.
Client-side hydration updates the UI appropriately.
Partial hydration with Suspense boundaries works as intended.
No client-side runtime errors occur during streaming and hydration.
This test file complements the SSR and streaming infrastructure by providing automated verification, facilitating confident development and deployment of SSR features in the React application.