youtube-transcript-to-pdf

Testing

Strategy

Automated tests cover the pure modules in src/lib/ — the parsers and the PDF writer, where the logic actually lives. The chrome.* layer is verified by hand, because putting a fake extension API around it would test the fake rather than Chrome.

That split is the reason src/lib/ is forbidden from importing chrome.*.

Running

npm test                                        # all 31 tests
node --test test/pdf.test.js                    # one file
node --test --test-name-pattern="wrapText"      # one case

Node’s built-in runner. No framework, no install, no config.

Suites

File Tests Covers
test/youtube.test.js 12 URL parsing across every accepted shape, hostname-allowlist rejection, watch-page parsing, the bare-captionTracks fallback, unplayable videos, the parsed flag, regex-literal escaping, track selection precedence
test/transcript.test.js 8 json3 and legacy-XML parsing, empty-body handling, format dispatch, timestamp formatting, paragraph grouping on gaps and length caps, render options
test/pdf.test.js 11 WinAnsi transliteration, word wrap including hard-split of over-long words, PDF structural validity, pagination, escaping, empty input, filename sanitisation

Coverage target

Meaningful coverage of the important and changed or risky paths, with assertions on behaviour and observable effects. Not a blanket line-percentage mandate — a line-count target rewards tests that execute code without checking it.

Concretely, that means:

CI gate: npm run check must pass on Node 20, 22, and 24. There is no coverage percentage gate, and adding one is not planned.

What the PDF tests actually assert

verifyStructure in test/pdf.test.js is the interesting helper. It parses the generated bytes back out: header, %%EOF marker, startxref offset, and then every cross-reference entry, checking that each declared byte offset really does land on the object it claims. A PDF with a corrupt xref table opens in some readers and not others, so testing the structure rather than the bytes catches the failure that matters.

Manual test plan

No test covers the extension layer, so run this before any release, and before merging anything that touches the service worker, popup, viewer, or content script.

  1. Prefill — open a YouTube video, click the extension. The URL is filled in and the status says the video was detected.
  2. Human captions — fetch a video with author-provided captions. Preview shows timestamped paragraphs.
  3. Auto captions — repeat with an auto-generated track. The meta line says auto-generated captions.
  4. Strategy 2 — fetch a video with no tab open on it. A background tab opens, closes, and the meta line says read from transcript panel.
  5. Tab reuse — with the video already open, fetch again. No new tab appears.
  6. Language switch — pick another language from the dropdown. The transcript refetches in that language.
  7. Options — toggle timestamps and paragraphs. The preview updates, and the setting survives closing and reopening the popup.
  8. Download — click Download PDF. The file lands in Downloads and opens correctly. Check the last page, not just the first.
  9. Print — click Print. A viewer tab opens; Chrome’s print preview shows the transcript.
  10. Non-Latin — repeat 8 and 9 on a video with CJK or Cyrillic captions. The download shows ?; the print path renders correctly. This is expected.
  11. No captions — fetch a video without captions. A clear error, no crash.
  12. Bad URL — submit a non-YouTube URL. A clear error, no request made.
  13. Long video — fetch something over an hour. Timestamps carry hours, and the PDF paginates.

Adding tests

New behaviour in src/lib/ needs a test. New behaviour in the chrome.* layer needs a line in the PR describing how it was verified, and a step added above if it is a path worth re-checking every release.