165 lines
5.6 KiB
TypeScript
165 lines
5.6 KiB
TypeScript
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(<FilePreview dump={d} />, { 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(<FilePreview dump={d} compact />, { 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(<FilePreview dump={file("application/zip")} />);
|
|
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(<FilePreview dump={file("audio/mpeg")} compact />, {
|
|
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(
|
|
<FilePreview dump={file("audio/mpeg")} compact />,
|
|
);
|
|
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(<FilePreview dump={d} compact />, {
|
|
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(
|
|
<FilePreview dump={d} global />,
|
|
);
|
|
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(<FilePreview dump={d} compact />, {
|
|
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(<FilePreview dump={d} global />, {
|
|
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(<FilePreview dump={file("audio/ogg")} global />, {
|
|
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(
|
|
<FilePreview dump={file("application/pdf")} compact />,
|
|
);
|
|
expect(container).toHaveTextContent("📄");
|
|
});
|
|
});
|
|
|
|
import { route } from "../../fetch.ts";
|