@alepha/react 0.14.2 → 0.14.4

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 (57) hide show
  1. package/dist/auth/index.browser.js +29 -14
  2. package/dist/auth/index.browser.js.map +1 -1
  3. package/dist/auth/index.js +960 -195
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/core/index.d.ts +4 -0
  6. package/dist/core/index.d.ts.map +1 -1
  7. package/dist/core/index.js +7 -4
  8. package/dist/core/index.js.map +1 -1
  9. package/dist/head/index.browser.js +59 -19
  10. package/dist/head/index.browser.js.map +1 -1
  11. package/dist/head/index.d.ts +99 -560
  12. package/dist/head/index.d.ts.map +1 -1
  13. package/dist/head/index.js +92 -87
  14. package/dist/head/index.js.map +1 -1
  15. package/dist/router/index.browser.js +30 -15
  16. package/dist/router/index.browser.js.map +1 -1
  17. package/dist/router/index.d.ts +616 -192
  18. package/dist/router/index.d.ts.map +1 -1
  19. package/dist/router/index.js +961 -196
  20. package/dist/router/index.js.map +1 -1
  21. package/package.json +4 -4
  22. package/src/auth/__tests__/$auth.spec.ts +188 -0
  23. package/src/core/__tests__/Router.spec.tsx +169 -0
  24. package/src/core/hooks/useAction.browser.spec.tsx +569 -0
  25. package/src/core/hooks/useAction.ts +11 -0
  26. package/src/form/hooks/useForm.browser.spec.tsx +366 -0
  27. package/src/head/helpers/SeoExpander.spec.ts +203 -0
  28. package/src/head/hooks/useHead.spec.tsx +288 -0
  29. package/src/head/index.ts +11 -28
  30. package/src/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  31. package/src/head/providers/BrowserHeadProvider.ts +25 -19
  32. package/src/head/providers/HeadProvider.ts +76 -10
  33. package/src/head/providers/ServerHeadProvider.ts +22 -138
  34. package/src/i18n/__tests__/integration.spec.tsx +239 -0
  35. package/src/i18n/components/Localize.spec.tsx +357 -0
  36. package/src/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  37. package/src/i18n/providers/I18nProvider.spec.ts +389 -0
  38. package/src/router/__tests__/page-head-browser.browser.spec.ts +91 -0
  39. package/src/router/__tests__/page-head.spec.ts +44 -0
  40. package/src/router/__tests__/seo-head.spec.ts +121 -0
  41. package/src/router/atoms/ssrManifestAtom.ts +60 -0
  42. package/src/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  43. package/src/router/errors/Redirection.ts +1 -1
  44. package/src/router/index.shared.ts +1 -0
  45. package/src/router/index.ts +16 -2
  46. package/src/router/primitives/$page.browser.spec.tsx +702 -0
  47. package/src/router/primitives/$page.spec.tsx +702 -0
  48. package/src/router/primitives/$page.ts +46 -10
  49. package/src/router/providers/ReactBrowserProvider.ts +14 -29
  50. package/src/router/providers/ReactBrowserRouterProvider.ts +5 -0
  51. package/src/router/providers/ReactPageProvider.ts +11 -4
  52. package/src/router/providers/ReactServerProvider.spec.tsx +316 -0
  53. package/src/router/providers/ReactServerProvider.ts +331 -315
  54. package/src/router/providers/ReactServerTemplateProvider.ts +775 -0
  55. package/src/router/providers/SSRManifestProvider.ts +365 -0
  56. package/src/router/services/ReactPageServerService.ts +5 -3
  57. package/src/router/services/ReactRouter.ts +3 -3
@@ -0,0 +1,702 @@
1
+ import { AlephaReact } from "@alepha/react";
2
+ import { $page, NestedView, Redirection, ReactRouter } from "../index.browser.ts";
3
+ import { waitFor } from "@testing-library/dom";
4
+ import { Alepha, t } from "alepha";
5
+ import { act } from "react";
6
+ import { beforeEach, describe, expect, it, vi } from "vitest";
7
+
8
+ describe("$page browser tests", () => {
9
+ let alepha: Alepha;
10
+
11
+ beforeEach(() => {
12
+ // Reset document state
13
+ document.title = "";
14
+ document.head.innerHTML = "";
15
+ document.body.innerHTML = '<div id="root"></div>';
16
+ });
17
+
18
+ it("should render home page via router.go", async () => {
19
+ class App {
20
+ home = $page({
21
+ path: "/",
22
+ component: () => <div data-testid="home">Welcome Home</div>,
23
+ });
24
+ }
25
+
26
+ alepha = Alepha.create().with(AlephaReact).with(App);
27
+ await alepha.start();
28
+
29
+ const router = alepha.inject(ReactRouter);
30
+
31
+ await act(async () => {
32
+ await router.go("/");
33
+ });
34
+
35
+ await waitFor(() => {
36
+ const element = document.querySelector('[data-testid="home"]');
37
+ expect(element).toBeDefined();
38
+ expect(element?.textContent).toBe("Welcome Home");
39
+ });
40
+ });
41
+
42
+ it("should navigate between pages", async () => {
43
+ class App {
44
+ home = $page({
45
+ path: "/",
46
+ component: () => <div data-testid="home">Home</div>,
47
+ });
48
+
49
+ about = $page({
50
+ path: "/about",
51
+ component: () => <div data-testid="about">About Us</div>,
52
+ });
53
+ }
54
+
55
+ alepha = Alepha.create().with(AlephaReact).with(App);
56
+ await alepha.start();
57
+
58
+ const router = alepha.inject(ReactRouter);
59
+
60
+ // Navigate to home
61
+ await act(async () => {
62
+ await router.go("/");
63
+ });
64
+
65
+ await waitFor(() => {
66
+ expect(document.querySelector('[data-testid="home"]')).toBeDefined();
67
+ });
68
+
69
+ // Navigate to about
70
+ await act(async () => {
71
+ await router.go("/about");
72
+ });
73
+
74
+ await waitFor(() => {
75
+ expect(document.querySelector('[data-testid="about"]')).toBeDefined();
76
+ expect(document.querySelector('[data-testid="about"]')?.textContent).toBe(
77
+ "About Us",
78
+ );
79
+ });
80
+ });
81
+
82
+ describe("resolve function", () => {
83
+ it("should pass resolved data to component", async () => {
84
+ class App {
85
+ user = $page({
86
+ path: "/user/:id",
87
+ schema: {
88
+ params: t.object({
89
+ id: t.text(),
90
+ }),
91
+ },
92
+ loader: ({ params }) => ({
93
+ userId: params.id,
94
+ userName: `User ${params.id}`,
95
+ }),
96
+ component: ({
97
+ userId,
98
+ userName,
99
+ }: {
100
+ userId: string;
101
+ userName: string;
102
+ }) => (
103
+ <div data-testid="user">
104
+ <span data-testid="id">{userId}</span>
105
+ <span data-testid="name">{userName}</span>
106
+ </div>
107
+ ),
108
+ });
109
+ }
110
+
111
+ alepha = Alepha.create().with(AlephaReact).with(App);
112
+ await alepha.start();
113
+
114
+ const router = alepha.inject(ReactRouter);
115
+
116
+ await act(async () => {
117
+ await router.go("/user/123");
118
+ });
119
+
120
+ await waitFor(() => {
121
+ expect(document.querySelector('[data-testid="id"]')?.textContent).toBe(
122
+ "123",
123
+ );
124
+ expect(
125
+ document.querySelector('[data-testid="name"]')?.textContent,
126
+ ).toBe("User 123");
127
+ });
128
+ });
129
+
130
+ it.skip("should handle async resolve function", async () => {
131
+ // Skipped: Timing issues with multiple rapid navigations in jsdom
132
+ class App {
133
+ async = $page({
134
+ path: "/async",
135
+ loader: async () => {
136
+ await new Promise((resolve) => setTimeout(resolve, 10));
137
+ return { message: "Loaded async data" };
138
+ },
139
+ component: ({ message }: { message: string }) => (
140
+ <div data-testid="async">{message}</div>
141
+ ),
142
+ });
143
+ }
144
+
145
+ alepha = Alepha.create().with(AlephaReact).with(App);
146
+ await alepha.start();
147
+
148
+ const router = alepha.inject(ReactRouter);
149
+
150
+ await act(async () => {
151
+ await router.go("/async");
152
+ });
153
+
154
+ await waitFor(
155
+ () => {
156
+ expect(
157
+ document.querySelector('[data-testid="async"]')?.textContent,
158
+ ).toBe("Loaded async data");
159
+ },
160
+ { timeout: 3000 },
161
+ );
162
+ });
163
+ });
164
+
165
+ describe("errorHandler", () => {
166
+ it("should handle errors and redirect", async () => {
167
+ let isAuthenticated = false;
168
+
169
+ class App {
170
+ login = $page({
171
+ path: "/login",
172
+ component: () => <div data-testid="login">Login Page</div>,
173
+ });
174
+
175
+ protected = $page({
176
+ path: "/protected",
177
+ loader: () => {
178
+ if (!isAuthenticated) {
179
+ throw new Error("Unauthorized");
180
+ }
181
+ return { data: "secret" };
182
+ },
183
+ errorHandler: (error) => {
184
+ if (error.message === "Unauthorized") {
185
+ return new Redirection("/login");
186
+ }
187
+ return undefined;
188
+ },
189
+ component: ({ data }: { data: string }) => (
190
+ <div data-testid="protected">Data: {data}</div>
191
+ ),
192
+ });
193
+ }
194
+
195
+ alepha = Alepha.create().with(AlephaReact).with(App);
196
+ await alepha.start();
197
+
198
+ const router = alepha.inject(ReactRouter);
199
+
200
+ // Try to access protected - should redirect to login
201
+ await act(async () => {
202
+ await router.go("/protected");
203
+ });
204
+
205
+ await waitFor(() => {
206
+ expect(document.querySelector('[data-testid="login"]')).toBeDefined();
207
+ });
208
+
209
+ // Now authenticate
210
+ isAuthenticated = true;
211
+
212
+ await act(async () => {
213
+ await router.go("/protected");
214
+ });
215
+
216
+ await waitFor(() => {
217
+ expect(
218
+ document.querySelector('[data-testid="protected"]'),
219
+ ).toBeDefined();
220
+ expect(
221
+ document.querySelector('[data-testid="protected"]')?.textContent,
222
+ ).toBe("Data: secret");
223
+ });
224
+ });
225
+
226
+ it("should render error handler ReactNode", async () => {
227
+ class App {
228
+ errorPage = $page({
229
+ path: "/error",
230
+ loader: () => {
231
+ throw new Error("Something went wrong");
232
+ },
233
+ errorHandler: (error) => (
234
+ <div data-testid="error">Error: {error.message}</div>
235
+ ),
236
+ component: () => <div>Should not render</div>,
237
+ });
238
+ }
239
+
240
+ alepha = Alepha.create().with(AlephaReact).with(App);
241
+ await alepha.start();
242
+
243
+ const router = alepha.inject(ReactRouter);
244
+
245
+ await act(async () => {
246
+ await router.go("/error");
247
+ });
248
+
249
+ await waitFor(() => {
250
+ expect(
251
+ document.querySelector('[data-testid="error"]')?.textContent,
252
+ ).toBe("Error: Something went wrong");
253
+ });
254
+ });
255
+ });
256
+
257
+ describe("parent-child hierarchy", () => {
258
+ it("should render parent layout with nested child", async () => {
259
+ class App {
260
+ layout = $page({
261
+ path: "/",
262
+ loader: () => ({ appName: "My App" }),
263
+ component: ({ appName }: { appName: string }) => (
264
+ <div data-testid="layout">
265
+ <header data-testid="header">{appName}</header>
266
+ <NestedView />
267
+ </div>
268
+ ),
269
+ children: [],
270
+ });
271
+
272
+ home = $page({
273
+ path: "/",
274
+ parent: this.layout,
275
+ component: () => <div data-testid="home">Home Content</div>,
276
+ });
277
+
278
+ about = $page({
279
+ path: "/about",
280
+ parent: this.layout,
281
+ component: () => <div data-testid="about">About Content</div>,
282
+ });
283
+ }
284
+
285
+ alepha = Alepha.create().with(AlephaReact).with(App);
286
+ await alepha.start();
287
+
288
+ const router = alepha.inject(ReactRouter);
289
+
290
+ // Navigate to home - should show layout and home
291
+ await act(async () => {
292
+ await router.go("/");
293
+ });
294
+
295
+ await waitFor(() => {
296
+ expect(
297
+ document.querySelector('[data-testid="header"]')?.textContent,
298
+ ).toBe("My App");
299
+ expect(document.querySelector('[data-testid="home"]')).toBeDefined();
300
+ });
301
+
302
+ // Navigate to about - layout should persist
303
+ await act(async () => {
304
+ await router.go("/about");
305
+ });
306
+
307
+ await waitFor(() => {
308
+ expect(
309
+ document.querySelector('[data-testid="header"]')?.textContent,
310
+ ).toBe("My App");
311
+ expect(document.querySelector('[data-testid="about"]')).toBeDefined();
312
+ });
313
+ });
314
+
315
+ it("should pass parent resolved data to children", async () => {
316
+ class App {
317
+ layout = $page({
318
+ path: "/",
319
+ loader: () => ({ theme: "dark" }),
320
+ component: ({ theme }: { theme: string }) => (
321
+ <div data-testid="layout" data-theme={theme}>
322
+ <NestedView />
323
+ </div>
324
+ ),
325
+ children: [],
326
+ });
327
+
328
+ page = $page({
329
+ path: "/page",
330
+ parent: this.layout,
331
+ loader: ({ theme }) => ({
332
+ message: `Theme is ${theme}`,
333
+ }),
334
+ component: ({ message }: { message: string }) => (
335
+ <div data-testid="page">{message}</div>
336
+ ),
337
+ });
338
+ }
339
+
340
+ alepha = Alepha.create().with(AlephaReact).with(App);
341
+ await alepha.start();
342
+
343
+ const router = alepha.inject(ReactRouter);
344
+
345
+ await act(async () => {
346
+ await router.go("/page");
347
+ });
348
+
349
+ await waitFor(() => {
350
+ expect(
351
+ document
352
+ .querySelector('[data-testid="layout"]')
353
+ ?.getAttribute("data-theme"),
354
+ ).toBe("dark");
355
+ expect(
356
+ document.querySelector('[data-testid="page"]')?.textContent,
357
+ ).toBe("Theme is dark");
358
+ });
359
+ });
360
+
361
+ it("should handle multi-level nesting", async () => {
362
+ class App {
363
+ root = $page({
364
+ path: "/",
365
+ loader: () => ({ level: "root" }),
366
+ component: ({ level }: { level: string }) => (
367
+ <div data-testid="root">
368
+ {level}
369
+ <NestedView />
370
+ </div>
371
+ ),
372
+ });
373
+
374
+ section = $page({
375
+ path: "/section",
376
+ parent: this.root,
377
+ loader: ({ level }) => ({ level: `${level} > section` }),
378
+ component: ({ level }: { level: string }) => (
379
+ <div data-testid="section">
380
+ {level}
381
+ <NestedView />
382
+ </div>
383
+ ),
384
+ });
385
+
386
+ page = $page({
387
+ path: "/page",
388
+ parent: this.section,
389
+ loader: ({ level }) => ({ level: `${level} > page` }),
390
+ component: ({ level }: { level: string }) => (
391
+ <div data-testid="page">{level}</div>
392
+ ),
393
+ });
394
+ }
395
+
396
+ alepha = Alepha.create().with(AlephaReact).with(App);
397
+ await alepha.start();
398
+
399
+ const router = alepha.inject(ReactRouter);
400
+
401
+ await act(async () => {
402
+ await router.go("/section/page");
403
+ });
404
+
405
+ await waitFor(() => {
406
+ expect(document.querySelector('[data-testid="root"]')).toBeDefined();
407
+ expect(document.querySelector('[data-testid="section"]')).toBeDefined();
408
+ expect(
409
+ document.querySelector('[data-testid="page"]')?.textContent,
410
+ ).toBe("root > section > page");
411
+ });
412
+ });
413
+ });
414
+
415
+ describe("onLeave hook", () => {
416
+ it("should call onLeave when navigating away", async () => {
417
+ const onLeaveSpy = vi.fn();
418
+
419
+ class App {
420
+ home = $page({
421
+ path: "/",
422
+ onLeave: onLeaveSpy,
423
+ component: () => <div data-testid="home">Home</div>,
424
+ });
425
+
426
+ about = $page({
427
+ path: "/about",
428
+ component: () => <div data-testid="about">About</div>,
429
+ });
430
+ }
431
+
432
+ alepha = Alepha.create().with(AlephaReact).with(App);
433
+ await alepha.start();
434
+
435
+ const router = alepha.inject(ReactRouter);
436
+
437
+ // Navigate to home
438
+ await act(async () => {
439
+ await router.go("/");
440
+ });
441
+
442
+ await waitFor(() => {
443
+ expect(document.querySelector('[data-testid="home"]')).toBeDefined();
444
+ });
445
+
446
+ expect(onLeaveSpy).not.toHaveBeenCalled();
447
+
448
+ // Navigate away from home to about
449
+ await act(async () => {
450
+ await router.go("/about");
451
+ });
452
+
453
+ await waitFor(() => {
454
+ expect(document.querySelector('[data-testid="about"]')).toBeDefined();
455
+ });
456
+
457
+ // onLeave should have been called
458
+ expect(onLeaveSpy).toHaveBeenCalledTimes(1);
459
+ });
460
+ });
461
+
462
+ describe("schema validation", () => {
463
+ it("should validate and parse params", async () => {
464
+ class App {
465
+ user = $page({
466
+ path: "/user/:id",
467
+ schema: {
468
+ params: t.object({
469
+ id: t.text(),
470
+ }),
471
+ },
472
+ loader: ({ params }) => ({
473
+ userId: params.id,
474
+ }),
475
+ component: ({ userId }: { userId: string }) => (
476
+ <div data-testid="user">User: {userId}</div>
477
+ ),
478
+ });
479
+ }
480
+
481
+ alepha = Alepha.create().with(AlephaReact).with(App);
482
+ await alepha.start();
483
+
484
+ const router = alepha.inject(ReactRouter);
485
+
486
+ await act(async () => {
487
+ await router.go("/user/abc123");
488
+ });
489
+
490
+ await waitFor(() => {
491
+ expect(
492
+ document.querySelector('[data-testid="user"]')?.textContent,
493
+ ).toBe("User: abc123");
494
+ });
495
+ });
496
+
497
+ it("should validate and parse query params", async () => {
498
+ class App {
499
+ search = $page({
500
+ path: "/search",
501
+ schema: {
502
+ query: t.object({
503
+ q: t.text({ default: "" }),
504
+ page: t.number({ default: 1 }),
505
+ }),
506
+ },
507
+ loader: ({ query }) => ({
508
+ searchQuery: query.q,
509
+ currentPage: query.page,
510
+ }),
511
+ component: ({
512
+ searchQuery,
513
+ currentPage,
514
+ }: {
515
+ searchQuery: string;
516
+ currentPage: number;
517
+ }) => (
518
+ <div data-testid="search">
519
+ <div data-testid="query">{searchQuery}</div>
520
+ <div data-testid="page">{currentPage}</div>
521
+ </div>
522
+ ),
523
+ });
524
+ }
525
+
526
+ alepha = Alepha.create().with(AlephaReact).with(App);
527
+ await alepha.start();
528
+
529
+ const router = alepha.inject(ReactRouter);
530
+
531
+ await act(async () => {
532
+ await router.go("/search?q=typescript&page=2");
533
+ });
534
+
535
+ await waitFor(() => {
536
+ expect(
537
+ document.querySelector('[data-testid="query"]')?.textContent,
538
+ ).toBe("typescript");
539
+ expect(
540
+ document.querySelector('[data-testid="page"]')?.textContent,
541
+ ).toBe("2");
542
+ });
543
+ });
544
+
545
+ it.skip("should update when navigating with different query params", async () => {
546
+ // Skipped: Multiple rapid navigations to same page in jsdom have timing issues
547
+ class App {
548
+ search = $page({
549
+ path: "/search",
550
+ schema: {
551
+ query: t.object({
552
+ q: t.text({ default: "" }),
553
+ page: t.number({ default: 1 }),
554
+ }),
555
+ },
556
+ loader: ({ query }) => ({
557
+ searchQuery: query.q,
558
+ currentPage: query.page,
559
+ }),
560
+ component: ({
561
+ searchQuery,
562
+ currentPage,
563
+ }: {
564
+ searchQuery: string;
565
+ currentPage: number;
566
+ }) => (
567
+ <div data-testid="search">
568
+ <div data-testid="query">{searchQuery}</div>
569
+ <div data-testid="page">{currentPage}</div>
570
+ </div>
571
+ ),
572
+ });
573
+ }
574
+
575
+ alepha = Alepha.create().with(AlephaReact).with(App);
576
+ await alepha.start();
577
+
578
+ const router = alepha.inject(ReactRouter);
579
+
580
+ // Navigate with query params
581
+ await act(async () => {
582
+ await router.go("/search?q=alepha&page=5");
583
+ });
584
+
585
+ await waitFor(() => {
586
+ expect(
587
+ document.querySelector('[data-testid="query"]')?.textContent,
588
+ ).toBe("alepha");
589
+ expect(
590
+ document.querySelector('[data-testid="page"]')?.textContent,
591
+ ).toBe("5");
592
+ });
593
+ });
594
+
595
+ it("should validate params and query together", async () => {
596
+ class App {
597
+ userPosts = $page({
598
+ path: "/users/:userId/posts",
599
+ schema: {
600
+ params: t.object({
601
+ userId: t.text(),
602
+ }),
603
+ query: t.object({
604
+ sort: t.text({ default: "recent" }),
605
+ limit: t.number({ default: 10 }),
606
+ }),
607
+ },
608
+ loader: ({ params, query }) => ({
609
+ userId: params.userId,
610
+ sortBy: query.sort,
611
+ limit: query.limit,
612
+ }),
613
+ component: ({
614
+ userId,
615
+ sortBy,
616
+ limit,
617
+ }: {
618
+ userId: string;
619
+ sortBy: string;
620
+ limit: number;
621
+ }) => (
622
+ <div data-testid="posts">
623
+ <div data-testid="user">{userId}</div>
624
+ <div data-testid="sort">{sortBy}</div>
625
+ <div data-testid="limit">{limit}</div>
626
+ </div>
627
+ ),
628
+ });
629
+ }
630
+
631
+ alepha = Alepha.create().with(AlephaReact).with(App);
632
+ await alepha.start();
633
+
634
+ const router = alepha.inject(ReactRouter);
635
+
636
+ await act(async () => {
637
+ await router.go("/users/john/posts?sort=popular&limit=20");
638
+ });
639
+
640
+ await waitFor(() => {
641
+ expect(
642
+ document.querySelector('[data-testid="user"]')?.textContent,
643
+ ).toBe("john");
644
+ expect(
645
+ document.querySelector('[data-testid="sort"]')?.textContent,
646
+ ).toBe("popular");
647
+ expect(
648
+ document.querySelector('[data-testid="limit"]')?.textContent,
649
+ ).toBe("20");
650
+ });
651
+ });
652
+
653
+ it("should use default values for missing query params", async () => {
654
+ class App {
655
+ search = $page({
656
+ path: "/search",
657
+ schema: {
658
+ query: t.object({
659
+ q: t.text({ default: "default search" }),
660
+ page: t.number({ default: 1 }),
661
+ }),
662
+ },
663
+ loader: ({ query }) => ({
664
+ searchQuery: query.q,
665
+ currentPage: query.page,
666
+ }),
667
+ component: ({
668
+ searchQuery,
669
+ currentPage,
670
+ }: {
671
+ searchQuery: string;
672
+ currentPage: number;
673
+ }) => (
674
+ <div data-testid="search">
675
+ <div data-testid="query">{searchQuery}</div>
676
+ <div data-testid="page">{currentPage}</div>
677
+ </div>
678
+ ),
679
+ });
680
+ }
681
+
682
+ alepha = Alepha.create().with(AlephaReact).with(App);
683
+ await alepha.start();
684
+
685
+ const router = alepha.inject(ReactRouter);
686
+
687
+ // Navigate without query params - should use defaults
688
+ await act(async () => {
689
+ await router.go("/search");
690
+ });
691
+
692
+ await waitFor(() => {
693
+ expect(
694
+ document.querySelector('[data-testid="query"]')?.textContent,
695
+ ).toBe("default search");
696
+ expect(
697
+ document.querySelector('[data-testid="page"]')?.textContent,
698
+ ).toBe("1");
699
+ });
700
+ });
701
+ });
702
+ });