@richie-router/react 0.1.1 → 0.1.3

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.
@@ -0,0 +1,413 @@
1
+ // packages/react/src/router.test.ts
2
+ var import_bun_test = require("bun:test");
3
+ var import_router = require("./router.cjs");
4
+ function createTestRouteTree(options) {
5
+ const rootRoute = import_router.createRootRoute({
6
+ component: () => null
7
+ });
8
+ const indexRoute = import_router.createFileRoute("/")({
9
+ component: () => null
10
+ });
11
+ const aboutRoute = import_router.createFileRoute("/about")({
12
+ component: () => null
13
+ });
14
+ aboutRoute._setServerHead(options?.serverHead);
15
+ return rootRoute._addFileChildren({
16
+ index: indexRoute,
17
+ about: aboutRoute
18
+ });
19
+ }
20
+ function createNestedHeadRouteTree() {
21
+ const rootRoute = import_router.createRootRoute({
22
+ component: () => null
23
+ });
24
+ const postsRoute = import_router.createFileRoute("/posts")({
25
+ component: () => null,
26
+ head: [
27
+ { tag: "title", children: "Posts" }
28
+ ]
29
+ });
30
+ const postRoute = import_router.createFileRoute("/posts/$postId")({
31
+ component: () => null
32
+ });
33
+ rootRoute._setServerHead(true);
34
+ postRoute._setServerHead(true);
35
+ postsRoute._addFileChildren({
36
+ post: postRoute
37
+ });
38
+ return rootRoute._addFileChildren({
39
+ posts: postsRoute
40
+ });
41
+ }
42
+ function createRootServerHeadTree() {
43
+ const rootRoute = import_router.createRootRoute({
44
+ component: () => null
45
+ });
46
+ const indexRoute = import_router.createFileRoute("/")({
47
+ component: () => null
48
+ });
49
+ const aboutRoute = import_router.createFileRoute("/about")({
50
+ component: () => null
51
+ });
52
+ rootRoute._setServerHead(true);
53
+ return rootRoute._addFileChildren({
54
+ index: indexRoute,
55
+ about: aboutRoute
56
+ });
57
+ }
58
+ function createNestedServerHeadTree() {
59
+ const rootRoute = import_router.createRootRoute({
60
+ component: () => null
61
+ });
62
+ const postsRoute = import_router.createFileRoute("/posts")({
63
+ component: () => null
64
+ });
65
+ const postRoute = import_router.createFileRoute("/posts/$postId")({
66
+ component: () => null
67
+ });
68
+ rootRoute._setServerHead(true);
69
+ postsRoute._setServerHead(true);
70
+ postRoute._setServerHead(true);
71
+ postsRoute._addFileChildren({
72
+ post: postRoute
73
+ });
74
+ return rootRoute._addFileChildren({
75
+ posts: postsRoute
76
+ });
77
+ }
78
+ import_bun_test.describe("createRouter basePath", () => {
79
+ import_bun_test.test("strips the basePath from the current history location", () => {
80
+ const history = import_router.createMemoryHistory({
81
+ initialEntries: ["/project/about?tab=team#bio"]
82
+ });
83
+ const router = import_router.createRouter({
84
+ routeTree: createTestRouteTree(),
85
+ history,
86
+ basePath: "/project"
87
+ });
88
+ import_bun_test.expect(router.state.location.pathname).toBe("/about");
89
+ import_bun_test.expect(router.state.location.href).toBe("/about?tab=team#bio");
90
+ import_bun_test.expect(router.state.matches.at(-1)?.route.fullPath).toBe("/about");
91
+ });
92
+ import_bun_test.test("prefixes generated hrefs and history writes with the basePath", async () => {
93
+ const history = import_router.createMemoryHistory({
94
+ initialEntries: ["/project"]
95
+ });
96
+ const router = import_router.createRouter({
97
+ routeTree: createTestRouteTree(),
98
+ history,
99
+ basePath: "/project"
100
+ });
101
+ import_bun_test.expect(router.buildHref({ to: "/about" })).toBe("/project/about");
102
+ await router.navigate({
103
+ to: "/about",
104
+ search: {
105
+ tab: "team"
106
+ }
107
+ });
108
+ import_bun_test.expect(history.location.href).toBe("/project/about?tab=team");
109
+ import_bun_test.expect(router.state.location.href).toBe("/about?tab=team");
110
+ });
111
+ import_bun_test.test("uses the basePath for the default head API endpoint", async () => {
112
+ const history = import_router.createMemoryHistory({
113
+ initialEntries: ["/project/about"]
114
+ });
115
+ const fetchCalls = [];
116
+ const originalFetch = globalThis.fetch;
117
+ globalThis.fetch = async (input) => {
118
+ fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
119
+ return new Response(JSON.stringify({
120
+ head: [],
121
+ routeHeads: [
122
+ { routeId: "/about", head: [] }
123
+ ]
124
+ }), {
125
+ status: 200,
126
+ headers: {
127
+ "content-type": "application/json"
128
+ }
129
+ });
130
+ };
131
+ try {
132
+ const router = import_router.createRouter({
133
+ routeTree: createTestRouteTree({ serverHead: true }),
134
+ history,
135
+ basePath: "/project"
136
+ });
137
+ await router.load();
138
+ import_bun_test.expect(fetchCalls).toHaveLength(1);
139
+ import_bun_test.expect(fetchCalls[0]).toBe("/project/head-api?href=%2Fproject%2Fabout");
140
+ } finally {
141
+ globalThis.fetch = originalFetch;
142
+ }
143
+ });
144
+ import_bun_test.test("uses one document head request and preserves inline head precedence", async () => {
145
+ const history = import_router.createMemoryHistory({
146
+ initialEntries: ["/posts/alpha"]
147
+ });
148
+ const fetchCalls = [];
149
+ const originalFetch = globalThis.fetch;
150
+ globalThis.fetch = async (input) => {
151
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
152
+ fetchCalls.push(url);
153
+ return new Response(JSON.stringify({
154
+ head: [
155
+ { tag: "title", children: "Site" },
156
+ { tag: "title", children: "Alpha" }
157
+ ],
158
+ routeHeads: [
159
+ {
160
+ routeId: "__root__",
161
+ head: [{ tag: "title", children: "Site" }],
162
+ staleTime: 60000
163
+ },
164
+ {
165
+ routeId: "/posts/$postId",
166
+ head: [{ tag: "title", children: "Alpha" }],
167
+ staleTime: 1e4
168
+ }
169
+ ],
170
+ staleTime: 1e4
171
+ }), {
172
+ status: 200,
173
+ headers: {
174
+ "content-type": "application/json"
175
+ }
176
+ });
177
+ };
178
+ try {
179
+ const router = import_router.createRouter({
180
+ routeTree: createNestedHeadRouteTree(),
181
+ history
182
+ });
183
+ await router.load();
184
+ import_bun_test.expect(fetchCalls).toEqual(["/head-api?href=%2Fposts%2Falpha"]);
185
+ import_bun_test.expect(router.state.head).toEqual([
186
+ { tag: "title", children: "Alpha" }
187
+ ]);
188
+ } finally {
189
+ globalThis.fetch = originalFetch;
190
+ }
191
+ });
192
+ import_bun_test.test("reuses a fresh root server head across navigations without refetching", async () => {
193
+ const history = import_router.createMemoryHistory({
194
+ initialEntries: ["/"]
195
+ });
196
+ const fetchCalls = [];
197
+ const originalFetch = globalThis.fetch;
198
+ globalThis.fetch = async (input) => {
199
+ fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
200
+ return new Response(JSON.stringify({
201
+ head: [
202
+ { tag: "title", children: "Site" }
203
+ ],
204
+ routeHeads: [
205
+ {
206
+ routeId: "__root__",
207
+ head: [{ tag: "title", children: "Site" }],
208
+ staleTime: 60000
209
+ }
210
+ ],
211
+ staleTime: 60000
212
+ }), {
213
+ status: 200,
214
+ headers: {
215
+ "content-type": "application/json"
216
+ }
217
+ });
218
+ };
219
+ try {
220
+ const router = import_router.createRouter({
221
+ routeTree: createRootServerHeadTree(),
222
+ history
223
+ });
224
+ await router.load();
225
+ await router.navigate({
226
+ to: "/about"
227
+ });
228
+ import_bun_test.expect(fetchCalls).toEqual(["/head-api?href=%2F"]);
229
+ } finally {
230
+ globalThis.fetch = originalFetch;
231
+ }
232
+ });
233
+ import_bun_test.test("seeds the route head cache from the dehydrated snapshot", async () => {
234
+ const history = import_router.createMemoryHistory({
235
+ initialEntries: ["/about"]
236
+ });
237
+ const fetchCalls = [];
238
+ const originalFetch = globalThis.fetch;
239
+ const globalWithWindow = globalThis;
240
+ const originalWindow = globalWithWindow.window;
241
+ globalThis.fetch = async (input) => {
242
+ fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
243
+ return new Response(JSON.stringify({
244
+ head: [
245
+ { tag: "title", children: "Site" }
246
+ ],
247
+ routeHeads: [
248
+ {
249
+ routeId: "__root__",
250
+ head: [{ tag: "title", children: "Site" }],
251
+ staleTime: 60000
252
+ }
253
+ ],
254
+ staleTime: 60000
255
+ }), {
256
+ status: 200,
257
+ headers: {
258
+ "content-type": "application/json"
259
+ }
260
+ });
261
+ };
262
+ globalWithWindow.window = {
263
+ __RICHIE_ROUTER_HEAD__: {
264
+ href: "/about",
265
+ head: [
266
+ { tag: "title", children: "Site" }
267
+ ],
268
+ routeHeads: [
269
+ {
270
+ routeId: "__root__",
271
+ head: [{ tag: "title", children: "Site" }],
272
+ staleTime: 60000
273
+ }
274
+ ]
275
+ }
276
+ };
277
+ try {
278
+ const router = import_router.createRouter({
279
+ routeTree: createRootServerHeadTree(),
280
+ history
281
+ });
282
+ await router.load();
283
+ import_bun_test.expect(fetchCalls).toHaveLength(0);
284
+ import_bun_test.expect(router.state.head).toEqual([
285
+ { tag: "title", children: "Site" }
286
+ ]);
287
+ } finally {
288
+ globalThis.fetch = originalFetch;
289
+ if (originalWindow === undefined) {
290
+ Reflect.deleteProperty(globalWithWindow, "window");
291
+ } else {
292
+ originalWindow.__RICHIE_ROUTER_HEAD__ = undefined;
293
+ globalWithWindow.window = originalWindow;
294
+ }
295
+ }
296
+ });
297
+ import_bun_test.test("reuses the initial merged head snapshot without fetching when the branch has no inline head", async () => {
298
+ const history = import_router.createMemoryHistory({
299
+ initialEntries: ["/about"]
300
+ });
301
+ const fetchCalls = [];
302
+ const originalFetch = globalThis.fetch;
303
+ const globalWithWindow = globalThis;
304
+ const originalWindow = globalWithWindow.window;
305
+ globalThis.fetch = async (input) => {
306
+ fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
307
+ return new Response("{}", {
308
+ status: 500
309
+ });
310
+ };
311
+ globalWithWindow.window = {
312
+ __RICHIE_ROUTER_HEAD__: {
313
+ href: "/about",
314
+ head: [
315
+ { tag: "title", children: "About from SSR" }
316
+ ]
317
+ }
318
+ };
319
+ try {
320
+ const router = import_router.createRouter({
321
+ routeTree: createTestRouteTree({ serverHead: true }),
322
+ history
323
+ });
324
+ await router.load();
325
+ import_bun_test.expect(fetchCalls).toHaveLength(0);
326
+ import_bun_test.expect(router.state.head).toEqual([
327
+ { tag: "title", children: "About from SSR" }
328
+ ]);
329
+ } finally {
330
+ globalThis.fetch = originalFetch;
331
+ if (originalWindow === undefined) {
332
+ Reflect.deleteProperty(globalWithWindow, "window");
333
+ } else {
334
+ originalWindow.__RICHIE_ROUTER_HEAD__ = undefined;
335
+ globalWithWindow.window = originalWindow;
336
+ }
337
+ }
338
+ });
339
+ import_bun_test.test("uses the merged document head without route fallback requests when the branch has no inline head", async () => {
340
+ const history = import_router.createMemoryHistory({
341
+ initialEntries: ["/posts/alpha"]
342
+ });
343
+ const fetchCalls = [];
344
+ const originalFetch = globalThis.fetch;
345
+ globalThis.fetch = async (input) => {
346
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
347
+ fetchCalls.push(url);
348
+ return new Response(JSON.stringify({
349
+ head: [
350
+ { tag: "meta", name: "description", content: "Nested server head" },
351
+ { tag: "title", children: "Alpha" }
352
+ ],
353
+ staleTime: 1e4
354
+ }), {
355
+ status: 200,
356
+ headers: {
357
+ "content-type": "application/json"
358
+ }
359
+ });
360
+ };
361
+ try {
362
+ const router = import_router.createRouter({
363
+ routeTree: createNestedServerHeadTree(),
364
+ history
365
+ });
366
+ await router.load();
367
+ import_bun_test.expect(fetchCalls).toEqual(["/head-api?href=%2Fposts%2Falpha"]);
368
+ import_bun_test.expect(router.state.head).toEqual([
369
+ { tag: "meta", name: "description", content: "Nested server head" },
370
+ { tag: "title", children: "Alpha" }
371
+ ]);
372
+ } finally {
373
+ globalThis.fetch = originalFetch;
374
+ }
375
+ });
376
+ import_bun_test.test("keeps loadRouteHead as the route-scoped override path", async () => {
377
+ const history = import_router.createMemoryHistory({
378
+ initialEntries: ["/about"]
379
+ });
380
+ const fetchCalls = [];
381
+ const loadRouteHeadCalls = [];
382
+ const originalFetch = globalThis.fetch;
383
+ globalThis.fetch = async (input) => {
384
+ fetchCalls.push(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
385
+ return new Response("{}", {
386
+ status: 500
387
+ });
388
+ };
389
+ try {
390
+ const router = import_router.createRouter({
391
+ routeTree: createTestRouteTree({ serverHead: true }),
392
+ history,
393
+ loadRouteHead: async ({ routeId }) => {
394
+ loadRouteHeadCalls.push(routeId);
395
+ return {
396
+ head: [
397
+ { tag: "title", children: "About override" }
398
+ ],
399
+ staleTime: 1000
400
+ };
401
+ }
402
+ });
403
+ await router.load();
404
+ import_bun_test.expect(loadRouteHeadCalls).toEqual(["/about"]);
405
+ import_bun_test.expect(fetchCalls).toHaveLength(0);
406
+ import_bun_test.expect(router.state.head).toEqual([
407
+ { tag: "title", children: "About override" }
408
+ ]);
409
+ } finally {
410
+ globalThis.fetch = originalFetch;
411
+ }
412
+ });
413
+ });