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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)