import * as React from "react";
import { describe, expect, it, vi } from "vitest";
import { ImageFieldRenderer, type ImageFieldValue } from "../../src/components/ImageFieldRenderer";
import { render } from "../utils/render.tsx";
vi.mock("../../src/components/MediaPickerModal", () => ({
MediaPickerModal: ({ open, onSelect }: { open: boolean; onSelect: (item: unknown) => void }) =>
open ? (
) : null,
}));
const selectedImage: ImageFieldValue = {
id: "featured-image",
provider: "local",
filename: "notes-on-simplicity.jpg",
mimeType: "image/jpeg",
alt: "Geometric pattern carved into white paper",
width: 1200,
height: 800,
meta: { storageKey: "featured-image.jpg" },
};
describe("ImageFieldRenderer", () => {
it("renders the featured variant as a full-width media card with metadata", async () => {
const screen = await render(
,
);
const filename = screen.getByText("notes-on-simplicity.jpg");
await expect.element(filename).toBeVisible();
const metadata = screen.getByText("1200 × 800 · image/jpeg");
await expect.element(metadata).toBeVisible();
expect(metadata.element()).toHaveAttribute("dir", "ltr");
const replaceButton = screen.getByRole("button", { name: "Replace" });
await expect.element(replaceButton).toBeVisible();
expect(replaceButton.element().querySelector("svg")).not.toBeNull();
const removeButton = screen.getByRole("button", { name: "Remove image" });
await expect.element(removeButton).toBeVisible();
expect(removeButton.element()).toHaveTextContent("Remove");
const image = screen.container.querySelector("img");
expect(image).toHaveAttribute("src", "/_emdash/api/media/file/featured-image.jpg");
});
it("falls back cleanly when optional featured-image metadata is missing", async () => {
const screen = await render(
,
);
await expect.element(screen.getByText("Selected image")).toBeVisible();
expect(screen.container.textContent).not.toContain("×");
expect(screen.container.textContent).not.toContain("·");
});
it("encodes path-unsafe characters in local storage keys", async () => {
const screen = await render(
,
);
const image = screen.container.querySelector("img");
expect(image).toHaveAttribute("src", "/_emdash/api/media/file/featured%3Fdraft%231.jpg");
});
it("preserves filename and MIME type when a replacement is selected", async () => {
const onChange = vi.fn();
const screen = await render(
,
);
await screen.getByRole("button", { name: "Replace" }).click();
await screen.getByRole("button", { name: "Choose replacement" }).click();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
id: "replacement-image",
filename: "replacement.webp",
mimeType: "image/webp",
width: 1600,
height: 800,
}),
);
});
it("removes the featured image immediately", async () => {
const onChange = vi.fn();
const screen = await render(
,
);
await screen.getByRole("button", { name: "Remove image" }).click();
expect(onChange).toHaveBeenCalledWith(null);
});
it("keeps the featured card and actions available when the image is broken", async () => {
const screen = await render(
,
);
const image = screen.container.querySelector("img");
expect(image).not.toBeNull();
image!.dispatchEvent(new Event("error"));
await expect.element(screen.getByText("Image not found")).toBeVisible();
await expect.element(screen.getByText("notes-on-simplicity.jpg")).toBeVisible();
await expect.element(screen.getByRole("button", { name: "Replace" })).toBeVisible();
await expect.element(screen.getByRole("button", { name: "Remove image" })).toBeVisible();
});
it("opens the picker from the featured empty state and reports required validation", async () => {
const screen = await render(
,
);
const selectButton = screen.getByRole("button", { name: "Select image" });
await expect.element(selectButton).toBeVisible();
await expect.element(screen.getByText("This field is required")).toBeVisible();
await selectButton.click();
await expect.element(screen.getByRole("button", { name: "Choose replacement" })).toBeVisible();
});
it("does not show featured metadata in the default variant", async () => {
const screen = await render(
,
);
expect(screen.getByText("notes-on-simplicity.jpg").query()).toBeNull();
expect(screen.getByText("1200 × 800 · image/jpeg").query()).toBeNull();
});
});