Skip to content

Speed up the Jest run - #6303

Draft
peachbits wants to merge 5 commits into
developfrom
matthew/jest-speed
Draft

peachbits wants to merge 5 commits into
developfrom
matthew/jest-speed

Conversation

@peachbits

Copy link
Copy Markdown
Contributor

Description

This branch speeds up npm test, which the precommit hook also runs. Each Jest test file starts with a fresh module registry, so anything jestSetup.js or a common import pulls in loads again in every one of the 141 files. Two suites also slept on real timers.

develop this branch
npm test, wall time 12.5s 7.1s
CPU time across all workers 95.3s 84.8s

These are medians of 5 alternating runs on a 16-core Mac, with a load average of 60 to 90 from other work. Every branch run passed all 141 suites and 1,393 tests. One develop run lost a Jest worker to a segfault in GiftCardActions.test.ts, which this branch doesn't touch.

  1. Limit jest roots to src. Without watchman, Jest's haste map crawls every file under the project root on each run, including about 140k in node_modules. Every test and manual mock lives under src, so roots now points there.
  2. Stop loading reanimated in every jest file. jestSetup.js called Reanimated's setUpTests(), which only registers the toHaveAnimatedStyle and toHaveAnimatedProps matchers. No test uses them, but the call loaded about 300 Reanimated modules into every test file, including the ones that never render a component.
  3. Import date-fns format by path. date-fns 2 ships only CommonJS, so import { format } from 'date-fns' in intl.ts loaded all ~280 of its functions into every test file. It was the only import from the package root. The precommit hook took intl.ts off the ESLint warnings list.
  4. Mock name lookups in the recipient network test. Picking Zcash through "Myself" runs a reverse name lookup against the live ZcashNames indexer. The reply took about 600ms and the test polls for about 1s, so a slow network failed it at random.
  5. Use fake timers in the staking and rates tests. The staking stall tests waited out the provider's 2 second stall timeout and a 2.5 second slow node, and the bulk rates test slept 3 seconds. The two suites drop from 7.6s and 5.5s to 2.3s and 2.6s.

Testing:

  • verify-repo.sh passes: install, prepare, ESLint on the changed files, and Jest.
  • tsc passes.
  • Both staking stall tests fail when the provider stops asking another node on a stall. The late-answer test fails right away with a diff when a stalled node's late answer takes over.

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

peachbits and others added 5 commits October 29, 2026 15:45
Without watchman, jest's haste map crawls every file under the
project root on each run, including about 140k files in node_modules.
That took about 3s before the first test started. Every test and
manual mock lives under src, so crawl only that.
setUpTests only registers the toHaveAnimatedStyle and
toHaveAnimatedProps matchers, which no test uses, but calling it
loads all of react-native-reanimated (about 300 modules) into every
test file, including the ones that never render a component.
date-fns 2 only ships CommonJS, so importing a name from the package
root requires its index, which loads all ~280 of its functions. This
was the only such import, and it pulled them into every jest file.
A same-chain Zcash pick through "Myself" runs a reverse name lookup,
which asked the live ZcashNames indexer. The reply took about 600ms,
and the test polls for only about 1s before giving up, so a slow
network failed it at random. No test here checks names, so answer
every lookup with none.
stakingRpcProviders and exchangeRates waited on real timers. The
staking stall tests sat through the provider's 2 second stall timeout
and a 2.5 second slow node, and the bulk rates test slept 3 seconds
around the rate queue's 1 second delay. With fake timers the two
suites take 2.3 and 2.6 seconds instead of 7.6 and 5.5, the medians of
5 alternating runs on a 16-core Mac.

The full jest run only drops from about 11.3 to 10.5 seconds, within
its run-to-run spread. Jest's 15 workers stay busy loading modules for
the whole run, so it takes about the total suite time divided by 15,
and the 8 seconds of sleeping saved is about half a second of that.

- The staking stall tests fake the timers and run them with
  runAllTimersAsync. The other staking tests keep real timers, now
  reset before each test.
- The rates test advances fake time where it slept. It also waits for
  the first batch of rates before asking again, which the 2 second
  sleep always allowed, so the later requests still come from the
  cache.

Verified: both stall tests fail when the provider stops asking another
node on a stall, and the late-answer test fails right away with a diff
when a stalled node's late answer takes over.

Co-Authored-By: Claude Opus 5.5 <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