Bug 1430571 - Fire DOM change event on WebDriver:ElementClear. r?automatedtester draft
authorAndreas Tolfsen <ato@sny.no>
Wed, 24 Jan 2018 17:27:26 +0000
changeset 724226 fddb2e4afc439ba76a4c6c39fc9a403b3af0289a
parent 724080 0e62eb7804c00c0996a9bdde5350328a384fb7af
child 747106 bd294e8ecae98a12c93585d0fa57e05a198dbfec
push id96699
push userbmo:ato@sny.no
push dateWed, 24 Jan 2018 18:26:59 +0000
reviewersautomatedtester
bugs1430571
milestone60.0a1
Bug 1430571 - Fire DOM change event on WebDriver:ElementClear. r?automatedtester When clearing an element using the WebDriver Element Clear command, the DOM change event should be fired. MozReview-Commit-ID: 94iG8bsRe4S
testing/marionette/interaction.js
testing/web-platform/tests/webdriver/tests/interaction/element_clear.py
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -337,16 +337,17 @@ interaction.clearElement = function(el) 
 };
 
 function clearContentEditableElement(el) {
   if (el.innerHTML === "") {
     return;
   }
   event.focus(el);
   el.innerHTML = "";
+  event.change(el);
   event.blur(el);
 }
 
 function clearResettableElement(el) {
   if (!element.isMutableFormControl(el)) {
     throw new InvalidElementStateError(pprint`Not an editable form control: ${el}`);
   }
 
@@ -362,16 +363,17 @@ function clearResettableElement(el) {
   }
 
   if (el.validity.valid && isEmpty) {
     return;
   }
 
   event.focus(el);
   el.value = "";
+  event.change(el);
   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.
  *
--- a/testing/web-platform/tests/webdriver/tests/interaction/element_clear.py
+++ b/testing/web-platform/tests/webdriver/tests/interaction/element_clear.py
@@ -1,14 +1,28 @@
 import pytest
 
 from tests.support.asserts import assert_error, assert_success
 from tests.support.inline import inline
 
 
+def add_event_listeners(element):
+    element.session.execute_script("""
+        let [target] = arguments;
+        window.events = [];
+        for (let expected of ["focus", "blur", "change"]) {
+          target.addEventListener(expected, ({type}) => window.events.push(type));
+        }
+        """, args=(element,))
+
+
+def get_events(session):
+    return session.execute_script("return window.events")
+
+
 @pytest.fixture(scope="session")
 def text_file(tmpdir_factory):
     fh = tmpdir_factory.mktemp("tmp").join("hello.txt")
     fh.write("hello")
     return fh
 
 
 def element_clear(session, element):
@@ -79,21 +93,26 @@ def test_keyboard_interactable(session):
                           ("datetime", "2017-12-26T19:48", ""),
                           ("datetime-local", "2017-12-26T19:48", ""),
                           ("time", "19:48", ""),
                           ("month", "2017-11", ""),
                           ("week", "2017-W52", "")])
 def test_input(session, type, value, default):
     session.url = inline("<input type=%s value='%s'>" % (type, value))
     element = session.find.css("input", all=False)
+    add_event_listeners(element)
     assert element.property("value") == value
 
     response = element_clear(session, element)
     assert_success(response)
     assert element.property("value") == default
+    events = get_events(session)
+    assert "focus" in events
+    assert "change" in events
+    assert "blur" in events
 
 
 @pytest.mark.parametrize("type",
                          ["number",
                           "range",
                           "email",
                           "password",
                           "search",
@@ -139,21 +158,26 @@ def test_input_readonly(session, type):
 
     response = element_clear(session, element)
     assert_error(response, "invalid element state")
 
 
 def test_textarea(session):
     session.url = inline("<textarea>foobar</textarea>")
     element = session.find.css("textarea", all=False)
+    add_event_listeners(element)
     assert element.property("value") == "foobar"
 
     response = element_clear(session, element)
     assert_success(response)
     assert element.property("value") == ""
+    events = get_events(session)
+    assert "focus" in events
+    assert "change" in events
+    assert "blur" in events
 
 
 def test_textarea_disabled(session):
     session.url = inline("<textarea disabled></textarea>")
     element = session.find.css("textarea", all=False)
 
     response = element_clear(session, element)
     assert_error(response, "invalid element state")
@@ -226,95 +250,47 @@ def test_button_with_subtree(session):
 
     response = element_clear(session, text_field)
     assert_error(response, "element not interactable")
 
 
 def test_contenteditable(session):
     session.url = inline("<p contenteditable>foobar</p>")
     element = session.find.css("p", all=False)
+    add_event_listeners(element)
     assert element.property("innerHTML") == "foobar"
 
     response = element_clear(session, element)
     assert_success(response)
     assert element.property("innerHTML") == ""
-
-
-def test_contenteditable_focus(session):
-    session.url = inline("""
-        <p contenteditable>foobar</p>
+    assert get_events(session) == ["focus", "change", "blur"]
 
-        <script>
-        window.events = [];
-        let p = document.querySelector("p");
-        for (let ev of ["focus", "blur"]) {
-          p.addEventListener(ev, ({type}) => window.events.push(type));
-        }
-        </script>
-        """)
-    element = session.find.css("p", all=False)
-    assert element.property("innerHTML") == "foobar"
-
-    response = element_clear(session, element)
-    assert_success(response)
-    assert element.property("innerHTML") == ""
-    assert session.execute_script("return window.events") == ["focus", "blur"]
 
 
 def test_designmode(session):
     session.url = inline("foobar")
     element = session.find.css("body", all=False)
     assert element.property("innerHTML") == "foobar"
     session.execute_script("document.designMode = 'on'")
 
     response = element_clear(session, element)
     assert_success(response)
     assert element.property("innerHTML") == "<br>"
 
 
-def test_resettable_element_focus(session):
-    session.url = inline("""
-        <input value="foobar">
-
-        <script>
-        window.events = [];
-        let input = document.querySelector("input");
-        for (let ev of ["focus", "blur"]) {
-          input.addEventListener(ev, ({type}) => window.events.push(type));
-        }
-        </script>
-        """)
+def test_resettable_element_focus_when_empty(session):
+    session.url = inline("<input>")
     element = session.find.css("input", all=False)
-    assert element.property("value") == "foobar"
-
-    response = element_clear(session, element)
-    assert_success(response)
-    assert element.property("value") == ""
-    assert session.execute_script("return window.events") == ["focus", "blur"]
-
-
-def test_resettable_element_focus_when_empty(session):
-    session.url = inline("""
-        <input>
-
-        <script>
-        window.events = [];
-        let p = document.querySelector("input");
-        for (let ev of ["focus", "blur"]) {
-          p.addEventListener(ev, ({type}) => window.events.push(type));
-        }
-        </script>
-        """)
-    element = session.find.css("input", all=False)
+    add_event_listeners(element)
     assert element.property("value") == ""
 
     response = element_clear(session, element)
     assert_success(response)
     assert element.property("value") == ""
-    assert session.execute_script("return window.events") == []
+    assert get_events(session) == []
 
 
 @pytest.mark.parametrize("type,invalid_value",
                          [("number", "foo"),
                           ("range", "foo"),
                           ("email", "foo"),
                           ("url", "foo"),
                           ("color", "foo"),