fix: resolve frontend audit findings
Frontend security / security (push) Successful in 10s
Frontend verify / verify (push) Successful in 4m57s
Conventional commits / commits (pull_request) Successful in 28s
Frontend security / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 5m10s
Frontend security / security (push) Successful in 10s
Frontend verify / verify (push) Successful in 4m57s
Conventional commits / commits (pull_request) Successful in 28s
Frontend security / security (pull_request) Successful in 10s
Frontend verify / verify (pull_request) Successful in 5m10s
Repair investment recovery, draft preservation, date validation, currency presentation, accessibility, and route error recovery. Correct Docker and dependency checks and consume explicit income roles.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { MantineProvider } from "@mantine/core";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import type { FC } from "react";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { type FC, lazy, Suspense } from "react";
|
||||
import { MemoryRouter, useLocation, useNavigate } from "react-router";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { RouteErrorBoundary } from "./RouteErrorBoundary";
|
||||
@@ -52,6 +52,74 @@ function renderBoundary(path: string) {
|
||||
}
|
||||
|
||||
describe("RouteErrorBoundary", () => {
|
||||
it.each(["/accounts", "/login"])(
|
||||
"recovers a transient failure through the fallback on %s itself",
|
||||
async (path) => {
|
||||
authenticated = path === "/accounts";
|
||||
let failing = true;
|
||||
const RecoverableRoute: FC = () => {
|
||||
if (failing) throw new Error("temporary failure");
|
||||
return <div>recovered route</div>;
|
||||
};
|
||||
render(
|
||||
<MantineProvider>
|
||||
<MemoryRouter initialEntries={[path]}>
|
||||
<RouteErrorBoundary>
|
||||
<RecoverableRoute />
|
||||
</RouteErrorBoundary>
|
||||
</MemoryRouter>
|
||||
</MantineProvider>,
|
||||
);
|
||||
const action = screen.getByRole("link", { name: "Продолжить" });
|
||||
failing = false;
|
||||
fireEvent.click(action);
|
||||
expect(await screen.findByText("recovered route")).toBeInTheDocument();
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps a persistent failure recoverable without an automatic reset loop", () => {
|
||||
authenticated = true;
|
||||
renderBoundary("/accounts");
|
||||
fireEvent.click(screen.getByRole("link", { name: "Продолжить" }));
|
||||
expect(
|
||||
screen.getByRole("heading", { name: "Не удалось открыть страницу" }),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.queryByText("test route failure")).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("link", { name: "Перезагрузить страницу" }),
|
||||
).toHaveAttribute("href", "/accounts");
|
||||
});
|
||||
|
||||
it("offers document navigation when React.lazy caches a rejected chunk", async () => {
|
||||
authenticated = false;
|
||||
const load = vi.fn(() => Promise.reject(new Error("chunk failed")));
|
||||
const Chunk = lazy(load);
|
||||
render(
|
||||
<MantineProvider>
|
||||
<MemoryRouter initialEntries={["/login"]}>
|
||||
<RouteErrorBoundary>
|
||||
<Suspense fallback={<div>loading chunk</div>}>
|
||||
<Chunk />
|
||||
</Suspense>
|
||||
</RouteErrorBoundary>
|
||||
</MemoryRouter>
|
||||
</MantineProvider>,
|
||||
);
|
||||
fireEvent.click(await screen.findByRole("link", { name: "Продолжить" }));
|
||||
expect(load).toHaveBeenCalledTimes(1);
|
||||
const reload = screen.getByRole("link", { name: "Перезагрузить страницу" });
|
||||
expect(reload).toHaveAttribute("href", "/login");
|
||||
// Cancel browser navigation in JSDOM only after proving React Router did not intercept it.
|
||||
let intercepted = true;
|
||||
const preventNavigation = (event: MouseEvent) => {
|
||||
intercepted = event.defaultPrevented;
|
||||
event.preventDefault();
|
||||
};
|
||||
document.addEventListener("click", preventNavigation, { once: true });
|
||||
fireEvent.click(reload);
|
||||
expect(intercepted).toBe(false);
|
||||
});
|
||||
|
||||
it("offers login after a public route error without error details", () => {
|
||||
authenticated = false;
|
||||
renderBoundary("/login");
|
||||
|
||||
@@ -51,17 +51,23 @@ const RouteErrorFallback: FC<RouteErrorFallbackProps> = (props) => {
|
||||
<Button component={Link} to={to}>
|
||||
{t("routeError.action")}
|
||||
</Button>
|
||||
<Button component="a" href={to} variant="default">
|
||||
{t("routeError.reload")}
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const RouteErrorBoundary: FC<PropsWithChildren> = (props) => {
|
||||
const { children } = props;
|
||||
const { pathname, search } = useLocation();
|
||||
const { pathname, search, key } = useLocation();
|
||||
const { user } = useAuth();
|
||||
|
||||
return (
|
||||
<Boundary authenticated={Boolean(user)} routeKey={`${pathname}${search}`}>
|
||||
<Boundary
|
||||
authenticated={Boolean(user)}
|
||||
routeKey={`${pathname}${search}:${key}`}
|
||||
>
|
||||
{children}
|
||||
</Boundary>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user