diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /dom/media/mediacontrol/tests | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/mediacontrol/tests')
-rw-r--r-- | dom/media/mediacontrol/tests/browser/browser_media_control_position_state.js | 243 | ||||
-rw-r--r-- | dom/media/mediacontrol/tests/browser/head.js | 81 |
2 files changed, 284 insertions, 40 deletions
diff --git a/dom/media/mediacontrol/tests/browser/browser_media_control_position_state.js b/dom/media/mediacontrol/tests/browser/browser_media_control_position_state.js index 6074e2ee16..75f65eb34b 100644 --- a/dom/media/mediacontrol/tests/browser/browser_media_control_position_state.js +++ b/dom/media/mediacontrol/tests/browser/browser_media_control_position_state.js @@ -4,6 +4,7 @@ const IFRAME_URL = "https://example.com/browser/dom/media/mediacontrol/tests/browser/file_iframe_media.html"; const testVideoId = "video"; +const videoDuration = 5.589333; add_task(async function setupTestingPref() { await SpecialPowers.pushPrefEnv({ @@ -18,9 +19,15 @@ add_task(async function setupTestingPref() { add_task(async function testSetPositionState() { info(`open media page`); const tab = await createLoadedTabWrapper(PAGE_URL); + logPositionStateChangeEvents(tab); + + info(`apply initial position state`); + await applyPositionState(tab, { duration: 10 }); info(`start media`); + const initialPositionState = isNextPositionState(tab, { duration: 10 }); await playMedia(tab, testVideoId); + await initialPositionState; info(`set duration only`); await setPositionState(tab, { @@ -47,9 +54,15 @@ add_task(async function testSetPositionState() { add_task(async function testSetPositionStateFromInactiveMediaSession() { info(`open media page`); const tab = await createLoadedTabWrapper(PAGE_URL); + logPositionStateChangeEvents(tab); + + info(`apply initial position state`); + await applyPositionState(tab, { duration: 10 }); info(`start media`); + const initialPositionState = isNextPositionState(tab, { duration: 10 }); await playMedia(tab, testVideoId); + await initialPositionState; info( `add an event listener to measure how many times the position state changes` @@ -82,48 +95,193 @@ add_task(async function testSetPositionStateFromInactiveMediaSession() { }); /** - * The following are helper functions. + * + * @param {boolean} withMetadata + * Specifies if the tab should set metadata for the playing video */ -async function setPositionState(tab, positionState) { +async function testGuessedPositionState(withMetadata) { + info(`open media page`); + const tab = await createLoadedTabWrapper(PAGE_URL); + logPositionStateChangeEvents(tab); + + if (withMetadata) { + info(`set media metadata`); + await setMediaMetadata(tab, { title: "A Video" }); + } + + info(`start media`); + await emitsPositionState(() => playMedia(tab, testVideoId), tab, { + duration: videoDuration, + position: 0, + playbackRate: 1.0, + }); + + info(`set playback rate to 2x`); + await emitsPositionState(() => setPlaybackRate(tab, testVideoId, 2.0), tab, { + duration: videoDuration, + position: null, // ignored, + playbackRate: 2.0, + }); + + info(`seek to 1s`); + await emitsPositionState(() => setCurrentTime(tab, testVideoId, 1.0), tab, { + duration: videoDuration, + position: 1.0, + playbackRate: 2.0, + }); + + let positionChangedNum = 0; const controller = tab.linkedBrowser.browsingContext.mediaController; - const positionStateChanged = new Promise(r => { - controller.addEventListener( - "positionstatechange", - event => { - const { duration, playbackRate, position } = positionState; - // duration is mandatory. - is( - event.duration, - duration, - `expected duration ${event.duration} is equal to ${duration}` - ); - - // Playback rate is optional, if it's not present, default should be 1.0 - if (playbackRate) { - is( - event.playbackRate, - playbackRate, - `expected playbackRate ${event.playbackRate} is equal to ${playbackRate}` - ); - } else { - is(event.playbackRate, 1.0, `expected default playbackRate is 1.0`); - } - - // Position state is optional, if it's not present, default should be 0.0 - if (position) { - is( - event.position, - position, - `expected position ${event.position} is equal to ${position}` - ); - } else { - is(event.position, 0.0, `expected default position is 0.0`); - } - r(); - }, - { once: true } - ); + controller.onpositionstatechange = () => positionChangedNum++; + + info(`pause media`); + // shouldn't generate an event + await pauseMedia(tab, testVideoId); + + info(`seek to 2s`); + await emitsPositionState(() => setCurrentTime(tab, testVideoId, 2.0), tab, { + duration: videoDuration, + position: 2.0, + playbackRate: 2.0, + }); + + info(`start media`); + await emitsPositionState(() => playMedia(tab, testVideoId), tab, { + duration: videoDuration, + position: 2.0, + playbackRate: 2.0, }); + + is( + positionChangedNum, + 2, + `We should only receive two of position changes, because pausing is effectless` + ); + + info(`remove tab`); + await tab.close(); +} + +add_task(async function testGuessedPositionStateWithMetadata() { + testGuessedPositionState(true); +}); + +add_task(async function testGuessedPositionStateWithoutMetadata() { + testGuessedPositionState(false); +}); + +/** + * @typedef {{ + * duration: number, + * playbackRate?: number | null, + * position?: number | null, + * }} ExpectedPositionState + */ + +/** + * Checks if the next received position state matches the expected one. + * + * @param {tab} tab + * The tab that contains the media + * @param {ExpectedPositionState} positionState + * The expected position state. `duration` is mandatory. `playbackRate` + * and `position` are optional. If they're `null`, they're ignored, + * otherwise if they're not present or undefined, they're expected to + * be the default value. + * @returns {Promise} + * Resolves when the event has been received + */ +async function isNextPositionState(tab, positionState) { + const got = await nextPositionState(tab); + isPositionState(got, positionState); +} + +/** + * Waits for the next position state and returns it + * + * @param {tab} tab The tab to receive position state from + * @returns {Promise<MediaPositionState>} The emitted position state + */ +function nextPositionState(tab) { + const controller = tab.linkedBrowser.browsingContext.mediaController; + return new Promise(r => { + controller.addEventListener("positionstatechange", r, { once: true }); + }); +} + +/** + * @param {MediaPositionState} got + * The received position state + * @param {ExpectedPositionState} expected + * The expected position state. `duration` is mandatory. `playbackRate` + * and `position` are optional. If they're `null`, they're ignored, + * otherwise if they're not present or undefined, they're expected to + * be the default value. + */ +function isPositionState(got, expected) { + const { duration, playbackRate, position } = expected; + // duration is mandatory. + isFuzzyEq(got.duration, duration, "duration"); + + // Playback rate is optional, if it's not present, default should be 1.0 + if (typeof playbackRate === "number") { + isFuzzyEq(got.playbackRate, playbackRate, "playbackRate"); + } else if (playbackRate !== null) { + is(got.playbackRate, 1.0, `expected default playbackRate is 1.0`); + } + + // Position is optional, if it's not present, default should be 0.0 + if (typeof position === "number") { + isFuzzyEq(got.position, position, "position"); + } else if (position !== null) { + is(got.position, 0.0, `expected default position is 0.0`); + } +} + +/** + * Checks if two numbers are equal within one significant digit + * + * @param {number} got + * The value received while testing + * @param {number} expected + * The expected value + * @param {string} role + * The role of the check (used for formatting) + */ +function isFuzzyEq(got, expected, role) { + expected = expected.toFixed(1); + got = got.toFixed(1); + is(got, expected, `expected ${role} ${got} to equal ${expected}`); +} + +/** + * Test if `cb` emits a position state event. + * + * @param {() => (void | Promise<void>)} cb + * A callback that is expected to generate a position state event + * @param {tab} tab + * The tab that contains the media + * @param {ExpectedPositionState} positionState + * The expected position state to be generated. + */ +async function emitsPositionState(cb, tab, positionState) { + const positionStateChanged = isNextPositionState(tab, positionState); + await cb(); + await positionStateChanged; +} + +/** + * The following are helper functions. + */ +async function setPositionState(tab, positionState) { + await emitsPositionState( + () => applyPositionState(tab, positionState), + tab, + positionState + ); +} + +async function applyPositionState(tab, positionState) { await SpecialPowers.spawn( tab.linkedBrowser, [positionState], @@ -131,7 +289,12 @@ async function setPositionState(tab, positionState) { content.navigator.mediaSession.setPositionState(positionState); } ); - await positionStateChanged; +} + +async function setMediaMetadata(tab, metadata) { + await SpecialPowers.spawn(tab.linkedBrowser, [metadata], data => { + content.navigator.mediaSession.metadata = new content.MediaMetadata(data); + }); } async function setPositionStateOnInactiveMediaSession(tab) { diff --git a/dom/media/mediacontrol/tests/browser/head.js b/dom/media/mediacontrol/tests/browser/head.js index cac96c0bff..7c6a1e37e4 100644 --- a/dom/media/mediacontrol/tests/browser/head.js +++ b/dom/media/mediacontrol/tests/browser/head.js @@ -195,6 +195,58 @@ function checkOrWaitUntilMediaStartedPlaying(tab, elementId) { } /** + * Set the playback rate on a media element. + * + * @param {tab} tab + * The tab that contains the media which we would check + * @param {string} elementId + * The element Id of the media which we would check + * @param {number} rate + * The playback rate to set + * @return {Promise} + * Resolve when the playback rate has been set + */ +function setPlaybackRate(tab, elementId, rate) { + return SpecialPowers.spawn( + tab.linkedBrowser, + [elementId, rate], + (Id, rate) => { + const video = content.document.getElementById(Id); + if (!video) { + ok(false, `can't get the media element!`); + } + video.playbackRate = rate; + } + ); +} + +/** + * Set the time on a media element. + * + * @param {tab} tab + * The tab that contains the media which we would check + * @param {string} elementId + * The element Id of the media which we would check + * @param {number} currentTime + * The time to set + * @return {Promise} + * Resolve when the time has been set + */ +function setCurrentTime(tab, elementId, currentTime) { + return SpecialPowers.spawn( + tab.linkedBrowser, + [elementId, currentTime], + (Id, currentTime) => { + const video = content.document.getElementById(Id); + if (!video) { + ok(false, `can't get the media element!`); + } + video.currentTime = currentTime; + } + ); +} + +/** * Returns a promise that resolves when the specific media stops playing. * * @param {tab} tab @@ -390,6 +442,18 @@ function waitUntilMediaControllerAmountChanged() { } /** + * Wait until the position state that would be displayed on the virtual control + * interface changes. we would observe that by listening for + * `media-position-state-changed` notification. + * + * @return {Promise} + * Resolve when observing `media-position-state-changed` + */ +function waitUntilPositionStateChanged() { + return BrowserUtils.promiseObserved("media-position-state-changed"); +} + +/** * check if the media controll from given tab is active. If not, return a * promise and resolve it when controller become active. */ @@ -400,3 +464,20 @@ async function checkOrWaitUntilControllerBecomeActive(tab) { } await new Promise(r => (controller.onactivated = r)); } + +/** + * Logs all `positionstatechange` events in a tab. + */ +function logPositionStateChangeEvents(tab) { + tab.linkedBrowser.browsingContext.mediaController.addEventListener( + "positionstatechange", + event => + info( + `got position state: ${JSON.stringify({ + duration: event.duration, + playbackRate: event.playbackRate, + position: event.position, + })}` + ) + ); +} |