Files
IGP-Fan-Reference/docs/AI Gen Docs/test-blazor-hydration-timing.md
T
2026-05-31 14:33:58 -04:00

76 lines
3.5 KiB
Markdown

---
type: Task
status: AI Gen TODO
category: QA
isAgentGenerated: "true"
---
# Test: Blazor WASM Hydration Timing and State Reconciliation
## Description
Verify that the Blazor WASM client correctly reconciles state during hydration — the prerendered HTML should match the client-rendered output, and user interactions initiated **before** Blazor finishes loading should either be queued or gracefully ignored, not cause a crash or a stale render.
## Rationale
The app uses Blazor WASM with server-side prerendering. During the hydration gap (between `window.onload` and Blazor attaching its event handlers), the page shows static prerendered HTML. If a user (or test) interacts with a `<select>` or button during this gap, Blazor may:
- Lose the native-DOM change on re-render (the select value reverts).
- Miss the event entirely (no handler is connected yet).
- Throw a JS interop exception after hydration completes.
Our existing tests all navigate with `waitUntil: 'load'` and immediately start clicking. The `debug_initial_state.js` script revealed that Blazor components re-render asynchronously after load, destroying and recreating the DOM elements. This gap is a real source of flakiness.
## Playwright Feature
This test uses **`page.waitForResponse()`** to wait for a specific WASM resource (`dotnet.js` or `dotnet.wasm`) to confirm the .NET runtime has loaded, plus **`page.evaluate()` with polling** to detect when Blazor's component tree is fully hydrated.
### Approach
```js
// Wait until the Blazor runtime signals it's ready
await page.waitForResponse(response =>
response.url().includes('dotnet.wasm') && response.status() === 200
);
// OR poll for a Blazor-specific DOM signal
await page.waitForFunction(() => {
// Blazor sets the `_blazorCircuitId` or similar on the root element
// Note: adjust the selector to match the app's actual signal
return document.querySelector('select')?.classList.contains('blazor-hydrated')
|| document.querySelector('.keyContainer > div') !== null;
});
```
### Test Cases
| Scenario | Interaction Timing | Expected Behavior |
|---|---|---|
| **Early select** | Select faction BEFORE dotnet.wasm loads | Select reverts on hydration, but WASM should apply the correct default filter |
| **Early click** | Click "Clear Build Order" before Blazor ready | Click is ignored or queued; no crash |
| **Early keyboard** | Press Q/W/E before key handler attached | Keys are ignored; no crash; no entity added |
| **Mid-hydration** | Interact during component re-render (DOM destruction phase) | Interaction should be lost but never crash |
| **Late interaction** | Wait for `dotnet.wasm` response, THEN interact | Should work correctly (this is the happy path that already passes) |
### Detecting Hydration Completion
Since Blazor WASM with prerendering doesn't emit a built-in "I'm done" event, the most reliable signal is:
```js
await page.waitForFunction(() => {
// Wait for at least one Blazor-triggered re-render by checking
// the keyContainer has child divs with keyboard buttons
const container = document.querySelector('.keyContainer');
if (!container) return false;
const buttons = container.querySelectorAll(':scope > div');
return buttons.length === 19 || buttons.length > 10;
});
```
### What This Test Catches
- Race conditions between `page.goto('waitUntil: load')` and Blazor hydration.
- DOM elements that are destroyed and re-created during hydration (stale locator references).
- Test flakiness caused by Blazor's async initialization.
- Missing error boundaries when JS interop calls fail during hydration.