Skip to content

Commit

Permalink
fix: wait for hydration via react effect
Browse files Browse the repository at this point in the history
  • Loading branch information
katywings committed Jul 1, 2024
1 parent 8600f15 commit fa09531
Show file tree
Hide file tree
Showing 14 changed files with 83 additions and 16 deletions.
1 change: 1 addition & 0 deletions test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ testDevAndProd("basic", ({ createFixture }) => {
test("hydrates", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/", true);
await app.isReady();

expect(await app.getHtml("[data-test-id=content]")).toBe(
prettyHtml(`<h1 data-test-id="content">Hello from Vinxi</h1>`),
Expand Down
5 changes: 3 additions & 2 deletions test/fs-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ testDevAndProd("fs-router", ({ createFixture }) => {
test("hydrates", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/", true);
await app.isReady();

expect(await app.getHtml("[data-test-id=title]")).toBe(
prettyHtml(`<h1 data-test-id="title">Vinxi Home</h1>`),
Expand All @@ -76,7 +77,7 @@ testDevAndProd("fs-router", ({ createFixture }) => {

await app.clickElement("a[href='/hello']");
await app.waitForURL("/hello");
await new Promise((r) => setTimeout(r, 1000));
await app.isReady();

expect(await app.getHtml("[data-test-id=title]")).toBe(
prettyHtml(`<h1 data-test-id="title">Hello from Vinxi</h1>`),
Expand All @@ -87,7 +88,7 @@ testDevAndProd("fs-router", ({ createFixture }) => {

await app.clickElement("a[href='/']");
await app.waitForURL("/");
await new Promise((r) => setTimeout(r, 2000));
await app.isReady();

expect(await app.getHtml("[data-test-id=title]")).toBe(
prettyHtml(`<h1 data-test-id="title">Vinxi Home</h1>`),
Expand Down
3 changes: 3 additions & 0 deletions test/helpers/create-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,10 @@ export async function createDevFixture(init: FixtureInit) {
await fse.remove(path.join(projectDir, filename));
} else {
await fse.writeFile(path.join(projectDir, filename), prevValue);
await new Promise((r) => setTimeout(r, 2000));
}
}
cache.clear();
// await fse.remove(projectDir);
// projectDir = await createFixtureProject(init);
},
Expand All @@ -157,6 +159,7 @@ export async function createDevFixture(init: FixtureInit) {
}

await fse.writeFile(path.join(projectDir, filename), content);
await new Promise((r) => setTimeout(r, 2000));
},
createServer: async () => {
return {
Expand Down
30 changes: 30 additions & 0 deletions test/helpers/playwright-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,36 @@ export class PlaywrightFixture {
cp.exec(`open ${this.app.serverUrl}${href}`);
return new Promise(res => setTimeout(res, ms));
}

/**
* Checks if the page is hydrated via a data-ready attribute,
* that is being set with useEffect.
*/
async isReady () {
let DEBUG = !!process.env.DEBUG;

const readyCheck = async (timeout?: number) => {
await this.page.waitForLoadState("networkidle");
await this.page.waitForLoadState("load");
await this.page.locator('[data-ready]').waitFor({ state: "attached", timeout });
}

for(let i = 1; i <= 3; i++) {
try {
await readyCheck(20 * i * 1000)
// Give React Suspense some extra time to remove its magical display: hidden
await new Promise(res => setTimeout(res, 2000));
return true;
} catch(err) {
if (DEBUG) {
console.log('Something went wrong during the page hydration, reloading the page')
}
await this.page.reload()
}
}

throw new Error('Page hydration failed');
}
}

export async function getHtml(page: Page, selector?: string) {
Expand Down
9 changes: 1 addition & 8 deletions test/hmr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ test.describe("hmr", () => {
}`,
);

await new Promise((r) => setTimeout(r, 1000));

res = await fixture.requestDocument("/");
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toBe("text/html");
Expand All @@ -87,6 +85,7 @@ test.describe("hmr", () => {
test("client hmr", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/", true);
await app.isReady();

expect(await app.getHtml("[data-test-id=content]")).toBe(
prettyHtml(`<h1 data-test-id="content">Hello from Vinxi</h1>`),
Expand Down Expand Up @@ -118,8 +117,6 @@ test.describe("hmr", () => {
}`,
);

await new Promise((r) => setTimeout(r, 1000));

expect(await app.getHtml("[data-test-id=button]")).toBe(
prettyHtml(`<button data-test-id="button">Click me again</button>`),
);
Expand All @@ -138,8 +135,6 @@ test.describe("hmr", () => {
}`,
);

await new Promise((r) => setTimeout(r, 1000));

res = await fixture.requestDocument("/api/hello");
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toBe("text/html");
Expand All @@ -152,8 +147,6 @@ test.describe("hmr", () => {
}`,
);

await new Promise((r) => setTimeout(r, 2000));

res = await fixture.requestDocument("/api/new");
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toBe("text/html");
Expand Down
3 changes: 1 addition & 2 deletions test/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ const config: PlaywrightTestConfig = {
testDir: ".",
testMatch: ["**/*.test.ts"],
fullyParallel: false,
timeout: process.env.CI ? 120_000 : 30_000, // 2 minutes in CI, 30 seconds locally
timeout: process.env.CI ? 360_000 : 30_000, // 5 minutes in CI, 30 seconds locally
expect: {
timeout: 5_000, // 5 second retries for assertions
},
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI
? "github"
: [["html", { open: process.env.TEST_REPORT ? "always" : "none" }]],
Expand Down
1 change: 1 addition & 0 deletions test/rsc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ test.describe("rsc", () => {
test("spa", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/", true);
await app.isReady();

expect(await app.getHtml("[data-test-id=title]")).toBe(
prettyHtml(`<h1 data-test-id="title">Hello from Vinxi</h1>`),
Expand Down
1 change: 1 addition & 0 deletions test/srv-fn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ testDevAndProd("srv-fn", ({ createFixture }) => {
test("spa", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/", true);
await app.isReady();

let responses = app.collectResponses();
await app.clickElement("[data-test-id=button]");
Expand Down
8 changes: 7 additions & 1 deletion test/templates/react-rsc/app/Counter.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
"use client";

import { useState } from "react";
import { useEffect, useState } from "react";

export function Counter({ onChange }) {
useEffect(() => {
document.documentElement.dataset.ready = "";
return () => {
document.documentElement.dataset.ready = null;
}
}, []);
const [count, setCount] = useState(0);
return (
<button
Expand Down
9 changes: 9 additions & 0 deletions test/templates/react-srv-fn/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import React, { useEffect } from "react";

async function greetServer(name: string) {
"use server";
console.log(`Hi, server. My name is ${name}.`);
}

export function App() {
useEffect(() => {
document.documentElement.dataset.ready = "";
return () => {
document.documentElement.dataset.ready = null;
}
}, []);

return (
<div>
<button onClick={() => greetServer("client")} data-test-id="button">
Expand Down
10 changes: 8 additions & 2 deletions test/templates/react-ssr-fs/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React from "react";
import React, { useEffect } from "react";

import { Counter } from "./Counter";
import "./style.css";

export default function App({ assets, children }) {
const [count, setCount] = React.useState(0);
useEffect(() => {
document.documentElement.dataset.ready = "";
return () => {
document.documentElement.dataset.ready = null;
}
}, []);

return (
<html lang="en">
<head>
Expand Down
9 changes: 9 additions & 0 deletions test/templates/react-to-web-request/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import React, { useEffect } from "react";

async function getData() {
"use server";
console.log(`I have not been blocked.`);
}

export function App() {
useEffect(() => {
document.documentElement.dataset.ready = "";
return () => {
document.documentElement.dataset.ready = null;
}
}, []);

return (
<div>
<button onClick={() => getData()} data-test-id="button">
Expand Down
9 changes: 8 additions & 1 deletion test/templates/react/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import React from "react";

import { useEffect } from "react";
import { Counter } from "./Counter";

export default function App({ assets }) {
useEffect(() => {
document.documentElement.dataset.ready = "";
return () => {
document.documentElement.dataset.ready = null;
}
}, []);

return (
<html lang="en">
<head>
Expand Down
1 change: 1 addition & 0 deletions test/to-web-request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ testDevAndProd("toWebRequest", ({ createFixture }) => {
test("readBody call after toWebRequest does not block", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/", true);
await app.isReady();

const el = await page.$("[data-test-id=button]");
await el.click();
Expand Down

0 comments on commit fa09531

Please sign in to comment.