@@ -10,6 +10,7 @@ import {
1010 createReactRouterV6CompatibleTracingIntegration ,
1111 updateNavigationSpan ,
1212} from '../../src/reactrouter-compat-utils' ;
13+ import { addRoutesToAllRoutes , allRoutes } from '../../src/reactrouter-compat-utils/instrumentation' ;
1314import type { Location , RouteObject } from '../../src/types' ;
1415
1516const mockUpdateName = vi . fn ( ) ;
@@ -47,6 +48,7 @@ vi.mock('../../src/reactrouter-compat-utils/utils', () => ({
4748 initializeRouterUtils : vi . fn ( ) ,
4849 getGlobalLocation : vi . fn ( ( ) => ( { pathname : '/test' , search : '' , hash : '' } ) ) ,
4950 getGlobalPathname : vi . fn ( ( ) => '/test' ) ,
51+ routeIsDescendant : vi . fn ( ( ) => false ) ,
5052} ) ) ;
5153
5254vi . mock ( '../../src/reactrouter-compat-utils/lazy-routes' , ( ) => ( {
@@ -141,3 +143,193 @@ describe('reactrouter-compat-utils/instrumentation', () => {
141143 } ) ;
142144 } ) ;
143145} ) ;
146+
147+ describe ( 'addRoutesToAllRoutes' , ( ) => {
148+ beforeEach ( ( ) => {
149+ vi . clearAllMocks ( ) ;
150+ vi . resetModules ( ) ;
151+ allRoutes . clear ( ) ;
152+ } ) ;
153+
154+ it ( 'should add simple routes without nesting' , ( ) => {
155+ const routes = [
156+ { path : '/' , element : < div /> } ,
157+ { path : '/user/:id' , element : < div /> } ,
158+ { path : '/group/:group/:user?' , element : < div /> } ,
159+ ] ;
160+
161+ addRoutesToAllRoutes ( routes ) ;
162+ const allRoutesArr = Array . from ( allRoutes ) ;
163+
164+ expect ( allRoutesArr ) . toHaveLength ( 3 ) ;
165+ expect ( allRoutesArr ) . toEqual (
166+ expect . arrayContaining ( [
167+ expect . objectContaining ( { path : '/' } ) ,
168+ expect . objectContaining ( { path : '/user/:id' } ) ,
169+ expect . objectContaining ( { path : '/group/:group/:user?' } ) ,
170+ ] ) ,
171+ ) ;
172+
173+ // Verify exact structure matches manual testing results
174+ allRoutesArr . forEach ( route => {
175+ expect ( route ) . toHaveProperty ( 'element' ) ;
176+ expect ( route . element ) . toHaveProperty ( 'props' ) ;
177+ } ) ;
178+ } ) ;
179+
180+ it ( 'should handle complex nested routes with multiple levels' , ( ) => {
181+ const routes = [
182+ { path : '/' , element : < div /> } ,
183+ { path : '/user/:id' , element : < div /> } ,
184+ { path : '/group/:group/:user?' , element : < div /> } ,
185+ {
186+ path : '/v1/post/:post' ,
187+ element : < div /> ,
188+ children : [
189+ { path : 'featured' , element : < div /> } ,
190+ { path : '/v1/post/:post/related' , element : < div /> } ,
191+ {
192+ element : < div > More Nested Children</ div > ,
193+ children : [ { path : 'edit' , element : < div > Edit Post</ div > } ] ,
194+ } ,
195+ ] ,
196+ } ,
197+ {
198+ path : '/v2/post/:post' ,
199+ element : < div /> ,
200+ children : [
201+ { index : true , element : < div /> } ,
202+ { path : 'featured' , element : < div /> } ,
203+ { path : '/v2/post/:post/related' , element : < div /> } ,
204+ ] ,
205+ } ,
206+ ] ;
207+
208+ addRoutesToAllRoutes ( routes ) ;
209+ const allRoutesArr = Array . from ( allRoutes ) ;
210+
211+ expect ( allRoutesArr ) . toEqual ( [
212+ { path : '/' , element : < div /> } ,
213+ { path : '/user/:id' , element : < div /> } ,
214+ { path : '/group/:group/:user?' , element : < div /> } ,
215+ // v1 routes ----
216+ {
217+ path : '/v1/post/:post' ,
218+ element : < div /> ,
219+ children : [
220+ { element : < div /> , path : 'featured' } ,
221+ { element : < div /> , path : '/v1/post/:post/related' } ,
222+ { children : [ { element : < div > Edit Post</ div > , path : 'edit' } ] , element : < div > More Nested Children</ div > } ,
223+ ] ,
224+ } ,
225+ { element : < div /> , path : 'featured' } ,
226+ { element : < div /> , path : '/v1/post/:post/related' } ,
227+ { children : [ { element : < div > Edit Post</ div > , path : 'edit' } ] , element : < div > More Nested Children</ div > } ,
228+ { element : < div > Edit Post</ div > , path : 'edit' } ,
229+ // v2 routes ---
230+ {
231+ path : '/v2/post/:post' ,
232+ element : expect . objectContaining ( { type : 'div' , props : { } } ) ,
233+ children : [
234+ { element : < div /> , index : true } ,
235+ { element : < div /> , path : 'featured' } ,
236+ { element : < div /> , path : '/v2/post/:post/related' } ,
237+ ] ,
238+ } ,
239+ { element : < div /> , index : true } ,
240+ { element : < div /> , path : 'featured' } ,
241+ { element : < div /> , path : '/v2/post/:post/related' } ,
242+ ] ) ;
243+ } ) ;
244+
245+ it ( 'should handle routes with nested index routes' , ( ) => {
246+ const routes = [
247+ {
248+ path : '/dashboard' ,
249+ element : < div /> ,
250+ children : [
251+ { index : true , element : < div > Dashboard Index</ div > } ,
252+ { path : 'settings' , element : < div > Settings</ div > } ,
253+ ] ,
254+ } ,
255+ ] ;
256+
257+ addRoutesToAllRoutes ( routes ) ;
258+ const allRoutesArr = Array . from ( allRoutes ) ;
259+
260+ expect ( allRoutesArr ) . toEqual ( [
261+ {
262+ path : '/dashboard' ,
263+ element : expect . objectContaining ( { type : 'div' } ) ,
264+ children : [
265+ { element : < div > Dashboard Index</ div > , index : true } ,
266+ { element : < div > Settings</ div > , path : 'settings' } ,
267+ ] ,
268+ } ,
269+ { element : < div > Dashboard Index</ div > , index : true } ,
270+ { element : < div > Settings</ div > , path : 'settings' } ,
271+ ] ) ;
272+ } ) ;
273+
274+ it ( 'should handle deeply nested routes with layout wrappers' , ( ) => {
275+ const routes = [
276+ {
277+ path : '/' ,
278+ element : < div > Root</ div > ,
279+ children : [
280+ { path : 'dashboard' , element : < div > Dashboard</ div > } ,
281+ {
282+ element : < div > AuthLayout</ div > ,
283+ children : [ { path : 'login' , element : < div > Login</ div > } ] ,
284+ } ,
285+ ] ,
286+ } ,
287+ ] ;
288+
289+ addRoutesToAllRoutes ( routes ) ;
290+ const allRoutesArr = Array . from ( allRoutes ) ;
291+
292+ expect ( allRoutesArr ) . toEqual ( [
293+ {
294+ path : '/' ,
295+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Root' } } ) ,
296+ children : [
297+ {
298+ path : 'dashboard' ,
299+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Dashboard' } } ) ,
300+ } ,
301+ {
302+ element : expect . objectContaining ( { type : 'div' , props : { children : 'AuthLayout' } } ) ,
303+ children : [
304+ {
305+ path : 'login' ,
306+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Login' } } ) ,
307+ } ,
308+ ] ,
309+ } ,
310+ ] ,
311+ } ,
312+ { element : < div > Dashboard</ div > , path : 'dashboard' } ,
313+ {
314+ children : [ { element : < div > Login</ div > , path : 'login' } ] ,
315+ element : < div > AuthLayout</ div > ,
316+ } ,
317+ { element : < div > Login</ div > , path : 'login' } ,
318+ ] ) ;
319+ } ) ;
320+
321+ it ( 'should not duplicate routes when called multiple times' , ( ) => {
322+ const routes = [
323+ { path : '/' , element : < div /> } ,
324+ { path : '/about' , element : < div /> } ,
325+ ] ;
326+
327+ addRoutesToAllRoutes ( routes ) ;
328+ const firstCount = allRoutes . size ;
329+
330+ addRoutesToAllRoutes ( routes ) ;
331+ const secondCount = allRoutes . size ;
332+
333+ expect ( firstCount ) . toBe ( secondCount ) ;
334+ } ) ;
335+ } ) ;
0 commit comments