fix(extension): handle a rejected sendMessage in loadOpportunityBadge - #6430
Conversation
loadOpportunityBadge awaited chrome.runtime.sendMessage with no try/catch, unlike every other message call site in this extension. sendMessage really does reject under MV3 (service worker asleep/restarting, "Extension context invalidated"), and mountOpportunityBadge calls this as a floating void promise -- so a rejection became an unhandled rejection and left the badge <aside> in the DOM permanently hidden, with no user-visible feedback. It now cleans up via the same container.remove() the function already uses for its other failure paths. background.js and options.js are untouched; their handling was already correct. Adds test/content.test.ts covering the rejected path plus the not-ok and ok paths, and exposes loadOpportunityBadge on the existing test-internals seam so it can be driven directly. content.js mounts at import only on a GitHub issue pathname, so importing it on a non-issue path stays side-effect-free and needs none of the deferred jsdom mount harness. content.js remains outside coverage.include, so the measured baseline is unchanged. Closes JSONbored#6189
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-16 07:41:50 UTC
Review summary Nits — 4 non-blocking
Flagged checks (non-blocking)
Decision drivers
Context & advisory signals — never blocks the verdict
Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|

Summary
Closes #6189
content.js'sloadOpportunityBadgeawaitedchrome.runtime.sendMessagewith notry/catch, unlike every other message call site in this extension (background.js:19-21,options.js:78-94,options.js:99-113).sendMessagegenuinely rejects under MV3 — service worker asleep/restarting, "Extension context invalidated". BecausemountOpportunityBadgecalls this as a floatingvoidpromise, a rejection became an unhandled rejection and left the badge<aside>in the DOM permanently hidden with zero user-visible feedback.container.remove()the function already uses for its other failure paths — the graceful behaviour the issue asks for.background.jsandoptions.jsare untouched; their handling was already correct, as the issue states.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #123) — a linked open issue is required for every contributor PR.Validation
git diff --checknpm run actionlintnpm run typecheck(this workspace'stypecheckis itsnode --checklint gate — run and passing)npm run test:coveragelocally;codecov/patchrequires ≥99% coverage of the lines AND branches you changed (aim for 100% on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateIf any required check was skipped, explain why:
try/catchreverted (and the test seam left in place, to isolate the fix itself), the new test fails with "promise rejected 'Error: Extension context invalidated' instead of resolving" — precisely the unhandled rejection described — and passes with the fix.npx vitest run --coveragepasses 22 tests across 4 files, with coverage 100% statements / 100% functions / 100% lines / 95.79% branches, all abovevitest.config.ts's thresholds (98/98/98/94).npm run lint(node --checkon all five scripts) passes.content.jsis deliberately outsidecoverage.include(the README defers it pending a jsdom mount harness), so this adds real regression coverage without touching the thresholds or the deferral policy.Safety
UI Evidencesection below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository.No
UI Evidencesection: there is no new rendered state to screenshot. This strictly removes a broken one — on failure the badge container is now cleanly removed instead of persisting as a hidden orphan element, which is the extension's existing behaviour for every other failure path.Notes
loadOpportunityBadgeis added to the existing__loopoverMinerContentInternalstest seam so the test can drive it directly.content.jsmounts the badge at import time only whenlocation.pathnameis a GitHub issue URL, so importing it on a non-issue path loads the module free of DOM side effects — that keeps this focused on thesendMessagefailure path and needs none of the jsdom mount harness the README defers.Closes #6189