- Fix LogStreamPanel event listener cleanup with synchronous unlisten - Fix eventBus async-unsafe unsubscribe with proper error handling - Fix KubernetesPage infinite loading by resetting state on section change - Add ErrorBoundary component with reset capability - Add Badge component with multiple variants - Add ResourceDetailsDrawer for slide-out details panel - Add useFavorites hook with localStorage persistence - Add useKeyboardShortcuts hook for declarative shortcuts - Add comprehensive test coverage for all new components/hooks - Add keyboard shortcuts documentation to README - Wrap KubernetesPage with ErrorBoundary for crash recovery - Install react-window for virtual scrolling support Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
85 lines
2.6 KiB
TypeScript
85 lines
2.6 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import { render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { ErrorBoundary } from "./ErrorBoundary";
|
|
|
|
const ThrowError = ({ shouldThrow }: { shouldThrow: boolean }) => {
|
|
if (shouldThrow) {
|
|
throw new Error("Test error");
|
|
}
|
|
return <div>Content</div>;
|
|
};
|
|
|
|
describe("ErrorBoundary", () => {
|
|
it("renders children when there is no error", () => {
|
|
render(
|
|
<ErrorBoundary>
|
|
<ThrowError shouldThrow={false} />
|
|
</ErrorBoundary>
|
|
);
|
|
expect(screen.getByText("Content")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders error UI when child throws", () => {
|
|
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
render(
|
|
<ErrorBoundary>
|
|
<ThrowError shouldThrow={true} />
|
|
</ErrorBoundary>
|
|
);
|
|
expect(screen.getByText("Something went wrong")).toBeInTheDocument();
|
|
expect(screen.getByText(/Test error/)).toBeInTheDocument();
|
|
consoleError.mockRestore();
|
|
});
|
|
|
|
it("resets error when reset button is clicked", async () => {
|
|
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
const user = userEvent.setup();
|
|
const { rerender } = render(
|
|
<ErrorBoundary>
|
|
<ThrowError shouldThrow={true} />
|
|
</ErrorBoundary>
|
|
);
|
|
expect(screen.getByText("Something went wrong")).toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole("button", { name: /Reset Component/i }));
|
|
|
|
rerender(
|
|
<ErrorBoundary>
|
|
<ThrowError shouldThrow={false} />
|
|
</ErrorBoundary>
|
|
);
|
|
expect(screen.getByText("Content")).toBeInTheDocument();
|
|
consoleError.mockRestore();
|
|
});
|
|
|
|
it("uses custom fallback when provided", () => {
|
|
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
const customFallback = (error: Error, resetError: () => void) => (
|
|
<div>
|
|
<p>Custom error: {error.message}</p>
|
|
<button onClick={resetError}>Custom Reset</button>
|
|
</div>
|
|
);
|
|
render(
|
|
<ErrorBoundary fallback={customFallback}>
|
|
<ThrowError shouldThrow={true} />
|
|
</ErrorBoundary>
|
|
);
|
|
expect(screen.getByText("Custom error: Test error")).toBeInTheDocument();
|
|
expect(screen.getByText("Custom Reset")).toBeInTheDocument();
|
|
consoleError.mockRestore();
|
|
});
|
|
|
|
it("logs error to console", () => {
|
|
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
render(
|
|
<ErrorBoundary>
|
|
<ThrowError shouldThrow={true} />
|
|
</ErrorBoundary>
|
|
);
|
|
expect(consoleError).toHaveBeenCalled();
|
|
consoleError.mockRestore();
|
|
});
|
|
});
|