Bug 1416104 - Part 10: Add tests. r?gl draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Fri, 19 Jan 2018 16:40:38 +0900
changeset 722550 a1720f07daaca1831395ff0348b690f948b5c438
parent 722549 f7c1f3cf3098df74d773bc833d068b434141c8b2
child 722575 4600c58b388c9521aa24c45e6908df6d6ce5b124
push id96169
push userbmo:dakatsuka@mozilla.com
push dateFri, 19 Jan 2018 07:44:47 +0000
reviewersgl
bugs1416104
milestone59.0a1
Bug 1416104 - Part 10: Add tests. r?gl MozReview-Commit-ID: cqjceHqcYt
devtools/client/inspector/animation/components/AnimatedPropertyList.js
devtools/client/inspector/animation/components/AnimatedPropertyListContainer.js
devtools/client/inspector/animation/components/AnimationDetailContainer.js
devtools/client/inspector/animation/components/App.js
devtools/client/inspector/animation/test/browser.ini
devtools/client/inspector/animation/test/browser_animation_animated-property-list.js
devtools/client/inspector/animation/test/browser_animation_animation-detail_close-button.js
devtools/client/inspector/animation/test/browser_animation_animation-detail_title.js
devtools/client/inspector/animation/test/browser_animation_animation-detail_visibility.js
devtools/client/inspector/animation/test/head.js
--- a/devtools/client/inspector/animation/components/AnimatedPropertyList.js
+++ b/devtools/client/inspector/animation/components/AnimatedPropertyList.js
@@ -9,16 +9,17 @@ const dom = require("devtools/client/sha
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const AnimatedPropertyItem = createFactory(require("./AnimatedPropertyItem"));
 
 class AnimatedPropertyList extends PureComponent {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
+      emitEventForTest: PropTypes.func.isRequired,
       getAnimatedPropertyMap: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
@@ -30,20 +31,25 @@ class AnimatedPropertyList extends PureC
     this.updateKeyframesList(this.props.animation);
   }
 
   componentWillReceiveProps(nextProps) {
     this.updateKeyframesList(nextProps.animation);
   }
 
   async updateKeyframesList(animation) {
-    const { getAnimatedPropertyMap } = this.props;
+    const {
+      getAnimatedPropertyMap,
+      emitEventForTest,
+    } = this.props;
     const animatedPropertyMap = await getAnimatedPropertyMap(animation);
 
     this.setState({ animatedPropertyMap });
+
+    emitEventForTest("animation-keyframes-rendered");
   }
 
   render() {
     const { animatedPropertyMap } = this.state;
 
     if (!animatedPropertyMap) {
       return null;
     }
--- a/devtools/client/inspector/animation/components/AnimatedPropertyListContainer.js
+++ b/devtools/client/inspector/animation/components/AnimatedPropertyListContainer.js
@@ -10,34 +10,37 @@ const PropTypes = require("devtools/clie
 
 const AnimatedPropertyList = createFactory(require("./AnimatedPropertyList"));
 const AnimatedPropertyListHeader = createFactory(require("./AnimatedPropertyListHeader"));
 
 class AnimatedPropertyListContainer extends PureComponent {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
+      emitEventForTest: PropTypes.func.isRequired,
       getAnimatedPropertyMap: PropTypes.func.isRequired,
     };
   }
 
   render() {
     const {
       animation,
+      emitEventForTest,
       getAnimatedPropertyMap,
     } = this.props;
 
     return dom.div(
       {
         className: "animated-property-list-container"
       },
       AnimatedPropertyListHeader(),
       AnimatedPropertyList(
         {
           animation,
+          emitEventForTest,
           getAnimatedPropertyMap,
         }
       )
     );
   }
 }
 
 module.exports = AnimatedPropertyListContainer;
--- a/devtools/client/inspector/animation/components/AnimationDetailContainer.js
+++ b/devtools/client/inspector/animation/components/AnimationDetailContainer.js
@@ -12,24 +12,26 @@ const PropTypes = require("devtools/clie
 const AnimationDetailHeader = createFactory(require("./AnimationDetailHeader"));
 const AnimatedPropertyListContainer =
   createFactory(require("./AnimatedPropertyListContainer"));
 
 class AnimationDetailContainer extends PureComponent {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
+      emitEventForTest: PropTypes.func.isRequired,
       getAnimatedPropertyMap: PropTypes.func.isRequired,
       setDetailVisibility: PropTypes.func.isRequired,
     };
   }
 
   render() {
     const {
       animation,
+      emitEventForTest,
       getAnimatedPropertyMap,
       setDetailVisibility,
     } = this.props;
 
     return dom.div(
       {
         className: "animation-detail-container"
       },
@@ -41,16 +43,17 @@ class AnimationDetailContainer extends P
           }
         )
       :
         null,
       animation ?
         AnimatedPropertyListContainer(
           {
             animation,
+            emitEventForTest,
             getAnimatedPropertyMap,
           }
         )
       :
         null
     );
   }
 }
--- a/devtools/client/inspector/animation/components/App.js
+++ b/devtools/client/inspector/animation/components/App.js
@@ -57,16 +57,17 @@ class App extends PureComponent {
         id: "animation-container",
         className: detailVisibility ? "animation-detail-visible" : "",
       },
       animations.length ?
       SplitBox({
         className: "animation-container-splitter",
         endPanel: AnimationDetailContainer(
           {
+            emitEventForTest,
             getAnimatedPropertyMap,
             setDetailVisibility,
           }
         ),
         endPanelControl: true,
         initialHeight: "50%",
         splitterSize: 1,
         startPanel: AnimationListContainer(
--- a/devtools/client/inspector/animation/test/browser.ini
+++ b/devtools/client/inspector/animation/test/browser.ini
@@ -6,16 +6,20 @@ support-files =
   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_animated-property-list.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-timeline-tick.js]
 [browser_animation_empty_on_invalid_nodes.js]
 [browser_animation_inspector_exists.js]
 [browser_animation_summary-graph_animation-name.js]
 [browser_animation_summary-graph_compositor.js]
 [browser_animation_summary-graph_computed-timing-path.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_animated-property-list.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test following animated property list test.
+// 1. Existence for animated property list.
+// 2. Number of animated property item.
+
+const TEST_CASES = [
+  {
+    target: ".animated",
+    expectedNumber: 1,
+  },
+  {
+    target: ".compositor-notall",
+    expectedNumber: 3,
+  },
+];
+
+add_task(async function () {
+  await addTab(URL_ROOT + "doc_simple_animation.html");
+  const { inspector, panel } = await openAnimationInspector();
+
+  info("Checking animated property list and items existence at initial");
+  ok(!panel.querySelector(".animated-property-list"),
+     "The animated-property-list should not be in the DOM at initial");
+
+  for (const testCase of TEST_CASES) {
+    info(`Checking animated-property-list and items existence at ${ testCase.target }`);
+    const animatedNode = await getNodeFront(testCase.target, inspector);
+    await selectNodeAndWaitForAnimations(animatedNode, inspector);
+    ok(panel.querySelector(".animated-property-list"),
+       `The animated-property-list should be in the DOM at ${ testCase.target }`);
+    const itemEls =
+      panel.querySelectorAll(".animated-property-list .animated-property-item");
+    is(itemEls.length, testCase.expectedNumber,
+       `The number of animated-property-list should be ${ testCase.expectedNumber } `
+       + `at ${ testCase.target }`);
+
+    if (itemEls.length < 2) {
+      continue;
+    }
+
+    info(`Checking the background color for `
+         + `the animated property item at ${ testCase.target }`);
+    const evenColor = panel.ownerGlobal.getComputedStyle(itemEls[0]).backgroundColor;
+    const oddColor = panel.ownerGlobal.getComputedStyle(itemEls[1]).backgroundColor;
+    isnot(evenColor, oddColor,
+          "Background color of an even animated property item "
+          + "should be different from odd");
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_animation-detail_close-button.js
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that whether close button in header of animation detail works.
+
+add_task(async function () {
+  await addTab(URL_ROOT + "doc_multi_timings.html");
+  const { animationInspector, panel } = await openAnimationInspector();
+
+  info("Checking close button in header of animation detail");
+  await clickOnAnimation(animationInspector, panel, 0);
+  const detailEl = panel.querySelector("#animation-container .controlled");
+  const win = panel.ownerGlobal;
+  isnot(win.getComputedStyle(detailEl).display, "none",
+    "detailEl should be visibled before clicking close button");
+  clickOnDetailCloseButton(panel);
+  is(win.getComputedStyle(detailEl).display, "none",
+    "detailEl should be unvisibled after clicking close button");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_animation-detail_title.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that whether title in header of animations detail.
+
+const TEST_CASES = [
+  {
+    target: ".cssanimation-normal",
+    expectedTitle: "cssanimation - CSS Animation",
+  },
+  {
+    target: ".delay-positive",
+    expectedTitle: "test-delay-animation - Script Animation",
+  },
+  {
+    target: ".easing-step",
+    expectedTitle: "Script Animation",
+  },
+];
+
+add_task(async function () {
+  await addTab(URL_ROOT + "doc_multi_timings.html");
+  const { inspector, panel } = await openAnimationInspector();
+
+  info("Checking title in each header of animation detail");
+
+  for (const testCase of TEST_CASES) {
+    info(`Checking title at ${ testCase.target }`);
+    const animatedNode = await getNodeFront(testCase.target, inspector);
+    await selectNodeAndWaitForAnimations(animatedNode, inspector);
+    const titleEl = panel.querySelector(".animation-detail-title");
+    is(titleEl.textContent, testCase.expectedTitle,
+       `Title of "${ testCase.target }" should be "${ testCase.expectedTitle }"`);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_animation-detail_visibility.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that whether animations detail could be displayed if there is selected animation.
+
+add_task(async function () {
+  await addTab(URL_ROOT + "doc_multi_timings.html");
+  const { animationInspector, inspector, panel } = await openAnimationInspector();
+
+  info("Checking animation detail visibility if animation was unselected");
+  const detailEl = panel.querySelector("#animation-container .controlled");
+  ok(detailEl, "The detail pane should be in the DOM");
+  const win = panel.ownerGlobal;
+  is(win.getComputedStyle(detailEl).display, "none", "detailEl should be unvisibled");
+
+  info("Checking animation detail visibility if animation was selected by click");
+  await clickOnAnimation(animationInspector, panel, 0);
+  isnot(win.getComputedStyle(detailEl).display, "none", "detailEl should be visibled");
+
+  info("Checking animation detail visibility when choose node which has animations");
+  const htmlNode = await getNodeFront("html", inspector);
+  await selectNodeAndWaitForAnimations(htmlNode, inspector);
+  is(win.getComputedStyle(detailEl).display, "none",
+     "detailEl should be unvisibled after choose html node");
+
+  info("Checking animation detail visibility when choose node which has an animation");
+  const animatedNode = await getNodeFront(".cssanimation-normal", inspector);
+  await selectNodeAndWaitForAnimations(animatedNode, inspector);
+  isnot(win.getComputedStyle(detailEl).display, "none",
+        "detailEl should be visibled after choose .cssanimation-normal node");
+});
--- a/devtools/client/inspector/animation/test/head.js
+++ b/devtools/client/inspector/animation/test/head.js
@@ -65,32 +65,70 @@ const enableAnimationFeatures = function
       ["layout.css.frames-timing.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
+ * @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
  */
 const _addTab = addTab;
 addTab = async function (url) {
   await enableAnimationFeatures();
   const tab = await _addTab(url);
   const 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;
 };
 
 /**
+ * Click on an animation in the timeline to select it.
+ *
+ * @param {AnimationInspector} animationInspector.
+ * @param {AnimationsPanel} panel
+ *        The panel instance.
+ * @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];
+  // Scroll to show the timeBlock since the element may be out of displayed area.
+  summaryGraphEl.scrollIntoView(false);
+  const bounds = summaryGraphEl.getBoundingClientRect();
+  const x = bounds.width / 2;
+  const y = bounds.height / 2;
+  EventUtils.synthesizeMouse(summaryGraphEl, x, y, {}, summaryGraphEl.ownerGlobal);
+
+  await waitForAnimationDetail(animationInspector);
+};
+
+/**
+ * Click on close button for animation detail pane.
+ *
+ * @param {AnimationsPanel} panel
+ *        The panel instance.
+ */
+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);
+};
+
+/**
  * 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
@@ -127,20 +165,32 @@ const setSidebarWidth = async function (
  * Wait for rendering.
  *
  * @param {AnimationInspector} animationInspector
  */
 const waitForRendering = async function (animationInspector) {
   await Promise.all([
     waitForAllAnimationTargets(animationInspector),
     waitForAllSummaryGraph(animationInspector),
+    waitForAnimationDetail(animationInspector),
   ]);
 };
 
 /**
+ * Wait for rendering of animation keyframes.
+ *
+ * @param {AnimationInspector} inspector
+ */
+const waitForAnimationDetail = async function (animationInspector) {
+  if (animationInspector.animations.length === 1) {
+    await animationInspector.once("animation-keyframes-rendered");
+  }
+};
+
+/**
  * Wait for all AnimationTarget components to be fully loaded
  * (fetched their related actor and rendered).
  *
  * @param {AnimationInspector} animationInspector
  */
 const waitForAllAnimationTargets = async function (animationInspector) {
   for (let i = 0; i < animationInspector.animations.length; i++) {
     await animationInspector.once("animation-target-rendered");