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 Jun 30, 2024
1 parent 8600f15 commit 3214b5f
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 15 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
3 changes: 3 additions & 0 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,6 +77,7 @@ testDevAndProd("fs-router", ({ createFixture }) => {

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

expect(await app.getHtml("[data-test-id=title]")).toBe(
Expand All @@ -87,6 +89,7 @@ testDevAndProd("fs-router", ({ createFixture }) => {

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

expect(await app.getHtml("[data-test-id=title]")).toBe(
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
28 changes: 28 additions & 0 deletions test/helpers/playwright-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,34 @@ 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 = true || !!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)
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
4 changes: 2 additions & 2 deletions test/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { devices } from "@playwright/test";
const config: PlaywrightTestConfig = {
testDir: ".",
testMatch: ["**/*.test.ts"],
workers: 1,
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
16 changes: 14 additions & 2 deletions test/templates/react-ssr-fs/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import React from "react";
import React, { useEffect, useRef } from "react";

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

const useReadyRef = <T extends HTMLElement,>() => {
const ref = useRef<T>();
useEffect(() => {
ref.current.dataset.ready = "";
return () => {
ref.current.dataset.ready = null;
}
}, []);
return ref;
}

export default function App({ assets, children }) {
const [count, setCount] = React.useState(0);
const ref = useReadyRef<HTMLHtmlElement>();
return (
<html lang="en">
<html lang="en" ref={ref}>
<head>
<link rel="icon" href="/favicon.ico" />
{assets}
Expand Down
17 changes: 14 additions & 3 deletions test/templates/react/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import React from "react";

import React, { useEffect, useRef } from "react";
import { Counter } from "./Counter";

const useReadyRef = <T extends HTMLElement,>() => {
const ref = useRef<T>();
useEffect(() => {
ref.current.dataset.ready = "";
return () => {
ref.current.dataset.ready = null;
}
}, []);
return ref;
}

export default function App({ assets }) {
const ref = useReadyRef<HTMLHtmlElement>();
return (
<html lang="en">
<html lang="en" ref={ref}>
<head>
<link rel="icon" href="/favicon.ico" />
{assets}
Expand Down

0 comments on commit 3214b5f

Please sign in to comment.