test: stop booting a real libp2p node inside jsdom from the two screen tests that still did - #37
Merged
Merged
Conversation
…n tests that still did
CI on main has gone red twice today with every test passing:
Vitest caught 2 unhandled errors during the test run.
TypeError: Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is not of type 'Event'.
Queue.emitEmpty it-queue/src/index.ts
Timeout.later it-queue/src/utils.ts
This error originated in "src/screens/InitialSetup/__tests__/ConnectToBlox.test.tsx"
... caught after test environment was torn down.
The last ConnectToBlox test navigates on to Set authorizer, which calls
`Helper.initFula` on mount to mint the app peer id. That file mocked LAN, BLE
and discovery but not `@/utils/helper`, so the call was real: it created an
actual libp2p node inside jsdom. The test finished on its assertion, the tree
was unmounted, and libp2p's `it-queue` kept ticking -- one of its timers then
dispatched a Node-realm Event into a torn-down jsdom EventTarget. Vitest counts
that as a failure regardless of the test tally. Timing-dependent, so it hit
roughly one full-suite run in three, on CI and on main.
guards.test.tsx had the same hole from the other side: landing on /blox mounts
the main shell, whose `useEnsureFulaClient` calls `initFula` for the current
Blox. Seconds of libp2p boot that routing tests never look at -- and the reason
"set up -> / lands on /blox" was the one test that failed under full-suite load
while passing alone.
Every sibling setup test (SetBloxAuthorizer, SetupComplete, ConnectToExistingBlox,
LinkPassword, setupFlow) already stubs `initFula`. These two now do too, the
same way. Test-only; no runtime change.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BpqXrkEPA9odTyRdK5Mnx
ehsan6sha
added a commit
that referenced
this pull request
Sep 5, 2026
…one dial (#39) First diagnostic log from the reporter's phone (build 7187fba), return from the connect approval: [tab] visible after 28875ms hidden [relay] socket is down (connecting=true) -- opening the transport [relay] transportOpen did not get the socket up within the bound -- restarting (+2503ms) [relay] transport restart finished in 8272ms, connected=true (+8273ms) [wallet] connected=true (+8651ms) Not a zombie socket. A socket that was DOWN with a connect attempt pending. While the tab was hidden Android took the network, the socket closed, and the library did what it does on a close: scheduled a reconnect, dialled, failed, slept its backoff, dialled, failed, slept longer -- `connect()` retries five times with a sleep of `attempt` seconds between. The tab came back mid-sleep with `connectPromise` pending, and everything awaits that promise: the library's own `transportOpen()`, and every lever in relayWake. Nothing cancels a setTimeout inside the library. The return paid the rest of the sleep, then one dial, plus 2.5 s of our own bound waiting on a promise that could not resolve inside it. Nine seconds, none of them a dial that reached the relay. The third return in the same log landed at a luckier point in the loop and took 1.5 s. That is the floor: one dial on that network. So: `transportClose()` the moment the tab goes hidden. It sets `transportExplicitlyClosed`, the one flag every auto-reconnect path checks first, so nothing dials a network that is not there, no backoff accrues, and no promise is pending when the tab returns. `connect()` clears the flag on the way back in, so `transportOpen()` on visible is a single clean dial. The wallet's approval or signature, published while we were away, is queued by the relay against its topic and pushed on re-subscribe. The close itself happens while the user is in the wallet, where no one is waiting on it. Also from the log: "fresh socket up in 1ms" on the second return. The #38 fast path checked `connected`, which the socket being replaced still reports -- the dead socket, congratulated. It now requires a NEW provider object as well. And the `guards` route test gets a 30 s budget instead of 15. It still missed 15 s about one full-suite run in three after #37, with nothing else competing: the Blox chunk is the largest in the app and is transformed in a worker sharing the machine with every other file. The test asserts which route matched, never how fast. Claude-Session: https://claude.ai/code/session_013BpqXrkEPA9odTyRdK5Mnx Co-authored-by: Claude Fable 5.1 <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.
CI on
mainhas gone red twice today with every test passing — and it blocked #36 the same way:Cause
The last
ConnectToBloxtest navigates on to Set authorizer, which callsHelper.initFulaon mount to mint the app peer id. That file mocked LAN, BLE and discovery but not@/utils/helper, so the call was real: it booted an actual libp2p node inside jsdom (the CI log showsFula initialized via with peerId: 12D3KooW…). The test finished on its assertion, the tree unmounted, and libp2p'sit-queuekept ticking — one of its timers then dispatched a Node-realmEventinto a torn-down jsdomEventTarget. Vitest counts that as a run failure regardless of the tally. Timing-dependent, so roughly one full-suite run in three.guards.test.tsxhad the same hole from the other side: landing on/bloxmounts the main shell, whoseuseEnsureFulaClientcallsinitFula. Seconds of libp2p boot that routing tests never look at — and the reason "set up → / lands on /blox" was the one test in the suite that failed under full-suite load while passing alone.Fix
Every sibling setup test (
SetBloxAuthorizer,SetupComplete,ConnectToExistingBlox,LinkPassword,setupFlow) already stubsinitFula. These two now do too, identically. Test-only; no runtime change.Testing
Full suite run locally; both previously-flaky files pass under load. Merging this first, then bringing #36 up to date so its CI reruns clean.
🤖 Generated with Claude Code
https://claude.ai/code/session_013BpqXrkEPA9odTyRdK5Mnx