seleniumbase 4.27.5__py3-none-any.whl → 4.28.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.
seleniumbase/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import collections
2
+ import os
2
3
  import pdb
3
4
  try:
4
5
  import pdbp # (Pdb+) --- Python Debugger Plus
@@ -34,11 +35,13 @@ if sys.version_info[0] < 3 and "pdbp" in locals():
34
35
  pdb.DefaultConfig.truncate_long_lines = False
35
36
  pdb.DefaultConfig.sticky_by_default = True
36
37
  colored_traceback.add_hook()
38
+ os.environ["SE_AVOID_STATS"] = "true" # Disable Selenium Manager stats
37
39
  if sys.version_info >= (3, 7):
38
40
  webdriver.TouchActions = None # Lifeline for past selenium-wire versions
39
41
  if sys.version_info >= (3, 10):
40
42
  collections.Callable = collections.abc.Callable # Lifeline for nosetests
41
43
  del collections # Undo "import collections" / Simplify "dir(seleniumbase)"
44
+ del os # Undo "import os" / Simplify "dir(seleniumbase)"
42
45
  del sys # Undo "import sys" / Simplify "dir(seleniumbase)"
43
46
  del webdriver # Undo "import webdriver" / Simplify "dir(seleniumbase)"
44
47
 
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.27.5"
2
+ __version__ = "4.28.0"
@@ -862,14 +862,17 @@ def get_configured_sb(context):
862
862
  and not sb.headless2
863
863
  and not sb.xvfb
864
864
  ):
865
- print(
866
- '(Linux uses "-D headless" by default. '
867
- 'To override, use "-D headed" / "-D gui". '
868
- 'For Xvfb mode instead, use "-D xvfb". '
869
- "Or you can hide this info by using"
870
- '"-D headless" / "-D headless2".)'
871
- )
872
- sb.headless = True
865
+ if not sb.undetectable:
866
+ print(
867
+ '(Linux uses "-D headless" by default. '
868
+ 'To override, use "-D headed" / "-D gui". '
869
+ 'For Xvfb mode instead, use "-D xvfb". '
870
+ "Or you can hide this info by using"
871
+ '"-D headless" / "-D headless2" / "-D uc".)'
872
+ )
873
+ sb.headless = True
874
+ else:
875
+ sb.xvfb = True
873
876
  # Recorder Mode can still optimize scripts in --headless2 mode.
874
877
  if sb.recorder_mode and sb.headless:
875
878
  sb.headless = False
@@ -11,6 +11,7 @@ import urllib3
11
11
  import warnings
12
12
  from selenium import webdriver
13
13
  from selenium.common.exceptions import ElementClickInterceptedException
14
+ from selenium.common.exceptions import InvalidSessionIdException
14
15
  from selenium.webdriver.chrome.service import Service as ChromeService
15
16
  from selenium.webdriver.common.options import ArgOptions
16
17
  from selenium.webdriver.common.service import utils as service_utils
@@ -28,6 +29,7 @@ from seleniumbase.core import proxy_helper
28
29
  from seleniumbase.core import sb_driver
29
30
  from seleniumbase.fixtures import constants
30
31
  from seleniumbase.fixtures import js_utils
32
+ from seleniumbase.fixtures import page_actions
31
33
  from seleniumbase.fixtures import shared_utils
32
34
 
33
35
  urllib3.disable_warnings()
@@ -130,9 +132,13 @@ def extend_driver(driver):
130
132
  page.assert_element_not_visible = DM.assert_element_not_visible
131
133
  page.assert_text = DM.assert_text
132
134
  page.assert_exact_text = DM.assert_exact_text
135
+ page.assert_non_empty_text = DM.assert_non_empty_text
136
+ page.assert_text_not_visible = DM.assert_text_not_visible
133
137
  page.wait_for_element = DM.wait_for_element
134
138
  page.wait_for_text = DM.wait_for_text
135
139
  page.wait_for_exact_text = DM.wait_for_exact_text
140
+ page.wait_for_non_empty_text = DM.wait_for_non_empty_text
141
+ page.wait_for_text_not_visible = DM.wait_for_text_not_visible
136
142
  page.wait_for_and_accept_alert = DM.wait_for_and_accept_alert
137
143
  page.wait_for_and_dismiss_alert = DM.wait_for_and_dismiss_alert
138
144
  page.is_element_present = DM.is_element_present
@@ -140,12 +146,20 @@ def extend_driver(driver):
140
146
  page.is_text_visible = DM.is_text_visible
141
147
  page.is_exact_text_visible = DM.is_exact_text_visible
142
148
  page.is_attribute_present = DM.is_attribute_present
149
+ page.is_non_empty_text_visible = DM.is_non_empty_text_visible
143
150
  page.get_text = DM.get_text
144
151
  page.find_element = DM.find_element
145
152
  page.find_elements = DM.find_elements
146
153
  page.locator = DM.locator
147
154
  page.get_page_source = DM.get_page_source
148
155
  page.get_title = DM.get_title
156
+ page.switch_to_default_window = DM.switch_to_default_window
157
+ page.switch_to_newest_window = DM.switch_to_newest_window
158
+ page.open_new_window = DM.open_new_window
159
+ page.open_new_tab = DM.open_new_tab
160
+ page.switch_to_window = DM.switch_to_window
161
+ page.switch_to_tab = DM.switch_to_tab
162
+ page.switch_to_frame = DM.switch_to_frame
149
163
  driver.page = page
150
164
  js = types.SimpleNamespace()
151
165
  js.js_click = DM.js_click
@@ -169,12 +183,16 @@ def extend_driver(driver):
169
183
  driver.assert_element_not_visible = DM.assert_element_not_visible
170
184
  driver.assert_text = DM.assert_text
171
185
  driver.assert_exact_text = DM.assert_exact_text
186
+ driver.assert_non_empty_text = DM.assert_non_empty_text
187
+ driver.assert_text_not_visible = DM.assert_text_not_visible
172
188
  driver.wait_for_element = DM.wait_for_element
173
189
  driver.wait_for_element_visible = DM.wait_for_element_visible
174
190
  driver.wait_for_element_present = DM.wait_for_element_present
175
191
  driver.wait_for_selector = DM.wait_for_selector
176
192
  driver.wait_for_text = DM.wait_for_text
177
193
  driver.wait_for_exact_text = DM.wait_for_exact_text
194
+ driver.wait_for_non_empty_text = DM.wait_for_non_empty_text
195
+ driver.wait_for_text_not_visible = DM.wait_for_text_not_visible
178
196
  driver.wait_for_and_accept_alert = DM.wait_for_and_accept_alert
179
197
  driver.wait_for_and_dismiss_alert = DM.wait_for_and_dismiss_alert
180
198
  driver.is_element_present = DM.is_element_present
@@ -182,8 +200,10 @@ def extend_driver(driver):
182
200
  driver.is_text_visible = DM.is_text_visible
183
201
  driver.is_exact_text_visible = DM.is_exact_text_visible
184
202
  driver.is_attribute_present = DM.is_attribute_present
185
- driver.get_text = DM.get_text
203
+ driver.is_non_empty_text_visible = DM.is_non_empty_text_visible
204
+ driver.is_online = DM.is_online
186
205
  driver.js_click = DM.js_click
206
+ driver.get_text = DM.get_text
187
207
  driver.get_active_element_css = DM.get_active_element_css
188
208
  driver.get_locale_code = DM.get_locale_code
189
209
  driver.get_origin = DM.get_origin
@@ -195,6 +215,12 @@ def extend_driver(driver):
195
215
  driver.get_attribute = DM.get_attribute
196
216
  driver.get_page_source = DM.get_page_source
197
217
  driver.get_title = DM.get_title
218
+ driver.switch_to_default_window = DM.switch_to_default_window
219
+ driver.switch_to_newest_window = DM.switch_to_newest_window
220
+ driver.open_new_window = DM.open_new_window
221
+ driver.open_new_tab = DM.open_new_tab
222
+ driver.switch_to_window = DM.switch_to_window
223
+ driver.switch_to_tab = DM.switch_to_tab
198
224
  driver.switch_to_frame = DM.switch_to_frame
199
225
  if hasattr(driver, "proxy"):
200
226
  driver.set_wire_proxy = DM.set_wire_proxy
@@ -409,7 +435,7 @@ def uc_open(driver, url):
409
435
  if (url.startswith("http:") or url.startswith("https:")):
410
436
  with driver:
411
437
  script = 'window.location.href = "%s";' % url
412
- js_utils.call_me_later(driver, script, 33)
438
+ js_utils.call_me_later(driver, script, 5)
413
439
  else:
414
440
  driver.default_get(url) # The original one
415
441
  return None
@@ -440,22 +466,28 @@ def uc_open_with_reconnect(driver, url, reconnect_time=None):
440
466
  url = "https://" + url
441
467
  if (url.startswith("http:") or url.startswith("https:")):
442
468
  script = 'window.open("%s","_blank");' % url
443
- js_utils.call_me_later(driver, script, 3)
444
- time.sleep(0.007)
469
+ driver.execute_script(script)
470
+ time.sleep(0.05)
445
471
  driver.close()
446
472
  if reconnect_time == "disconnect":
447
473
  driver.disconnect()
448
- time.sleep(0.007)
474
+ time.sleep(0.008)
449
475
  else:
450
476
  driver.reconnect(reconnect_time)
451
- driver.switch_to.window(driver.window_handles[-1])
477
+ time.sleep(0.004)
478
+ try:
479
+ driver.switch_to.window(driver.window_handles[-1])
480
+ except InvalidSessionIdException:
481
+ time.sleep(0.05)
482
+ driver.switch_to.window(driver.window_handles[-1])
452
483
  else:
453
484
  driver.default_get(url) # The original one
454
485
  return None
455
486
 
456
487
 
457
- def uc_open_with_disconnect(driver, url):
488
+ def uc_open_with_disconnect(driver, url, timeout=None):
458
489
  """Open a url and disconnect chromedriver.
490
+ Then waits for the duration of the timeout.
459
491
  Note: You can't perform Selenium actions again
460
492
  until after you've called driver.connect()."""
461
493
  if url.startswith("//"):
@@ -464,11 +496,16 @@ def uc_open_with_disconnect(driver, url):
464
496
  url = "https://" + url
465
497
  if (url.startswith("http:") or url.startswith("https:")):
466
498
  script = 'window.open("%s","_blank");' % url
467
- js_utils.call_me_later(driver, script, 3)
468
- time.sleep(0.007)
499
+ driver.execute_script(script)
500
+ time.sleep(0.05)
469
501
  driver.close()
470
502
  driver.disconnect()
471
- time.sleep(0.007)
503
+ min_timeout = 0.008
504
+ if timeout and not str(timeout).replace(".", "", 1).isdigit():
505
+ timeout = min_timeout
506
+ if not timeout or timeout < min_timeout:
507
+ timeout = min_timeout
508
+ time.sleep(timeout)
472
509
  else:
473
510
  driver.default_get(url) # The original one
474
511
  return None
@@ -490,7 +527,7 @@ def uc_click(
490
527
  pass
491
528
  element = driver.wait_for_selector(selector, by=by, timeout=timeout)
492
529
  tag_name = element.tag_name
493
- if not tag_name == "span": # Element must be "visible"
530
+ if not tag_name == "span" and not tag_name == "input": # Must be "visible"
494
531
  element = driver.wait_for_element(selector, by=by, timeout=timeout)
495
532
  try:
496
533
  element.uc_click(
@@ -509,7 +546,154 @@ def uc_click(
509
546
  driver.reconnect(reconnect_time)
510
547
 
511
548
 
512
- def uc_switch_to_frame(driver, frame, reconnect_time=None):
549
+ def verify_pyautogui_has_a_headed_browser():
550
+ """PyAutoGUI requires a headed browser so that it can
551
+ focus on the correct element when performing actions."""
552
+ if sb_config.headless or sb_config.headless2:
553
+ raise Exception(
554
+ "PyAutoGUI can't be used in headless mode!"
555
+ )
556
+
557
+
558
+ def install_pyautogui_if_missing():
559
+ verify_pyautogui_has_a_headed_browser()
560
+ pip_find_lock = fasteners.InterProcessLock(
561
+ constants.PipInstall.FINDLOCK
562
+ )
563
+ with pip_find_lock: # Prevent issues with multiple processes
564
+ try:
565
+ import pyautogui
566
+ try:
567
+ use_pyautogui_ver = constants.PyAutoGUI.VER
568
+ if pyautogui.__version__ != use_pyautogui_ver:
569
+ del pyautogui
570
+ shared_utils.pip_install(
571
+ "pyautogui", version=use_pyautogui_ver
572
+ )
573
+ import pyautogui
574
+ except Exception:
575
+ pass
576
+ except Exception:
577
+ print("\nPyAutoGUI required! Installing now...")
578
+ shared_utils.pip_install(
579
+ "pyautogui", version=constants.PyAutoGUI.VER
580
+ )
581
+
582
+
583
+ def get_configured_pyautogui(pyautogui_copy):
584
+ if (
585
+ IS_LINUX
586
+ and hasattr(pyautogui_copy, "_pyautogui_x11")
587
+ and "DISPLAY" in os.environ.keys()
588
+ ):
589
+ if (
590
+ hasattr(sb_config, "_pyautogui_x11_display")
591
+ and sb_config._pyautogui_x11_display
592
+ and hasattr(pyautogui_copy._pyautogui_x11, "_display")
593
+ and (
594
+ sb_config._pyautogui_x11_display
595
+ == pyautogui_copy._pyautogui_x11._display
596
+ )
597
+ ):
598
+ pass
599
+ else:
600
+ import Xlib.display
601
+ pyautogui_copy._pyautogui_x11._display = (
602
+ Xlib.display.Display(os.environ['DISPLAY'])
603
+ )
604
+ sb_config._pyautogui_x11_display = (
605
+ pyautogui_copy._pyautogui_x11._display
606
+ )
607
+ return pyautogui_copy
608
+
609
+
610
+ def uc_gui_press_key(driver, key):
611
+ install_pyautogui_if_missing()
612
+ import pyautogui
613
+ pyautogui = get_configured_pyautogui(pyautogui)
614
+ gui_lock = fasteners.InterProcessLock(
615
+ constants.MultiBrowser.PYAUTOGUILOCK
616
+ )
617
+ with gui_lock:
618
+ pyautogui.press(key)
619
+
620
+
621
+ def uc_gui_press_keys(driver, keys):
622
+ install_pyautogui_if_missing()
623
+ import pyautogui
624
+ pyautogui = get_configured_pyautogui(pyautogui)
625
+ gui_lock = fasteners.InterProcessLock(
626
+ constants.MultiBrowser.PYAUTOGUILOCK
627
+ )
628
+ with gui_lock:
629
+ for key in keys:
630
+ pyautogui.press(key)
631
+
632
+
633
+ def uc_gui_write(driver, text):
634
+ install_pyautogui_if_missing()
635
+ import pyautogui
636
+ pyautogui = get_configured_pyautogui(pyautogui)
637
+ gui_lock = fasteners.InterProcessLock(
638
+ constants.MultiBrowser.PYAUTOGUILOCK
639
+ )
640
+ with gui_lock:
641
+ pyautogui.write(text)
642
+
643
+
644
+ def uc_gui_handle_cf(driver, frame="iframe"):
645
+ source = driver.get_page_source()
646
+ if (
647
+ "//challenges.cloudflare.com" not in source
648
+ and 'aria-label="Cloudflare"' not in source
649
+ ):
650
+ return
651
+ install_pyautogui_if_missing()
652
+ import pyautogui
653
+ pyautogui = get_configured_pyautogui(pyautogui)
654
+ gui_lock = fasteners.InterProcessLock(
655
+ constants.MultiBrowser.PYAUTOGUILOCK
656
+ )
657
+ with gui_lock: # Prevent issues with multiple processes
658
+ needs_switch = False
659
+ is_in_frame = js_utils.is_in_frame(driver)
660
+ if is_in_frame and driver.is_element_present("#challenge-stage"):
661
+ driver.switch_to.parent_frame()
662
+ needs_switch = True
663
+ is_in_frame = js_utils.is_in_frame(driver)
664
+ if not is_in_frame:
665
+ # Make sure the window is on top
666
+ page_actions.switch_to_window(
667
+ driver,
668
+ driver.current_window_handle,
669
+ timeout=settings.SMALL_TIMEOUT,
670
+ )
671
+ if not is_in_frame or needs_switch:
672
+ # Currently not in frame (or nested frame outside CF one)
673
+ try:
674
+ driver.switch_to_frame(frame)
675
+ except Exception:
676
+ if driver.is_element_present("iframe"):
677
+ driver.switch_to_frame("iframe")
678
+ else:
679
+ return
680
+ try:
681
+ driver.execute_script('document.querySelector("input").focus()')
682
+ except Exception:
683
+ try:
684
+ driver.switch_to.default_content()
685
+ except Exception:
686
+ return
687
+ driver.disconnect()
688
+ try:
689
+ pyautogui.press(" ")
690
+ except Exception:
691
+ pass
692
+ reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.5
693
+ driver.reconnect(reconnect_time)
694
+
695
+
696
+ def uc_switch_to_frame(driver, frame="iframe", reconnect_time=None):
513
697
  from selenium.webdriver.remote.webelement import WebElement
514
698
  if isinstance(frame, WebElement):
515
699
  if not reconnect_time:
@@ -847,7 +1031,7 @@ def _set_chrome_options(
847
1031
  prefs["download.prompt_for_download"] = False
848
1032
  prefs["credentials_enable_service"] = False
849
1033
  prefs["local_discovery.notifications_enabled"] = False
850
- prefs["safebrowsing.enabled"] = False
1034
+ prefs["safebrowsing.enabled"] = False # Prevent PW "data breach" pop-ups
851
1035
  prefs["safebrowsing.disable_download_protection"] = True
852
1036
  prefs["omnibox-max-zero-suggest-matches"] = 0
853
1037
  prefs["omnibox-use-existing-autocomplete-client"] = 0
@@ -1145,6 +1329,7 @@ def _set_chrome_options(
1145
1329
  chrome_options.add_argument("--auto-open-devtools-for-tabs")
1146
1330
  if user_agent:
1147
1331
  chrome_options.add_argument("--user-agent=%s" % user_agent)
1332
+ chrome_options.add_argument("--safebrowsing-disable-download-protection")
1148
1333
  chrome_options.add_argument("--disable-browser-side-navigation")
1149
1334
  chrome_options.add_argument("--disable-save-password-bubble")
1150
1335
  chrome_options.add_argument("--disable-single-click-autofill")
@@ -1183,10 +1368,9 @@ def _set_chrome_options(
1183
1368
  included_disabled_features.append("DownloadBubbleV2")
1184
1369
  included_disabled_features.append("InsecureDownloadWarnings")
1185
1370
  included_disabled_features.append("InterestFeedContentSuggestions")
1186
- if user_data_dir:
1187
- included_disabled_features.append("PrivacySandboxSettings4")
1188
- if not is_using_uc(undetectable, browser_name) or user_data_dir:
1189
- included_disabled_features.append("SidePanelPinning")
1371
+ included_disabled_features.append("PrivacySandboxSettings4")
1372
+ included_disabled_features.append("SidePanelPinning")
1373
+ included_disabled_features.append("UserAgentClientHint")
1190
1374
  for item in extra_disabled_features:
1191
1375
  if item not in included_disabled_features:
1192
1376
  included_disabled_features.append(item)
@@ -1374,7 +1558,7 @@ def _set_firefox_options(
1374
1558
  f_pref_value = False
1375
1559
  elif f_pref_value.isdigit():
1376
1560
  f_pref_value = int(f_pref_value)
1377
- elif f_pref_value.isdecimal():
1561
+ elif f_pref_value.replace(".", "", 1).isdigit():
1378
1562
  f_pref_value = float(f_pref_value)
1379
1563
  else:
1380
1564
  pass # keep as string
@@ -2438,7 +2622,7 @@ def get_local_driver(
2438
2622
  "credentials_enable_service": False,
2439
2623
  "local_discovery.notifications_enabled": False,
2440
2624
  "safebrowsing.disable_download_protection": True,
2441
- "safebrowsing.enabled": False,
2625
+ "safebrowsing.enabled": False, # Prevent PW "data breach" pop-ups
2442
2626
  "omnibox-max-zero-suggest-matches": 0,
2443
2627
  "omnibox-use-existing-autocomplete-client": 0,
2444
2628
  "omnibox-trending-zero-prefix-suggestions-on-ntp": 0,
@@ -2707,6 +2891,7 @@ def get_local_driver(
2707
2891
  edge_options.add_argument(
2708
2892
  "--disable-autofill-keyboard-accessory-view[8]"
2709
2893
  )
2894
+ edge_options.add_argument("--safebrowsing-disable-download-protection")
2710
2895
  edge_options.add_argument("--disable-browser-side-navigation")
2711
2896
  edge_options.add_argument("--disable-translate")
2712
2897
  if not enable_ws:
@@ -2848,10 +3033,9 @@ def get_local_driver(
2848
3033
  included_disabled_features.append("OptimizationGuideModelDownloading")
2849
3034
  included_disabled_features.append("InsecureDownloadWarnings")
2850
3035
  included_disabled_features.append("InterestFeedContentSuggestions")
2851
- if user_data_dir:
2852
- included_disabled_features.append("PrivacySandboxSettings4")
2853
- if not is_using_uc(undetectable, browser_name) or user_data_dir:
2854
- included_disabled_features.append("SidePanelPinning")
3036
+ included_disabled_features.append("PrivacySandboxSettings4")
3037
+ included_disabled_features.append("SidePanelPinning")
3038
+ included_disabled_features.append("UserAgentClientHint")
2855
3039
  for item in extra_disabled_features:
2856
3040
  if item not in included_disabled_features:
2857
3041
  included_disabled_features.append(item)
@@ -3822,6 +4006,26 @@ def get_local_driver(
3822
4006
  driver.uc_click = lambda *args, **kwargs: uc_click(
3823
4007
  driver, *args, **kwargs
3824
4008
  )
4009
+ driver.uc_gui_press_key = (
4010
+ lambda *args, **kwargs: uc_gui_press_key(
4011
+ driver, *args, **kwargs
4012
+ )
4013
+ )
4014
+ driver.uc_gui_press_keys = (
4015
+ lambda *args, **kwargs: uc_gui_press_keys(
4016
+ driver, *args, **kwargs
4017
+ )
4018
+ )
4019
+ driver.uc_gui_write = (
4020
+ lambda *args, **kwargs: uc_gui_write(
4021
+ driver, *args, **kwargs
4022
+ )
4023
+ )
4024
+ driver.uc_gui_handle_cf = (
4025
+ lambda *args, **kwargs: uc_gui_handle_cf(
4026
+ driver, *args, **kwargs
4027
+ )
4028
+ )
3825
4029
  driver.uc_switch_to_frame = (
3826
4030
  lambda *args, **kwargs: uc_switch_to_frame(
3827
4031
  driver, *args, **kwargs
@@ -94,6 +94,16 @@ class DriverMethods():
94
94
  def assert_exact_text(self, *args, **kwargs):
95
95
  page_actions.assert_exact_text(self.driver, *args, **kwargs)
96
96
 
97
+ def assert_non_empty_text(self, *args, **kwargs):
98
+ return page_actions.assert_non_empty_text(
99
+ self.driver, *args, **kwargs
100
+ )
101
+
102
+ def assert_text_not_visible(self, *args, **kwargs):
103
+ return page_actions.assert_text_not_visible(
104
+ self.driver, *args, **kwargs
105
+ )
106
+
97
107
  def wait_for_element(self, *args, **kwargs):
98
108
  return page_actions.wait_for_element(self.driver, *args, **kwargs)
99
109
 
@@ -112,6 +122,16 @@ class DriverMethods():
112
122
  def wait_for_exact_text(self, *args, **kwargs):
113
123
  return page_actions.wait_for_exact_text(self.driver, *args, **kwargs)
114
124
 
125
+ def wait_for_non_empty_text(self, *args, **kwargs):
126
+ return page_actions.wait_for_non_empty_text(
127
+ self.driver, *args, **kwargs
128
+ )
129
+
130
+ def wait_for_text_not_visible(self, *args, **kwargs):
131
+ return page_actions.wait_for_text_not_visible(
132
+ self.driver, *args, **kwargs
133
+ )
134
+
115
135
  def wait_for_and_accept_alert(self, *args, **kwargs):
116
136
  return page_actions.wait_for_and_accept_alert(
117
137
  self.driver, *args, **kwargs
@@ -134,14 +154,22 @@ class DriverMethods():
134
154
  def is_exact_text_visible(self, *args, **kwargs):
135
155
  return page_actions.is_exact_text_visible(self.driver, *args, **kwargs)
136
156
 
137
- def get_text(self, *args, **kwargs):
138
- return page_actions.get_text(self.driver, *args, **kwargs)
157
+ def is_attribute_present(self, *args, **kwargs):
158
+ return page_actions.has_attribute(self.driver, *args, **kwargs)
159
+
160
+ def is_non_empty_text_visible(self, *args, **kwargs):
161
+ return page_actions.is_non_empty_text_visible(
162
+ self.driver, *args, **kwargs
163
+ )
164
+
165
+ def is_online(self):
166
+ return self.driver.execute_script("return navigator.onLine;")
139
167
 
140
168
  def js_click(self, *args, **kwargs):
141
169
  return page_actions.js_click(self.driver, *args, **kwargs)
142
170
 
143
- def is_attribute_present(self, *args, **kwargs):
144
- return page_actions.has_attribute(self.driver, *args, **kwargs)
171
+ def get_text(self, *args, **kwargs):
172
+ return page_actions.get_text(self.driver, *args, **kwargs)
145
173
 
146
174
  def get_active_element_css(self, *args, **kwargs):
147
175
  return js_utils.get_active_element_css(self.driver, *args, **kwargs)
@@ -182,7 +210,32 @@ class DriverMethods():
182
210
  if self.is_element_visible(selector, by=by):
183
211
  self.highlight(selector, by=by, loops=loops, scroll=scroll)
184
212
 
185
- def switch_to_frame(self, frame):
213
+ def switch_to_default_window(self):
214
+ self.driver.switch_to.window(self.driver.window_handles[0])
215
+
216
+ def switch_to_newest_window(self):
217
+ self.driver.switch_to.window(self.driver.window_handles[-1])
218
+
219
+ def open_new_window(self, switch_to=True):
220
+ if switch_to:
221
+ try:
222
+ self.driver.switch_to.new_window("tab")
223
+ except Exception:
224
+ self.driver.execute_script("window.open('');")
225
+ self.switch_to_newest_window()
226
+ else:
227
+ self.driver.execute_script("window.open('');")
228
+
229
+ def open_new_tab(self, switch_to=True):
230
+ self.open_new_window(switch_to=switch_to)
231
+
232
+ def switch_to_window(self, *args, **kwargs):
233
+ page_actions.switch_to_window(self.driver, *args, **kwargs)
234
+
235
+ def switch_to_tab(self, *args, **kwargs):
236
+ self.switch_to_window(*args, **kwargs)
237
+
238
+ def switch_to_frame(self, frame="iframe"):
186
239
  if isinstance(frame, WebElement):
187
240
  self.driver.switch_to.frame(frame)
188
241
  else:
Binary file