Files
siftlode/frontend/src/components/plexShow.aside.dom.test.tsx
T
peter 78fdcda6bb feat(ui): E31 S4c — the channel header folds, the Plex pages fill their hero, the player gets a side panel
- Channel page: the banner and identity block fold away once the grid scrolls
  (ui/scrollFold: folds past 32px only if the page can stay scrolled, unfolds
  at the very top) and the name rides in the tab row. One Back button in both
  states keeps its focus; folded at a tablet width the actions wrap instead of
  pushing the page sideways.
- Plex movie and show pages: the TMDB facts over the key crew are the hero's
  second column (plexDetailAside), the keywords sit under the text; the show
  page shows them for the first time, with the same three toggles. A
  collection's hero is sized to its text.
- YouTube player, 1280px and wider: the title, channel, actions, description
  and queue sit in a side panel beside the video, which takes the window's
  height (1438x809 at 1920x911, was ~1008 wide). The wheel scrolls that panel
  while it can and turns the volume elsewhere.
- DESIGN.md decision log: the keywords' place under the text.
2026-10-02 02:13:17 +02:00

106 lines
3.3 KiB
TypeScript

// @vitest-environment jsdom
//
// E31 (the owner's pick "A"): TMDB enriches shows as it does movies — studios, countries, keywords, key
// crew — and the show page showed none of it, beside a hero whose text stopped at ~55% of the panel. Its
// hero now has the movie page's second column (facts over crew) and the keywords under the text.
import { describe, it, expect, afterEach, beforeAll, vi } from "vitest";
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
beforeAll(() => {
class Stub {
observe() {}
unobserve() {}
disconnect() {}
takeRecords() {
return [];
}
}
globalThis.ResizeObserver ??= Stub as unknown as typeof ResizeObserver;
});
vi.mock("react-i18next", async (importOriginal) => {
const actual = await importOriginal<typeof import("react-i18next")>();
return {
...actual,
useTranslation: () => ({ i18n: { language: "en" }, t: (key: string) => key }),
};
});
const detail = {
show: {
id: "show",
title: "Fargo",
summary: "Various chronicles.",
year: 2014,
thumb: "",
art: "",
content_rating: "TV-MA",
studio: "26 Keys",
genres: ["Crime"],
cast: [],
status: "new",
episode_count: 0,
collection_keys: [],
tmdb: {
keywords: ["minnesota", "winter"],
crew: [{ name: "Noah Hawley", job: "Creator" }],
studios: ["26 Keys Productions", "MGM Television"],
production_countries: [{ code: "US", name: "United States" }],
recommendations: [],
backdrops: [],
},
},
seasons: [],
related: [],
};
vi.mock("../lib/api", () => ({
api: {
plexShow: () => Promise.resolve(detail),
updatePrefs: () => Promise.resolve({}),
},
EMPTY_PLEX_FILTERS: {},
plexFilterCount: () => 0,
}));
import { PlexShowView } from "./plexViews";
afterEach(cleanup);
function renderShow(onFilter = vi.fn(), onOpenPerson = vi.fn()) {
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
render(
<QueryClientProvider client={qc}>
<PlexShowView
showId="show"
onBack={() => {}}
onPlay={() => {}}
onOpenSeason={() => {}}
onOpenShow={() => {}}
onFilter={onFilter}
onOpenPerson={onOpenPerson}
/>
</QueryClientProvider>,
);
return { onFilter, onOpenPerson };
}
describe("the show page's facts, keywords and crew (E31)", () => {
it("shows the facts over the crew beside the text, and the keywords under it", async () => {
const { onFilter, onOpenPerson } = renderShow();
const aside = await screen.findByTestId("detail-aside");
expect(aside.textContent).toContain("MGM Television");
expect(aside.textContent).toContain("Noah Hawley");
expect(aside.parentElement!.className).toContain("@5xl:grid-cols-");
const main = aside.parentElement!.children[0]!;
expect(main.contains(screen.getByTestId("detail-keywords"))).toBe(true);
expect(main.contains(screen.getByText("Various chronicles."))).toBe(true);
// They act as on the movie page: a keyword filters, a crew name opens the person.
fireEvent.click(screen.getByText("minnesota"));
expect(onFilter).toHaveBeenCalledWith({ keywords: ["minnesota"] });
fireEvent.click(screen.getByText("Noah Hawley"));
expect(onOpenPerson).toHaveBeenCalledWith("Noah Hawley");
});
});