10000 fix(site): ensure Error Boundary catches render errors correctly by Parkreiner · Pull Request #15963 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

fix(site): ensure Error Boundary catches render errors correctly #15963

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 3, 2025
Merged
Prev Previous commit
Next Next commit
chore: finish new stories
  • Loading branch information
Parkreiner committed Dec 26, 2024
commit f8a03476c5c758c914b2fcaf2c289b8b9f659cac
39 changes: 0 additions & 39 deletions site/src/components/ErrorBoundary/ErrorBoundary.tsx

This file was deleted.

92 changes: 92 additions & 0 deletions site/src/components/ErrorBoundary/GlobalErrorBoundary.stories.tsx
10000
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type { Meta, StoryObj } from "@storybook/react";
import { GlobalErrorBoundaryInner } from "./GlobalErrorBoundary";
import { within } from "@testing-library/react";
import type { ErrorResponse } from "react-router-dom";
import { expect, userEvent } from "@storybook/test";

/**
* React Router ErrorResponses have a "hidden" internal field that RR uses to
* detect whether something is a loader error. The property doesn't exist in
* the type information, but it does exist at runtime, and we need it to mock
* out the story correctly
*/
type FullErrorResponse = Readonly<
ErrorResponse & {
internal: true;
}
>;

const meta = {
title: "components/GlobalErrorBoundary",
component: GlobalErrorBoundaryInner,
} satisfies Meta<typeof GlobalErrorBoundaryInner>;

export default meta;
type Story = StoryObj<typeof meta>;

export const VanillaJavascriptError: Story = {
args: {
error: new Error("Something blew up :("),
},
play: async ({ canvasElement, args }) => {
const error = args.error as Error;
const canvas = within(canvasElement);
const showErrorButton = canvas.getByRole("button", {
name: /Show error/i,
});
await userEvent.click(showErrorButton);

// Verify that error message content is now on screen; defer to
// accessible name queries as much as possible
canvas.getByRole("heading", { name: /Error/i });

const p = canvas.getByTestId("description");
expect(p).toHaveTextContent(error.message);

const codeBlock = canvas.getByTestId("code");
expect(codeBlock).toHaveTextContent(error.name);
expect(codeBlock).toHaveTextContent(error.message);
},
};

export const ReactRouterErrorResponse: Story = {
args: {
error: {
internal: true,
status: 500,
statusText: "Aww, beans!",
data: { message: "beans" },
} satisfies FullErrorResponse,
},
play: async ({ canvasElement, args }) => {
const error = args.error as FullErrorResponse;
const canvas = within(canvasElement);
const showErrorButton = canvas.getByRole("button", {
name: /Show error/i,
});
await userEvent.click(showErrorButton);

// Verify that error message content is now on screen; defer to
// accessible name queries as much as possible
const header = canvas.getByRole("heading", { name: /Aww, beans!/i });
expect(header).toHaveTextContent(String(error.status));

const codeBlock = canvas.getByTestId("code");
const content = codeBlock.innerText;
const parsed = JSON.parse(content);
expect(parsed).toEqual(error.data);
},
};

export const UnparsableError: Story = {
args: {
error: class WellThisIsDefinitelyWrong {},
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const showErrorButton = canvas.queryByRole("button", {
name: /Show error/i,
});
expect(showErrorButton).toBe(null);
},
};
11 changes: 6 additions & 5 deletions site/src/components/ErrorBoundary/GlobalErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,23 @@ const ErrorStack: FC<ErrorStackProps> = ({ error }) => {
{isRouteErrorResponse(error) ? (
<>
<h2 className="text-base font-bold text-content-primary m-0">
HTTP error code {error.status}
HTTP {error.status} - {error.statusText}
</h2>
<p className="pb-4 leading-5 m-0">{error.statusText}</p>
<pre className="m-0 py-2 px-0 overflow-x-auto text-xs">
<code>{serializeDataAsJson(error.data)}</code>
<code data-testid="code">{serializeDataAsJson(error.data)}</code>
</pre>
</>
) : (
<>
<h2 className="text-base font-bold text-content-primary m-0">
{error.name}
</h2>
<p className="pb-4 leading-5 m-0">{error.message}</p>
<p data-testid="description" className="pb-4 leading-5 m-0">
{error.message}
</p>
{error.stack && (
<pre className="m-0 py-2 px-0 overflow-x-auto text-xs">
<code>{error.stack}</code>
<code data-testid="code">{error.stack}</code>
</pre>
)}
</>
Expand Down
24 changes: 0 additions & 24 deletions site/src/components/ErrorBoundary/RuntimeErrorState.stories.tsx

This file was deleted.

198 changes: 0 additions & 198 deletions site/src/components/ErrorBoundary/RuntimeErrorState.tsx

This file was deleted.

Loading
0