Skip to content

Commit 7a4f4c7

Browse files
committed
add unit test
1 parent f12b55b commit 7a4f4c7

File tree

1 file changed

+317
-0
lines changed

1 file changed

+317
-0
lines changed
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
import { prepareFlightRouterStateForRequest } from './flight-data-helpers'
2+
import type { FlightRouterState } from '../server/app-render/types'
3+
import { HasLoadingBoundary } from '../server/app-render/types'
4+
5+
describe('prepareFlightRouterStateForRequest', () => {
6+
describe('HMR refresh handling', () => {
7+
it('should preserve complete state for HMR refresh requests', () => {
8+
const flightRouterState: FlightRouterState = [
9+
'__PAGE__?{"sensitive":"data"}',
10+
{},
11+
'/some/url',
12+
'refresh',
13+
true,
14+
1,
15+
]
16+
17+
const result = prepareFlightRouterStateForRequest(flightRouterState, true)
18+
const decoded = JSON.parse(decodeURIComponent(result))
19+
20+
expect(decoded).toEqual(flightRouterState)
21+
})
22+
})
23+
24+
describe('__PAGE__ segment handling', () => {
25+
it('should strip search params from __PAGE__ segments', () => {
26+
const flightRouterState: FlightRouterState = [
27+
'__PAGE__?{"param":"value","foo":"bar"}',
28+
{},
29+
]
30+
31+
const result = prepareFlightRouterStateForRequest(flightRouterState)
32+
const decoded = JSON.parse(decodeURIComponent(result))
33+
34+
expect(decoded[0]).toBe('__PAGE__')
35+
})
36+
37+
it('should preserve non-page segments', () => {
38+
const flightRouterState: FlightRouterState = ['regular-segment', {}]
39+
40+
const result = prepareFlightRouterStateForRequest(flightRouterState)
41+
const decoded = JSON.parse(decodeURIComponent(result))
42+
43+
expect(decoded[0]).toBe('regular-segment')
44+
})
45+
46+
it('should preserve dynamic segments', () => {
47+
const dynamicSegment: [string, string, 'd'] = ['slug', 'test-value', 'd']
48+
const flightRouterState: FlightRouterState = [dynamicSegment, {}]
49+
50+
const result = prepareFlightRouterStateForRequest(flightRouterState)
51+
const decoded = JSON.parse(decodeURIComponent(result))
52+
53+
expect(decoded[0]).toEqual(dynamicSegment)
54+
})
55+
})
56+
57+
describe('URL stripping', () => {
58+
it('should always set URL (index 2) to null', () => {
59+
const flightRouterState: FlightRouterState = [
60+
'segment',
61+
{},
62+
'/sensitive/url/path',
63+
null,
64+
]
65+
66+
const result = prepareFlightRouterStateForRequest(flightRouterState)
67+
const decoded = JSON.parse(decodeURIComponent(result))
68+
69+
expect(decoded[2]).toBeNull()
70+
})
71+
})
72+
73+
describe('refresh marker handling', () => {
74+
it('should preserve "refetch" marker', () => {
75+
const flightRouterState: FlightRouterState = [
76+
'segment',
77+
{},
78+
'/url',
79+
'refetch',
80+
]
81+
82+
const result = prepareFlightRouterStateForRequest(flightRouterState)
83+
const decoded = JSON.parse(decodeURIComponent(result))
84+
85+
expect(decoded[3]).toBe('refetch')
86+
})
87+
88+
it('should preserve "inside-shared-layout" marker', () => {
89+
const flightRouterState: FlightRouterState = [
90+
'segment',
91+
{},
92+
'/url',
93+
'inside-shared-layout',
94+
]
95+
96+
const result = prepareFlightRouterStateForRequest(flightRouterState)
97+
const decoded = JSON.parse(decodeURIComponent(result))
98+
99+
expect(decoded[3]).toBe('inside-shared-layout')
100+
})
101+
102+
it('should strip "refresh" marker (client-only)', () => {
103+
const flightRouterState: FlightRouterState = [
104+
'segment',
105+
{},
106+
'/url',
107+
'refresh',
108+
]
109+
110+
const result = prepareFlightRouterStateForRequest(flightRouterState)
111+
const decoded = JSON.parse(decodeURIComponent(result))
112+
113+
expect(decoded[3]).toBeNull()
114+
})
115+
116+
it('should strip null refresh marker', () => {
117+
const flightRouterState: FlightRouterState = ['segment', {}, '/url', null]
118+
119+
const result = prepareFlightRouterStateForRequest(flightRouterState)
120+
const decoded = JSON.parse(decodeURIComponent(result))
121+
122+
expect(decoded[3]).toBeNull()
123+
})
124+
})
125+
126+
describe('optional fields preservation', () => {
127+
it('should preserve isRootLayout when true', () => {
128+
const flightRouterState: FlightRouterState = [
129+
'segment',
130+
{},
131+
null,
132+
null,
133+
true,
134+
]
135+
136+
const result = prepareFlightRouterStateForRequest(flightRouterState)
137+
const decoded = JSON.parse(decodeURIComponent(result))
138+
139+
expect(decoded[4]).toBe(true)
140+
})
141+
142+
it('should preserve isRootLayout when false', () => {
143+
const flightRouterState: FlightRouterState = [
144+
'segment',
145+
{},
146+
null,
147+
null,
148+
false,
149+
]
150+
151+
const result = prepareFlightRouterStateForRequest(flightRouterState)
152+
const decoded = JSON.parse(decodeURIComponent(result))
153+
154+
expect(decoded[4]).toBe(false)
155+
})
156+
157+
it('should preserve hasLoadingBoundary', () => {
158+
const flightRouterState: FlightRouterState = [
159+
'segment',
160+
{},
161+
null,
162+
null,
163+
undefined,
164+
1, // HasLoadingBoundary value
165+
]
166+
167+
const result = prepareFlightRouterStateForRequest(flightRouterState)
168+
const decoded = JSON.parse(decodeURIComponent(result))
169+
170+
expect(decoded[5]).toBe(1)
171+
})
172+
173+
it('should handle minimal FlightRouterState (only segment and parallelRoutes)', () => {
174+
const flightRouterState: FlightRouterState = ['segment', {}]
175+
176+
const result = prepareFlightRouterStateForRequest(flightRouterState)
177+
const decoded = JSON.parse(decodeURIComponent(result))
178+
179+
expect(decoded).toEqual([
180+
'segment',
181+
{},
182+
null, // URL
183+
null, // refresh marker
184+
])
185+
})
186+
})
187+
188+
describe('recursive processing of parallel routes', () => {
189+
it('should recursively process nested parallel routes', () => {
190+
const flightRouterState: FlightRouterState = [
191+
'parent',
192+
{
193+
children: [
194+
'__PAGE__?{"nested":"param"}',
195+
{},
196+
'/nested/url',
197+
'refresh',
198+
],
199+
modal: ['modal-segment', {}, '/modal/url', 'refetch'],
200+
},
201+
'/parent/url',
202+
'inside-shared-layout',
203+
]
204+
205+
const result = prepareFlightRouterStateForRequest(flightRouterState)
206+
const decoded = JSON.parse(decodeURIComponent(result))
207+
208+
expect(decoded).toEqual([
209+
'parent',
210+
{
211+
children: [
212+
'__PAGE__', // search params stripped
213+
{},
214+
null, // URL stripped
215+
null, // 'refresh' marker stripped
216+
],
217+
modal: [
218+
'modal-segment',
219+
{},
220+
null, // URL stripped
221+
'refetch', // server marker preserved
222+
],
223+
},
224+
null, // URL stripped
225+
'inside-shared-layout', // server marker preserved
226+
])
227+
})
228+
229+
it('should handle deeply nested parallel routes', () => {
230+
const flightRouterState: FlightRouterState = [
231+
'root',
232+
{
233+
children: [
234+
'level1',
235+
{
236+
children: [
237+
'__PAGE__?{"deep":"nesting"}',
238+
{},
239+
'/deep/url',
240+
'refetch',
241+
],
242+
},
243+
],
244+
},
245+
]
246+
247+
const result = prepareFlightRouterStateForRequest(flightRouterState)
248+
const decoded = JSON.parse(decodeURIComponent(result))
249+
250+
expect(decoded[1].children[1].children[0]).toBe('__PAGE__')
251+
expect(decoded[1].children[1].children[2]).toBeNull()
252+
expect(decoded[1].children[1].children[3]).toBe('refetch')
253+
})
254+
})
255+
256+
describe('real-world scenarios', () => {
257+
it('should handle complex FlightRouterState with all features', () => {
258+
const complexState: FlightRouterState = [
259+
'__PAGE__?{"userId":"123"}',
260+
{
261+
children: [
262+
'dashboard',
263+
{
264+
modal: [
265+
'__PAGE__?{"modalParam":"data"}',
266+
{},
267+
'/modal/path',
268+
'refresh',
269+
false,
270+
HasLoadingBoundary.SegmentHasLoadingBoundary,
271+
],
272+
},
273+
'/dashboard/url',
274+
'refetch',
275+
true,
276+
1,
277+
],
278+
sidebar: [['slug', 'user-123', 'd'], {}, '/sidebar/url', null],
279+
},
280+
'/main/url',
281+
'inside-shared-layout',
282+
true,
283+
1,
284+
]
285+
286+
const result = prepareFlightRouterStateForRequest(complexState)
287+
const decoded = JSON.parse(decodeURIComponent(result))
288+
289+
// Root level checks
290+
expect(decoded[0]).toBe('__PAGE__') // search params stripped
291+
expect(decoded[2]).toBeNull() // URL stripped
292+
expect(decoded[3]).toBe('inside-shared-layout') // server marker preserved
293+
expect(decoded[4]).toBe(true) // isRootLayout preserved
294+
expect(decoded[5]).toBe(1) // hasLoadingBoundary preserved
295+
296+
// Children route checks
297+
const childrenRoute = decoded[1].children
298+
expect(childrenRoute[2]).toBeNull() // URL stripped
299+
expect(childrenRoute[3]).toBe('refetch') // server marker preserved
300+
expect(childrenRoute[4]).toBe(true) // isRootLayout preserved
301+
302+
// Modal route checks
303+
const modalRoute = childrenRoute[1].modal
304+
expect(modalRoute[0]).toBe('__PAGE__') // search params stripped
305+
expect(modalRoute[2]).toBeNull() // URL stripped
306+
expect(modalRoute[3]).toBeNull() // 'refresh' marker stripped
307+
expect(modalRoute[4]).toBe(false) // isRootLayout preserved
308+
expect(modalRoute[5]).toBe(HasLoadingBoundary.SegmentHasLoadingBoundary) // hasLoadingBoundary preserved
309+
310+
// Sidebar route (dynamic segment) checks
311+
const sidebarRoute = decoded[1].sidebar
312+
expect(sidebarRoute[0]).toEqual(['slug', 'user-123', 'd']) // dynamic segment preserved
313+
expect(sidebarRoute[2]).toBeNull() // URL stripped
314+
expect(sidebarRoute[3]).toBeNull() // null marker remains null
315+
})
316+
})
317+
})

0 commit comments

Comments
 (0)