import { describe, it, expect } from "vitest"; import { shouldApplyTimeUpdate } from "./timeTracking"; /** * TRACES: UT-245 | DR-265 */ describe("shouldApplyTimeUpdate", () => { const base = { isPlaying: false, isSeeking: false, isDraggingSeekBar: false, readyState: 4 }; it("applies the update while the video is PLAYING", () => { // THE REPORTED BUG. `timeupdate` was the only position source that still // fires once requestAnimationFrame stops -- which is exactly what happens // when the activity is paused behind a picture-in-picture window. Gating it // on `!isPlaying` disabled it precisely when it was the only thing left, // so the component's `currentTime` froze at the moment PiP was entered // while the element played on. The background-audio handoff then resumed // the audio-only stream at that frozen position. expect(shouldApplyTimeUpdate({ ...base, isPlaying: true })).toBe(true); }); it("still applies the update while paused", () => { // The case it always handled: RAF is stopped, timeupdate carries the seek. expect(shouldApplyTimeUpdate(base)).toBe(true); }); it("yields to an in-flight seek", () => { // A seek owns the position until it settles; a stale element read landing // mid-seek is what makes a scrubbed video snap back. expect(shouldApplyTimeUpdate({ ...base, isSeeking: true })).toBe(false); expect(shouldApplyTimeUpdate({ ...base, isPlaying: true, isSeeking: true })).toBe(false); }); it("yields while the user is dragging the seek bar", () => { expect(shouldApplyTimeUpdate({ ...base, isDraggingSeekBar: true })).toBe(false); expect(shouldApplyTimeUpdate({ ...base, isPlaying: true, isDraggingSeekBar: true })).toBe( false, ); }); it("ignores an element with no usable data yet", () => { // readyState < HAVE_CURRENT_DATA reads 0, which would rewind the position. expect(shouldApplyTimeUpdate({ ...base, readyState: 1 })).toBe(false); expect(shouldApplyTimeUpdate({ ...base, isPlaying: true, readyState: 0 })).toBe(false); }); });