Skip to content

feat: automatically advance fake timers in Vitest#1304

Open
TrevorBurnham wants to merge 1 commit intotesting-library:mainfrom
TrevorBurnham:fix-user-event-fake-timers
Open

feat: automatically advance fake timers in Vitest#1304
TrevorBurnham wants to merge 1 commit intotesting-library:mainfrom
TrevorBurnham:fix-user-event-fake-timers

Conversation

@TrevorBurnham
Copy link
Copy Markdown

@TrevorBurnham TrevorBurnham commented Oct 5, 2025

This PR adds automatic detection of fake timers from both Jest and Vitest, eliminating the need for users to manually configure advanceTimers when using vi.useFakeTimers() or jest.useFakeTimers().

Fixes #1115

Problem

When using Vitest with fake timers, userEvent would timeout because it wasn't advancing the fake timers. Users had to work around this by shimming the Jest global:

globalThis.jest = {
  advanceTimersByTime: vi.advanceTimersByTime.bind(vi),
}

This happened because userEvent only checked for Jest's timer APIs, not Vitest's.

Solution

Auto-detect Jest and Vitest timer globals so advanceTimers doesn't need to be configured manually.

  • New getTimerAdvancer() in src/utils/misc/timerDetection.ts — checks globalThis.jest and globalThis.vi for an advanceTimersByTime method and returns a bound reference to it (or null).
  • wait.ts calls getTimerAdvancer() when the user hasn't provided a custom advanceTimers option, falling back to the default no-op if no framework is detected.
  • Manual advanceTimers configuration still takes precedence over auto-detection.
  • When both Jest and Vitest globals are present, Jest takes precedence for backward compatibility.

Before

test('button click', async () => {
  vi.useFakeTimers()
  globalThis.jest = {
    advanceTimersByTime: vi.advanceTimersByTime.bind(vi),
  }
  const user = userEvent.setup()
  await user.click(button) // Would timeout without workaround
})

After

test('button click', async () => {
  vi.useFakeTimers()
  const user = userEvent.setup()
  await user.click(button) // Works automatically
})

@codesandbox-ci
Copy link
Copy Markdown

codesandbox-ci bot commented Oct 5, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@JoueBien
Copy link
Copy Markdown

JoueBien commented Feb 18, 2026

Before this is merged you'll probably want to fix the circular import of defaultAdvanceTimers which shows up in a warning when build is run.

When using this is the intent to have jest in the languageOptions.globals?
like so:

 languageOptions: {
      globals: {
        ...globals.jest,
      },
    },

Or for earlier versions of jest in a set up file:

global.jest = {
  ...jest,
};

@TrevorBurnham TrevorBurnham force-pushed the fix-user-event-fake-timers branch from 66cbd51 to 2e77fcc Compare February 18, 2026 13:46
@TrevorBurnham
Copy link
Copy Markdown
Author

@JoueBien Thanks! I've updated the PR branch.

@pbomb
Copy link
Copy Markdown

pbomb commented Mar 17, 2026

Just wondering if the plan is still to land this fix. We're seeing this in a large monorepo in which we'd like to incrementally migrate from Jest to Vitest.

@TrevorBurnham TrevorBurnham force-pushed the fix-user-event-fake-timers branch from 2e77fcc to d4c8ab5 Compare March 29, 2026 18:04
@TrevorBurnham
Copy link
Copy Markdown
Author

I've rebased the branch. The build failure in CI appears to be unrelated to this change:

error eslint-visitor-keys@5.0.1: The engine "node" is incompatible with this module. Expected version "^20.19.0 || ^22.13.0 || >=24". Got "20.12.2"

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.

userEvent.click() fails when used with vi.useFakeTimers(), all available solutions are not working

3 participants