import { readFileSync } from "node:fs"; import { expect, it } from "vitest"; import { cardImage } from "./plexImage"; import { readSources } from "./rawSources.testkit"; // A card's srcset names each Plex cut by its width, and the browser picks one by that width. The // widths are the image proxy's (backend `app/plex/imgcache.py` SIZES), with no wire format between // the two: a backend-only change would have the browser pick a cut by a width it does not have. // Read from source, as plexFinishMargin.contract does. const source = readFileSync( new URL("../../../backend/app/plex/imgcache.py", import.meta.url), "utf8", ); const backendWidth = (variant: string) => { const m = new RegExp(`"${variant}": \\((\\d+), \\d+\\)`).exec(source); // If this fails the variant was renamed or moved: re-point this test, the invariant stayed. expect(m, `${variant} not in imgcache.SIZES`).not.toBeNull(); return Number(m![1]); }; const srcsetWidths = (url: string) => Object.fromEntries( cardImage(url) .srcSet!.split(", ") .map((c) => { const [u, w] = c.split(" "); return [ new URL(u!, "http://x").searchParams.get("variant") ?? "thumb", Number(w!.slice(0, -1)), ]; }), ); it("names each poster cut by the width the proxy cuts it at", () => { expect(srcsetWidths("/api/plex/image/1")).toEqual({ thumb_sm: backendWidth("thumb_sm"), thumb: backendWidth("thumb"), }); }); it("names each backdrop cut by the width the proxy cuts it at", () => { expect(srcsetWidths("/api/plex/image/1?variant=art")).toEqual({ art_sm: backendWidth("art_sm"), art: backendWidth("art"), }); }); // Every small Plex picture asks for its card-size cut through cardImage (the // playlist rows, the search box and the collection picker still fetched the 400px poster for a 32px // picture). A plain `src={…}` is allowed only where the full cut is the point or the URL is not the // proxy's; the SET is pinned, so a new one fails here. Every 's src counts, wherever it sits // among the attributes and whatever its variable is called: the first cut of this test read only a // leading `src={…thumb…}` and missed two of the three sites it was written for. const plexFiles = readSources(__dirname, [ "../modules/plex/{Plex,plex}*.tsx", "../features/plexCards/{Plex,plex}*.tsx", "../modules/home/Home.tsx", "../modules/plex/{PosterTile,UpNextCard}.tsx", "!../**/*.test.tsx", ]); const ALLOWED = new Set([ "plexCards.tsx pic.src", // cardImage's own src (srcSet/sizes beside it): the card cut "plexCards.tsx c.thumb", // cast photos: the person-image route, not the image proxy "PlexBrowse.tsx p.photo", // the person filter chip's headshot: the person-image route "plexViews.tsx d.photo", // the person page's headshot: the same "PlexInfo.tsx c.thumb", // the same cast photos on the detail page "PlexInfo.tsx detail.thumb", // the detail page's own poster: the full cut is the point "plexDetailUi.tsx thumb", // the show/season header poster: the same "Home.tsx card.thumb", // the hero (full width) and the shelf's own tiles (YouTube channel art) ]); it("leaves no small Plex picture on the full cut", () => { expect(Object.keys(plexFiles).length).toBeGreaterThan(5); const bare = new Set(); for (const [path, src] of Object.entries(plexFiles)) for (const m of (src as string).matchAll(/]*?\bsrc=\{([^}]*)\}/g)) bare.add(`${path.split("/").pop()} ${m[1]!.trim()}`); expect([...bare].filter((b) => !ALLOWED.has(b))).toEqual([]); });