seleniumbase 4.24.11__py3-none-any.whl → 4.33.15__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. sbase/__init__.py +1 -0
  2. sbase/steps.py +7 -0
  3. seleniumbase/__init__.py +16 -7
  4. seleniumbase/__version__.py +1 -1
  5. seleniumbase/behave/behave_sb.py +97 -32
  6. seleniumbase/common/decorators.py +16 -7
  7. seleniumbase/config/proxy_list.py +3 -3
  8. seleniumbase/config/settings.py +4 -0
  9. seleniumbase/console_scripts/logo_helper.py +47 -8
  10. seleniumbase/console_scripts/run.py +345 -335
  11. seleniumbase/console_scripts/sb_behave_gui.py +5 -12
  12. seleniumbase/console_scripts/sb_caseplans.py +6 -13
  13. seleniumbase/console_scripts/sb_commander.py +5 -12
  14. seleniumbase/console_scripts/sb_install.py +62 -54
  15. seleniumbase/console_scripts/sb_mkchart.py +13 -20
  16. seleniumbase/console_scripts/sb_mkdir.py +11 -17
  17. seleniumbase/console_scripts/sb_mkfile.py +69 -43
  18. seleniumbase/console_scripts/sb_mkpres.py +13 -20
  19. seleniumbase/console_scripts/sb_mkrec.py +88 -21
  20. seleniumbase/console_scripts/sb_objectify.py +30 -30
  21. seleniumbase/console_scripts/sb_print.py +5 -12
  22. seleniumbase/console_scripts/sb_recorder.py +16 -11
  23. seleniumbase/core/browser_launcher.py +1658 -221
  24. seleniumbase/core/log_helper.py +42 -27
  25. seleniumbase/core/mysql.py +1 -4
  26. seleniumbase/core/proxy_helper.py +35 -30
  27. seleniumbase/core/recorder_helper.py +24 -5
  28. seleniumbase/core/sb_cdp.py +1951 -0
  29. seleniumbase/core/sb_driver.py +162 -8
  30. seleniumbase/core/settings_parser.py +6 -0
  31. seleniumbase/core/style_sheet.py +10 -0
  32. seleniumbase/extensions/recorder.zip +0 -0
  33. seleniumbase/fixtures/base_case.py +1225 -614
  34. seleniumbase/fixtures/constants.py +10 -1
  35. seleniumbase/fixtures/js_utils.py +171 -144
  36. seleniumbase/fixtures/page_actions.py +177 -13
  37. seleniumbase/fixtures/page_utils.py +25 -53
  38. seleniumbase/fixtures/shared_utils.py +97 -11
  39. seleniumbase/js_code/active_css_js.py +1 -1
  40. seleniumbase/js_code/recorder_js.py +1 -1
  41. seleniumbase/plugins/base_plugin.py +2 -3
  42. seleniumbase/plugins/driver_manager.py +340 -65
  43. seleniumbase/plugins/pytest_plugin.py +276 -47
  44. seleniumbase/plugins/sb_manager.py +412 -99
  45. seleniumbase/plugins/selenium_plugin.py +122 -17
  46. seleniumbase/translate/translator.py +0 -7
  47. seleniumbase/undetected/__init__.py +59 -52
  48. seleniumbase/undetected/cdp.py +0 -1
  49. seleniumbase/undetected/cdp_driver/__init__.py +1 -0
  50. seleniumbase/undetected/cdp_driver/_contradict.py +110 -0
  51. seleniumbase/undetected/cdp_driver/browser.py +829 -0
  52. seleniumbase/undetected/cdp_driver/cdp_util.py +458 -0
  53. seleniumbase/undetected/cdp_driver/config.py +334 -0
  54. seleniumbase/undetected/cdp_driver/connection.py +639 -0
  55. seleniumbase/undetected/cdp_driver/element.py +1168 -0
  56. seleniumbase/undetected/cdp_driver/tab.py +1323 -0
  57. seleniumbase/undetected/dprocess.py +4 -7
  58. seleniumbase/undetected/options.py +6 -8
  59. seleniumbase/undetected/patcher.py +11 -13
  60. seleniumbase/undetected/reactor.py +0 -1
  61. seleniumbase/undetected/webelement.py +16 -3
  62. {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/LICENSE +1 -1
  63. {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/METADATA +299 -252
  64. {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/RECORD +67 -69
  65. {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/WHEEL +1 -1
  66. sbase/ReadMe.txt +0 -2
  67. seleniumbase/ReadMe.md +0 -25
  68. seleniumbase/common/ReadMe.md +0 -71
  69. seleniumbase/console_scripts/ReadMe.md +0 -731
  70. seleniumbase/drivers/ReadMe.md +0 -27
  71. seleniumbase/extensions/ReadMe.md +0 -12
  72. seleniumbase/masterqa/ReadMe.md +0 -61
  73. seleniumbase/resources/ReadMe.md +0 -31
  74. seleniumbase/resources/favicon.ico +0 -0
  75. seleniumbase/utilities/selenium_grid/ReadMe.md +0 -84
  76. seleniumbase/utilities/selenium_ide/ReadMe.md +0 -111
  77. {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/entry_points.txt +0 -0
  78. {seleniumbase-4.24.11.dist-info → seleniumbase-4.33.15.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1951 @@
1
+ """Add CDP methods to extend the driver"""
2
+ import fasteners
3
+ import os
4
+ import re
5
+ import sys
6
+ import time
7
+ from contextlib import suppress
8
+ from seleniumbase import config as sb_config
9
+ from seleniumbase.config import settings
10
+ from seleniumbase.fixtures import constants
11
+ from seleniumbase.fixtures import js_utils
12
+ from seleniumbase.fixtures import page_utils
13
+ from seleniumbase.fixtures import shared_utils
14
+
15
+
16
+ class CDPMethods():
17
+ def __init__(self, loop, page, driver):
18
+ self.loop = loop
19
+ self.page = page
20
+ self.driver = driver
21
+
22
+ def __slow_mode_pause_if_set(self):
23
+ if (
24
+ (hasattr(sb_config, "demo_mode") and sb_config.demo_mode)
25
+ or "--demo" in sys.argv
26
+ ):
27
+ time.sleep(0.48)
28
+ elif (
29
+ (hasattr(sb_config, "slow_mode") and sb_config.slow_mode)
30
+ or "--slow" in sys.argv
31
+ ):
32
+ time.sleep(0.24)
33
+
34
+ def __add_light_pause(self):
35
+ time.sleep(0.007)
36
+
37
+ def __convert_to_css_if_xpath(self, selector):
38
+ if page_utils.is_xpath_selector(selector):
39
+ with suppress(Exception):
40
+ css = js_utils.convert_to_css_selector(selector, "xpath")
41
+ if css:
42
+ return css
43
+ return selector
44
+
45
+ def __add_sync_methods(self, element):
46
+ if not element:
47
+ return element
48
+ element.clear_input = lambda: self.__clear_input(element)
49
+ element.click = lambda: self.__click(element)
50
+ element.flash = lambda *args, **kwargs: self.__flash(
51
+ element, *args, **kwargs
52
+ )
53
+ element.focus = lambda: self.__focus(element)
54
+ element.highlight_overlay = lambda: self.__highlight_overlay(element)
55
+ element.mouse_click = lambda: self.__mouse_click(element)
56
+ element.mouse_drag = (
57
+ lambda destination: self.__mouse_drag(element, destination)
58
+ )
59
+ element.mouse_move = lambda: self.__mouse_move(element)
60
+ element.query_selector = (
61
+ lambda selector: self.__query_selector(element, selector)
62
+ )
63
+ element.querySelector = element.query_selector
64
+ element.query_selector_all = (
65
+ lambda selector: self.__query_selector_all(element, selector)
66
+ )
67
+ element.querySelectorAll = element.query_selector_all
68
+ element.remove_from_dom = lambda: self.__remove_from_dom(element)
69
+ element.save_screenshot = (
70
+ lambda *args, **kwargs: self.__save_screenshot(
71
+ element, *args, **kwargs)
72
+ )
73
+ element.save_to_dom = lambda: self.__save_to_dom(element)
74
+ element.scroll_into_view = lambda: self.__scroll_into_view(element)
75
+ element.select_option = lambda: self.__select_option(element)
76
+ element.send_file = (
77
+ lambda *file_paths: self.__send_file(element, *file_paths)
78
+ )
79
+ element.send_keys = lambda text: self.__send_keys(element, text)
80
+ element.set_text = lambda value: self.__set_text(element, value)
81
+ element.set_value = lambda value: self.__set_value(element, value)
82
+ element.type = lambda text: self.__type(element, text)
83
+ element.get_position = lambda: self.__get_position(element)
84
+ element.get_html = lambda: self.__get_html(element)
85
+ element.get_js_attributes = lambda: self.__get_js_attributes(element)
86
+ element.get_attribute = (
87
+ lambda attribute: self.__get_attribute(element, attribute)
88
+ )
89
+ return element
90
+
91
+ def get(self, url):
92
+ url = shared_utils.fix_url_as_needed(url)
93
+ driver = self.driver
94
+ if hasattr(driver, "cdp_base"):
95
+ driver = driver.cdp_base
96
+ self.page = self.loop.run_until_complete(driver.get(url))
97
+ url_protocol = url.split(":")[0]
98
+ safe_url = True
99
+ if url_protocol not in ["about", "data", "chrome"]:
100
+ safe_url = False
101
+ if not safe_url:
102
+ time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
103
+ if shared_utils.is_windows():
104
+ time.sleep(constants.UC.EXTRA_WINDOWS_WAIT)
105
+ else:
106
+ time.sleep(0.012)
107
+ self.__slow_mode_pause_if_set()
108
+ self.loop.run_until_complete(self.page.wait())
109
+
110
+ def open(self, url):
111
+ self.get(url)
112
+
113
+ def reload(self, ignore_cache=True, script_to_evaluate_on_load=None):
114
+ self.loop.run_until_complete(
115
+ self.page.reload(
116
+ ignore_cache=ignore_cache,
117
+ script_to_evaluate_on_load=script_to_evaluate_on_load,
118
+ )
119
+ )
120
+
121
+ def refresh(self, *args, **kwargs):
122
+ self.reload(*args, **kwargs)
123
+
124
+ def get_event_loop(self):
125
+ return self.loop
126
+
127
+ def add_handler(self, event, handler):
128
+ self.page.add_handler(event, handler)
129
+
130
+ def find_element(self, selector, best_match=False, timeout=None):
131
+ """Similar to select(), but also finds elements by text content.
132
+ When using text-based searches, if best_match=False, then will
133
+ find the first element with the text. If best_match=True, then
134
+ if multiple elements have that text, then will use the element
135
+ with the closest text-length to the text being searched for."""
136
+ if not timeout:
137
+ timeout = settings.SMALL_TIMEOUT
138
+ self.__add_light_pause()
139
+ selector = self.__convert_to_css_if_xpath(selector)
140
+ early_failure = False
141
+ if (":contains(") in selector:
142
+ selector, _ = page_utils.recalculate_selector(
143
+ selector, by="css selector", xp_ok=True
144
+ )
145
+ failure = False
146
+ try:
147
+ if early_failure:
148
+ raise Exception("Failed!")
149
+ element = self.loop.run_until_complete(
150
+ self.page.find(
151
+ selector, best_match=best_match, timeout=timeout
152
+ )
153
+ )
154
+ except Exception:
155
+ failure = True
156
+ plural = "s"
157
+ if timeout == 1:
158
+ plural = ""
159
+ message = "\n Element {%s} was not found after %s second%s!" % (
160
+ selector,
161
+ timeout,
162
+ plural,
163
+ )
164
+ if failure:
165
+ raise Exception(message)
166
+ element = self.__add_sync_methods(element)
167
+ self.__slow_mode_pause_if_set()
168
+ return element
169
+
170
+ def find_element_by_text(self, text, tag_name=None, timeout=None):
171
+ """Returns an element by matching text.
172
+ Optionally, provide a tag_name to narrow down the search to an
173
+ element with the given tag. (Eg: a, button, div, script, span)"""
174
+ if not timeout:
175
+ timeout = settings.SMALL_TIMEOUT
176
+ self.__add_light_pause()
177
+ time_now = time.time()
178
+ self.assert_text(text, timeout=timeout)
179
+ spent = int(time.time() - time_now)
180
+ remaining = 1 + timeout - spent
181
+ if tag_name:
182
+ self.assert_element(tag_name, timeout=remaining)
183
+ elements = self.loop.run_until_complete(
184
+ self.page.find_elements_by_text(text=text)
185
+ )
186
+ if tag_name:
187
+ tag_name = tag_name.lower().strip()
188
+ for element in elements:
189
+ if element and not tag_name:
190
+ element = self.__add_sync_methods(element)
191
+ return self.__add_sync_methods(element)
192
+ elif (
193
+ element
194
+ and tag_name in element.tag_name.lower()
195
+ and text.strip() in element.text
196
+ ):
197
+ element = self.__add_sync_methods(element)
198
+ return self.__add_sync_methods(element)
199
+ elif (
200
+ element.parent
201
+ and tag_name in element.parent.tag_name.lower()
202
+ and text.strip() in element.parent.text
203
+ ):
204
+ element = self.__add_sync_methods(element.parent)
205
+ return self.__add_sync_methods(element)
206
+ elif (
207
+ element.parent.parent
208
+ and tag_name in element.parent.parent.tag_name.lower()
209
+ and text.strip() in element.parent.parent.text
210
+ ):
211
+ element = self.__add_sync_methods(element.parent.parent)
212
+ return self.__add_sync_methods(element)
213
+ plural = "s"
214
+ if timeout == 1:
215
+ plural = ""
216
+ raise Exception(
217
+ "Text {%s} with tag {%s} was not found after %s second%s!"
218
+ % (text, tag_name, timeout, plural)
219
+ )
220
+
221
+ def find_all(self, selector, timeout=None):
222
+ if not timeout:
223
+ timeout = settings.SMALL_TIMEOUT
224
+ self.__add_light_pause()
225
+ selector = self.__convert_to_css_if_xpath(selector)
226
+ elements = self.loop.run_until_complete(
227
+ self.page.find_all(selector, timeout=timeout)
228
+ )
229
+ updated_elements = []
230
+ for element in elements:
231
+ element = self.__add_sync_methods(element)
232
+ updated_elements.append(element)
233
+ return updated_elements
234
+
235
+ def find_elements_by_text(self, text, tag_name=None):
236
+ """Returns a list of elements by matching text.
237
+ Optionally, provide a tag_name to narrow down the search to only
238
+ elements with the given tag. (Eg: a, button, div, script, span)"""
239
+ self.__add_light_pause()
240
+ elements = self.loop.run_until_complete(
241
+ self.page.find_elements_by_text(text=text)
242
+ )
243
+ updated_elements = []
244
+ if tag_name:
245
+ tag_name = tag_name.lower().strip()
246
+ for element in elements:
247
+ if element and not tag_name:
248
+ element = self.__add_sync_methods(element)
249
+ if element not in updated_elements:
250
+ updated_elements.append(element)
251
+ elif (
252
+ element
253
+ and tag_name in element.tag_name.lower()
254
+ and text.strip() in element.text
255
+ ):
256
+ element = self.__add_sync_methods(element)
257
+ if element not in updated_elements:
258
+ updated_elements.append(element)
259
+ elif (
260
+ element.parent
261
+ and tag_name in element.parent.tag_name.lower()
262
+ and text.strip() in element.parent.text
263
+ ):
264
+ element = self.__add_sync_methods(element.parent)
265
+ if element not in updated_elements:
266
+ updated_elements.append(element)
267
+ elif (
268
+ element.parent.parent
269
+ and tag_name in element.parent.parent.tag_name.lower()
270
+ and text.strip() in element.parent.parent.text
271
+ ):
272
+ element = self.__add_sync_methods(element.parent.parent)
273
+ if element not in updated_elements:
274
+ updated_elements.append(element)
275
+ return updated_elements
276
+
277
+ def select(self, selector, timeout=None):
278
+ """Similar to find_element(), but without text-based search."""
279
+ if not timeout:
280
+ timeout = settings.SMALL_TIMEOUT
281
+ self.__add_light_pause()
282
+ selector = self.__convert_to_css_if_xpath(selector)
283
+ if (":contains(" in selector):
284
+ tag_name = selector.split(":contains(")[0].split(" ")[-1]
285
+ text = selector.split(":contains(")[1].split(")")[0][1:-1]
286
+ with suppress(Exception):
287
+ self.loop.run_until_complete(
288
+ self.page.select(tag_name, timeout=5)
289
+ )
290
+ self.loop.run_until_complete(self.page.find(text, timeout=5))
291
+ element = self.find_elements_by_text(text, tag_name=tag_name)[0]
292
+ return self.__add_sync_methods(element)
293
+ failure = False
294
+ try:
295
+ element = self.loop.run_until_complete(
296
+ self.page.select(selector, timeout=timeout)
297
+ )
298
+ except Exception:
299
+ failure = True
300
+ plural = "s"
301
+ if timeout == 1:
302
+ plural = ""
303
+ message = "\n Element {%s} was not found after %s second%s!" % (
304
+ selector,
305
+ timeout,
306
+ plural,
307
+ )
308
+ if failure:
309
+ raise Exception(message)
310
+ element = self.__add_sync_methods(element)
311
+ self.__slow_mode_pause_if_set()
312
+ return element
313
+
314
+ def select_all(self, selector, timeout=None):
315
+ if not timeout:
316
+ timeout = settings.SMALL_TIMEOUT
317
+ self.__add_light_pause()
318
+ selector = self.__convert_to_css_if_xpath(selector)
319
+ elements = self.loop.run_until_complete(
320
+ self.page.select_all(selector, timeout=timeout)
321
+ )
322
+ updated_elements = []
323
+ for element in elements:
324
+ element = self.__add_sync_methods(element)
325
+ updated_elements.append(element)
326
+ return updated_elements
327
+
328
+ def find_elements(self, selector, timeout=None):
329
+ if not timeout:
330
+ timeout = settings.SMALL_TIMEOUT
331
+ return self.select_all(selector, timeout=timeout)
332
+
333
+ def find_visible_elements(self, selector, timeout=None):
334
+ if not timeout:
335
+ timeout = settings.SMALL_TIMEOUT
336
+ visible_elements = []
337
+ elements = self.select_all(selector, timeout=timeout)
338
+ for element in elements:
339
+ with suppress(Exception):
340
+ position = element.get_position()
341
+ if (position.width != 0 or position.height != 0):
342
+ visible_elements.append(element)
343
+ return visible_elements
344
+
345
+ def click_nth_element(self, selector, number):
346
+ elements = self.select_all(selector)
347
+ if len(elements) < number:
348
+ raise Exception(
349
+ "Not enough matching {%s} elements to "
350
+ "click number %s!" % (selector, number)
351
+ )
352
+ number = number - 1
353
+ if number < 0:
354
+ number = 0
355
+ element = elements[number]
356
+ element.scroll_into_view()
357
+ element.click()
358
+
359
+ def click_nth_visible_element(self, selector, number):
360
+ """Finds all matching page elements and clicks the nth visible one.
361
+ Example: self.click_nth_visible_element('[type="checkbox"]', 5)
362
+ (Clicks the 5th visible checkbox on the page.)"""
363
+ elements = self.find_visible_elements(selector)
364
+ if len(elements) < number:
365
+ raise Exception(
366
+ "Not enough matching {%s} elements to "
367
+ "click number %s!" % (selector, number)
368
+ )
369
+ number = number - 1
370
+ if number < 0:
371
+ number = 0
372
+ element = elements[number]
373
+ element.scroll_into_view()
374
+ element.click()
375
+
376
+ def click_link(self, link_text):
377
+ self.find_elements_by_text(link_text, "a")[0].click()
378
+
379
+ def go_back(self):
380
+ self.loop.run_until_complete(self.page.back())
381
+
382
+ def go_forward(self):
383
+ self.loop.run_until_complete(self.page.forward())
384
+
385
+ def get_navigation_history(self):
386
+ return self.loop.run_until_complete(self.page.get_navigation_history())
387
+
388
+ def __clear_input(self, element):
389
+ return (
390
+ self.loop.run_until_complete(element.clear_input_async())
391
+ )
392
+
393
+ def __click(self, element):
394
+ result = (
395
+ self.loop.run_until_complete(element.click_async())
396
+ )
397
+ self.loop.run_until_complete(self.page.wait())
398
+ return result
399
+
400
+ def __flash(self, element, *args, **kwargs):
401
+ element.scroll_into_view()
402
+ if len(args) < 3 and "x_offset" not in kwargs:
403
+ x_offset = self.__get_x_scroll_offset()
404
+ kwargs["x_offset"] = x_offset
405
+ if len(args) < 3 and "y_offset" not in kwargs:
406
+ y_offset = self.__get_y_scroll_offset()
407
+ kwargs["y_offset"] = y_offset
408
+ return (
409
+ self.loop.run_until_complete(
410
+ element.flash_async(*args, **kwargs)
411
+ )
412
+ )
413
+
414
+ def __focus(self, element):
415
+ return (
416
+ self.loop.run_until_complete(element.focus_async())
417
+ )
418
+
419
+ def __highlight_overlay(self, element):
420
+ return (
421
+ self.loop.run_until_complete(element.highlight_overlay_async())
422
+ )
423
+
424
+ def __mouse_click(self, element):
425
+ result = (
426
+ self.loop.run_until_complete(element.mouse_click_async())
427
+ )
428
+ self.loop.run_until_complete(self.page.wait())
429
+ return result
430
+
431
+ def __mouse_drag(self, element, destination):
432
+ return (
433
+ self.loop.run_until_complete(element.mouse_drag_async(destination))
434
+ )
435
+
436
+ def __mouse_move(self, element):
437
+ return (
438
+ self.loop.run_until_complete(element.mouse_move_async())
439
+ )
440
+
441
+ def __query_selector(self, element, selector):
442
+ selector = self.__convert_to_css_if_xpath(selector)
443
+ element2 = self.loop.run_until_complete(
444
+ element.query_selector_async(selector)
445
+ )
446
+ element2 = self.__add_sync_methods(element2)
447
+ return element2
448
+
449
+ def __query_selector_all(self, element, selector):
450
+ selector = self.__convert_to_css_if_xpath(selector)
451
+ elements = self.loop.run_until_complete(
452
+ element.query_selector_all_async(selector)
453
+ )
454
+ updated_elements = []
455
+ for element in elements:
456
+ element = self.__add_sync_methods(element)
457
+ updated_elements.append(element)
458
+ self.__slow_mode_pause_if_set()
459
+ return updated_elements
460
+
461
+ def __remove_from_dom(self, element):
462
+ return (
463
+ self.loop.run_until_complete(element.remove_from_dom_async())
464
+ )
465
+
466
+ def __save_screenshot(self, element, *args, **kwargs):
467
+ return (
468
+ self.loop.run_until_complete(
469
+ element.save_screenshot_async(*args, **kwargs)
470
+ )
471
+ )
472
+
473
+ def __save_to_dom(self, element):
474
+ return (
475
+ self.loop.run_until_complete(element.save_to_dom_async())
476
+ )
477
+
478
+ def __scroll_into_view(self, element):
479
+ self.loop.run_until_complete(element.scroll_into_view_async())
480
+ self.__add_light_pause()
481
+ return None
482
+
483
+ def __select_option(self, element):
484
+ return (
485
+ self.loop.run_until_complete(element.select_option_async())
486
+ )
487
+
488
+ def __send_file(self, element, *file_paths):
489
+ return (
490
+ self.loop.run_until_complete(element.send_file_async(*file_paths))
491
+ )
492
+
493
+ def __send_keys(self, element, text):
494
+ return (
495
+ self.loop.run_until_complete(element.send_keys_async(text))
496
+ )
497
+
498
+ def __set_text(self, element, value):
499
+ return (
500
+ self.loop.run_until_complete(element.set_text_async(value))
501
+ )
502
+
503
+ def __set_value(self, element, value):
504
+ return (
505
+ self.loop.run_until_complete(element.set_value_async(value))
506
+ )
507
+
508
+ def __type(self, element, text):
509
+ with suppress(Exception):
510
+ element.clear_input()
511
+ element.send_keys(text)
512
+
513
+ def __get_position(self, element):
514
+ return (
515
+ self.loop.run_until_complete(element.get_position_async())
516
+ )
517
+
518
+ def __get_html(self, element):
519
+ return (
520
+ self.loop.run_until_complete(element.get_html_async())
521
+ )
522
+
523
+ def __get_js_attributes(self, element):
524
+ return (
525
+ self.loop.run_until_complete(element.get_js_attributes_async())
526
+ )
527
+
528
+ def __get_attribute(self, element, attribute):
529
+ try:
530
+ return element.get_js_attributes()[attribute]
531
+ except Exception:
532
+ if not attribute:
533
+ raise
534
+ try:
535
+ attribute_str = element.get_js_attributes()
536
+ locate = ' %s="' % attribute
537
+ if locate in attribute_str.outerHTML:
538
+ outer_html = attribute_str.outerHTML
539
+ attr_start = outer_html.find(locate) + len(locate)
540
+ attr_end = outer_html.find('"', attr_start)
541
+ value = outer_html[attr_start:attr_end]
542
+ return value
543
+ except Exception:
544
+ pass
545
+ return None
546
+
547
+ def __get_x_scroll_offset(self):
548
+ x_scroll_offset = self.loop.run_until_complete(
549
+ self.page.evaluate("window.pageXOffset")
550
+ )
551
+ return x_scroll_offset or 0
552
+
553
+ def __get_y_scroll_offset(self):
554
+ y_scroll_offset = self.loop.run_until_complete(
555
+ self.page.evaluate("window.pageYOffset")
556
+ )
557
+ return y_scroll_offset or 0
558
+
559
+ def tile_windows(self, windows=None, max_columns=0):
560
+ """Tile windows and return the grid of tiled windows."""
561
+ driver = self.driver
562
+ if hasattr(driver, "cdp_base"):
563
+ driver = driver.cdp_base
564
+ return self.loop.run_until_complete(
565
+ driver.tile_windows(windows, max_columns)
566
+ )
567
+
568
+ def get_all_cookies(self, *args, **kwargs):
569
+ driver = self.driver
570
+ if hasattr(driver, "cdp_base"):
571
+ driver = driver.cdp_base
572
+ return self.loop.run_until_complete(
573
+ driver.cookies.get_all(*args, **kwargs)
574
+ )
575
+
576
+ def set_all_cookies(self, *args, **kwargs):
577
+ driver = self.driver
578
+ if hasattr(driver, "cdp_base"):
579
+ driver = driver.cdp_base
580
+ return self.loop.run_until_complete(
581
+ driver.cookies.set_all(*args, **kwargs)
582
+ )
583
+
584
+ def save_cookies(self, *args, **kwargs):
585
+ driver = self.driver
586
+ if hasattr(driver, "cdp_base"):
587
+ driver = driver.cdp_base
588
+ return self.loop.run_until_complete(
589
+ driver.cookies.save(*args, **kwargs)
590
+ )
591
+
592
+ def load_cookies(self, *args, **kwargs):
593
+ driver = self.driver
594
+ if hasattr(driver, "cdp_base"):
595
+ driver = driver.cdp_base
596
+ return self.loop.run_until_complete(
597
+ driver.cookies.load(*args, **kwargs)
598
+ )
599
+
600
+ def clear_cookies(self):
601
+ driver = self.driver
602
+ if hasattr(driver, "cdp_base"):
603
+ driver = driver.cdp_base
604
+ return self.loop.run_until_complete(
605
+ driver.cookies.clear()
606
+ )
607
+
608
+ def sleep(self, seconds):
609
+ time.sleep(seconds)
610
+
611
+ def bring_active_window_to_front(self):
612
+ self.loop.run_until_complete(self.page.bring_to_front())
613
+ self.__add_light_pause()
614
+
615
+ def get_active_element(self):
616
+ return self.loop.run_until_complete(
617
+ self.page.js_dumps("document.activeElement")
618
+ )
619
+
620
+ def get_active_element_css(self):
621
+ from seleniumbase.js_code import active_css_js
622
+
623
+ js_code = active_css_js.get_active_element_css
624
+ js_code = js_code.replace("return getBestSelector", "getBestSelector")
625
+ return self.loop.run_until_complete(
626
+ self.page.evaluate(js_code)
627
+ )
628
+
629
+ def click(self, selector, timeout=None):
630
+ if not timeout:
631
+ timeout = settings.SMALL_TIMEOUT
632
+ self.__slow_mode_pause_if_set()
633
+ element = self.find_element(selector, timeout=timeout)
634
+ element.scroll_into_view()
635
+ element.click()
636
+ self.__slow_mode_pause_if_set()
637
+ self.loop.run_until_complete(self.page.wait())
638
+
639
+ def click_active_element(self):
640
+ self.loop.run_until_complete(
641
+ self.page.evaluate("document.activeElement.click()")
642
+ )
643
+ self.__slow_mode_pause_if_set()
644
+ self.loop.run_until_complete(self.page.wait())
645
+
646
+ def click_if_visible(self, selector):
647
+ if self.is_element_visible(selector):
648
+ with suppress(Exception):
649
+ element = self.find_element(selector, timeout=0)
650
+ element.scroll_into_view()
651
+ element.click()
652
+ self.__slow_mode_pause_if_set()
653
+ self.loop.run_until_complete(self.page.wait())
654
+
655
+ def click_visible_elements(self, selector, limit=0):
656
+ """Finds all matching page elements and clicks visible ones in order.
657
+ If a click reloads or opens a new page, the clicking will stop.
658
+ If no matching elements appear, an Exception will be raised.
659
+ If "limit" is set and > 0, will only click that many elements.
660
+ Also clicks elements that become visible from previous clicks.
661
+ Works best for actions such as clicking all checkboxes on a page.
662
+ Example: self.click_visible_elements('input[type="checkbox"]')"""
663
+ elements = self.select_all(selector)
664
+ click_count = 0
665
+ for element in elements:
666
+ if limit and limit > 0 and click_count >= limit:
667
+ return
668
+ try:
669
+ width = 0
670
+ height = 0
671
+ try:
672
+ position = element.get_position()
673
+ width = position.width
674
+ height = position.height
675
+ except Exception:
676
+ continue
677
+ if (width != 0 or height != 0):
678
+ element.scroll_into_view()
679
+ element.click()
680
+ click_count += 1
681
+ time.sleep(0.042)
682
+ self.__slow_mode_pause_if_set()
683
+ self.loop.run_until_complete(self.page.wait())
684
+ except Exception:
685
+ break
686
+
687
+ def mouse_click(self, selector, timeout=None):
688
+ """(Attempt simulating a mouse click)"""
689
+ if not timeout:
690
+ timeout = settings.SMALL_TIMEOUT
691
+ self.__slow_mode_pause_if_set()
692
+ element = self.find_element(selector, timeout=timeout)
693
+ element.scroll_into_view()
694
+ element.mouse_click()
695
+ self.__slow_mode_pause_if_set()
696
+ self.loop.run_until_complete(self.page.wait())
697
+
698
+ def nested_click(self, parent_selector, selector):
699
+ """
700
+ Find parent element and click on child element inside it.
701
+ (This can be used to click on elements inside an iframe.)
702
+ """
703
+ element = self.find_element(parent_selector)
704
+ element.query_selector(selector).mouse_click()
705
+ self.__slow_mode_pause_if_set()
706
+ self.loop.run_until_complete(self.page.wait())
707
+
708
+ def get_nested_element(self, parent_selector, selector):
709
+ """(Can be used to find an element inside an iframe)"""
710
+ element = self.find_element(parent_selector)
711
+ return element.query_selector(selector)
712
+
713
+ def select_option_by_text(self, dropdown_selector, option):
714
+ element = self.find_element(dropdown_selector)
715
+ element.scroll_into_view()
716
+ options = element.query_selector_all("option")
717
+ for found_option in options:
718
+ if found_option.text.strip() == option.strip():
719
+ found_option.select_option()
720
+ return
721
+ raise Exception(
722
+ "Unable to find text option {%s} in dropdown {%s}!"
723
+ % (dropdown_selector, option)
724
+ )
725
+
726
+ def flash(
727
+ self,
728
+ selector, # The CSS Selector to flash
729
+ duration=1, # (seconds) flash duration
730
+ color="44CC88", # RGB hex flash color
731
+ pause=0, # (seconds) If 0, the next action starts during flash
732
+ ):
733
+ """Paint a quickly-vanishing dot over an element."""
734
+ selector = self.__convert_to_css_if_xpath(selector)
735
+ element = self.find_element(selector)
736
+ element.scroll_into_view()
737
+ x_offset = self.__get_x_scroll_offset()
738
+ y_offset = self.__get_y_scroll_offset()
739
+ element.flash(duration, color, x_offset, y_offset)
740
+ if pause and isinstance(pause, (int, float)):
741
+ time.sleep(pause)
742
+
743
+ def highlight(self, selector):
744
+ """Highlight an element with multi-colors."""
745
+ selector = self.__convert_to_css_if_xpath(selector)
746
+ element = self.find_element(selector)
747
+ element.scroll_into_view()
748
+ x_offset = self.__get_x_scroll_offset()
749
+ y_offset = self.__get_y_scroll_offset()
750
+ element.flash(0.46, "44CC88", x_offset, y_offset)
751
+ time.sleep(0.15)
752
+ element.flash(0.42, "8844CC", x_offset, y_offset)
753
+ time.sleep(0.15)
754
+ element.flash(0.38, "CC8844", x_offset, y_offset)
755
+ time.sleep(0.15)
756
+ element.flash(0.30, "44CC88", x_offset, y_offset)
757
+ time.sleep(0.30)
758
+
759
+ def focus(self, selector):
760
+ element = self.find_element(selector)
761
+ element.scroll_into_view()
762
+ element.focus()
763
+
764
+ def highlight_overlay(self, selector):
765
+ self.find_element(selector).highlight_overlay()
766
+
767
+ def remove_element(self, selector):
768
+ self.select(selector).remove_from_dom()
769
+
770
+ def remove_from_dom(self, selector):
771
+ self.select(selector).remove_from_dom()
772
+
773
+ def remove_elements(self, selector):
774
+ """Remove all elements on the page that match the selector."""
775
+ css_selector = self.__convert_to_css_if_xpath(selector)
776
+ css_selector = re.escape(css_selector) # Add "\\" to special chars
777
+ css_selector = js_utils.escape_quotes_if_needed(css_selector)
778
+ js_code = (
779
+ """var $elements = document.querySelectorAll('%s');
780
+ var index = 0, length = $elements.length;
781
+ for(; index < length; index++){
782
+ $elements[index].remove();}"""
783
+ % css_selector
784
+ )
785
+ with suppress(Exception):
786
+ self.loop.run_until_complete(self.page.evaluate(js_code))
787
+
788
+ def send_keys(self, selector, text, timeout=None):
789
+ if not timeout:
790
+ timeout = settings.SMALL_TIMEOUT
791
+ self.__slow_mode_pause_if_set()
792
+ element = self.select(selector, timeout=timeout)
793
+ element.scroll_into_view()
794
+ if text.endswith("\n") or text.endswith("\r"):
795
+ text = text[:-1] + "\r\n"
796
+ element.send_keys(text)
797
+ self.__slow_mode_pause_if_set()
798
+ self.loop.run_until_complete(self.page.wait())
799
+
800
+ def press_keys(self, selector, text, timeout=None):
801
+ """Similar to send_keys(), but presses keys at human speed."""
802
+ if not timeout:
803
+ timeout = settings.SMALL_TIMEOUT
804
+ self.__slow_mode_pause_if_set()
805
+ element = self.select(selector, timeout=timeout)
806
+ element.scroll_into_view()
807
+ submit = False
808
+ if text.endswith("\n") or text.endswith("\r"):
809
+ submit = True
810
+ text = text[:-1]
811
+ for key in text:
812
+ element.send_keys(key)
813
+ time.sleep(0.044)
814
+ if submit:
815
+ element.send_keys("\r\n")
816
+ time.sleep(0.044)
817
+ self.__slow_mode_pause_if_set()
818
+ self.loop.run_until_complete(self.page.wait())
819
+
820
+ def type(self, selector, text, timeout=None):
821
+ """Similar to send_keys(), but clears the text field first."""
822
+ if not timeout:
823
+ timeout = settings.SMALL_TIMEOUT
824
+ self.__slow_mode_pause_if_set()
825
+ element = self.select(selector, timeout=timeout)
826
+ element.scroll_into_view()
827
+ with suppress(Exception):
828
+ element.clear_input()
829
+ if text.endswith("\n") or text.endswith("\r"):
830
+ text = text[:-1] + "\r\n"
831
+ element.send_keys(text)
832
+ self.__slow_mode_pause_if_set()
833
+ self.loop.run_until_complete(self.page.wait())
834
+
835
+ def set_value(self, selector, text, timeout=None):
836
+ """Similar to send_keys(), but clears the text field first."""
837
+ if not timeout:
838
+ timeout = settings.SMALL_TIMEOUT
839
+ self.__slow_mode_pause_if_set()
840
+ selector = self.__convert_to_css_if_xpath(selector)
841
+ element = self.select(selector, timeout=timeout)
842
+ element.scroll_into_view()
843
+ press_enter = False
844
+ if text.endswith("\n"):
845
+ text = text[:-1]
846
+ press_enter = True
847
+ value = js_utils.escape_quotes_if_needed(re.escape(text))
848
+ css_selector = re.escape(selector)
849
+ css_selector = js_utils.escape_quotes_if_needed(css_selector)
850
+ set_value_script = (
851
+ """m_elm = document.querySelector('%s');"""
852
+ """m_elm.value = '%s';""" % (css_selector, value)
853
+ )
854
+ self.loop.run_until_complete(self.page.evaluate(set_value_script))
855
+ the_type = self.get_element_attribute(selector, "type")
856
+ if the_type == "range":
857
+ # Some input sliders need a mouse event to trigger listeners.
858
+ with suppress(Exception):
859
+ mouse_move_script = (
860
+ """m_elm = document.querySelector('%s');"""
861
+ """m_evt = new Event('mousemove');"""
862
+ """m_elm.dispatchEvent(m_evt);""" % css_selector
863
+ )
864
+ self.loop.run_until_complete(
865
+ self.page.evaluate(mouse_move_script)
866
+ )
867
+ elif press_enter:
868
+ self.__add_light_pause()
869
+ self.send_keys(selector, "\n")
870
+ self.__slow_mode_pause_if_set()
871
+ self.loop.run_until_complete(self.page.wait())
872
+
873
+ def evaluate(self, expression):
874
+ """Run a JavaScript expression and return the result."""
875
+ if expression.startswith("return "):
876
+ expression = expression[len("return "):]
877
+ return self.loop.run_until_complete(
878
+ self.page.evaluate(expression)
879
+ )
880
+
881
+ def js_dumps(self, obj_name):
882
+ """Similar to evaluate(), but for dictionary results."""
883
+ if obj_name.startswith("return "):
884
+ obj_name = obj_name[len("return "):]
885
+ return self.loop.run_until_complete(
886
+ self.page.js_dumps(obj_name)
887
+ )
888
+
889
+ def maximize(self):
890
+ if self.get_window()[1].window_state.value == "maximized":
891
+ return
892
+ elif self.get_window()[1].window_state.value == "minimized":
893
+ self.loop.run_until_complete(self.page.maximize())
894
+ time.sleep(0.044)
895
+ return self.loop.run_until_complete(self.page.maximize())
896
+
897
+ def minimize(self):
898
+ if self.get_window()[1].window_state.value != "minimized":
899
+ return self.loop.run_until_complete(self.page.minimize())
900
+
901
+ def medimize(self):
902
+ if self.get_window()[1].window_state.value == "minimized":
903
+ self.loop.run_until_complete(self.page.medimize())
904
+ time.sleep(0.044)
905
+ return self.loop.run_until_complete(self.page.medimize())
906
+
907
+ def set_window_rect(self, x, y, width, height):
908
+ if self.get_window()[1].window_state.value == "minimized":
909
+ self.loop.run_until_complete(
910
+ self.page.set_window_size(
911
+ left=x, top=y, width=width, height=height)
912
+ )
913
+ time.sleep(0.044)
914
+ return self.loop.run_until_complete(
915
+ self.page.set_window_size(
916
+ left=x, top=y, width=width, height=height)
917
+ )
918
+
919
+ def reset_window_size(self):
920
+ x = settings.WINDOW_START_X
921
+ y = settings.WINDOW_START_Y
922
+ width = settings.CHROME_START_WIDTH
923
+ height = settings.CHROME_START_HEIGHT
924
+ self.set_window_rect(x, y, width, height)
925
+ self.__add_light_pause()
926
+
927
+ def get_window(self):
928
+ return self.loop.run_until_complete(
929
+ self.page.get_window()
930
+ )
931
+
932
+ def get_text(self, selector):
933
+ return self.find_element(selector).text_all
934
+
935
+ def get_title(self):
936
+ return self.loop.run_until_complete(
937
+ self.page.evaluate("document.title")
938
+ )
939
+
940
+ def get_current_url(self):
941
+ return self.loop.run_until_complete(
942
+ self.page.evaluate("window.location.href")
943
+ )
944
+
945
+ def get_origin(self):
946
+ return self.loop.run_until_complete(
947
+ self.page.evaluate("window.location.origin")
948
+ )
949
+
950
+ def get_page_source(self):
951
+ try:
952
+ source = self.loop.run_until_complete(
953
+ self.page.evaluate("document.documentElement.outerHTML")
954
+ )
955
+ except Exception:
956
+ time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
957
+ source = self.loop.run_until_complete(
958
+ self.page.evaluate("document.documentElement.outerHTML")
959
+ )
960
+ return source
961
+
962
+ def get_user_agent(self):
963
+ return self.loop.run_until_complete(
964
+ self.page.evaluate("navigator.userAgent")
965
+ )
966
+
967
+ def get_cookie_string(self):
968
+ return self.loop.run_until_complete(
969
+ self.page.evaluate("document.cookie")
970
+ )
971
+
972
+ def get_locale_code(self):
973
+ return self.loop.run_until_complete(
974
+ self.page.evaluate("navigator.language || navigator.languages[0]")
975
+ )
976
+
977
+ def get_screen_rect(self):
978
+ coordinates = self.loop.run_until_complete(
979
+ self.page.js_dumps("window.screen")
980
+ )
981
+ return coordinates
982
+
983
+ def get_window_rect(self):
984
+ coordinates = {}
985
+ innerWidth = self.loop.run_until_complete(
986
+ self.page.evaluate("window.innerWidth")
987
+ )
988
+ innerHeight = self.loop.run_until_complete(
989
+ self.page.evaluate("window.innerHeight")
990
+ )
991
+ outerWidth = self.loop.run_until_complete(
992
+ self.page.evaluate("window.outerWidth")
993
+ )
994
+ outerHeight = self.loop.run_until_complete(
995
+ self.page.evaluate("window.outerHeight")
996
+ )
997
+ pageXOffset = self.loop.run_until_complete(
998
+ self.page.evaluate("window.pageXOffset")
999
+ )
1000
+ pageYOffset = self.loop.run_until_complete(
1001
+ self.page.evaluate("window.pageYOffset")
1002
+ )
1003
+ scrollX = self.loop.run_until_complete(
1004
+ self.page.evaluate("window.scrollX")
1005
+ )
1006
+ scrollY = self.loop.run_until_complete(
1007
+ self.page.evaluate("window.scrollY")
1008
+ )
1009
+ screenLeft = self.loop.run_until_complete(
1010
+ self.page.evaluate("window.screenLeft")
1011
+ )
1012
+ screenTop = self.loop.run_until_complete(
1013
+ self.page.evaluate("window.screenTop")
1014
+ )
1015
+ x = self.loop.run_until_complete(
1016
+ self.page.evaluate("window.screenX")
1017
+ )
1018
+ y = self.loop.run_until_complete(
1019
+ self.page.evaluate("window.screenY")
1020
+ )
1021
+ coordinates["innerWidth"] = innerWidth
1022
+ coordinates["innerHeight"] = innerHeight
1023
+ coordinates["outerWidth"] = outerWidth
1024
+ coordinates["outerHeight"] = outerHeight
1025
+ coordinates["width"] = outerWidth
1026
+ coordinates["height"] = outerHeight
1027
+ coordinates["pageXOffset"] = pageXOffset if pageXOffset else 0
1028
+ coordinates["pageYOffset"] = pageYOffset if pageYOffset else 0
1029
+ coordinates["scrollX"] = scrollX if scrollX else 0
1030
+ coordinates["scrollY"] = scrollY if scrollY else 0
1031
+ coordinates["screenLeft"] = screenLeft if screenLeft else 0
1032
+ coordinates["screenTop"] = screenTop if screenTop else 0
1033
+ coordinates["x"] = x if x else 0
1034
+ coordinates["y"] = y if y else 0
1035
+ return coordinates
1036
+
1037
+ def get_window_size(self):
1038
+ coordinates = {}
1039
+ outerWidth = self.loop.run_until_complete(
1040
+ self.page.evaluate("window.outerWidth")
1041
+ )
1042
+ outerHeight = self.loop.run_until_complete(
1043
+ self.page.evaluate("window.outerHeight")
1044
+ )
1045
+ coordinates["width"] = outerWidth
1046
+ coordinates["height"] = outerHeight
1047
+ return coordinates
1048
+
1049
+ def get_window_position(self):
1050
+ coordinates = {}
1051
+ x = self.loop.run_until_complete(
1052
+ self.page.evaluate("window.screenX")
1053
+ )
1054
+ y = self.loop.run_until_complete(
1055
+ self.page.evaluate("window.screenY")
1056
+ )
1057
+ coordinates["x"] = x if x else 0
1058
+ coordinates["y"] = y if y else 0
1059
+ return coordinates
1060
+
1061
+ def get_element_rect(self, selector, timeout=None):
1062
+ if not timeout:
1063
+ timeout = settings.SMALL_TIMEOUT
1064
+ selector = self.__convert_to_css_if_xpath(selector)
1065
+ self.select(selector, timeout=timeout)
1066
+ self.__add_light_pause()
1067
+ coordinates = self.loop.run_until_complete(
1068
+ self.page.js_dumps(
1069
+ """document.querySelector"""
1070
+ """('%s').getBoundingClientRect()"""
1071
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
1072
+ )
1073
+ )
1074
+ return coordinates
1075
+
1076
+ def get_element_size(self, selector, timeout=None):
1077
+ if not timeout:
1078
+ timeout = settings.SMALL_TIMEOUT
1079
+ element_rect = self.get_element_rect(selector, timeout=timeout)
1080
+ coordinates = {}
1081
+ coordinates["width"] = element_rect["width"]
1082
+ coordinates["height"] = element_rect["height"]
1083
+ return coordinates
1084
+
1085
+ def get_element_position(self, selector, timeout=None):
1086
+ if not timeout:
1087
+ timeout = settings.SMALL_TIMEOUT
1088
+ element_rect = self.get_element_rect(selector, timeout=timeout)
1089
+ coordinates = {}
1090
+ coordinates["x"] = element_rect["x"]
1091
+ coordinates["y"] = element_rect["y"]
1092
+ return coordinates
1093
+
1094
+ def get_gui_element_rect(self, selector, timeout=None):
1095
+ """(Coordinates are relative to the screen. Not the window.)"""
1096
+ if not timeout:
1097
+ timeout = settings.SMALL_TIMEOUT
1098
+ element_rect = self.get_element_rect(selector, timeout=timeout)
1099
+ e_width = element_rect["width"]
1100
+ e_height = element_rect["height"]
1101
+ window_rect = self.get_window_rect()
1102
+ w_bottom_y = window_rect["y"] + window_rect["height"]
1103
+ viewport_height = window_rect["innerHeight"]
1104
+ x = window_rect["x"] + element_rect["x"]
1105
+ y = w_bottom_y - viewport_height + element_rect["y"]
1106
+ y_scroll_offset = window_rect["pageYOffset"]
1107
+ y = y - y_scroll_offset
1108
+ x = x + window_rect["scrollX"]
1109
+ y = y + window_rect["scrollY"]
1110
+ return ({"height": e_height, "width": e_width, "x": x, "y": y})
1111
+
1112
+ def get_gui_element_center(self, selector, timeout=None):
1113
+ """(Coordinates are relative to the screen. Not the window.)"""
1114
+ if not timeout:
1115
+ timeout = settings.SMALL_TIMEOUT
1116
+ element_rect = self.get_gui_element_rect(selector, timeout=timeout)
1117
+ e_width = element_rect["width"]
1118
+ e_height = element_rect["height"]
1119
+ e_x = element_rect["x"]
1120
+ e_y = element_rect["y"]
1121
+ return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5)
1122
+
1123
+ def get_document(self):
1124
+ return self.loop.run_until_complete(
1125
+ self.page.get_document()
1126
+ )
1127
+
1128
+ def get_flattened_document(self):
1129
+ return self.loop.run_until_complete(
1130
+ self.page.get_flattened_document()
1131
+ )
1132
+
1133
+ def get_element_attributes(self, selector):
1134
+ selector = self.__convert_to_css_if_xpath(selector)
1135
+ return self.loop.run_until_complete(
1136
+ self.page.js_dumps(
1137
+ """document.querySelector('%s')"""
1138
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
1139
+ )
1140
+ )
1141
+
1142
+ def get_element_attribute(self, selector, attribute):
1143
+ attributes = self.get_element_attributes(selector)
1144
+ with suppress(Exception):
1145
+ return attributes[attribute]
1146
+ locate = ' %s="' % attribute
1147
+ value = self.get_attribute(selector, attribute)
1148
+ if not value and locate not in attributes:
1149
+ raise KeyError(attribute)
1150
+ return value
1151
+
1152
+ def get_attribute(self, selector, attribute):
1153
+ return self.find_element(selector).get_attribute(attribute)
1154
+
1155
+ def get_element_html(self, selector):
1156
+ selector = self.__convert_to_css_if_xpath(selector)
1157
+ return self.loop.run_until_complete(
1158
+ self.page.evaluate(
1159
+ """document.querySelector('%s').outerHTML"""
1160
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
1161
+ )
1162
+ )
1163
+
1164
+ def set_locale(self, locale):
1165
+ """(Settings will take effect on the next page load)"""
1166
+ self.loop.run_until_complete(self.page.set_locale(locale))
1167
+
1168
+ def set_attributes(self, selector, attribute, value):
1169
+ """This method uses JavaScript to set/update a common attribute.
1170
+ All matching selectors from querySelectorAll() are used.
1171
+ Example => (Make all links on a website redirect to Google):
1172
+ self.set_attributes("a", "href", "https://google.com")"""
1173
+ attribute = re.escape(attribute)
1174
+ attribute = js_utils.escape_quotes_if_needed(attribute)
1175
+ value = re.escape(value)
1176
+ value = js_utils.escape_quotes_if_needed(value)
1177
+ css_selector = self.__convert_to_css_if_xpath(selector)
1178
+ css_selector = re.escape(css_selector) # Add "\\" to special chars
1179
+ css_selector = js_utils.escape_quotes_if_needed(css_selector)
1180
+ js_code = """var $elements = document.querySelectorAll('%s');
1181
+ var index = 0, length = $elements.length;
1182
+ for(; index < length; index++){
1183
+ $elements[index].setAttribute('%s','%s');}""" % (
1184
+ css_selector,
1185
+ attribute,
1186
+ value,
1187
+ )
1188
+ with suppress(Exception):
1189
+ self.loop.run_until_complete(self.page.evaluate(js_code))
1190
+
1191
+ def __make_sure_pyautogui_lock_is_writable(self):
1192
+ with suppress(Exception):
1193
+ shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)
1194
+
1195
+ def __verify_pyautogui_has_a_headed_browser(self):
1196
+ """PyAutoGUI requires a headed browser so that it can
1197
+ focus on the correct element when performing actions."""
1198
+ driver = self.driver
1199
+ if hasattr(driver, "cdp_base"):
1200
+ driver = driver.cdp_base
1201
+ if driver.config.headless:
1202
+ raise Exception(
1203
+ "PyAutoGUI can't be used in headless mode!"
1204
+ )
1205
+
1206
+ def __install_pyautogui_if_missing(self):
1207
+ self.__verify_pyautogui_has_a_headed_browser()
1208
+ driver = self.driver
1209
+ if hasattr(driver, "cdp_base"):
1210
+ driver = driver.cdp_base
1211
+ pip_find_lock = fasteners.InterProcessLock(
1212
+ constants.PipInstall.FINDLOCK
1213
+ )
1214
+ with pip_find_lock: # Prevent issues with multiple processes
1215
+ with suppress(Exception):
1216
+ shared_utils.make_writable(constants.PipInstall.FINDLOCK)
1217
+ try:
1218
+ import pyautogui
1219
+ with suppress(Exception):
1220
+ use_pyautogui_ver = constants.PyAutoGUI.VER
1221
+ if pyautogui.__version__ != use_pyautogui_ver:
1222
+ del pyautogui
1223
+ shared_utils.pip_install(
1224
+ "pyautogui", version=use_pyautogui_ver
1225
+ )
1226
+ import pyautogui
1227
+ except Exception:
1228
+ print("\nPyAutoGUI required! Installing now...")
1229
+ shared_utils.pip_install(
1230
+ "pyautogui", version=constants.PyAutoGUI.VER
1231
+ )
1232
+ try:
1233
+ import pyautogui
1234
+ except Exception:
1235
+ if (
1236
+ shared_utils.is_linux()
1237
+ and (not sb_config.headed or sb_config.xvfb)
1238
+ and not driver.config.headless
1239
+ ):
1240
+ from sbvirtualdisplay import Display
1241
+ xvfb_width = 1366
1242
+ xvfb_height = 768
1243
+ if (
1244
+ hasattr(sb_config, "_xvfb_width")
1245
+ and sb_config._xvfb_width
1246
+ and isinstance(sb_config._xvfb_width, int)
1247
+ and hasattr(sb_config, "_xvfb_height")
1248
+ and sb_config._xvfb_height
1249
+ and isinstance(sb_config._xvfb_height, int)
1250
+ ):
1251
+ xvfb_width = sb_config._xvfb_width
1252
+ xvfb_height = sb_config._xvfb_height
1253
+ if xvfb_width < 1024:
1254
+ xvfb_width = 1024
1255
+ sb_config._xvfb_width = xvfb_width
1256
+ if xvfb_height < 768:
1257
+ xvfb_height = 768
1258
+ sb_config._xvfb_height = xvfb_height
1259
+ with suppress(Exception):
1260
+ xvfb_display = Display(
1261
+ visible=True,
1262
+ size=(xvfb_width, xvfb_height),
1263
+ backend="xvfb",
1264
+ use_xauth=True,
1265
+ )
1266
+ xvfb_display.start()
1267
+
1268
+ def __get_configured_pyautogui(self, pyautogui_copy):
1269
+ if (
1270
+ shared_utils.is_linux()
1271
+ and hasattr(pyautogui_copy, "_pyautogui_x11")
1272
+ and "DISPLAY" in os.environ.keys()
1273
+ ):
1274
+ if (
1275
+ hasattr(sb_config, "_pyautogui_x11_display")
1276
+ and sb_config._pyautogui_x11_display
1277
+ and hasattr(pyautogui_copy._pyautogui_x11, "_display")
1278
+ and (
1279
+ sb_config._pyautogui_x11_display
1280
+ == pyautogui_copy._pyautogui_x11._display
1281
+ )
1282
+ ):
1283
+ pass
1284
+ else:
1285
+ import Xlib.display
1286
+ pyautogui_copy._pyautogui_x11._display = (
1287
+ Xlib.display.Display(os.environ['DISPLAY'])
1288
+ )
1289
+ sb_config._pyautogui_x11_display = (
1290
+ pyautogui_copy._pyautogui_x11._display
1291
+ )
1292
+ return pyautogui_copy
1293
+
1294
+ def gui_press_key(self, key):
1295
+ self.__install_pyautogui_if_missing()
1296
+ import pyautogui
1297
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1298
+ gui_lock = fasteners.InterProcessLock(
1299
+ constants.MultiBrowser.PYAUTOGUILOCK
1300
+ )
1301
+ with gui_lock:
1302
+ self.__make_sure_pyautogui_lock_is_writable()
1303
+ pyautogui.press(key)
1304
+ time.sleep(0.044)
1305
+ self.__slow_mode_pause_if_set()
1306
+ self.loop.run_until_complete(self.page.wait())
1307
+
1308
+ def gui_press_keys(self, keys):
1309
+ self.__install_pyautogui_if_missing()
1310
+ import pyautogui
1311
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1312
+ gui_lock = fasteners.InterProcessLock(
1313
+ constants.MultiBrowser.PYAUTOGUILOCK
1314
+ )
1315
+ with gui_lock:
1316
+ self.__make_sure_pyautogui_lock_is_writable()
1317
+ for key in keys:
1318
+ pyautogui.press(key)
1319
+ time.sleep(0.044)
1320
+ self.__slow_mode_pause_if_set()
1321
+ self.loop.run_until_complete(self.page.wait())
1322
+
1323
+ def gui_write(self, text):
1324
+ self.__install_pyautogui_if_missing()
1325
+ import pyautogui
1326
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1327
+ gui_lock = fasteners.InterProcessLock(
1328
+ constants.MultiBrowser.PYAUTOGUILOCK
1329
+ )
1330
+ with gui_lock:
1331
+ self.__make_sure_pyautogui_lock_is_writable()
1332
+ pyautogui.write(text)
1333
+ self.__slow_mode_pause_if_set()
1334
+ self.loop.run_until_complete(self.page.wait())
1335
+
1336
+ def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False):
1337
+ self.__install_pyautogui_if_missing()
1338
+ import pyautogui
1339
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1340
+ screen_width, screen_height = pyautogui.size()
1341
+ if x < 0 or y < 0 or x > screen_width or y > screen_height:
1342
+ raise Exception(
1343
+ "PyAutoGUI cannot click on point (%s, %s)"
1344
+ " outside screen. (Width: %s, Height: %s)"
1345
+ % (x, y, screen_width, screen_height)
1346
+ )
1347
+ if uc_lock:
1348
+ gui_lock = fasteners.InterProcessLock(
1349
+ constants.MultiBrowser.PYAUTOGUILOCK
1350
+ )
1351
+ with gui_lock: # Prevent issues with multiple processes
1352
+ self.__make_sure_pyautogui_lock_is_writable()
1353
+ pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
1354
+ if timeframe >= 0.25:
1355
+ time.sleep(0.056) # Wait if moving at human-speed
1356
+ if "--debug" in sys.argv:
1357
+ print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
1358
+ pyautogui.click(x=x, y=y)
1359
+ else:
1360
+ # Called from a method where the gui_lock is already active
1361
+ pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
1362
+ if timeframe >= 0.25:
1363
+ time.sleep(0.056) # Wait if moving at human-speed
1364
+ if "--debug" in sys.argv:
1365
+ print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y))
1366
+ pyautogui.click(x=x, y=y)
1367
+
1368
+ def gui_click_x_y(self, x, y, timeframe=0.25):
1369
+ gui_lock = fasteners.InterProcessLock(
1370
+ constants.MultiBrowser.PYAUTOGUILOCK
1371
+ )
1372
+ with gui_lock: # Prevent issues with multiple processes
1373
+ self.__make_sure_pyautogui_lock_is_writable()
1374
+ self.__install_pyautogui_if_missing()
1375
+ import pyautogui
1376
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1377
+ width_ratio = 1.0
1378
+ if shared_utils.is_windows():
1379
+ window_rect = self.get_window_rect()
1380
+ width = window_rect["width"]
1381
+ height = window_rect["height"]
1382
+ win_x = window_rect["x"]
1383
+ win_y = window_rect["y"]
1384
+ scr_width = pyautogui.size().width
1385
+ self.maximize()
1386
+ self.__add_light_pause()
1387
+ win_width = self.get_window_size()["width"]
1388
+ width_ratio = round(float(scr_width) / float(win_width), 2)
1389
+ width_ratio += 0.01
1390
+ if width_ratio < 0.45 or width_ratio > 2.55:
1391
+ width_ratio = 1.01
1392
+ sb_config._saved_width_ratio = width_ratio
1393
+ self.minimize()
1394
+ self.__add_light_pause()
1395
+ self.set_window_rect(win_x, win_y, width, height)
1396
+ self.__add_light_pause()
1397
+ x = x * width_ratio
1398
+ y = y * width_ratio
1399
+ self.bring_active_window_to_front()
1400
+ self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False)
1401
+
1402
+ def gui_click_element(self, selector, timeframe=0.25):
1403
+ self.__slow_mode_pause_if_set()
1404
+ x, y = self.get_gui_element_center(selector)
1405
+ self.__add_light_pause()
1406
+ self.gui_click_x_y(x, y, timeframe=timeframe)
1407
+ self.__slow_mode_pause_if_set()
1408
+ self.loop.run_until_complete(self.page.wait())
1409
+
1410
+ def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False):
1411
+ self.__install_pyautogui_if_missing()
1412
+ import pyautogui
1413
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1414
+ screen_width, screen_height = pyautogui.size()
1415
+ if x1 < 0 or y1 < 0 or x1 > screen_width or y1 > screen_height:
1416
+ raise Exception(
1417
+ "PyAutoGUI cannot drag-drop from point (%s, %s)"
1418
+ " outside screen. (Width: %s, Height: %s)"
1419
+ % (x1, y1, screen_width, screen_height)
1420
+ )
1421
+ if x2 < 0 or y2 < 0 or x2 > screen_width or y2 > screen_height:
1422
+ raise Exception(
1423
+ "PyAutoGUI cannot drag-drop to point (%s, %s)"
1424
+ " outside screen. (Width: %s, Height: %s)"
1425
+ % (x2, y2, screen_width, screen_height)
1426
+ )
1427
+ if uc_lock:
1428
+ gui_lock = fasteners.InterProcessLock(
1429
+ constants.MultiBrowser.PYAUTOGUILOCK
1430
+ )
1431
+ with gui_lock: # Prevent issues with multiple processes
1432
+ pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
1433
+ self.__add_light_pause()
1434
+ if "--debug" in sys.argv:
1435
+ print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1))
1436
+ pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
1437
+ else:
1438
+ # Called from a method where the gui_lock is already active
1439
+ pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad)
1440
+ self.__add_light_pause()
1441
+ if "--debug" in sys.argv:
1442
+ print(" <DEBUG> pyautogui.dragTo(%s, %s)" % (x2, y2))
1443
+ pyautogui.dragTo(x2, y2, button="left", duration=timeframe)
1444
+
1445
+ def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35):
1446
+ gui_lock = fasteners.InterProcessLock(
1447
+ constants.MultiBrowser.PYAUTOGUILOCK
1448
+ )
1449
+ with gui_lock: # Prevent issues with multiple processes
1450
+ self.__install_pyautogui_if_missing()
1451
+ import pyautogui
1452
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1453
+ width_ratio = 1.0
1454
+ if shared_utils.is_windows():
1455
+ window_rect = self.get_window_rect()
1456
+ width = window_rect["width"]
1457
+ height = window_rect["height"]
1458
+ win_x = window_rect["x"]
1459
+ win_y = window_rect["y"]
1460
+ scr_width = pyautogui.size().width
1461
+ self.maximize()
1462
+ self.__add_light_pause()
1463
+ win_width = self.get_window_size()["width"]
1464
+ width_ratio = round(float(scr_width) / float(win_width), 2)
1465
+ width_ratio += 0.01
1466
+ if width_ratio < 0.45 or width_ratio > 2.55:
1467
+ width_ratio = 1.01
1468
+ sb_config._saved_width_ratio = width_ratio
1469
+ self.minimize()
1470
+ self.__add_light_pause()
1471
+ self.set_window_rect(win_x, win_y, width, height)
1472
+ self.__add_light_pause()
1473
+ x1 = x1 * width_ratio
1474
+ y1 = y1 * width_ratio
1475
+ x2 = x2 * width_ratio
1476
+ y2 = y2 * width_ratio
1477
+ self.bring_active_window_to_front()
1478
+ self.__gui_drag_drop(
1479
+ x1, y1, x2, y2, timeframe=timeframe, uc_lock=False
1480
+ )
1481
+ self.__slow_mode_pause_if_set()
1482
+ self.loop.run_until_complete(self.page.wait())
1483
+
1484
+ def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35):
1485
+ self.__slow_mode_pause_if_set()
1486
+ self.bring_active_window_to_front()
1487
+ x1, y1 = self.get_gui_element_center(drag_selector)
1488
+ self.__add_light_pause()
1489
+ x2, y2 = self.get_gui_element_center(drop_selector)
1490
+ self.__add_light_pause()
1491
+ self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=timeframe)
1492
+
1493
+ def __gui_hover_x_y(self, x, y, timeframe=0.25, uc_lock=False):
1494
+ self.__install_pyautogui_if_missing()
1495
+ import pyautogui
1496
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1497
+ screen_width, screen_height = pyautogui.size()
1498
+ if x < 0 or y < 0 or x > screen_width or y > screen_height:
1499
+ raise Exception(
1500
+ "PyAutoGUI cannot hover on point (%s, %s)"
1501
+ " outside screen. (Width: %s, Height: %s)"
1502
+ % (x, y, screen_width, screen_height)
1503
+ )
1504
+ if uc_lock:
1505
+ gui_lock = fasteners.InterProcessLock(
1506
+ constants.MultiBrowser.PYAUTOGUILOCK
1507
+ )
1508
+ with gui_lock: # Prevent issues with multiple processes
1509
+ pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
1510
+ time.sleep(0.056)
1511
+ if "--debug" in sys.argv:
1512
+ print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
1513
+ else:
1514
+ # Called from a method where the gui_lock is already active
1515
+ pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
1516
+ time.sleep(0.056)
1517
+ if "--debug" in sys.argv:
1518
+ print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y))
1519
+
1520
+ def gui_hover_x_y(self, x, y, timeframe=0.25):
1521
+ gui_lock = fasteners.InterProcessLock(
1522
+ constants.MultiBrowser.PYAUTOGUILOCK
1523
+ )
1524
+ with gui_lock: # Prevent issues with multiple processes
1525
+ self.__install_pyautogui_if_missing()
1526
+ import pyautogui
1527
+ pyautogui = self.__get_configured_pyautogui(pyautogui)
1528
+ width_ratio = 1.0
1529
+ if (
1530
+ shared_utils.is_windows()
1531
+ and (
1532
+ not hasattr(sb_config, "_saved_width_ratio")
1533
+ or not sb_config._saved_width_ratio
1534
+ )
1535
+ ):
1536
+ window_rect = self.get_window_rect()
1537
+ width = window_rect["width"]
1538
+ height = window_rect["height"]
1539
+ win_x = window_rect["x"]
1540
+ win_y = window_rect["y"]
1541
+ if (
1542
+ hasattr(sb_config, "_saved_width_ratio")
1543
+ and sb_config._saved_width_ratio
1544
+ ):
1545
+ width_ratio = sb_config._saved_width_ratio
1546
+ else:
1547
+ scr_width = pyautogui.size().width
1548
+ self.maximize()
1549
+ self.__add_light_pause()
1550
+ win_width = self.get_window_size()["width"]
1551
+ width_ratio = round(float(scr_width) / float(win_width), 2)
1552
+ width_ratio += 0.01
1553
+ if width_ratio < 0.45 or width_ratio > 2.55:
1554
+ width_ratio = 1.01
1555
+ sb_config._saved_width_ratio = width_ratio
1556
+ self.set_window_rect(win_x, win_y, width, height)
1557
+ self.__add_light_pause()
1558
+ self.bring_active_window_to_front()
1559
+ elif (
1560
+ shared_utils.is_windows()
1561
+ and hasattr(sb_config, "_saved_width_ratio")
1562
+ and sb_config._saved_width_ratio
1563
+ ):
1564
+ width_ratio = sb_config._saved_width_ratio
1565
+ self.bring_active_window_to_front()
1566
+ if shared_utils.is_windows():
1567
+ x = x * width_ratio
1568
+ y = y * width_ratio
1569
+ self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
1570
+ return
1571
+ self.bring_active_window_to_front()
1572
+ self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False)
1573
+
1574
+ def gui_hover_element(self, selector, timeframe=0.25):
1575
+ self.__slow_mode_pause_if_set()
1576
+ element_rect = self.get_gui_element_rect(selector)
1577
+ width = element_rect["width"]
1578
+ height = element_rect["height"]
1579
+ if width > 0 and height > 0:
1580
+ x, y = self.get_gui_element_center(selector)
1581
+ self.bring_active_window_to_front()
1582
+ self.__gui_hover_x_y(x, y, timeframe=timeframe)
1583
+ self.__slow_mode_pause_if_set()
1584
+ self.loop.run_until_complete(self.page.wait())
1585
+
1586
+ def gui_hover_and_click(self, hover_selector, click_selector):
1587
+ gui_lock = fasteners.InterProcessLock(
1588
+ constants.MultiBrowser.PYAUTOGUILOCK
1589
+ )
1590
+ with gui_lock:
1591
+ self.__make_sure_pyautogui_lock_is_writable()
1592
+ self.bring_active_window_to_front()
1593
+ self.gui_hover_element(hover_selector)
1594
+ time.sleep(0.15)
1595
+ self.gui_hover_element(click_selector)
1596
+ self.click(click_selector)
1597
+
1598
+ def internalize_links(self):
1599
+ """All `target="_blank"` links become `target="_self"`.
1600
+ This prevents those links from opening in a new tab."""
1601
+ self.set_attributes('[target="_blank"]', "target", "_self")
1602
+
1603
+ def is_checked(self, selector):
1604
+ """Return True if checkbox (or radio button) is checked."""
1605
+ selector = self.__convert_to_css_if_xpath(selector)
1606
+ self.find_element(selector, timeout=settings.SMALL_TIMEOUT)
1607
+ return self.get_element_attribute(selector, "checked")
1608
+
1609
+ def is_selected(self, selector):
1610
+ selector = self.__convert_to_css_if_xpath(selector)
1611
+ return self.is_checked(selector)
1612
+
1613
+ def check_if_unchecked(self, selector):
1614
+ selector = self.__convert_to_css_if_xpath(selector)
1615
+ if not self.is_checked(selector):
1616
+ self.click(selector)
1617
+
1618
+ def select_if_unselected(self, selector):
1619
+ selector = self.__convert_to_css_if_xpath(selector)
1620
+ self.check_if_unchecked(selector)
1621
+
1622
+ def uncheck_if_checked(self, selector):
1623
+ selector = self.__convert_to_css_if_xpath(selector)
1624
+ if self.is_checked(selector):
1625
+ self.click(selector)
1626
+
1627
+ def unselect_if_selected(self, selector):
1628
+ selector = self.__convert_to_css_if_xpath(selector)
1629
+ self.uncheck_if_checked(selector)
1630
+
1631
+ def is_element_present(self, selector):
1632
+ try:
1633
+ self.select(selector, timeout=0.01)
1634
+ return True
1635
+ except Exception:
1636
+ return False
1637
+
1638
+ def is_element_visible(self, selector):
1639
+ selector = self.__convert_to_css_if_xpath(selector)
1640
+ element = None
1641
+ if ":contains(" not in selector:
1642
+ try:
1643
+ element = self.select(selector, timeout=0.01)
1644
+ except Exception:
1645
+ return False
1646
+ if not element:
1647
+ return False
1648
+ try:
1649
+ position = element.get_position()
1650
+ return (position.width != 0 or position.height != 0)
1651
+ except Exception:
1652
+ return False
1653
+ else:
1654
+ with suppress(Exception):
1655
+ tag_name = selector.split(":contains(")[0].split(" ")[-1]
1656
+ text = selector.split(":contains(")[1].split(")")[0][1:-1]
1657
+ self.loop.run_until_complete(
1658
+ self.page.select(tag_name, timeout=0.1)
1659
+ )
1660
+ self.loop.run_until_complete(self.page.find(text, timeout=0.1))
1661
+ return True
1662
+ return False
1663
+
1664
+ def wait_for_element_visible(self, selector, timeout=None):
1665
+ if not timeout:
1666
+ timeout = settings.SMALL_TIMEOUT
1667
+ try:
1668
+ self.select(selector, timeout=timeout)
1669
+ except Exception:
1670
+ raise Exception("Element {%s} was not found!" % selector)
1671
+ for i in range(30):
1672
+ if self.is_element_visible(selector):
1673
+ return self.select(selector)
1674
+ time.sleep(0.1)
1675
+ raise Exception("Element {%s} was not visible!" % selector)
1676
+
1677
+ def assert_element(self, selector, timeout=None):
1678
+ """Same as assert_element_visible()"""
1679
+ if not timeout:
1680
+ timeout = settings.SMALL_TIMEOUT
1681
+ try:
1682
+ self.select(selector, timeout=timeout)
1683
+ except Exception:
1684
+ raise Exception("Element {%s} was not found!" % selector)
1685
+ for i in range(30):
1686
+ if self.is_element_visible(selector):
1687
+ return True
1688
+ time.sleep(0.1)
1689
+ raise Exception("Element {%s} was not visible!" % selector)
1690
+
1691
+ def assert_element_visible(self, selector, timeout=None):
1692
+ """Same as assert_element()"""
1693
+ if not timeout:
1694
+ timeout = settings.SMALL_TIMEOUT
1695
+ try:
1696
+ self.select(selector, timeout=timeout)
1697
+ except Exception:
1698
+ raise Exception("Element {%s} was not found!" % selector)
1699
+ for i in range(30):
1700
+ if self.is_element_visible(selector):
1701
+ return True
1702
+ time.sleep(0.1)
1703
+ raise Exception("Element {%s} was not visible!" % selector)
1704
+
1705
+ def assert_element_present(self, selector, timeout=None):
1706
+ """Assert element is present in the DOM. (Visibility NOT required)"""
1707
+ if not timeout:
1708
+ timeout = settings.SMALL_TIMEOUT
1709
+ try:
1710
+ self.select(selector, timeout=timeout)
1711
+ except Exception:
1712
+ raise Exception("Element {%s} was not found!" % selector)
1713
+ return True
1714
+
1715
+ def assert_element_absent(self, selector, timeout=None):
1716
+ """Assert element is not present in the DOM."""
1717
+ if not timeout:
1718
+ timeout = settings.SMALL_TIMEOUT
1719
+ start_ms = time.time() * 1000.0
1720
+ stop_ms = start_ms + (timeout * 1000.0)
1721
+ for i in range(int(timeout * 10)):
1722
+ if not self.is_element_present(selector):
1723
+ return True
1724
+ now_ms = time.time() * 1000.0
1725
+ if now_ms >= stop_ms:
1726
+ break
1727
+ time.sleep(0.1)
1728
+ plural = "s"
1729
+ if timeout == 1:
1730
+ plural = ""
1731
+ raise Exception(
1732
+ "Element {%s} was still present after %s second%s!"
1733
+ % (selector, timeout, plural)
1734
+ )
1735
+
1736
+ def assert_element_not_visible(self, selector, timeout=None):
1737
+ """Assert element is not visible on page. (May still be in DOM)"""
1738
+ if not timeout:
1739
+ timeout = settings.SMALL_TIMEOUT
1740
+ start_ms = time.time() * 1000.0
1741
+ stop_ms = start_ms + (timeout * 1000.0)
1742
+ for i in range(int(timeout * 10)):
1743
+ if not self.is_element_present(selector):
1744
+ return True
1745
+ elif not self.is_element_visible(selector):
1746
+ return True
1747
+ now_ms = time.time() * 1000.0
1748
+ if now_ms >= stop_ms:
1749
+ break
1750
+ time.sleep(0.1)
1751
+ plural = "s"
1752
+ if timeout == 1:
1753
+ plural = ""
1754
+ raise Exception(
1755
+ "Element {%s} was still visible after %s second%s!"
1756
+ % (selector, timeout, plural)
1757
+ )
1758
+
1759
+ def assert_element_attribute(self, selector, attribute, value=None):
1760
+ attributes = self.get_element_attributes(selector)
1761
+ if attribute not in attributes:
1762
+ raise Exception(
1763
+ "Attribute {%s} was not found in element {%s}!"
1764
+ % (attribute, selector)
1765
+ )
1766
+ if value and attributes[attribute] != value:
1767
+ raise Exception(
1768
+ "Expected value {%s} of attribute {%s} "
1769
+ "was not found in element {%s}! "
1770
+ "(Actual value was {%s})"
1771
+ % (value, attribute, selector, attributes[attribute])
1772
+ )
1773
+
1774
+ def assert_title(self, title):
1775
+ expected = title.strip()
1776
+ actual = self.get_title().strip()
1777
+ error = (
1778
+ "Expected page title [%s] does not match the actual title [%s]!"
1779
+ )
1780
+ try:
1781
+ if expected != actual:
1782
+ raise Exception(error % (expected, actual))
1783
+ except Exception:
1784
+ time.sleep(2)
1785
+ actual = self.get_title().strip()
1786
+ if expected != actual:
1787
+ raise Exception(error % (expected, actual))
1788
+
1789
+ def assert_title_contains(self, substring):
1790
+ expected = substring.strip()
1791
+ actual = self.get_title().strip()
1792
+ error = (
1793
+ "Expected title substring [%s] does not appear "
1794
+ "in the actual page title [%s]!"
1795
+ )
1796
+ try:
1797
+ if expected not in actual:
1798
+ raise Exception(error % (expected, actual))
1799
+ except Exception:
1800
+ time.sleep(2)
1801
+ actual = self.get_title().strip()
1802
+ if expected not in actual:
1803
+ raise Exception(error % (expected, actual))
1804
+
1805
+ def assert_url(self, url):
1806
+ expected = url.strip()
1807
+ actual = self.get_current_url().strip()
1808
+ error = "Expected URL [%s] does not match the actual URL [%s]!"
1809
+ try:
1810
+ if expected != actual:
1811
+ raise Exception(error % (expected, actual))
1812
+ except Exception:
1813
+ time.sleep(2)
1814
+ actual = self.get_current_url().strip()
1815
+ if expected != actual:
1816
+ raise Exception(error % (expected, actual))
1817
+
1818
+ def assert_url_contains(self, substring):
1819
+ expected = substring.strip()
1820
+ actual = self.get_current_url().strip()
1821
+ error = (
1822
+ "Expected URL substring [%s] does not appear "
1823
+ "in the full URL [%s]!"
1824
+ )
1825
+ try:
1826
+ if expected not in actual:
1827
+ raise Exception(error % (expected, actual))
1828
+ except Exception:
1829
+ time.sleep(2)
1830
+ actual = self.get_current_url().strip()
1831
+ if expected not in actual:
1832
+ raise Exception(error % (expected, actual))
1833
+
1834
+ def assert_text(self, text, selector="body", timeout=None):
1835
+ if not timeout:
1836
+ timeout = settings.SMALL_TIMEOUT
1837
+ start_ms = time.time() * 1000.0
1838
+ stop_ms = start_ms + (timeout * 1000.0)
1839
+ text = text.strip()
1840
+ element = None
1841
+ try:
1842
+ element = self.find_element(selector, timeout=timeout)
1843
+ except Exception:
1844
+ raise Exception("Element {%s} not found!" % selector)
1845
+ for i in range(int(timeout * 10)):
1846
+ with suppress(Exception):
1847
+ element = self.find_element(selector, timeout=0.1)
1848
+ if text in element.text_all:
1849
+ return True
1850
+ now_ms = time.time() * 1000.0
1851
+ if now_ms >= stop_ms:
1852
+ break
1853
+ time.sleep(0.1)
1854
+ raise Exception(
1855
+ "Text {%s} not found in {%s}! Actual text: {%s}"
1856
+ % (text, selector, element.text_all)
1857
+ )
1858
+
1859
+ def assert_exact_text(self, text, selector="body", timeout=None):
1860
+ if not timeout:
1861
+ timeout = settings.SMALL_TIMEOUT
1862
+ start_ms = time.time() * 1000.0
1863
+ stop_ms = start_ms + (timeout * 1000.0)
1864
+ text = text.strip()
1865
+ element = None
1866
+ try:
1867
+ element = self.select(selector, timeout=timeout)
1868
+ except Exception:
1869
+ raise Exception("Element {%s} not found!" % selector)
1870
+ for i in range(int(timeout * 10)):
1871
+ with suppress(Exception):
1872
+ element = self.select(selector, timeout=0.1)
1873
+ if (
1874
+ self.is_element_visible(selector)
1875
+ and text.strip() == element.text_all.strip()
1876
+ ):
1877
+ return True
1878
+ now_ms = time.time() * 1000.0
1879
+ if now_ms >= stop_ms:
1880
+ break
1881
+ time.sleep(0.1)
1882
+ raise Exception(
1883
+ "Expected Text {%s}, is not equal to {%s} in {%s}!"
1884
+ % (text, element.text_all, selector)
1885
+ )
1886
+
1887
+ def assert_true(self, expression):
1888
+ if not expression:
1889
+ raise AssertionError("%s is not true" % expression)
1890
+
1891
+ def assert_false(self, expression):
1892
+ if expression:
1893
+ raise AssertionError("%s is not false" % expression)
1894
+
1895
+ def assert_equal(self, first, second):
1896
+ if first != second:
1897
+ raise AssertionError("%s is not equal to %s" % (first, second))
1898
+
1899
+ def assert_not_equal(self, first, second):
1900
+ if first == second:
1901
+ raise AssertionError("%s is equal to %s" % (first, second))
1902
+
1903
+ def assert_in(self, first, second):
1904
+ if first not in second:
1905
+ raise AssertionError("%s is not in %s" % (first, second))
1906
+
1907
+ def assert_not_in(self, first, second):
1908
+ if first in second:
1909
+ raise AssertionError("%s is in %s" % (first, second))
1910
+
1911
+ def scroll_into_view(self, selector):
1912
+ self.find_element(selector).scroll_into_view()
1913
+ self.loop.run_until_complete(self.page.wait())
1914
+
1915
+ def scroll_to_y(self, y):
1916
+ y = int(y)
1917
+ js_code = "window.scrollTo(0, %s);" % y
1918
+ with suppress(Exception):
1919
+ self.loop.run_until_complete(self.page.evaluate(js_code))
1920
+ self.loop.run_until_complete(self.page.wait())
1921
+
1922
+ def scroll_to_top(self):
1923
+ js_code = "window.scrollTo(0, 0);"
1924
+ with suppress(Exception):
1925
+ self.loop.run_until_complete(self.page.evaluate(js_code))
1926
+ self.loop.run_until_complete(self.page.wait())
1927
+
1928
+ def scroll_to_bottom(self):
1929
+ js_code = "window.scrollTo(0, 10000);"
1930
+ with suppress(Exception):
1931
+ self.loop.run_until_complete(self.page.evaluate(js_code))
1932
+ self.loop.run_until_complete(self.page.wait())
1933
+
1934
+ def scroll_up(self, amount=25):
1935
+ self.loop.run_until_complete(self.page.scroll_up(amount))
1936
+ self.loop.run_until_complete(self.page.wait())
1937
+
1938
+ def scroll_down(self, amount=25):
1939
+ self.loop.run_until_complete(self.page.scroll_down(amount))
1940
+ self.loop.run_until_complete(self.page.wait())
1941
+
1942
+ def save_screenshot(self, name, folder=None, selector=None):
1943
+ filename = name
1944
+ if folder:
1945
+ filename = os.path.join(folder, name)
1946
+ if not selector:
1947
+ self.loop.run_until_complete(
1948
+ self.page.save_screenshot(filename)
1949
+ )
1950
+ else:
1951
+ self.select(selector).save_screenshot(filename)