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

[Vitest 3] performance.now() Issue #7198

Closed
6 tasks done
DavidR95 opened this issue Jan 9, 2025 · 3 comments
Closed
6 tasks done

[Vitest 3] performance.now() Issue #7198

DavidR95 opened this issue Jan 9, 2025 · 3 comments

Comments

@DavidR95
Copy link

DavidR95 commented Jan 9, 2025

Describe the bug

It's a little tricky to explain, but using the latest beta version (3.0.0-beta.4), I am getting some new test failures when the code under test is using performance.mark(), which errors because it internally tries to use performance.now() as a default, and that is returning a negative timestamp.

It seems the timestamp being generated by performance.now() is now being affected by the faked "now" timer, and if that timer is in the past, the timestamp is negative, and performance.mark() breaks.

Note that this only seems to be the case when window.performance is assigned to a variable prior to the timer being mocked.

I assume this is perhaps somewhat related to #7097.

It's probably best demonstrated with the reproduction below.

Reproduction

https://stackblitz.com/edit/vitest-dev-vitest-a9ymmqty?file=vite.config.ts,package.json,test%2Fbasic.test.ts,tsconfig.json&initialPath=__vitest__/

You can see the test contains two logs. Even though they essentially call the "same" now function, the former (where perf is assigned before the timer is faked) returns a negative timestamp, and latter returns a (presumably mocked) 0.

If you revert to version 2.1.6 of Vitest, you'll see that both calls now return the same (positive) value.

Note that, in my case, the code using performance.mark is internally within vue-i18n so I can't easily change where the calls are made. Apologies if the underlying problem is more accurately attributed to a different lib (e.g., sinon, vue-i18n, etc).

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 18.20.3 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.2.3 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/bin/pnpm
  npmPackages:
    vite: ^6.0.6 => 6.0.7 
    vitest: 2.1.6 => 2.1.6

Used Package Manager

npm

Validations

@hi-ogawa
Copy link
Contributor

hi-ogawa commented Jan 10, 2025

I think what affected you is probably #6288. performance wasn't mocked on v2, but v3 does. If you want to leave performance out, you can use toFake to exclude performance like #7142 (comment)

I'm not sure if this is fake-timer issue. Probably, they mutate only window/globalThis.performance = fakePerformance and it doesn't mutate the original object property itself window/globalThis.performance.now = fakeNow.

Note that, in my case, the code using performance.mark is internally within vue-i18n so I can't easily change where the calls are made.

Can you provide a link to the code? Also having more realistic test cases would be nice to understand the significance of the issue.

@hi-ogawa
Copy link
Contributor

hi-ogawa commented Jan 10, 2025

If someone did const tmp = performance, then tmp.now won't get faked and that can be probably considered as an escape hatch from fake timers. Personally, I don't have a strong opinion, but if you think this behavior is not ideal, I would suggest discussing with fake-timers side https://github.com/sinonjs/fake-timers.

Note that Date.now also works similarly and const tmpDate = Date + tmpDate.now is a way to get around of mock timers. I made a quick repro to confirm this behavior https://github.com/hi-ogawa/reproductions/tree/main/vitest-7198-fake-timers-performance-now

I'll close this for now, but I'm curious what's the situation with vue-i18n, so if you can provide a repro with that, I might be able to suggest some workaround.

@DavidR95
Copy link
Author

Thanks for having a look @hi-ogawa

As for vue-i18n, it's difficult to create an exact reproduction but the code causing the problem is effectively here. As you can see, they assign window.performance in the top-level of a module, so it pretty much always gets called before any test-level mocking occurs. This means any of my tests that mocked the date to be in the past, and invoked any vue-i18n code would now break (since their "custom" mark method is almost always called internally somehere).

It was a little tricky to figure out exactly what was happening originally, but I grant this is probably a fairly uncommon use-case 😁

In any case, I'm sure I can work around it easily, including the suggestions you mentioned above. Thanks again 👍

@github-actions github-actions bot locked and limited conversation to collaborators Jan 25, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants