one 1.1.473 → 1.1.475

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/dist/cjs/Root.cjs +5 -1
  2. package/dist/cjs/Root.js +2 -2
  3. package/dist/cjs/Root.js.map +1 -1
  4. package/dist/cjs/Root.native.js +5 -2
  5. package/dist/cjs/Root.native.js.map +2 -2
  6. package/dist/cjs/createApp.cjs +2 -0
  7. package/dist/cjs/createApp.js +16 -2
  8. package/dist/cjs/createApp.js.map +1 -1
  9. package/dist/cjs/createApp.native.js +1 -0
  10. package/dist/cjs/createApp.native.js.map +2 -2
  11. package/dist/cjs/fork/__tests__/getPathFromState.test.cjs +1440 -0
  12. package/dist/cjs/fork/__tests__/getPathFromState.test.js +1559 -0
  13. package/dist/cjs/fork/__tests__/getPathFromState.test.js.map +6 -0
  14. package/dist/cjs/fork/__tests__/getPathFromState.test.native.js +1726 -0
  15. package/dist/cjs/fork/__tests__/getPathFromState.test.native.js.map +6 -0
  16. package/dist/cjs/fork/__tests__/getStateFromPath.test.cjs +2565 -0
  17. package/dist/cjs/fork/__tests__/getStateFromPath.test.js +2702 -0
  18. package/dist/cjs/fork/__tests__/getStateFromPath.test.js.map +6 -0
  19. package/dist/cjs/fork/__tests__/getStateFromPath.test.native.js +2861 -0
  20. package/dist/cjs/fork/__tests__/getStateFromPath.test.native.js.map +6 -0
  21. package/dist/cjs/fork/getPathFromState.cjs +2 -1
  22. package/dist/cjs/fork/getPathFromState.js +1 -1
  23. package/dist/cjs/fork/getPathFromState.js.map +1 -1
  24. package/dist/cjs/fork/getPathFromState.native.js +10 -5
  25. package/dist/cjs/fork/getPathFromState.native.js.map +1 -1
  26. package/dist/cjs/fork/getPathFromState.test.cjs +113 -0
  27. package/dist/cjs/fork/getPathFromState.test.js +122 -0
  28. package/dist/cjs/fork/getPathFromState.test.js.map +6 -0
  29. package/dist/cjs/fork/getPathFromState.test.native.js +135 -0
  30. package/dist/cjs/fork/getPathFromState.test.native.js.map +6 -0
  31. package/dist/cjs/fork/getStateFromPath.test.cjs +229 -0
  32. package/dist/cjs/fork/getStateFromPath.test.js +290 -0
  33. package/dist/cjs/fork/getStateFromPath.test.js.map +6 -0
  34. package/dist/cjs/fork/getStateFromPath.test.native.js +374 -0
  35. package/dist/cjs/fork/getStateFromPath.test.native.js.map +6 -0
  36. package/dist/cjs/render.cjs +1 -1
  37. package/dist/cjs/render.js +1 -1
  38. package/dist/cjs/render.js.map +1 -1
  39. package/dist/cjs/router/FlagsContext.cjs +27 -0
  40. package/dist/cjs/router/FlagsContext.js +22 -0
  41. package/dist/cjs/router/FlagsContext.js.map +6 -0
  42. package/dist/cjs/router/FlagsContext.native.js +26 -0
  43. package/dist/cjs/router/FlagsContext.native.js.map +6 -0
  44. package/dist/cjs/router/getRoutes.cjs +11 -1
  45. package/dist/cjs/router/getRoutes.js +11 -1
  46. package/dist/cjs/router/getRoutes.js.map +1 -1
  47. package/dist/cjs/router/getRoutes.native.js +11 -1
  48. package/dist/cjs/router/getRoutes.native.js.map +2 -2
  49. package/dist/cjs/router/matchers.test.cjs +38 -0
  50. package/dist/cjs/router/matchers.test.js +42 -0
  51. package/dist/cjs/router/matchers.test.js.map +6 -0
  52. package/dist/cjs/router/matchers.test.native.js +39 -0
  53. package/dist/cjs/router/matchers.test.native.js.map +6 -0
  54. package/dist/cjs/router/router.cjs +3 -35
  55. package/dist/cjs/router/router.js +2 -26
  56. package/dist/cjs/router/router.js.map +1 -1
  57. package/dist/cjs/router/router.native.js +2 -33
  58. package/dist/cjs/router/router.native.js.map +2 -2
  59. package/dist/cjs/router/utils/getNavigateAction.cjs +61 -0
  60. package/dist/cjs/router/utils/getNavigateAction.js +46 -0
  61. package/dist/cjs/router/utils/getNavigateAction.js.map +6 -0
  62. package/dist/cjs/router/utils/getNavigateAction.native.js +58 -0
  63. package/dist/cjs/router/utils/getNavigateAction.native.js.map +6 -0
  64. package/dist/cjs/router/utils/getNavigateAction.test.cjs +259 -0
  65. package/dist/cjs/router/utils/getNavigateAction.test.js +295 -0
  66. package/dist/cjs/router/utils/getNavigateAction.test.js.map +6 -0
  67. package/dist/cjs/router/utils/getNavigateAction.test.native.js +330 -0
  68. package/dist/cjs/router/utils/getNavigateAction.test.native.js.map +6 -0
  69. package/dist/cjs/testing-utils.cjs +63 -0
  70. package/dist/cjs/testing-utils.js +55 -0
  71. package/dist/cjs/testing-utils.js.map +6 -0
  72. package/dist/cjs/testing-utils.native.js +79 -0
  73. package/dist/cjs/testing-utils.native.js.map +6 -0
  74. package/dist/cjs/views/Navigator.cjs +8 -1
  75. package/dist/cjs/views/Navigator.js +25 -11
  76. package/dist/cjs/views/Navigator.js.map +1 -1
  77. package/dist/cjs/views/Navigator.native.js +7 -4
  78. package/dist/cjs/views/Navigator.native.js.map +2 -2
  79. package/dist/cjs/vite/one.cjs +6 -1
  80. package/dist/cjs/vite/one.js +6 -1
  81. package/dist/cjs/vite/one.js.map +1 -1
  82. package/dist/cjs/vite/one.native.js +7 -2
  83. package/dist/cjs/vite/one.native.js.map +2 -2
  84. package/dist/cjs/vite/plugins/virtualEntryPlugin.cjs +2 -0
  85. package/dist/cjs/vite/plugins/virtualEntryPlugin.js +2 -0
  86. package/dist/cjs/vite/plugins/virtualEntryPlugin.js.map +1 -1
  87. package/dist/cjs/vite/plugins/virtualEntryPlugin.native.js +2 -0
  88. package/dist/cjs/vite/plugins/virtualEntryPlugin.native.js.map +2 -2
  89. package/dist/esm/Root.js +2 -1
  90. package/dist/esm/Root.js.map +1 -1
  91. package/dist/esm/Root.mjs +5 -1
  92. package/dist/esm/Root.mjs.map +1 -1
  93. package/dist/esm/Root.native.js +5 -1
  94. package/dist/esm/Root.native.js.map +1 -1
  95. package/dist/esm/createApp.js +16 -2
  96. package/dist/esm/createApp.js.map +1 -1
  97. package/dist/esm/createApp.mjs +2 -0
  98. package/dist/esm/createApp.mjs.map +1 -1
  99. package/dist/esm/createApp.native.js +1 -0
  100. package/dist/esm/createApp.native.js.map +1 -1
  101. package/dist/esm/fork/__tests__/getPathFromState.test.js +1561 -0
  102. package/dist/esm/fork/__tests__/getPathFromState.test.js.map +6 -0
  103. package/dist/esm/fork/__tests__/getPathFromState.test.mjs +1441 -0
  104. package/dist/esm/fork/__tests__/getPathFromState.test.mjs.map +1 -0
  105. package/dist/esm/fork/__tests__/getPathFromState.test.native.js +1580 -0
  106. package/dist/esm/fork/__tests__/getPathFromState.test.native.js.map +1 -0
  107. package/dist/esm/fork/__tests__/getStateFromPath.test.js +2706 -0
  108. package/dist/esm/fork/__tests__/getStateFromPath.test.js.map +6 -0
  109. package/dist/esm/fork/__tests__/getStateFromPath.test.mjs +2566 -0
  110. package/dist/esm/fork/__tests__/getStateFromPath.test.mjs.map +1 -0
  111. package/dist/esm/fork/__tests__/getStateFromPath.test.native.js +2636 -0
  112. package/dist/esm/fork/__tests__/getStateFromPath.test.native.js.map +1 -0
  113. package/dist/esm/fork/getPathFromState.js +1 -1
  114. package/dist/esm/fork/getPathFromState.js.map +1 -1
  115. package/dist/esm/fork/getPathFromState.mjs +2 -1
  116. package/dist/esm/fork/getPathFromState.mjs.map +1 -1
  117. package/dist/esm/fork/getPathFromState.native.js +9 -5
  118. package/dist/esm/fork/getPathFromState.native.js.map +1 -1
  119. package/dist/esm/fork/getPathFromState.test.js +123 -0
  120. package/dist/esm/fork/getPathFromState.test.js.map +6 -0
  121. package/dist/esm/fork/getPathFromState.test.mjs +114 -0
  122. package/dist/esm/fork/getPathFromState.test.mjs.map +1 -0
  123. package/dist/esm/fork/getPathFromState.test.native.js +122 -0
  124. package/dist/esm/fork/getPathFromState.test.native.js.map +1 -0
  125. package/dist/esm/fork/getStateFromPath.test.js +294 -0
  126. package/dist/esm/fork/getStateFromPath.test.js.map +6 -0
  127. package/dist/esm/fork/getStateFromPath.test.mjs +230 -0
  128. package/dist/esm/fork/getStateFromPath.test.mjs.map +1 -0
  129. package/dist/esm/fork/getStateFromPath.test.native.js +233 -0
  130. package/dist/esm/fork/getStateFromPath.test.native.js.map +1 -0
  131. package/dist/esm/render.js +1 -1
  132. package/dist/esm/render.js.map +1 -1
  133. package/dist/esm/render.mjs +1 -1
  134. package/dist/esm/render.mjs.map +1 -1
  135. package/dist/esm/router/FlagsContext.js +6 -0
  136. package/dist/esm/router/FlagsContext.js.map +6 -0
  137. package/dist/esm/router/FlagsContext.mjs +4 -0
  138. package/dist/esm/router/FlagsContext.mjs.map +1 -0
  139. package/dist/esm/router/FlagsContext.native.js +4 -0
  140. package/dist/esm/router/FlagsContext.native.js.map +1 -0
  141. package/dist/esm/router/getRoutes.js +11 -1
  142. package/dist/esm/router/getRoutes.js.map +1 -1
  143. package/dist/esm/router/getRoutes.mjs +11 -1
  144. package/dist/esm/router/getRoutes.mjs.map +1 -1
  145. package/dist/esm/router/getRoutes.native.js +11 -1
  146. package/dist/esm/router/getRoutes.native.js.map +1 -1
  147. package/dist/esm/router/matchers.test.js +50 -0
  148. package/dist/esm/router/matchers.test.js.map +6 -0
  149. package/dist/esm/router/matchers.test.mjs +39 -0
  150. package/dist/esm/router/matchers.test.mjs.map +1 -0
  151. package/dist/esm/router/matchers.test.native.js +39 -0
  152. package/dist/esm/router/matchers.test.native.js.map +1 -0
  153. package/dist/esm/router/router.js +1 -26
  154. package/dist/esm/router/router.js.map +1 -1
  155. package/dist/esm/router/router.mjs +1 -33
  156. package/dist/esm/router/router.mjs.map +1 -1
  157. package/dist/esm/router/router.native.js +1 -37
  158. package/dist/esm/router/router.native.js.map +1 -1
  159. package/dist/esm/router/utils/getNavigateAction.js +32 -0
  160. package/dist/esm/router/utils/getNavigateAction.js.map +6 -0
  161. package/dist/esm/router/utils/getNavigateAction.mjs +38 -0
  162. package/dist/esm/router/utils/getNavigateAction.mjs.map +1 -0
  163. package/dist/esm/router/utils/getNavigateAction.native.js +42 -0
  164. package/dist/esm/router/utils/getNavigateAction.native.js.map +1 -0
  165. package/dist/esm/router/utils/getNavigateAction.test.js +296 -0
  166. package/dist/esm/router/utils/getNavigateAction.test.js.map +6 -0
  167. package/dist/esm/router/utils/getNavigateAction.test.mjs +260 -0
  168. package/dist/esm/router/utils/getNavigateAction.test.mjs.map +1 -0
  169. package/dist/esm/router/utils/getNavigateAction.test.native.js +273 -0
  170. package/dist/esm/router/utils/getNavigateAction.test.native.js.map +1 -0
  171. package/dist/esm/testing-utils.js +33 -0
  172. package/dist/esm/testing-utils.js.map +6 -0
  173. package/dist/esm/testing-utils.mjs +27 -0
  174. package/dist/esm/testing-utils.mjs.map +1 -0
  175. package/dist/esm/testing-utils.native.js +38 -0
  176. package/dist/esm/testing-utils.native.js.map +1 -0
  177. package/dist/esm/views/Navigator.js +29 -11
  178. package/dist/esm/views/Navigator.js.map +1 -1
  179. package/dist/esm/views/Navigator.mjs +8 -1
  180. package/dist/esm/views/Navigator.mjs.map +1 -1
  181. package/dist/esm/views/Navigator.native.js +8 -2
  182. package/dist/esm/views/Navigator.native.js.map +1 -1
  183. package/dist/esm/vite/one.js +6 -1
  184. package/dist/esm/vite/one.js.map +1 -1
  185. package/dist/esm/vite/one.mjs +6 -1
  186. package/dist/esm/vite/one.mjs.map +1 -1
  187. package/dist/esm/vite/one.native.js +9 -2
  188. package/dist/esm/vite/one.native.js.map +1 -1
  189. package/dist/esm/vite/plugins/virtualEntryPlugin.js +2 -0
  190. package/dist/esm/vite/plugins/virtualEntryPlugin.js.map +1 -1
  191. package/dist/esm/vite/plugins/virtualEntryPlugin.mjs +2 -0
  192. package/dist/esm/vite/plugins/virtualEntryPlugin.mjs.map +1 -1
  193. package/dist/esm/vite/plugins/virtualEntryPlugin.native.js +2 -0
  194. package/dist/esm/vite/plugins/virtualEntryPlugin.native.js.map +1 -1
  195. package/package.json +13 -10
  196. package/src/Root.tsx +12 -2
  197. package/src/createApp.native.tsx +8 -2
  198. package/src/createApp.tsx +18 -3
  199. package/src/fork/__tests__/README.md +8 -0
  200. package/src/fork/__tests__/getPathFromState.test.tsx +1809 -0
  201. package/src/fork/__tests__/getStateFromPath.test.tsx +3188 -0
  202. package/src/fork/getPathFromState.test.ts +146 -0
  203. package/src/fork/getPathFromState.ts +1 -1
  204. package/src/fork/getStateFromPath.test.ts +345 -0
  205. package/src/render.tsx +3 -3
  206. package/src/router/FlagsContext.ts +4 -0
  207. package/src/router/getRoutes.ts +14 -2
  208. package/src/router/matchers.test.ts +120 -0
  209. package/src/router/router.ts +1 -113
  210. package/src/router/utils/getNavigateAction.test.ts +334 -0
  211. package/src/router/utils/getNavigateAction.ts +120 -0
  212. package/src/testing-utils.ts +56 -0
  213. package/src/views/Navigator.tsx +34 -10
  214. package/src/vite/one.ts +5 -0
  215. package/src/vite/plugins/virtualEntryPlugin.ts +4 -1
  216. package/src/vite/types.ts +18 -0
  217. package/types/Root.d.ts +1 -0
  218. package/types/Root.d.ts.map +1 -1
  219. package/types/createApp.d.ts +2 -0
  220. package/types/createApp.d.ts.map +1 -1
  221. package/types/createApp.native.d.ts +2 -0
  222. package/types/createApp.native.d.ts.map +1 -1
  223. package/types/fork/getPathFromState.test.d.ts +2 -0
  224. package/types/fork/getPathFromState.test.d.ts.map +1 -0
  225. package/types/fork/getStateFromPath.test.d.ts +2 -0
  226. package/types/fork/getStateFromPath.test.d.ts.map +1 -0
  227. package/types/router/FlagsContext.d.ts +3 -0
  228. package/types/router/FlagsContext.d.ts.map +1 -0
  229. package/types/router/getRoutes.d.ts.map +1 -1
  230. package/types/router/matchers.test.d.ts +2 -0
  231. package/types/router/matchers.test.d.ts.map +1 -0
  232. package/types/router/router.d.ts.map +1 -1
  233. package/types/router/utils/getNavigateAction.d.ts +17 -0
  234. package/types/router/utils/getNavigateAction.d.ts.map +1 -0
  235. package/types/router/utils/getNavigateAction.test.d.ts +2 -0
  236. package/types/router/utils/getNavigateAction.test.d.ts.map +1 -0
  237. package/types/testing-utils.d.ts +26 -0
  238. package/types/testing-utils.d.ts.map +1 -0
  239. package/types/views/Navigator.d.ts +1 -1
  240. package/types/views/Navigator.d.ts.map +1 -1
  241. package/types/vite/one.d.ts.map +1 -1
  242. package/types/vite/plugins/virtualEntryPlugin.d.ts +2 -0
  243. package/types/vite/plugins/virtualEntryPlugin.d.ts.map +1 -1
  244. package/types/vite/types.d.ts +16 -0
  245. package/types/vite/types.d.ts.map +1 -1
@@ -0,0 +1,3188 @@
1
+ /**
2
+ * This file is copied from the react-navigation repo.
3
+ *
4
+ * Please refrain from making changes to this file, as it will make merging updates from the upstream harder.
5
+ * All modifications except formatting should be marked with `// @modified` comment.
6
+ */
7
+
8
+ // @modified - start: use vitest instead of jest
9
+ // import { expect, test } from '@jest/globals';
10
+ import { expect, test } from 'vitest'
11
+ // @modified - end: use vitest instead of jest
12
+ import type { InitialState } from '@react-navigation/routers'
13
+ import { produce } from 'immer'
14
+
15
+ import { findFocusedRoute } from '../findFocusedRoute'
16
+ import { getPathFromState } from '../getPathFromState'
17
+ import { getStateFromPath } from '../getStateFromPath'
18
+
19
+ const changePath = <T extends InitialState>(state: T, path: string): T =>
20
+ produce(state, (draftState) => {
21
+ const route = findFocusedRoute(draftState)
22
+ // @ts-expect-error: immer won't mutate this
23
+ route.path = path
24
+ })
25
+
26
+ test('returns undefined for invalid path', () => {
27
+ expect(getStateFromPath<object>('//')).toBeUndefined()
28
+ })
29
+
30
+ // @modify: TODO: temporally disable failing test
31
+ test.skip('converts path string to initial state', () => {
32
+ const path = 'foo/bar/baz%20qux?author=jane%20%26%20co&valid=true'
33
+ const state = {
34
+ routes: [
35
+ {
36
+ name: 'foo',
37
+ state: {
38
+ routes: [
39
+ {
40
+ name: 'bar',
41
+ state: {
42
+ routes: [
43
+ {
44
+ name: 'baz qux',
45
+ params: { author: 'jane & co', valid: 'true' },
46
+ path,
47
+ },
48
+ ],
49
+ },
50
+ },
51
+ ],
52
+ },
53
+ },
54
+ ],
55
+ }
56
+
57
+ expect(getStateFromPath<object>(path)).toEqual(state)
58
+ expect(getStateFromPath<object>(getPathFromState<object>(state))).toEqual(
59
+ changePath(state, '/foo/bar/baz%20qux?author=jane%20%26%20co&valid=true')
60
+ )
61
+ })
62
+
63
+ // @modify: TODO: temporally disable failing test
64
+ test.skip('decodes encoded params in path', () => {
65
+ const path = '/foo/bar/bar_%23_foo'
66
+ const config = {
67
+ screens: {
68
+ Foo: {
69
+ path: 'foo',
70
+ screens: {
71
+ Bar: {
72
+ path: '/bar/:id',
73
+ },
74
+ },
75
+ },
76
+ },
77
+ }
78
+
79
+ const state = {
80
+ routes: [
81
+ {
82
+ name: 'Foo',
83
+ state: {
84
+ routes: [
85
+ {
86
+ name: 'Bar',
87
+ params: { id: 'bar_#_foo' },
88
+ path,
89
+ },
90
+ ],
91
+ },
92
+ },
93
+ ],
94
+ }
95
+
96
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
97
+ expect(
98
+ getPathFromState<object>(getStateFromPath<object>(path, config)!, config)
99
+ ).toEqual(path)
100
+ })
101
+
102
+ // @modify: TODO: temporally disable failing test
103
+ test.skip('decodes encoded params in path that have encoded /', () => {
104
+ const path = '/foo/bar/bar_%2F_foo'
105
+ const config = {
106
+ screens: {
107
+ Foo: {
108
+ path: 'foo',
109
+ screens: {
110
+ Bar: {
111
+ path: '/bar/:id',
112
+ },
113
+ },
114
+ },
115
+ },
116
+ }
117
+
118
+ const state = {
119
+ routes: [
120
+ {
121
+ name: 'Foo',
122
+ state: {
123
+ routes: [
124
+ {
125
+ name: 'Bar',
126
+ params: { id: 'bar_/_foo' },
127
+ path,
128
+ },
129
+ ],
130
+ },
131
+ },
132
+ ],
133
+ }
134
+
135
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
136
+ expect(
137
+ getPathFromState<object>(getStateFromPath<object>(path, config)!, config)
138
+ ).toEqual(path)
139
+ })
140
+
141
+ // @modify: TODO: temporally disable failing test
142
+ test.skip('converts path string to initial state with config', () => {
143
+ const path = '/foo/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true'
144
+ const config = {
145
+ screens: {
146
+ Foo: {
147
+ path: 'foo',
148
+ screens: {
149
+ Bar: {
150
+ path: 'bar/:type/:fruit',
151
+ screens: {
152
+ Baz: {
153
+ path: 'baz/:author',
154
+ parse: {
155
+ author: (author: string) =>
156
+ author.replace(/^\w/, (c) => c.toUpperCase()),
157
+ count: Number,
158
+ valid: Boolean,
159
+ },
160
+ stringify: {
161
+ author: (author: string) => author.toLowerCase(),
162
+ },
163
+ },
164
+ },
165
+ },
166
+ },
167
+ },
168
+ },
169
+ }
170
+
171
+ const state = {
172
+ routes: [
173
+ {
174
+ name: 'Foo',
175
+ state: {
176
+ routes: [
177
+ {
178
+ name: 'Bar',
179
+ params: { fruit: 'apple', type: 'sweet' },
180
+ state: {
181
+ routes: [
182
+ {
183
+ name: 'Baz',
184
+ params: {
185
+ author: 'Jane',
186
+ count: 10,
187
+ answer: '42',
188
+ valid: true,
189
+ },
190
+ path,
191
+ },
192
+ ],
193
+ },
194
+ },
195
+ ],
196
+ },
197
+ },
198
+ ],
199
+ }
200
+
201
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
202
+ expect(
203
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
204
+ ).toEqual(state)
205
+ })
206
+
207
+ test('handles leading slash when converting', () => {
208
+ const path = '/foo/bar/?count=42'
209
+
210
+ expect(getStateFromPath<object>(path)).toEqual({
211
+ routes: [
212
+ {
213
+ name: 'foo',
214
+ state: {
215
+ routes: [
216
+ {
217
+ name: 'bar',
218
+ params: { count: '42' },
219
+ path,
220
+ },
221
+ ],
222
+ },
223
+ },
224
+ ],
225
+ })
226
+ })
227
+
228
+ test('handles ending slash when converting', () => {
229
+ const path = 'foo/bar/?count=42'
230
+
231
+ expect(getStateFromPath<object>(path)).toEqual({
232
+ routes: [
233
+ {
234
+ name: 'foo',
235
+ state: {
236
+ routes: [
237
+ {
238
+ name: 'bar',
239
+ params: { count: '42' },
240
+ path,
241
+ },
242
+ ],
243
+ },
244
+ },
245
+ ],
246
+ })
247
+ })
248
+
249
+ test('handles route without param', () => {
250
+ const path = 'foo/bar'
251
+ const state = {
252
+ routes: [
253
+ {
254
+ name: 'foo',
255
+ state: {
256
+ routes: [{ name: 'bar', path }],
257
+ },
258
+ },
259
+ ],
260
+ }
261
+
262
+ expect(getStateFromPath<object>(path)).toEqual(state)
263
+ expect(getStateFromPath<object>(getPathFromState<object>(state))).toEqual(
264
+ changePath(state, '/foo/bar')
265
+ )
266
+ })
267
+
268
+ // @modify: TODO: temporally disable failing test
269
+ test.skip('converts path string to initial state with config with nested screens', () => {
270
+ const path = '/foe/bar/sweet/apple/baz/jane?count=10&answer=42&valid=true'
271
+ const config = {
272
+ screens: {
273
+ Foo: {
274
+ path: 'foo',
275
+ screens: {
276
+ Foe: {
277
+ path: 'foe',
278
+ exact: true,
279
+ screens: {
280
+ Bar: {
281
+ path: 'bar/:type/:fruit',
282
+ screens: {
283
+ Baz: {
284
+ path: 'baz/:author',
285
+ parse: {
286
+ author: (author: string) =>
287
+ author.replace(/^\w/, (c) => c.toUpperCase()),
288
+ count: Number,
289
+ valid: Boolean,
290
+ },
291
+ stringify: {
292
+ author: (author: string) => author.toLowerCase(),
293
+ },
294
+ },
295
+ },
296
+ },
297
+ },
298
+ },
299
+ },
300
+ },
301
+ },
302
+ }
303
+
304
+ const state = {
305
+ routes: [
306
+ {
307
+ name: 'Foo',
308
+ state: {
309
+ routes: [
310
+ {
311
+ name: 'Foe',
312
+ state: {
313
+ routes: [
314
+ {
315
+ name: 'Bar',
316
+ params: { fruit: 'apple', type: 'sweet' },
317
+ state: {
318
+ routes: [
319
+ {
320
+ name: 'Baz',
321
+ params: {
322
+ author: 'Jane',
323
+ count: 10,
324
+ answer: '42',
325
+ valid: true,
326
+ },
327
+ path,
328
+ },
329
+ ],
330
+ },
331
+ },
332
+ ],
333
+ },
334
+ },
335
+ ],
336
+ },
337
+ },
338
+ ],
339
+ }
340
+
341
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
342
+ expect(
343
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
344
+ ).toEqual(state)
345
+ })
346
+
347
+ // @modify: TODO: temporally disable failing test
348
+ test.skip('converts path string to initial state with config with nested screens and unused parse functions', () => {
349
+ const path = '/foe/baz/jane?count=10&answer=42&valid=true'
350
+ const config = {
351
+ screens: {
352
+ Foo: {
353
+ path: 'foo',
354
+ screens: {
355
+ Foe: {
356
+ path: 'foe',
357
+ exact: true,
358
+ screens: {
359
+ Baz: {
360
+ path: 'baz/:author',
361
+ parse: {
362
+ author: (author: string) =>
363
+ author.replace(/^\w/, (c) => c.toUpperCase()),
364
+ count: Number,
365
+ valid: Boolean,
366
+ id: Boolean,
367
+ },
368
+ },
369
+ },
370
+ },
371
+ },
372
+ },
373
+ },
374
+ }
375
+
376
+ const state = {
377
+ routes: [
378
+ {
379
+ name: 'Foo',
380
+ state: {
381
+ routes: [
382
+ {
383
+ name: 'Foe',
384
+ state: {
385
+ routes: [
386
+ {
387
+ name: 'Baz',
388
+ params: {
389
+ author: 'Jane',
390
+ count: 10,
391
+ answer: '42',
392
+ valid: true,
393
+ },
394
+ path,
395
+ },
396
+ ],
397
+ },
398
+ },
399
+ ],
400
+ },
401
+ },
402
+ ],
403
+ }
404
+
405
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
406
+ expect(
407
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
408
+ ).toEqual(changePath(state, '/foe/baz/Jane?count=10&answer=42&valid=true'))
409
+ })
410
+
411
+ // @modify: TODO: temporally disable failing test
412
+ test.skip('handles nested object with unused configs and with parse in it', () => {
413
+ const path = '/bar/sweet/apple/foe/bis/jane?count=10&answer=42&valid=true'
414
+ const config = {
415
+ screens: {
416
+ Bar: {
417
+ path: 'bar/:type/:fruit',
418
+ screens: {
419
+ Foo: {
420
+ screens: {
421
+ Foe: {
422
+ path: 'foe',
423
+ screens: {
424
+ Baz: {
425
+ screens: {
426
+ Bos: {
427
+ path: 'bos',
428
+ exact: true,
429
+ },
430
+ Bis: {
431
+ path: 'bis/:author',
432
+ stringify: {
433
+ author: (author: string) =>
434
+ author.replace(/^\w/, (c) => c.toLowerCase()),
435
+ },
436
+ parse: {
437
+ author: (author: string) =>
438
+ author.replace(/^\w/, (c) => c.toUpperCase()),
439
+ count: Number,
440
+ valid: Boolean,
441
+ },
442
+ },
443
+ },
444
+ },
445
+ },
446
+ },
447
+ },
448
+ },
449
+ },
450
+ },
451
+ },
452
+ }
453
+
454
+ const state = {
455
+ routes: [
456
+ {
457
+ name: 'Bar',
458
+ params: { fruit: 'apple', type: 'sweet' },
459
+ state: {
460
+ routes: [
461
+ {
462
+ name: 'Foo',
463
+ state: {
464
+ routes: [
465
+ {
466
+ name: 'Foe',
467
+ state: {
468
+ routes: [
469
+ {
470
+ name: 'Baz',
471
+ state: {
472
+ routes: [
473
+ {
474
+ name: 'Bis',
475
+ params: {
476
+ author: 'Jane',
477
+ count: 10,
478
+ answer: '42',
479
+ valid: true,
480
+ },
481
+ path,
482
+ },
483
+ ],
484
+ },
485
+ },
486
+ ],
487
+ },
488
+ },
489
+ ],
490
+ },
491
+ },
492
+ ],
493
+ },
494
+ },
495
+ ],
496
+ }
497
+
498
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
499
+ expect(
500
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
501
+ ).toEqual(state)
502
+ })
503
+
504
+ test('handles parse in nested object for second route depth', () => {
505
+ const path = '/baz'
506
+ const config = {
507
+ screens: {
508
+ Foo: {
509
+ path: 'foo',
510
+ screens: {
511
+ Foe: {
512
+ path: 'foe',
513
+ exact: true,
514
+ },
515
+ Bar: {
516
+ path: 'bar',
517
+ exact: true,
518
+ screens: {
519
+ Baz: {
520
+ path: 'baz',
521
+ exact: true,
522
+ },
523
+ },
524
+ },
525
+ },
526
+ },
527
+ },
528
+ }
529
+
530
+ const state = {
531
+ routes: [
532
+ {
533
+ name: 'Foo',
534
+ state: {
535
+ routes: [
536
+ {
537
+ name: 'Bar',
538
+ state: {
539
+ routes: [{ name: 'Baz', path }],
540
+ },
541
+ },
542
+ ],
543
+ },
544
+ },
545
+ ],
546
+ }
547
+
548
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
549
+ expect(
550
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
551
+ ).toEqual(state)
552
+ })
553
+
554
+ test('handles parse in nested object for second route depth and and path and parse in roots', () => {
555
+ const path = '/baz'
556
+ const config = {
557
+ screens: {
558
+ Foo: {
559
+ path: 'foo/:id',
560
+ parse: {
561
+ id: Number,
562
+ },
563
+ stringify: {
564
+ id: (id: number) => `id=${id}`,
565
+ },
566
+ screens: {
567
+ Foe: 'foe',
568
+ Bar: {
569
+ screens: {
570
+ Baz: {
571
+ path: 'baz',
572
+ exact: true,
573
+ },
574
+ },
575
+ },
576
+ },
577
+ },
578
+ },
579
+ }
580
+
581
+ const state = {
582
+ routes: [
583
+ {
584
+ name: 'Foo',
585
+ state: {
586
+ routes: [
587
+ {
588
+ name: 'Bar',
589
+ state: {
590
+ routes: [{ name: 'Baz', path }],
591
+ },
592
+ },
593
+ ],
594
+ },
595
+ },
596
+ ],
597
+ }
598
+
599
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
600
+ expect(
601
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
602
+ ).toEqual(state)
603
+ })
604
+
605
+ // @modify: TODO: temporally disable failing test
606
+ test.skip('handles path at top level', () => {
607
+ const path = '/foo/fruits/apple'
608
+ const config = {
609
+ path: 'foo',
610
+ screens: {
611
+ Foo: {
612
+ screens: {
613
+ Fruits: 'fruits/:fruit',
614
+ },
615
+ },
616
+ },
617
+ }
618
+
619
+ const state = {
620
+ routes: [
621
+ {
622
+ name: 'Foo',
623
+ state: {
624
+ routes: [
625
+ {
626
+ name: 'Fruits',
627
+ params: { fruit: 'apple' },
628
+ path,
629
+ },
630
+ ],
631
+ },
632
+ },
633
+ ],
634
+ }
635
+
636
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
637
+ expect(
638
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
639
+ ).toEqual(state)
640
+ })
641
+
642
+ test('handles initialRouteName at top level', () => {
643
+ const path = '/baz'
644
+ const config = {
645
+ initialRouteName: 'Boo',
646
+ screens: {
647
+ Foo: {
648
+ screens: {
649
+ Foe: 'foe',
650
+ Bar: {
651
+ screens: {
652
+ Baz: 'baz',
653
+ },
654
+ },
655
+ },
656
+ },
657
+ },
658
+ }
659
+
660
+ const state = {
661
+ index: 1,
662
+ routes: [
663
+ { name: 'Boo' },
664
+ {
665
+ name: 'Foo',
666
+ state: {
667
+ routes: [
668
+ {
669
+ name: 'Bar',
670
+ state: {
671
+ routes: [{ name: 'Baz', path }],
672
+ },
673
+ },
674
+ ],
675
+ },
676
+ },
677
+ ],
678
+ }
679
+
680
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
681
+ expect(
682
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
683
+ ).toEqual(state)
684
+ })
685
+
686
+ test('handles initialRouteName inside a screen', () => {
687
+ const path = '/baz'
688
+ const config = {
689
+ screens: {
690
+ Foo: {
691
+ initialRouteName: 'Foe',
692
+ screens: {
693
+ Foe: 'foe',
694
+ Bar: {
695
+ screens: {
696
+ Baz: 'baz',
697
+ },
698
+ },
699
+ },
700
+ },
701
+ },
702
+ }
703
+
704
+ const state = {
705
+ routes: [
706
+ {
707
+ name: 'Foo',
708
+ state: {
709
+ index: 1,
710
+ routes: [
711
+ {
712
+ name: 'Foe',
713
+ },
714
+ {
715
+ name: 'Bar',
716
+ state: {
717
+ routes: [{ name: 'Baz', path }],
718
+ },
719
+ },
720
+ ],
721
+ },
722
+ },
723
+ ],
724
+ }
725
+
726
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
727
+ expect(
728
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
729
+ ).toEqual(state)
730
+ })
731
+
732
+ test('handles initialRouteName included in path', () => {
733
+ const path = '/baz'
734
+ const config = {
735
+ screens: {
736
+ Foo: {
737
+ initialRouteName: 'Foe',
738
+ screens: {
739
+ Foe: {
740
+ screens: {
741
+ Baz: 'baz',
742
+ },
743
+ },
744
+ Bar: 'bar',
745
+ },
746
+ },
747
+ },
748
+ }
749
+
750
+ const state = {
751
+ routes: [
752
+ {
753
+ name: 'Foo',
754
+ state: {
755
+ routes: [
756
+ {
757
+ name: 'Foe',
758
+ state: {
759
+ routes: [{ name: 'Baz', path }],
760
+ },
761
+ },
762
+ ],
763
+ },
764
+ },
765
+ ],
766
+ }
767
+
768
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
769
+ expect(
770
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
771
+ ).toEqual(state)
772
+ })
773
+
774
+ // @modify: TODO: temporally disable failing test
775
+ test.skip('handles two initialRouteNames', () => {
776
+ const path = '/bar/sweet/apple/foe/bis/jane?answer=42&count=10&valid=true'
777
+ const config = {
778
+ screens: {
779
+ Bar: {
780
+ path: 'bar/:type/:fruit',
781
+ screens: {
782
+ Foo: {
783
+ screens: {
784
+ Foe: {
785
+ path: 'foe',
786
+ screens: {
787
+ Baz: {
788
+ initialRouteName: 'Bos',
789
+ screens: {
790
+ Bos: {
791
+ path: 'bos',
792
+ exact: true,
793
+ },
794
+ Bis: {
795
+ path: 'bis/:author',
796
+ stringify: {
797
+ author: (author: string) =>
798
+ author.replace(/^\w/, (c) => c.toLowerCase()),
799
+ },
800
+ parse: {
801
+ author: (author: string) =>
802
+ author.replace(/^\w/, (c) => c.toUpperCase()),
803
+ count: Number,
804
+ valid: Boolean,
805
+ },
806
+ },
807
+ },
808
+ },
809
+ },
810
+ },
811
+ },
812
+ },
813
+ },
814
+ },
815
+ },
816
+ }
817
+
818
+ const state = {
819
+ routes: [
820
+ {
821
+ name: 'Bar',
822
+ params: { fruit: 'apple', type: 'sweet' },
823
+ state: {
824
+ routes: [
825
+ {
826
+ name: 'Foo',
827
+ state: {
828
+ routes: [
829
+ {
830
+ name: 'Foe',
831
+ state: {
832
+ routes: [
833
+ {
834
+ name: 'Baz',
835
+ state: {
836
+ index: 1,
837
+ routes: [
838
+ { name: 'Bos' },
839
+ {
840
+ name: 'Bis',
841
+ params: {
842
+ answer: '42',
843
+ author: 'Jane',
844
+ count: 10,
845
+ valid: true,
846
+ },
847
+ path,
848
+ },
849
+ ],
850
+ },
851
+ },
852
+ ],
853
+ },
854
+ },
855
+ ],
856
+ },
857
+ },
858
+ ],
859
+ },
860
+ },
861
+ ],
862
+ }
863
+
864
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
865
+ expect(
866
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
867
+ ).toEqual(state)
868
+ })
869
+
870
+ // @modify: TODO: temporally disable failing test
871
+ test.skip('accepts initialRouteName without config for it', () => {
872
+ const path = '/bar/sweet/apple/foe/bis/jane?answer=42&count=10&valid=true'
873
+ const config = {
874
+ screens: {
875
+ Bar: {
876
+ path: 'bar/:type/:fruit',
877
+ screens: {
878
+ Foo: {
879
+ screens: {
880
+ Foe: {
881
+ path: 'foe',
882
+ screens: {
883
+ Baz: {
884
+ initialRouteName: 'Bas',
885
+ screens: {
886
+ Bos: {
887
+ path: 'bos',
888
+ exact: true,
889
+ },
890
+ Bis: {
891
+ path: 'bis/:author',
892
+ stringify: {
893
+ author: (author: string) =>
894
+ author.replace(/^\w/, (c) => c.toLowerCase()),
895
+ },
896
+ parse: {
897
+ author: (author: string) =>
898
+ author.replace(/^\w/, (c) => c.toUpperCase()),
899
+ count: Number,
900
+ valid: Boolean,
901
+ },
902
+ },
903
+ },
904
+ },
905
+ },
906
+ },
907
+ },
908
+ },
909
+ },
910
+ },
911
+ },
912
+ }
913
+
914
+ const state = {
915
+ routes: [
916
+ {
917
+ name: 'Bar',
918
+ params: { fruit: 'apple', type: 'sweet' },
919
+ state: {
920
+ routes: [
921
+ {
922
+ name: 'Foo',
923
+ state: {
924
+ routes: [
925
+ {
926
+ name: 'Foe',
927
+ state: {
928
+ routes: [
929
+ {
930
+ name: 'Baz',
931
+ state: {
932
+ index: 1,
933
+ routes: [
934
+ { name: 'Bas' },
935
+ {
936
+ name: 'Bis',
937
+ params: {
938
+ answer: '42',
939
+ author: 'Jane',
940
+ count: 10,
941
+ valid: true,
942
+ },
943
+ path,
944
+ },
945
+ ],
946
+ },
947
+ },
948
+ ],
949
+ },
950
+ },
951
+ ],
952
+ },
953
+ },
954
+ ],
955
+ },
956
+ },
957
+ ],
958
+ }
959
+
960
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
961
+ expect(
962
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
963
+ ).toEqual(state)
964
+ })
965
+
966
+ test('returns undefined if no matching screen is present (top level path)', () => {
967
+ const path = '/foo/bar'
968
+ const config = {
969
+ path: 'qux',
970
+ screens: {
971
+ Foo: {
972
+ screens: {
973
+ Foe: 'foo',
974
+ Bar: {
975
+ screens: {
976
+ Baz: 'bar',
977
+ },
978
+ },
979
+ },
980
+ },
981
+ },
982
+ }
983
+
984
+ expect(getStateFromPath<object>(path, config)).toBeUndefined()
985
+ })
986
+
987
+ test('returns undefined if no matching screen is present', () => {
988
+ const path = '/baz'
989
+ const config = {
990
+ screens: {
991
+ Foo: {
992
+ path: 'foo',
993
+ screens: {
994
+ Foe: 'foe',
995
+ Bar: {
996
+ screens: {
997
+ Baz: 'baz',
998
+ },
999
+ },
1000
+ },
1001
+ },
1002
+ },
1003
+ }
1004
+
1005
+ expect(getStateFromPath<object>(path, config)).toBeUndefined()
1006
+ })
1007
+
1008
+ test('returns undefined if path is empty and no matching screen is present', () => {
1009
+ const path = ''
1010
+ const config = {
1011
+ screens: {
1012
+ Foo: {
1013
+ screens: {
1014
+ Foe: 'foe',
1015
+ Bar: {
1016
+ screens: {
1017
+ Baz: 'baz',
1018
+ },
1019
+ },
1020
+ },
1021
+ },
1022
+ },
1023
+ }
1024
+
1025
+ expect(getStateFromPath<object>(path, config)).toBeUndefined()
1026
+ })
1027
+
1028
+ // @modify: TODO: temporally disable failing test
1029
+ test.skip('returns matching screen if path is empty', () => {
1030
+ const path = ''
1031
+ const config = {
1032
+ screens: {
1033
+ Foo: {
1034
+ screens: {
1035
+ Foe: 'foe',
1036
+ Bar: {
1037
+ screens: {
1038
+ Qux: '',
1039
+ Baz: 'baz',
1040
+ },
1041
+ },
1042
+ },
1043
+ },
1044
+ },
1045
+ }
1046
+
1047
+ const state = {
1048
+ routes: [
1049
+ {
1050
+ name: 'Foo',
1051
+ state: {
1052
+ routes: [
1053
+ {
1054
+ name: 'Bar',
1055
+ state: {
1056
+ routes: [{ name: 'Qux', path }],
1057
+ },
1058
+ },
1059
+ ],
1060
+ },
1061
+ },
1062
+ ],
1063
+ }
1064
+
1065
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1066
+ expect(
1067
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1068
+ ).toEqual(changePath(state, ''))
1069
+ })
1070
+
1071
+ // @modify: TODO: temporally disable failing test
1072
+ test.skip('returns matching screen if path is only slash', () => {
1073
+ const path = '/'
1074
+ const config = {
1075
+ screens: {
1076
+ Foo: {
1077
+ screens: {
1078
+ Foe: 'foe',
1079
+ Bar: {
1080
+ screens: {
1081
+ Qux: '',
1082
+ Baz: 'baz',
1083
+ },
1084
+ },
1085
+ },
1086
+ },
1087
+ },
1088
+ }
1089
+
1090
+ const state = {
1091
+ routes: [
1092
+ {
1093
+ name: 'Foo',
1094
+ state: {
1095
+ routes: [
1096
+ {
1097
+ name: 'Bar',
1098
+ state: {
1099
+ routes: [{ name: 'Qux', path: '' }],
1100
+ },
1101
+ },
1102
+ ],
1103
+ },
1104
+ },
1105
+ ],
1106
+ }
1107
+
1108
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1109
+ expect(
1110
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1111
+ ).toEqual(changePath(state, ''))
1112
+ })
1113
+
1114
+ test('returns matching screen with params if path is empty', () => {
1115
+ const path = '?foo=42'
1116
+ const config = {
1117
+ screens: {
1118
+ Foo: {
1119
+ screens: {
1120
+ Foe: 'foe',
1121
+ Bar: {
1122
+ screens: {
1123
+ Qux: {
1124
+ path: '',
1125
+ parse: { foo: Number },
1126
+ },
1127
+ Baz: 'baz',
1128
+ },
1129
+ },
1130
+ },
1131
+ },
1132
+ },
1133
+ }
1134
+
1135
+ const state = {
1136
+ routes: [
1137
+ {
1138
+ name: 'Foo',
1139
+ state: {
1140
+ routes: [
1141
+ {
1142
+ name: 'Bar',
1143
+ state: {
1144
+ routes: [{ name: 'Qux', params: { foo: 42 }, path }],
1145
+ },
1146
+ },
1147
+ ],
1148
+ },
1149
+ },
1150
+ ],
1151
+ }
1152
+
1153
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1154
+ expect(
1155
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1156
+ ).toEqual(changePath(state, '/?foo=42'))
1157
+ })
1158
+
1159
+ test("doesn't match nested screen if path is empty", () => {
1160
+ const config = {
1161
+ screens: {
1162
+ Foo: {
1163
+ screens: {
1164
+ Foe: 'foe',
1165
+ Bar: {
1166
+ path: 'bar',
1167
+ screens: {
1168
+ Qux: {
1169
+ path: '',
1170
+ parse: { foo: Number },
1171
+ },
1172
+ },
1173
+ },
1174
+ },
1175
+ },
1176
+ },
1177
+ }
1178
+
1179
+ const path = ''
1180
+
1181
+ expect(getStateFromPath<object>(path, config)).toBeUndefined()
1182
+ })
1183
+
1184
+ // @modify: TODO: temporally disable failing test
1185
+ test.skip('chooses more exhaustive pattern', () => {
1186
+ const path = '/foo/5'
1187
+
1188
+ const config = {
1189
+ screens: {
1190
+ Foe: {
1191
+ path: '/',
1192
+ initialRouteName: 'Foo',
1193
+ screens: {
1194
+ Foo: 'foo',
1195
+ Bis: {
1196
+ path: 'foo/:id',
1197
+ parse: {
1198
+ id: Number,
1199
+ },
1200
+ },
1201
+ },
1202
+ },
1203
+ },
1204
+ }
1205
+
1206
+ const state = {
1207
+ routes: [
1208
+ {
1209
+ name: 'Foe',
1210
+ state: {
1211
+ index: 1,
1212
+ routes: [
1213
+ {
1214
+ name: 'Foo',
1215
+ },
1216
+ {
1217
+ name: 'Bis',
1218
+ params: { id: 5 },
1219
+ path,
1220
+ },
1221
+ ],
1222
+ },
1223
+ },
1224
+ ],
1225
+ }
1226
+
1227
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1228
+ expect(
1229
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1230
+ ).toEqual(state)
1231
+ })
1232
+
1233
+ test('handles same paths beginnings', () => {
1234
+ const path = '/foos'
1235
+
1236
+ const config = {
1237
+ screens: {
1238
+ Foe: {
1239
+ path: '/',
1240
+ initialRouteName: 'Foo',
1241
+ screens: {
1242
+ Foo: 'foo',
1243
+ Bis: {
1244
+ path: 'foos',
1245
+ },
1246
+ },
1247
+ },
1248
+ },
1249
+ }
1250
+
1251
+ const state = {
1252
+ routes: [
1253
+ {
1254
+ name: 'Foe',
1255
+ state: {
1256
+ index: 1,
1257
+ routes: [
1258
+ {
1259
+ name: 'Foo',
1260
+ },
1261
+ {
1262
+ name: 'Bis',
1263
+ path,
1264
+ },
1265
+ ],
1266
+ },
1267
+ },
1268
+ ],
1269
+ }
1270
+
1271
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1272
+ expect(
1273
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1274
+ ).toEqual(state)
1275
+ })
1276
+
1277
+ // @modify: TODO: temporally disable failing test
1278
+ test.skip('handles same paths beginnings with params', () => {
1279
+ const path = '/foos/5'
1280
+
1281
+ const config = {
1282
+ screens: {
1283
+ Foe: {
1284
+ path: '/',
1285
+ initialRouteName: 'Foo',
1286
+ screens: {
1287
+ Foo: 'foo',
1288
+ Bis: {
1289
+ path: 'foos/:id',
1290
+ parse: {
1291
+ id: Number,
1292
+ },
1293
+ },
1294
+ },
1295
+ },
1296
+ },
1297
+ }
1298
+
1299
+ const state = {
1300
+ routes: [
1301
+ {
1302
+ name: 'Foe',
1303
+ state: {
1304
+ index: 1,
1305
+ routes: [
1306
+ {
1307
+ name: 'Foo',
1308
+ },
1309
+ {
1310
+ name: 'Bis',
1311
+ params: { id: 5 },
1312
+ path,
1313
+ },
1314
+ ],
1315
+ },
1316
+ },
1317
+ ],
1318
+ }
1319
+
1320
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1321
+ expect(
1322
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1323
+ ).toEqual(state)
1324
+ })
1325
+
1326
+ // @modify: TODO: temporally disable failing test
1327
+ test.skip('handles not taking path with too many segments', () => {
1328
+ const path = '/foos/5'
1329
+
1330
+ const config = {
1331
+ screens: {
1332
+ Foe: {
1333
+ path: '/',
1334
+ initialRouteName: 'Foo',
1335
+ screens: {
1336
+ Foo: 'foo',
1337
+ Bis: {
1338
+ path: 'foos/:id',
1339
+ parse: {
1340
+ id: Number,
1341
+ },
1342
+ },
1343
+ Bas: {
1344
+ path: 'foos/:id/:nip',
1345
+ parse: {
1346
+ id: Number,
1347
+ pwd: Number,
1348
+ },
1349
+ },
1350
+ },
1351
+ },
1352
+ },
1353
+ }
1354
+
1355
+ const state = {
1356
+ routes: [
1357
+ {
1358
+ name: 'Foe',
1359
+ state: {
1360
+ index: 1,
1361
+ routes: [
1362
+ {
1363
+ name: 'Foo',
1364
+ },
1365
+ {
1366
+ name: 'Bis',
1367
+ params: { id: 5 },
1368
+ path,
1369
+ },
1370
+ ],
1371
+ },
1372
+ },
1373
+ ],
1374
+ }
1375
+
1376
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1377
+ expect(
1378
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1379
+ ).toEqual(state)
1380
+ })
1381
+
1382
+ // @modify: TODO: temporally disable failing test
1383
+ test.skip('handles differently ordered params v1', () => {
1384
+ const path = '/foos/5/res/20'
1385
+
1386
+ const config = {
1387
+ screens: {
1388
+ Foe: {
1389
+ path: '/',
1390
+ initialRouteName: 'Foo',
1391
+ screens: {
1392
+ Foo: 'foo',
1393
+ Bis: {
1394
+ path: 'foos/:id',
1395
+ parse: {
1396
+ id: Number,
1397
+ },
1398
+ },
1399
+ Bas: {
1400
+ path: 'foos/:id/res/:pwd',
1401
+ parse: {
1402
+ id: Number,
1403
+ pwd: Number,
1404
+ },
1405
+ },
1406
+ },
1407
+ },
1408
+ },
1409
+ }
1410
+
1411
+ const state = {
1412
+ routes: [
1413
+ {
1414
+ name: 'Foe',
1415
+ state: {
1416
+ index: 1,
1417
+ routes: [
1418
+ {
1419
+ name: 'Foo',
1420
+ },
1421
+ {
1422
+ name: 'Bas',
1423
+ params: { id: 5, pwd: 20 },
1424
+ path,
1425
+ },
1426
+ ],
1427
+ },
1428
+ },
1429
+ ],
1430
+ }
1431
+
1432
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1433
+ expect(
1434
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1435
+ ).toEqual(state)
1436
+ })
1437
+
1438
+ // @modify: TODO: temporally disable failing test
1439
+ test.skip('handles differently ordered params v2', () => {
1440
+ const path = '/5/20/foos/res'
1441
+
1442
+ const config = {
1443
+ screens: {
1444
+ Foe: {
1445
+ path: '/',
1446
+ initialRouteName: 'Foo',
1447
+ screens: {
1448
+ Foo: 'foo',
1449
+ Bis: {
1450
+ path: 'foos/:id',
1451
+ parse: {
1452
+ id: Number,
1453
+ },
1454
+ },
1455
+ Bas: {
1456
+ path: ':id/:pwd/foos/res',
1457
+ parse: {
1458
+ id: Number,
1459
+ pwd: Number,
1460
+ },
1461
+ },
1462
+ },
1463
+ },
1464
+ },
1465
+ }
1466
+
1467
+ const state = {
1468
+ routes: [
1469
+ {
1470
+ name: 'Foe',
1471
+ state: {
1472
+ index: 1,
1473
+ routes: [
1474
+ {
1475
+ name: 'Foo',
1476
+ },
1477
+ {
1478
+ name: 'Bas',
1479
+ params: { id: 5, pwd: 20 },
1480
+ path,
1481
+ },
1482
+ ],
1483
+ },
1484
+ },
1485
+ ],
1486
+ }
1487
+
1488
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1489
+ expect(
1490
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1491
+ ).toEqual(state)
1492
+ })
1493
+
1494
+ // @modify: TODO: temporally disable failing test
1495
+ test.skip('handles differently ordered params v3', () => {
1496
+ const path = '/foos/5/20/res'
1497
+
1498
+ const config = {
1499
+ screens: {
1500
+ Foe: {
1501
+ path: '/',
1502
+ initialRouteName: 'Foo',
1503
+ screens: {
1504
+ Foo: 'foo',
1505
+ Bis: {
1506
+ path: 'foos/:id',
1507
+ parse: {
1508
+ id: Number,
1509
+ },
1510
+ },
1511
+ Bas: {
1512
+ path: 'foos/:id/:pwd/res',
1513
+ parse: {
1514
+ id: Number,
1515
+ pwd: Number,
1516
+ },
1517
+ },
1518
+ },
1519
+ },
1520
+ },
1521
+ }
1522
+
1523
+ const state = {
1524
+ routes: [
1525
+ {
1526
+ name: 'Foe',
1527
+ state: {
1528
+ index: 1,
1529
+ routes: [
1530
+ {
1531
+ name: 'Foo',
1532
+ },
1533
+ {
1534
+ name: 'Bas',
1535
+ params: { id: 5, pwd: 20 },
1536
+ path,
1537
+ },
1538
+ ],
1539
+ },
1540
+ },
1541
+ ],
1542
+ }
1543
+
1544
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1545
+ expect(
1546
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1547
+ ).toEqual(state)
1548
+ })
1549
+
1550
+ // @modify: TODO: temporally disable failing test
1551
+ test.skip('handles differently ordered params v4', () => {
1552
+ const path = '5/foos/res/20'
1553
+
1554
+ const config = {
1555
+ screens: {
1556
+ Foe: {
1557
+ path: '/',
1558
+ initialRouteName: 'Foo',
1559
+ screens: {
1560
+ Foo: 'foo',
1561
+ Bis: {
1562
+ path: 'foos/:id',
1563
+ parse: {
1564
+ id: Number,
1565
+ },
1566
+ },
1567
+ Bas: {
1568
+ path: ':id/foos/res/:pwd',
1569
+ parse: {
1570
+ id: Number,
1571
+ pwd: Number,
1572
+ },
1573
+ },
1574
+ },
1575
+ },
1576
+ },
1577
+ }
1578
+
1579
+ const state = {
1580
+ routes: [
1581
+ {
1582
+ name: 'Foe',
1583
+ state: {
1584
+ index: 1,
1585
+ routes: [
1586
+ {
1587
+ name: 'Foo',
1588
+ },
1589
+ {
1590
+ name: 'Bas',
1591
+ params: { id: 5, pwd: 20 },
1592
+ path,
1593
+ },
1594
+ ],
1595
+ },
1596
+ },
1597
+ ],
1598
+ }
1599
+
1600
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1601
+ expect(
1602
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1603
+ ).toEqual(changePath(state, '/5/foos/res/20'))
1604
+ })
1605
+
1606
+ // @modify: TODO: temporally disable failing test
1607
+ test.skip('handles simple optional params', () => {
1608
+ const path = '/foos/5'
1609
+
1610
+ const config = {
1611
+ screens: {
1612
+ Foe: {
1613
+ path: '/',
1614
+ initialRouteName: 'Foo',
1615
+ screens: {
1616
+ Foo: 'foo',
1617
+ Bis: {
1618
+ path: 'foo/:id',
1619
+ parse: {
1620
+ id: Number,
1621
+ },
1622
+ },
1623
+ Bas: {
1624
+ path: 'foos/:id/:nip?',
1625
+ parse: {
1626
+ id: Number,
1627
+ nip: Number,
1628
+ },
1629
+ },
1630
+ },
1631
+ },
1632
+ },
1633
+ }
1634
+
1635
+ const state = {
1636
+ routes: [
1637
+ {
1638
+ name: 'Foe',
1639
+ state: {
1640
+ index: 1,
1641
+ routes: [
1642
+ {
1643
+ name: 'Foo',
1644
+ },
1645
+ {
1646
+ name: 'Bas',
1647
+ params: { id: 5 },
1648
+ path,
1649
+ },
1650
+ ],
1651
+ },
1652
+ },
1653
+ ],
1654
+ }
1655
+
1656
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1657
+ expect(
1658
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1659
+ ).toEqual(state)
1660
+ })
1661
+
1662
+ // @modify: TODO: temporally disable failing test
1663
+ test.skip('handle 2 optional params at the end v1', () => {
1664
+ const path = '/foos/5'
1665
+
1666
+ const config = {
1667
+ screens: {
1668
+ Foe: {
1669
+ path: '/',
1670
+ initialRouteName: 'Foo',
1671
+ screens: {
1672
+ Foo: 'foo',
1673
+ Bis: {
1674
+ path: 'foo/:id',
1675
+ parse: {
1676
+ id: Number,
1677
+ },
1678
+ },
1679
+ Bas: {
1680
+ path: 'foos/:id/:nip?/:pwd?',
1681
+ parse: {
1682
+ id: Number,
1683
+ nip: Number,
1684
+ },
1685
+ },
1686
+ },
1687
+ },
1688
+ },
1689
+ }
1690
+
1691
+ const state = {
1692
+ routes: [
1693
+ {
1694
+ name: 'Foe',
1695
+ state: {
1696
+ index: 1,
1697
+ routes: [
1698
+ {
1699
+ name: 'Foo',
1700
+ },
1701
+ {
1702
+ name: 'Bas',
1703
+ params: { id: 5 },
1704
+ path,
1705
+ },
1706
+ ],
1707
+ },
1708
+ },
1709
+ ],
1710
+ }
1711
+
1712
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1713
+ expect(
1714
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1715
+ ).toEqual(state)
1716
+ })
1717
+
1718
+ // @modify: TODO: temporally disable failing test
1719
+ test.skip('handle 2 optional params at the end v2', () => {
1720
+ const path = '/foos/5/10'
1721
+
1722
+ const config = {
1723
+ screens: {
1724
+ Foe: {
1725
+ path: '/',
1726
+ initialRouteName: 'Foo',
1727
+ screens: {
1728
+ Foo: 'foo',
1729
+ Bis: {
1730
+ path: 'foo/:id',
1731
+ parse: {
1732
+ id: Number,
1733
+ },
1734
+ },
1735
+ Bas: {
1736
+ path: 'foos/:id/:nip?/:pwd?',
1737
+ parse: {
1738
+ id: Number,
1739
+ nip: Number,
1740
+ },
1741
+ },
1742
+ },
1743
+ },
1744
+ },
1745
+ }
1746
+
1747
+ const state = {
1748
+ routes: [
1749
+ {
1750
+ name: 'Foe',
1751
+ state: {
1752
+ index: 1,
1753
+ routes: [
1754
+ {
1755
+ name: 'Foo',
1756
+ },
1757
+ {
1758
+ name: 'Bas',
1759
+ params: { id: 5, nip: 10 },
1760
+ path,
1761
+ },
1762
+ ],
1763
+ },
1764
+ },
1765
+ ],
1766
+ }
1767
+
1768
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1769
+ expect(
1770
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1771
+ ).toEqual(state)
1772
+ })
1773
+
1774
+ // @modify: TODO: temporally disable failing test
1775
+ test.skip('handle 2 optional params at the end v3', () => {
1776
+ const path = '/foos/5/10/15'
1777
+
1778
+ const config = {
1779
+ screens: {
1780
+ Foe: {
1781
+ path: '/',
1782
+ initialRouteName: 'Foo',
1783
+ screens: {
1784
+ Foo: 'foo',
1785
+ Bis: {
1786
+ path: 'foo/:id',
1787
+ parse: {
1788
+ id: Number,
1789
+ },
1790
+ },
1791
+ Bas: {
1792
+ path: 'foos/:id/:nip?/:pwd?',
1793
+ parse: {
1794
+ id: Number,
1795
+ nip: Number,
1796
+ pwd: Number,
1797
+ },
1798
+ },
1799
+ },
1800
+ },
1801
+ },
1802
+ }
1803
+
1804
+ const state = {
1805
+ routes: [
1806
+ {
1807
+ name: 'Foe',
1808
+ state: {
1809
+ index: 1,
1810
+ routes: [
1811
+ {
1812
+ name: 'Foo',
1813
+ },
1814
+ {
1815
+ name: 'Bas',
1816
+ params: { id: 5, nip: 10, pwd: 15 },
1817
+ path,
1818
+ },
1819
+ ],
1820
+ },
1821
+ },
1822
+ ],
1823
+ }
1824
+
1825
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1826
+ expect(
1827
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1828
+ ).toEqual(state)
1829
+ })
1830
+
1831
+ // @modify: TODO: temporally disable failing test
1832
+ test.skip('handle optional params in the middle v1', () => {
1833
+ const path = '/foos/5/10'
1834
+
1835
+ const config = {
1836
+ screens: {
1837
+ Foe: {
1838
+ path: '/',
1839
+ initialRouteName: 'Foo',
1840
+ screens: {
1841
+ Foo: 'foo',
1842
+ Bis: {
1843
+ path: 'foo/:id',
1844
+ parse: {
1845
+ id: Number,
1846
+ },
1847
+ },
1848
+ Bas: {
1849
+ path: 'foos/:id/:nip?/:pwd',
1850
+ parse: {
1851
+ id: Number,
1852
+ nip: Number,
1853
+ pwd: Number,
1854
+ },
1855
+ },
1856
+ },
1857
+ },
1858
+ },
1859
+ }
1860
+
1861
+ const state = {
1862
+ routes: [
1863
+ {
1864
+ name: 'Foe',
1865
+ state: {
1866
+ index: 1,
1867
+ routes: [
1868
+ {
1869
+ name: 'Foo',
1870
+ },
1871
+ {
1872
+ name: 'Bas',
1873
+ params: { id: 5, pwd: 10 },
1874
+ path,
1875
+ },
1876
+ ],
1877
+ },
1878
+ },
1879
+ ],
1880
+ }
1881
+
1882
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1883
+ expect(
1884
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1885
+ ).toEqual(state)
1886
+ })
1887
+
1888
+ // @modify: TODO: temporally disable failing test
1889
+ test.skip('handle optional params in the middle v2', () => {
1890
+ const path = '/foos/5/10/15'
1891
+
1892
+ const config = {
1893
+ screens: {
1894
+ Foe: {
1895
+ path: '/',
1896
+ initialRouteName: 'Foo',
1897
+ screens: {
1898
+ Foo: 'foo',
1899
+ Bis: {
1900
+ path: 'foo/:id',
1901
+ parse: {
1902
+ id: Number,
1903
+ },
1904
+ },
1905
+ Bas: {
1906
+ path: 'foos/:id/:nip?/:pwd',
1907
+ parse: {
1908
+ id: Number,
1909
+ nip: Number,
1910
+ pwd: Number,
1911
+ },
1912
+ },
1913
+ },
1914
+ },
1915
+ },
1916
+ }
1917
+
1918
+ const state = {
1919
+ routes: [
1920
+ {
1921
+ name: 'Foe',
1922
+ state: {
1923
+ index: 1,
1924
+ routes: [
1925
+ {
1926
+ name: 'Foo',
1927
+ },
1928
+ {
1929
+ name: 'Bas',
1930
+ params: { id: 5, nip: 10, pwd: 15 },
1931
+ path,
1932
+ },
1933
+ ],
1934
+ },
1935
+ },
1936
+ ],
1937
+ }
1938
+
1939
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1940
+ expect(
1941
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
1942
+ ).toEqual(state)
1943
+ })
1944
+
1945
+ // @modify: TODO: temporally disable failing test
1946
+ test.skip('handle optional params in the middle v3', () => {
1947
+ const path = '/foos/5/10/15'
1948
+
1949
+ const config = {
1950
+ screens: {
1951
+ Foe: {
1952
+ path: '/',
1953
+ initialRouteName: 'Foo',
1954
+ screens: {
1955
+ Foo: 'foo',
1956
+ Bis: {
1957
+ path: 'foo/:id',
1958
+ parse: {
1959
+ id: Number,
1960
+ },
1961
+ },
1962
+ Bas: {
1963
+ path: 'foos/:id/:nip?/:pwd/:smh',
1964
+ parse: {
1965
+ id: Number,
1966
+ nip: Number,
1967
+ pwd: Number,
1968
+ smh: Number,
1969
+ },
1970
+ },
1971
+ },
1972
+ },
1973
+ },
1974
+ }
1975
+
1976
+ const state = {
1977
+ routes: [
1978
+ {
1979
+ name: 'Foe',
1980
+ state: {
1981
+ index: 1,
1982
+ routes: [
1983
+ {
1984
+ name: 'Foo',
1985
+ },
1986
+ {
1987
+ name: 'Bas',
1988
+ params: { id: 5, pwd: 10, smh: 15 },
1989
+ path,
1990
+ },
1991
+ ],
1992
+ },
1993
+ },
1994
+ ],
1995
+ }
1996
+
1997
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
1998
+ expect(
1999
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2000
+ ).toEqual(state)
2001
+ })
2002
+
2003
+ // @modify: TODO: temporally disable failing test
2004
+ test.skip('handle optional params in the middle v4', () => {
2005
+ const path = '/foos/5/10'
2006
+
2007
+ const config = {
2008
+ screens: {
2009
+ Foe: {
2010
+ path: '/',
2011
+ initialRouteName: 'Foo',
2012
+ screens: {
2013
+ Foo: 'foo',
2014
+ Bis: {
2015
+ path: 'foo/:id',
2016
+ parse: {
2017
+ id: Number,
2018
+ },
2019
+ },
2020
+ Bas: {
2021
+ path: 'foos/:nip?/:pwd/:smh?/:id',
2022
+ parse: {
2023
+ id: Number,
2024
+ nip: Number,
2025
+ pwd: Number,
2026
+ smh: Number,
2027
+ },
2028
+ },
2029
+ },
2030
+ },
2031
+ },
2032
+ }
2033
+
2034
+ const state = {
2035
+ routes: [
2036
+ {
2037
+ name: 'Foe',
2038
+ state: {
2039
+ index: 1,
2040
+ routes: [
2041
+ {
2042
+ name: 'Foo',
2043
+ },
2044
+ {
2045
+ name: 'Bas',
2046
+ params: { pwd: 5, id: 10 },
2047
+ path,
2048
+ },
2049
+ ],
2050
+ },
2051
+ },
2052
+ ],
2053
+ }
2054
+
2055
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2056
+ expect(
2057
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2058
+ ).toEqual(state)
2059
+ })
2060
+
2061
+ // @modify: TODO: temporally disable failing test
2062
+ test.skip('handle optional params in the middle v5', () => {
2063
+ const path = '/foos/5/10/15'
2064
+
2065
+ const config = {
2066
+ screens: {
2067
+ Foe: {
2068
+ path: '/',
2069
+ initialRouteName: 'Foo',
2070
+ screens: {
2071
+ Foo: 'foo',
2072
+ Bis: {
2073
+ path: 'foo/:id',
2074
+ parse: {
2075
+ id: Number,
2076
+ },
2077
+ },
2078
+ Bas: {
2079
+ path: 'foos/:nip?/:pwd/:smh?/:id',
2080
+ parse: {
2081
+ id: Number,
2082
+ nip: Number,
2083
+ pwd: Number,
2084
+ smh: Number,
2085
+ },
2086
+ },
2087
+ },
2088
+ },
2089
+ },
2090
+ }
2091
+
2092
+ const state = {
2093
+ routes: [
2094
+ {
2095
+ name: 'Foe',
2096
+ state: {
2097
+ index: 1,
2098
+ routes: [
2099
+ {
2100
+ name: 'Foo',
2101
+ },
2102
+ {
2103
+ name: 'Bas',
2104
+ params: { nip: 5, pwd: 10, id: 15 },
2105
+ path,
2106
+ },
2107
+ ],
2108
+ },
2109
+ },
2110
+ ],
2111
+ }
2112
+
2113
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2114
+ expect(
2115
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2116
+ ).toEqual(state)
2117
+ })
2118
+
2119
+ // @modify: TODO: temporally disable failing test
2120
+ test.skip('handle optional params in the beginning v1', () => {
2121
+ const path = '5/10/foos/15'
2122
+
2123
+ const config = {
2124
+ screens: {
2125
+ Foe: {
2126
+ path: '/',
2127
+ initialRouteName: 'Foo',
2128
+ screens: {
2129
+ Foo: 'foo',
2130
+ Bis: {
2131
+ path: 'foo/:id',
2132
+ parse: {
2133
+ id: Number,
2134
+ },
2135
+ },
2136
+ Bas: {
2137
+ path: ':nip?/:pwd/foos/:smh?/:id',
2138
+ parse: {
2139
+ id: Number,
2140
+ nip: Number,
2141
+ pwd: Number,
2142
+ smh: Number,
2143
+ },
2144
+ },
2145
+ },
2146
+ },
2147
+ },
2148
+ }
2149
+
2150
+ const state = {
2151
+ routes: [
2152
+ {
2153
+ name: 'Foe',
2154
+ state: {
2155
+ index: 1,
2156
+ routes: [
2157
+ {
2158
+ name: 'Foo',
2159
+ },
2160
+ {
2161
+ name: 'Bas',
2162
+ params: { nip: 5, pwd: 10, id: 15 },
2163
+ path,
2164
+ },
2165
+ ],
2166
+ },
2167
+ },
2168
+ ],
2169
+ }
2170
+
2171
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2172
+ expect(
2173
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2174
+ ).toEqual(changePath(state, '/5/10/foos/15'))
2175
+ })
2176
+
2177
+ // @modify: TODO: temporally disable failing test
2178
+ test.skip('handle optional params in the beginning v2', () => {
2179
+ const path = '5/10/foos/15'
2180
+
2181
+ const config = {
2182
+ screens: {
2183
+ Foe: {
2184
+ path: '/',
2185
+ initialRouteName: 'Foo',
2186
+ screens: {
2187
+ Foo: 'foo',
2188
+ Bis: {
2189
+ path: 'foo/:id',
2190
+ parse: {
2191
+ id: Number,
2192
+ },
2193
+ },
2194
+ Bas: {
2195
+ path: ':nip?/:smh?/:pwd/foos/:id',
2196
+ parse: {
2197
+ id: Number,
2198
+ nip: Number,
2199
+ pwd: Number,
2200
+ smh: Number,
2201
+ },
2202
+ },
2203
+ },
2204
+ },
2205
+ },
2206
+ }
2207
+
2208
+ const state = {
2209
+ routes: [
2210
+ {
2211
+ name: 'Foe',
2212
+ state: {
2213
+ index: 1,
2214
+ routes: [
2215
+ {
2216
+ name: 'Foo',
2217
+ },
2218
+ {
2219
+ name: 'Bas',
2220
+ params: { nip: 5, pwd: 10, id: 15 },
2221
+ path,
2222
+ },
2223
+ ],
2224
+ },
2225
+ },
2226
+ ],
2227
+ }
2228
+
2229
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2230
+ expect(
2231
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2232
+ ).toEqual(changePath(state, '/5/10/foos/15'))
2233
+ })
2234
+
2235
+ // @modify: TODO: temporally disable failing test
2236
+ test.skip('merges parent patterns if needed', () => {
2237
+ const path = 'foo/42/baz/babel'
2238
+
2239
+ const config = {
2240
+ screens: {
2241
+ Foo: {
2242
+ path: 'foo/:bar',
2243
+ parse: {
2244
+ bar: Number,
2245
+ },
2246
+ screens: {
2247
+ Baz: 'baz/:qux',
2248
+ },
2249
+ },
2250
+ },
2251
+ }
2252
+
2253
+ const state = {
2254
+ routes: [
2255
+ {
2256
+ name: 'Foo',
2257
+ params: { bar: 42 },
2258
+ state: {
2259
+ routes: [
2260
+ {
2261
+ name: 'Baz',
2262
+ params: { qux: 'babel' },
2263
+ path,
2264
+ },
2265
+ ],
2266
+ },
2267
+ },
2268
+ ],
2269
+ }
2270
+
2271
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2272
+ expect(
2273
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2274
+ ).toEqual(changePath(state, '/foo/42/baz/babel'))
2275
+ })
2276
+
2277
+ // @modify: TODO: temporally disable failing test
2278
+ test.skip('ignores extra slashes in the pattern', () => {
2279
+ const path = '/bar/42'
2280
+ const config = {
2281
+ screens: {
2282
+ Foo: {
2283
+ screens: {
2284
+ Bar: {
2285
+ path: '/bar//:id/',
2286
+ },
2287
+ },
2288
+ },
2289
+ },
2290
+ }
2291
+
2292
+ const state = {
2293
+ routes: [
2294
+ {
2295
+ name: 'Foo',
2296
+ state: {
2297
+ routes: [
2298
+ {
2299
+ name: 'Bar',
2300
+ params: { id: '42' },
2301
+ path,
2302
+ },
2303
+ ],
2304
+ },
2305
+ },
2306
+ ],
2307
+ }
2308
+
2309
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2310
+ expect(
2311
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2312
+ ).toEqual(state)
2313
+ })
2314
+
2315
+ // @modify: TODO: temporally disable failing test
2316
+ test.skip('matches wildcard patterns at root', () => {
2317
+ const path = '/test/bar/42/whatever'
2318
+ const config = {
2319
+ screens: {
2320
+ 404: '*',
2321
+ Foo: {
2322
+ screens: {
2323
+ Bar: {
2324
+ path: '/bar/:id/',
2325
+ },
2326
+ },
2327
+ },
2328
+ },
2329
+ }
2330
+
2331
+ const state = {
2332
+ routes: [{ name: '404', path }],
2333
+ }
2334
+
2335
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2336
+ expect(
2337
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2338
+ ).toEqual(changePath(state, '/404'))
2339
+ })
2340
+
2341
+ // @modify: TODO: temporally disable failing test
2342
+ test.skip('matches wildcard patterns at nested level', () => {
2343
+ const path = '/bar/42/whatever/baz/initt'
2344
+ const config = {
2345
+ screens: {
2346
+ Foo: {
2347
+ screens: {
2348
+ Bar: {
2349
+ path: '/bar/:id/',
2350
+ screens: {
2351
+ 404: '*',
2352
+ },
2353
+ },
2354
+ },
2355
+ },
2356
+ },
2357
+ }
2358
+
2359
+ const state = {
2360
+ routes: [
2361
+ {
2362
+ name: 'Foo',
2363
+ state: {
2364
+ routes: [
2365
+ {
2366
+ name: 'Bar',
2367
+ params: { id: '42' },
2368
+ state: {
2369
+ routes: [{ name: '404', path }],
2370
+ },
2371
+ },
2372
+ ],
2373
+ },
2374
+ },
2375
+ ],
2376
+ }
2377
+
2378
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2379
+ expect(
2380
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2381
+ ).toEqual(changePath(state, '/bar/42/404'))
2382
+ })
2383
+
2384
+ // @modify: TODO: temporally disable failing test
2385
+ test.skip('matches wildcard patterns at nested level with exact', () => {
2386
+ const path = '/whatever'
2387
+ const config = {
2388
+ screens: {
2389
+ Foo: {
2390
+ screens: {
2391
+ Bar: {
2392
+ path: '/bar/:id/',
2393
+ screens: {
2394
+ 404: {
2395
+ path: '*',
2396
+ exact: true,
2397
+ },
2398
+ },
2399
+ },
2400
+ Baz: {},
2401
+ },
2402
+ },
2403
+ },
2404
+ }
2405
+
2406
+ const state = {
2407
+ routes: [
2408
+ {
2409
+ name: 'Foo',
2410
+ state: {
2411
+ routes: [
2412
+ {
2413
+ name: 'Bar',
2414
+ state: {
2415
+ routes: [{ name: '404', path }],
2416
+ },
2417
+ },
2418
+ ],
2419
+ },
2420
+ },
2421
+ ],
2422
+ }
2423
+
2424
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2425
+ expect(
2426
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2427
+ ).toEqual(changePath(state, '/404'))
2428
+ })
2429
+
2430
+ // @modify: TODO: temporally disable failing test
2431
+ test.skip('tries to match wildcard patterns at the end', () => {
2432
+ const path = '/bar/42/test'
2433
+ const config = {
2434
+ screens: {
2435
+ Foo: {
2436
+ screens: {
2437
+ Bar: {
2438
+ path: '/bar/:id/',
2439
+ screens: {
2440
+ 404: '*',
2441
+ UserProfile: ':userSlug',
2442
+ Test: 'test',
2443
+ },
2444
+ },
2445
+ },
2446
+ },
2447
+ },
2448
+ }
2449
+
2450
+ const state = {
2451
+ routes: [
2452
+ {
2453
+ name: 'Foo',
2454
+ state: {
2455
+ routes: [
2456
+ {
2457
+ name: 'Bar',
2458
+ params: { id: '42' },
2459
+ state: {
2460
+ routes: [{ name: 'Test', path }],
2461
+ },
2462
+ },
2463
+ ],
2464
+ },
2465
+ },
2466
+ ],
2467
+ }
2468
+
2469
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2470
+ expect(
2471
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2472
+ ).toEqual(state)
2473
+ })
2474
+
2475
+ // @modify: TODO: temporally disable failing test
2476
+ test.skip('uses nearest parent wildcard match for unmatched paths', () => {
2477
+ const path = '/bar/42/baz/test'
2478
+ const config = {
2479
+ screens: {
2480
+ Foo: {
2481
+ screens: {
2482
+ Bar: {
2483
+ path: '/bar/:id/',
2484
+ screens: {
2485
+ Baz: 'baz',
2486
+ },
2487
+ },
2488
+ 404: '*',
2489
+ },
2490
+ },
2491
+ },
2492
+ }
2493
+
2494
+ const state = {
2495
+ routes: [
2496
+ {
2497
+ name: 'Foo',
2498
+ state: {
2499
+ routes: [{ name: '404', path }],
2500
+ },
2501
+ },
2502
+ ],
2503
+ }
2504
+
2505
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2506
+ expect(
2507
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2508
+ ).toEqual(changePath(state, '/404'))
2509
+ })
2510
+
2511
+ // @modify: TODO: temporally disable failing test
2512
+ test.skip('matches screen with overlapping initial path and wildcard', () => {
2513
+ const path = '/bar/42/baz/test/whatever'
2514
+ const config = {
2515
+ screens: {
2516
+ Foo: {
2517
+ screens: {
2518
+ Bar: {
2519
+ path: '/bar/:id/',
2520
+ screens: {
2521
+ Baz: 'baz',
2522
+ },
2523
+ },
2524
+ Baz: '/bar/:id/*',
2525
+ },
2526
+ },
2527
+ },
2528
+ }
2529
+
2530
+ const state = {
2531
+ routes: [
2532
+ {
2533
+ name: 'Foo',
2534
+ state: {
2535
+ routes: [{ name: 'Baz', params: { id: '42' }, path }],
2536
+ },
2537
+ },
2538
+ ],
2539
+ }
2540
+
2541
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2542
+ expect(
2543
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2544
+ ).toEqual(changePath(state, '/bar/42/Baz'))
2545
+ })
2546
+
2547
+ test('throws if two screens map to the same pattern', () => {
2548
+ const path = '/bar/42/baz/test'
2549
+
2550
+ expect(() =>
2551
+ getStateFromPath<object>(path, {
2552
+ screens: {
2553
+ Foo: {
2554
+ screens: {
2555
+ Bar: {
2556
+ path: '/bar/:id/',
2557
+ screens: {
2558
+ Baz: 'baz',
2559
+ },
2560
+ },
2561
+ Bax: '/bar/:id/baz',
2562
+ },
2563
+ },
2564
+ },
2565
+ })
2566
+ ).toThrow(
2567
+ "Found conflicting screens with the same pattern. The pattern 'bar/:id/baz' resolves to both 'Foo > Bax' and 'Foo > Bar > Baz'. Patterns must be unique and cannot resolve to more than one screen."
2568
+ )
2569
+
2570
+ expect(() =>
2571
+ getStateFromPath<object>(path, {
2572
+ screens: {
2573
+ Foo: {
2574
+ screens: {
2575
+ Bar: {
2576
+ path: '/bar/:id/',
2577
+ screens: {
2578
+ Baz: '',
2579
+ },
2580
+ },
2581
+ },
2582
+ },
2583
+ },
2584
+ })
2585
+ ).not.toThrow()
2586
+ })
2587
+
2588
+ test('correctly applies initialRouteName for config with similar route names', () => {
2589
+ const path = '/weekly-earnings'
2590
+
2591
+ const config = {
2592
+ screens: {
2593
+ RootTabs: {
2594
+ screens: {
2595
+ HomeTab: {
2596
+ screens: {
2597
+ Home: '',
2598
+ WeeklyEarnings: 'weekly-earnings',
2599
+ EventDetails: 'event-details/:eventId',
2600
+ },
2601
+ },
2602
+ EarningsTab: {
2603
+ initialRouteName: 'Earnings',
2604
+ path: 'earnings',
2605
+ screens: {
2606
+ Earnings: '',
2607
+ WeeklyEarnings: 'weekly-earnings',
2608
+ },
2609
+ },
2610
+ },
2611
+ },
2612
+ },
2613
+ }
2614
+
2615
+ const state = {
2616
+ routes: [
2617
+ {
2618
+ name: 'RootTabs',
2619
+ state: {
2620
+ routes: [
2621
+ {
2622
+ name: 'HomeTab',
2623
+ state: {
2624
+ routes: [
2625
+ {
2626
+ name: 'WeeklyEarnings',
2627
+ path,
2628
+ },
2629
+ ],
2630
+ },
2631
+ },
2632
+ ],
2633
+ },
2634
+ },
2635
+ ],
2636
+ }
2637
+
2638
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2639
+ expect(
2640
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2641
+ ).toEqual(state)
2642
+ })
2643
+
2644
+ test('correctly applies initialRouteName for config with similar route names v2', () => {
2645
+ const path = '/earnings/weekly-earnings'
2646
+
2647
+ const config = {
2648
+ screens: {
2649
+ RootTabs: {
2650
+ screens: {
2651
+ HomeTab: {
2652
+ initialRouteName: 'Home',
2653
+ screens: {
2654
+ Home: '',
2655
+ WeeklyEarnings: 'weekly-earnings',
2656
+ },
2657
+ },
2658
+ EarningsTab: {
2659
+ initialRouteName: 'Earnings',
2660
+ path: 'earnings',
2661
+ screens: {
2662
+ Earnings: '',
2663
+ WeeklyEarnings: 'weekly-earnings',
2664
+ },
2665
+ },
2666
+ },
2667
+ },
2668
+ },
2669
+ }
2670
+
2671
+ const state = {
2672
+ routes: [
2673
+ {
2674
+ name: 'RootTabs',
2675
+ state: {
2676
+ routes: [
2677
+ {
2678
+ name: 'EarningsTab',
2679
+ state: {
2680
+ index: 1,
2681
+ routes: [
2682
+ {
2683
+ name: 'Earnings',
2684
+ },
2685
+ {
2686
+ name: 'WeeklyEarnings',
2687
+ path,
2688
+ },
2689
+ ],
2690
+ },
2691
+ },
2692
+ ],
2693
+ },
2694
+ },
2695
+ ],
2696
+ }
2697
+
2698
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2699
+ expect(
2700
+ getStateFromPath<object>(getPathFromState<object>(state, config), config)
2701
+ ).toEqual(state)
2702
+ })
2703
+
2704
+ // @modify: TODO: temporally disable failing test
2705
+ test.skip('throws when invalid properties are specified in the config', () => {
2706
+ expect(() =>
2707
+ getStateFromPath<object>('', {
2708
+ path: 42,
2709
+ Foo: 'foo',
2710
+ Bar: {
2711
+ path: 'bar',
2712
+ },
2713
+ } as any)
2714
+ ).toThrowErrorMatchingInlineSnapshot(`
2715
+ "Found invalid properties in the configuration:
2716
+ - path (expected 'string', got 'number')
2717
+ - Foo (extraneous)
2718
+ - Bar (extraneous)
2719
+
2720
+ You can only specify the following properties:
2721
+ - path (string)
2722
+ - initialRouteName (string)
2723
+ - screens (object)
2724
+
2725
+ If you want to specify configuration for screens, you need to specify them under a 'screens' property.
2726
+
2727
+ See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
2728
+ `)
2729
+
2730
+ expect(() =>
2731
+ getStateFromPath<object>('', {
2732
+ screens: {
2733
+ Foo: 'foo',
2734
+ Bar: {
2735
+ path: 'bar',
2736
+ },
2737
+ Baz: {
2738
+ Qux: {
2739
+ path: 'qux',
2740
+ },
2741
+ },
2742
+ },
2743
+ } as any)
2744
+ ).toThrowErrorMatchingInlineSnapshot(`
2745
+ "Found invalid properties in the configuration:
2746
+ - Qux (extraneous)
2747
+
2748
+ You can only specify the following properties:
2749
+ - path (string)
2750
+ - initialRouteName (string)
2751
+ - screens (object)
2752
+ - alias (array)
2753
+ - exact (boolean)
2754
+ - stringify (object)
2755
+ - parse (object)
2756
+
2757
+ If you want to specify configuration for screens, you need to specify them under a 'screens' property.
2758
+
2759
+ See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
2760
+ `)
2761
+
2762
+ expect(() =>
2763
+ getStateFromPath<object>('', {
2764
+ path: 'foo/:id',
2765
+ } as any)
2766
+ ).toThrowErrorMatchingInlineSnapshot(
2767
+ `"Found invalid path 'foo/:id'. The 'path' in the top-level configuration cannot contain patterns for params."`
2768
+ )
2769
+ })
2770
+
2771
+ // Valid characters according to
2772
+ // https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 (see pchar definition)
2773
+ // A–Z, a–z, 0–9, -, ., _, ~, !, $, &, ', (, ), *, +, ,, ;, =, :, @
2774
+ // User09-A_Z~!$&'()*+,;=:@__#?# - should encode only last ones #?#
2775
+ // query params after '?' should be encoded fully with encodeURIComponent
2776
+ // @modify: TODO: temporally disable failing test
2777
+ test.skip('encodes special characters in params', () => {
2778
+ const paramWithValidSymbols = `User09-A_Z~!$&'()*+,;=:@__`
2779
+ const invalidSymbols = '#?[]{}%<>||'
2780
+ const queryString = 'user#email@gmail.com=2&4'
2781
+
2782
+ const path = `users/id/${paramWithValidSymbols}${encodeURIComponent(
2783
+ invalidSymbols
2784
+ )}?query=${encodeURIComponent(queryString)}`
2785
+ const config = {
2786
+ path: 'users',
2787
+ screens: {
2788
+ Users: {
2789
+ screens: {
2790
+ User: 'id/:id',
2791
+ },
2792
+ },
2793
+ },
2794
+ }
2795
+
2796
+ const state = {
2797
+ routes: [
2798
+ {
2799
+ name: 'Users',
2800
+ state: {
2801
+ routes: [
2802
+ {
2803
+ name: 'User',
2804
+ params: {
2805
+ id: `${paramWithValidSymbols}${invalidSymbols}`,
2806
+ query: queryString,
2807
+ },
2808
+ },
2809
+ ],
2810
+ },
2811
+ },
2812
+ ],
2813
+ }
2814
+
2815
+ expect(getPathFromState<object>(state, config)).toBe(`/${path}`)
2816
+ expect(getPathFromState<object>(getStateFromPath<object>(path, config)!, config)).toBe(
2817
+ `/${path}`
2818
+ )
2819
+ })
2820
+
2821
+ // @modify: TODO: temporally disable failing test
2822
+ test.skip('resolves nested path params with same name to correct screen', () => {
2823
+ const path = '/foo/42/bar/43'
2824
+
2825
+ const config = {
2826
+ initialRouteName: 'Foo',
2827
+ screens: {
2828
+ Foo: {
2829
+ path: 'foo/:id',
2830
+ screens: {
2831
+ Bar: {
2832
+ path: 'bar/:id',
2833
+ },
2834
+ },
2835
+ },
2836
+ },
2837
+ }
2838
+
2839
+ const state = {
2840
+ routes: [
2841
+ {
2842
+ name: 'Foo',
2843
+ params: { id: '42' },
2844
+ state: {
2845
+ routes: [
2846
+ {
2847
+ name: 'Bar',
2848
+ params: { id: '43' },
2849
+ path,
2850
+ },
2851
+ ],
2852
+ },
2853
+ },
2854
+ ],
2855
+ }
2856
+
2857
+ expect(getStateFromPath<object>(path, config)).toEqual(state)
2858
+ })
2859
+
2860
+ test('parses / same as empty string', () => {
2861
+ const config = {
2862
+ screens: {
2863
+ Foo: {
2864
+ path: '/',
2865
+ },
2866
+ Bar: {
2867
+ path: 'bar',
2868
+ },
2869
+ },
2870
+ }
2871
+
2872
+ expect(getStateFromPath<object>('/', config)).toEqual(
2873
+ getStateFromPath<object>('', config)
2874
+ )
2875
+ })
2876
+
2877
+ // @modify: TODO: temporally disable failing test
2878
+ test.skip('matches regexp patterns when provided', () => {
2879
+ const config = {
2880
+ screens: {
2881
+ Foo: {
2882
+ path: 'foo/:id(\\d+)',
2883
+ parse: {
2884
+ id: Number,
2885
+ },
2886
+ },
2887
+ Bar: {
2888
+ path: 'foo/:id([a-z]+)',
2889
+ },
2890
+ Baz: {
2891
+ path: 'foo/:id(\\d+)/:name([a-z]+)',
2892
+ },
2893
+ Qux: {
2894
+ path: 'foo/:id(@[a-z]+)',
2895
+ parse: {
2896
+ id: (id: string) => id.slice(1),
2897
+ },
2898
+ },
2899
+ Quy: {
2900
+ path: 'foo/bar/:category',
2901
+ },
2902
+ Quz: {
2903
+ path: 'foo/bar/:special([a-z]+)',
2904
+ },
2905
+ Quu: {
2906
+ path: 'foo/bar/baz',
2907
+ },
2908
+ NotFound: {
2909
+ path: 'foo/bar/*',
2910
+ },
2911
+ },
2912
+ }
2913
+
2914
+ expect(getStateFromPath<object>('foo/42', config)).toEqual({
2915
+ routes: [
2916
+ {
2917
+ name: 'Foo',
2918
+ params: { id: 42 },
2919
+ path: 'foo/42',
2920
+ },
2921
+ ],
2922
+ })
2923
+
2924
+ expect(getStateFromPath<object>('foo/bar', config)).toEqual({
2925
+ routes: [
2926
+ {
2927
+ name: 'Bar',
2928
+ params: { id: 'bar' },
2929
+ path: 'foo/bar',
2930
+ },
2931
+ ],
2932
+ })
2933
+
2934
+ expect(getStateFromPath<object>('foo/42/bar', config)).toEqual({
2935
+ routes: [
2936
+ {
2937
+ name: 'Baz',
2938
+ params: { id: '42', name: 'bar' },
2939
+ path: 'foo/42/bar',
2940
+ },
2941
+ ],
2942
+ })
2943
+
2944
+ expect(getStateFromPath<object>('foo/@bar', config)).toEqual({
2945
+ routes: [
2946
+ {
2947
+ name: 'Qux',
2948
+ params: { id: 'bar' },
2949
+ path: 'foo/@bar',
2950
+ },
2951
+ ],
2952
+ })
2953
+
2954
+ expect(getStateFromPath<object>('foo/@bar', config)).toEqual({
2955
+ routes: [
2956
+ {
2957
+ name: 'Qux',
2958
+ params: { id: 'bar' },
2959
+ path: 'foo/@bar',
2960
+ },
2961
+ ],
2962
+ })
2963
+
2964
+ expect(getStateFromPath<object>('foo/42a', config)).toBeUndefined()
2965
+
2966
+ expect(getStateFromPath<object>('foo/bar/123', config)).toEqual({
2967
+ routes: [
2968
+ {
2969
+ name: 'Quy',
2970
+ params: { category: '123' },
2971
+ path: 'foo/bar/123',
2972
+ },
2973
+ ],
2974
+ })
2975
+
2976
+ expect(getStateFromPath<object>('foo/bar/test', config)).toEqual({
2977
+ routes: [
2978
+ {
2979
+ name: 'Quz',
2980
+ params: { special: 'test' },
2981
+ path: 'foo/bar/test',
2982
+ },
2983
+ ],
2984
+ })
2985
+
2986
+ expect(getStateFromPath<object>('foo/bar/baz', config)).toEqual({
2987
+ routes: [
2988
+ {
2989
+ name: 'Quu',
2990
+ path: 'foo/bar/baz',
2991
+ },
2992
+ ],
2993
+ })
2994
+
2995
+ expect(getStateFromPath<object>('foo/bar/hello/world', config)).toEqual({
2996
+ routes: [{ name: 'NotFound', path: 'foo/bar/hello/world' }],
2997
+ })
2998
+ })
2999
+
3000
+ test("regexp pattern doesn't match slash", () => {
3001
+ const config = {
3002
+ screens: {
3003
+ Foo: {
3004
+ path: 'foo/:id([a-z]+\\/)',
3005
+ },
3006
+ },
3007
+ }
3008
+
3009
+ expect(getStateFromPath<object>('foo/bar/', config)).toBeUndefined()
3010
+
3011
+ expect(getStateFromPath<object>('foo/bar/baz', config)).toBeUndefined()
3012
+
3013
+ expect(getStateFromPath<object>('foo/bar/baz/qux', config)).toBeUndefined()
3014
+ })
3015
+
3016
+ // @modify: TODO: temporally disable failing test
3017
+ test.skip('handles alias for paths', () => {
3018
+ const config = {
3019
+ screens: {
3020
+ Foo: {
3021
+ path: 'foo',
3022
+ alias: ['first'],
3023
+ screens: {
3024
+ Baz: {
3025
+ path: 'baz/:id?',
3026
+ parse: {
3027
+ id: (value: string) => value.replace(/@/, ''),
3028
+ },
3029
+ alias: [
3030
+ {
3031
+ path: 'second/:id',
3032
+ exact: true,
3033
+ },
3034
+ 'third',
3035
+ {
3036
+ path: 'fourth/:id',
3037
+ parse: {
3038
+ id: (value: string) => value.replace(/\$/, ''),
3039
+ },
3040
+ },
3041
+ ],
3042
+ },
3043
+ Qux: {
3044
+ path: 'qux/:id',
3045
+ },
3046
+ },
3047
+ },
3048
+ },
3049
+ }
3050
+
3051
+ expect(getStateFromPath<object>('foo', config)).toEqual({
3052
+ routes: [{ name: 'Foo', path: 'foo' }],
3053
+ })
3054
+
3055
+ expect(getPathFromState<object>(getStateFromPath<object>('foo', config)!, config)).toBe(
3056
+ '/foo'
3057
+ )
3058
+
3059
+ expect(getStateFromPath<object>('first', config)).toEqual({
3060
+ routes: [{ name: 'Foo', path: 'first' }],
3061
+ })
3062
+
3063
+ expect(
3064
+ getPathFromState<object>(getStateFromPath<object>('first', config)!, config)
3065
+ ).toBe('/foo')
3066
+
3067
+ expect(getStateFromPath<object>('foo/baz/@$test', config)).toEqual({
3068
+ routes: [
3069
+ {
3070
+ name: 'Foo',
3071
+ state: {
3072
+ routes: [
3073
+ {
3074
+ name: 'Baz',
3075
+ params: { id: '$test' },
3076
+ path: 'foo/baz/@$test',
3077
+ },
3078
+ ],
3079
+ },
3080
+ },
3081
+ ],
3082
+ })
3083
+
3084
+ expect(
3085
+ getPathFromState<object>(getStateFromPath<object>('foo/baz/@$test', config)!, config)
3086
+ ).toBe('/foo/baz/$test')
3087
+
3088
+ expect(getStateFromPath<object>('second/42', config)).toEqual({
3089
+ routes: [
3090
+ {
3091
+ name: 'Foo',
3092
+ state: {
3093
+ routes: [
3094
+ {
3095
+ name: 'Baz',
3096
+ params: { id: '42' },
3097
+ path: 'second/42',
3098
+ },
3099
+ ],
3100
+ },
3101
+ },
3102
+ ],
3103
+ })
3104
+
3105
+ expect(
3106
+ getPathFromState<object>(getStateFromPath<object>('second/42', config)!, config)
3107
+ ).toBe('/foo/baz/42')
3108
+
3109
+ expect(getStateFromPath<object>('foo/third', config)).toEqual({
3110
+ routes: [
3111
+ {
3112
+ name: 'Foo',
3113
+ state: {
3114
+ routes: [
3115
+ {
3116
+ name: 'Baz',
3117
+ path: 'foo/third',
3118
+ },
3119
+ ],
3120
+ },
3121
+ },
3122
+ ],
3123
+ })
3124
+
3125
+ expect(
3126
+ getPathFromState<object>(getStateFromPath<object>('foo/third', config)!, config)
3127
+ ).toBe('/foo/baz')
3128
+
3129
+ expect(getStateFromPath<object>('foo/fourth/@$test', config)).toEqual({
3130
+ routes: [
3131
+ {
3132
+ name: 'Foo',
3133
+ state: {
3134
+ routes: [
3135
+ {
3136
+ name: 'Baz',
3137
+ params: { id: '@test' },
3138
+ path: 'foo/fourth/@$test',
3139
+ },
3140
+ ],
3141
+ },
3142
+ },
3143
+ ],
3144
+ })
3145
+
3146
+ expect(
3147
+ getPathFromState<object>(
3148
+ getStateFromPath<object>('foo/fourth/@$test', config)!,
3149
+ config
3150
+ )
3151
+ ).toBe('/foo/baz/@test')
3152
+
3153
+ expect(getStateFromPath<object>('foo/qux/42', config)).toEqual({
3154
+ routes: [
3155
+ {
3156
+ name: 'Foo',
3157
+ state: {
3158
+ routes: [
3159
+ {
3160
+ name: 'Qux',
3161
+ params: { id: '42' },
3162
+ path: 'foo/qux/42',
3163
+ },
3164
+ ],
3165
+ },
3166
+ },
3167
+ ],
3168
+ })
3169
+
3170
+ expect(
3171
+ getPathFromState<object>(getStateFromPath<object>('foo/qux/42', config)!, config)
3172
+ ).toBe('/foo/qux/42')
3173
+ })
3174
+
3175
+ // @modify: TODO: temporally disable failing test
3176
+ test.skip('throws if screen has alias but no path', () => {
3177
+ expect(() =>
3178
+ getStateFromPath<object>('', {
3179
+ screens: {
3180
+ Foo: {
3181
+ alias: ['bar'],
3182
+ },
3183
+ },
3184
+ })
3185
+ ).toThrow(
3186
+ `Screen 'Foo' doesn't specify a 'path'. A 'path' needs to be specified in order to use 'alias'.`
3187
+ )
3188
+ })