Bug 1283999 - Fix lookup of hyperlinks in XHTML documents; r?automatedtester draft
authorAndreas Tolfsen <ato@mozilla.com>
Sat, 02 Jul 2016 21:38:48 +0100
changeset 383348 655926aecb7350bcd6763420bd77a01ce514ca51
parent 383347 f9ebc8f6a99a824a1ba9517a1d3d818cd8f6b5d6
child 383349 5c7ff5d4c1a57d72f931de08b47676e156403fa0
push id21992
push userbmo:ato@mozilla.com
push dateSat, 02 Jul 2016 21:01:45 +0000
reviewersautomatedtester
bugs1283999
milestone50.0a1
Bug 1283999 - Fix lookup of hyperlinks in XHTML documents; r?automatedtester Lower-case "a" matches hyperlinks in XHTML documents as well as HTML documents. Upper-case "A" only matches HTML documents. The patch also refactors link text- and partial link text lookup into distinct functions, so that there is no more worry about variable scoping in match blocks. MozReview-Commit-ID: FB7MAmosBoR
testing/marionette/element.js
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -334,16 +334,68 @@ function findByXPathAll(root, value, nod
   while (element) {
     elements.push(element);
     element = values.iterateNext();
   }
   return elements;
 }
 
 /**
+ * Find all hyperlinks dscendant of |node| which link text is |s|.
+ *
+ * @param {DOMElement} node
+ *     Where in the DOM hierarchy to being searching.
+ * @param {string} s
+ *     Link text to search for.
+ *
+ * @return {Array.<DOMAnchorElement>}
+ *     Sequence of link elements which text is |s|.
+ */
+element.findByLinkText = function(node, s) {
+  return filterLinks(node, link => link.text === s);
+};
+
+/**
+ * Find all hyperlinks descendant of |node| which link text contains |s|.
+ *
+ * @param {DOMElement} node
+ *     Where in the DOM hierachy to begin searching.
+ * @param {string} s
+ *     Link text to search for.
+ *
+ * @return {Array.<DOMAnchorElement>}
+ *     Sequence of link elements which text containins |s|.
+ */
+element.findByPartialLinkText = function(node, s) {
+  return filterLinks(node, link => link.text.indexOf(s) != -1);
+};
+
+/**
+ * Filters all hyperlinks that are descendant of |node| by |predicate|.
+ *
+ * @param {DOMElement} node
+ *     Where in the DOM hierarchy to begin searching.
+ * @param {function(DOMAnchorElement): boolean} predicate
+ *     Function that determines if given link should be included in
+ *     return value or filtered away.
+ *
+ * @return {Array.<DOMAnchorElement>}
+ *     Sequence of link elements matching |predicate|.
+ */
+function filterLinks(node, predicate) {
+  let rv = [];
+  for (let link of node.getElementsByTagName("a")) {
+    if (predicate(link)) {
+      rv.push(link);
+    }
+  }
+  return rv;
+}
+
+/**
  * Finds a single element.
  *
  * @param {element.Strategy} using
  *     Selector strategy to use.
  * @param {string} value
  *     Selector expression.
  * @param {DOMElement} rootNode
  *     Document root.
@@ -378,32 +430,31 @@ function findElement(using, value, rootN
 
     case element.Strategy.TagName:
       // works for all elements
       return startNode.getElementsByTagName(value)[0];
 
     case element.Strategy.XPath:
       return  findByXPath(rootNode, value, startNode);
 
-    // TODO(ato): Rewrite this, it's hairy:
     case element.Strategy.LinkText:
-    case element.Strategy.PartialLinkText:
-      let el;
-      let allLinks = startNode.getElementsByTagName("A");
-      for (let i = 0; i < allLinks.length && !el; i++) {
-        let text = allLinks[i].text;
-        if (using == element.Strategy.PartialLinkText) {
-          if (text.indexOf(value) != -1) {
-            el = allLinks[i];
-          }
-        } else if (text == value) {
-          el = allLinks[i];
+      for (let link of startNode.getElementsByTagName("a")) {
+        if (link.text === value) {
+          return link;
         }
       }
-      return el;
+      break;
+
+    case element.Strategy.PartialLinkText:
+      for (let link of startNode.getElementsByTagName("a")) {
+        if (link.text.indexOf(value) != -1) {
+          return link;
+        }
+      }
+      break;
 
     case element.Strategy.Selector:
       try {
         return startNode.querySelector(value);
       } catch (e) {
         throw new InvalidSelectorError(`${e.message}: "${value}"`);
       }
 
@@ -456,30 +507,20 @@ function findElements(using, value, root
 
     case element.Strategy.ClassName:
       return startNode.getElementsByClassName(value);
 
     case element.Strategy.TagName:
       return startNode.getElementsByTagName(value);
 
     case element.Strategy.LinkText:
+      return element.findByLinkText(startNode, value);
+
     case element.Strategy.PartialLinkText:
-      let els = [];
-      let allLinks = startNode.getElementsByTagName("A");
-      for (let i = 0; i < allLinks.length; i++) {
-        let text = allLinks[i].text;
-        if (using == element.Strategy.PartialLinkText) {
-          if (text.indexOf(value) != -1) {
-            els.push(allLinks[i]);
-          }
-        } else if (text == value) {
-          els.push(allLinks[i]);
-        }
-      }
-      return els;
+      return element.findByPartialLinkText(startNode, value);
 
     case element.Strategy.Selector:
       return startNode.querySelectorAll(value);
 
     case element.Strategy.Anon:
       return rootNode.getAnonymousNodes(startNode);
 
     case element.Strategy.AnonAttribute: