@@ -116,7 +125,15 @@ const files = {
export const clientLoader = async () => {
inUnsplittableMainChunk();
- await new Promise((resolve) => setTimeout(resolve, 100));
+ const pollingPromise = (async () => {
+ while (globalThis.blockClientLoader !== false) {
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ }
+ })();
+ const timeoutPromise = new Promise((_, reject) => {
+ setTimeout(() => reject(new Error("Client loader wasn't unblocked after 2s")), 2000);
+ });
+ await Promise.race([pollingPromise, timeoutPromise]);
return "clientLoader in main chunk: " + eval("typeof inUnsplittableMainChunk === 'function'");
};
@@ -138,6 +155,7 @@ const files = {
inUnsplittableMainChunk();
return (
<>
+
Unsplittable Route
loaderData = {JSON.stringify(loaderData)}
{actionData ? (
actionData = {JSON.stringify(actionData)}
@@ -163,7 +181,15 @@ const files = {
export const clientLoader = async () => {
inMixedMainChunk();
- await new Promise((resolve) => setTimeout(resolve, 100));
+ const pollingPromise = (async () => {
+ while (globalThis.blockClientLoader !== false) {
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ }
+ })();
+ const timeoutPromise = new Promise((_, reject) => {
+ setTimeout(() => reject(new Error("Client loader wasn't unblocked after 2s")), 2000);
+ });
+ await Promise.race([pollingPromise, timeoutPromise]);
return "clientLoader in main chunk: " + eval("typeof inMixedMainChunk === 'function'");
};
@@ -184,6 +210,7 @@ const files = {
inMixedMainChunk();
return (
<>
+
Mixed Route
loaderData = {JSON.stringify(loaderData)}
{actionData ? (
actionData = {JSON.stringify(actionData)}
@@ -215,6 +242,12 @@ async function mixedHydrateFallbackDownloaded(page: Page) {
);
}
+async function unblockClientLoader(page: Page) {
+ await page.evaluate(() => {
+ (globalThis as any).blockClientLoader = false;
+ });
+}
+
test.describe("Split route modules", async () => {
test.describe("enabled", () => {
let splitRouteModules = true;
@@ -242,10 +275,12 @@ test.describe("Split route modules", async () => {
page.on("pageerror", (error) => pageErrors.push(error));
await page.goto(`http://localhost:${port}`, { waitUntil: "networkidle" });
+ await unblockClientLoader(page);
expect(pageErrors).toEqual([]);
// Ensure splittable exports are not in main chunk
await page.getByRole("link", { name: "/splittable" }).click();
+ await expect(page.getByText("Splittable Route")).toBeVisible();
expect(await splittableHydrateFallbackDownloaded(page)).toBe(false);
await expect(page.locator("[data-loader-data]")).toHaveText(
`loaderData = "clientLoader in main chunk: false"`
@@ -262,6 +297,7 @@ test.describe("Split route modules", async () => {
// Ensure unsplittable exports are in main chunk
await page.getByRole("link", { name: "/unsplittable" }).click();
+ await expect(page.getByText("Unsplittable Route")).toBeVisible();
expect(await unsplittableHydrateFallbackDownloaded(page)).toBe(true);
await expect(page.locator("[data-loader-data]")).toHaveText(
'loaderData = "clientLoader in main chunk: true"'
@@ -276,6 +312,7 @@ test.describe("Split route modules", async () => {
// Ensure mix of splittable and unsplittable exports are handled correctly.
// Note that only the client action is in its own chunk.
await page.getByRole("link", { name: "/mixed" }).click();
+ await expect(page.getByText("Mixed Route")).toBeVisible();
await expect(page.locator("[data-loader-data]")).toHaveText(
'loaderData = "clientLoader in main chunk: true"'
);
@@ -287,21 +324,30 @@ test.describe("Split route modules", async () => {
// Ensure splittable HydrateFallback and client loader work during SSR
await page.goto(`http://localhost:${port}/splittable`);
- expect(page.locator("[data-hydrate-fallback]")).toHaveText("Loading...");
- expect(page.locator("[data-hydrate-fallback]")).toHaveCSS(
+ await expect(page.locator("[data-hydrate-fallback]")).toHaveText(
+ "Loading..."
+ );
+ await expect(page.locator("[data-hydrate-fallback]")).toHaveCSS(
"padding",
"20px"
);
expect(await splittableHydrateFallbackDownloaded(page)).toBe(true);
+ await unblockClientLoader(page);
await expect(page.locator("[data-loader-data]")).toHaveText(
`loaderData = "clientLoader in main chunk: false"`
);
- expect(page.locator("[data-loader-data]")).toHaveCSS("padding", "20px");
+ await expect(page.locator("[data-loader-data]")).toHaveCSS(
+ "padding",
+ "20px"
+ );
// Ensure unsplittable HydrateFallback and client loader work during SSR
await page.goto(`http://localhost:${port}/unsplittable`);
- expect(page.locator("[data-hydrate-fallback]")).toHaveText("Loading...");
+ await expect(page.locator("[data-hydrate-fallback]")).toHaveText(
+ "Loading..."
+ );
expect(await unsplittableHydrateFallbackDownloaded(page)).toBe(true);
+ await unblockClientLoader(page);
await expect(page.locator("[data-loader-data]")).toHaveText(
`loaderData = "clientLoader in main chunk: true"`
);
@@ -334,25 +380,34 @@ test.describe("Split route modules", async () => {
page.on("pageerror", (error) => pageErrors.push(error));
await page.goto(`http://localhost:${port}`, { waitUntil: "networkidle" });
+ await unblockClientLoader(page);
expect(pageErrors).toEqual([]);
// Ensure splittable exports are kept in main chunk
await page.getByRole("link", { name: "/splittable" }).click();
+ await expect(page.getByText("Splittable Route")).toBeVisible();
expect(await splittableHydrateFallbackDownloaded(page)).toBe(true);
await expect(page.locator("[data-loader-data]")).toHaveText(
`loaderData = "clientLoader in main chunk: true"`
);
- expect(page.locator("[data-loader-data]")).toHaveCSS("padding", "20px");
+ await expect(page.locator("[data-loader-data]")).toHaveCSS(
+ "padding",
+ "20px"
+ );
await page.getByRole("button").click();
await expect(page.locator("[data-action-data]")).toHaveText(
'actionData = "clientAction in main chunk: true"'
);
- expect(page.locator("[data-action-data]")).toHaveCSS("padding", "20px");
+ await expect(page.locator("[data-action-data]")).toHaveCSS(
+ "padding",
+ "20px"
+ );
await page.goBack();
// Ensure unsplittable exports are kept in main chunk
await page.getByRole("link", { name: "/unsplittable" }).click();
+ await expect(page.getByText("Unsplittable Route")).toBeVisible();
expect(await unsplittableHydrateFallbackDownloaded(page)).toBe(true);
await expect(page.locator("[data-loader-data]")).toHaveText(
'loaderData = "clientLoader in main chunk: true"'
@@ -369,6 +424,7 @@ test.describe("Split route modules", async () => {
"padding",
"20px"
);
+ await unblockClientLoader(page);
await expect(page.locator("[data-loader-data]")).toHaveText(
`loaderData = "clientLoader in main chunk: true"`
);
@@ -376,6 +432,7 @@ test.describe("Split route modules", async () => {
// Ensure unsplittable client loader works during SSR
await page.goto(`http://localhost:${port}/unsplittable`);
expect(page.locator("[data-hydrate-fallback]")).toHaveText("Loading...");
+ await unblockClientLoader(page);
await expect(page.locator("[data-loader-data]")).toHaveText(
`loaderData = "clientLoader in main chunk: true"`
);