proxy failed test when using fetch with TypeError: Only absolute URLs are supported
See original GitHub issueDescribe the bug
A clear and concise description of what the bug is.
I’m using fetch to post requests in the component like this
const authActions = (store) => ({
logIn: (state, userForm) => {
return fetch("/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(userForm),
})
.then((response) => {
if (!response.ok) {
throw response;
}
localStorage.setItem("current_user", JSON.stringify(userForm.username));
})
.catch((error) => {
console.error("Error:", error);
});
},
In the testing-library, I use msw to mock the post request
describe("Login", () => {
const handlers = [
rest.post("/api/login", (req, res, ctx) => {
return res(
ctx.delay(150),
ctx.json({
ok: true,
msg: "",
})
);
}),
];
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test("should render login page", () => {
render(<Login />);
userEvent.type(getByPlaceholderText("Username"), "valid_username");
userEvent.type(getByPlaceholderText("Password"), "valid_password");
userEvent.click(getByText(/Log in/i));
await waitFor(() => {
expect(window.localStorage.getItem("current_user")).toEqual("valid_username");
});
});
The problem is, the test fails with the following TypeError
Error: TypeError: Only absolute URLs are supported
at getNodeRequestOptions (/Users/hkoo/dom/ui/node_modules/node-fetch/lib/index.js:1327:9)
at /Users/hkoo/dom/ui/node_modules/node-fetch/lib/index.js:1432:19
at new Promise (<anonymous>)
at fetch (/Users/hkoo/dom/ui/node_modules/node-fetch/lib/index.js:1429:9)
at fetch (/Users/hkoo/dom/ui/node_modules/isomorphic-unfetch/index.js:4:34)
at logIn (/Users/hkoo/dom/ui/src/actions/authActions.js:5:12)
at logIn (/Users/hkoo/dom/ui/node_modules/redux-zero/react/index.js:81:34)
at onFinish (/Users/hkoo/dom/ui/src/components/Login.js:28:5)
at onFinish (/Users/hkoo/dom/ui/node_modules/rc-field-form/lib/Form.js:91:9)
at /Users/hkoo/dom/ui/node_modules/rc-field-form/lib/useForm.js:836:11
But when I manually log in on the browser, it works fine without error. I use webpack to proxy the api request like this
devServer: {
port: 3000,
historyApiFallback: true,
proxy: {
"/api": {
target: "https://my-api.com",
changeOrigin: true,
},
},
Can I ask why the TypeError occurred with fetch in testing? Does testing not use HTTP://localhost:3000
as base URL?
When I added absolute URL in both fetch
and res.post
like fetch("HTTP://localhost:3000/api/login")
and rest.post(“http://localhost:3000/api/login”), the error was gone. Also, I tried using
axiosinstead of
fetch`, the error was gone too. Is there a way to use relative path with fetch in mocking?
Environment
msw: 0.35.0
nodejs: 16.13.0
npm: 8.1.0
jest: ^27.3.1
,testing-library/preact: ^2.0.1
,preact: ^10.5.15
,redux-zero: ^5.1.7
Chrome version 96.0.4664.45 (Official Build) (x86_64)
Expected behavior
A clear and concise description of what you expected to happen.
The post response should be mocked as expected without raising error.
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (1 by maintainers)
Top GitHub Comments
Hey, @helloitsjoe. Thank you for raising this. Please see the answer to your question below.
Short answer
When testing browser-oriented code, use browser-oriented polyfills (i.e. whatwg-fetch instead of
node-fetch
).Long answer
There’s no such thing as “relative URL” in Node.js: there’s nothing to be relative to. In the browser, relative URLs are resolved against the current location. If you’re testing code that makes requests to relative URLs, this likely implies you’re testing code that was written to run in a browser. Jest, along with many other unit/integration testing frameworks, runs in a Node.js process, so all Node.js restrictions still apply. Jest circumvents some of those limitations by introducing JSDOM—a browser environment polyfill for Node.js. In JSDOM,
window.location
is polyfilled, so that’s why you can make relative requests in Jest tests.In the same way,
window.fetch
doesn’t exist in Node.js. This one, however, doesn’t exist in JSDOM either. That’s where you should pick an appropriate polyfill forfetch
so whenever your tested code calls it, the poyfill is called instead.node-fetch
is indeed a fetch polyfill but it’s designed for Node.js environments (where, as you’ve learned, relative URLs are non-existent). You cannot use this particular polyfill to test browser-oriented code, because it will simply throw the exception you’re experiencing whenever given a relative URL.Instead, there are fetch polyfills that are designed to run in either ambiguous or browser-like environments. The most prominent of those is whatwg-fetch fromGitHub. This polyfill does take relative URLs into account and will function as close as possible to the native
window.fetch
.One quick question - is this node-fetch used by msw? Or if I use fetch API in React app, does it use node-fetch? I didn’t import it -> Never mind. It came from jest-preset-preact. Browsers resolve the relative path unlike node-fetch