Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/py/test/selenium/webdriver/common/bidi_input_tests.py
1865 views
1
# Licensed to the Software Freedom Conservancy (SFC) under one
2
# or more contributor license agreements. See the NOTICE file
3
# distributed with this work for additional information
4
# regarding copyright ownership. The SFC licenses this file
5
# to you under the Apache License, Version 2.0 (the
6
# "License"); you may not use this file except in compliance
7
# with the License. You may obtain a copy of the License at
8
#
9
# http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing,
12
# software distributed under the License is distributed on an
13
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
# KIND, either express or implied. See the License for the
15
# specific language governing permissions and limitations
16
# under the License.
17
18
import os
19
import tempfile
20
import time
21
22
import pytest
23
24
from selenium.webdriver.common.bidi.input import (
25
ElementOrigin,
26
FileDialogInfo,
27
KeyDownAction,
28
KeySourceActions,
29
KeyUpAction,
30
Origin,
31
PauseAction,
32
PointerCommonProperties,
33
PointerDownAction,
34
PointerMoveAction,
35
PointerParameters,
36
PointerSourceActions,
37
PointerType,
38
PointerUpAction,
39
WheelScrollAction,
40
WheelSourceActions,
41
)
42
from selenium.webdriver.common.by import By
43
from selenium.webdriver.support.ui import WebDriverWait
44
45
46
def test_input_initialized(driver):
47
"""Test that the input module is initialized properly."""
48
assert driver.input is not None
49
50
51
def test_basic_key_input(driver, pages):
52
"""Test basic keyboard input using BiDi."""
53
pages.load("single_text_input.html")
54
55
input_element = driver.find_element(By.ID, "textInput")
56
57
# Create keyboard actions to type "hello"
58
key_actions = KeySourceActions(
59
id="keyboard",
60
actions=[
61
KeyDownAction(value="h"),
62
KeyUpAction(value="h"),
63
KeyDownAction(value="e"),
64
KeyUpAction(value="e"),
65
KeyDownAction(value="l"),
66
KeyUpAction(value="l"),
67
KeyDownAction(value="l"),
68
KeyUpAction(value="l"),
69
KeyDownAction(value="o"),
70
KeyUpAction(value="o"),
71
],
72
)
73
74
driver.input.perform_actions(driver.current_window_handle, [key_actions])
75
76
WebDriverWait(driver, 5).until(lambda d: input_element.get_attribute("value") == "hello")
77
assert input_element.get_attribute("value") == "hello"
78
79
80
def test_key_input_with_pause(driver, pages):
81
"""Test keyboard input with pause actions."""
82
pages.load("single_text_input.html")
83
84
input_element = driver.find_element(By.ID, "textInput")
85
86
# Create keyboard actions with pauses
87
key_actions = KeySourceActions(
88
id="keyboard",
89
actions=[
90
KeyDownAction(value="a"),
91
KeyUpAction(value="a"),
92
PauseAction(duration=100),
93
KeyDownAction(value="b"),
94
KeyUpAction(value="b"),
95
],
96
)
97
98
driver.input.perform_actions(driver.current_window_handle, [key_actions])
99
100
WebDriverWait(driver, 5).until(lambda d: input_element.get_attribute("value") == "ab")
101
assert input_element.get_attribute("value") == "ab"
102
103
104
def test_pointer_click(driver, pages):
105
"""Test basic pointer click using BiDi."""
106
pages.load("javascriptPage.html")
107
108
button = driver.find_element(By.ID, "clickField")
109
110
# Get button location
111
location = button.location
112
size = button.size
113
x = location["x"] + size["width"] // 2
114
y = location["y"] + size["height"] // 2
115
116
# Create pointer actions for a click
117
pointer_actions = PointerSourceActions(
118
id="mouse",
119
parameters=PointerParameters(pointer_type=PointerType.MOUSE),
120
actions=[
121
PointerMoveAction(x=x, y=y),
122
PointerDownAction(button=0),
123
PointerUpAction(button=0),
124
],
125
)
126
127
driver.input.perform_actions(driver.current_window_handle, [pointer_actions])
128
129
WebDriverWait(driver, 5).until(lambda d: button.get_attribute("value") == "Clicked")
130
assert button.get_attribute("value") == "Clicked"
131
132
133
def test_pointer_move_with_element_origin(driver, pages):
134
"""Test pointer move with element origin."""
135
pages.load("javascriptPage.html")
136
137
button = driver.find_element(By.ID, "clickField")
138
139
# Get element reference for BiDi
140
element_id = button.id
141
element_ref = {"sharedId": element_id}
142
element_origin = ElementOrigin(element_ref)
143
144
# Create pointer actions with element origin
145
pointer_actions = PointerSourceActions(
146
id="mouse",
147
parameters=PointerParameters(pointer_type=PointerType.MOUSE),
148
actions=[
149
PointerMoveAction(x=0, y=0, origin=element_origin),
150
PointerDownAction(button=0),
151
PointerUpAction(button=0),
152
],
153
)
154
155
driver.input.perform_actions(driver.current_window_handle, [pointer_actions])
156
157
WebDriverWait(driver, 5).until(lambda d: button.get_attribute("value") == "Clicked")
158
assert button.get_attribute("value") == "Clicked"
159
160
161
def test_pointer_with_common_properties(driver, pages):
162
"""Test pointer actions with common properties."""
163
pages.load("javascriptPage.html")
164
165
button = driver.find_element(By.ID, "clickField")
166
location = button.location
167
size = button.size
168
x = location["x"] + size["width"] // 2
169
y = location["y"] + size["height"] // 2
170
171
# Create pointer properties
172
properties = PointerCommonProperties(
173
width=2, height=2, pressure=0.5, tangential_pressure=0.0, twist=45, altitude_angle=0.5, azimuth_angle=1.0
174
)
175
176
pointer_actions = PointerSourceActions(
177
id="mouse",
178
parameters=PointerParameters(pointer_type=PointerType.MOUSE),
179
actions=[
180
PointerMoveAction(x=x, y=y, properties=properties),
181
PointerDownAction(button=0, properties=properties),
182
PointerUpAction(button=0),
183
],
184
)
185
186
driver.input.perform_actions(driver.current_window_handle, [pointer_actions])
187
188
WebDriverWait(driver, 5).until(lambda d: button.get_attribute("value") == "Clicked")
189
assert button.get_attribute("value") == "Clicked"
190
191
192
def test_wheel_scroll(driver, pages):
193
"""Test wheel scroll actions."""
194
# page that can be scrolled
195
pages.load("scroll3.html")
196
197
# Scroll down
198
wheel_actions = WheelSourceActions(
199
id="wheel", actions=[WheelScrollAction(x=100, y=100, delta_x=0, delta_y=100, origin=Origin.VIEWPORT)]
200
)
201
202
driver.input.perform_actions(driver.current_window_handle, [wheel_actions])
203
204
# Verify the page scrolled by checking scroll position
205
scroll_y = driver.execute_script("return window.pageYOffset;")
206
assert scroll_y == 100
207
208
209
def test_combined_input_actions(driver, pages):
210
"""Test combining multiple input sources."""
211
pages.load("single_text_input.html")
212
213
input_element = driver.find_element(By.ID, "textInput")
214
215
# First click on the input field, then type
216
location = input_element.location
217
size = input_element.size
218
x = location["x"] + size["width"] // 2
219
y = location["y"] + size["height"] // 2
220
221
# Pointer actions to click
222
pointer_actions = PointerSourceActions(
223
id="mouse",
224
parameters=PointerParameters(pointer_type=PointerType.MOUSE),
225
actions=[
226
PauseAction(duration=0), # Sync with keyboard
227
PointerMoveAction(x=x, y=y),
228
PointerDownAction(button=0),
229
PointerUpAction(button=0),
230
],
231
)
232
233
# Keyboard actions to type
234
key_actions = KeySourceActions(
235
id="keyboard",
236
actions=[
237
PauseAction(duration=0), # Sync with pointer
238
# write "test"
239
KeyDownAction(value="t"),
240
KeyUpAction(value="t"),
241
KeyDownAction(value="e"),
242
KeyUpAction(value="e"),
243
KeyDownAction(value="s"),
244
KeyUpAction(value="s"),
245
KeyDownAction(value="t"),
246
KeyUpAction(value="t"),
247
],
248
)
249
250
driver.input.perform_actions(driver.current_window_handle, [pointer_actions, key_actions])
251
252
WebDriverWait(driver, 5).until(lambda d: input_element.get_attribute("value") == "test")
253
assert input_element.get_attribute("value") == "test"
254
255
256
def test_set_files(driver, pages):
257
"""Test setting files on file input element."""
258
pages.load("formPage.html")
259
260
upload_element = driver.find_element(By.ID, "upload")
261
assert upload_element.get_attribute("value") == ""
262
263
# Create a temporary file
264
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as temp_file:
265
temp_file.write("test content")
266
temp_file_path = temp_file.name
267
268
try:
269
# Get element reference for BiDi
270
element_id = upload_element.id
271
element_ref = {"sharedId": element_id}
272
273
# Set files using BiDi
274
driver.input.set_files(driver.current_window_handle, element_ref, [temp_file_path])
275
276
# Verify file was set
277
value = upload_element.get_attribute("value")
278
assert os.path.basename(temp_file_path) in value
279
280
finally:
281
# Clean up temp file
282
if os.path.exists(temp_file_path):
283
os.unlink(temp_file_path)
284
285
286
def test_set_multiple_files(driver):
287
"""Test setting multiple files on a file input element with 'multiple' attribute using BiDi."""
288
driver.get("data:text/html,<input id=upload type=file multiple />")
289
290
upload_element = driver.find_element(By.ID, "upload")
291
292
# Create temporary files
293
temp_files = []
294
for i in range(2):
295
temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False)
296
temp_file.write(f"test content {i}")
297
temp_files.append(temp_file.name)
298
temp_file.close()
299
300
try:
301
# Get element reference for BiDi
302
element_id = upload_element.id
303
element_ref = {"sharedId": element_id}
304
305
driver.input.set_files(driver.current_window_handle, element_ref, temp_files)
306
307
value = upload_element.get_attribute("value")
308
assert value != ""
309
310
finally:
311
# Clean up temp files
312
for temp_file_path in temp_files:
313
if os.path.exists(temp_file_path):
314
os.unlink(temp_file_path)
315
316
317
def test_release_actions(driver, pages):
318
"""Test releasing input actions."""
319
pages.load("single_text_input.html")
320
321
input_element = driver.find_element(By.ID, "textInput")
322
323
# Perform some actions first
324
key_actions = KeySourceActions(
325
id="keyboard",
326
actions=[
327
KeyDownAction(value="a"),
328
# Note: not releasing the key
329
],
330
)
331
332
driver.input.perform_actions(driver.current_window_handle, [key_actions])
333
334
# Now release all actions
335
driver.input.release_actions(driver.current_window_handle)
336
337
# The key should be released now, so typing more should work normally
338
key_actions2 = KeySourceActions(
339
id="keyboard",
340
actions=[
341
KeyDownAction(value="b"),
342
KeyUpAction(value="b"),
343
],
344
)
345
346
driver.input.perform_actions(driver.current_window_handle, [key_actions2])
347
348
# Should be able to type normally
349
WebDriverWait(driver, 5).until(lambda d: "b" in input_element.get_attribute("value"))
350
351
352
@pytest.mark.parametrize("multiple", [True, False])
353
@pytest.mark.xfail_firefox(reason="File dialog handling not implemented in Firefox yet")
354
def test_file_dialog_event_handler_multiple(driver, multiple):
355
"""Test file dialog event handler with multiple as true and false."""
356
file_dialog_events = []
357
358
def file_dialog_handler(file_dialog_info):
359
file_dialog_events.append(file_dialog_info)
360
361
# Test event handler registration
362
handler_id = driver.input.add_file_dialog_handler(file_dialog_handler)
363
assert handler_id is not None
364
365
driver.get(f"data:text/html,<input id=upload type=file {'multiple' if multiple else ''} />")
366
367
# Use script.evaluate to trigger the file dialog with user activation
368
driver.script._evaluate(
369
expression="document.getElementById('upload').click()",
370
target={"context": driver.current_window_handle},
371
await_promise=False,
372
user_activation=True,
373
)
374
375
# Wait for the file dialog event to be triggered
376
WebDriverWait(driver, 5).until(lambda d: len(file_dialog_events) > 0)
377
378
assert len(file_dialog_events) > 0
379
file_dialog_info = file_dialog_events[0]
380
assert isinstance(file_dialog_info, FileDialogInfo)
381
assert file_dialog_info.context == driver.current_window_handle
382
# Check if multiple attribute is set correctly (True, False)
383
assert file_dialog_info.multiple is multiple
384
385
driver.input.remove_file_dialog_handler(handler_id)
386
387
388
@pytest.mark.xfail_firefox(reason="File dialog handling not implemented in Firefox yet")
389
def test_file_dialog_event_handler_unsubscribe(driver):
390
"""Test file dialog event handler unsubscribe."""
391
file_dialog_events = []
392
393
def file_dialog_handler(file_dialog_info):
394
file_dialog_events.append(file_dialog_info)
395
396
# Register the handler
397
handler_id = driver.input.add_file_dialog_handler(file_dialog_handler)
398
assert handler_id is not None
399
400
# Unsubscribe the handler
401
driver.input.remove_file_dialog_handler(handler_id)
402
403
driver.get("data:text/html,<input id=upload type=file />")
404
405
# Trigger the file dialog
406
driver.script._evaluate(
407
expression="document.getElementById('upload').click()",
408
target={"context": driver.current_window_handle},
409
await_promise=False,
410
user_activation=True,
411
)
412
413
# Wait to ensure no events are captured
414
time.sleep(1)
415
assert len(file_dialog_events) == 0
416
417