1import { findFocusedRoute } from '@react-navigation/native';
2import type { InitialState } from '@react-navigation/routers';
3import produce from 'immer';
4
5import { configFromFs } from '../../utils/mockState';
6import getPathFromState from '../getPathFromState';
7import getStateFromPath from '../getStateFromPath';
8
9const changePath = <T extends InitialState>(state: T, path: string): T =>
10  produce(state, (draftState) => {
11    const route = findFocusedRoute(draftState);
12    // @ts-expect-error: immer won't mutate this
13    route.path = path;
14  });
15
16it('returns undefined for invalid path', () => {
17  expect(getStateFromPath<object>('//', { screens: {} })).toBe(undefined);
18});
19
20it('converts path string to initial state with config', () => {
21  const path = '/foo/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true';
22  const config = {
23    screens: {
24      Foo: {
25        path: 'foo',
26        screens: {
27          Bar: {
28            path: 'bar/:type/:fruit',
29            screens: {
30              Baz: {
31                path: 'baz/:author',
32                parse: {
33                  author: (author: string) => author.replace(/^\w/, (c) => c.toUpperCase()),
34                  count: Number,
35                  valid: Boolean,
36                },
37                stringify: {
38                  author: (author: string) => author.toLowerCase(),
39                },
40              },
41            },
42          },
43        },
44      },
45    },
46  };
47
48  const state = {
49    routes: [
50      {
51        name: 'Foo',
52        params: {
53          author: 'Jane',
54          fruit: 'apple',
55          type: 'sweet',
56        },
57        state: {
58          routes: [
59            {
60              name: 'Bar',
61              params: { author: 'Jane', fruit: 'apple', type: 'sweet' },
62              state: {
63                routes: [
64                  {
65                    name: 'Baz',
66                    params: {
67                      author: 'Jane',
68                      count: 10,
69                      fruit: 'apple',
70                      type: 'sweet',
71                      answer: '42',
72                      valid: true,
73                    },
74                    path,
75                  },
76                ],
77              },
78            },
79          ],
80        },
81      },
82    ],
83  };
84
85  testConversions(path, config, state);
86});
87
88it('handles leading slash when converting', () => {
89  const path = '/foo/bar/?count=42';
90
91  expect(
92    getStateFromPath<object>(path, {
93      screens: {
94        foo: {
95          path: 'foo',
96          screens: {
97            bar: {
98              path: 'bar',
99            },
100          },
101        },
102      },
103    })
104  ).toEqual({
105    routes: [
106      {
107        name: 'foo',
108        state: {
109          routes: [
110            {
111              name: 'bar',
112              params: { count: '42' },
113              path,
114            },
115          ],
116        },
117      },
118    ],
119  });
120});
121
122it('handles ending slash when converting', () => {
123  const path = 'foo/bar/?count=42';
124
125  expect(
126    getStateFromPath<object>(path, {
127      screens: {
128        foo: {
129          path: 'foo',
130          screens: {
131            bar: {
132              path: 'bar',
133            },
134          },
135        },
136      },
137    })
138  ).toEqual({
139    routes: [
140      {
141        name: 'foo',
142        state: {
143          routes: [
144            {
145              name: 'bar',
146              params: { count: '42' },
147              path,
148            },
149          ],
150        },
151      },
152    ],
153  });
154});
155
156it('converts path string to initial state with config with nested screens', () => {
157  const path = '/foe/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true';
158  const config = {
159    screens: {
160      Foo: {
161        path: 'foo',
162        screens: {
163          Foe: {
164            path: 'foe',
165            exact: true,
166            screens: {
167              Bar: {
168                path: 'bar/:type/:fruit',
169                screens: {
170                  Baz: {
171                    path: 'baz/:author',
172                    parse: {
173                      author: (author: string) => author.replace(/^\w/, (c) => c.toUpperCase()),
174                      count: Number,
175                      valid: Boolean,
176                    },
177                    stringify: {
178                      author: (author: string) => author.toLowerCase(),
179                    },
180                  },
181                },
182              },
183            },
184          },
185        },
186      },
187    },
188  };
189
190  const state = {
191    routes: [
192      {
193        name: 'Foo',
194        params: {
195          fruit: 'apple',
196          type: 'sweet',
197          author: 'Jane',
198        },
199        state: {
200          routes: [
201            {
202              name: 'Foe',
203              params: {
204                fruit: 'apple',
205                type: 'sweet',
206                author: 'Jane',
207              },
208              state: {
209                routes: [
210                  {
211                    name: 'Bar',
212                    params: {
213                      fruit: 'apple',
214                      type: 'sweet',
215                      author: 'Jane',
216                    },
217                    state: {
218                      routes: [
219                        {
220                          name: 'Baz',
221                          params: {
222                            fruit: 'apple',
223                            type: 'sweet',
224                            author: 'Jane',
225                            count: 10,
226                            answer: '42',
227                            valid: true,
228                          },
229                          path,
230                        },
231                      ],
232                    },
233                  },
234                ],
235              },
236            },
237          ],
238        },
239      },
240    ],
241  };
242
243  testConversions(path, config, state);
244});
245
246it('converts path string to initial state with config with nested screens and unused parse functions', () => {
247  const path = '/foe/baz/jane?count=10&answer=42&valid=true';
248  const config = {
249    screens: {
250      Foo: {
251        path: 'foo',
252        screens: {
253          Foe: {
254            path: 'foe',
255            exact: true,
256            screens: {
257              Baz: {
258                path: 'baz/:author',
259                parse: {
260                  author: (author: string) => author.replace(/^\w/, (c) => c.toUpperCase()),
261                  count: Number,
262                  valid: Boolean,
263                  id: Boolean,
264                },
265              },
266            },
267          },
268        },
269      },
270    },
271  };
272
273  const state = {
274    routes: [
275      {
276        name: 'Foo',
277        params: {
278          author: 'Jane',
279        },
280        state: {
281          routes: [
282            {
283              name: 'Foe',
284              params: {
285                author: 'Jane',
286              },
287              state: {
288                routes: [
289                  {
290                    name: 'Baz',
291                    params: {
292                      author: 'Jane',
293                      count: 10,
294                      answer: '42',
295                      valid: true,
296                    },
297                    path,
298                  },
299                ],
300              },
301            },
302          ],
303        },
304      },
305    ],
306  };
307
308  expect(getStateFromPath<object>(path, config)).toEqual(state);
309  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
310    changePath(state, '/foe/baz/Jane?count=10&answer=42&valid=true')
311  );
312});
313
314it('handles nested object with unused configs and with parse in it', () => {
315  const path = '/bar/sweet/apple/foe/bis/jane?count=10&answer=42&valid=true';
316  const config = {
317    screens: {
318      Bar: {
319        path: 'bar/:type/:fruit',
320        screens: {
321          Foo: {
322            screens: {
323              Foe: {
324                path: 'foe',
325                screens: {
326                  Baz: {
327                    screens: {
328                      Bos: {
329                        path: 'bos',
330                        exact: true,
331                      },
332                      Bis: {
333                        path: 'bis/:author',
334                        stringify: {
335                          author: (author: string) => author.replace(/^\w/, (c) => c.toLowerCase()),
336                        },
337                        parse: {
338                          author: (author: string) => author.replace(/^\w/, (c) => c.toUpperCase()),
339                          count: Number,
340                          valid: Boolean,
341                        },
342                      },
343                    },
344                  },
345                },
346              },
347            },
348          },
349        },
350      },
351    },
352  };
353
354  const state = {
355    routes: [
356      {
357        name: 'Bar',
358        params: { fruit: 'apple', type: 'sweet', author: 'Jane' },
359        state: {
360          routes: [
361            {
362              name: 'Foo',
363              params: {
364                author: 'Jane',
365                fruit: 'apple',
366                type: 'sweet',
367              },
368              state: {
369                routes: [
370                  {
371                    name: 'Foe',
372                    params: {
373                      author: 'Jane',
374                      fruit: 'apple',
375                      type: 'sweet',
376                    },
377                    state: {
378                      routes: [
379                        {
380                          name: 'Baz',
381                          params: {
382                            author: 'Jane',
383                            fruit: 'apple',
384                            type: 'sweet',
385                          },
386                          state: {
387                            routes: [
388                              {
389                                name: 'Bis',
390                                params: {
391                                  author: 'Jane',
392                                  count: 10,
393                                  answer: '42',
394                                  valid: true,
395                                  fruit: 'apple',
396                                  type: 'sweet',
397                                },
398                                path,
399                              },
400                            ],
401                          },
402                        },
403                      ],
404                    },
405                  },
406                ],
407              },
408            },
409          ],
410        },
411      },
412    ],
413  };
414
415  testConversions(path, config, state);
416});
417
418it('handles parse in nested object for second route depth', () => {
419  const path = '/baz';
420  const config = {
421    screens: {
422      Foo: {
423        path: 'foo',
424        screens: {
425          Foe: {
426            path: 'foe',
427            exact: true,
428          },
429          Bar: {
430            path: 'bar',
431            exact: true,
432            screens: {
433              Baz: {
434                path: 'baz',
435                exact: true,
436              },
437            },
438          },
439        },
440      },
441    },
442  };
443
444  const state = {
445    routes: [
446      {
447        name: 'Foo',
448        state: {
449          routes: [
450            {
451              name: 'Bar',
452              state: {
453                routes: [{ name: 'Baz', path }],
454              },
455            },
456          ],
457        },
458      },
459    ],
460  };
461
462  testConversions(path, config, state);
463});
464
465it('handles parse in nested object for second route depth and and path and parse in roots', () => {
466  const path = '/baz';
467  const config = {
468    screens: {
469      Foo: {
470        path: 'foo/:id',
471        parse: {
472          id: Number,
473        },
474        stringify: {
475          id: (id: number) => `id=${id}`,
476        },
477        screens: {
478          Foe: 'foe',
479          Bar: {
480            screens: {
481              Baz: {
482                path: 'baz',
483                exact: true,
484              },
485            },
486          },
487        },
488      },
489    },
490  };
491
492  const state = {
493    routes: [
494      {
495        name: 'Foo',
496        state: {
497          routes: [
498            {
499              name: 'Bar',
500              state: {
501                routes: [{ name: 'Baz', path }],
502              },
503            },
504          ],
505        },
506      },
507    ],
508  };
509
510  testConversions(path, config, state);
511});
512
513it('handles initialRouteName inside a screen', () => {
514  const path = '/baz';
515  const config = {
516    screens: {
517      Foo: {
518        initialRouteName: 'Foe',
519        screens: {
520          Foe: 'foe',
521          Bar: {
522            screens: {
523              Baz: 'baz',
524            },
525          },
526        },
527      },
528    },
529  };
530
531  const state = {
532    routes: [
533      {
534        name: 'Foo',
535        state: {
536          index: 1,
537          routes: [
538            {
539              name: 'Foe',
540            },
541            {
542              name: 'Bar',
543              state: {
544                routes: [{ name: 'Baz', path }],
545              },
546            },
547          ],
548        },
549      },
550    ],
551  };
552
553  testConversions(path, config, state);
554});
555
556it('handles initialRouteName included in path', () => {
557  const path = '/baz';
558  const config = {
559    screens: {
560      Foo: {
561        initialRouteName: 'Foe',
562        screens: {
563          Foe: {
564            screens: {
565              Baz: 'baz',
566            },
567          },
568          Bar: 'bar',
569        },
570      },
571    },
572  };
573
574  const state = {
575    routes: [
576      {
577        name: 'Foo',
578        state: {
579          routes: [
580            {
581              name: 'Foe',
582              state: {
583                routes: [{ name: 'Baz', path }],
584              },
585            },
586          ],
587        },
588      },
589    ],
590  };
591
592  testConversions(path, config, state);
593});
594
595it('handles two initialRouteNames', () => {
596  const path = '/bar/sweet/apple/foe/bis/jane?answer=42&count=10&valid=true';
597  const config = {
598    screens: {
599      Bar: {
600        path: 'bar/:type/:fruit',
601        screens: {
602          Foo: {
603            screens: {
604              Foe: {
605                path: 'foe',
606                screens: {
607                  Baz: {
608                    initialRouteName: 'Bos',
609                    screens: {
610                      Bos: {
611                        path: 'bos',
612                        exact: true,
613                      },
614                      Bis: {
615                        path: 'bis/:author',
616                        stringify: {
617                          author: (author: string) => author.replace(/^\w/, (c) => c.toLowerCase()),
618                        },
619                        parse: {
620                          author: (author: string) => author.replace(/^\w/, (c) => c.toUpperCase()),
621                          count: Number,
622                          valid: Boolean,
623                        },
624                      },
625                    },
626                  },
627                },
628              },
629            },
630          },
631        },
632      },
633    },
634  };
635
636  const state = {
637    routes: [
638      {
639        name: 'Bar',
640        params: { author: 'Jane', fruit: 'apple', type: 'sweet' },
641        state: {
642          routes: [
643            {
644              name: 'Foo',
645              params: { author: 'Jane', fruit: 'apple', type: 'sweet' },
646              state: {
647                routes: [
648                  {
649                    name: 'Foe',
650                    params: { author: 'Jane', fruit: 'apple', type: 'sweet' },
651                    state: {
652                      routes: [
653                        {
654                          name: 'Baz',
655                          params: {
656                            author: 'Jane',
657                            fruit: 'apple',
658                            type: 'sweet',
659                          },
660                          state: {
661                            index: 1,
662                            routes: [
663                              { name: 'Bos' },
664                              {
665                                name: 'Bis',
666                                params: {
667                                  answer: '42',
668                                  author: 'Jane',
669                                  count: 10,
670                                  valid: true,
671                                  fruit: 'apple',
672                                  type: 'sweet',
673                                },
674                                path,
675                              },
676                            ],
677                          },
678                        },
679                      ],
680                    },
681                  },
682                ],
683              },
684            },
685          ],
686        },
687      },
688    ],
689  };
690
691  testConversions(path, config, state);
692});
693
694it('accepts initialRouteName without config for it', () => {
695  const path = '/bar/sweet/apple/foe/bis/jane?answer=42&count=10&valid=true';
696  const config = {
697    screens: {
698      Bar: {
699        path: 'bar/:type/:fruit',
700        screens: {
701          Foo: {
702            screens: {
703              Foe: {
704                path: 'foe',
705                screens: {
706                  Baz: {
707                    initialRouteName: 'Bas',
708                    screens: {
709                      Bos: {
710                        path: 'bos',
711                        exact: true,
712                      },
713                      Bis: {
714                        path: 'bis/:author',
715                        stringify: {
716                          author: (author: string) => author.replace(/^\w/, (c) => c.toLowerCase()),
717                        },
718                        parse: {
719                          author: (author: string) => author.replace(/^\w/, (c) => c.toUpperCase()),
720                          count: Number,
721                          valid: Boolean,
722                        },
723                      },
724                    },
725                  },
726                },
727              },
728            },
729          },
730        },
731      },
732    },
733  };
734
735  const state = {
736    routes: [
737      {
738        name: 'Bar',
739        params: { author: 'Jane', fruit: 'apple', type: 'sweet' },
740        state: {
741          routes: [
742            {
743              name: 'Foo',
744              params: { author: 'Jane', fruit: 'apple', type: 'sweet' },
745              state: {
746                routes: [
747                  {
748                    name: 'Foe',
749                    params: { author: 'Jane', fruit: 'apple', type: 'sweet' },
750                    state: {
751                      routes: [
752                        {
753                          name: 'Baz',
754                          params: {
755                            author: 'Jane',
756                            fruit: 'apple',
757                            type: 'sweet',
758                          },
759                          state: {
760                            index: 1,
761                            routes: [
762                              {
763                                name: 'Bas',
764                              },
765                              {
766                                name: 'Bis',
767                                params: {
768                                  answer: '42',
769                                  author: 'Jane',
770                                  count: 10,
771                                  fruit: 'apple',
772                                  type: 'sweet',
773                                  valid: true,
774                                },
775                                path: '/bar/sweet/apple/foe/bis/jane?answer=42&count=10&valid=true',
776                              },
777                            ],
778                          },
779                        },
780                      ],
781                    },
782                  },
783                ],
784              },
785            },
786          ],
787        },
788      },
789    ],
790  };
791
792  testConversions(path, config, state);
793});
794
795it('returns undefined if path is empty and no matching screen is present', () => {
796  const config = {
797    screens: {
798      Foo: {
799        screens: {
800          Foe: 'foe',
801          Bar: {
802            screens: {
803              Baz: 'baz',
804            },
805          },
806        },
807      },
808    },
809  };
810
811  const path = '';
812
813  expect(getStateFromPath<object>(path, config)).toEqual(undefined);
814});
815
816it('returns matching screen if path is empty', () => {
817  const path = '';
818  const config = {
819    screens: {
820      Foo: {
821        screens: {
822          Foe: 'foe',
823          Bar: {
824            screens: {
825              Qux: '',
826              Baz: 'baz',
827            },
828          },
829        },
830      },
831    },
832  };
833
834  const state = {
835    routes: [
836      {
837        name: 'Foo',
838        state: {
839          routes: [
840            {
841              name: 'Bar',
842              state: {
843                routes: [{ name: 'Qux', path }],
844              },
845            },
846          ],
847        },
848      },
849    ],
850  };
851
852  expect(getStateFromPath<object>(path, config)).toEqual(state);
853  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
854    changePath(state, '/')
855  );
856});
857
858it('returns matching screen if path is only slash', () => {
859  const path = '/';
860  const config = {
861    screens: {
862      Foo: {
863        screens: {
864          Foe: 'foe',
865          Bar: {
866            screens: {
867              Qux: '',
868              Baz: 'baz',
869            },
870          },
871        },
872      },
873    },
874  };
875
876  const state = {
877    routes: [
878      {
879        name: 'Foo',
880        state: {
881          routes: [
882            {
883              name: 'Bar',
884              state: {
885                routes: [{ name: 'Qux', path }],
886              },
887            },
888          ],
889        },
890      },
891    ],
892  };
893
894  expect(getStateFromPath<object>(path, config)).toEqual(state);
895  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
896    changePath(state, '/')
897  );
898});
899
900// Test `app/(app)/index.js` matching `/`
901it('returns matching screen if path is only slash and root is a group', () => {
902  expect(
903    getStateFromPath<object>('/', {
904      screens: {
905        '(app)/index': '(app)',
906        '[id]': ':id',
907        '[...404]': '*404',
908      },
909    })
910  ).toEqual({ routes: [{ name: '(app)/index', path: '/' }] });
911});
912
913// Test `app/(one)/(two)/index.js` matching `/`
914it('returns matching screen if path is only slash and root is a nested group', () => {
915  expect(
916    getStateFromPath<object>('/', {
917      screens: {
918        '(one)/(two)/index': '(one)/(two)',
919        '[id]': ':id',
920        '[...404]': '*404',
921      },
922    })
923  ).toEqual({ routes: [{ name: '(one)/(two)/index', path: '/' }] });
924});
925
926it('returns matching screen with params if path is empty', () => {
927  const path = '?foo=42';
928  const config = {
929    screens: {
930      Foo: {
931        screens: {
932          Foe: 'foe',
933          Bar: {
934            screens: {
935              Qux: {
936                path: '',
937                parse: { foo: Number },
938              },
939              Baz: 'baz',
940            },
941          },
942        },
943      },
944    },
945  };
946
947  const state = {
948    routes: [
949      {
950        name: 'Foo',
951        state: {
952          routes: [
953            {
954              name: 'Bar',
955              state: {
956                routes: [{ name: 'Qux', params: { foo: 42 }, path }],
957              },
958            },
959          ],
960        },
961      },
962    ],
963  };
964
965  expect(getStateFromPath<object>(path, config)).toEqual(state);
966  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
967    changePath(state, '/?foo=42')
968  );
969});
970
971it("doesn't match nested screen if path is empty", () => {
972  const config = {
973    screens: {
974      Foo: {
975        screens: {
976          Foe: 'foe',
977          Bar: {
978            path: 'bar',
979            screens: {
980              Qux: {
981                path: '',
982                parse: { foo: Number },
983              },
984            },
985          },
986        },
987      },
988    },
989  };
990
991  const path = '';
992
993  expect(getStateFromPath<object>(path, config)).toEqual(undefined);
994});
995
996it('chooses more exhaustive pattern', () => {
997  const path = '/foo/5';
998
999  const config = {
1000    screens: {
1001      Foe: {
1002        path: '/',
1003        initialRouteName: 'Foo',
1004        screens: {
1005          Foo: 'foo',
1006          Bis: {
1007            path: 'foo/:id',
1008            parse: {
1009              id: Number,
1010            },
1011          },
1012        },
1013      },
1014    },
1015  };
1016
1017  const state = {
1018    routes: [
1019      {
1020        name: 'Foe',
1021        params: {
1022          id: 5,
1023        },
1024        state: {
1025          index: 1,
1026          routes: [
1027            {
1028              name: 'Foo',
1029            },
1030            {
1031              name: 'Bis',
1032              params: { id: 5 },
1033              path,
1034            },
1035          ],
1036        },
1037      },
1038    ],
1039  };
1040
1041  testConversions(path, config, state);
1042});
1043
1044it('handles same paths beginnings', () => {
1045  const path = '/foos';
1046
1047  const config = {
1048    screens: {
1049      Foe: {
1050        path: '/',
1051        initialRouteName: 'Foo',
1052        screens: {
1053          Foo: 'foo',
1054          Bis: {
1055            path: 'foos',
1056          },
1057        },
1058      },
1059    },
1060  };
1061
1062  const state = {
1063    routes: [
1064      {
1065        name: 'Foe',
1066        state: {
1067          index: 1,
1068          routes: [
1069            {
1070              name: 'Foo',
1071            },
1072            {
1073              name: 'Bis',
1074              path,
1075            },
1076          ],
1077        },
1078      },
1079    ],
1080  };
1081
1082  testConversions(path, config, state);
1083});
1084
1085it('handles same paths beginnings with params', () => {
1086  const path = '/foos/5';
1087
1088  const config = {
1089    screens: {
1090      Foe: {
1091        path: '/',
1092        initialRouteName: 'Foo',
1093        screens: {
1094          Foo: 'foo',
1095          Bis: {
1096            path: 'foos/:id',
1097            parse: {
1098              id: Number,
1099            },
1100          },
1101        },
1102      },
1103    },
1104  };
1105
1106  const state = {
1107    routes: [
1108      {
1109        name: 'Foe',
1110        params: {
1111          id: 5,
1112        },
1113
1114        state: {
1115          index: 1,
1116          routes: [
1117            {
1118              name: 'Foo',
1119            },
1120            {
1121              name: 'Bis',
1122              params: { id: 5 },
1123              path,
1124            },
1125          ],
1126        },
1127      },
1128    ],
1129  };
1130
1131  testConversions(path, config, state);
1132});
1133
1134it('handles not taking path with too many segments', () => {
1135  const path = '/foos/5';
1136
1137  const config = {
1138    screens: {
1139      Foe: {
1140        path: '/',
1141        initialRouteName: 'Foo',
1142        screens: {
1143          Foo: 'foo',
1144          Bis: {
1145            path: 'foos/:id',
1146            parse: {
1147              id: Number,
1148            },
1149          },
1150          Bas: {
1151            path: 'foos/:id/:nip',
1152            parse: {
1153              id: Number,
1154              pwd: Number,
1155            },
1156          },
1157        },
1158      },
1159    },
1160  };
1161
1162  const state = {
1163    routes: [
1164      {
1165        name: 'Foe',
1166        params: {
1167          id: 5,
1168        },
1169
1170        state: {
1171          index: 1,
1172          routes: [
1173            {
1174              name: 'Foo',
1175            },
1176            {
1177              name: 'Bis',
1178              params: { id: 5 },
1179              path,
1180            },
1181          ],
1182        },
1183      },
1184    ],
1185  };
1186
1187  testConversions(path, config, state);
1188});
1189
1190it('handles differently ordered params v1', () => {
1191  const path = '/foos/5/res/20';
1192
1193  const config = {
1194    screens: {
1195      Foe: {
1196        path: '/',
1197        initialRouteName: 'Foo',
1198        screens: {
1199          Foo: 'foo',
1200          Bis: {
1201            path: 'foos/:id',
1202            parse: {
1203              id: Number,
1204            },
1205          },
1206          Bas: {
1207            path: 'foos/:id/res/:pwd',
1208            parse: {
1209              id: Number,
1210              pwd: Number,
1211            },
1212          },
1213        },
1214      },
1215    },
1216  };
1217
1218  const state = {
1219    routes: [
1220      {
1221        name: 'Foe',
1222        params: {
1223          id: 5,
1224          pwd: 20,
1225        },
1226        state: {
1227          index: 1,
1228          routes: [
1229            {
1230              name: 'Foo',
1231            },
1232            {
1233              name: 'Bas',
1234              params: { id: 5, pwd: 20 },
1235              path,
1236            },
1237          ],
1238        },
1239      },
1240    ],
1241  };
1242
1243  testConversions(path, config, state);
1244});
1245
1246it('handles differently ordered params v2', () => {
1247  const path = '/5/20/foos/res';
1248
1249  const config = {
1250    screens: {
1251      Foe: {
1252        path: '/',
1253        initialRouteName: 'Foo',
1254        screens: {
1255          Foo: 'foo',
1256          Bis: {
1257            path: 'foos/:id',
1258            parse: {
1259              id: Number,
1260            },
1261          },
1262          Bas: {
1263            path: ':id/:pwd/foos/res',
1264            parse: {
1265              id: Number,
1266              pwd: Number,
1267            },
1268          },
1269        },
1270      },
1271    },
1272  };
1273
1274  const state = {
1275    routes: [
1276      {
1277        name: 'Foe',
1278        params: {
1279          id: 5,
1280          pwd: 20,
1281        },
1282
1283        state: {
1284          index: 1,
1285          routes: [
1286            {
1287              name: 'Foo',
1288            },
1289            {
1290              name: 'Bas',
1291              params: { id: 5, pwd: 20 },
1292              path,
1293            },
1294          ],
1295        },
1296      },
1297    ],
1298  };
1299
1300  testConversions(path, config, state);
1301});
1302
1303it('handles differently ordered params v3', () => {
1304  const path = '/foos/5/20/res';
1305
1306  const config = {
1307    screens: {
1308      Foe: {
1309        path: '/',
1310        initialRouteName: 'Foo',
1311        screens: {
1312          Foo: 'foo',
1313          Bis: {
1314            path: 'foos/:id',
1315            parse: {
1316              id: Number,
1317            },
1318          },
1319          Bas: {
1320            path: 'foos/:id/:pwd/res',
1321            parse: {
1322              id: Number,
1323              pwd: Number,
1324            },
1325          },
1326        },
1327      },
1328    },
1329  };
1330
1331  const state = {
1332    routes: [
1333      {
1334        name: 'Foe',
1335        params: {
1336          id: 5,
1337          pwd: 20,
1338        },
1339        state: {
1340          index: 1,
1341          routes: [
1342            {
1343              name: 'Foo',
1344            },
1345            {
1346              name: 'Bas',
1347              params: { id: 5, pwd: 20 },
1348              path,
1349            },
1350          ],
1351        },
1352      },
1353    ],
1354  };
1355
1356  testConversions(path, config, state);
1357});
1358
1359it('handles differently ordered params v4', () => {
1360  const path = '5/foos/res/20';
1361
1362  const config = {
1363    screens: {
1364      Foe: {
1365        path: '/',
1366        initialRouteName: 'Foo',
1367        screens: {
1368          Foo: 'foo',
1369          Bis: {
1370            path: 'foos/:id',
1371            parse: {
1372              id: Number,
1373            },
1374          },
1375          Bas: {
1376            path: ':id/foos/res/:pwd',
1377            parse: {
1378              id: Number,
1379              pwd: Number,
1380            },
1381          },
1382        },
1383      },
1384    },
1385  };
1386
1387  const state = {
1388    routes: [
1389      {
1390        name: 'Foe',
1391        params: {
1392          id: 5,
1393          pwd: 20,
1394        },
1395        state: {
1396          index: 1,
1397          routes: [
1398            {
1399              name: 'Foo',
1400            },
1401            {
1402              name: 'Bas',
1403              params: { id: 5, pwd: 20 },
1404              path,
1405            },
1406          ],
1407        },
1408      },
1409    ],
1410  };
1411
1412  expect(getStateFromPath<object>(path, config)).toEqual(state);
1413  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
1414    changePath(state, '/5/foos/res/20')
1415  );
1416});
1417
1418it('handles simple optional params', () => {
1419  const path = '/foos/5';
1420
1421  const config = {
1422    screens: {
1423      Foe: {
1424        path: '/',
1425        initialRouteName: 'Foo',
1426        screens: {
1427          Foo: 'foo',
1428          Bis: {
1429            path: 'foo/:id',
1430            parse: {
1431              id: Number,
1432            },
1433          },
1434          Bas: {
1435            path: 'foos/:id/:nip?',
1436            parse: {
1437              id: Number,
1438              nip: Number,
1439            },
1440          },
1441        },
1442      },
1443    },
1444  };
1445
1446  const state = {
1447    routes: [
1448      {
1449        name: 'Foe',
1450        params: {
1451          id: 5,
1452        },
1453        state: {
1454          index: 1,
1455          routes: [
1456            {
1457              name: 'Foo',
1458            },
1459            {
1460              name: 'Bas',
1461              params: { id: 5 },
1462              path,
1463            },
1464          ],
1465        },
1466      },
1467    ],
1468  };
1469
1470  testConversions(path, config, state);
1471});
1472
1473it('handle 2 optional params at the end v1', () => {
1474  const path = '/foos/5';
1475
1476  const config = {
1477    screens: {
1478      Foe: {
1479        path: '/',
1480        initialRouteName: 'Foo',
1481        screens: {
1482          Foo: 'foo',
1483          Bis: {
1484            path: 'foo/:id',
1485            parse: {
1486              id: Number,
1487            },
1488          },
1489          Bas: {
1490            path: 'foos/:id/:nip?/:pwd?',
1491            parse: {
1492              id: Number,
1493              nip: Number,
1494            },
1495          },
1496        },
1497      },
1498    },
1499  };
1500
1501  const state = {
1502    routes: [
1503      {
1504        name: 'Foe',
1505        params: {
1506          id: 5,
1507        },
1508        state: {
1509          index: 1,
1510          routes: [
1511            {
1512              name: 'Foo',
1513            },
1514            {
1515              name: 'Bas',
1516              params: { id: 5 },
1517              path,
1518            },
1519          ],
1520        },
1521      },
1522    ],
1523  };
1524
1525  testConversions(path, config, state);
1526});
1527
1528it('handle 2 optional params at the end v2', () => {
1529  const path = '/foos/5/10';
1530
1531  const config = {
1532    screens: {
1533      Foe: {
1534        path: '/',
1535        initialRouteName: 'Foo',
1536        screens: {
1537          Foo: 'foo',
1538          Bis: {
1539            path: 'foo/:id',
1540            parse: {
1541              id: Number,
1542            },
1543          },
1544          Bas: {
1545            path: 'foos/:id/:nip?/:pwd?',
1546            parse: {
1547              id: Number,
1548              nip: Number,
1549            },
1550          },
1551        },
1552      },
1553    },
1554  };
1555
1556  const state = {
1557    routes: [
1558      {
1559        name: 'Foe',
1560        params: {
1561          id: 5,
1562          nip: 10,
1563        },
1564
1565        state: {
1566          index: 1,
1567          routes: [
1568            {
1569              name: 'Foo',
1570            },
1571            {
1572              name: 'Bas',
1573              params: { id: 5, nip: 10 },
1574              path,
1575            },
1576          ],
1577        },
1578      },
1579    ],
1580  };
1581
1582  testConversions(path, config, state);
1583});
1584
1585it('handle 2 optional params at the end v3', () => {
1586  const path = '/foos/5/10/15';
1587
1588  const config = {
1589    screens: {
1590      Foe: {
1591        path: '/',
1592        initialRouteName: 'Foo',
1593        screens: {
1594          Foo: 'foo',
1595          Bis: {
1596            path: 'foo/:id',
1597            parse: {
1598              id: Number,
1599            },
1600          },
1601          Bas: {
1602            path: 'foos/:id/:nip?/:pwd?',
1603            parse: {
1604              id: Number,
1605              nip: Number,
1606              pwd: Number,
1607            },
1608          },
1609        },
1610      },
1611    },
1612  };
1613
1614  const state = {
1615    routes: [
1616      {
1617        name: 'Foe',
1618        params: {
1619          nip: 10,
1620          pwd: 15,
1621          id: 5,
1622        },
1623        state: {
1624          index: 1,
1625          routes: [
1626            {
1627              name: 'Foo',
1628            },
1629            {
1630              name: 'Bas',
1631              params: { id: 5, nip: 10, pwd: 15 },
1632              path,
1633            },
1634          ],
1635        },
1636      },
1637    ],
1638  };
1639
1640  testConversions(path, config, state);
1641});
1642
1643it('handle optional params in the middle v1', () => {
1644  const path = '/foos/5/10';
1645
1646  const config = {
1647    screens: {
1648      Foe: {
1649        path: '/',
1650        initialRouteName: 'Foo',
1651        screens: {
1652          Foo: 'foo',
1653          Bis: {
1654            path: 'foo/:id',
1655            parse: {
1656              id: Number,
1657            },
1658          },
1659          Bas: {
1660            path: 'foos/:id/:nip?/:pwd',
1661            parse: {
1662              id: Number,
1663              nip: Number,
1664              pwd: Number,
1665            },
1666          },
1667        },
1668      },
1669    },
1670  };
1671
1672  const state = {
1673    routes: [
1674      {
1675        name: 'Foe',
1676        params: {
1677          id: 5,
1678          pwd: 10,
1679        },
1680
1681        state: {
1682          index: 1,
1683          routes: [
1684            {
1685              name: 'Foo',
1686            },
1687            {
1688              name: 'Bas',
1689              params: { id: 5, pwd: 10 },
1690              path,
1691            },
1692          ],
1693        },
1694      },
1695    ],
1696  };
1697
1698  testConversions(path, config, state);
1699});
1700
1701it('handle optional params in the middle v2', () => {
1702  const path = '/foos/5/10/15';
1703
1704  const config = {
1705    screens: {
1706      Foe: {
1707        path: '/',
1708        initialRouteName: 'Foo',
1709        screens: {
1710          Foo: 'foo',
1711          Bis: {
1712            path: 'foo/:id',
1713            parse: {
1714              id: Number,
1715            },
1716          },
1717          Bas: {
1718            path: 'foos/:id/:nip?/:pwd',
1719            parse: {
1720              id: Number,
1721              nip: Number,
1722              pwd: Number,
1723            },
1724          },
1725        },
1726      },
1727    },
1728  };
1729
1730  const state = {
1731    routes: [
1732      {
1733        name: 'Foe',
1734        params: {
1735          id: 5,
1736          nip: 10,
1737          pwd: 15,
1738        },
1739        state: {
1740          index: 1,
1741          routes: [
1742            {
1743              name: 'Foo',
1744            },
1745            {
1746              name: 'Bas',
1747              params: { id: 5, nip: 10, pwd: 15 },
1748              path,
1749            },
1750          ],
1751        },
1752      },
1753    ],
1754  };
1755
1756  testConversions(path, config, state);
1757});
1758
1759it('handle optional params in the middle v3', () => {
1760  const path = '/foos/5/10/15';
1761
1762  const config = {
1763    screens: {
1764      Foe: {
1765        path: '/',
1766        initialRouteName: 'Foo',
1767        screens: {
1768          Foo: 'foo',
1769          Bis: {
1770            path: 'foo/:id',
1771            parse: {
1772              id: Number,
1773            },
1774          },
1775          Bas: {
1776            path: 'foos/:id/:nip?/:pwd/:smh',
1777            parse: {
1778              id: Number,
1779              nip: Number,
1780              pwd: Number,
1781              smh: Number,
1782            },
1783          },
1784        },
1785      },
1786    },
1787  };
1788
1789  const state = {
1790    routes: [
1791      {
1792        name: 'Foe',
1793        params: {
1794          id: 5,
1795          pwd: 10,
1796          smh: 15,
1797        },
1798
1799        state: {
1800          index: 1,
1801          routes: [
1802            {
1803              name: 'Foo',
1804            },
1805            {
1806              name: 'Bas',
1807              params: { id: 5, pwd: 10, smh: 15 },
1808              path,
1809            },
1810          ],
1811        },
1812      },
1813    ],
1814  };
1815
1816  testConversions(path, config, state);
1817});
1818
1819it('handle optional params in the middle v4', () => {
1820  const path = '/foos/5/10';
1821
1822  const config = {
1823    screens: {
1824      Foe: {
1825        path: '/',
1826        initialRouteName: 'Foo',
1827        screens: {
1828          Foo: 'foo',
1829          Bis: {
1830            path: 'foo/:id',
1831            parse: {
1832              id: Number,
1833            },
1834          },
1835          Bas: {
1836            path: 'foos/:nip?/:pwd/:smh?/:id',
1837            parse: {
1838              id: Number,
1839              nip: Number,
1840              pwd: Number,
1841              smh: Number,
1842            },
1843          },
1844        },
1845      },
1846    },
1847  };
1848
1849  const state = {
1850    routes: [
1851      {
1852        name: 'Foe',
1853        params: {
1854          id: 10,
1855          pwd: 5,
1856        },
1857        state: {
1858          index: 1,
1859          routes: [
1860            {
1861              name: 'Foo',
1862            },
1863            {
1864              name: 'Bas',
1865              params: { pwd: 5, id: 10 },
1866              path,
1867            },
1868          ],
1869        },
1870      },
1871    ],
1872  };
1873
1874  testConversions(path, config, state);
1875});
1876
1877it('handle optional params in the middle v5', () => {
1878  const path = '/foos/5/10/15';
1879
1880  const config = {
1881    screens: {
1882      Foe: {
1883        path: '/',
1884        initialRouteName: 'Foo',
1885        screens: {
1886          Foo: 'foo',
1887          Bis: {
1888            path: 'foo/:id',
1889            parse: {
1890              id: Number,
1891            },
1892          },
1893          Bas: {
1894            path: 'foos/:nip?/:pwd/:smh?/:id',
1895            parse: {
1896              id: Number,
1897              nip: Number,
1898              pwd: Number,
1899              smh: Number,
1900            },
1901          },
1902        },
1903      },
1904    },
1905  };
1906
1907  const state = {
1908    routes: [
1909      {
1910        name: 'Foe',
1911        params: {
1912          id: 15,
1913          nip: 5,
1914          pwd: 10,
1915        },
1916
1917        state: {
1918          index: 1,
1919          routes: [
1920            {
1921              name: 'Foo',
1922            },
1923            {
1924              name: 'Bas',
1925              params: { nip: 5, pwd: 10, id: 15 },
1926              path,
1927            },
1928          ],
1929        },
1930      },
1931    ],
1932  };
1933
1934  testConversions(path, config, state);
1935});
1936
1937it('handle optional params in the beginning v1', () => {
1938  const path = '5/10/foos/15';
1939
1940  const config = {
1941    screens: {
1942      Foe: {
1943        path: '/',
1944        initialRouteName: 'Foo',
1945        screens: {
1946          Foo: 'foo',
1947          Bis: {
1948            path: 'foo/:id',
1949            parse: {
1950              id: Number,
1951            },
1952          },
1953          Bas: {
1954            path: ':nip?/:pwd/foos/:smh?/:id',
1955            parse: {
1956              id: Number,
1957              nip: Number,
1958              pwd: Number,
1959              smh: Number,
1960            },
1961          },
1962        },
1963      },
1964    },
1965  };
1966
1967  const state = {
1968    routes: [
1969      {
1970        name: 'Foe',
1971        params: {
1972          id: 15,
1973          nip: 5,
1974          pwd: 10,
1975        },
1976        state: {
1977          index: 1,
1978          routes: [
1979            {
1980              name: 'Foo',
1981            },
1982            {
1983              name: 'Bas',
1984              params: { nip: 5, pwd: 10, id: 15 },
1985              path,
1986            },
1987          ],
1988        },
1989      },
1990    ],
1991  };
1992
1993  expect(getStateFromPath<object>(path, config)).toEqual(state);
1994  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
1995    changePath(state, '/5/10/foos/15')
1996  );
1997});
1998
1999it('handle optional params in the beginning v2', () => {
2000  const path = '5/10/foos/15';
2001
2002  const config = {
2003    screens: {
2004      Foe: {
2005        path: '/',
2006        initialRouteName: 'Foo',
2007        screens: {
2008          Foo: 'foo',
2009          Bis: {
2010            path: 'foo/:id',
2011            parse: {
2012              id: Number,
2013            },
2014          },
2015          Bas: {
2016            path: ':nip?/:smh?/:pwd/foos/:id',
2017            parse: {
2018              id: Number,
2019              nip: Number,
2020              pwd: Number,
2021              smh: Number,
2022            },
2023          },
2024        },
2025      },
2026    },
2027  };
2028
2029  const state = {
2030    routes: [
2031      {
2032        name: 'Foe',
2033        params: {
2034          id: 15,
2035          nip: 5,
2036          pwd: 10,
2037        },
2038        state: {
2039          index: 1,
2040          routes: [
2041            {
2042              name: 'Foo',
2043            },
2044            {
2045              name: 'Bas',
2046              params: { nip: 5, pwd: 10, id: 15 },
2047              path,
2048            },
2049          ],
2050        },
2051      },
2052    ],
2053  };
2054
2055  expect(getStateFromPath<object>(path, config)).toEqual(state);
2056  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
2057    changePath(state, '/5/10/foos/15')
2058  );
2059});
2060
2061it('merges parent patterns if needed', () => {
2062  const path = 'foo/42/baz/babel';
2063
2064  const config = {
2065    screens: {
2066      Foo: {
2067        path: 'foo/:bar',
2068        parse: {
2069          bar: Number,
2070        },
2071        screens: {
2072          Baz: 'baz/:qux',
2073        },
2074      },
2075    },
2076  };
2077
2078  const state = {
2079    routes: [
2080      {
2081        name: 'Foo',
2082        params: { bar: 42, qux: 'babel' },
2083        state: {
2084          routes: [
2085            {
2086              name: 'Baz',
2087              params: { bar: 42, qux: 'babel' },
2088              path,
2089            },
2090          ],
2091        },
2092      },
2093    ],
2094  };
2095
2096  expect(getStateFromPath<object>(path, config)).toEqual(state);
2097  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
2098    changePath(state, '/foo/42/baz/babel')
2099  );
2100});
2101
2102it('ignores extra slashes in the pattern', () => {
2103  const path = '/bar/42';
2104  const config = {
2105    screens: {
2106      Foo: {
2107        screens: {
2108          Bar: {
2109            path: '/bar//:id/',
2110          },
2111        },
2112      },
2113    },
2114  };
2115
2116  const state = {
2117    routes: [
2118      {
2119        name: 'Foo',
2120        params: {
2121          id: '42',
2122        },
2123        state: {
2124          routes: [
2125            {
2126              name: 'Bar',
2127              params: { id: '42' },
2128              path,
2129            },
2130          ],
2131        },
2132      },
2133    ],
2134  };
2135
2136  testConversions(path, config, state);
2137});
2138
2139it('matches wildcard patterns at root', () => {
2140  const path = '/test/bar/42/whatever';
2141  const config = configFromFs(['[...404].js', 'foo/bar.js', 'index.js']);
2142  const state = {
2143    routes: [
2144      {
2145        params: {
2146          '404': ['test', 'bar', '42', 'whatever'],
2147        },
2148        name: '[...404]',
2149        path,
2150      },
2151    ],
2152  };
2153
2154  testConversions(path, config, state);
2155});
2156
2157it('matches wildcard patterns at nested level', () => {
2158  const path = '/bar/42/whatever/baz/initt';
2159
2160  const config = configFromFs([
2161    '(foo)/_layout.tsx',
2162    '(foo)/bar/_layout.tsx',
2163    '(foo)/bar/[id].tsx',
2164    '(foo)/bar/[...rest].tsx',
2165  ]);
2166
2167  const state = {
2168    routes: [
2169      {
2170        name: '(foo)',
2171        params: { rest: ['42', 'whatever', 'baz', 'initt'] },
2172        state: {
2173          routes: [
2174            {
2175              name: 'bar',
2176              params: { rest: ['42', 'whatever', 'baz', 'initt'] },
2177              state: {
2178                routes: [
2179                  {
2180                    name: '[...rest]',
2181                    params: {
2182                      rest: ['42', 'whatever', 'baz', 'initt'],
2183                    },
2184                    path: '/bar/42/whatever/baz/initt',
2185                  },
2186                ],
2187              },
2188            },
2189          ],
2190        },
2191      },
2192    ],
2193  };
2194
2195  testConversions(path, config, state);
2196});
2197
2198xit('matches wildcard patterns at nested level with exact', () => {
2199  const path = '/whatever';
2200  const config = {
2201    screens: {
2202      Foo: {
2203        screens: {
2204          Bar: {
2205            path: '/bar/:id/',
2206            screens: {
2207              404: {
2208                path: '*404',
2209                exact: true,
2210              },
2211            },
2212          },
2213          Baz: {},
2214        },
2215      },
2216    },
2217  };
2218
2219  const state = {
2220    routes: [
2221      {
2222        name: 'Foo',
2223        params: {
2224          '404': ['whatever'],
2225        },
2226        state: {
2227          routes: [
2228            {
2229              name: 'Bar',
2230              params: {
2231                '404': ['whatever'],
2232              },
2233              state: {
2234                routes: [
2235                  {
2236                    name: '404',
2237                    params: {
2238                      '404': ['whatever'],
2239                    },
2240                    path,
2241                  },
2242                ],
2243              },
2244            },
2245          ],
2246        },
2247      },
2248    ],
2249  };
2250
2251  expect(getStateFromPath<object>(path, config)).toEqual(state);
2252  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(
2253    changePath(state, path)
2254  );
2255});
2256
2257it('tries to match wildcard patterns at the end', () => {
2258  const path = '/bar/42/test';
2259  const config = {
2260    screens: {
2261      bar: {
2262        path: 'bar',
2263        screens: {
2264          '[...404]': '*404',
2265          '[userSlug]': ':userSlug',
2266          ':id': {
2267            path: ':id',
2268            screens: {
2269              test: 'test',
2270            },
2271          },
2272        },
2273      },
2274    },
2275  };
2276
2277  const state = {
2278    routes: [
2279      {
2280        name: 'bar',
2281        params: { id: '42' },
2282        state: {
2283          routes: [
2284            {
2285              name: ':id',
2286              params: { id: '42' },
2287              state: {
2288                routes: [
2289                  {
2290                    name: 'test',
2291                    params: { id: '42' },
2292                    path: '/bar/42/test',
2293                  },
2294                ],
2295              },
2296            },
2297          ],
2298        },
2299      },
2300    ],
2301  };
2302
2303  testConversions(path, config, state);
2304});
2305
2306it('uses nearest parent wildcard match for unmatched paths', () => {
2307  const path = '/bar/42/baz/test';
2308  const config = {
2309    screens: {
2310      Foo: {
2311        screens: {
2312          Bar: {
2313            path: '/bar/:id/',
2314            screens: {
2315              Baz: 'baz',
2316            },
2317          },
2318          '[...404]': '*404',
2319        },
2320      },
2321    },
2322  };
2323
2324  const state = {
2325    routes: [
2326      {
2327        name: 'Foo',
2328        params: {
2329          '404': ['bar', '42', 'baz', 'test'],
2330        },
2331        state: {
2332          routes: [
2333            {
2334              name: '[...404]',
2335              params: {
2336                '404': ['bar', '42', 'baz', 'test'],
2337              },
2338              path,
2339            },
2340          ],
2341        },
2342      },
2343    ],
2344  };
2345
2346  testConversions(path, config, state);
2347});
2348
2349it('throws if two screens map to the same pattern', () => {
2350  const path = '/bar/42/baz/test';
2351
2352  expect(() =>
2353    getStateFromPath<object>(path, {
2354      screens: {
2355        Foo: {
2356          screens: {
2357            Bar: {
2358              path: '/bar/:id/',
2359              screens: {
2360                Baz: 'baz',
2361              },
2362            },
2363            Bax: '/bar/:id/baz',
2364          },
2365        },
2366      },
2367    })
2368  ).toThrow(
2369    "The route pattern 'bar/:id/baz' resolves to both 'Foo//bar/:id/baz' and 'Foo/Bar/baz'. Patterns must be unique and cannot resolve to more than one route."
2370  );
2371
2372  expect(() =>
2373    getStateFromPath<object>(path, {
2374      screens: {
2375        Foo: {
2376          screens: {
2377            Bar: {
2378              path: '/bar/:id/',
2379              screens: {
2380                Baz: '',
2381              },
2382            },
2383          },
2384        },
2385      },
2386    })
2387  ).not.toThrow();
2388});
2389
2390it('correctly applies initialRouteName for config with similar route names', () => {
2391  const path = '/weekly-earnings';
2392
2393  const config = {
2394    screens: {
2395      RootTabs: {
2396        screens: {
2397          HomeTab: {
2398            screens: {
2399              Home: '',
2400              WeeklyEarnings: 'weekly-earnings',
2401              EventDetails: 'event-details/:eventId',
2402            },
2403          },
2404          EarningsTab: {
2405            initialRouteName: 'Earnings',
2406            path: 'earnings',
2407            screens: {
2408              Earnings: '',
2409              WeeklyEarnings: 'weekly-earnings',
2410            },
2411          },
2412        },
2413      },
2414    },
2415  };
2416
2417  const state = {
2418    routes: [
2419      {
2420        name: 'RootTabs',
2421        state: {
2422          routes: [
2423            {
2424              name: 'HomeTab',
2425              state: {
2426                routes: [
2427                  {
2428                    name: 'WeeklyEarnings',
2429                    path,
2430                  },
2431                ],
2432              },
2433            },
2434          ],
2435        },
2436      },
2437    ],
2438  };
2439
2440  testConversions(path, config, state);
2441});
2442
2443it('correctly applies initialRouteName for config with similar route names v2', () => {
2444  const path = '/earnings/weekly-earnings';
2445
2446  const config = {
2447    screens: {
2448      RootTabs: {
2449        screens: {
2450          HomeTab: {
2451            initialRouteName: 'Home',
2452            screens: {
2453              Home: '',
2454              WeeklyEarnings: 'weekly-earnings',
2455            },
2456          },
2457          EarningsTab: {
2458            initialRouteName: 'Earnings',
2459            path: 'earnings',
2460            screens: {
2461              Earnings: '',
2462              WeeklyEarnings: 'weekly-earnings',
2463            },
2464          },
2465        },
2466      },
2467    },
2468  };
2469
2470  const state = {
2471    routes: [
2472      {
2473        name: 'RootTabs',
2474        state: {
2475          routes: [
2476            {
2477              name: 'EarningsTab',
2478              state: {
2479                index: 1,
2480                routes: [
2481                  {
2482                    name: 'Earnings',
2483                  },
2484                  {
2485                    name: 'WeeklyEarnings',
2486                    path,
2487                  },
2488                ],
2489              },
2490            },
2491          ],
2492        },
2493      },
2494    ],
2495  };
2496
2497  testConversions(path, config, state);
2498});
2499
2500it('throws when invalid properties are specified in the config', () => {
2501  expect(() =>
2502    getStateFromPath<object>('', {
2503      Foo: 'foo',
2504      Bar: {
2505        path: 'bar',
2506      },
2507    } as any)
2508  ).toThrowErrorMatchingInlineSnapshot(`
2509    "Found invalid properties in the configuration:
2510    - Foo
2511    - Bar
2512
2513    Did you forget to specify them under a 'screens' property?
2514
2515    You can only specify the following properties:
2516    - initialRouteName
2517    - screens
2518    - _route
2519
2520    See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
2521  `);
2522
2523  expect(() =>
2524    getStateFromPath<object>('', {
2525      screens: {
2526        Foo: 'foo',
2527        Bar: {
2528          path: 'bar',
2529        },
2530        Baz: {
2531          Qux: {
2532            path: 'qux',
2533          },
2534        },
2535      },
2536    } as any)
2537  ).toThrowErrorMatchingInlineSnapshot(`
2538    "Found invalid properties in the configuration:
2539    - Qux
2540
2541    Did you forget to specify them under a 'screens' property?
2542
2543    You can only specify the following properties:
2544    - initialRouteName
2545    - screens
2546    - _route
2547    - path
2548    - exact
2549    - stringify
2550    - parse
2551
2552    See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
2553  `);
2554});
2555
2556function testConversions(path: string, config: any, state: any) {
2557  expect(getStateFromPath<object>(path, config)).toEqual(state);
2558  expect(getStateFromPath<object>(getPathFromState<object>(state, config), config)).toEqual(state);
2559}
2560