Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions develop-docs/frontend/using-rtl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ router.navigate('/new/path/');
router.navigate(-1); // Simulates clicking the back button
```

### Route Param Values

If you need to test route param values (as in `useParams()`), the `route` will need to be provided in the config:

```tsx
Expand All @@ -97,6 +99,106 @@ const {router} = render(<TestComponent />, {
expect(screen.getByText('123')).toBeInTheDocument();
```

### Outlet Context

If you need to test outlet param values (as in `useOutletContext()`), you can pass them inside the `initialRouterConfig` object.

```tsx
function TestComponent() {
const { id } = useOutletContext();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should suggest they use a typed hook no? These return unknown.

Really they should be typed as

interface OutletContext {
  id: string
}

export function useWhateverOutletContext() {
  return useOutletContext<OutletContext>();
}

export function MyParentView() {
  return <Outlet id="whatever" />
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah. i can add that in so it's most copy+pasteable

return <div>{id}</div>;
}

const { router } = render(<TestComponent />, {
initialRouterConfig: {
outletContext: { id: '123' },
},
});

expect(screen.getByText('123')).toBeInTheDocument();
```

### Nested Routes

If directly setting outlet context isn't enough, instead you want to render some nested routes you can do that too!

Suppose routes.tsx defines some nested routes like:
```tsx
{
path: 'settings/',
component: SettingsWrapper,
children: [
{index: true, component: SettingsIndex},
{path: ':projectId/', component: ProjectSettings},
]
}
```

We can configure the in-memory router to know about just the route tree that you need. Remember to render the wrapper component itself, and the router + route child components will render themsevles.
```tsx
// settingsWrapper.tsx
interface OutletContext {
name: string;
}

export function useCustomOutletContext() {
return useOutletContext<OutletContext>();
}

export default function SettingsWrapper() {
const context: OutletContext = {name: "Default"};
return <Outlet context={context} />;
}

// settingsIndex.tsx
function SettingsIndex() {
const {name} = useCustomOutletContext();
return <div>Settings > {name}</div>;
}

// projectSettings.tsx
function ProjectSettings() {
const {name} = useCustomOutletContext();
const {projectId} = useParams();
return <div>Settings > {name} > Project: {projectId}</div>;
}

// settingsIndex.spec.tsx
render(<SettingsWrapper />, {
initialRouterConfig: {
location: {
pathname: '/settings/',
},
route: '/settings/',
children: [
{
index: true,
element: <SettingsIndex />,
},
],
},
});
expect(screen.getByText('Settings > Default')).toBeInTheDocument();

// projectSettings.spec.tsx
render(<SettingsWrapper />, {
initialRouterConfig: {
location: {
pathname: '/settings/123/',
},
route: '/settings/:projectId',
children: [
{
path: ':projectId/',
element: <ProjectSettings />,
},
],
},
});
expect(screen.getByText('Settings > Default > Project: 123')).toBeInTheDocument();
```


## Querying

- use `getBy...` as much as possible
Expand Down
Loading