How to use __jquery_slow_scroll_to method in SeleniumBase

Best Python code snippet using SeleniumBase

base_case.py

Source:base_case.py Github

copy

Full Screen

...630 if self.browser == "safari":631 if self.demo_mode:632 self.wait_for_link_text_present(link_text, timeout=timeout)633 try:634 self.__jquery_slow_scroll_to(link_text, by=By.LINK_TEXT)635 except Exception:636 pass637 o_bs = '' # original_box_shadow638 loops = settings.HIGHLIGHTS639 selector = self.convert_to_css_selector(640 link_text, by=By.LINK_TEXT)641 selector = self.__make_css_match_first_element_only(selector)642 try:643 selector = re.escape(selector)644 selector = self.__escape_quotes_if_needed(selector)645 self.__highlight_with_jquery(selector, loops, o_bs)646 except Exception:647 pass # JQuery probably couldn't load. Skip highlighting.648 self.__jquery_click(link_text, by=By.LINK_TEXT)649 return650 if not self.is_link_text_present(link_text):651 self.wait_for_link_text_present(link_text, timeout=timeout)652 pre_action_url = self.get_current_url()653 try:654 element = self.wait_for_link_text_visible(655 link_text, timeout=0.2)656 self.__demo_mode_highlight_if_active(link_text, by=By.LINK_TEXT)657 try:658 element.click()659 except (StaleElementReferenceException, ENI_Exception):660 self.wait_for_ready_state_complete()661 time.sleep(0.16)662 element = self.wait_for_link_text_visible(663 link_text, timeout=timeout)664 element.click()665 except Exception:666 found_css = False667 text_id = self.get_link_attribute(link_text, "id", False)668 if text_id:669 link_css = '[id="%s"]' % link_text670 found_css = True671 if not found_css:672 href = self.__get_href_from_link_text(link_text, False)673 if href:674 if href.startswith('/') or page_utils.is_valid_url(href):675 link_css = '[href="%s"]' % href676 found_css = True677 if not found_css:678 ngclick = self.get_link_attribute(link_text, "ng-click", False)679 if ngclick:680 link_css = '[ng-click="%s"]' % ngclick681 found_css = True682 if not found_css:683 onclick = self.get_link_attribute(link_text, "onclick", False)684 if onclick:685 link_css = '[onclick="%s"]' % onclick686 found_css = True687 success = False688 if found_css:689 if self.is_element_visible(link_css):690 self.click(link_css)691 success = True692 else:693 # The link text might be hidden under a dropdown menu694 success = self.__click_dropdown_link_text(695 link_text, link_css)696 if not success:697 element = self.wait_for_link_text_visible(698 link_text, timeout=settings.MINI_TIMEOUT)699 element.click()700 if settings.WAIT_FOR_RSC_ON_CLICKS:701 self.wait_for_ready_state_complete()702 if self.demo_mode:703 if self.driver.current_url != pre_action_url:704 self.__demo_mode_pause_if_active()705 else:706 self.__demo_mode_pause_if_active(tiny=True)707 elif self.slow_mode:708 self.__slow_mode_pause_if_active()709 def click_partial_link_text(self, partial_link_text, timeout=None):710 """ This method clicks the partial link text on a page. """711 # If using phantomjs, might need to extract and open the link directly712 if not timeout:713 timeout = settings.SMALL_TIMEOUT714 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:715 timeout = self.__get_new_timeout(timeout)716 if self.browser == 'phantomjs':717 if self.is_partial_link_text_visible(partial_link_text):718 element = self.wait_for_partial_link_text(partial_link_text)719 element.click()720 return721 soup = self.get_beautiful_soup()722 html_links = soup.fetch('a')723 for html_link in html_links:724 if partial_link_text in html_link.text:725 for html_attribute in html_link.attrs:726 if html_attribute[0] == 'href':727 href = html_attribute[1]728 if href.startswith('//'):729 link = "http:" + href730 elif href.startswith('/'):731 url = self.driver.current_url732 domain_url = self.get_domain_url(url)733 link = domain_url + href734 else:735 link = href736 self.open(link)737 return738 raise Exception(739 'Could not parse link from partial link_text '740 '{%s}' % partial_link_text)741 raise Exception(742 "Partial link text {%s} was not found!" % partial_link_text)743 if not self.is_partial_link_text_present(partial_link_text):744 self.wait_for_partial_link_text_present(745 partial_link_text, timeout=timeout)746 pre_action_url = self.get_current_url()747 try:748 element = self.wait_for_partial_link_text(749 partial_link_text, timeout=0.2)750 self.__demo_mode_highlight_if_active(751 partial_link_text, by=By.LINK_TEXT)752 try:753 element.click()754 except (StaleElementReferenceException, ENI_Exception):755 self.wait_for_ready_state_complete()756 time.sleep(0.16)757 element = self.wait_for_partial_link_text(758 partial_link_text, timeout=timeout)759 element.click()760 except Exception:761 found_css = False762 text_id = self.get_partial_link_text_attribute(763 partial_link_text, "id", False)764 if text_id:765 link_css = '[id="%s"]' % partial_link_text766 found_css = True767 if not found_css:768 href = self.__get_href_from_partial_link_text(769 partial_link_text, False)770 if href:771 if href.startswith('/') or page_utils.is_valid_url(href):772 link_css = '[href="%s"]' % href773 found_css = True774 if not found_css:775 ngclick = self.get_partial_link_text_attribute(776 partial_link_text, "ng-click", False)777 if ngclick:778 link_css = '[ng-click="%s"]' % ngclick779 found_css = True780 if not found_css:781 onclick = self.get_partial_link_text_attribute(782 partial_link_text, "onclick", False)783 if onclick:784 link_css = '[onclick="%s"]' % onclick785 found_css = True786 success = False787 if found_css:788 if self.is_element_visible(link_css):789 self.click(link_css)790 success = True791 else:792 # The link text might be hidden under a dropdown menu793 success = self.__click_dropdown_partial_link_text(794 partial_link_text, link_css)795 if not success:796 element = self.wait_for_partial_link_text(797 partial_link_text, timeout=settings.MINI_TIMEOUT)798 element.click()799 if settings.WAIT_FOR_RSC_ON_CLICKS:800 self.wait_for_ready_state_complete()801 if self.demo_mode:802 if self.driver.current_url != pre_action_url:803 self.__demo_mode_pause_if_active()804 else:805 self.__demo_mode_pause_if_active(tiny=True)806 elif self.slow_mode:807 self.__slow_mode_pause_if_active()808 def get_text(self, selector, by=By.CSS_SELECTOR, timeout=None):809 if not timeout:810 timeout = settings.SMALL_TIMEOUT811 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:812 timeout = self.__get_new_timeout(timeout)813 selector, by = self.__recalculate_selector(selector, by)814 self.wait_for_ready_state_complete()815 time.sleep(0.01)816 element = page_actions.wait_for_element_visible(817 self.driver, selector, by, timeout)818 try:819 element_text = element.text820 except (StaleElementReferenceException, ENI_Exception):821 self.wait_for_ready_state_complete()822 time.sleep(0.14)823 element = page_actions.wait_for_element_visible(824 self.driver, selector, by, timeout)825 element_text = element.text826 return element_text827 def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR,828 timeout=None, hard_fail=True):829 """ This method uses JavaScript to get the value of an attribute. """830 if not timeout:831 timeout = settings.SMALL_TIMEOUT832 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:833 timeout = self.__get_new_timeout(timeout)834 selector, by = self.__recalculate_selector(selector, by)835 self.wait_for_ready_state_complete()836 time.sleep(0.01)837 element = page_actions.wait_for_element_present(838 self.driver, selector, by, timeout)839 try:840 attribute_value = element.get_attribute(attribute)841 except (StaleElementReferenceException, ENI_Exception):842 self.wait_for_ready_state_complete()843 time.sleep(0.14)844 element = page_actions.wait_for_element_present(845 self.driver, selector, by, timeout)846 attribute_value = element.get_attribute(attribute)847 if attribute_value is not None:848 return attribute_value849 else:850 if hard_fail:851 raise Exception("Element {%s} has no attribute {%s}!" % (852 selector, attribute))853 else:854 return None855 def set_attribute(self, selector, attribute, value, by=By.CSS_SELECTOR,856 timeout=None):857 """ This method uses JavaScript to set/update an attribute.858 Only the first matching selector from querySelector() is used. """859 if not timeout:860 timeout = settings.SMALL_TIMEOUT861 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:862 timeout = self.__get_new_timeout(timeout)863 selector, by = self.__recalculate_selector(selector, by)864 if self.is_element_visible(selector, by=by):865 try:866 self.scroll_to(selector, by=by, timeout=timeout)867 except Exception:868 pass869 attribute = re.escape(attribute)870 attribute = self.__escape_quotes_if_needed(attribute)871 value = re.escape(value)872 value = self.__escape_quotes_if_needed(value)873 css_selector = self.convert_to_css_selector(selector, by=by)874 css_selector = re.escape(css_selector) # Add "\\" to special chars875 css_selector = self.__escape_quotes_if_needed(css_selector)876 script = ("""document.querySelector('%s').setAttribute('%s','%s');"""877 % (css_selector, attribute, value))878 self.execute_script(script)879 def set_attributes(self, selector, attribute, value, by=By.CSS_SELECTOR):880 """ This method uses JavaScript to set/update a common attribute.881 All matching selectors from querySelectorAll() are used.882 Example => (Make all links on a website redirect to Google):883 self.set_attributes("a", "href", "https://google.com") """884 selector, by = self.__recalculate_selector(selector, by)885 attribute = re.escape(attribute)886 attribute = self.__escape_quotes_if_needed(attribute)887 value = re.escape(value)888 value = self.__escape_quotes_if_needed(value)889 css_selector = self.convert_to_css_selector(selector, by=by)890 css_selector = re.escape(css_selector) # Add "\\" to special chars891 css_selector = self.__escape_quotes_if_needed(css_selector)892 script = ("""var $elements = document.querySelectorAll('%s');893 var index = 0, length = $elements.length;894 for(; index < length; index++){895 $elements[index].setAttribute('%s','%s');}"""896 % (css_selector, attribute, value))897 try:898 self.execute_script(script)899 except Exception:900 pass901 def set_attribute_all(self, selector, attribute, value,902 by=By.CSS_SELECTOR):903 """ Same as set_attributes(), but using querySelectorAll naming scheme.904 This method uses JavaScript to set/update a common attribute.905 All matching selectors from querySelectorAll() are used.906 Example => (Make all links on a website redirect to Google):907 self.set_attribute_all("a", "href", "https://google.com") """908 self.set_attributes(selector, attribute, value, by=by)909 def remove_attribute(self, selector, attribute, by=By.CSS_SELECTOR,910 timeout=None):911 """ This method uses JavaScript to remove an attribute.912 Only the first matching selector from querySelector() is used. """913 if not timeout:914 timeout = settings.SMALL_TIMEOUT915 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:916 timeout = self.__get_new_timeout(timeout)917 selector, by = self.__recalculate_selector(selector, by)918 if self.is_element_visible(selector, by=by):919 try:920 self.scroll_to(selector, by=by, timeout=timeout)921 except Exception:922 pass923 attribute = re.escape(attribute)924 attribute = self.__escape_quotes_if_needed(attribute)925 css_selector = self.convert_to_css_selector(selector, by=by)926 css_selector = re.escape(css_selector) # Add "\\" to special chars927 css_selector = self.__escape_quotes_if_needed(css_selector)928 script = ("""document.querySelector('%s').removeAttribute('%s');"""929 % (css_selector, attribute))930 self.execute_script(script)931 def remove_attributes(self, selector, attribute, by=By.CSS_SELECTOR):932 """ This method uses JavaScript to remove a common attribute.933 All matching selectors from querySelectorAll() are used. """934 selector, by = self.__recalculate_selector(selector, by)935 attribute = re.escape(attribute)936 attribute = self.__escape_quotes_if_needed(attribute)937 css_selector = self.convert_to_css_selector(selector, by=by)938 css_selector = re.escape(css_selector) # Add "\\" to special chars939 css_selector = self.__escape_quotes_if_needed(css_selector)940 script = ("""var $elements = document.querySelectorAll('%s');941 var index = 0, length = $elements.length;942 for(; index < length; index++){943 $elements[index].removeAttribute('%s');}"""944 % (css_selector, attribute))945 try:946 self.execute_script(script)947 except Exception:948 pass949 def get_property_value(self, selector, property, by=By.CSS_SELECTOR,950 timeout=None):951 """ Returns the property value of a page element's computed style.952 Example:953 opacity = self.get_property_value("html body a", "opacity")954 self.assertTrue(float(opacity) > 0, "Element not visible!") """955 if not timeout:956 timeout = settings.SMALL_TIMEOUT957 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:958 timeout = self.__get_new_timeout(timeout)959 selector, by = self.__recalculate_selector(selector, by)960 self.wait_for_ready_state_complete()961 page_actions.wait_for_element_present(962 self.driver, selector, by, timeout)963 try:964 selector = self.convert_to_css_selector(selector, by=by)965 except Exception:966 # Don't run action if can't convert to CSS_Selector for JavaScript967 raise Exception(968 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (969 selector, by))970 selector = re.escape(selector)971 selector = self.__escape_quotes_if_needed(selector)972 script = ("""var $elm = document.querySelector('%s');973 $val = window.getComputedStyle($elm).getPropertyValue('%s');974 return $val;"""975 % (selector, property))976 value = self.execute_script(script)977 if value is not None:978 return value979 else:980 return "" # Return an empty string if the property doesn't exist981 def get_image_url(self, selector, by=By.CSS_SELECTOR, timeout=None):982 """ Extracts the URL from an image element on the page. """983 if not timeout:984 timeout = settings.SMALL_TIMEOUT985 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:986 timeout = self.__get_new_timeout(timeout)987 return self.get_attribute(selector,988 attribute='src', by=by, timeout=timeout)989 def find_elements(self, selector, by=By.CSS_SELECTOR, limit=0):990 """ Returns a list of matching WebElements.991 Elements could be either hidden or visible on the page.992 If "limit" is set and > 0, will only return that many elements. """993 selector, by = self.__recalculate_selector(selector, by)994 self.wait_for_ready_state_complete()995 time.sleep(0.05)996 elements = self.driver.find_elements(by=by, value=selector)997 if limit and limit > 0 and len(elements) > limit:998 elements = elements[:limit]999 return elements1000 def find_visible_elements(self, selector, by=By.CSS_SELECTOR, limit=0):1001 """ Returns a list of matching WebElements that are visible.1002 If "limit" is set and > 0, will only return that many elements. """1003 selector, by = self.__recalculate_selector(selector, by)1004 self.wait_for_ready_state_complete()1005 time.sleep(0.05)1006 v_elems = page_actions.find_visible_elements(self.driver, selector, by)1007 if limit and limit > 0 and len(v_elems) > limit:1008 v_elems = v_elems[:limit]1009 return v_elems1010 def click_visible_elements(1011 self, selector, by=By.CSS_SELECTOR, limit=0, timeout=None):1012 """ Finds all matching page elements and clicks visible ones in order.1013 If a click reloads or opens a new page, the clicking will stop.1014 If no matching elements appear, an Exception will be raised.1015 If "limit" is set and > 0, will only click that many elements.1016 Also clicks elements that become visible from previous clicks.1017 Works best for actions such as clicking all checkboxes on a page.1018 Example: self.click_visible_elements('input[type="checkbox"]') """1019 if not timeout:1020 timeout = settings.SMALL_TIMEOUT1021 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1022 timeout = self.__get_new_timeout(timeout)1023 selector, by = self.__recalculate_selector(selector, by)1024 self.wait_for_element_present(selector, by=by, timeout=timeout)1025 elements = self.find_elements(selector, by=by)1026 if self.browser == "safari":1027 if not limit:1028 limit = 01029 num_elements = len(elements)1030 if num_elements == 0:1031 raise Exception(1032 "No matching elements found for selector {%s}!" % selector)1033 elif num_elements < limit or limit == 0:1034 limit = num_elements1035 selector, by = self.__recalculate_selector(selector, by)1036 css_selector = self.convert_to_css_selector(selector, by=by)1037 last_css_chunk = css_selector.split(' ')[-1]1038 if ":" in last_css_chunk:1039 self.__js_click_all(css_selector)1040 self.wait_for_ready_state_complete()1041 return1042 else:1043 for i in range(1, limit+1):1044 new_selector = css_selector + ":nth-of-type(%s)" % str(i)1045 if self.is_element_visible(new_selector):1046 self.__js_click(new_selector)1047 self.wait_for_ready_state_complete()1048 return1049 click_count = 01050 for element in elements:1051 if limit and limit > 0 and click_count >= limit:1052 return1053 try:1054 if element.is_displayed():1055 self.__scroll_to_element(element)1056 element.click()1057 click_count += 11058 self.wait_for_ready_state_complete()1059 except ECI_Exception:1060 continue # ElementClickInterceptedException (Overlay likely)1061 except (StaleElementReferenceException, ENI_Exception):1062 self.wait_for_ready_state_complete()1063 time.sleep(0.12)1064 try:1065 if element.is_displayed():1066 self.__scroll_to_element(element)1067 element.click()1068 click_count += 11069 self.wait_for_ready_state_complete()1070 except (StaleElementReferenceException, ENI_Exception):1071 return # Probably on new page / Elements are all stale1072 def click_nth_visible_element(1073 self, selector, number, by=By.CSS_SELECTOR, timeout=None):1074 """ Finds all matching page elements and clicks the nth visible one.1075 Example: self.click_nth_visible_element('[type="checkbox"]', 5)1076 (Clicks the 5th visible checkbox on the page.) """1077 if not timeout:1078 timeout = settings.SMALL_TIMEOUT1079 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1080 timeout = self.__get_new_timeout(timeout)1081 selector, by = self.__recalculate_selector(selector, by)1082 self.wait_for_element_present(selector, by=by, timeout=timeout)1083 elements = self.find_visible_elements(selector, by=by)1084 if len(elements) < number:1085 raise Exception("Not enough matching {%s} elements of type {%s} to"1086 " click number %s!" % (selector, by, number))1087 number = number - 11088 if number < 0:1089 number = 01090 element = elements[number]1091 self.wait_for_ready_state_complete()1092 try:1093 self.__scroll_to_element(element)1094 element.click()1095 except (StaleElementReferenceException, ENI_Exception):1096 self.wait_for_ready_state_complete()1097 time.sleep(0.12)1098 self.__scroll_to_element(element)1099 element.click()1100 def click_if_visible(self, selector, by=By.CSS_SELECTOR):1101 """ If the page selector exists and is visible, clicks on the element.1102 This method only clicks on the first matching element found.1103 (Use click_visible_elements() to click all matching elements.) """1104 self.wait_for_ready_state_complete()1105 if self.is_element_visible(selector, by=by):1106 self.click(selector, by=by)1107 def is_checked(self, selector, by=By.CSS_SELECTOR, timeout=None):1108 """ Determines if a checkbox or a radio button element is checked.1109 Returns True if the element is checked.1110 Returns False if the element is not checked.1111 If the element is not present on the page, raises an exception.1112 If the element is not a checkbox or radio, raises an exception. """1113 if not timeout:1114 timeout = settings.SMALL_TIMEOUT1115 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1116 timeout = self.__get_new_timeout(timeout)1117 selector, by = self.__recalculate_selector(selector, by)1118 kind = self.get_attribute(selector, "type", by=by, timeout=timeout)1119 if kind != "checkbox" and kind != "radio":1120 raise Exception("Expecting a checkbox or a radio button element!")1121 is_checked = self.get_attribute(1122 selector, "checked", by=by, timeout=timeout, hard_fail=False)1123 if is_checked:1124 return True1125 else: # (NoneType)1126 return False1127 def is_selected(self, selector, by=By.CSS_SELECTOR, timeout=None):1128 """ Same as is_checked() """1129 return self.is_checked(selector, by=by, timeout=timeout)1130 def check_if_unchecked(self, selector, by=By.CSS_SELECTOR):1131 """ If a checkbox or radio button is not checked, will check it. """1132 selector, by = self.__recalculate_selector(selector, by)1133 if not self.is_checked(selector, by=by):1134 if self.is_element_visible(selector, by=by):1135 self.click(selector, by=by)1136 else:1137 selector = self.convert_to_css_selector(selector, by=by)1138 self.js_click(selector, by=By.CSS_SELECTOR)1139 def select_if_unselected(self, selector, by=By.CSS_SELECTOR):1140 """ Same as check_if_unchecked() """1141 self.check_if_unchecked(selector, by=by)1142 def uncheck_if_checked(self, selector, by=By.CSS_SELECTOR):1143 """ If a checkbox is checked, will uncheck it. """1144 selector, by = self.__recalculate_selector(selector, by)1145 if self.is_checked(selector, by=by):1146 if self.is_element_visible(selector, by=by):1147 self.click(selector, by=by)1148 else:1149 selector = self.convert_to_css_selector(selector, by=by)1150 self.js_click(selector, by=By.CSS_SELECTOR)1151 def unselect_if_selected(self, selector, by=By.CSS_SELECTOR):1152 """ Same as uncheck_if_checked() """1153 self.uncheck_if_checked(selector, by=by)1154 def is_element_in_an_iframe(self, selector, by=By.CSS_SELECTOR):1155 """ Returns True if the selector's element is located in an iframe.1156 Otherwise returns False. """1157 selector, by = self.__recalculate_selector(selector, by)1158 if self.is_element_present(selector, by=by):1159 return False1160 soup = self.get_beautiful_soup()1161 iframe_list = soup.select('iframe')1162 for iframe in iframe_list:1163 iframe_identifier = None1164 if iframe.has_attr('name') and len(iframe['name']) > 0:1165 iframe_identifier = iframe['name']1166 elif iframe.has_attr('id') and len(iframe['id']) > 0:1167 iframe_identifier = iframe['id']1168 elif iframe.has_attr('class') and len(iframe['class']) > 0:1169 iframe_class = " ".join(iframe["class"])1170 iframe_identifier = '[class="%s"]' % iframe_class1171 else:1172 continue1173 self.switch_to_frame(iframe_identifier)1174 if self.is_element_present(selector, by=by):1175 self.switch_to_default_content()1176 return True1177 self.switch_to_default_content()1178 return False1179 def switch_to_frame_of_element(self, selector, by=By.CSS_SELECTOR):1180 """ Set driver control to the iframe containing element (assuming the1181 element is in a single-nested iframe) and returns the iframe name.1182 If element is not in an iframe, returns None, and nothing happens.1183 May not work if multiple iframes are nested within each other. """1184 selector, by = self.__recalculate_selector(selector, by)1185 if self.is_element_present(selector, by=by):1186 return None1187 soup = self.get_beautiful_soup()1188 iframe_list = soup.select('iframe')1189 for iframe in iframe_list:1190 iframe_identifier = None1191 if iframe.has_attr('name') and len(iframe['name']) > 0:1192 iframe_identifier = iframe['name']1193 elif iframe.has_attr('id') and len(iframe['id']) > 0:1194 iframe_identifier = iframe['id']1195 elif iframe.has_attr('class') and len(iframe['class']) > 0:1196 iframe_class = " ".join(iframe["class"])1197 iframe_identifier = '[class="%s"]' % iframe_class1198 else:1199 continue1200 try:1201 self.switch_to_frame(iframe_identifier, timeout=1)1202 if self.is_element_present(selector, by=by):1203 return iframe_identifier1204 except Exception:1205 pass1206 self.switch_to_default_content()1207 try:1208 self.switch_to_frame(selector, timeout=1)1209 return selector1210 except Exception:1211 if self.is_element_present(selector, by=by):1212 return ""1213 raise Exception("Could not switch to iframe containing "1214 "element {%s}!" % selector)1215 def hover_on_element(self, selector, by=By.CSS_SELECTOR):1216 selector, by = self.__recalculate_selector(selector, by)1217 if page_utils.is_xpath_selector(selector):1218 selector = self.convert_to_css_selector(selector, By.XPATH)1219 by = By.CSS_SELECTOR1220 self.wait_for_element_visible(1221 selector, by=by, timeout=settings.SMALL_TIMEOUT)1222 self.__demo_mode_highlight_if_active(selector, by)1223 self.scroll_to(selector, by=by)1224 time.sleep(0.05) # Settle down from scrolling before hovering1225 if self.browser != "chrome":1226 return page_actions.hover_on_element(self.driver, selector)1227 # Using Chrome1228 # (Pure hover actions won't work on early chromedriver versions)1229 try:1230 return page_actions.hover_on_element(self.driver, selector)1231 except WebDriverException as e:1232 driver_capabilities = self.driver.__dict__["capabilities"]1233 if "version" in driver_capabilities:1234 chrome_version = driver_capabilities["version"]1235 else:1236 chrome_version = driver_capabilities["browserVersion"]1237 major_chrome_version = chrome_version.split('.')[0]1238 chrome_dict = self.driver.__dict__["capabilities"]["chrome"]1239 chromedriver_version = chrome_dict["chromedriverVersion"]1240 chromedriver_version = chromedriver_version.split(' ')[0]1241 major_chromedriver_version = chromedriver_version.split('.')[0]1242 install_sb = (1243 "seleniumbase install chromedriver %s" % major_chrome_version)1244 if major_chromedriver_version < major_chrome_version:1245 # Upgrading the driver is required for performing hover actions1246 message = (1247 "\n"1248 "You need a newer chromedriver to perform hover actions!\n"1249 "Your version of chromedriver is: %s\n"1250 "And your version of Chrome is: %s\n"1251 "You can fix this issue by running:\n>>> %s\n"1252 "" % (chromedriver_version, chrome_version, install_sb))1253 raise Exception(message)1254 else:1255 raise Exception(e)1256 def hover_and_click(self, hover_selector, click_selector,1257 hover_by=By.CSS_SELECTOR, click_by=By.CSS_SELECTOR,1258 timeout=None):1259 """ When you want to hover over an element or dropdown menu,1260 and then click an element that appears after that. """1261 if not timeout:1262 timeout = settings.SMALL_TIMEOUT1263 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1264 timeout = self.__get_new_timeout(timeout)1265 hover_selector, hover_by = self.__recalculate_selector(1266 hover_selector, hover_by)1267 hover_selector = self.convert_to_css_selector(1268 hover_selector, hover_by)1269 hover_by = By.CSS_SELECTOR1270 click_selector, click_by = self.__recalculate_selector(1271 click_selector, click_by)1272 dropdown_element = self.wait_for_element_visible(1273 hover_selector, by=hover_by, timeout=timeout)1274 self.__demo_mode_highlight_if_active(hover_selector, hover_by)1275 self.scroll_to(hover_selector, by=hover_by)1276 pre_action_url = self.driver.current_url1277 outdated_driver = False1278 element = None1279 try:1280 if self.mobile_emulator:1281 # On mobile, click to hover the element1282 dropdown_element.click()1283 elif self.browser == "safari":1284 # Use the workaround for hover-clicking on Safari1285 raise Exception("This Exception will be caught.")1286 else:1287 page_actions.hover_element(self.driver, dropdown_element)1288 except Exception:1289 outdated_driver = True1290 element = self.wait_for_element_present(1291 click_selector, click_by, timeout)1292 if click_by == By.LINK_TEXT:1293 self.open(self.__get_href_from_link_text(click_selector))1294 elif click_by == By.PARTIAL_LINK_TEXT:1295 self.open(self.__get_href_from_partial_link_text(1296 click_selector))1297 else:1298 self.js_click(click_selector, by=click_by)1299 if outdated_driver:1300 pass # Already did the click workaround1301 elif self.mobile_emulator:1302 self.click(click_selector, by=click_by)1303 elif not outdated_driver:1304 element = page_actions.hover_and_click(1305 self.driver, hover_selector, click_selector,1306 hover_by, click_by, timeout)1307 if self.demo_mode:1308 if self.driver.current_url != pre_action_url:1309 self.__demo_mode_pause_if_active()1310 else:1311 self.__demo_mode_pause_if_active(tiny=True)1312 elif self.slow_mode:1313 self.__slow_mode_pause_if_active()1314 return element1315 def hover_and_double_click(self, hover_selector, click_selector,1316 hover_by=By.CSS_SELECTOR,1317 click_by=By.CSS_SELECTOR,1318 timeout=None):1319 """ When you want to hover over an element or dropdown menu,1320 and then double-click an element that appears after that. """1321 if not timeout:1322 timeout = settings.SMALL_TIMEOUT1323 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1324 timeout = self.__get_new_timeout(timeout)1325 hover_selector, hover_by = self.__recalculate_selector(1326 hover_selector, hover_by)1327 hover_selector = self.convert_to_css_selector(1328 hover_selector, hover_by)1329 hover_by = By.CSS_SELECTOR1330 click_selector, click_by = self.__recalculate_selector(1331 click_selector, click_by)1332 dropdown_element = self.wait_for_element_visible(1333 hover_selector, by=hover_by, timeout=timeout)1334 self.__demo_mode_highlight_if_active(hover_selector, hover_by)1335 self.scroll_to(hover_selector, by=hover_by)1336 pre_action_url = self.driver.current_url1337 outdated_driver = False1338 element = None1339 try:1340 page_actions.hover_element(self.driver, dropdown_element)1341 except Exception:1342 outdated_driver = True1343 element = self.wait_for_element_present(1344 click_selector, click_by, timeout)1345 if click_by == By.LINK_TEXT:1346 self.open(self.__get_href_from_link_text(click_selector))1347 elif click_by == By.PARTIAL_LINK_TEXT:1348 self.open(self.__get_href_from_partial_link_text(1349 click_selector))1350 else:1351 self.js_click(click_selector, click_by)1352 if not outdated_driver:1353 element = page_actions.hover_element_and_double_click(1354 self.driver, dropdown_element, click_selector,1355 click_by=By.CSS_SELECTOR, timeout=timeout)1356 if self.demo_mode:1357 if self.driver.current_url != pre_action_url:1358 self.__demo_mode_pause_if_active()1359 else:1360 self.__demo_mode_pause_if_active(tiny=True)1361 elif self.slow_mode:1362 self.__slow_mode_pause_if_active()1363 return element1364 def drag_and_drop(self, drag_selector, drop_selector,1365 drag_by=By.CSS_SELECTOR, drop_by=By.CSS_SELECTOR,1366 timeout=None):1367 """ Drag and drop an element from one selector to another. """1368 if not timeout:1369 timeout = settings.SMALL_TIMEOUT1370 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1371 timeout = self.__get_new_timeout(timeout)1372 drag_selector, drag_by = self.__recalculate_selector(1373 drag_selector, drag_by)1374 drop_selector, drop_by = self.__recalculate_selector(1375 drop_selector, drop_by)1376 drag_element = self.wait_for_element_visible(1377 drag_selector, by=drag_by, timeout=timeout)1378 self.__demo_mode_highlight_if_active(drag_selector, drag_by)1379 self.wait_for_element_visible(1380 drop_selector, by=drop_by, timeout=timeout)1381 self.__demo_mode_highlight_if_active(drop_selector, drop_by)1382 self.scroll_to(drag_selector, by=drag_by)1383 drag_selector = self.convert_to_css_selector(1384 drag_selector, drag_by)1385 drop_selector = self.convert_to_css_selector(1386 drop_selector, drop_by)1387 drag_and_drop_script = js_utils.get_drag_and_drop_script()1388 self.safe_execute_script(1389 drag_and_drop_script + (1390 "$('%s').simulateDragDrop("1391 "{dropTarget: "1392 "'%s'});" % (drag_selector, drop_selector)))1393 if self.demo_mode:1394 self.__demo_mode_pause_if_active()1395 elif self.slow_mode:1396 self.__slow_mode_pause_if_active()1397 return drag_element1398 def __select_option(self, dropdown_selector, option,1399 dropdown_by=By.CSS_SELECTOR, option_by="text",1400 timeout=None):1401 """ Selects an HTML <select> option by specification.1402 Option specifications are by "text", "index", or "value".1403 Defaults to "text" if option_by is unspecified or unknown. """1404 from selenium.webdriver.support.ui import Select1405 if not timeout:1406 timeout = settings.SMALL_TIMEOUT1407 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1408 timeout = self.__get_new_timeout(timeout)1409 dropdown_selector, dropdown_by = self.__recalculate_selector(1410 dropdown_selector, dropdown_by)1411 self.wait_for_ready_state_complete()1412 element = self.wait_for_element_present(1413 dropdown_selector, by=dropdown_by, timeout=timeout)1414 if self.is_element_visible(dropdown_selector, by=dropdown_by):1415 self.__demo_mode_highlight_if_active(1416 dropdown_selector, dropdown_by)1417 pre_action_url = self.driver.current_url1418 try:1419 if option_by == "index":1420 Select(element).select_by_index(option)1421 elif option_by == "value":1422 Select(element).select_by_value(option)1423 else:1424 Select(element).select_by_visible_text(option)1425 except (StaleElementReferenceException, ENI_Exception):1426 self.wait_for_ready_state_complete()1427 time.sleep(0.14)1428 element = self.wait_for_element_present(1429 dropdown_selector, by=dropdown_by, timeout=timeout)1430 if option_by == "index":1431 Select(element).select_by_index(option)1432 elif option_by == "value":1433 Select(element).select_by_value(option)1434 else:1435 Select(element).select_by_visible_text(option)1436 if settings.WAIT_FOR_RSC_ON_CLICKS:1437 self.wait_for_ready_state_complete()1438 if self.demo_mode:1439 if self.driver.current_url != pre_action_url:1440 self.__demo_mode_pause_if_active()1441 else:1442 self.__demo_mode_pause_if_active(tiny=True)1443 elif self.slow_mode:1444 self.__slow_mode_pause_if_active()1445 def select_option_by_text(self, dropdown_selector, option,1446 dropdown_by=By.CSS_SELECTOR,1447 timeout=None):1448 """ Selects an HTML <select> option by option text.1449 @Params1450 dropdown_selector - the <select> selector1451 option - the text of the option """1452 if not timeout:1453 timeout = settings.SMALL_TIMEOUT1454 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1455 timeout = self.__get_new_timeout(timeout)1456 self.__select_option(dropdown_selector, option,1457 dropdown_by=dropdown_by, option_by="text",1458 timeout=timeout)1459 def select_option_by_index(self, dropdown_selector, option,1460 dropdown_by=By.CSS_SELECTOR,1461 timeout=None):1462 """ Selects an HTML <select> option by option index.1463 @Params1464 dropdown_selector - the <select> selector1465 option - the index number of the option """1466 if not timeout:1467 timeout = settings.SMALL_TIMEOUT1468 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1469 timeout = self.__get_new_timeout(timeout)1470 self.__select_option(dropdown_selector, option,1471 dropdown_by=dropdown_by, option_by="index",1472 timeout=timeout)1473 def select_option_by_value(self, dropdown_selector, option,1474 dropdown_by=By.CSS_SELECTOR,1475 timeout=None):1476 """ Selects an HTML <select> option by option value.1477 @Params1478 dropdown_selector - the <select> selector1479 option - the value property of the option """1480 if not timeout:1481 timeout = settings.SMALL_TIMEOUT1482 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1483 timeout = self.__get_new_timeout(timeout)1484 self.__select_option(dropdown_selector, option,1485 dropdown_by=dropdown_by, option_by="value",1486 timeout=timeout)1487 def load_html_string(self, html_string, new_page=True):1488 """ Loads an HTML string into the web browser.1489 If new_page==True, the page will switch to: "data:text/html,"1490 If new_page==False, will load HTML into the current page. """1491 soup = self.get_beautiful_soup(html_string)1492 found_base = False1493 links = soup.findAll("link")1494 href = None1495 for link in links:1496 if link.get("rel") == ["canonical"] and link.get("href"):1497 found_base = True1498 href = link.get("href")1499 href = self.get_domain_url(href)1500 if found_base and html_string.count("<head>") == 1 and (1501 html_string.count("<base") == 0):1502 html_string = html_string.replace(1503 "<head>", '<head><base href="%s">' % href)1504 elif not found_base:1505 bases = soup.findAll("base")1506 for base in bases:1507 if base.get("href"):1508 href = base.get("href")1509 if href:1510 html_string = html_string.replace(1511 'base: "."', 'base: "%s"' % href)1512 soup = self.get_beautiful_soup(html_string)1513 scripts = soup.findAll("script")1514 for script in scripts:1515 if script.get("type") != "application/json":1516 html_string = html_string.replace(str(script), "")1517 soup = self.get_beautiful_soup(html_string)1518 found_head = False1519 found_body = False1520 html_head = None1521 html_body = None1522 if soup.head and len(str(soup.head)) > 12:1523 found_head = True1524 html_head = str(soup.head)1525 html_head = re.escape(html_head)1526 html_head = self.__escape_quotes_if_needed(html_head)1527 html_head = html_head.replace('\\ ', ' ')1528 if soup.body and len(str(soup.body)) > 12:1529 found_body = True1530 html_body = str(soup.body)1531 html_body = html_body.replace("\xc2\xa0", "&#xA0;")1532 html_body = html_body.replace("\xc2\xa1", "&#xA1;")1533 html_body = html_body.replace("\xc2\xa9", "&#xA9;")1534 html_body = html_body.replace("\xc2\xb7", "&#xB7;")1535 html_body = html_body.replace("\xc2\xbf", "&#xBF;")1536 html_body = html_body.replace("\xc3\x97", "&#xD7;")1537 html_body = html_body.replace("\xc3\xb7", "&#xF7;")1538 html_body = re.escape(html_body)1539 html_body = self.__escape_quotes_if_needed(html_body)1540 html_body = html_body.replace('\\ ', ' ')1541 html_string = re.escape(html_string)1542 html_string = self.__escape_quotes_if_needed(html_string)1543 html_string = html_string.replace('\\ ', ' ')1544 if new_page:1545 self.open("data:text/html,")1546 inner_head = '''document.getElementsByTagName("head")[0].innerHTML'''1547 inner_body = '''document.getElementsByTagName("body")[0].innerHTML'''1548 if not found_body:1549 self.execute_script(1550 '''%s = \"%s\"''' % (inner_body, html_string))1551 elif found_body and not found_head:1552 self.execute_script(1553 '''%s = \"%s\"''' % (inner_body, html_body))1554 elif found_body and found_head:1555 self.execute_script(1556 '''%s = \"%s\"''' % (inner_head, html_head))1557 self.execute_script(1558 '''%s = \"%s\"''' % (inner_body, html_body))1559 else:1560 raise Exception("Logic Error!")1561 for script in scripts:1562 js_code = script.string1563 js_src = script.get("src")1564 if js_code and script.get("type") != "application/json":1565 js_code_lines = js_code.split('\n')1566 new_lines = []1567 for line in js_code_lines:1568 line = line.strip()1569 new_lines.append(line)1570 js_code = '\n'.join(new_lines)1571 js_utils.add_js_code(self.driver, js_code)1572 elif js_src:1573 js_utils.add_js_link(self.driver, js_src)1574 else:1575 pass1576 def load_html_file(self, html_file, new_page=True):1577 """ Loads a local html file into the browser from a relative file path.1578 If new_page==True, the page will switch to: "data:text/html,"1579 If new_page==False, will load HTML into the current page.1580 Local images and other local src content WILL BE IGNORED. """1581 if self.__looks_like_a_page_url(html_file):1582 self.open(html_file)1583 return1584 if len(html_file) < 6 or not html_file.endswith(".html"):1585 raise Exception('Expecting a ".html" file!')1586 abs_path = os.path.abspath('.')1587 file_path = None1588 if abs_path in html_file:1589 file_path = html_file1590 else:1591 file_path = abs_path + "/%s" % html_file1592 html_string = None1593 with open(file_path, 'r') as f:1594 html_string = f.read().strip()1595 self.load_html_string(html_string, new_page)1596 def open_html_file(self, html_file):1597 """ Opens a local html file into the browser from a relative file path.1598 The URL displayed in the web browser will start with "file://". """1599 if self.__looks_like_a_page_url(html_file):1600 self.open(html_file)1601 return1602 if len(html_file) < 6 or not html_file.endswith(".html"):1603 raise Exception('Expecting a ".html" file!')1604 abs_path = os.path.abspath('.')1605 file_path = None1606 if abs_path in html_file:1607 file_path = html_file1608 else:1609 file_path = abs_path + "/%s" % html_file1610 self.open("file://" + file_path)1611 def execute_script(self, script):1612 return self.driver.execute_script(script)1613 def execute_async_script(self, script, timeout=None):1614 if not timeout:1615 timeout = settings.EXTREME_TIMEOUT1616 return js_utils.execute_async_script(self.driver, script, timeout)1617 def safe_execute_script(self, script):1618 """ When executing a script that contains a jQuery command,1619 it's important that the jQuery library has been loaded first.1620 This method will load jQuery if it wasn't already loaded. """1621 try:1622 return self.execute_script(script)1623 except Exception:1624 # The likely reason this fails is because: "jQuery is not defined"1625 self.activate_jquery() # It's a good thing we can define it here1626 return self.execute_script(script)1627 def set_window_rect(self, x, y, width, height):1628 self.driver.set_window_rect(x, y, width, height)1629 self.__demo_mode_pause_if_active()1630 def set_window_size(self, width, height):1631 self.driver.set_window_size(width, height)1632 self.__demo_mode_pause_if_active()1633 def maximize_window(self):1634 self.driver.maximize_window()1635 self.__demo_mode_pause_if_active()1636 def switch_to_frame(self, frame, timeout=None):1637 """1638 Wait for an iframe to appear, and switch to it. This should be1639 usable as a drop-in replacement for driver.switch_to.frame().1640 The iframe identifier can be a selector, an index, an id, a name,1641 or a web element, but scrolling to the iframe first will only occur1642 for visible iframes with a string selector.1643 @Params1644 frame - the frame element, name, id, index, or selector1645 timeout - the time to wait for the alert in seconds1646 """1647 if not timeout:1648 timeout = settings.SMALL_TIMEOUT1649 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1650 timeout = self.__get_new_timeout(timeout)1651 if type(frame) is str and self.is_element_visible(frame):1652 try:1653 self.scroll_to(frame, timeout=1)1654 except Exception:1655 pass1656 page_actions.switch_to_frame(self.driver, frame, timeout)1657 def switch_to_default_content(self):1658 """ Brings driver control outside the current iframe.1659 (If driver control is inside an iframe, the driver control1660 will be set to one level above the current frame. If the driver1661 control is not currenly in an iframe, nothing will happen.) """1662 self.driver.switch_to.default_content()1663 def open_new_window(self, switch_to=True):1664 """ Opens a new browser tab/window and switches to it by default. """1665 self.driver.execute_script("window.open('');")1666 time.sleep(0.01)1667 if switch_to:1668 self.switch_to_window(len(self.driver.window_handles) - 1)1669 def switch_to_window(self, window, timeout=None):1670 if not timeout:1671 timeout = settings.SMALL_TIMEOUT1672 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:1673 timeout = self.__get_new_timeout(timeout)1674 page_actions.switch_to_window(self.driver, window, timeout)1675 def switch_to_default_window(self):1676 self.switch_to_window(0)1677 def get_new_driver(self, browser=None, headless=None, locale_code=None,1678 servername=None, port=None, proxy=None, agent=None,1679 switch_to=True, cap_file=None, cap_string=None,1680 disable_csp=None, enable_ws=None, enable_sync=None,1681 use_auto_ext=None, no_sandbox=None, disable_gpu=None,1682 incognito=None, guest_mode=None, devtools=None,1683 swiftshader=None, block_images=None, user_data_dir=None,1684 extension_zip=None, extension_dir=None, is_mobile=False,1685 d_width=None, d_height=None, d_p_r=None):1686 """ This method spins up an extra browser for tests that require1687 more than one. The first browser is already provided by tests1688 that import base_case.BaseCase from seleniumbase. If parameters1689 aren't specified, the method uses the same as the default driver.1690 @Params1691 browser - the browser to use. (Ex: "chrome", "firefox")1692 headless - the option to run webdriver in headless mode1693 locale_code - the Language Locale Code for the web browser1694 servername - if using a Selenium Grid, set the host address here1695 port - if using a Selenium Grid, set the host port here1696 proxy - if using a proxy server, specify the "host:port" combo here1697 switch_to - the option to switch to the new driver (default = True)1698 cap_file - the file containing desired capabilities for the browser1699 cap_string - the string with desired capabilities for the browser1700 disable_csp - an option to disable Chrome's Content Security Policy1701 enable_ws - the option to enable the Web Security feature (Chrome)1702 enable_sync - the option to enable the Chrome Sync feature (Chrome)1703 use_auto_ext - the option to enable Chrome's Automation Extension1704 no_sandbox - the option to enable the "No-Sandbox" feature (Chrome)1705 disable_gpu - the option to enable Chrome's "Disable GPU" feature1706 incognito - the option to enable Chrome's Incognito mode (Chrome)1707 guest - the option to enable Chrome's Guest mode (Chrome)1708 devtools - the option to open Chrome's DevTools on start (Chrome)1709 swiftshader the option to use "--use-gl=swiftshader" (Chrome-only)1710 block_images - the option to block images from loading (Chrome)1711 user_data_dir - Chrome's User Data Directory to use (Chrome-only)1712 extension_zip - A Chrome Extension ZIP file to use (Chrome-only)1713 extension_dir - A Chrome Extension folder to use (Chrome-only)1714 is_mobile - the option to use the mobile emulator (Chrome-only)1715 d_width - the device width of the mobile emulator (Chrome-only)1716 d_height - the device height of the mobile emulator (Chrome-only)1717 d_p_r - the device pixel ratio of the mobile emulator (Chrome-only)1718 """1719 if self.browser == "remote" and self.servername == "localhost":1720 raise Exception('Cannot use "remote" browser driver on localhost!'1721 ' Did you mean to connect to a remote Grid server'1722 ' such as BrowserStack or Sauce Labs? In that'1723 ' case, you must specify the "server" and "port"'1724 ' parameters on the command line! '1725 'Example: '1726 '--server=user:key@hub.browserstack.com --port=80')1727 browserstack_ref = (1728 'https://browserstack.com/automate/capabilities')1729 sauce_labs_ref = (1730 'https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/')1731 if self.browser == "remote" and not (self.cap_file or self.cap_string):1732 raise Exception('Need to specify a desired capabilities file when '1733 'using "--browser=remote". Add "--cap_file=FILE". '1734 'File should be in the Python format used by: '1735 '%s OR '1736 '%s '1737 'See SeleniumBase/examples/sample_cap_file_BS.py '1738 'and SeleniumBase/examples/sample_cap_file_SL.py'1739 % (browserstack_ref, sauce_labs_ref))1740 if browser is None:1741 browser = self.browser1742 browser_name = browser1743 if headless is None:1744 headless = self.headless1745 if locale_code is None:1746 locale_code = self.locale_code1747 if servername is None:1748 servername = self.servername1749 if port is None:1750 port = self.port1751 use_grid = False1752 if servername != "localhost":1753 # Use Selenium Grid (Use "127.0.0.1" for localhost Grid)1754 use_grid = True1755 proxy_string = proxy1756 if proxy_string is None:1757 proxy_string = self.proxy_string1758 user_agent = agent1759 if user_agent is None:1760 user_agent = self.user_agent1761 if disable_csp is None:1762 disable_csp = self.disable_csp1763 if enable_ws is None:1764 enable_ws = self.enable_ws1765 if enable_sync is None:1766 enable_sync = self.enable_sync1767 if use_auto_ext is None:1768 use_auto_ext = self.use_auto_ext1769 if no_sandbox is None:1770 no_sandbox = self.no_sandbox1771 if disable_gpu is None:1772 disable_gpu = self.disable_gpu1773 if incognito is None:1774 incognito = self.incognito1775 if guest_mode is None:1776 guest_mode = self.guest_mode1777 if devtools is None:1778 devtools = self.devtools1779 if swiftshader is None:1780 swiftshader = self.swiftshader1781 if block_images is None:1782 block_images = self.block_images1783 if user_data_dir is None:1784 user_data_dir = self.user_data_dir1785 if extension_zip is None:1786 extension_zip = self.extension_zip1787 if extension_dir is None:1788 extension_dir = self.extension_dir1789 test_id = self.__get_test_id()1790 if cap_file is None:1791 cap_file = self.cap_file1792 if cap_string is None:1793 cap_string = self.cap_string1794 if is_mobile is None:1795 is_mobile = False1796 if d_width is None:1797 d_width = self.__device_width1798 if d_height is None:1799 d_height = self.__device_height1800 if d_p_r is None:1801 d_p_r = self.__device_pixel_ratio1802 valid_browsers = constants.ValidBrowsers.valid_browsers1803 if browser_name not in valid_browsers:1804 raise Exception("Browser: {%s} is not a valid browser option. "1805 "Valid options = {%s}" % (browser, valid_browsers))1806 # Launch a web browser1807 from seleniumbase.core import browser_launcher1808 new_driver = browser_launcher.get_driver(browser_name=browser_name,1809 headless=headless,1810 locale_code=locale_code,1811 use_grid=use_grid,1812 servername=servername,1813 port=port,1814 proxy_string=proxy_string,1815 user_agent=user_agent,1816 cap_file=cap_file,1817 cap_string=cap_string,1818 disable_csp=disable_csp,1819 enable_ws=enable_ws,1820 enable_sync=enable_sync,1821 use_auto_ext=use_auto_ext,1822 no_sandbox=no_sandbox,1823 disable_gpu=disable_gpu,1824 incognito=incognito,1825 guest_mode=guest_mode,1826 devtools=devtools,1827 swiftshader=swiftshader,1828 block_images=block_images,1829 user_data_dir=user_data_dir,1830 extension_zip=extension_zip,1831 extension_dir=extension_dir,1832 test_id=test_id,1833 mobile_emulator=is_mobile,1834 device_width=d_width,1835 device_height=d_height,1836 device_pixel_ratio=d_p_r)1837 self._drivers_list.append(new_driver)1838 if switch_to:1839 self.driver = new_driver1840 if self.headless:1841 # Make sure the invisible browser window is big enough1842 width = settings.HEADLESS_START_WIDTH1843 height = settings.HEADLESS_START_HEIGHT1844 try:1845 self.driver.set_window_size(width, height)1846 self.wait_for_ready_state_complete()1847 except Exception:1848 # This shouldn't fail, but in case it does,1849 # get safely through setUp() so that1850 # WebDrivers can get closed during tearDown().1851 pass1852 else:1853 if self.browser == 'chrome' or self.browser == 'edge':1854 width = settings.CHROME_START_WIDTH1855 height = settings.CHROME_START_HEIGHT1856 try:1857 if self.maximize_option:1858 self.driver.maximize_window()1859 else:1860 self.driver.set_window_size(width, height)1861 self.wait_for_ready_state_complete()1862 except Exception:1863 pass # Keep existing browser resolution1864 elif self.browser == 'firefox':1865 width = settings.CHROME_START_WIDTH1866 try:1867 if self.maximize_option:1868 self.driver.maximize_window()1869 else:1870 self.driver.set_window_size(width, 720)1871 self.wait_for_ready_state_complete()1872 except Exception:1873 pass # Keep existing browser resolution1874 elif self.browser == 'safari':1875 width = settings.CHROME_START_WIDTH1876 if self.maximize_option:1877 try:1878 self.driver.maximize_window()1879 self.wait_for_ready_state_complete()1880 except Exception:1881 pass # Keep existing browser resolution1882 else:1883 try:1884 self.driver.set_window_rect(10, 30, width, 630)1885 except Exception:1886 pass1887 elif self.browser == 'opera':1888 width = settings.CHROME_START_WIDTH1889 if self.maximize_option:1890 try:1891 self.driver.maximize_window()1892 self.wait_for_ready_state_complete()1893 except Exception:1894 pass # Keep existing browser resolution1895 else:1896 try:1897 self.driver.set_window_rect(10, 30, width, 700)1898 except Exception:1899 pass1900 if self.start_page and len(self.start_page) >= 4:1901 if page_utils.is_valid_url(self.start_page):1902 self.open(self.start_page)1903 else:1904 new_start_page = "http://" + self.start_page1905 if page_utils.is_valid_url(new_start_page):1906 self.open(new_start_page)1907 return new_driver1908 def switch_to_driver(self, driver):1909 """ Sets self.driver to the specified driver.1910 You may need this if using self.get_new_driver() in your code. """1911 self.driver = driver1912 def switch_to_default_driver(self):1913 """ Sets self.driver to the default/original driver. """1914 self.driver = self._default_driver1915 def save_screenshot(self, name, folder=None):1916 """ The screenshot will be in PNG format. """1917 return page_actions.save_screenshot(self.driver, name, folder)1918 def save_page_source(self, name, folder=None):1919 """ Saves the page HTML to the current directory (or given subfolder).1920 If the folder specified doesn't exist, it will get created.1921 @Params1922 name - The file name to save the current page's HTML to.1923 folder - The folder to save the file to. (Default = current folder)1924 """1925 return page_actions.save_page_source(self.driver, name, folder)1926 def save_cookies(self, name="cookies.txt"):1927 """ Saves the page cookies to the "saved_cookies" folder. """1928 cookies = self.driver.get_cookies()1929 json_cookies = json.dumps(cookies)1930 if name.endswith('/'):1931 raise Exception("Invalid filename for Cookies!")1932 if '/' in name:1933 name = name.split('/')[-1]1934 if len(name) < 1:1935 raise Exception("Filename for Cookies is too short!")1936 if not name.endswith(".txt"):1937 name = name + ".txt"1938 folder = constants.SavedCookies.STORAGE_FOLDER1939 abs_path = os.path.abspath('.')1940 file_path = abs_path + "/%s" % folder1941 if not os.path.exists(file_path):1942 os.makedirs(file_path)1943 cookies_file_path = "%s/%s" % (file_path, name)1944 cookies_file = codecs.open(cookies_file_path, "w+", encoding="utf-8")1945 cookies_file.writelines(json_cookies)1946 cookies_file.close()1947 def load_cookies(self, name="cookies.txt"):1948 """ Loads the page cookies from the "saved_cookies" folder. """1949 if name.endswith('/'):1950 raise Exception("Invalid filename for Cookies!")1951 if '/' in name:1952 name = name.split('/')[-1]1953 if len(name) < 1:1954 raise Exception("Filename for Cookies is too short!")1955 if not name.endswith(".txt"):1956 name = name + ".txt"1957 folder = constants.SavedCookies.STORAGE_FOLDER1958 abs_path = os.path.abspath('.')1959 file_path = abs_path + "/%s" % folder1960 cookies_file_path = "%s/%s" % (file_path, name)1961 json_cookies = None1962 with open(cookies_file_path, 'r') as f:1963 json_cookies = f.read().strip()1964 cookies = json.loads(json_cookies)1965 for cookie in cookies:1966 if 'expiry' in cookie:1967 del cookie['expiry']1968 self.driver.add_cookie(cookie)1969 def delete_all_cookies(self):1970 """ Deletes all cookies in the web browser.1971 Does NOT delete the saved cookies file. """1972 self.driver.delete_all_cookies()1973 def delete_saved_cookies(self, name="cookies.txt"):1974 """ Deletes the cookies file from the "saved_cookies" folder.1975 Does NOT delete the cookies from the web browser. """1976 if name.endswith('/'):1977 raise Exception("Invalid filename for Cookies!")1978 if '/' in name:1979 name = name.split('/')[-1]1980 if len(name) < 1:1981 raise Exception("Filename for Cookies is too short!")1982 if not name.endswith(".txt"):1983 name = name + ".txt"1984 folder = constants.SavedCookies.STORAGE_FOLDER1985 abs_path = os.path.abspath('.')1986 file_path = abs_path + "/%s" % folder1987 cookies_file_path = "%s/%s" % (file_path, name)1988 if os.path.exists(cookies_file_path):1989 if cookies_file_path.endswith('.txt'):1990 os.remove(cookies_file_path)1991 def wait_for_ready_state_complete(self, timeout=None):1992 try:1993 # If there's an alert, skip1994 self.driver.switch_to.alert1995 return1996 except Exception:1997 # If there's no alert, continue1998 pass1999 if not timeout:2000 timeout = settings.EXTREME_TIMEOUT2001 if self.timeout_multiplier and timeout == settings.EXTREME_TIMEOUT:2002 timeout = self.__get_new_timeout(timeout)2003 is_ready = js_utils.wait_for_ready_state_complete(self.driver, timeout)2004 self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)2005 if self.js_checking_on:2006 self.assert_no_js_errors()2007 if self.ad_block_on:2008 # If the ad_block feature is enabled, then block ads for new URLs2009 current_url = self.get_current_url()2010 if not current_url == self.__last_page_load_url:2011 time.sleep(0.02)2012 self.ad_block()2013 time.sleep(0.01)2014 if self.is_element_present("iframe"):2015 time.sleep(0.07) # iframe ads take slightly longer to load2016 self.ad_block() # Do ad_block on slower-loading iframes2017 self.__last_page_load_url = current_url2018 return is_ready2019 def wait_for_angularjs(self, timeout=None, **kwargs):2020 if not timeout:2021 timeout = settings.LARGE_TIMEOUT2022 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2023 timeout = self.__get_new_timeout(timeout)2024 js_utils.wait_for_angularjs(self.driver, timeout, **kwargs)2025 def sleep(self, seconds):2026 if not sb_config.time_limit:2027 time.sleep(seconds)2028 elif seconds <= 0.3:2029 shared_utils.check_if_time_limit_exceeded()2030 time.sleep(seconds)2031 shared_utils.check_if_time_limit_exceeded()2032 else:2033 start_ms = time.time() * 1000.02034 stop_ms = start_ms + (seconds * 1000.0)2035 for x in range(int(seconds * 5)):2036 shared_utils.check_if_time_limit_exceeded()2037 now_ms = time.time() * 1000.02038 if now_ms >= stop_ms:2039 break2040 time.sleep(0.2)2041 def activate_design_mode(self):2042 # Activate Chrome's Design Mode, which lets you edit a site directly.2043 # See: https://twitter.com/sulco/status/11775591505633443842044 script = ("""document.designMode = 'on';""")2045 self.execute_script(script)2046 def deactivate_design_mode(self):2047 # Deactivate Chrome's Design Mode.2048 script = ("""document.designMode = 'off';""")2049 self.execute_script(script)2050 def activate_jquery(self):2051 """ If "jQuery is not defined", use this method to activate it for use.2052 This happens because jQuery is not always defined on web sites. """2053 js_utils.activate_jquery(self.driver)2054 self.wait_for_ready_state_complete()2055 def __are_quotes_escaped(self, string):2056 return js_utils.are_quotes_escaped(string)2057 def __escape_quotes_if_needed(self, string):2058 return js_utils.escape_quotes_if_needed(string)2059 def bring_to_front(self, selector, by=By.CSS_SELECTOR):2060 """ Updates the Z-index of a page element to bring it into view.2061 Useful when getting a WebDriverException, such as the one below:2062 { Element is not clickable at point (#, #).2063 Other element would receive the click: ... } """2064 selector, by = self.__recalculate_selector(selector, by)2065 self.wait_for_element_visible(2066 selector, by=by, timeout=settings.SMALL_TIMEOUT)2067 try:2068 selector = self.convert_to_css_selector(selector, by=by)2069 except Exception:2070 # Don't run action if can't convert to CSS_Selector for JavaScript2071 return2072 selector = re.escape(selector)2073 selector = self.__escape_quotes_if_needed(selector)2074 script = ("""document.querySelector('%s').style.zIndex = '999999';"""2075 % selector)2076 self.execute_script(script)2077 def highlight_click(self, selector, by=By.CSS_SELECTOR,2078 loops=3, scroll=True):2079 if not self.demo_mode:2080 self.highlight(selector, by=by, loops=loops, scroll=scroll)2081 self.click(selector, by=by)2082 def highlight_update_text(self, selector, text, by=By.CSS_SELECTOR,2083 loops=3, scroll=True):2084 if not self.demo_mode:2085 self.highlight(selector, by=by, loops=loops, scroll=scroll)2086 self.update_text(selector, text, by=by)2087 def highlight(self, selector, by=By.CSS_SELECTOR,2088 loops=None, scroll=True):2089 """ This method uses fancy JavaScript to highlight an element.2090 Used during demo_mode.2091 @Params2092 selector - the selector of the element to find2093 by - the type of selector to search by (Default: CSS)2094 loops - # of times to repeat the highlight animation2095 (Default: 4. Each loop lasts for about 0.18s)2096 scroll - the option to scroll to the element first (Default: True)2097 """2098 selector, by = self.__recalculate_selector(selector, by)2099 element = self.wait_for_element_visible(2100 selector, by=by, timeout=settings.SMALL_TIMEOUT)2101 if not loops:2102 loops = settings.HIGHLIGHTS2103 if scroll:2104 try:2105 if self.browser != "safari":2106 scroll_distance = js_utils.get_scroll_distance_to_element(2107 self.driver, element)2108 if abs(scroll_distance) > SSMD:2109 self.__jquery_slow_scroll_to(selector, by)2110 else:2111 self.__slow_scroll_to_element(element)2112 else:2113 self.__jquery_slow_scroll_to(selector, by)2114 except (StaleElementReferenceException, ENI_Exception, JS_Exc):2115 self.wait_for_ready_state_complete()2116 time.sleep(0.12)2117 element = self.wait_for_element_visible(2118 selector, by=by, timeout=settings.SMALL_TIMEOUT)2119 self.__slow_scroll_to_element(element)2120 try:2121 selector = self.convert_to_css_selector(selector, by=by)2122 except Exception:2123 # Don't highlight if can't convert to CSS_SELECTOR2124 return2125 if self.highlights:2126 loops = self.highlights2127 if self.browser == 'ie':2128 loops = 1 # Override previous setting because IE is slow2129 loops = int(loops)2130 o_bs = '' # original_box_shadow2131 try:2132 style = element.get_attribute('style')2133 except (StaleElementReferenceException, ENI_Exception):2134 self.wait_for_ready_state_complete()2135 time.sleep(0.12)2136 element = self.wait_for_element_visible(2137 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)2138 style = element.get_attribute('style')2139 if style:2140 if 'box-shadow: ' in style:2141 box_start = style.find('box-shadow: ')2142 box_end = style.find(';', box_start) + 12143 original_box_shadow = style[box_start:box_end]2144 o_bs = original_box_shadow2145 if ":contains" not in selector and ":first" not in selector:2146 selector = re.escape(selector)2147 selector = self.__escape_quotes_if_needed(selector)2148 self.__highlight_with_js(selector, loops, o_bs)2149 else:2150 selector = self.__make_css_match_first_element_only(selector)2151 selector = re.escape(selector)2152 selector = self.__escape_quotes_if_needed(selector)2153 try:2154 self.__highlight_with_jquery(selector, loops, o_bs)2155 except Exception:2156 pass # JQuery probably couldn't load. Skip highlighting.2157 time.sleep(0.065)2158 def __highlight_with_js(self, selector, loops, o_bs):2159 js_utils.highlight_with_js(self.driver, selector, loops, o_bs)2160 def __highlight_with_jquery(self, selector, loops, o_bs):2161 js_utils.highlight_with_jquery(self.driver, selector, loops, o_bs)2162 def press_up_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2163 """ Simulates pressing the UP Arrow on the keyboard.2164 By default, "html" will be used as the CSS Selector target.2165 You can specify how many times in-a-row the action happens. """2166 if times < 1:2167 return2168 element = self.wait_for_element_present(selector)2169 self.__demo_mode_highlight_if_active(selector, by)2170 if not self.demo_mode and not self.slow_mode:2171 self.__scroll_to_element(element, selector, by)2172 for i in range(int(times)):2173 try:2174 element.send_keys(Keys.ARROW_UP)2175 except Exception:2176 self.wait_for_ready_state_complete()2177 element = self.wait_for_element_visible(selector)2178 element.send_keys(Keys.ARROW_UP)2179 time.sleep(0.01)2180 if self.slow_mode:2181 time.sleep(0.1)2182 def press_down_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2183 """ Simulates pressing the DOWN Arrow on the keyboard.2184 By default, "html" will be used as the CSS Selector target.2185 You can specify how many times in-a-row the action happens. """2186 if times < 1:2187 return2188 element = self.wait_for_element_present(selector)2189 self.__demo_mode_highlight_if_active(selector, by)2190 if not self.demo_mode and not self.slow_mode:2191 self.__scroll_to_element(element, selector, by)2192 for i in range(int(times)):2193 try:2194 element.send_keys(Keys.ARROW_DOWN)2195 except Exception:2196 self.wait_for_ready_state_complete()2197 element = self.wait_for_element_visible(selector)2198 element.send_keys(Keys.ARROW_DOWN)2199 time.sleep(0.01)2200 if self.slow_mode:2201 time.sleep(0.1)2202 def press_left_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2203 """ Simulates pressing the LEFT Arrow on the keyboard.2204 By default, "html" will be used as the CSS Selector target.2205 You can specify how many times in-a-row the action happens. """2206 if times < 1:2207 return2208 element = self.wait_for_element_present(selector)2209 self.__demo_mode_highlight_if_active(selector, by)2210 if not self.demo_mode and not self.slow_mode:2211 self.__scroll_to_element(element, selector, by)2212 for i in range(int(times)):2213 try:2214 element.send_keys(Keys.ARROW_LEFT)2215 except Exception:2216 self.wait_for_ready_state_complete()2217 element = self.wait_for_element_visible(selector)2218 element.send_keys(Keys.ARROW_LEFT)2219 time.sleep(0.01)2220 if self.slow_mode:2221 time.sleep(0.1)2222 def press_right_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):2223 """ Simulates pressing the RIGHT Arrow on the keyboard.2224 By default, "html" will be used as the CSS Selector target.2225 You can specify how many times in-a-row the action happens. """2226 if times < 1:2227 return2228 element = self.wait_for_element_present(selector)2229 self.__demo_mode_highlight_if_active(selector, by)2230 if not self.demo_mode and not self.slow_mode:2231 self.__scroll_to_element(element, selector, by)2232 for i in range(int(times)):2233 try:2234 element.send_keys(Keys.ARROW_RIGHT)2235 except Exception:2236 self.wait_for_ready_state_complete()2237 element = self.wait_for_element_visible(selector)2238 element.send_keys(Keys.ARROW_RIGHT)2239 time.sleep(0.01)2240 if self.slow_mode:2241 time.sleep(0.1)2242 def scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):2243 ''' Fast scroll to destination '''2244 if not timeout:2245 timeout = settings.SMALL_TIMEOUT2246 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2247 timeout = self.__get_new_timeout(timeout)2248 if self.demo_mode or self.slow_mode:2249 self.slow_scroll_to(selector, by=by, timeout=timeout)2250 return2251 element = self.wait_for_element_visible(2252 selector, by=by, timeout=timeout)2253 try:2254 self.__scroll_to_element(element, selector, by)2255 except (StaleElementReferenceException, ENI_Exception):2256 self.wait_for_ready_state_complete()2257 time.sleep(0.12)2258 element = self.wait_for_element_visible(2259 selector, by=by, timeout=timeout)2260 self.__scroll_to_element(element, selector, by)2261 def slow_scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):2262 ''' Slow motion scroll to destination '''2263 if not timeout:2264 timeout = settings.SMALL_TIMEOUT2265 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2266 timeout = self.__get_new_timeout(timeout)2267 selector, by = self.__recalculate_selector(selector, by)2268 element = self.wait_for_element_visible(2269 selector, by=by, timeout=timeout)2270 try:2271 scroll_distance = js_utils.get_scroll_distance_to_element(2272 self.driver, element)2273 if abs(scroll_distance) > SSMD:2274 self.__jquery_slow_scroll_to(selector, by)2275 else:2276 self.__slow_scroll_to_element(element)2277 except (StaleElementReferenceException, ENI_Exception, JS_Exc):2278 self.wait_for_ready_state_complete()2279 time.sleep(0.12)2280 element = self.wait_for_element_visible(2281 selector, by=by, timeout=timeout)2282 self.__slow_scroll_to_element(element)2283 def scroll_to_top(self):2284 """ Scroll to the top of the page. """2285 scroll_script = "window.scrollTo(0, 0);"2286 try:2287 self.execute_script(scroll_script)2288 time.sleep(0.012)2289 return True2290 except Exception:2291 return False2292 def scroll_to_bottom(self):2293 """ Scroll to the bottom of the page. """2294 scroll_script = "window.scrollTo(0, 10000);"2295 try:2296 self.execute_script(scroll_script)2297 time.sleep(0.012)2298 return True2299 except Exception:2300 return False2301 def click_xpath(self, xpath):2302 # Technically self.click() will automatically detect an xpath selector,2303 # so self.click_xpath() is just a longer name for the same action.2304 self.click(xpath, by=By.XPATH)2305 def js_click(self, selector, by=By.CSS_SELECTOR, all_matches=False):2306 """ Clicks an element using JavaScript.2307 If "all_matches" is False, only the first match is clicked. """2308 selector, by = self.__recalculate_selector(selector, by)2309 if by == By.LINK_TEXT:2310 message = (2311 "Pure JavaScript doesn't support clicking by Link Text. "2312 "You may want to use self.jquery_click() instead, which "2313 "allows this with :contains(), assuming jQuery isn't blocked. "2314 "For now, self.js_click() will use a regular WebDriver click.")2315 logging.debug(message)2316 self.click(selector, by=by)2317 return2318 element = self.wait_for_element_present(2319 selector, by=by, timeout=settings.SMALL_TIMEOUT)2320 if self.is_element_visible(selector, by=by):2321 self.__demo_mode_highlight_if_active(selector, by)2322 if not self.demo_mode and not self.slow_mode:2323 self.__scroll_to_element(element, selector, by)2324 css_selector = self.convert_to_css_selector(selector, by=by)2325 css_selector = re.escape(css_selector) # Add "\\" to special chars2326 css_selector = self.__escape_quotes_if_needed(css_selector)2327 if not all_matches:2328 if ":contains\\(" not in css_selector:2329 self.__js_click(selector, by=by)2330 else:2331 click_script = """jQuery('%s')[0].click();""" % css_selector2332 self.safe_execute_script(click_script)2333 else:2334 if ":contains\\(" not in css_selector:2335 self.__js_click_all(selector, by=by)2336 else:2337 click_script = """jQuery('%s').click();""" % css_selector2338 self.safe_execute_script(click_script)2339 self.wait_for_ready_state_complete()2340 self.__demo_mode_pause_if_active()2341 def js_click_all(self, selector, by=By.CSS_SELECTOR):2342 """ Clicks all matching elements using pure JS. (No jQuery) """2343 self.js_click(selector, by=By.CSS_SELECTOR, all_matches=True)2344 def jquery_click(self, selector, by=By.CSS_SELECTOR):2345 """ Clicks an element using jQuery. Different from using pure JS. """2346 selector, by = self.__recalculate_selector(selector, by)2347 self.wait_for_element_present(2348 selector, by=by, timeout=settings.SMALL_TIMEOUT)2349 if self.is_element_visible(selector, by=by):2350 self.__demo_mode_highlight_if_active(selector, by)2351 selector = self.convert_to_css_selector(selector, by=by)2352 selector = self.__make_css_match_first_element_only(selector)2353 click_script = """jQuery('%s')[0].click();""" % selector2354 self.safe_execute_script(click_script)2355 self.__demo_mode_pause_if_active()2356 def jquery_click_all(self, selector, by=By.CSS_SELECTOR):2357 """ Clicks all matching elements using jQuery. """2358 selector, by = self.__recalculate_selector(selector, by)2359 self.wait_for_element_present(2360 selector, by=by, timeout=settings.SMALL_TIMEOUT)2361 if self.is_element_visible(selector, by=by):2362 self.__demo_mode_highlight_if_active(selector, by)2363 css_selector = self.convert_to_css_selector(selector, by=by)2364 click_script = """jQuery('%s').click();""" % css_selector2365 self.safe_execute_script(click_script)2366 self.__demo_mode_pause_if_active()2367 def hide_element(self, selector, by=By.CSS_SELECTOR):2368 """ Hide the first element on the page that matches the selector. """2369 selector, by = self.__recalculate_selector(selector, by)2370 selector = self.convert_to_css_selector(selector, by=by)2371 selector = self.__make_css_match_first_element_only(selector)2372 hide_script = """jQuery('%s').hide();""" % selector2373 self.safe_execute_script(hide_script)2374 def hide_elements(self, selector, by=By.CSS_SELECTOR):2375 """ Hide all elements on the page that match the selector. """2376 selector, by = self.__recalculate_selector(selector, by)2377 selector = self.convert_to_css_selector(selector, by=by)2378 hide_script = """jQuery('%s').hide();""" % selector2379 self.safe_execute_script(hide_script)2380 def show_element(self, selector, by=By.CSS_SELECTOR):2381 """ Show the first element on the page that matches the selector. """2382 selector, by = self.__recalculate_selector(selector, by)2383 selector = self.convert_to_css_selector(selector, by=by)2384 selector = self.__make_css_match_first_element_only(selector)2385 show_script = """jQuery('%s').show(0);""" % selector2386 self.safe_execute_script(show_script)2387 def show_elements(self, selector, by=By.CSS_SELECTOR):2388 """ Show all elements on the page that match the selector. """2389 selector, by = self.__recalculate_selector(selector, by)2390 selector = self.convert_to_css_selector(selector, by=by)2391 show_script = """jQuery('%s').show(0);""" % selector2392 self.safe_execute_script(show_script)2393 def remove_element(self, selector, by=By.CSS_SELECTOR):2394 """ Remove the first element on the page that matches the selector. """2395 selector, by = self.__recalculate_selector(selector, by)2396 selector = self.convert_to_css_selector(selector, by=by)2397 selector = self.__make_css_match_first_element_only(selector)2398 remove_script = """jQuery('%s').remove();""" % selector2399 self.safe_execute_script(remove_script)2400 def remove_elements(self, selector, by=By.CSS_SELECTOR):2401 """ Remove all elements on the page that match the selector. """2402 selector, by = self.__recalculate_selector(selector, by)2403 selector = self.convert_to_css_selector(selector, by=by)2404 remove_script = """jQuery('%s').remove();""" % selector2405 self.safe_execute_script(remove_script)2406 def ad_block(self):2407 """ Block ads that appear on the current web page. """2408 from seleniumbase.config import ad_block_list2409 for css_selector in ad_block_list.AD_BLOCK_LIST:2410 css_selector = re.escape(css_selector) # Add "\\" to special chars2411 css_selector = self.__escape_quotes_if_needed(css_selector)2412 script = ("""var $elements = document.querySelectorAll('%s');2413 var index = 0, length = $elements.length;2414 for(; index < length; index++){2415 $elements[index].remove();}"""2416 % css_selector)2417 try:2418 self.execute_script(script)2419 except Exception:2420 pass # Don't fail test if ad_blocking fails2421 def get_domain_url(self, url):2422 return page_utils.get_domain_url(url)2423 def get_beautiful_soup(self, source=None):2424 """ BeautifulSoup is a toolkit for dissecting an HTML document2425 and extracting what you need. It's great for screen-scraping! """2426 from bs4 import BeautifulSoup2427 if not source:2428 self.wait_for_ready_state_complete()2429 source = self.get_page_source()2430 soup = BeautifulSoup(source, "html.parser")2431 return soup2432 def get_unique_links(self):2433 """ Get all unique links in the html of the page source.2434 Page links include those obtained from:2435 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2436 page_url = self.get_current_url()2437 soup = self.get_beautiful_soup(self.get_page_source())2438 links = page_utils._get_unique_links(page_url, soup)2439 return links2440 def get_link_status_code(self, link, allow_redirects=False, timeout=5):2441 """ Get the status code of a link.2442 If the timeout is exceeded, will return a 404.2443 For a list of available status codes, see:2444 https://en.wikipedia.org/wiki/List_of_HTTP_status_codes """2445 status_code = page_utils._get_link_status_code(2446 link, allow_redirects=allow_redirects, timeout=timeout)2447 return status_code2448 def assert_link_status_code_is_not_404(self, link):2449 status_code = str(self.get_link_status_code(link))2450 bad_link_str = 'Error: "%s" returned a 404!' % link2451 self.assertNotEqual(status_code, "404", bad_link_str)2452 def assert_no_404_errors(self, multithreaded=True):2453 """ Assert no 404 errors from page links obtained from:2454 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2455 all_links = self.get_unique_links()2456 links = []2457 for link in all_links:2458 if "javascript:" not in link and "mailto:" not in link:2459 links.append(link)2460 if multithreaded:2461 from multiprocessing.dummy import Pool as ThreadPool2462 pool = ThreadPool(10)2463 pool.map(self.assert_link_status_code_is_not_404, links)2464 pool.close()2465 pool.join()2466 else:2467 for link in links:2468 self.assert_link_status_code_is_not_404(link)2469 if self.demo_mode:2470 a_t = "ASSERT NO 404 ERRORS"2471 if self._language != "English":2472 from seleniumbase.fixtures.words import SD2473 a_t = SD.translate_assert_no_404_errors(self._language)2474 messenger_post = ("%s" % a_t)2475 self.__highlight_with_assert_success(messenger_post, "html")2476 def print_unique_links_with_status_codes(self):2477 """ Finds all unique links in the html of the page source2478 and then prints out those links with their status codes.2479 Format: ["link" -> "status_code"] (per line)2480 Page links include those obtained from:2481 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2482 page_url = self.get_current_url()2483 soup = self.get_beautiful_soup(self.get_page_source())2484 page_utils._print_unique_links_with_status_codes(page_url, soup)2485 def __fix_unicode_conversion(self, text):2486 """ Fixing Chinese characters when converting from PDF to HTML. """2487 if sys.version_info[0] < 3:2488 # Update encoding for Python 2 users2489 reload(sys) # noqa2490 sys.setdefaultencoding('utf8')2491 text = text.replace(u'\u2f8f', u'\u884c')2492 text = text.replace(u'\u2f45', u'\u65b9')2493 text = text.replace(u'\u2f08', u'\u4eba')2494 text = text.replace(u'\u2f70', u'\u793a')2495 return text2496 def get_pdf_text(self, pdf, page=None, maxpages=None,2497 password=None, codec='utf-8', wrap=False, nav=False,2498 override=False):2499 """ Gets text from a PDF file.2500 PDF can be either a URL or a file path on the local file system.2501 @Params2502 pdf - The URL or file path of the PDF file.2503 page - The page number (or a list of page numbers) of the PDF.2504 If a page number is provided, looks only at that page.2505 (1 is the first page, 2 is the second page, etc.)2506 If no page number is provided, returns all PDF text.2507 maxpages - Instead of providing a page number, you can provide2508 the number of pages to use from the beginning.2509 password - If the PDF is password-protected, enter it here.2510 codec - The compression format for character encoding.2511 (The default codec used by this method is 'utf-8'.)2512 wrap - Replaces ' \n' with ' ' so that individual sentences2513 from a PDF don't get broken up into seperate lines when2514 getting converted into text format.2515 nav - If PDF is a URL, navigates to the URL in the browser first.2516 (Not needed because the PDF will be downloaded anyway.)2517 override - If the PDF file to be downloaded already exists in the2518 downloaded_files/ folder, that PDF will be used2519 instead of downloading it again. """2520 import warnings2521 with warnings.catch_warnings():2522 warnings.simplefilter("ignore", category=UserWarning)2523 from pdfminer.high_level import extract_text2524 if not password:2525 password = ''2526 if not maxpages:2527 maxpages = 02528 if not pdf.lower().endswith('.pdf'):2529 raise Exception("%s is not a PDF file! (Expecting a .pdf)" % pdf)2530 file_path = None2531 if page_utils.is_valid_url(pdf):2532 if nav:2533 if self.get_current_url() != pdf:2534 self.open(pdf)2535 file_name = pdf.split('/')[-1]2536 file_path = self.get_downloads_folder() + '/' + file_name2537 if not os.path.exists(file_path):2538 self.download_file(pdf)2539 elif override:2540 self.download_file(pdf)2541 else:2542 if not os.path.exists(pdf):2543 raise Exception("%s is not a valid URL or file path!" % pdf)2544 file_path = os.path.abspath(pdf)2545 page_search = None # (Pages are delimited by '\x0c')2546 if type(page) is list:2547 pages = page2548 page_search = []2549 for page in pages:2550 page_search.append(page - 1)2551 elif type(page) is int:2552 page = page - 12553 if page < 0:2554 page = 02555 page_search = [page]2556 else:2557 page_search = None2558 pdf_text = extract_text(2559 file_path, password='', page_numbers=page_search,2560 maxpages=maxpages, caching=False, codec=codec)2561 pdf_text = self.__fix_unicode_conversion(pdf_text)2562 if wrap:2563 pdf_text = pdf_text.replace(' \n', ' ')2564 pdf_text = pdf_text.strip() # Remove leading and trailing whitespace2565 return pdf_text2566 def assert_pdf_text(self, pdf, text, page=None, maxpages=None,2567 password=None, codec='utf-8', wrap=True, nav=False,2568 override=False):2569 """ Asserts text in a PDF file.2570 PDF can be either a URL or a file path on the local file system.2571 @Params2572 pdf - The URL or file path of the PDF file.2573 text - The expected text to verify in the PDF.2574 page - The page number of the PDF to use (optional).2575 If a page number is provided, looks only at that page.2576 (1 is the first page, 2 is the second page, etc.)2577 If no page number is provided, looks at all the pages.2578 maxpages - Instead of providing a page number, you can provide2579 the number of pages to use from the beginning.2580 password - If the PDF is password-protected, enter it here.2581 codec - The compression format for character encoding.2582 (The default codec used by this method is 'utf-8'.)2583 wrap - Replaces ' \n' with ' ' so that individual sentences2584 from a PDF don't get broken up into seperate lines when2585 getting converted into text format.2586 nav - If PDF is a URL, navigates to the URL in the browser first.2587 (Not needed because the PDF will be downloaded anyway.)2588 override - If the PDF file to be downloaded already exists in the2589 downloaded_files/ folder, that PDF will be used2590 instead of downloading it again. """2591 text = self.__fix_unicode_conversion(text)2592 if not codec:2593 codec = 'utf-8'2594 pdf_text = self.get_pdf_text(2595 pdf, page=page, maxpages=maxpages, password=password, codec=codec,2596 wrap=wrap, nav=nav, override=override)2597 if type(page) is int:2598 if text not in pdf_text:2599 raise Exception("PDF [%s] is missing expected text [%s] on "2600 "page [%s]!" % (pdf, text, page))2601 else:2602 if text not in pdf_text:2603 raise Exception("PDF [%s] is missing expected text [%s]!"2604 "" % (pdf, text))2605 return True2606 def create_folder(self, folder):2607 """ Creates a folder of the given name if it doesn't already exist. """2608 if folder.endswith("/"):2609 folder = folder[:-1]2610 if len(folder) < 1:2611 raise Exception("Minimum folder name length = 1.")2612 if not os.path.exists(folder):2613 try:2614 os.makedirs(folder)2615 except Exception:2616 pass2617 def choose_file(self, selector, file_path, by=By.CSS_SELECTOR,2618 timeout=None):2619 """ This method is used to choose a file to upload to a website.2620 It works by populating a file-chooser "input" field of type="file".2621 A relative file_path will get converted into an absolute file_path.2622 Example usage:2623 self.choose_file('input[type="file"]', "my_dir/my_file.txt")2624 """2625 if not timeout:2626 timeout = settings.LARGE_TIMEOUT2627 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2628 timeout = self.__get_new_timeout(timeout)2629 selector, by = self.__recalculate_selector(selector, by)2630 abs_path = os.path.abspath(file_path)2631 self.add_text(selector, abs_path, by=by, timeout=timeout)2632 def save_element_as_image_file(self, selector, file_name, folder=None):2633 """ Take a screenshot of an element and save it as an image file.2634 If no folder is specified, will save it to the current folder. """2635 element = self.wait_for_element_visible(selector)2636 element_png = element.screenshot_as_png2637 if len(file_name.split('.')[0]) < 1:2638 raise Exception("Error: file_name length must be > 0.")2639 if not file_name.endswith(".png"):2640 file_name = file_name + ".png"2641 image_file_path = None2642 if folder:2643 if folder.endswith("/"):2644 folder = folder[:-1]2645 if len(folder) > 0:2646 self.create_folder(folder)2647 image_file_path = "%s/%s" % (folder, file_name)2648 if not image_file_path:2649 image_file_path = file_name2650 with open(image_file_path, "wb") as file:2651 file.write(element_png)2652 def download_file(self, file_url, destination_folder=None):2653 """ Downloads the file from the url to the destination folder.2654 If no destination folder is specified, the default one is used.2655 (The default downloads folder = "./downloaded_files") """2656 if not destination_folder:2657 destination_folder = constants.Files.DOWNLOADS_FOLDER2658 if not os.path.exists(destination_folder):2659 os.makedirs(destination_folder)2660 page_utils._download_file_to(file_url, destination_folder)2661 def save_file_as(self, file_url, new_file_name, destination_folder=None):2662 """ Similar to self.download_file(), except that you get to rename the2663 file being downloaded to whatever you want. """2664 if not destination_folder:2665 destination_folder = constants.Files.DOWNLOADS_FOLDER2666 page_utils._download_file_to(2667 file_url, destination_folder, new_file_name)2668 def save_data_as(self, data, file_name, destination_folder=None):2669 """ Saves the data specified to a file of the name specified.2670 If no destination folder is specified, the default one is used.2671 (The default downloads folder = "./downloaded_files") """2672 if not destination_folder:2673 destination_folder = constants.Files.DOWNLOADS_FOLDER2674 page_utils._save_data_as(data, destination_folder, file_name)2675 def get_downloads_folder(self):2676 """ Returns the OS path of the Downloads Folder.2677 (Works with Chrome and Firefox only, for now.) """2678 from seleniumbase.core import download_helper2679 return download_helper.get_downloads_folder()2680 def get_path_of_downloaded_file(self, file):2681 """ Returns the OS path of the downloaded file. """2682 return os.path.join(self.get_downloads_folder(), file)2683 def is_downloaded_file_present(self, file):2684 """ Checks if the file exists in the Downloads Folder. """2685 return os.path.exists(self.get_path_of_downloaded_file(file))2686 def assert_downloaded_file(self, file, timeout=None):2687 """ Asserts that the file exists in the Downloads Folder. """2688 if not timeout:2689 timeout = settings.LARGE_TIMEOUT2690 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2691 timeout = self.__get_new_timeout(timeout)2692 start_ms = time.time() * 1000.02693 stop_ms = start_ms + (timeout * 1000.0)2694 for x in range(int(timeout)):2695 shared_utils.check_if_time_limit_exceeded()2696 try:2697 self.assertTrue(2698 os.path.exists(self.get_path_of_downloaded_file(file)),2699 "File [%s] was not found in the downloads folder [%s]!"2700 "" % (file, self.get_downloads_folder()))2701 if self.demo_mode:2702 messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)2703 js_utils.post_messenger_success_message(2704 self.driver, messenger_post, self.message_duration)2705 return2706 except Exception:2707 now_ms = time.time() * 1000.02708 if now_ms >= stop_ms:2709 break2710 time.sleep(1)2711 if not os.path.exists(self.get_path_of_downloaded_file(file)):2712 message = (2713 "File {%s} was not found in the downloads folder {%s} "2714 "after %s seconds! (Or the download didn't complete!)"2715 "" % (file, self.get_downloads_folder(), timeout))2716 page_actions.timeout_exception("NoSuchFileException", message)2717 if self.demo_mode:2718 messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)2719 js_utils.post_messenger_success_message(2720 self.driver, messenger_post, self.message_duration)2721 def assert_true(self, expr, msg=None):2722 """ Asserts that the expression is True.2723 Will raise an exception if the statement if False. """2724 self.assertTrue(expr, msg=msg)2725 def assert_false(self, expr, msg=None):2726 """ Asserts that the expression is False.2727 Will raise an exception if the statement if True. """2728 self.assertFalse(expr, msg=msg)2729 def assert_equal(self, first, second, msg=None):2730 """ Asserts that the two values are equal.2731 Will raise an exception if the values are not equal. """2732 self.assertEqual(first, second, msg=msg)2733 def assert_not_equal(self, first, second, msg=None):2734 """ Asserts that the two values are not equal.2735 Will raise an exception if the values are equal. """2736 self.assertNotEqual(first, second, msg=msg)2737 def assert_raises(self, *args, **kwargs):2738 """ Asserts that the following block of code raises an exception.2739 Will raise an exception if the block of code has no exception.2740 Usage Example =>2741 # Verify that the expected exception is raised.2742 with self.assert_raises(Exception):2743 raise Exception("Expected Exception!")2744 """2745 self.assertRaises(*args, **kwargs)2746 def assert_title(self, title):2747 """ Asserts that the web page title matches the expected title. """2748 self.wait_for_ready_state_complete()2749 expected = title2750 actual = self.get_page_title()2751 try:2752 self.assertEqual(expected, actual, "Expected page title [%s] "2753 "does not match the actual page title [%s]!"2754 "" % (expected, actual))2755 except Exception:2756 self.wait_for_ready_state_complete()2757 self.sleep(settings.MINI_TIMEOUT)2758 actual = self.get_page_title()2759 self.assertEqual(expected, actual, "Expected page title [%s] "2760 "does not match the actual page title [%s]!"2761 "" % (expected, actual))2762 if self.demo_mode:2763 a_t = "ASSERT TITLE"2764 if self._language != "English":2765 from seleniumbase.fixtures.words import SD2766 a_t = SD.translate_assert_title(self._language)2767 messenger_post = ("%s: {%s}" % (a_t, title))2768 self.__highlight_with_assert_success(messenger_post, "html")2769 def assert_no_js_errors(self):2770 """ Asserts that there are no JavaScript "SEVERE"-level page errors.2771 Works ONLY for Chrome (non-headless) and Chrome-based browsers.2772 Does NOT work on Firefox, Edge, IE, and some other browsers:2773 * See https://github.com/SeleniumHQ/selenium/issues/11612774 Based on the following Stack Overflow solution:2775 * https://stackoverflow.com/a/41150512/7058266 """2776 time.sleep(0.1) # May take a moment for errors to appear after loads.2777 try:2778 browser_logs = self.driver.get_log('browser')2779 except (ValueError, WebDriverException):2780 # If unable to get browser logs, skip the assert and return.2781 return2782 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"2783 errors = []2784 for entry in browser_logs:2785 if entry['level'] == 'SEVERE':2786 if messenger_library not in entry['message']:2787 # Add errors if not caused by SeleniumBase dependencies2788 errors.append(entry)2789 if len(errors) > 0:2790 current_url = self.get_current_url()2791 raise Exception(2792 "JavaScript errors found on %s => %s" % (current_url, errors))2793 if self.demo_mode:2794 if (self.browser == 'chrome' or self.browser == 'edge'):2795 a_t = "ASSERT NO JS ERRORS"2796 if self._language != "English":2797 from seleniumbase.fixtures.words import SD2798 a_t = SD.translate_assert_no_js_errors(self._language)2799 messenger_post = ("%s" % a_t)2800 self.__highlight_with_assert_success(messenger_post, "html")2801 def __activate_html_inspector(self):2802 self.wait_for_ready_state_complete()2803 time.sleep(0.05)2804 js_utils.activate_html_inspector(self.driver)2805 def inspect_html(self):2806 """ Inspects the Page HTML with HTML-Inspector.2807 (https://github.com/philipwalton/html-inspector)2808 (https://cdnjs.com/libraries/html-inspector)2809 Prints the results and also returns them. """2810 self.__activate_html_inspector()2811 script = ("""HTMLInspector.inspect();""")2812 self.execute_script(script)2813 time.sleep(0.1)2814 browser_logs = []2815 try:2816 browser_logs = self.driver.get_log('browser')2817 except (ValueError, WebDriverException):2818 # If unable to get browser logs, skip the assert and return.2819 return("(Unable to Inspect HTML! -> Only works on Chrome!)")2820 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"2821 url = self.get_current_url()2822 header = '\n* HTML Inspection Results: %s' % url2823 results = [header]2824 row_count = 02825 for entry in browser_logs:2826 message = entry['message']2827 if "0:6053 " in message:2828 message = message.split("0:6053")[1]2829 message = message.replace("\\u003C", "<")2830 if message.startswith(' "') and message.count('"') == 2:2831 message = message.split('"')[1]2832 message = "X - " + message2833 if messenger_library not in message:2834 if message not in results:2835 results.append(message)2836 row_count += 12837 if row_count > 0:2838 results.append('* (See the Console output for details!)')2839 else:2840 results.append('* (No issues detected!)')2841 results = '\n'.join(results)2842 print(results)2843 return(results)2844 def get_google_auth_password(self, totp_key=None):2845 """ Returns a time-based one-time password based on the2846 Google Authenticator password algorithm. Works with Authy.2847 If "totp_key" is not specified, defaults to using the one2848 provided in seleniumbase/config/settings.py2849 Google Auth passwords expire and change at 30-second intervals.2850 If the fetched password expires in the next 1.5 seconds, waits2851 for a new one before returning it (may take up to 1.5 seconds).2852 See https://pyotp.readthedocs.io/en/latest/ for details. """2853 import pyotp2854 if not totp_key:2855 totp_key = settings.TOTP_KEY2856 epoch_interval = time.time() / 30.02857 cycle_lifespan = float(epoch_interval) - int(epoch_interval)2858 if float(cycle_lifespan) > 0.95:2859 # Password expires in the next 1.5 seconds. Wait for a new one.2860 for i in range(30):2861 time.sleep(0.05)2862 epoch_interval = time.time() / 30.02863 cycle_lifespan = float(epoch_interval) - int(epoch_interval)2864 if not float(cycle_lifespan) > 0.95:2865 # The new password cycle has begun2866 break2867 totp = pyotp.TOTP(totp_key)2868 return str(totp.now())2869 def convert_css_to_xpath(self, css):2870 return css_to_xpath.convert_css_to_xpath(css)2871 def convert_xpath_to_css(self, xpath):2872 return xpath_to_css.convert_xpath_to_css(xpath)2873 def convert_to_css_selector(self, selector, by):2874 """ This method converts a selector to a CSS_SELECTOR.2875 jQuery commands require a CSS_SELECTOR for finding elements.2876 This method should only be used for jQuery/JavaScript actions.2877 Pure JavaScript doesn't support using a:contains("LINK_TEXT"). """2878 if by == By.CSS_SELECTOR:2879 return selector2880 elif by == By.ID:2881 return '#%s' % selector2882 elif by == By.CLASS_NAME:2883 return '.%s' % selector2884 elif by == By.NAME:2885 return '[name="%s"]' % selector2886 elif by == By.TAG_NAME:2887 return selector2888 elif by == By.XPATH:2889 return self.convert_xpath_to_css(selector)2890 elif by == By.LINK_TEXT:2891 return 'a:contains("%s")' % selector2892 elif by == By.PARTIAL_LINK_TEXT:2893 return 'a:contains("%s")' % selector2894 else:2895 raise Exception(2896 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (2897 selector, by))2898 def set_value(self, selector, text, by=By.CSS_SELECTOR, timeout=None):2899 """ This method uses JavaScript to update a text field. """2900 if not timeout:2901 timeout = settings.LARGE_TIMEOUT2902 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2903 timeout = self.__get_new_timeout(timeout)2904 selector, by = self.__recalculate_selector(selector, by)2905 orginal_selector = selector2906 css_selector = self.convert_to_css_selector(selector, by=by)2907 self.__demo_mode_highlight_if_active(orginal_selector, by)2908 if not self.demo_mode and not self.slow_mode:2909 self.scroll_to(orginal_selector, by=by, timeout=timeout)2910 if type(text) is int or type(text) is float:2911 text = str(text)2912 value = re.escape(text)2913 value = self.__escape_quotes_if_needed(value)2914 css_selector = re.escape(css_selector) # Add "\\" to special chars2915 css_selector = self.__escape_quotes_if_needed(css_selector)2916 if ":contains\\(" not in css_selector:2917 script = (2918 """document.querySelector('%s').value='%s';"""2919 "" % (css_selector, value))2920 self.execute_script(script)2921 else:2922 script = (2923 """jQuery('%s')[0].value='%s';"""2924 "" % (css_selector, value))2925 self.safe_execute_script(script)2926 if text.endswith('\n'):2927 element = self.wait_for_element_present(2928 orginal_selector, by=by, timeout=timeout)2929 element.send_keys(Keys.RETURN)2930 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2931 self.wait_for_ready_state_complete()2932 self.__demo_mode_pause_if_active()2933 def js_update_text(self, selector, text, by=By.CSS_SELECTOR,2934 timeout=None):2935 """ JavaScript + send_keys are used to update a text field.2936 Performs self.set_value() and triggers event listeners.2937 If text ends in "\n", set_value() presses RETURN after.2938 Works faster than send_keys() alone due to the JS call.2939 """2940 if not timeout:2941 timeout = settings.LARGE_TIMEOUT2942 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2943 timeout = self.__get_new_timeout(timeout)2944 selector, by = self.__recalculate_selector(selector, by)2945 if type(text) is int or type(text) is float:2946 text = str(text)2947 self.set_value(2948 selector, text, by=by, timeout=timeout)2949 if not text.endswith('\n'):2950 try:2951 element = page_actions.wait_for_element_present(2952 self.driver, selector, by, timeout=0.2)2953 element.send_keys(" \b")2954 except Exception:2955 pass2956 def js_type(self, selector, text, by=By.CSS_SELECTOR,2957 timeout=None):2958 """ Same as self.js_update_text()2959 JavaScript + send_keys are used to update a text field.2960 Performs self.set_value() and triggers event listeners.2961 If text ends in "\n", set_value() presses RETURN after.2962 Works faster than send_keys() alone due to the JS call.2963 """2964 if not timeout:2965 timeout = settings.LARGE_TIMEOUT2966 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2967 timeout = self.__get_new_timeout(timeout)2968 selector, by = self.__recalculate_selector(selector, by)2969 if type(text) is int or type(text) is float:2970 text = str(text)2971 self.set_value(2972 selector, text, by=by, timeout=timeout)2973 if not text.endswith('\n'):2974 try:2975 element = page_actions.wait_for_element_present(2976 self.driver, selector, by, timeout=0.2)2977 element.send_keys(" \b")2978 except Exception:2979 pass2980 def set_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None):2981 """ Same as self.js_update_text()2982 JavaScript + send_keys are used to update a text field.2983 Performs self.set_value() and triggers event listeners.2984 If text ends in "\n", set_value() presses RETURN after.2985 Works faster than send_keys() alone due to the JS call. """2986 if not timeout:2987 timeout = settings.LARGE_TIMEOUT2988 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2989 timeout = self.__get_new_timeout(timeout)2990 selector, by = self.__recalculate_selector(selector, by)2991 if type(text) is int or type(text) is float:2992 text = str(text)2993 self.set_value(2994 selector, text, by=by, timeout=timeout)2995 if not text.endswith('\n'):2996 try:2997 element = page_actions.wait_for_element_present(2998 self.driver, selector, by, timeout=0.2)2999 element.send_keys(" \b")3000 except Exception:3001 pass3002 def jquery_update_text(self, selector, text, by=By.CSS_SELECTOR,3003 timeout=None):3004 """ This method uses jQuery to update a text field.3005 If the text string ends with the newline character,3006 Selenium finishes the call, which simulates pressing3007 {Enter/Return} after the text is entered. """3008 if not timeout:3009 timeout = settings.LARGE_TIMEOUT3010 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3011 timeout = self.__get_new_timeout(timeout)3012 selector, by = self.__recalculate_selector(selector, by)3013 element = self.wait_for_element_visible(3014 selector, by=by, timeout=timeout)3015 self.__demo_mode_highlight_if_active(selector, by)3016 self.scroll_to(selector, by=by)3017 selector = self.convert_to_css_selector(selector, by=by)3018 selector = self.__make_css_match_first_element_only(selector)3019 selector = self.__escape_quotes_if_needed(selector)3020 text = re.escape(text)3021 text = self.__escape_quotes_if_needed(text)3022 update_text_script = """jQuery('%s').val('%s');""" % (3023 selector, text)3024 self.safe_execute_script(update_text_script)3025 if text.endswith('\n'):3026 element.send_keys('\n')3027 self.__demo_mode_pause_if_active()3028 def set_time_limit(self, time_limit):3029 if time_limit:3030 try:3031 sb_config.time_limit = float(time_limit)3032 except Exception:3033 sb_config.time_limit = None3034 else:3035 sb_config.time_limit = None3036 if sb_config.time_limit and sb_config.time_limit > 0:3037 sb_config.time_limit_ms = int(sb_config.time_limit * 1000.0)3038 self.time_limit = sb_config.time_limit3039 else:3040 self.time_limit = None3041 sb_config.time_limit = None3042 sb_config.time_limit_ms = None3043 def skip(self, reason=""):3044 """ Mark the test as Skipped. """3045 self.skipTest(reason)3046 ############3047 # Application "Local Storage" controls3048 def set_local_storage_item(self, key, value):3049 self.execute_script(3050 "window.localStorage.setItem('{}', '{}');".format(key, value))3051 def get_local_storage_item(self, key):3052 return self.execute_script(3053 "return window.localStorage.getItem('{}');".format(key))3054 def remove_local_storage_item(self, key):3055 self.execute_script(3056 "window.localStorage.removeItem('{}');".format(key))3057 def clear_local_storage(self):3058 self.execute_script("window.localStorage.clear();")3059 def get_local_storage_keys(self):3060 return self.execute_script(3061 "var ls = window.localStorage, keys = []; "3062 "for (var i = 0; i < ls.length; ++i) "3063 " keys[i] = ls.key(i); "3064 "return keys;")3065 def get_local_storage_items(self):3066 return self.execute_script(3067 r"var ls = window.localStorage, items = {}; "3068 "for (var i = 0, k; i < ls.length; ++i) "3069 " items[k = ls.key(i)] = ls.getItem(k); "3070 "return items;")3071 # Application "Session Storage" controls3072 def set_session_storage_item(self, key, value):3073 self.execute_script(3074 "window.sessionStorage.setItem('{}', '{}');".format(key, value))3075 def get_session_storage_item(self, key):3076 return self.execute_script(3077 "return window.sessionStorage.getItem('{}');".format(key))3078 def remove_session_storage_item(self, key):3079 self.execute_script(3080 "window.sessionStorage.removeItem('{}');".format(key))3081 def clear_session_storage(self):3082 self.execute_script("window.sessionStorage.clear();")3083 def get_session_storage_keys(self):3084 return self.execute_script(3085 "var ls = window.sessionStorage, keys = []; "3086 "for (var i = 0; i < ls.length; ++i) "3087 " keys[i] = ls.key(i); "3088 "return keys;")3089 def get_session_storage_items(self):3090 return self.execute_script(3091 r"var ls = window.sessionStorage, items = {}; "3092 "for (var i = 0, k; i < ls.length; ++i) "3093 " items[k = ls.key(i)] = ls.getItem(k); "3094 "return items;")3095 ############3096 # Duplicates (Avoids name confusion when migrating from other frameworks.)3097 def open_url(self, url):3098 """ Same as self.open() """3099 self.open(url)3100 def visit(self, url):3101 """ Same as self.open() """3102 self.open(url)3103 def visit_url(self, url):3104 """ Same as self.open() """3105 self.open(url)3106 def goto(self, url):3107 """ Same as self.open() """3108 self.open(url)3109 def go_to(self, url):3110 """ Same as self.open() """3111 self.open(url)3112 def reload(self):3113 """ Same as self.refresh_page() """3114 self.refresh_page()3115 def reload_page(self):3116 """ Same as self.refresh_page() """3117 self.refresh_page()3118 def input(self, selector, text, by=By.CSS_SELECTOR,3119 timeout=None, retry=False):3120 """ Same as self.update_text() """3121 if not timeout:3122 timeout = settings.LARGE_TIMEOUT3123 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3124 timeout = self.__get_new_timeout(timeout)3125 selector, by = self.__recalculate_selector(selector, by)3126 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)3127 def write(self, selector, text, by=By.CSS_SELECTOR,3128 timeout=None, retry=False):3129 """ Same as self.update_text() """3130 if not timeout:3131 timeout = settings.LARGE_TIMEOUT3132 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3133 timeout = self.__get_new_timeout(timeout)3134 selector, by = self.__recalculate_selector(selector, by)3135 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)3136 def send_keys(self, selector, text, by=By.CSS_SELECTOR, timeout=None):3137 """ Same as self.add_text() """3138 if not timeout:3139 timeout = settings.LARGE_TIMEOUT3140 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3141 timeout = self.__get_new_timeout(timeout)3142 selector, by = self.__recalculate_selector(selector, by)3143 self.add_text(selector, text, by=by, timeout=timeout)3144 def click_link(self, link_text, timeout=None):3145 """ Same as self.click_link_text() """3146 if not timeout:3147 timeout = settings.SMALL_TIMEOUT3148 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3149 timeout = self.__get_new_timeout(timeout)3150 self.click_link_text(link_text, timeout=timeout)3151 def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR,3152 timeout=None):3153 """ Same as self.wait_for_element() """3154 if not timeout:3155 timeout = settings.LARGE_TIMEOUT3156 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3157 timeout = self.__get_new_timeout(timeout)3158 selector, by = self.__recalculate_selector(selector, by)3159 return page_actions.wait_for_element_visible(3160 self.driver, selector, by, timeout)3161 def wait_for_element_not_present(self, selector, by=By.CSS_SELECTOR,3162 timeout=None):3163 """ Same as self.wait_for_element_absent()3164 Waits for an element to no longer appear in the HTML of a page.3165 A hidden element still counts as appearing in the page HTML.3166 If an element with "hidden" status is acceptable,3167 use wait_for_element_not_visible() instead. """3168 if not timeout:3169 timeout = settings.LARGE_TIMEOUT3170 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3171 timeout = self.__get_new_timeout(timeout)3172 selector, by = self.__recalculate_selector(selector, by)3173 return page_actions.wait_for_element_absent(3174 self.driver, selector, by, timeout)3175 def assert_element_not_present(self, selector, by=By.CSS_SELECTOR,3176 timeout=None):3177 """ Same as self.assert_element_absent()3178 Will raise an exception if the element stays present.3179 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3180 if not timeout:3181 timeout = settings.SMALL_TIMEOUT3182 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3183 timeout = self.__get_new_timeout(timeout)3184 self.wait_for_element_absent(selector, by=by, timeout=timeout)3185 return True3186 def assert_no_broken_links(self, multithreaded=True):3187 """ Same as self.assert_no_404_errors() """3188 self.assert_no_404_errors(multithreaded=multithreaded)3189 def wait(self, seconds):3190 """ Same as self.sleep() - Some JS frameworks use this method name. """3191 self.sleep(seconds)3192 def block_ads(self):3193 """ Same as self.ad_block() """3194 self.ad_block()3195 def _print(self, msg):3196 """ Same as Python's print() """3197 print(msg)3198 def start_tour(self, name=None, interval=0):3199 self.play_tour(name=name, interval=interval)3200 ############3201 def add_css_link(self, css_link):3202 js_utils.add_css_link(self.driver, css_link)3203 def add_js_link(self, js_link):3204 js_utils.add_js_link(self.driver, js_link)3205 def add_css_style(self, css_style):3206 js_utils.add_css_style(self.driver, css_style)3207 def add_js_code_from_link(self, js_link):3208 js_utils.add_js_code_from_link(self.driver, js_link)3209 def add_js_code(self, js_code):3210 js_utils.add_js_code(self.driver, js_code)3211 def add_meta_tag(self, http_equiv=None, content=None):3212 js_utils.add_meta_tag(3213 self.driver, http_equiv=http_equiv, content=content)3214 ############3215 def create_presentation(3216 self, name=None, theme="default", transition="default"):3217 """ Creates a Reveal-JS presentation that you can add slides to.3218 @Params3219 name - If creating multiple presentations at the same time,3220 use this to specify the name of the current presentation.3221 theme - Set a theme with a unique style for the presentation.3222 Valid themes: "serif" (default), "sky", "white", "black",3223 "simple", "league", "moon", "night",3224 "beige", "blood", and "solarized".3225 transition - Set a transition between slides.3226 Valid transitions: "none" (default), "slide", "fade",3227 "zoom", "convex", and "concave".3228 """3229 if not name:3230 name = "default"3231 if not theme or theme == "default":3232 theme = "serif"3233 valid_themes = (["serif", "white", "black", "beige", "simple", "sky",3234 "league", "moon", "night", "blood", "solarized"])3235 theme = theme.lower()3236 if theme not in valid_themes:3237 raise Exception(3238 "Theme {%s} not found! Valid themes: %s"3239 "" % (theme, valid_themes))3240 if not transition or transition == "default":3241 transition = "none"3242 valid_transitions = (3243 ["none", "slide", "fade", "zoom", "convex", "concave"])3244 transition = transition.lower()3245 if transition not in valid_transitions:3246 raise Exception(3247 "Transition {%s} not found! Valid transitions: %s"3248 "" % (transition, valid_transitions))3249 reveal_theme_css = None3250 if theme == "serif":3251 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS3252 elif theme == "sky":3253 reveal_theme_css = constants.Reveal.SKY_MIN_CSS3254 elif theme == "white":3255 reveal_theme_css = constants.Reveal.WHITE_MIN_CSS3256 elif theme == "black":3257 reveal_theme_css = constants.Reveal.BLACK_MIN_CSS3258 elif theme == "simple":3259 reveal_theme_css = constants.Reveal.SIMPLE_MIN_CSS3260 elif theme == "league":3261 reveal_theme_css = constants.Reveal.LEAGUE_MIN_CSS3262 elif theme == "moon":3263 reveal_theme_css = constants.Reveal.MOON_MIN_CSS3264 elif theme == "night":3265 reveal_theme_css = constants.Reveal.NIGHT_MIN_CSS3266 elif theme == "beige":3267 reveal_theme_css = constants.Reveal.BEIGE_MIN_CSS3268 elif theme == "blood":3269 reveal_theme_css = constants.Reveal.BLOOD_MIN_CSS3270 elif theme == "solarized":3271 reveal_theme_css = constants.Reveal.SOLARIZED_MIN_CSS3272 else:3273 # Use the default if unable to determine the theme3274 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS3275 new_presentation = (3276 '<html>\n'3277 '<head>\n'3278 '<meta charset="utf-8">\n'3279 '<meta http-equiv="Content-Type" '3280 'content="text/html; charset=utf-8;">\n'3281 '<meta name="viewport" content="text/html;">\n'3282 '<link rel="stylesheet" href="%s">\n'3283 '<link rel="stylesheet" href="%s">\n'3284 '<style>\n'3285 'pre{background-color:#fbe8d4;border-radius:8px;}\n'3286 'div[flex_div]{height:75vh;margin:0;align-items:center;'3287 'justify-content:center;}\n'3288 'img[rounded]{border-radius:16px;max-width:82%%;}\n'3289 '</style>\n'3290 '</head>\n\n'3291 '<body>\n'3292 '<!-- Generated by SeleniumBase - https://seleniumbase.io -->\n'3293 '<div class="reveal">\n'3294 '<div class="slides">\n'3295 '' % (constants.Reveal.MIN_CSS, reveal_theme_css))3296 self._presentation_slides[name] = []3297 self._presentation_slides[name].append(new_presentation)3298 self._presentation_transition[name] = transition3299 def add_slide(self, content=None, image=None, code=None, iframe=None,3300 content2=None, notes=None, transition=None, name=None):3301 """ Allows the user to add slides to a presentation.3302 @Params3303 content - The HTML content to display on the presentation slide.3304 image - Attach an image (from a URL link) to the slide.3305 code - Attach code of any programming language to the slide.3306 Language-detection will be used to add syntax formatting.3307 iframe - Attach an iFrame (from a URL link) to the slide.3308 content2 - HTML content to display after adding an image or code.3309 notes - Additional notes to include with the slide.3310 ONLY SEEN if show_notes is set for the presentation.3311 transition - Set a transition between slides. (overrides previous)3312 Valid transitions: "none" (default), "slide", "fade",3313 "zoom", "convex", and "concave".3314 name - If creating multiple presentations at the same time,3315 use this to select the presentation to add slides to.3316 """3317 if not name:3318 name = "default"3319 if name not in self._presentation_slides:3320 # Create a presentation if it doesn't already exist3321 self.create_presentation(name=name)3322 if not content:3323 content = ""3324 if not content2:3325 content2 = ""3326 if not notes:3327 notes = ""3328 if not transition:3329 transition = self._presentation_transition[name]3330 elif transition == "default":3331 transition = "none"3332 valid_transitions = (3333 ["none", "slide", "fade", "zoom", "convex", "concave"])3334 transition = transition.lower()3335 if transition not in valid_transitions:3336 raise Exception(3337 "Transition {%s} not found! Valid transitions: %s"3338 "" % (transition, valid_transitions))3339 add_line = ""3340 if content.startswith("<"):3341 add_line = "\n"3342 html = (3343 '\n<section data-transition="%s">%s%s' % (3344 transition, add_line, content))3345 if image:3346 html += '\n<div flex_div><img rounded src="%s"></div>' % image3347 if code:3348 html += '\n<div></div>'3349 html += '\n<pre class="prettyprint">\n%s</pre>' % code3350 if iframe:3351 html += ('\n<div></div>'3352 '\n<iframe src="%s" style="width:92%%;height:550px;" '3353 'title="iframe content"></iframe>' % iframe)3354 add_line = ""3355 if content2.startswith("<"):3356 add_line = "\n"3357 if content2:3358 html += '%s%s' % (add_line, content2)3359 html += '\n<aside class="notes">%s</aside>' % notes3360 html += '\n</section>\n'3361 self._presentation_slides[name].append(html)3362 def save_presentation(3363 self, name=None, filename=None, show_notes=False, interval=0):3364 """ Saves a Reveal-JS Presentation to a file for later use.3365 @Params3366 name - If creating multiple presentations at the same time,3367 use this to select the one you wish to use.3368 filename - The name of the HTML file that you wish to3369 save the presentation to. (filename must end in ".html")3370 show_notes - When set to True, the Notes feature becomes enabled,3371 which allows presenters to see notes next to slides.3372 interval - The delay time between autoplaying slides. (in seconds)3373 If set to 0 (default), autoplay is disabled.3374 """3375 if not name:3376 name = "default"3377 if not filename:3378 filename = "my_presentation.html"3379 if name not in self._presentation_slides:3380 raise Exception("Presentation {%s} does not exist!" % name)3381 if not filename.endswith('.html'):3382 raise Exception('Presentation file must end in ".html"!')3383 if not interval:3384 interval = 03385 if not type(interval) is int and not type(interval) is float:3386 raise Exception('Expecting a numeric value for "interval"!')3387 if interval < 0:3388 raise Exception('The "interval" cannot be a negative number!')3389 interval_ms = float(interval) * 1000.03390 show_notes_str = "false"3391 if show_notes:3392 show_notes_str = "true"3393 the_html = ""3394 for slide in self._presentation_slides[name]:3395 the_html += slide3396 the_html += (3397 '\n</div>\n'3398 '</div>\n'3399 '<script src="%s"></script>\n'3400 '<script src="%s"></script>\n'3401 '<script>Reveal.initialize('3402 '{showNotes: %s, slideNumber: true, '3403 'autoSlide: %s,});'3404 '</script>\n'3405 '</body>\n'3406 '</html>\n'3407 '' % (constants.Reveal.MIN_JS,3408 constants.PrettifyJS.RUN_PRETTIFY_JS,3409 show_notes_str,3410 interval_ms))3411 saved_presentations_folder = constants.Presentations.SAVED_FOLDER3412 if saved_presentations_folder.endswith("/"):3413 saved_presentations_folder = saved_presentations_folder[:-1]3414 if not os.path.exists(saved_presentations_folder):3415 try:3416 os.makedirs(saved_presentations_folder)3417 except Exception:3418 pass3419 file_path = saved_presentations_folder + "/" + filename3420 out_file = codecs.open(file_path, "w+", encoding="utf-8")3421 out_file.writelines(the_html)3422 out_file.close()3423 print('\n>>> [%s] was saved!\n' % file_path)3424 return file_path3425 def begin_presentation(3426 self, name=None, filename=None, show_notes=False, interval=0):3427 """ Begin a Reveal-JS Presentation in the web browser.3428 @Params3429 name - If creating multiple presentations at the same time,3430 use this to select the one you wish to use.3431 filename - The name of the HTML file that you wish to3432 save the presentation to. (filename must end in ".html")3433 show_notes - When set to True, the Notes feature becomes enabled,3434 which allows presenters to see notes next to slides.3435 interval - The delay time between autoplaying slides. (in seconds)3436 If set to 0 (default), autoplay is disabled.3437 """3438 if self.headless:3439 return # Presentations should not run in headless mode.3440 if not name:3441 name = "default"3442 if not filename:3443 filename = "my_presentation.html"3444 if name not in self._presentation_slides:3445 raise Exception("Presentation {%s} does not exist!" % name)3446 if not filename.endswith('.html'):3447 raise Exception('Presentation file must end in ".html"!')3448 if not interval:3449 interval = 03450 if not type(interval) is int and not type(interval) is float:3451 raise Exception('Expecting a numeric value for "interval"!')3452 if interval < 0:3453 raise Exception('The "interval" cannot be a negative number!')3454 end_slide = (3455 '\n<section data-transition="none">\n'3456 '<p class="End_Presentation_Now"> </p>\n</section>\n')3457 self._presentation_slides[name].append(end_slide)3458 file_path = self.save_presentation(3459 name=name, filename=filename,3460 show_notes=show_notes, interval=interval)3461 self._presentation_slides[name].pop()3462 self.open_html_file(file_path)3463 presentation_folder = constants.Presentations.SAVED_FOLDER3464 try:3465 while (len(self.driver.window_handles) > 0 and (3466 presentation_folder in self.get_current_url())):3467 time.sleep(0.05)3468 if self.is_element_visible(3469 "section.present p.End_Presentation_Now"):3470 break3471 time.sleep(0.05)3472 except Exception:3473 pass3474 ############3475 def create_pie_chart(3476 self, chart_name=None, title=None, subtitle=None,3477 data_name=None, unit=None, libs=True):3478 """ Creates a JavaScript pie chart using "HighCharts".3479 @Params3480 chart_name - If creating multiple charts,3481 use this to select which one.3482 title - The title displayed for the chart.3483 subtitle - The subtitle displayed for the chart.3484 data_name - Set the series name. Useful for multi-series charts.3485 unit - The description label given to the chart's y-axis values.3486 libs - The option to include Chart libraries (JS and CSS files).3487 Should be set to True (default) for the first time creating3488 a chart on a web page. If creating multiple charts on the3489 same web page, you won't need to re-import the libraries3490 when creating additional charts.3491 """3492 if not chart_name:3493 chart_name = "default"3494 if not data_name:3495 data_name = ""3496 style = "pie"3497 self.__create_highchart(3498 chart_name=chart_name, title=title, subtitle=subtitle,3499 style=style, data_name=data_name, unit=unit, libs=libs)3500 def create_bar_chart(3501 self, chart_name=None, title=None, subtitle=None,3502 data_name=None, unit=None, libs=True):3503 """ Creates a JavaScript bar chart using "HighCharts".3504 @Params3505 chart_name - If creating multiple charts,3506 use this to select which one.3507 title - The title displayed for the chart.3508 subtitle - The subtitle displayed for the chart.3509 data_name - Set the series name. Useful for multi-series charts.3510 unit - The description label given to the chart's y-axis values.3511 libs - The option to include Chart libraries (JS and CSS files).3512 Should be set to True (default) for the first time creating3513 a chart on a web page. If creating multiple charts on the3514 same web page, you won't need to re-import the libraries3515 when creating additional charts.3516 """3517 if not chart_name:3518 chart_name = "default"3519 if not data_name:3520 data_name = ""3521 style = "bar"3522 self.__create_highchart(3523 chart_name=chart_name, title=title, subtitle=subtitle,3524 style=style, data_name=data_name, unit=unit, libs=libs)3525 def create_column_chart(3526 self, chart_name=None, title=None, subtitle=None,3527 data_name=None, unit=None, libs=True):3528 """ Creates a JavaScript column chart using "HighCharts".3529 @Params3530 chart_name - If creating multiple charts,3531 use this to select which one.3532 title - The title displayed for the chart.3533 subtitle - The subtitle displayed for the chart.3534 data_name - Set the series name. Useful for multi-series charts.3535 unit - The description label given to the chart's y-axis values.3536 libs - The option to include Chart libraries (JS and CSS files).3537 Should be set to True (default) for the first time creating3538 a chart on a web page. If creating multiple charts on the3539 same web page, you won't need to re-import the libraries3540 when creating additional charts.3541 """3542 if not chart_name:3543 chart_name = "default"3544 if not data_name:3545 data_name = ""3546 style = "column"3547 self.__create_highchart(3548 chart_name=chart_name, title=title, subtitle=subtitle,3549 style=style, data_name=data_name, unit=unit, libs=libs)3550 def create_line_chart(3551 self, chart_name=None, title=None, subtitle=None,3552 data_name=None, unit=None, zero=False, libs=True):3553 """ Creates a JavaScript line chart using "HighCharts".3554 @Params3555 chart_name - If creating multiple charts,3556 use this to select which one.3557 title - The title displayed for the chart.3558 subtitle - The subtitle displayed for the chart.3559 data_name - Set the series name. Useful for multi-series charts.3560 unit - The description label given to the chart's y-axis values.3561 zero - If True, the y-axis always starts at 0. (Default: False).3562 libs - The option to include Chart libraries (JS and CSS files).3563 Should be set to True (default) for the first time creating3564 a chart on a web page. If creating multiple charts on the3565 same web page, you won't need to re-import the libraries3566 when creating additional charts.3567 """3568 if not chart_name:3569 chart_name = "default"3570 if not data_name:3571 data_name = ""3572 style = "line"3573 self.__create_highchart(3574 chart_name=chart_name, title=title, subtitle=subtitle,3575 style=style, data_name=data_name, unit=unit, zero=zero, libs=libs)3576 def create_area_chart(3577 self, chart_name=None, title=None, subtitle=None,3578 data_name=None, unit=None, zero=False, libs=True):3579 """ Creates a JavaScript area chart using "HighCharts".3580 @Params3581 chart_name - If creating multiple charts,3582 use this to select which one.3583 title - The title displayed for the chart.3584 subtitle - The subtitle displayed for the chart.3585 data_name - Set the series name. Useful for multi-series charts.3586 unit - The description label given to the chart's y-axis values.3587 zero - If True, the y-axis always starts at 0. (Default: False).3588 libs - The option to include Chart libraries (JS and CSS files).3589 Should be set to True (default) for the first time creating3590 a chart on a web page. If creating multiple charts on the3591 same web page, you won't need to re-import the libraries3592 when creating additional charts.3593 """3594 if not chart_name:3595 chart_name = "default"3596 if not data_name:3597 data_name = ""3598 style = "area"3599 self.__create_highchart(3600 chart_name=chart_name, title=title, subtitle=subtitle,3601 style=style, data_name=data_name, unit=unit, zero=zero, libs=libs)3602 def __create_highchart(3603 self, chart_name=None, title=None, subtitle=None,3604 style=None, data_name=None, unit=None, zero=False, libs=True):3605 """ Creates a JavaScript chart using the "HighCharts" library. """3606 if not chart_name:3607 chart_name = "default"3608 if not title:3609 title = ""3610 if not subtitle:3611 subtitle = ""3612 if not style:3613 style = "pie"3614 if not data_name:3615 data_name = "Series 1"3616 if not unit:3617 unit = "Values"3618 title = title.replace("'", "\\'")3619 subtitle = subtitle.replace("'", "\\'")3620 unit = unit.replace("'", "\\'")3621 self._chart_count += 13622 chart_libs = (3623 """3624 <script src="%s"></script>3625 <script src="%s"></script>3626 <script src="%s"></script>3627 <script src="%s"></script>3628 <script src="%s"></script>3629 """ % (3630 constants.JQuery.MIN_JS,3631 constants.HighCharts.HC_JS,3632 constants.HighCharts.EXPORTING_JS,3633 constants.HighCharts.EXPORT_DATA_JS,3634 constants.HighCharts.ACCESSIBILITY_JS))3635 if not libs:3636 chart_libs = ""3637 chart_css = (3638 """3639 <style>3640 .highcharts-figure, .highcharts-data-table table {3641 min-width: 320px;3642 max-width: 660px;3643 margin: 1em auto;3644 }3645 .highcharts-data-table table {3646 font-family: Verdana, sans-serif;3647 border-collapse: collapse;3648 border: 1px solid #EBEBEB;3649 margin: 10px auto;3650 text-align: center;3651 width: 100%;3652 max-width: 500px;3653 }3654 .highcharts-data-table caption {3655 padding: 1em 0;3656 font-size: 1.2em;3657 color: #555;3658 }3659 .highcharts-data-table th {3660 font-weight: 600;3661 padding: 0.5em;3662 }3663 .highcharts-data-table td, .highcharts-data-table th,3664 .highcharts-data-table caption {3665 padding: 0.5em;3666 }3667 .highcharts-data-table thead tr,3668 .highcharts-data-table tr:nth-child(even) {3669 background: #f8f8f8;3670 }3671 .highcharts-data-table tr:hover {3672 background: #f1f7ff;3673 }3674 </style>3675 """)3676 if not libs:3677 chart_css = ""3678 chart_description = ""3679 chart_figure = (3680 """3681 <figure class="highcharts-figure">3682 <div id="chartcontainer%s"></div>3683 <p class="highcharts-description">%s</p>3684 </figure>3685 """ % (self._chart_count, chart_description))3686 min_zero = ""3687 if zero:3688 min_zero = "min: 0,"3689 chart_init_1 = (3690 """3691 <script>3692 // Build the chart3693 Highcharts.chart('chartcontainer%s', {3694 credits: {3695 enabled: false3696 },3697 title: {3698 text: '%s'3699 },3700 subtitle: {3701 text: '%s'3702 },3703 xAxis: { },3704 yAxis: {3705 %s3706 title: {3707 text: '%s',3708 style: {3709 fontSize: '14px'3710 }3711 },3712 labels: {3713 useHTML: true,3714 style: {3715 fontSize: '14px'3716 }3717 }3718 },3719 chart: {3720 renderTo: 'statusChart',3721 plotBackgroundColor: null,3722 plotBorderWidth: null,3723 plotShadow: false,3724 type: '%s'3725 },3726 """ % (self._chart_count, title, subtitle, min_zero, unit, style))3727 # "{series.name}:"3728 point_format = (r'<b>{point.y}</b><br />'3729 r'<b>{point.percentage:.1f}%</b>')3730 if style != "pie":3731 point_format = (r'<b>{point.y}</b>')3732 chart_init_2 = (3733 """3734 tooltip: {3735 enabled: true,3736 useHTML: true,3737 style: {3738 padding: '6px',3739 fontSize: '14px'3740 },3741 pointFormat: '%s'3742 },3743 """ % point_format)3744 chart_init_3 = (3745 r"""3746 accessibility: {3747 point: {3748 valueSuffix: '%'3749 }3750 },3751 plotOptions: {3752 pie: {3753 allowPointSelect: true,3754 cursor: 'pointer',3755 dataLabels: {3756 enabled: false,3757 format: '{point.name}: {point.y:.1f}%'3758 },3759 states: {3760 hover: {3761 enabled: true3762 }3763 },3764 showInLegend: true3765 }3766 },3767 """)3768 if style != "pie":3769 chart_init_3 = (3770 """3771 allowPointSelect: true,3772 cursor: 'pointer',3773 legend: {3774 layout: 'vertical',3775 align: 'right',3776 verticalAlign: 'middle'3777 },3778 states: {3779 hover: {3780 enabled: true3781 }3782 },3783 plotOptions: {3784 series: {3785 showInLegend: true,3786 animation: true,3787 shadow: false,3788 lineWidth: 3,3789 fillOpacity: 0.5,3790 marker: {3791 enabled: true3792 }3793 }3794 },3795 """)3796 chart_init = chart_init_1 + chart_init_2 + chart_init_33797 color_by_point = "true"3798 if style != "pie":3799 color_by_point = "false"3800 series = (3801 """3802 series: [{3803 name: '%s',3804 colorByPoint: %s,3805 data: [3806 """ % (data_name, color_by_point))3807 new_chart = chart_libs + chart_css + chart_figure + chart_init + series3808 self._chart_data[chart_name] = []3809 self._chart_label[chart_name] = []3810 self._chart_data[chart_name].append(new_chart)3811 self._chart_first_series[chart_name] = True3812 self._chart_series_count[chart_name] = 13813 def add_series_to_chart(self, data_name=None, chart_name=None):3814 """ Add a new data series to an existing chart.3815 This allows charts to have multiple data sets.3816 @Params3817 data_name - Set the series name. Useful for multi-series charts.3818 chart_name - If creating multiple charts,3819 use this to select which one.3820 """3821 if not chart_name:3822 chart_name = "default"3823 self._chart_series_count[chart_name] += 13824 if not data_name:3825 data_name = "Series %s" % self._chart_series_count[chart_name]3826 series = (3827 """3828 ]3829 },3830 {3831 name: '%s',3832 colorByPoint: false,3833 data: [3834 """ % data_name)3835 self._chart_data[chart_name].append(series)3836 self._chart_first_series[chart_name] = False3837 def add_data_point(self, label, value, color=None, chart_name=None):3838 """ Add a data point to a SeleniumBase-generated chart.3839 @Params3840 label - The label name for the data point.3841 value - The numeric value of the data point.3842 color - The HTML color of the data point.3843 Can be an RGB color. Eg: "#55ACDC".3844 Can also be a named color. Eg: "Teal".3845 chart_name - If creating multiple charts,3846 use this to select which one.3847 """3848 if not chart_name:3849 chart_name = "default"3850 if chart_name not in self._chart_data:3851 # Create a chart if it doesn't already exist3852 self.create_pie_chart(chart_name=chart_name)3853 if not value:3854 value = 03855 if not type(value) is int and not type(value) is float:3856 raise Exception('Expecting a numeric value for "value"!')3857 if not color:3858 color = ""3859 label = label.replace("'", "\\'")3860 color = color.replace("'", "\\'")3861 data_point = (3862 """3863 {3864 name: '%s',3865 y: %s,3866 color: '%s'3867 },3868 """ % (label, value, color))3869 self._chart_data[chart_name].append(data_point)3870 if self._chart_first_series[chart_name]:3871 self._chart_label[chart_name].append(label)3872 def save_chart(self, chart_name=None, filename=None):3873 """ Saves a SeleniumBase-generated chart to a file for later use.3874 @Params3875 chart_name - If creating multiple charts at the same time,3876 use this to select the one you wish to use.3877 filename - The name of the HTML file that you wish to3878 save the chart to. (filename must end in ".html")3879 """3880 if not chart_name:3881 chart_name = "default"3882 if not filename:3883 filename = "my_chart.html"3884 if chart_name not in self._chart_data:3885 raise Exception("Chart {%s} does not exist!" % chart_name)3886 if not filename.endswith('.html'):3887 raise Exception('Chart file must end in ".html"!')3888 the_html = '<meta charset="utf-8">\n'3889 the_html += '<meta http-equiv="Content-Type" '3890 the_html += 'content="text/html; charset=utf-8;">\n'3891 the_html += '<meta name="viewport" content="text/html;">\n'3892 for chart_data_point in self._chart_data[chart_name]:3893 the_html += chart_data_point3894 the_html += (3895 """3896 ]3897 }]3898 });3899 </script>3900 """)3901 axis = "xAxis: {\n"3902 axis += " labels: {\n"3903 axis += " useHTML: true,\n"3904 axis += " style: {\n"3905 axis += " fontSize: '14px',\n"3906 axis += " },\n"3907 axis += " },\n"3908 axis += " categories: ["3909 for label in self._chart_label[chart_name]:3910 axis += "'%s'," % label3911 axis += "], crosshair: false},"3912 the_html = the_html.replace("xAxis: { },", axis)3913 saved_charts_folder = constants.Charts.SAVED_FOLDER3914 if saved_charts_folder.endswith("/"):3915 saved_charts_folder = saved_charts_folder[:-1]3916 if not os.path.exists(saved_charts_folder):3917 try:3918 os.makedirs(saved_charts_folder)3919 except Exception:3920 pass3921 file_path = saved_charts_folder + "/" + filename3922 out_file = codecs.open(file_path, "w+", encoding="utf-8")3923 out_file.writelines(the_html)3924 out_file.close()3925 print('\n>>> [%s] was saved!' % file_path)3926 return file_path3927 def display_chart(self, chart_name=None, filename=None, interval=0):3928 """ Displays a SeleniumBase-generated chart in the browser window.3929 @Params3930 chart_name - If creating multiple charts at the same time,3931 use this to select the one you wish to use.3932 filename - The name of the HTML file that you wish to3933 save the chart to. (filename must end in ".html")3934 interval - The delay time for auto-advancing charts. (in seconds)3935 If set to 0 (default), auto-advancing is disabled.3936 """3937 if self.headless:3938 interval = 1 # Race through chart if running in headless mode3939 if not chart_name:3940 chart_name = "default"3941 if not filename:3942 filename = "my_chart.html"3943 if not interval:3944 interval = 03945 if not type(interval) is int and not type(interval) is float:3946 raise Exception('Expecting a numeric value for "interval"!')3947 if interval < 0:3948 raise Exception('The "interval" cannot be a negative number!')3949 if chart_name not in self._chart_data:3950 raise Exception("Chart {%s} does not exist!" % chart_name)3951 if not filename.endswith('.html'):3952 raise Exception('Chart file must end in ".html"!')3953 file_path = self.save_chart(chart_name=chart_name, filename=filename)3954 self.open_html_file(file_path)3955 chart_folder = constants.Charts.SAVED_FOLDER3956 if interval == 0:3957 try:3958 print("\n*** Close the browser window to continue ***")3959 # Will also continue if manually navigating to a new page3960 while (len(self.driver.window_handles) > 0 and (3961 chart_folder in self.get_current_url())):3962 time.sleep(0.05)3963 except Exception:3964 pass3965 else:3966 try:3967 start_ms = time.time() * 1000.03968 stop_ms = start_ms + (interval * 1000.0)3969 for x in range(int(interval * 10)):3970 now_ms = time.time() * 1000.03971 if now_ms >= stop_ms:3972 break3973 if len(self.driver.window_handles) == 0:3974 break3975 if chart_folder not in self.get_current_url():3976 break3977 time.sleep(0.1)3978 except Exception:3979 pass3980 def extract_chart(self, chart_name=None):3981 """ Extracts the HTML from a SeleniumBase-generated chart.3982 @Params3983 chart_name - If creating multiple charts at the same time,3984 use this to select the one you wish to use.3985 """3986 if not chart_name:3987 chart_name = "default"3988 if chart_name not in self._chart_data:3989 raise Exception("Chart {%s} does not exist!" % chart_name)3990 the_html = ""3991 for chart_data_point in self._chart_data[chart_name]:3992 the_html += chart_data_point3993 the_html += (3994 """3995 ]3996 }]3997 });3998 </script>3999 """)4000 axis = "xAxis: {\n"4001 axis += " labels: {\n"4002 axis += " useHTML: true,\n"4003 axis += " style: {\n"4004 axis += " fontSize: '14px',\n"4005 axis += " },\n"4006 axis += " },\n"4007 axis += " categories: ["4008 for label in self._chart_label[chart_name]:4009 axis += "'%s'," % label4010 axis += "], crosshair: false},"4011 the_html = the_html.replace("xAxis: { },", axis)4012 return the_html4013 ############4014 def create_tour(self, name=None, theme=None):4015 """ Creates a tour for a website. By default, the Shepherd JavaScript4016 Library is used with the Shepherd "Light" / "Arrows" theme.4017 @Params4018 name - If creating multiple tours at the same time,4019 use this to select the tour you wish to add steps to.4020 theme - Sets the default theme for the tour.4021 Choose from "light"/"arrows", "dark", "default", "square",4022 and "square-dark". ("arrows" is used if None is selected.)4023 Alternatively, you may use a different JavaScript Library4024 as the theme. Those include "IntroJS", "DriverJS",4025 "Hopscotch", and "Bootstrap".4026 """4027 if not name:4028 name = "default"4029 if theme:4030 if theme.lower() == "bootstrap":4031 self.create_bootstrap_tour(name)4032 elif theme.lower() == "hopscotch":4033 self.create_hopscotch_tour(name)4034 elif theme.lower() == "intro":4035 self.create_introjs_tour(name)4036 elif theme.lower() == "introjs":4037 self.create_introjs_tour(name)4038 elif theme.lower() == "driver":4039 self.create_driverjs_tour(name)4040 elif theme.lower() == "driverjs":4041 self.create_driverjs_tour(name)4042 elif theme.lower() == "shepherd":4043 self.create_shepherd_tour(name, theme="light")4044 elif theme.lower() == "light":4045 self.create_shepherd_tour(name, theme="light")4046 elif theme.lower() == "dark":4047 self.create_shepherd_tour(name, theme="dark")4048 elif theme.lower() == "arrows":4049 self.create_shepherd_tour(name, theme="light")4050 elif theme.lower() == "square":4051 self.create_shepherd_tour(name, theme="square")4052 elif theme.lower() == "square-dark":4053 self.create_shepherd_tour(name, theme="square-dark")4054 elif theme.lower() == "default":4055 self.create_shepherd_tour(name, theme="default")4056 else:4057 self.create_shepherd_tour(name, theme)4058 else:4059 self.create_shepherd_tour(name, theme="light")4060 def create_shepherd_tour(self, name=None, theme=None):4061 """ Creates a Shepherd JS website tour.4062 @Params4063 name - If creating multiple tours at the same time,4064 use this to select the tour you wish to add steps to.4065 theme - Sets the default theme for the tour.4066 Choose from "light"/"arrows", "dark", "default", "square",4067 and "square-dark". ("light" is used if None is selected.)4068 """4069 shepherd_theme = "shepherd-theme-arrows"4070 if theme:4071 if theme.lower() == "default":4072 shepherd_theme = "shepherd-theme-default"4073 elif theme.lower() == "dark":4074 shepherd_theme = "shepherd-theme-dark"4075 elif theme.lower() == "light":4076 shepherd_theme = "shepherd-theme-arrows"4077 elif theme.lower() == "arrows":4078 shepherd_theme = "shepherd-theme-arrows"4079 elif theme.lower() == "square":4080 shepherd_theme = "shepherd-theme-square"4081 elif theme.lower() == "square-dark":4082 shepherd_theme = "shepherd-theme-square-dark"4083 if not name:4084 name = "default"4085 new_tour = (4086 """4087 // Shepherd Tour4088 var tour = new Shepherd.Tour({4089 defaults: {4090 classes: '%s',4091 scrollTo: true4092 }4093 });4094 var allButtons = {4095 skip: {4096 text: "Skip",4097 action: tour.cancel,4098 classes: 'shepherd-button-secondary tour-button-left'4099 },4100 back: {4101 text: "Back",4102 action: tour.back,4103 classes: 'shepherd-button-secondary'4104 },4105 next: {4106 text: "Next",4107 action: tour.next,4108 classes: 'shepherd-button-primary tour-button-right'4109 },4110 };4111 var firstStepButtons = [allButtons.skip, allButtons.next];4112 var midTourButtons = [allButtons.back, allButtons.next];4113 """ % shepherd_theme)4114 self._tour_steps[name] = []4115 self._tour_steps[name].append(new_tour)4116 def create_bootstrap_tour(self, name=None):4117 """ Creates a Bootstrap tour for a website.4118 @Params4119 name - If creating multiple tours at the same time,4120 use this to select the tour you wish to add steps to.4121 """4122 if not name:4123 name = "default"4124 new_tour = (4125 """4126 // Bootstrap Tour4127 var tour = new Tour({4128 });4129 tour.addSteps([4130 """)4131 self._tour_steps[name] = []4132 self._tour_steps[name].append(new_tour)4133 def create_driverjs_tour(self, name=None):4134 """ Creates a DriverJS tour for a website.4135 @Params4136 name - If creating multiple tours at the same time,4137 use this to select the tour you wish to add steps to.4138 """4139 if not name:4140 name = "default"4141 new_tour = (4142 """4143 // DriverJS Tour4144 var tour = new Driver({4145 opacity: 0.24, // Background opacity (0: no popover / overlay)4146 padding: 6, // Distance of element from around the edges4147 allowClose: false, // Whether clicking on overlay should close4148 overlayClickNext: false, // Move to next step on overlay click4149 doneBtnText: 'Done', // Text that appears on the Done button4150 closeBtnText: 'Close', // Text appearing on the Close button4151 nextBtnText: 'Next', // Text that appears on the Next button4152 prevBtnText: 'Previous', // Text appearing on Previous button4153 showButtons: true, // This shows control buttons in the footer4154 keyboardControl: true, // (escape to close, arrow keys to move)4155 animate: true, // Animate while changing highlighted element4156 });4157 tour.defineSteps([4158 """)4159 self._tour_steps[name] = []4160 self._tour_steps[name].append(new_tour)4161 def create_hopscotch_tour(self, name=None):4162 """ Creates a Hopscotch tour for a website.4163 @Params4164 name - If creating multiple tours at the same time,4165 use this to select the tour you wish to add steps to.4166 """4167 if not name:4168 name = "default"4169 new_tour = (4170 """4171 // Hopscotch Tour4172 var tour = {4173 id: "hopscotch_tour",4174 steps: [4175 """)4176 self._tour_steps[name] = []4177 self._tour_steps[name].append(new_tour)4178 def create_introjs_tour(self, name=None):4179 """ Creates an IntroJS tour for a website.4180 @Params4181 name - If creating multiple tours at the same time,4182 use this to select the tour you wish to add steps to.4183 """4184 if not name:4185 name = "default"4186 new_tour = (4187 """4188 // IntroJS Tour4189 function startIntro(){4190 var intro = introJs();4191 intro.setOptions({4192 steps: [4193 """)4194 self._tour_steps[name] = []4195 self._tour_steps[name].append(new_tour)4196 def add_tour_step(self, message, selector=None, name=None,4197 title=None, theme=None, alignment=None, duration=None):4198 """ Allows the user to add tour steps for a website.4199 @Params4200 message - The message to display.4201 selector - The CSS Selector of the Element to attach to.4202 name - If creating multiple tours at the same time,4203 use this to select the tour you wish to add steps to.4204 title - Additional header text that appears above the message.4205 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.4206 Choose from "light"/"arrows", "dark", "default", "square",4207 and "square-dark". ("arrows" is used if None is selected.)4208 alignment - Choose from "top", "bottom", "left", and "right".4209 ("top" is default, except for Hopscotch and DriverJS).4210 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,4211 before automatically advancing to the next tour step.4212 """4213 if not selector:4214 selector = "html"4215 if page_utils.is_name_selector(selector):4216 name = page_utils.get_name_from_selector(selector)4217 selector = '[name="%s"]' % name4218 if page_utils.is_xpath_selector(selector):4219 selector = self.convert_to_css_selector(selector, By.XPATH)4220 selector = self.__escape_quotes_if_needed(selector)4221 if not name:4222 name = "default"4223 if name not in self._tour_steps:4224 # By default, will create an IntroJS tour if no tours exist4225 self.create_tour(name=name, theme="introjs")4226 if not title:4227 title = ""4228 title = self.__escape_quotes_if_needed(title)4229 if message:4230 message = self.__escape_quotes_if_needed(message)4231 else:4232 message = ""4233 if not alignment or (4234 alignment not in ["top", "bottom", "left", "right"]):4235 t_name = self._tour_steps[name][0]4236 if "Hopscotch" not in t_name and "DriverJS" not in t_name:4237 alignment = "top"4238 else:4239 alignment = "bottom"4240 if "Bootstrap" in self._tour_steps[name][0]:4241 self.__add_bootstrap_tour_step(4242 message, selector=selector, name=name, title=title,4243 alignment=alignment, duration=duration)4244 elif "DriverJS" in self._tour_steps[name][0]:4245 self.__add_driverjs_tour_step(4246 message, selector=selector, name=name, title=title,4247 alignment=alignment)4248 elif "Hopscotch" in self._tour_steps[name][0]:4249 self.__add_hopscotch_tour_step(4250 message, selector=selector, name=name, title=title,4251 alignment=alignment)4252 elif "IntroJS" in self._tour_steps[name][0]:4253 self.__add_introjs_tour_step(4254 message, selector=selector, name=name, title=title,4255 alignment=alignment)4256 else:4257 self.__add_shepherd_tour_step(4258 message, selector=selector, name=name, title=title,4259 theme=theme, alignment=alignment)4260 def __add_shepherd_tour_step(self, message, selector=None, name=None,4261 title=None, theme=None, alignment=None):4262 """ Allows the user to add tour steps for a website.4263 @Params4264 message - The message to display.4265 selector - The CSS Selector of the Element to attach to.4266 name - If creating multiple tours at the same time,4267 use this to select the tour you wish to add steps to.4268 title - Additional header text that appears above the message.4269 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.4270 Choose from "light"/"arrows", "dark", "default", "square",4271 and "square-dark". ("arrows" is used if None is selected.)4272 alignment - Choose from "top", "bottom", "left", and "right".4273 ("top" is the default alignment).4274 """4275 if theme == "default":4276 shepherd_theme = "shepherd-theme-default"4277 elif theme == "dark":4278 shepherd_theme = "shepherd-theme-dark"4279 elif theme == "light":4280 shepherd_theme = "shepherd-theme-arrows"4281 elif theme == "arrows":4282 shepherd_theme = "shepherd-theme-arrows"4283 elif theme == "square":4284 shepherd_theme = "shepherd-theme-square"4285 elif theme == "square-dark":4286 shepherd_theme = "shepherd-theme-square-dark"4287 else:4288 shepherd_base_theme = re.search(4289 r"[\S\s]+classes: '([\S\s]+)',[\S\s]+",4290 self._tour_steps[name][0]).group(1)4291 shepherd_theme = shepherd_base_theme4292 shepherd_classes = shepherd_theme4293 if selector == "html":4294 shepherd_classes += " shepherd-orphan"4295 buttons = "firstStepButtons"4296 if len(self._tour_steps[name]) > 1:4297 buttons = "midTourButtons"4298 step = ("""4299 tour.addStep('%s', {4300 title: '%s',4301 classes: '%s',4302 text: '%s',4303 attachTo: {element: '%s', on: '%s'},4304 buttons: %s,4305 advanceOn: '.docs-link click'4306 });""" % (4307 name, title, shepherd_classes, message, selector, alignment,4308 buttons))4309 self._tour_steps[name].append(step)4310 def __add_bootstrap_tour_step(self, message, selector=None, name=None,4311 title=None, alignment=None, duration=None):4312 """ Allows the user to add tour steps for a website.4313 @Params4314 message - The message to display.4315 selector - The CSS Selector of the Element to attach to.4316 name - If creating multiple tours at the same time,4317 use this to select the tour you wish to add steps to.4318 title - Additional header text that appears above the message.4319 alignment - Choose from "top", "bottom", "left", and "right".4320 ("top" is the default alignment).4321 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,4322 before automatically advancing to the next tour step.4323 """4324 if selector != "html":4325 selector = self.__make_css_match_first_element_only(selector)4326 element_row = "element: '%s'," % selector4327 else:4328 element_row = ""4329 if not duration:4330 duration = "0"4331 else:4332 duration = str(float(duration) * 1000.0)4333 step = ("""{4334 %s4335 title: '%s',4336 content: '%s',4337 orphan: true,4338 placement: 'auto %s',4339 smartPlacement: true,4340 duration: %s,4341 },""" % (element_row, title, message, alignment, duration))4342 self._tour_steps[name].append(step)4343 def __add_driverjs_tour_step(self, message, selector=None, name=None,4344 title=None, alignment=None):4345 """ Allows the user to add tour steps for a website.4346 @Params4347 message - The message to display.4348 selector - The CSS Selector of the Element to attach to.4349 name - If creating multiple tours at the same time,4350 use this to select the tour you wish to add steps to.4351 title - Additional header text that appears above the message.4352 alignment - Choose from "top", "bottom", "left", and "right".4353 ("top" is the default alignment).4354 """4355 message = (4356 '<font size=\"3\" color=\"#33477B\"><b>' + message + '</b></font>')4357 title_row = ""4358 if not title:4359 title_row = "title: '%s'," % message4360 message = ""4361 else:4362 title_row = "title: '%s'," % title4363 align_row = "position: '%s'," % alignment4364 ani_row = "animate: true,"4365 if not selector or selector == "html" or selector == "body":4366 selector = "body"4367 ani_row = "animate: false,"4368 align_row = "position: '%s'," % "mid-center"4369 element_row = "element: '%s'," % selector4370 desc_row = "description: '%s'," % message4371 step = ("""{4372 %s4373 %s4374 popover: {4375 className: 'popover-class',4376 %s4377 %s4378 %s4379 }4380 },""" % (element_row, ani_row, title_row, desc_row, align_row))4381 self._tour_steps[name].append(step)4382 def __add_hopscotch_tour_step(self, message, selector=None, name=None,4383 title=None, alignment=None):4384 """ Allows the user to add tour steps for a website.4385 @Params4386 message - The message to display.4387 selector - The CSS Selector of the Element to attach to.4388 name - If creating multiple tours at the same time,4389 use this to select the tour you wish to add steps to.4390 title - Additional header text that appears above the message.4391 alignment - Choose from "top", "bottom", "left", and "right".4392 ("bottom" is the default alignment).4393 """4394 arrow_offset_row = None4395 if not selector or selector == "html":4396 selector = "head"4397 alignment = "bottom"4398 arrow_offset_row = "arrowOffset: '200',"4399 else:4400 arrow_offset_row = ""4401 step = ("""{4402 target: '%s',4403 title: '%s',4404 content: '%s',4405 %s4406 showPrevButton: 'true',4407 scrollDuration: '550',4408 placement: '%s'},4409 """ % (selector, title, message, arrow_offset_row, alignment))4410 self._tour_steps[name].append(step)4411 def __add_introjs_tour_step(self, message, selector=None, name=None,4412 title=None, alignment=None):4413 """ Allows the user to add tour steps for a website.4414 @Params4415 message - The message to display.4416 selector - The CSS Selector of the Element to attach to.4417 name - If creating multiple tours at the same time,4418 use this to select the tour you wish to add steps to.4419 title - Additional header text that appears above the message.4420 alignment - Choose from "top", "bottom", "left", and "right".4421 ("top" is the default alignment).4422 """4423 if selector != "html":4424 element_row = "element: '%s'," % selector4425 else:4426 element_row = ""4427 if title:4428 message = "<center><b>" + title + "</b></center><hr>" + message4429 message = '<font size=\"3\" color=\"#33477B\">' + message + '</font>'4430 step = ("""{%s4431 intro: '%s',4432 position: '%s'},4433 """ % (element_row, message, alignment))4434 self._tour_steps[name].append(step)4435 def play_tour(self, name=None, interval=0):4436 """ Plays a tour on the current website.4437 @Params4438 name - If creating multiple tours at the same time,4439 use this to select the tour you wish to add steps to.4440 interval - The delay time between autoplaying tour steps. (Seconds)4441 If set to 0 (default), the tour is fully manual control.4442 """4443 if self.headless:4444 return # Tours should not run in headless mode.4445 if not name:4446 name = "default"4447 if name not in self._tour_steps:4448 raise Exception("Tour {%s} does not exist!" % name)4449 if "Bootstrap" in self._tour_steps[name][0]:4450 tour_helper.play_bootstrap_tour(4451 self.driver, self._tour_steps, self.browser,4452 self.message_duration, name=name, interval=interval)4453 elif "DriverJS" in self._tour_steps[name][0]:4454 tour_helper.play_driverjs_tour(4455 self.driver, self._tour_steps, self.browser,4456 self.message_duration, name=name, interval=interval)4457 elif "Hopscotch" in self._tour_steps[name][0]:4458 tour_helper.play_hopscotch_tour(4459 self.driver, self._tour_steps, self.browser,4460 self.message_duration, name=name, interval=interval)4461 elif "IntroJS" in self._tour_steps[name][0]:4462 tour_helper.play_introjs_tour(4463 self.driver, self._tour_steps, self.browser,4464 self.message_duration, name=name, interval=interval)4465 else:4466 # "Shepherd"4467 tour_helper.play_shepherd_tour(4468 self.driver, self._tour_steps,4469 self.message_duration, name=name, interval=interval)4470 def export_tour(self, name=None, filename="my_tour.js", url=None):4471 """ Exports a tour as a JS file.4472 You can call self.export_tour() anywhere where you would4473 normally use self.play_tour() to play a website tour.4474 It will include necessary resources as well, such as jQuery.4475 You'll be able to copy the tour directly into the Console of4476 any web browser to play the tour outside of SeleniumBase runs.4477 @Params4478 name - If creating multiple tours at the same time,4479 use this to select the tour you wish to add steps to.4480 filename - The name of the JavaScript file that you wish to4481 save the tour to.4482 url - The URL where the tour starts. If not specified, the URL4483 of the current page will be used. """4484 if not url:4485 url = self.get_current_url()4486 tour_helper.export_tour(4487 self._tour_steps, name=name, filename=filename, url=url)4488 def activate_jquery_confirm(self):4489 """ See https://craftpip.github.io/jquery-confirm/ for usage. """4490 js_utils.activate_jquery_confirm(self.driver)4491 self.wait_for_ready_state_complete()4492 def activate_messenger(self):4493 js_utils.activate_messenger(self.driver)4494 self.wait_for_ready_state_complete()4495 def set_messenger_theme(self, theme="default", location="default",4496 max_messages="default"):4497 """ Sets a theme for posting messages.4498 Themes: ["flat", "future", "block", "air", "ice"]4499 Locations: ["top_left", "top_center", "top_right",4500 "bottom_left", "bottom_center", "bottom_right"]4501 max_messages is the limit of concurrent messages to display. """4502 if not theme:4503 theme = "default" # "flat"4504 if not location:4505 location = "default" # "bottom_right"4506 if not max_messages:4507 max_messages = "default" # "8"4508 else:4509 max_messages = str(max_messages) # Value must be in string format4510 js_utils.set_messenger_theme(4511 self.driver, theme=theme,4512 location=location, max_messages=max_messages)4513 def post_message(self, message, duration=None, pause=True, style="info"):4514 """ Post a message on the screen with Messenger.4515 Arguments:4516 message: The message to display.4517 duration: The time until the message vanishes. (Default: 2.55s)4518 pause: If True, the program waits until the message completes.4519 style: "info", "success", or "error".4520 You can also post messages by using =>4521 self.execute_script('Messenger().post("My Message")')4522 """4523 if style not in ["info", "success", "error"]:4524 style = "info"4525 if not duration:4526 if not self.message_duration:4527 duration = settings.DEFAULT_MESSAGE_DURATION4528 else:4529 duration = self.message_duration4530 try:4531 js_utils.post_message(self.driver, message, duration, style=style)4532 except Exception:4533 print(" * %s message: %s" % (style.upper(), message))4534 if pause:4535 duration = float(duration) + 0.154536 time.sleep(float(duration))4537 def post_message_and_highlight(4538 self, message, selector, by=By.CSS_SELECTOR):4539 """ Post a message on the screen and highlight an element.4540 Arguments:4541 message: The message to display.4542 selector: The selector of the Element to highlight.4543 by: The type of selector to search by. (Default: CSS Selector)4544 """4545 self.__highlight_with_assert_success(message, selector, by=by)4546 def post_success_message(self, message, duration=None, pause=True):4547 """ Post a success message on the screen with Messenger.4548 Arguments:4549 message: The success message to display.4550 duration: The time until the message vanishes. (Default: 2.55s)4551 pause: If True, the program waits until the message completes.4552 """4553 if not duration:4554 if not self.message_duration:4555 duration = settings.DEFAULT_MESSAGE_DURATION4556 else:4557 duration = self.message_duration4558 try:4559 js_utils.post_message(4560 self.driver, message, duration, style="success")4561 except Exception:4562 print(" * SUCCESS message: %s" % message)4563 if pause:4564 duration = float(duration) + 0.154565 time.sleep(float(duration))4566 def post_error_message(self, message, duration=None, pause=True):4567 """ Post an error message on the screen with Messenger.4568 Arguments:4569 message: The error message to display.4570 duration: The time until the message vanishes. (Default: 2.55s)4571 pause: If True, the program waits until the message completes.4572 """4573 if not duration:4574 if not self.message_duration:4575 duration = settings.DEFAULT_MESSAGE_DURATION4576 else:4577 duration = self.message_duration4578 try:4579 js_utils.post_message(4580 self.driver, message, duration, style="error")4581 except Exception:4582 print(" * ERROR message: %s" % message)4583 if pause:4584 duration = float(duration) + 0.154585 time.sleep(float(duration))4586 ############4587 def generate_referral(self, start_page, destination_page):4588 """ This method opens the start_page, creates a referral link there,4589 and clicks on that link, which goes to the destination_page.4590 (This generates real traffic for testing analytics software.) """4591 if not page_utils.is_valid_url(destination_page):4592 raise Exception(4593 "Exception: destination_page {%s} is not a valid URL!"4594 % destination_page)4595 if start_page:4596 if not page_utils.is_valid_url(start_page):4597 raise Exception(4598 "Exception: start_page {%s} is not a valid URL! "4599 "(Use an empty string or None to start from current page.)"4600 % start_page)4601 self.open(start_page)4602 time.sleep(0.08)4603 self.wait_for_ready_state_complete()4604 referral_link = ('''<body>'''4605 '''<a class='analytics referral test' href='%s' '''4606 '''style='font-family: Arial,sans-serif; '''4607 '''font-size: 30px; color: #18a2cd'>'''4608 '''Magic Link Button</a></body>''' % destination_page)4609 self.execute_script(4610 '''document.body.outerHTML = \"%s\"''' % referral_link)4611 self.click(4612 "a.analytics.referral.test", timeout=2) # Clicks generated button4613 time.sleep(0.15)4614 try:4615 self.click("html")4616 time.sleep(0.08)4617 except Exception:4618 pass4619 def generate_traffic(self, start_page, destination_page, loops=1):4620 """ Similar to generate_referral(), but can do multiple loops. """4621 for loop in range(loops):4622 self.generate_referral(start_page, destination_page)4623 time.sleep(0.05)4624 def generate_referral_chain(self, pages):4625 """ Use this method to chain the action of creating button links on4626 one website page that will take you to the next page.4627 (When you want to create a referral to a website for traffic4628 generation without increasing the bounce rate, you'll want to visit4629 at least one additional page on that site with a button click.) """4630 if not type(pages) is tuple and not type(pages) is list:4631 raise Exception(4632 "Exception: Expecting a list of website pages for chaining!")4633 if len(pages) < 2:4634 raise Exception(4635 "Exception: At least two website pages required for chaining!")4636 for page in pages:4637 # Find out if any of the web pages are invalid before continuing4638 if not page_utils.is_valid_url(page):4639 raise Exception(4640 "Exception: Website page {%s} is not a valid URL!" % page)4641 for page in pages:4642 self.generate_referral(None, page)4643 def generate_traffic_chain(self, pages, loops=1):4644 """ Similar to generate_referral_chain(), but for multiple loops. """4645 for loop in range(loops):4646 self.generate_referral_chain(pages)4647 time.sleep(0.05)4648 ############4649 def wait_for_element_present(self, selector, by=By.CSS_SELECTOR,4650 timeout=None):4651 """ Waits for an element to appear in the HTML of a page.4652 The element does not need be visible (it may be hidden). """4653 if not timeout:4654 timeout = settings.LARGE_TIMEOUT4655 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4656 timeout = self.__get_new_timeout(timeout)4657 selector, by = self.__recalculate_selector(selector, by)4658 return page_actions.wait_for_element_present(4659 self.driver, selector, by, timeout)4660 def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=None):4661 """ Waits for an element to appear in the HTML of a page.4662 The element must be visible (it cannot be hidden). """4663 if not timeout:4664 timeout = settings.LARGE_TIMEOUT4665 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4666 timeout = self.__get_new_timeout(timeout)4667 selector, by = self.__recalculate_selector(selector, by)4668 return page_actions.wait_for_element_visible(4669 self.driver, selector, by, timeout)4670 def get_element(self, selector, by=By.CSS_SELECTOR, timeout=None):4671 """ Same as wait_for_element_present() - returns the element.4672 The element does not need be visible (it may be hidden). """4673 if not timeout:4674 timeout = settings.SMALL_TIMEOUT4675 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4676 timeout = self.__get_new_timeout(timeout)4677 selector, by = self.__recalculate_selector(selector, by)4678 return self.wait_for_element_present(selector, by=by, timeout=timeout)4679 def assert_element_present(self, selector, by=By.CSS_SELECTOR,4680 timeout=None):4681 """ Similar to wait_for_element_present(), but returns nothing.4682 Waits for an element to appear in the HTML of a page.4683 The element does not need be visible (it may be hidden).4684 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4685 if not timeout:4686 timeout = settings.SMALL_TIMEOUT4687 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4688 timeout = self.__get_new_timeout(timeout)4689 self.wait_for_element_present(selector, by=by, timeout=timeout)4690 return True4691 def find_element(self, selector, by=By.CSS_SELECTOR, timeout=None):4692 """ Same as wait_for_element_visible() - returns the element """4693 if not timeout:4694 timeout = settings.LARGE_TIMEOUT4695 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4696 timeout = self.__get_new_timeout(timeout)4697 return self.wait_for_element_visible(selector, by=by, timeout=timeout)4698 def assert_element(self, selector, by=By.CSS_SELECTOR, timeout=None):4699 """ Similar to wait_for_element_visible(), but returns nothing.4700 As above, will raise an exception if nothing can be found.4701 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4702 if not timeout:4703 timeout = settings.SMALL_TIMEOUT4704 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4705 timeout = self.__get_new_timeout(timeout)4706 self.wait_for_element_visible(selector, by=by, timeout=timeout)4707 if self.demo_mode:4708 selector, by = self.__recalculate_selector(selector, by)4709 a_t = "ASSERT"4710 if self._language != "English":4711 from seleniumbase.fixtures.words import SD4712 a_t = SD.translate_assert(self._language)4713 messenger_post = "%s %s: %s" % (a_t, by.upper(), selector)4714 self.__highlight_with_assert_success(messenger_post, selector, by)4715 return True4716 def assert_element_visible(self, selector, by=By.CSS_SELECTOR,4717 timeout=None):4718 """ Same as self.assert_element()4719 As above, will raise an exception if nothing can be found. """4720 if not timeout:4721 timeout = settings.SMALL_TIMEOUT4722 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4723 timeout = self.__get_new_timeout(timeout)4724 self.assert_element(selector, by=by, timeout=timeout)4725 return True4726 ############4727 def wait_for_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,4728 timeout=None):4729 if not timeout:4730 timeout = settings.LARGE_TIMEOUT4731 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4732 timeout = self.__get_new_timeout(timeout)4733 selector, by = self.__recalculate_selector(selector, by)4734 return page_actions.wait_for_text_visible(4735 self.driver, text, selector, by, timeout)4736 def wait_for_exact_text_visible(self, text, selector="html",4737 by=By.CSS_SELECTOR,4738 timeout=None):4739 if not timeout:4740 timeout = settings.LARGE_TIMEOUT4741 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4742 timeout = self.__get_new_timeout(timeout)4743 selector, by = self.__recalculate_selector(selector, by)4744 return page_actions.wait_for_exact_text_visible(4745 self.driver, text, selector, by, timeout)4746 def wait_for_text(self, text, selector="html", by=By.CSS_SELECTOR,4747 timeout=None):4748 """ The shorter version of wait_for_text_visible() """4749 if not timeout:4750 timeout = settings.LARGE_TIMEOUT4751 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4752 timeout = self.__get_new_timeout(timeout)4753 return self.wait_for_text_visible(4754 text, selector, by=by, timeout=timeout)4755 def find_text(self, text, selector="html", by=By.CSS_SELECTOR,4756 timeout=None):4757 """ Same as wait_for_text_visible() - returns the element """4758 if not timeout:4759 timeout = settings.LARGE_TIMEOUT4760 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4761 timeout = self.__get_new_timeout(timeout)4762 return self.wait_for_text_visible(4763 text, selector, by=by, timeout=timeout)4764 def assert_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,4765 timeout=None):4766 """ Same as assert_text() """4767 if not timeout:4768 timeout = settings.SMALL_TIMEOUT4769 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4770 timeout = self.__get_new_timeout(timeout)4771 return self.assert_text(text, selector, by=by, timeout=timeout)4772 def assert_text(self, text, selector="html", by=By.CSS_SELECTOR,4773 timeout=None):4774 """ Similar to wait_for_text_visible()4775 Raises an exception if the element or the text is not found.4776 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4777 if not timeout:4778 timeout = settings.SMALL_TIMEOUT4779 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4780 timeout = self.__get_new_timeout(timeout)4781 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)4782 if self.demo_mode:4783 selector, by = self.__recalculate_selector(selector, by)4784 a_t = "ASSERT TEXT"4785 i_n = "in"4786 if self._language != "English":4787 from seleniumbase.fixtures.words import SD4788 a_t = SD.translate_assert_text(self._language)4789 i_n = SD.translate_in(self._language)4790 messenger_post = ("%s: {%s} %s %s: %s"4791 % (a_t, text, i_n, by.upper(), selector))4792 self.__highlight_with_assert_success(messenger_post, selector, by)4793 return True4794 def assert_exact_text(self, text, selector="html", by=By.CSS_SELECTOR,4795 timeout=None):4796 """ Similar to assert_text(), but the text must be exact, rather than4797 exist as a subset of the full text.4798 (Extra whitespace at the beginning or the end doesn't count.)4799 Raises an exception if the element or the text is not found.4800 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4801 if not timeout:4802 timeout = settings.SMALL_TIMEOUT4803 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4804 timeout = self.__get_new_timeout(timeout)4805 self.wait_for_exact_text_visible(4806 text, selector, by=by, timeout=timeout)4807 if self.demo_mode:4808 selector, by = self.__recalculate_selector(selector, by)4809 a_t = "ASSERT EXACT TEXT"4810 i_n = "in"4811 if self._language != "English":4812 from seleniumbase.fixtures.words import SD4813 a_t = SD.translate_assert_exact_text(self._language)4814 i_n = SD.translate_in(self._language)4815 messenger_post = ("%s: {%s} %s %s: %s"4816 % (a_t, text, i_n, by.upper(), selector))4817 self.__highlight_with_assert_success(messenger_post, selector, by)4818 return True4819 ############4820 def wait_for_link_text_present(self, link_text, timeout=None):4821 if not timeout:4822 timeout = settings.SMALL_TIMEOUT4823 start_ms = time.time() * 1000.04824 stop_ms = start_ms + (timeout * 1000.0)4825 for x in range(int(timeout * 5)):4826 shared_utils.check_if_time_limit_exceeded()4827 try:4828 if not self.is_link_text_present(link_text):4829 raise Exception(4830 "Link text {%s} was not found!" % link_text)4831 return4832 except Exception:4833 now_ms = time.time() * 1000.04834 if now_ms >= stop_ms:4835 break4836 time.sleep(0.2)4837 message = (4838 "Link text {%s} was not present after %s seconds!"4839 "" % (link_text, timeout))4840 page_actions.timeout_exception("NoSuchElementException", message)4841 def wait_for_partial_link_text_present(self, link_text, timeout=None):4842 if not timeout:4843 timeout = settings.SMALL_TIMEOUT4844 start_ms = time.time() * 1000.04845 stop_ms = start_ms + (timeout * 1000.0)4846 for x in range(int(timeout * 5)):4847 shared_utils.check_if_time_limit_exceeded()4848 try:4849 if not self.is_partial_link_text_present(link_text):4850 raise Exception(4851 "Partial Link text {%s} was not found!" % link_text)4852 return4853 except Exception:4854 now_ms = time.time() * 1000.04855 if now_ms >= stop_ms:4856 break4857 time.sleep(0.2)4858 message = (4859 "Partial Link text {%s} was not present after %s seconds!"4860 "" % (link_text, timeout))4861 page_actions.timeout_exception("NoSuchElementException", message)4862 def wait_for_link_text_visible(self, link_text, timeout=None):4863 if not timeout:4864 timeout = settings.LARGE_TIMEOUT4865 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4866 timeout = self.__get_new_timeout(timeout)4867 return self.wait_for_element_visible(4868 link_text, by=By.LINK_TEXT, timeout=timeout)4869 def wait_for_link_text(self, link_text, timeout=None):4870 """ The shorter version of wait_for_link_text_visible() """4871 if not timeout:4872 timeout = settings.LARGE_TIMEOUT4873 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4874 timeout = self.__get_new_timeout(timeout)4875 return self.wait_for_link_text_visible(link_text, timeout=timeout)4876 def find_link_text(self, link_text, timeout=None):4877 """ Same as wait_for_link_text_visible() - returns the element """4878 if not timeout:4879 timeout = settings.LARGE_TIMEOUT4880 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4881 timeout = self.__get_new_timeout(timeout)4882 return self.wait_for_link_text_visible(link_text, timeout=timeout)4883 def assert_link_text(self, link_text, timeout=None):4884 """ Similar to wait_for_link_text_visible(), but returns nothing.4885 As above, will raise an exception if nothing can be found.4886 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4887 if not timeout:4888 timeout = settings.SMALL_TIMEOUT4889 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4890 timeout = self.__get_new_timeout(timeout)4891 self.wait_for_link_text_visible(link_text, timeout=timeout)4892 if self.demo_mode:4893 a_t = "ASSERT LINK TEXT"4894 if self._language != "English":4895 from seleniumbase.fixtures.words import SD4896 a_t = SD.translate_assert_link_text(self._language)4897 messenger_post = ("%s: {%s}" % (a_t, link_text))4898 self.__highlight_with_assert_success(4899 messenger_post, link_text, by=By.LINK_TEXT)4900 return True4901 def wait_for_partial_link_text(self, partial_link_text, timeout=None):4902 if not timeout:4903 timeout = settings.LARGE_TIMEOUT4904 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4905 timeout = self.__get_new_timeout(timeout)4906 return self.wait_for_element_visible(4907 partial_link_text, by=By.PARTIAL_LINK_TEXT, timeout=timeout)4908 def find_partial_link_text(self, partial_link_text, timeout=None):4909 """ Same as wait_for_partial_link_text() - returns the element """4910 if not timeout:4911 timeout = settings.LARGE_TIMEOUT4912 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4913 timeout = self.__get_new_timeout(timeout)4914 return self.wait_for_partial_link_text(4915 partial_link_text, timeout=timeout)4916 def assert_partial_link_text(self, partial_link_text, timeout=None):4917 """ Similar to wait_for_partial_link_text(), but returns nothing.4918 As above, will raise an exception if nothing can be found.4919 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4920 if not timeout:4921 timeout = settings.SMALL_TIMEOUT4922 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4923 timeout = self.__get_new_timeout(timeout)4924 self.wait_for_partial_link_text(partial_link_text, timeout=timeout)4925 if self.demo_mode:4926 a_t = "ASSERT PARTIAL LINK TEXT"4927 if self._language != "English":4928 from seleniumbase.fixtures.words import SD4929 a_t = SD.translate_assert_link_text(self._language)4930 messenger_post = ("%s: {%s}" % (a_t, partial_link_text))4931 self.__highlight_with_assert_success(4932 messenger_post, partial_link_text, by=By.PARTIAL_LINK_TEXT)4933 return True4934 ############4935 def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR,4936 timeout=None):4937 """ Waits for an element to no longer appear in the HTML of a page.4938 A hidden element still counts as appearing in the page HTML.4939 If an element with "hidden" status is acceptable,4940 use wait_for_element_not_visible() instead. """4941 if not timeout:4942 timeout = settings.LARGE_TIMEOUT4943 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4944 timeout = self.__get_new_timeout(timeout)4945 selector, by = self.__recalculate_selector(selector, by)4946 return page_actions.wait_for_element_absent(4947 self.driver, selector, by, timeout)4948 def assert_element_absent(self, selector, by=By.CSS_SELECTOR,4949 timeout=None):4950 """ Similar to wait_for_element_absent() - returns nothing.4951 As above, will raise an exception if the element stays present.4952 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4953 if not timeout:4954 timeout = settings.SMALL_TIMEOUT4955 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4956 timeout = self.__get_new_timeout(timeout)4957 self.wait_for_element_absent(selector, by=by, timeout=timeout)4958 return True4959 ############4960 def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR,4961 timeout=None):4962 """ Waits for an element to no longer be visible on a page.4963 The element can be non-existant in the HTML or hidden on the page4964 to qualify as not visible. """4965 if not timeout:4966 timeout = settings.LARGE_TIMEOUT4967 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4968 timeout = self.__get_new_timeout(timeout)4969 selector, by = self.__recalculate_selector(selector, by)4970 return page_actions.wait_for_element_not_visible(4971 self.driver, selector, by, timeout)4972 def assert_element_not_visible(self, selector, by=By.CSS_SELECTOR,4973 timeout=None):4974 """ Similar to wait_for_element_not_visible() - returns nothing.4975 As above, will raise an exception if the element stays visible.4976 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4977 if not timeout:4978 timeout = settings.SMALL_TIMEOUT4979 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4980 timeout = self.__get_new_timeout(timeout)4981 self.wait_for_element_not_visible(selector, by=by, timeout=timeout)4982 return True4983 ############4984 def wait_for_text_not_visible(self, text, selector="html",4985 by=By.CSS_SELECTOR,4986 timeout=None):4987 if not timeout:4988 timeout = settings.LARGE_TIMEOUT4989 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4990 timeout = self.__get_new_timeout(timeout)4991 selector, by = self.__recalculate_selector(selector, by)4992 return page_actions.wait_for_text_not_visible(4993 self.driver, text, selector, by, timeout)4994 def assert_text_not_visible(self, text, selector="html",4995 by=By.CSS_SELECTOR,4996 timeout=None):4997 """ Similar to wait_for_text_not_visible()4998 Raises an exception if the element or the text is not found.4999 Returns True if successful. Default timeout = SMALL_TIMEOUT. """5000 if not timeout:5001 timeout = settings.SMALL_TIMEOUT5002 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5003 timeout = self.__get_new_timeout(timeout)5004 self.wait_for_text_not_visible(text, selector, by=by, timeout=timeout)5005 ############5006 def wait_for_and_accept_alert(self, timeout=None):5007 if not timeout:5008 timeout = settings.LARGE_TIMEOUT5009 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5010 timeout = self.__get_new_timeout(timeout)5011 return page_actions.wait_for_and_accept_alert(self.driver, timeout)5012 def wait_for_and_dismiss_alert(self, timeout=None):5013 if not timeout:5014 timeout = settings.LARGE_TIMEOUT5015 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5016 timeout = self.__get_new_timeout(timeout)5017 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)5018 def wait_for_and_switch_to_alert(self, timeout=None):5019 if not timeout:5020 timeout = settings.LARGE_TIMEOUT5021 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:5022 timeout = self.__get_new_timeout(timeout)5023 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)5024 ############5025 def accept_alert(self, timeout=None):5026 """ Same as wait_for_and_accept_alert(), but smaller default T_O """5027 if not timeout:5028 timeout = settings.SMALL_TIMEOUT5029 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5030 timeout = self.__get_new_timeout(timeout)5031 return page_actions.wait_for_and_accept_alert(self.driver, timeout)5032 def dismiss_alert(self, timeout=None):5033 """ Same as wait_for_and_dismiss_alert(), but smaller default T_O """5034 if not timeout:5035 timeout = settings.SMALL_TIMEOUT5036 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5037 timeout = self.__get_new_timeout(timeout)5038 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)5039 def switch_to_alert(self, timeout=None):5040 """ Same as wait_for_and_switch_to_alert(), but smaller default T_O """5041 if not timeout:5042 timeout = settings.SMALL_TIMEOUT5043 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:5044 timeout = self.__get_new_timeout(timeout)5045 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)5046 ############5047 def __assert_eq(self, *args, **kwargs):5048 """ Minified assert_equal() using only the list diff. """5049 minified_exception = None5050 try:5051 self.assertEqual(*args, **kwargs)5052 except Exception as e:5053 str_e = str(e)5054 minified_exception = "\nAssertionError:\n"5055 lines = str_e.split('\n')5056 countdown = 35057 countdown_on = False5058 for line in lines:5059 if countdown_on:5060 minified_exception += line + '\n'5061 countdown = countdown - 15062 if countdown == 0:5063 countdown_on = False5064 elif line.startswith('F'):5065 countdown_on = True5066 countdown = 35067 minified_exception += line + '\n'5068 elif line.startswith('+') or line.startswith('-'):5069 minified_exception += line + '\n'5070 elif line.startswith('?'):5071 minified_exception += line + '\n'5072 elif line.strip().startswith('*'):5073 minified_exception += line + '\n'5074 if minified_exception:5075 raise Exception(minified_exception)5076 def check_window(self, name="default", level=0, baseline=False):5077 """ *** Automated Visual Testing with SeleniumBase ***5078 The first time a test calls self.check_window() for a unique "name"5079 parameter provided, it will set a visual baseline, meaning that it5080 creates a folder, saves the URL to a file, saves the current window5081 screenshot to a file, and creates the following three files5082 with the listed data saved:5083 tags_level1.txt -> HTML tags from the window5084 tags_level2.txt -> HTML tags + attributes from the window5085 tags_level3.txt -> HTML tags + attributes/values from the window5086 Baseline folders are named based on the test name and the name5087 parameter passed to self.check_window(). The same test can store5088 multiple baseline folders.5089 If the baseline is being set/reset, the "level" doesn't matter.5090 After the first run of self.check_window(), it will compare the5091 HTML tags of the latest window to the one from the initial run.5092 Here's how the level system works:5093 * level=0 ->5094 DRY RUN ONLY - Will perform a comparison to the baseline, and5095 print out any differences that are found, but5096 won't fail the test even if differences exist.5097 * level=1 ->5098 HTML tags are compared to tags_level1.txt5099 * level=2 ->5100 HTML tags are compared to tags_level1.txt and5101 HTML tags/attributes are compared to tags_level2.txt5102 * level=3 ->5103 HTML tags are compared to tags_level1.txt and5104 HTML tags + attributes are compared to tags_level2.txt and5105 HTML tags + attributes/values are compared to tags_level3.txt5106 As shown, Level-3 is the most strict, Level-1 is the least strict.5107 If the comparisons from the latest window to the existing baseline5108 don't match, the current test will fail, except for Level-0 tests.5109 You can reset the visual baseline on the command line by using:5110 --visual_baseline5111 As long as "--visual_baseline" is used on the command line while5112 running tests, the self.check_window() method cannot fail because5113 it will rebuild the visual baseline rather than comparing the html5114 tags of the latest run to the existing baseline. If there are any5115 expected layout changes to a website that you're testing, you'll5116 need to reset the baseline to prevent unnecessary failures.5117 self.check_window() will fail with "Page Domain Mismatch Failure"5118 if the page domain doesn't match the domain of the baseline.5119 If you want to use self.check_window() to compare a web page to5120 a later version of itself from within the same test run, you can5121 add the parameter "baseline=True" to the first time you call5122 self.check_window() in a test to use that as the baseline. This5123 only makes sense if you're calling self.check_window() more than5124 once with the same name parameter in the same test.5125 Automated Visual Testing with self.check_window() is not very5126 effective for websites that have dynamic content that changes5127 the layout and structure of web pages. For those, you're much5128 better off using regular SeleniumBase functional testing.5129 Example usage:5130 self.check_window(name="testing", level=0)5131 self.check_window(name="xkcd_home", level=1)5132 self.check_window(name="github_page", level=2)5133 self.check_window(name="wikipedia_page", level=3)5134 """5135 if level == "0":5136 level = 05137 if level == "1":5138 level = 15139 if level == "2":5140 level = 25141 if level == "3":5142 level = 35143 if level != 0 and level != 1 and level != 2 and level != 3:5144 raise Exception('Parameter "level" must be set to 0, 1, 2, or 3!')5145 if self.demo_mode:5146 raise Exception(5147 "WARNING: Using Demo Mode will break layout tests "5148 "that use the check_window() method due to custom "5149 "HTML edits being made on the page!\n"5150 "Please rerun without using Demo Mode!")5151 module = self.__class__.__module__5152 if '.' in module and len(module.split('.')[-1]) > 1:5153 module = module.split('.')[-1]5154 test_id = "%s.%s" % (module, self._testMethodName)5155 if not name or len(name) < 1:5156 name = "default"5157 name = str(name)5158 from seleniumbase.core import visual_helper5159 visual_helper.visual_baseline_folder_setup()5160 baseline_dir = constants.VisualBaseline.STORAGE_FOLDER5161 visual_baseline_path = baseline_dir + "/" + test_id + "/" + name5162 page_url_file = visual_baseline_path + "/page_url.txt"5163 screenshot_file = visual_baseline_path + "/screenshot.png"5164 level_1_file = visual_baseline_path + "/tags_level_1.txt"5165 level_2_file = visual_baseline_path + "/tags_level_2.txt"5166 level_3_file = visual_baseline_path + "/tags_level_3.txt"5167 set_baseline = False5168 if baseline or self.visual_baseline:5169 set_baseline = True5170 if not os.path.exists(visual_baseline_path):5171 set_baseline = True5172 try:5173 os.makedirs(visual_baseline_path)5174 except Exception:5175 pass # Only reachable during multi-threaded test runs5176 if not os.path.exists(page_url_file):5177 set_baseline = True5178 if not os.path.exists(screenshot_file):5179 set_baseline = True5180 if not os.path.exists(level_1_file):5181 set_baseline = True5182 if not os.path.exists(level_2_file):5183 set_baseline = True5184 if not os.path.exists(level_3_file):5185 set_baseline = True5186 page_url = self.get_current_url()5187 soup = self.get_beautiful_soup()5188 html_tags = soup.body.find_all()5189 level_1 = [[tag.name] for tag in html_tags]5190 level_1 = json.loads(json.dumps(level_1)) # Tuples become lists5191 level_2 = [[tag.name, sorted(tag.attrs.keys())] for tag in html_tags]5192 level_2 = json.loads(json.dumps(level_2)) # Tuples become lists5193 level_3 = [[tag.name, sorted(tag.attrs.items())] for tag in html_tags]5194 level_3 = json.loads(json.dumps(level_3)) # Tuples become lists5195 if set_baseline:5196 self.save_screenshot("screenshot.png", visual_baseline_path)5197 out_file = codecs.open(page_url_file, "w+", encoding="utf-8")5198 out_file.writelines(page_url)5199 out_file.close()5200 out_file = codecs.open(level_1_file, "w+", encoding="utf-8")5201 out_file.writelines(json.dumps(level_1))5202 out_file.close()5203 out_file = codecs.open(level_2_file, "w+", encoding="utf-8")5204 out_file.writelines(json.dumps(level_2))5205 out_file.close()5206 out_file = codecs.open(level_3_file, "w+", encoding="utf-8")5207 out_file.writelines(json.dumps(level_3))5208 out_file.close()5209 if not set_baseline:5210 f = open(page_url_file, 'r')5211 page_url_data = f.read().strip()5212 f.close()5213 f = open(level_1_file, 'r')5214 level_1_data = json.loads(f.read())5215 f.close()5216 f = open(level_2_file, 'r')5217 level_2_data = json.loads(f.read())5218 f.close()5219 f = open(level_3_file, 'r')5220 level_3_data = json.loads(f.read())5221 f.close()5222 domain_fail = (5223 "\nPage Domain Mismatch Failure: "5224 "Current Page Domain doesn't match the Page Domain of the "5225 "Baseline! Can't compare two completely different sites! "5226 "Run with --visual_baseline to reset the baseline!")5227 level_1_failure = (5228 "\n*\n*** Exception: <Level 1> Visual Diff Failure:\n"5229 "* HTML tags don't match the baseline!")5230 level_2_failure = (5231 "\n*\n*** Exception: <Level 2> Visual Diff Failure:\n"5232 "* HTML tag attribute names don't match the baseline!")5233 level_3_failure = (5234 "\n*\n*** Exception: <Level 3> Visual Diff Failure:\n"5235 "* HTML tag attribute values don't match the baseline!")5236 page_domain = self.get_domain_url(page_url)5237 page_data_domain = self.get_domain_url(page_url_data)5238 unittest.TestCase.maxDiff = 10005239 if level != 0:5240 self.assertEqual(page_data_domain, page_domain, domain_fail)5241 unittest.TestCase.maxDiff = None5242 if level == 3:5243 self.__assert_eq(level_3_data, level_3, level_3_failure)5244 if level == 2:5245 self.__assert_eq(level_2_data, level_2, level_2_failure)5246 unittest.TestCase.maxDiff = 10005247 if level == 1:5248 self.__assert_eq(level_1_data, level_1, level_1_failure)5249 unittest.TestCase.maxDiff = None5250 if level == 0:5251 try:5252 unittest.TestCase.maxDiff = 10005253 self.assertEqual(5254 page_domain, page_data_domain, domain_fail)5255 unittest.TestCase.maxDiff = None5256 self.__assert_eq(level_3_data, level_3, level_3_failure)5257 except Exception as e:5258 print(e) # Level-0 Dry Run (Only print the differences)5259 ############5260 def __get_new_timeout(self, timeout):5261 """ When using --timeout_multiplier=#.# """5262 try:5263 timeout_multiplier = float(self.timeout_multiplier)5264 if timeout_multiplier <= 0.5:5265 timeout_multiplier = 0.55266 timeout = int(math.ceil(timeout_multiplier * timeout))5267 return timeout5268 except Exception:5269 # Wrong data type for timeout_multiplier (expecting int or float)5270 return timeout5271 ############5272 def __get_exception_message(self):5273 """ This method extracts the message from an exception if there5274 was an exception that occurred during the test, assuming5275 that the exception was in a try/except block and not thrown. """5276 exception_info = sys.exc_info()[1]5277 if hasattr(exception_info, 'msg'):5278 exc_message = exception_info.msg5279 elif hasattr(exception_info, 'message'):5280 exc_message = exception_info.message5281 else:5282 exc_message = sys.exc_info()5283 return exc_message5284 def __get_improved_exception_message(self):5285 """5286 If Chromedriver is out-of-date, make it clear!5287 Given the high popularity of the following StackOverflow article:5288 https://stackoverflow.com/questions/49162667/unknown-error-5289 call-function-result-missing-value-for-selenium-send-keys-even5290 ... the original error message was not helpful. Tell people directly.5291 (Only expected when using driver.send_keys() with an old Chromedriver.)5292 """5293 exc_message = self.__get_exception_message()5294 maybe_using_old_chromedriver = False5295 if "unknown error: call function result missing" in exc_message:5296 maybe_using_old_chromedriver = True5297 if self.browser == 'chrome' and maybe_using_old_chromedriver:5298 update = ("Your version of ChromeDriver may be out-of-date! "5299 "Please go to "5300 "https://sites.google.com/a/chromium.org/chromedriver/ "5301 "and download the latest version to your system PATH! "5302 "Or use: ``seleniumbase install chromedriver`` . "5303 "Original Exception Message: %s" % exc_message)5304 exc_message = update5305 return exc_message5306 def __add_deferred_assert_failure(self):5307 """ Add a deferred_assert failure to a list for future processing. """5308 current_url = self.driver.current_url5309 message = self.__get_exception_message()5310 self.__deferred_assert_failures.append(5311 "CHECK #%s: (%s)\n %s" % (5312 self.__deferred_assert_count, current_url, message))5313 ############5314 def deferred_assert_element(self, selector, by=By.CSS_SELECTOR,5315 timeout=None):5316 """ A non-terminating assertion for an element on a page.5317 Failures will be saved until the process_deferred_asserts()5318 method is called from inside a test, likely at the end of it. """5319 if not timeout:5320 timeout = settings.MINI_TIMEOUT5321 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:5322 timeout = self.__get_new_timeout(timeout)5323 self.__deferred_assert_count += 15324 try:5325 url = self.get_current_url()5326 if url == self.__last_url_of_deferred_assert:5327 timeout = 15328 else:5329 self.__last_url_of_deferred_assert = url5330 except Exception:5331 pass5332 try:5333 self.wait_for_element_visible(selector, by=by, timeout=timeout)5334 return True5335 except Exception:5336 self.__add_deferred_assert_failure()5337 return False5338 def deferred_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,5339 timeout=None):5340 """ A non-terminating assertion for text from an element on a page.5341 Failures will be saved until the process_deferred_asserts()5342 method is called from inside a test, likely at the end of it. """5343 if not timeout:5344 timeout = settings.MINI_TIMEOUT5345 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:5346 timeout = self.__get_new_timeout(timeout)5347 self.__deferred_assert_count += 15348 try:5349 url = self.get_current_url()5350 if url == self.__last_url_of_deferred_assert:5351 timeout = 15352 else:5353 self.__last_url_of_deferred_assert = url5354 except Exception:5355 pass5356 try:5357 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)5358 return True5359 except Exception:5360 self.__add_deferred_assert_failure()5361 return False5362 def process_deferred_asserts(self, print_only=False):5363 """ To be used with any test that uses deferred_asserts, which are5364 non-terminating verifications that only raise exceptions5365 after this method is called.5366 This is useful for pages with multiple elements to be checked when5367 you want to find as many bugs as possible in a single test run5368 before having all the exceptions get raised simultaneously.5369 Might be more useful if this method is called after processing all5370 the deferred asserts on a single html page so that the failure5371 screenshot matches the location of the deferred asserts.5372 If "print_only" is set to True, the exception won't get raised. """5373 if self.__deferred_assert_failures:5374 exception_output = ''5375 exception_output += "\n*** DEFERRED ASSERTION FAILURES FROM: "5376 exception_output += "%s\n" % self.id()5377 all_failing_checks = self.__deferred_assert_failures5378 self.__deferred_assert_failures = []5379 for tb in all_failing_checks:5380 exception_output += "%s\n" % tb5381 if print_only:5382 print(exception_output)5383 else:5384 raise Exception(exception_output)5385 ############5386 # Alternate naming scheme for the "deferred_assert" methods.5387 def delayed_assert_element(self, selector, by=By.CSS_SELECTOR,5388 timeout=None):5389 """ Same as self.deferred_assert_element() """5390 return self.deferred_assert_element(5391 selector=selector, by=by, timeout=timeout)5392 def delayed_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,5393 timeout=None):5394 """ Same as self.deferred_assert_text() """5395 return self.deferred_assert_text(5396 text=text, selector=selector, by=by, timeout=timeout)5397 def process_delayed_asserts(self, print_only=False):5398 """ Same as self.process_deferred_asserts() """5399 self.process_deferred_asserts(print_only=print_only)5400 ############5401 def __js_click(self, selector, by=By.CSS_SELECTOR):5402 """ Clicks an element using pure JS. Does not use jQuery. """5403 selector, by = self.__recalculate_selector(selector, by)5404 css_selector = self.convert_to_css_selector(selector, by=by)5405 css_selector = re.escape(css_selector) # Add "\\" to special chars5406 css_selector = self.__escape_quotes_if_needed(css_selector)5407 script = ("""var simulateClick = function (elem) {5408 var evt = new MouseEvent('click', {5409 bubbles: true,5410 cancelable: true,5411 view: window5412 });5413 var canceled = !elem.dispatchEvent(evt);5414 };5415 var someLink = document.querySelector('%s');5416 simulateClick(someLink);"""5417 % css_selector)5418 self.execute_script(script)5419 def __js_click_all(self, selector, by=By.CSS_SELECTOR):5420 """ Clicks all matching elements using pure JS. (No jQuery) """5421 selector, by = self.__recalculate_selector(selector, by)5422 css_selector = self.convert_to_css_selector(selector, by=by)5423 css_selector = re.escape(css_selector) # Add "\\" to special chars5424 css_selector = self.__escape_quotes_if_needed(css_selector)5425 script = ("""var simulateClick = function (elem) {5426 var evt = new MouseEvent('click', {5427 bubbles: true,5428 cancelable: true,5429 view: window5430 });5431 var canceled = !elem.dispatchEvent(evt);5432 };5433 var $elements = document.querySelectorAll('%s');5434 var index = 0, length = $elements.length;5435 for(; index < length; index++){5436 simulateClick($elements[index]);}"""5437 % css_selector)5438 self.execute_script(script)5439 def __jquery_slow_scroll_to(self, selector, by=By.CSS_SELECTOR):5440 selector, by = self.__recalculate_selector(selector, by)5441 element = self.wait_for_element_present(5442 selector, by=by, timeout=settings.SMALL_TIMEOUT)5443 dist = js_utils.get_scroll_distance_to_element(self.driver, element)5444 time_offset = 05445 try:5446 if dist and abs(dist) > SSMD:5447 time_offset = int(float(abs(dist) - SSMD) / 12.5)5448 if time_offset > 950:5449 time_offset = 9505450 except Exception:5451 time_offset = 05452 scroll_time_ms = 550 + time_offset5453 sleep_time = 0.625 + (float(time_offset) / 1000.0)5454 selector = self.convert_to_css_selector(selector, by=by)5455 selector = self.__make_css_match_first_element_only(selector)5456 scroll_script = (5457 """jQuery([document.documentElement, document.body]).animate({5458 scrollTop: jQuery('%s').offset().top - 130}, %s);5459 """ % (selector, scroll_time_ms))5460 self.safe_execute_script(scroll_script)5461 self.sleep(sleep_time)5462 def __jquery_click(self, selector, by=By.CSS_SELECTOR):5463 """ Clicks an element using jQuery. Different from using pure JS. """5464 selector, by = self.__recalculate_selector(selector, by)5465 self.wait_for_element_present(5466 selector, by=by, timeout=settings.SMALL_TIMEOUT)5467 selector = self.convert_to_css_selector(selector, by=by)5468 selector = self.__make_css_match_first_element_only(selector)5469 click_script = """jQuery('%s')[0].click();""" % selector5470 self.safe_execute_script(click_script)5471 def __get_href_from_link_text(self, link_text, hard_fail=True):5472 href = self.get_link_attribute(link_text, "href", hard_fail)5473 if not href:5474 return None5475 if href.startswith('//'):5476 link = "http:" + href5477 elif href.startswith('/'):5478 url = self.driver.current_url5479 domain_url = self.get_domain_url(url)5480 link = domain_url + href5481 else:5482 link = href5483 return link5484 def __click_dropdown_link_text(self, link_text, link_css):5485 """ When a link may be hidden under a dropdown menu, use this. """5486 soup = self.get_beautiful_soup()5487 drop_down_list = []5488 for item in soup.select('li[class]'):5489 drop_down_list.append(item)5490 csstype = link_css.split('[')[1].split('=')[0]5491 for item in drop_down_list:5492 item_text_list = item.text.split('\n')5493 if link_text in item_text_list and csstype in item.decode():5494 dropdown_css = ""5495 try:5496 for css_class in item['class']:5497 dropdown_css += '.'5498 dropdown_css += css_class5499 except Exception:5500 continue5501 dropdown_css = item.name + dropdown_css5502 matching_dropdowns = self.find_visible_elements(dropdown_css)5503 for dropdown in matching_dropdowns:5504 # The same class names might be used for multiple dropdowns5505 if dropdown.is_displayed():5506 try:5507 try:5508 page_actions.hover_element(5509 self.driver, dropdown)5510 except Exception:5511 # If hovering fails, driver is likely outdated5512 # Time to go directly to the hidden link text5513 self.open(self.__get_href_from_link_text(5514 link_text))5515 return True5516 page_actions.hover_element_and_click(5517 self.driver, dropdown, link_text,5518 click_by=By.LINK_TEXT, timeout=0.12)5519 return True5520 except Exception:5521 pass5522 return False5523 def __get_href_from_partial_link_text(self, link_text, hard_fail=True):5524 href = self.get_partial_link_text_attribute(5525 link_text, "href", hard_fail)5526 if not href:5527 return None5528 if href.startswith('//'):5529 link = "http:" + href5530 elif href.startswith('/'):5531 url = self.driver.current_url5532 domain_url = self.get_domain_url(url)5533 link = domain_url + href5534 else:5535 link = href5536 return link5537 def __click_dropdown_partial_link_text(self, link_text, link_css):5538 """ When a partial link may be hidden under a dropdown, use this. """5539 soup = self.get_beautiful_soup()5540 drop_down_list = []5541 for item in soup.select('li[class]'):5542 drop_down_list.append(item)5543 csstype = link_css.split('[')[1].split('=')[0]5544 for item in drop_down_list:5545 item_text_list = item.text.split('\n')5546 if link_text in item_text_list and csstype in item.decode():5547 dropdown_css = ""5548 try:5549 for css_class in item['class']:5550 dropdown_css += '.'5551 dropdown_css += css_class5552 except Exception:5553 continue5554 dropdown_css = item.name + dropdown_css5555 matching_dropdowns = self.find_visible_elements(dropdown_css)5556 for dropdown in matching_dropdowns:5557 # The same class names might be used for multiple dropdowns5558 if dropdown.is_displayed():5559 try:5560 try:5561 page_actions.hover_element(5562 self.driver, dropdown)5563 except Exception:5564 # If hovering fails, driver is likely outdated5565 # Time to go directly to the hidden link text5566 self.open(5567 self.__get_href_from_partial_link_text(5568 link_text))5569 return True5570 page_actions.hover_element_and_click(5571 self.driver, dropdown, link_text,5572 click_by=By.LINK_TEXT, timeout=0.12)5573 return True5574 except Exception:5575 pass5576 return False5577 def __recalculate_selector(self, selector, by):5578 # Use auto-detection to return the correct selector with "by" updated5579 _type = type(selector) # First make sure the selector is a string5580 if _type is not str:5581 msg = 'Expecting a selector of type: "<class \'str\'>" (string)!'5582 raise Exception('Invalid selector type: "%s"\n%s' % (_type, msg))5583 if page_utils.is_xpath_selector(selector):5584 by = By.XPATH5585 if page_utils.is_link_text_selector(selector):5586 selector = page_utils.get_link_text_from_selector(selector)5587 by = By.LINK_TEXT5588 if page_utils.is_partial_link_text_selector(selector):5589 selector = page_utils.get_partial_link_text_from_selector(selector)5590 by = By.PARTIAL_LINK_TEXT5591 if page_utils.is_name_selector(selector):5592 name = page_utils.get_name_from_selector(selector)5593 selector = '[name="%s"]' % name5594 by = By.CSS_SELECTOR5595 if ":contains(" in selector and by == By.CSS_SELECTOR:5596 selector = self.convert_css_to_xpath(selector)5597 by = By.XPATH5598 return (selector, by)5599 def __looks_like_a_page_url(self, url):5600 """ Returns True if the url parameter looks like a URL. This method5601 is slightly more lenient than page_utils.is_valid_url(url) due to5602 possible typos when calling self.get(url), which will try to5603 navigate to the page if a URL is detected, but will instead call5604 self.get_element(URL_AS_A_SELECTOR) if the input in not a URL. """5605 if (url.startswith("http:") or url.startswith("https:") or (5606 url.startswith("://") or url.startswith("chrome:") or (5607 url.startswith("about:") or url.startswith("data:") or (5608 url.startswith("file:") or url.startswith("edge:") or (5609 url.startswith("opera:")))))):5610 return True5611 else:5612 return False5613 def __make_css_match_first_element_only(self, selector):5614 # Only get the first match5615 return page_utils.make_css_match_first_element_only(selector)5616 def __demo_mode_pause_if_active(self, tiny=False):5617 if self.demo_mode:5618 wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT5619 if self.demo_sleep:5620 wait_time = float(self.demo_sleep)5621 if not tiny:5622 time.sleep(wait_time)5623 else:5624 time.sleep(wait_time / 3.4)5625 elif self.slow_mode:5626 self.__slow_mode_pause_if_active()5627 def __slow_mode_pause_if_active(self):5628 if self.slow_mode:5629 wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT5630 if self.demo_sleep:5631 wait_time = float(self.demo_sleep)5632 time.sleep(wait_time)5633 def __demo_mode_scroll_if_active(self, selector, by):5634 if self.demo_mode:5635 self.slow_scroll_to(selector, by=by)5636 def __demo_mode_highlight_if_active(self, selector, by):5637 if self.demo_mode:5638 # Includes self.slow_scroll_to(selector, by=by) by default5639 self.highlight(selector, by=by)5640 elif self.slow_mode:5641 # Just do the slow scroll part of the highlight() method5642 self.sleep(0.08)5643 selector, by = self.__recalculate_selector(selector, by)5644 element = self.wait_for_element_visible(5645 selector, by=by, timeout=settings.SMALL_TIMEOUT)5646 try:5647 scroll_distance = js_utils.get_scroll_distance_to_element(5648 self.driver, element)5649 if abs(scroll_distance) > SSMD:5650 self.__jquery_slow_scroll_to(selector, by)5651 else:5652 self.__slow_scroll_to_element(element)5653 except (StaleElementReferenceException, ENI_Exception):5654 self.wait_for_ready_state_complete()5655 time.sleep(0.12)5656 element = self.wait_for_element_visible(5657 selector, by=by, timeout=settings.SMALL_TIMEOUT)5658 self.__slow_scroll_to_element(element)5659 self.sleep(0.12)5660 def __scroll_to_element(self, element, selector=None, by=By.CSS_SELECTOR):5661 success = js_utils.scroll_to_element(self.driver, element)5662 if not success and selector:5663 self.wait_for_ready_state_complete()5664 element = page_actions.wait_for_element_visible(5665 self.driver, selector, by, timeout=settings.SMALL_TIMEOUT)5666 self.__demo_mode_pause_if_active(tiny=True)5667 def __slow_scroll_to_element(self, element):5668 try:5669 js_utils.slow_scroll_to_element(self.driver, element, self.browser)5670 except Exception:5671 # Scroll to the element instantly if the slow scroll fails5672 js_utils.scroll_to_element(self.driver, element)5673 def __highlight_with_assert_success(5674 self, message, selector, by=By.CSS_SELECTOR):5675 selector, by = self.__recalculate_selector(selector, by)5676 element = self.wait_for_element_visible(5677 selector, by=by, timeout=settings.SMALL_TIMEOUT)5678 try:5679 selector = self.convert_to_css_selector(selector, by=by)5680 except Exception:5681 # Don't highlight if can't convert to CSS_SELECTOR5682 return5683 try:5684 scroll_distance = js_utils.get_scroll_distance_to_element(5685 self.driver, element)5686 if abs(scroll_distance) > SSMD:5687 self.__jquery_slow_scroll_to(selector, by)5688 else:5689 self.__slow_scroll_to_element(element)5690 except (StaleElementReferenceException, ENI_Exception):5691 self.wait_for_ready_state_complete()5692 time.sleep(0.12)5693 element = self.wait_for_element_visible(5694 selector, by=by, timeout=settings.SMALL_TIMEOUT)5695 self.__slow_scroll_to_element(element)5696 o_bs = '' # original_box_shadow5697 try:5698 style = element.get_attribute('style')5699 except (StaleElementReferenceException, ENI_Exception):5700 self.wait_for_ready_state_complete()5701 time.sleep(0.12)...

Full Screen

Full Screen

Automation Testing Tutorials

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.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run SeleniumBase automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful