Bug 1430575 - Adjust editable definition to match WebDriver. r=automatedtester
authorAndreas Tolfsen <ato@sny.no>
Mon, 15 Jan 2018 17:14:37 +0000
changeset 399647 2f1762f3233a161c74478174322f7fbb40367b08
parent 399646 bc54bd9b08c8dce6119d90bf437b9167d763e3d9
child 399648 7220cf5de9a8394c8e95a1afe574f8dcc156f2de
push id58219
push useratolfsen@mozilla.com
push dateWed, 17 Jan 2018 14:49:07 +0000
treeherderautoland@949cf88764e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester
bugs1430575
milestone59.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 1430575 - Adjust editable definition to match WebDriver. r=automatedtester Introduces a new function, isMutableFormControl, to the element module in Marionette that tests if an element is a form control that can be edited by the user. This replaces the proprietary UNEDITABLE_INPUTS set used previously. An editable element is, according to the WebDriver standard, an element which belongs to the two subcategories of editable elements. This patch implements the first category of the mutable form controls. MozReview-Commit-ID: Aix19mq3lcb
testing/marionette/element.js
testing/marionette/test_element.js
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -29,25 +29,16 @@ this.EXPORTED_SYMBOLS = [
 
 const {
   FIRST_ORDERED_NODE_TYPE,
   ORDERED_NODE_ITERATOR_TYPE,
 } = Ci.nsIDOMXPathResult;
 const ELEMENT_NODE = 1;
 const DOCUMENT_NODE = 9;
 
-const UNEDITABLE_INPUTS = new Set([
-  "checkbox",
-  "radio",
-  "hidden",
-  "submit",
-  "button",
-  "image",
-]);
-
 const XBLNS = "http://www.mozilla.org/xbl";
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 /** XUL elements that support checked property. */
 const XUL_CHECKED_ELS = new Set([
   "button",
   "checkbox",
   "listitem",
@@ -839,16 +830,70 @@ element.isDisabled = function(el) {
       return el.disabled;
 
     default:
       return false;
   }
 };
 
 /**
+ * Denotes elements that can be used for typing and clearing.
+ *
+ * Elements that are considered WebDriver-editable are non-readonly
+ * and non-disabled <code>&lt;input&gt;</code> elements in the Text,
+ * Search, URL, Telephone, Email, Password, Date, Month, Date and
+ * Time Local, Number, Range, Color, and File Upload states, and
+ * <code>&lt;textarea&gt;</code> elements.
+ *
+ * @param {Element} el
+ *     Element to test.
+ *
+ * @return {boolean}
+ *     True if editable, false otherwise.
+ */
+element.isMutableFormControl = function(el) {
+  if (!element.isDOMElement(el)) {
+    return false;
+  }
+  if (element.isReadOnly(el) || element.isDisabled(el)) {
+    return false;
+  }
+
+  if (el.localName == "textarea") {
+    return true;
+  }
+
+  if (el.localName != "input") {
+    return false;
+  }
+
+  switch (el.type) {
+    case "color":
+    case "date":
+    case "datetime-local":
+    case "email":
+    case "file":
+    case "month":
+    case "number":
+    case "password":
+    case "range":
+    case "search":
+    case "tel":
+    case "text":
+    case "time":
+    case "url":
+    case "week":
+      return true;
+
+    default:
+      return false;
+  }
+};
+
+/**
  * An editing host is a node that is either an HTML element with a
  * <code>contenteditable</code> attribute, or the HTML element child
  * of a document whose <code>designMode</code> is enabled.
  *
  * @param {Element} el
  *     Element to determine if is an editing host.
  *
  * @return {boolean}
@@ -887,19 +932,17 @@ element.isEditable = function(el) {
   if (!element.isDOMElement(el)) {
     return false;
   }
 
   if (element.isReadOnly(el) || element.isDisabled(el)) {
     return false;
   }
 
-  return (el.localName == "input" && !UNEDITABLE_INPUTS.has(el.type)) ||
-      el.localName == "textarea" ||
-      element.isEditingHost(el);
+  return element.isMutableFormControl(el) || element.isEditingHost(el);
 };
 
 /**
  * This function generates a pair of coordinates relative to the viewport
  * given a target element and coordinates relative to that element's
  * top-left corner.
  *
  * @param {Node} node
--- a/testing/marionette/test_element.js
+++ b/testing/marionette/test_element.js
@@ -39,20 +39,25 @@ class Element {
     return tags.includes(this.localName);
   }
 }
 
 class DOMElement extends Element {
   constructor(tagName, attrs = {}) {
     super(tagName, attrs);
 
-    this.namespaceURI = XHTMLNS;
+    if (typeof this.namespaceURI == "undefined") {
+      this.namespaceURI = XHTMLNS;
+    }
     if (typeof this.ownerDocument == "undefined") {
       this.ownerDocument = {designMode: "off"};
     }
+    if (typeof this.type == "undefined") {
+      this.type = "text";
+    }
 
     if (this.localName == "option") {
       this.selected = false;
     }
 
     if (this.localName == "input" && ["checkbox", "radio"].includes(this.type)) {
       this.checked = false;
     }
@@ -253,16 +258,50 @@ add_test(function test_isEditable() {
 
   ok(element.isEditable(new DOMElement("textarea")));
   ok(element.isEditable(new DOMElement("p", {ownerDocument: {designMode: "on"}})));
   ok(element.isEditable(new DOMElement("p", {isContentEditable: true})));
 
   run_next_test();
 });
 
+add_test(function test_isMutableFormControlElement() {
+  ok(!element.isMutableFormControl(null));
+  ok(!element.isMutableFormControl(new DOMElement("textarea", {readOnly: true})));
+  ok(!element.isMutableFormControl(new DOMElement("textarea", {disabled: true})));
+
+  const mutableStates = new Set([
+    "color",
+    "date",
+    "datetime-local",
+    "email",
+    "file",
+    "month",
+    "number",
+    "password",
+    "range",
+    "search",
+    "tel",
+    "text",
+    "url",
+    "week",
+  ]);
+  for (let type of mutableStates) {
+    ok(element.isMutableFormControl(new DOMElement("input", {type})));
+  }
+  ok(element.isMutableFormControl(new DOMElement("textarea")));
+
+  ok(!element.isMutableFormControl(new DOMElement("input", {type: "hidden"})));
+  ok(!element.isMutableFormControl(new DOMElement("p")));
+  ok(!element.isMutableFormControl(new DOMElement("p", {isContentEditable: true})));
+  ok(!element.isMutableFormControl(new DOMElement("p", {ownerDocument: {designMode: "on"}})));
+
+  run_next_test();
+});
+
 add_test(function test_coordinates() {
   let p = element.coordinates(domEl);
   ok(p.hasOwnProperty("x"));
   ok(p.hasOwnProperty("y"));
   equal("number", typeof p.x);
   equal("number", typeof p.y);
 
   deepEqual({x: 50, y: 50}, element.coordinates(domEl));