diff --git a/backend/tests/test_surprise_affinity.py b/backend/tests/test_surprise_affinity.py index 3276c200..ba828e1d 100644 --- a/backend/tests/test_surprise_affinity.py +++ b/backend/tests/test_surprise_affinity.py @@ -42,6 +42,20 @@ def test_a_short_side_is_topped_up_from_the_other(): assert draw.blend(["a", "b", "c"], [], 0, 3) == ["a", "b", "c"] +def test_a_watch_weighs_by_its_age_an_undated_one_two_half_lives_and_a_future_one_now(): + # Both affinities weigh every watch through this one rule; an old import has no date, and a + # clock ahead of the server's must not weigh more than a watch made now. + assert draw.recency(NOW, NOW, 30) == 1.0 + assert draw.recency(NOW - timedelta(days=30), NOW, 30) == 0.5 + assert draw.recency(None, NOW, 30) == 0.25 + assert draw.recency(NOW + timedelta(days=5), NOW, 30) == 1.0 + + +def test_scores_are_scaled_to_the_strongest_and_nothing_scored_is_empty(): + assert draw.scaled({"a": 4.0, "b": 1.0}) == {"a": 1.0, "b": 0.25} + assert draw.scaled({}) == {} + + # ── YouTube: the channels the user watches ─────────────────────────────────────────────────────── diff --git a/frontend/src/lib/theme.contract.test.ts b/frontend/src/lib/theme.contract.test.ts index 7849aa3f..676453ca 100644 --- a/frontend/src/lib/theme.contract.test.ts +++ b/frontend/src/lib/theme.contract.test.ts @@ -59,17 +59,6 @@ describe("the media colours", () => { // accent for what must be seen: a light theme's are dark (#b91c1c 2.8:1, Slate's accent 2.8:1 on // the chrome). Its error edge and text take the media red, and every focus ring in it — the base // ring and a field's focus edge both read --focus — the media amber. [WCAG 1.4.11, 1.4.3] - // Text over the picture sits on a black panel the frame shows through: its worst ground is a white - // frame. The error line on it is the media red (a light theme's danger was 1.6:1 there). [WCAG 1.4.3] - it("an error over the picture holds 4.5:1 on its panel over a white frame", () => { - const player = readFileSync(new URL("../modules/plex/PlexPlayer.tsx", import.meta.url), "utf8"); - const over = /const OVER_VIDEO =\s*"([^"]+)"/.exec(player)?.[1] ?? ""; - expect(over.split(" ")).toContain("media-scope"); // --danger is the media red under it - const alpha = Number(/\bbg-black\/(\d+)\b/.exec(over)?.[1]) / 100; - const ground = mixHex("#000000", "#ffffff", alpha); - expect(hexContrast(media()["media-danger"]!, ground)).toBeGreaterThanOrEqual(4.5); - expect(hexContrast("#ffffff", ground)).toBeGreaterThanOrEqual(4.5); - }); it("a form on the player chrome says an error and a focus in colours that hold there", () => { expect(hexContrast(media()["media-danger"]!, media()["media-chrome"]!)).toBeGreaterThanOrEqual( 4.5, @@ -91,6 +80,17 @@ describe("the media colours", () => { expect(field).toMatch(/\bfocus-within:ring-focus\b/); expect(field).not.toMatch(/\bfocus-within:(border|ring)-accent\b/); }); + // Text over the picture sits on a black panel the frame shows through: its worst ground is a white + // frame. The error line on it is the media red (a light theme's danger was 1.6:1 there). [WCAG 1.4.3] + it("an error over the picture holds 4.5:1 on its panel over a white frame", () => { + const player = readFileSync(new URL("../modules/plex/PlexPlayer.tsx", import.meta.url), "utf8"); + const over = /const OVER_VIDEO =\s*"([^"]+)"/.exec(player)?.[1] ?? ""; + expect(over.split(" ")).toContain("media-scope"); // --danger is the media red under it + const alpha = Number(/\bbg-black\/(\d+)\b/.exec(over)?.[1]) / 100; + const ground = mixHex("#000000", "#ffffff", alpha); + expect(hexContrast(media()["media-danger"]!, ground)).toBeGreaterThanOrEqual(4.5); + expect(hexContrast("#ffffff", ground)).toBeGreaterThanOrEqual(4.5); + }); it("every surface painted the player chrome names its marks: the focus ring and what is chosen", () => { const block = (name: string) => {