import { create } from "@bufbuild/protobuf"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import PreviewImageDialog from "@/components/PreviewImageDialog"; import { AttachmentSchema, MediaCaptureTimeSchema, MediaLocationSchema, MediaMetadataSchema, PhotoMetadataSchema, } from "@/types/proto/api/v1/attachment_service_pb"; vi.mock("@/hooks/useMediaQuery", () => ({ __esModule: true, default: () => false, })); vi.mock("@/components/map/LazyLocationPicker", () => ({ LazyLocationPicker: () =>
, })); const labels: Record = { "attachment-details.actions.hide": "Hide attachment details", "attachment-details.actions.hide-map": "Hide map", "attachment-details.actions.show": "Show attachment details", "attachment-details.actions.show-map": "Show map", "attachment-details.empty": "No saved capture metadata.", "attachment-details.fields.altitude": "Altitude", "attachment-details.fields.camera": "Camera", "attachment-details.fields.captured": "Captured", "attachment-details.fields.dimensions": "Dimensions", "attachment-details.fields.exposure": "Exposure", "attachment-details.fields.file": "File", "attachment-details.fields.lens": "Lens", "attachment-details.fields.location": "Coordinates", "attachment-details.sections.camera": "Camera", "attachment-details.sections.capture": "Capture", "attachment-details.sections.file": "File", "attachment-details.sections.location": "Location", "attachment-details.timezone-unknown": "Time zone unknown", "attachment-details.title": "Details", }; vi.mock("@/utils/i18n", () => ({ findNearestMatchedLanguage: (language: string) => language || "en", useTranslate: () => (key: string) => labels[key] ?? key, })); const buildAttachmentWithPhotoMetadata = () => create(AttachmentSchema, { name: "attachments/photo-1", filename: "photo.jpg", type: "image/jpeg", size: 4_200_000n, mediaMetadata: create(MediaMetadataSchema, { width: 4032, height: 3024, details: { case: "photo", value: create(PhotoMetadataSchema, { captureTime: create(MediaCaptureTimeSchema, { localDateTime: "2026-08-10T14:32:18" }), location: create(MediaLocationSchema, { latitude: 1.3521, longitude: 103.8198, altitudeMeters: 18.4 }), sourceExifOrientation: 6, cameraMake: "Apple", cameraModel: "iPhone", lensModel: "Main Camera", fNumber: 1.78, exposureTimeSeconds: 1 / 120, iso: 64, focalLengthMm: 6.86, }), }, }), }); describe("", () => { it("provides a dialog description without accessibility warnings", async () => { const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); render( , ); await waitFor(() => { expect(warnSpy).not.toHaveBeenCalledWith(expect.stringContaining("Missing `Description`")); }); }); it("keeps hook order stable when preview items appear after an empty render", () => { const { rerender } = render(); expect(() => { rerender( , ); }).not.toThrow(); expect(screen.getByAltText("Preview image 1 of 1")).toBeInTheDocument(); }); it("shows zoom controls for image previews", () => { render( , ); expect(screen.getByRole("button", { name: /zoom in/i })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /zoom out/i })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /reset zoom/i })).toBeInTheDocument(); expect(screen.getByText("100%")).toBeInTheDocument(); }); it("toggles image zoom on double click", () => { render( , ); const image = screen.getByAltText("Preview image 1 of 1"); fireEvent.doubleClick(image); expect(image).toHaveStyle({ transform: "translate3d(0px, 0px, 0) scale(2)" }); expect(screen.getByText("200%")).toBeInTheDocument(); }); it("zooms image previews with the wheel", () => { render( , ); fireEvent.wheel(screen.getByTestId("preview-zoom-surface"), { deltaY: -100 }); expect(screen.getByText("120%")).toBeInTheDocument(); }); it("does not show zoom controls for video previews", () => { render( , ); expect(screen.queryByRole("button", { name: /zoom in/i })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /zoom out/i })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /reset zoom/i })).not.toBeInTheDocument(); }); it("keeps previous and next controls available for mobile image galleries", () => { render( , ); expect(screen.getByRole("button", { name: /previous item/i })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /next item/i })).toBeInTheDocument(); }); it("shows saved media details on demand with grouped, lean facts", () => { const attachment = buildAttachmentWithPhotoMetadata(); render( , ); expect(screen.queryByText("4032 × 3024 px")).not.toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Show attachment details" })); expect(screen.getByText("4032 × 3024 px")).toBeInTheDocument(); expect(screen.getByText("Apple iPhone")).toBeInTheDocument(); expect(screen.getByText("ƒ/1.78 · 1/120 s · ISO 64 · 6.86 mm")).toBeInTheDocument(); expect(screen.getByText("1.35210°, 103.81980°")).toBeInTheDocument(); expect(screen.getByText("Time zone unknown")).toBeInTheDocument(); }); it("loads the attachment map only after an explicit action", () => { const attachment = buildAttachmentWithPhotoMetadata(); render( , ); fireEvent.click(screen.getByRole("button", { name: "Show attachment details" })); expect(screen.queryByTestId("attachment-location-map")).not.toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Show map" })); expect(screen.getByTestId("attachment-location-map")).toBeInTheDocument(); }); it("closes details before closing the media dialog with Escape", async () => { const onOpenChange = vi.fn(); render( , ); fireEvent.click(screen.getByRole("button", { name: "Show attachment details" })); fireEvent.keyDown(document, { key: "Escape" }); await waitFor(() => expect(screen.queryByLabelText("Details")).not.toBeInTheDocument()); expect(onOpenChange).not.toHaveBeenCalled(); fireEvent.keyDown(document, { key: "Escape" }); await waitFor(() => expect(onOpenChange).toHaveBeenCalledWith(false)); }); });