Add unit tests for sender.ts routing and retry logic - #428
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
source/sender.tsis the largest module in the library (470 lines) and had no unit tests. CI'sTestjob runsvitest run, which excludessource/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 onlygetActionForMessage.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 tosender.tscould pass all four CI jobs while breaking messaging. This is also the file where both recent behavioral changes landed.Bugs this would have caught
0.35.0 regression: local-handling short-circuit breaks messaging in about:srcdoc content scripts #409 (Handle messages locally in content script as well #363, reverted in Revert "Handle messages locally in content script as well" #410). 0.35.0 hoisted a
compareTargets()short-circuit onto the send path. In a sandboxedabout:srcdocframelocation.originis the string"null", sonew URL(page, location.origin)threw and every outbound message from that frame failed, starting withstoreTabData()'s__getTabDatacall. It shipped, broke srcdoc iframes downstream (pixiebrix/pixiebrix-source#8368), and was reverted eight months later.I verified the pin rather than assuming it: re-applying Handle messages locally in content script as well #363 on top of this branch fails
reaches the runtime from an opaque-origin framewithTypeError: Invalid URL— the exact production symptom. Re-applying Handle messages locally in content script as well #363 fails 20 of the 32 new tests overall.Drop
p-retry; fix retry bugs #362 (Drop p-retry; fix retry bugs) rewrote the retry loop by hand — backoff, the safety cap, tab-liveness checks, thefailed-attempt/attempts-exhaustedevents — with nothing covering any of it.What the tests verify
sendMessage(extensionId, …); the background page invoking its own handler instead of messaging itself; extension pages via a single-argumentsendMessage; tab targets falling back to the runtime where the tabs API is absent;frameIddefaulting to0, honouring an explicit frame, and dropping out to reachallFrames; the__webextMessengermarker and an increasingseq.{tabId}target is never handled locally by the sending frame.MessengerErrorfrom a still-booting target; a single attempt whenretry: 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-exhaustedfires, and the 15-attempt safety cap is never the reason it stopped.undefined, send once, and swallow errors;getMethodwith a fixed, promised, or caller-supplied target;getNotifierreturnsvoidand never rejects.Test-only — no production file is touched. 48 tests pass in ~350ms;
tsc,eslint, andprettierare clean.One thing worth a separate look
tscemits test files intodistribution/, and.npmignoreships top-leveldistribution/*, sotargetLogic.test.jsis already published to npm today. This PR adds a second one (sender.test.js). Pre-existing and out of scope here, but atsconfigexcludewould drop both from the package.