Their titles open the item's page, a pointer-only door; the card menu is the keyboard's.
114 lines
4.2 KiB
TypeScript
114 lines
4.2 KiB
TypeScript
// @vitest-environment jsdom
|
|
//
|
|
// A show's card in the Browse grid plays (the owner, 2026-10-09): its picture asks the server which
|
|
// episode and opens the player on it; a started show's Restart opens it from the start; its title
|
|
// opens the show's page. The hook is tested on its own (usePlayShow); this is the grid wiring it.
|
|
import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from "vitest";
|
|
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
|
|
vi.mock("react-i18next", async (importOriginal) => ({
|
|
...(await importOriginal<typeof import("react-i18next")>()),
|
|
useTranslation: () => ({ t: (key: string) => key, i18n: { language: "en" } }),
|
|
}));
|
|
vi.mock("./PlexPlayer", () => ({
|
|
default: ({ itemId, fromStart }: { itemId: string; fromStart?: boolean }) => (
|
|
<div>
|
|
player {itemId} {fromStart ? "from the start" : "resumed"}
|
|
</div>
|
|
),
|
|
}));
|
|
vi.mock("./plexViews", async (importOriginal) => ({
|
|
...(await importOriginal<typeof import("./plexViews")>()),
|
|
PlexShowView: ({ showId }: { showId: string }) => <div>show page {showId}</div>,
|
|
PlexInfoView: ({ id }: { id: string }) => <div>info page {id}</div>,
|
|
}));
|
|
|
|
import PlexBrowse from "./PlexBrowse";
|
|
import { api, type PlexCard } from "../lib/api";
|
|
|
|
beforeAll(() => {
|
|
class Stub {
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {}
|
|
takeRecords() {
|
|
return [];
|
|
}
|
|
}
|
|
globalThis.ResizeObserver ??= Stub as unknown as typeof ResizeObserver;
|
|
globalThis.IntersectionObserver ??= Stub as unknown as typeof IntersectionObserver;
|
|
});
|
|
beforeEach(() => window.history.replaceState({ _sub: { kind: "grid" } }, ""));
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
const show: PlexCard = {
|
|
id: "sh",
|
|
type: "show",
|
|
title: "Grimm",
|
|
thumb: "",
|
|
status: "in_progress",
|
|
resumable: true,
|
|
};
|
|
|
|
function mount(episodes: PlexCard[] = []) {
|
|
vi.spyOn(api, "plexLibrary").mockResolvedValue({
|
|
items: [show],
|
|
total: 1,
|
|
offset: 0,
|
|
episodes,
|
|
} as never);
|
|
const next = vi.spyOn(api, "plexShowNext").mockResolvedValue({
|
|
next: { id: "e7", type: "episode", title: "Ep 7", thumb: "" },
|
|
});
|
|
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
|
render(
|
|
<QueryClientProvider client={qc}>
|
|
<PlexBrowse />
|
|
</QueryClientProvider>,
|
|
);
|
|
return { next };
|
|
}
|
|
|
|
describe("PlexBrowse — a show's card in the grid", () => {
|
|
it("plays the episode the server names", async () => {
|
|
const { next } = mount();
|
|
fireEvent.click(await screen.findByRole("button", { name: "card.continue" }));
|
|
expect(await screen.findByText("player e7 resumed")).toBeTruthy();
|
|
expect(next).toHaveBeenCalledWith("sh", "en");
|
|
});
|
|
|
|
it("starts that episode over from its Restart", async () => {
|
|
mount();
|
|
fireEvent.click(await screen.findByRole("button", { name: "card.restart" }));
|
|
expect(await screen.findByText("player e7 from the start")).toBeTruthy();
|
|
});
|
|
|
|
it("opens the show's page from its title, asking nothing", async () => {
|
|
const { next } = mount();
|
|
fireEvent.click(await screen.findByText("Grimm"), { detail: 1 });
|
|
expect(await screen.findByText("show page sh")).toBeTruthy();
|
|
expect(next).not.toHaveBeenCalled();
|
|
});
|
|
|
|
// A matched episode's title opens its page, a pointer's door: its menu offers the same to the keyboard.
|
|
it("opens a matched episode's page from its menu, as its title does", async () => {
|
|
mount([{ id: "ep1", type: "episode", title: "Pilot", thumb: "", show_title: "Grimm" }]);
|
|
const section = (await screen.findByText("plex.unified.episodes")).parentElement!;
|
|
fireEvent.click(section.querySelector<HTMLButtonElement>("button[aria-haspopup]")!);
|
|
const rows = await screen.findAllByRole("menuitem", { hidden: true });
|
|
fireEvent.click(rows.find((r) => r.textContent === "plex.info.title")!);
|
|
expect(await screen.findByText("info page ep1")).toBeTruthy();
|
|
});
|
|
|
|
it("opens the show's page from its ⓘ, asking nothing", async () => {
|
|
const { next } = mount();
|
|
fireEvent.click(await screen.findByRole("button", { name: "plex.openShow" }));
|
|
expect(await screen.findByText("show page sh")).toBeTruthy();
|
|
expect(next).not.toHaveBeenCalled();
|
|
});
|
|
});
|