-
-
Notifications
You must be signed in to change notification settings - Fork 527
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
TypeError: performance.markResourceTiming is not a function #1851
Comments
Hi, @robdodson. This error implies you are running a version of Node.js older than 18.2.0 (see this). Please upgrade your Node.js version and that will fix the issue. |
It's possible that something is wrong with my setup, but logging
I'll see if I can put together a minimal reproduction at some point. It's a little tough because our app is an ejected create-react-app that has been modified a fair bit. |
Interesting! It looks like the code in undici is checking for the node version though, and avoiding it when the version is incorrect Are your tests running in jsdom? Maybe that global isn't implemented in the version of jsdom you're using @robdodson - I think that could explain it not being available with the node version being correct? |
It looks like we are using jest-environment-jsdom v29.7.0 uses jsdom v20 jsdom v20.0.0 does have the I think In jest.polyfills.js we set |
I've tried to reproduce the issue in this repo: https://github.com/robdodson/msw-test I'm not seeing the same error, but I am seeing a different issue with undici trying to access a global that exists in Node but not JSDom:
I'm not entirely sure what's going on, but my rough guess is when we polyfill fetch with undici in jest.polyfills.js then undici starts trying to use the globals from JSDom. Neither performance.markResourceTiming nor clearImmediate exist in JSDom so undici hits runtime errors. Does that assumption sound correct? |
Forgot to mention - in the repo, I specifically commented out my msw handlers to make the test fail. If you comment them back in, everything will work. So I'm guessing we're hitting a code path in undici when we 404 the request and it causes it to try to call clearImmediate |
interesting! definitely looks like that 404 / clearImmediate relates to node apis that jsdom doesn't implement. The issue of "node" and "jsdom" implementations and how they kind of leak between eachother is...fun |
Yeah, if the assumption is correct—that undici is trying to use missing globals in JSDom—then I'm wondering:
@kettanaito what do you think? |
@robdodson
How do I add clearImmediate to the jest.polyfills.js? |
@samanthaburboz Here's what my jest.polyfills.js looks like. I think /**
* @note The block below contains polyfills for Node.js globals
* required for Jest to function when running JSDOM tests.
* These HAVE to be require's and HAVE to be in this exact
* order, since "undici" depends on the "TextEncoder" global API.
*
* Consider migrating to a more modern test runner if
* you don't want to deal with this.
*/
const { performance } = require("node:perf_hooks");
const { TextDecoder, TextEncoder } = require("node:util");
const { clearImmediate } = require("node:timers");
Object.defineProperties(globalThis, {
TextDecoder: { value: TextDecoder },
TextEncoder: { value: TextEncoder },
performance: { value: performance },
clearImmediate: { value: clearImmediate },
});
const { Blob } = require("node:buffer");
const { fetch, Headers, FormData, Request, Response } = require("undici");
Object.defineProperties(globalThis, {
fetch: { value: fetch, writable: true },
Blob: { value: Blob },
Headers: { value: Headers },
FormData: { value: FormData },
Request: { value: Request },
Response: { value: Response },
}); |
I'm starting to think this might be a bigger issue and this polyfills file might make msw incompatible with Jest and libraries designed to work with Jest. Here are some examples from our tests that I don't know how to work around:
If I comment out the polyfills then all of the above issues go away and the tests pass again. edit: Maybe a possible fix for the clearImmediate issue would be to make it writeable?
But i'm not sure how to work around the other issues with FormData and Blob |
Thanks for the hint regarding adding the additional polyfills. However, the jest.useFakeTimers function breaks for me as well. Running nodejs v18.16.0
Im currently thinking about migrating the project to vitest... I wonder what else breaks then ;-) Edit: configuring
|
If I make performance and clearImmediate writable, and remove Blob and FormData from the polyfills, then all of my tests pass again. Here's my current polyfills:
@mattcosta7 @kettanaito do either of you know why Blob and FormData were included in the original set of polyfills? |
I don't offhand know, my assumption would be consistency of the underlying stream behaviors? |
@robdodson, yes, they were included in the original suggestion for const { Blob } = require('node:buffer')
const { FormData } = require('undici') We shouldn't remove them.
Naturally, since
This isn't something you should care about. RTL runs exclusively in JSDOM. You say it yourself—that API is implemented in JSDOM. I fail to see the issue here.
Thanks for noticing that order. It bit me quite a lot trying to figure out the right order of things for that polyfill file 👍
Very hard to reason about this. Using fake timers by design will affect test performance since you defer the test execution to the moment when you resolve the timers. Imo, not something MSW should be concerned about.
A humble plug that you would spent 0 minutes on this if you used modern, supported testing framework, like Vitest. I'm okay with providing polyfills and suggestions on how to patch Jest to let you in on the modern JavaScript but this isn't something I'm planning on supporting at all. |
Also, a very important note: React Testing Library doesn't support Node.js v18. Inevitably, any attempts to run MSW (which requires Node.js v18+) with RTL will result in all sorts of odd behaviors. With that in mind, here's my conclusion on this issue.
|
I think the issues here are mostly around jsdom operation Since jsdom intends to act as a browser and not as node - tests using jsdom call browser apis. By polyfilling with node versions of these, were removing the ability to use browser like parts of them in jsdom - using node apis only. Do/can we support browser based versions of these when running in jsdom? (I'm not actually sure offhand) but this might help avoid some issues generally. Testing in jsdom is something I conceptually have trouble with because it's not but it's also trying very hard not to be - and we do want some node apis there but not all of them? Tools designed to operate for jsdom like react testing library expect those dom like versions of these things (fetch, request, response,blob, form data) and maybe there's a better way to support them both without always relying on the node version? |
Yes I think this is the core issue, not the lack of Node v18 support in RTL (that might be a separate issue, but I don't think it's the one I'm hitting in this ticket). In my test I'm attempting to use the Clipboard API that RTL stubs out: expect(await navigator.clipboard.readText()).toBe(expectedUrl.href) Which throws this error from the
Here is the // node_modules/jsdom/lib/jsdom/living/generated/Blob.js
exports.is = value => {
return utils.isObject(value) && utils.hasOwn(value, implSymbol) && value[implSymbol] instanceof Impl.implementation;
};
exports.isImpl = value => {
return utils.isObject(value) && value instanceof Impl.implementation;
};
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
if (exports.is(value)) { // <-- We are failing this check 🔴
return utils.implForWrapper(value);
}
throw new globalObject.TypeError(`${context} is not of type 'Blob'.`);
}; If I console.log the steps from utils.isObject(value) // true
utils.hasOwn(value, implSymbol) // false
value[implSymbol] instanceof Impl.implementation // false My guess is that when we replace JSDom's Blob with Node's Blob in jest.polyfills.js, it allows for the creation of Blobs that are missing a Symbol that JSDom normally attaches to them. When code tries to interact with the Blob in JSDom, it fails this identity check. If I remove Blob from my jest.polyfills.js then the test passes. I suspect this happens because I'm not yet using any part of undici that depends on a Node-specific version of Blob.
I'm really curious to see how Vitest works around this? I'm going to try to recreate the issue with RTL in a Vitest app to see if everything "just works". |
Prerequisites
Environment check
msw
versionNode.js version
20.9.0
Reproduction steps
Current behavior
undici will throw an error:
TypeError: performance.markResourceTiming is not a function
. I'm guessing this is becauseperformance.markResourceTiming
is not a browser API, but the jest.polyfills.js file replaces fetch withundici
which attempts to call this Node API here.Expected behavior
fetch should work
I worked around the issue by adding
performance
to my jest.polyfills.js.Apologies for not creating a reproduction repository. Hopefully this is the correct workaround and it helps someone.
The text was updated successfully, but these errors were encountered: