Bug 1453010 - Part 4: Add test for locking highlighting. r?gl
This patch depends on following PR.
https://github.com/devtools-html/devtools-core/pull/1028
MozReview-Commit-ID: 5IAWzZ3YTyg
--- a/devtools/client/inspector/animation/components/AnimationTarget.js
+++ b/devtools/client/inspector/animation/components/AnimationTarget.js
@@ -49,17 +49,17 @@ class AnimationTarget extends Component
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.nodeFront !== nextState.nodeFront ||
this.props.highlightedNode !== nextState.highlightedNode;
}
async updateNodeFront(animation) {
- const { emitEventForTest, getNodeFromActor } = this.props;
+ const { getNodeFromActor } = this.props;
// Try and get it from the playerFront directly.
let nodeFront = animation.animationTargetNodeFront;
// Next, get it from the walkerActor if it wasn't found.
if (!nodeFront) {
try {
nodeFront = await getNodeFromActor(animation.actorID);
@@ -68,21 +68,21 @@ class AnimationTarget extends Component
// attributed to the panel having been destroyed in the meantime, this
// error needs to be logged and render needs to stop.
console.error(e);
return;
}
}
this.setState({ nodeFront });
- emitEventForTest("animation-target-rendered");
}
render() {
const {
+ emitEventForTest,
onHideBoxModelHighlighter,
onShowBoxModelHighlighterForNode,
highlightedNode,
setHighlightedNode,
setSelectedNode,
} = this.props;
const { nodeFront } = this.state;
@@ -90,16 +90,18 @@ class AnimationTarget extends Component
if (!nodeFront) {
return dom.div(
{
className: "animation-target"
}
);
}
+ emitEventForTest("animation-target-rendered");
+
const isHighlighted = nodeFront.actorID === highlightedNode;
return dom.div(
{
className: "animation-target" +
(isHighlighted ? " highlighting" : ""),
},
Rep(
--- a/devtools/client/inspector/animation/test/browser.ini
+++ b/devtools/client/inspector/animation/test/browser.ini
@@ -19,16 +19,17 @@ support-files =
[browser_animation_animated-property-list.js]
[browser_animation_animated-property-list_unchanged-items.js]
[browser_animation_animated-property-name.js]
[browser_animation_animation-detail_close-button.js]
[browser_animation_animation-detail_title.js]
[browser_animation_animation-detail_visibility.js]
[browser_animation_animation-list.js]
[browser_animation_animation-target.js]
+[browser_animation_animation-target_highlight.js]
[browser_animation_animation-timeline-tick.js]
[browser_animation_current-time-label.js]
[browser_animation_current-time-scrubber.js]
[browser_animation_empty_on_invalid_nodes.js]
[browser_animation_inspector_exists.js]
[browser_animation_keyframes-graph_computed-value-path.js]
[browser_animation_keyframes-graph_computed-value-path_easing-hint.js]
[browser_animation_keyframes-graph_keyframe-marker.js]
--- a/devtools/client/inspector/animation/test/browser_animation_animation-target.js
+++ b/devtools/client/inspector/animation/test/browser_animation_animation-target.js
@@ -2,34 +2,42 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for following AnimationTarget component works.
// * element existance
// * number of elements
// * content of element
+// * select an animated node by clicking on inspect node
+// * title of inspect icon
add_task(async function() {
await addTab(URL_ROOT + "doc_simple_animation.html");
await removeAnimatedElementsExcept([".animated", ".long"]);
- const { animationInspector, inspector, panel } = await openAnimationInspector();
+ const { animationInspector, panel } = await openAnimationInspector();
info("Checking the animation target elements existance");
const animationItemEls = panel.querySelectorAll(".animation-list .animation-item");
is(animationItemEls.length, animationInspector.state.animations.length,
"Number of animation target element should be same to number of animations " +
"that displays");
for (const animationItemEl of animationItemEls) {
const animationTargetEl = animationItemEl.querySelector(".animation-target");
ok(animationTargetEl,
"The animation target element should be in each animation item element");
}
+ info("Checking the selecting an animated node by clicking the target node");
+ await clickOnTargetNode(animationInspector, panel, 0);
+ is(panel.querySelectorAll(".animation-target").length, 1,
+ "The length of animations should be 1");
+
info("Checking the content of animation target");
- await selectNodeAndWaitForAnimations(".animated", inspector);
const animationTargetEl =
panel.querySelector(".animation-list .animation-item .animation-target");
is(animationTargetEl.textContent, "div.ball.animated",
"The target element's content is correct");
ok(animationTargetEl.querySelector(".objectBox"), "objectBox is in the page exists");
+ ok(animationTargetEl.querySelector(".open-inspector").title,
+ INSPECTOR_L10N.getStr("inspector.nodePreview.highlightNodeLabel"));
});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_animation-target_highlight.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following highlighting related.
+// * highlight when mouse over on a target node
+// * unhighlight when mouse out from the above element
+// * lock highlighting when click on the inspect icon in animation target component
+// * add 'highlighting' class to animation target component during locking
+// * unlock highlighting when click on the above icon
+// * lock highlighting when click on the other inspect icon
+// * if the locked node has multi animations,
+// the class will add to those animation target as well
+
+add_task(async function() {
+ await addTab(URL_ROOT + "doc_simple_animation.html");
+ await removeAnimatedElementsExcept([".animated", ".multi"]);
+ const { animationInspector, panel, toolbox } = await openAnimationInspector();
+
+ info("Check highlighting when mouse over on a target node");
+ let onHighlight = toolbox.once("node-highlight");
+ mouseOverOnTargetNode(animationInspector, panel, 0);
+ let nodeFront = await onHighlight;
+ assertNodeFront(nodeFront, "DIV", "ball animated");
+
+ info("Check unhighlighting when mouse out on a target node");
+ let onUnhighlight = toolbox.once("node-unhighlight");
+ mouseOutOnTargetNode(animationInspector, panel, 0);
+ await onUnhighlight;
+ ok(true, "Unhighlighted the targe node");
+
+ info("Check node is highlighted when the inspect icon is clicked");
+ onHighlight = toolbox.once("node-highlight");
+ await clickOnInspectIcon(animationInspector, panel, 0);
+ nodeFront = await onHighlight;
+ assertNodeFront(nodeFront, "DIV", "ball animated");
+ ok(panel.querySelectorAll(".animation-target")[0].classList.contains("highlighting"),
+ "The highlighted animation target element should have 'highlighting' class");
+
+ info("Check if the animation target is still highlighted on mouse out");
+ mouseOutOnTargetNode(animationInspector, panel, 0);
+ await wait(500);
+ ok(panel.querySelectorAll(".animation-target")[0].classList.contains("highlighting"),
+ "The highlighted element still should have 'highlighting' class");
+
+ info("Highlighting another animation target");
+ onHighlight = toolbox.once("node-highlight");
+ await clickOnInspectIcon(animationInspector, panel, 1);
+ nodeFront = await onHighlight;
+ assertNodeFront(nodeFront, "DIV", "ball multi");
+
+ info("Check the highlighted state of the animation targets");
+ const animationTargetEls = panel.querySelectorAll(".animation-target");
+ ok(!animationTargetEls[0].classList.contains("highlighting"),
+ "The animation target[0] should not have 'highlighting' class");
+ ok(animationTargetEls[1].classList.contains("highlighting"),
+ "The animation target[1] should have 'highlighting' class");
+ ok(animationTargetEls[2].classList.contains("highlighting"),
+ "The animation target[2] should have 'highlighting' class");
+});
+
+function assertNodeFront(nodeFront, tagName, classValue) {
+ is(nodeFront.tagName, "DIV",
+ "The highlighted node has the correct tagName");
+ is(nodeFront.attributes[0].name, "class",
+ "The highlighted node has the correct attributes");
+ is(nodeFront.attributes[0].value, classValue,
+ "The highlighted node has the correct class");
+}
--- a/devtools/client/inspector/animation/test/head.js
+++ b/devtools/client/inspector/animation/test/head.js
@@ -93,97 +93,98 @@ addTab = async function(url) {
const removeAnimatedElementsExcept = async function(selectors) {
return executeInContent("Test:RemoveAnimatedElementsExcept", { selectors });
};
/**
* Click on an animation in the timeline to select it.
*
* @param {AnimationInspector} animationInspector.
- * @param {AnimationsPanel} panel
- * The panel instance.
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Number} index
* The index of the animation to click on.
*/
const clickOnAnimation = async function(animationInspector, panel, index) {
info("Click on animation " + index + " in the timeline");
const summaryGraphEl = panel.querySelectorAll(".animation-summary-graph")[index];
await clickOnSummaryGraph(animationInspector, panel, summaryGraphEl);
};
/**
* Click on an animation by given selector of node which is target element of animation.
*
* @param {AnimationInspector} animationInspector.
- * @param {AnimationsPanel} panel
- * The panel instance.
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {String} selector
* Selector of node which is target element of animation.
*/
const clickOnAnimationByTargetSelector = async function(animationInspector,
panel, selector) {
info(`Click on animation whose selector of target element is '${ selector }'`);
const animationItemEl = findAnimationItemElementsByTargetSelector(panel, selector);
const summaryGraphEl = animationItemEl.querySelector(".animation-summary-graph");
await clickOnSummaryGraph(animationInspector, panel, summaryGraphEl);
};
/**
* Click on close button for animation detail pane.
*
- * @param {AnimationsPanel} panel
- * The panel instance.
+ * @param {DOMElement} panel
+ * #animation-container element.
*/
const clickOnDetailCloseButton = function(panel) {
info("Click on close button for animation detail pane");
const buttonEl = panel.querySelector(".animation-detail-close-button");
const bounds = buttonEl.getBoundingClientRect();
const x = bounds.width / 2;
const y = bounds.height / 2;
EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
};
/**
* Click on pause/resume button.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
- * The panel instance.
+ * @param {DOMElement} panel
+ * #animation-container element.
*/
const clickOnPauseResumeButton = async function(animationInspector, panel) {
info("Click on pause/resume button");
const buttonEl = panel.querySelector(".pause-resume-button");
const bounds = buttonEl.getBoundingClientRect();
const x = bounds.width / 2;
const y = bounds.height / 2;
EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
await waitForSummaryAndDetail(animationInspector);
};
/**
* Click on rewind button.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
- * The panel instance.
+ * @param {DOMElement} panel
+ * #animation-container element.
*/
const clickOnRewindButton = async function(animationInspector, panel) {
info("Click on rewind button");
const buttonEl = panel.querySelector(".rewind-button");
const bounds = buttonEl.getBoundingClientRect();
const x = bounds.width / 2;
const y = bounds.height / 2;
EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
await waitForSummaryAndDetail(animationInspector);
};
/**
* Click on the scrubber controller pane to update the animation current time.
*
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Number} mouseDownPosition
* rate on scrubber controller pane.
* This method calculates
* `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
* as the clientX of MouseEvent.
*/
const clickOnCurrentTimeScrubberController = async function(animationInspector,
panel,
@@ -194,20 +195,40 @@ const clickOnCurrentTimeScrubberControll
const mousedonwX = bounds.width * mouseDownPosition;
info(`Click ${ mousedonwX } on scrubber controller`);
EventUtils.synthesizeMouse(controllerEl, mousedonwX, 0, {}, controllerEl.ownerGlobal);
await waitForSummaryAndDetail(animationInspector);
};
/**
+ * Click on the inspect icon for the given AnimationTargetComponent.
+ *
+ * @param {AnimationInspector} animationInspector.
+ * @param {DOMElement} panel
+ * #animation-container element.
+ * @param {Number} index
+ * The index of the AnimationTargetComponent to click on.
+ */
+const clickOnInspectIcon = async function(animationInspector, panel, index) {
+ info(`Click on an inspect icon in animation target component[${ index }]`);
+ const iconEl =
+ panel.querySelectorAll(".animation-target .objectBox .open-inspector")[index];
+ iconEl.scrollIntoView(false);
+ EventUtils.synthesizeMouseAtCenter(iconEl, {}, iconEl.ownerGlobal);
+ // We wait just one time, because the components are updated synchronously.
+ await animationInspector.once("animation-target-rendered");
+};
+
+/**
* Click on playback rate selector to select given rate.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Number} rate
*/
const clickOnPlaybackRateSelector = async function(animationInspector, panel, rate) {
info(`Click on playback rate selector to select ${rate}`);
const selectEl = panel.querySelector(".playback-rate-selector");
const optionEl = [...selectEl.options].filter(o => Number(o.value) === rate)[0];
if (!optionEl) {
@@ -221,35 +242,56 @@ const clickOnPlaybackRateSelector = asyn
EventUtils.synthesizeMouseAtCenter(optionEl, { type: "mouseup" }, win);
await waitForSummaryAndDetail(animationInspector);
};
/**
* Click on given summary graph element.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Element} summaryGraphEl
*/
const clickOnSummaryGraph = async function(animationInspector, panel, summaryGraphEl) {
// Disable pointer-events of the scrubber in order to avoid to click accidently.
const scrubberEl = panel.querySelector(".current-time-scrubber");
scrubberEl.style.pointerEvents = "none";
// Scroll to show the timeBlock since the element may be out of displayed area.
summaryGraphEl.scrollIntoView(false);
EventUtils.synthesizeMouseAtCenter(summaryGraphEl, {}, summaryGraphEl.ownerGlobal);
await waitForAnimationDetail(animationInspector);
// Restore the scrubber style.
scrubberEl.style.pointerEvents = "unset";
};
/**
+ * Click on the target node for the given AnimationTargetComponent index.
+ *
+ * @param {AnimationInspector} animationInspector.
+ * @param {DOMElement} panel
+ * #animation-container element.
+ * @param {Number} index
+ * The index of the AnimationTargetComponent to click on.
+ */
+const clickOnTargetNode = async function(animationInspector, panel, index) {
+ info(`Click on a target node in animation target component[${ index }]`);
+ const targetEl = panel.querySelectorAll(".animation-target .objectBox")[index];
+ targetEl.scrollIntoView(false);
+ const onHighlight = animationInspector.inspector.toolbox.once("node-highlight");
+ EventUtils.synthesizeMouseAtCenter(targetEl, {}, targetEl.ownerGlobal);
+ await waitForRendering(animationInspector);
+ await onHighlight;
+};
+
+/**
* Drag on the scrubber to update the animation current time.
*
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Number} mouseDownPosition
* rate on scrubber controller pane.
* This method calculates
* `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
* as the clientX of MouseEvent.
* @param {Number} mouseMovePosition
* Dispatch mousemove event with mouseMovePosition after mousedown.
* Calculation for clinetX is same to above.
@@ -275,17 +317,18 @@ const dragOnCurrentTimeScrubber = async
EventUtils.synthesizeMouse(controllerEl, mousemoveX, mouseYPixel,
{ type: "mouseup" }, controllerEl.ownerGlobal);
await waitForSummaryAndDetail(animationInspector);
};
/**
* Drag on the scrubber controller pane to update the animation current time.
*
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Number} mouseDownPosition
* rate on scrubber controller pane.
* This method calculates
* `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
* as the clientX of MouseEvent.
* @param {Number} mouseMovePosition
* Dispatch mousemove event with mouseMovePosition after mousedown.
* Calculation for clinetX is same to above.
@@ -310,17 +353,18 @@ const dragOnCurrentTimeScrubberControlle
await waitForSummaryAndDetail(animationInspector);
};
/**
* Get current animation duration and rate of
* clickOrDragOnCurrentTimeScrubberController in given pixels.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Number} pixels
* @return {Object}
* {
* duration,
* rate,
* }
*/
const getDurationAndRate = function(animationInspector, panel, pixels) {
@@ -328,16 +372,48 @@ const getDurationAndRate = function(anim
const bounds = controllerEl.getBoundingClientRect();
const duration =
animationInspector.state.timeScale.getDuration() / bounds.width * pixels;
const rate = 1 / bounds.width * pixels;
return { duration, rate };
};
/**
+ * Mouse over the target node for the given AnimationTargetComponent index.
+ *
+ * @param {AnimationInspector} animationInspector.
+ * @param {DOMElement} panel
+ * #animation-container element.
+ * @param {Number} index
+ * The index of the AnimationTargetComponent to click on.
+ */
+const mouseOverOnTargetNode = function(animationInspector, panel, index) {
+ info(`Mouse over on a target node in animation target component[${ index }]`);
+ const el = panel.querySelectorAll(".animation-target .objectBox")[index];
+ el.scrollIntoView(false);
+ EventUtils.synthesizeMouse(el, 10, 5, { type: "mouseover" }, el.ownerGlobal);
+};
+
+/**
+ * Mouse out of the target node for the given AnimationTargetComponent index.
+ *
+ * @param {AnimationInspector} animationInspector.
+ * @param {DOMElement} panel
+ * #animation-container element.
+ * @param {Number} index
+ * The index of the AnimationTargetComponent to click on.
+ */
+const mouseOutOnTargetNode = function(animationInspector, panel, index) {
+ info(`Mouse out on a target node in animation target component[${ index }]`);
+ const el = panel.querySelectorAll(".animation-target .objectBox")[index];
+ el.scrollIntoView(false);
+ EventUtils.synthesizeMouse(el, -1, -1, { type: "mouseout" }, el.ownerGlobal);
+};
+
+/**
* Select animation inspector in sidebar and toolbar.
*
* @param {InspectorPanel} inspector
*/
const selectAnimationInspector = async function(inspector) {
await inspector.toolbox.selectTool("inspector");
const onUpdated = inspector.once("inspector-updated");
inspector.sidebar.select("newanimationinspector");
@@ -367,17 +443,18 @@ const selectNodeAndWaitForAnimations = a
await onUpdated;
await waitForRendering(inspector.animationinspector);
};
/**
* Send keyboard event of space to given panel.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
*/
const sendSpaceKeyEvent = async function(animationInspector, panel) {
panel.focus();
EventUtils.sendKey("SPACE", panel.ownerGlobal);
await waitForSummaryAndDetail(animationInspector);
};
/**
@@ -494,40 +571,43 @@ const waitForSummaryAndDetail = async fu
waitForAnimationDetail(animationInspector),
]);
};
/**
* Check whether current time of all animations and UI are given specified time.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {Number} time
*/
function assertAnimationsCurrentTime(animationInspector, time) {
const isTimeEqual =
animationInspector.state.animations.every(({state}) => state.currentTime === time);
ok(isTimeEqual, `Current time of animations should be ${ time }`);
}
/**
* Check whether the animations are pausing.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
*/
function assertAnimationsPausing(animationInspector, panel) {
assertAnimationsPausingOrRunning(animationInspector, panel, true);
}
/**
* Check whether the animations are pausing/running.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
* @param {boolean} shouldPause
*/
function assertAnimationsPausingOrRunning(animationInspector, panel, shouldPause) {
const hasRunningAnimation =
animationInspector.state.animations.some(({state}) => state.playState === "running");
if (shouldPause) {
is(hasRunningAnimation, false, "All animations should be paused");
@@ -535,17 +615,18 @@ function assertAnimationsPausingOrRunnin
is(hasRunningAnimation, true, "Animations should be running at least one");
}
}
/**
* Check whether the animations are running.
*
* @param {AnimationInspector} animationInspector
- * @param {AnimationsPanel} panel
+ * @param {DOMElement} panel
+ * #animation-container element.
*/
function assertAnimationsRunning(animationInspector, panel) {
assertAnimationsPausingOrRunning(animationInspector, panel, false);
}
/**
* Check the <stop> element in the given linearGradientEl for the correct offset
* and color attributes.
@@ -628,19 +709,22 @@ function isPassingThrough(pathSegList, x
return false;
}
/**
* Return animation item element by target node selector.
* This function compares betweem animation-target textContent and given selector.
* Then returns matched first item.
*
- * @param {Element} panel - root element of animation inspector.
- * @param {String} selector - selector of tested element.
- * @return {Element} animation item element.
+ * @param {DOMElement} panel
+ * #animation-container element.
+ * @param {String} selector
+ * Selector of tested element.
+ * @return {DOMElement}
+ * Animation item element.
*/
function findAnimationItemElementsByTargetSelector(panel, selector) {
const attrNameEls = panel.querySelectorAll(".animation-target .attrName");
const regexp = new RegExp(`\\${ selector }(\\.|$)`, "gi");
for (const attrNameEl of attrNameEls) {
if (regexp.exec(attrNameEl.textContent)) {
return attrNameEl.closest(".animation-item");