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";