Bug 1222737 - Tests for the computed-view copy feature; r=pbro
authorPatrick Brosset <pbrosset@mozilla.com>
Thu, 11 May 2017 15:12:27 +0200
changeset 586614 09e9ea98152d33e7fbd189f2c5be5857c8e1ebe7
parent 586613 12d05f45e5bd47663d7c1f9923703c7e57bc0c21
child 586615 3c2d9164782ae36180f607c484a036395616adfa
push id61473
push userwcosta@mozilla.com
push dateTue, 30 May 2017 20:31:31 +0000
reviewerspbro
bugs1222737
milestone55.0a1
Bug 1222737 - Tests for the computed-view copy feature; r=pbro MozReview-Commit-ID: KR1H9tn0u7r
devtools/client/inspector/computed/computed.js
devtools/client/inspector/computed/test/browser.ini
devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles-01.js
devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles-02.js
devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js
devtools/client/inspector/computed/test/head.js
devtools/client/inspector/shared/test/head.js
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -325,17 +325,18 @@ CssComputedView.prototype = {
     let classes = node.classList;
 
     // Check if the node isn't a selector first since this doesn't require
     // walking the DOM
     if (classes.contains("matched") ||
         classes.contains("bestmatch") ||
         classes.contains("parentmatch")) {
       let selectorText = "";
-      for (let child of node.childNodes) {
+
+      for (let child of node.childNodes[0].childNodes) {
         if (child.nodeType === node.TEXT_NODE) {
           selectorText += child.textContent;
         }
       }
       return {
         type: VIEW_NODE_SELECTOR_TYPE,
         value: selectorText.trim()
       };
@@ -363,25 +364,25 @@ CssComputedView.prototype = {
     let value, type;
 
     // Get the property and value for a node that's a property name or value
     let isHref = classes.contains("theme-link") && !classes.contains("link");
     if (propertyView && (classes.contains("property-name") ||
                          classes.contains("property-value") ||
                          isHref)) {
       value = {
-        property: parent.querySelector(".property-name").textContent,
+        property: parent.querySelector(".property-name").firstChild.textContent,
         value: parent.querySelector(".property-value").textContent
       };
     }
     if (propertyContent && (classes.contains("other-property-value") ||
                             isHref)) {
       let view = propertyContent.previousSibling;
       value = {
-        property: view.querySelector(".property-name").textContent,
+        property: view.querySelector(".property-name").firstChild.textContent,
         value: node.textContent
       };
     }
 
     // Get the type
     if (classes.contains("property-name")) {
       type = VIEW_NODE_PROPERTY_TYPE;
     } else if (classes.contains("property-value") ||
@@ -1145,17 +1146,17 @@ PropertyView.prototype = {
       shortcuts.on("Return", () => selector.openStyleEditor());
 
       let status = createChild(p, "span", {
         dir: "ltr",
         class: "rule-text theme-fg-color3 " + selector.statusClass,
         title: selector.statusText
       });
 
-      let keyDiv = createChild(status, "div", {
+      createChild(status, "div", {
         class: "fix-get-selection",
         textContent: selector.sourceText
       });
 
       let valueDiv = createChild(status, "div", {
         class: "fix-get-selection other-property-value theme-fg-color1"
       });
       valueDiv.appendChild(selector.outputFragment);
--- a/devtools/client/inspector/computed/test/browser.ini
+++ b/devtools/client/inspector/computed/test/browser.ini
@@ -32,12 +32,15 @@ support-files =
 [browser_computed_refresh-on-style-change_01.js]
 [browser_computed_search-filter.js]
 [browser_computed_search-filter_clear.js]
 [browser_computed_search-filter_context-menu.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_computed_search-filter_escape-keypress.js]
 [browser_computed_search-filter_noproperties.js]
-[browser_computed_select-and-copy-styles.js]
+[browser_computed_select-and-copy-styles-01.js]
+subsuite = clipboard
+skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
+[browser_computed_select-and-copy-styles-02.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_computed_style-editor-link.js]
rename from devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js
rename to devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles-01.js
--- a/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js
+++ b/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles-01.js
@@ -1,18 +1,16 @@
 /* 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";
 
 // Tests that properties can be selected and copied from the computed view.
 
-const osString = Services.appinfo.OS;
-
 const TEST_URI = `
   <style type="text/css">
     span {
       font-variant-caps: small-caps;
       color: #000000;
     }
     .nomatches {
       color: #ff0000;
@@ -33,86 +31,32 @@ const TEST_URI = `
     <p>even more text</p>
   </div>
 `;
 
 add_task(function* () {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openComputedView();
   yield selectNode("span", inspector);
-  yield checkCopySelection(view);
-  yield checkSelectAll(view);
+
+  yield testCopySome(view);
+  yield testCopyAll(view);
 });
 
-function* checkCopySelection(view) {
-  info("Testing selection copy");
-
-  let contentDocument = view.styleDocument;
-  let props = contentDocument.querySelectorAll(".property-view");
-  ok(props, "captain, we have the property-view nodes");
-
-  let range = contentDocument.createRange();
-  range.setStart(props[1], 0);
-  range.setEnd(props[3], 2);
-  contentDocument.defaultView.getSelection().addRange(range);
-
-  info("Checking that cssHtmlTree.siBoundCopy() returns the correct " +
-    "clipboard value");
-
+function* testCopySome(view) {
   let expectedPattern = "font-family: helvetica,sans-serif;[\\r\\n]+" +
                         "font-size: 16px;[\\r\\n]+" +
                         "font-variant-caps: small-caps;[\\r\\n]*";
 
-  try {
-    yield waitForClipboardPromise(() => fireCopyEvent(props[0]),
-                           () => checkClipboardData(expectedPattern));
-  } catch (e) {
-    failedClipboard(expectedPattern);
-  }
+  yield copySomeTextAndCheckClipboard(view, {
+    start: {prop: 1, offset: 0},
+    end: {prop: 3, offset: 2}
+  }, expectedPattern);
 }
 
-function* checkSelectAll(view) {
-  info("Testing select-all copy");
-
-  let contentDoc = view.styleDocument;
-  let prop = contentDoc.querySelector(".property-view");
-
-  info("Checking that _onSelectAll() then copy returns the correct " +
-    "clipboard value");
-  view._contextmenu._onSelectAll();
+function* testCopyAll(view) {
   let expectedPattern = "color: rgb\\(255, 255, 0\\);[\\r\\n]+" +
                         "font-family: helvetica,sans-serif;[\\r\\n]+" +
                         "font-size: 16px;[\\r\\n]+" +
                         "font-variant-caps: small-caps;[\\r\\n]*";
 
-  try {
-    yield waitForClipboardPromise(() => fireCopyEvent(prop),
-                           () => checkClipboardData(expectedPattern));
-  } catch (e) {
-    failedClipboard(expectedPattern);
-  }
-}
-
-function checkClipboardData(expectedPattern) {
-  let actual = SpecialPowers.getClipboardData("text/unicode");
-  let expectedRegExp = new RegExp(expectedPattern, "g");
-  return expectedRegExp.test(actual);
+  yield copyAllAndCheckClipboard(view, expectedPattern);
 }
-
-function failedClipboard(expectedPattern) {
-  // Format expected text for comparison
-  let terminator = osString == "WINNT" ? "\r\n" : "\n";
-  expectedPattern = expectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
-  expectedPattern = expectedPattern.replace(/\\\(/g, "(");
-  expectedPattern = expectedPattern.replace(/\\\)/g, ")");
-
-  let actual = SpecialPowers.getClipboardData("text/unicode");
-
-  // Trim the right hand side of our strings. This is because expectedPattern
-  // accounts for windows sometimes adding a newline to our copied data.
-  expectedPattern = expectedPattern.trimRight();
-  actual = actual.trimRight();
-
-  dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
-    "results (escaped for accurate comparison):\n");
-  info("Actual: " + escape(actual));
-  info("Expected: " + escape(expectedPattern));
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles-02.js
@@ -0,0 +1,36 @@
+/* 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";
+
+// Tests that properties can be selected and copied from the computed view.
+
+const TEST_URI = `<div style="text-align:left;width:25px;">Hello world</div>`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openComputedView();
+  yield selectNode("div", inspector);
+
+  let expectedPattern = "text-align: left;[\\r\\n]+" +
+                        "width: 25px;[\\r\\n]*";
+  yield copyAllAndCheckClipboard(view, expectedPattern);
+
+  info("Testing expand then select all copy");
+
+  expectedPattern = "text-align: left;[\\r\\n]+" +
+                    "element[\\r\\n]+" +
+                    "this.style[\\r\\n]+" +
+                    "left[\\r\\n]+" +
+                    "width: 25px;[\\r\\n]+" +
+                    "element[\\r\\n]+" +
+                    "this.style[\\r\\n]+" +
+                    "25px[\\r\\n]*";
+
+  info("Expanding computed view properties");
+  yield expandComputedViewPropertyByIndex(view, 0);
+  yield expandComputedViewPropertyByIndex(view, 1);
+
+  yield copyAllAndCheckClipboard(view, expectedPattern);
+});
--- a/devtools/client/inspector/computed/test/head.js
+++ b/devtools/client/inspector/computed/test/head.js
@@ -34,17 +34,17 @@ function fireCopyEvent(element) {
  * @return an object {nameSpan, valueSpan}
  */
 function getComputedViewProperty(view, name) {
   let prop;
   for (let property of view.styleDocument.querySelectorAll(".property-view")) {
     let nameSpan = property.querySelector(".property-name");
     let valueSpan = property.querySelector(".property-value");
 
-    if (nameSpan.textContent === name) {
+    if (nameSpan.firstChild.textContent === name) {
       prop = {nameSpan: nameSpan, valueSpan: valueSpan};
       break;
     }
   }
   return prop;
 }
 
 /**
@@ -82,17 +82,17 @@ function getComputedViewPropertyView(vie
  * @return {Promise} A promise that resolves to the property matched rules
  * container
  */
 var getComputedViewMatchedRules = Task.async(function* (view, name) {
   let expander;
   let propertyContent;
   for (let property of view.styleDocument.querySelectorAll(".property-view")) {
     let nameSpan = property.querySelector(".property-name");
-    if (nameSpan.textContent === name) {
+    if (nameSpan.firstChild.textContent === name) {
       expander = property.querySelector(".expandable");
       propertyContent = property.nextSibling;
       break;
     }
   }
 
   if (!expander.hasAttribute("open")) {
     // Need to expand the property
@@ -150,8 +150,100 @@ function expandComputedViewPropertyByInd
  * @param {Number} index
  *        The index of the link to be retrieved
  * @return {DOMNode} The link at the given index, if one exists, null otherwise
  */
 function getComputedViewLinkByIndex(view, index) {
   let links = view.styleDocument.querySelectorAll(".rule-link .link");
   return links[index];
 }
+
+/**
+ * Trigger the select all action in the computed view.
+ *
+ * @param {CssComputedView} view
+ *        The instance of the computed view panel
+ */
+function selectAllText(view) {
+  info("Selecting all the text");
+  view._contextmenu._onSelectAll();
+}
+
+/**
+ * Select all the text, copy it, and check the content in the clipboard.
+ *
+ * @param {CssComputedView} view
+ *        The instance of the computed view panel
+ * @param {String} expectedPattern
+ *        A regular expression used to check the content of the clipboard
+ */
+function* copyAllAndCheckClipboard(view, expectedPattern) {
+  selectAllText(view);
+  let contentDoc = view.styleDocument;
+  let prop = contentDoc.querySelector(".property-view");
+
+  try {
+    info("Trigger a copy event and wait for the clipboard content");
+    yield waitForClipboardPromise(() => fireCopyEvent(prop),
+                                  () => checkClipboard(expectedPattern));
+  } catch (e) {
+    failClipboardCheck(expectedPattern);
+  }
+}
+
+/**
+ * Select some text, copy it, and check the content in the clipboard.
+ *
+ * @param {CssComputedView} view
+ *        The instance of the computed view panel
+ * @param {Object} positions
+ *        The start and end positions of the text to be selected. This must be an object
+ *        like this:
+ *        { start: {prop: 1, offset: 0}, end: {prop: 3, offset: 5} }
+ * @param {String} expectedPattern
+ *        A regular expression used to check the content of the clipboard
+ */
+function* copySomeTextAndCheckClipboard(view, positions, expectedPattern) {
+  info("Testing selection copy");
+
+  let contentDocument = view.styleDocument;
+  let props = contentDocument.querySelectorAll(".property-view");
+
+  info("Create the text selection range");
+  let range = contentDocument.createRange();
+  range.setStart(props[positions.start.prop], positions.start.offset);
+  range.setEnd(props[positions.end.prop], positions.end.offset);
+  contentDocument.defaultView.getSelection().addRange(range);
+
+  try {
+    info("Trigger a copy event and wait for the clipboard content");
+    yield waitForClipboardPromise(() => fireCopyEvent(props[0]),
+                                  () => checkClipboard(expectedPattern));
+  } catch (e) {
+    failClipboardCheck(expectedPattern);
+  }
+}
+
+function checkClipboard(expectedPattern) {
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+  let expectedRegExp = new RegExp(expectedPattern, "g");
+  return expectedRegExp.test(actual);
+}
+
+function failClipboardCheck(expectedPattern) {
+  // Format expected text for comparison
+  let terminator = Services.appinfo.OS == "WINNT" ? "\r\n" : "\n";
+  expectedPattern = expectedPattern.replace(/\[\\r\\n\][+*]/g, terminator);
+  expectedPattern = expectedPattern.replace(/\\\(/g, "(");
+  expectedPattern = expectedPattern.replace(/\\\)/g, ")");
+
+  let actual = SpecialPowers.getClipboardData("text/unicode");
+
+  // Trim the right hand side of our strings. This is because expectedPattern
+  // accounts for windows sometimes adding a newline to our copied data.
+  expectedPattern = expectedPattern.trimRight();
+  actual = actual.trimRight();
+
+  dump("TEST-UNEXPECTED-FAIL | Clipboard text does not match expected ... " +
+    "results (escaped for accurate comparison):\n");
+  info("Actual: " + escape(actual));
+  info("Expected: " + escape(expectedPattern));
+}
--- a/devtools/client/inspector/shared/test/head.js
+++ b/devtools/client/inspector/shared/test/head.js
@@ -198,18 +198,18 @@ var simulateColorPickerChange = Task.asy
  * @return an object {nameSpan, valueSpan}
  */
 function getComputedViewProperty(view, name) {
   let prop;
   for (let property of view.styleDocument.querySelectorAll(".property-view")) {
     let nameSpan = property.querySelector(".property-name");
     let valueSpan = property.querySelector(".property-value");
 
-    if (nameSpan.textContent === name) {
-      prop = {nameSpan: nameSpan, valueSpan: valueSpan};
+    if (nameSpan.firstChild.textContent === name) {
+      prop = {nameSpan, valueSpan};
       break;
     }
   }
   return prop;
 }
 
 /**
  * Get the text value of the property corresponding to a given name in the
@@ -217,11 +217,10 @@ function getComputedViewProperty(view, n
  *
  * @param {CssComputedView} view
  *        The instance of the computed view panel
  * @param {String} name
  *        The name of the property to retrieve
  * @return {String} The property value
  */
 function getComputedViewPropertyValue(view, name, propertyName) {
-  return getComputedViewProperty(view, name, propertyName)
-    .valueSpan.textContent;
+  return getComputedViewProperty(view, name, propertyName).valueSpan.textContent;
 }