Skip to content

fix(tests): raise testTimeout to 60s to match hookTimeout#54

Merged
SZoloth merged 12 commits into
mainfrom
fix/increase-test-timeout
Apr 6, 2026
Merged

fix(tests): raise testTimeout to 60s to match hookTimeout#54
SZoloth merged 12 commits into
mainfrom
fix/increase-test-timeout

Conversation

@SZoloth
Copy link
Copy Markdown
Owner

@SZoloth SZoloth commented Apr 3, 2026

Summary

  • Raises testTimeout from 30s to 60s to match hookTimeout
  • Under CPU throttling (background agents, Spotlight indexing), jsdom init and test execution slow dramatically — tests that normally take <5ms can hit 30s+ under load, causing false timeout failures
  • 60s gives headroom while still catching real hangs

Context

Observed 6+ load-induced failures on 2026-04-02. Pattern: ToggleSwitch, search.test, and other small/late-running tests timeout at exactly 30s, then pass cleanly on re-run. Filed as margin-909. hookTimeout was already 60s for the same reason.

Test plan

  • pnpm test: 287/287 passing
  • cargo test: 224/224 passing
  • pnpm tsc --noEmit: clean

Generated with Claude Code

SZoloth and others added 12 commits March 24, 2026 07:43
…nt vmThreads timeout

Both tests hang when run after heavier tests deplete VM worker resources.
Passes in isolation; same root cause and fix pattern as FloatingToolbar/ToggleSwitch.
SettingsPage.test fails intermittently when run late in the full suite.
React 19's act() spin-waits for all pending async work; accumulated stale
microtasks from prior files (especially fake-timer tests and unlisten
promise chains from useTestRunContext) cause the await user.click() call
in test 6 to hang until the 30s timeout fires.

Same pattern as TabBar/ReadingSection/FloatingToolbar. Moving it into the
early bucket ensures it runs on a clean worker before resources are depleted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SettingsPage.test was added to early with push(), putting it last in
the early array. The early bucket already contains fake-timer tests
(useFileWatcher, FloatingToolbar, ExportAnnotationsPopover) which
contaminate React 19's scheduler — causing act() inside
user.keyboard() to spin-wait until the 30s timeout.

Use unshift() so SettingsPage.test runs FIRST in the early bucket,
before any fake-timer contamination can reach it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eads timeout

front-matter.test renders Reader (TipTap + all extensions) and was landing
in the rest bucket, causing 30s timeouts when run after memory is depleted
by other heavy editor tests — same pattern as TabBar/ReadingSection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…front-matter

The afterEach drain loop (3x setTimeout(0)) can stall under V8 GC pressure in a
long-running vmThreads worker, causing browser-stubs.test and diff-engine.test to
fail intermittently with a 30s hook timeout. Doubling hookTimeout to 60s gives GC
pauses room to complete.

front-matter.test's "preserves front matter after setContent" test does two renders +
waitFor and needs more than the default 30s when running on a warm worker — explicit
60s timeout added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both are lightweight (no TipTap) but were timing out when run last
within the early bucket — after Reader/FloatingToolbar/front-matter
deplete the vmThreads worker. Moving them to earlyLight ensures they
run before the heavy early tests on a fresh worker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hangs

StyleMemorySection.test: adds explicit 60s per-test timeout — the test uses a
real 6s window.setTimeout for the export toast, so it runs ~9-13s in isolation.
The global testTimeout:30s was too tight under system load.

SettingsPage.test: replaces userEvent.click with fireEvent.click in the
'passes settings and setSetting' test — avoids async act() spin-wait caused by
TestRunProvider's resolved-Promise effects in React 19.

apply-accepted-correction.test: changes outer await act(async) to sync act() for
the applyAcceptedCorrection assertion; moves HTML assertions to waitFor({timeout:10000})
to avoid TipTap's async scheduler hanging inside async act().

vitest.config: promotes TabBar.test and ReadingSection.test from early to earlyLight
bucket — they're lightweight (no TipTap) but their afterEach hook times out when run
after heavy tests deplete VM worker resources.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous commit removed the userEvent import but left setup() calls in
two tests (switching sections, escape key), causing TS2552 on CI. Replace
both with fireEvent (sync) — same rationale: avoids async act() hang from
TestRunProvider's listen() resolved-Promise in React 19.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
StyleMemorySection.test (mustRunFirst) triggers a real window.setTimeout(6000)
for its toast. vi.clearAllTimers() only clears fake timers, so this timer
persists and fires during the first act() of the next file, causing React 19
to spin-wait for the 30s testTimeout.

useSettings.test and DiffBanner.test don't mock @/lib/tauri-commands, so they
can safely run before mustRunFirst (no mock isolation impact). Moving them to a
new preFirst bucket guarantees they run with a completely clean scheduler state.

New order: preFirst → mustRunFirst → earlyFirst → earlyLight → early → small → rest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rEach

DiffBanner's last test uses vi.useFakeTimers(). The afterEach called cleanup()
then vi.useRealTimers(), but skipped vi.clearAllTimers(). React's scheduler
queues work via fake setTimeout internally during render; without explicitly
clearing those callbacks before restoring real timers, they become orphaned
and React 19's act() spin-waits on them in the next file (StyleMemorySection).

Adding vi.clearAllTimers() between cleanup() and vi.useRealTimers() ensures
all fake-timer scheduler work is drained before the fake timer registry is
abandoned.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…act() hang

RulesTab runs last in mustRunFirst, immediately before SettingsPage (earlyFirst).
Its delete test uses 2 userEvent clicks + 2 waitFor calls, leaving more stale
async callbacks than the 3 global drain cycles clear. Under system load, React
19's act() in SettingsPage's fireEvent.click spin-waits on these orphaned
callbacks, causing intermittent 12s hangs and test failures.

Fix: afterAll with 8 extra drain cycles in RulesTab.test.tsx, running after all
RulesTab tests complete (before vi.resetModules and before SettingsPage loads).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Under CPU throttling (background agents, indexing, other processes),
jsdom init and test execution slow dramatically. Tests that take <5ms
normally can hit 30s+ under load, causing false timeout failures.

60s matches hookTimeout and gives headroom without hiding real hangs.
Fixes intermittent load-induced failures in ToggleSwitch, search, and
other small tests that run late in the suite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@SZoloth SZoloth merged commit fdc6830 into main Apr 6, 2026
4 checks passed
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