Wiring up #vitest and #rr7 for tests is not obvious.
- Requires a separate
vitest.config.ts
- Requires
createRoutesStub
vitest.config.ts: #
We need a separate vite.config
that omits @react-router/dev/vite
, and then
configures the typical test
features.
import react from "@vitejs/plugin-react"
import autoprefixer from "autoprefixer"
import tailwindcss from "tailwindcss"
import { defineConfig, loadEnv } from "vite"
import tsconfigPaths from "vite-tsconfig-paths"
// -----------------------------------------------------------------------------
export default defineConfig({
css: {
postcss: {
plugins: [tailwindcss, autoprefixer],
},
},
// using `@vitejs/plugin-react` instead of `@react-router/dev/vite`
plugins: [react(), tsconfigPaths()],
test: {
env: loadEnv("test", process.cwd(), ""),
environment: "happy-dom",
// @NOTE: https://testing-library.com/docs/react-testing-library/setup#auto-cleanup-in-vitest
setupFiles: ["./vite.cleanup.ts"],
},
})
A test: #
Now, we need to use createRoutesStub
anywhere a component renders anything to
do with #reactrouter. Alternatively, we could build a "wrapper" utility,
similar to mocking a redux
provider, but in practice we'll be passing
createRoutesStub
to a wrapper utility anyways.
import userEvent from "@testing-library/user-event"
import { Link } from "react-router"
import { createRoutesStub } from "react-router"
import { render, screen, waitFor } from "@testing-library/react"
import { test } from "vitest"
// -----------------------------------------------------------------------------
const Welcome: React.FC = () => {
return <Link to="/login">Log In</Link>
}
const Success: React.FC = () => {
return <>you did it!</>
}
test("Welcome screen navigates to /login", async () => {
const Stub = createRoutesStub([
{ Component: Welcome, path: "/" },
{ Component: Success, path: "/login" },
])
render(<Stub />)
await userEvent.click(screen.getByText("Log In"))
await waitFor(() => screen.findByText("you did it!"))
})