Bug 1282721 - Add unit tests for shapes highlighter translate/scale for all shapes. r=pbro
authorMike Park <mikeparkms@gmail.com>
Thu, 12 Oct 2017 15:54:45 -0400
changeset 443380 209581894f955f0daa5857247def66daa0a6b819
parent 443379 40e53d1e45443af27e1b73c6e2646b25aa976796
child 443381 90453cc7c3b4021b28e3a03eb4c540f372c5974d
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1282721
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1282721 - Add unit tests for shapes highlighter translate/scale for all shapes. r=pbro MozReview-Commit-ID: JJg0zFtvi9s
devtools/client/inspector/rules/test/browser.ini
devtools/client/inspector/rules/test/browser_rules_shapes-toggle_07.js
devtools/client/inspector/test/browser.ini
devtools/client/inspector/test/browser_inspector_highlighter-cssshape_06.js
devtools/client/inspector/test/doc_inspector_highlighter_cssshapes.html
devtools/server/tests/unit/test_shapes_highlighter_helpers.js
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -237,16 +237,17 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_rules_selector-highlighter_05.js]
 [browser_rules_selector_highlight.js]
 [browser_rules_shapes-toggle_01.js]
 [browser_rules_shapes-toggle_02.js]
 [browser_rules_shapes-toggle_03.js]
 [browser_rules_shapes-toggle_04.js]
 [browser_rules_shapes-toggle_05.js]
 [browser_rules_shapes-toggle_06.js]
+[browser_rules_shapes-toggle_07.js]
 [browser_rules_shorthand-overridden-lists.js]
 [browser_rules_strict-search-filter-computed-list_01.js]
 [browser_rules_strict-search-filter_01.js]
 [browser_rules_strict-search-filter_02.js]
 [browser_rules_strict-search-filter_03.js]
 [browser_rules_style-editor-link.js]
 skip-if = true # Bug 1309759
 [browser_rules_url-click-opens-new-tab.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_07.js
@@ -0,0 +1,79 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling transform mode of the shapes highlighter
+
+const TEST_URI = `
+  <style type='text/css'>
+    #shape {
+      width: 800px;
+      height: 800px;
+      clip-path: circle(25%);
+    }
+  </style>
+  <div id="shape"></div>
+`;
+
+const HIGHLIGHTER_TYPE = "ShapesHighlighter";
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  let highlighters = view.highlighters;
+
+  yield selectNode("#shape", inspector);
+  let container = getRuleViewProperty(view, "#shape", "clip-path").valueSpan;
+  let shapesToggle = container.querySelector(".ruleview-shape");
+
+  info("Checking the initial state of the CSS shape toggle in the rule-view.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS shapes highlighter exists in the rule-view.");
+  ok(!highlighters.shapesHighlighterShown, "No CSS shapes highlighter is shown.");
+
+  info("Toggling ON the CSS shapes highlighter with transform mode on.");
+  let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
+  EventUtils.sendMouseEvent({type: "click", metaKey: true, ctrlKey: true},
+    shapesToggle, view.styleWindow);
+  yield onHighlighterShown;
+
+  info("Checking the CSS shapes highlighter is created and transform mode is on");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS shapes highlighter created in the rule-view.");
+  ok(highlighters.shapesHighlighterShown, "CSS shapes highlighter is shown.");
+  ok(highlighters.state.shapes.options.transformMode, "Transform mode is on.");
+
+  info("Toggling OFF the CSS shapes highlighter from the rule-view.");
+  let onHighlighterHidden = highlighters.once("shapes-highlighter-hidden");
+  EventUtils.sendMouseEvent({type: "click"},
+    shapesToggle, view.styleWindow);
+  yield onHighlighterHidden;
+
+  info("Checking the CSS shapes highlighter is not shown.");
+  ok(!highlighters.shapesHighlighterShown, "No CSS shapes highlighter is shown.");
+
+  info("Toggling ON the CSS shapes highlighter with transform mode off.");
+  onHighlighterShown = highlighters.once("shapes-highlighter-shown");
+  EventUtils.sendMouseEvent({type: "click"}, shapesToggle, view.styleWindow);
+  yield onHighlighterShown;
+
+  info("Checking the CSS shapes highlighter is created and transform mode is off");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS shapes highlighter created in the rule-view.");
+  ok(highlighters.shapesHighlighterShown, "CSS shapes highlighter is shown.");
+  ok(!highlighters.state.shapes.options.transformMode, "Transform mode is off.");
+
+  info("Clicking shapes toggle to turn on transform mode while highlighter is shown.");
+  onHighlighterShown = highlighters.once("shapes-highlighter-shown");
+  EventUtils.sendMouseEvent({type: "click", metaKey: true, ctrlKey: true},
+    shapesToggle, view.styleWindow);
+  yield onHighlighterShown;
+
+  info("Checking the CSS shapes highlighter is created and transform mode is on");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS shapes highlighter created in the rule-view.");
+  ok(highlighters.shapesHighlighterShown, "CSS shapes highlighter is shown.");
+  ok(highlighters.state.shapes.options.transformMode, "Transform mode is on.");
+});
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -78,16 +78,17 @@ skip-if = os == "mac" # Full keyboard na
 [browser_inspector_highlighter-comments.js]
 [browser_inspector_highlighter-cssgrid_01.js]
 [browser_inspector_highlighter-cssgrid_02.js]
 [browser_inspector_highlighter-cssshape_01.js]
 [browser_inspector_highlighter-cssshape_02.js]
 [browser_inspector_highlighter-cssshape_03.js]
 [browser_inspector_highlighter-cssshape_04.js]
 [browser_inspector_highlighter-cssshape_05.js]
+[browser_inspector_highlighter-cssshape_06.js]
 [browser_inspector_highlighter-cssshape_iframe_01.js]
 [browser_inspector_highlighter-csstransform_01.js]
 [browser_inspector_highlighter-csstransform_02.js]
 [browser_inspector_highlighter-embed.js]
 [browser_inspector_highlighter-eyedropper-clipboard.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_inspector_highlighter-eyedropper-csp.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_06.js
@@ -0,0 +1,152 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Test that shapes are updated correctly on mouse events in transform mode.
+
+const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
+const HIGHLIGHTER_TYPE = "ShapesHighlighter";
+const SHAPE_IDS = ["#polygon-transform", "#circle", "#ellipse", "#inset"];
+
+add_task(function* () {
+  let inspector = yield openInspectorForURL(TEST_URL);
+  let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
+  let {testActor} = inspector;
+
+  yield testTranslate(testActor, helper);
+  yield testScale(testActor, helper);
+
+  helper.finalize();
+});
+
+function* testTranslate(testActor, helper) {
+  for (let shape of SHAPE_IDS) {
+    info(`Displaying ${shape}`);
+    yield helper.show(shape, {mode: "cssClipPath", transformMode: true});
+    let { mouse } = helper;
+
+    let { top, left, width, height } = yield getBoundingBoxInPx(testActor, helper, shape);
+    let x = left + width / 2;
+    let y = top + height / 2;
+    let dx = width / 10;
+    let dy = height / 10;
+
+    info(`Translating ${shape}`);
+    yield mouse.down(x, y, shape);
+    yield mouse.move(x + dx, y + dy, shape);
+    yield mouse.up(x + dx, y + dy, shape);
+    yield testActor.reflow();
+
+    let newBB = yield getBoundingBoxInPx(testActor, helper);
+    isnot(newBB.top, top, `${shape} translated on y axis`);
+    isnot(newBB.left, left, `${shape} translated on x axis`);
+
+    info(`Translating ${shape} back`);
+    yield mouse.down(x + dx, y + dy, shape);
+    yield mouse.move(x, y, shape);
+    yield mouse.up(x, y, shape);
+    yield testActor.reflow();
+
+    newBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    is(newBB.top, top, `${shape} translated back on y axis`);
+    is(newBB.left, left, `${shape} translated back on x axis`);
+  }
+}
+
+function* testScale(testActor, helper) {
+  for (let shape of SHAPE_IDS) {
+    info(`Displaying ${shape}`);
+    yield helper.show(shape, {mode: "cssClipPath", transformMode: true});
+    let { mouse } = helper;
+
+    let { top, left, width, height } = yield getBoundingBoxInPx(testActor, helper, shape);
+
+    // if the top or left edges are not visible, move the shape so it is.
+    if (top < 0 || left < 0) {
+      let x = left + width / 2;
+      let y = top + height / 2;
+      let dx = Math.max(0, -left);
+      let dy = Math.max(0, -top);
+      yield mouse.down(x, y, shape);
+      yield mouse.move(x + dx, y + dy, shape);
+      yield mouse.up(x + dx, y + dy, shape);
+      yield testActor.reflow();
+      left += dx;
+      top += dy;
+    }
+    let dx = width / 10;
+    let dy = height / 10;
+
+    info("Scaling from nw");
+    yield mouse.down(left, top, shape);
+    yield mouse.move(left + dx, top + dy, shape);
+    yield mouse.up(left + dx, top + dy, shape);
+    yield testActor.reflow();
+
+    let nwBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    isnot(nwBB.top, top, `${shape} top moved down after nw scale`);
+    isnot(nwBB.left, left, `${shape} left moved right after nw scale`);
+    isnot(nwBB.width, width, `${shape} width reduced after nw scale`);
+    isnot(nwBB.height, height, `${shape} height reduced after nw scale`);
+
+    info("Scaling from ne");
+    yield mouse.down(nwBB.left + nwBB.width, nwBB.top, shape);
+    yield mouse.move(nwBB.left + nwBB.width - dx, nwBB.top + dy, shape);
+    yield mouse.up(nwBB.left + nwBB.width - dx, nwBB.top + dy, shape);
+    yield testActor.reflow();
+
+    let neBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    isnot(neBB.top, nwBB.top, `${shape} top moved down after ne scale`);
+    is(neBB.left, nwBB.left, `${shape} left not moved right after ne scale`);
+    isnot(neBB.width, nwBB.width, `${shape} width reduced after ne scale`);
+    isnot(neBB.height, nwBB.height, `${shape} height reduced after ne scale`);
+
+    info("Scaling from sw");
+    yield mouse.down(neBB.left, neBB.top + neBB.height, shape);
+    yield mouse.move(neBB.left + dx, neBB.top + neBB.height - dy, shape);
+    yield mouse.up(neBB.left + dx, neBB.top + neBB.height - dy, shape);
+    yield testActor.reflow();
+
+    let swBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    is(swBB.top, neBB.top, `${shape} top not moved down after sw scale`);
+    isnot(swBB.left, neBB.left, `${shape} left moved right after sw scale`);
+    isnot(swBB.width, neBB.width, `${shape} width reduced after sw scale`);
+    isnot(swBB.height, neBB.height, `${shape} height reduced after sw scale`);
+
+    info("Scaling from se");
+    yield mouse.down(swBB.left + swBB.width, swBB.top + swBB.height, shape);
+    yield mouse.move(swBB.left + swBB.width - dx, swBB.top + swBB.height - dy, shape);
+    yield mouse.up(swBB.left + swBB.width - dx, swBB.top + swBB.height - dy, shape);
+    yield testActor.reflow();
+
+    let seBB = yield getBoundingBoxInPx(testActor, helper, shape);
+    is(seBB.top, swBB.top, `${shape} top not moved down after se scale`);
+    is(seBB.left, swBB.left, `${shape} left not moved right after se scale`);
+    isnot(seBB.width, swBB.width, `${shape} width reduced after se scale`);
+    isnot(seBB.height, swBB.height, `${shape} height reduced after se scale`);
+  }
+}
+
+function* getBoundingBoxInPx(testActor, helper, shape = "#polygon") {
+  let bbTop = parseFloat(yield helper.getElementAttribute("shapes-bounding-box", "y"));
+  let bbLeft = parseFloat(yield helper.getElementAttribute("shapes-bounding-box", "x"));
+  let bbWidth = parseFloat(yield helper.getElementAttribute("shapes-bounding-box",
+    "width"));
+  let bbHeight = parseFloat(yield helper.getElementAttribute("shapes-bounding-box",
+    "height"));
+
+  let quads = yield testActor.getAllAdjustedQuads(shape);
+  let { width, height } = quads.content[0].bounds;
+  let computedStyle = yield helper.highlightedNode.getComputedStyle();
+  let paddingTop = parseFloat(computedStyle["padding-top"].value);
+  let paddingLeft = parseFloat(computedStyle["padding-left"].value);
+
+  return {
+    top: paddingTop + height * bbTop / 100,
+    left: paddingLeft + width * bbLeft / 100,
+    width: width * bbWidth / 100,
+    height: height * bbHeight / 100
+  };
+}
--- a/devtools/client/inspector/test/doc_inspector_highlighter_cssshapes.html
+++ b/devtools/client/inspector/test/doc_inspector_highlighter_cssshapes.html
@@ -56,17 +56,34 @@
                        800px 0,
                        90% 100%,
                        50% 60%,
                        10% 100%);
     stroke: red;
     stroke-width: 20px;
     fill: blue;
   }
+  #polygon-transform {
+    width: 600px;
+    height: 600px;
+    clip-path: polygon(0 0,
+                       100px 50%,
+                       200px 0,
+                       300px 50%,
+                       400px 0,
+                       500px 50%,
+                       600px 0,
+                       700px 50%,
+                       800px 0,
+                       90% 100%,
+                       50% 60%,
+                       10% 100%);
+  }
 </style>
 <div class="wrapper" id="polygon"></div>
 <div class="wrapper" id="circle"></div>
 <div class="wrapper" id="ellipse"></div>
 <div class="wrapper" id="ellipse-padding-box"></div>
 <div class="wrapper" id="inset"></div>
+<div class="wrapper" id="polygon-transform"></div>
 <svg class="svg">
   <rect id="rect" x="10" y="10" width="700" height="700"></rect>
 </svg>
--- a/devtools/server/tests/unit/test_shapes_highlighter_helpers.js
+++ b/devtools/server/tests/unit/test_shapes_highlighter_helpers.js
@@ -10,24 +10,26 @@
 const {
   splitCoords,
   coordToPercent,
   evalCalcExpression,
   shapeModeToCssPropertyName,
   getCirclePath,
   getUnit
 } = require("devtools/server/actors/highlighters/shapes");
+const { scalePoint } = require("devtools/server/actors/utils/shapes-utils");
 
 function run_test() {
   test_split_coords();
   test_coord_to_percent();
   test_eval_calc_expression();
   test_shape_mode_to_css_property_name();
   test_get_circle_path();
   test_get_unit();
+  test_scale_point();
   run_next_test();
 }
 
 function test_split_coords() {
   const tests = [{
     desc: "splitCoords for basic coordinate pair",
     expr: "30% 20%",
     expected: ["30%", "20%"]
@@ -160,8 +162,36 @@ function test_get_unit() {
     desc: "getUnit with farthest-side",
     expr: "farthest-side", expected: "px"
   }];
 
   for (let { desc, expr, expected } of tests) {
     equal(getUnit(expr), expected, desc);
   }
 }
+
+function test_scale_point() {
+  const tests = [{
+    desc: "scalePoint with 0,0",
+    x: 0, y: 0, transX: 0, transY: 0, scale: 0.9, expected: [0, 0]
+  }, {
+    desc: "scalePoint with scale factor 1",
+    x: 10, y: 10, transX: 100, transY: 100, scale: 1, expected: [10, 10]
+  }, {
+    desc: "scalePoint with scale factor 0.9, no translation",
+    x: 10, y: 20, transX: 0, transY: 0, scale: 0.9, expected: [9, 18]
+  }, {
+    desc: "scalePoint with scale factor 0.9, translation",
+    x: 10, y: 20, transX: 10, transY: 10, scale: 0.9, expected: [10, 19]
+  }, {
+    desc: "scalePoint with scale factor 2, negative translation",
+    x: 20, y: 30, transX: -10, transY: -10, scale: 2, expected: [50, 70]
+  }, {
+    desc: "scalePoint with scale factor 2, translation = coordinates",
+    x: 20, y: 30, transX: 20, transY: 30, scale: 2, expected: [20, 30]
+  }];
+
+  for (let { desc, x, y, transX, transY, scale, expected } of tests) {
+    let [newX, newY] = scalePoint(x, y, transX, transY, scale);
+    equal(newX, expected[0], desc + " x");
+    equal(newY, expected[1], desc + " y");
+  }
+}