Bug 1430575 - Add validity state check for WebDriver:ElementClear. r?automatedtester draft
authorAndreas Tolfsen <ato@sny.no>
Mon, 15 Jan 2018 17:17:11 +0000
changeset 721572 159d23009bb3d0c20166d84ea73cbe12e17844e7
parent 721571 5dd13cfe669f632d556f267543695af019b92442
child 746369 df8e49be7589e8b3b16bb7b382f3fb1cb5b29026
push id95875
push userbmo:ato@sny.no
push dateWed, 17 Jan 2018 13:45:56 +0000
reviewersautomatedtester
bugs1430575
milestone59.0a1
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):