From 0aea1da5ef6803674846b57d6e109af62c083cde Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 11:10:12 -0700 Subject: [PATCH 1/9] docs(react testing): Update testing docs to demo nested route fields --- develop-docs/frontend/using-rtl.mdx | 78 +++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index d4803681c02ce..e37d783b5c4c5 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -97,6 +97,84 @@ const {router} = render(, { expect(screen.getByText('123')).toBeInTheDocument(); ``` +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(); + return
{id}
; +} + +const { router } = render(, { + initialRouterConfig: { + outletContext: { id: '123' }, + }, +}); + +expect(screen.getByText('123')).toBeInTheDocument(); +``` + +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 +function SettingsWrapper() { + return ; +} +function SettingsIndex() { + return
Settings Index
; +} +function AccountSettings() { + const {projectID} = useParams(); + return
Project: {projectID}
; +} + +render(, { + initialRouterConfig: { + location: { + pathname: '/settings/', + }, + route: '/settings/', + children: [ + { + index: true, + element: , + }, + ], + }, +}); +expect(screen.getByText('Setting Index')).toBeInTheDocument(); + +render(, { + initialRouterConfig: { + location: { + pathname: '/settings/123', + }, + route: '/settings/:projectId', + children: [ + { + path: ':projectId/', + element: , + }, + ], + }, +}); +expect(screen.getByText('Project: 123')).toBeInTheDocument(); +``` + + ## Querying - use `getBy...` as much as possible From bd182aa6c4454ac8c2e5dcb139089e30c73f1d77 Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 11:16:31 -0700 Subject: [PATCH 2/9] capitalization --- develop-docs/frontend/using-rtl.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index e37d783b5c4c5..f3a59af076053 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -123,7 +123,7 @@ Suppose routes.tsx defines some nested routes like: component: SettingsWrapper, children: [ {index: true, component: SettingsIndex}, - {path: ':projectID/', component: ProjectSettings}, + {path: ':projectId/', component: ProjectSettings}, ] } ``` @@ -137,8 +137,8 @@ function SettingsIndex() { return
Settings Index
; } function AccountSettings() { - const {projectID} = useParams(); - return
Project: {projectID}
; + const {projectId} = useParams(); + return
Project: {projectId}
; } render(, { From fc4038a52dfd6352299f1c24a18dfbe9783688d9 Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 11:19:47 -0700 Subject: [PATCH 3/9] full example --- develop-docs/frontend/using-rtl.mdx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index f3a59af076053..f21ec2c432a64 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -131,14 +131,17 @@ Suppose routes.tsx defines some nested routes like: 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 function SettingsWrapper() { - return ; + const settings = useSettings(); // returns `{name: 'Default'}` or something + return ; } function SettingsIndex() { - return
Settings Index
; + const {settings} = useOutletContext(); + return
Settings > {settings.name}
; } function AccountSettings() { + const {settings} = useOutletContext(); const {projectId} = useParams(); - return
Project: {projectId}
; + return
Settings > {settings.name} > Project: {projectId}
; } render(, { @@ -155,7 +158,7 @@ render(, { ], }, }); -expect(screen.getByText('Setting Index')).toBeInTheDocument(); +expect(screen.getByText('Settings > Default')).toBeInTheDocument(); render(, { initialRouterConfig: { @@ -171,7 +174,7 @@ render(, { ], }, }); -expect(screen.getByText('Project: 123')).toBeInTheDocument(); +expect(screen.getByText('Settings > Default > Project: 123')).toBeInTheDocument(); ``` From 1de69e24aafe391c5fe3190319a953bca58aa4bb Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 11:33:06 -0700 Subject: [PATCH 4/9] get some titles in there --- develop-docs/frontend/using-rtl.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index f21ec2c432a64..8acbc4d8d1fcd 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -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 @@ -97,6 +99,8 @@ const {router} = render(, { 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 @@ -114,6 +118,8 @@ const { router } = render(, { 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: From c37f92c979e1a584b1319049f7beeef81ade05d2 Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 11:42:11 -0700 Subject: [PATCH 5/9] location and route should have trailing / --- develop-docs/frontend/using-rtl.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index 8acbc4d8d1fcd..de2499b071a35 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -169,7 +169,7 @@ expect(screen.getByText('Settings > Default')).toBeInTheDocument(); render(, { initialRouterConfig: { location: { - pathname: '/settings/123', + pathname: '/settings/123/', }, route: '/settings/:projectId', children: [ From 2757793079f6188949fa39bf28484bf644e5fea0 Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 11:52:14 -0700 Subject: [PATCH 6/9] fuller useOutletContext types --- develop-docs/frontend/using-rtl.mdx | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index de2499b071a35..3f08ecc4c424f 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -136,20 +136,34 @@ Suppose routes.tsx defines some nested routes like: 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 -function SettingsWrapper() { +// settingsWrapper.tsx +export interface OutletContext { + settings: {name: string} +} + +export function useCustomOutletContext() { + return useOutletContext(); +} + +export default function SettingsWrapper() { const settings = useSettings(); // returns `{name: 'Default'}` or something - return ; + return ; } + +// settingsIndex.tsx function SettingsIndex() { - const {settings} = useOutletContext(); + const {settings} = useCustomOutletContext(); return
Settings > {settings.name}
; } + +// accountSettings.tsx function AccountSettings() { - const {settings} = useOutletContext(); + const {settings} = useCustomOutletContext(); const {projectId} = useParams(); return
Settings > {settings.name} > Project: {projectId}
; } +// tests.spec.tsx render(, { initialRouterConfig: { location: { From c1398546d63fafa6e9271c533ba335a8581a3e8b Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 11:57:52 -0700 Subject: [PATCH 7/9] tweaks to names of examples --- develop-docs/frontend/using-rtl.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index 3f08ecc4c424f..5292c426e9ece 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -146,8 +146,7 @@ export function useCustomOutletContext() { } export default function SettingsWrapper() { - const settings = useSettings(); // returns `{name: 'Default'}` or something - return ; + return ; } // settingsIndex.tsx @@ -156,14 +155,14 @@ function SettingsIndex() { return
Settings > {settings.name}
; } -// accountSettings.tsx -function AccountSettings() { +// projectSettings.tsx +function ProjectSettings() { const {settings} = useCustomOutletContext(); const {projectId} = useParams(); return
Settings > {settings.name} > Project: {projectId}
; } -// tests.spec.tsx +// settingsIndex.spec.tsx render(, { initialRouterConfig: { location: { @@ -180,6 +179,7 @@ render(, { }); expect(screen.getByText('Settings > Default')).toBeInTheDocument(); +// projectSettings.spec.tsx render(, { initialRouterConfig: { location: { @@ -189,7 +189,7 @@ render(, { children: [ { path: ':projectId/', - element: , + element: , }, ], }, From 8e43df09a2a477ea12ed9016689897412898fa9c Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Mon, 29 Sep 2025 14:42:03 -0700 Subject: [PATCH 8/9] Update develop-docs/frontend/using-rtl.mdx --- develop-docs/frontend/using-rtl.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index 5292c426e9ece..f7a8f3434617e 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -138,7 +138,7 @@ We can configure the in-memory router to know about just the route tree that you ```tsx // settingsWrapper.tsx export interface OutletContext { - settings: {name: string} + name: string, } export function useCustomOutletContext() { From 793cd7958ed90cf36a5ec31f8168004fdd474bbf Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Tue, 30 Sep 2025 10:20:53 -0700 Subject: [PATCH 9/9] fix bugs in example --- develop-docs/frontend/using-rtl.mdx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/develop-docs/frontend/using-rtl.mdx b/develop-docs/frontend/using-rtl.mdx index f7a8f3434617e..0cd83ed13bb92 100644 --- a/develop-docs/frontend/using-rtl.mdx +++ b/develop-docs/frontend/using-rtl.mdx @@ -137,8 +137,8 @@ Suppose routes.tsx defines some nested routes like: 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 -export interface OutletContext { - name: string, +interface OutletContext { + name: string; } export function useCustomOutletContext() { @@ -146,20 +146,21 @@ export function useCustomOutletContext() { } export default function SettingsWrapper() { - return ; + const context: OutletContext = {name: "Default"}; + return ; } // settingsIndex.tsx function SettingsIndex() { - const {settings} = useCustomOutletContext(); - return
Settings > {settings.name}
; + const {name} = useCustomOutletContext(); + return
Settings > {name}
; } // projectSettings.tsx function ProjectSettings() { - const {settings} = useCustomOutletContext(); + const {name} = useCustomOutletContext(); const {projectId} = useParams(); - return
Settings > {settings.name} > Project: {projectId}
; + return
Settings > {name} > Project: {projectId}
; } // settingsIndex.spec.tsx