Unreviewed. Update W3C WebDriver imported tests.
[WebKit-https.git] / WebDriverTests / imported / w3c / webdriver / tests / interaction / element_clear.py
1 # META: timeout=long
2
3 import pytest
4
5 from tests.support.asserts import (
6     assert_element_has_focus,
7     assert_error,
8     assert_success,
9 )
10 from tests.support.inline import inline
11
12
13 def add_event_listeners(element):
14     element.session.execute_script("""
15         let [target] = arguments;
16         window.events = [];
17         for (let expected of ["focus", "blur", "change"]) {
18           target.addEventListener(expected, ({type}) => window.events.push(type));
19         }
20         """, args=(element,))
21
22
23 def get_events(session):
24     return session.execute_script("return window.events")
25
26
27 @pytest.fixture(scope="session")
28 def text_file(tmpdir_factory):
29     fh = tmpdir_factory.mktemp("tmp").join("hello.txt")
30     fh.write("hello")
31     return fh
32
33
34 def element_clear(session, element):
35     return session.transport.send("POST", "/session/%s/element/%s/clear" %
36                                   (session.session_id, element.id))
37
38
39 def test_closed_context(session, create_window):
40     new_window = create_window()
41     session.window_handle = new_window
42     session.url = inline("<input>")
43     element = session.find.css("input", all=False)
44     session.close()
45
46     response = element_clear(session, element)
47     assert_error(response, "no such window")
48
49
50 def test_connected_element(session):
51     session.url = inline("<input>")
52     element = session.find.css("input", all=False)
53
54     session.url = inline("<input>")
55     response = element_clear(session, element)
56     assert_error(response, "stale element reference")
57
58
59 def test_pointer_interactable(session):
60     session.url = inline("<input style='margin-left: -1000px' value=foobar>")
61     element = session.find.css("input", all=False)
62
63     response = element_clear(session, element)
64     assert_error(response, "element not interactable")
65
66
67 def test_keyboard_interactable(session):
68     session.url = inline("""
69         <input value=foobar>
70         <div></div>
71
72         <style>
73         div {
74           position: absolute;
75           background: blue;
76           top: 0;
77         }
78         </style>
79         """)
80     element = session.find.css("input", all=False)
81     assert element.property("value") == "foobar"
82
83     response = element_clear(session, element)
84     assert_success(response)
85     assert element.property("value") == ""
86
87
88 @pytest.mark.parametrize("type,value,default",
89                          [("number", "42", ""),
90                           ("range", "42", "50"),
91                           ("email", "foo@example.com", ""),
92                           ("password", "password", ""),
93                           ("search", "search", ""),
94                           ("tel", "999", ""),
95                           ("text", "text", ""),
96                           ("url", "https://example.com/", ""),
97                           ("color", "#ff0000", "#000000"),
98                           ("date", "2017-12-26", ""),
99                           ("datetime", "2017-12-26T19:48", ""),
100                           ("datetime-local", "2017-12-26T19:48", ""),
101                           ("time", "19:48", ""),
102                           ("month", "2017-11", ""),
103                           ("week", "2017-W52", "")])
104 def test_input(session, type, value, default):
105     session.url = inline("<input type=%s value='%s'>" % (type, value))
106     element = session.find.css("input", all=False)
107     add_event_listeners(element)
108     assert element.property("value") == value
109
110     response = element_clear(session, element)
111     assert_success(response)
112     assert element.property("value") == default
113     events = get_events(session)
114     assert "focus" in events
115     assert "change" in events
116     assert "blur" in events
117     assert_element_has_focus(session.execute_script("return document.body"))
118
119
120 @pytest.mark.parametrize("type",
121                          ["number",
122                           "range",
123                           "email",
124                           "password",
125                           "search",
126                           "tel",
127                           "text",
128                           "url",
129                           "color",
130                           "date",
131                           "datetime",
132                           "datetime-local",
133                           "time",
134                           "month",
135                           "week",
136                           "file"])
137 def test_input_disabled(session, type):
138     session.url = inline("<input type=%s disabled>" % type)
139     element = session.find.css("input", all=False)
140
141     response = element_clear(session, element)
142     assert_error(response, "invalid element state")
143
144
145 @pytest.mark.parametrize("type",
146                          ["number",
147                           "range",
148                           "email",
149                           "password",
150                           "search",
151                           "tel",
152                           "text",
153                           "url",
154                           "color",
155                           "date",
156                           "datetime",
157                           "datetime-local",
158                           "time",
159                           "month",
160                           "week",
161                           "file"])
162 def test_input_readonly(session, type):
163     session.url = inline("<input type=%s readonly>" % type)
164     element = session.find.css("input", all=False)
165
166     response = element_clear(session, element)
167     assert_error(response, "invalid element state")
168
169
170 def test_textarea(session):
171     session.url = inline("<textarea>foobar</textarea>")
172     element = session.find.css("textarea", all=False)
173     add_event_listeners(element)
174     assert element.property("value") == "foobar"
175
176     response = element_clear(session, element)
177     assert_success(response)
178     assert element.property("value") == ""
179     events = get_events(session)
180     assert "focus" in events
181     assert "change" in events
182     assert "blur" in events
183
184
185 def test_textarea_disabled(session):
186     session.url = inline("<textarea disabled></textarea>")
187     element = session.find.css("textarea", all=False)
188
189     response = element_clear(session, element)
190     assert_error(response, "invalid element state")
191
192
193 def test_textarea_readonly(session):
194     session.url = inline("<textarea readonly></textarea>")
195     element = session.find.css("textarea", all=False)
196
197     response = element_clear(session, element)
198     assert_error(response, "invalid element state")
199
200
201 def test_input_file(session, text_file):
202     session.url = inline("<input type=file>")
203     element = session.find.css("input", all=False)
204     element.send_keys(str(text_file))
205
206     response = element_clear(session, element)
207     assert_success(response)
208     assert element.property("value") == ""
209
210
211 def test_input_file_multiple(session, text_file):
212     session.url = inline("<input type=file multiple>")
213     element = session.find.css("input", all=False)
214     element.send_keys(str(text_file))
215     element.send_keys(str(text_file))
216
217     response = element_clear(session, element)
218     assert_success(response)
219     assert element.property("value") == ""
220
221
222 def test_select(session):
223     session.url = inline("""
224         <select>
225           <option>foo
226         </select>
227         """)
228     select = session.find.css("select", all=False)
229     option = session.find.css("option", all=False)
230
231     response = element_clear(session, select)
232     assert_error(response, "invalid element state")
233     response = element_clear(session, option)
234     assert_error(response, "invalid element state")
235
236
237 def test_button(session):
238     session.url = inline("<button></button>")
239     button = session.find.css("button", all=False)
240
241     response = element_clear(session, button)
242     assert_error(response, "invalid element state")
243
244
245 def test_button_with_subtree(session):
246     """
247     Whilst an <input> is normally editable, the focusable area
248     where it is placed will default to the <button>.  I.e. if you
249     try to click <input> to focus it, you will hit the <button>.
250     """
251     session.url = inline("""
252         <button>
253           <input value=foobar>
254         </button>
255         """)
256     text_field = session.find.css("input", all=False)
257
258     response = element_clear(session, text_field)
259     assert_error(response, "element not interactable")
260
261
262 def test_contenteditable(session):
263     session.url = inline("<p contenteditable>foobar</p>")
264     element = session.find.css("p", all=False)
265     add_event_listeners(element)
266     assert element.property("innerHTML") == "foobar"
267
268     response = element_clear(session, element)
269     assert_success(response)
270     assert element.property("innerHTML") == ""
271     assert get_events(session) == ["focus", "change", "blur"]
272     assert_element_has_focus(session.execute_script("return document.body"))
273
274
275
276 def test_designmode(session):
277     session.url = inline("foobar")
278     element = session.find.css("body", all=False)
279     assert element.property("innerHTML") == "foobar"
280     session.execute_script("document.designMode = 'on'")
281
282     response = element_clear(session, element)
283     assert_success(response)
284     assert element.property("innerHTML") == "<br>"
285     assert_element_has_focus(session.execute_script("return document.body"))
286
287
288 def test_resettable_element_focus_when_empty(session):
289     session.url = inline("<input>")
290     element = session.find.css("input", all=False)
291     add_event_listeners(element)
292     assert element.property("value") == ""
293
294     response = element_clear(session, element)
295     assert_success(response)
296     assert element.property("value") == ""
297     assert get_events(session) == []
298
299
300 @pytest.mark.parametrize("type,invalid_value",
301                          [("number", "foo"),
302                           ("range", "foo"),
303                           ("email", "foo"),
304                           ("url", "foo"),
305                           ("color", "foo"),
306                           ("date", "foo"),
307                           ("datetime", "foo"),
308                           ("datetime-local", "foo"),
309                           ("time", "foo"),
310                           ("month", "foo"),
311                           ("week", "foo")])
312 def test_resettable_element_does_not_satisfy_validation_constraints(session, type, invalid_value):
313     """
314     Some UAs allow invalid input to certain types of constrained
315     form controls.  For example, Gecko allows non-valid characters
316     to be typed into <input type=number> but Chrome does not.
317     Since we want to test that Element Clear works for clearing the
318     invalid characters in these UAs, it is fine to skip this test
319     where UAs do not allow the element to not satisfy its constraints.
320     """
321     session.url = inline("<input type=%s>" % type)
322     element = session.find.css("input", all=False)
323
324     def is_valid(element):
325         return session.execute_script("""
326             let [input] = arguments;
327             return input.validity.valid;
328             """, args=(element,))
329
330     # value property does not get updated if the input is invalid
331     element.send_keys(invalid_value)
332
333     # UA does not allow invalid input for this form control type
334     if is_valid(element):
335         return
336
337     response = element_clear(session, element)
338     assert_success(response)
339     assert is_valid(element)
340
341
342 @pytest.mark.parametrize("type",
343                          ["checkbox",
344                           "radio",
345                           "hidden",
346                           "submit",
347                           "button",
348                           "image"])
349 def test_non_editable_inputs(session, type):
350     session.url = inline("<input type=%s>" % type)
351     element = session.find.css("input", all=False)
352
353     response = element_clear(session, element)
354     assert_error(response, "invalid element state")
355
356
357 def test_scroll_into_view(session):
358     session.url = inline("""
359         <input value=foobar>
360         <div style='height: 200vh; width: 5000vh'>
361         """)
362     element = session.find.css("input", all=False)
363     assert element.property("value") == "foobar"
364     assert session.execute_script("return window.scrollY") == 0
365
366     # scroll to the bottom right of the page
367     session.execute_script("""
368         let {scrollWidth, scrollHeight} = document.body;
369         window.scrollTo(scrollWidth, scrollHeight);
370         """)
371
372     # clear and scroll back to the top of the page
373     response = element_clear(session, element)
374     assert_success(response)
375     assert element.property("value") == ""
376
377     # check if element cleared is scrolled into view
378     rect = session.execute_script("""
379         let [input] = arguments;
380         return input.getBoundingClientRect();
381         """, args=(element,))
382     window = session.execute_script("""
383         let {innerHeight, innerWidth, pageXOffset, pageYOffset} = window;
384         return {innerHeight, innerWidth, pageXOffset, pageYOffset};
385         """)
386
387     assert rect["top"] < (window["innerHeight"] + window["pageYOffset"]) and \
388            rect["left"] < (window["innerWidth"] + window["pageXOffset"]) and \
389            (rect["top"] + element.rect["height"]) > window["pageYOffset"] and \
390            (rect["left"] + element.rect["width"]) > window["pageXOffset"]