@@ -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 ( ) ;
@@ -141,3 +142,246 @@ describe('reactrouter-compat-utils/instrumentation', () => {
141142 } ) ;
142143 } ) ;
143144} ) ;
145+
146+ describe ( 'addRoutesToAllRoutes' , ( ) => {
147+ beforeEach ( ( ) => {
148+ vi . clearAllMocks ( ) ;
149+ vi . resetModules ( ) ;
150+ allRoutes . clear ( ) ;
151+ } ) ;
152+
153+ it ( 'should add simple routes without nesting' , ( ) => {
154+ const routes = [
155+ { path : '/' , element : < div /> } ,
156+ { path : '/user/:id' , element : < div /> } ,
157+ { path : '/group/:group/:user?' , element : < div /> } ,
158+ ] ;
159+
160+ addRoutesToAllRoutes ( routes ) ;
161+ const allRoutesArr = Array . from ( allRoutes ) ;
162+
163+ expect ( allRoutesArr ) . toHaveLength ( 3 ) ;
164+ expect ( allRoutesArr ) . toEqual (
165+ expect . arrayContaining ( [
166+ expect . objectContaining ( { path : '/' } ) ,
167+ expect . objectContaining ( { path : '/user/:id' } ) ,
168+ expect . objectContaining ( { path : '/group/:group/:user?' } ) ,
169+ ] ) ,
170+ ) ;
171+
172+ // Verify exact structure matches manual testing results
173+ allRoutesArr . forEach ( route => {
174+ expect ( route ) . toHaveProperty ( 'element' ) ;
175+ expect ( route . element ) . toHaveProperty ( 'props' ) ;
176+ } ) ;
177+ } ) ;
178+
179+ it ( 'should preserve nested children on parent routes without creating duplicate top-level entries' , ( ) => {
180+ const routes = [
181+ { path : '/' , element : < div /> } ,
182+ { path : '/user/:id' , element : < div /> } ,
183+ { path : '/group/:group/:user?' , element : < div /> } ,
184+ {
185+ path : '/v2/post/:post' ,
186+ element : < div /> ,
187+ children : [
188+ { index : true , element : < div /> } ,
189+ { path : 'featured' , element : < div /> } ,
190+ { path : '/v2/post/:post/related' , element : < div /> } ,
191+ ] ,
192+ } ,
193+ ] ;
194+
195+ addRoutesToAllRoutes ( routes ) ;
196+ const allRoutesArr = Array . from ( allRoutes ) ;
197+
198+ // Should have all routes flattened (parent + all descendants)
199+ expect ( allRoutesArr . length ) . toBeGreaterThanOrEqual ( 4 ) ;
200+
201+ // Find the parent route
202+ const parentRoute = allRoutesArr . find ( ( r : any ) => r . path === '/v2/post/:post' ) ;
203+ expect ( parentRoute ) . toBeTruthy ( ) ;
204+ expect ( parentRoute ! . children ) . toBeDefined ( ) ;
205+ expect ( Array . isArray ( parentRoute ! . children ) ) . toBe ( true ) ;
206+ expect ( parentRoute ! . children ) . toHaveLength ( 3 ) ;
207+
208+ // Verify children structure is preserved
209+ expect ( parentRoute ! . children ) . toEqual (
210+ expect . arrayContaining ( [
211+ expect . objectContaining ( { index : true } ) ,
212+ expect . objectContaining ( { path : 'featured' } ) ,
213+ expect . objectContaining ( { path : '/v2/post/:post/related' } ) ,
214+ ] ) ,
215+ ) ;
216+
217+ // Verify that children are NOT in allRoutes as individual entries
218+ expect ( allRoutesArr ) . not . toEqual (
219+ expect . arrayContaining ( [
220+ expect . objectContaining ( { index : true } ) ,
221+ expect . objectContaining ( { path : 'featured' } ) ,
222+ expect . objectContaining ( { path : '/v2/post/:post/related' } ) ,
223+ ] ) ,
224+ ) ;
225+ } ) ;
226+
227+ it ( 'should handle complex nested routes with multiple levels' , ( ) => {
228+ const routes = [
229+ { path : '/' , element : < div /> } ,
230+ { path : '/user/:id' , element : < div /> } ,
231+ { path : '/group/:group/:user?' , element : < div /> } ,
232+ {
233+ path : '/v1/post/:post' ,
234+ element : < div /> ,
235+ children : [
236+ { path : 'featured' , element : < div /> } ,
237+ { path : '/v1/post/:post/related' , element : < div /> } ,
238+ {
239+ element : < div > More Nested Children</ div > ,
240+ children : [ { path : 'edit' , element : < div > Edit Post</ div > } ] ,
241+ } ,
242+ ] ,
243+ } ,
244+ {
245+ path : '/v2/post/:post' ,
246+ element : < div /> ,
247+ children : [
248+ { index : true , element : < div /> } ,
249+ { path : 'featured' , element : < div /> } ,
250+ { path : '/v2/post/:post/related' , element : < div /> } ,
251+ ] ,
252+ } ,
253+ ] ;
254+
255+ addRoutesToAllRoutes ( routes ) ;
256+ const allRoutesArr = Array . from ( allRoutes ) ;
257+
258+ expect ( allRoutesArr ) . toEqual ( [
259+ {
260+ path : '/' ,
261+ element : expect . objectContaining ( { type : 'div' , props : { } } ) ,
262+ } ,
263+ {
264+ path : '/user/:id' ,
265+ element : expect . objectContaining ( { type : 'div' , props : { } } ) ,
266+ } ,
267+ {
268+ path : '/group/:group/:user?' ,
269+ element : expect . objectContaining ( { type : 'div' , props : { } } ) ,
270+ } ,
271+ {
272+ path : '/v1/post/:post' ,
273+ element : expect . objectContaining ( { type : 'div' , props : { } } ) ,
274+ children : [
275+ { element : < div /> , path : 'featured' } ,
276+ { element : < div /> , path : '/v1/post/:post/related' } ,
277+ { children : [ { element : < div > Edit Post</ div > , path : 'edit' } ] , element : < div > More Nested Children</ div > } ,
278+ ] ,
279+ } ,
280+ {
281+ path : '/v2/post/:post' ,
282+ element : expect . objectContaining ( { type : 'div' , props : { } } ) ,
283+ children : [
284+ { element : < div /> , index : true } ,
285+ { element : < div /> , path : 'featured' } ,
286+ { element : < div /> , path : '/v2/post/:post/related' } ,
287+ ] ,
288+ } ,
289+ ] ) ;
290+ } ) ;
291+
292+ it ( 'should handle routes with nested index routes' , ( ) => {
293+ const routes = [
294+ {
295+ path : '/dashboard' ,
296+ element : < div /> ,
297+ children : [
298+ { index : true , element : < div > Dashboard Index</ div > } ,
299+ { path : 'settings' , element : < div > Settings</ div > } ,
300+ ] ,
301+ } ,
302+ ] ;
303+
304+ addRoutesToAllRoutes ( routes ) ;
305+ const allRoutesArr = Array . from ( allRoutes ) ;
306+
307+ expect ( allRoutesArr ) . toEqual ( [
308+ {
309+ path : '/dashboard' ,
310+ element : expect . objectContaining ( { type : 'div' } ) ,
311+ children : [
312+ { element : < div > Dashboard Index</ div > , index : true } ,
313+ { element : < div > Settings</ div > , path : 'settings' } ,
314+ ] ,
315+ } ,
316+ ] ) ;
317+ } ) ;
318+
319+ it ( 'should handle deeply nested routes with layout wrappers' , ( ) => {
320+ const routes = [
321+ {
322+ path : '/' ,
323+ element : < div > Root</ div > ,
324+ children : [
325+ { path : 'contact' , element : < div > Contact</ div > } ,
326+ { path : 'dashboard' , element : < div > Dashboard</ div > } ,
327+ {
328+ element : < div > AuthLayout</ div > ,
329+ children : [
330+ { path : 'login' , element : < div > Login</ div > } ,
331+ { path : 'logout' , element : < div > Logout</ div > } ,
332+ ] ,
333+ } ,
334+ ] ,
335+ } ,
336+ ] ;
337+
338+ addRoutesToAllRoutes ( routes ) ;
339+ const allRoutesArr = Array . from ( allRoutes ) ;
340+
341+ // Verify all routes are flattened into allRoutes
342+ expect ( allRoutesArr ) . toEqual ( [
343+ {
344+ path : '/' ,
345+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Root' } } ) ,
346+ children : [
347+ {
348+ path : 'contact' ,
349+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Contact' } } ) ,
350+ } ,
351+ {
352+ path : 'dashboard' ,
353+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Dashboard' } } ) ,
354+ } ,
355+ {
356+ element : expect . objectContaining ( { type : 'div' , props : { children : 'AuthLayout' } } ) ,
357+ children : [
358+ {
359+ path : 'login' ,
360+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Login' } } ) ,
361+ } ,
362+ {
363+ path : 'logout' ,
364+ element : expect . objectContaining ( { type : 'div' , props : { children : 'Logout' } } ) ,
365+ } ,
366+ ] ,
367+ } ,
368+ ] ,
369+ } ,
370+ ] ) ;
371+ } ) ;
372+
373+ it ( 'should not duplicate routes when called multiple times' , ( ) => {
374+ const routes = [
375+ { path : '/' , element : < div /> } ,
376+ { path : '/about' , element : < div /> } ,
377+ ] ;
378+
379+ addRoutesToAllRoutes ( routes ) ;
380+ const firstCount = allRoutes . size ;
381+
382+ addRoutesToAllRoutes ( routes ) ;
383+ const secondCount = allRoutes . size ;
384+
385+ expect ( firstCount ) . toBe ( secondCount ) ;
386+ } ) ;
387+ } ) ;
0 commit comments