The URL object in the browser (in this case Chrome) compared to Node.js do not behave the same. As a result, I'm getting tests that pass when run in Node, but the behavior doesn't match in the browser.
To demonstrate, here's a simple parseURL function that tries to parse a URL from a string. If it can't parse it properly, then it instead returns a URL that equates to a Google search using the string provided.
function parseURL(query) {
const formattedQuery = query.includes('://') ? query : `http://${query}`;
try {
return new URL(formattedQuery);
} catch (err) {
return new URL(`https://www.google.com/search?q=${encodeURIComponent(query)}`);
}
}
And a simple Jest test to demonstrate how this function is expected to work:
describe('parseURL', () => {
it('accepts valid URL string', () => {
expect(parseURL('https://example.com')).toEqual(
new URL('https://example.com')
);
});
it('prepends protocol when missing', () => {
expect(parseURL('example.com')).toEqual(
new URL('http://example.com')
);
});
it('converts search queries', () => {
expect(parseURL('example.com lol jk')).toEqual(
new URL('https://www.google.com/search?q=example.com+lol+jk')
);
});
});
The first two tests have matching behavior for both Node.js and the browser. Great!
The third test, however, passes in Node.js even though the behavior doesn't match inside the browser. Specifically, when calling new URL('http://example.com lol jk') in Node.js, an error is raised. In the browser though, no error is raised, and instead a URL object is returned:
{
href: "http://example.com%20lol%20jk/",
origin: "http://example.com%20lol%20jk",
protocol: "http:",
username: "",
password: "",
hash: "",
host: "example.com%20lol%20jk",
hostname: "example.com%20lol%20jk",
href: "http://example.com%20lol%20jk/",
origin: "http://example.com%20lol%20jk",
password: "",
pathname: "/",
port: "",
protocol: "http:",
search: "",
searchParams: URLSearchParams {},
username: ""
}
How can I be confident that my tests reflect what is actually happening in the browser when using objects like this?
The problem is that URL is inconsistent between implementations.
It is expected behaviour that
new URL('http://example.com lol jk')
throws an error, it's not a valid URL. It's correctly implemented in Node.js and Firefox but not in Chrome (which is supposedly referred as 'the browser' here).
It's safe to rely on Node.js implementation in Jest. In case the consistency is required in production, URL needs to be replaced with spec-compliant implementation, whatwg-url.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With