Skip to content

Add unit tests for sender.ts routing and retry logic - #428

Merged
grahamlangford merged 1 commit into
mainfrom
test-sender-routing-retry-coverage
Aug 3, 2026
Merged

Add unit tests for sender.ts routing and retry logic#428
grahamlangford merged 1 commit into
mainfrom
test-sender-routing-retry-coverage

Conversation

@grahamlangford

Copy link
Copy Markdown
Collaborator

source/sender.ts is the largest module in the library (470 lines) and had no unit tests. CI's Test job runs vitest run, which excludes source/test/ — those are browser-driven demo tests that need a built extension. The entire CI unit suite was therefore one file, targetLogic.test.ts, covering only getActionForMessage.

Gap identified

Every decision messenger() makes — which browser API to call, what the envelope looks like, whether an error is retryable, when to stop — was covered only by the demo extension, which CI does not run. A change to sender.ts could pass all four CI jobs while breaking messaging. This is also the file where both recent behavioral changes landed.

Bugs this would have caught

What the tests verify

  • Routing — external extensions via sendMessage(extensionId, …); the background page invoking its own handler instead of messaging itself; extension pages via a single-argument sendMessage; tab targets falling back to the runtime where the tabs API is absent; frameId defaulting to 0, honouring an explicit frame, and dropping out to reach allFrames; the __webextMessenger marker and an increasing seq.
  • Content-script sends (0.35.0 regression: local-handling short-circuit breaks messaging in about:srcdoc content scripts #409 regression) — an opaque-origin frame reaches the runtime instead of throwing, and a {tabId} target is never handled locally by the sending frame.
  • Responses — values unwrapped, errors deserialized and rethrown without retrying, and the three distinct "nobody answered" errors (missing page target, messenger-less tab, third-party listener conflict).
  • Retry — retries a missing receiver and a MessengerError from a still-booting target; a single attempt when retry: false; no retry after the target closed early, for a missing external extension, or once the context is invalidated; stops when the tab is gone or discarded; exponential backoff; attempts-exhausted fires, and the 15-attempt safety cap is never the reason it stopped.
  • Notifications and helpers — notifications return undefined, send once, and swallow errors; getMethod with a fixed, promised, or caller-supplied target; getNotifier returns void and never rejects.

Test-only — no production file is touched. 48 tests pass in ~350ms; tsc, eslint, and prettier are clean.

One thing worth a separate look

tsc emits test files into distribution/, and .npmignore ships top-level distribution/*, so targetLogic.test.js is already published to npm today. This PR adds a second one (sender.test.js). Pre-existing and out of scope here, but a tsconfig exclude would drop both from the package.

sender.ts is the largest module in the library and had no unit tests: CI
runs `vitest run`, which excludes source/test (browser-driven demo tests),
so the entire CI unit suite was targetLogic.test.ts.

Both recent behavioral changes landed in this file — the retry rewrite in
#362 and the local-handling short-circuit in #363, reverted in #410 after
it broke messaging in about:srcdoc frames (#409).

Includes a regression pin for #409: re-applying #363 fails
"reaches the runtime from an opaque-origin frame" with TypeError: Invalid
URL, the exact production symptom.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@grahamlangford
grahamlangford merged commit 63a4c53 into main Aug 3, 2026
5 checks passed
@grahamlangford
grahamlangford deleted the test-sender-routing-retry-coverage branch August 3, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant