Bug 1535454 - Add some preliminary Picture-in-Picture automated tests. r=Felipe

Differential Revision: https://phabricator.services.mozilla.com/D23730

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mike Conley 2019-03-19 15:07:04 +00:00
parent 1dc9c72510
commit fe0b02a356
9 changed files with 211 additions and 0 deletions

View File

@ -9,3 +9,7 @@ JAR_MANIFESTS += ['jar.mn']
EXTRA_JS_MODULES += [
'PictureInPicture.jsm',
]
BROWSER_CHROME_MANIFESTS += [
'tests/browser.ini',
]

View File

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/browser-test"
]
};

View File

@ -0,0 +1,12 @@
[DEFAULT]
support-files =
head.js
test-page.html
test-video.mp4
prefs =
media.videocontrols.picture-in-picture.enabled=true
[browser_cannotTriggerFromContent.js]
[browser_closeTab.js]
[browser_showMessage.js]

View File

@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that the MozTogglePictureInPicture event is ignored if
* fired by unprivileged web content.
*/
add_task(async () => {
await BrowserTestUtils.withNewTab({
url: TEST_PAGE,
gBrowser,
}, async browser => {
// For now, the easiest way to ensure that this didn't happen is to fail
// if we receive the PictureInPicture:Request message.
const MESSAGE = "PictureInPicture:Request";
let sawMessage = false;
let listener = msg => {
sawMessage = true;
};
browser.messageManager.addMessageListener(MESSAGE, listener);
await ContentTask.spawn(browser, null, async () => {
content.wrappedJSObject.fireEvents();
});
browser.messageManager.removeMessageListener(MESSAGE, listener);
ok(!sawMessage, "Got PictureInPicture:Request message unexpectedly.");
});
});

View File

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that if the tab that's hosting a <video> that's opened in a
* Picture-in-Picture window is closed, that the Picture-in-Picture
* window is also closed.
*/
add_task(async () => {
for (let videoID of ["with-controls", "no-controls"]) {
info(`Testing ${videoID} case.`);
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
let browser = tab.linkedBrowser;
let pipWin = await triggerPictureInPicture(browser, videoID);
ok(pipWin, "Got Picture-in-Picture window.");
let pipClosed = BrowserTestUtils.domWindowClosed(pipWin);
BrowserTestUtils.removeTab(tab);
await pipClosed;
}
});

View File

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that triggering Picture-in-Picture causes the Picture-in-Picture
* window to be opened, and a message to be displayed in the original video
* player area. Also ensures that once the Picture-in-Picture window is closed,
* the video goes back to the original state.
*/
add_task(async () => {
for (let videoID of ["with-controls", "no-controls"]) {
info(`Testing ${videoID} case.`);
await BrowserTestUtils.withNewTab({
url: TEST_PAGE,
gBrowser,
}, async browser => {
let pipWin = await triggerPictureInPicture(browser, videoID);
ok(pipWin, "Got Picture-in-Picture window.");
try {
await assertShowingMessage(browser, videoID, true);
} finally {
let uaWidgetUpdate = BrowserTestUtils.waitForContentEvent(browser, "UAWidgetSetupOrChange");
pipWin.close();
await uaWidgetUpdate;
}
// no-controls case is disabled until we ensure that there's a UAWidget for
// the no-controls case on Desktop (which should be fixed as part of
// bug 1535354).
if (videoID !== "no-controls") {
await assertShowingMessage(browser, videoID, false);
}
});
}
});

View File

@ -0,0 +1,66 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
const TEST_PAGE = TEST_ROOT + "test-page.html";
const WINDOW_TYPE = "Toolkit:PictureInPicture";
/**
* Given a browser and the ID for a <video> element, triggers
* Picture-in-Picture for that <video>, and resolves with the
* Picture-in-Picture window once it is ready to be used.
*
* @param {Element} browser The <xul:browser> hosting the <video>
*
* @param {String} videoID The ID of the video to trigger
* Picture-in-Picture on.
*
* @returns Promise
* @resolves With the Picture-in-Picture window when ready.
*/
async function triggerPictureInPicture(browser, videoID) {
let domWindowOpened = BrowserTestUtils.domWindowOpened(null);
let videoReady = ContentTask.spawn(browser, videoID, async videoID => {
let video = content.document.getElementById(videoID);
let event = new content.CustomEvent("MozTogglePictureInPicture", { bubbles: true });
video.dispatchEvent(event);
await ContentTaskUtils.waitForCondition(() => {
return video.isCloningElementVisually;
}, "Video is being cloned visually.");
});
let win = await domWindowOpened;
await BrowserTestUtils.waitForEvent(win, "load");
await videoReady;
return win;
}
/**
* Given a browser and the ID for a <video> element, checks that the
* video is showing the "This video is playing in Picture-in-Picture mode."
* status message overlay.
*
* @param {Element} browser The <xul:browser> hosting the <video>
*
* @param {String} videoID The ID of the video to trigger
* Picture-in-Picture on.
*
* @param {bool} expected True if we expect the message to be showing.
*
* @returns Promise
* @resolves When the checks have completed.
*/
async function assertShowingMessage(browser, videoID, expected) {
let showing = await ContentTask.spawn(browser, videoID, async videoID => {
let video = content.document.getElementById(videoID);
let shadowRoot = video.openOrClosedShadowRoot;
let pipOverlay = shadowRoot.querySelector(".pictureInPictureOverlay");
ok(pipOverlay, "Should be able to find Picture-in-Picture overlay.");
let rect = pipOverlay.getBoundingClientRect();
return rect.height > 0 && rect.width > 0;
});
Assert.equal(showing, expected,
"Video should be showing the expected state.");
}

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Picture-in-Picture tests</title>
</head>
<style>
video {
display: block;
border: 1px solid black;
}
</style>
<body>
<h1>Video with controls</h1>
<video id="with-controls" src="test-video.mp4" controls loop="true"></video>
<h1>Video without controls</h1>
<video id="no-controls" src="test-video.mp4" loop="true"></video>
<script>
function fireEvents() {
for (let videoID of ["with-controls", "no-controls"]) {
let video = document.getElementById(videoID);
let event = new CustomEvent("MozTogglePictureInPicture", { bubbles: true });
video.dispatchEvent(event);
}
}
</script>
</body>
</html>