Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
xsjcTony opened this issue Apr 12, 2023 · 5 comments
Labels
environment:react Issue specific to React

Comments

@xsjcTony
Copy link

xsjcTony commented Apr 12, 2023

Reproduction example

https://stackblitz.com/edit/vitejs-vite-askvcq?file=src/__tests__/App.test.tsx

Prerequisites

Describe the bug

I have a React component which is a timer (minimal reproduction), that starts automatically after mounted, and there's a button to RESET the timer.

In the test, I'm using vi.useFakeTimers() and await vi.advanceTimersByTimeAsync(500) to test the timer segmentally.

However, I'm not able to use await user.click() to click the button.

This is a known issue with fake timers, however, none of the existing solutions works.

None of these works

const user = userEvent.setup({
  advanceTimers: vi.advanceTimersByTime,
  // advanceTimers: vi.advanceTimersByTimeAsync
  // advanceTimers: vi.advanceTimersByTime.bind(vi)
  // advanceTimers: vi.advanceTimersByTimeAsync.bind(vi)
  // delay: null,
})

And I cannot use vi.useRealTimers() before clicking button, since it will break the fake timer to further test the component's reset functionality.


Those solutions above are all based on Jest since almost all resources on the internet are for Jest. But since I've never used Jest, so I'm not sure if it's working in Jest, and is this an issue with @testing-library/user-event or Vitest

Expected behavior

The button is successfully clicked

Actual behavior

It makes the test timed out

User-event version

14.0.0

Environment

System:
  OS: Windows 10 10.0.22000
  CPU: (8) x64 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
  Memory: 9.23GB / 31.69GB
Binaries:
  Node: 16.18.0 - C:\Program Files\nodejs\node.EXE
  npm: 8.19.2 - C:\Program Files\nodejs\npm.CMD
Browsers:
  Edge: Spartan (44.22000.120.0), Chromium (112.0.1722.39)
  Internet Explorer: 11.0.22000.120
npmPackages:
  @vitejs/plugin-react: ^3.1.0 => 3.1.0
  vite: ^4.2.1 => 4.2.1
  vitest: ^0.30.1 => 0.30.1
  @testing-library/jest-dom: ^5.16.5 => 5.16.5
  @testing-library/react: ^14.0.0 => 14.0.0
  @testing-library/user-event: ^14.4.3 => 14.4.3
  jsdom: ^21.1.1 => 21.1.1
  react: ^18.2.0 => 18.2.0
  react-dom: ^18.2.0 => 18.2.0

Additional context

Please also refer to vitest-dev/vitest#3184

@xsjcTony xsjcTony added bug Something isn't working needs assessment This needs to be looked at by a team member labels Apr 12, 2023
@xsjcTony xsjcTony changed the title userEvent.click() fails when used with vi.useFakeTimers(), all available solutions are not working userEvent.click() fails when used with vi.useFakeTimers(), all available solutions are not working Apr 12, 2023
@ph-fritsche ph-fritsche added environment:react Issue specific to React and removed bug Something isn't working needs assessment This needs to be looked at by a team member labels Apr 12, 2023
@ph-fritsche
Copy link
Member

This is an issue with @testing-library/react. See testing-library/react-testing-library#1197

Trick this piece of code into recognizing your environment as Jest.

globalThis.jest = 'neitherUndefinedNorNull'

@xsjcTony
Copy link
Author

@ph-fritsche Thanks a lot, I got what the problem is.

But regarding your solutions, I think for a temporary workaround, this makes more sense to me wojtekmaj/react-async-button@2d26f21

Is there any ETA to fix this issue? since I personally don't like temp workaround to be there forever in my code🤣

Regarding the fix, just like letting users pass advanceTimer option in userEvent.setup(), instead of using jest.advanceTimersByTime, this should be testing-framework agnostic, since there are also some ppl like me who had never used Jest

@xsjcTony
Copy link
Author

xsjcTony commented Apr 13, 2023

In your test suites using fake timers

import { beforeAll, vi, describe } from 'vitest';

describe('this suite uses fake timers', () => {
  // Temporarily workaround for bug in @testing-library/react when use user-event with `vi.useFakeTimers()`
  beforeAll(() => {
    const _jest = globalThis.jest;
  
    globalThis.jest = {
      ...globalThis.jest,
      advanceTimersByTime: vi.advanceTimersByTime.bind(vi)
    };
  
    return () => void (globalThis.jest = _jest);
  });
})

@iulspop
Copy link

iulspop commented May 27, 2023

I ran into same problem today. My work around:

  beforeEach(() => {
    vi.useFakeTimers()

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

  beforeAll(() => {
    vi.useRealTimers()
  })
  
 test('given a new recurring question submitted: form data contains only question text', async () => {
    ...
    const user = userEvent.setup({
      advanceTimers: vi.advanceTimersByTime.bind(vi),
    })
  })

This worked but causes a Warning: An update to RouterProvider inside a test was not wrapped in act(...). warning.

Would appreciate if y'all fixed this. Testing library should work out of the box with Vitest imo.

Full test file
import { unstable_createRemixStub as createRemixStub } from '@remix-run/testing'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import { CreateQuestionFormComponent } from './create-question-form-component'

describe('CreateQuestionForm component', () => {
  beforeEach(() => {
    vi.useFakeTimers()

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

  beforeAll(() => {
    vi.useRealTimers()
  })

  test('given a new recurring question submitted: form data contains only question text', async () => {
    const date = new Date('2022-10-20T01:00:00.000Z')
    vi.setSystemTime(date)

    let formData: FormData | undefined
    const RemixStub = createRemixStub([
      {
        path: '/',
        element: <CreateQuestionFormComponent />,
        action: async ({ request }) => {
          formData = await request.formData()
          return null
        },
      },
    ])

    const user = userEvent.setup({
      advanceTimers: vi.advanceTimersByTime.bind(vi),
    })

    render(<RemixStub />)

    const questionText = 'Did you go to bed between 8 and 9PM?'
    await user.type(screen.getByLabelText('What is the recurring question?'), questionText)
    await user.click(screen.getByRole('button', { name: /submit/i }))

    const formEntries = Object.fromEntries(formData.entries())
    expect(formEntries).toEqual({
      text: questionText,
      timestamp: date.toISOString(),
      utcOffsetInMinutes: String(date.getTimezoneOffset()),
    })
  })

  test('given form render: has cancel link to back to /questions', async () => {
    const RemixStub = createRemixStub([
      {
        path: '/',
        element: <CreateQuestionFormComponent />,
      },
    ])
    render(<RemixStub />)

    expect(screen.getByRole('link', { name: /cancel/i })).toHaveAttribute('href', '/questions')
  })

  test('given click submit before input text: does not submit form', async () => {
    let formData: FormData | undefined
    const RemixStub = createRemixStub([
      {
        path: '/',
        element: <CreateQuestionFormComponent />,
        action: async ({ request }) => {
          formData = await request.formData()
          return null
        },
      },
    ])

    const user = userEvent.setup({
      advanceTimers: vi.advanceTimersByTime.bind(vi),
    })
    render(<RemixStub />)

    await user.click(screen.getByRole('button', { name: /submit/i }))
    expect(formData).toEqual(undefined)
  })
})

@mrwwalmsley
Copy link

Should this be added to documentation somewhere?
I spent a few hours trying to get my tests with fake timers migrated from Jest to Vite.
This solved most of my issues. It would be great to have this in the docs somewhere??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
environment:react Issue specific to React
Projects
None yet
Development

No branches or pull requests

4 participants