Bug 1354211 - Add element.findClosest for finding ancestor by CSS selector. r?automatedtester
Introduces a new function, element.findClosest, that finds the
closest parent node by a CSS selector expression. This is useful to
find arbitrary elements in the tree above us and is quite possibly
reusable for element.getContainer.
MozReview-Commit-ID: 8rBEepmDdPm
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -651,16 +651,40 @@ function findElements(strategy, selector
return [];
default:
throw new InvalidSelectorError(`No such strategy: ${strategy}`);
}
}
/**
+ * Finds the closest parent node of <var>startNode</var> by CSS a
+ * <var>selector</var> expression.
+ *
+ * @param {Node} startNode
+ * Cyce through <var>startNode</var>'s parent nodes in tree-order
+ * and return the first match to <var>selector</var>.
+ * @param {string} selector
+ * CSS selector expression.
+ *
+ * @return {Node=}
+ * First match to <var>selector</var>, or null if no match was found.
+ */
+element.findClosest = function(startNode, selector) {
+ let node = startNode;
+ while (node.parentNode && node.parentNode.nodeType == ELEMENT_NODE) {
+ node = node.parentNode;
+ if (node.matches(selector)) {
+ return node;
+ }
+ }
+ return null;
+};
+
+/**
* Determines if <var>obj<var> is an HTML or JS collection.
*
* @param {*} seq
* Type to determine.
*
* @return {boolean}
* True if <var>seq</va> is collection.
*/
--- a/testing/marionette/test_element.js
+++ b/testing/marionette/test_element.js
@@ -26,16 +26,23 @@ class Element {
for (let attr in attrs) {
this[attr] = attrs[attr];
}
}
get nodeType() { return 1; }
get ELEMENT_NODE() { return 1; }
+
+ // this is a severely limited CSS selector
+ // that only supports lists of tag names
+ matches(selector) {
+ let tags = selector.split(",");
+ return tags.includes(this.localName);
+ }
}
class DOMElement extends Element {
constructor(tagName, attrs = {}) {
super(tagName, attrs);
this.namespaceURI = XHTMLNS;
@@ -89,16 +96,27 @@ class WindowProxy {
get self() { return this; }
toString() { return "[object Window]"; }
}
const domWin = new WindowProxy();
const domFrame = new class extends WindowProxy {
get parent() { return domWin; }
};
+add_test(function test_findClosest() {
+ equal(element.findClosest(domEl, "foo"), null);
+
+ let foo = new DOMElement("foo");
+ let bar = new DOMElement("bar");
+ bar.parentNode = foo;
+ equal(element.findClosest(bar, "foo"), foo);
+
+ run_next_test();
+});
+
add_test(function test_isSelected() {
let checkbox = new DOMElement("input", {type: "checkbox"});
ok(!element.isSelected(checkbox));
checkbox.checked = true;
ok(element.isSelected(checkbox));
// selected is not a property of <input type=checkbox>
checkbox.selected = true;