Skip to content

Commit

Permalink
Fix flakey firefox tests (#4912)
Browse files Browse the repository at this point in the history
* Fix flakey firefox tests

* Fix flakey upload tests

* Add integration tests for useLoaderData/ErrorBoundary
  • Loading branch information
brophdawg11 authored Dec 19, 2022
1 parent aa0aa37 commit fc5b75e
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 11 deletions.
239 changes: 239 additions & 0 deletions integration/error-boundary-test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { test, expect } from "@playwright/test";
import { ServerMode } from "@remix-run/server-runtime/mode";

import { createAppFixture, createFixture, js } from "./helpers/create-fixture";
import type { Fixture, AppFixture } from "./helpers/create-fixture";
Expand Down Expand Up @@ -636,3 +637,241 @@ test.describe("ErrorBoundary", () => {
});
});
});

test.describe("loaderData in ErrorBoundary", () => {
let fixture: Fixture;
let appFixture: AppFixture;
let consoleErrors: string[];
let oldConsoleError: () => void;

test.beforeAll(async () => {
fixture = await createFixture({
files: {
"app/root.jsx": js`
import { Links, Meta, Outlet, Scripts } from "@remix-run/react";
export default function Root() {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<main>
<Outlet />
</main>
<Scripts />
</body>
</html>
);
}
`,

"app/routes/parent.jsx": js`
import { Outlet, useLoaderData, useMatches } from "@remix-run/react";
export function loader() {
return "PARENT";
}
export default function () {
return (
<div>
<p id="parent-data">{useLoaderData()}</p>
<Outlet />
</div>
)
}
export function ErrorBoundary({ error }) {
return (
<>
<p id="parent-data">{useLoaderData()}</p>
<p id="parent-matches-data">
{useMatches().find(m => m.id === 'routes/parent').data}
</p>
<p id="parent-error">{error.message}</p>
</>
);
}
`,

"app/routes/parent/child-with-boundary.jsx": js`
import { Form, useLoaderData } from "@remix-run/react";
export function loader() {
return "CHILD";
}
export function action() {
throw new Error("Broken!");
}
export default function () {
return (
<>
<p id="child-data">{useLoaderData()}</p>
<Form method="post">
<button type="submit" name="key" value="value">
Submit
</button>
</Form>
</>
)
}
export function ErrorBoundary({ error }) {
return (
<>
<p id="child-data">{useLoaderData()}</p>
<p id="child-error">{error.message}</p>
</>
);
}
`,

"app/routes/parent/child-without-boundary.jsx": js`
import { Form, useLoaderData } from "@remix-run/react";
export function loader() {
return "CHILD";
}
export function action() {
throw new Error("Broken!");
}
export default function () {
return (
<>
<p id="child-data">{useLoaderData()}</p>
<Form method="post">
<button type="submit" name="key" value="value">
Submit
</button>
</Form>
</>
)
}
`,
},
});

appFixture = await createAppFixture(fixture, ServerMode.Development);
});

test.afterAll(() => {
appFixture.close();
});

test.beforeEach(({ page }) => {
oldConsoleError = console.error;
console.error = () => {};
consoleErrors = [];
// Listen for all console events and handle errors
page.on("console", (msg) => {
if (msg.type() === "error") {
consoleErrors.push(msg.text());
}
});
});

test.afterEach(() => {
console.error = oldConsoleError;
});

test.describe("without JavaScript", () => {
test.use({ javaScriptEnabled: false });
runBoundaryTests();
});

test.describe("with JavaScript", () => {
test.use({ javaScriptEnabled: true });
runBoundaryTests();
});

function runBoundaryTests() {
test("Prevents useLoaderData in self ErrorBoundary", async ({
page,
javaScriptEnabled,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/parent/child-with-boundary");

expect(await app.getHtml("#parent-data")).toEqual(
'<p id="parent-data">PARENT</p>'
);
expect(await app.getHtml("#child-data")).toEqual(
'<p id="child-data">CHILD</p>'
);
expect(consoleErrors).toEqual([]);

await app.clickSubmitButton("/parent/child-with-boundary");
await page.waitForSelector("#child-error");

expect(await app.getHtml("#child-error")).toEqual(
'<p id="child-error">Broken!</p>'
);
expect(await app.getHtml("#parent-data")).toEqual(
'<p id="parent-data">PARENT</p>'
);
expect(await app.getHtml("#child-data")).toEqual(
'<p id="child-data"></p>'
);

// Only look for this message. Chromium browsers will also log the
// network error but firefox does not
// "Failed to load resource: the server responded with a status of 500 (Internal Server Error)",
let msg =
"You cannot `useLoaderData` in an errorElement (routeId: routes/parent/child-with-boundary)";
if (javaScriptEnabled) {
expect(consoleErrors.filter((m) => m === msg)).toEqual([msg]);
} else {
// We don't get the useLoaderData message in the client when JS is disabled
expect(consoleErrors.filter((m) => m === msg)).toEqual([]);
}
});

test("Prevents useLoaderData in bubbled ErrorBoundary", async ({
page,
javaScriptEnabled,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/parent/child-without-boundary");

expect(await app.getHtml("#parent-data")).toEqual(
'<p id="parent-data">PARENT</p>'
);
expect(await app.getHtml("#child-data")).toEqual(
'<p id="child-data">CHILD</p>'
);
expect(consoleErrors).toEqual([]);

await app.clickSubmitButton("/parent/child-without-boundary");
await page.waitForSelector("#parent-error");

expect(await app.getHtml("#parent-error")).toEqual(
'<p id="parent-error">Broken!</p>'
);
expect(await app.getHtml("#parent-matches-data")).toEqual(
'<p id="parent-matches-data"></p>'
);
expect(await app.getHtml("#parent-data")).toEqual(
'<p id="parent-data"></p>'
);

// Only look for this message. Chromium browsers will also log the
// network error but firefox does not
// "Failed to load resource: the server responded with a status of 500 (Internal Server Error)",
let msg =
"You cannot `useLoaderData` in an errorElement (routeId: routes/parent)";
if (javaScriptEnabled) {
expect(consoleErrors.filter((m) => m === msg)).toEqual([msg]);
} else {
// We don't get the useLoaderData message in the client when JS is disabled
expect(consoleErrors.filter((m) => m === msg)).toEqual([]);
}
});
}
});
8 changes: 8 additions & 0 deletions integration/fetcher-layout-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ test("fetcher calls layout route action when at index route", async ({
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-action");
await app.clickElement("button");
await page.waitForSelector("#layout-fetcher-data");
let dataElement = await app.getElement("#layout-fetcher-data");
expect(dataElement.text()).toBe("layout action data");
dataElement = await app.getElement("#child-data");
Expand All @@ -203,6 +204,7 @@ test("fetcher calls layout route loader when at index route", async ({
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-loader");
await app.clickElement("button");
await page.waitForSelector("#layout-fetcher-data");
let dataElement = await app.getElement("#layout-fetcher-data");
expect(dataElement.text()).toBe("layout loader data");
});
Expand All @@ -213,6 +215,7 @@ test("fetcher calls index route action when at index route", async ({
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-action");
await app.clickElement("#index-fetcher");
await page.waitForSelector("#index-fetcher-data");
let dataElement = await app.getElement("#index-fetcher-data");
expect(dataElement.text()).toBe("index action data");
dataElement = await app.getElement("#child-data");
Expand All @@ -225,6 +228,7 @@ test("fetcher calls index route loader when at index route", async ({
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-loader");
await app.clickElement("#index-fetcher");
await page.waitForSelector("#index-fetcher-data");
let dataElement = await app.getElement("#index-fetcher-data");
expect(dataElement.text()).toBe("index data");
});
Expand All @@ -235,6 +239,7 @@ test("fetcher calls layout route action when at paramaterized route", async ({
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-action/foo");
await app.clickElement("button");
await page.waitForSelector("#layout-fetcher-data");
let dataElement = await app.getElement("#layout-fetcher-data");
expect(dataElement.text()).toBe("layout action data");
dataElement = await app.getElement("#child-data");
Expand All @@ -247,6 +252,7 @@ test("fetcher calls layout route loader when at paramaterized route", async ({
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-loader/foo");
await app.clickElement("button");
await page.waitForSelector("#layout-fetcher-data");
let dataElement = await app.getElement("#layout-fetcher-data");
expect(dataElement.text()).toBe("layout loader data");
});
Expand All @@ -255,6 +261,7 @@ test("fetcher calls paramaterized route route action", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-action/foo");
await app.clickElement("#param-fetcher");
await page.waitForSelector("#param-fetcher-data");
let dataElement = await app.getElement("#param-fetcher-data");
expect(dataElement.text()).toBe("param action data");
dataElement = await app.getElement("#child-data");
Expand All @@ -265,6 +272,7 @@ test("fetcher calls paramaterized route route loader", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/layout-loader/foo");
await app.clickElement("#param-fetcher");
await page.waitForSelector("#param-fetcher-data");
let dataElement = await app.getElement("#param-fetcher-data");
expect(dataElement.text()).toBe("foo");
});
5 changes: 4 additions & 1 deletion integration/file-uploads-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ test.describe("file-uploads", () => {
};
export default function Upload() {
let actionData = useActionData();
return (
<>
<Form method="post" encType="multipart/form-data">
Expand All @@ -69,7 +70,7 @@ test.describe("file-uploads", () => {
<input type="hidden" name="test" value="hidden" />
<button type="submit">Submit</button>
</Form>
<pre>{JSON.stringify(useActionData(), null, 2)}</pre>
{actionData ? <pre>{JSON.stringify(actionData, null, 2)}</pre> : null}
</>
);
}
Expand Down Expand Up @@ -100,6 +101,7 @@ test.describe("file-uploads", () => {
await app.goto("/file-upload");
await app.uploadFile("#file", uploadFile);
await app.clickSubmitButton("/file-upload");
await page.waitForSelector("pre");
expect(await app.getHtml("pre")).toBe(`<pre>
{
"name": "underLimit.txt",
Expand All @@ -126,6 +128,7 @@ test.describe("file-uploads", () => {
await app.goto("/file-upload");
await app.uploadFile("#file", uploadFile);
await app.clickSubmitButton("/file-upload");
await page.waitForSelector("pre");
expect(await app.getHtml("pre")).toBe(`<pre>
{
"errorMessage": "Field \\"file\\" exceeded upload size of 10000 bytes."
Expand Down
Loading

0 comments on commit fc5b75e

Please sign in to comment.