Bug 1430575 - Add validity state check for WebDriver:ElementClear. r=automatedtester
authorAndreas Tolfsen <ato@sny.no>
Mon, 15 Jan 2018 17:17:11 +0000
changeset 453927 949cf88764e6779ea42af6498de9f8e97037a1b3
parent 453926 7220cf5de9a8394c8e95a1afe574f8dcc156f2de
child 453928 777ee4ac78e4cd6c16f916fa1f654403a2268f44
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [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 - Add validity state check for WebDriver:ElementClear. r=automatedtester This patch checks that the element satisfies its form control constraints, as well as being empty, before deciding not to clear the element. This will make it possible to clear elements that have invalid input. The "clear a resettable element" algorithm is missing a check of the <input> element's ValidityState. WebDriver:ElementClear has a subtle bug that only manifests in Gecko because Blink rejects invalid key input to validation fields such as <input type=number>, but Gecko does not. The value property of <input type=number> will not be updated unless the input is actually valid, which means the first step of the algorithm will pass irregardless of whether the user has actually modified it. MozReview-Commit-ID: C2M3Fl1iKx6
testing/marionette/interaction.js
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/webdriver/tests/interaction/element_clear.py
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -324,41 +324,56 @@ interaction.clearElement = function(el) 
   if (!element.isInView(el)) {
     element.scrollIntoView(el);
   }
   if (!element.isInView(el)) {
     throw new ElementNotInteractableError(
         pprint`Element ${el} could not be scrolled into view`);
   }
 
-  let attr;
   if (element.isEditingHost(el)) {
-    attr = "innerHTML";
+    clearContentEditableElement(el);
   } else {
-    attr = "value";
+    clearResettableElement(el);
+  }
+};
+
+function clearContentEditableElement(el) {
+  if (el.innerHTML === "") {
+    return;
+  }
+  event.focus(el);
+  el.innerHTML = "";
+  event.blur(el);
+}
+
+function clearResettableElement(el) {
+  if (!element.isMutableFormControl(el)) {
+    throw new InvalidElementStateError(pprint`Not an editable form control: ${el}`);
   }
 
+  let isEmpty;
   switch (el.type) {
     case "file":
-      if (el.files.length == 0) {
-        return;
-      }
+      isEmpty = el.files.length == 0;
       break;
 
     default:
-      if (el[attr] === "") {
-        return;
-      }
+      isEmpty = el.value === "";
       break;
   }
 
+  if (el.validity.valid && isEmpty) {
+    return;
+  }
+
   event.focus(el);
-  el[attr] = "";
+  el.value = "";
   event.blur(el);
-};
+}
 
 /**
  * Waits until the event loop has spun enough times to process the
  * DOM events generated by clicking an element, or until the document
  * is unloaded.
  *
  * @param {Element} el
  *     Element that is expected to receive the click.
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -535032,17 +535032,17 @@
    "d94cfadf60dc146f80f7c1dc9937d7841600f9ef",
    "testharness"
   ],
   "fetch/api/response/response-init-001.html": [
    "a45bf860ad19e31ff95c3add7b6f4ad7c0573254",
    "testharness"
   ],
   "fetch/api/response/response-init-002.html": [
-   "9806050696657f48e609bbc943ba15a78d6464d1",
+   "5b87fa0d008f633d73bd87ab1755eee719b104cc",
    "testharness"
   ],
   "fetch/api/response/response-static-error.html": [
    "96b1d4f58925cb9ba5a07aef28aa4b869c67f93b",
    "testharness"
   ],
   "fetch/api/response/response-static-redirect.html": [
    "e497388ce041fd300200d23c63caadb1fe53d1c1",
@@ -583356,17 +583356,17 @@
    "817011a8cdff7cfd7e445fb8ecb84e5d91f03993",
    "wdspec"
   ],
   "webdriver/tests/get_window_rect.py": [
    "c9139c16aa950c734c776887d6a762b867790812",
    "wdspec"
   ],
   "webdriver/tests/interaction/element_clear.py": [
-   "109a1b9fed21b257503321b42bd670f9c36a0bcc",
+   "222a472b70c38e9178bdb64cc13a99053169a831",
    "wdspec"
   ],
   "webdriver/tests/interaction/send_keys_content_editable.py": [
    "9c071e60e1203cf31120f20874b5f38ba41dacc3",
    "wdspec"
   ],
   "webdriver/tests/interface.html": [
    "6625887cfa7f461dc428c11861fce71c47bef57d",
--- a/testing/web-platform/tests/webdriver/tests/interaction/element_clear.py
+++ b/testing/web-platform/tests/webdriver/tests/interaction/element_clear.py
@@ -307,16 +307,58 @@ def test_resettable_element_focus_when_e
     assert element.property("value") == ""
 
     response = element_clear(session, element)
     assert_success(response)
     assert element.property("value") == ""
     assert session.execute_script("return window.events") == []
 
 
+@pytest.mark.parametrize("type,invalid_value",
+                         [("number", "foo"),
+                          ("range", "foo"),
+                          ("email", "foo"),
+                          ("url", "foo"),
+                          ("color", "foo"),
+                          ("date", "foo"),
+                          ("datetime", "foo"),
+                          ("datetime-local", "foo"),
+                          ("time", "foo"),
+                          ("month", "foo"),
+                          ("week", "foo")])
+def test_resettable_element_does_not_satisfy_validation_constraints(session, type, invalid_value):
+    """
+    Some UAs allow invalid input to certain types of constrained
+    form controls.  For example, Gecko allows non-valid characters
+    to be typed into <input type=number> but Chrome does not.
+    Since we want to test that Element Clear works for clearing the
+    invalid characters in these UAs, it is fine to skip this test
+    where UAs do not allow the element to not satisfy its constraints.
+    """
+    session.url = inline("<input type=%s>" % type)
+    element = session.find.css("input", all=False)
+
+    def is_valid(element):
+        return session.execute_script("""
+            let [input] = arguments;
+            return input.validity.valid;
+            """, args=(element,))
+
+    # value property does not get updated if the input is invalid
+    element.send_keys(invalid_value)
+
+    # UA does not allow invalid input for this form control type
+    if is_valid(element):
+        return
+
+    response = element_clear(session, element)
+    assert_success(response)
+    assert is_valid(element)
+
+
 @pytest.mark.parametrize("type",
                          ["checkbox",
                           "radio",
                           "hidden",
                           "submit",
                           "button",
                           "image"])
 def test_non_editable_inputs(session, type):