Best Python code snippet using SeleniumBase
base_case.py
Source:base_case.py
...594 is_ready = js_utils.wait_for_ready_state_complete(self.driver, timeout)595 self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)596 return is_ready597 @staticmethod598 def __looks_like_a_page_url(url):599 """Returns True if the url parameter looks like a URL. This method600 is slightly more lenient than page_utils.is_valid_url(url) due to601 possible typos when calling self.get(url), which will try to602 navigate to the page if a URL is detected, but will instead call603 self.get_element(URL_AS_A_SELECTOR) if the input in not a URL."""604 if (605 url.startswith("http:")606 or url.startswith("https:")607 or url.startswith("://")608 or url.startswith("chrome:")609 or url.startswith("about:")610 or url.startswith("data:")611 or url.startswith("file:")612 or url.startswith("edge:")613 or url.startswith("opera:")614 ):615 return True616 else:617 return False618 @staticmethod619 def __escape_quotes_if_needed(string):620 """621 __escape_quotes_if_needed622 """623 return js_utils.escape_quotes_if_needed(string)624 def execute_script(self, script, *args, **kwargs):625 """626 execute_script627 """628 return self.driver.execute_script(script, *args, **kwargs)629 def __get_new_timeout(self, timeout):630 """ When using --timeout_multiplier=#.# """631 import math632 try:633 timeout_multiplier = float(self.timeout_multiplier)634 if timeout_multiplier <= 0.5:635 timeout_multiplier = 0.5636 timeout = int(math.ceil(timeout_multiplier * timeout))637 return timeout638 except Exception:639 # Wrong data type for timeout_multiplier (expecting int or float)640 return timeout641 def wait_for_angularjs(self, timeout=None, **kwargs):642 """643 wait_for_angularjs644 """645 if not timeout:646 timeout = settings.LARGE_TIMEOUT647 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:648 timeout = self.__get_new_timeout(timeout)649 js_utils.wait_for_angularjs(self.driver, timeout, **kwargs)650 def assert_no_js_errors(self):651 """Asserts that there are no JavaScript "SEVERE"-level page errors.652 Works ONLY for Chrome (non-headless) and Chrome-based browsers.653 Does NOT work on Firefox, Edge, IE, and some other browsers:654 * See https://github.com/SeleniumHQ/selenium/issues/1161655 Based on the following Stack Overflow solution:656 * https://stackoverflow.com/a/41150512/7058266657 """658 time.sleep(0.1) # May take a moment for errors to appear after loads.659 try:660 browser_logs = self.driver.get_log("browser")661 except (ValueError, WebDriverException):662 # If unable to get browser logs, skip the assert and return.663 return664 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"665 errors = []666 for entry in browser_logs:667 if entry["level"] == "SEVERE":668 if messenger_library not in entry["message"]:669 # Add errors if not caused by SeleniumBase dependencies670 errors.append(entry)671 if len(errors) > 0:672 current_url = self.get_current_url()673 raise Exception(674 "JavaScript errors found on %s => %s" % (current_url, errors)675 )676 def get_current_url(self):677 """678 get_current_url679 """680 current_url = self.driver.current_url681 if "%" in current_url and sys.version_info[0] >= 3:682 try:683 import urllib.parse684 current_url = urllib.parse.unquote(current_url, errors="strict")685 except Exception:686 pass687 return current_url688 def ad_block(self):689 """ Block ads that appear on the current web page. """690 from seleniumbase.config import ad_block_list691 for css_selector in ad_block_list.AD_BLOCK_LIST:692 css_selector = re.escape(css_selector) # Add "\\" to special chars693 css_selector = self.__escape_quotes_if_needed(css_selector)694 script = (695 """var $elements = document.querySelectorAll('%s');696 var index = 0, length = $elements.length;697 for(; index < length; index++){698 $elements[index].remove();}"""699 % css_selector700 )701 try:702 self.execute_script(script)703 except Exception:704 pass # Don't fail test if ad_blocking fails705 def is_element_present(self, selector, by=By.CSS_SELECTOR):706 """707 is_element_present708 """709 self.wait_for_ready_state_complete()710 selector, by = self.__recalculate_selector(selector, by)711 return page_actions.is_element_present(self.driver, selector, by)712 def __recalculate_selector(self, selector=None, by=None, xp_ok=True):713 """Use autodetection to return the correct selector with "by" updated.714 If "xp_ok" is False, don't call convert_css_to_xpath(), which is715 used to make the ":contains()" selector valid outside JS calls."""716 _type = type(selector) # First make sure the selector is a string717 not_string = False718 if sys.version_info[0] < 3:719 if _type is not str and _type is not unicode: # noqa: F821720 not_string = True721 else:722 if _type is not str:723 not_string = True724 if not_string:725 msg = "Expecting a selector of type: \"<class 'str'>\" (string)!"726 raise Exception('Invalid selector type: "%s"\n%s' % (_type, msg))727 if page_utils.is_xpath_selector(selector):728 by = By.XPATH729 if page_utils.is_link_text_selector(selector):730 selector = page_utils.get_link_text_from_selector(selector)731 by = By.LINK_TEXT732 if page_utils.is_partial_link_text_selector(selector):733 selector = page_utils.get_partial_link_text_from_selector(selector)734 by = By.PARTIAL_LINK_TEXT735 if page_utils.is_name_selector(selector):736 name = page_utils.get_name_from_selector(selector)737 selector = '[name="%s"]' % name738 by = By.CSS_SELECTOR739 if xp_ok:740 if ":contains(" in selector and by == By.CSS_SELECTOR:741 selector = self.convert_css_to_xpath(selector)742 by = By.XPATH743 return selector, by744 def convert_css_to_xpath(self, css):745 """746 convert_css_to_xpath747 """748 return css_to_xpath.convert_css_to_xpath(css)749 def open(self, url):750 """ Navigates the current browser window to the specified page. """751 if type(url) is str:752 url = url.strip() # Remove leading and trailing whitespace753 if (type(url) is not str) or not self.__looks_like_a_page_url(url):754 # url should start with one of the following:755 # "http:", "https:", "://", "data:", "file:",756 # "about:", "chrome:", "opera:", or "edge:".757 msg = 'Did you forget to prefix your URL with "http:" or "https:"?'758 raise Exception('Invalid URL: "%s"\n%s' % (url, msg))759 self.__last_page_load_url = None760 js_utils.clear_out_console_logs(self.driver)761 if url.startswith("://"):762 # Convert URLs such as "://google.com" into "https://google.com"763 url = "https" + url764 if self.browser == "safari" and url.startswith("data:"):765 url = re.escape(url)766 url = self.__escape_quotes_if_needed(url)767 self.execute_script("window.location.href='%s';" % url)768 else:769 self.driver.get(url)770 def get(self, url):771 """If "url" looks like a page URL, open the URL in the web browser.772 Otherwise, return self.get_element(URL_AS_A_SELECTOR)773 Examples:774 self.get("https://seleniumbase.io") # Navigates to the URL775 self.get("input.class") # Finds and returns the WebElement776 """777 if self.__looks_like_a_page_url(url):778 self.open(url)779 else:780 return self.get_element(url) # url is treated like a selector781 def refresh_page(self):782 """783 refresh_page784 """785 self.__last_page_load_url = None786 js_utils.clear_out_console_logs(self.driver)787 self.driver.refresh()788 self.wait_for_ready_state_complete()789 def refresh(self):790 """ The shorter version of self.refresh_page() """791 self.refresh_page()792 def get_page_title(self):793 """794 get_page_title795 """796 self.wait_for_ready_state_complete()797 time.sleep(0.03)798 return self.driver.title799 def get_title(self):800 """ The shorter version of self.get_page_title() """801 return self.get_page_title()802 def get_user_agent(self):803 """804 get_user_agent805 """806 user_agent = self.driver.execute_script("return navigator.userAgent;")807 return user_agent808 def get_locale_code(self):809 """810 get_locale_code811 """812 locale_code = self.driver.execute_script(813 "return navigator.language || navigator.languages[0];"814 )815 return locale_code816 def go_back(self):817 """818 go_back819 """820 self.__last_page_load_url = None821 self.driver.back()822 if self.browser == "safari":823 self.wait_for_ready_state_complete()824 self.driver.refresh()825 self.wait_for_ready_state_complete()826 def go_forward(self):827 """828 go_forward829 """830 self.__last_page_load_url = None831 self.driver.forward()832 self.wait_for_ready_state_complete()833 def open_start_page(self):834 """Navigates the current browser window to the start_page.835 You can set the start_page on the command-line in three ways:836 '--start_page=URL', '--start-page=URL', or '--url=URL'.837 If the start_page is not set, then "data:," will be used."""838 start_page = self.start_page839 if type(start_page) is str:840 start_page = start_page.strip() # Remove extra whitespace841 if start_page and len(start_page) >= 4:842 if page_utils.is_valid_url(start_page):843 self.open(start_page)844 else:845 new_start_page = "http://" + start_page846 if page_utils.is_valid_url(new_start_page):847 self.open(new_start_page)848 else:849 logging.info('Invalid URL: "%s"!' % start_page)850 self.open("data:,")851 else:852 self.open("data:,")853 def load_html_string(self, html_string, new_page=True):854 """Loads an HTML string into the web browser.855 If new_page==True, the page will switch to: "data:text/html,"856 If new_page==False, will load HTML into the current page."""857 self.__check_scope()858 soup = self.get_beautiful_soup(html_string)859 found_base = False860 links = soup.findAll("link")861 href = None862 for link in links:863 if link.get("rel") == ["canonical"] and link.get("href"):864 found_base = True865 href = link.get("href")866 href = self.get_domain_url(href)867 if (868 found_base869 and html_string.count("<head>") == 1870 and html_string.count("<base") == 0871 ):872 html_string = html_string.replace(873 "<head>", '<head><base href="%s">' % href874 )875 elif not found_base:876 bases = soup.findAll("base")877 for base in bases:878 if base.get("href"):879 href = base.get("href")880 if href:881 html_string = html_string.replace('base: "."', 'base: "%s"' % href)882 soup = self.get_beautiful_soup(html_string)883 scripts = soup.findAll("script")884 for script in scripts:885 if script.get("type") != "application/json":886 html_string = html_string.replace(str(script), "")887 soup = self.get_beautiful_soup(html_string)888 found_head = False889 found_body = False890 html_head = None891 html_body = None892 if soup.head and len(str(soup.head)) > 12:893 found_head = True894 html_head = str(soup.head)895 html_head = re.escape(html_head)896 html_head = self.__escape_quotes_if_needed(html_head)897 html_head = html_head.replace("\\ ", " ")898 if soup.body and len(str(soup.body)) > 12:899 found_body = True900 html_body = str(soup.body)901 html_body = html_body.replace("\xc2\xa0", " ")902 html_body = html_body.replace("\xc2\xa1", "¡")903 html_body = html_body.replace("\xc2\xa9", "©")904 html_body = html_body.replace("\xc2\xb7", "·")905 html_body = html_body.replace("\xc2\xbf", "¿")906 html_body = html_body.replace("\xc3\x97", "×")907 html_body = html_body.replace("\xc3\xb7", "÷")908 html_body = re.escape(html_body)909 html_body = self.__escape_quotes_if_needed(html_body)910 html_body = html_body.replace("\\ ", " ")911 html_string = re.escape(html_string)912 html_string = self.__escape_quotes_if_needed(html_string)913 html_string = html_string.replace("\\ ", " ")914 if new_page:915 self.open("data:text/html,")916 inner_head = """document.getElementsByTagName("head")[0].innerHTML"""917 inner_body = """document.getElementsByTagName("body")[0].innerHTML"""918 if not found_body:919 self.execute_script('''%s = \"%s\"''' % (inner_body, html_string))920 elif found_body and not found_head:921 self.execute_script('''%s = \"%s\"''' % (inner_body, html_body))922 elif found_body and found_head:923 self.execute_script('''%s = \"%s\"''' % (inner_head, html_head))924 self.execute_script('''%s = \"%s\"''' % (inner_body, html_body))925 else:926 raise Exception("Logic Error!")927 for script in scripts:928 js_code = script.string929 js_src = script.get("src")930 if js_code and script.get("type") != "application/json":931 js_code_lines = js_code.split("\n")932 new_lines = []933 for line in js_code_lines:934 line = line.strip()935 new_lines.append(line)936 js_code = "\n".join(new_lines)937 js_code = re.escape(js_code)938 js_utils.add_js_code(self.driver, js_code)939 elif js_src:940 js_utils.add_js_link(self.driver, js_src)941 else:942 pass943 def set_content(self, html_string, new_page=False):944 """ Same as load_html_string(), but "new_page" defaults to False. """945 self.load_html_string(html_string, new_page=new_page)946 def load_html_file(self, html_file, new_page=True):947 """Loads a local html file into the browser from a relative file path.948 If new_page==True, the page will switch to: "data:text/html,"949 If new_page==False, will load HTML into the current page.950 Local images and other local src content WILL BE IGNORED.951 """952 self.__check_scope()953 if self.__looks_like_a_page_url(html_file):954 self.open(html_file)955 return956 if len(html_file) < 6 or not html_file.endswith(".html"):957 raise Exception('Expecting a ".html" file!')958 abs_path = os.path.abspath(".")959 file_path = None960 if abs_path in html_file:961 file_path = html_file962 else:963 file_path = abs_path + "/%s" % html_file964 html_string = None965 with open(file_path, "r") as f:966 html_string = f.read().strip()967 self.load_html_string(html_string, new_page)968 def open_html_file(self, html_file):969 """Opens a local html file into the browser from a relative file path.970 The URL displayed in the web browser will start with "file://".971 """972 self.__check_scope()973 if self.__looks_like_a_page_url(html_file):974 self.open(html_file)975 return976 if len(html_file) < 6 or not html_file.endswith(".html"):977 raise Exception('Expecting a ".html" file!')978 abs_path = os.path.abspath(".")979 file_path = None980 if abs_path in html_file:981 file_path = html_file982 else:983 file_path = abs_path + "/%s" % html_file984 self.open("file://" + file_path)985 def execute_async_script(self, script, timeout=None):986 """987 execute_async_script988 """989 self.__check_scope()990 if not timeout:991 timeout = settings.EXTREME_TIMEOUT992 return js_utils.execute_async_script(self.driver, script, timeout)993 def set_window_rect(self, x, y, width, height):994 """995 set_window_rect996 """997 self.__check_scope()998 self.driver.set_window_rect(x, y, width, height)999 def set_window_size(self, width, height):1000 """1001 set_window_size1002 """1003 self.__check_scope()1004 self.driver.set_window_size(width, height)1005 def maximize_window(self):1006 """1007 maximize_window1008 """1009 self.__check_scope()1010 self.driver.maximize_window()1011 def open_new_window(self, switch_to=True):1012 """ Opens a new browser tab/window and switches to it by default. """1013 self.__check_scope()1014 self.driver.execute_script("window.open('');")1015 time.sleep(0.01)1016 if switch_to:1017 self.switch_to_newest_window()1018 time.sleep(0.01)1019 if self.browser == "safari":1020 self.wait_for_ready_state_complete()1021 def switch_to_window(self, window, timeout=None):1022 """1023 switch_to_window1024 """1025 self.__check_scope()1026 if not timeout:1027 timeout = settings.SMALL_TIMEOUT1028 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1029 timeout = self.__get_new_timeout(timeout)1030 page_actions.switch_to_window(self.driver, window, timeout)1031 def switch_to_default_window(self):1032 """1033 switch_to_default_window1034 """1035 self.switch_to_window(0)1036 def switch_to_driver(self, driver):1037 """Sets self.driver to the specified driver.1038 You may need this if using self.get_new_driver() in your code."""1039 self.driver = driver1040 if self.driver in self.__driver_browser_map:1041 self.browser = self.__driver_browser_map[self.driver]1042 def switch_to_default_driver(self):1043 """ Sets self.driver to the default/original driver. """1044 self.__check_scope()1045 self.driver = self._default_driver1046 if self.driver in self.__driver_browser_map:1047 self.browser = self.__driver_browser_map[self.driver]1048 def save_screenshot(self, name, folder=None):1049 """Saves a screenshot of the current page.1050 If no folder is specified, uses the folder where pytest was called.1051 The screenshot will be in PNG format."""1052 self.wait_for_ready_state_complete()1053 return page_actions.save_screenshot(self.driver, name, folder)1054 def save_page_source(self, name, folder=None):1055 """Saves the page HTML to the current directory (or given subfolder).1056 If the folder specified doesn't exist, it will get created.1057 @Params1058 name - The file name to save the current page's HTML to.1059 folder - The folder to save the file to. (Default = current folder)1060 """1061 self.wait_for_ready_state_complete()1062 return page_actions.save_page_source(self.driver, name, folder)1063 def save_cookies(self, name="cookies.txt"):1064 """ Saves the page cookies to the "saved_cookies" folder. """1065 self.wait_for_ready_state_complete()1066 cookies = self.driver.get_cookies()1067 json_cookies = json.dumps(cookies)1068 if name.endswith("/"):1069 raise Exception("Invalid filename for Cookies!")1070 if "/" in name:1071 name = name.split("/")[-1]1072 if len(name) < 1:1073 raise Exception("Filename for Cookies is too short!")1074 if not name.endswith(".txt"):1075 name = name + ".txt"1076 folder = constants.SavedCookies.STORAGE_FOLDER1077 abs_path = os.path.abspath(".")1078 file_path = abs_path + "/%s" % folder1079 if not os.path.exists(file_path):1080 os.makedirs(file_path)1081 cookies_file_path = "%s/%s" % (file_path, name)1082 cookies_file = codecs.open(cookies_file_path, "w+", encoding="utf-8")1083 cookies_file.writelines(json_cookies)1084 cookies_file.close()1085 def load_cookies(self, name="cookies.txt"):1086 """ Loads the page cookies from the "saved_cookies" folder. """1087 self.wait_for_ready_state_complete()1088 if name.endswith("/"):1089 raise Exception("Invalid filename for Cookies!")1090 if "/" in name:1091 name = name.split("/")[-1]1092 if len(name) < 1:1093 raise Exception("Filename for Cookies is too short!")1094 if not name.endswith(".txt"):1095 name = name + ".txt"1096 folder = constants.SavedCookies.STORAGE_FOLDER1097 abs_path = os.path.abspath(".")1098 file_path = abs_path + "/%s" % folder1099 cookies_file_path = "%s/%s" % (file_path, name)1100 json_cookies = None1101 with open(cookies_file_path, "r") as f:1102 json_cookies = f.read().strip()1103 cookies = json.loads(json_cookies)1104 for cookie in cookies:1105 if "expiry" in cookie:1106 del cookie["expiry"]1107 self.driver.add_cookie(cookie)1108 def delete_all_cookies(self):1109 """Deletes all cookies in the web browser.1110 Does NOT delete the saved cookies file."""1111 self.wait_for_ready_state_complete()1112 self.driver.delete_all_cookies()1113 def delete_saved_cookies(self, name="cookies.txt"):1114 """Deletes the cookies file from the "saved_cookies" folder.1115 Does NOT delete the cookies from the web browser."""1116 self.wait_for_ready_state_complete()1117 if name.endswith("/"):1118 raise Exception("Invalid filename for Cookies!")1119 if "/" in name:1120 name = name.split("/")[-1]1121 if len(name) < 1:1122 raise Exception("Filename for Cookies is too short!")1123 if not name.endswith(".txt"):1124 name = name + ".txt"1125 folder = constants.SavedCookies.STORAGE_FOLDER1126 abs_path = os.path.abspath(".")1127 file_path = abs_path + "/%s" % folder1128 cookies_file_path = "%s/%s" % (file_path, name)1129 if os.path.exists(cookies_file_path):1130 if cookies_file_path.endswith(".txt"):1131 os.remove(cookies_file_path)1132 def sleep(self, seconds):1133 """1134 sleep1135 """1136 self.__check_scope()1137 if not sb_config.time_limit:1138 time.sleep(seconds)1139 elif seconds <= 0.3:1140 shared_utils.check_if_time_limit_exceeded()1141 time.sleep(seconds)1142 shared_utils.check_if_time_limit_exceeded()1143 else:1144 start_ms = time.time() * 1000.01145 stop_ms = start_ms + (seconds * 1000.0)1146 for x in range(int(seconds * 5)):1147 shared_utils.check_if_time_limit_exceeded()1148 now_ms = time.time() * 1000.01149 if now_ms >= stop_ms:1150 break1151 time.sleep(0.2)1152 def install_addon(self, xpi_file):1153 """Installs a Firefox add-on instantly at run-time.1154 @Params1155 xpi_file - A file archive in .xpi format.1156 """1157 self.wait_for_ready_state_complete()1158 if self.browser != "firefox":1159 raise Exception(1160 "install_addon(xpi_file) is for Firefox ONLY!\n"1161 "To load a Chrome extension, use the comamnd-line:\n"1162 "--extension_zip=CRX_FILE OR --extension_dir=DIR"1163 )1164 xpi_path = os.path.abspath(xpi_file)1165 self.driver.install_addon(xpi_path, temporary=True)1166 def activate_design_mode(self):1167 """1168 activate_design_mode1169 """1170 # Activate Chrome's Design Mode, which lets you edit a site directly.1171 # See: https://twitter.com/sulco/status/11775591505633443841172 self.wait_for_ready_state_complete()1173 script = """document.designMode = 'on';"""1174 self.execute_script(script)1175 def deactivate_design_mode(self):1176 """1177 deactivate_design_mode1178 """1179 # Deactivate Chrome's Design Mode.1180 self.wait_for_ready_state_complete()1181 script = """document.designMode = 'off';"""1182 self.execute_script(script)1183 @staticmethod1184 def __are_quotes_escaped(string):1185 """1186 __are_quotes_escaped1187 """1188 return js_utils.are_quotes_escaped(string)1189 def get_unique_links(self):1190 """Get all unique links in the html of the page source.1191 Page links include those obtained from:1192 "a"->"href", "img"->"src", "link"->"href", and "script"->"src".1193 """1194 page_url = self.get_current_url()1195 soup = self.get_beautiful_soup(self.get_page_source())1196 links = page_utils._get_unique_links(page_url, soup)1197 return links1198 @staticmethod1199 def get_link_status_code(link, allow_redirects=False, timeout=5):1200 """Get the status code of a link.1201 If the timeout is exceeded, will return a 404.1202 For a list of available status codes, see:1203 https://en.wikipedia.org/wiki/List_of_HTTP_status_codes1204 """1205 status_code = page_utils._get_link_status_code(1206 link, allow_redirects=allow_redirects, timeout=timeout1207 )1208 return status_code1209 def assert_link_status_code_is_not_404(self, link):1210 """1211 assert_link_status_code_is_not_4041212 """1213 status_code = str(self.get_link_status_code(link))1214 bad_link_str = 'Error: "%s" returned a 404!' % link1215 self.unittest.assertNotEqual(status_code, "404", bad_link_str)1216 def __get_link_if_404_error(self, link):1217 status_code = str(self.get_link_status_code(link))1218 if status_code == "404":1219 # Verify again to be sure. (In case of multi-threading overload.)1220 status_code = str(self.get_link_status_code(link))1221 if status_code == "404":1222 return link1223 else:1224 return None1225 else:1226 return None1227 def assert_no_404_errors(self, multithreaded=True):1228 """Assert no 404 errors from page links obtained from:1229 "a"->"href", "img"->"src", "link"->"href", and "script"->"src".1230 (A 404 error represents a broken link on a web page.)1231 """1232 all_links = self.get_unique_links()1233 links = []1234 for link in all_links:1235 if (1236 "javascript:" not in link1237 and "mailto:" not in link1238 and "data:" not in link1239 ):1240 links.append(link)1241 broken_links = []1242 if multithreaded:1243 from multiprocessing.dummy import Pool as ThreadPool1244 pool = ThreadPool(10)1245 results = pool.map(self.__get_link_if_404_error, links)1246 pool.close()1247 pool.join()1248 for result in results:1249 if result:1250 broken_links.append(result)1251 else:1252 broken_links = []1253 for link in links:1254 if self.__get_link_if_404_error(link):1255 broken_links.append(link)1256 if len(broken_links) > 0:1257 bad_links_str = "\n".join(broken_links)1258 if len(broken_links) == 1:1259 # self.fail("Broken link detected:\n%s" % bad_links_str)1260 raise ("Broken link detected:\n%s" % bad_links_str)1261 elif len(broken_links) > 1:1262 # self.fail("Broken links detected:\n%s" % bad_links_str)1263 raise ("Broken links detected:\n%s" % bad_links_str)1264 def print_unique_links_with_status_codes(self):1265 """Finds all unique links in the html of the page source1266 and then prints out those links with their status codes.1267 Format: ["link" -> "status_code"] (per line)1268 Page links include those obtained from:1269 "a"->"href", "img"->"src", "link"->"href", and "script"->"src".1270 """1271 page_url = self.get_current_url()1272 soup = self.get_beautiful_soup(self.get_page_source())1273 page_utils._print_unique_links_with_status_codes(page_url, soup)1274 @staticmethod1275 def __fix_unicode_conversion(text):1276 """ Fixing Chinese characters when converting from PDF to HTML. """1277 text = text.replace("\u2f8f", "\u884c")1278 text = text.replace("\u2f45", "\u65b9")1279 text = text.replace("\u2f08", "\u4eba")1280 text = text.replace("\u2f70", "\u793a")1281 text = text.replace("\xe2\xbe\x8f", "\xe8\xa1\x8c")1282 text = text.replace("\xe2\xbd\xb0", "\xe7\xa4\xba")1283 text = text.replace("\xe2\xbe\x8f", "\xe8\xa1\x8c")1284 text = text.replace("\xe2\xbd\x85", "\xe6\x96\xb9")1285 return text1286 def get_pdf_text(1287 self,1288 pdf,1289 page=None,1290 maxpages=None,1291 password=None,1292 codec="utf-8",1293 wrap=False,1294 nav=False,1295 override=False,1296 ):1297 """Gets text from a PDF file.1298 PDF can be either a URL or a file path on the local file system.1299 @Params1300 pdf - The URL or file path of the PDF file.1301 page - The page number (or a list of page numbers) of the PDF.1302 If a page number is provided, looks only at that page.1303 (1 is the first page, 2 is the second page, etc.)1304 If no page number is provided, returns all PDF text.1305 maxpages - Instead of providing a page number, you can provide1306 the number of pages to use from the beginning.1307 password - If the PDF is password-protected, enter it here.1308 codec - The compression format for character encoding.1309 (The default codec used by this method is 'utf-8'.)1310 wrap - Replaces ' \n' with ' ' so that individual sentences1311 from a PDF don't get broken up into separate lines when1312 getting converted into text format.1313 nav - If PDF is a URL, navigates to the URL in the browser first.1314 (Not needed because the PDF will be downloaded anyway.)1315 override - If the PDF file to be downloaded already exists in the1316 downloaded_files/ folder, that PDF will be used1317 instead of downloading it again."""1318 import warnings1319 with warnings.catch_warnings():1320 warnings.simplefilter("ignore", category=UserWarning)1321 from pdfminer.high_level import extract_text1322 if not password:1323 password = ""1324 if not maxpages:1325 maxpages = 01326 if not pdf.lower().endswith(".pdf"):1327 raise Exception("%s is not a PDF file! (Expecting a .pdf)" % pdf)1328 file_path = None1329 if page_utils.is_valid_url(pdf):1330 from seleniumbase.core import download_helper1331 downloads_folder = download_helper.get_downloads_folder()1332 if nav:1333 if self.get_current_url() != pdf:1334 self.open(pdf)1335 file_name = pdf.split("/")[-1]1336 file_path = downloads_folder + "/" + file_name1337 if not os.path.exists(file_path):1338 self.download_file(pdf)1339 elif override:1340 self.download_file(pdf)1341 else:1342 if not os.path.exists(pdf):1343 raise Exception("%s is not a valid URL or file path!" % pdf)1344 file_path = os.path.abspath(pdf)1345 page_search = None # (Pages are delimited by '\x0c')1346 if type(page) is list:1347 pages = page1348 page_search = []1349 for page in pages:1350 page_search.append(page - 1)1351 elif type(page) is int:1352 page = page - 11353 if page < 0:1354 page = 01355 page_search = [page]1356 else:1357 page_search = None1358 pdf_text = extract_text(1359 file_path,1360 password="",1361 page_numbers=page_search,1362 maxpages=maxpages,1363 caching=False,1364 codec=codec,1365 )1366 pdf_text = self.__fix_unicode_conversion(pdf_text)1367 if wrap:1368 pdf_text = pdf_text.replace(" \n", " ")1369 pdf_text = pdf_text.strip() # Remove leading and trailing whitespace1370 return pdf_text1371 def assert_pdf_text(1372 self,1373 pdf,1374 text,1375 page=None,1376 maxpages=None,1377 password=None,1378 codec="utf-8",1379 wrap=True,1380 nav=False,1381 override=False,1382 ):1383 """Asserts text in a PDF file.1384 PDF can be either a URL or a file path on the local file system.1385 @Params1386 pdf - The URL or file path of the PDF file.1387 text - The expected text to verify in the PDF.1388 page - The page number of the PDF to use (optional).1389 If a page number is provided, looks only at that page.1390 (1 is the first page, 2 is the second page, etc.)1391 If no page number is provided, looks at all the pages.1392 maxpages - Instead of providing a page number, you can provide1393 the number of pages to use from the beginning.1394 password - If the PDF is password-protected, enter it here.1395 codec - The compression format for character encoding.1396 (The default codec used by this method is 'utf-8'.)1397 wrap - Replaces ' \n' with ' ' so that individual sentences1398 from a PDF don't get broken up into separate lines when1399 getting converted into text format.1400 nav - If PDF is a URL, navigates to the URL in the browser first.1401 (Not needed because the PDF will be downloaded anyway.)1402 override - If the PDF file to be downloaded already exists in the1403 downloaded_files/ folder, that PDF will be used1404 instead of downloading it again."""1405 text = self.__fix_unicode_conversion(text)1406 if not codec:1407 codec = "utf-8"1408 pdf_text = self.get_pdf_text(1409 pdf,1410 page=page,1411 maxpages=maxpages,1412 password=password,1413 codec=codec,1414 wrap=wrap,1415 nav=nav,1416 override=override,1417 )1418 if type(page) is int:1419 if text not in pdf_text:1420 raise Exception(1421 "PDF [%s] is missing expected text [%s] on "1422 "page [%s]!" % (pdf, text, page)1423 )1424 else:1425 if text not in pdf_text:1426 raise Exception(1427 "PDF [%s] is missing expected text [%s]!" % (pdf, text)1428 )1429 return True1430 @staticmethod1431 def create_folder(folder):1432 """1433 create_folder1434 """1435 """ Creates a folder of the given name if it doesn't already exist. """1436 if folder.endswith("/"):1437 folder = folder[:-1]1438 if len(folder) < 1:1439 raise Exception("Minimum folder name length = 1.")1440 if not os.path.exists(folder):1441 try:1442 os.makedirs(folder)1443 except Exception as e:1444 logging.info(e)1445 pass1446 @staticmethod1447 def download_file(file_url, destination_folder=None):1448 """Downloads the file from the url to the destination folder.1449 If no destination folder is specified, the default one is used.1450 (The default [Downloads Folder] = "./downloaded_files")"""1451 if not destination_folder:1452 destination_folder = constants.Files.DOWNLOADS_FOLDER1453 if not os.path.exists(destination_folder):1454 os.makedirs(destination_folder)1455 page_utils._download_file_to(file_url, destination_folder)1456 @staticmethod1457 def save_file_as(file_url, new_file_name, destination_folder=None):1458 """Similar to self.download_file(), except that you get to rename the1459 file being downloaded to whatever you want."""1460 if not destination_folder:1461 destination_folder = constants.Files.DOWNLOADS_FOLDER1462 page_utils._download_file_to(1463 file_url, destination_folder, new_file_name1464 )1465 def save_data_as(self, data, file_name, destination_folder=None):1466 """Saves the data specified to a file of the name specified.1467 If no destination folder is specified, the default one is used.1468 (The default [Downloads Folder] = "./downloaded_files")"""1469 if not destination_folder:1470 destination_folder = constants.Files.DOWNLOADS_FOLDER1471 page_utils._save_data_as(data, destination_folder, file_name)1472 def get_downloads_folder(self):1473 """Returns the path of the SeleniumBase "downloaded_files/" folder.1474 Calling self.download_file(file_url) will put that file in here.1475 With the exception of Safari, IE, and Chromium Guest Mode,1476 any clicks that download files will also use this folder1477 rather than using the browser's default "downloads/" path."""1478 self.__check_scope()1479 from seleniumbase.core import download_helper1480 return download_helper.get_downloads_folder()1481 def get_browser_downloads_folder(self):1482 """Returns the path that is used when a click initiates a download.1483 SeleniumBase overrides the system path to be "downloaded_files/"1484 The path can't be changed on Safari, IE, or Chromium Guest Mode.1485 The same problem occurs when using an out-of-date chromedriver.1486 """1487 self.__check_scope()1488 if self.is_chromium() and self.guest_mode and not self.headless:1489 # Guest Mode (non-headless) can force the default downloads path1490 return os.path.join(os.path.expanduser("~"), "downloads")1491 elif self.browser == "safari" or self.browser == "ie":1492 # Can't change the system [Downloads Folder] on Safari or IE1493 return os.path.join(os.path.expanduser("~"), "downloads")1494 elif (1495 self.driver.capabilities["browserName"].lower() == "chrome"1496 and int(self.get_chromedriver_version().split(".")[0]) < 731497 and self.headless1498 ):1499 return os.path.join(os.path.expanduser("~"), "downloads")1500 else:1501 from seleniumbase.core import download_helper1502 return download_helper.get_downloads_folder()1503 def get_path_of_downloaded_file(self, file, browser=False):1504 """ Returns the OS path of the downloaded file. """1505 if browser:1506 return os.path.join(self.get_browser_downloads_folder(), file)1507 else:1508 return os.path.join(self.get_downloads_folder(), file)1509 def is_downloaded_file_present(self, file, browser=False):1510 """Returns True if the file exists in the pre-set [Downloads Folder].1511 For browser click-initiated downloads, SeleniumBase will override1512 the system [Downloads Folder] to be "./downloaded_files/",1513 but that path can't be overridden when using Safari, IE,1514 or Chromium Guest Mode, which keeps the default system path.1515 self.download_file(file_url) will always use "./downloaded_files/".1516 @Params1517 file - The filename of the downloaded file.1518 browser - If True, uses the path set by click-initiated downloads.1519 If False, uses the self.download_file(file_url) path.1520 Those paths are often the same. (browser-dependent)1521 (Default: False).1522 """1523 return os.path.exists(1524 self.get_path_of_downloaded_file(file, browser=browser)1525 )1526 def delete_downloaded_file_if_present(self, file, browser=False):1527 """Deletes the file from the [Downloads Folder] if the file exists.1528 For browser click-initiated downloads, SeleniumBase will override1529 the system [Downloads Folder] to be "./downloaded_files/",1530 but that path can't be overridden when using Safari, IE,1531 or Chromium Guest Mode, which keeps the default system path.1532 self.download_file(file_url) will always use "./downloaded_files/".1533 @Params1534 file - The filename to be deleted from the [Downloads Folder].1535 browser - If True, uses the path set by click-initiated downloads.1536 If False, uses the self.download_file(file_url) path.1537 Those paths are usually the same. (browser-dependent)1538 (Default: False).1539 """1540 if self.is_downloaded_file_present(file, browser=browser):1541 file_path = self.get_path_of_downloaded_file(file, browser=browser)1542 try:1543 os.remove(file_path)1544 except Exception:1545 pass1546 def delete_downloaded_file(self, file, browser=False):1547 """Same as self.delete_downloaded_file_if_present()1548 Deletes the file from the [Downloads Folder] if the file exists.1549 For browser click-initiated downloads, SeleniumBase will override1550 the system [Downloads Folder] to be "./downloaded_files/",1551 but that path can't be overridden when using Safari, IE,1552 or Chromium Guest Mode, which keeps the default system path.1553 self.download_file(file_url) will always use "./downloaded_files/".1554 @Params1555 file - The filename to be deleted from the [Downloads Folder].1556 browser - If True, uses the path set by click-initiated downloads.1557 If False, uses the self.download_file(file_url) path.1558 Those paths are usually the same. (browser-dependent)1559 (Default: False).1560 """1561 if self.is_downloaded_file_present(file, browser=browser):1562 file_path = self.get_path_of_downloaded_file(file, browser=browser)1563 try:1564 os.remove(file_path)1565 except Exception:1566 pass1567 def assert_equal(self, first, second, msg=None):1568 """Asserts that the two values are equal.1569 Will raise an exception if the values are not equal."""1570 self.unittest.assertEqual(first, second, msg=msg)1571 def assert_not_equal(self, first, second, msg=None):1572 """Asserts that the two values are not equal.1573 Will raise an exception if the values are equal."""1574 self.unittest.assertNotEqual(first, second, msg=msg)1575 def assert_raises(self, *args, **kwargs):1576 """Asserts that the following block of code raises an exception.1577 Will raise an exception if the block of code has no exception.1578 Usage Example =>1579 # Verify that the expected exception is raised.1580 with self.assert_raises(Exception):1581 raise Exception("Expected Exception!")1582 """1583 self.unittest.assertRaises(*args, **kwargs)1584 def scroll_to_top(self):1585 """ Scroll to the top of the page. """1586 self.__check_scope()1587 scroll_script = "window.scrollTo(0, 0);"1588 try:1589 self.execute_script(scroll_script)1590 time.sleep(0.012)1591 return True1592 except Exception:1593 return False1594 def scroll_to_bottom(self):1595 """ Scroll to the bottom of the page. """1596 self.__check_scope()1597 scroll_script = "window.scrollTo(0, 10000);"1598 try:1599 self.execute_script(scroll_script)1600 time.sleep(0.012)1601 return True1602 except Exception:1603 return False1604 def assert_title(self, title):1605 """Asserts that the web page title matches the expected title.1606 When a web page initially loads, the title starts as the URL,1607 but then the title switches over to the actual page title.1608 A slow connection could delay the actual title from displaying."""1609 self.wait_for_ready_state_complete()1610 expected = title.strip()1611 actual = self.get_page_title().strip()1612 error = (1613 "Expected page title [%s] does not match the actual title [%s]!"1614 )1615 try:1616 self.unittest.assertEqual(expected, actual, error % (expected, actual))1617 except Exception:1618 self.wait_for_ready_state_complete()1619 self.sleep(settings.MINI_TIMEOUT)1620 actual = self.get_page_title().strip()1621 try:1622 self.unittest.assertEqual(expected, actual, error % (expected, actual))1623 except Exception:1624 self.wait_for_ready_state_complete()1625 self.sleep(settings.MINI_TIMEOUT)1626 actual = self.get_page_title().strip()1627 self.unittest.assertEqual(expected, actual, error % (expected, actual))1628 def __activate_html_inspector(self):1629 self.wait_for_ready_state_complete()1630 time.sleep(0.05)1631 js_utils.activate_html_inspector(self.driver)1632 def inspect_html(self):1633 """Inspects the Page HTML with HTML-Inspector.1634 (https://github.com/philipwalton/html-inspector)1635 (https://cdnjs.com/libraries/html-inspector)1636 Prints the results and also returns them."""1637 self.__activate_html_inspector()1638 self.wait_for_ready_state_complete()1639 script = """HTMLInspector.inspect();"""1640 try:1641 self.execute_script(script)1642 except Exception:1643 # If unable to load the JavaScript, skip inspection and return.1644 msg = "(Unable to load HTML-Inspector JS! Inspection Skipped!)"1645 print("\n" + msg)1646 return msg1647 time.sleep(0.1)1648 browser_logs = []1649 try:1650 browser_logs = self.driver.get_log("browser")1651 except (ValueError, WebDriverException):1652 # If unable to get browser logs, skip the assert and return.1653 msg = "(Unable to Inspect HTML! -> Only works on Chromium!)"1654 print("\n" + msg)1655 return msg1656 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"1657 url = self.get_current_url()1658 header = "\n* HTML Inspection Results: %s" % url1659 results = [header]1660 row_count = 01661 for entry in browser_logs:1662 message = entry["message"]1663 if "0:6053 " in message:1664 message = message.split("0:6053")[1]1665 message = message.replace("\\u003C", "<")1666 if message.startswith(' "') and message.count('"') == 2:1667 message = message.split('"')[1]1668 message = "X - " + message1669 if messenger_library not in message:1670 if message not in results:1671 results.append(message)1672 row_count += 11673 if row_count > 0:1674 results.append("* (See the Console output for details!)")1675 else:1676 results.append("* (No issues detected!)")1677 results = "\n".join(results)1678 print(results)1679 return results1680 def is_chromium(self):1681 """ Return True if the browser is Chrome, Edge, or Opera. """1682 self.__check_scope()1683 chromium = False1684 browser_name = self.driver.capabilities["browserName"]1685 if browser_name.lower() in ("chrome", "edge", "msedge", "opera"):1686 chromium = True1687 return chromium1688 def __fail_if_not_using_chrome(self, method):1689 chrome = False1690 browser_name = self.driver.capabilities["browserName"]1691 if browser_name.lower() == "chrome":1692 chrome = True1693 if not chrome:1694 from seleniumbase.common.exceptions import NotUsingChromeException1695 message = (1696 'Error: "%s" should only be called '1697 'by tests running with self.browser == "chrome"! '1698 'You should add an "if" statement to your code before calling '1699 "this method if using browsers that are Not Chrome! "1700 'The browser detected was: "%s".' % (method, browser_name)1701 )1702 raise NotUsingChromeException(message)1703 def get_chrome_version(self):1704 self.__check_scope()1705 self.__fail_if_not_using_chrome("get_chrome_version()")1706 driver_capabilities = self.driver.capabilities1707 if "version" in driver_capabilities:1708 chrome_version = driver_capabilities["version"]1709 else:1710 chrome_version = driver_capabilities["browserVersion"]1711 return chrome_version1712 def get_chromedriver_version(self):1713 self.__check_scope()1714 self.__fail_if_not_using_chrome("get_chromedriver_version()")1715 chrome_dict = self.driver.capabilities["chrome"]1716 chromedriver_version = chrome_dict["chromedriverVersion"]1717 chromedriver_version = chromedriver_version.split(" ")[0]1718 return chromedriver_version1719 def is_chromedriver_too_old(self):1720 """There are known issues with chromedriver versions below 73.1721 This can impact tests that need to hover over an element, or ones1722 that require a custom downloads folder ("./downloaded_files").1723 Due to the situation that newer versions of chromedriver require1724 an exact match to the version of Chrome, an "old" version of1725 chromedriver is installed by default. It is then up to the user1726 to upgrade to the correct version of chromedriver from there.1727 This method can be used to change test behavior when trying1728 to perform an action that is impacted by having an old version1729 of chromedriver installed."""1730 self.__check_scope()1731 self.__fail_if_not_using_chrome("is_chromedriver_too_old()")1732 if int(self.get_chromedriver_version().split(".")[0]) < 73:1733 return True # chromedriver is too old! Please upgrade!1734 return False1735 def get_google_auth_password(self, totp_key=None):1736 """Returns a time-based one-time password based on the1737 Google Authenticator password algorithm. Works with Authy.1738 If "totp_key" is not specified, defaults to using the one1739 provided in seleniumbase/config/settings.py1740 Google Auth passwords expire and change at 30-second intervals.1741 If the fetched password expires in the next 1.5 seconds, waits1742 for a new one before returning it (may take up to 1.5 seconds).1743 See https://pyotp.readthedocs.io/en/latest/ for details."""1744 import pyotp1745 if not totp_key:1746 totp_key = settings.TOTP_KEY1747 epoch_interval = time.time() / 30.01748 cycle_lifespan = float(epoch_interval) - int(epoch_interval)1749 if float(cycle_lifespan) > 0.95:1750 # Password expires in the next 1.5 seconds. Wait for a new one.1751 for i in range(30):1752 time.sleep(0.05)1753 epoch_interval = time.time() / 30.01754 cycle_lifespan = float(epoch_interval) - int(epoch_interval)1755 if not float(cycle_lifespan) > 0.95:1756 # The new password cycle has begun1757 break1758 totp = pyotp.TOTP(totp_key)1759 return str(totp.now())1760 def set_default_timeout(self, timeout):1761 """This method changes the default timeout values of test methods1762 for the duration of the current test.1763 Effected timeouts: (used by methods that wait for elements)1764 * settings.SMALL_TIMEOUT - (default value: 6 seconds)1765 * settings.LARGE_TIMEOUT - (default value: 10 seconds)1766 The minimum allowable default timeout is: 0.5 seconds.1767 The maximum allowable default timeout is: 60.0 seconds.1768 (Test methods can still override timeouts outside that range.)1769 """1770 self.__check_scope()1771 if not type(timeout) is int and not type(timeout) is float:1772 raise Exception('Expecting a numeric value for "timeout"!')1773 if timeout < 0:1774 raise Exception('The "timeout" cannot be a negative number!')1775 timeout = float(timeout)1776 # Min default timeout: 0.5 seconds. Max default timeout: 60.0 seconds.1777 min_timeout = 0.51778 max_timeout = 60.01779 if timeout < min_timeout:1780 logging.info("Minimum default timeout = %s" % min_timeout)1781 timeout = min_timeout1782 elif timeout > max_timeout:1783 logging.info("Maximum default timeout = %s" % max_timeout)1784 timeout = max_timeout1785 self.__overrided_default_timeouts = True1786 sb_config._is_timeout_changed = True1787 settings.SMALL_TIMEOUT = timeout1788 settings.LARGE_TIMEOUT = timeout1789 # Application "Local Storage" controls1790 def set_local_storage_item(self, key, value):1791 self.__check_scope()1792 self.execute_script(1793 "window.localStorage.setItem('{}', '{}');".format(key, value)1794 )1795 def get_local_storage_item(self, key):1796 self.__check_scope()1797 return self.execute_script(1798 "return window.localStorage.getItem('{}');".format(key)1799 )1800 def remove_local_storage_item(self, key):1801 self.__check_scope()1802 self.execute_script(1803 "window.localStorage.removeItem('{}');".format(key)1804 )1805 def clear_local_storage(self):1806 self.__check_scope()1807 self.execute_script("window.localStorage.clear();")1808 def get_local_storage_keys(self):1809 self.__check_scope()1810 return self.execute_script(1811 "var ls = window.localStorage, keys = []; "1812 "for (var i = 0; i < ls.length; ++i) "1813 " keys[i] = ls.key(i); "1814 "return keys;"1815 )1816 def get_local_storage_items(self):1817 self.__check_scope()1818 return self.execute_script(1819 r"var ls = window.localStorage, items = {}; "1820 "for (var i = 0, k; i < ls.length; ++i) "1821 " items[k = ls.key(i)] = ls.getItem(k); "1822 "return items;"1823 )1824 # Application "Session Storage" controls1825 def set_session_storage_item(self, key, value):1826 self.__check_scope()1827 self.execute_script(1828 "window.sessionStorage.setItem('{}', '{}');".format(key, value)1829 )1830 def get_session_storage_item(self, key):1831 self.__check_scope()1832 return self.execute_script(1833 "return window.sessionStorage.getItem('{}');".format(key)1834 )1835 def remove_session_storage_item(self, key):1836 self.__check_scope()1837 self.execute_script(1838 "window.sessionStorage.removeItem('{}');".format(key)1839 )1840 def clear_session_storage(self):1841 self.__check_scope()1842 self.execute_script("window.sessionStorage.clear();")1843 def get_session_storage_keys(self):1844 self.__check_scope()1845 return self.execute_script(1846 "var ls = window.sessionStorage, keys = []; "1847 "for (var i = 0; i < ls.length; ++i) "1848 " keys[i] = ls.key(i); "1849 "return keys;"1850 )1851 def get_session_storage_items(self):1852 self.__check_scope()1853 return self.execute_script(1854 r"var ls = window.sessionStorage, items = {}; "1855 "for (var i = 0, k; i < ls.length; ++i) "1856 " items[k = ls.key(i)] = ls.getItem(k); "1857 "return items;"1858 )1859 def get_domain_url(self, url):1860 self.__check_scope()1861 return page_utils.get_domain_url(url)1862 @decorators.deprecated(1863 "jq_format() is deprecated. Use re.escape() instead!"1864 )1865 def jq_format(self, code):1866 # DEPRECATED - re.escape() already performs the intended action!1867 return js_utils._jq_format(code)1868 ############1869 def get_element(self, timeout=None):1870 """Same as wait_for_element_present() - returns the element.1871 The element does not need be visible (it may be hidden)."""1872 self.__check_scope()1873 if not timeout:1874 timeout = settings.SMALL_TIMEOUT1875 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1876 timeout = self.__get_new_timeout(timeout)1877 return self.wait_for_element_present(timeout=timeout)1878 def wait_for_element_present(self, timeout=None):1879 """Waits for an element to appear in the HTML of a page.1880 The element does not need be visible (it may be hidden)."""1881 self.__check_scope()1882 if not timeout:1883 timeout = settings.LARGE_TIMEOUT1884 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:1885 timeout = self.__get_new_timeout(timeout)1886 selector, by = self.__recalculate_selector()1887 return page_actions.wait_for_element_present(1888 self.driver, selector, by, timeout1889 )1890 def get_beautiful_soup(self, source=None):1891 """BeautifulSoup is a toolkit for dissecting an HTML document1892 and extracting what you need. It's great for screen-scraping!1893 See: https://www.crummy.com/software/BeautifulSoup/bs4/doc/1894 """1895 from bs4 import BeautifulSoup1896 if not source:1897 source = self.get_page_source()1898 soup = BeautifulSoup(source, "html.parser")1899 return soup1900 def get_page_source(self):1901 self.wait_for_ready_state_complete()1902 return self.driver.page_source1903 def switch_to_newest_window(self):1904 self.switch_to_window(len(self.driver.window_handles) - 1)1905class BaseCase:1906 """ <Class seleniumbase.BaseCase> """1907 driver = None1908 def __init__(self, selector, by=By.CSS_SELECTOR, description=None):1909 self.selector = selector1910 self.by = by1911 self.description = description1912 self.environment = None1913 self.env = None # Add a shortened version of self.environment1914 self.__called_setup = False1915 self.__called_teardown = False1916 self.__start_time_ms = None1917 self.__screenshot_count = 01918 self.__will_be_skipped = False1919 self.__passed_then_skipped = False1920 self.__last_url_of_deferred_assert = "data:,"1921 self.__last_page_load_url = "data:,"1922 self.__last_page_screenshot = None1923 self.__last_page_screenshot_png = None1924 self.__last_page_url = None1925 self.__last_page_source = None1926 self.__skip_reason = None1927 self.__overrided_default_timeouts = False1928 self.__added_pytest_html_extra = None1929 self.__deferred_assert_count = 01930 self.__deferred_assert_failures = []1931 self.__device_width = None1932 self.__device_height = None1933 self.__device_pixel_ratio = None1934 self.__driver_browser_map = {}1935 # Requires self._* instead of self.__* for external class use1936 self._language = "English"1937 self._presentation_slides = {}1938 self._presentation_transition = {}1939 self._sb_test_identifier = None1940 self._html_report_extra = [] # (Used by pytest_plugin.py)1941 self._default_driver = None1942 self._drivers_list = []1943 self._chart_data = {}1944 self._chart_count = 01945 self._chart_label = {}1946 self._chart_xcount = 01947 self._chart_first_series = {}1948 self._chart_series_count = {}1949 self._tour_steps = {}1950 self.unittest = TestCase()1951 self.servername = sb_config.servername1952 self.cap_file = sb_config.cap_file1953 self.cap_string = sb_config.cap_string1954 self.headless = sb_config.headless1955 self.locale_code = sb_config.locale_code1956 self.port = sb_config.port1957 self.protocol = sb_config.protocol1958 self.proxy_string = sb_config.proxy_string1959 self.user_agent = sb_config.user_agent1960 self.disable_csp = sb_config.disable_csp1961 self.enable_ws = sb_config.enable_ws1962 self.enable_sync = sb_config.enable_sync1963 self.use_auto_ext = sb_config.use_auto_ext1964 self.no_sandbox = sb_config.no_sandbox1965 self.disable_gpu = sb_config.disable_gpu1966 self.incognito = sb_config.incognito1967 self.guest_mode = sb_config.guest_mode1968 self.devtools = sb_config.devtools1969 self.remote_debug = sb_config.remote_debug1970 self.swiftshader = sb_config.swiftshader1971 self.block_images = sb_config.block_images1972 self.chromium_arg = sb_config.chromium_arg1973 self.firefox_arg = sb_config.firefox_arg1974 self.firefox_pref = sb_config.firefox_pref1975 self.user_data_dir = sb_config.user_data_dir1976 self.extension_zip = sb_config.extension_zip1977 self.extension_dir = sb_config.extension_dir1978 self.mobile_emulator = sb_config.mobile_emulator1979 self.maximize_option = sb_config.maximize_option1980 self.start_page = sb_config.start_page1981 self.timeout_multiplier = sb_config.timeout_multiplier1982 self.js_checking_on = sb_config.js_checking_on1983 self.ad_block_on = sb_config.ad_block_on1984 self.message_duration = sb_config.message_duration1985 self.browser = "chrome"1986 def __get__(self, instance, owner):1987 """1988 è·ådriver驱å¨1989 :param instance:1990 :param owner:1991 :return:1992 """1993 if instance is None:1994 return None1995 global driver1996 driver = instance.driver1997 self.driver = driver1998 return self1999 # def get_element(self, sec=10) -> WebElement:2000 # """2001 # è·åä¸ä¸ªå
ç´ 2002 # :param sec:çå¾
ç§æ°2003 # :return: å
ç´ å¯æ¾å°è¿åelement对象ï¼å¦åè¿åFalse2004 # """2005 # global driver2006 # if self.wait_element(sec):2007 # try:2008 # element = driver.find_element(**self.by, *self.selector)2009 # return element2010 # except Exception as e:2011 # raise e2012 # def get_elements(self) -> WebElement:2013 # """2014 # è·åä¸ç»å
ç´ 2015 # :return: elements2016 # """2017 # try:2018 # elements = WebDriverWait(driver, 60, 1).until(lambda x: x.find_elements(**self.by, *self.selector))2019 # return elements2020 # except Exception as e:2021 # raise e2022 def wait_for_element_visible(self, selector=None, by=None, timeout=None):2023 """ Same as self.wait_for_element() """2024 self.__check_scope()2025 if not timeout:2026 timeout = settings.LARGE_TIMEOUT2027 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2028 timeout = self.__get_new_timeout(timeout)2029 selector, by = self.__recalculate_selector(selector, by)2030 return page_actions.wait_for_element_visible(self.driver, selector, by, timeout)2031 def click(self, timeout=None, delay=0.0):2032 """2033 ç¹å»2034 """2035 if not timeout:2036 timeout = settings.SMALL_TIMEOUT2037 original_selector = self.selector2038 original_by = self.by2039 selector, by = self.__recalculate_selector()2040 if delay and (type(delay) in [int, float]) and delay > 0:2041 time.sleep(delay)2042 if page_utils.is_link_text_selector(selector) or by == By.LINK_TEXT:2043 if not self.is_link_text_visible(selector):2044 # Handle a special case of links hidden in dropdowns2045 self.click_link_text(selector, timeout=timeout)2046 return2047 if (2048 page_utils.is_partial_link_text_selector(selector)2049 or by == By.PARTIAL_LINK_TEXT2050 ):2051 if not self.is_partial_link_text_visible(selector):2052 # Handle a special case of partial links hidden in dropdowns2053 self.click_partial_link_text(selector, timeout=timeout)2054 return2055 element = page_actions.wait_for_element_visible(self.driver, self.selector, self.by, timeout=timeout)2056 self.__scroll_to_element(element, selector, by)2057 pre_action_url = self.driver.current_url2058 try:2059 if self.browser == "ie" and by == By.LINK_TEXT:2060 # An issue with clicking Link Text on IE means using jquery2061 self.__jquery_click(selector, by=by)2062 elif self.browser == "safari":2063 if by == By.LINK_TEXT:2064 self.__jquery_click(selector, by=by)2065 else:2066 self.__js_click(selector, by=by)2067 else:2068 href = None2069 new_tab = False2070 onclick = None2071 try:2072 if self.headless and element.tag_name == "a":2073 # Handle a special case of opening a new tab (headless)2074 href = element.get_attribute("href").strip()2075 onclick = element.get_attribute("onclick")2076 target = element.get_attribute("target")2077 if target == "_blank":2078 new_tab = True2079 if new_tab and self.__looks_like_a_page_url(href):2080 if onclick:2081 try:2082 self.execute_script(onclick)2083 except Exception as e:2084 logging.info(e)2085 pass2086 current_window = self.driver.current_window_handle2087 self.open_new_window()2088 try:2089 self.open(href)2090 except Exception as e:2091 logging.info(e)2092 pass2093 self.switch_to_window(current_window)2094 return2095 except Exception as e:2096 logging.info(e)2097 pass2098 # Normal click2099 element.click()2100 except StaleElementReferenceException:2101 self.wait_for_ready_state_complete()2102 time.sleep(0.16)2103 element = page_actions.wait_for_element_visible(2104 self.driver, selector, by, timeout=timeout2105 )2106 try:2107 self.__scroll_to_element(element, selector, by)2108 except Exception as e:2109 logging.info(e)2110 print(e)2111 pass2112 if self.browser == "safari":2113 if by == By.LINK_TEXT:2114 self.__jquery_click(selector, by=by)2115 else:2116 self.__js_click(selector, by=by)2117 else:2118 element.click()2119 except ENI_Exception:2120 self.wait_for_ready_state_complete()2121 time.sleep(0.1)2122 element = page_actions.wait_for_element_visible(2123 self.driver, selector, by, timeout=timeout2124 )2125 href = None2126 new_tab = False2127 onclick = None2128 try:2129 if element.tag_name == "a":2130 # Handle a special case of opening a new tab (non-headless)2131 href = element.get_attribute("href").strip()2132 onclick = element.get_attribute("onclick")2133 target = element.get_attribute("target")2134 if target == "_blank":2135 new_tab = True2136 if new_tab and self.__looks_like_a_page_url(href):2137 if onclick:2138 try:2139 self.execute_script(onclick)2140 except Exception as e:2141 logging.info(e)2142 pass2143 current_window = self.driver.current_window_handle2144 self.open_new_window()2145 try:2146 self.open(href)2147 except Exception as e:2148 logging.info(e)2149 pass2150 self.switch_to_window(current_window)2151 return2152 except Exception as e:2153 logging.info(e)2154 pass2155 self.__scroll_to_element(element, selector, by)2156 if self.browser == "safari":2157 if by == By.LINK_TEXT:2158 self.__jquery_click(selector, by=by)2159 else:2160 self.__js_click(selector, by=by)2161 else:2162 element.click()2163 except (WebDriverException, MoveTargetOutOfBoundsException):2164 self.wait_for_ready_state_complete()2165 try:2166 self.__js_click(selector, by=by)2167 except Exception as e:2168 logging.info(e)2169 try:2170 self.__jquery_click(selector, by=by)2171 except Exception as e:2172 logging.info(e)2173 # One more attempt to click on the element2174 element = page_actions.wait_for_element_visible(2175 self.driver, selector, by, timeout=timeout2176 )2177 element.click()2178 if settings.WAIT_FOR_RSC_ON_CLICKS:2179 self.wait_for_ready_state_complete()2180 def slow_click(self, timeout=None):2181 """Similar to click(), but pauses for a brief moment before clicking.2182 When used in combination with setting the user-agent, you can often2183 bypass bot-detection by tricking websites into thinking that you're2184 not a bot. (Useful on websites that block web automation tools.)2185 To set the user-agent, use: ``--agent=AGENT``.2186 Here's an example message from GitHub's bot-blocker:2187 ``You have triggered an abuse detection mechanism...``2188 """2189 self.__check_scope()2190 if not timeout:2191 timeout = settings.SMALL_TIMEOUT2192 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2193 timeout = self.__get_new_timeout(timeout)2194 self.click(timeout=timeout, delay=1.05)2195 else:2196 # Demo Mode already includes a small delay2197 self.click(timeout=timeout, delay=0.25)2198 def double_click(self, timeout=None):2199 """2200 åå»2201 """2202 from selenium.webdriver.common.action_chains import ActionChains2203 self.__check_scope()2204 if not timeout:2205 timeout = settings.SMALL_TIMEOUT2206 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2207 timeout = self.__get_new_timeout(timeout)2208 original_selector = self.selector2209 original_by = self.by2210 selector, by = self.__recalculate_selector()2211 element = page_actions.wait_for_element_visible(2212 self.driver, selector, by, timeout=timeout2213 )2214 self.wait_for_ready_state_complete()2215 # Find the element one more time in case scrolling hid it2216 element = page_actions.wait_for_element_visible(2217 self.driver, selector, by, timeout=timeout2218 )2219 pre_action_url = self.driver.current_url2220 try:2221 if self.browser == "safari":2222 # Jump to the "except" block where the other script should work2223 raise Exception("This Exception will be caught.")2224 actions = ActionChains(self.driver)2225 actions.double_click(element).perform()2226 except Exception as e:2227 logging.info(e)2228 css_selector = self.convert_to_css_selector(selector, by=by)2229 css_selector = re.escape(css_selector) # Add "\\" to special chars2230 css_selector = self.__escape_quotes_if_needed(css_selector)2231 double_click_script = (2232 """var targetElement1 = document.querySelector('%s');2233 var clickEvent1 = document.createEvent('MouseEvents');2234 clickEvent1.initEvent('dblclick', true, true);2235 targetElement1.dispatchEvent(clickEvent1);"""2236 % css_selector2237 )2238 if ":contains\\(" not in css_selector:2239 self.execute_script(double_click_script)2240 else:2241 double_click_script = (2242 """jQuery('%s').dblclick();""" % css_selector2243 )2244 self.safe_execute_script(double_click_script)2245 if settings.WAIT_FOR_RSC_ON_CLICKS:2246 self.wait_for_ready_state_complete()2247 def update_text(self, text, timeout=None, retry=False):2248 """This method updates an element's text field with new text.2249 Has multiple parts:2250 * Waits for the element to be visible.2251 * Waits for the element to be interactive.2252 * Clears the text field.2253 * Types in the new text.2254 * Hits Enter/Submit (if the text ends in "\n").2255 @Params2256 selector - the selector of the text field2257 text - the new text to type into the text field2258 by - the type of selector to search by (Default: CSS Selector)2259 timeout - how long to wait for the selector to be visible2260 retry - if True, use JS if the Selenium text update fails2261 """2262 self.__check_scope()2263 if not timeout:2264 timeout = settings.LARGE_TIMEOUT2265 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2266 timeout = self.__get_new_timeout(timeout)2267 selector, by = self.__recalculate_selector()2268 element = self.wait_for_element_visible(timeout=timeout)2269 try:2270 element.clear() # May need https://stackoverflow.com/a/506916252271 backspaces = Keys.BACK_SPACE * 42 # Is the answer to everything2272 element.send_keys(backspaces) # In case autocomplete keeps text2273 except (StaleElementReferenceException, ENI_Exception):2274 self.wait_for_ready_state_complete()2275 time.sleep(0.16)2276 element = self.wait_for_element_visible(timeout=timeout)2277 try:2278 element.clear()2279 except Exception as e:2280 logging.info(e)2281 pass # Clearing the text field first might not be necessary2282 except Exception as e:2283 logging.info(e)2284 pass # Clearing the text field first might not be necessary2285 if type(text) is int or type(text) is float:2286 text = str(text)2287 try:2288 if not text.endswith("\n"):2289 element.send_keys(text)2290 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2291 self.wait_for_ready_state_complete()2292 else:2293 element.send_keys(text[:-1])2294 element.send_keys(Keys.RETURN)2295 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2296 self.wait_for_ready_state_complete()2297 except (StaleElementReferenceException, ENI_Exception):2298 self.wait_for_ready_state_complete()2299 time.sleep(0.16)2300 element = self.wait_for_element_visible(timeout=timeout)2301 element.clear()2302 if not text.endswith("\n"):2303 element.send_keys(text)2304 else:2305 element.send_keys(text[:-1])2306 element.send_keys(Keys.RETURN)2307 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2308 self.wait_for_ready_state_complete()2309 except Exception:2310 exc_message = self.__get_improved_exception_message()2311 raise Exception(exc_message)2312 if (2313 retry2314 and element.get_attribute("value") != text2315 and not text.endswith("\n")2316 ):2317 logging.debug("update_text() is falling back to JavaScript!")2318 self.set_value(selector, text, by=by)2319 def add_text(self, text, timeout=None):2320 """The more-reliable version of driver.send_keys()2321 Similar to update_text(), but won't clear the text field first."""2322 self.__check_scope()2323 if not timeout:2324 timeout = settings.LARGE_TIMEOUT2325 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2326 timeout = self.__get_new_timeout(timeout)2327 selector, by = self.__recalculate_selector()2328 element = self.wait_for_element_visible(timeout=timeout)2329 pre_action_url = self.driver.current_url2330 if type(text) is int or type(text) is float:2331 text = str(text)2332 try:2333 if not text.endswith("\n"):2334 element.send_keys(text)2335 else:2336 element.send_keys(text[:-1])2337 element.send_keys(Keys.RETURN)2338 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2339 self.wait_for_ready_state_complete()2340 except (StaleElementReferenceException, ENI_Exception):2341 self.wait_for_ready_state_complete()2342 time.sleep(0.16)2343 element = self.wait_for_element_visible(timeout=timeout)2344 if not text.endswith("\n"):2345 element.send_keys(text)2346 else:2347 element.send_keys(text[:-1])2348 element.send_keys(Keys.RETURN)2349 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2350 self.wait_for_ready_state_complete()2351 except Exception:2352 exc_message = self.__get_improved_exception_message()2353 raise Exception(exc_message)2354 def type(self, text, timeout=None, retry=False):2355 """Same as self.update_text()2356 This method updates an element's text field with new text.2357 Has multiple parts:2358 * Waits for the element to be visible.2359 * Waits for the element to be interactive.2360 * Clears the text field.2361 * Types in the new text.2362 * Hits Enter/Submit (if the text ends in "\n").2363 @Params2364 selector - the selector of the text field2365 text - the new text to type into the text field2366 by - the type of selector to search by (Default: CSS Selector)2367 timeout - how long to wait for the selector to be visible2368 retry - if True, use JS if the Selenium text update fails2369 DO NOT confuse self.type() with Python type()! They are different!2370 """2371 self.__check_scope()2372 if not timeout:2373 timeout = settings.LARGE_TIMEOUT2374 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2375 timeout = self.__get_new_timeout(timeout)2376 self.update_text(text, timeout=timeout, retry=retry)2377 def submit(self):2378 """ Alternative to self.driver.find_element_by_*(SELECTOR).submit() """2379 self.__check_scope()2380 element = self.wait_for_element_visible(timeout=settings.SMALL_TIMEOUT)2381 element.submit()2382 def clear(self, timeout=None):2383 """This method clears an element's text field.2384 A clear() is already included with most methods that type text,2385 such as self.type(), self.update_text(), etc.2386 Does not use Demo Mode highlights, mainly because we expect2387 that some users will be calling an unnecessary clear() before2388 calling a method that already includes clear() as part of it.2389 In case websites trigger an autofill after clearing a field,2390 add backspaces to make sure autofill doesn't undo the clear.2391 @Params2392 selector - the selector of the text field2393 by - the type of selector to search by (Default: CSS Selector)2394 timeout - how long to wait for the selector to be visible2395 """2396 self.__check_scope()2397 if not timeout:2398 timeout = settings.LARGE_TIMEOUT2399 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2400 timeout = self.__get_new_timeout(timeout)2401 element = self.wait_for_element_visible(timeout=timeout)2402 self.scroll_to(timeout=timeout)2403 try:2404 element.clear()2405 backspaces = Keys.BACK_SPACE * 42 # Autofill Defense2406 element.send_keys(backspaces)2407 except (StaleElementReferenceException, ENI_Exception):2408 self.wait_for_ready_state_complete()2409 time.sleep(0.16)2410 element = self.wait_for_element_visible(timeout=timeout)2411 element.clear()2412 try:2413 backspaces = Keys.BACK_SPACE * 42 # Autofill Defense2414 element.send_keys(backspaces)2415 except Exception as e:2416 logging.info(e)2417 pass2418 except Exception as e:2419 logging.info(e)2420 element.clear()2421 def focus(self, timeout=None):2422 """Make the current page focus on an interactable element.2423 If the element is not interactable, only scrolls to it.2424 The "tab" key is another way of setting the page focus."""2425 self.__check_scope()2426 if not timeout:2427 timeout = settings.LARGE_TIMEOUT2428 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2429 timeout = self.__get_new_timeout(timeout)2430 element = self.wait_for_element_visible(timeout=timeout)2431 self.scroll_to(timeout=timeout)2432 try:2433 element.send_keys(Keys.NULL)2434 except (StaleElementReferenceException, ENI_Exception):2435 self.wait_for_ready_state_complete()2436 time.sleep(0.12)2437 element = self.wait_for_element_visible(timeout=timeout)2438 try:2439 element.send_keys(Keys.NULL)2440 except ENI_Exception:2441 # Non-interactable element. Skip focus and continue.2442 pass2443 def is_element_present(self, **kwargs):2444 """2445 is_element_present2446 """2447 self.wait_for_ready_state_complete()2448 selector, by = self.__recalculate_selector()2449 return page_actions.is_element_present(self.driver, selector, by)2450 def is_element_visible(self, selector=None, by=By.CSS_SELECTOR):2451 """2452 is_element_visible2453 """2454 self.wait_for_ready_state_complete()2455 selector, by = self.__recalculate_selector(selector, by)2456 return page_actions.is_element_visible(self.driver, selector, by)2457 def is_element_enabled(self):2458 """2459 is_element_enabled2460 """2461 self.wait_for_ready_state_complete()2462 selector, by = self.__recalculate_selector()2463 return page_actions.is_element_enabled(self.driver, selector, by)2464 def is_text_visible(self, text):2465 """2466 is_text_visible2467 """2468 global driver2469 self.wait_for_ready_state_complete()2470 time.sleep(0.01)2471 selector, by = self.__recalculate_selector()2472 return page_actions.is_text_visible(driver, text, selector, by)2473 def get_text(self, timeout=None):2474 """2475 get_text2476 """2477 self.__check_scope()2478 if not timeout:2479 timeout = settings.SMALL_TIMEOUT2480 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2481 timeout = self.__get_new_timeout(timeout)2482 selector, by = self.__recalculate_selector()2483 self.wait_for_ready_state_complete()2484 time.sleep(0.01)2485 element = page_actions.wait_for_element_visible(2486 self.driver, selector, by, timeout2487 )2488 try:2489 element_text = element.text2490 except (StaleElementReferenceException, ENI_Exception):2491 self.wait_for_ready_state_complete()2492 time.sleep(0.14)2493 element = page_actions.wait_for_element_visible(2494 self.driver, selector, by, timeout2495 )2496 element_text = element.text2497 return element_text2498 def get_attribute(self, attribute, timeout=None, hard_fail=True):2499 """ This method uses JavaScript to get the value of an attribute. """2500 self.__check_scope()2501 if not timeout:2502 timeout = settings.SMALL_TIMEOUT2503 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2504 timeout = self.__get_new_timeout(timeout)2505 selector, by = self.__recalculate_selector()2506 self.wait_for_ready_state_complete()2507 time.sleep(0.01)2508 element = page_actions.wait_for_element_present(2509 self.driver, selector, by, timeout2510 )2511 try:2512 attribute_value = element.get_attribute(attribute)2513 except (StaleElementReferenceException, ENI_Exception):2514 self.wait_for_ready_state_complete()2515 time.sleep(0.14)2516 element = page_actions.wait_for_element_present(2517 self.driver, selector, by, timeout2518 )2519 attribute_value = element.get_attribute(attribute)2520 if attribute_value is not None:2521 return attribute_value2522 else:2523 if hard_fail:2524 raise Exception(2525 "Element {%s} has no attribute {%s}!"2526 % (selector, attribute)2527 )2528 else:2529 return None2530 def set_attribute(self, attribute, value, timeout=None):2531 """This method uses JavaScript to set/update an attribute.2532 Only the first matching selector from querySelector() is used."""2533 self.__check_scope()2534 if not timeout:2535 timeout = settings.SMALL_TIMEOUT2536 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2537 timeout = self.__get_new_timeout(timeout)2538 selector, by = self.__recalculate_selector()2539 if self.is_element_visible():2540 try:2541 self.scroll_to(timeout=timeout)2542 except Exception as e:2543 logging.info(e)2544 pass2545 attribute = re.escape(attribute)2546 attribute = self.__escape_quotes_if_needed(attribute)2547 value = re.escape(value)2548 value = self.__escape_quotes_if_needed(value)2549 css_selector = self.convert_to_css_selector(selector, by=by)2550 css_selector = re.escape(css_selector) # Add "\\" to special chars2551 css_selector = self.__escape_quotes_if_needed(css_selector)2552 script = (2553 """document.querySelector('%s').setAttribute('%s','%s');"""2554 % (css_selector, attribute, value)2555 )2556 self.execute_script(script)2557 def set_attributes(self, attribute, value):2558 """This method uses JavaScript to set/update a common attribute.2559 All matching selectors from querySelectorAll() are used.2560 Example => (Make all links on a website redirect to Google):2561 self.set_attributes("a", "href", "https://google.com")"""2562 self.__check_scope()2563 selector, by = self.__recalculate_selector()2564 attribute = re.escape(attribute)2565 attribute = self.__escape_quotes_if_needed(attribute)2566 value = re.escape(value)2567 value = self.__escape_quotes_if_needed(value)2568 css_selector = self.convert_to_css_selector(selector, by=by)2569 css_selector = re.escape(css_selector) # Add "\\" to special chars2570 css_selector = self.__escape_quotes_if_needed(css_selector)2571 script = """var $elements = document.querySelectorAll('%s');2572 var index = 0, length = $elements.length;2573 for(; index < length; index++){2574 $elements[index].setAttribute('%s','%s');}""" % (2575 css_selector,2576 attribute,2577 value,2578 )2579 try:2580 self.execute_script(script)2581 except Exception:2582 pass2583 def set_attribute_all(self, attribute, value):2584 """Same as set_attributes(), but using querySelectorAll naming scheme.2585 This method uses JavaScript to set/update a common attribute.2586 All matching selectors from querySelectorAll() are used.2587 Example => (Make all links on a website redirect to Google):2588 self.set_attribute_all("a", "href", "https://google.com")"""2589 self.set_attributes(attribute, value)2590 def remove_attribute(self, attribute, timeout=None):2591 """This method uses JavaScript to remove an attribute.2592 Only the first matching selector from querySelector() is used."""2593 self.__check_scope()2594 if not timeout:2595 timeout = settings.SMALL_TIMEOUT2596 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2597 timeout = self.__get_new_timeout(timeout)2598 selector, by = self.__recalculate_selector()2599 if self.is_element_visible():2600 try:2601 self.scroll_to(timeout=timeout)2602 except Exception as e:2603 logging.info(e)2604 pass2605 attribute = re.escape(attribute)2606 attribute = self.__escape_quotes_if_needed(attribute)2607 css_selector = self.convert_to_css_selector(selector, by=by)2608 css_selector = re.escape(css_selector) # Add "\\" to special chars2609 css_selector = self.__escape_quotes_if_needed(css_selector)2610 script = """document.querySelector('%s').removeAttribute('%s');""" % (2611 css_selector,2612 attribute,2613 )2614 self.execute_script(script)2615 def remove_attributes(self, attribute):2616 """This method uses JavaScript to remove a common attribute.2617 All matching selectors from querySelectorAll() are used."""2618 self.__check_scope()2619 selector, by = self.__recalculate_selector()2620 attribute = re.escape(attribute)2621 attribute = self.__escape_quotes_if_needed(attribute)2622 css_selector = self.convert_to_css_selector(selector, by=by)2623 css_selector = re.escape(css_selector) # Add "\\" to special chars2624 css_selector = self.__escape_quotes_if_needed(css_selector)2625 script = """var $elements = document.querySelectorAll('%s');2626 var index = 0, length = $elements.length;2627 for(; index < length; index++){2628 $elements[index].removeAttribute('%s');}""" % (2629 css_selector,2630 attribute,2631 )2632 try:2633 self.execute_script(script)2634 except Exception:2635 pass2636 def get_property_value(self, property, timeout=None):2637 """Returns the property value of a page element's computed style.2638 Example:2639 opacity = self.get_property_value("html body a", "opacity")2640 self.assertTrue(float(opacity) > 0, "Element not visible!")"""2641 self.__check_scope()2642 if not timeout:2643 timeout = settings.SMALL_TIMEOUT2644 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2645 timeout = self.__get_new_timeout(timeout)2646 selector, by = self.__recalculate_selector()2647 self.wait_for_ready_state_complete()2648 page_actions.wait_for_element_present(2649 self.driver, selector, by, timeout2650 )2651 try:2652 selector = self.convert_to_css_selector(selector, by=by)2653 except Exception:2654 # Don't run action if can't convert to CSS_Selector for JavaScript2655 raise Exception(2656 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!"2657 % (selector, by)2658 )2659 selector = re.escape(selector)2660 selector = self.__escape_quotes_if_needed(selector)2661 script = """var $elm = document.querySelector('%s');2662 $val = window.getComputedStyle($elm).getPropertyValue('%s');2663 return $val;""" % (2664 selector,2665 property,2666 )2667 value = self.execute_script(script)2668 if value is not None:2669 return value2670 else:2671 return "" # Return an empty string if the property doesn't exist2672 def get_image_url(self, timeout=None):2673 """ Extracts the URL from an image element on the page. """2674 self.__check_scope()2675 if not timeout:2676 timeout = settings.SMALL_TIMEOUT2677 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2678 timeout = self.__get_new_timeout(timeout)2679 return self.get_attribute(attribute="src", timeout=timeout)2680 def find_elements(self, limit=0):2681 """Returns a list of matching WebElements.2682 Elements could be either hidden or visible on the page.2683 If "limit" is set and > 0, will only return that many elements."""2684 selector, by = self.__recalculate_selector()2685 self.wait_for_ready_state_complete()2686 time.sleep(0.05)2687 elements = self.driver.find_elements(by=by, value=selector)2688 if limit and limit > 0 and len(elements) > limit:2689 elements = elements[:limit]2690 return elements2691 def find_visible_elements(self, limit=0):2692 """Returns a list of matching WebElements that are visible.2693 If "limit" is set and > 0, will only return that many elements."""2694 selector, by = self.__recalculate_selector()2695 self.wait_for_ready_state_complete()2696 time.sleep(0.05)2697 v_elems = page_actions.find_visible_elements(self.driver, selector, by)2698 if limit and limit > 0 and len(v_elems) > limit:2699 v_elems = v_elems[:limit]2700 return v_elems2701 def click_visible_elements(self, limit=0, timeout=None):2702 """Finds all matching page elements and clicks visible ones in order.2703 If a click reloads or opens a new page, the clicking will stop.2704 If no matching elements appear, an Exception will be raised.2705 If "limit" is set and > 0, will only click that many elements.2706 Also clicks elements that become visible from previous clicks.2707 Works best for actions such as clicking all checkboxes on a page.2708 Example: self.click_visible_elements('input[type="checkbox"]')"""2709 self.__check_scope()2710 if not timeout:2711 timeout = settings.SMALL_TIMEOUT2712 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2713 timeout = self.__get_new_timeout(timeout)2714 selector, by = self.__recalculate_selector()2715 self.wait_for_element_present(timeout=timeout)2716 elements = self.find_elements()2717 if self.browser == "safari":2718 if not limit:2719 limit = 02720 num_elements = len(elements)2721 if num_elements == 0:2722 raise Exception(2723 "No matching elements found for selector {%s}!" % selector2724 )2725 elif num_elements < limit or limit == 0:2726 limit = num_elements2727 selector, by = self.__recalculate_selector()2728 css_selector = self.convert_to_css_selector(selector, by=by)2729 last_css_chunk = css_selector.split(" ")[-1]2730 if ":" in last_css_chunk:2731 self.__js_click_all(css_selector)2732 self.wait_for_ready_state_complete()2733 return2734 else:2735 for i in range(1, limit + 1):2736 new_selector = css_selector + ":nth-of-type(%s)" % str(i)2737 if self.is_element_visible(new_selector):2738 self.__js_click(new_selector)2739 self.wait_for_ready_state_complete()2740 return2741 click_count = 02742 for element in elements:2743 if limit and limit > 0 and click_count >= limit:2744 return2745 try:2746 if element.is_displayed():2747 self.__scroll_to_element(element)2748 element.click()2749 click_count += 12750 self.wait_for_ready_state_complete()2751 except ECI_Exception:2752 continue # ElementClickInterceptedException (Overlay likely)2753 except (StaleElementReferenceException, ENI_Exception):2754 self.wait_for_ready_state_complete()2755 time.sleep(0.12)2756 try:2757 if element.is_displayed():2758 self.__scroll_to_element(element)2759 element.click()2760 click_count += 12761 self.wait_for_ready_state_complete()2762 except (StaleElementReferenceException, ENI_Exception):2763 return # Probably on new page / Elements are all stale2764 def click_nth_visible_element(self, number, timeout=None):2765 """Finds all matching page elements and clicks the nth visible one.2766 Example: self.click_nth_visible_element('[type="checkbox"]', 5)2767 (Clicks the 5th visible checkbox on the page.)"""2768 self.__check_scope()2769 if not timeout:2770 timeout = settings.SMALL_TIMEOUT2771 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2772 timeout = self.__get_new_timeout(timeout)2773 selector, by = self.__recalculate_selector()2774 self.wait_for_element_present(timeout=timeout)2775 elements = self.find_visible_elements()2776 if len(elements) < number:2777 raise Exception(2778 "Not enough matching {%s} elements of type {%s} to "2779 "click number %s!" % (selector, by, number)2780 )2781 number -= 12782 if number < 0:2783 number = 02784 element = elements[number]2785 self.wait_for_ready_state_complete()2786 try:2787 self.__scroll_to_element(element)2788 element.click()2789 except (StaleElementReferenceException, ENI_Exception):2790 self.wait_for_ready_state_complete()2791 time.sleep(0.12)2792 self.__scroll_to_element(element)2793 element.click()2794 def click_if_visible(self):2795 """If the page selector exists and is visible, clicks on the element.2796 This method only clicks on the first matching element found.2797 (Use click_visible_elements() to click all matching elements.)"""2798 self.wait_for_ready_state_complete()2799 if self.is_element_visible():2800 self.click()2801 def is_checked(self, timeout=None):2802 """Determines if a checkbox or a radio button element is checked.2803 Returns True if the element is checked.2804 Returns False if the element is not checked.2805 If the element is not present on the page, raises an exception.2806 If the element is not a checkbox or radio, raises an exception."""2807 self.__check_scope()2808 if not timeout:2809 timeout = settings.SMALL_TIMEOUT2810 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2811 timeout = self.__get_new_timeout(timeout)2812 selector, by = self.__recalculate_selector()2813 kind = self.get_attribute("type", timeout=timeout)2814 if kind != "checkbox" and kind != "radio":2815 raise Exception("Expecting a checkbox or a radio button element!")2816 is_checked = self.get_attribute("checked", timeout=timeout, hard_fail=False2817 )2818 if is_checked:2819 return True2820 else: # (NoneType)2821 return False2822 def is_selected(self, timeout=None):2823 """ Same as is_checked() """2824 return self.is_checked(timeout=timeout)2825 def check_if_unchecked(self):2826 """ If a checkbox or radio button is not checked, will check it. """2827 self.__check_scope()2828 selector, by = self.__recalculate_selector()2829 if not self.is_checked():2830 if self.is_element_visible():2831 self.click()2832 else:2833 selector = self.convert_to_css_selector(selector, by=by)2834 self.js_click(selector, by=By.CSS_SELECTOR)2835 def select_if_unselected(self):2836 """ Same as check_if_unchecked() """2837 self.check_if_unchecked()2838 def uncheck_if_checked(self):2839 """ If a checkbox is checked, will uncheck it. """2840 self.__check_scope()2841 selector, by = self.__recalculate_selector()2842 if self.is_checked():2843 if self.is_element_visible(selector, by=by):2844 self.click()2845 else:2846 selector = self.convert_to_css_selector(selector, by=by)2847 self.js_click()2848 def unselect_if_selected(self):2849 """ Same as uncheck_if_checked() """2850 self.uncheck_if_checked()2851 def is_element_in_an_iframe(self):2852 """Returns True if the selector's element is located in an iframe.2853 Otherwise returns False."""2854 self.__check_scope()2855 selector, by = self.__recalculate_selector()2856 if self.is_element_present():2857 return False2858 soup = self.get_beautiful_soup()2859 iframe_list = soup.select("iframe")2860 for iframe in iframe_list:2861 iframe_identifier = None2862 if iframe.has_attr("name") and len(iframe["name"]) > 0:2863 iframe_identifier = iframe["name"]2864 elif iframe.has_attr("id") and len(iframe["id"]) > 0:2865 iframe_identifier = iframe["id"]2866 elif iframe.has_attr("class") and len(iframe["class"]) > 0:2867 iframe_class = " ".join(iframe["class"])2868 iframe_identifier = '[class="%s"]' % iframe_class2869 else:2870 continue2871 self.switch_to_frame(iframe_identifier)2872 if self.is_element_present():2873 self.switch_to_default_content()2874 return True2875 self.switch_to_default_content()2876 return False2877 def switch_to_frame_of_element(self):2878 """Set driver control to the iframe containing element (assuming the2879 element is in a single-nested iframe) and returns the iframe name.2880 If element is not in an iframe, returns None, and nothing happens.2881 May not work if multiple iframes are nested within each other."""2882 self.__check_scope()2883 selector, by = self.__recalculate_selector()2884 if self.is_element_present():2885 return None2886 soup = self.get_beautiful_soup()2887 iframe_list = soup.select("iframe")2888 for iframe in iframe_list:2889 iframe_identifier = None2890 if iframe.has_attr("name") and len(iframe["name"]) > 0:2891 iframe_identifier = iframe["name"]2892 elif iframe.has_attr("id") and len(iframe["id"]) > 0:2893 iframe_identifier = iframe["id"]2894 elif iframe.has_attr("class") and len(iframe["class"]) > 0:2895 iframe_class = " ".join(iframe["class"])2896 iframe_identifier = '[class="%s"]' % iframe_class2897 else:2898 continue2899 try:2900 self.switch_to_frame(iframe_identifier, timeout=1)2901 if self.is_element_present():2902 return iframe_identifier2903 except Exception as e:2904 logging.info(e)2905 pass2906 self.switch_to_default_content()2907 try:2908 self.switch_to_frame(selector, timeout=1)2909 return selector2910 except Exception:2911 if self.is_element_present():2912 return ""2913 raise Exception(2914 "Could not switch to iframe containing "2915 "element {%s}!" % selector2916 )2917 def hover_on_element(self):2918 """2919 hover_on_element2920 """2921 self.__check_scope()2922 original_selector = self.selector2923 original_by = self.by2924 selector, by = self.__recalculate_selector(self.selector, self.by)2925 if page_utils.is_xpath_selector(selector):2926 selector = self.convert_to_css_selector(selector, By.XPATH)2927 by = By.CSS_SELECTOR2928 self.wait_for_element_visible(timeout=settings.SMALL_TIMEOUT)2929 self.scroll_to()2930 time.sleep(0.05) # Settle down from scrolling before hovering2931 if self.browser != "chrome":2932 return page_actions.hover_on_element(self.driver, selector)2933 # Using Chrome2934 # (Pure hover actions won't work on early chromedriver versions)2935 try:2936 return page_actions.hover_on_element(self.driver, selector)2937 except WebDriverException as e:2938 driver_capabilities = self.driver.__dict__["capabilities"]2939 if "version" in driver_capabilities:2940 chrome_version = driver_capabilities["version"]2941 else:2942 chrome_version = driver_capabilities["browserVersion"]2943 major_chrome_version = chrome_version.split(".")[0]2944 chrome_dict = self.driver.__dict__["capabilities"]["chrome"]2945 chromedriver_version = chrome_dict["chromedriverVersion"]2946 chromedriver_version = chromedriver_version.split(" ")[0]2947 major_chromedriver_version = chromedriver_version.split(".")[0]2948 install_sb = (2949 "seleniumbase install chromedriver %s" % major_chrome_version2950 )2951 if major_chromedriver_version < major_chrome_version:2952 # Upgrading the driver is required for performing hover actions2953 message = (2954 "\n"2955 "You need a newer chromedriver to perform hover actions!\n"2956 "Your version of chromedriver is: %s\n"2957 "And your version of Chrome is: %s\n"2958 "You can fix this issue by running:\n>>> %s\n"2959 % (chromedriver_version, chrome_version, install_sb)2960 )2961 raise Exception(message)2962 else:2963 raise Exception(e)2964 def drag_and_drop(self, drop_selector, drop_by=By.CSS_SELECTOR, timeout=None):2965 """ Drag and drop an element from one selector to another. """2966 drag_selector = self.selector2967 drag_by = self.by2968 self.__check_scope()2969 if not timeout:2970 timeout = settings.SMALL_TIMEOUT2971 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2972 timeout = self.__get_new_timeout(timeout)2973 drag_selector, drag_by = self.__recalculate_selector(2974 drag_selector, drag_by2975 )2976 drop_selector, drop_by = self.__recalculate_selector(drop_selector, drop_by2977 )2978 drag_element = self.wait_for_element_visible(timeout=timeout)2979 self.scroll_to()2980 drag_selector = self.convert_to_css_selector(drag_selector, drag_by)2981 drop_selector = self.convert_to_css_selector(drop_selector, drop_by)2982 drag_and_drop_script = js_utils.get_drag_and_drop_script()2983 self.safe_execute_script(2984 drag_and_drop_script2985 + (2986 "$('%s').simulateDragDrop("2987 "{dropTarget: "2988 "'%s'});" % (drag_selector, drop_selector)2989 )2990 )2991 return drag_element2992 def drag_and_drop_with_offset(self, x, y, timeout=None):2993 """ Drag and drop an element to an {X,Y}-offset location. """2994 self.__check_scope()2995 if not timeout:2996 timeout = settings.SMALL_TIMEOUT2997 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2998 timeout = self.__get_new_timeout(timeout)2999 selector, by = self.__recalculate_selector()3000 css_selector = self.convert_to_css_selector(selector, by=by)3001 element = self.wait_for_element_visible(css_selector, timeout=timeout)3002 css_selector = re.escape(css_selector) # Add "\\" to special chars3003 css_selector = self.__escape_quotes_if_needed(css_selector)3004 script = js_utils.get_drag_and_drop_with_offset_script(3005 css_selector, x, y3006 )3007 self.safe_execute_script(script)3008 return element3009 def select_option(self, option, option_by="text", timeout=None):3010 """Selects an HTML <select> option by specification.3011 Option specifications are by "text", "index", or "value".3012 Defaults to "text" if option_by is unspecified or unknown."""3013 from selenium.webdriver.support.ui import Select3014 self.__check_scope()3015 if not timeout:3016 timeout = settings.SMALL_TIMEOUT3017 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3018 timeout = self.__get_new_timeout(timeout)3019 dropdown_selector, dropdown_by = self.__recalculate_selector()3020 self.wait_for_ready_state_complete()3021 element = self.wait_for_element_present(timeout=timeout)3022 pre_action_url = self.driver.current_url3023 try:3024 if option_by == "index":3025 Select(element).select_by_index(option)3026 elif option_by == "value":3027 Select(element).select_by_value(option)3028 else:3029 Select(element).select_by_visible_text(option)3030 except (StaleElementReferenceException, ENI_Exception):3031 self.wait_for_ready_state_complete()3032 time.sleep(0.14)3033 element = self.wait_for_element_present(timeout=timeout)3034 if option_by == "index":3035 Select(element).select_by_index(option)3036 elif option_by == "value":3037 Select(element).select_by_value(option)3038 else:3039 Select(element).select_by_visible_text(option)3040 if settings.WAIT_FOR_RSC_ON_CLICKS:3041 self.wait_for_ready_state_complete()3042 def select_option_by_text(self, option, timeout=None):3043 """Selects an HTML <select> option by option text.3044 @Params3045 dropdown_selector - the <select> selector.3046 option - the text of the option.3047 """3048 self.__check_scope()3049 if not timeout:3050 timeout = settings.SMALL_TIMEOUT3051 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3052 timeout = self.__get_new_timeout(timeout)3053 self.select_option(option, timeout=timeout)3054 def select_option_by_index(self, option, timeout=None):3055 """Selects an HTML <select> option by option index.3056 @Params3057 dropdown_selector - the <select> selector.3058 option - the index number of the option.3059 """3060 self.__check_scope()3061 if not timeout:3062 timeout = settings.SMALL_TIMEOUT3063 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3064 timeout = self.__get_new_timeout(timeout)3065 self.select_option(option, option_by="index", timeout=timeout)3066 def select_option_by_value(self, option, timeout=None):3067 """Selects an HTML <select> option by option value.3068 @Params3069 dropdown_selector - the <select> selector.3070 option - the value property of the option.3071 """3072 self.__check_scope()3073 if not timeout:3074 timeout = settings.SMALL_TIMEOUT3075 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3076 timeout = self.__get_new_timeout(timeout)3077 self.select_option(option, option_by="value", timeout=timeout)3078 def bring_to_front(self):3079 """Updates the Z-index of a page element to bring it into view.3080 Useful when getting a WebDriverException, such as the one below:3081 { Element is not clickable at point (#, #).3082 Other element would receive the click: ... }"""3083 self.__check_scope()3084 selector, by = self.__recalculate_selector()3085 self.wait_for_element_visible(3086 selector, by=by, timeout=settings.SMALL_TIMEOUT3087 )3088 try:3089 selector = self.convert_to_css_selector(selector, by=by)3090 except Exception as e:3091 logging.info(e)3092 # Don't run action if can't convert to CSS_Selector for JavaScript3093 return3094 selector = re.escape(selector)3095 selector = self.__escape_quotes_if_needed(selector)3096 script = (3097 """document.querySelector('%s').style.zIndex = '999999';"""3098 % selector3099 )3100 self.execute_script(script)3101 def highlight_update_text(self, text, loops=3, scroll=True):3102 """3103 highlight_update_text3104 """3105 self.__check_scope()3106 # self.highlight(loops=loops, scroll=scroll)3107 self.update_text(text)3108 def __highlight_with_js(self, selector, loops, o_bs):3109 self.wait_for_ready_state_complete()3110 js_utils.highlight_with_js(self.driver, selector, loops, o_bs)3111 def __highlight_with_jquery(self, selector, loops, o_bs):3112 self.wait_for_ready_state_complete()3113 js_utils.highlight_with_jquery(self.driver, selector, loops, o_bs)3114 def press_up_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):3115 """Simulates pressing the UP Arrow on the keyboard.3116 By default, "html" will be used as the CSS Selector target.3117 You can specify how many times in-a-row the action happens."""3118 self.__check_scope()3119 if times < 1:3120 return3121 element = self.wait_for_element_present(selector)3122 for i in range(int(times)):3123 try:3124 element.send_keys(Keys.ARROW_UP)3125 except Exception:3126 self.wait_for_ready_state_complete()3127 element = self.wait_for_element_visible(selector)3128 element.send_keys(Keys.ARROW_UP)3129 time.sleep(0.01)3130 def press_down_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):3131 """Simulates pressing the DOWN Arrow on the keyboard.3132 By default, "html" will be used as the CSS Selector target.3133 You can specify how many times in-a-row the action happens."""3134 self.__check_scope()3135 if times < 1:3136 return3137 element = self.wait_for_element_present(selector)3138 for i in range(int(times)):3139 try:3140 element.send_keys(Keys.ARROW_DOWN)3141 except Exception:3142 self.wait_for_ready_state_complete()3143 element = self.wait_for_element_visible(selector)3144 element.send_keys(Keys.ARROW_DOWN)3145 time.sleep(0.01)3146 def press_left_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):3147 """Simulates pressing the LEFT Arrow on the keyboard.3148 By default, "html" will be used as the CSS Selector target.3149 You can specify how many times in-a-row the action happens."""3150 self.__check_scope()3151 if times < 1:3152 return3153 element = self.wait_for_element_present(selector)3154 for i in range(int(times)):3155 try:3156 element.send_keys(Keys.ARROW_LEFT)3157 except Exception:3158 self.wait_for_ready_state_complete()3159 element = self.wait_for_element_visible(selector)3160 element.send_keys(Keys.ARROW_LEFT)3161 time.sleep(0.01)3162 def press_right_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):3163 """Simulates pressing the RIGHT Arrow on the keyboard.3164 By default, "html" will be used as the CSS Selector target.3165 You can specify how many times in-a-row the action happens."""3166 self.__check_scope()3167 if times < 1:3168 return3169 element = self.wait_for_element_present(selector)3170 for i in range(int(times)):3171 try:3172 element.send_keys(Keys.ARROW_RIGHT)3173 except Exception:3174 self.wait_for_ready_state_complete()3175 element = self.wait_for_element_visible(selector)3176 element.send_keys(Keys.ARROW_RIGHT)3177 time.sleep(0.01)3178 def scroll_to(self, selector=None, by=By.CSS_SELECTOR, timeout=None):3179 """ Fast scroll to destination """3180 if selector is None:3181 selector = self.selector3182 self.__check_scope()3183 if not timeout:3184 timeout = settings.SMALL_TIMEOUT3185 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3186 timeout = self.__get_new_timeout(timeout)3187 element = self.wait_for_element_visible(3188 selector, by=by, timeout=timeout3189 )3190 try:3191 self.__scroll_to_element(element, selector, by)3192 except (StaleElementReferenceException, ENI_Exception):3193 self.wait_for_ready_state_complete()3194 time.sleep(0.12)3195 element = self.wait_for_element_visible(3196 selector, by=by, timeout=timeout3197 )3198 self.__scroll_to_element(element, selector, by)3199 def slow_scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):3200 """ Slow motion scroll to destination """3201 self.__check_scope()3202 if not timeout:3203 timeout = settings.SMALL_TIMEOUT3204 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3205 timeout = self.__get_new_timeout(timeout)3206 selector, by = self.__recalculate_selector(selector, by)3207 element = self.wait_for_element_visible(3208 selector, by=by, timeout=timeout3209 )3210 try:3211 scroll_distance = js_utils.get_scroll_distance_to_element(3212 self.driver, element3213 )3214 if abs(scroll_distance) > constants.Values.SSMD:3215 self.__jquery_slow_scroll_to(selector, by)3216 else:3217 self.__slow_scroll_to_element(element)3218 except Exception:3219 self.wait_for_ready_state_complete()3220 time.sleep(0.12)3221 element = self.wait_for_element_visible(3222 selector, by=by, timeout=timeout3223 )3224 self.__slow_scroll_to_element(element)3225 def js_click(self, selector=None, by=None, all_matches=False):3226 """Clicks an element using JavaScript.3227 Can be used to click hidden / invisible elements.3228 If "all_matches" is False, only the first match is clicked."""3229 self.wait_for_ready_state_complete()3230 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)3231 if by == By.LINK_TEXT:3232 message = (3233 "Pure JavaScript doesn't support clicking by Link Text. "3234 "You may want to use self.jquery_click() instead, which "3235 "allows this with :contains(), assuming jQuery isn't blocked. "3236 "For now, self.js_click() will use a regular WebDriver click."3237 )3238 logging.debug(message)3239 self.click()3240 return3241 element = self.wait_for_element_present(timeout=settings.SMALL_TIMEOUT)3242 if self.is_element_visible(selector, by=by):3243 success = js_utils.scroll_to_element(self.driver, element)3244 if not success:3245 self.wait_for_ready_state_complete()3246 timeout = settings.SMALL_TIMEOUT3247 element = page_actions.wait_for_element_present(3248 self.driver, selector, by, timeout=timeout3249 )3250 css_selector = self.convert_to_css_selector(selector, by=by)3251 css_selector = re.escape(css_selector) # Add "\\" to special chars3252 css_selector = self.__escape_quotes_if_needed(css_selector)3253 if not all_matches:3254 if ":contains\\(" not in css_selector:3255 self.__js_click(selector, by=by)3256 else:3257 click_script = """jQuery('%s')[0].click();""" % css_selector3258 self.safe_execute_script(click_script)3259 else:3260 if ":contains\\(" not in css_selector:3261 self.__js_click_all(selector, by=by)3262 else:3263 click_script = """jQuery('%s').click();""" % css_selector3264 self.safe_execute_script(click_script)3265 self.wait_for_ready_state_complete()3266 def js_click_all(self, selector, by=By.CSS_SELECTOR):3267 """ Clicks all matching elements using pure JS. (No jQuery) """3268 self.js_click(selector, by=By.CSS_SELECTOR, all_matches=True)3269 def jquery_click(self, selector, by=By.CSS_SELECTOR):3270 """Clicks an element using jQuery. (Different from using pure JS.)3271 Can be used to click hidden / invisible elements."""3272 self.__check_scope()3273 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)3274 self.wait_for_element_present(timeout=settings.SMALL_TIMEOUT)3275 selector = self.convert_to_css_selector(selector, by=by)3276 selector = self.__make_css_match_first_element_only(selector)3277 click_script = """jQuery('%s')[0].click();""" % selector3278 self.safe_execute_script(click_script)3279 def jquery_click_all(self, selector, by=By.CSS_SELECTOR):3280 """ Clicks all matching elements using jQuery. """3281 self.__check_scope()3282 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)3283 self.wait_for_element_present(timeout=settings.SMALL_TIMEOUT)3284 css_selector = self.convert_to_css_selector(selector, by=by)3285 click_script = """jQuery('%s').click();""" % css_selector3286 self.safe_execute_script(click_script)3287 def hide_element(self, selector, by=By.CSS_SELECTOR):3288 """ Hide the first element on the page that matches the selector. """3289 self.__check_scope()3290 selector, by = self.__recalculate_selector(selector, by)3291 selector = self.convert_to_css_selector(selector, by=by)3292 selector = self.__make_css_match_first_element_only(selector)3293 hide_script = """jQuery('%s').hide();""" % selector3294 self.safe_execute_script(hide_script)3295 def hide_elements(self, selector, by=By.CSS_SELECTOR):3296 """ Hide all elements on the page that match the selector. """3297 self.__check_scope()3298 selector, by = self.__recalculate_selector(selector, by)3299 selector = self.convert_to_css_selector(selector, by=by)3300 hide_script = """jQuery('%s').hide();""" % selector3301 self.safe_execute_script(hide_script)3302 def show_element(self, selector, by=By.CSS_SELECTOR):3303 """ Show the first element on the page that matches the selector. """3304 self.__check_scope()3305 selector, by = self.__recalculate_selector(selector, by)3306 selector = self.convert_to_css_selector(selector, by=by)3307 selector = self.__make_css_match_first_element_only(selector)3308 show_script = """jQuery('%s').show(0);""" % selector3309 self.safe_execute_script(show_script)3310 def show_elements(self, selector, by=By.CSS_SELECTOR):3311 """ Show all elements on the page that match the selector. """3312 self.__check_scope()3313 selector, by = self.__recalculate_selector(selector, by)3314 selector = self.convert_to_css_selector(selector, by=by)3315 show_script = """jQuery('%s').show(0);""" % selector3316 self.safe_execute_script(show_script)3317 def remove_element(self, selector, by=By.CSS_SELECTOR):3318 """ Remove the first element on the page that matches the selector. """3319 self.__check_scope()3320 selector, by = self.__recalculate_selector(selector, by)3321 selector = self.convert_to_css_selector(selector, by=by)3322 selector = self.__make_css_match_first_element_only(selector)3323 remove_script = """jQuery('%s').remove();""" % selector3324 self.safe_execute_script(remove_script)3325 def remove_elements(self, selector, by=By.CSS_SELECTOR):3326 """ Remove all elements on the page that match the selector. """3327 self.__check_scope()3328 selector, by = self.__recalculate_selector(selector, by)3329 selector = self.convert_to_css_selector(selector, by=by)3330 remove_script = """jQuery('%s').remove();""" % selector3331 self.safe_execute_script(remove_script)3332 def wait_for_attribute(self, attribute, value=None, timeout=None):3333 """Raises an exception if the element attribute/value is not found.3334 If the value is not specified, the attribute only needs to exist.3335 Returns the element that contains the attribute if successful.3336 Default timeout = LARGE_TIMEOUT."""3337 self.__check_scope()3338 if not timeout:3339 timeout = settings.LARGE_TIMEOUT3340 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3341 timeout = self.__get_new_timeout(timeout)3342 selector, by = self.__recalculate_selector()3343 return page_actions.wait_for_attribute(self.driver, selector, attribute, value=value, by=by, timeout=timeout)3344 def assert_attribute(self, attribute, value=None, timeout=None):3345 """Raises an exception if the element attribute/value is not found.3346 If the value is not specified, the attribute only needs to exist.3347 Returns True if successful. Default timeout = SMALL_TIMEOUT."""3348 self.__check_scope()3349 if not timeout:3350 timeout = settings.SMALL_TIMEOUT3351 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3352 timeout = self.__get_new_timeout(timeout)3353 selector, by = self.__recalculate_selector()3354 self.wait_for_attribute(attribute, value=value, timeout=timeout)3355 if (3356 self.is_element_visible(selector, by=by)3357 ):3358 a_a = "ASSERT ATTRIBUTE"3359 i_n = "in"3360 if self._language != "English":3361 from seleniumbase.fixtures.words import SD3362 a_a = SD.translate_assert_attribute(*self._language)3363 i_n = SD.translate_in(*self._language)3364 if not value:3365 messenger_post = "%s: {%s} %s %s: %s" % (3366 a_a,3367 attribute,3368 i_n,3369 by.upper(),3370 selector,3371 )3372 else:3373 messenger_post = '%s: {%s == "%s"} %s %s: %s' % (3374 a_a,3375 attribute,3376 value,3377 i_n,3378 by.upper(),3379 selector,3380 )3381 self.__highlight_with_assert_success(messenger_post, selector, by)3382 return True3383 def convert_to_css_selector(self, selector=None, by=None):3384 """This method converts a selector to a CSS_SELECTOR.3385 jQuery commands require a CSS_SELECTOR for finding elements.3386 This method should only be used for jQuery/JavaScript actions.3387 Pure JavaScript doesn't support using a:contains("LINK_TEXT")."""3388 if selector is None or by is None:3389 selector = self.selector3390 by = self.by3391 if by == By.CSS_SELECTOR:3392 return selector3393 elif by == By.ID:3394 return "#%s" % selector3395 elif by == By.CLASS_NAME:3396 return ".%s" % selector3397 elif by == By.NAME:3398 return '[name="%s"]' % selector3399 elif by == By.TAG_NAME:3400 return selector3401 elif by == By.XPATH:3402 return self.convert_xpath_to_css(selector)3403 elif by == By.LINK_TEXT:3404 return 'a:contains("%s")' % selector3405 elif by == By.PARTIAL_LINK_TEXT:3406 return 'a:contains("%s")' % selector3407 else:3408 raise Exception(3409 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!"3410 % (selector, by)3411 )3412 def set_value(self, selector=None, text=None, by=By.CSS_SELECTOR, timeout=None):3413 """ This method uses JavaScript to update a text field. """3414 self.__check_scope()3415 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)3416 if not timeout:3417 timeout = settings.LARGE_TIMEOUT3418 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3419 timeout = self.__get_new_timeout(timeout)3420 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)3421 orginal_selector = selector3422 css_selector = self.convert_to_css_selector()3423 self.scroll_to(timeout=timeout)3424 if type(text) is int or type(text) is float:3425 text = str(text)3426 value = re.escape(text)3427 value = self.__escape_quotes_if_needed(value)3428 css_selector = re.escape(css_selector) # Add "\\" to special chars3429 css_selector = self.__escape_quotes_if_needed(css_selector)3430 if ":contains\\(" not in css_selector:3431 script = """document.querySelector('%s').value='%s';""" % (3432 css_selector,3433 value,3434 )3435 self.execute_script(script)3436 else:3437 script = """jQuery('%s')[0].value='%s';""" % (css_selector, value)3438 self.safe_execute_script(script)3439 if text.endswith("\n"):3440 element = self.wait_for_element_present(timeout=timeout)3441 element.send_keys(Keys.RETURN)3442 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:3443 self.wait_for_ready_state_complete()3444 def js_update_text(self, selector=None, text=None, by=By.CSS_SELECTOR, timeout=None):3445 """JavaScript + send_keys are used to update a text field.3446 Performs self.set_value() and triggers event listeners.3447 If text ends in "\n", set_value() presses RETURN after.3448 Works faster than send_keys() alone due to the JS call.3449 """3450 self.__check_scope()3451 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)3452 if not timeout:3453 timeout = settings.LARGE_TIMEOUT3454 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3455 timeout = self.__get_new_timeout(timeout)3456 selector, by = self.__recalculate_selector(selector, by)3457 if type(text) is int or type(text) is float:3458 text = str(text)3459 self.set_value(selector, text, by=by, timeout=timeout)3460 if not text.endswith("\n"):3461 try:3462 element = page_actions.wait_for_element_present(3463 self.driver, selector, by, timeout=0.23464 )3465 element.send_keys(" " + Keys.BACK_SPACE)3466 except Exception:3467 pass3468 def js_type(self, selector=None, text=None, by=By.CSS_SELECTOR, timeout=None):3469 """Same as self.js_update_text()3470 JavaScript + send_keys are used to update a text field.3471 Performs self.set_value() and triggers event listeners.3472 If text ends in "\n", set_value() presses RETURN after.3473 Works faster than send_keys() alone due to the JS call.3474 """3475 self.__check_scope()3476 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)3477 if not timeout:3478 timeout = settings.LARGE_TIMEOUT3479 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3480 timeout = self.__get_new_timeout(timeout)3481 selector, by = self.__recalculate_selector(selector, by)3482 self.js_update_text(selector, text, by=by, timeout=timeout)3483 def set_text(self, selector=None, text=None, by=By.CSS_SELECTOR, timeout=None):3484 """Same as self.js_update_text()3485 JavaScript + send_keys are used to update a text field.3486 Performs self.set_value() and triggers event listeners.3487 If text ends in "\n", set_value() presses RETURN after.3488 Works faster than send_keys() alone due to the JS call."""3489 self.__check_scope()3490 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)3491 if not timeout:3492 timeout = settings.LARGE_TIMEOUT3493 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3494 timeout = self.__get_new_timeout(timeout)3495 selector, by = self.__recalculate_selector(selector, by)3496 self.js_update_text(selector, text, by=by, timeout=timeout)3497 def jquery_update_text(self, selector=None, text=None, by=By.CSS_SELECTOR, timeout=None):3498 """This method uses jQuery to update a text field.3499 If the text string ends with the newline character,3500 Selenium finishes the call, which simulates pressing3501 {Enter/Return} after the text is entered."""3502 self.__check_scope()3503 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)3504 if not timeout:3505 timeout = settings.LARGE_TIMEOUT3506 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3507 timeout = self.__get_new_timeout(timeout)3508 selector, by = self.__recalculate_selector(selector, by, xp_ok=False)3509 element = self.wait_for_element_visible(3510 selector, by=by, timeout=timeout3511 )3512 self.scroll_to()3513 selector = self.convert_to_css_selector()3514 selector = self.__make_css_match_first_element_only(selector)3515 selector = self.__escape_quotes_if_needed(selector)3516 text = re.escape(text)3517 text = self.__escape_quotes_if_needed(text)3518 update_text_script = """jQuery('%s').val('%s');""" % (selector, text)3519 self.safe_execute_script(update_text_script)3520 if text.endswith("\n"):3521 element.send_keys("\n")3522 ############3523 # Duplicates (Avoids name confusion when migrating from other frameworks.)3524 def create_presentation(self, name=None, theme="default", transition="default"):3525 """Creates a Reveal-JS presentation that you can add slides to.3526 @Params3527 name - If creating multiple presentations at the same time,3528 use this to specify the name of the current presentation.3529 theme - Set a theme with a unique style for the presentation.3530 Valid themes: "serif" (default), "sky", "white", "black",3531 "simple", "league", "moon", "night",3532 "beige", "blood", and "solarized".3533 transition - Set a transition between slides.3534 Valid transitions: "none" (default), "slide", "fade",3535 "zoom", "convex", and "concave".3536 """3537 if not name:3538 name = "default"3539 if not theme or theme == "default":3540 theme = "serif"3541 valid_themes = [3542 "serif",3543 "white",3544 "black",3545 "beige",3546 "simple",3547 "sky",3548 "league",3549 "moon",3550 "night",3551 "blood",3552 "solarized",3553 ]3554 theme = theme.lower()3555 if theme not in valid_themes:3556 raise Exception(3557 "Theme {%s} not found! Valid themes: %s"3558 % (theme, valid_themes)3559 )3560 if not transition or transition == "default":3561 transition = "none"3562 valid_transitions = [3563 "none",3564 "slide",3565 "fade",3566 "zoom",3567 "convex",3568 "concave",3569 ]3570 transition = transition.lower()3571 if transition not in valid_transitions:3572 raise Exception(3573 "Transition {%s} not found! Valid transitions: %s"3574 % (transition, valid_transitions)3575 )3576 reveal_theme_css = None3577 if theme == "serif":3578 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS3579 elif theme == "sky":3580 reveal_theme_css = constants.Reveal.SKY_MIN_CSS3581 elif theme == "white":3582 reveal_theme_css = constants.Reveal.WHITE_MIN_CSS3583 elif theme == "black":3584 reveal_theme_css = constants.Reveal.BLACK_MIN_CSS3585 elif theme == "simple":3586 reveal_theme_css = constants.Reveal.SIMPLE_MIN_CSS3587 elif theme == "league":3588 reveal_theme_css = constants.Reveal.LEAGUE_MIN_CSS3589 elif theme == "moon":3590 reveal_theme_css = constants.Reveal.MOON_MIN_CSS3591 elif theme == "night":3592 reveal_theme_css = constants.Reveal.NIGHT_MIN_CSS3593 elif theme == "beige":3594 reveal_theme_css = constants.Reveal.BEIGE_MIN_CSS3595 elif theme == "blood":3596 reveal_theme_css = constants.Reveal.BLOOD_MIN_CSS3597 elif theme == "solarized":3598 reveal_theme_css = constants.Reveal.SOLARIZED_MIN_CSS3599 else:3600 # Use the default if unable to determine the theme3601 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS3602 new_presentation = (3603 "<html>\n"3604 "<head>\n"3605 '<meta charset="utf-8">\n'3606 '<meta http-equiv="Content-Type" content="text/html">\n'3607 '<meta name="viewport" content="shrink-to-fit=no">\n'3608 '<link rel="stylesheet" href="%s">\n'3609 '<link rel="stylesheet" href="%s">\n'3610 "<style>\n"3611 "pre{background-color:#fbe8d4;border-radius:8px;}\n"3612 "div[flex_div]{height:68vh;margin:0;align-items:center;"3613 "justify-content:center;}\n"3614 "img[rounded]{border-radius:16px;max-width:64%%;}\n"3615 "</style>\n"3616 "</head>\n\n"3617 "<body>\n"3618 "<!-- Generated by SeleniumBase - https://seleniumbase.io -->\n"3619 '<div class="reveal">\n'3620 '<div class="slides">\n'3621 % (constants.Reveal.MIN_CSS, reveal_theme_css)3622 )3623 self._presentation_slides[name] = []3624 self._presentation_slides[name].append(new_presentation)3625 self._presentation_transition[name] = transition3626 def add_slide(3627 self,3628 content=None,3629 image=None,3630 code=None,3631 iframe=None,3632 content2=None,3633 notes=None,3634 transition=None,3635 name=None,3636 ):3637 """Allows the user to add slides to a presentation.3638 @Params3639 content - The HTML content to display on the presentation slide.3640 image - Attach an image (from a URL link) to the slide.3641 code - Attach code of any programming language to the slide.3642 Language-detection will be used to add syntax formatting.3643 iframe - Attach an iFrame (from a URL link) to the slide.3644 content2 - HTML content to display after adding an image or code.3645 notes - Additional notes to include with the slide.3646 ONLY SEEN if show_notes is set for the presentation.3647 transition - Set a transition between slides. (overrides previous)3648 Valid transitions: "none" (default), "slide", "fade",3649 "zoom", "convex", and "concave".3650 name - If creating multiple presentations at the same time,3651 use this to select the presentation to add slides to.3652 """3653 if not name:3654 name = "default"3655 if name not in self._presentation_slides:3656 # Create a presentation if it doesn't already exist3657 self.create_presentation(name=name)3658 if not content:3659 content = ""3660 if not content2:3661 content2 = ""3662 if not notes:3663 notes = ""3664 if not transition:3665 transition = self._presentation_transition[name]3666 elif transition == "default":3667 transition = "none"3668 valid_transitions = [3669 "none",3670 "slide",3671 "fade",3672 "zoom",3673 "convex",3674 "concave",3675 ]3676 transition = transition.lower()3677 if transition not in valid_transitions:3678 raise Exception(3679 "Transition {%s} not found! Valid transitions: %s"3680 "" % (transition, valid_transitions)3681 )3682 add_line = ""3683 if content.startswith("<"):3684 add_line = "\n"3685 html = '\n<section data-transition="%s">%s%s' % (3686 transition,3687 add_line,3688 content,3689 )3690 if image:3691 html += '\n<div flex_div><img rounded src="%s" /></div>' % image3692 if code:3693 html += "\n<div></div>"3694 html += '\n<pre class="prettyprint">\n%s</pre>' % code3695 if iframe:3696 html += (3697 "\n<div></div>"3698 '\n<iframe src="%s" style="width:92%%;height:550px;" '3699 'title="iframe content"></iframe>' % iframe3700 )3701 add_line = ""3702 if content2.startswith("<"):3703 add_line = "\n"3704 if content2:3705 html += "%s%s" % (add_line, content2)3706 html += '\n<aside class="notes">%s</aside>' % notes3707 html += "\n</section>\n"3708 self._presentation_slides[name].append(html)3709 def create_pie_chart(3710 self,3711 chart_name=None,3712 title=None,3713 subtitle=None,3714 data_name=None,3715 unit=None,3716 libs=True,3717 labels=True,3718 legend=True,3719 ):3720 """Creates a JavaScript pie chart using "HighCharts".3721 @Params3722 chart_name - If creating multiple charts,3723 use this to select which one.3724 title - The title displayed for the chart.3725 subtitle - The subtitle displayed for the chart.3726 data_name - The series name. Useful for multi-series charts.3727 If no data_name, will default to using "Series 1".3728 unit - The description label given to the chart's y-axis values.3729 libs - The option to include Chart libraries (JS and CSS files).3730 Should be set to True (default) for the first time creating3731 a chart on a web page. If creating multiple charts on the3732 same web page, you won't need to re-import the libraries3733 when creating additional charts.3734 labels - If True, displays labels on the chart for data points.3735 legend - If True, displays the data point legend on the chart.3736 """3737 if not chart_name:3738 chart_name = "default"3739 if not data_name:3740 data_name = ""3741 style = "pie"3742 self.__create_high_chart(3743 chart_name=chart_name,3744 title=title,3745 subtitle=subtitle,3746 style=style,3747 data_name=data_name,3748 unit=unit,3749 libs=libs,3750 labels=labels,3751 legend=legend,3752 )3753 def create_bar_chart(3754 self,3755 chart_name=None,3756 title=None,3757 subtitle=None,3758 data_name=None,3759 unit=None,3760 libs=True,3761 labels=True,3762 legend=True,3763 ):3764 """Creates a JavaScript bar chart using "HighCharts".3765 @Params3766 chart_name - If creating multiple charts,3767 use this to select which one.3768 title - The title displayed for the chart.3769 subtitle - The subtitle displayed for the chart.3770 data_name - The series name. Useful for multi-series charts.3771 If no data_name, will default to using "Series 1".3772 unit - The description label given to the chart's y-axis values.3773 libs - The option to include Chart libraries (JS and CSS files).3774 Should be set to True (default) for the first time creating3775 a chart on a web page. If creating multiple charts on the3776 same web page, you won't need to re-import the libraries3777 when creating additional charts.3778 labels - If True, displays labels on the chart for data points.3779 legend - If True, displays the data point legend on the chart.3780 """3781 if not chart_name:3782 chart_name = "default"3783 if not data_name:3784 data_name = ""3785 style = "bar"3786 self.__create_high_chart(3787 chart_name=chart_name,3788 title=title,3789 subtitle=subtitle,3790 style=style,3791 data_name=data_name,3792 unit=unit,3793 libs=libs,3794 labels=labels,3795 legend=legend,3796 )3797 def create_column_chart(3798 self,3799 chart_name=None,3800 title=None,3801 subtitle=None,3802 data_name=None,3803 unit=None,3804 libs=True,3805 labels=True,3806 legend=True,3807 ):3808 """Creates a JavaScript column chart using "HighCharts".3809 @Params3810 chart_name - If creating multiple charts,3811 use this to select which one.3812 title - The title displayed for the chart.3813 subtitle - The subtitle displayed for the chart.3814 data_name - The series name. Useful for multi-series charts.3815 If no data_name, will default to using "Series 1".3816 unit - The description label given to the chart's y-axis values.3817 libs - The option to include Chart libraries (JS and CSS files).3818 Should be set to True (default) for the first time creating3819 a chart on a web page. If creating multiple charts on the3820 same web page, you won't need to re-import the libraries3821 when creating additional charts.3822 labels - If True, displays labels on the chart for data points.3823 legend - If True, displays the data point legend on the chart.3824 """3825 if not chart_name:3826 chart_name = "default"3827 if not data_name:3828 data_name = ""3829 style = "column"3830 self.__create_high_chart(3831 chart_name=chart_name,3832 title=title,3833 subtitle=subtitle,3834 style=style,3835 data_name=data_name,3836 unit=unit,3837 libs=libs,3838 labels=labels,3839 legend=legend,3840 )3841 def create_line_chart(3842 self,3843 chart_name=None,3844 title=None,3845 subtitle=None,3846 data_name=None,3847 unit=None,3848 zero=False,3849 libs=True,3850 labels=True,3851 legend=True,3852 ):3853 """Creates a JavaScript line chart using "HighCharts".3854 @Params3855 chart_name - If creating multiple charts,3856 use this to select which one.3857 title - The title displayed for the chart.3858 subtitle - The subtitle displayed for the chart.3859 data_name - The series name. Useful for multi-series charts.3860 If no data_name, will default to using "Series 1".3861 unit - The description label given to the chart's y-axis values.3862 zero - If True, the y-axis always starts at 0. (Default: False).3863 libs - The option to include Chart libraries (JS and CSS files).3864 Should be set to True (default) for the first time creating3865 a chart on a web page. If creating multiple charts on the3866 same web page, you won't need to re-import the libraries3867 when creating additional charts.3868 labels - If True, displays labels on the chart for data points.3869 legend - If True, displays the data point legend on the chart.3870 """3871 if not chart_name:3872 chart_name = "default"3873 if not data_name:3874 data_name = ""3875 style = "line"3876 self.__create_high_chart(3877 chart_name=chart_name,3878 title=title,3879 subtitle=subtitle,3880 style=style,3881 data_name=data_name,3882 unit=unit,3883 zero=zero,3884 libs=libs,3885 labels=labels,3886 legend=legend,3887 )3888 def create_area_chart(3889 self,3890 chart_name=None,3891 title=None,3892 subtitle=None,3893 data_name=None,3894 unit=None,3895 zero=False,3896 libs=True,3897 labels=True,3898 legend=True,3899 ):3900 """Creates a JavaScript area chart using "HighCharts".3901 @Params3902 chart_name - If creating multiple charts,3903 use this to select which one.3904 title - The title displayed for the chart.3905 subtitle - The subtitle displayed for the chart.3906 data_name - The series name. Useful for multi-series charts.3907 If no data_name, will default to using "Series 1".3908 unit - The description label given to the chart's y-axis values.3909 zero - If True, the y-axis always starts at 0. (Default: False).3910 libs - The option to include Chart libraries (JS and CSS files).3911 Should be set to True (default) for the first time creating3912 a chart on a web page. If creating multiple charts on the3913 same web page, you won't need to re-import the libraries3914 when creating additional charts.3915 labels - If True, displays labels on the chart for data points.3916 legend - If True, displays the data point legend on the chart.3917 """3918 if not chart_name:3919 chart_name = "default"3920 if not data_name:3921 data_name = ""3922 style = "area"3923 self.__create_high_chart(3924 chart_name=chart_name,3925 title=title,3926 subtitle=subtitle,3927 style=style,3928 data_name=data_name,3929 unit=unit,3930 zero=zero,3931 libs=libs,3932 labels=labels,3933 legend=legend,3934 )3935 def __create_high_chart(3936 self,3937 chart_name=None,3938 title=None,3939 subtitle=None,3940 style=None,3941 data_name=None,3942 unit=None,3943 zero=False,3944 libs=True,3945 labels=True,3946 legend=True,3947 ):3948 """ Creates a JavaScript chart using the "HighCharts" library. """3949 if not chart_name:3950 chart_name = "default"3951 if not title:3952 title = ""3953 if not subtitle:3954 subtitle = ""3955 if not style:3956 style = "pie"3957 if not data_name:3958 data_name = "Series 1"3959 if not unit:3960 unit = "Values"3961 if labels:3962 labels = "true"3963 else:3964 labels = "false"3965 if legend:3966 legend = "true"3967 else:3968 legend = "false"3969 title = title.replace("'", "\\'")3970 subtitle = subtitle.replace("'", "\\'")3971 unit = unit.replace("'", "\\'")3972 self._chart_count += 13973 # If chart_libs format is changed, also change: save_presentation()3974 chart_libs = """3975 <script src="%s"></script>3976 <script src="%s"></script>3977 <script src="%s"></script>3978 <script src="%s"></script>3979 """ % (3980 constants.HighCharts.HC_JS,3981 constants.HighCharts.EXPORTING_JS,3982 constants.HighCharts.EXPORT_DATA_JS,3983 constants.HighCharts.ACCESSIBILITY_JS,3984 )3985 if not libs:3986 chart_libs = ""3987 chart_css = """3988 <style>3989 .highcharts-figure, .highcharts-data-table table {3990 min-width: 320px;3991 max-width: 660px;3992 margin: 1em auto;3993 }3994 .highcharts-data-table table {3995 font-family: Verdana, sans-serif;3996 border-collapse: collapse;3997 border: 1px solid #EBEBEB;3998 margin: 10px auto;3999 text-align: center;4000 width: 100%;4001 max-width: 500px;4002 }4003 .highcharts-data-table caption {4004 padding: 1em 0;4005 font-size: 1.2em;4006 color: #555;4007 }4008 .highcharts-data-table th {4009 font-weight: 600;4010 padding: 0.5em;4011 }4012 .highcharts-data-table td, .highcharts-data-table th,4013 .highcharts-data-table caption {4014 padding: 0.5em;4015 }4016 .highcharts-data-table thead tr,4017 .highcharts-data-table tr:nth-child(even) {4018 background: #f8f8f8;4019 }4020 .highcharts-data-table tr:hover {4021 background: #f1f7ff;4022 }4023 </style>4024 """4025 if not libs:4026 chart_css = ""4027 chart_description = ""4028 chart_figure = """4029 <figure class="highcharts-figure">4030 <div id="chartcontainer_num_%s"></div>4031 <p class="highcharts-description">%s</p>4032 </figure>4033 """ % (4034 self._chart_count,4035 chart_description,4036 )4037 min_zero = ""4038 if zero:4039 min_zero = "min: 0,"4040 chart_init_1 = """4041 <script>4042 // Build the chart4043 Highcharts.chart('chartcontainer_num_%s', {4044 credits: {4045 enabled: false4046 },4047 title: {4048 text: '%s'4049 },4050 subtitle: {4051 text: '%s'4052 },4053 xAxis: { },4054 yAxis: {4055 %s4056 title: {4057 text: '%s',4058 style: {4059 fontSize: '14px'4060 }4061 },4062 labels: {4063 useHTML: true,4064 style: {4065 fontSize: '14px'4066 }4067 }4068 },4069 chart: {4070 renderTo: 'statusChart',4071 plotBackgroundColor: null,4072 plotBorderWidth: null,4073 plotShadow: false,4074 type: '%s'4075 },4076 """ % (4077 self._chart_count,4078 title,4079 subtitle,4080 min_zero,4081 unit,4082 style,4083 )4084 # "{series.name}:"4085 point_format = (4086 r"<b>{point.y}</b><br />" r"<b>{point.percentage:.1f}%</b>"4087 )4088 if style != "pie":4089 point_format = r"<b>{point.y}</b>"4090 chart_init_2 = (4091 """4092 tooltip: {4093 enabled: true,4094 useHTML: true,4095 style: {4096 padding: '6px',4097 fontSize: '14px'4098 },4099 backgroundColor: {4100 linearGradient: {4101 x1: 0,4102 y1: 0,4103 x2: 0,4104 y2: 14105 },4106 stops: [4107 [0, 'rgba(255, 255, 255, 0.78)'],4108 [0.5, 'rgba(235, 235, 235, 0.76)'],4109 [1, 'rgba(244, 252, 255, 0.74)']4110 ]4111 },4112 hideDelay: 40,4113 pointFormat: '%s'4114 },4115 """4116 % point_format4117 )4118 chart_init_3 = """4119 accessibility: {4120 point: {4121 valueSuffix: '%%'4122 }4123 },4124 plotOptions: {4125 series: {4126 states: {4127 inactive: {4128 opacity: 0.854129 }4130 }4131 },4132 pie: {4133 size: "95%%",4134 allowPointSelect: true,4135 animation: false,4136 cursor: 'pointer',4137 dataLabels: {4138 enabled: %s,4139 formatter: function() {4140 if (this.y > 0) {4141 return this.point.name + ': ' + this.point.y4142 }4143 }4144 },4145 states: {4146 hover: {4147 enabled: true4148 }4149 },4150 showInLegend: %s4151 }4152 },4153 """ % (4154 labels,4155 legend,4156 )4157 if style != "pie":4158 chart_init_3 = """4159 allowPointSelect: true,4160 cursor: 'pointer',4161 legend: {4162 layout: 'vertical',4163 align: 'right',4164 verticalAlign: 'middle'4165 },4166 states: {4167 hover: {4168 enabled: true4169 }4170 },4171 plotOptions: {4172 series: {4173 dataLabels: {4174 enabled: %s4175 },4176 showInLegend: %s,4177 animation: false,4178 shadow: false,4179 lineWidth: 3,4180 fillOpacity: 0.5,4181 marker: {4182 enabled: true4183 }4184 }4185 },4186 """ % (4187 labels,4188 legend,4189 )4190 chart_init = chart_init_1 + chart_init_2 + chart_init_34191 color_by_point = "true"4192 if style != "pie":4193 color_by_point = "false"4194 series = """4195 series: [{4196 name: '%s',4197 colorByPoint: %s,4198 data: [4199 """ % (4200 data_name,4201 color_by_point,4202 )4203 new_chart = chart_libs + chart_css + chart_figure + chart_init + series4204 self._chart_data[chart_name] = []4205 self._chart_label[chart_name] = []4206 self._chart_data[chart_name].append(new_chart)4207 self._chart_first_series[chart_name] = True4208 self._chart_series_count[chart_name] = 14209 def add_series_to_chart(self, data_name=None, chart_name=None):4210 """Add a new data series to an existing chart.4211 This allows charts to have multiple data sets.4212 @Params4213 data_name - Set the series name. Useful for multi-series charts.4214 chart_name - If creating multiple charts,4215 use this to select which one.4216 """4217 if not chart_name:4218 chart_name = "default"4219 self._chart_series_count[chart_name] += 14220 if not data_name:4221 data_name = "Series %s" % self._chart_series_count[chart_name]4222 series = (4223 """4224 ]4225 },4226 {4227 name: '%s',4228 colorByPoint: false,4229 data: [4230 """4231 % data_name4232 )4233 self._chart_data[chart_name].append(series)4234 self._chart_first_series[chart_name] = False4235 def add_data_point(self, label, value, color=None, chart_name=None):4236 """Add a data point to a SeleniumBase-generated chart.4237 @Params4238 label - The label name for the data point.4239 value - The numeric value of the data point.4240 color - The HTML color of the data point.4241 Can be an RGB color. Eg: "#55ACDC".4242 Can also be a named color. Eg: "Teal".4243 chart_name - If creating multiple charts,4244 use this to select which one.4245 """4246 if not chart_name:4247 chart_name = "default"4248 if chart_name not in self._chart_data:4249 # Create a chart if it doesn't already exist4250 self.create_pie_chart(chart_name=chart_name)4251 if not value:4252 value = 04253 if not type(value) is int and not type(value) is float:4254 raise Exception('Expecting a numeric value for "value"!')4255 if not color:4256 color = ""4257 label = label.replace("'", "\\'")4258 color = color.replace("'", "\\'")4259 data_point = """4260 {4261 name: '%s',4262 y: %s,4263 color: '%s'4264 },4265 """ % (4266 label,4267 value,4268 color,4269 )4270 self._chart_data[chart_name].append(data_point)4271 if self._chart_first_series[chart_name]:4272 self._chart_label[chart_name].append(label)4273 def save_chart(self, chart_name=None, filename=None, folder=None):4274 """Saves a SeleniumBase-generated chart to a file for later use.4275 @Params4276 chart_name - If creating multiple charts at the same time,4277 use this to select the one you wish to use.4278 filename - The name of the HTML file that you wish to4279 save the chart to. (filename must end in ".html")4280 folder - The name of the folder where you wish to4281 save the HTML file. (Default: "./saved_charts/")4282 """4283 if not chart_name:4284 chart_name = "default"4285 if not filename:4286 filename = "my_chart.html"4287 if chart_name not in self._chart_data:4288 raise Exception("Chart {%s} does not exist!" % chart_name)4289 if not filename.endswith(".html"):4290 raise Exception('Chart file must end in ".html"!')4291 the_html = '<meta charset="utf-8">\n'4292 the_html += '<meta http-equiv="Content-Type" content="text/html">\n'4293 the_html += '<meta name="viewport" content="shrink-to-fit=no">\n'4294 for chart_data_point in self._chart_data[chart_name]:4295 the_html += chart_data_point4296 the_html += """4297 ]4298 }]4299 });4300 </script>4301 """4302 axis = "xAxis: {\n"4303 axis += " labels: {\n"4304 axis += " useHTML: true,\n"4305 axis += " style: {\n"4306 axis += " fontSize: '14px',\n"4307 axis += " },\n"4308 axis += " },\n"4309 axis += " categories: ["4310 for label in self._chart_label[chart_name]:4311 axis += "'%s'," % label4312 axis += "], crosshair: false},"4313 the_html = the_html.replace("xAxis: { },", axis)4314 if not folder:4315 saved_charts_folder = constants.Charts.SAVED_FOLDER4316 else:4317 saved_charts_folder = folder4318 if saved_charts_folder.endswith("/"):4319 saved_charts_folder = saved_charts_folder[:-1]4320 if not os.path.exists(saved_charts_folder):4321 try:4322 os.makedirs(saved_charts_folder)4323 except Exception:4324 pass4325 file_path = saved_charts_folder + "/" + filename4326 out_file = codecs.open(file_path, "w+", encoding="utf-8")4327 out_file.writelines(the_html)4328 out_file.close()4329 print("\n>>> [%s] was saved!" % file_path)4330 return file_path4331 def extract_chart(self, chart_name=None):4332 """Extracts the HTML from a SeleniumBase-generated chart.4333 @Params4334 chart_name - If creating multiple charts at the same time,4335 use this to select the one you wish to use.4336 """4337 if not chart_name:4338 chart_name = "default"4339 if chart_name not in self._chart_data:4340 raise Exception("Chart {%s} does not exist!" % chart_name)4341 the_html = ""4342 for chart_data_point in self._chart_data[chart_name]:4343 the_html += chart_data_point4344 the_html += """4345 ]4346 }]4347 });4348 </script>4349 """4350 axis = "xAxis: {\n"4351 axis += " labels: {\n"4352 axis += " useHTML: true,\n"4353 axis += " style: {\n"4354 axis += " fontSize: '14px',\n"4355 axis += " },\n"4356 axis += " },\n"4357 axis += " categories: ["4358 for label in self._chart_label[chart_name]:4359 axis += "'%s'," % label4360 axis += "], crosshair: false},"4361 the_html = the_html.replace("xAxis: { },", axis)4362 self._chart_xcount += 14363 the_html = the_html.replace(4364 "chartcontainer_num_", "chartcontainer_%s_" % self._chart_xcount4365 )4366 return the_html4367 ############4368 def create_tour(self, name=None, theme=None):4369 """Creates a tour for a website. By default, the Shepherd JavaScript4370 Library is used with the Shepherd "Light" / "Arrows" theme.4371 @Params4372 name - If creating multiple tours at the same time,4373 use this to select the tour you wish to add steps to.4374 theme - Sets the default theme for the tour.4375 Choose from "light"/"arrows", "dark", "default", "square",4376 and "square-dark". ("arrows" is used if None is selected.)4377 Alternatively, you may use a different JavaScript Library4378 as the theme. Those include "IntroJS", "DriverJS",4379 "Hopscotch", and "Bootstrap".4380 """4381 if not name:4382 name = "default"4383 if theme:4384 if theme.lower() == "bootstrap":4385 self.create_bootstrap_tour(name)4386 elif theme.lower() == "hopscotch":4387 self.create_hopscotch_tour(name)4388 elif theme.lower() == "intro":4389 self.create_intro_js_tour(name)4390 elif theme.lower() == "introjs":4391 self.create_intro_js_tour(name)4392 elif theme.lower() == "driver":4393 self.create_driver_js_tour(name)4394 elif theme.lower() == "driverjs":4395 self.create_driver_js_tour(name)4396 elif theme.lower() == "shepherd":4397 self.create_shepherd_tour(name, theme="light")4398 elif theme.lower() == "light":4399 self.create_shepherd_tour(name, theme="light")4400 elif theme.lower() == "dark":4401 self.create_shepherd_tour(name, theme="dark")4402 elif theme.lower() == "arrows":4403 self.create_shepherd_tour(name, theme="light")4404 elif theme.lower() == "square":4405 self.create_shepherd_tour(name, theme="square")4406 elif theme.lower() == "square-dark":4407 self.create_shepherd_tour(name, theme="square-dark")4408 elif theme.lower() == "default":4409 self.create_shepherd_tour(name, theme="default")4410 else:4411 self.create_shepherd_tour(name, theme)4412 else:4413 self.create_shepherd_tour(name, theme="light")4414 def create_shepherd_tour(self, name=None, theme=None):4415 """Creates a Shepherd JS website tour.4416 @Params4417 name - If creating multiple tours at the same time,4418 use this to select the tour you wish to add steps to.4419 theme - Sets the default theme for the tour.4420 Choose from "light"/"arrows", "dark", "default", "square",4421 and "square-dark". ("light" is used if None is selected.)4422 """4423 shepherd_theme = "shepherd-theme-arrows"4424 if theme:4425 if theme.lower() == "default":4426 shepherd_theme = "shepherd-theme-default"4427 elif theme.lower() == "dark":4428 shepherd_theme = "shepherd-theme-dark"4429 elif theme.lower() == "light":4430 shepherd_theme = "shepherd-theme-arrows"4431 elif theme.lower() == "arrows":4432 shepherd_theme = "shepherd-theme-arrows"4433 elif theme.lower() == "square":4434 shepherd_theme = "shepherd-theme-square"4435 elif theme.lower() == "square-dark":4436 shepherd_theme = "shepherd-theme-square-dark"4437 if not name:4438 name = "default"4439 new_tour = (4440 """4441 // Shepherd Tour4442 var tour = new Shepherd.Tour({4443 defaults: {4444 classes: '%s',4445 scrollTo: true4446 }4447 });4448 var allButtons = {4449 skip: {4450 text: "Skip",4451 action: tour.cancel,4452 classes: 'shepherd-button-secondary tour-button-left'4453 },4454 back: {4455 text: "Back",4456 action: tour.back,4457 classes: 'shepherd-button-secondary'4458 },4459 next: {4460 text: "Next",4461 action: tour.next,4462 classes: 'shepherd-button-primary tour-button-right'4463 },4464 };4465 var firstStepButtons = [allButtons.skip, allButtons.next];4466 var midTourButtons = [allButtons.back, allButtons.next];4467 """4468 % shepherd_theme4469 )4470 self._tour_steps[name] = []4471 self._tour_steps[name].append(new_tour)4472 def create_bootstrap_tour(self, name=None):4473 """Creates a Bootstrap tour for a website.4474 @Params4475 name - If creating multiple tours at the same time,4476 use this to select the tour you wish to add steps to.4477 """4478 if not name:4479 name = "default"4480 new_tour = """4481 // Bootstrap Tour4482 var tour = new Tour({4483 container: 'body',4484 animation: true,4485 keyboard: true,4486 orphan: true,4487 smartPlacement: true,4488 autoscroll: true,4489 backdrop: true,4490 backdropContainer: 'body',4491 backdropPadding: 3,4492 });4493 tour.addSteps([4494 """4495 self._tour_steps[name] = []4496 self._tour_steps[name].append(new_tour)4497 def create_driver_js_tour(self, name=None):4498 """Creates a DriverJS tour for a website.4499 @Params4500 name - If creating multiple tours at the same time,4501 use this to select the tour you wish to add steps to.4502 """4503 if not name:4504 name = "default"4505 new_tour = """4506 // DriverJS Tour4507 var tour = new Driver({4508 opacity: 0.24, // Background opacity (0: no popover / overlay)4509 padding: 6, // Distance of element from around the edges4510 allowClose: false, // Whether clicking on overlay should close4511 overlayClickNext: false, // Move to next step on overlay click4512 doneBtnText: 'Done', // Text that appears on the Done button4513 closeBtnText: 'Close', // Text appearing on the Close button4514 nextBtnText: 'Next', // Text that appears on the Next button4515 prevBtnText: 'Previous', // Text appearing on Previous button4516 showButtons: true, // This shows control buttons in the footer4517 keyboardControl: true, // (escape to close, arrow keys to move)4518 animate: true, // Animate while changing highlighted element4519 });4520 tour.defineSteps([4521 """4522 self._tour_steps[name] = []4523 self._tour_steps[name].append(new_tour)4524 def create_hopscotch_tour(self, name=None):4525 """Creates a Hopscotch tour for a website.4526 @Params4527 name - If creating multiple tours at the same time,4528 use this to select the tour you wish to add steps to.4529 """4530 if not name:4531 name = "default"4532 new_tour = """4533 // Hopscotch Tour4534 var tour = {4535 id: "hopscotch_tour",4536 steps: [4537 """4538 self._tour_steps[name] = []4539 self._tour_steps[name].append(new_tour)4540 def create_intro_js_tour(self, name=None):4541 """Creates an IntroJS tour for a website.4542 @Params4543 name - If creating multiple tours at the same time,4544 use this to select the tour you wish to add steps to.4545 """4546 if not name:4547 name = "default"4548 new_tour = """4549 // IntroJS Tour4550 function startIntro(){4551 var intro = introJs();4552 intro.setOptions({4553 steps: [4554 """4555 self._tour_steps[name] = []4556 self._tour_steps[name].append(new_tour)4557 def add_tour_step(4558 self,4559 message,4560 selector=None,4561 name=None,4562 title=None,4563 theme=None,4564 alignment=None,4565 duration=None,4566 ):4567 """Allows the user to add tour steps for a website.4568 @Params4569 message - The message to display.4570 selector - The CSS Selector of the Element to attach to.4571 name - If creating multiple tours at the same time,4572 use this to select the tour you wish to add steps to.4573 title - Additional header text that appears above the message.4574 theme - (Shepherd Tours ONLY) The styling of the tour step.4575 Choose from "light"/"arrows", "dark", "default", "square",4576 and "square-dark". ("arrows" is used if None is selected.)4577 alignment - Choose from "top", "bottom", "left", and "right".4578 ("top" is default, except for Hopscotch and DriverJS).4579 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,4580 before automatically advancing to the next tour step.4581 """4582 if not selector:4583 selector = "html"4584 if page_utils.is_name_selector(selector):4585 name = page_utils.get_name_from_selector(selector)4586 selector = '[name="%s"]' % name4587 if page_utils.is_xpath_selector(selector):4588 selector = self.convert_to_css_selector()4589 selector = self.__escape_quotes_if_needed(selector)4590 if not name:4591 name = "default"4592 if name not in self._tour_steps:4593 # By default, will create an IntroJS tour if no tours exist4594 self.create_tour(name=name, theme="introjs")4595 if not title:4596 title = ""4597 title = self.__escape_quotes_if_needed(title)4598 if message:4599 message = self.__escape_quotes_if_needed(message)4600 else:4601 message = ""4602 if not alignment or alignment not in [4603 "top",4604 "bottom",4605 "left",4606 "right",4607 ]:4608 t_name = self._tour_steps[name][0]4609 if "Hopscotch" not in t_name and "DriverJS" not in t_name:4610 alignment = "top"4611 else:4612 alignment = "bottom"4613 if "Bootstrap" in self._tour_steps[name][0]:4614 self.__add_bootstrap_tour_step(4615 message,4616 selector=selector,4617 name=name,4618 title=title,4619 alignment=alignment,4620 duration=duration,4621 )4622 elif "DriverJS" in self._tour_steps[name][0]:4623 self.__add_driver_js_tour_step(4624 message,4625 selector=selector,4626 name=name,4627 title=title,4628 alignment=alignment,4629 )4630 elif "Hopscotch" in self._tour_steps[name][0]:4631 self.__add_hopscotch_tour_step(4632 message,4633 selector=selector,4634 name=name,4635 title=title,4636 alignment=alignment,4637 )4638 elif "IntroJS" in self._tour_steps[name][0]:4639 self.__add_intro_js_tour_step(4640 message,4641 selector=selector,4642 name=name,4643 title=title,4644 alignment=alignment,4645 )4646 else:4647 self.__add_shepherd_tour_step(4648 message,4649 selector=selector,4650 name=name,4651 title=title,4652 theme=theme,4653 alignment=alignment,4654 )4655 def __add_shepherd_tour_step(4656 self,4657 message,4658 selector=None,4659 name=None,4660 title=None,4661 theme=None,4662 alignment=None,4663 ):4664 """Allows the user to add tour steps for a website.4665 @Params4666 message - The message to display.4667 selector - The CSS Selector of the Element to attach to.4668 name - If creating multiple tours at the same time,4669 use this to select the tour you wish to add steps to.4670 title - Additional header text that appears above the message.4671 theme - (Shepherd Tours ONLY) The styling of the tour step.4672 Choose from "light"/"arrows", "dark", "default", "square",4673 and "square-dark". ("arrows" is used if None is selected.)4674 alignment - Choose from "top", "bottom", "left", and "right".4675 ("top" is the default alignment).4676 """4677 if theme == "default":4678 shepherd_theme = "shepherd-theme-default"4679 elif theme == "dark":4680 shepherd_theme = "shepherd-theme-dark"4681 elif theme == "light":4682 shepherd_theme = "shepherd-theme-arrows"4683 elif theme == "arrows":4684 shepherd_theme = "shepherd-theme-arrows"4685 elif theme == "square":4686 shepherd_theme = "shepherd-theme-square"4687 elif theme == "square-dark":4688 shepherd_theme = "shepherd-theme-square-dark"4689 else:4690 shepherd_base_theme = re.search(4691 r"[\S\s]+classes: '([\S\s]+)',[\S\s]+",4692 self._tour_steps[name][0],4693 ).group(1)4694 shepherd_theme = shepherd_base_theme4695 shepherd_classes = shepherd_theme4696 if selector == "html":4697 shepherd_classes += " shepherd-orphan"4698 buttons = "firstStepButtons"4699 if len(self._tour_steps[name]) > 1:4700 buttons = "midTourButtons"4701 step = """tour.addStep('%s', {4702 title: '%s',4703 classes: '%s',4704 text: '%s',4705 attachTo: {element: '%s', on: '%s'},4706 buttons: %s,4707 advanceOn: '.docs-link click'4708 });""" % (4709 name,4710 title,4711 shepherd_classes,4712 message,4713 selector,4714 alignment,4715 buttons,4716 )4717 self._tour_steps[name].append(step)4718 def __add_bootstrap_tour_step(4719 self,4720 message,4721 selector=None,4722 name=None,4723 title=None,4724 alignment=None,4725 duration=None,4726 ):4727 """Allows the user to add tour steps for a website.4728 @Params4729 message - The message to display.4730 selector - The CSS Selector of the Element to attach to.4731 name - If creating multiple tours at the same time,4732 use this to select the tour you wish to add steps to.4733 title - Additional header text that appears above the message.4734 alignment - Choose from "top", "bottom", "left", and "right".4735 ("top" is the default alignment).4736 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,4737 before automatically advancing to the next tour step.4738 """4739 if selector != "html":4740 selector = self.__make_css_match_first_element_only(selector)4741 element_row = "element: '%s'," % selector4742 else:4743 element_row = ""4744 if not duration:4745 duration = "0"4746 else:4747 duration = str(float(duration) * 1000.0)4748 bd = "backdrop: true,"4749 if selector == "html":4750 bd = "backdrop: false,"4751 step = """{4752 %s4753 title: '%s',4754 content: '%s',4755 orphan: true,4756 autoscroll: true,4757 %s4758 placement: 'auto %s',4759 smartPlacement: true,4760 duration: %s,4761 },""" % (4762 element_row,4763 title,4764 message,4765 bd,4766 alignment,4767 duration,4768 )4769 self._tour_steps[name].append(step)4770 def __add_driver_js_tour_step(4771 self, message, selector=None, name=None, title=None, alignment=None4772 ):4773 """Allows the user to add tour steps for a website.4774 @Params4775 message - The message to display.4776 selector - The CSS Selector of the Element to attach to.4777 name - If creating multiple tours at the same time,4778 use this to select the tour you wish to add steps to.4779 title - Additional header text that appears above the message.4780 alignment - Choose from "top", "bottom", "left", and "right".4781 ("top" is the default alignment).4782 """4783 message = (4784 '<font size="3" color="#33477B"><b>' + message + "</b></font>"4785 )4786 title_row = ""4787 if not title:4788 title_row = "title: '%s'," % message4789 message = ""4790 else:4791 title_row = "title: '%s'," % title4792 align_row = "position: '%s'," % alignment4793 ani_row = "animate: true,"4794 if not selector or selector == "html" or selector == "body":4795 selector = "body"4796 ani_row = "animate: false,"4797 align_row = "position: '%s'," % "mid-center"4798 element_row = "element: '%s'," % selector4799 desc_row = "description: '%s'," % message4800 step = """{4801 %s4802 %s4803 popover: {4804 className: 'popover-class',4805 %s4806 %s4807 %s4808 }4809 },""" % (4810 element_row,4811 ani_row,4812 title_row,4813 desc_row,4814 align_row,4815 )4816 self._tour_steps[name].append(step)4817 def __add_hopscotch_tour_step(4818 self, message, selector=None, name=None, title=None, alignment=None4819 ):4820 """Allows the user to add tour steps for a website.4821 @Params4822 message - The message to display.4823 selector - The CSS Selector of the Element to attach to.4824 name - If creating multiple tours at the same time,4825 use this to select the tour you wish to add steps to.4826 title - Additional header text that appears above the message.4827 alignment - Choose from "top", "bottom", "left", and "right".4828 ("bottom" is the default alignment).4829 """4830 arrow_offset_row = None4831 if not selector or selector == "html":4832 selector = "head"4833 alignment = "bottom"4834 arrow_offset_row = "arrowOffset: '200',"4835 else:4836 arrow_offset_row = ""4837 step = """{4838 target: '%s',4839 title: '%s',4840 content: '%s',4841 %s4842 showPrevButton: 'true',4843 scrollDuration: '550',4844 placement: '%s'},4845 """ % (4846 selector,4847 title,4848 message,4849 arrow_offset_row,4850 alignment,4851 )4852 self._tour_steps[name].append(step)4853 def __add_intro_js_tour_step(4854 self, message, selector=None, name=None, title=None, alignment=None4855 ):4856 """Allows the user to add tour steps for a website.4857 @Params4858 message - The message to display.4859 selector - The CSS Selector of the Element to attach to.4860 name - If creating multiple tours at the same time,4861 use this to select the tour you wish to add steps to.4862 title - Additional header text that appears above the message.4863 alignment - Choose from "top", "bottom", "left", and "right".4864 ("top" is the default alignment).4865 """4866 if selector != "html":4867 element_row = "element: '%s'," % selector4868 else:4869 element_row = ""4870 if title:4871 message = "<center><b>" + title + "</b></center><hr>" + message4872 message = '<font size="3" color="#33477B">' + message + "</font>"4873 step = """{%s4874 intro: '%s',4875 position: '%s'},""" % (4876 element_row,4877 message,4878 alignment,4879 )4880 self._tour_steps[name].append(step)4881 def export_tour(self, name=None, filename="my_tour.js", url=None):4882 """Exports a tour as a JS file.4883 You can call self.export_tour() anywhere where you would4884 normally use self.play_tour() to play a website tour.4885 It will include necessary resources as well, such as jQuery.4886 You'll be able to copy the tour directly into the Console of4887 any web browser to play the tour outside of SeleniumBase runs.4888 @Params4889 name - If creating multiple tours at the same time,4890 use this to select the tour you wish to add steps to.4891 filename - The name of the JavaScript file that you wish to4892 save the tour to.4893 url - The URL where the tour starts. If not specified, the URL4894 of the current page will be used.4895 """4896 from seleniumbase.core import tour_helper4897 if not url:4898 url = self.get_current_url()4899 tour_helper.export_tour(4900 self._tour_steps, name=name, filename=filename, url=url4901 )4902 def activate_jquery_confirm(self):4903 """ See https://craftpip.github.io/jquery-confirm/ for usage. """4904 self.__check_scope()4905 js_utils.activate_jquery_confirm(self.driver)4906 self.wait_for_ready_state_complete()4907 def activate_messenger(self):4908 """4909 activate_messenger4910 """4911 self.__check_scope()4912 js_utils.activate_messenger(self.driver)4913 self.wait_for_ready_state_complete()4914 def set_messenger_theme(4915 self, theme="default", location="default", max_messages="default"4916 ):4917 """Sets a theme for posting messages.4918 Themes: ["flat", "future", "block", "air", "ice"]4919 Locations: ["top_left", "top_center", "top_right",4920 "bottom_left", "bottom_center", "bottom_right"]4921 max_messages is the limit of concurrent messages to display.4922 """4923 self.__check_scope()4924 if not theme:4925 theme = "default" # "flat"4926 if not location:4927 location = "default" # "bottom_right"4928 if not max_messages:4929 max_messages = "default" # "8"4930 else:4931 max_messages = str(max_messages) # Value must be in string format4932 js_utils.set_messenger_theme(4933 self.driver,4934 theme=theme,4935 location=location,4936 max_messages=max_messages,4937 )4938 def post_message_and_highlight(4939 self, message, selector, by=By.CSS_SELECTOR4940 ):4941 """Post a message on the screen and highlight an element.4942 Arguments:4943 message: The message to display.4944 selector: The selector of the Element to highlight.4945 by: The type of selector to search by. (Default: CSS Selector)4946 """4947 self.__check_scope()4948 self.__highlight_with_assert_success(message, selector, by=by)4949 ############4950 def is_link_text_visible(self, link_text):4951 """4952 is_link_text_visible4953 """4954 self.wait_for_ready_state_complete()4955 time.sleep(0.01)4956 return page_actions.is_element_visible(4957 self.driver, link_text, by=By.LINK_TEXT4958 )4959 def is_partial_link_text_visible(self, partial_link_text):4960 """4961 is_partial_link_text_visible4962 """4963 self.wait_for_ready_state_complete()4964 time.sleep(0.01)4965 return page_actions.is_element_visible(4966 self.driver, partial_link_text, by=By.PARTIAL_LINK_TEXT4967 )4968 def is_link_text_present(self, link_text):4969 """Returns True if the link text appears in the HTML of the page.4970 The element doesn't need to be visible,4971 such as elements hidden inside a dropdown selection."""4972 self.wait_for_ready_state_complete()4973 soup = self.get_beautiful_soup()4974 html_links = soup.find_all("a")4975 for html_link in html_links:4976 if html_link.text.strip() == link_text.strip():4977 return True4978 return False4979 def is_partial_link_text_present(self, link_text):4980 """Returns True if the partial link appears in the HTML of the page.4981 The element doesn't need to be visible,4982 such as elements hidden inside a dropdown selection."""4983 self.wait_for_ready_state_complete()4984 soup = self.get_beautiful_soup()4985 html_links = soup.find_all("a")4986 for html_link in html_links:4987 if link_text.strip() in html_link.text.strip():4988 return True4989 return False4990 def get_link_attribute(self, link_text, attribute, hard_fail=True):4991 """Finds a link by link text and then returns the attribute's value.4992 If the link text or attribute cannot be found, an exception will4993 get raised if hard_fail is True (otherwise None is returned)."""4994 self.wait_for_ready_state_complete()4995 soup = self.get_beautiful_soup()4996 html_links = soup.find_all("a")4997 for html_link in html_links:4998 if html_link.text.strip() == link_text.strip():4999 if html_link.has_attr(attribute):5000 attribute_value = html_link.get(attribute)5001 return attribute_value5002 if hard_fail:5003 raise Exception(5004 "Unable to find attribute {%s} from link text {%s}!"5005 % (attribute, link_text)5006 )5007 else:5008 return None5009 if hard_fail:5010 raise Exception("Link text {%s} was not found!" % link_text)5011 else:5012 return None5013 def get_link_text_attribute(self, link_text, attribute, hard_fail=True):5014 """Same as self.get_link_attribute()5015 Finds a link by link text and then returns the attribute's value.5016 If the link text or attribute cannot be found, an exception will5017 get raised if hard_fail is True (otherwise None is returned)."""5018 return self.get_link_attribute(link_text, attribute, hard_fail)5019 def get_partial_link_text_attribute(5020 self, link_text, attribute, hard_fail=True5021 ):5022 """Finds a link by partial link text and then returns the attribute's5023 value. If the partial link text or attribute cannot be found, an5024 exception will get raised if hard_fail is True (otherwise None5025 is returned)."""5026 self.wait_for_ready_state_complete()5027 soup = self.get_beautiful_soup()5028 html_links = soup.find_all("a")5029 for html_link in html_links:5030 if link_text.strip() in html_link.text.strip():5031 if html_link.has_attr(attribute):5032 attribute_value = html_link.get(attribute)5033 return attribute_value5034 if hard_fail:5035 raise Exception(5036 "Unable to find attribute {%s} from "5037 "partial link text {%s}!" % (attribute, link_text)5038 )5039 else:5040 return None5041 if hard_fail:5042 raise Exception(5043 "Partial Link text {%s} was not found!" % link_text5044 )5045 else:5046 return None5047 def click_link_text(self, link_text, timeout=None):5048 """ This method clicks link text on a page """5049 # If using phantomjs, might need to extract and open the link directly5050 global link_css5051 self.__check_scope()5052 if not timeout:5053 timeout = settings.SMALL_TIMEOUT5054 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5055 timeout = self.__get_new_timeout(timeout)5056 if self.browser == "phantomjs":5057 if self.is_link_text_visible(link_text):5058 element = self.wait_for_link_text_visible(link_text, timeout=timeout)5059 element.click()5060 return5061 self.open(self.__get_href_from_link_text(link_text))5062 return5063 if self.browser == "safari":5064 self.__jquery_click(link_text, by=By.LINK_TEXT)5065 return5066 if not self.is_link_text_present(link_text):5067 self.wait_for_link_text_present(link_text, timeout=timeout)5068 pre_action_url = self.get_current_url()5069 try:5070 element = self.wait_for_link_text_visible(link_text, timeout=0.2)5071 try:5072 element.click()5073 except (StaleElementReferenceException, ENI_Exception):5074 self.wait_for_ready_state_complete()5075 time.sleep(0.16)5076 element = self.wait_for_link_text_visible(5077 link_text, timeout=timeout5078 )5079 element.click()5080 except Exception:5081 found_css = False5082 text_id = self.get_link_attribute(link_text, "id", False)5083 if text_id:5084 link_css = '[id="%s"]' % link_text5085 found_css = True5086 if not found_css:5087 href = self.__get_href_from_link_text(link_text, False)5088 if href:5089 if href.startswith("/") or page_utils.is_valid_url(href):5090 link_css = '[href="%s"]' % href5091 found_css = True5092 if not found_css:5093 ngclick = self.get_link_attribute(link_text, "ng-click", False)5094 if ngclick:5095 link_css = '[ng-click="%s"]' % ngclick5096 found_css = True5097 if not found_css:5098 onclick = self.get_link_attribute(link_text, "onclick", False)5099 if onclick:5100 link_css = '[onclick="%s"]' % onclick5101 found_css = True5102 success = False5103 if found_css:5104 if self.is_element_visible(link_css):5105 self.click(link_css)5106 success = True5107 else:5108 # The link text might be hidden under a dropdown menu5109 success = self.__click_dropdown_link_text(5110 link_text, link_css5111 )5112 if not success:5113 element = self.wait_for_link_text_visible(5114 link_text, timeout=settings.MINI_TIMEOUT5115 )5116 element.click()5117 if settings.WAIT_FOR_RSC_ON_CLICKS:5118 self.wait_for_ready_state_complete()5119 def click_partial_link_text(self, partial_link_text, timeout=None):5120 """ This method clicks the partial link text on a page. """5121 # If using phantomjs, might need to extract and open the link directly5122 global link_css5123 self.__check_scope()5124 if not timeout:5125 timeout = settings.SMALL_TIMEOUT5126 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5127 timeout = self.__get_new_timeout(timeout)5128 if self.browser == "phantomjs":5129 if self.is_partial_link_text_visible(partial_link_text):5130 element = self.wait_for_partial_link_text(partial_link_text)5131 element.click()5132 return5133 soup = self.get_beautiful_soup()5134 html_links = soup.fetch("a")5135 for html_link in html_links:5136 if partial_link_text in html_link.text:5137 for html_attribute in html_link.attrs:5138 if html_attribute[0] == "href":5139 href = html_attribute[1]5140 if href.startswith("//"):5141 link = "http:" + href5142 elif href.startswith("/"):5143 url = self.driver.current_url5144 domain_url = self.get_domain_url(url)5145 link = domain_url + href5146 else:5147 link = href5148 self.open(link)5149 return5150 raise Exception(5151 "Could not parse link from partial link_text "5152 "{%s}" % partial_link_text5153 )5154 raise Exception(5155 "Partial link text {%s} was not found!" % partial_link_text5156 )5157 if not self.is_partial_link_text_present(partial_link_text):5158 self.wait_for_partial_link_text_present(5159 partial_link_text, timeout=timeout5160 )5161 pre_action_url = self.get_current_url()5162 try:5163 element = self.wait_for_partial_link_text(5164 partial_link_text, timeout=0.25165 )5166 try:5167 element.click()5168 except (StaleElementReferenceException, ENI_Exception):5169 self.wait_for_ready_state_complete()5170 time.sleep(0.16)5171 element = self.wait_for_partial_link_text(5172 partial_link_text, timeout=timeout5173 )5174 element.click()5175 except Exception:5176 found_css = False5177 text_id = self.get_partial_link_text_attribute(5178 partial_link_text, "id", False5179 )5180 if text_id:5181 link_css = '[id="%s"]' % partial_link_text5182 found_css = True5183 if not found_css:5184 href = self.__get_href_from_partial_link_text(5185 partial_link_text, False5186 )5187 if href:5188 if href.startswith("/") or page_utils.is_valid_url(href):5189 link_css = '[href="%s"]' % href5190 found_css = True5191 if not found_css:5192 ngclick = self.get_partial_link_text_attribute(5193 partial_link_text, "ng-click", False5194 )5195 if ngclick:5196 link_css = '[ng-click="%s"]' % ngclick5197 found_css = True5198 if not found_css:5199 onclick = self.get_partial_link_text_attribute(5200 partial_link_text, "onclick", False5201 )5202 if onclick:5203 link_css = '[onclick="%s"]' % onclick5204 found_css = True5205 success = False5206 if found_css:5207 if self.is_element_visible(link_css):5208 self.click(link_css)5209 success = True5210 else:5211 # The link text might be hidden under a dropdown menu5212 success = self.__click_dropdown_partial_link_text(5213 partial_link_text, link_css5214 )5215 if not success:5216 element = self.wait_for_partial_link_text(5217 partial_link_text, timeout=settings.MINI_TIMEOUT5218 )5219 element.click()5220 if settings.WAIT_FOR_RSC_ON_CLICKS:5221 self.wait_for_ready_state_complete()5222 ############5223 def wait_for_element_present(self, timeout=None):5224 """Waits for an element to appear in the HTML of a page.5225 The element does not need be visible (it may be hidden)."""5226 self.__check_scope()5227 if not timeout:5228 timeout = settings.LARGE_TIMEOUT5229 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5230 timeout = self.__get_new_timeout(timeout)5231 selector, by = self.__recalculate_selector()5232 return page_actions.wait_for_element_present(5233 self.driver, selector, by, timeout5234 )5235 def wait_for_element(self, selector=None, by=By.CSS_SELECTOR, timeout=None):5236 """Waits for an element to appear in the HTML of a page.5237 The element must be visible (it cannot be hidden)."""5238 self.__check_scope()5239 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5240 if not timeout:5241 timeout = settings.LARGE_TIMEOUT5242 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5243 timeout = self.__get_new_timeout(timeout)5244 selector, by = self.__recalculate_selector(selector, by)5245 return page_actions.wait_for_element_visible(5246 self.driver, selector, by, timeout5247 )5248 def get_element(self, timeout=None):5249 """Same as wait_for_element_present() - returns the element.5250 The element does not need be visible (it may be hidden)."""5251 self.__check_scope()5252 if not timeout:5253 timeout = settings.SMALL_TIMEOUT5254 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5255 timeout = self.__get_new_timeout(timeout)5256 return self.wait_for_element_present(timeout=timeout)5257 def assert_element_present(self, timeout=None):5258 """Similar to wait_for_element_present(), but returns nothing.5259 Waits for an element to appear in the HTML of a page.5260 The element does not need be visible (it may be hidden).5261 Returns True if successful. Default timeout = SMALL_TIMEOUT."""5262 self.__check_scope()5263 selector = self.selector5264 by = self.by5265 if not timeout:5266 timeout = settings.SMALL_TIMEOUT5267 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5268 timeout = self.__get_new_timeout(timeout)5269 if type(selector) is list:5270 self.assert_elements_present(selector, by=by, timeout=timeout)5271 return True5272 self.wait_for_element_present(timeout=timeout)5273 return True5274 def assert_elements_present(self, *args, **kwargs):5275 """Similar to self.assert_element_present(),5276 but can assert that multiple elements are present in the HTML.5277 The input is a list of elements.5278 Optional kwargs include "by" and "timeout" (used by all selectors).5279 Raises an exception if any of the elements are not visible.5280 Examples:5281 self.assert_elements_present("head", "style", "script", "body")5282 OR5283 self.assert_elements_present(["head", "body", "h1", "h2"])5284 """5285 self.__check_scope()5286 selectors = []5287 timeout = None5288 by = By.CSS_SELECTOR5289 for kwarg in kwargs:5290 if kwarg == "timeout":5291 timeout = kwargs["timeout"]5292 elif kwarg == "by":5293 by = kwargs["by"]5294 elif kwarg == "selector":5295 selector = kwargs["selector"]5296 if type(selector) is str:5297 selectors.append(selector)5298 elif type(selector) is list:5299 for a_selector in selector:5300 if type(a_selector) is str:5301 selectors.append(a_selector)5302 else:5303 raise Exception('Unknown kwarg: "%s"!' % kwarg)5304 if not timeout:5305 timeout = settings.SMALL_TIMEOUT5306 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5307 timeout = self.__get_new_timeout(timeout)5308 for arg in args:5309 if type(arg) is list:5310 for selector in arg:5311 if type(selector) is str:5312 selectors.append(selector)5313 elif type(arg) is str:5314 selectors.append(arg)5315 for selector in selectors:5316 self.wait_for_element_present(timeout=timeout)5317 continue5318 return True5319 def find_element(self, selector=None, by=By.CSS_SELECTOR, timeout=None):5320 """ Same as wait_for_element_visible() - returns the element """5321 self.__check_scope()5322 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5323 if not timeout:5324 timeout = settings.LARGE_TIMEOUT5325 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5326 timeout = self.__get_new_timeout(timeout)5327 return self.wait_for_element_visible(selector, by=by, timeout=timeout)5328 def assert_element(self, selector=None, by=By.CSS_SELECTOR, timeout=None):5329 """Similar to wait_for_element_visible(), but returns nothing.5330 As above, will raise an exception if nothing can be found.5331 Returns True if successful. Default timeout = SMALL_TIMEOUT."""5332 self.__check_scope()5333 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5334 if not timeout:5335 timeout = settings.SMALL_TIMEOUT5336 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5337 timeout = self.__get_new_timeout(timeout)5338 if type(selector) is list:5339 self.assert_elements(selector, by=by, timeout=timeout)5340 return True5341 self.wait_for_element_visible(selector, by=by, timeout=timeout)5342 return True5343 def assert_element_visible(5344 self, selector=None, by=By.CSS_SELECTOR, timeout=None5345 ):5346 """Same as self.assert_element()5347 As above, will raise an exception if nothing can be found."""5348 self.__check_scope()5349 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5350 if not timeout:5351 timeout = settings.SMALL_TIMEOUT5352 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5353 timeout = self.__get_new_timeout(timeout)5354 self.assert_element(selector, by=by, timeout=timeout)5355 return True5356 def assert_elements(self, *args, **kwargs):5357 """Similar to self.assert_element(), but can assert multiple elements.5358 The input is a list of elements.5359 Optional kwargs include "by" and "timeout" (used by all selectors).5360 Raises an exception if any of the elements are not visible.5361 Examples:5362 self.assert_elements("h1", "h2", "h3")5363 OR5364 self.assert_elements(["h1", "h2", "h3"])"""5365 self.__check_scope()5366 selectors = []5367 timeout = None5368 by = By.CSS_SELECTOR5369 for kwarg in kwargs:5370 if kwarg == "timeout":5371 timeout = kwargs["timeout"]5372 elif kwarg == "by":5373 by = kwargs["by"]5374 elif kwarg == "selector":5375 selector = kwargs["selector"]5376 if type(selector) is str:5377 selectors.append(selector)5378 elif type(selector) is list:5379 for a_selector in selector:5380 if type(a_selector) is str:5381 selectors.append(a_selector)5382 else:5383 raise Exception('Unknown kwarg: "%s"!' % kwarg)5384 if not timeout:5385 timeout = settings.SMALL_TIMEOUT5386 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5387 timeout = self.__get_new_timeout(timeout)5388 for arg in args:5389 if type(arg) is list:5390 for selector in arg:5391 if type(selector) is str:5392 selectors.append(selector)5393 elif type(arg) is str:5394 selectors.append(arg)5395 for selector in selectors:5396 self.wait_for_element_visible(timeout=timeout)5397 continue5398 return True5399 def assert_elements_visible(self, *args, **kwargs):5400 """Same as self.assert_elements()5401 Raises an exception if any element cannot be found."""5402 return self.assert_elements(*args, **kwargs)5403 ############5404 def wait_for_text_visible(5405 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5406 ):5407 """5408 wait_for_text_visible5409 """5410 self.__check_scope()5411 if not timeout:5412 timeout = settings.LARGE_TIMEOUT5413 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5414 timeout = self.__get_new_timeout(timeout)5415 selector, by = self.__recalculate_selector(selector, by)5416 return page_actions.wait_for_text_visible(5417 self.driver, text, selector, by, timeout5418 )5419 def wait_for_exact_text_visible(5420 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5421 ):5422 """5423 wait_for_exact_text_visible5424 """5425 self.__check_scope()5426 if not timeout:5427 timeout = settings.LARGE_TIMEOUT5428 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5429 timeout = self.__get_new_timeout(timeout)5430 selector, by = self.__recalculate_selector(selector, by)5431 return page_actions.wait_for_exact_text_visible(5432 self.driver, text, selector, by, timeout5433 )5434 def wait_for_text(5435 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5436 ):5437 """ The shorter version of wait_for_text_visible() """5438 self.__check_scope()5439 if not timeout:5440 timeout = settings.LARGE_TIMEOUT5441 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5442 timeout = self.__get_new_timeout(timeout)5443 return self.wait_for_text_visible(5444 text, selector, by=by, timeout=timeout5445 )5446 def find_text(5447 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5448 ):5449 """ Same as wait_for_text_visible() - returns the element """5450 self.__check_scope()5451 if not timeout:5452 timeout = settings.LARGE_TIMEOUT5453 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5454 timeout = self.__get_new_timeout(timeout)5455 return self.wait_for_text_visible(5456 text, selector, by=by, timeout=timeout5457 )5458 def assert_text_visible(5459 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5460 ):5461 """ Same as assert_text() """5462 self.__check_scope()5463 if not timeout:5464 timeout = settings.SMALL_TIMEOUT5465 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5466 timeout = self.__get_new_timeout(timeout)5467 return self.assert_text(text, selector, by=by, timeout=timeout)5468 def assert_text(5469 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5470 ):5471 """Similar to wait_for_text_visible()5472 Raises an exception if the element or the text is not found.5473 Returns True if successful. Default timeout = SMALL_TIMEOUT."""5474 self.__check_scope()5475 if not timeout:5476 timeout = settings.SMALL_TIMEOUT5477 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5478 timeout = self.__get_new_timeout(timeout)5479 selector, by = self.__recalculate_selector(selector, by)5480 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)5481 return True5482 def assert_exact_text(5483 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5484 ):5485 """Similar to assert_text(), but the text must be exact, rather than5486 exist as a subset of the full text.5487 (Extra whitespace at the beginning or the end doesn't count.)5488 Raises an exception if the element or the text is not found.5489 Returns True if successful. Default timeout = SMALL_TIMEOUT."""5490 self.__check_scope()5491 if not timeout:5492 timeout = settings.SMALL_TIMEOUT5493 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5494 timeout = self.__get_new_timeout(timeout)5495 selector, by = self.__recalculate_selector(selector, by)5496 self.wait_for_exact_text_visible(5497 text, selector, by=by, timeout=timeout5498 )5499 return True5500 def wait_for_element_absent(5501 self, selector=None, by=By.CSS_SELECTOR, timeout=None5502 ):5503 """Waits for an element to no longer appear in the HTML of a page.5504 A hidden element still counts as appearing in the page HTML.5505 If an element with "hidden" status is acceptable,5506 use wait_for_element_not_visible() instead."""5507 self.__check_scope()5508 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5509 if not timeout:5510 timeout = settings.LARGE_TIMEOUT5511 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5512 timeout = self.__get_new_timeout(timeout)5513 selector, by = self.__recalculate_selector(selector, by)5514 return page_actions.wait_for_element_absent(5515 self.driver, selector, by, timeout5516 )5517 def assert_element_absent(5518 self, selector=None, by=By.CSS_SELECTOR, timeout=None5519 ):5520 """Similar to wait_for_element_absent() - returns nothing.5521 As above, will raise an exception if the element stays present.5522 Returns True if successful. Default timeout = SMALL_TIMEOUT."""5523 self.__check_scope()5524 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5525 if not timeout:5526 timeout = settings.SMALL_TIMEOUT5527 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5528 timeout = self.__get_new_timeout(timeout)5529 self.wait_for_element_absent(selector, by=by, timeout=timeout)5530 return True5531 ############5532 def wait_for_element_not_visible(5533 self, selector=None, by=By.CSS_SELECTOR, timeout=None5534 ):5535 """Waits for an element to no longer be visible on a page.5536 The element can be non-existent in the HTML or hidden on the page5537 to qualify as not visible."""5538 self.__check_scope()5539 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5540 if not timeout:5541 timeout = settings.LARGE_TIMEOUT5542 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5543 timeout = self.__get_new_timeout(timeout)5544 selector, by = self.__recalculate_selector(selector, by)5545 return page_actions.wait_for_element_not_visible(5546 self.driver, selector, by, timeout5547 )5548 def assert_element_not_visible(5549 self, selector=None, by=By.CSS_SELECTOR, timeout=None5550 ):5551 """Similar to wait_for_element_not_visible() - returns nothing.5552 As above, will raise an exception if the element stays visible.5553 Returns True if successful. Default timeout = SMALL_TIMEOUT."""5554 self.__check_scope()5555 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5556 if not timeout:5557 timeout = settings.SMALL_TIMEOUT5558 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5559 timeout = self.__get_new_timeout(timeout)5560 self.wait_for_element_not_visible(selector, by=by, timeout=timeout)5561 return True5562 ############5563 def wait_for_text_not_visible(5564 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5565 ):5566 """5567 wait_for_text_not_visible5568 """5569 self.__check_scope()5570 if not timeout:5571 timeout = settings.LARGE_TIMEOUT5572 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5573 timeout = self.__get_new_timeout(timeout)5574 selector, by = self.__recalculate_selector(selector, by)5575 return page_actions.wait_for_text_not_visible(5576 self.driver, text, selector, by, timeout5577 )5578 def assert_text_not_visible(5579 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None5580 ):5581 """Similar to wait_for_text_not_visible()5582 Raises an exception if the element or the text is not found.5583 Returns True if successful. Default timeout = SMALL_TIMEOUT."""5584 self.__check_scope()5585 if not timeout:5586 timeout = settings.SMALL_TIMEOUT5587 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5588 timeout = self.__get_new_timeout(timeout)5589 return self.wait_for_text_not_visible(5590 text, selector, by=by, timeout=timeout5591 )5592 ############5593 def wait_for_and_accept_alert(self, timeout=None):5594 """5595 wait_for_and_dismiss_alert5596 """5597 self.__check_scope()5598 if not timeout:5599 timeout = settings.LARGE_TIMEOUT5600 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5601 timeout = self.__get_new_timeout(timeout)5602 return page_actions.wait_for_and_accept_alert(self.driver, timeout)5603 def wait_for_and_dismiss_alert(self, timeout=None):5604 """5605 wait_for_and_dismiss_alert5606 """5607 self.__check_scope()5608 if not timeout:5609 timeout = settings.LARGE_TIMEOUT5610 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5611 timeout = self.__get_new_timeout(timeout)5612 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)5613 def wait_for_and_switch_to_alert(self, timeout=None):5614 """5615 wait_for_and_switch_to_alert5616 """5617 self.__check_scope()5618 if not timeout:5619 timeout = settings.LARGE_TIMEOUT5620 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5621 timeout = self.__get_new_timeout(timeout)5622 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)5623 ############5624 def accept_alert(self, timeout=None):5625 """ Same as wait_for_and_accept_alert(), but smaller default T_O """5626 self.__check_scope()5627 if not timeout:5628 timeout = settings.SMALL_TIMEOUT5629 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5630 timeout = self.__get_new_timeout(timeout)5631 return page_actions.wait_for_and_accept_alert(self.driver, timeout)5632 def dismiss_alert(self, timeout=None):5633 """ Same as wait_for_and_dismiss_alert(), but smaller default T_O """5634 self.__check_scope()5635 if not timeout:5636 timeout = settings.SMALL_TIMEOUT5637 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5638 timeout = self.__get_new_timeout(timeout)5639 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)5640 def switch_to_alert(self, timeout=None):5641 """ Same as wait_for_and_switch_to_alert(), but smaller default T_O """5642 self.__check_scope()5643 if not timeout:5644 timeout = settings.SMALL_TIMEOUT5645 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5646 timeout = self.__get_new_timeout(timeout)5647 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)5648 ############5649 # def check_window(self, name="default", level=0, baseline=False):5650 # """*** Automated Visual Testing with SeleniumBase ***5651 #5652 # The first time a test calls self.check_window() for a unique "name"5653 # parameter provided, it will set a visual baseline, meaning that it5654 # creates a folder, saves the URL to a file, saves the current window5655 # screenshot to a file, and creates the following three files5656 # with the listed data saved:5657 # tags_level1.txt -> HTML tags from the window5658 # tags_level2.txt -> HTML tags + attributes from the window5659 # tags_level3.txt -> HTML tags + attributes/values from the window5660 #5661 # Baseline folders are named based on the test name and the name5662 # parameter passed to self.check_window(). The same test can store5663 # multiple baseline folders.5664 #5665 # If the baseline is being set/reset, the "level" doesn't matter.5666 #5667 # After the first run of self.check_window(), it will compare the5668 # HTML tags of the latest window to the one from the initial run.5669 # Here's how the level system works:5670 # * level=0 ->5671 # DRY RUN ONLY - Will perform a comparison to the baseline, and5672 # print out any differences that are found, but5673 # won't fail the test even if differences exist.5674 # * level=1 ->5675 # HTML tags are compared to tags_level1.txt5676 # * level=2 ->5677 # HTML tags are compared to tags_level1.txt and5678 # HTML tags/attributes are compared to tags_level2.txt5679 # * level=3 ->5680 # HTML tags are compared to tags_level1.txt and5681 # HTML tags + attributes are compared to tags_level2.txt and5682 # HTML tags + attributes/values are compared to tags_level3.txt5683 # As shown, Level-3 is the most strict, Level-1 is the least strict.5684 # If the comparisons from the latest window to the existing baseline5685 # don't match, the current test will fail, except for Level-0 tests.5686 #5687 # You can reset the visual baseline on the command line by using:5688 # --visual_baseline5689 # As long as "--visual_baseline" is used on the command line while5690 # running tests, the self.check_window() method cannot fail because5691 # it will rebuild the visual baseline rather than comparing the html5692 # tags of the latest run to the existing baseline. If there are any5693 # expected layout changes to a website that you're testing, you'll5694 # need to reset the baseline to prevent unnecessary failures.5695 #5696 # self.check_window() will fail with "Page Domain Mismatch Failure"5697 # if the page domain doesn't match the domain of the baseline.5698 #5699 # If you want to use self.check_window() to compare a web page to5700 # a later version of itself from within the same test run, you can5701 # add the parameter "baseline=True" to the first time you call5702 # self.check_window() in a test to use that as the baseline. This5703 # only makes sense if you're calling self.check_window() more than5704 # once with the same name parameter in the same test.5705 #5706 # Automated Visual Testing with self.check_window() is not very5707 # effective for websites that have dynamic content that changes5708 # the layout and structure of web pages. For those, you're much5709 # better off using regular SeleniumBase functional testing.5710 #5711 # Example usage:5712 # self.check_window(name="testing", level=0)5713 # self.check_window(name="xkcd_home", level=1)5714 # self.check_window(name="github_page", level=2)5715 # self.check_window(name="wikipedia_page", level=3)5716 # """5717 # self.wait_for_ready_state_complete()5718 # if level == "0":5719 # level = 05720 # if level == "1":5721 # level = 15722 # if level == "2":5723 # level = 25724 # if level == "3":5725 # level = 35726 # if level != 0 and level != 1 and level != 2 and level != 3:5727 # raise Exception('Parameter "level" must be set to 0, 1, 2, or 3!')5728 #5729 # if self.demo_mode:5730 # message = (5731 # "WARNING: Using check_window() from Demo Mode may lead "5732 # "to unexpected results caused by Demo Mode HTML changes."5733 # )5734 # logging.info(message)5735 #5736 # test_id = self.__get_display_id().split("::")[-1]5737 #5738 # if not name or len(name) < 1:5739 # name = "default"5740 # name = str(name)5741 # from seleniumbase.core import visual_helper5742 #5743 # visual_helper.visual_baseline_folder_setup()5744 # baseline_dir = constants.VisualBaseline.STORAGE_FOLDER5745 # visual_baseline_path = baseline_dir + "/" + test_id + "/" + name5746 # page_url_file = visual_baseline_path + "/page_url.txt"5747 # screenshot_file = visual_baseline_path + "/screenshot.png"5748 # level_1_file = visual_baseline_path + "/tags_level_1.txt"5749 # level_2_file = visual_baseline_path + "/tags_level_2.txt"5750 # level_3_file = visual_baseline_path + "/tags_level_3.txt"5751 #5752 # set_baseline = False5753 # if baseline or self.visual_baseline:5754 # set_baseline = True5755 # if not os.path.exists(visual_baseline_path):5756 # set_baseline = True5757 # try:5758 # os.makedirs(visual_baseline_path)5759 # except Exception:5760 # pass # Only reachable during multi-threaded test runs5761 # if not os.path.exists(page_url_file):5762 # set_baseline = True5763 # if not os.path.exists(screenshot_file):5764 # set_baseline = True5765 # if not os.path.exists(level_1_file):5766 # set_baseline = True5767 # if not os.path.exists(level_2_file):5768 # set_baseline = True5769 # if not os.path.exists(level_3_file):5770 # set_baseline = True5771 #5772 # page_url = self.get_current_url()5773 # soup = self.get_beautiful_soup()5774 # html_tags = soup.body.find_all()5775 # level_1 = [[tag.name] for tag in html_tags]5776 # level_1 = json.loads(json.dumps(level_1)) # Tuples become lists5777 # level_2 = [[tag.name, sorted(tag.attrs.keys())] for tag in html_tags]5778 # level_2 = json.loads(json.dumps(level_2)) # Tuples become lists5779 # level_3 = [[tag.name, sorted(tag.attrs.items())] for tag in html_tags]5780 # level_3 = json.loads(json.dumps(level_3)) # Tuples become lists5781 #5782 # if set_baseline:5783 # self.save_screenshot("screenshot.png", visual_baseline_path)5784 # out_file = codecs.open(page_url_file, "w+", encoding="utf-8")5785 # out_file.writelines(page_url)5786 # out_file.close()5787 # out_file = codecs.open(level_1_file, "w+", encoding="utf-8")5788 # out_file.writelines(json.dumps(level_1))5789 # out_file.close()5790 # out_file = codecs.open(level_2_file, "w+", encoding="utf-8")5791 # out_file.writelines(json.dumps(level_2))5792 # out_file.close()5793 # out_file = codecs.open(level_3_file, "w+", encoding="utf-8")5794 # out_file.writelines(json.dumps(level_3))5795 # out_file.close()5796 #5797 # if not set_baseline:5798 # f = open(page_url_file, "r")5799 # page_url_data = f.read().strip()5800 # f.close()5801 # f = open(level_1_file, "r")5802 # level_1_data = json.loads(f.read())5803 # f.close()5804 # f = open(level_2_file, "r")5805 # level_2_data = json.loads(f.read())5806 # f.close()5807 # f = open(level_3_file, "r")5808 # level_3_data = json.loads(f.read())5809 # f.close()5810 #5811 # domain_fail = (5812 # "\nPage Domain Mismatch Failure: "5813 # "Current Page Domain doesn't match the Page Domain of the "5814 # "Baseline! Can't compare two completely different sites! "5815 # "Run with --visual_baseline to reset the baseline!"5816 # )5817 # level_1_failure = (5818 # "\n*\n*** Exception: <Level 1> Visual Diff Failure:\n"5819 # "* HTML tags don't match the baseline!"5820 # )5821 # level_2_failure = (5822 # "\n*\n*** Exception: <Level 2> Visual Diff Failure:\n"5823 # "* HTML tag attribute names don't match the baseline!"5824 # )5825 # level_3_failure = (5826 # "\n*\n*** Exception: <Level 3> Visual Diff Failure:\n"5827 # "* HTML tag attribute values don't match the baseline!"5828 # )5829 #5830 # page_domain = self.get_domain_url(page_url)5831 # page_data_domain = self.get_domain_url(page_url_data)5832 # unittest.TestCase.maxDiff = 10005833 # if level != 0:5834 # self.assertEqual(page_data_domain, page_domain, domain_fail)5835 # unittest.TestCase.maxDiff = None5836 # if level == 3:5837 # self.__assert_eq(level_3_data, level_3, level_3_failure)5838 # if level == 2:5839 # self.__assert_eq(level_2_data, level_2, level_2_failure)5840 # unittest.TestCase.maxDiff = 10005841 # if level == 1:5842 # self.__assert_eq(level_1_data, level_1, level_1_failure)5843 # unittest.TestCase.maxDiff = None5844 # if level == 0:5845 # try:5846 # unittest.TestCase.maxDiff = 10005847 # self.assertEqual(5848 # page_domain, page_data_domain, domain_fail5849 # )5850 # unittest.TestCase.maxDiff = None5851 # self.__assert_eq(level_3_data, level_3, level_3_failure)5852 # except Exception as e:5853 # print(e) # Level-0 Dry Run (Only print the differences)5854 ############5855 def __get_new_timeout(self, timeout):5856 """ When using --timeout_multiplier=#.# """5857 import math5858 self.__check_scope()5859 try:5860 timeout_multiplier = float(self.timeout_multiplier)5861 if timeout_multiplier <= 0.5:5862 timeout_multiplier = 0.55863 timeout = int(math.ceil(timeout_multiplier * timeout))5864 return timeout5865 except Exception as e:5866 logging.info(e)5867 # Wrong data type for timeout_multiplier (expecting int or float)5868 return timeout5869 ############5870 def __check_scope(self):5871 if hasattr(self, "browser"): # self.browser stores the type of browser5872 return # All good: setUp() already initialized variables in "self"5873 else:5874 from seleniumbase.common.exceptions import OutOfScopeException5875 message = (5876 "\n It looks like you are trying to call a SeleniumBase method"5877 "\n from outside the scope of your test class's `self` object,"5878 "\n which is initialized by calling BaseCase's setUp() method."5879 "\n The `self` object is where all test variables are defined."5880 "\n If you created a custom setUp() method (that overrided the"5881 "\n the default one), make sure to call super().setUp() in it."5882 "\n When using page objects, be sure to pass the `self` object"5883 "\n from your test class into your page object methods so that"5884 "\n they can call BaseCase class methods with all the required"5885 "\n variables, which are initialized during the setUp() method"5886 "\n that runs automatically before all tests called by pytest."5887 )5888 raise OutOfScopeException(message)5889 ############5890 @staticmethod5891 def __get_exception_message():5892 """This method extracts the message from an exception if there5893 was an exception that occurred during the test, assuming5894 that the exception was in a try/except block and not thrown."""5895 exception_info = sys.exc_info()[1]5896 if hasattr(exception_info, "msg"):5897 exc_message = exception_info.msg5898 elif hasattr(exception_info, "message"):5899 exc_message = exception_info.message5900 else:5901 exc_message = sys.exc_info()5902 return exc_message5903 def __get_improved_exception_message(self):5904 """If Chromedriver is out-of-date, make it clear!5905 Given the high popularity of the following StackOverflow article:5906 https://stackoverflow.com/questions/49162667/unknown-error-5907 call-function-result-missing-value-for-selenium-send-keys-even5908 ... the original error message was not helpful. Tell people directly.5909 (Only expected when using driver.send_keys() with an old Chromedriver.)5910 """5911 exc_message = self.__get_exception_message()5912 maybe_using_old_chromedriver = False5913 if "unknown error: call function result missing" in exc_message:5914 maybe_using_old_chromedriver = True5915 if self.browser == "chrome" and maybe_using_old_chromedriver:5916 update = (5917 "Your version of ChromeDriver may be out-of-date! "5918 "Please go to "5919 "https://sites.google.com/a/chromium.org/chromedriver/ "5920 "and download the latest version to your system PATH! "5921 "Or use: ``seleniumbase install chromedriver`` . "5922 "Original Exception Message: %s" % exc_message5923 )5924 exc_message = update5925 return exc_message5926 def __add_deferred_assert_failure(self):5927 """ Add a deferred_assert failure to a list for future processing. """5928 self.__check_scope()5929 current_url = self.driver.current_url5930 message = self.__get_exception_message()5931 self.__deferred_assert_failures.append(5932 "CHECK #%s: (%s)\n %s"5933 % (self.__deferred_assert_count, current_url, message)5934 )5935 ############5936 def deferred_assert_element(5937 self, selector=None, by=By.CSS_SELECTOR, timeout=None5938 ):5939 """A non-terminating assertion for an element on a page.5940 Failures will be saved until the process_deferred_asserts()5941 method is called from inside a test, likely at the end of it."""5942 self.__check_scope()5943 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5944 if not timeout:5945 timeout = settings.MINI_TIMEOUT5946 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:5947 timeout = self.__get_new_timeout(timeout)5948 self.__deferred_assert_count += 15949 try:5950 url = self.get_current_url()5951 if url == self.__last_url_of_deferred_assert:5952 timeout = 15953 else:5954 self.__last_url_of_deferred_assert = url5955 except Exception:5956 pass5957 try:5958 self.wait_for_element_visible(selector, by=by, timeout=timeout)5959 return True5960 except Exception:5961 self.__add_deferred_assert_failure()5962 return False5963 def deferred_assert_text(5964 self, text=None, selector="html", by=By.CSS_SELECTOR, timeout=None5965 ):5966 """A non-terminating assertion for text from an element on a page.5967 Failures will be saved until the process_deferred_asserts()5968 method is called from inside a test, likely at the end of it."""5969 self.__check_scope()5970 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5971 if not timeout:5972 timeout = settings.MINI_TIMEOUT5973 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:5974 timeout = self.__get_new_timeout(timeout)5975 self.__deferred_assert_count += 15976 try:5977 url = self.get_current_url()5978 if url == self.__last_url_of_deferred_assert:5979 timeout = 15980 else:5981 self.__last_url_of_deferred_assert = url5982 except Exception:5983 pass5984 try:5985 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)5986 return True5987 except Exception:5988 self.__add_deferred_assert_failure()5989 return False5990 ############5991 # Alternate naming scheme for the "deferred_assert" methods.5992 def delayed_assert_element(5993 self, selector=None, by=By.CSS_SELECTOR, timeout=None5994 ):5995 """ Same as self.deferred_assert_element() """5996 (selector, by) = (self.selector, self.by) if selector is None else (selector, by)5997 return self.deferred_assert_element(5998 selector=selector, by=by, timeout=timeout5999 )6000 def delayed_assert_text(6001 self, text, selector="html", by=By.CSS_SELECTOR, timeout=None6002 ):6003 """ Same as self.deferred_assert_text() """6004 return self.deferred_assert_text(6005 text=text, selector=selector, by=by, timeout=timeout6006 )6007 ############6008 def execute_script(self, script, *args, **kwargs):6009 """6010 execute_script6011 """6012 self.__check_scope()6013 return self.driver.execute_script(script, *args, **kwargs)6014 def safe_execute_script(self, script, *args, **kwargs):6015 """When executing a script that contains a jQuery command,6016 it's important that the jQuery library has been loaded first.6017 This method will load jQuery if it wasn't already loaded."""6018 self.__check_scope()6019 if not js_utils.is_jquery_activated(self.driver):6020 self.activate_jquery()6021 return self.driver.execute_script(script, *args, **kwargs)6022 ############6023 def __js_click(self, selector, by=By.CSS_SELECTOR):6024 """ Clicks an element using pure JS. Does not use jQuery. """6025 selector, by = self.__recalculate_selector(selector, by)6026 css_selector = self.convert_to_css_selector(selector, by=by)6027 css_selector = re.escape(css_selector) # Add "\\" to special chars6028 css_selector = self.__escape_quotes_if_needed(css_selector)6029 script = (6030 """var simulateClick = function (elem) {6031 var evt = new MouseEvent('click', {6032 bubbles: true,6033 cancelable: true,6034 view: window6035 });6036 var canceled = !elem.dispatchEvent(evt);6037 };6038 var someLink = document.querySelector('%s');6039 simulateClick(someLink);"""6040 % css_selector6041 )6042 self.execute_script(script)6043 def __js_click_all(self, selector, by=By.CSS_SELECTOR):6044 """ Clicks all matching elements using pure JS. (No jQuery) """6045 selector, by = self.__recalculate_selector(selector, by)6046 css_selector = self.convert_to_css_selector(selector, by=by)6047 css_selector = re.escape(css_selector) # Add "\\" to special chars6048 css_selector = self.__escape_quotes_if_needed(css_selector)6049 script = (6050 """var simulateClick = function (elem) {6051 var evt = new MouseEvent('click', {6052 bubbles: true,6053 cancelable: true,6054 view: window6055 });6056 var canceled = !elem.dispatchEvent(evt);6057 };6058 var $elements = document.querySelectorAll('%s');6059 var index = 0, length = $elements.length;6060 for(; index < length; index++){6061 simulateClick($elements[index]);}"""6062 % css_selector6063 )6064 self.execute_script(script)6065 def __jquery_slow_scroll_to(self, selector, by=By.CSS_SELECTOR):6066 selector, by = self.__recalculate_selector(selector, by)6067 element = self.wait_for_element_present(timeout=settings.SMALL_TIMEOUT)6068 dist = js_utils.get_scroll_distance_to_element(self.driver, element)6069 time_offset = 06070 try:6071 if dist and abs(dist) > constants.Values.SSMD:6072 time_offset = int(6073 float(abs(dist) - constants.Values.SSMD) / 12.56074 )6075 if time_offset > 950:6076 time_offset = 9506077 except Exception:6078 time_offset = 06079 scroll_time_ms = 550 + time_offset6080 sleep_time = 0.625 + (float(time_offset) / 1000.0)6081 selector = self.convert_to_css_selector(selector, by=by)6082 selector = self.__make_css_match_first_element_only(selector)6083 scroll_script = (6084 """jQuery([document.documentElement, document.body]).animate({"""6085 """scrollTop: jQuery('%s').offset().top - 130}, %s);"""6086 % (selector, scroll_time_ms)6087 )6088 if js_utils.is_jquery_activated(self.driver):6089 self.execute_script(scroll_script)6090 else:6091 self.__slow_scroll_to_element(element)6092 self.sleep(sleep_time)6093 def __jquery_click(self, selector, by=By.CSS_SELECTOR):6094 """ Clicks an element using jQuery. Different from using pure JS. """6095 selector, by = self.__recalculate_selector(selector, by)6096 self.wait_for_element_present(timeout=settings.SMALL_TIMEOUT)6097 selector = self.convert_to_css_selector(selector, by=by)6098 selector = self.__make_css_match_first_element_only(selector)6099 click_script = """jQuery('%s')[0].click();""" % selector6100 self.safe_execute_script(click_script)6101 def __get_href_from_link_text(self, link_text, hard_fail=True):6102 href = self.get_link_attribute(link_text, "href", hard_fail)6103 if not href:6104 return None6105 if href.startswith("//"):6106 link = "http:" + href6107 elif href.startswith("/"):6108 url = self.driver.current_url6109 domain_url = self.get_domain_url(url)6110 link = domain_url + href6111 else:6112 link = href6113 return link6114 def __click_dropdown_link_text(self, link_text, link_css):6115 """ When a link may be hidden under a dropdown menu, use this. """6116 soup = self.get_beautiful_soup()6117 drop_down_list = []6118 for item in soup.select("li[class]"):6119 drop_down_list.append(item)6120 csstype = link_css.split("[")[1].split("=")[0]6121 for item in drop_down_list:6122 item_text_list = item.text.split("\n")6123 if link_text in item_text_list and csstype in item.decode():6124 dropdown_css = ""6125 try:6126 for css_class in item["class"]:6127 dropdown_css += "."6128 dropdown_css += css_class6129 except Exception:6130 continue6131 dropdown_css = item.name + dropdown_css6132 matching_dropdowns = self.find_visible_elements(dropdown_css)6133 for dropdown in matching_dropdowns:6134 # The same class names might be used for multiple dropdowns6135 if dropdown.is_displayed():6136 try:6137 try:6138 page_actions.hover_element(6139 self.driver,6140 dropdown,6141 )6142 except Exception:6143 # If hovering fails, driver is likely outdated6144 # Time to go directly to the hidden link text6145 self.open(6146 self.__get_href_from_link_text(link_text)6147 )6148 return True6149 page_actions.hover_element_and_click(6150 self.driver,6151 dropdown,6152 link_text,6153 click_by=By.LINK_TEXT,6154 timeout=0.12,6155 )6156 return True6157 except Exception:6158 pass6159 return False6160 def __get_href_from_partial_link_text(self, link_text, hard_fail=True):6161 href = self.get_partial_link_text_attribute(6162 link_text, "href", hard_fail6163 )6164 if not href:6165 return None6166 if href.startswith("//"):6167 link = "http:" + href6168 elif href.startswith("/"):6169 url = self.driver.current_url6170 domain_url = self.get_domain_url(url)6171 link = domain_url + href6172 else:6173 link = href6174 return link6175 def __click_dropdown_partial_link_text(self, link_text, link_css):6176 """ When a partial link may be hidden under a dropdown, use this. """6177 soup = self.get_beautiful_soup()6178 drop_down_list = []6179 for item in soup.select("li[class]"):6180 drop_down_list.append(item)6181 csstype = link_css.split("[")[1].split("=")[0]6182 for item in drop_down_list:6183 item_text_list = item.text.split("\n")6184 if link_text in item_text_list and csstype in item.decode():6185 dropdown_css = ""6186 try:6187 for css_class in item["class"]:6188 dropdown_css += "."6189 dropdown_css += css_class6190 except Exception:6191 continue6192 dropdown_css = item.name + dropdown_css6193 matching_dropdowns = self.find_visible_elements(dropdown_css)6194 for dropdown in matching_dropdowns:6195 # The same class names might be used for multiple dropdowns6196 if dropdown.is_displayed():6197 try:6198 try:6199 page_actions.hover_element(6200 self.driver, dropdown6201 )6202 except Exception:6203 # If hovering fails, driver is likely outdated6204 # Time to go directly to the hidden link text6205 self.open(6206 self.__get_href_from_partial_link_text(6207 link_text6208 )6209 )6210 return True6211 page_actions.hover_element_and_click(6212 self.driver,6213 dropdown,6214 link_text,6215 click_by=By.LINK_TEXT,6216 timeout=0.12,6217 )6218 return True6219 except Exception:6220 pass6221 return False6222 def __recalculate_selector(self, selector=None, by=None, xp_ok=True):6223 """Use autodetection to return the correct selector with "by" updated.6224 If "xp_ok" is False, don't call convert_css_to_xpath(), which is6225 used to make the ":contains()" selector valid outside JS calls."""6226 if selector is None or by is None:6227 selector = self.selector6228 by = self.by6229 _type = type(selector) # First make sure the selector is a string6230 not_string = False6231 if sys.version_info[0] < 3:6232 if _type is not str and _type is not unicode: # noqa: F8216233 not_string = True6234 else:6235 if _type is not str:6236 not_string = True6237 if not_string:6238 msg = "Expecting a selector of type: \"<class 'str'>\" (string)!"6239 raise Exception('Invalid selector type: "%s"\n%s' % (_type, msg))6240 if page_utils.is_xpath_selector(selector):6241 by = By.XPATH6242 if page_utils.is_link_text_selector(selector):6243 selector = page_utils.get_link_text_from_selector(selector)6244 by = By.LINK_TEXT6245 if page_utils.is_partial_link_text_selector(selector):6246 selector = page_utils.get_partial_link_text_from_selector(selector)6247 by = By.PARTIAL_LINK_TEXT6248 if page_utils.is_name_selector(selector):6249 name = page_utils.get_name_from_selector(selector)6250 selector = '[name="%s"]' % name6251 by = By.CSS_SELECTOR6252 if xp_ok:6253 if ":contains(" in selector and by == By.CSS_SELECTOR:6254 selector = self.convert_css_to_xpath(selector)6255 by = By.XPATH6256 return selector, by6257 @staticmethod6258 def __looks_like_a_page_url(url):6259 """Returns True if the url parameter looks like a URL. This method6260 is slightly more lenient than page_utils.is_valid_url(url) due to6261 possible typos when calling self.get(url), which will try to6262 navigate to the page if a URL is detected, but will instead call6263 self.get_element(URL_AS_A_SELECTOR) if the input in not a URL."""6264 if (6265 url.startswith("http:")6266 or url.startswith("https:")6267 or url.startswith("://")6268 or url.startswith("chrome:")6269 or url.startswith("about:")6270 or url.startswith("data:")6271 or url.startswith("file:")6272 or url.startswith("edge:")6273 or url.startswith("opera:")6274 ):6275 return True6276 else:6277 return False6278 @staticmethod6279 def __make_css_match_first_element_only(selector):6280 """6281 __make_css_match_first_element_only6282 """6283 # Only get the first match6284 return page_utils.make_css_match_first_element_only(selector)6285 def __scroll_to_element(self, element, selector=None, by=By.CSS_SELECTOR):6286 """6287 __scroll_to_element6288 """6289 success = js_utils.scroll_to_element(self.driver, element)6290 if not success and selector:6291 self.wait_for_ready_state_complete()6292 element = page_actions.wait_for_element_visible(6293 self.driver, selector, by, timeout=settings.SMALL_TIMEOUT6294 )6295 def __slow_scroll_to_element(self, element):6296 try:6297 js_utils.slow_scroll_to_element(self.driver, element, self.browser)6298 except Exception as e:6299 logging.info(e)6300 # Scroll to the element instantly if the slow scroll fails6301 js_utils.scroll_to_element(self.driver, element)6302 def __highlight_with_assert_success(6303 self, message, selector, by=By.CSS_SELECTOR6304 ):6305 selector, by = self.__recalculate_selector(selector, by)6306 element = self.wait_for_element_visible(6307 selector, by=by, timeout=settings.SMALL_TIMEOUT6308 )6309 try:6310 scroll_distance = js_utils.get_scroll_distance_to_element(6311 self.driver, element6312 )6313 if abs(scroll_distance) > constants.Values.SSMD:6314 self.__jquery_slow_scroll_to(selector, by)6315 else:6316 self.__slow_scroll_to_element(element)6317 except Exception:6318 self.wait_for_ready_state_complete()6319 time.sleep(0.12)6320 element = self.wait_for_element_visible(6321 selector, by=by, timeout=settings.SMALL_TIMEOUT6322 )6323 self.__slow_scroll_to_element(element)6324 try:6325 selector = self.convert_to_css_selector(selector, by=by)6326 except Exception:6327 # Don't highlight if can't convert to CSS_SELECTOR6328 return6329 o_bs = "" # original_box_shadow6330 try:6331 style = element.get_attribute("style")6332 except Exception:6333 self.wait_for_ready_state_complete()6334 time.sleep(0.12)6335 element = self.wait_for_element_visible(6336 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT6337 )6338 style = element.get_attribute("style")6339 if style:6340 if "box-shadow: " in style:6341 box_start = style.find("box-shadow: ")6342 box_end = style.find(";", box_start) + 16343 original_box_shadow = style[box_start:box_end]6344 o_bs = original_box_shadow6345 if ":contains" not in selector and ":first" not in selector:6346 selector = re.escape(selector)6347 selector = self.__escape_quotes_if_needed(selector)6348 self.__highlight_with_js_2(message, selector, o_bs)6349 else:6350 selector = self.__make_css_match_first_element_only(selector)6351 selector = re.escape(selector)6352 selector = self.__escape_quotes_if_needed(selector)6353 try:6354 self.__highlight_with_jquery_2(message, selector, o_bs)6355 except Exception:6356 pass # JQuery probably couldn't load. Skip highlighting.6357 time.sleep(0.065)6358 def __highlight_with_js_2(self, message, selector, o_bs):6359 js_utils.highlight_with_js_2(6360 self.driver, message, selector, o_bs, self.message_duration6361 )6362 def __highlight_with_jquery_2(self, message, selector, o_bs):6363 js_utils.highlight_with_jquery_2(6364 self.driver, message, selector, o_bs, self.message_duration6365 )6366 def open(self, url):6367 """ Navigates the current browser window to the specified page. """6368 self.__check_scope()6369 if type(url) is str:6370 url = url.strip() # Remove leading and trailing whitespace6371 if (type(url) is not str) or not self.__looks_like_a_page_url(url):6372 # url should start with one of the following:6373 # "http:", "https:", "://", "data:", "file:",6374 # "about:", "chrome:", "opera:", or "edge:".6375 msg = 'Did you forget to prefix your URL with "http:" or "https:"?'6376 raise Exception('Invalid URL: "%s"\n%s' % (url, msg))6377 self.__last_page_load_url = None6378 js_utils.clear_out_console_logs(self.driver)6379 if url.startswith("://"):6380 # Convert URLs such as "://google.com" into "https://google.com"6381 url = "https" + url6382 if self.browser == "safari" and url.startswith("data:"):6383 url = re.escape(url)6384 url = self.__escape_quotes_if_needed(url)6385 self.execute_script("window.location.href='%s';" % url)6386 else:6387 self.driver.get(url)6388 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:6389 self.wait_for_ready_state_complete()6390 def open_new_window(self, switch_to=True):6391 """ Opens a new browser tab/window and switches to it by default. """6392 self.__check_scope()6393 self.driver.execute_script("window.open('');")6394 time.sleep(0.01)6395 if switch_to:6396 self.switch_to_newest_window()6397 time.sleep(0.01)6398 if self.browser == "safari":6399 self.wait_for_ready_state_complete()6400 def switch_to_window(self, window, timeout=None):6401 """6402 switch_to_window6403 """6404 self.__check_scope()6405 if not timeout:6406 timeout = settings.SMALL_TIMEOUT6407 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:6408 timeout = self.__get_new_timeout(timeout)6409 page_actions.switch_to_window(self.driver, window, timeout)6410 def wait_for_ready_state_complete(self, timeout=None):6411 """6412 wait_for_ready_state_complete6413 """6414 self.__check_scope()6415 if not timeout:6416 timeout = settings.EXTREME_TIMEOUT6417 if self.timeout_multiplier and timeout == settings.EXTREME_TIMEOUT:6418 timeout = self.__get_new_timeout(timeout)6419 is_ready = js_utils.wait_for_ready_state_complete(self.driver, timeout)6420 self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)6421 if self.js_checking_on:6422 self.assert_no_js_errors()6423 if self.ad_block_on:6424 # If the ad_block feature is enabled, then block ads for new URLs6425 current_url = self.get_current_url()6426 if not current_url == self.__last_page_load_url:6427 time.sleep(0.02)6428 self.ad_block()6429 time.sleep(0.02)6430 if self.is_element_present():6431 time.sleep(0.1) # iframe ads take slightly longer to load6432 self.ad_block() # Do ad_block on slower-loading iframes6433 self.__last_page_load_url = current_url6434 return is_ready6435 @staticmethod6436 def __escape_quotes_if_needed(string):6437 """6438 __escape_quotes_if_needed6439 """6440 return js_utils.escape_quotes_if_needed(string)6441 def get_beautiful_soup(self, source=None):6442 """BeautifulSoup is a toolkit for dissecting an HTML document6443 and extracting what you need. It's great for screen-scraping!6444 See: https://www.crummy.com/software/BeautifulSoup/bs4/doc/6445 """6446 from bs4 import BeautifulSoup6447 if not source:6448 source = self.get_page_source()6449 soup = BeautifulSoup(source, "html.parser")6450 return soup6451 def switch_to_frame(self, frame, timeout=None):6452 """Wait for an iframe to appear, and switch to it. This should be6453 usable as a drop-in replacement for driver.switch_to.frame().6454 The iframe identifier can be a selector, an index, an id, a name,6455 or a web element, but scrolling to the iframe first will only occur6456 for visible iframes with a string selector.6457 @Params6458 frame - the frame element, name, id, index, or selector6459 timeout - the time to wait for the alert in seconds6460 """6461 self.__check_scope()6462 if not timeout:6463 timeout = settings.SMALL_TIMEOUT6464 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:6465 timeout = self.__get_new_timeout(timeout)6466 if type(frame) is str and self.is_element_visible(frame):6467 try:6468 self.scroll_to(frame, timeout=1)6469 except Exception:6470 pass6471 page_actions.switch_to_frame(self.driver, frame, timeout)6472 def switch_to_default_content(self):6473 """Brings driver control outside the current iframe.6474 (If driver control is inside an iframe, the driver control6475 will be set to one level above the current frame. If the driver6476 control is not currently in an iframe, nothing will happen.)"""6477 self.__check_scope()6478 self.driver.switch_to.default_content()6479 @staticmethod6480 def convert_xpath_to_css(xpath):6481 """6482 convert_xpath_to_css6483 """6484 return xpath_to_css.convert_xpath_to_css(xpath)6485 def open_html_file(self, html_file):6486 """Opens a local html file into the browser from a relative file path.6487 The URL displayed in the web browser will start with "file://".6488 """6489 self.__check_scope()6490 if self.__looks_like_a_page_url(html_file):6491 self.open(html_file)6492 return6493 if len(html_file) < 6 or not html_file.endswith(".html"):6494 raise Exception('Expecting a ".html" file!')6495 abs_path = os.path.abspath(".")6496 file_path = None6497 if abs_path in html_file:6498 file_path = html_file6499 else:6500 file_path = abs_path + "/%s" % html_file6501 self.open("file://" + file_path)6502 def get_current_url(self):6503 """6504 get_current_url...
webdrivertest.py
Source:webdrivertest.py
...732 target = element.get_attribute("target")733 new_tab = False734 if target == "_blank":735 _new_tab = True736 if new_tab and self.__looks_like_a_page_url(href):737 if onclick:738 try:739 self.execute_script(onclick)740 except WebDriverException | JavascriptException:741 pass742 current_window = self.driver.current_window_handle743 self.open_new_window()744 try:745 self.open(href)746 except WebDriverException:747 pass748 self.switch_to_window(current_window)749 return750 except WebDriverException:...
baseCase.py
Source:baseCase.py
...28 def open(self, url):29 self.__check_scope()30 if type(url) is str:31 url = url.strip()32 if (type(url) is not str) or not self.__looks_like_a_page_url(url):33 msg = 'æ¯å¦å¿è®°è¾å
¥äºåç¼ "http:" or "https:"?'34 raise Exception('Invalid URL: "%s"\n%s' % (url, msg))35 if url.startswith("://"):36 url = "https" + url37 if self.browser == "safari" and url.startswith("data:"):38 url = re.escape(url)39 url = self.__escape_quotes_if_needed(url)40 self.execute_script("window.location.href='%s';" % url)41 else:42 self.driver.get(url)43 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:44 self.wait_for_ready_state_complete()45 self.__demo_mode_pause_if_active()46 def __check_scope(self):47 if hasattr(self, "browser"):48 return49 else:50 message = (51 "\n It looks like you are trying to call a SeleniumBase method"52 "\n from outside the scope of your test class's `self` object,"53 "\n which is initialized by calling BaseCase's setUp() method."54 "\n The `self` object is where all test variables are defined."55 "\n If you created a custom setUp() method (that overrided the"56 "\n the default one), make sure to call super().setUp() in it."57 "\n When using page objects, be sure to pass the `self` object"58 "\n from your test class into your page object methods so that"59 "\n they can call BaseCase class methods with all the required"60 "\n variables, which are initialized during the setUp() method"61 "\n that runs automatically before all tests called by pytest."62 )63 raise OutOfScopeException(message)64 def __looks_like_a_page_url(self, url):65 """66 å¹é
urlç¸ä¼¼åº¦67 :param url:68 :return:69 """70 if (71 url.startswith("http:")72 or url.startswith("https:")73 or url.startswith("://")74 or url.startswith("chrome:")75 or url.startswith("about:")76 or url.startswith("data:")77 or url.startswith("file:")78 or url.startswith("edge:")...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!