seleniumbase 4.33.4__py3-none-any.whl → 4.34.2__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.
- seleniumbase/__version__.py +1 -1
- seleniumbase/behave/behave_sb.py +10 -2
- seleniumbase/console_scripts/run.py +6 -2
- seleniumbase/console_scripts/sb_commander.py +5 -5
- seleniumbase/console_scripts/sb_install.py +235 -6
- seleniumbase/console_scripts/sb_mkdir.py +1 -0
- seleniumbase/core/browser_launcher.py +358 -105
- seleniumbase/core/log_helper.py +33 -12
- seleniumbase/core/proxy_helper.py +35 -30
- seleniumbase/core/sb_cdp.py +277 -74
- seleniumbase/core/settings_parser.py +2 -0
- seleniumbase/core/style_sheet.py +10 -0
- seleniumbase/fixtures/base_case.py +216 -127
- seleniumbase/fixtures/constants.py +3 -0
- seleniumbase/fixtures/js_utils.py +2 -0
- seleniumbase/fixtures/page_actions.py +7 -2
- seleniumbase/fixtures/shared_utils.py +25 -0
- seleniumbase/plugins/driver_manager.py +28 -0
- seleniumbase/plugins/pytest_plugin.py +110 -0
- seleniumbase/plugins/sb_manager.py +41 -0
- seleniumbase/plugins/selenium_plugin.py +9 -0
- seleniumbase/undetected/cdp_driver/_contradict.py +3 -3
- seleniumbase/undetected/cdp_driver/browser.py +8 -6
- seleniumbase/undetected/cdp_driver/cdp_util.py +3 -0
- seleniumbase/undetected/cdp_driver/config.py +0 -1
- seleniumbase/undetected/cdp_driver/element.py +22 -20
- seleniumbase/undetected/patcher.py +20 -5
- {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/LICENSE +1 -1
- {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/METADATA +111 -86
- {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/RECORD +33 -33
- {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/WHEEL +1 -1
- {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.33.4.dist-info → seleniumbase-4.34.2.dist-info}/top_level.txt +0 -0
seleniumbase/core/sb_cdp.py
CHANGED
@@ -83,6 +83,9 @@ class CDPMethods():
|
|
83
83
|
element.get_position = lambda: self.__get_position(element)
|
84
84
|
element.get_html = lambda: self.__get_html(element)
|
85
85
|
element.get_js_attributes = lambda: self.__get_js_attributes(element)
|
86
|
+
element.get_attribute = (
|
87
|
+
lambda attribute: self.__get_attribute(element, attribute)
|
88
|
+
)
|
86
89
|
return element
|
87
90
|
|
88
91
|
def get(self, url):
|
@@ -124,14 +127,14 @@ class CDPMethods():
|
|
124
127
|
def add_handler(self, event, handler):
|
125
128
|
self.page.add_handler(event, handler)
|
126
129
|
|
127
|
-
def find_element(
|
128
|
-
self, selector, best_match=False, timeout=settings.SMALL_TIMEOUT
|
129
|
-
):
|
130
|
+
def find_element(self, selector, best_match=False, timeout=None):
|
130
131
|
"""Similar to select(), but also finds elements by text content.
|
131
132
|
When using text-based searches, if best_match=False, then will
|
132
133
|
find the first element with the text. If best_match=True, then
|
133
134
|
if multiple elements have that text, then will use the element
|
134
135
|
with the closest text-length to the text being searched for."""
|
136
|
+
if not timeout:
|
137
|
+
timeout = settings.SMALL_TIMEOUT
|
135
138
|
self.__add_light_pause()
|
136
139
|
selector = self.__convert_to_css_if_xpath(selector)
|
137
140
|
early_failure = False
|
@@ -164,7 +167,60 @@ class CDPMethods():
|
|
164
167
|
self.__slow_mode_pause_if_set()
|
165
168
|
return element
|
166
169
|
|
167
|
-
def
|
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
|
168
224
|
self.__add_light_pause()
|
169
225
|
selector = self.__convert_to_css_if_xpath(selector)
|
170
226
|
elements = self.loop.run_until_complete(
|
@@ -174,30 +230,54 @@ class CDPMethods():
|
|
174
230
|
for element in elements:
|
175
231
|
element = self.__add_sync_methods(element)
|
176
232
|
updated_elements.append(element)
|
177
|
-
self.__slow_mode_pause_if_set()
|
178
233
|
return updated_elements
|
179
234
|
|
180
235
|
def find_elements_by_text(self, text, tag_name=None):
|
181
236
|
"""Returns a list of elements by matching text.
|
182
|
-
Optionally, provide a tag_name to narrow down the search
|
183
|
-
|
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)"""
|
184
239
|
self.__add_light_pause()
|
185
240
|
elements = self.loop.run_until_complete(
|
186
241
|
self.page.find_elements_by_text(text=text)
|
187
242
|
)
|
188
243
|
updated_elements = []
|
244
|
+
if tag_name:
|
245
|
+
tag_name = tag_name.lower().strip()
|
189
246
|
for element in elements:
|
190
|
-
if
|
191
|
-
|
192
|
-
|
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
|
193
255
|
):
|
194
256
|
element = self.__add_sync_methods(element)
|
195
|
-
updated_elements
|
196
|
-
|
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)
|
197
275
|
return updated_elements
|
198
276
|
|
199
|
-
def select(self, selector, timeout=
|
277
|
+
def select(self, selector, timeout=None):
|
200
278
|
"""Similar to find_element(), but without text-based search."""
|
279
|
+
if not timeout:
|
280
|
+
timeout = settings.SMALL_TIMEOUT
|
201
281
|
self.__add_light_pause()
|
202
282
|
selector = self.__convert_to_css_if_xpath(selector)
|
203
283
|
if (":contains(" in selector):
|
@@ -231,7 +311,9 @@ class CDPMethods():
|
|
231
311
|
self.__slow_mode_pause_if_set()
|
232
312
|
return element
|
233
313
|
|
234
|
-
def select_all(self, selector, timeout=
|
314
|
+
def select_all(self, selector, timeout=None):
|
315
|
+
if not timeout:
|
316
|
+
timeout = settings.SMALL_TIMEOUT
|
235
317
|
self.__add_light_pause()
|
236
318
|
selector = self.__convert_to_css_if_xpath(selector)
|
237
319
|
elements = self.loop.run_until_complete(
|
@@ -241,13 +323,16 @@ class CDPMethods():
|
|
241
323
|
for element in elements:
|
242
324
|
element = self.__add_sync_methods(element)
|
243
325
|
updated_elements.append(element)
|
244
|
-
self.__slow_mode_pause_if_set()
|
245
326
|
return updated_elements
|
246
327
|
|
247
|
-
def find_elements(self, selector, timeout=
|
328
|
+
def find_elements(self, selector, timeout=None):
|
329
|
+
if not timeout:
|
330
|
+
timeout = settings.SMALL_TIMEOUT
|
248
331
|
return self.select_all(selector, timeout=timeout)
|
249
332
|
|
250
|
-
def find_visible_elements(self, selector, timeout=
|
333
|
+
def find_visible_elements(self, selector, timeout=None):
|
334
|
+
if not timeout:
|
335
|
+
timeout = settings.SMALL_TIMEOUT
|
251
336
|
visible_elements = []
|
252
337
|
elements = self.select_all(selector, timeout=timeout)
|
253
338
|
for element in elements:
|
@@ -268,6 +353,7 @@ class CDPMethods():
|
|
268
353
|
if number < 0:
|
269
354
|
number = 0
|
270
355
|
element = elements[number]
|
356
|
+
element.scroll_into_view()
|
271
357
|
element.click()
|
272
358
|
|
273
359
|
def click_nth_visible_element(self, selector, number):
|
@@ -284,6 +370,7 @@ class CDPMethods():
|
|
284
370
|
if number < 0:
|
285
371
|
number = 0
|
286
372
|
element = elements[number]
|
373
|
+
element.scroll_into_view()
|
287
374
|
element.click()
|
288
375
|
|
289
376
|
def click_link(self, link_text):
|
@@ -311,6 +398,13 @@ class CDPMethods():
|
|
311
398
|
return result
|
312
399
|
|
313
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
|
314
408
|
return (
|
315
409
|
self.loop.run_until_complete(
|
316
410
|
element.flash_async(*args, **kwargs)
|
@@ -382,9 +476,9 @@ class CDPMethods():
|
|
382
476
|
)
|
383
477
|
|
384
478
|
def __scroll_into_view(self, element):
|
385
|
-
|
386
|
-
|
387
|
-
|
479
|
+
self.loop.run_until_complete(element.scroll_into_view_async())
|
480
|
+
self.__add_light_pause()
|
481
|
+
return None
|
388
482
|
|
389
483
|
def __select_option(self, element):
|
390
484
|
return (
|
@@ -431,6 +525,37 @@ class CDPMethods():
|
|
431
525
|
self.loop.run_until_complete(element.get_js_attributes_async())
|
432
526
|
)
|
433
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
|
+
|
434
559
|
def tile_windows(self, windows=None, max_columns=0):
|
435
560
|
"""Tile windows and return the grid of tiled windows."""
|
436
561
|
driver = self.driver
|
@@ -472,12 +597,12 @@ class CDPMethods():
|
|
472
597
|
driver.cookies.load(*args, **kwargs)
|
473
598
|
)
|
474
599
|
|
475
|
-
def clear_cookies(self
|
600
|
+
def clear_cookies(self):
|
476
601
|
driver = self.driver
|
477
602
|
if hasattr(driver, "cdp_base"):
|
478
603
|
driver = driver.cdp_base
|
479
604
|
return self.loop.run_until_complete(
|
480
|
-
driver.cookies.clear(
|
605
|
+
driver.cookies.clear()
|
481
606
|
)
|
482
607
|
|
483
608
|
def sleep(self, seconds):
|
@@ -501,10 +626,12 @@ class CDPMethods():
|
|
501
626
|
self.page.evaluate(js_code)
|
502
627
|
)
|
503
628
|
|
504
|
-
def click(self, selector, timeout=
|
629
|
+
def click(self, selector, timeout=None):
|
630
|
+
if not timeout:
|
631
|
+
timeout = settings.SMALL_TIMEOUT
|
505
632
|
self.__slow_mode_pause_if_set()
|
506
633
|
element = self.find_element(selector, timeout=timeout)
|
507
|
-
|
634
|
+
element.scroll_into_view()
|
508
635
|
element.click()
|
509
636
|
self.__slow_mode_pause_if_set()
|
510
637
|
self.loop.run_until_complete(self.page.wait())
|
@@ -518,9 +645,12 @@ class CDPMethods():
|
|
518
645
|
|
519
646
|
def click_if_visible(self, selector):
|
520
647
|
if self.is_element_visible(selector):
|
521
|
-
|
522
|
-
|
523
|
-
|
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())
|
524
654
|
|
525
655
|
def click_visible_elements(self, selector, limit=0):
|
526
656
|
"""Finds all matching page elements and clicks visible ones in order.
|
@@ -545,19 +675,22 @@ class CDPMethods():
|
|
545
675
|
except Exception:
|
546
676
|
continue
|
547
677
|
if (width != 0 or height != 0):
|
678
|
+
element.scroll_into_view()
|
548
679
|
element.click()
|
549
680
|
click_count += 1
|
550
|
-
time.sleep(0.
|
681
|
+
time.sleep(0.042)
|
551
682
|
self.__slow_mode_pause_if_set()
|
552
683
|
self.loop.run_until_complete(self.page.wait())
|
553
684
|
except Exception:
|
554
685
|
break
|
555
686
|
|
556
|
-
def mouse_click(self, selector, timeout=
|
687
|
+
def mouse_click(self, selector, timeout=None):
|
557
688
|
"""(Attempt simulating a mouse click)"""
|
689
|
+
if not timeout:
|
690
|
+
timeout = settings.SMALL_TIMEOUT
|
558
691
|
self.__slow_mode_pause_if_set()
|
559
692
|
element = self.find_element(selector, timeout=timeout)
|
560
|
-
|
693
|
+
element.scroll_into_view()
|
561
694
|
element.mouse_click()
|
562
695
|
self.__slow_mode_pause_if_set()
|
563
696
|
self.loop.run_until_complete(self.page.wait())
|
@@ -579,6 +712,7 @@ class CDPMethods():
|
|
579
712
|
|
580
713
|
def select_option_by_text(self, dropdown_selector, option):
|
581
714
|
element = self.find_element(dropdown_selector)
|
715
|
+
element.scroll_into_view()
|
582
716
|
options = element.query_selector_all("option")
|
583
717
|
for found_option in options:
|
584
718
|
if found_option.text.strip() == option.strip():
|
@@ -599,7 +733,10 @@ class CDPMethods():
|
|
599
733
|
"""Paint a quickly-vanishing dot over an element."""
|
600
734
|
selector = self.__convert_to_css_if_xpath(selector)
|
601
735
|
element = self.find_element(selector)
|
602
|
-
element.
|
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)
|
603
740
|
if pause and isinstance(pause, (int, float)):
|
604
741
|
time.sleep(pause)
|
605
742
|
|
@@ -607,17 +744,22 @@ class CDPMethods():
|
|
607
744
|
"""Highlight an element with multi-colors."""
|
608
745
|
selector = self.__convert_to_css_if_xpath(selector)
|
609
746
|
element = self.find_element(selector)
|
610
|
-
element.
|
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)
|
611
751
|
time.sleep(0.15)
|
612
|
-
element.flash(0.42, "8844CC")
|
752
|
+
element.flash(0.42, "8844CC", x_offset, y_offset)
|
613
753
|
time.sleep(0.15)
|
614
|
-
element.flash(0.38, "CC8844")
|
754
|
+
element.flash(0.38, "CC8844", x_offset, y_offset)
|
615
755
|
time.sleep(0.15)
|
616
|
-
element.flash(0.30, "44CC88")
|
756
|
+
element.flash(0.30, "44CC88", x_offset, y_offset)
|
617
757
|
time.sleep(0.30)
|
618
758
|
|
619
759
|
def focus(self, selector):
|
620
|
-
self.find_element(selector)
|
760
|
+
element = self.find_element(selector)
|
761
|
+
element.scroll_into_view()
|
762
|
+
element.focus()
|
621
763
|
|
622
764
|
def highlight_overlay(self, selector):
|
623
765
|
self.find_element(selector).highlight_overlay()
|
@@ -643,21 +785,25 @@ class CDPMethods():
|
|
643
785
|
with suppress(Exception):
|
644
786
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
645
787
|
|
646
|
-
def send_keys(self, selector, text, timeout=
|
788
|
+
def send_keys(self, selector, text, timeout=None):
|
789
|
+
if not timeout:
|
790
|
+
timeout = settings.SMALL_TIMEOUT
|
647
791
|
self.__slow_mode_pause_if_set()
|
648
792
|
element = self.select(selector, timeout=timeout)
|
649
|
-
|
793
|
+
element.scroll_into_view()
|
650
794
|
if text.endswith("\n") or text.endswith("\r"):
|
651
795
|
text = text[:-1] + "\r\n"
|
652
796
|
element.send_keys(text)
|
653
797
|
self.__slow_mode_pause_if_set()
|
654
798
|
self.loop.run_until_complete(self.page.wait())
|
655
799
|
|
656
|
-
def press_keys(self, selector, text, timeout=
|
800
|
+
def press_keys(self, selector, text, timeout=None):
|
657
801
|
"""Similar to send_keys(), but presses keys at human speed."""
|
802
|
+
if not timeout:
|
803
|
+
timeout = settings.SMALL_TIMEOUT
|
658
804
|
self.__slow_mode_pause_if_set()
|
659
805
|
element = self.select(selector, timeout=timeout)
|
660
|
-
|
806
|
+
element.scroll_into_view()
|
661
807
|
submit = False
|
662
808
|
if text.endswith("\n") or text.endswith("\r"):
|
663
809
|
submit = True
|
@@ -671,11 +817,13 @@ class CDPMethods():
|
|
671
817
|
self.__slow_mode_pause_if_set()
|
672
818
|
self.loop.run_until_complete(self.page.wait())
|
673
819
|
|
674
|
-
def type(self, selector, text, timeout=
|
820
|
+
def type(self, selector, text, timeout=None):
|
675
821
|
"""Similar to send_keys(), but clears the text field first."""
|
822
|
+
if not timeout:
|
823
|
+
timeout = settings.SMALL_TIMEOUT
|
676
824
|
self.__slow_mode_pause_if_set()
|
677
825
|
element = self.select(selector, timeout=timeout)
|
678
|
-
|
826
|
+
element.scroll_into_view()
|
679
827
|
with suppress(Exception):
|
680
828
|
element.clear_input()
|
681
829
|
if text.endswith("\n") or text.endswith("\r"):
|
@@ -684,12 +832,14 @@ class CDPMethods():
|
|
684
832
|
self.__slow_mode_pause_if_set()
|
685
833
|
self.loop.run_until_complete(self.page.wait())
|
686
834
|
|
687
|
-
def set_value(self, selector, text, timeout=
|
835
|
+
def set_value(self, selector, text, timeout=None):
|
688
836
|
"""Similar to send_keys(), but clears the text field first."""
|
837
|
+
if not timeout:
|
838
|
+
timeout = settings.SMALL_TIMEOUT
|
689
839
|
self.__slow_mode_pause_if_set()
|
690
840
|
selector = self.__convert_to_css_if_xpath(selector)
|
691
|
-
self.select(selector, timeout=timeout)
|
692
|
-
|
841
|
+
element = self.select(selector, timeout=timeout)
|
842
|
+
element.scroll_into_view()
|
693
843
|
press_enter = False
|
694
844
|
if text.endswith("\n"):
|
695
845
|
text = text[:-1]
|
@@ -908,7 +1058,9 @@ class CDPMethods():
|
|
908
1058
|
coordinates["y"] = y if y else 0
|
909
1059
|
return coordinates
|
910
1060
|
|
911
|
-
def get_element_rect(self, selector, timeout=
|
1061
|
+
def get_element_rect(self, selector, timeout=None):
|
1062
|
+
if not timeout:
|
1063
|
+
timeout = settings.SMALL_TIMEOUT
|
912
1064
|
selector = self.__convert_to_css_if_xpath(selector)
|
913
1065
|
self.select(selector, timeout=timeout)
|
914
1066
|
self.__add_light_pause()
|
@@ -921,23 +1073,29 @@ class CDPMethods():
|
|
921
1073
|
)
|
922
1074
|
return coordinates
|
923
1075
|
|
924
|
-
def get_element_size(self, selector):
|
925
|
-
|
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)
|
926
1080
|
coordinates = {}
|
927
1081
|
coordinates["width"] = element_rect["width"]
|
928
1082
|
coordinates["height"] = element_rect["height"]
|
929
1083
|
return coordinates
|
930
1084
|
|
931
|
-
def get_element_position(self, selector):
|
932
|
-
|
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)
|
933
1089
|
coordinates = {}
|
934
1090
|
coordinates["x"] = element_rect["x"]
|
935
1091
|
coordinates["y"] = element_rect["y"]
|
936
1092
|
return coordinates
|
937
1093
|
|
938
|
-
def get_gui_element_rect(self, selector):
|
1094
|
+
def get_gui_element_rect(self, selector, timeout=None):
|
939
1095
|
"""(Coordinates are relative to the screen. Not the window.)"""
|
940
|
-
|
1096
|
+
if not timeout:
|
1097
|
+
timeout = settings.SMALL_TIMEOUT
|
1098
|
+
element_rect = self.get_element_rect(selector, timeout=timeout)
|
941
1099
|
e_width = element_rect["width"]
|
942
1100
|
e_height = element_rect["height"]
|
943
1101
|
window_rect = self.get_window_rect()
|
@@ -951,9 +1109,11 @@ class CDPMethods():
|
|
951
1109
|
y = y + window_rect["scrollY"]
|
952
1110
|
return ({"height": e_height, "width": e_width, "x": x, "y": y})
|
953
1111
|
|
954
|
-
def get_gui_element_center(self, selector):
|
1112
|
+
def get_gui_element_center(self, selector, timeout=None):
|
955
1113
|
"""(Coordinates are relative to the screen. Not the window.)"""
|
956
|
-
|
1114
|
+
if not timeout:
|
1115
|
+
timeout = settings.SMALL_TIMEOUT
|
1116
|
+
element_rect = self.get_gui_element_rect(selector, timeout=timeout)
|
957
1117
|
e_width = element_rect["width"]
|
958
1118
|
e_height = element_rect["height"]
|
959
1119
|
e_x = element_rect["x"]
|
@@ -981,7 +1141,16 @@ class CDPMethods():
|
|
981
1141
|
|
982
1142
|
def get_element_attribute(self, selector, attribute):
|
983
1143
|
attributes = self.get_element_attributes(selector)
|
984
|
-
|
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)
|
985
1154
|
|
986
1155
|
def get_element_html(self, selector):
|
987
1156
|
selector = self.__convert_to_css_if_xpath(selector)
|
@@ -1019,6 +1188,10 @@ class CDPMethods():
|
|
1019
1188
|
with suppress(Exception):
|
1020
1189
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1021
1190
|
|
1191
|
+
def __make_sure_pyautogui_lock_is_writable(self):
|
1192
|
+
with suppress(Exception):
|
1193
|
+
shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)
|
1194
|
+
|
1022
1195
|
def __verify_pyautogui_has_a_headed_browser(self):
|
1023
1196
|
"""PyAutoGUI requires a headed browser so that it can
|
1024
1197
|
focus on the correct element when performing actions."""
|
@@ -1039,6 +1212,8 @@ class CDPMethods():
|
|
1039
1212
|
constants.PipInstall.FINDLOCK
|
1040
1213
|
)
|
1041
1214
|
with pip_find_lock: # Prevent issues with multiple processes
|
1215
|
+
with suppress(Exception):
|
1216
|
+
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
|
1042
1217
|
try:
|
1043
1218
|
import pyautogui
|
1044
1219
|
with suppress(Exception):
|
@@ -1124,6 +1299,7 @@ class CDPMethods():
|
|
1124
1299
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1125
1300
|
)
|
1126
1301
|
with gui_lock:
|
1302
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1127
1303
|
pyautogui.press(key)
|
1128
1304
|
time.sleep(0.044)
|
1129
1305
|
self.__slow_mode_pause_if_set()
|
@@ -1137,6 +1313,7 @@ class CDPMethods():
|
|
1137
1313
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1138
1314
|
)
|
1139
1315
|
with gui_lock:
|
1316
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1140
1317
|
for key in keys:
|
1141
1318
|
pyautogui.press(key)
|
1142
1319
|
time.sleep(0.044)
|
@@ -1151,6 +1328,7 @@ class CDPMethods():
|
|
1151
1328
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1152
1329
|
)
|
1153
1330
|
with gui_lock:
|
1331
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1154
1332
|
pyautogui.write(text)
|
1155
1333
|
self.__slow_mode_pause_if_set()
|
1156
1334
|
self.loop.run_until_complete(self.page.wait())
|
@@ -1171,6 +1349,7 @@ class CDPMethods():
|
|
1171
1349
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1172
1350
|
)
|
1173
1351
|
with gui_lock: # Prevent issues with multiple processes
|
1352
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1174
1353
|
pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad)
|
1175
1354
|
if timeframe >= 0.25:
|
1176
1355
|
time.sleep(0.056) # Wait if moving at human-speed
|
@@ -1191,6 +1370,7 @@ class CDPMethods():
|
|
1191
1370
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1192
1371
|
)
|
1193
1372
|
with gui_lock: # Prevent issues with multiple processes
|
1373
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1194
1374
|
self.__install_pyautogui_if_missing()
|
1195
1375
|
import pyautogui
|
1196
1376
|
pyautogui = self.__get_configured_pyautogui(pyautogui)
|
@@ -1408,6 +1588,7 @@ class CDPMethods():
|
|
1408
1588
|
constants.MultiBrowser.PYAUTOGUILOCK
|
1409
1589
|
)
|
1410
1590
|
with gui_lock:
|
1591
|
+
self.__make_sure_pyautogui_lock_is_writable()
|
1411
1592
|
self.bring_active_window_to_front()
|
1412
1593
|
self.gui_hover_element(hover_selector)
|
1413
1594
|
time.sleep(0.15)
|
@@ -1423,7 +1604,7 @@ class CDPMethods():
|
|
1423
1604
|
"""Return True if checkbox (or radio button) is checked."""
|
1424
1605
|
selector = self.__convert_to_css_if_xpath(selector)
|
1425
1606
|
self.find_element(selector, timeout=settings.SMALL_TIMEOUT)
|
1426
|
-
return self.get_element_attribute(selector, "checked")
|
1607
|
+
return bool(self.get_element_attribute(selector, "checked"))
|
1427
1608
|
|
1428
1609
|
def is_selected(self, selector):
|
1429
1610
|
selector = self.__convert_to_css_if_xpath(selector)
|
@@ -1480,9 +1661,9 @@ class CDPMethods():
|
|
1480
1661
|
return True
|
1481
1662
|
return False
|
1482
1663
|
|
1483
|
-
def wait_for_element_visible(
|
1484
|
-
|
1485
|
-
|
1664
|
+
def wait_for_element_visible(self, selector, timeout=None):
|
1665
|
+
if not timeout:
|
1666
|
+
timeout = settings.SMALL_TIMEOUT
|
1486
1667
|
try:
|
1487
1668
|
self.select(selector, timeout=timeout)
|
1488
1669
|
except Exception:
|
@@ -1493,8 +1674,10 @@ class CDPMethods():
|
|
1493
1674
|
time.sleep(0.1)
|
1494
1675
|
raise Exception("Element {%s} was not visible!" % selector)
|
1495
1676
|
|
1496
|
-
def assert_element(self, selector, timeout=
|
1677
|
+
def assert_element(self, selector, timeout=None):
|
1497
1678
|
"""Same as assert_element_visible()"""
|
1679
|
+
if not timeout:
|
1680
|
+
timeout = settings.SMALL_TIMEOUT
|
1498
1681
|
try:
|
1499
1682
|
self.select(selector, timeout=timeout)
|
1500
1683
|
except Exception:
|
@@ -1505,8 +1688,10 @@ class CDPMethods():
|
|
1505
1688
|
time.sleep(0.1)
|
1506
1689
|
raise Exception("Element {%s} was not visible!" % selector)
|
1507
1690
|
|
1508
|
-
def assert_element_visible(self, selector, timeout=
|
1691
|
+
def assert_element_visible(self, selector, timeout=None):
|
1509
1692
|
"""Same as assert_element()"""
|
1693
|
+
if not timeout:
|
1694
|
+
timeout = settings.SMALL_TIMEOUT
|
1510
1695
|
try:
|
1511
1696
|
self.select(selector, timeout=timeout)
|
1512
1697
|
except Exception:
|
@@ -1517,16 +1702,20 @@ class CDPMethods():
|
|
1517
1702
|
time.sleep(0.1)
|
1518
1703
|
raise Exception("Element {%s} was not visible!" % selector)
|
1519
1704
|
|
1520
|
-
def assert_element_present(self, selector, timeout=
|
1705
|
+
def assert_element_present(self, selector, timeout=None):
|
1521
1706
|
"""Assert element is present in the DOM. (Visibility NOT required)"""
|
1707
|
+
if not timeout:
|
1708
|
+
timeout = settings.SMALL_TIMEOUT
|
1522
1709
|
try:
|
1523
1710
|
self.select(selector, timeout=timeout)
|
1524
1711
|
except Exception:
|
1525
1712
|
raise Exception("Element {%s} was not found!" % selector)
|
1526
1713
|
return True
|
1527
1714
|
|
1528
|
-
def assert_element_absent(self, selector, timeout=
|
1715
|
+
def assert_element_absent(self, selector, timeout=None):
|
1529
1716
|
"""Assert element is not present in the DOM."""
|
1717
|
+
if not timeout:
|
1718
|
+
timeout = settings.SMALL_TIMEOUT
|
1530
1719
|
start_ms = time.time() * 1000.0
|
1531
1720
|
stop_ms = start_ms + (timeout * 1000.0)
|
1532
1721
|
for i in range(int(timeout * 10)):
|
@@ -1544,10 +1733,10 @@ class CDPMethods():
|
|
1544
1733
|
% (selector, timeout, plural)
|
1545
1734
|
)
|
1546
1735
|
|
1547
|
-
def assert_element_not_visible(
|
1548
|
-
self, selector, timeout=settings.SMALL_TIMEOUT
|
1549
|
-
):
|
1736
|
+
def assert_element_not_visible(self, selector, timeout=None):
|
1550
1737
|
"""Assert element is not visible on page. (May still be in DOM)"""
|
1738
|
+
if not timeout:
|
1739
|
+
timeout = settings.SMALL_TIMEOUT
|
1551
1740
|
start_ms = time.time() * 1000.0
|
1552
1741
|
stop_ms = start_ms + (timeout * 1000.0)
|
1553
1742
|
for i in range(int(timeout * 10)):
|
@@ -1642,39 +1831,53 @@ class CDPMethods():
|
|
1642
1831
|
if expected not in actual:
|
1643
1832
|
raise Exception(error % (expected, actual))
|
1644
1833
|
|
1645
|
-
def assert_text(
|
1646
|
-
|
1647
|
-
|
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)
|
1648
1839
|
text = text.strip()
|
1649
1840
|
element = None
|
1650
1841
|
try:
|
1651
1842
|
element = self.find_element(selector, timeout=timeout)
|
1652
1843
|
except Exception:
|
1653
1844
|
raise Exception("Element {%s} not found!" % selector)
|
1654
|
-
for i in range(
|
1845
|
+
for i in range(int(timeout * 10)):
|
1846
|
+
with suppress(Exception):
|
1847
|
+
element = self.find_element(selector, timeout=0.1)
|
1655
1848
|
if text in element.text_all:
|
1656
1849
|
return True
|
1850
|
+
now_ms = time.time() * 1000.0
|
1851
|
+
if now_ms >= stop_ms:
|
1852
|
+
break
|
1657
1853
|
time.sleep(0.1)
|
1658
1854
|
raise Exception(
|
1659
1855
|
"Text {%s} not found in {%s}! Actual text: {%s}"
|
1660
1856
|
% (text, selector, element.text_all)
|
1661
1857
|
)
|
1662
1858
|
|
1663
|
-
def assert_exact_text(
|
1664
|
-
|
1665
|
-
|
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)
|
1666
1864
|
text = text.strip()
|
1667
1865
|
element = None
|
1668
1866
|
try:
|
1669
1867
|
element = self.select(selector, timeout=timeout)
|
1670
1868
|
except Exception:
|
1671
1869
|
raise Exception("Element {%s} not found!" % selector)
|
1672
|
-
for i in range(
|
1870
|
+
for i in range(int(timeout * 10)):
|
1871
|
+
with suppress(Exception):
|
1872
|
+
element = self.select(selector, timeout=0.1)
|
1673
1873
|
if (
|
1674
1874
|
self.is_element_visible(selector)
|
1675
1875
|
and text.strip() == element.text_all.strip()
|
1676
1876
|
):
|
1677
1877
|
return True
|
1878
|
+
now_ms = time.time() * 1000.0
|
1879
|
+
if now_ms >= stop_ms:
|
1880
|
+
break
|
1678
1881
|
time.sleep(0.1)
|
1679
1882
|
raise Exception(
|
1680
1883
|
"Expected Text {%s}, is not equal to {%s} in {%s}!"
|