Files
ditto.site/compiler/test/videoSourceSelect.test.ts
Samraaj BathandClaude Fable 5 144e7ab16b Clear the show-poster flag after per-viewport video source reload
v.load() resets the media element's show-poster flag; pause cancels
autoplay and the at-zero seek skip left nothing to clear it, so
screenshots after a source re-selection rendered the poster instead of
the new source's frame 0. Reloaded videos now force an epsilon seek
(awaiting seeked, which also serves as the readiness signal on slow
CDNs), and re-selection emits a video_source_reselected log event.

386 tests pass (4 new), typecheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 03:13:14 -07:00

94 lines
4.3 KiB
TypeScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { selectVideoSourceIndex, planVideoSeek, type VideoSourceCandidate } from "../src/capture/capture.js";
// Predicate builders for the injected matchMedia / canPlayType.
const matchesAny = (matching: Set<string>) => (m: string) => matching.has(m);
const canPlayAll = () => true;
const canPlayNone = () => false;
describe("selectVideoSourceIndex (video <source> resource-selection)", () => {
it("picks the first source whose media matches; missing media matches unconditionally", () => {
// Aspect-gated hero: landscape sources first, portrait after, plain fall-through last.
const sources: VideoSourceCandidate[] = [
{ media: "(min-aspect-ratio: 21/9)", type: "video/webm" },
{ media: "(min-aspect-ratio: 16/9)", type: "video/webm" },
{ media: "(max-aspect-ratio: 4/5)", type: "video/webm" },
{ media: null, type: "video/webm" }, // plain fall-through
];
// Portrait viewport: only the max-aspect-ratio query matches → index 2.
assert.equal(
selectVideoSourceIndex(sources, matchesAny(new Set(["(max-aspect-ratio: 4/5)"])), canPlayAll),
2,
);
// Landscape/wide viewport: the min-aspect 16/9 query matches → index 1.
assert.equal(
selectVideoSourceIndex(sources, matchesAny(new Set(["(min-aspect-ratio: 16/9)"])), canPlayAll),
1,
);
// Nothing matches → falls through to the plain no-media source (index 3).
assert.equal(selectVideoSourceIndex(sources, matchesAny(new Set()), canPlayAll), 3);
});
it("skips a source whose type the UA cannot play", () => {
const sources: VideoSourceCandidate[] = [
{ media: null, type: "video/webm" }, // unplayable here
{ media: null, type: "video/mp4" },
];
const canPlayMp4 = (t: string) => t === "video/mp4";
assert.equal(selectVideoSourceIndex(sources, matchesAny(new Set()), canPlayMp4), 1);
});
it("treats a missing/empty type as never disqualifying", () => {
const sources: VideoSourceCandidate[] = [
{ media: null, type: "" },
{ media: null },
];
// canPlay is never consulted when type is absent, even if it would reject everything.
assert.equal(selectVideoSourceIndex(sources, matchesAny(new Set()), canPlayNone), 0);
});
it("returns -1 when no source is eligible", () => {
const sources: VideoSourceCandidate[] = [
{ media: "(max-aspect-ratio: 4/5)", type: "video/webm" },
];
assert.equal(selectVideoSourceIndex(sources, matchesAny(new Set()), canPlayNone), -1);
assert.equal(selectVideoSourceIndex([], matchesAny(new Set()), canPlayAll), -1);
});
it("is deterministic and first-match-wins in document order", () => {
const sources: VideoSourceCandidate[] = [
{ media: "(min-width: 100px)", type: "video/mp4" },
{ media: "(min-width: 100px)", type: "video/mp4" }, // also eligible, but later
];
assert.equal(selectVideoSourceIndex(sources, matchesAny(new Set(["(min-width: 100px)"])), canPlayAll), 0);
});
});
describe("planVideoSeek (post-reselection seek decision)", () => {
it("forces an epsilon seek on a reloaded video even at t=0 (clears the show-poster flag)", () => {
// The core regression: a just-reloaded video is at t=0 with the poster flag set. Seeking to 0
// fires no `seeked` and leaves the poster showing; only a genuine seek to a nonzero epsilon
// clears the flag and paints the new source's frame 0.
const plan = planVideoSeek(true, 0);
assert.ok(plan, "reloaded video must seek");
assert.ok(plan!.target > 0, "reloaded seek target must be nonzero so `seeked` actually fires");
assert.ok(plan!.target < 1e-3, "epsilon must be well inside frame 0");
});
it("still forces the epsilon seek on a reloaded video that reports a small nonzero time", () => {
const plan = planVideoSeek(true, 5e-4);
assert.deepEqual(plan, { target: 1e-4 });
});
it("fast-skips a non-reloaded video already at frame 0", () => {
assert.equal(planVideoSeek(false, 0), null);
assert.equal(planVideoSeek(false, 5e-4), null); // within the 1e-3 skip band
});
it("seeks a non-reloaded playing video back to 0", () => {
assert.deepEqual(planVideoSeek(false, 3.2), { target: 0 });
assert.deepEqual(planVideoSeek(false, 1e-3), { target: 0 }); // exactly at the band edge → seek
});
});