Files
silo-server/web/src/components/EpisodeRow.test.tsx
T
f377be9d6e feat(catalog): include per-episode overlay summaries on season episode listings (#364)
* feat(catalog): include per-episode overlay summaries on season episode listings

Episode cards already render user-configurable overlay badges everywhere
except the series/season detail pages: the season-episodes endpoint never
included overlay_summary, so SeasonEpisodeGrid and EpisodeRow had nothing
to render.

Server: add overlay_summary (omitempty) to episodeResponse and populate it
in buildEpisodeResponses via overlays.BuildSummary over the episode's
already-loaded media files, access-filtered with FilterMediaFilesByAccess
to match the browse/sections paths. No extra queries; the files were
already batch-fetched for the files[] payload. Additive-only API change.

Web: extend EpisodeListItem with overlay_summary, add
overlayDataFromEpisodeListItem (shared extract helper), and render
CardOverlays (variant="wide") on the episode stills in SeasonEpisodeGrid
and EpisodeRow using useOverlayPrefs, matching the ContinueWatchingCard
pattern. The hook already honors the admin kill switch and per-user prefs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(catalog): apply library restrictions in FilterMediaFilesByAccess without a quality ceiling

FilterMediaFilesByAccess short-circuited whenever MaxPlaybackQuality was
empty, skipping the AllowedLibraryIDs/DisabledLibraryIDs checks that
FileAllowedByAccess enforces. For viewers with library restrictions but
no quality ceiling, callers (episode listings, browse/section overlay
summaries, item detail versions) received files from restricted
libraries. Short-circuit only when no access criteria are set at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: rxwatcher <rxwatcher@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 10:37:34 -04:00

70 lines
1.8 KiB
TypeScript

import { renderToStaticMarkup } from "react-dom/server";
import { MemoryRouter } from "react-router";
import { describe, expect, it, vi } from "vitest";
import EpisodeRow from "./EpisodeRow";
vi.mock("@/components/overlays/CardOverlays", () => ({
default: () => null,
}));
vi.mock("@/hooks/useOverlayPrefs", () => ({
useOverlayPrefs: () => ({ prefs: null }),
}));
describe("EpisodeRow", () => {
it("renders progress from inline episode user_data", () => {
const markup = renderToStaticMarkup(
<MemoryRouter>
<EpisodeRow
episode={{
content_id: "ep-001",
season_number: 1,
episode_number: 1,
title: "Pilot",
overview: "",
air_date: "2024-01-01",
runtime: 42,
still_url: "",
still_thumbhash: "",
user_data: {
played: false,
is_in_progress: true,
position_seconds: 600,
duration_seconds: 2400,
},
files: [],
}}
/>
</MemoryRouter>,
);
expect(markup).toContain("width:25%");
});
it("renders a watched indicator from inline episode user_data", () => {
const markup = renderToStaticMarkup(
<MemoryRouter>
<EpisodeRow
episode={{
content_id: "ep-002",
season_number: 1,
episode_number: 2,
title: "Cat's in the Bag...",
overview: "",
air_date: null,
runtime: 48,
still_url: "",
still_thumbhash: "",
user_data: {
played: true,
},
files: [],
}}
/>
</MemoryRouter>,
);
expect(markup).toContain("text-success");
});
});