Fix sleep bug, fix menu return
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m1s
Traceability Validation / Check Requirement Traces (push) Successful in 23s
Build & Release / Run Tests (push) Successful in 4m7s
🏗️ Build and Test JellyTau / Build Android APK (push) Successful in 19m5s
Build & Release / Build Linux (push) Successful in 16m20s
Build & Release / Build Android (push) Successful in 19m12s
Build & Release / Create Release (push) Successful in 8s

This commit is contained in:
2026-07-01 23:49:51 +02:00
parent 342f95cac1
commit 75014ee00f
22 changed files with 880 additions and 149 deletions
+78
View File
@@ -0,0 +1,78 @@
import { describe, it, expect } from "vitest";
import type { MediaItem } from "$lib/api/types";
import { buildHeroMix, shuffle } from "./heroMix";
/** Minimal MediaItem for hero tests; `art: false` strips the image tags. */
function item(id: string, art = true): MediaItem {
return {
id,
name: `Item ${id}`,
type: "Movie",
primaryImageTag: art ? `tag-${id}` : undefined,
} as MediaItem;
}
const hasArt = (i: MediaItem) => !!i.primaryImageTag;
describe("shuffle", () => {
it("returns a permutation without mutating the input", () => {
const input = [1, 2, 3, 4, 5];
const copy = [...input];
const result = shuffle(input);
expect(input).toEqual(copy);
expect([...result].sort()).toEqual([...input].sort());
});
});
describe("buildHeroMix", () => {
it("returns empty for empty pools", () => {
expect(buildHeroMix([[], []], hasArt)).toEqual([]);
});
it("caps the rotation at count", () => {
const pool = Array.from({ length: 20 }, (_, i) => item(`a${i}`));
expect(buildHeroMix([pool], hasArt, 6)).toHaveLength(6);
});
it("filters items without artwork", () => {
const result = buildHeroMix([[item("a", false), item("b")]], hasArt);
expect(result.map(i => i.id)).toEqual(["b"]);
});
it("de-duplicates across pools", () => {
const a = item("a");
const result = buildHeroMix([[a], [a, item("b")]], hasArt);
const ids = result.map(i => i.id);
expect(ids).toHaveLength(new Set(ids).size);
expect(ids).toContain("a");
expect(ids).toContain("b");
});
it("leads with an item from the first non-empty pool", () => {
const personal = [item("p1"), item("p2"), item("p3")];
const rest = [item("r1"), item("r2"), item("r3")];
for (let run = 0; run < 20; run++) {
const result = buildHeroMix([personal, rest], hasArt);
expect(["p1", "p2", "p3"]).toContain(result[0].id);
}
});
it("takes at most perPool items per pool before backfilling", () => {
const a = [item("a1"), item("a2"), item("a3"), item("a4")];
const b = [item("b1"), item("b2"), item("b3"), item("b4")];
// count 4 with perPool 2: exactly 2 from each pool, no backfill needed.
for (let run = 0; run < 20; run++) {
const result = buildHeroMix([a, b], hasArt, 4, 2);
const fromA = result.filter(i => i.id.startsWith("a")).length;
const fromB = result.filter(i => i.id.startsWith("b")).length;
expect(fromA).toBe(2);
expect(fromB).toBe(2);
}
});
it("backfills from leftovers when samples fall short of count", () => {
const a = [item("a1"), item("a2"), item("a3"), item("a4"), item("a5"), item("a6")];
const result = buildHeroMix([a], hasArt, 6, 2);
expect(result).toHaveLength(6);
});
});