Loading and error states
Hold brief branches still, verify them, and restore the app.
Query loading
Use the bound TanStack Start session from Measure the loading-state height gap. Hold its known query, then prove the cache and visible branch agree:
npx @genie-react/cli call query_simulate_state \
'{"queryKey":["dashboard","metrics"],"state":"pending"}'
query_state="$(npx @genie-react/cli call query_get \
'{"queryKey":["dashboard","metrics"]}' --json)"
printf '%s\n' "$query_state" | \
jq -e '.status == "pending" and .simulatedState == "pending"'
agent-browser --session docs-case-start get text body | rg -F 'Loading metrics…'Suspense fallback
Use the bound Router session from Verify routes and forced states. Navigate, discover the current component ID, force the fallback, then join React evidence to visible text:
npx @genie-react/cli call router_navigate '{"to":"/suspense"}'
npx @genie-react/cli call devtools_wait \
'{"condition":"navigation","name":"/suspense","timeoutMs":5000}'
agent-browser --session docs-case-router wait '[data-testid="lazy-content"]'
lazy_panel_id="$(npx @genie-react/cli call react_find_components \
'{"query":"LazyPanel","exact":true}' --json | \
jq -er 'if (.matches | length) == 1 then .matches[0].id else error("expected one LazyPanel") end')"
npx @genie-react/cli call react_toggle_suspense_fallback \
"$(jq -cn --argjson id "$lazy_panel_id" '{id:$id,showFallback:true}')" --json
suspense_state="$(npx @genie-react/cli call react_error_state '{}' --json)"
printf '%s\n' "$suspense_state" | \
jq -e 'any(.suspended[]; .forced == true and .isFallbackShowing == true)'
agent-browser --session docs-case-router get text body | rg -F 'Loading lazy panel…'Error boundary
Navigate to the error fixture and discover its current child ID before forcing the boundary:
npx @genie-react/cli call react_reset_overrides '{}'
npx @genie-react/cli call router_navigate '{"to":"/error"}'
npx @genie-react/cli call devtools_wait \
'{"condition":"navigation","name":"/error","timeoutMs":5000}'
agent-browser --session docs-case-router wait '[data-testid="throw"]'
bomb_id="$(npx @genie-react/cli call react_find_components \
'{"query":"Bomb","exact":true}' --json | \
jq -er 'if (.matches | length) == 1 then .matches[0].id else error("expected one Bomb") end')"
npx @genie-react/cli call react_force_error_boundary \
"$(jq -cn --argjson id "$bomb_id" '{id:$id,forceError:true}')" --json
error_state="$(npx @genie-react/cli call react_error_state '{}' --json)"
printf '%s\n' "$error_state" | \
jq -e 'any(.caughtErrors[]; .forced == true and .boundaryName == "Boundary")'
agent-browser --session docs-case-router get text body | \
rg -F 'Caught: Simulated error coming from DevTools'forced:true separates these development checks from organic errors and suspensions.
Verify the UI
Use the browser driver to check:
- The skeleton matches the final content size.
- Buttons and links keep their position.
- The fallback has a useful accessible name or status.
- The error stays inside the intended boundary.
- Retry returns to real content.
Restore
Restore the Query session and verify the synthetic marker is gone:
npx @genie-react/cli call query_restore_state \
'{"queryKey":["dashboard","metrics"]}' --json
query_state="$(npx @genie-react/cli call query_get \
'{"queryKey":["dashboard","metrics"]}' --json)"
printf '%s\n' "$query_state" | jq -e 'has("simulatedState") | not'
agent-browser --session docs-case-start get text body | rg -F 'Acme NA #1'Reset the Router session and verify the real child is visible again:
reset="$(npx @genie-react/cli call react_reset_overrides '{}' --json)"
printf '%s\n' "$reset" | jq -e '.remaining == 0'
agent-browser --session docs-case-router get text body | rg -F 'Throw'
unset query_state suspense_state error_state reset lazy_panel_id bomb_idAgent outcome
The agent can verify states that normally last only a few milliseconds. This makes layout-shift, fallback, and error-boundary work testable without adding temporary app code.