seleniumbase 4.31.6a3__py3-none-any.whl → 4.32.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. seleniumbase/__version__.py +1 -1
  2. seleniumbase/behave/behave_sb.py +0 -2
  3. seleniumbase/common/decorators.py +8 -7
  4. seleniumbase/console_scripts/logo_helper.py +0 -2
  5. seleniumbase/console_scripts/run.py +4 -38
  6. seleniumbase/console_scripts/sb_behave_gui.py +0 -1
  7. seleniumbase/console_scripts/sb_caseplans.py +0 -1
  8. seleniumbase/console_scripts/sb_commander.py +0 -1
  9. seleniumbase/console_scripts/sb_install.py +4 -5
  10. seleniumbase/console_scripts/sb_mkchart.py +0 -2
  11. seleniumbase/console_scripts/sb_mkdir.py +0 -2
  12. seleniumbase/console_scripts/sb_mkfile.py +0 -2
  13. seleniumbase/console_scripts/sb_mkpres.py +0 -2
  14. seleniumbase/console_scripts/sb_mkrec.py +0 -2
  15. seleniumbase/console_scripts/sb_print.py +0 -2
  16. seleniumbase/console_scripts/sb_recorder.py +0 -1
  17. seleniumbase/core/browser_launcher.py +252 -44
  18. seleniumbase/core/sb_cdp.py +898 -0
  19. seleniumbase/core/sb_driver.py +44 -0
  20. seleniumbase/fixtures/base_case.py +208 -11
  21. seleniumbase/fixtures/constants.py +1 -0
  22. seleniumbase/fixtures/js_utils.py +4 -0
  23. seleniumbase/fixtures/page_actions.py +106 -0
  24. seleniumbase/fixtures/shared_utils.py +26 -0
  25. seleniumbase/plugins/driver_manager.py +136 -47
  26. seleniumbase/plugins/pytest_plugin.py +0 -2
  27. seleniumbase/plugins/sb_manager.py +40 -2
  28. seleniumbase/translate/translator.py +0 -2
  29. seleniumbase/undetected/__init__.py +2 -0
  30. seleniumbase/undetected/patcher.py +5 -5
  31. {seleniumbase-4.31.6a3.dist-info → seleniumbase-4.32.0.dist-info}/METADATA +26 -20
  32. {seleniumbase-4.31.6a3.dist-info → seleniumbase-4.32.0.dist-info}/RECORD +36 -35
  33. {seleniumbase-4.31.6a3.dist-info → seleniumbase-4.32.0.dist-info}/WHEEL +1 -1
  34. {seleniumbase-4.31.6a3.dist-info → seleniumbase-4.32.0.dist-info}/LICENSE +0 -0
  35. {seleniumbase-4.31.6a3.dist-info → seleniumbase-4.32.0.dist-info}/entry_points.txt +0 -0
  36. {seleniumbase-4.31.6a3.dist-info → seleniumbase-4.32.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,898 @@
1
+ """Add CDP methods to extend the driver"""
2
+ import math
3
+ import os
4
+ import re
5
+ import time
6
+ from contextlib import suppress
7
+ from seleniumbase import config as sb_config
8
+ from seleniumbase.config import settings
9
+ from seleniumbase.fixtures import constants
10
+ from seleniumbase.fixtures import js_utils
11
+ from seleniumbase.fixtures import page_utils
12
+ from seleniumbase.fixtures import shared_utils
13
+
14
+
15
+ class CDPMethods():
16
+ def __init__(self, loop, page, driver):
17
+ self.loop = loop
18
+ self.page = page
19
+ self.driver = driver
20
+
21
+ def __slow_mode_pause_if_set(self):
22
+ if hasattr(sb_config, "slow_mode") and sb_config.slow_mode:
23
+ time.sleep(0.16)
24
+
25
+ def __add_light_pause(self):
26
+ time.sleep(0.007)
27
+
28
+ def __convert_to_css_if_xpath(self, selector):
29
+ if page_utils.is_xpath_selector(selector):
30
+ with suppress(Exception):
31
+ css = js_utils.convert_to_css_selector(selector, "xpath")
32
+ if css:
33
+ return css
34
+ return selector
35
+
36
+ def __add_sync_methods(self, element):
37
+ if not element:
38
+ return element
39
+ element.clear_input = lambda: self.__clear_input(element)
40
+ element.click = lambda: self.__click(element)
41
+ element.flash = lambda: self.__flash(element)
42
+ element.focus = lambda: self.__focus(element)
43
+ element.highlight_overlay = lambda: self.__highlight_overlay(element)
44
+ element.mouse_click = lambda: self.__mouse_click(element)
45
+ element.mouse_drag = (
46
+ lambda destination: self.__mouse_drag(element, destination)
47
+ )
48
+ element.mouse_move = lambda: self.__mouse_move(element)
49
+ element.query_selector = (
50
+ lambda selector: self.__query_selector(element, selector)
51
+ )
52
+ element.querySelector = element.query_selector
53
+ element.query_selector_all = (
54
+ lambda selector: self.__query_selector_all(element, selector)
55
+ )
56
+ element.querySelectorAll = element.query_selector_all
57
+ element.remove_from_dom = lambda: self.__remove_from_dom(element)
58
+ element.save_screenshot = (
59
+ lambda *args, **kwargs: self.__save_screenshot(
60
+ element, *args, **kwargs)
61
+ )
62
+ element.save_to_dom = lambda: self.__save_to_dom(element)
63
+ element.scroll_into_view = lambda: self.__scroll_into_view(element)
64
+ element.select_option = lambda: self.__select_option(element)
65
+ element.send_file = (
66
+ lambda *file_paths: self.__send_file(element, *file_paths)
67
+ )
68
+ element.send_keys = lambda text: self.__send_keys(element, text)
69
+ element.set_text = lambda value: self.__set_text(element, value)
70
+ element.set_value = lambda value: self.__set_value(element, value)
71
+ element.type = lambda text: self.__type(element, text)
72
+ element.get_position = lambda: self.__get_position(element)
73
+ element.get_html = lambda: self.__get_html(element)
74
+ element.get_js_attributes = lambda: self.__get_js_attributes(element)
75
+ return element
76
+
77
+ def get(self, url):
78
+ url = shared_utils.fix_url_as_needed(url)
79
+ self.page = self.loop.run_until_complete(self.driver.cdp_base.get(url))
80
+ url_protocol = url.split(":")[0]
81
+ safe_url = True
82
+ if url_protocol not in ["about", "data", "chrome"]:
83
+ safe_url = False
84
+ if not safe_url:
85
+ time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
86
+
87
+ def reload(self, ignore_cache=True, script_to_evaluate_on_load=None):
88
+ self.loop.run_until_complete(
89
+ self.page.reload(
90
+ ignore_cache=ignore_cache,
91
+ script_to_evaluate_on_load=script_to_evaluate_on_load,
92
+ )
93
+ )
94
+
95
+ def refresh(self, *args, **kwargs):
96
+ self.reload(*args, **kwargs)
97
+
98
+ def get_event_loop(self):
99
+ return self.loop
100
+
101
+ def add_handler(self, event, handler):
102
+ self.page.add_handler(event, handler)
103
+
104
+ def find_element(
105
+ self, selector, best_match=False, timeout=settings.SMALL_TIMEOUT
106
+ ):
107
+ """Similar to select(), but also finds elements by text content.
108
+ When using text-based searches, if best_match=False, then will
109
+ find the first element with the text. If best_match=True, then
110
+ if multiple elements have that text, then will use the element
111
+ with the closest text-length to the text being searched for."""
112
+ self.__add_light_pause()
113
+ selector = self.__convert_to_css_if_xpath(selector)
114
+ if (":contains(" in selector):
115
+ tag_name = selector.split(":contains(")[0].split(" ")[-1]
116
+ text = selector.split(":contains(")[1].split(")")[0][1:-1]
117
+ with suppress(Exception):
118
+ self.loop.run_until_complete(
119
+ self.page.select(tag_name, timeout=3)
120
+ )
121
+ self.loop.run_until_complete(self.page.find(text, timeout=3))
122
+ element = self.find_elements_by_text(text, tag_name=tag_name)[0]
123
+ return self.__add_sync_methods(element)
124
+ failure = False
125
+ try:
126
+ element = self.loop.run_until_complete(
127
+ self.page.find(
128
+ selector, best_match=best_match, timeout=timeout
129
+ )
130
+ )
131
+ except Exception:
132
+ failure = True
133
+ plural = "s"
134
+ if timeout == 1:
135
+ plural = ""
136
+ message = "\n Element {%s} was not found after %s second%s!" % (
137
+ selector,
138
+ timeout,
139
+ plural,
140
+ )
141
+ if failure:
142
+ raise Exception(message)
143
+ element = self.__add_sync_methods(element)
144
+ self.__slow_mode_pause_if_set()
145
+ return element
146
+
147
+ def find_all(self, selector, timeout=settings.SMALL_TIMEOUT):
148
+ self.__add_light_pause()
149
+ selector = self.__convert_to_css_if_xpath(selector)
150
+ elements = self.loop.run_until_complete(
151
+ self.page.find_all(selector, timeout=timeout)
152
+ )
153
+ updated_elements = []
154
+ for element in elements:
155
+ element = self.__add_sync_methods(element)
156
+ updated_elements.append(element)
157
+ self.__slow_mode_pause_if_set()
158
+ return updated_elements
159
+
160
+ def find_elements_by_text(self, text, tag_name=None):
161
+ """Returns a list of elements by matching text.
162
+ Optionally, provide a tag_name to narrow down the search
163
+ to only elements with the given tag. (Eg: a, div, script, span)"""
164
+ self.__add_light_pause()
165
+ elements = self.loop.run_until_complete(
166
+ self.page.find_elements_by_text(text=text)
167
+ )
168
+ updated_elements = []
169
+ for element in elements:
170
+ if not tag_name or tag_name.lower() == element.tag_name.lower():
171
+ element = self.__add_sync_methods(element)
172
+ updated_elements.append(element)
173
+ self.__slow_mode_pause_if_set()
174
+ return updated_elements
175
+
176
+ def select(self, selector, timeout=settings.SMALL_TIMEOUT):
177
+ """Similar to find_element(), but without text-based search."""
178
+ self.__add_light_pause()
179
+ selector = self.__convert_to_css_if_xpath(selector)
180
+ if (":contains(" in selector):
181
+ tag_name = selector.split(":contains(")[0].split(" ")[-1]
182
+ text = selector.split(":contains(")[1].split(")")[0][1:-1]
183
+ with suppress(Exception):
184
+ self.loop.run_until_complete(
185
+ self.page.select(tag_name, timeout=5)
186
+ )
187
+ self.loop.run_until_complete(self.page.find(text, timeout=5))
188
+ element = self.find_elements_by_text(text, tag_name=tag_name)[0]
189
+ return self.__add_sync_methods(element)
190
+ failure = False
191
+ try:
192
+ element = self.loop.run_until_complete(
193
+ self.page.select(selector, timeout=timeout)
194
+ )
195
+ except Exception:
196
+ failure = True
197
+ plural = "s"
198
+ if timeout == 1:
199
+ plural = ""
200
+ message = "\n Element {%s} was not found after %s second%s!" % (
201
+ selector,
202
+ timeout,
203
+ plural,
204
+ )
205
+ if failure:
206
+ raise Exception(message)
207
+ element = self.__add_sync_methods(element)
208
+ self.__slow_mode_pause_if_set()
209
+ return element
210
+
211
+ def select_all(self, selector, timeout=settings.SMALL_TIMEOUT):
212
+ self.__add_light_pause()
213
+ selector = self.__convert_to_css_if_xpath(selector)
214
+ elements = self.loop.run_until_complete(
215
+ self.page.select_all(selector, timeout=timeout)
216
+ )
217
+ updated_elements = []
218
+ for element in elements:
219
+ element = self.__add_sync_methods(element)
220
+ updated_elements.append(element)
221
+ self.__slow_mode_pause_if_set()
222
+ return updated_elements
223
+
224
+ def click_link(self, link_text):
225
+ self.find_elements_by_text(link_text, "a")[0].click()
226
+
227
+ def __clear_input(self, element):
228
+ return (
229
+ self.loop.run_until_complete(element.clear_input_async())
230
+ )
231
+
232
+ def __click(self, element):
233
+ return (
234
+ self.loop.run_until_complete(element.click_async())
235
+ )
236
+
237
+ def __flash(self, element):
238
+ return (
239
+ self.loop.run_until_complete(element.flash_async())
240
+ )
241
+
242
+ def __focus(self, element):
243
+ return (
244
+ self.loop.run_until_complete(element.focus_async())
245
+ )
246
+
247
+ def __highlight_overlay(self, element):
248
+ return (
249
+ self.loop.run_until_complete(element.highlight_overlay_async())
250
+ )
251
+
252
+ def __mouse_click(self, element):
253
+ return (
254
+ self.loop.run_until_complete(element.mouse_click_async())
255
+ )
256
+
257
+ def __mouse_drag(self, element, destination):
258
+ return (
259
+ self.loop.run_until_complete(element.mouse_drag_async(destination))
260
+ )
261
+
262
+ def __mouse_move(self, element):
263
+ return (
264
+ self.loop.run_until_complete(element.mouse_move_async())
265
+ )
266
+
267
+ def __query_selector(self, element, selector):
268
+ selector = self.__convert_to_css_if_xpath(selector)
269
+ element = self.loop.run_until_complete(
270
+ element.query_selector_async(selector)
271
+ )
272
+ element = self.__add_sync_methods(element)
273
+ return element
274
+
275
+ def __query_selector_all(self, element, selector):
276
+ selector = self.__convert_to_css_if_xpath(selector)
277
+ elements = self.loop.run_until_complete(
278
+ element.query_selector_all_async(selector)
279
+ )
280
+ updated_elements = []
281
+ for element in elements:
282
+ element = self.__add_sync_methods(element)
283
+ updated_elements.append(element)
284
+ self.__slow_mode_pause_if_set()
285
+ return updated_elements
286
+
287
+ def __remove_from_dom(self, element):
288
+ return (
289
+ self.loop.run_until_complete(element.remove_from_dom_async())
290
+ )
291
+
292
+ def __save_screenshot(self, element, *args, **kwargs):
293
+ return (
294
+ self.loop.run_until_complete(
295
+ element.save_screenshot_async(*args, **kwargs)
296
+ )
297
+ )
298
+
299
+ def __save_to_dom(self, element):
300
+ return (
301
+ self.loop.run_until_complete(element.save_to_dom_async())
302
+ )
303
+
304
+ def __scroll_into_view(self, element):
305
+ return (
306
+ self.loop.run_until_complete(element.scroll_into_view_async())
307
+ )
308
+
309
+ def __select_option(self, element):
310
+ return (
311
+ self.loop.run_until_complete(element.select_option_async())
312
+ )
313
+
314
+ def __send_file(self, element, *file_paths):
315
+ return (
316
+ self.loop.run_until_complete(element.send_file_async(*file_paths))
317
+ )
318
+
319
+ def __send_keys(self, element, text):
320
+ return (
321
+ self.loop.run_until_complete(element.send_keys_async(text))
322
+ )
323
+
324
+ def __set_text(self, element, value):
325
+ return (
326
+ self.loop.run_until_complete(element.set_text_async(value))
327
+ )
328
+
329
+ def __set_value(self, element, value):
330
+ return (
331
+ self.loop.run_until_complete(element.set_value_async(value))
332
+ )
333
+
334
+ def __type(self, element, text):
335
+ with suppress(Exception):
336
+ element.clear_input()
337
+ element.send_keys(text)
338
+
339
+ def __get_position(self, element):
340
+ return (
341
+ self.loop.run_until_complete(element.get_position_async())
342
+ )
343
+
344
+ def __get_html(self, element):
345
+ return (
346
+ self.loop.run_until_complete(element.get_html_async())
347
+ )
348
+
349
+ def __get_js_attributes(self, element):
350
+ return (
351
+ self.loop.run_until_complete(element.get_js_attributes_async())
352
+ )
353
+
354
+ def tile_windows(self, windows=None, max_columns=0):
355
+ """Tile windows and return the grid of tiled windows."""
356
+ return self.loop.run_until_complete(
357
+ self.driver.cdp_base.tile_windows(windows, max_columns)
358
+ )
359
+
360
+ def get_all_cookies(self, *args, **kwargs):
361
+ return self.loop.run_until_complete(
362
+ self.driver.cdp_base.cookies.get_all(*args, **kwargs)
363
+ )
364
+
365
+ def set_all_cookies(self, *args, **kwargs):
366
+ return self.loop.run_until_complete(
367
+ self.driver.cdp_base.cookies.set_all(*args, **kwargs)
368
+ )
369
+
370
+ def save_cookies(self, *args, **kwargs):
371
+ return self.loop.run_until_complete(
372
+ self.driver.cdp_base.cookies.save(*args, **kwargs)
373
+ )
374
+
375
+ def load_cookies(self, *args, **kwargs):
376
+ return self.loop.run_until_complete(
377
+ self.driver.cdp_base.cookies.load(*args, **kwargs)
378
+ )
379
+
380
+ def clear_cookies(self, *args, **kwargs):
381
+ return self.loop.run_until_complete(
382
+ self.driver.cdp_base.cookies.clear(*args, **kwargs)
383
+ )
384
+
385
+ def sleep(self, seconds):
386
+ time.sleep(seconds)
387
+
388
+ def bring_active_window_to_front(self):
389
+ self.loop.run_until_complete(self.page.bring_to_front())
390
+
391
+ def get_active_element(self):
392
+ return self.loop.run_until_complete(
393
+ self.page.js_dumps("document.activeElement")
394
+ )
395
+
396
+ def get_active_element_css(self):
397
+ from seleniumbase.js_code import active_css_js
398
+
399
+ js_code = active_css_js.get_active_element_css
400
+ js_code = js_code.replace("return getBestSelector", "getBestSelector")
401
+ return self.loop.run_until_complete(
402
+ self.page.evaluate(js_code)
403
+ )
404
+
405
+ def click(self, selector, timeout=settings.SMALL_TIMEOUT):
406
+ self.__slow_mode_pause_if_set()
407
+ element = self.find_element(selector, timeout=timeout)
408
+ self.__add_light_pause()
409
+ element.click()
410
+ self.__slow_mode_pause_if_set()
411
+
412
+ def click_active_element(self):
413
+ self.loop.run_until_complete(
414
+ self.page.evaluate("document.activeElement.click()")
415
+ )
416
+ self.__slow_mode_pause_if_set()
417
+
418
+ def click_if_visible(self, selector):
419
+ if self.is_element_visible(selector):
420
+ self.find_element(selector).click()
421
+ self.__slow_mode_pause_if_set()
422
+
423
+ def mouse_click(self, selector, timeout=settings.SMALL_TIMEOUT):
424
+ """(Attempt simulating a mouse click)"""
425
+ self.__slow_mode_pause_if_set()
426
+ element = self.find_element(selector, timeout=timeout)
427
+ self.__add_light_pause()
428
+ element.mouse_click()
429
+ self.__slow_mode_pause_if_set()
430
+
431
+ def nested_click(self, parent_selector, selector):
432
+ """
433
+ Find parent element and click on child element inside it.
434
+ (This can be used to click on elements inside an iframe.)
435
+ """
436
+ element = self.find_element(parent_selector)
437
+ element.query_selector(selector).mouse_click()
438
+ self.__slow_mode_pause_if_set()
439
+
440
+ def get_nested_element(self, parent_selector, selector):
441
+ """(Can be used to find an element inside an iframe)"""
442
+ element = self.find_element(parent_selector)
443
+ return element.query_selector(selector)
444
+
445
+ def flash(self, selector):
446
+ """Paint a quickly-vanishing dot over an element."""
447
+ self.find_element(selector).flash()
448
+
449
+ def focus(self, selector):
450
+ self.find_element(selector).focus()
451
+
452
+ def highlight_overlay(self, selector):
453
+ self.find_element(selector).highlight_overlay()
454
+
455
+ def remove_element(self, selector):
456
+ self.select(selector).remove_from_dom()
457
+
458
+ def remove_from_dom(self, selector):
459
+ self.select(selector).remove_from_dom()
460
+
461
+ def remove_elements(self, selector):
462
+ """Remove all elements on the page that match the selector."""
463
+ css_selector = self.__convert_to_css_if_xpath(selector)
464
+ css_selector = re.escape(css_selector) # Add "\\" to special chars
465
+ css_selector = js_utils.escape_quotes_if_needed(css_selector)
466
+ js_code = (
467
+ """var $elements = document.querySelectorAll('%s');
468
+ var index = 0, length = $elements.length;
469
+ for(; index < length; index++){
470
+ $elements[index].remove();}"""
471
+ % css_selector
472
+ )
473
+ with suppress(Exception):
474
+ self.loop.run_until_complete(self.page.evaluate(js_code))
475
+
476
+ def scroll_into_view(self, selector):
477
+ self.find_element(selector).scroll_into_view()
478
+
479
+ def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
480
+ element = self.select(selector)
481
+ self.__slow_mode_pause_if_set()
482
+ if text.endswith("\n") or text.endswith("\r"):
483
+ text = text[:-1] + "\r\n"
484
+ element.send_keys(text)
485
+ self.__slow_mode_pause_if_set()
486
+
487
+ def press_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
488
+ """Similar to send_keys(), but presses keys at human speed."""
489
+ element = self.select(selector)
490
+ self.__slow_mode_pause_if_set()
491
+ submit = False
492
+ if text.endswith("\n") or text.endswith("\r"):
493
+ submit = True
494
+ text = text[:-1]
495
+ for key in text:
496
+ element.send_keys(key)
497
+ time.sleep(0.0375)
498
+ if submit:
499
+ element.send_keys("\r\n")
500
+ time.sleep(0.0375)
501
+ self.__slow_mode_pause_if_set()
502
+
503
+ def type(self, selector, text, timeout=settings.SMALL_TIMEOUT):
504
+ """Similar to send_keys(), but clears the text field first."""
505
+ element = self.select(selector)
506
+ self.__slow_mode_pause_if_set()
507
+ with suppress(Exception):
508
+ element.clear_input()
509
+ if text.endswith("\n") or text.endswith("\r"):
510
+ text = text[:-1] + "\r\n"
511
+ element.send_keys(text)
512
+ self.__slow_mode_pause_if_set()
513
+
514
+ def evaluate(self, expression):
515
+ """Run a JavaScript expression and return the result."""
516
+ return self.loop.run_until_complete(
517
+ self.page.evaluate(expression)
518
+ )
519
+
520
+ def js_dumps(self, obj_name):
521
+ """Similar to evaluate(), but for dictionary results."""
522
+ return self.loop.run_until_complete(
523
+ self.page.js_dumps(obj_name)
524
+ )
525
+
526
+ def maximize(self):
527
+ return self.loop.run_until_complete(
528
+ self.page.maximize()
529
+ )
530
+
531
+ def minimize(self):
532
+ return self.loop.run_until_complete(
533
+ self.page.minimize()
534
+ )
535
+
536
+ def medimize(self):
537
+ return self.loop.run_until_complete(
538
+ self.page.medimize()
539
+ )
540
+
541
+ def set_window_rect(self, x, y, width, height):
542
+ return self.loop.run_until_complete(
543
+ self.page.set_window_size(
544
+ left=x, top=y, width=width, height=height)
545
+ )
546
+
547
+ def reset_window_size(self):
548
+ x = settings.WINDOW_START_X
549
+ y = settings.WINDOW_START_Y
550
+ width = settings.CHROME_START_WIDTH
551
+ height = settings.CHROME_START_HEIGHT
552
+ self.set_window_rect(x, y, width, height)
553
+
554
+ def get_window(self):
555
+ return self.loop.run_until_complete(
556
+ self.page.get_window()
557
+ )
558
+
559
+ def get_text(self, selector):
560
+ return self.find_element(selector).text_all
561
+
562
+ def get_title(self):
563
+ return self.loop.run_until_complete(
564
+ self.page.evaluate("document.title")
565
+ )
566
+
567
+ def get_current_url(self):
568
+ return self.loop.run_until_complete(
569
+ self.page.evaluate("window.location.href")
570
+ )
571
+
572
+ def get_origin(self):
573
+ return self.loop.run_until_complete(
574
+ self.page.evaluate("window.location.origin")
575
+ )
576
+
577
+ def get_page_source(self):
578
+ try:
579
+ source = self.loop.run_until_complete(
580
+ self.page.evaluate("document.documentElement.outerHTML")
581
+ )
582
+ except Exception:
583
+ time.sleep(constants.UC.CDP_MODE_OPEN_WAIT)
584
+ source = self.loop.run_until_complete(
585
+ self.page.evaluate("document.documentElement.outerHTML")
586
+ )
587
+ return source
588
+
589
+ def get_user_agent(self):
590
+ return self.loop.run_until_complete(
591
+ self.page.evaluate("navigator.userAgent")
592
+ )
593
+
594
+ def get_cookie_string(self):
595
+ return self.loop.run_until_complete(
596
+ self.page.evaluate("document.cookie")
597
+ )
598
+
599
+ def get_locale_code(self):
600
+ return self.loop.run_until_complete(
601
+ self.page.evaluate("navigator.language || navigator.languages[0]")
602
+ )
603
+
604
+ def get_screen_rect(self):
605
+ coordinates = self.loop.run_until_complete(
606
+ self.page.js_dumps("window.screen")
607
+ )
608
+ return coordinates
609
+
610
+ def get_window_rect(self):
611
+ coordinates = {}
612
+ innerWidth = self.loop.run_until_complete(
613
+ self.page.evaluate("window.innerWidth")
614
+ )
615
+ innerHeight = self.loop.run_until_complete(
616
+ self.page.evaluate("window.innerHeight")
617
+ )
618
+ outerWidth = self.loop.run_until_complete(
619
+ self.page.evaluate("window.outerWidth")
620
+ )
621
+ outerHeight = self.loop.run_until_complete(
622
+ self.page.evaluate("window.outerHeight")
623
+ )
624
+ pageXOffset = self.loop.run_until_complete(
625
+ self.page.evaluate("window.pageXOffset")
626
+ )
627
+ pageYOffset = self.loop.run_until_complete(
628
+ self.page.evaluate("window.pageYOffset")
629
+ )
630
+ scrollX = self.loop.run_until_complete(
631
+ self.page.evaluate("window.scrollX")
632
+ )
633
+ scrollY = self.loop.run_until_complete(
634
+ self.page.evaluate("window.scrollY")
635
+ )
636
+ screenLeft = self.loop.run_until_complete(
637
+ self.page.evaluate("window.screenLeft")
638
+ )
639
+ screenTop = self.loop.run_until_complete(
640
+ self.page.evaluate("window.screenTop")
641
+ )
642
+ x = self.loop.run_until_complete(
643
+ self.page.evaluate("window.screenX")
644
+ )
645
+ y = self.loop.run_until_complete(
646
+ self.page.evaluate("window.screenY")
647
+ )
648
+ coordinates["innerWidth"] = innerWidth
649
+ coordinates["innerHeight"] = innerHeight
650
+ coordinates["outerWidth"] = outerWidth
651
+ coordinates["outerHeight"] = outerHeight
652
+ coordinates["width"] = outerWidth
653
+ coordinates["height"] = outerHeight
654
+ coordinates["pageXOffset"] = pageXOffset if pageXOffset else 0
655
+ coordinates["pageYOffset"] = pageYOffset if pageYOffset else 0
656
+ coordinates["scrollX"] = scrollX if scrollX else 0
657
+ coordinates["scrollY"] = scrollY if scrollY else 0
658
+ coordinates["screenLeft"] = screenLeft if screenLeft else 0
659
+ coordinates["screenTop"] = screenTop if screenTop else 0
660
+ coordinates["x"] = x if x else 0
661
+ coordinates["y"] = y if y else 0
662
+ return coordinates
663
+
664
+ def get_window_size(self):
665
+ coordinates = {}
666
+ outerWidth = self.loop.run_until_complete(
667
+ self.page.evaluate("window.outerWidth")
668
+ )
669
+ outerHeight = self.loop.run_until_complete(
670
+ self.page.evaluate("window.outerHeight")
671
+ )
672
+ coordinates["width"] = outerWidth
673
+ coordinates["height"] = outerHeight
674
+ return coordinates
675
+
676
+ def get_window_position(self):
677
+ coordinates = {}
678
+ x = self.loop.run_until_complete(
679
+ self.page.evaluate("window.screenX")
680
+ )
681
+ y = self.loop.run_until_complete(
682
+ self.page.evaluate("window.screenY")
683
+ )
684
+ coordinates["x"] = x if x else 0
685
+ coordinates["y"] = y if y else 0
686
+ return coordinates
687
+
688
+ def get_element_rect(self, selector):
689
+ selector = self.__convert_to_css_if_xpath(selector)
690
+ coordinates = self.loop.run_until_complete(
691
+ self.page.js_dumps(
692
+ """document.querySelector"""
693
+ """('%s').getBoundingClientRect()"""
694
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
695
+ )
696
+ )
697
+ return coordinates
698
+
699
+ def get_element_size(self, selector):
700
+ element_rect = self.get_element_rect(selector)
701
+ coordinates = {}
702
+ coordinates["width"] = element_rect["width"]
703
+ coordinates["height"] = element_rect["height"]
704
+ return coordinates
705
+
706
+ def get_element_position(self, selector):
707
+ element_rect = self.get_element_rect(selector)
708
+ coordinates = {}
709
+ coordinates["x"] = element_rect["x"]
710
+ coordinates["y"] = element_rect["y"]
711
+ return coordinates
712
+
713
+ def get_gui_element_rect(self, selector):
714
+ """(Coordinates are relative to the screen. Not the window.)"""
715
+ element_rect = self.get_element_rect(selector)
716
+ e_width = element_rect["width"]
717
+ e_height = element_rect["height"]
718
+ window_rect = self.get_window_rect()
719
+ w_bottom_y = window_rect["y"] + window_rect["height"]
720
+ viewport_height = window_rect["innerHeight"]
721
+ x = math.ceil(window_rect["x"] + element_rect["x"])
722
+ y = math.ceil(w_bottom_y - viewport_height + element_rect["y"])
723
+ y_scroll_offset = window_rect["pageYOffset"]
724
+ y = int(y - y_scroll_offset)
725
+ return ({"height": e_height, "width": e_width, "x": x, "y": y})
726
+
727
+ def get_gui_element_center(self, selector):
728
+ """(Coordinates are relative to the screen. Not the window.)"""
729
+ element_rect = self.get_gui_element_rect(selector)
730
+ e_width = element_rect["width"]
731
+ e_height = element_rect["height"]
732
+ e_x = element_rect["x"]
733
+ e_y = element_rect["y"]
734
+ return ((e_x + e_width / 2), (e_y + e_height / 2))
735
+
736
+ def get_document(self):
737
+ return self.loop.run_until_complete(
738
+ self.page.get_document()
739
+ )
740
+
741
+ def get_flattened_document(self):
742
+ return self.loop.run_until_complete(
743
+ self.page.get_flattened_document()
744
+ )
745
+
746
+ def get_element_attributes(self, selector):
747
+ return self.loop.run_until_complete(
748
+ self.page.js_dumps(
749
+ """document.querySelector('%s')"""
750
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
751
+ )
752
+ )
753
+
754
+ def get_element_html(self, selector):
755
+ selector = self.__convert_to_css_if_xpath(selector)
756
+ return self.loop.run_until_complete(
757
+ self.page.evaluate(
758
+ """document.querySelector('%s').outerHTML"""
759
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
760
+ )
761
+ )
762
+
763
+ def set_attributes(self, selector, attribute, value):
764
+ """This method uses JavaScript to set/update a common attribute.
765
+ All matching selectors from querySelectorAll() are used.
766
+ Example => (Make all links on a website redirect to Google):
767
+ self.set_attributes("a", "href", "https://google.com")"""
768
+ attribute = re.escape(attribute)
769
+ attribute = js_utils.escape_quotes_if_needed(attribute)
770
+ value = re.escape(value)
771
+ value = js_utils.escape_quotes_if_needed(value)
772
+ css_selector = self.__convert_to_css_if_xpath(selector)
773
+ css_selector = re.escape(css_selector) # Add "\\" to special chars
774
+ css_selector = js_utils.escape_quotes_if_needed(css_selector)
775
+ js_code = """var $elements = document.querySelectorAll('%s');
776
+ var index = 0, length = $elements.length;
777
+ for(; index < length; index++){
778
+ $elements[index].setAttribute('%s','%s');}""" % (
779
+ css_selector,
780
+ attribute,
781
+ value,
782
+ )
783
+ with suppress(Exception):
784
+ self.loop.run_until_complete(self.page.evaluate(js_code))
785
+
786
+ def internalize_links(self):
787
+ """All `target="_blank"` links become `target="_self"`.
788
+ This prevents those links from opening in a new tab."""
789
+ self.set_attributes('[target="_blank"]', "target", "_self")
790
+
791
+ def is_element_present(self, selector):
792
+ try:
793
+ self.select(selector, timeout=0.01)
794
+ return True
795
+ except Exception:
796
+ return False
797
+ selector = self.__convert_to_css_if_xpath(selector)
798
+ element = self.loop.run_until_complete(
799
+ self.page.js_dumps(
800
+ """document.querySelector('%s')"""
801
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
802
+ )
803
+ )
804
+ return element is not None
805
+
806
+ def is_element_visible(self, selector):
807
+ selector = self.__convert_to_css_if_xpath(selector)
808
+ element = None
809
+ if ":contains(" not in selector:
810
+ try:
811
+ element = self.loop.run_until_complete(
812
+ self.page.js_dumps(
813
+ """window.getComputedStyle(document.querySelector"""
814
+ """('%s'))"""
815
+ % js_utils.escape_quotes_if_needed(re.escape(selector))
816
+ )
817
+ )
818
+ except Exception:
819
+ return False
820
+ if not element:
821
+ return False
822
+ return element.get("display") != "none"
823
+ else:
824
+ with suppress(Exception):
825
+ tag_name = selector.split(":contains(")[0].split(" ")[-1]
826
+ text = selector.split(":contains(")[1].split(")")[0][1:-1]
827
+ self.loop.run_until_complete(
828
+ self.page.select(tag_name, timeout=0.1)
829
+ )
830
+ self.loop.run_until_complete(self.page.find(text, timeout=0.1))
831
+ return True
832
+ return False
833
+
834
+ def assert_element(self, selector, timeout=settings.SMALL_TIMEOUT):
835
+ try:
836
+ self.select(selector, timeout=timeout)
837
+ except Exception:
838
+ raise Exception("Element {%s} not found!" % selector)
839
+ for i in range(30):
840
+ if self.is_element_visible(selector):
841
+ return True
842
+ time.sleep(0.1)
843
+ raise Exception("Element {%s} not visible!" % selector)
844
+
845
+ def assert_element_present(self, selector, timeout=settings.SMALL_TIMEOUT):
846
+ try:
847
+ self.select(selector, timeout=timeout)
848
+ except Exception:
849
+ raise Exception("Element {%s} not found!" % selector)
850
+ return True
851
+
852
+ def assert_text(
853
+ self, text, selector="html", timeout=settings.SMALL_TIMEOUT
854
+ ):
855
+ element = None
856
+ try:
857
+ element = self.select(selector, timeout=timeout)
858
+ except Exception:
859
+ raise Exception("Element {%s} not found!" % selector)
860
+ for i in range(30):
861
+ if self.is_element_visible(selector) and text in element.text_all:
862
+ return True
863
+ time.sleep(0.1)
864
+ raise Exception(
865
+ "Text {%s} not found in {%s}! Actual text: {%s}"
866
+ % (text, selector, element.text_all)
867
+ )
868
+
869
+ def assert_exact_text(
870
+ self, text, selector="html", timeout=settings.SMALL_TIMEOUT
871
+ ):
872
+ element = None
873
+ try:
874
+ element = self.select(selector, timeout=timeout)
875
+ except Exception:
876
+ raise Exception("Element {%s} not found!" % selector)
877
+ for i in range(30):
878
+ if (
879
+ self.is_element_visible(selector)
880
+ and text.strip() == element.text_all.strip()
881
+ ):
882
+ return True
883
+ time.sleep(0.1)
884
+ raise Exception(
885
+ "Expected Text {%s}, is not equal to {%s} in {%s}!"
886
+ % (text, element.text_all, selector)
887
+ )
888
+
889
+ def save_screenshot(self, name, folder=None, selector=None):
890
+ filename = name
891
+ if folder:
892
+ filename = os.path.join(folder, name)
893
+ if not selector:
894
+ self.loop.run_until_complete(
895
+ self.page.save_screenshot(filename)
896
+ )
897
+ else:
898
+ self.select(selector).save_screenshot(filename)