The object thrown by the constructor of URL is not an instance of Error.
Create a file error.test.js with the following contents:
#!/usr/bin/env node
try {
new URL("not_a_url");
} catch (anything) {
process.exit(anything instanceof Error ? 0 : 1);
}
If you run node error.test.js the program exits successfully. However, if you use jest to run this file you get:
§ jest
● process.exit called with "1"
3 | new URL("not_a_url");
4 | } catch (anything) {
> 5 | process.exit(anything instanceof Error ? 0 : 1);
| ^
6 | }
7 |
at Object.exit (error.test.js:5:11)
RUNS ./error.test.js
I expect the error raised by the call to URL to be an instance of Error.
System:
OS: macOS Mojave 10.14.6
CPU: (8) x64 Intel(R) Core(TM) i7-8559U CPU @ 2.70GHz
Binaries:
Node: 12.1.0 - /usr/local/bin/node
Yarn: 1.22.0 - ~/.yarn/bin/yarn
npm: 6.13.7 - /usr/local/bin/npm
Is jest perhaps not using the same implementation of URL as when I run node directly?
Indeed printing out URL.toString() gives different results for the two different invocations.
@fredefox looks like you are correct, whatwg-url is being used (polyfill for URL), but that is not the issue.
Instead, it is related to #2549 (via https://github.com/facebook/jest/issues/6788#issuecomment-409371060).
This does look like it could be a duplicate. Yes. Thanks @carsonreinke. What I'm not really getting is what it has to do with instanceof. I'm not aware that it's possible to override built-in operators. Isn't the problem more that the Jest runtime mutates the global environment.
@fredefox it is really strange, I have not really gotten to the bottom of it, but I did try using https://www.npmjs.com/package/jest-runner-mocha and it does NOT exhibit that behavior.
I am getting TypeError: URL is not a constructor in my jest test, while it works fine in node. Is this perhaps related?
I am getting
TypeError: URL is not a constructorin my jest test, while it works fine in node. Is this perhaps related?
I don't think that's related, no.
This is #2549, yeah
@fredefox it is really strange, I have not really gotten to the bottom of it, but I did try using npmjs.com/package/jest-runner-mocha and it does NOT exhibit that behavior.
Yeah, this behavior is specific to Jest since we use the vm module to create sandboxes - mocha runs all the tests in the same context.
Latest update to #2549 is essentially https://github.com/nodejs/node/issues/31852
Thanks @SimenB
@fredefox I believe a good work around for this problem, wherever you are using URL you could specifically import instead of relying on it being globally defined.
@fredefox I believe a good work around for this problem, wherever you are using
URLyou could specifically import instead of relying on it being globally defined.
I wasn't aware that I could import built-ins. Would you happen to know how you would modify my example to circumvent this? If you don't know off the top of your head it's ok.
@fredefox so I think as simple as adding const URL = require('url'); wherever you are using URL.
Here is an example:
const URL = require('url');
try {
new URL("not_a_url");
}
catch (anything) {
console.log(anything instanceof TypeError); //will be true
}
try {
new global.URL("not_a_url");
}
catch (anything) {
console.log(anything instanceof TypeError); //will be false
}
I'm not really sure how this would impact the polyfill though, probably nothing since this is a Node/Jest only problem.
However, when I tried it I found that the resulting object is actually not the same, as there is not SearchParameters object
@lukasoppermann Something like const URLSearchParams = require('url').URLSearchParams, if you look at require('url') is actually this:
{
Url: [Function: Url],
parse: [Function: urlParse],
resolve: [Function: urlResolve],
resolveObject: [Function: urlResolveObject],
format: [Function: urlFormat],
URL: [Function: URL],
URLSearchParams: [Function: URLSearchParams],
domainToASCII: [Function: domainToASCII],
domainToUnicode: [Function: domainToUnicode],
pathToFileURL: [Function: pathToFileURL],
fileURLToPath: [Function: fileURLToPath]
}
Ah. Thank you very much @carsonreinke! That'll do it.
And re the "not the same" part. Just to expand on what @carsonreinke already wrote. In his example he's shadowing the built-in URL, but the thing doing the shadowing, is _not_ the same as the thing it is shadowing. The thing he is shadowing is present at URL.URL.