Bug 1404801 - Part 6: Add mochitests. r?gl,pbro draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Thu, 26 Oct 2017 16:59:55 +0900
changeset 686840 da4b024afe09ed81b19b8ba2703277b1a8e174ae
parent 686839 0cb66757e3c2cdde404304debe7ee90ba2754d68
child 686842 c5b9c9c90a9047d33b7d0bf6d9cc749aa2a1fbb3
push id86313
push userbmo:dakatsuka@mozilla.com
push dateThu, 26 Oct 2017 14:09:43 +0000
reviewersgl, pbro
bugs1404801
milestone58.0a1
Bug 1404801 - Part 6: Add mochitests. r?gl,pbro MozReview-Commit-ID: 6iskCXC4Fa8
devtools/client/inspector/animation/animation.js
devtools/client/inspector/animation/test/browser.ini
devtools/client/inspector/animation/test/browser_animation_animation_list_exists.js
devtools/client/inspector/animation/test/browser_animation_empty_on_invalid_nodes.js
devtools/client/inspector/animation/test/doc_simple_animation.html
devtools/client/inspector/animation/test/head.js
--- a/devtools/client/inspector/animation/animation.js
+++ b/devtools/client/inspector/animation/animation.js
@@ -59,26 +59,30 @@ class AnimationInspector {
   }
 
   async update() {
     if (!this.inspector || !this.isPanelVisible()) {
       // AnimationInspector was destroyed already or the panel is hidden.
       return;
     }
 
+    const done = this.inspector.updating("newanimationinspector");
+
     const selection = this.inspector.selection;
     const animations =
       selection.isConnected() && selection.isElementNode()
       ? await this.animationsFront.getAnimationPlayersForNode(selection.nodeFront)
       : [];
 
     if (!this.animations || !isAllTimingEffectEqual(animations, this.animations)) {
       this.inspector.store.dispatch(updateAnimations(animations));
       this.animations = animations;
     }
+
+    done();
   }
 
   isPanelVisible() {
     return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
            this.inspector.toolbox.currentToolId === "inspector" &&
            this.inspector.sidebar.getCurrentTabID() === "newanimationinspector";
   }
 
--- a/devtools/client/inspector/animation/test/browser.ini
+++ b/devtools/client/inspector/animation/test/browser.ini
@@ -1,12 +1,15 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 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_inspector_exists.js]
\ No newline at end of file
+[browser_animation_animation_list_exists.js]
+[browser_animation_empty_on_invalid_nodes.js]
+[browser_animation_inspector_exists.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_animation_list_exists.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test that whether animations ui could be displayed
+
+add_task(async function () {
+  await addTab(URL_ROOT + "doc_simple_animation.html");
+
+  const { animationInspector, inspector, panel } = await openAnimationInspector();
+
+  info("Checking animation list and items existence");
+  ok(panel.querySelector(".animation-list"),
+     "The animation-list is in the DOM");
+  is(panel.querySelectorAll(".animation-list .animation-item").length,
+     animationInspector.animations.length,
+     "The number of animations displayed matches the number of animations");
+
+  info("Checking the background color for the animation list items");
+  const animationItemEls = panel.querySelectorAll(".animation-list .animation-item");
+  const evenColor =
+    panel.ownerGlobal.getComputedStyle(animationItemEls[0]).backgroundColor;
+  const oddColor =
+    panel.ownerGlobal.getComputedStyle(animationItemEls[1]).backgroundColor;
+  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
+  // * header existance.
+  // * name label in animation element existance.
+  // * target node in animation element existance.
+  // * summary graph in animation element existance.
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/browser_animation_empty_on_invalid_nodes.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+requestLongerTimeout(2);
+
+// Test that the panel shows no animation data for invalid or not animated nodes
+
+add_task(async function () {
+  await addTab(URL_ROOT + "doc_simple_animation.html");
+
+  const { inspector, panel } = await openAnimationInspector();
+
+  info("Checking animation list and error message existence for a still node");
+  const stillNode = await getNodeFront(".still", inspector);
+  await selectNodeAndWaitForAnimations(stillNode, inspector);
+
+  ok(panel.querySelector(".animation-error-message"),
+     "Element which has animation-error-message class should exist for a still node");
+  is(panel.querySelector(".animation-error-message > p").textContent,
+     ANIMATION_L10N.getStr("panel.noAnimation"),
+     "The correct error message is displayed");
+  ok(!panel.querySelector(".animation-list"),
+     "Element which has animations class should not exist for a still node");
+
+  info("Checking animation list and error message existence for a text node");
+  const commentNode = await inspector.walker.previousSibling(stillNode);
+  await selectNodeAndWaitForAnimations(commentNode, inspector);
+
+  ok(panel.querySelector(".animation-error-message"),
+     "Element which has animation-error-message class should exist for a text node");
+  ok(!panel.querySelector(".animation-list"),
+     "Element which has animations class should not exist for a text node");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/test/doc_simple_animation.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <style>
+    .ball {
+      width: 80px;
+      height: 80px;
+      /* Add a border here to avoid layout warnings in Linux debug builds: Bug 1329784 */
+      border: 1px solid transparent;
+      border-radius: 50%;
+      background: #f06;
+
+      position: absolute;
+    }
+
+    .still {
+      top: 0;
+      left: 10px;
+    }
+
+    .animated {
+      top: 100px;
+      left: 10px;
+
+      animation: simple-animation 2s infinite alternate;
+    }
+
+    .multi {
+      top: 200px;
+      left: 10px;
+
+      animation: simple-animation 2s infinite alternate,
+                 other-animation 5s infinite alternate;
+    }
+
+    .delayed {
+      top: 300px;
+      left: 10px;
+      background: rebeccapurple;
+
+      animation: simple-animation 3s 60s 10;
+    }
+
+    .multi-finite {
+      top: 400px;
+      left: 10px;
+      background: yellow;
+
+      animation: simple-animation 3s,
+                 other-animation 4s;
+    }
+
+    .short {
+      top: 500px;
+      left: 10px;
+      background: red;
+
+      animation: simple-animation 2s normal;
+    }
+
+    .long {
+      top: 600px;
+      left: 10px;
+      background: blue;
+
+      animation: simple-animation 120s;
+    }
+
+    .negative-delay {
+      top: 700px;
+      left: 10px;
+      background: gray;
+
+      animation: simple-animation 15s -10s;
+      animation-fill-mode: forwards;
+    }
+
+    .no-compositor {
+      top: 0;
+      right: 10px;
+      background: gold;
+
+      animation: no-compositor 10s cubic-bezier(.57,-0.02,1,.31) forwards;
+    }
+
+    .compositor-all {
+      animation: compositor-all 2s infinite;
+    }
+
+    .compositor-notall {
+      animation: compositor-notall 2s infinite;
+    }
+
+    @keyframes simple-animation {
+      100% {
+        transform: translateX(300px);
+      }
+    }
+
+    @keyframes other-animation {
+      100% {
+        background: blue;
+      }
+    }
+
+    @keyframes no-compositor {
+      100% {
+        margin-right: 600px;
+      }
+    }
+
+    @keyframes compositor-all {
+      to { opacity: 0.5 }
+    }
+
+    @keyframes compositor-notall {
+      from {
+        opacity: 0;
+        width: 0px;
+        transform: translate(0px);
+      }
+      to {
+        opacity: 1;
+        width: 100px;
+        transform: translate(100px);
+      }
+    }
+  </style>
+</head>
+<body>
+  <!-- Comment node -->
+  <div class="ball still"></div>
+  <div class="ball animated"></div>
+  <div class="ball multi"></div>
+  <div class="ball delayed"></div>
+  <div class="ball multi-finite"></div>
+  <div class="ball short"></div>
+  <div class="ball long"></div>
+  <div class="ball negative-delay"></div>
+  <div class="ball no-compositor"></div>
+  <div class="ball" id="endDelayed"></div>
+  <div class="ball compositor-all"></div>
+  <div class="ball compositor-notall"></div>
+  <script>
+    /* globals KeyframeEffect, Animation */
+    "use strict";
+
+    var el = document.getElementById("endDelayed");
+    let effect = new KeyframeEffect(el, [
+      { opacity: 0, offset: 0 },
+      { opacity: 1, offset: 1 }
+    ], { duration: 1000000, endDelay: 500000, fill: "none" });
+    let animation = new Animation(effect, document.timeline);
+    animation.play();
+  </script>
+</body>
+</html>
--- a/devtools/client/inspector/animation/test/head.js
+++ b/devtools/client/inspector/animation/test/head.js
@@ -4,18 +4,23 @@
 
 "use strict";
 
 /* import-globals-from ../../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 COMMON_FRAME_SCRIPT_URL = "chrome://devtools/content/shared/frame-script-utils.js";
+const FRAME_SCRIPT_URL = CHROME_URL_ROOT + "doc_frame_script.js";
 const TAB_NAME = "newanimationinspector";
 
+const ANIMATION_L10N =
+  new LocalizationHelper("devtools/client/locales/animationinspector.properties");
+
 // Enable new animation inspector.
 Services.prefs.setBoolPref("devtools.new-animationinspector.enabled", true);
 
 // Auto clean-up when a test ends.
 // 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.new-animationinspector.enabled");
@@ -24,22 +29,78 @@ 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");
   const { animationinspector: animationInspector } = inspector;
   const panel = inspector.panelWin.document.getElementById("animation-container");
-  return { toolbox, inspector, animationInspector, panel };
+  return { animationInspector, toolbox, inspector, panel };
 };
 
 /**
  * Close the toolbox.
  *
  * @return {Promise} that resolves when the toolbox has closed.
  */
 const closeAnimationInspector = async function () {
   const target = TargetFactory.forTab(gBrowser.selectedTab);
   return gDevTools.closeToolbox(target);
 };
+
+/**
+ * Some animation features are not enabled by default in release/beta channels
+ * yet including:
+ *   * parts of the Web Animations API (Bug 1264101), and
+ *   * the frames() timing function (Bug 1379582).
+ */
+const enableAnimationFeatures = function () {
+  return new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set": [
+      ["dom.animations-api.core.enabled", true],
+      ["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
+ * @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;
+};
+
+/**
+ * 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.
+ */
+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;
+};