|
1 | 1 | import { createMemoryHistory } from "../../lib/router/history";
|
2 | 2 | import { createRouter, createStaticHandler } from "../../lib/router/router";
|
| 3 | +import { |
| 4 | + createMemoryRouter, |
| 5 | + hydrationRouteProperties, |
| 6 | +} from "../../lib/components"; |
3 | 7 |
|
4 | 8 | import type {
|
5 | 9 | TestNonIndexRouteObject,
|
@@ -561,6 +565,69 @@ describe("lazily loaded route modules", () => {
|
561 | 565 | expect(t.router.state.matches[0].route.action).toBeUndefined();
|
562 | 566 | });
|
563 | 567 |
|
| 568 | + it("only resolves lazy hydration route properties on hydration", async () => { |
| 569 | + let [lazyLoaderForHydration, lazyLoaderDeferredForHydration] = |
| 570 | + createAsyncStub(); |
| 571 | + let [lazyLoaderForNavigation, lazyLoaderDeferredForNavigation] = |
| 572 | + createAsyncStub(); |
| 573 | + let [ |
| 574 | + lazyHydrateFallbackForHydration, |
| 575 | + lazyHydrateFallbackDeferredForHydration, |
| 576 | + ] = createAsyncStub(); |
| 577 | + let [ |
| 578 | + lazyHydrateFallbackElementForHydration, |
| 579 | + lazyHydrateFallbackElementDeferredForHydration, |
| 580 | + ] = createAsyncStub(); |
| 581 | + let lazyHydrateFallbackForNavigation = jest.fn(async () => null); |
| 582 | + let lazyHydrateFallbackElementForNavigation = jest.fn(async () => null); |
| 583 | + let router = createMemoryRouter( |
| 584 | + [ |
| 585 | + { |
| 586 | + path: "/hydration", |
| 587 | + lazy: { |
| 588 | + HydrateFallback: lazyHydrateFallbackForHydration, |
| 589 | + hydrateFallbackElement: lazyHydrateFallbackElementForHydration, |
| 590 | + loader: lazyLoaderForHydration, |
| 591 | + }, |
| 592 | + }, |
| 593 | + { |
| 594 | + path: "/navigation", |
| 595 | + lazy: { |
| 596 | + HydrateFallback: lazyHydrateFallbackForNavigation, |
| 597 | + hydrateFallbackElement: lazyHydrateFallbackElementForNavigation, |
| 598 | + loader: lazyLoaderForNavigation, |
| 599 | + }, |
| 600 | + }, |
| 601 | + ], |
| 602 | + { |
| 603 | + initialEntries: ["/hydration"], |
| 604 | + } |
| 605 | + ); |
| 606 | + expect(router.state.initialized).toBe(false); |
| 607 | + |
| 608 | + expect(lazyHydrateFallbackForHydration).toHaveBeenCalledTimes(1); |
| 609 | + expect(lazyHydrateFallbackElementForHydration).toHaveBeenCalledTimes(1); |
| 610 | + expect(lazyLoaderForHydration).toHaveBeenCalledTimes(1); |
| 611 | + await lazyHydrateFallbackDeferredForHydration.resolve(null); |
| 612 | + await lazyHydrateFallbackElementDeferredForHydration.resolve(null); |
| 613 | + await lazyLoaderDeferredForHydration.resolve(null); |
| 614 | + |
| 615 | + expect(router.state.location.pathname).toBe("/hydration"); |
| 616 | + expect(router.state.navigation.state).toBe("idle"); |
| 617 | + expect(router.state.initialized).toBe(true); |
| 618 | + |
| 619 | + let navigationPromise = router.navigate("/navigation"); |
| 620 | + expect(router.state.location.pathname).toBe("/hydration"); |
| 621 | + expect(router.state.navigation.state).toBe("loading"); |
| 622 | + expect(lazyHydrateFallbackForNavigation).not.toHaveBeenCalled(); |
| 623 | + expect(lazyHydrateFallbackElementForNavigation).not.toHaveBeenCalled(); |
| 624 | + expect(lazyLoaderForNavigation).toHaveBeenCalledTimes(1); |
| 625 | + await lazyLoaderDeferredForNavigation.resolve(null); |
| 626 | + await navigationPromise; |
| 627 | + expect(router.state.location.pathname).toBe("/navigation"); |
| 628 | + expect(router.state.navigation.state).toBe("idle"); |
| 629 | + }); |
| 630 | + |
564 | 631 | it("fetches lazy route functions on fetcher.load", async () => {
|
565 | 632 | let { routes, lazy, lazyDeferred } = createBasicLazyFunctionRoutes();
|
566 | 633 | let t = setup({ routes });
|
@@ -606,6 +673,40 @@ describe("lazily loaded route modules", () => {
|
606 | 673 | expect(lazyLoader).toHaveBeenCalledTimes(1);
|
607 | 674 | });
|
608 | 675 |
|
| 676 | + it("skips lazy hydration route properties on fetcher.load", async () => { |
| 677 | + let [lazyLoader, lazyLoaderDeferred] = createAsyncStub(); |
| 678 | + let lazyHydrateFallback = jest.fn(async () => null); |
| 679 | + let lazyHydrateFallbackElement = jest.fn(async () => null); |
| 680 | + let routes = createBasicLazyRoutes({ |
| 681 | + loader: lazyLoader, |
| 682 | + // @ts-expect-error |
| 683 | + HydrateFallback: lazyHydrateFallback, |
| 684 | + hydrateFallbackElement: lazyHydrateFallbackElement, |
| 685 | + }); |
| 686 | + let t = setup({ routes, hydrationRouteProperties }); |
| 687 | + expect(lazyHydrateFallback).not.toHaveBeenCalled(); |
| 688 | + expect(lazyHydrateFallbackElement).not.toHaveBeenCalled(); |
| 689 | + |
| 690 | + let key = "key"; |
| 691 | + await t.fetch("/lazy", key); |
| 692 | + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); |
| 693 | + expect(lazyLoader).toHaveBeenCalledTimes(1); |
| 694 | + expect(lazyHydrateFallback).not.toHaveBeenCalled(); |
| 695 | + expect(lazyHydrateFallbackElement).not.toHaveBeenCalled(); |
| 696 | + |
| 697 | + let loaderDeferred = createDeferred(); |
| 698 | + lazyLoaderDeferred.resolve(() => loaderDeferred.promise); |
| 699 | + expect(t.router.state.fetchers.get(key)?.state).toBe("loading"); |
| 700 | + |
| 701 | + await loaderDeferred.resolve("LAZY LOADER"); |
| 702 | + expect(t.fetchers[key].state).toBe("idle"); |
| 703 | + expect(t.fetchers[key].data).toBe("LAZY LOADER"); |
| 704 | + |
| 705 | + expect(lazyLoader).toHaveBeenCalledTimes(1); |
| 706 | + expect(lazyHydrateFallback).not.toHaveBeenCalled(); |
| 707 | + expect(lazyHydrateFallbackElement).not.toHaveBeenCalled(); |
| 708 | + }); |
| 709 | + |
609 | 710 | it("fetches lazy route functions on fetcher.submit", async () => {
|
610 | 711 | let { routes, lazy, lazyDeferred } = createBasicLazyFunctionRoutes();
|
611 | 712 | let t = setup({ routes });
|
@@ -666,6 +767,49 @@ describe("lazily loaded route modules", () => {
|
666 | 767 | expect(lazyAction).toHaveBeenCalledTimes(1);
|
667 | 768 | });
|
668 | 769 |
|
| 770 | + it("skips lazy hydration route properties on fetcher.submit", async () => { |
| 771 | + let [lazyLoaderStub, lazyLoaderDeferred] = createAsyncStub(); |
| 772 | + let [lazyActionStub, lazyActionDeferred] = createAsyncStub(); |
| 773 | + let lazyHydrateFallback = jest.fn(async () => null); |
| 774 | + let lazyHydrateFallbackElement = jest.fn(async () => null); |
| 775 | + let routes = createBasicLazyRoutes({ |
| 776 | + loader: lazyLoaderStub, |
| 777 | + action: lazyActionStub, |
| 778 | + // @ts-expect-error |
| 779 | + HydrateFallback: lazyHydrateFallback, |
| 780 | + hydrateFallbackElement: lazyHydrateFallbackElement, |
| 781 | + }); |
| 782 | + let t = setup({ routes, hydrationRouteProperties }); |
| 783 | + expect(lazyLoaderStub).not.toHaveBeenCalled(); |
| 784 | + expect(lazyActionStub).not.toHaveBeenCalled(); |
| 785 | + |
| 786 | + let key = "key"; |
| 787 | + await t.fetch("/lazy", key, { |
| 788 | + formMethod: "post", |
| 789 | + formData: createFormData({}), |
| 790 | + }); |
| 791 | + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); |
| 792 | + expect(lazyLoaderStub).toHaveBeenCalledTimes(1); |
| 793 | + expect(lazyActionStub).toHaveBeenCalledTimes(1); |
| 794 | + expect(lazyHydrateFallback).not.toHaveBeenCalled(); |
| 795 | + expect(lazyHydrateFallbackElement).not.toHaveBeenCalled(); |
| 796 | + |
| 797 | + let actionDeferred = createDeferred(); |
| 798 | + let loaderDeferred = createDeferred(); |
| 799 | + lazyLoaderDeferred.resolve(() => loaderDeferred.promise); |
| 800 | + lazyActionDeferred.resolve(() => actionDeferred.promise); |
| 801 | + expect(t.router.state.fetchers.get(key)?.state).toBe("submitting"); |
| 802 | + |
| 803 | + await actionDeferred.resolve("LAZY ACTION"); |
| 804 | + expect(t.fetchers[key]?.state).toBe("idle"); |
| 805 | + expect(t.fetchers[key]?.data).toBe("LAZY ACTION"); |
| 806 | + |
| 807 | + expect(lazyLoaderStub).toHaveBeenCalledTimes(1); |
| 808 | + expect(lazyActionStub).toHaveBeenCalledTimes(1); |
| 809 | + expect(lazyHydrateFallback).not.toHaveBeenCalled(); |
| 810 | + expect(lazyHydrateFallbackElement).not.toHaveBeenCalled(); |
| 811 | + }); |
| 812 | + |
669 | 813 | it("fetches lazy route functions on staticHandler.query()", async () => {
|
670 | 814 | let { query } = createStaticHandler([
|
671 | 815 | {
|
@@ -751,6 +895,35 @@ describe("lazily loaded route modules", () => {
|
751 | 895 | let data = await response.json();
|
752 | 896 | expect(data).toEqual({ value: "LAZY LOADER" });
|
753 | 897 | });
|
| 898 | + |
| 899 | + it("resolves lazy hydration route properties on staticHandler.queryRoute()", async () => { |
| 900 | + let lazyHydrateFallback = jest.fn(async () => null); |
| 901 | + let lazyHydrateFallbackElement = jest.fn(async () => null); |
| 902 | + let { queryRoute } = createStaticHandler( |
| 903 | + [ |
| 904 | + { |
| 905 | + id: "lazy", |
| 906 | + path: "/lazy", |
| 907 | + lazy: { |
| 908 | + loader: async () => { |
| 909 | + await tick(); |
| 910 | + return () => Response.json({ value: "LAZY LOADER" }); |
| 911 | + }, |
| 912 | + // @ts-expect-error |
| 913 | + HydrateFallback: lazyHydrateFallback, |
| 914 | + hydrateFallbackElement: lazyHydrateFallbackElement, |
| 915 | + }, |
| 916 | + }, |
| 917 | + ], |
| 918 | + { hydrationRouteProperties } |
| 919 | + ); |
| 920 | + |
| 921 | + let response = await queryRoute(createRequest("/lazy")); |
| 922 | + let data = await response.json(); |
| 923 | + expect(data).toEqual({ value: "LAZY LOADER" }); |
| 924 | + expect(lazyHydrateFallback).toHaveBeenCalled(); |
| 925 | + expect(lazyHydrateFallbackElement).toHaveBeenCalled(); |
| 926 | + }); |
754 | 927 | });
|
755 | 928 |
|
756 | 929 | describe("statically defined fields", () => {
|
|
0 commit comments