Skip to content

Commit 834375c

Browse files
authored
Update testing docs for framework mode to discuss typegen types (#14104)
1 parent fed3b63 commit 834375c

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

docs/start/framework/testing.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,57 @@ test("LoginForm renders error messages", async () => {
7777
await waitFor(() => screen.findByText(PASSWORD_MESSAGE));
7878
});
7979
```
80+
81+
## Using with Framework Mode Types
82+
83+
It's important to note that `createRoutesStub` is designed for _unit_ testing of reusable components in your application that rely on on contextual router information (i.e., `loaderData`, `actionData`, `matches`). These components usually obtain this information via the hooks (`useLoaderData`, `useActionData`, `useMatches`) or via props passed down from the ancestor route component. We **strongly** recommend limiting your usage of `createRoutesStub` to unit testing of these types of reusable components.
84+
85+
`createRoutesStub` is _not designed_ for (and is arguably incompatible with) direct testing of Route components using the [`Route.\*`](../../explanation/type-safety) types available in Framework Mode. This is because the `Route.*` types are derived from your actual application - including the real `loader`/`action` functions as well as the structure of your route tree structure (which defines the `matches` type). When you use `createRoutesStub`, you are providing stubbed values for `loaderData`, `actionData`, and even your `matches` based on the route tree you pass to `createRoutesStub`. Therefore, the types won't align with the `Route.*` types and you'll get type issues trying to use a route component in a route stub.
86+
87+
```tsx filename=routes/login.tsx
88+
export default function Login({
89+
actionData,
90+
}: Route.ComponentProps) {
91+
return <Form method="post">...</Form>;
92+
}
93+
```
94+
95+
```tsx filename=routes/login.test.tsx
96+
import LoginRoute from "./login";
97+
98+
test("LoginRoute renders error messages", async () => {
99+
const Stub = createRoutesStub([
100+
{
101+
path: "/login",
102+
Component: LoginRoute,
103+
// ^ ❌ Types of property 'matches' are incompatible.
104+
action() {
105+
/*...*/
106+
},
107+
},
108+
]);
109+
110+
// ...
111+
});
112+
```
113+
114+
These type errors are generally accurate if you try to setup your tests like this. As long as your stubbed `loader`/`action` functions match your real implementations, then the types for `loaderData`/`actionData` will be correct, but if they differ your types will be lying to you.
115+
116+
`matches` is more complicated since you don't usually stub out all of the ancestor routes. In this example, there is no `root` route so `matches` will only contain your test route, while it will contain the root route and any other ancestors at runtime. There's no great way to automatically align the typegen types with the runtime types in your test.
117+
118+
Therefore, if you need to test Route level components, we recommend you do that via an Integration/E2E test (Playwright, Cypress, etc.) against a running application because you're venturing out of unit testing territory when testing your route as a whole.
119+
120+
If you _need_ to write a unit test against the route, you can add a `@ts-expect-error` comment in your test to silence the TypeScript error:
121+
122+
```tsx
123+
const Stub = createRoutesStub([
124+
{
125+
path: "/login",
126+
// @ts-expect-error: `matches` won't align between test code and app code
127+
Component: LoginRoute,
128+
action() {
129+
/*...*/
130+
},
131+
},
132+
]);
133+
```

0 commit comments

Comments
 (0)