Manual verification notes for issue #513 — live region announcements during quote refresh.
SwapCard(/swap) — primary swap UIDemoSwap(demo page usinguseQuoteRefresh) — debounced amount entry
Announcements are rendered by QuoteRefreshLiveRegion and driven by useQuoteRefreshAnnouncements.
- Start the frontend:
cd frontend && npm run dev - Use a screen reader:
- Windows: NVDA (free) or JAWS
- macOS: VoiceOver (
Cmd + F5)
- Ensure the quote API is reachable (or use mocked/demo data as documented for local dev)
| Scenario | Region | Priority | Expected announcement |
|---|---|---|---|
| Quote fetch succeeds (manual refresh, auto-refresh, or debounced amount) | role="status" |
polite | “Quote updated.” or “Quote updated. {rate}” |
| Hard failure (4xx, offline, retries exhausted) | role="alert" |
assertive | “Quote refresh failed. {message}” |
| Transient failure while auto-retry is pending | — | — | No announcement until retries finish |
| Rapid amount edits during debounce | — | — | One success announcement per completed quote, not per keystroke |
Focus must remain on the control the user is editing (amount field, refresh button, etc.).
- Open
/swapand connect wallet (or use demo with valid pair/amount). - Enter a valid pay amount and wait for the initial quote.
- Tab to Refresh quote and activate it.
- Pass: Screen reader announces a polite “Quote updated…” message; focus stays on the refresh control.
- Focus the pay amount field.
- Type
1, then quickly append0and0(e.g.100) without pausing long between keys. - Pass: After debounce settles and the request completes, exactly one polite quote-updated announcement is heard (not three).
- Stop the API or force an invalid amount that returns a non-retryable 400.
- Trigger a quote refresh.
- Pass: Assertive “Quote refresh failed…” is announced; no duplicate failure spam on subsequent renders.
- Simulate a transient 503/429 that auto-retries (or use integration test mocks).
- Pass: No assertive failure during the retry window; polite success after recovery if the retry succeeds.
- Disable network in devtools.
- Attempt to refresh the quote.
- Pass: Assertive failure mentioning offline/reconnect guidance.
SwapStatusLiveRegion covers the submission lifecycle after a quote is accepted.
| Status | Region | Priority | Expected announcement |
|---|---|---|---|
pending |
role="status" |
polite | “Swap submitted. Waiting for confirmation.” |
finalized |
role="status" |
polite | “Swap confirmed. {tx detail}” |
error |
role="alert" |
assertive | “Swap failed. {message} Update the quote and try again.” |
- Open
/swap, connect a wallet, and enter a valid amount. - Keyboard path: without touching the mouse,
Tabto Update quote, pressEnter(a fresh quote is fetched), thenTabto Confirm swap and pressEnter. Pass: both controls are reachable and activate onEnter/Space. - While the transaction is in flight, Pass: a polite “Swap submitted…” is announced and both action buttons report as dimmed/unavailable.
- On success, Pass: a polite “Swap confirmed…” is announced; focus has not moved.
- Force a failure (reject in wallet, or point the API at an unreachable host).
Pass: an assertive “Swap failed…” interrupts, and the Update quote control is
immediately reachable with a single
Tabfrom where focus already sits.
cd frontend
npm test -- lib/quote-refresh-announcements.test.ts hooks/useQuoteRefreshAnnouncements.test.ts components/swap/QuoteRefreshLiveRegion.test.tsx components/swap/SwapStatusLiveRegion.test.tsx- Polite announcement on successful refresh
- Assertive announcement on hard failure
- No duplicate announcements during debounce
- Focus never moves to the live region
- Polite pending / finalized and assertive error announcements on swap submission
- Confirm and update-quote reachable by keyboard alone