--- a/devtools/client/inspector/animation/components/AnimationItem.js
+++ b/devtools/client/inspector/animation/components/AnimationItem.js
@@ -51,16 +51,17 @@ class AnimationItem extends PureComponen
onHideBoxModelHighlighter,
onShowBoxModelHighlighterForNode,
setSelectedNode,
}
),
SummaryGraph(
{
animation,
+ emitEventForTest,
getAnimatedPropertyMap,
simulateAnimation,
timeScale,
}
)
);
}
}
--- a/devtools/client/inspector/animation/components/graph/SummaryGraph.js
+++ b/devtools/client/inspector/animation/components/graph/SummaryGraph.js
@@ -14,16 +14,17 @@ const EndDelaySign = createFactory(requi
const SummaryGraphPath = createFactory(require("./SummaryGraphPath"));
const { getFormatStr, getStr, numberWithDecimals } = require("../../utils/l10n");
class SummaryGraph extends PureComponent {
static get propTypes() {
return {
animation: PropTypes.object.isRequired,
+ emitEventForTest: PropTypes.func.isRequired,
getAnimatedPropertyMap: PropTypes.func.isRequired,
simulateAnimation: PropTypes.func.isRequired,
timeScale: PropTypes.object.isRequired,
};
}
getTitleText(state) {
const getTime =
@@ -119,30 +120,32 @@ class SummaryGraph extends PureComponent
}
return text;
}
render() {
const {
animation,
+ emitEventForTest,
getAnimatedPropertyMap,
simulateAnimation,
timeScale,
} = this.props;
return dom.div(
{
className: "animation-summary-graph" +
(animation.state.isRunningOnCompositor ? " compositor" : ""),
title: this.getTitleText(animation.state),
},
SummaryGraphPath(
{
animation,
+ emitEventForTest,
getAnimatedPropertyMap,
simulateAnimation,
timeScale,
}
),
animation.state.delay ?
DelaySign(
{
--- a/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js
+++ b/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js
@@ -17,16 +17,17 @@ const { DEFAULT_GRAPH_HEIGHT } = require
// Minimum opacity for semitransparent fill color for keyframes's easing graph.
const MIN_KEYFRAMES_EASING_OPACITY = 0.5;
class SummaryGraphPath extends PureComponent {
static get propTypes() {
return {
animation: PropTypes.object.isRequired,
+ emitEventForTest: PropTypes.func.isRequired,
getAnimatedPropertyMap: PropTypes.object.isRequired,
simulateAnimation: PropTypes.func.isRequired,
timeScale: PropTypes.object.isRequired,
};
}
constructor(props) {
super(props);
@@ -137,28 +138,31 @@ class SummaryGraphPath extends PureCompo
}
}
return true;
}
async updateState(animation) {
const {
+ emitEventForTest,
getAnimatedPropertyMap,
timeScale,
} = this.props;
const animatedPropertyMap = await getAnimatedPropertyMap(animation);
const keyframesList = this.getOffsetAndEasingOnlyKeyframes(animatedPropertyMap);
const thisEl = ReactDOM.findDOMNode(this);
const totalDuration = this.getTotalDuration(animation, timeScale);
const durationPerPixel = totalDuration / thisEl.parentNode.clientWidth;
this.setState({ durationPerPixel, keyframesList });
+
+ emitEventForTest("animation-summary-graph-rendered");
}
render() {
const { durationPerPixel, keyframesList } = this.state;
if (!durationPerPixel) {
return dom.svg();
}
--- a/devtools/client/inspector/animation/test/browser.ini
+++ b/devtools/client/inspector/animation/test/browser.ini
@@ -1,17 +1,27 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
+ doc_multi_timings.html
doc_simple_animation.html
head.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/test/head.js
!/devtools/client/inspector/test/shared-head.js
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js
[browser_animation_animation_list_exists.js]
[browser_animation_animation_list_time_tick.js]
[browser_animation_AnimationTarget.js]
[browser_animation_empty_on_invalid_nodes.js]
[browser_animation_inspector_exists.js]
+[browser_animation_SummaryGraph_AnimationName.js]
+[browser_animation_SummaryGraph_compositor.js]
+[browser_animation_SummaryGraph_ComputedTimingPath.js]
+[browser_animation_SummaryGraph_DelaySign.js]
+[browser_animation_SummaryGraph_EndDelaySign.js]
+[browser_animation_SummaryGraph_EffectTimingPath.js]
+[browser_animation_SummaryGraph_NegativeDelayPath.js]
+[browser_animation_SummaryGraph_NegativeEndDelayPath.js]
+[browser_animation_SummaryGraph_tooltip.js]
--- a/devtools/client/inspector/animation/test/browser_animation_AnimationTarget.js
+++ b/devtools/client/inspector/animation/test/browser_animation_AnimationTarget.js
@@ -1,14 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
-// Test existance and content of animation target.
+// Test for following AnimationTarget component works.
+// * element existance
+// * number of elements
+// * content of element
add_task(async function () {
await addTab(URL_ROOT + "doc_simple_animation.html");
const { animationInspector, inspector, panel } = await openAnimationInspector();
info("Checking the animation target elements existance");
const animationItemEls = panel.querySelectorAll(".animation-list .animation-item");
is(animationItemEls.length, animationInspector.animations.length,
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_AnimationName.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following AnimationName component works.
+// * element existance
+// * name text
+
+const TEST_CASES = [
+ {
+ targetClassName: "cssanimation-normal",
+ expectedLabel: "cssanimation",
+ },
+ {
+ targetClassName: "cssanimation-linear",
+ expectedLabel: "cssanimation",
+ },
+ {
+ targetClassName: "delay-positive",
+ expectedLabel: "test-delay-animation",
+ },
+ {
+ targetClassName: "delay-negative",
+ expectedLabel: "test-negative-delay-animation",
+ },
+ {
+ targetClassName: "easing-step",
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedLabel,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+
+ info(`Checking animation name element existance for ${ targetClassName }`);
+ const animationNameEl = animationItemEl.querySelector(".animation-name");
+
+ if (expectedLabel) {
+ ok(animationNameEl,
+ "The animation name element should be in animation item element");
+ is(animationNameEl.textContent, expectedLabel,
+ `The animation name should be ${ expectedLabel }`);
+ } else {
+ ok(!animationNameEl,
+ "The animation name element should not be in animation item element");
+ }
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_ComputedTimingPath.js
@@ -0,0 +1,466 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following ComputedTimingPath component works.
+// * element existance
+// * iterations: path, count
+// * delay: path
+// * fill: path
+// * endDelay: path
+
+const TEST_CASES = [
+ {
+ targetClassName: "cssanimation-normal",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 40.851 },
+ { x: 50000, y: 80.24},
+ { x: 75000, y: 96.05 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "cssanimation-linear",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "delay-positive",
+ expectedDelayPath: [
+ { x: 0, y: 0 },
+ { x: 50000, y: 0 },
+ ],
+ expectedIterationPathList: [
+ [
+ { x: 50000, y: 0 },
+ { x: 75000, y: 25 },
+ { x: 100000, y: 50 },
+ { x: 125000, y: 75 },
+ { x: 150000, y: 100 },
+ { x: 150000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "delay-negative",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 0, y: 50 },
+ { x: 25000, y: 75 },
+ { x: 50000, y: 100 },
+ { x: 50000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "easing-step",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 49999, y: 0 },
+ { x: 50000, y: 50 },
+ { x: 99999, y: 50 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "enddelay-positive",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ expectedEndDelayPath: [
+ { x: 100000, y: 0 },
+ { x: 150000, y: 0 },
+ ],
+ },
+ {
+ targetClassName: "enddelay-negative",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 50000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "enddelay-with-fill-forwards",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ expectedEndDelayPath: [
+ { x: 100000, y: 0 },
+ { x: 100000, y: 100 },
+ { x: 150000, y: 100 },
+ { x: 150000, y: 0 },
+ ],
+ expectedForwardsPath: [
+ { x: 150000, y: 0 },
+ { x: 150000, y: 100 },
+ { x: 200000, y: 100 },
+ { x: 200000, y: 0 },
+ ],
+ },
+ {
+ targetClassName: "enddelay-with-iterations-infinity",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ],
+ [
+ { x: 100000, y: 0 },
+ { x: 125000, y: 25 },
+ { x: 150000, y: 50 },
+ { x: 175000, y: 75 },
+ { x: 200000, y: 100 },
+ { x: 200000, y: 0 },
+ ]
+ ],
+ isInfinity: true,
+ },
+ {
+ targetClassName: "direction-alternate-with-iterations-infinity",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ],
+ [
+ { x: 100000, y: 0 },
+ { x: 100000, y: 100 },
+ { x: 125000, y: 75 },
+ { x: 150000, y: 50 },
+ { x: 175000, y: 25 },
+ { x: 200000, y: 0 },
+ ]
+ ],
+ isInfinity: true,
+ },
+ {
+ targetClassName: "direction-alternate-reverse-with-iterations-infinity",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 0, y: 100 },
+ { x: 25000, y: 75 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 25 },
+ { x: 100000, y: 0 },
+ ],
+ [
+ { x: 100000, y: 0 },
+ { x: 125000, y: 25 },
+ { x: 150000, y: 50 },
+ { x: 175000, y: 75 },
+ { x: 200000, y: 100 },
+ { x: 200000, y: 0 },
+ ]
+ ],
+ isInfinity: true,
+ },
+ {
+ targetClassName: "direction-reverse-with-iterations-infinity",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 0, y: 100 },
+ { x: 25000, y: 75 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 25 },
+ { x: 100000, y: 0 },
+ ],
+ [
+ { x: 100000, y: 0 },
+ { x: 100000, y: 100 },
+ { x: 125000, y: 75 },
+ { x: 150000, y: 50 },
+ { x: 175000, y: 25 },
+ { x: 200000, y: 0 },
+ ]
+ ],
+ isInfinity: true,
+ },
+ {
+ targetClassName: "fill-backwards",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "fill-backwards-with-delay-iterationstart",
+ expectedDelayPath: [
+ { x: 0, y: 0 },
+ { x: 0, y: 50 },
+ { x: 50000, y: 50 },
+ { x: 50000, y: 0 },
+ ],
+ expectedIterationPathList: [
+ [
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ],
+ [
+ { x: 100000, y: 0 },
+ { x: 125000, y: 25 },
+ { x: 150000, y: 50 },
+ { x: 150000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "fill-both",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ expectedForwardsPath: [
+ { x: 100000, y: 0 },
+ { x: 100000, y: 100 },
+ { x: 200000, y: 100 },
+ { x: 200000, y: 0 },
+ ],
+ },
+ {
+ targetClassName: "fill-both-width-delay-iterationstart",
+ expectedDelayPath: [
+ { x: 0, y: 0 },
+ { x: 0, y: 50 },
+ { x: 50000, y: 50 },
+ { x: 50000, y: 0 },
+ ],
+ expectedIterationPathList: [
+ [
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ],
+ [
+ { x: 100000, y: 0 },
+ { x: 125000, y: 25 },
+ { x: 150000, y: 50 },
+ { x: 150000, y: 0 },
+ ]
+ ],
+ expectedForwardsPath: [
+ { x: 150000, y: 0 },
+ { x: 150000, y: 50 },
+ ],
+ },
+ {
+ targetClassName: "fill-forwards",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ expectedForwardsPath: [
+ { x: 100000, y: 0 },
+ { x: 100000, y: 100 },
+ { x: 200000, y: 100 },
+ { x: 200000, y: 0 },
+ ],
+ },
+ {
+ targetClassName: "iterationstart",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 50 },
+ { x: 25000, y: 75 },
+ { x: 50000, y: 100 },
+ { x: 50000, y: 0 },
+ ],
+ [
+ { x: 50000, y: 0 },
+ { x: 75000, y: 25 },
+ { x: 100000, y: 50 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "no-compositor",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "keyframes-easing-step",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 49999, y: 0 },
+ { x: 50000, y: 50 },
+ { x: 99999, y: 50 },
+ { x: 100000, y: 0 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "narrow-keyframes",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 10000, y: 10 },
+ { x: 11000, y: 10 },
+ { x: 11500, y: 10 },
+ { x: 12999, y: 10 },
+ { x: 13000, y: 13 },
+ { x: 13500, y: 13.5 },
+ ]
+ ],
+ },
+ {
+ targetClassName: "duplicate-offsets",
+ expectedIterationPathList: [
+ [
+ { x: 0, y: 0 },
+ { x: 25000, y: 25 },
+ { x: 50000, y: 50 },
+ { x: 99999, y: 50 },
+ ]
+ ],
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedDelayPath,
+ expectedEndDelayPath,
+ expectedForwardsPath,
+ expectedIterationPathList,
+ isInfinity,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+
+ info(`Checking computed timing path existance for ${ targetClassName }`);
+ const computedTimingPathEl =
+ animationItemEl.querySelector(".animation-computed-timing-path");
+ ok(computedTimingPathEl,
+ "The computed timing path element should be in each animation item element");
+
+ info(`Checking delay path for ${ targetClassName }`);
+ const delayPathEl = computedTimingPathEl.querySelector(".animation-delay-path");
+
+ if (expectedDelayPath) {
+ ok(delayPathEl, "delay path should be existance");
+ assertPathSegments(delayPathEl, true, expectedDelayPath);
+ } else {
+ ok(!delayPathEl, "delay path should not be existance");
+ }
+
+ info(`Checking iteration path list for ${ targetClassName }`);
+ const iterationPathEls =
+ computedTimingPathEl.querySelectorAll(".animation-iteration-path");
+ is(iterationPathEls.length, expectedIterationPathList.length,
+ `Number of iteration path should be ${ expectedIterationPathList.length }`);
+
+ for (const [j, iterationPathEl] of iterationPathEls.entries()) {
+ assertPathSegments(iterationPathEl, true, expectedIterationPathList[j]);
+
+ info(`Checking infinity ${ targetClassName }`);
+ if (isInfinity && j >= 1) {
+ ok(iterationPathEl.classList.contains("infinity"),
+ "iteration path should have 'infinity' class");
+ } else {
+ ok(!iterationPathEl.classList.contains("infinity"),
+ "iteration path should not have 'infinity' class");
+ }
+ }
+
+ info(`Checking endDelay path for ${ targetClassName }`);
+ const endDelayPathEl = computedTimingPathEl.querySelector(".animation-enddelay-path");
+
+ if (expectedEndDelayPath) {
+ ok(endDelayPathEl, "endDelay path should be existance");
+ assertPathSegments(endDelayPathEl, true, expectedEndDelayPath);
+ } else {
+ ok(!endDelayPathEl, "endDelay path should not be existance");
+ }
+
+ info(`Checking forwards fill path for ${ targetClassName }`);
+ const forwardsPathEl =
+ computedTimingPathEl.querySelector(".animation-fill-forwards-path");
+
+ if (expectedForwardsPath) {
+ ok(forwardsPathEl, "forwards path should be existance");
+ assertPathSegments(forwardsPathEl, true, expectedForwardsPath);
+ } else {
+ ok(!forwardsPathEl, "forwards path should not be existance");
+ }
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_DelaySign.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following DelaySign component works.
+// * element existance
+// * left position
+// * width
+// * additinal class
+
+const TEST_CASES = [
+ {
+ targetClassName: "delay-positive",
+ expectedResult: {
+ left: "25%",
+ width: "25%",
+ },
+ },
+ {
+ targetClassName: "delay-negative",
+ expectedResult: {
+ additionalClass: "negative",
+ left: "0%",
+ width: "25%",
+ },
+ },
+ {
+ targetClassName: "fill-backwards-with-delay-iterationstart",
+ expectedResult: {
+ additionalClass: "fill",
+ left: "25%",
+ width: "25%",
+ },
+ },
+ {
+ targetClassName: "fill-both",
+ },
+ {
+ targetClassName: "fill-both-width-delay-iterationstart",
+ expectedResult: {
+ additionalClass: "fill",
+ left: "25%",
+ width: "25%",
+ },
+ },
+ {
+ targetClassName: "keyframes-easing-step",
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedResult,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+
+ info(`Checking delay sign existance for ${ targetClassName }`);
+ const delaySignEl = animationItemEl.querySelector(".animation-delay-sign");
+
+ if (expectedResult) {
+ ok(delaySignEl, "The delay sign element should be in animation item element");
+
+ is(delaySignEl.style.left, expectedResult.left,
+ `Left position should be ${ expectedResult.left }`);
+ is(delaySignEl.style.width, expectedResult.width,
+ `Width should be ${ expectedResult.width }`);
+
+ if (expectedResult.additionalClass) {
+ ok(delaySignEl.classList.contains(expectedResult.additionalClass),
+ `delay sign element should have ${ expectedResult.additionalClass } class`);
+ } else {
+ ok(!delaySignEl.classList.contains(expectedResult.additionalClass),
+ "delay sign element should not have " +
+ `${ expectedResult.additionalClass } class`);
+ }
+ } else {
+ ok(!delaySignEl, "The delay sign element should not be in animation item element");
+ }
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_EffectTimingPath.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following EffectTimingPath component works.
+// * element existance
+// * path
+
+const TEST_CASES = [
+ {
+ targetClassName: "cssanimation-linear",
+ },
+ {
+ targetClassName: "delay-negative",
+ },
+ {
+ targetClassName: "easing-step",
+ expectedPath: [
+ { x: 0, y: 0 },
+ { x: 49900, y: 0 },
+ { x: 50000, y: 50 },
+ { x: 99999, y: 50 },
+ { x: 100000, y: 0 },
+ ],
+ },
+ {
+ targetClassName: "keyframes-easing-step",
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedPath,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+
+ info(`Checking effect timing path existance for ${ targetClassName }`);
+ const effectTimingPathEl =
+ animationItemEl.querySelector(".animation-effect-timing-path");
+
+ if (expectedPath) {
+ ok(effectTimingPathEl,
+ "The effect timing path element should be in animation item element");
+ const pathEl = effectTimingPathEl.querySelector(".animation-iteration-path");
+ assertPathSegments(pathEl, false, expectedPath);
+ } else {
+ ok(!effectTimingPathEl,
+ "The effect timing path element should not be in animation item element");
+ }
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_EndDelaySign.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following EndDelaySign component works.
+// * element existance
+// * left position
+// * width
+// * additinal class
+
+const TEST_CASES = [
+ {
+ targetClassName: "enddelay-positive",
+ expectedResult: {
+ left: "75%",
+ width: "25%",
+ },
+ },
+ {
+ targetClassName: "enddelay-negative",
+ expectedResult: {
+ additionalClass: "negative",
+ left: "50%",
+ width: "25%",
+ },
+ },
+ {
+ targetClassName: "enddelay-with-fill-forwards",
+ expectedResult: {
+ additionalClass: "fill",
+ left: "75%",
+ width: "25%",
+ },
+ },
+ {
+ targetClassName: "enddelay-with-iterations-infinity",
+ },
+ {
+ targetClassName: "keyframes-easing-step",
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedResult,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+
+ info(`Checking endDelay sign existance for ${ targetClassName }`);
+ const endDelaySignEl = animationItemEl.querySelector(".animation-end-delay-sign");
+
+ if (expectedResult) {
+ ok(endDelaySignEl, "The endDelay sign element should be in animation item element");
+
+ is(endDelaySignEl.style.left, expectedResult.left,
+ `Left position should be ${ expectedResult.left }`);
+ is(endDelaySignEl.style.width, expectedResult.width,
+ `Width should be ${ expectedResult.width }`);
+
+ if (expectedResult.additionalClass) {
+ ok(endDelaySignEl.classList.contains(expectedResult.additionalClass),
+ `endDelay sign element should have ${ expectedResult.additionalClass } class`);
+ } else {
+ ok(!endDelaySignEl.classList.contains(expectedResult.additionalClass),
+ "endDelay sign element should not have " +
+ `${ expectedResult.additionalClass } class`);
+ }
+ } else {
+ ok(!endDelaySignEl,
+ "The endDelay sign element should not be in animation item element");
+ }
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_NegativeDelayPath.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following NegativeDelayPath component works.
+// * element existance
+// * path
+
+const TEST_CASES = [
+ {
+ targetClassName: "delay-positive",
+ },
+ {
+ targetClassName: "delay-negative",
+ expectedPath: [
+ { x: -50000, y: 0 },
+ { x: -25000, y: 25 },
+ { x: 0, y: 50 },
+ { x: 0, y: 0 },
+ ],
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedPath,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+
+ info(`Checking negative delay path existence for ${ targetClassName }`);
+ const negativeDelayPathEl =
+ animationItemEl.querySelector(".animation-negative-delay-path");
+
+ if (expectedPath) {
+ ok(negativeDelayPathEl,
+ "The negative delay path element should be in animation item element");
+ const pathEl = negativeDelayPathEl.querySelector("path");
+ assertPathSegments(pathEl, true, expectedPath);
+ } else {
+ ok(!negativeDelayPathEl,
+ "The negative delay path element should not be in animation item element");
+ }
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_NegativeEndDelayPath.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for following NegativeEndDelayPath component works.
+// * element existance
+// * path
+
+const TEST_CASES = [
+ {
+ targetClassName: "enddelay-positive",
+ },
+ {
+ targetClassName: "enddelay-negative",
+ expectedPath: [
+ { x: 50000, y: 0 },
+ { x: 50000, y: 50 },
+ { x: 75000, y: 75 },
+ { x: 100000, y: 100 },
+ { x: 100000, y: 0 },
+ ],
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedPath,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+
+ info(`Checking negative endDelay path existance for ${ targetClassName }`);
+ const negativeEndDelayPathEl =
+ animationItemEl.querySelector(".animation-negative-end-delay-path");
+
+ if (expectedPath) {
+ ok(negativeEndDelayPathEl,
+ "The negative endDelay path element should be in animation item element");
+ const pathEl = negativeEndDelayPathEl.querySelector("path");
+ assertPathSegments(pathEl, true, expectedPath);
+ } else {
+ ok(!negativeEndDelayPathEl,
+ "The negative endDelay path element should not be in animation item element");
+ }
+ }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_compositor.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that when animations displayed in the timeline are running on the
+// compositor, they get a special icon and information in the tooltip.
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_simple_animation.html");
+
+ const { inspector, panel } = await openAnimationInspector();
+
+ info("Select a test node we know has an animation running on the compositor");
+ await selectNodeAndWaitForAnimations(".compositor-all", inspector);
+
+ const summaryGraphEl = panel.querySelector(".animation-summary-graph");
+ ok(summaryGraphEl.classList.contains("compositor"),
+ "The element has the compositor css class");
+ ok(hasTooltip(summaryGraphEl,
+ ANIMATION_L10N.getStr("player.allPropertiesOnCompositorTooltip")),
+ "The element has the right tooltip content");
+
+ info("Select a node we know doesn't have an animation on the compositor");
+ await selectNodeAndWaitForAnimations(".no-compositor", inspector);
+
+ ok(!summaryGraphEl.classList.contains("compositor"),
+ "The element does not have the compositor css class");
+ ok(!hasTooltip(summaryGraphEl,
+ ANIMATION_L10N.getStr("player.allPropertiesOnCompositorTooltip")),
+ "The element does not have oncompositor tooltip content");
+ ok(!hasTooltip(summaryGraphEl,
+ ANIMATION_L10N.getStr("player.somePropertiesOnCompositorTooltip")),
+ "The element does not have oncompositor tooltip content");
+
+ info("Select a node we know has animation on the compositor and not on the compositor");
+ await selectNodeAndWaitForAnimations(".compositor-notall", inspector);
+
+ ok(summaryGraphEl.classList.contains("compositor"),
+ "The element has the compositor css class");
+ ok(hasTooltip(summaryGraphEl,
+ ANIMATION_L10N.getStr("player.somePropertiesOnCompositorTooltip")),
+ "The element has the right tooltip content");
+});
+
+function hasTooltip(summaryGraphEl, expected) {
+ const tooltip = summaryGraphEl.getAttribute("title");
+ return tooltip.includes(expected);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_SummaryGraph_tooltip.js
@@ -0,0 +1,305 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for existance and content of tooltip on summary graph element.
+
+const TEST_CASES = [
+ {
+ targetClassName: "cssanimation-normal",
+ expectedResult: {
+ nameAndType: "cssanimation - CSS Animation",
+ duration: "100s",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "cssanimation-linear",
+ expectedResult: {
+ nameAndType: "cssanimation - CSS Animation",
+ duration: "100s",
+ animationTimingFunction: "linear",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "delay-positive",
+ expectedResult: {
+ nameAndType: "test-delay-animation - Script Animation",
+ delay: "50s",
+ duration: "100s",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "delay-negative",
+ expectedResult: {
+ nameAndType: "test-negative-delay-animation - Script Animation",
+ delay: "-50s",
+ duration: "100s",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "easing-step",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ easing: "steps(2)",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "enddelay-positive",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ endDelay: "50s",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "enddelay-negative",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ endDelay: "-50s",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "enddelay-with-fill-forwards",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ endDelay: "50s",
+ fill: "forwards",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "enddelay-with-iterations-infinity",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ endDelay: "50s",
+ iterations: "\u221E",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "direction-alternate-with-iterations-infinity",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ direction: "alternate",
+ iterations: "\u221E",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "direction-alternate-reverse-with-iterations-infinity",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ direction: "alternate-reverse",
+ iterations: "\u221E",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "direction-reverse-with-iterations-infinity",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ direction: "reverse",
+ iterations: "\u221E",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "fill-backwards",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ fill: "backwards",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "fill-backwards-with-delay-iterationstart",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ delay: "50s",
+ duration: "100s",
+ fill: "backwards",
+ iterationStart: "0.5",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "fill-both",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ fill: "both",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "fill-both-width-delay-iterationstart",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ delay: "50s",
+ duration: "100s",
+ fill: "both",
+ iterationStart: "0.5",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "fill-forwards",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ fill: "forwards",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "iterationstart",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ iterationStart: "0.5",
+ isAllOnCompositor: true,
+ },
+ },
+ {
+ targetClassName: "no-compositor",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ },
+ },
+ {
+ targetClassName: "keyframes-easing-step",
+ expectedResult: {
+ nameAndType: "Script Animation",
+ duration: "100s",
+ isAllOnCompositor: true,
+ },
+ },
+];
+
+add_task(async function () {
+ await addTab(URL_ROOT + "doc_multi_timings.html");
+
+ const { panel } = await openAnimationInspector();
+
+ for (const testCase of TEST_CASES) {
+ const {
+ expectedResult,
+ targetClassName,
+ } = testCase;
+
+ const animationItemEl =
+ findAnimationItemElementsByTargetClassName(panel, targetClassName);
+ const summaryGraphEl = animationItemEl.querySelector(".animation-summary-graph");
+
+ info(`Checking tooltip for ${ targetClassName }`);
+ ok(summaryGraphEl.hasAttribute("title"),
+ "Summary graph should have 'title' attribute");
+
+ const tooltip = summaryGraphEl.getAttribute("title");
+ const {
+ animationTimingFunction,
+ delay,
+ easing,
+ endDelay,
+ direction,
+ duration,
+ fill,
+ iterations,
+ iterationStart,
+ nameAndType,
+ isAllOnCompositor,
+ } = expectedResult;
+
+ ok(tooltip.startsWith(nameAndType), "Tooltip should start with name and type");
+
+ if (animationTimingFunction) {
+ const expected = `Animation timing function: ${ animationTimingFunction }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Animation timing function:"),
+ "Tooltip should not include animation timing function");
+ }
+
+ if (delay) {
+ const expected = `Delay: ${ delay }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Delay:"), "Tooltip should not include delay");
+ }
+
+ if (direction) {
+ const expected = `Direction: ${ direction }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Direction:"), "Tooltip should not include delay");
+ }
+
+ if (duration) {
+ const expected = `Duration: ${ duration }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Duration:"), "Tooltip should not include delay");
+ }
+
+ if (easing) {
+ const expected = `Overall easing: ${ easing }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Overall easing:"), "Tooltip should not include easing");
+ }
+
+ if (endDelay) {
+ const expected = `End delay: ${ endDelay }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("End delay:"), "Tooltip should not include endDelay");
+ }
+
+ if (fill) {
+ const expected = `Fill: ${ fill }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Fill:"), "Tooltip should not include fill");
+ }
+
+ if (iterations) {
+ const expected = `Repeats: ${ iterations }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Repeats:"), "Tooltip should not include iterations");
+ }
+
+ if (iterationStart) {
+ const expected = `Iteration start: ${ iterationStart }`;
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("Iteration start:"),
+ "Tooltip should not include iterationStart");
+ }
+
+ if (isAllOnCompositor) {
+ const expected = "All animation properties are optimized";
+ ok(tooltip.includes(expected), `Tooltip should include '${ expected }'`);
+ } else {
+ ok(!tooltip.includes("optimized"),
+ "Tooltip should not include a message for optmization");
+ }
+ }
+});
--- a/devtools/client/inspector/animation/test/browser_animation_animation_list_exists.js
+++ b/devtools/client/inspector/animation/test/browser_animation_animation_list_exists.js
@@ -26,14 +26,9 @@ add_task(async function () {
isnot(evenColor, oddColor,
"Background color of an even animation should be different from odd");
info("Checking list and items existence after select a element which has an animation");
const animatedNode = await getNodeFront(".animated", inspector);
await selectNodeAndWaitForAnimations(animatedNode, inspector);
is(panel.querySelectorAll(".animation-list .animation-item").length, 1,
"The number of animations displayed should be 1 for .animated element");
-
- // TODO: We need to add following tests after implement since this test has same role
- // of animationinspector/test/browser_animation_timeline_ui.js
- // * name label in animation element existance.
- // * summary graph in animation element existance.
});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/doc_multi_timings.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <style>
+ div {
+ background-color: lime;
+ height: 100px;
+ width: 100px;
+ }
+
+ .cssanimation-normal {
+ animation: cssanimation 100s;
+ }
+
+ .cssanimation-linear {
+ animation: cssanimation 100s linear;
+ }
+
+ @keyframes cssanimation {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+ }
+ </style>
+ </head>
+ <body>
+ <div class="cssanimation-normal"></div>
+ <div class="cssanimation-linear"></div>
+ <script>
+ "use strict";
+
+ const duration = 100000;
+
+ function createAnimation(keyframes, effect, className) {
+ const div = document.createElement("div");
+ div.classList.add(className);
+ document.body.appendChild(div);
+ effect.duration = duration;
+ div.animate(keyframes, effect);
+ }
+
+ createAnimation({ opacity: [0, 1] },
+ { delay: 50000, id: "test-delay-animation" },
+ "delay-positive");
+
+ createAnimation({ opacity: [0, 1] },
+ { delay: -50000, id: "test-negative-delay-animation" },
+ "delay-negative");
+
+ createAnimation({ opacity: [0, 1] },
+ { easing: "steps(2)" },
+ "easing-step");
+
+ createAnimation({ opacity: [0, 1] },
+ { endDelay: 50000 },
+ "enddelay-positive");
+
+ createAnimation({ opacity: [0, 1] },
+ { endDelay: -50000 },
+ "enddelay-negative");
+
+ createAnimation({ opacity: [0, 1] },
+ { endDelay: 50000, fill: "forwards" },
+ "enddelay-with-fill-forwards");
+
+ createAnimation({ opacity: [0, 1] },
+ { endDelay: 50000, iterations: Infinity },
+ "enddelay-with-iterations-infinity");
+
+ createAnimation({ opacity: [0, 1] },
+ { direction: "alternate", iterations: Infinity },
+ "direction-alternate-with-iterations-infinity");
+
+ createAnimation({ opacity: [0, 1] },
+ { direction: "alternate-reverse", iterations: Infinity },
+ "direction-alternate-reverse-with-iterations-infinity");
+
+ createAnimation({ opacity: [0, 1] },
+ { direction: "reverse", iterations: Infinity },
+ "direction-reverse-with-iterations-infinity");
+
+ createAnimation({ opacity: [0, 1] },
+ { fill: "backwards" },
+ "fill-backwards");
+
+ createAnimation({ opacity: [0, 1] },
+ { fill: "backwards", delay: 50000, iterationStart: 0.5 },
+ "fill-backwards-with-delay-iterationstart");
+
+ createAnimation({ opacity: [0, 1] },
+ { fill: "both" },
+ "fill-both");
+
+ createAnimation({ opacity: [0, 1] },
+ { fill: "both", delay: 50000, iterationStart: 0.5 },
+ "fill-both-width-delay-iterationstart");
+
+ createAnimation({ opacity: [0, 1] },
+ { fill: "forwards" },
+ "fill-forwards");
+
+ createAnimation({ opacity: [0, 1] },
+ { iterationStart: 0.5 },
+ "iterationstart");
+
+ createAnimation({ width: ["100px", "150px"] },
+ {},
+ "no-compositor");
+
+ createAnimation([{ opacity: 0, easing: "steps(2)" }, { opacity: 1 }],
+ {},
+ "keyframes-easing-step");
+
+ createAnimation(
+ [
+ {
+ opacity: 0,
+ offset: 0,
+ },
+ {
+ opacity: 1,
+ offset: 0.1,
+ easing: "steps(1)"
+ },
+ {
+ opacity: 0,
+ offset: 0.13,
+ }
+ ],
+ {},
+ "narrow-keyframes");
+
+ createAnimation(
+ [
+ {
+ offset: 0,
+ opacity: 1,
+ },
+ {
+ offset: 0.5,
+ opacity: 1,
+ },
+ {
+ offset: 0.5,
+ easing: "steps(1)",
+ opacity: 0,
+ },
+ {
+ offset: 1,
+ opacity: 1,
+ }
+ ],
+ {},
+ "duplicate-offsets");
+ </script>
+ </body>
+</html>
--- a/devtools/client/inspector/animation/test/head.js
+++ b/devtools/client/inspector/animation/test/head.js
@@ -31,18 +31,18 @@ registerCleanupFunction(() => {
* Open the toolbox, with the inspector tool visible and the animationinspector
* sidebar selected.
*
* @return {Promise} that resolves when the inspector is ready.
*/
const openAnimationInspector = async function () {
const { inspector, toolbox } = await openInspectorSidebarTab(TAB_NAME);
await inspector.once("inspector-updated");
- await waitForAllAnimationTargets(inspector);
const { animationinspector: animationInspector } = inspector;
+ await waitForRendering(animationInspector);
const panel = inspector.panelWin.document.getElementById("animation-container");
return { animationInspector, toolbox, inspector, panel };
};
/**
* Close the toolbox.
*
* @return {Promise} that resolves when the toolbox has closed.
@@ -100,17 +100,17 @@ addTab = async function (url) {
* and animations of its subtree are properly displayed.
*/
const selectNodeAndWaitForAnimations = async function (data, inspector, reason = "test") {
// 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).
const onUpdated = inspector.once("inspector-updated");
await selectNode(data, inspector, reason);
await onUpdated;
- await waitForAllAnimationTargets(inspector);
+ await waitForRendering(inspector.animationinspector);
};
/**
* Set the sidebar width by given parameter.
*
* @param {String} width
* Change sidebar width by given parameter.
* @param {InspectorPanel} inspector
@@ -119,20 +119,128 @@ const selectNodeAndWaitForAnimations = a
*/
const setSidebarWidth = async function (width, inspector) {
const onUpdated = inspector.toolbox.once("inspector-sidebar-resized");
inspector.splitBox.setState({ width });
await onUpdated;
};
/**
+ * Wait for rendering.
+ *
+ * @param {AnimationInspector} animationInspector
+ */
+const waitForRendering = async function (animationInspector) {
+ await Promise.all([
+ waitForAllAnimationTargets(animationInspector),
+ waitForAllSummaryGraph(animationInspector),
+ ]);
+};
+
+/**
* Wait for all AnimationTarget components to be fully loaded
* (fetched their related actor and rendered).
*
- * @param {Inspector} inspector
+ * @param {AnimationInspector} animationInspector
*/
-const waitForAllAnimationTargets = async function (inspector) {
- const { animationinspector: animationInspector } = inspector;
-
+const waitForAllAnimationTargets = async function (animationInspector) {
for (let i = 0; i < animationInspector.animations.length; i++) {
await animationInspector.once("animation-target-rendered");
}
};
+
+/**
+ * Wait for all SummaryGraph components to be fully loaded
+ *
+ * @param {AnimationInspector} inspector
+ */
+const waitForAllSummaryGraph = async function (animationInspector) {
+ for (let i = 0; i < animationInspector.animations.length; i++) {
+ await animationInspector.once("animation-summary-graph-rendered");
+ }
+};
+
+/**
+ * SummaryGraph is constructed by <path> element.
+ * This function checks the vertex of path segments.
+ *
+ * @param {Element} pathEl
+ * <path> element.
+ * @param {boolean} hasClosePath
+ * Set true if the path shoud be closing.
+ * @param {Object} expectedValues
+ * JSON object format. We can test the vertex and color.
+ * e.g.
+ * [
+ * { x: 0, y: 0 },
+ * { x: 0, y: 1 },
+ * ]
+ */
+function assertPathSegments(pathEl, hasClosePath, expectedValues) {
+ const pathSegList = pathEl.pathSegList;
+ ok(pathSegList, "The tested element should have pathSegList");
+
+ expectedValues.forEach(expectedValue => {
+ ok(isPassingThrough(pathSegList, expectedValue.x, expectedValue.y),
+ `The path segment of x ${ expectedValue.x }, y ${ expectedValue.y } `
+ + `should be passing through`);
+ });
+
+ if (hasClosePath) {
+ const closePathSeg = pathSegList.getItem(pathSegList.numberOfItems - 1);
+ is(closePathSeg.pathSegType, closePathSeg.PATHSEG_CLOSEPATH,
+ "The last segment should be close path");
+ }
+}
+
+/**
+ * Check whether the given vertex is passing throug on the path.
+ *
+ * @param {pathSegList} pathSegList - pathSegList of <path> element.
+ * @param {float} x - x of vertex.
+ * @param {float} y - y of vertex.
+ * @return {boolean} true: passing through, false: no on the path.
+ */
+function isPassingThrough(pathSegList, x, y) {
+ let previousPathSeg = pathSegList.getItem(0);
+ for (let i = 0; i < pathSegList.numberOfItems; i++) {
+ const pathSeg = pathSegList.getItem(i);
+ if (pathSeg.x === undefined) {
+ continue;
+ }
+ const currentX = parseFloat(pathSeg.x.toFixed(3));
+ const currentY = parseFloat(pathSeg.y.toFixed(3));
+ if (currentX === x && currentY === y) {
+ return true;
+ }
+ const previousX = parseFloat(previousPathSeg.x.toFixed(3));
+ const previousY = parseFloat(previousPathSeg.y.toFixed(3));
+ if (previousX <= x && x <= currentX &&
+ Math.min(previousY, currentY) <= y && y <= Math.max(previousY, currentY)) {
+ return true;
+ }
+ previousPathSeg = pathSeg;
+ }
+ return false;
+}
+
+/**
+ * Return animation item element by target node class.
+ * This function compares betweem animation-target textContent and given className.
+ * Also, this function premises one class name.
+ *
+ * @param {Element} panel - root element of animation inspector.
+ * @param {String} targetClassName - class name of tested element.
+ * @return {Element} animation item element.
+ */
+function findAnimationItemElementsByTargetClassName(panel, targetClassName) {
+ const animationTargetEls = panel.querySelectorAll(".animation-target");
+
+ for (const animationTargetEl of animationTargetEls) {
+ const className = animationTargetEl.textContent.split(".")[1];
+
+ if (className === targetClassName) {
+ return animationTargetEl.closest(".animation-item");
+ }
+ }
+
+ return null;
+}