Bug 1286186 - Reps: Test that array indexes are sorted as numbers. r=honza
☠☠ backed out by 24530e316b12 ☠ ☠
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Wed, 20 Jul 2016 09:30:16 +0100
changeset 345892 b9c789ba01a0cd5c0ddf30b26c283ce41bcf2610
parent 345891 a6c528c3fe72fcd5d2959c08d5163a1e48276433
child 345893 63a62a6c3cddfefae733a428c24def6063d320d1
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonza
bugs1286186
milestone50.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 1286186 - Reps: Test that array indexes are sorted as numbers. r=honza
devtools/client/dom/test/browser.ini
devtools/client/dom/test/browser_dom_array.js
devtools/client/dom/test/head.js
devtools/client/dom/test/page_array.html
devtools/client/shared/components/test/mochitest/.eslintrc
devtools/client/shared/components/test/mochitest/head.js
devtools/client/shared/components/test/mochitest/test_reps_array.html
devtools/client/shared/components/tree/label-cell.js
--- a/devtools/client/dom/test/browser.ini
+++ b/devtools/client/dom/test/browser.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
+  page_array.html
   page_basic.html
   !/devtools/client/framework/test/shared-head.js
 
+[browser_dom_array.js]
 [browser_dom_basic.js]
 [browser_dom_refresh.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/dom/test/browser_dom_array.js
@@ -0,0 +1,40 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PAGE_URL = URL_ROOT + "page_array.html";
+const TEST_ARRAY = [
+  "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+  "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
+];
+
+/**
+ * Basic test that checks content of the DOM panel.
+ */
+add_task(function* () {
+  info("Test DOM Panel Array Expansion started");
+
+  let { panel } = yield addTestTab(TEST_PAGE_URL);
+
+  // Expand specified row and wait till children are displayed.
+  yield expandRow(panel, "_a");
+
+  // Verify that children is displayed now.
+  let childRows = getAllRowsForLabel(panel, "_a");
+
+  let item = childRows.pop();
+  is(item.name, "length", "length property is correct");
+  is(item.value, 26, "length property value is 26");
+
+  let i = 0;
+  for (let name in childRows) {
+    let row = childRows[name];
+
+    is(name, i++, `index ${name} is correct and sorted into the correct position`);
+    ok(typeof row.name === "number", "array index is displayed as a number");
+    is(TEST_ARRAY[name], row.value, `value for array[${name}] is ${row.value}`);
+  }
+});
--- a/devtools/client/dom/test/head.js
+++ b/devtools/client/dom/test/head.js
@@ -93,16 +93,84 @@ function synthesizeMouseClickSoon(panel,
 function getRowByLabel(panel, text) {
   let doc = panel.panelWin.document;
   let labels = [...doc.querySelectorAll(".treeLabel")];
   let label = labels.find(node => node.textContent == text);
   return label ? label.closest(".treeRow") : null;
 }
 
 /**
+ * Returns the children (tree row text) of the specified object name as an
+ * array.
+ */
+function getAllRowsForLabel(panel, text) {
+  let rootObjectLevel;
+  let node;
+  let result = [];
+  let doc = panel.panelWin.document;
+  let nodes = [...doc.querySelectorAll(".treeLabel")];
+
+  // Find the label (object name) for which we want the children. We remove
+  // nodes from the start of the array until we reach the property. The children
+  // are then at the start of the array.
+  while (true) {
+    node = nodes.shift();
+
+    if (!node || node.textContent === text) {
+      rootObjectLevel = node.getAttribute("data-level");
+      break;
+    }
+  }
+
+  // Return an empty array if the node is not found.
+  if (!node) {
+    return result;
+  }
+
+  // Now get the children.
+  for (node of nodes) {
+    let level = node.getAttribute("data-level");
+
+    if (level > rootObjectLevel) {
+      result.push({
+        name: normalizeTreeValue(node.textContent),
+        value: normalizeTreeValue(node.parentNode.nextElementSibling.textContent)
+      });
+    } else {
+      break;
+    }
+  }
+
+  return result;
+}
+
+/**
+ * Strings in the tree are in the form ""a"" and numbers in the form "1". We
+ * normalize these values by converting ""a"" to "a" and "1" to 1.
+ *
+ * @param  {String} value
+ *         The value to normalize.
+ * @return {String|Number}
+ *         The normalized value.
+ */
+function normalizeTreeValue(value) {
+  if (value === `""`) {
+    return "";
+  }
+  if (value.startsWith(`"`) && value.endsWith(`"`)) {
+    return value.substr(1, value.length - 2);
+  }
+  if (isFinite(value) && parseInt(value, 10) == value) {
+    return parseInt(value, 10);
+  }
+
+  return value;
+}
+
+/**
  * Expands elements with given label and waits till
  * children are received from the backend.
  */
 function expandRow(panel, labelText) {
   let row = getRowByLabel(panel, labelText);
   return synthesizeMouseClickSoon(panel, row).then(() => {
     // Wait till children (properties) are fetched
     // from the backend.
new file mode 100644
--- /dev/null
+++ b/devtools/client/dom/test/page_array.html
@@ -0,0 +1,19 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>DOM Panel Array Expansion Test Page</title>
+  </head>
+  <body>
+  <h2>DOM Panel Array Expansion Test Page</h2>
+  <script type="text/javascript">
+    "use strict";
+    window._a = [
+      "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+      "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
+    ];
+  </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/test/mochitest/.eslintrc
@@ -0,0 +1,4 @@
+{
+  // Extend from the shared list of defined globals for mochitests.
+  "extends": "../../../../../.eslintrc.mochitests"
+}
--- a/devtools/client/shared/components/test/mochitest/head.js
+++ b/devtools/client/shared/components/test/mochitest/head.js
@@ -1,10 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { Assert } = require("resource://testing-common/Assert.jsm");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
--- a/devtools/client/shared/components/test/mochitest/test_reps_array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_array.html
@@ -9,16 +9,19 @@ Test ArrayRep rep
   <title>Rep test - ArrayRep</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
+"use strict";
+/* import-globals-from head.js */
+
 window.onload = Task.async(function* () {
   let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
   let { ArrayRep } = browserRequire("devtools/client/shared/components/reps/array");
 
   let componentUnderTest = ArrayRep;
   const maxLength = {
     short: 3,
     long: 300
@@ -30,27 +33,31 @@ window.onload = Task.async(function* () 
     // Test property iterator
     yield testMaxProps();
     yield testMoreThanShortMaxProps();
     yield testMoreThanLongMaxProps();
     yield testRecursiveArray();
 
     // Test that properties are rendered as expected by ItemRep
     yield testNested();
-  } catch(e) {
+
+    yield testArray();
+  } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
     // Test that correct rep is chosen
     const stub = [];
     const renderedRep = shallowRenderComponent(Rep, { object: stub });
-    is(renderedRep.type, ArrayRep.rep, `Rep correctly selects ${ArrayRep.rep.displayName}`);
+    is(renderedRep.type, ArrayRep.rep,
+       `Rep correctly selects ${ArrayRep.rep.displayName}`);
+
 
     // Test rendering
     const defaultOutput = `[]`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
@@ -205,13 +212,46 @@ window.onload = Task.async(function* () 
       {
         mode: "long",
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, "testNested", componentUnderTest, stub);
   }
+
+  function testArray() {
+    let stub = [
+      "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+      "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
+    ];
+
+    const defaultOutput = `["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",` +
+                          ` "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",` +
+                          ` "u", "v", "w", "x", "y", "z"]`;
+    const shortOutput = `["a", "b", "c", more...]`;
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: shortOutput,
+      },
+      {
+        mode: "tiny",
+        expectedOutput: `[26]`,
+      },
+      {
+        mode: "short",
+        expectedOutput: shortOutput,
+      },
+      {
+        mode: "long",
+        expectedOutput: defaultOutput,
+      }
+    ];
+
+    testRepRenderModes(modeTests, "testNested", componentUnderTest, stub);
+  }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/components/tree/label-cell.js
+++ b/devtools/client/shared/components/tree/label-cell.js
@@ -37,19 +37,20 @@ define(function (require, exports, modul
       };
 
       return (
         td({
           className: "treeLabelCell",
           key: "default",
           style: rowStyle},
           span({ className: "treeIcon" }),
-          span({ className: "treeLabel " + member.type + "Label" },
-            member.name
-          )
+          span({
+            className: "treeLabel " + member.type + "Label",
+            "data-level": level
+          }, member.name)
         )
       );
     }
   });
 
   // Exports from this module
   module.exports = LabelCell;
 });