import { act, fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import AboutModal from "../../components/AboutModal";
// Mock App module for constants
vi.mock("../../App", () => ({
FRONTEND_VERSION: "1.0.0",
GITHUB_URL: "https://github.com/test/repo",
}));
describe("AboutModal", () => {
const defaultProps = {
isOpen: true,
onClose: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
});
it("returns null when not open", () => {
const { container } = render();
expect(container.firstChild).toBeNull();
});
it("renders when open", () => {
render();
expect(screen.getByText(/about\.appName/i)).toBeInTheDocument();
});
it("displays version number", () => {
render();
expect(screen.getByText(/1\.0\.0/)).toBeInTheDocument();
});
it("calls onClose when close button is clicked", () => {
render();
fireEvent.click(screen.getByText("×"));
expect(defaultProps.onClose).toHaveBeenCalled();
});
it("calls onClose when overlay is clicked", () => {
const { container } = render();
const overlay = container.querySelector(".modal-overlay");
fireEvent.click(overlay!);
expect(defaultProps.onClose).toHaveBeenCalled();
});
it("does not call onClose when modal content is clicked", () => {
const { container } = render();
const content = container.querySelector(".about-modal");
if (content) {
fireEvent.click(content);
expect(defaultProps.onClose).not.toHaveBeenCalled();
}
});
it("renders GitHub link", () => {
render();
const links = screen.getAllByRole("link");
expect(links.length).toBeGreaterThan(0);
});
it("renders version as link to GitHub release", () => {
render();
const versionLink = screen.getByText("1.0.0").closest("a");
expect(versionLink).toHaveAttribute("href", "https://github.com/test/repo/releases/tag/v1.0.0");
expect(versionLink).toHaveAttribute("target", "_blank");
});
it("shows up-to-date result after successful version check", async () => {
vi.useFakeTimers();
(global.fetch as ReturnType).mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ tag_name: "v1.0.0" }),
});
render();
await act(async () => {
fireEvent.click(screen.getByRole("button", { name: /about\.checkForUpdates/i }));
await vi.advanceTimersByTimeAsync(1000);
});
expect(screen.getByText(/about\.upToDate/i)).toBeInTheDocument();
vi.useRealTimers();
});
it("shows update available result with download link", async () => {
vi.useFakeTimers();
(global.fetch as ReturnType).mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ tag_name: "v1.2.0" }),
});
render();
await act(async () => {
fireEvent.click(screen.getByRole("button", { name: /about\.checkForUpdates/i }));
await vi.advanceTimersByTimeAsync(1000);
});
expect(screen.getByText(/about\.updateAvailable/i)).toBeInTheDocument();
const downloadLink = screen.getByRole("link", { name: /about\.downloadUpdate/i });
expect(downloadLink).toHaveAttribute("href", "https://github.com/test/repo/releases/latest");
vi.useRealTimers();
});
it("shows error result when update check fails", async () => {
vi.useFakeTimers();
(global.fetch as ReturnType).mockResolvedValueOnce({
ok: false,
json: () => Promise.resolve({}),
});
render();
await act(async () => {
fireEvent.click(screen.getByRole("button", { name: /about\.checkForUpdates/i }));
await vi.advanceTimersByTimeAsync(1000);
});
expect(screen.getByText(/about\.checkFailed/i)).toBeInTheDocument();
vi.useRealTimers();
});
});