Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

refactor: extract handleBeforeSend keepAlive + popup closures for testability - #117

Merged
rubenhensen merged 1 commit into
mainfrom
refactor/extract-closures-issue-116
May 16, 2026
Merged

refactor: extract handleBeforeSend keepAlive + popup closures for testability#117
rubenhensen merged 1 commit into
mainfrom
refactor/extract-closures-issue-116

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Closes #116.

Summary

  • Lifted handleBeforeSend's keepAlive closure into runBeforeSendEncryption(state, details, tabId, deps) in src/background/encryption-flow.ts. background.ts passes the live browser.* / pg-js / openCryptoPopup surfaces in.
  • Lifted handleEncrypt / handleDecrypt / the init+error flow into src/pages/yivi-popup/{encrypt-popup,decrypt-popup,init-flow}.ts. yivi-popup.ts collapses to a thin DOM + dep wire-up.
  • Moved extractFromZip to src/pages/yivi-popup/zip.ts (already pure).

Test surface

npm test: 126 passed / 22 todo → 149 passed / 0 todo. The 22 placeholders #116 listed are now real tests:

  • tests/encryption-flow.test.ts — encryption-failure, popup-cancel, and plaintext-leak guard now drive runBeforeSendEncryption with a stubbed openCryptoPopup that throws. Each pins { cancel: true }, no details block, notifyError("encryptionError") fired.
  • tests/crypto-popup.test.ts — init paths (windowId failure, no pending entry, PostGuard ctor call), encrypt paths (mime decode, Yivi element selector, attachment-size pass-through, windowId on cryptoPopupDone), decrypt paths (ciphertext decode, recipient, uuid-mode unzip), and error paths (cryptoPopupError sent, UI shown, no auto-close) now drive the extracted functions with stubbed pg + runtime.

npm run typecheck and npm run build are both green.

Preserved behaviour

This is the live production crypto path; the lift keeps every guard intact at the listener call site:

  • composeTabs.has() race guard (PR fix: prevent unencrypted send when encryption is enabled #68) — still in background.ts.
  • Persisted encrypt-state restore — unchanged.
  • keepAlive("onBeforeSend", ...) wrap — still in background.ts; the extracted function returns a plain promise.
  • onUploadInit callback inside pg-js's upload-stream start handler — still fire-and-forget so a runtime.sendMessage rejection cannot abort the upload.
  • The 5 MB local cap (MAX_ATTACHMENT_SIZE) and the attachmentBase64 != null short-circuit — both preserved.

Reviewer quickstart

git fetch origin && git checkout refactor/extract-closures-issue-116 && npm install && npm test

Manual smoke-test recommended before merge

The bot ran unit tests + typecheck + production build, but does not have a Thunderbird in its workspace, so the encrypt and decrypt happy-paths were not click-tested live. Given this touches the production crypto pipeline (the closures inside handleBeforeSend + handleEncrypt/handleDecrypt), a manual round-trip in Thunderbird against the staging PKG / Cryptify is recommended before merge — send an encrypted message and decrypt it on the other end. The state-lifecycle tests added in #110 pin the persistence invariants from #68, but the SDK call sites themselves are best exercised live.

Test plan

  • CI green (npm test, npm run typecheck, npm run build)
  • Manual: send an encrypted message in Thunderbird (tier 1/2 — small attachment path)
  • Manual: send an encrypted message large enough to land in tier 3 (Cryptify upload)
  • Manual: decrypt a received tier-1/2 message
  • Manual: decrypt a received tier-3 message
  • Manual: cancel the Yivi popup mid-send and confirm the compose draft is left untouched (plaintext-leak guard)

🤖 Generated with Claude Code

…tability

Closes #116.

Lift two listener closures out from behind module-scoped singletons so
the remaining 22 it.todo placeholders can drive real code instead of
shape checks:

- runBeforeSendEncryption() in src/background/encryption-flow.ts now
  owns the body of handleBeforeSend's keepAlive closure. background.ts
  passes the live browser.* / pg-js / popup deps in.
- runEncryptInPopup() / runDecryptInPopup() / initCryptoPopup() in
  src/pages/yivi-popup/ own the popup's encrypt + decrypt + init/error
  flow. yivi-popup.ts collapses to ~60 lines of DOM + dep wire-up.
- extractFromZip moved to src/pages/yivi-popup/zip.ts.

Behaviour unchanged: composeTabs.has() guard, persistEncryptState,
keepAlive wrap, fire-and-forget onUploadInit, and the
attachment-size-cap arithmetic are all preserved on the call sites.

Tests: 126 passed / 22 todo → 149 passed / 0 todo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@rubenhensen
rubenhensen merged commit 98b8c87 into main May 16, 2026
2 checks passed
@rubenhensen
rubenhensen deleted the refactor/extract-closures-issue-116 branch May 16, 2026 14:46

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-hoc review of the merged diff (the bot can't approve its own PR — recording findings as a comment).

Verified locally

  • npm test → 149 passed / 0 todo (matches PR description; 22 previously-todo cases are now real)
  • npm run typecheck → clean
  • npm run build → clean

Correctness

  • runBeforeSendEncryption faithfully reproduces the previous keepAlive closure body. Attachment removal still happens before openCryptoPopup, the encrypted attachment is re-added only when ≤ MAX_ATTACHMENT_SIZE, and any thrown error short-circuits to { cancel: true } with no details block — so the plaintext-leak guard called out in #68 is preserved.
  • handleBeforeSend keeps all listener-site guards (composeTabs.has() race guard, persisted-state restore, keepAlive(\"onBeforeSend\", …) wrap, BCC / policy-editor early returns via evaluateBeforeSendGuards). After if (guard || !state) every branch returns, so the proceed path is correctly narrowed.
  • runEncryptInPopup preserves the fire-and-forget runtime.sendMessage inside onUploadInit — a rejected send cannot abort the upload.
  • initCryptoPopup reproduces the windowId-failure / no-pending-entry / encrypt-or-decrypt / error tail exactly, including the UploadSessionExpiredError-specific i18n message and the 750ms auto-close.
  • runDecryptInPopup keeps the uuid-mode unzip via extractFromZip (postguard-js#39 workaround) and the data-mode path, with proper type narrowing on the result.

Test coverage

  • The three failure-mode pins on runBeforeSendEncryption (encryption-failure, popup-cancel, plaintext-leak guard) drive the function with a stubbed openCryptoPopup that throws and verify { cancel: true }, no details, and notifyError(\"encryptionError\"). Exactly the regressions a closure refactor could introduce silently.
  • Popup tests now cover init paths, encrypt mime decode + selector + windowId, decrypt uuid vs ciphertext branches, and error paths.

Style

  • DI shape (RunBeforeSendDeps, EncryptPopupDeps, DecryptPopupDeps, InitCryptoPopupDeps) is consistent across the four lifts and matches the pattern from #110#115.

Minor / non-blocking

  • handleBeforeSend(tab, details: any)details: any is pre-existing, not introduced here.

The manual Thunderbird smoke-test items in the PR body still warrant attention before the next release tag, but the static surface and the lifted unit-test surface look clean. Approving in spirit.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: extract handleBeforeSend keepAlive + popup handleEncrypt/handleDecrypt closures for testability

1 participant