nuxt / test-utils

🧪 Test utilities for Nuxt
http://nuxt.com/docs/getting-started/testing
MIT License
322 stars 83 forks source link

useFakeTimers isn't working for the setInterval. #897

Open gsamal opened 3 months ago

gsamal commented 3 months ago

Environment

System:
    OS: Linux 5.15 Ubuntu 20.04.6 LTS (Focal Fossa)
    CPU: (8) x64 Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz
    Memory: 7.71 GB / 15.38 GB
    Container: Yes
    Shell: 5.0.17 - /bin/bash
  Binaries:
    Node: 20.11.1 - ~/.nvm/versions/node/v20.11.1/bin/node
    npm: 10.2.4 - ~/.nvm/versions/node/v20.11.1/bin/npm
  Browsers:
    Chrome: 112.0.5615.49
  npmPackages:
    vitest: 1.6.0 => 1.6.0

Reproduction

Non-working code with setInterval -

  describe('Testing timers', () => {
    let triggered = false;
    beforeAll(() => {
      vi.useFakeTimers();
      setInterval(()=>{
        triggered = true;
      }, 1000)
    });
    afterAll(() => {
      vi.useRealTimers();
    });
    it.only('Should trigger the timer', () => {
      vi.advanceTimersByTime(1000);
      expect(triggered).toEqual(true); // Fails. triggered = false here
    });
  });

Working with setTimeout

  describe('Testing timers', () => {
    let triggered = false;
    beforeAll(() => {
      vi.useFakeTimers();
      setTimeout(()=>{
        triggered = true;
      }, 1000)
    });
    afterAll(() => {
      vi.useRealTimers();
    });
    it.only('Should trigger the timer', () => {
      vi.advanceTimersByTime(1000);
      expect(triggered).toEqual(true); // Successful.
    });
  });

Reproduction - https://stackblitz.com/edit/github-agxb9y-xks33q?file=test%2Fapp.nuxt.spec.ts Works without nuxt - https://stackblitz.com/edit/vitest-dev-vitest-hr7i1i?file=test%2Ftimer.test.ts

Describe the bug

setInterval isn't triggering with fakeTimer, however setTimeout does. I have tried advanceTimersByTime, advanceTimersToNextTimer and runAllTimers but doesn't help.

Note - I don't find this issue with the Vitest itself. using defineVitestConfig is causing this behaviour.

Additional context

No response

Logs

No response

dosubot[bot] commented 3 months ago

Hey @gsamal! I'm here to help you with any bugs, questions, or contributions you may have. Let's work together to solve this issue!

The issue you're encountering with vi.useFakeTimers() and setInterval in the test-utils repository might be due to several factors:

  1. Custom Implementation of setInterval in Nuxt: The Nuxt framework has a custom implementation for setInterval that might interfere with vi.useFakeTimers(). This implementation checks if the code is running on the client side and either uses window.setInterval or throws an error/logs a message if it is running on the server side [1].

  2. Lifecycle Hooks: There are specific lifecycle hooks in the Nuxt framework that could affect the execution of setInterval when using vi.useFakeTimers(). setInterval should not be used on the server and should be wrapped with an onNuxtReady, onBeforeMount, or onMounted lifecycle hook, or ensured to be called only in the browser by checking import.meta.client [1].

  3. Environment Setup: The test-utils repository contains specific setup and teardown code that might affect the behavior of timers in tests. The setup function prepares the test environment, while the teardown function cleans up after all tests have run. This setup and teardown process could potentially influence the behavior of timers if they are dependent on the state of the file system or the environment set up by these functions [2][3].

  4. Interaction with happy-dom or jsdom: There is a known interaction between happy-dom or jsdom environments and vi.useFakeTimers() that could cause issues with setInterval. The environment setup does not explicitly address the interaction with vi.useFakeTimers(), which can lead to issues where setInterval does not behave as expected when fake timers are used [4].

To address this issue, you can try the following steps:

  1. Ensure Client-Side Execution: Make sure that setInterval is only called on the client side by wrapping it in appropriate lifecycle hooks or checking import.meta.client.

  2. Review Environment Setup: Check the setup and teardown code in your tests to ensure that they do not interfere with the behavior of timers.

  3. Use happy-dom or jsdom Configuration: Ensure that the configuration for happy-dom or jsdom is correctly set up to handle fake timers.

Here is an example of how you might modify your test to ensure setInterval is only called on the client side:

describe('Testing timers', () => {
  let triggered = false;
  beforeAll(() => {
    vi.useFakeTimers();
    if (import.meta.client) {
      setInterval(() => {
        triggered = true;
      }, 1000);
    }
  });
  afterAll(() => {
    vi.useRealTimers();
  });
  it.only('Should trigger the timer', () => {
    vi.advanceTimersByTime(1000);
    expect(triggered).toEqual(true); // Should pass if setInterval is called on the client side
  });
});

This ensures that setInterval is only executed in the client environment, which might resolve the issue with vi.useFakeTimers().

To continue talking to Dosu, mention @dosu.

gsamal commented 3 months ago

When I tried with window.setInterval, this works. As per the above reference, it seems like we have defined our own setInterval. Is there any config in the nuxt to prevent that?

dosubot[bot] commented 3 months ago

No, there is no configuration in Nuxt to prevent it from using its custom setInterval implementation and allow the standard window.setInterval to be used instead. The custom implementation is designed to ensure that setInterval is not used on the server side, and it conditionally uses window.setInterval only on the client side [1].

To continue talking to Dosu, mention @dosu.

cuixiaorui commented 4 days ago

Using window.setInterval is definitely not a good idea. If users are not familiar with Nuxt's implementation details, they will be very confused about why tests fail when using setInterval. Before seeing this issue, I spent a lot of time debugging this problem. If Nuxt has wrapped the original setInterval, then this issue should be handled properly to allow users to directly call setInterval.