---
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.
