diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /devtools/client/animationinspector/test/head.js | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'devtools/client/animationinspector/test/head.js')
-rw-r--r-- | devtools/client/animationinspector/test/head.js | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/devtools/client/animationinspector/test/head.js b/devtools/client/animationinspector/test/head.js new file mode 100644 index 000000000..554a36430 --- /dev/null +++ b/devtools/client/animationinspector/test/head.js @@ -0,0 +1,426 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */ + +"use strict"; + +/* import-globals-from ../../inspector/test/head.js */ +// Import the inspector's head.js first (which itself imports shared-head.js). +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js", + this); + +const FRAME_SCRIPT_URL = CHROME_URL_ROOT + "doc_frame_script.js"; +const COMMON_FRAME_SCRIPT_URL = "chrome://devtools/content/shared/frame-script-utils.js"; +const TAB_NAME = "animationinspector"; +const ANIMATION_L10N = + new LocalizationHelper("devtools/client/locales/animationinspector.properties"); + +// Auto clean-up when a test ends +registerCleanupFunction(function* () { + yield closeAnimationInspector(); + + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +}); + +// Clean-up all prefs that might have been changed during a test run +// (safer here because if the test fails, then the pref is never reverted) +registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.debugger.log"); +}); + +// WebAnimations API is not enabled by default in all release channels yet, see +// Bug 1264101. +function enableWebAnimationsAPI() { + return new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.animations-api.core.enabled", true] + ]}, resolve); + }); +} + +/** + * Add a new test tab in the browser and load the given url. + * @param {String} url The url to be loaded in the new tab + * @return a promise that resolves to the tab object when the url is loaded + */ +var _addTab = addTab; +addTab = function (url) { + return enableWebAnimationsAPI().then(() => _addTab(url)).then(tab => { + let browser = tab.linkedBrowser; + info("Loading the helper frame script " + FRAME_SCRIPT_URL); + browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false); + info("Loading the helper frame script " + COMMON_FRAME_SCRIPT_URL); + browser.messageManager.loadFrameScript(COMMON_FRAME_SCRIPT_URL, false); + return tab; + }); +}; + +/** + * Reload the current tab location. + * @param {InspectorPanel} inspector The instance of InspectorPanel currently + * loaded in the toolbox + */ +function* reloadTab(inspector) { + let onNewRoot = inspector.once("new-root"); + yield executeInContent("devtools:test:reload", {}, {}, false); + yield onNewRoot; + yield inspector.once("inspector-updated"); +} + +/* + * Set the inspector's current selection to a node or to the first match of the + * given css selector and wait for the animations to be displayed + * @param {String|NodeFront} + * data The node to select + * @param {InspectorPanel} inspector + * The instance of InspectorPanel currently + * loaded in the toolbox + * @param {String} reason + * Defaults to "test" which instructs the inspector not + * to highlight the node upon selection + * @return {Promise} Resolves when the inspector is updated with the new node + and animations of its subtree are properly displayed. + */ +var selectNodeAndWaitForAnimations = Task.async( + function* (data, inspector, reason = "test") { + yield selectNode(data, inspector, reason); + + // We want to make sure the rest of the test waits for the animations to + // be properly displayed (wait for all target DOM nodes to be previewed). + let {AnimationsPanel} = inspector.sidebar.getWindowForTab(TAB_NAME); + yield waitForAllAnimationTargets(AnimationsPanel); + } +); + +/** + * Check if there are the expected number of animations being displayed in the + * panel right now. + * @param {AnimationsPanel} panel + * @param {Number} nbAnimations The expected number of animations. + * @param {String} msg An optional string to be used as the assertion message. + */ +function assertAnimationsDisplayed(panel, nbAnimations, msg = "") { + msg = msg || `There are ${nbAnimations} animations in the panel`; + is(panel.animationsTimelineComponent + .animationsEl + .querySelectorAll(".animation").length, nbAnimations, msg); +} + +/** + * Takes an Inspector panel that was just created, and waits + * for a "inspector-updated" event as well as the animation inspector + * sidebar to be ready. Returns a promise once these are completed. + * + * @param {InspectorPanel} inspector + * @return {Promise} + */ +var waitForAnimationInspectorReady = Task.async(function* (inspector) { + let win = inspector.sidebar.getWindowForTab(TAB_NAME); + let updated = inspector.once("inspector-updated"); + + // In e10s, if we wait for underlying toolbox actors to + // load (by setting DevToolsUtils.testing to true), we miss the + // "animationinspector-ready" event on the sidebar, so check to see if the + // iframe is already loaded. + let tabReady = win.document.readyState === "complete" ? + promise.resolve() : + inspector.sidebar.once("animationinspector-ready"); + + return promise.all([updated, tabReady]); +}); + +/** + * Open the toolbox, with the inspector tool visible and the animationinspector + * sidebar selected. + * @return a promise that resolves when the inspector is ready. + */ +var openAnimationInspector = Task.async(function* () { + let {inspector, toolbox} = yield openInspectorSidebarTab(TAB_NAME); + + info("Waiting for the inspector and sidebar to be ready"); + yield waitForAnimationInspectorReady(inspector); + + let win = inspector.sidebar.getWindowForTab(TAB_NAME); + let {AnimationsController, AnimationsPanel} = win; + + info("Waiting for the animation controller and panel to be ready"); + if (AnimationsPanel.initialized) { + yield AnimationsPanel.initialized; + } else { + yield AnimationsPanel.once(AnimationsPanel.PANEL_INITIALIZED); + } + + // Make sure we wait for all animations to be loaded (especially their target + // nodes to be lazily displayed). This is safe to do even if there are no + // animations displayed. + yield waitForAllAnimationTargets(AnimationsPanel); + + return { + toolbox: toolbox, + inspector: inspector, + controller: AnimationsController, + panel: AnimationsPanel, + window: win + }; +}); + +/** + * Close the toolbox. + * @return a promise that resolves when the toolbox has closed. + */ +var closeAnimationInspector = Task.async(function* () { + let target = TargetFactory.forTab(gBrowser.selectedTab); + yield gDevTools.closeToolbox(target); +}); + +/** + * Wait for a content -> chrome message on the message manager (the window + * messagemanager is used). + * @param {String} name The message name + * @return {Promise} A promise that resolves to the response data when the + * message has been received + */ +function waitForContentMessage(name) { + info("Expecting message " + name + " from content"); + + let mm = gBrowser.selectedBrowser.messageManager; + + return new Promise(resolve => { + mm.addMessageListener(name, function onMessage(msg) { + mm.removeMessageListener(name, onMessage); + resolve(msg.data); + }); + }); +} + +/** + * Send an async message to the frame script (chrome -> content) and wait for a + * response message with the same name (content -> chrome). + * @param {String} name The message name. Should be one of the messages defined + * in doc_frame_script.js + * @param {Object} data Optional data to send along + * @param {Object} objects Optional CPOW objects to send along + * @param {Boolean} expectResponse If set to false, don't wait for a response + * with the same name from the content script. Defaults to true. + * @return {Promise} Resolves to the response data if a response is expected, + * immediately resolves otherwise + */ +function executeInContent(name, data = {}, objects = {}, + expectResponse = true) { + info("Sending message " + name + " to content"); + let mm = gBrowser.selectedBrowser.messageManager; + + mm.sendAsyncMessage(name, data, objects); + if (expectResponse) { + return waitForContentMessage(name); + } + + return promise.resolve(); +} + +/** + * Get the current playState of an animation player on a given node. + */ +var getAnimationPlayerState = Task.async(function* (selector, + animationIndex = 0) { + let playState = yield executeInContent("Test:GetAnimationPlayerState", + {selector, animationIndex}); + return playState; +}); + +/** + * Is the given node visible in the page (rendered in the frame tree). + * @param {DOMNode} + * @return {Boolean} + */ +function isNodeVisible(node) { + return !!node.getClientRects().length; +} + +/** + * Wait for all AnimationTargetNode instances to be fully loaded + * (fetched their related actor and rendered), and return them. + * @param {AnimationsPanel} panel + * @return {Array} all AnimationTargetNode instances + */ +var waitForAllAnimationTargets = Task.async(function* (panel) { + let targets = panel.animationsTimelineComponent.targetNodes; + yield promise.all(targets.map(t => { + if (!t.previewer.nodeFront) { + return t.once("target-retrieved"); + } + return false; + })); + return targets; +}); + +/** + * Check the scrubber element in the timeline is moving. + * @param {AnimationPanel} panel + * @param {Boolean} isMoving + */ +function* assertScrubberMoving(panel, isMoving) { + let timeline = panel.animationsTimelineComponent; + + if (isMoving) { + // If we expect the scrubber to move, just wait for a couple of + // timeline-data-changed events and compare times. + let {time: time1} = yield timeline.once("timeline-data-changed"); + let {time: time2} = yield timeline.once("timeline-data-changed"); + ok(time2 > time1, "The scrubber is moving"); + } else { + // If instead we expect the scrubber to remain at its position, just wait + // for some time and make sure timeline-data-changed isn't emitted. + let hasMoved = false; + timeline.once("timeline-data-changed", () => { + hasMoved = true; + }); + yield new Promise(r => setTimeout(r, 500)); + ok(!hasMoved, "The scrubber is not moving"); + } +} + +/** + * Click the play/pause button in the timeline toolbar and wait for animations + * to update. + * @param {AnimationsPanel} panel + */ +function* clickTimelinePlayPauseButton(panel) { + let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT); + + let btn = panel.playTimelineButtonEl; + let win = btn.ownerDocument.defaultView; + EventUtils.sendMouseEvent({type: "click"}, btn, win); + + yield onUiUpdated; + yield waitForAllAnimationTargets(panel); +} + +/** + * Click the rewind button in the timeline toolbar and wait for animations to + * update. + * @param {AnimationsPanel} panel + */ +function* clickTimelineRewindButton(panel) { + let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT); + + let btn = panel.rewindTimelineButtonEl; + let win = btn.ownerDocument.defaultView; + EventUtils.sendMouseEvent({type: "click"}, btn, win); + + yield onUiUpdated; + yield waitForAllAnimationTargets(panel); +} + +/** + * Select a rate inside the playback rate selector in the timeline toolbar and + * wait for animations to update. + * @param {AnimationsPanel} panel + * @param {Number} rate The new rate value to be selected + */ +function* changeTimelinePlaybackRate(panel, rate) { + let onUiUpdated = panel.once(panel.UI_UPDATED_EVENT); + + let select = panel.rateSelectorEl.firstChild; + let win = select.ownerDocument.defaultView; + + // Get the right option. + let option = [...select.options].filter(o => o.value === rate + "")[0]; + if (!option) { + ok(false, + "Could not find an option for rate " + rate + " in the rate selector. " + + "Values are: " + [...select.options].map(o => o.value)); + return; + } + + // Simulate the right events to select the option in the drop-down. + EventUtils.synthesizeMouseAtCenter(select, {type: "mousedown"}, win); + EventUtils.synthesizeMouseAtCenter(option, {type: "mouseup"}, win); + + yield onUiUpdated; + yield waitForAllAnimationTargets(panel); + + // Simulate a mousemove outside of the rate selector area to avoid subsequent + // tests from failing because of unwanted mouseover events. + EventUtils.synthesizeMouseAtCenter( + win.document.querySelector("#timeline-toolbar"), {type: "mousemove"}, win); +} + +/** + * Prevent the toolbox common highlighter from making backend requests. + * @param {Toolbox} toolbox + */ +function disableHighlighter(toolbox) { + toolbox._highlighter = { + showBoxModel: () => new Promise(r => r()), + hideBoxModel: () => new Promise(r => r()), + pick: () => new Promise(r => r()), + cancelPick: () => new Promise(r => r()), + destroy: () => {}, + traits: {} + }; +} + +/** + * Click on an animation in the timeline to select/unselect it. + * @param {AnimationsPanel} panel The panel instance. + * @param {Number} index The index of the animation to click on. + * @param {Boolean} shouldClose Set to true if clicking should close the + * animation. + * @return {Promise} resolves to the animation whose state has changed. + */ +function* clickOnAnimation(panel, index, shouldClose) { + let timeline = panel.animationsTimelineComponent; + + // Expect a selection event. + let onSelectionChanged = timeline.once(shouldClose + ? "animation-unselected" + : "animation-selected"); + + // If we're opening the animation, also wait for the keyframes-retrieved + // event. + let onReady = shouldClose + ? Promise.resolve() + : timeline.details[index].once("keyframes-retrieved"); + + info("Click on animation " + index + " in the timeline"); + let timeBlock = timeline.rootWrapperEl.querySelectorAll(".time-block")[index]; + EventUtils.sendMouseEvent({type: "click"}, timeBlock, + timeBlock.ownerDocument.defaultView); + + yield onReady; + return yield onSelectionChanged; +} + +/** + * Get an instance of the Keyframes component from the timeline. + * @param {AnimationsPanel} panel The panel instance. + * @param {Number} animationIndex The index of the animation in the timeline. + * @param {String} propertyName The name of the animated property. + * @return {Keyframes} The Keyframes component instance. + */ +function getKeyframeComponent(panel, animationIndex, propertyName) { + let timeline = panel.animationsTimelineComponent; + let detailsComponent = timeline.details[animationIndex]; + return detailsComponent.keyframeComponents + .find(c => c.propertyName === propertyName); +} + +/** + * Get a keyframe element from the timeline. + * @param {AnimationsPanel} panel The panel instance. + * @param {Number} animationIndex The index of the animation in the timeline. + * @param {String} propertyName The name of the animated property. + * @param {Index} keyframeIndex The index of the keyframe. + * @return {DOMNode} The keyframe element. + */ +function getKeyframeEl(panel, animationIndex, propertyName, keyframeIndex) { + let keyframeComponent = getKeyframeComponent(panel, animationIndex, + propertyName); + return keyframeComponent.keyframesEl + .querySelectorAll(".frame")[keyframeIndex]; +} |