import { describe, expect, test, vi } from "vitest"; import { screen } from "@testing-library/react"; import { userEvent } from "@testing-library/user-event"; import { FilePreview } from "../../../components/dump/FilePreview.tsx"; import { makeDump, makeSession } from "../../fixtures.ts"; import { renderWithContexts } from "../../render.tsx"; import { dumpFileUrl, dumpThumbnailUrl } from "../../../utils/shared/urls.ts"; import { dumpFileVersion } from "../../../../shared/dump.ts"; describe("FilePreview", () => { const session = makeSession(); const file = (mime: string, over = {}) => makeDump({ id: "f1", kind: "file", fileName: "thing", fileMime: mime, fileSize: 2048, url: undefined, ...over, }); test("images show inline, with the media token", () => { const d = file("image/png"); renderWithContexts(, { session }); expect(screen.getByRole("img", { name: "thing" })).toHaveAttribute( "src", `http://localhost:8000/api/dumps/f1/file?v=${ dumpFileVersion(d) }&token=${session.mediaToken}`, ); }); test("a compact image shows the server's preview, not the upload", () => { const d = file("image/png"); renderWithContexts(, { session }); const img = screen.getByRole("img", { name: "thing" }); // The copy scaled to a card's preview, on a dense screen. expect(img).toHaveAttribute( "src", `${dumpThumbnailUrl(d, session.mediaToken)}&w=320`, ); expect(img).toHaveAttribute("sizes", "128px"); expect(img).toHaveAttribute("loading", "lazy"); }); test("other files are a download link", () => { renderWithContexts(); const link = screen.getByRole("link", { name: /Download thing/ }); expect(link).toHaveAttribute("download", "thing"); expect(link).toHaveTextContent("(2 kB)"); }); test("compact audio plays in the global player", async () => { const play = vi.fn(); renderWithContexts(, { player: { play }, }); await userEvent.click(screen.getByRole("button", { name: "Play" })); expect(play).toHaveBeenCalledWith(expect.objectContaining({ kind: "file", mimeType: "audio/mpeg", title: "Dump f1", subtitle: "thing", dumpHref: "/@owner/dumps/f1", artworkUrl: undefined, })); }); test("compact audio without a cover shows a placeholder, not an image", () => { const { container } = renderWithContexts( , ); expect(container.querySelector("img")).toBeNull(); expect(container.querySelector(".thumb-placeholder")).not.toBeNull(); }); test("compact audio pauses once it's the one playing", async () => { const play = vi.fn(); const togglePlay = vi.fn(); const d = file("audio/mpeg"); renderWithContexts(, { player: { play, togglePlay, playing: true, current: { kind: "file", fileUrl: dumpFileUrl(d), mimeType: "audio/mpeg", }, }, }); const button = screen.getByRole("button", { name: "Pause" }); expect(button).toHaveTextContent("⏸"); await userEvent.click(button); expect(togglePlay).toHaveBeenCalled(); expect(play).not.toHaveBeenCalled(); }); test("the global audio cover is the uploaded thumbnail", () => { route("GET /api/dumps/:id/file", () => new TypeError("no audio in jsdom")); const d = file("audio/mpeg", { thumbnailMime: "image/png" }); const { container } = renderWithContexts( , ); expect(container.querySelector("button img")).toHaveAttribute( "src", `${dumpThumbnailUrl(d)}&w=320`, ); }); test("compact video claims its generated still as artwork", async () => { const play = vi.fn(); const d = file("video/mp4"); renderWithContexts(, { player: { play }, }); await userEvent.click(screen.getByRole("button", { name: "Play" })); expect(play.mock.calls[0][0].artworkUrl).toBe(dumpThumbnailUrl(d)); expect(play.mock.calls[0][0].artworkUrl).toMatch( /^http:\/\/localhost:8000\/api\/dumps\/f1\/thumbnail\?v=2048-\d+$/, ); }); test("the global video button toggles when it's the one playing", async () => { const togglePlay = vi.fn(); const d = file("video/mp4"); renderWithContexts(, { player: { togglePlay, playing: true, current: { kind: "file", fileUrl: dumpFileUrl(d), mimeType: "video/mp4", }, }, }); await userEvent.click(screen.getByRole("button", { name: "Pause" })); expect(togglePlay).toHaveBeenCalled(); }); test("the global audio waveform starts playback", async () => { const play = vi.fn(); route("GET /api/dumps/:id/file", () => new TypeError("no audio in jsdom")); renderWithContexts(, { player: { play }, }); await userEvent.click(screen.getByRole("button", { name: "Play" })); expect(play).toHaveBeenCalledWith( expect.objectContaining({ kind: "file", mimeType: "audio/ogg" }), ); }); test("compact PDFs and text get an icon", () => { const { container } = renderWithContexts( , ); expect(container).toHaveTextContent("📄"); }); }); import { route } from "../../fetch.ts";