import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { createRef } from "react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { PreviewIframe } from "../PreviewIframe";
vi.mock("lucide-react", () => ({
AlertTriangle: () => ⚠️,
Loader2: () => ⏳,
ShieldAlert: () => 🛡️,
}));
describe("PreviewIframe", () => {
const originalWindowOpen = window.open;
beforeEach(() => {
window.open = vi.fn();
});
afterEach(() => {
window.open = originalWindowOpen;
});
it("renders null when url is null", () => {
const { container } = render(
()}
embedContext={null}
/>,
);
expect(container).toBeEmptyDOMElement();
});
it("renders iframe with correct url, sandbox attributes, and title", () => {
render(
()}
embedContext={null}
/>,
);
const iframe = screen.getByTitle("Dev server preview");
expect(iframe).toBeInTheDocument();
expect(iframe).toHaveAttribute("src", "http://localhost:3000");
expect(iframe).toHaveAttribute(
"sandbox",
"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox",
);
});
it("moves unknown status into loading", async () => {
const onEmbedStatusChange = vi.fn();
render(
()}
embedContext={null}
/>,
);
await waitFor(() => {
expect(onEmbedStatusChange).toHaveBeenCalledWith("loading");
});
});
it("iframe load marks status as embedded", () => {
const onEmbedStatusChange = vi.fn();
render(
()}
embedContext={null}
/>,
);
fireEvent.load(screen.getByTitle("Dev server preview"));
expect(onEmbedStatusChange).toHaveBeenCalledWith("embedded");
});
it("iframe error marks status as error", () => {
const onEmbedStatusChange = vi.fn();
render(
()}
embedContext={null}
/>,
);
fireEvent(screen.getByTitle("Dev server preview"), new Event("error"));
expect(onEmbedStatusChange).toHaveBeenCalledWith("error");
});
it("applies custom className", () => {
render(
()}
embedContext={null}
className="custom-class"
/>,
);
expect(screen.getByTitle("Dev server preview")).toHaveClass("custom-class");
});
it("blocked state shows ShieldAlert icon and embed context", () => {
render(
()}
embedContext="The server may block iframe embedding..."
/>,
);
expect(screen.getByTestId("shield-icon")).toBeInTheDocument();
expect(screen.getByText("Preview cannot be embedded")).toBeInTheDocument();
expect(screen.getByText("The server may block iframe embedding...")).toBeInTheDocument();
});
it("blocked state open in new tab opens external URL", () => {
render(
()}
embedContext="The server may block iframe embedding..."
/>,
);
fireEvent.click(screen.getByRole("button", { name: "Open in new tab" }));
expect(window.open).toHaveBeenCalledWith("http://localhost:3000", "_blank", "noopener,noreferrer");
});
it("blocked state retry calls onRetry", () => {
const onRetry = vi.fn();
render(
()}
embedContext="The server may block iframe embedding..."
onRetry={onRetry}
/>,
);
fireEvent.click(screen.getByRole("button", { name: "Retry" }));
expect(onRetry).toHaveBeenCalledTimes(1);
});
it("error state shows AlertTriangle and contextual message", () => {
render(
()}
embedContext="The preview URL could not be loaded..."
/>,
);
expect(screen.getByTestId("alert-icon")).toBeInTheDocument();
expect(screen.getByText("Unable to load preview")).toBeInTheDocument();
expect(screen.getByText("The preview URL could not be loaded...")).toBeInTheDocument();
});
it("error state retry calls onRetry", () => {
const onRetry = vi.fn();
render(
()}
embedContext="The preview URL could not be loaded..."
onRetry={onRetry}
/>,
);
fireEvent.click(screen.getByRole("button", { name: "Retry" }));
expect(onRetry).toHaveBeenCalledTimes(1);
});
it("loading state shows loading spinner without action buttons", () => {
render(
()}
embedContext={null}
/>,
);
expect(screen.getByTestId("loader-icon")).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Open in new tab" })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Retry" })).not.toBeInTheDocument();
});
});