import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; import videoCard from "./VideoCard.tsx?raw"; import { HOVER_REVEAL, HOVER_YIELD } from "../../components/ui/reveal"; import { MEDIA_BAR, MEDIA_HOVER_LAYER, MEDIA_PICTURE_CONTAINER, MEDIA_RESUME_LAYER, mediaPictureBox, } from "../../components/ui/mediaCardStyles"; // Measured: at text size 1.3 a 242px feed thumbnail put the 63px Play 13px into the 37px hover // row (the Play and the row grow with the root font, the picture's height does not). A narrow picture // (under 14rem, so the cut moves with the text size) centres what it carries above the row instead. describe("a media picture", () => { it("is a size container, so what sits on it answers its width", () => { expect(mediaPictureBox("wide")).toContain(MEDIA_PICTURE_CONTAINER); expect(mediaPictureBox("poster")).toContain(MEDIA_PICTURE_CONTAINER); // The video card draws its own box (it is not a mediaPictureBox). expect(videoCard).toContain('clsx("relative min-h-0", MEDIA_PICTURE_CONTAINER, className)'); }); it.each([ ["the Play", MEDIA_HOVER_LAYER], ["the Continue + Restart pair", MEDIA_RESUME_LAYER], ])("leaves the hover row its band when narrow: %s", (_, layer) => { expect(layer.split(" ")).toContain("@max-[14rem]/pic:pb-(--bar-room)"); }); // The lift once applied to every narrow picture, and the feed's row and tile views (a // 10rem thumbnail, no hover row) showed their Play 1.25rem above centre over an empty band. The band // is now the row's own: set only by a box that holds one. it("lifts it only where a hover row is there to clear", () => { expect(MEDIA_BAR.split(" ")).toContain("media-bar"); expect(MEDIA_PICTURE_CONTAINER.split(" ")).toContain("has-[.media-bar]:[--bar-room:2.5rem]"); for (const layer of [MEDIA_HOVER_LAYER, MEDIA_RESUME_LAYER]) expect(layer).not.toMatch(/pic:pb-\d/); }); }); const indexCss = readFileSync(new URL("../../index.css", import.meta.url), "utf8"); // Back from a channel page hands the card its focus again, after a mouse trip: with `focus-within` // its hover row stayed up under no pointer until a reload (owner). The chrome answers the pointer and // KEYBOARD focus only (`:focus-visible` on the card or inside it); jsdom cannot evaluate either, so // the classes are the contract, and the global `.reveal` rule says the same. describe("a card's hover chrome", () => { it.each([ ["the hover row", HOVER_REVEAL], ["what yields to it", HOVER_YIELD], ["the Play layer", MEDIA_HOVER_LAYER], ["the Continue + Restart layer", MEDIA_RESUME_LAYER], ])("shows on keyboard focus, never on a focus handed back: %s", (_, cls) => { const parts = cls.split(" "); expect(parts.some((c) => c.startsWith("group-focus-within:"))).toBe(false); expect(parts.some((c) => c.startsWith("group-focus-visible:"))).toBe(true); expect(parts.some((c) => c.startsWith("group-has-focus-visible:"))).toBe(true); }); it("is the same in the global .reveal rule", () => { expect(indexCss).not.toMatch(/\.group:focus-within \.reveal/); expect(indexCss).toMatch(/\.group:has\(:focus-visible\) \.reveal/); }); });