Capturing NVDA Speech Logs for Manual Testing
Permalink to "Capturing NVDA Speech Logs for Manual Testing"A screen reader bug report that says “the sort isn’t announced properly” is hard to act on. One that quotes NVDA’s output — Amount button column header followed by nothing, where Sorted by Amount, ascending was expected — is easy. NVDA can show and record everything it speaks, which turns manual testing from impressions into evidence and lets you compare a release against the last one line by line.
This page covers three ways to capture NVDA’s speech: the Speech Viewer, the debug log, and a small add-on. It belongs to screen reader smoke testing.
Spec reference
Permalink to "Spec reference"NVDA (NV Access, free and open source) provides:
- Speech Viewer — Tools menu (
Insert+N, then Tools → Speech Viewer). A window showing every utterance as text, which can be selected and copied. It can be set to open on startup. - Log file — Tools → View log, or the
nvda.logfile in the user’s temp directory. At Debug log level (General settings → Logging level), each speech sequence is logged with a timestamp. The log also records focus and object events useful for diagnosing why something was or was not spoken. - Add-ons — Python add-ons can hook the speech pipeline (for example by wrapping
speech.speak) to write utterances to a file in a format you choose.
Automated capture on CI uses the same underlying output via @guidepup/guidepup, which drives NVDA and reads its spoken phrase log.
Criteria are not directly involved; this is test evidence for SC 4.1.2, 4.1.3, 1.3.1 and 2.4.3 findings.
When to capture logs — and when to listen only
Permalink to "When to capture logs — and when to listen only"Capture logs for every screen reader finding you will report, for baseline recordings of critical journeys before a release, and when comparing behaviour across NVDA versions or browsers.
Listen without logging during exploratory testing, when you are learning how a feature behaves. Logs are evidence; they do not replace hearing timing, interruptions and speech rate, which text cannot show.
The misapplication to name is pasting a whole unfiltered debug log into a bug report. Nobody reads 4,000 lines. Extract the few utterances around the step, with timestamps, and state what was expected.
Annotated code example
Permalink to "Annotated code example"Manual test with Speech Viewer — step markers typed into a scratch field
1. Open Speech Viewer (Insert+N → Tools → Speech Viewer)
2. Before each step, type a marker into a text field on the page, e.g. "STEP 3 SORT"
— NVDA speaks it, so it appears in the viewer and marks the boundary
3. Perform the step (Tab to "Amount", press Enter)
4. Copy the viewer text between markers into the report
Captured:
STEP 3 SORT
Amount button column header not sorted
Amount sorted ascending
Sorted by Amount, ascending. ← status message, expected
# speechlog/globalPlugins/speechlog.py — minimal NVDA add-on writing speech to a file
import globalPluginHandler, speech, time, os
LOG = os.path.join(os.path.expanduser("~"), "nvda-speech.log")
_orig = speech.speech.speak
def _logged(sequence, *args, **kwargs):
text = " ".join(s for s in sequence if isinstance(s, str)).strip()
if text:
with open(LOG, "a", encoding="utf-8") as f:
f.write(f"{time.strftime('%H:%M:%S')}\t{text}\n")
return _orig(sequence, *args, **kwargs)
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
def __init__(self):
super().__init__()
speech.speech.speak = _logged # wrap, don't replace, the speech pipeline
def terminate(self):
speech.speech.speak = _orig
// Normalise volatile values before diffing two logs
const normalise = (line) => line
.replace(/\d{2}:\d{2}:\d{2}\t/, '') // timestamps
.replace(/\b\d{1,3}(,\d{3})*(\.\d+)?\b/g, '#') // numbers
.replace(/\bINV-\d+\b/g, 'INV-#'); // record ids
The add-on wraps NVDA’s speak function — internal NVDA APIs can change between versions, so pin the NVDA version used for baselines and re-check the add-on after upgrades.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| NVDA command | Purpose in logging |
|---|---|
Insert+N → Tools → Speech Viewer |
Open the live speech text window |
Insert+N → Tools → View log |
Open the current log file |
Insert+F1 |
Speak developer info for the navigator object (logged at debug) |
Insert+Q |
Quit NVDA (closes the log cleanly) |
Ctrl |
Stop speech — useful to separate steps visually in the viewer |
Integration context
Permalink to "Integration context"Manual logs and automated smoke tests should use the same journeys and the same normalisation, so a baseline captured by hand can be compared with one captured by a Playwright smoke test with guidepup. The macOS equivalent is covered in VoiceOver smoke tests with guidepup.
Log excerpts are the core evidence in a screen reader bug report; the structure of a good report is in writing actionable accessibility bug reports.
Gotchas
Permalink to "Gotchas"Speech Viewer and speech rate. The viewer shows text even for utterances cut off by later speech. A message that appears in the viewer may not have been audible; note interruptions separately.
Log size. Debug logging is verbose and slows NVDA slightly on large pages. Turn it on for the test session only.
Privacy. Logs capture everything spoken, including personal data on screen. Use test data, and scrub logs before attaching them to tickets.
Design system notes
Permalink to "Design system notes"Keep baseline speech logs for the design system’s reference components (sortable table, treegrid, combobox filter, dialog) in the repository, captured with a pinned NVDA version and normalised. Product teams can then compare their compositions against known-good component announcements.
Testing checklist
Permalink to "Testing checklist"FAQ
Permalink to "FAQ"How can I see what NVDA said during testing?
Open the Speech Viewer from NVDA’s Tools menu. It shows every utterance as text, which you can copy into notes or bug reports.
How do I record a whole NVDA session?
Set the log level to Debug in NVDA’s General settings and read the log file afterwards, or install a small add-on that writes each utterance to a file with timestamps.
Why do logs show messages I did not hear?
The Speech Viewer and log record every utterance NVDA started, including ones interrupted by later speech. Note interruptions separately, because users only hear what was not cut off.
Which NVDA version should baselines use?
A pinned, recent version recorded with every log. Update the baseline deliberately when you upgrade NVDA, so wording changes in NVDA are not mistaken for regressions in your product.
Can NVDA speech capture be automated?
Yes. guidepup can start NVDA, drive a page and read its spoken phrase log, which is the basis of automated screen reader smoke tests.
Related
Permalink to "Related"- Screen reader smoke test with Playwright — automating the same capture
- Writing actionable bug reports — where the logs go
- VoiceOver smoke tests — the macOS counterpart