Files
silo-server/web/src/components/MatchItemDialog.test.tsx
T

177 lines
5.3 KiB
TypeScript

import { renderToStaticMarkup } from "react-dom/server";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ReactNode } from "react";
const mocks = vi.hoisted(() => ({
useSearchItemMatchCandidates: vi.fn(),
useApplyItemMatch: vi.fn(),
useCatalogItemDetail: vi.fn(),
}));
vi.mock("@/hooks/queries/items", () => ({
useSearchItemMatchCandidates: (...args: unknown[]) => mocks.useSearchItemMatchCandidates(...args),
useApplyItemMatch: (...args: unknown[]) => mocks.useApplyItemMatch(...args),
}));
vi.mock("@/hooks/queries/catalogRead", () => ({
useCatalogItemDetail: (...args: unknown[]) => mocks.useCatalogItemDetail(...args),
}));
// Mock Dialog components to render inline (Radix portals don't render in SSR)
vi.mock("@/components/ui/dialog", () => ({
Dialog: ({ children, open }: { children: ReactNode; open: boolean }) =>
open ? <div data-testid="dialog">{children}</div> : null,
DialogContent: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogHeader: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogTitle: ({ children }: { children: ReactNode }) => <h2>{children}</h2>,
}));
import MatchItemDialog from "./MatchItemDialog";
const baseItem = {
content_id: "movie-1",
title: "Inception",
year: 2010,
type: "movie" as const,
};
describe("MatchItemDialog", () => {
beforeEach(() => {
const mutate = vi.fn();
mocks.useSearchItemMatchCandidates.mockReturnValue({
mutate,
isPending: false,
isSuccess: false,
data: null,
});
mocks.useApplyItemMatch.mockReturnValue({
mutate,
isPending: false,
});
mocks.useCatalogItemDetail.mockReturnValue({
data: undefined,
isLoading: false,
});
});
it("renders title, year, IMDb, TMDB, and TVDB inputs", () => {
const markup = renderToStaticMarkup(
<MatchItemDialog item={baseItem} open={true} onOpenChange={() => {}} />,
);
expect(markup).toContain('id="match-title"');
expect(markup).toContain('id="match-year"');
expect(markup).toContain('id="match-imdb"');
expect(markup).toContain('id="match-tmdb"');
expect(markup).toContain('id="match-tvdb"');
});
it("renders a search button", () => {
const markup = renderToStaticMarkup(
<MatchItemDialog item={baseItem} open={true} onOpenChange={() => {}} />,
);
expect(markup).toContain("Search");
});
it("renders candidate list when search returns results", () => {
mocks.useSearchItemMatchCandidates.mockReturnValue({
mutate: vi.fn(),
isPending: false,
isSuccess: true,
data: {
candidates: [
{
title: "Inception",
year: 2010,
content_type: "movie",
overview: "A thief who steals secrets...",
image_url: "https://image.tmdb.org/poster.jpg",
provider_ids: { tmdb: "27205", imdb: "tt1375666" },
sources: ["tmdb", "tvdb"],
agreement_hints: ["agreed_by_tmdb_and_tvdb"],
},
{
title: "Inception (TV)",
year: 2012,
content_type: "series",
overview: "An unrelated series",
image_url: "",
provider_ids: { tvdb: "999" },
sources: ["tvdb"],
agreement_hints: [],
},
],
},
});
const markup = renderToStaticMarkup(
<MatchItemDialog item={baseItem} open={true} onOpenChange={() => {}} />,
);
expect(markup).toContain("Inception");
expect(markup).toContain("Inception (TV)");
expect(markup).toContain("tmdb");
expect(markup).toContain("2 sources agree");
expect(markup).toContain('data-testid="match-candidate"');
});
it("shows no results message when search returns empty", () => {
mocks.useSearchItemMatchCandidates.mockReturnValue({
mutate: vi.fn(),
isPending: false,
isSuccess: true,
data: { candidates: [] },
});
const markup = renderToStaticMarkup(
<MatchItemDialog item={baseItem} open={true} onOpenChange={() => {}} />,
);
expect(markup).toContain("No candidates found");
});
it("displays current item summary with title, year, and type badge", () => {
const markup = renderToStaticMarkup(
<MatchItemDialog item={baseItem} open={true} onOpenChange={() => {}} />,
);
expect(markup).toContain("Inception");
expect(markup).toContain("(2010)");
expect(markup).toContain("movie");
});
it("renders local media rows when file paths are available", () => {
const markup = renderToStaticMarkup(
<MatchItemDialog
item={{
...baseItem,
versions: [
{
file_id: 7,
file_path: "/media/Movies/Inception (2010)/Inception.4K.mkv",
file_name: "Inception.4K.mkv",
resolution: "2160p",
codec_video: "hevc",
codec_audio: "dts",
hdr: true,
container: "mkv",
file_size: 1,
duration: 1,
bitrate: 1,
},
],
}}
open={true}
onOpenChange={() => {}}
/>,
);
expect(markup).toContain("Local media");
expect(markup).toContain("Inception (2010)/");
expect(markup).toContain("Inception.4K.mkv");
expect(markup).toContain("2160p");
});
});