When writing JavaScript code that manipulates the browser URL, you might use history.pushState
to update the query parameters or path without reloading the page. Verifying this in tests is not as straightforward as it seems, especially when using JSDOM in Vitest or Jest.
In a real browser, calling:
history.pushState({}, null, "/new-path?foo=bar");
will update window.location.pathname
and window.location.search
. However, JSDOM does not fully simulate this behaviour. While pushState
is called, window.location
remains unchanged. This means a test like the following will fail:
it("updates the query params", () => {
history.pushState({}, null, "/new-path?foo=bar");
expect(window.location.search).toBe("?foo=bar"); // fails in JSDOM
});
JSDOM updates its internal history stack but does not re-parse window.location
.
A more reliable way is to test whether your code called history.pushState
with the correct arguments. In Vitest you can use vi.spyOn
:
it("removes filters from the URL", async () => {
const pushSpy = vi.spyOn(history, "pushState");
await useFiltersToParams([], MOCK_ROUTER);
expect(pushSpy).toHaveBeenCalledWith(
{},
null,
expect.not.stringContaining("filters")
);
pushSpy.mockRestore();
});
Here we are not asserting against window.location.search
, but against the URL passed into pushState
. This ensures the function under test is producing the expected side effect.
When to use spies vs window.location
:
- Unit tests: prefer spying on
pushState
, because it isolates your codeβs behavior from JSDOM quirks. - Integration or end-to-end tests: use
window.location
assertions in Cypress, Playwright, or a real browser environment, where the URL actually changes.
TLDR
When testing URL updates in Vitest, donβt rely on window.location
. Instead, spy on history.pushState
to verify the correct URL was passed. This keeps your tests reliable while avoiding JSDOM limitations.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.