Skip to content

Testing

Testing components that use @farbenmeer/router is straightforward because the Router component is designed to be decoupled from the browser environment. You can explicitly provide location and history objects via props, allowing you to simulate any route state and capture navigation events without a real browser window.

To render your application at a specific route during a test, you can pass a custom location object to the Router component. This object should mimic the window.location interface, primarily needing pathname, search, and hash.

If you don’t provide a location, the router will default to using window.location, which might not be what you want in a test environment (usually defaults to /).

import { test, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { Router, Route } from "@farbenmeer/router";
test("renders user profile", () => {
render(
<Router
location={{
pathname: "/users/123",
search: "",
hash: ""
}}
>
<Route path="/users/:id">
<UserProfile />
</Route>
</Router>
);
expect(screen.getByText("User 123")).toBeInTheDocument();
});

To test interactions that cause navigation (like clicking a link or submitting a form), you need to provide a mock history object. The router uses history.pushState and history.replaceState to change routes.

You can create a helper to generate a mock history object. This helper should maintain the current URL state so that when the router asks for it, it gets the updated value.

Here is an example of how to implement a mock history helper using vitest (or jest):

test-utils.ts
import { vi } from "vitest"; // or jest
export function createMockHistory(initialPath = "/") {
// We use a real URL object to handle relative paths and query params logic automatically
const location = new URL(initialPath, "http://localhost:3000");
const history = {
pushState: vi.fn((_state, _title, url) => {
// Update our location object when navigation happens
location.href = new URL(url, location.href).href;
}),
replaceState: vi.fn((_state, _title, url) => {
location.href = new URL(url, location.href).href;
}),
};
return { location, history };
}

Now you can use this helper to inject both the location and history into your Router. This allows the Router to read the initial state and write updates back to your mock.

import { test, expect } from "vitest"
import { render, screen, fireEvent } from "@testing-library/react";
import { Router, Link } from "@farbenmeer/router";
import { createMockHistory } from "./test-utils";
test("navigates to about page on click", () => {
// 1. Create the mock history starting at home
const { location, history } = createMockHistory("/");
render(
<Router location={location} history={history}>
<nav>
<Link href="/about">About Us</Link>
</nav>
</Router>
);
// 2. Perform the navigation action
fireEvent.click(screen.getByText("About Us"));
// 3. Verify that pushState was called
expect(history.pushState).toHaveBeenCalled();
// 4. Verify the new location state
expect(location.pathname).toBe("/about");
});
  1. Static Rendering: Pass a location object to <Router> to set the initial state.
  2. Interactive Tests: Pass both location and history objects (created via a helper) to <Router> to track and assert on navigation.