Skip to content

test: stop booting a real libp2p node inside jsdom from the two screen tests that still did - #37

Merged
ehsan6sha merged 1 commit into
mainfrom
test/no-real-libp2p-in-screen-tests
Sep 4, 2026
Merged

test: stop booting a real libp2p node inside jsdom from the two screen tests that still did#37
ehsan6sha merged 1 commit into
mainfrom
test/no-real-libp2p-in-screen-tests

Conversation

@ehsan6sha

Copy link
Copy Markdown
Member

CI on main has gone red twice today with every test passing — and it blocked #36 the same way:

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.

Cause

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 booted an actual libp2p node inside jsdom (the CI log shows Fula initialized via with peerId: 12D3KooW…). The test finished on its assertion, the tree 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 run failure regardless of the tally. Timing-dependent, so roughly one full-suite run in three.

guards.test.tsx had the same hole from the other side: landing on /blox mounts the main shell, whose useEnsureFulaClient calls initFula. 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 stubs initFula. 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

…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
ehsan6sha merged commit beddfda into main Sep 4, 2026
2 checks passed
@ehsan6sha
ehsan6sha deleted the test/no-real-libp2p-in-screen-tests branch September 4, 2026 22:04
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant