seleniumbase 4.33.4__py3-none-any.whl → 4.34.2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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}!"
|