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

Three ways to capture NVDA speech Comparison of NVDA's Speech Viewer, the debug log file and a custom logging add-on for capturing speech during manual testing. Three ways to capture NVDA speechSpeech Viewer and debug logGood for single findings and demosLog also shows focus and event contextLogging add-onWrites only speech, one line per utteranceTimestamps and step markers you defineEasy to diff between releasesNeeds a little Python to install
Speech Viewer for quick checks, the debug log for full sessions, an add-on when you want clean files automatically.

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
A logged manual test of one journey Timeline of a manual NVDA test session with logging: start with Speech Viewer open, place a step marker, perform the step, copy the excerpt, and compare with the baseline. A logged manual test of one journeyStartNVDA + Speech ViewerMarker"STEP 3 SORT" spokenPerformTab, EnterExcerptcopy utterances since markerCompareagainst last release's baselineone manual journey
Markers make logs readable; baselines make them comparable.

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.

From log to bug report Steps from a captured NVDA log to a bug report: find the step marker, extract the utterances, add the expected text, and note versions. From log to bug reportFind the markerlocate "STEP 3 SORT"in viewer or logExtractutterances until the next markerwith timestampsExpectedwrite the expected utterance under the actualone line eachVersionsNVDA, browser, OS, page buildat the top of the excerpt
Four lines of evidence beat four paragraphs of description.

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.

Permalink to "Related"

← Back to Screen Reader Smoke Testing