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

Bug: event handlers not invoked when using jest.useFakeTimers or vi.useFakeTimers #1854

Closed
aethr opened this issue Nov 10, 2022 · 4 comments · Fixed by #1856
Closed

Bug: event handlers not invoked when using jest.useFakeTimers or vi.useFakeTimers #1854

aethr opened this issue Nov 10, 2022 · 4 comments · Fixed by #1856
Labels
bug Something isn't working

Comments

@aethr
Copy link
Contributor

aethr commented Nov 10, 2022

Describe the bug
We are experiencing an issue in our unit and integration tests that utilise useFakeTimers when using:

  • vue@3.2.41 on jest@27.5.1
  • vue@3.2.40 on vitest@0.25.1

Components with event bindings on outer elements are not having their handlers invoked when using useFakeTimers in jest or vitest.

To Reproduce
https://codesandbox.io/s/vue3-vue-test-utils-vitest-5yex3q?file=/src/useFakeTimers.test.js

Expected behavior
We expect all relevant event handlers to be invoked when userFakeTimers is used.

Related information:

  • @vue/test-utils version: 2.2.1
  • Vue version: 3.2.40+
  • vitest version: 0.25.1

Related issue in vitest: vitest-dev/vitest#649

Additional context
This isn't actually a bug in VTU, it's a side-effect of a recent edge-case change to vue, visible here: https://github.com/vuejs/core/blob/5ee40532a63e0b792e0c1eccf3cf68546a4e23e9/packages/runtime-dom/src/modules/events.ts#L100-L104

Vue is trying to prevent an edge case where an inner event handler causes a state change that attaches new event handlers to outer elements, where the new event handlers would catch the original event as it bubbles outwards. This code won't invoke any event handlers that were attached in the same tick when the event was created.

Using useFakeTimers mocks the Date object, and causes all Date.now() to return the same value, which means that handlers and events all get the same timestamp, so the check in the code linked above prevents event handlers from firing.

A simple workaround is to advance the timers after the component is mounted, but before the event is invoked (see the last unit test in the codesandbox).

I've created a simple plugin that will advance timers by 1ms before triggering any event if useFakeTimers is in use:

const triggerWithFakeTimers = (wrapper) => {
  const vtuTrigger = wrapper.trigger;

  return {
    trigger: async (...args) => {
      if (vi && vi._timers._fakingTime) {
        vi.advanceTimersByTime(1);
      }
      return vtuTrigger.bind(wrapper)(...args);
    },
  };
};

config.plugins.DOMWrapper.install(triggerWithFakeTimers)
@aethr aethr added the bug Something isn't working label Nov 10, 2022
@aethr
Copy link
Contributor Author

aethr commented Nov 10, 2022

Sorry to post this as a bug, when it really isn't a bug in VTU. I spent a considerable amount of time debugging this, and I thought it was likely that others experiencing this issue would look here for answers. Hopefully this is helpful to someone else.

@aethr
Copy link
Contributor Author

aethr commented Nov 10, 2022

Another workaround from the linked vitest issue is to only fake the timers that are needed for the test, and most importantly not to fake the Date object:

// won't trigger this issue
vi.useFakeTimers({
  toFake: ['setTimeout', 'clearTimeout']
})
// will trigger this issue
vi.useFakeTimers({
  toFake: ['setTimeout', 'clearTimeout', 'Date']
})

@cexbrayat
Copy link
Member

Interesting, thanks @aethr for taking the time to share this issue and the solution you found 👍

I'm wondering what we can do in VTU to help. Maybe trigger could log a warning if Date is faked (if this is possible to easily find out for jest and vitest without relying on them...)?

cexbrayat added a commit to cexbrayat/vue-test-utils-next that referenced this issue Nov 11, 2022
cexbrayat added a commit to cexbrayat/vue-test-utils-next that referenced this issue Nov 11, 2022
@cexbrayat
Copy link
Member

@aethr I tried something in #1856 to trick Vue. It looks like this should fix the issue, but I'm not super convinced by the hack...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants