Best Python code snippet using SeleniumBase
base_case.py
Source:base_case.py
...1588 extension_dir = self.extension_dir1589 # Due to https://stackoverflow.com/questions/23055651/ , skip extension1590 # if self.demo_mode or self.masterqa_mode:1591 # disable_csp = True1592 test_id = self.__get_test_id()1593 if cap_file is None:1594 cap_file = self.cap_file1595 if cap_string is None:1596 cap_string = self.cap_string1597 if is_mobile is None:1598 is_mobile = False1599 if d_width is None:1600 d_width = self.__device_width1601 if d_height is None:1602 d_height = self.__device_height1603 if d_p_r is None:1604 d_p_r = self.__device_pixel_ratio1605 valid_browsers = constants.ValidBrowsers.valid_browsers1606 if browser_name not in valid_browsers:1607 raise Exception("Browser: {%s} is not a valid browser option. "1608 "Valid options = {%s}" % (browser, valid_browsers))1609 # Launch a web browser1610 from seleniumbase.core import browser_launcher1611 new_driver = browser_launcher.get_driver(browser_name=browser_name,1612 headless=headless,1613 use_grid=use_grid,1614 servername=servername,1615 port=port,1616 proxy_string=proxy_string,1617 user_agent=user_agent,1618 cap_file=cap_file,1619 cap_string=cap_string,1620 disable_csp=disable_csp,1621 enable_sync=enable_sync,1622 use_auto_ext=use_auto_ext,1623 no_sandbox=no_sandbox,1624 disable_gpu=disable_gpu,1625 incognito=incognito,1626 guest_mode=guest_mode,1627 devtools=devtools,1628 user_data_dir=user_data_dir,1629 extension_zip=extension_zip,1630 extension_dir=extension_dir,1631 test_id=test_id,1632 mobile_emulator=is_mobile,1633 device_width=d_width,1634 device_height=d_height,1635 device_pixel_ratio=d_p_r)1636 self._drivers_list.append(new_driver)1637 if switch_to:1638 self.driver = new_driver1639 if self.headless:1640 # Make sure the invisible browser window is big enough1641 width = settings.HEADLESS_START_WIDTH1642 height = settings.HEADLESS_START_HEIGHT1643 try:1644 self.driver.set_window_size(width, height)1645 self.wait_for_ready_state_complete()1646 except Exception:1647 # This shouldn't fail, but in case it does,1648 # get safely through setUp() so that1649 # WebDrivers can get closed during tearDown().1650 pass1651 else:1652 if self.browser == 'chrome' or self.browser == 'edge':1653 width = settings.CHROME_START_WIDTH1654 height = settings.CHROME_START_HEIGHT1655 try:1656 if self.maximize_option:1657 self.driver.maximize_window()1658 else:1659 self.driver.set_window_size(width, height)1660 self.wait_for_ready_state_complete()1661 except Exception:1662 pass # Keep existing browser resolution1663 elif self.browser == 'firefox':1664 pass # No changes1665 elif self.browser == 'safari':1666 if self.maximize_option:1667 try:1668 self.driver.maximize_window()1669 self.wait_for_ready_state_complete()1670 except Exception:1671 pass # Keep existing browser resolution1672 else:1673 try:1674 self.driver.set_window_rect(10, 30, 945, 630)1675 except Exception:1676 pass1677 if self.start_page and len(self.start_page) >= 4:1678 if page_utils.is_valid_url(self.start_page):1679 self.open(self.start_page)1680 else:1681 new_start_page = "http://" + self.start_page1682 if page_utils.is_valid_url(new_start_page):1683 self.open(new_start_page)1684 return new_driver1685 def switch_to_driver(self, driver):1686 """ Sets self.driver to the specified driver.1687 You may need this if using self.get_new_driver() in your code. """1688 self.driver = driver1689 def switch_to_default_driver(self):1690 """ Sets self.driver to the default/original driver. """1691 self.driver = self._default_driver1692 def save_screenshot(self, name, folder=None):1693 """ The screenshot will be in PNG format. """1694 return page_actions.save_screenshot(self.driver, name, folder)1695 def save_page_source(self, name, folder=None):1696 """ Saves the page HTML to the current directory (or given subfolder).1697 If the folder specified doesn't exist, it will get created.1698 @Params1699 name - The file name to save the current page's HTML to.1700 folder - The folder to save the file to. (Default = current folder)1701 """1702 return page_actions.save_page_source(self.driver, name, folder)1703 def save_cookies(self, name="cookies.txt"):1704 """ Saves the page cookies to the "saved_cookies" folder. """1705 cookies = self.driver.get_cookies()1706 json_cookies = json.dumps(cookies)1707 if name.endswith('/'):1708 raise Exception("Invalid filename for Cookies!")1709 if '/' in name:1710 name = name.split('/')[-1]1711 if len(name) < 1:1712 raise Exception("Filename for Cookies is too short!")1713 if not name.endswith(".txt"):1714 name = name + ".txt"1715 folder = constants.SavedCookies.STORAGE_FOLDER1716 abs_path = os.path.abspath('.')1717 file_path = abs_path + "/%s" % folder1718 if not os.path.exists(file_path):1719 os.makedirs(file_path)1720 cookies_file_path = "%s/%s" % (file_path, name)1721 cookies_file = codecs.open(cookies_file_path, "w+")1722 cookies_file.writelines(json_cookies)1723 cookies_file.close()1724 def load_cookies(self, name="cookies.txt"):1725 """ Loads the page cookies from the "saved_cookies" folder. """1726 if name.endswith('/'):1727 raise Exception("Invalid filename for Cookies!")1728 if '/' in name:1729 name = name.split('/')[-1]1730 if len(name) < 1:1731 raise Exception("Filename for Cookies is too short!")1732 if not name.endswith(".txt"):1733 name = name + ".txt"1734 folder = constants.SavedCookies.STORAGE_FOLDER1735 abs_path = os.path.abspath('.')1736 file_path = abs_path + "/%s" % folder1737 cookies_file_path = "%s/%s" % (file_path, name)1738 json_cookies = None1739 with open(cookies_file_path, 'r') as f:1740 json_cookies = f.read().strip()1741 cookies = json.loads(json_cookies)1742 for cookie in cookies:1743 if 'expiry' in cookie:1744 del cookie['expiry']1745 self.driver.add_cookie(cookie)1746 def delete_all_cookies(self):1747 """ Deletes all cookies in the web browser.1748 Does NOT delete the saved cookies file. """1749 self.driver.delete_all_cookies()1750 def delete_saved_cookies(self, name="cookies.txt"):1751 """ Deletes the cookies file from the "saved_cookies" folder.1752 Does NOT delete the cookies from the web browser. """1753 if name.endswith('/'):1754 raise Exception("Invalid filename for Cookies!")1755 if '/' in name:1756 name = name.split('/')[-1]1757 if len(name) < 1:1758 raise Exception("Filename for Cookies is too short!")1759 if not name.endswith(".txt"):1760 name = name + ".txt"1761 folder = constants.SavedCookies.STORAGE_FOLDER1762 abs_path = os.path.abspath('.')1763 file_path = abs_path + "/%s" % folder1764 cookies_file_path = "%s/%s" % (file_path, name)1765 if os.path.exists(cookies_file_path):1766 if cookies_file_path.endswith('.txt'):1767 os.remove(cookies_file_path)1768 def wait_for_ready_state_complete(self, timeout=None):1769 try:1770 # If there's an alert, skip1771 self.driver.switch_to.alert1772 return1773 except Exception:1774 # If there's no alert, continue1775 pass1776 if not timeout:1777 timeout = settings.EXTREME_TIMEOUT1778 if self.timeout_multiplier and timeout == settings.EXTREME_TIMEOUT:1779 timeout = self.__get_new_timeout(timeout)1780 is_ready = js_utils.wait_for_ready_state_complete(self.driver, timeout)1781 self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)1782 if self.js_checking_on:1783 self.assert_no_js_errors()1784 if self.ad_block_on:1785 # If the ad_block feature is enabled, then block ads for new URLs1786 current_url = self.get_current_url()1787 if not current_url == self.__last_page_load_url:1788 time.sleep(0.02)1789 self.ad_block()1790 time.sleep(0.01)1791 if self.is_element_present("iframe"):1792 time.sleep(0.07) # iframe ads take slightly longer to load1793 self.ad_block() # Do ad_block on slower-loading iframes1794 self.__last_page_load_url = current_url1795 return is_ready1796 def wait_for_angularjs(self, timeout=None, **kwargs):1797 if not timeout:1798 timeout = settings.LARGE_TIMEOUT1799 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:1800 timeout = self.__get_new_timeout(timeout)1801 js_utils.wait_for_angularjs(self.driver, timeout, **kwargs)1802 def sleep(self, seconds):1803 if not sb_config.time_limit:1804 time.sleep(seconds)1805 elif seconds <= 0.3:1806 shared_utils.check_if_time_limit_exceeded()1807 time.sleep(seconds)1808 shared_utils.check_if_time_limit_exceeded()1809 else:1810 start_ms = time.time() * 1000.01811 stop_ms = start_ms + (seconds * 1000.0)1812 for x in range(int(seconds * 5)):1813 shared_utils.check_if_time_limit_exceeded()1814 now_ms = time.time() * 1000.01815 if now_ms >= stop_ms:1816 break1817 time.sleep(0.2)1818 def activate_jquery(self):1819 """ If "jQuery is not defined", use this method to activate it for use.1820 This happens because jQuery is not always defined on web sites. """1821 js_utils.activate_jquery(self.driver)1822 self.wait_for_ready_state_complete()1823 def __are_quotes_escaped(self, string):1824 return js_utils.are_quotes_escaped(string)1825 def __escape_quotes_if_needed(self, string):1826 return js_utils.escape_quotes_if_needed(string)1827 def bring_to_front(self, selector, by=By.CSS_SELECTOR):1828 """ Updates the Z-index of a page element to bring it into view.1829 Useful when getting a WebDriverException, such as the one below:1830 { Element is not clickable at point (#, #).1831 Other element would receive the click: ... } """1832 selector, by = self.__recalculate_selector(selector, by)1833 self.wait_for_element_visible(1834 selector, by=by, timeout=settings.SMALL_TIMEOUT)1835 try:1836 selector = self.convert_to_css_selector(selector, by=by)1837 except Exception:1838 # Don't run action if can't convert to CSS_Selector for JavaScript1839 return1840 selector = re.escape(selector)1841 selector = self.__escape_quotes_if_needed(selector)1842 script = ("""document.querySelector('%s').style.zIndex = '999999';"""1843 % selector)1844 self.execute_script(script)1845 def highlight_click(self, selector, by=By.CSS_SELECTOR,1846 loops=3, scroll=True):1847 if not self.demo_mode:1848 self.highlight(selector, by=by, loops=loops, scroll=scroll)1849 self.click(selector, by=by)1850 def highlight_update_text(self, selector, text, by=By.CSS_SELECTOR,1851 loops=3, scroll=True):1852 if not self.demo_mode:1853 self.highlight(selector, by=by, loops=loops, scroll=scroll)1854 self.update_text(selector, text, by=by)1855 def highlight(self, selector, by=By.CSS_SELECTOR,1856 loops=None, scroll=True):1857 """ This method uses fancy JavaScript to highlight an element.1858 Used during demo_mode.1859 @Params1860 selector - the selector of the element to find1861 by - the type of selector to search by (Default: CSS)1862 loops - # of times to repeat the highlight animation1863 (Default: 4. Each loop lasts for about 0.18s)1864 scroll - the option to scroll to the element first (Default: True)1865 """1866 selector, by = self.__recalculate_selector(selector, by)1867 element = self.wait_for_element_visible(1868 selector, by=by, timeout=settings.SMALL_TIMEOUT)1869 if not loops:1870 loops = settings.HIGHLIGHTS1871 if scroll:1872 try:1873 self.__slow_scroll_to_element(element)1874 except (StaleElementReferenceException, ENI_Exception):1875 self.wait_for_ready_state_complete()1876 time.sleep(0.05)1877 element = self.wait_for_element_visible(1878 selector, by=by, timeout=settings.SMALL_TIMEOUT)1879 self.__slow_scroll_to_element(element)1880 try:1881 selector = self.convert_to_css_selector(selector, by=by)1882 except Exception:1883 # Don't highlight if can't convert to CSS_SELECTOR1884 return1885 if self.highlights:1886 loops = self.highlights1887 if self.browser == 'ie':1888 loops = 1 # Override previous setting because IE is slow1889 loops = int(loops)1890 o_bs = '' # original_box_shadow1891 try:1892 style = element.get_attribute('style')1893 except (StaleElementReferenceException, ENI_Exception):1894 self.wait_for_ready_state_complete()1895 time.sleep(0.05)1896 element = self.wait_for_element_visible(1897 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)1898 style = element.get_attribute('style')1899 if style:1900 if 'box-shadow: ' in style:1901 box_start = style.find('box-shadow: ')1902 box_end = style.find(';', box_start) + 11903 original_box_shadow = style[box_start:box_end]1904 o_bs = original_box_shadow1905 if ":contains" not in selector and ":first" not in selector:1906 selector = re.escape(selector)1907 selector = self.__escape_quotes_if_needed(selector)1908 self.__highlight_with_js(selector, loops, o_bs)1909 else:1910 selector = self.__make_css_match_first_element_only(selector)1911 selector = re.escape(selector)1912 selector = self.__escape_quotes_if_needed(selector)1913 try:1914 self.__highlight_with_jquery(selector, loops, o_bs)1915 except Exception:1916 pass # JQuery probably couldn't load. Skip highlighting.1917 time.sleep(0.065)1918 def __highlight_with_js(self, selector, loops, o_bs):1919 js_utils.highlight_with_js(self.driver, selector, loops, o_bs)1920 def __highlight_with_jquery(self, selector, loops, o_bs):1921 js_utils.highlight_with_jquery(self.driver, selector, loops, o_bs)1922 def press_up_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):1923 """ Simulates pressing the UP Arrow on the keyboard.1924 By default, "html" will be used as the CSS Selector target.1925 You can specify how many times in-a-row the action happens. """1926 if times < 1:1927 return1928 element = self.wait_for_element_present(selector)1929 self.__demo_mode_highlight_if_active(selector, by)1930 if not self.demo_mode:1931 self.__scroll_to_element(element, selector, by)1932 for i in range(int(times)):1933 try:1934 element.send_keys(Keys.ARROW_UP)1935 except Exception:1936 self.wait_for_ready_state_complete()1937 element = self.wait_for_element_visible(selector)1938 element.send_keys(Keys.ARROW_UP)1939 time.sleep(0.01)1940 if self.slow_mode:1941 time.sleep(0.1)1942 def press_down_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):1943 """ Simulates pressing the DOWN Arrow on the keyboard.1944 By default, "html" will be used as the CSS Selector target.1945 You can specify how many times in-a-row the action happens. """1946 if times < 1:1947 return1948 element = self.wait_for_element_present(selector)1949 self.__demo_mode_highlight_if_active(selector, by)1950 if not self.demo_mode:1951 self.__scroll_to_element(element, selector, by)1952 for i in range(int(times)):1953 try:1954 element.send_keys(Keys.ARROW_DOWN)1955 except Exception:1956 self.wait_for_ready_state_complete()1957 element = self.wait_for_element_visible(selector)1958 element.send_keys(Keys.ARROW_DOWN)1959 time.sleep(0.01)1960 if self.slow_mode:1961 time.sleep(0.1)1962 def press_left_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):1963 """ Simulates pressing the LEFT Arrow on the keyboard.1964 By default, "html" will be used as the CSS Selector target.1965 You can specify how many times in-a-row the action happens. """1966 if times < 1:1967 return1968 element = self.wait_for_element_present(selector)1969 self.__demo_mode_highlight_if_active(selector, by)1970 if not self.demo_mode:1971 self.__scroll_to_element(element, selector, by)1972 for i in range(int(times)):1973 try:1974 element.send_keys(Keys.ARROW_LEFT)1975 except Exception:1976 self.wait_for_ready_state_complete()1977 element = self.wait_for_element_visible(selector)1978 element.send_keys(Keys.ARROW_LEFT)1979 time.sleep(0.01)1980 if self.slow_mode:1981 time.sleep(0.1)1982 def press_right_arrow(self, selector="html", times=1, by=By.CSS_SELECTOR):1983 """ Simulates pressing the RIGHT Arrow on the keyboard.1984 By default, "html" will be used as the CSS Selector target.1985 You can specify how many times in-a-row the action happens. """1986 if times < 1:1987 return1988 element = self.wait_for_element_present(selector)1989 self.__demo_mode_highlight_if_active(selector, by)1990 if not self.demo_mode:1991 self.__scroll_to_element(element, selector, by)1992 for i in range(int(times)):1993 try:1994 element.send_keys(Keys.ARROW_RIGHT)1995 except Exception:1996 self.wait_for_ready_state_complete()1997 element = self.wait_for_element_visible(selector)1998 element.send_keys(Keys.ARROW_RIGHT)1999 time.sleep(0.01)2000 if self.slow_mode:2001 time.sleep(0.1)2002 def scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):2003 ''' Fast scroll to destination '''2004 if not timeout:2005 timeout = settings.SMALL_TIMEOUT2006 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2007 timeout = self.__get_new_timeout(timeout)2008 if self.demo_mode or self.slow_mode:2009 self.slow_scroll_to(selector, by=by, timeout=timeout)2010 return2011 element = self.wait_for_element_visible(2012 selector, by=by, timeout=timeout)2013 try:2014 self.__scroll_to_element(element, selector, by)2015 except (StaleElementReferenceException, ENI_Exception):2016 self.wait_for_ready_state_complete()2017 time.sleep(0.05)2018 element = self.wait_for_element_visible(2019 selector, by=by, timeout=timeout)2020 self.__scroll_to_element(element, selector, by)2021 def slow_scroll_to(self, selector, by=By.CSS_SELECTOR, timeout=None):2022 ''' Slow motion scroll to destination '''2023 if not timeout:2024 timeout = settings.SMALL_TIMEOUT2025 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2026 timeout = self.__get_new_timeout(timeout)2027 selector, by = self.__recalculate_selector(selector, by)2028 element = self.wait_for_element_visible(2029 selector, by=by, timeout=timeout)2030 try:2031 self.__slow_scroll_to_element(element)2032 except (StaleElementReferenceException, ENI_Exception):2033 self.wait_for_ready_state_complete()2034 time.sleep(0.05)2035 element = self.wait_for_element_visible(2036 selector, by=by, timeout=timeout)2037 self.__slow_scroll_to_element(element)2038 def scroll_to_top(self):2039 """ Scroll to the top of the page. """2040 scroll_script = "window.scrollTo(0, 0);"2041 try:2042 self.execute_script(scroll_script)2043 time.sleep(0.012)2044 return True2045 except Exception:2046 return False2047 def scroll_to_bottom(self):2048 """ Scroll to the bottom of the page. """2049 scroll_script = "window.scrollTo(0, 10000);"2050 try:2051 self.execute_script(scroll_script)2052 time.sleep(0.012)2053 return True2054 except Exception:2055 return False2056 def click_xpath(self, xpath):2057 # Technically self.click() will automatically detect an xpath selector,2058 # so self.click_xpath() is just a longer name for the same action.2059 self.click(xpath, by=By.XPATH)2060 def js_click(self, selector, by=By.CSS_SELECTOR, all_matches=False):2061 """ Clicks an element using pure JS. Does not use jQuery.2062 If "all_matches" is False, only the first match is clicked. """2063 selector, by = self.__recalculate_selector(selector, by)2064 if by == By.LINK_TEXT:2065 message = (2066 "Pure JavaScript doesn't support clicking by Link Text. "2067 "You may want to use self.jquery_click() instead, which "2068 "allows this with :contains(), assuming jQuery isn't blocked. "2069 "For now, self.js_click() will use a regular WebDriver click.")2070 logging.debug(message)2071 self.click(selector, by=by)2072 return2073 element = self.wait_for_element_present(2074 selector, by=by, timeout=settings.SMALL_TIMEOUT)2075 if self.is_element_visible(selector, by=by):2076 self.__demo_mode_highlight_if_active(selector, by)2077 if not self.demo_mode:2078 self.__scroll_to_element(element, selector, by)2079 css_selector = self.convert_to_css_selector(selector, by=by)2080 css_selector = re.escape(css_selector)2081 css_selector = self.__escape_quotes_if_needed(css_selector)2082 if not all_matches:2083 self.__js_click(selector, by=by) # The real "magic" happens2084 else:2085 self.__js_click_all(selector, by=by) # The real "magic" happens2086 self.wait_for_ready_state_complete()2087 self.__demo_mode_pause_if_active()2088 def js_click_all(self, selector, by=By.CSS_SELECTOR):2089 """ Clicks all matching elements using pure JS. (No jQuery) """2090 self.js_click(selector, by=By.CSS_SELECTOR, all_matches=True)2091 def jquery_click(self, selector, by=By.CSS_SELECTOR):2092 """ Clicks an element using jQuery. Different from using pure JS. """2093 selector, by = self.__recalculate_selector(selector, by)2094 self.wait_for_element_present(2095 selector, by=by, timeout=settings.SMALL_TIMEOUT)2096 if self.is_element_visible(selector, by=by):2097 self.__demo_mode_highlight_if_active(selector, by)2098 selector = self.convert_to_css_selector(selector, by=by)2099 selector = self.__make_css_match_first_element_only(selector)2100 click_script = """jQuery('%s')[0].click()""" % selector2101 self.safe_execute_script(click_script)2102 self.__demo_mode_pause_if_active()2103 def jquery_click_all(self, selector, by=By.CSS_SELECTOR):2104 """ Clicks all matching elements using jQuery. """2105 selector, by = self.__recalculate_selector(selector, by)2106 self.wait_for_element_present(2107 selector, by=by, timeout=settings.SMALL_TIMEOUT)2108 if self.is_element_visible(selector, by=by):2109 self.__demo_mode_highlight_if_active(selector, by)2110 selector = self.convert_to_css_selector(selector, by=by)2111 click_script = """jQuery('%s').click()""" % selector2112 self.safe_execute_script(click_script)2113 self.__demo_mode_pause_if_active()2114 def hide_element(self, selector, by=By.CSS_SELECTOR):2115 """ Hide the first element on the page that matches the selector. """2116 selector, by = self.__recalculate_selector(selector, by)2117 selector = self.convert_to_css_selector(selector, by=by)2118 selector = self.__make_css_match_first_element_only(selector)2119 hide_script = """jQuery('%s').hide()""" % selector2120 self.safe_execute_script(hide_script)2121 def hide_elements(self, selector, by=By.CSS_SELECTOR):2122 """ Hide all elements on the page that match the selector. """2123 selector, by = self.__recalculate_selector(selector, by)2124 selector = self.convert_to_css_selector(selector, by=by)2125 hide_script = """jQuery('%s').hide()""" % selector2126 self.safe_execute_script(hide_script)2127 def show_element(self, selector, by=By.CSS_SELECTOR):2128 """ Show the first element on the page that matches the selector. """2129 selector, by = self.__recalculate_selector(selector, by)2130 selector = self.convert_to_css_selector(selector, by=by)2131 selector = self.__make_css_match_first_element_only(selector)2132 show_script = """jQuery('%s').show(0)""" % selector2133 self.safe_execute_script(show_script)2134 def show_elements(self, selector, by=By.CSS_SELECTOR):2135 """ Show all elements on the page that match the selector. """2136 selector, by = self.__recalculate_selector(selector, by)2137 selector = self.convert_to_css_selector(selector, by=by)2138 show_script = """jQuery('%s').show(0)""" % selector2139 self.safe_execute_script(show_script)2140 def remove_element(self, selector, by=By.CSS_SELECTOR):2141 """ Remove the first element on the page that matches the selector. """2142 selector, by = self.__recalculate_selector(selector, by)2143 selector = self.convert_to_css_selector(selector, by=by)2144 selector = self.__make_css_match_first_element_only(selector)2145 remove_script = """jQuery('%s').remove()""" % selector2146 self.safe_execute_script(remove_script)2147 def remove_elements(self, selector, by=By.CSS_SELECTOR):2148 """ Remove all elements on the page that match the selector. """2149 selector, by = self.__recalculate_selector(selector, by)2150 selector = self.convert_to_css_selector(selector, by=by)2151 remove_script = """jQuery('%s').remove()""" % selector2152 self.safe_execute_script(remove_script)2153 def ad_block(self):2154 """ Block ads that appear on the current web page. """2155 self.wait_for_ready_state_complete()2156 from seleniumbase.config import ad_block_list2157 for css_selector in ad_block_list.AD_BLOCK_LIST:2158 css_selector = re.escape(css_selector)2159 css_selector = self.__escape_quotes_if_needed(css_selector)2160 script = ("""var $elements = document.querySelectorAll('%s');2161 var index = 0, length = $elements.length;2162 for(; index < length; index++){2163 $elements[index].remove();}"""2164 % css_selector)2165 try:2166 self.execute_script(script)2167 except Exception:2168 pass # Don't fail test if ad_blocking fails2169 def get_domain_url(self, url):2170 return page_utils.get_domain_url(url)2171 def get_beautiful_soup(self, source=None):2172 """ BeautifulSoup is a toolkit for dissecting an HTML document2173 and extracting what you need. It's great for screen-scraping! """2174 from bs4 import BeautifulSoup2175 if not source:2176 self.wait_for_ready_state_complete()2177 source = self.get_page_source()2178 soup = BeautifulSoup(source, "html.parser")2179 return soup2180 def get_unique_links(self):2181 """ Get all unique links in the html of the page source.2182 Page links include those obtained from:2183 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2184 page_url = self.get_current_url()2185 soup = self.get_beautiful_soup(self.get_page_source())2186 links = page_utils._get_unique_links(page_url, soup)2187 return links2188 def get_link_status_code(self, link, allow_redirects=False, timeout=5):2189 """ Get the status code of a link.2190 If the timeout is exceeded, will return a 404.2191 For a list of available status codes, see:2192 https://en.wikipedia.org/wiki/List_of_HTTP_status_codes """2193 status_code = page_utils._get_link_status_code(2194 link, allow_redirects=allow_redirects, timeout=timeout)2195 return status_code2196 def assert_link_status_code_is_not_404(self, link):2197 status_code = str(self.get_link_status_code(link))2198 bad_link_str = 'Error: "%s" returned a 404!' % link2199 self.assertNotEqual(status_code, "404", bad_link_str)2200 def assert_no_404_errors(self, multithreaded=True):2201 """ Assert no 404 errors from page links obtained from:2202 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2203 all_links = self.get_unique_links()2204 links = []2205 for link in all_links:2206 if "javascript:" not in link and "mailto:" not in link:2207 links.append(link)2208 if multithreaded:2209 from multiprocessing.dummy import Pool as ThreadPool2210 pool = ThreadPool(10)2211 pool.map(self.assert_link_status_code_is_not_404, links)2212 pool.close()2213 pool.join()2214 else:2215 for link in links:2216 self.assert_link_status_code_is_not_404(link)2217 if self.demo_mode:2218 a_t = "ASSERT NO 404 ERRORS"2219 if self._language != "English":2220 from seleniumbase.fixtures.words import SD2221 a_t = SD.translate_assert_no_404_errors(self._language)2222 messenger_post = ("%s" % a_t)2223 self.__highlight_with_assert_success(messenger_post, "html")2224 def print_unique_links_with_status_codes(self):2225 """ Finds all unique links in the html of the page source2226 and then prints out those links with their status codes.2227 Format: ["link" -> "status_code"] (per line)2228 Page links include those obtained from:2229 "a"->"href", "img"->"src", "link"->"href", and "script"->"src". """2230 page_url = self.get_current_url()2231 soup = self.get_beautiful_soup(self.get_page_source())2232 page_utils._print_unique_links_with_status_codes(page_url, soup)2233 def __fix_unicode_conversion(self, text):2234 """ Fixing Chinese characters when converting from PDF to HTML. """2235 if sys.version_info[0] < 3:2236 # Update encoding for Python 2 users2237 reload(sys) # noqa2238 sys.setdefaultencoding('utf8')2239 text = text.replace(u'\u2f8f', u'\u884c')2240 text = text.replace(u'\u2f45', u'\u65b9')2241 text = text.replace(u'\u2f08', u'\u4eba')2242 text = text.replace(u'\u2f70', u'\u793a')2243 return text2244 def get_pdf_text(self, pdf, page=None, maxpages=None,2245 password=None, codec='utf-8', wrap=False, nav=False,2246 override=False):2247 """ Gets text from a PDF file.2248 PDF can be either a URL or a file path on the local file system.2249 @Params2250 pdf - The URL or file path of the PDF file.2251 page - The page number (or a list of page numbers) of the PDF.2252 If a page number is provided, looks only at that page.2253 (1 is the first page, 2 is the second page, etc.)2254 If no page number is provided, returns all PDF text.2255 maxpages - Instead of providing a page number, you can provide2256 the number of pages to use from the beginning.2257 password - If the PDF is password-protected, enter it here.2258 codec - The compression format for character encoding.2259 (The default codec used by this method is 'utf-8'.)2260 wrap - Replaces ' \n' with ' ' so that individual sentences2261 from a PDF don't get broken up into seperate lines when2262 getting converted into text format.2263 nav - If PDF is a URL, navigates to the URL in the browser first.2264 (Not needed because the PDF will be downloaded anyway.)2265 override - If the PDF file to be downloaded already exists in the2266 downloaded_files/ folder, that PDF will be used2267 instead of downloading it again. """2268 import warnings2269 with warnings.catch_warnings():2270 warnings.simplefilter("ignore", category=UserWarning)2271 from pdfminer.high_level import extract_text2272 if not password:2273 password = ''2274 if not maxpages:2275 maxpages = 02276 if not pdf.lower().endswith('.pdf'):2277 raise Exception("%s is not a PDF file! (Expecting a .pdf)" % pdf)2278 file_path = None2279 if page_utils.is_valid_url(pdf):2280 if nav:2281 if self.get_current_url() != pdf:2282 self.open(pdf)2283 file_name = pdf.split('/')[-1]2284 file_path = self.get_downloads_folder() + '/' + file_name2285 if not os.path.exists(file_path):2286 self.download_file(pdf)2287 elif override:2288 self.download_file(pdf)2289 else:2290 if not os.path.exists(pdf):2291 raise Exception("%s is not a valid URL or file path!" % pdf)2292 file_path = os.path.abspath(pdf)2293 page_search = None # (Pages are delimited by '\x0c')2294 if type(page) is list:2295 pages = page2296 page_search = []2297 for page in pages:2298 page_search.append(page - 1)2299 elif type(page) is int:2300 page = page - 12301 if page < 0:2302 page = 02303 page_search = [page]2304 else:2305 page_search = None2306 pdf_text = extract_text(2307 file_path, password='', page_numbers=page_search,2308 maxpages=maxpages, caching=False, codec=codec)2309 pdf_text = self.__fix_unicode_conversion(pdf_text)2310 if wrap:2311 pdf_text = pdf_text.replace(' \n', ' ')2312 pdf_text = pdf_text.strip() # Remove leading and trailing whitespace2313 return pdf_text2314 def assert_pdf_text(self, pdf, text, page=None, maxpages=None,2315 password=None, codec='utf-8', wrap=True, nav=False,2316 override=False):2317 """ Asserts text in a PDF file.2318 PDF can be either a URL or a file path on the local file system.2319 @Params2320 pdf - The URL or file path of the PDF file.2321 text - The expected text to verify in the PDF.2322 page - The page number of the PDF to use (optional).2323 If a page number is provided, looks only at that page.2324 (1 is the first page, 2 is the second page, etc.)2325 If no page number is provided, looks at all the pages.2326 maxpages - Instead of providing a page number, you can provide2327 the number of pages to use from the beginning.2328 password - If the PDF is password-protected, enter it here.2329 codec - The compression format for character encoding.2330 (The default codec used by this method is 'utf-8'.)2331 wrap - Replaces ' \n' with ' ' so that individual sentences2332 from a PDF don't get broken up into seperate lines when2333 getting converted into text format.2334 nav - If PDF is a URL, navigates to the URL in the browser first.2335 (Not needed because the PDF will be downloaded anyway.)2336 override - If the PDF file to be downloaded already exists in the2337 downloaded_files/ folder, that PDF will be used2338 instead of downloading it again. """2339 text = self.__fix_unicode_conversion(text)2340 if not codec:2341 codec = 'utf-8'2342 pdf_text = self.get_pdf_text(2343 pdf, page=page, maxpages=maxpages, password=password, codec=codec,2344 wrap=wrap, nav=nav, override=override)2345 if type(page) is int:2346 if text not in pdf_text:2347 raise Exception("PDF [%s] is missing expected text [%s] on "2348 "page [%s]!" % (pdf, text, page))2349 else:2350 if text not in pdf_text:2351 raise Exception("PDF [%s] is missing expected text [%s]!"2352 "" % (pdf, text))2353 return True2354 def create_folder(self, folder):2355 """ Creates a folder of the given name if it doesn't already exist. """2356 if folder.endswith("/"):2357 folder = folder[:-1]2358 if len(folder) < 1:2359 raise Exception("Minimum folder name length = 1.")2360 if not os.path.exists(folder):2361 try:2362 os.makedirs(folder)2363 except Exception:2364 pass2365 def choose_file(self, selector, file_path, by=By.CSS_SELECTOR,2366 timeout=None):2367 """ This method is used to choose a file to upload to a website.2368 It works by populating a file-chooser "input" field of type="file".2369 A relative file_path will get converted into an absolute file_path.2370 Example usage:2371 self.choose_file('input[type="file"]', "my_dir/my_file.txt")2372 """2373 if not timeout:2374 timeout = settings.LARGE_TIMEOUT2375 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2376 timeout = self.__get_new_timeout(timeout)2377 selector, by = self.__recalculate_selector(selector, by)2378 abs_path = os.path.abspath(file_path)2379 self.add_text(selector, abs_path, by=by, timeout=timeout)2380 def save_element_as_image_file(self, selector, file_name, folder=None):2381 """ Take a screenshot of an element and save it as an image file.2382 If no folder is specified, will save it to the current folder. """2383 element = self.wait_for_element_visible(selector)2384 element_png = element.screenshot_as_png2385 if len(file_name.split('.')[0]) < 1:2386 raise Exception("Error: file_name length must be > 0.")2387 if not file_name.endswith(".png"):2388 file_name = file_name + ".png"2389 image_file_path = None2390 if folder:2391 if folder.endswith("/"):2392 folder = folder[:-1]2393 if len(folder) > 0:2394 self.create_folder(folder)2395 image_file_path = "%s/%s" % (folder, file_name)2396 if not image_file_path:2397 image_file_path = file_name2398 with open(image_file_path, "wb") as file:2399 file.write(element_png)2400 def download_file(self, file_url, destination_folder=None):2401 """ Downloads the file from the url to the destination folder.2402 If no destination folder is specified, the default one is used.2403 (The default downloads folder = "./downloaded_files") """2404 if not destination_folder:2405 destination_folder = constants.Files.DOWNLOADS_FOLDER2406 if not os.path.exists(destination_folder):2407 os.makedirs(destination_folder)2408 page_utils._download_file_to(file_url, destination_folder)2409 def save_file_as(self, file_url, new_file_name, destination_folder=None):2410 """ Similar to self.download_file(), except that you get to rename the2411 file being downloaded to whatever you want. """2412 if not destination_folder:2413 destination_folder = constants.Files.DOWNLOADS_FOLDER2414 page_utils._download_file_to(2415 file_url, destination_folder, new_file_name)2416 def save_data_as(self, data, file_name, destination_folder=None):2417 """ Saves the data specified to a file of the name specified.2418 If no destination folder is specified, the default one is used.2419 (The default downloads folder = "./downloaded_files") """2420 if not destination_folder:2421 destination_folder = constants.Files.DOWNLOADS_FOLDER2422 page_utils._save_data_as(data, destination_folder, file_name)2423 def get_downloads_folder(self):2424 """ Returns the OS path of the Downloads Folder.2425 (Works with Chrome and Firefox only, for now.) """2426 from seleniumbase.core import download_helper2427 return download_helper.get_downloads_folder()2428 def get_path_of_downloaded_file(self, file):2429 """ Returns the OS path of the downloaded file. """2430 return os.path.join(self.get_downloads_folder(), file)2431 def is_downloaded_file_present(self, file):2432 """ Checks if the file exists in the Downloads Folder. """2433 return os.path.exists(self.get_path_of_downloaded_file(file))2434 def assert_downloaded_file(self, file, timeout=None):2435 """ Asserts that the file exists in the Downloads Folder. """2436 if not timeout:2437 timeout = settings.SMALL_TIMEOUT2438 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2439 timeout = self.__get_new_timeout(timeout)2440 start_ms = time.time() * 1000.02441 stop_ms = start_ms + (timeout * 1000.0)2442 for x in range(int(timeout)):2443 shared_utils.check_if_time_limit_exceeded()2444 try:2445 self.assertTrue(2446 os.path.exists(self.get_path_of_downloaded_file(file)),2447 "File [%s] was not found in the downloads folder [%s]!"2448 "" % (file, self.get_downloads_folder()))2449 if self.demo_mode:2450 messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)2451 js_utils.post_messenger_success_message(2452 self.driver, messenger_post, self.message_duration)2453 return2454 except Exception:2455 now_ms = time.time() * 1000.02456 if now_ms >= stop_ms:2457 break2458 time.sleep(1)2459 if not os.path.exists(self.get_path_of_downloaded_file(file)):2460 message = (2461 "File {%s} was not found in the downloads folder {%s} "2462 "after %s seconds! (Or the download didn't complete!)"2463 "" % (file, self.get_downloads_folder(), timeout))2464 page_actions.timeout_exception("NoSuchFileException", message)2465 if self.demo_mode:2466 messenger_post = ("ASSERT DOWNLOADED FILE: [%s]" % file)2467 js_utils.post_messenger_success_message(2468 self.driver, messenger_post, self.message_duration)2469 def assert_true(self, expr, msg=None):2470 """ Asserts that the expression is True.2471 Will raise an exception if the statement if False. """2472 self.assertTrue(expr, msg=msg)2473 def assert_false(self, expr, msg=None):2474 """ Asserts that the expression is False.2475 Will raise an exception if the statement if True. """2476 self.assertFalse(expr, msg=msg)2477 def assert_equal(self, first, second, msg=None):2478 """ Asserts that the two values are equal.2479 Will raise an exception if the values are not equal. """2480 self.assertEqual(first, second, msg=msg)2481 def assert_not_equal(self, first, second, msg=None):2482 """ Asserts that the two values are not equal.2483 Will raise an exception if the values are equal. """2484 self.assertNotEqual(first, second, msg=msg)2485 def assert_raises(self, *args, **kwargs):2486 """ Asserts that the following block of code raises an exception.2487 Will raise an exception if the block of code has no exception.2488 Usage Example =>2489 # Verify that the expected exception is raised.2490 with self.assert_raises(Exception):2491 raise Exception("Expected Exception!")2492 """2493 self.assertRaises(*args, **kwargs)2494 def assert_title(self, title):2495 """ Asserts that the web page title matches the expected title. """2496 expected = title2497 actual = self.get_page_title()2498 self.assertEqual(expected, actual, "Expected page title [%s] "2499 "does not match the actual page title [%s]!"2500 "" % (expected, actual))2501 if self.demo_mode:2502 a_t = "ASSERT TITLE"2503 if self._language != "English":2504 from seleniumbase.fixtures.words import SD2505 a_t = SD.translate_assert_title(self._language)2506 messenger_post = ("%s: {%s}" % (a_t, title))2507 self.__highlight_with_assert_success(messenger_post, "html")2508 def assert_no_js_errors(self):2509 """ Asserts that there are no JavaScript "SEVERE"-level page errors.2510 Works ONLY for Chrome (non-headless) and Chrome-based browsers.2511 Does NOT work on Firefox, Edge, IE, and some other browsers:2512 * See https://github.com/SeleniumHQ/selenium/issues/11612513 Based on the following Stack Overflow solution:2514 * https://stackoverflow.com/a/41150512/7058266 """2515 self.wait_for_ready_state_complete()2516 time.sleep(0.1) # May take a moment for errors to appear after loads.2517 try:2518 browser_logs = self.driver.get_log('browser')2519 except (ValueError, WebDriverException):2520 # If unable to get browser logs, skip the assert and return.2521 return2522 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"2523 errors = []2524 for entry in browser_logs:2525 if entry['level'] == 'SEVERE':2526 if messenger_library not in entry['message']:2527 # Add errors if not caused by SeleniumBase dependencies2528 errors.append(entry)2529 if len(errors) > 0:2530 current_url = self.get_current_url()2531 raise Exception(2532 "JavaScript errors found on %s => %s" % (current_url, errors))2533 if self.demo_mode:2534 if (self.browser == 'chrome' or self.browser == 'edge'):2535 a_t = "ASSERT NO JS ERRORS"2536 if self._language != "English":2537 from seleniumbase.fixtures.words import SD2538 a_t = SD.translate_assert_no_js_errors(self._language)2539 messenger_post = ("%s" % a_t)2540 self.__highlight_with_assert_success(messenger_post, "html")2541 def __activate_html_inspector(self):2542 self.wait_for_ready_state_complete()2543 time.sleep(0.05)2544 js_utils.activate_html_inspector(self.driver)2545 def inspect_html(self):2546 """ Inspects the Page HTML with HTML-Inspector.2547 (https://github.com/philipwalton/html-inspector)2548 (https://cdnjs.com/libraries/html-inspector)2549 Prints the results and also returns them. """2550 self.__activate_html_inspector()2551 script = ("""HTMLInspector.inspect();""")2552 self.execute_script(script)2553 time.sleep(0.1)2554 browser_logs = []2555 try:2556 browser_logs = self.driver.get_log('browser')2557 except (ValueError, WebDriverException):2558 # If unable to get browser logs, skip the assert and return.2559 return("(Unable to Inspect HTML! -> Only works on Chrome!)")2560 messenger_library = "//cdnjs.cloudflare.com/ajax/libs/messenger"2561 url = self.get_current_url()2562 header = '\n* HTML Inspection Results: %s' % url2563 results = [header]2564 row_count = 02565 for entry in browser_logs:2566 message = entry['message']2567 if "0:6053 " in message:2568 message = message.split("0:6053")[1]2569 message = message.replace("\\u003C", "<")2570 if message.startswith(' "') and message.count('"') == 2:2571 message = message.split('"')[1]2572 message = "X - " + message2573 if messenger_library not in message:2574 if message not in results:2575 results.append(message)2576 row_count += 12577 if row_count > 0:2578 results.append('* (See the Console output for details!)')2579 else:2580 results.append('* (No issues detected!)')2581 results = '\n'.join(results)2582 print(results)2583 return(results)2584 def get_google_auth_password(self, totp_key=None):2585 """ Returns a time-based one-time password based on the2586 Google Authenticator password algorithm. Works with Authy.2587 If "totp_key" is not specified, defaults to using the one2588 provided in seleniumbase/config/settings.py2589 Google Auth passwords expire and change at 30-second intervals.2590 If the fetched password expires in the next 1.5 seconds, waits2591 for a new one before returning it (may take up to 1.5 seconds).2592 See https://pyotp.readthedocs.io/en/latest/ for details. """2593 import pyotp2594 if not totp_key:2595 totp_key = settings.TOTP_KEY2596 epoch_interval = time.time() / 30.02597 cycle_lifespan = float(epoch_interval) - int(epoch_interval)2598 if float(cycle_lifespan) > 0.95:2599 # Password expires in the next 1.5 seconds. Wait for a new one.2600 for i in range(30):2601 time.sleep(0.05)2602 epoch_interval = time.time() / 30.02603 cycle_lifespan = float(epoch_interval) - int(epoch_interval)2604 if not float(cycle_lifespan) > 0.95:2605 # The new password cycle has begun2606 break2607 totp = pyotp.TOTP(totp_key)2608 return str(totp.now())2609 def convert_xpath_to_css(self, xpath):2610 return xpath_to_css.convert_xpath_to_css(xpath)2611 def convert_to_css_selector(self, selector, by):2612 """ This method converts a selector to a CSS_SELECTOR.2613 jQuery commands require a CSS_SELECTOR for finding elements.2614 This method should only be used for jQuery/JavaScript actions.2615 Pure JavaScript doesn't support using a:contains("LINK_TEXT"). """2616 if by == By.CSS_SELECTOR:2617 return selector2618 elif by == By.ID:2619 return '#%s' % selector2620 elif by == By.CLASS_NAME:2621 return '.%s' % selector2622 elif by == By.NAME:2623 return '[name="%s"]' % selector2624 elif by == By.TAG_NAME:2625 return selector2626 elif by == By.XPATH:2627 return self.convert_xpath_to_css(selector)2628 elif by == By.LINK_TEXT:2629 return 'a:contains("%s")' % selector2630 elif by == By.PARTIAL_LINK_TEXT:2631 return 'a:contains("%s")' % selector2632 else:2633 raise Exception(2634 "Exception: Could not convert {%s}(by=%s) to CSS_SELECTOR!" % (2635 selector, by))2636 def set_value(self, selector, text, by=By.CSS_SELECTOR, timeout=None):2637 """ This method uses JavaScript to update a text field. """2638 if not timeout:2639 timeout = settings.LARGE_TIMEOUT2640 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2641 timeout = self.__get_new_timeout(timeout)2642 selector, by = self.__recalculate_selector(selector, by)2643 orginal_selector = selector2644 css_selector = self.convert_to_css_selector(selector, by=by)2645 self.__demo_mode_highlight_if_active(orginal_selector, by)2646 if not self.demo_mode:2647 self.scroll_to(orginal_selector, by=by, timeout=timeout)2648 if type(text) is int or type(text) is float:2649 text = str(text)2650 value = re.escape(text)2651 value = self.__escape_quotes_if_needed(value)2652 css_selector = re.escape(css_selector)2653 css_selector = self.__escape_quotes_if_needed(css_selector)2654 script = ("""document.querySelector('%s').value='%s';"""2655 % (css_selector, value))2656 self.execute_script(script)2657 if text.endswith('\n'):2658 element = self.wait_for_element_present(2659 orginal_selector, by=by, timeout=timeout)2660 element.send_keys(Keys.RETURN)2661 if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:2662 self.wait_for_ready_state_complete()2663 self.__demo_mode_pause_if_active()2664 def js_update_text(self, selector, text, by=By.CSS_SELECTOR,2665 timeout=None):2666 """ JavaScript + send_keys are used to update a text field.2667 Performs self.set_value() and triggers event listeners.2668 If text ends in "\n", set_value() presses RETURN after.2669 Works faster than send_keys() alone due to the JS call.2670 """2671 if not timeout:2672 timeout = settings.LARGE_TIMEOUT2673 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2674 timeout = self.__get_new_timeout(timeout)2675 selector, by = self.__recalculate_selector(selector, by)2676 if type(text) is int or type(text) is float:2677 text = str(text)2678 self.set_value(2679 selector, text, by=by, timeout=timeout)2680 if not text.endswith('\n'):2681 try:2682 element = page_actions.wait_for_element_present(2683 self.driver, selector, by, timeout=0.2)2684 element.send_keys(" \b")2685 except Exception:2686 pass2687 def js_type(self, selector, text, by=By.CSS_SELECTOR,2688 timeout=None):2689 """ Same as self.js_update_text()2690 JavaScript + send_keys are used to update a text field.2691 Performs self.set_value() and triggers event listeners.2692 If text ends in "\n", set_value() presses RETURN after.2693 Works faster than send_keys() alone due to the JS call.2694 """2695 if not timeout:2696 timeout = settings.LARGE_TIMEOUT2697 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2698 timeout = self.__get_new_timeout(timeout)2699 selector, by = self.__recalculate_selector(selector, by)2700 if type(text) is int or type(text) is float:2701 text = str(text)2702 self.set_value(2703 selector, text, by=by, timeout=timeout)2704 if not text.endswith('\n'):2705 try:2706 element = page_actions.wait_for_element_present(2707 self.driver, selector, by, timeout=0.2)2708 element.send_keys(" \b")2709 except Exception:2710 pass2711 def jquery_update_text(self, selector, text, by=By.CSS_SELECTOR,2712 timeout=None):2713 """ This method uses jQuery to update a text field.2714 If the text string ends with the newline character,2715 Selenium finishes the call, which simulates pressing2716 {Enter/Return} after the text is entered. """2717 if not timeout:2718 timeout = settings.LARGE_TIMEOUT2719 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2720 timeout = self.__get_new_timeout(timeout)2721 selector, by = self.__recalculate_selector(selector, by)2722 element = self.wait_for_element_visible(2723 selector, by=by, timeout=timeout)2724 self.__demo_mode_highlight_if_active(selector, by)2725 self.scroll_to(selector, by=by)2726 selector = self.convert_to_css_selector(selector, by=by)2727 selector = self.__make_css_match_first_element_only(selector)2728 selector = self.__escape_quotes_if_needed(selector)2729 text = re.escape(text)2730 text = self.__escape_quotes_if_needed(text)2731 update_text_script = """jQuery('%s').val('%s')""" % (2732 selector, text)2733 self.safe_execute_script(update_text_script)2734 if text.endswith('\n'):2735 element.send_keys('\n')2736 self.__demo_mode_pause_if_active()2737 def set_time_limit(self, time_limit):2738 if time_limit:2739 try:2740 sb_config.time_limit = float(time_limit)2741 except Exception:2742 sb_config.time_limit = None2743 else:2744 sb_config.time_limit = None2745 if sb_config.time_limit and sb_config.time_limit > 0:2746 sb_config.time_limit_ms = int(sb_config.time_limit * 1000.0)2747 self.time_limit = sb_config.time_limit2748 else:2749 self.time_limit = None2750 sb_config.time_limit = None2751 sb_config.time_limit_ms = None2752 def skip(self, reason=""):2753 """ Mark the test as Skipped. """2754 self.skipTest(reason)2755 ############2756 # Application "Local Storage" controls2757 def set_local_storage_item(self, key, value):2758 self.execute_script(2759 "window.localStorage.setItem('{}', '{}');".format(key, value))2760 def get_local_storage_item(self, key):2761 return self.execute_script(2762 "return window.localStorage.getItem('{}');".format(key))2763 def remove_local_storage_item(self, key):2764 self.execute_script(2765 "window.localStorage.removeItem('{}');".format(key))2766 def clear_local_storage(self):2767 self.execute_script("window.localStorage.clear();")2768 def get_local_storage_keys(self):2769 return self.execute_script(2770 "var ls = window.localStorage, keys = []; "2771 "for (var i = 0; i < ls.length; ++i) "2772 " keys[i] = ls.key(i); "2773 "return keys;")2774 def get_local_storage_items(self):2775 return self.execute_script(2776 r"var ls = window.localStorage, items = {}; "2777 "for (var i = 0, k; i < ls.length; ++i) "2778 " items[k = ls.key(i)] = ls.getItem(k); "2779 "return items;")2780 # Application "Session Storage" controls2781 def set_session_storage_item(self, key, value):2782 self.execute_script(2783 "window.sessionStorage.setItem('{}', '{}');".format(key, value))2784 def get_session_storage_item(self, key):2785 return self.execute_script(2786 "return window.sessionStorage.getItem('{}');".format(key))2787 def remove_session_storage_item(self, key):2788 self.execute_script(2789 "window.sessionStorage.removeItem('{}');".format(key))2790 def clear_session_storage(self):2791 self.execute_script("window.sessionStorage.clear();")2792 def get_session_storage_keys(self):2793 return self.execute_script(2794 "var ls = window.sessionStorage, keys = []; "2795 "for (var i = 0; i < ls.length; ++i) "2796 " keys[i] = ls.key(i); "2797 "return keys;")2798 def get_session_storage_items(self):2799 return self.execute_script(2800 r"var ls = window.sessionStorage, items = {}; "2801 "for (var i = 0, k; i < ls.length; ++i) "2802 " items[k = ls.key(i)] = ls.getItem(k); "2803 "return items;")2804 ############2805 # Duplicates (Avoids name confusion when migrating from other frameworks.)2806 def open_url(self, url):2807 """ Same as self.open() """2808 self.open(url)2809 def visit(self, url):2810 """ Same as self.open() """2811 self.open(url)2812 def visit_url(self, url):2813 """ Same as self.open() """2814 self.open(url)2815 def goto(self, url):2816 """ Same as self.open() """2817 self.open(url)2818 def go_to(self, url):2819 """ Same as self.open() """2820 self.open(url)2821 def reload(self):2822 """ Same as self.refresh_page() """2823 self.refresh_page()2824 def reload_page(self):2825 """ Same as self.refresh_page() """2826 self.refresh_page()2827 def input(self, selector, text, by=By.CSS_SELECTOR,2828 timeout=None, retry=False):2829 """ Same as self.update_text() """2830 if not timeout:2831 timeout = settings.LARGE_TIMEOUT2832 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2833 timeout = self.__get_new_timeout(timeout)2834 selector, by = self.__recalculate_selector(selector, by)2835 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)2836 def write(self, selector, text, by=By.CSS_SELECTOR,2837 timeout=None, retry=False):2838 """ Same as self.update_text() """2839 if not timeout:2840 timeout = settings.LARGE_TIMEOUT2841 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2842 timeout = self.__get_new_timeout(timeout)2843 selector, by = self.__recalculate_selector(selector, by)2844 self.update_text(selector, text, by=by, timeout=timeout, retry=retry)2845 def send_keys(self, selector, text, by=By.CSS_SELECTOR, timeout=None):2846 """ Same as self.add_text() """2847 if not timeout:2848 timeout = settings.LARGE_TIMEOUT2849 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2850 timeout = self.__get_new_timeout(timeout)2851 selector, by = self.__recalculate_selector(selector, by)2852 self.add_text(selector, text, by=by, timeout=timeout)2853 def click_link(self, link_text, timeout=None):2854 """ Same as self.click_link_text() """2855 if not timeout:2856 timeout = settings.SMALL_TIMEOUT2857 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2858 timeout = self.__get_new_timeout(timeout)2859 self.click_link_text(link_text, timeout=timeout)2860 def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR,2861 timeout=None):2862 """ Same as self.wait_for_element() """2863 if not timeout:2864 timeout = settings.LARGE_TIMEOUT2865 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2866 timeout = self.__get_new_timeout(timeout)2867 selector, by = self.__recalculate_selector(selector, by)2868 return page_actions.wait_for_element_visible(2869 self.driver, selector, by, timeout)2870 def wait_for_element_not_present(self, selector, by=By.CSS_SELECTOR,2871 timeout=None):2872 """ Same as self.wait_for_element_absent()2873 Waits for an element to no longer appear in the HTML of a page.2874 A hidden element still counts as appearing in the page HTML.2875 If an element with "hidden" status is acceptable,2876 use wait_for_element_not_visible() instead. """2877 if not timeout:2878 timeout = settings.LARGE_TIMEOUT2879 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:2880 timeout = self.__get_new_timeout(timeout)2881 selector, by = self.__recalculate_selector(selector, by)2882 return page_actions.wait_for_element_absent(2883 self.driver, selector, by, timeout)2884 def assert_element_not_present(self, selector, by=By.CSS_SELECTOR,2885 timeout=None):2886 """ Same as self.assert_element_absent()2887 Will raise an exception if the element stays present.2888 Returns True if successful. Default timeout = SMALL_TIMEOUT. """2889 if not timeout:2890 timeout = settings.SMALL_TIMEOUT2891 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:2892 timeout = self.__get_new_timeout(timeout)2893 self.wait_for_element_absent(selector, by=by, timeout=timeout)2894 return True2895 def assert_no_broken_links(self, multithreaded=True):2896 """ Same as self.assert_no_404_errors() """2897 self.assert_no_404_errors(multithreaded=multithreaded)2898 def wait(self, seconds):2899 """ Same as self.sleep() - Some JS frameworks use this method name. """2900 self.sleep(seconds)2901 def block_ads(self):2902 """ Same as self.ad_block() """2903 self.ad_block()2904 def _print(self, msg):2905 """ Same as Python's print() """2906 print(msg)2907 def start_tour(self, name=None, interval=0):2908 self.play_tour(name=name, interval=interval)2909 ############2910 def add_css_link(self, css_link):2911 js_utils.add_css_link(self.driver, css_link)2912 def add_js_link(self, js_link):2913 js_utils.add_js_link(self.driver, js_link)2914 def add_css_style(self, css_style):2915 js_utils.add_css_style(self.driver, css_style)2916 def add_js_code_from_link(self, js_link):2917 js_utils.add_js_code_from_link(self.driver, js_link)2918 def add_js_code(self, js_code):2919 js_utils.add_js_code(self.driver, js_code)2920 def add_meta_tag(self, http_equiv=None, content=None):2921 js_utils.add_meta_tag(2922 self.driver, http_equiv=http_equiv, content=content)2923 ############2924 def create_presentation(self, name=None, theme="default"):2925 """ Creates a Reveal-JS presentation that you can add slides to.2926 @Params2927 name - If creating multiple presentations at the same time,2928 use this to specify the name of the current presentation.2929 theme - Set a theme with a unique style for the presentation.2930 Valid themes: "serif" (default), "sky", "white", "black",2931 "simple", "league", "moon", "night",2932 "beige", "blood", and "solarized".2933 """2934 if not name:2935 name = "default"2936 if not theme or theme == "default":2937 theme = "serif"2938 valid_themes = (["serif", "white", "black", "beige", "simple", "sky",2939 "league", "moon", "night", "blood", "solarized"])2940 theme = theme.lower()2941 if theme not in valid_themes:2942 raise Exception(2943 "Theme {%s} not found! Valid themes: %s"2944 "" % (theme, valid_themes))2945 reveal_theme_css = None2946 if theme == "serif":2947 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS2948 elif theme == "sky":2949 reveal_theme_css = constants.Reveal.SKY_MIN_CSS2950 elif theme == "white":2951 reveal_theme_css = constants.Reveal.WHITE_MIN_CSS2952 elif theme == "black":2953 reveal_theme_css = constants.Reveal.BLACK_MIN_CSS2954 elif theme == "simple":2955 reveal_theme_css = constants.Reveal.SIMPLE_MIN_CSS2956 elif theme == "league":2957 reveal_theme_css = constants.Reveal.LEAGUE_MIN_CSS2958 elif theme == "moon":2959 reveal_theme_css = constants.Reveal.MOON_MIN_CSS2960 elif theme == "night":2961 reveal_theme_css = constants.Reveal.NIGHT_MIN_CSS2962 elif theme == "beige":2963 reveal_theme_css = constants.Reveal.BEIGE_MIN_CSS2964 elif theme == "blood":2965 reveal_theme_css = constants.Reveal.BLOOD_MIN_CSS2966 elif theme == "solarized":2967 reveal_theme_css = constants.Reveal.SOLARIZED_MIN_CSS2968 else:2969 # Use the default if unable to determine the theme2970 reveal_theme_css = constants.Reveal.SERIF_MIN_CSS2971 new_presentation = (2972 '<html>\n'2973 '<head>\n'2974 '<link rel="stylesheet" href="%s">\n'2975 '<link rel="stylesheet" href="%s">\n'2976 '<style>\n'2977 'pre{background-color:#fbe8d4;border-radius:8px;}\n'2978 'div[flex_div]{height:75vh;margin:0;align-items:center;'2979 'justify-content:center;}\n'2980 'img[rounded]{border-radius:16px;max-width:82%%;}\n'2981 '</style>\n'2982 '</head>\n\n'2983 '<body>\n'2984 '<div class="reveal">\n'2985 '<div class="slides">\n'2986 '' % (constants.Reveal.MIN_CSS, reveal_theme_css))2987 self._presentation_slides[name] = []2988 self._presentation_slides[name].append(new_presentation)2989 def add_slide(self, content=None, image=None, code=None, iframe=None,2990 content2=None, notes=None, name=None):2991 """ Allows the user to add slides to a presentation.2992 @Params2993 content - The HTML content to display on the presentation slide.2994 image - Attach an image (from a URL link) to the slide.2995 code - Attach code of any programming language to the slide.2996 Language-detection will be used to add syntax formatting.2997 iframe - Attach an iFrame (from a URL link) to the slide.2998 content2 - HTML content to display after adding an image or code.2999 notes - Additional notes to include with the slide.3000 ONLY SEEN if show_notes is set for the presentation.3001 name - If creating multiple presentations at the same time,3002 use this to select the presentation to add slides to.3003 """3004 if not name:3005 name = "default"3006 if name not in self._presentation_slides:3007 # Create a presentation if it doesn't already exist3008 self.create_presentation(name=name)3009 if not content:3010 content = ""3011 if not content2:3012 content2 = ""3013 if not notes:3014 notes = ""3015 add_line = ""3016 if content.startswith("<"):3017 add_line = "\n"3018 html = ('\n<section data-transition="none">%s%s' % (add_line, content))3019 if image:3020 html += '\n<div flex_div><img rounded src="%s"></div>' % image3021 if code:3022 html += '\n<div></div>'3023 html += '\n<pre class="prettyprint">\n%s</pre>' % code3024 if iframe:3025 html += ('\n<div></div>'3026 '\n<iframe src="%s" style="width:92%%;height:550;'3027 'title="iframe content"></iframe>' % iframe)3028 add_line = ""3029 if content2.startswith("<"):3030 add_line = "\n"3031 if content2:3032 html += '%s%s' % (add_line, content2)3033 html += '\n<aside class="notes">%s</aside>' % notes3034 html += '\n</section>\n'3035 self._presentation_slides[name].append(html)3036 def save_presentation(3037 self, name=None, filename=None, show_notes=True, interval=0):3038 """ Saves a Reveal-JS Presentation to a file for later use.3039 @Params3040 name - If creating multiple presentations at the same time,3041 use this to select the one you wish to add slides to.3042 filename - The name of the HTML file that you wish to3043 save the presentation to. (filename must end in ".html")3044 show_notes - When set to True, the Notes feature becomes enabled,3045 which allows presenters to see notes next to slides.3046 interval - The delay time between autoplaying slides. (in seconds)3047 If set to 0 (default), autoplay is disabled.3048 """3049 if not name:3050 name = "default"3051 if not filename:3052 filename = "my_presentation.html"3053 if name not in self._presentation_slides:3054 raise Exception("Presentation {%s} does not exist!" % name)3055 if not filename.endswith('.html'):3056 raise Exception('Presentation file must end in ".html"!')3057 if not interval:3058 interval = 03059 if not type(interval) is int and not type(interval) is float:3060 raise Exception('Expecting a numeric value for "interval"!')3061 if interval < 0:3062 raise Exception('The "interval" cannot be a negative number!')3063 interval_ms = float(interval) * 1000.03064 show_notes_str = "false"3065 if show_notes:3066 show_notes_str = "true"3067 the_html = ""3068 for slide in self._presentation_slides[name]:3069 the_html += slide3070 the_html += (3071 '\n</div>\n'3072 '</div>\n'3073 '<script src="%s"></script>\n'3074 '<script src="%s"></script>\n'3075 '<script>Reveal.initialize('3076 '{showNotes: %s, slideNumber: true, '3077 'autoSlide: %s,});'3078 '</script>\n'3079 '</body>\n'3080 '</html>\n'3081 '' % (constants.Reveal.MIN_JS,3082 constants.PrettifyJS.RUN_PRETTIFY_JS,3083 show_notes_str,3084 interval_ms))3085 saved_presentations_folder = constants.Presentations.SAVED_FOLDER3086 if saved_presentations_folder.endswith("/"):3087 saved_presentations_folder = saved_presentations_folder[:-1]3088 if not os.path.exists(saved_presentations_folder):3089 try:3090 os.makedirs(saved_presentations_folder)3091 except Exception:3092 pass3093 file_path = saved_presentations_folder + "/" + filename3094 out_file = codecs.open(file_path, "w+")3095 out_file.writelines(the_html)3096 out_file.close()3097 print('\n>>> [%s] was saved!\n' % file_path)3098 return file_path3099 def begin_presentation(3100 self, name=None, filename=None, show_notes=True, interval=0):3101 """ Begin a Reveal-JS Presentation in the web browser.3102 @Params3103 name - If creating multiple presentations at the same time,3104 use this to select the one you wish to add slides to.3105 filename - The name of the HTML file that you wish to3106 save the presentation to. (filename must end in ".html")3107 show_notes - When set to True, the Notes feature becomes enabled,3108 which allows presenters to see notes next to slides.3109 interval - The delay time between autoplaying slides. (in seconds)3110 If set to 0 (default), autoplay is disabled.3111 """3112 if self.headless:3113 return # Presentations should not run in headless mode.3114 if not name:3115 name = "default"3116 if not filename:3117 filename = "my_presentation.html"3118 if name not in self._presentation_slides:3119 raise Exception("Presentation {%s} does not exist!" % name)3120 if not filename.endswith('.html'):3121 raise Exception('Presentation file must end in ".html"!')3122 if not interval:3123 interval = 03124 if not type(interval) is int and not type(interval) is float:3125 raise Exception('Expecting a numeric value for "interval"!')3126 if interval < 0:3127 raise Exception('The "interval" cannot be a negative number!')3128 end_slide = (3129 '\n<section data-transition="none">\n'3130 '<p class="End_Presentation_Now"> </p>\n</section>\n')3131 self._presentation_slides[name].append(end_slide)3132 file_path = self.save_presentation(3133 name=name, filename=filename,3134 show_notes=show_notes, interval=interval)3135 self._presentation_slides[name].pop()3136 self.open_html_file(file_path)3137 presentation_folder = constants.Presentations.SAVED_FOLDER3138 try:3139 while (len(self.driver.window_handles) > 0 and (3140 presentation_folder in self.get_current_url())):3141 time.sleep(0.05)3142 if self.is_element_visible(3143 "section.present p.End_Presentation_Now"):3144 break3145 time.sleep(0.05)3146 except Exception:3147 pass3148 ############3149 def create_tour(self, name=None, theme=None):3150 """ Creates a tour for a website. By default, the Shepherd JavaScript3151 Library is used with the Shepherd "Light" / "Arrows" theme.3152 @Params3153 name - If creating multiple tours at the same time,3154 use this to select the tour you wish to add steps to.3155 theme - Sets the default theme for the tour.3156 Choose from "light"/"arrows", "dark", "default", "square",3157 and "square-dark". ("arrows" is used if None is selected.)3158 Alternatively, you may use a different JavaScript Library3159 as the theme. Those include "IntroJS", "DriverJS",3160 "Hopscotch", and "Bootstrap".3161 """3162 if not name:3163 name = "default"3164 if theme:3165 if theme.lower() == "bootstrap":3166 self.create_bootstrap_tour(name)3167 return3168 elif theme.lower() == "hopscotch":3169 self.create_hopscotch_tour(name)3170 return3171 elif theme.lower() == "intro":3172 self.create_introjs_tour(name)3173 return3174 elif theme.lower() == "introjs":3175 self.create_introjs_tour(name)3176 return3177 elif theme.lower() == "driver":3178 self.create_driverjs_tour(name)3179 return3180 elif theme.lower() == "driverjs":3181 self.create_driverjs_tour(name)3182 return3183 elif theme.lower() == "shepherd":3184 self.create_shepherd_tour(name, theme="light")3185 return3186 else:3187 self.create_shepherd_tour(name, theme)3188 else:3189 self.create_shepherd_tour(name, theme="light")3190 def create_shepherd_tour(self, name=None, theme=None):3191 """ Creates a Shepherd JS website tour.3192 @Params3193 name - If creating multiple tours at the same time,3194 use this to select the tour you wish to add steps to.3195 theme - Sets the default theme for the tour.3196 Choose from "light"/"arrows", "dark", "default", "square",3197 and "square-dark". ("light" is used if None is selected.)3198 """3199 shepherd_theme = "shepherd-theme-arrows"3200 if theme:3201 if theme.lower() == "default":3202 shepherd_theme = "shepherd-theme-default"3203 elif theme.lower() == "dark":3204 shepherd_theme = "shepherd-theme-dark"3205 elif theme.lower() == "light":3206 shepherd_theme = "shepherd-theme-arrows"3207 elif theme.lower() == "arrows":3208 shepherd_theme = "shepherd-theme-arrows"3209 elif theme.lower() == "square":3210 shepherd_theme = "shepherd-theme-square"3211 elif theme.lower() == "square-dark":3212 shepherd_theme = "shepherd-theme-square-dark"3213 if not name:3214 name = "default"3215 new_tour = (3216 """3217 // Shepherd Tour3218 var tour = new Shepherd.Tour({3219 defaults: {3220 classes: '%s',3221 scrollTo: true3222 }3223 });3224 var allButtons = {3225 skip: {3226 text: "Skip",3227 action: tour.cancel,3228 classes: 'shepherd-button-secondary tour-button-left'3229 },3230 back: {3231 text: "Back",3232 action: tour.back,3233 classes: 'shepherd-button-secondary'3234 },3235 next: {3236 text: "Next",3237 action: tour.next,3238 classes: 'shepherd-button-primary tour-button-right'3239 },3240 };3241 var firstStepButtons = [allButtons.skip, allButtons.next];3242 var midTourButtons = [allButtons.back, allButtons.next];3243 """ % shepherd_theme)3244 self._tour_steps[name] = []3245 self._tour_steps[name].append(new_tour)3246 def create_bootstrap_tour(self, name=None):3247 """ Creates a Bootstrap tour for a website.3248 @Params3249 name - If creating multiple tours at the same time,3250 use this to select the tour you wish to add steps to.3251 """3252 if not name:3253 name = "default"3254 new_tour = (3255 """3256 // Bootstrap Tour3257 var tour = new Tour({3258 });3259 tour.addSteps([3260 """)3261 self._tour_steps[name] = []3262 self._tour_steps[name].append(new_tour)3263 def create_driverjs_tour(self, name=None):3264 """ Creates a DriverJS tour for a website.3265 @Params3266 name - If creating multiple tours at the same time,3267 use this to select the tour you wish to add steps to.3268 """3269 if not name:3270 name = "default"3271 new_tour = (3272 """3273 // DriverJS Tour3274 var tour = new Driver({3275 opacity: 0.24, // Background opacity (0: no popover / overlay)3276 padding: 6, // Distance of element from around the edges3277 allowClose: false, // Whether clicking on overlay should close3278 overlayClickNext: false, // Move to next step on overlay click3279 doneBtnText: 'Done', // Text that appears on the Done button3280 closeBtnText: 'Close', // Text appearing on the Close button3281 nextBtnText: 'Next', // Text that appears on the Next button3282 prevBtnText: 'Previous', // Text appearing on Previous button3283 showButtons: true, // This shows control buttons in the footer3284 keyboardControl: true, // (escape to close, arrow keys to move)3285 animate: true, // Animate while changing highlighted element3286 });3287 tour.defineSteps([3288 """)3289 self._tour_steps[name] = []3290 self._tour_steps[name].append(new_tour)3291 def create_hopscotch_tour(self, name=None):3292 """ Creates a Hopscotch tour for a website.3293 @Params3294 name - If creating multiple tours at the same time,3295 use this to select the tour you wish to add steps to.3296 """3297 if not name:3298 name = "default"3299 new_tour = (3300 """3301 // Hopscotch Tour3302 var tour = {3303 id: "hopscotch_tour",3304 steps: [3305 """)3306 self._tour_steps[name] = []3307 self._tour_steps[name].append(new_tour)3308 def create_introjs_tour(self, name=None):3309 """ Creates an IntroJS tour for a website.3310 @Params3311 name - If creating multiple tours at the same time,3312 use this to select the tour you wish to add steps to.3313 """3314 if not name:3315 name = "default"3316 new_tour = (3317 """3318 // IntroJS Tour3319 function startIntro(){3320 var intro = introJs();3321 intro.setOptions({3322 steps: [3323 """)3324 self._tour_steps[name] = []3325 self._tour_steps[name].append(new_tour)3326 def add_tour_step(self, message, selector=None, name=None,3327 title=None, theme=None, alignment=None, duration=None):3328 """ Allows the user to add tour steps for a website.3329 @Params3330 message - The message to display.3331 selector - The CSS Selector of the Element to attach to.3332 name - If creating multiple tours at the same time,3333 use this to select the tour you wish to add steps to.3334 title - Additional header text that appears above the message.3335 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.3336 Choose from "light"/"arrows", "dark", "default", "square",3337 and "square-dark". ("arrows" is used if None is selected.)3338 alignment - Choose from "top", "bottom", "left", and "right".3339 ("top" is default, except for Hopscotch and DriverJS).3340 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,3341 before automatically advancing to the next tour step.3342 """3343 if not selector:3344 selector = "html"3345 if page_utils.is_name_selector(selector):3346 name = page_utils.get_name_from_selector(selector)3347 selector = '[name="%s"]' % name3348 if page_utils.is_xpath_selector(selector):3349 selector = self.convert_to_css_selector(selector, By.XPATH)3350 selector = self.__escape_quotes_if_needed(selector)3351 if not name:3352 name = "default"3353 if name not in self._tour_steps:3354 # By default, will create an IntroJS tour if no tours exist3355 self.create_tour(name=name, theme="introjs")3356 if not title:3357 title = ""3358 title = self.__escape_quotes_if_needed(title)3359 if message:3360 message = self.__escape_quotes_if_needed(message)3361 else:3362 message = ""3363 if not alignment or (3364 alignment not in ["top", "bottom", "left", "right"]):3365 t_name = self._tour_steps[name][0]3366 if "Hopscotch" not in t_name and "DriverJS" not in t_name:3367 alignment = "top"3368 else:3369 alignment = "bottom"3370 if "Bootstrap" in self._tour_steps[name][0]:3371 self.__add_bootstrap_tour_step(3372 message, selector=selector, name=name, title=title,3373 alignment=alignment, duration=duration)3374 elif "DriverJS" in self._tour_steps[name][0]:3375 self.__add_driverjs_tour_step(3376 message, selector=selector, name=name, title=title,3377 alignment=alignment)3378 elif "Hopscotch" in self._tour_steps[name][0]:3379 self.__add_hopscotch_tour_step(3380 message, selector=selector, name=name, title=title,3381 alignment=alignment)3382 elif "IntroJS" in self._tour_steps[name][0]:3383 self.__add_introjs_tour_step(3384 message, selector=selector, name=name, title=title,3385 alignment=alignment)3386 else:3387 self.__add_shepherd_tour_step(3388 message, selector=selector, name=name, title=title,3389 theme=theme, alignment=alignment)3390 def __add_shepherd_tour_step(self, message, selector=None, name=None,3391 title=None, theme=None, alignment=None):3392 """ Allows the user to add tour steps for a website.3393 @Params3394 message - The message to display.3395 selector - The CSS Selector of the Element to attach to.3396 name - If creating multiple tours at the same time,3397 use this to select the tour you wish to add steps to.3398 title - Additional header text that appears above the message.3399 theme - (NON-Bootstrap Tours ONLY) The styling of the tour step.3400 Choose from "light"/"arrows", "dark", "default", "square",3401 and "square-dark". ("arrows" is used if None is selected.)3402 alignment - Choose from "top", "bottom", "left", and "right".3403 ("top" is the default alignment).3404 """3405 if theme == "default":3406 shepherd_theme = "shepherd-theme-default"3407 elif theme == "dark":3408 shepherd_theme = "shepherd-theme-dark"3409 elif theme == "light":3410 shepherd_theme = "shepherd-theme-arrows"3411 elif theme == "arrows":3412 shepherd_theme = "shepherd-theme-arrows"3413 elif theme == "square":3414 shepherd_theme = "shepherd-theme-square"3415 elif theme == "square-dark":3416 shepherd_theme = "shepherd-theme-square-dark"3417 else:3418 shepherd_base_theme = re.search(3419 r"[\S\s]+classes: '([\S\s]+)',[\S\s]+",3420 self._tour_steps[name][0]).group(1)3421 shepherd_theme = shepherd_base_theme3422 shepherd_classes = shepherd_theme3423 if selector == "html":3424 shepherd_classes += " shepherd-orphan"3425 buttons = "firstStepButtons"3426 if len(self._tour_steps[name]) > 1:3427 buttons = "midTourButtons"3428 step = ("""3429 tour.addStep('%s', {3430 title: '%s',3431 classes: '%s',3432 text: '%s',3433 attachTo: {element: '%s', on: '%s'},3434 buttons: %s,3435 advanceOn: '.docs-link click'3436 });""" % (3437 name, title, shepherd_classes, message, selector, alignment,3438 buttons))3439 self._tour_steps[name].append(step)3440 def __add_bootstrap_tour_step(self, message, selector=None, name=None,3441 title=None, alignment=None, duration=None):3442 """ Allows the user to add tour steps for a website.3443 @Params3444 message - The message to display.3445 selector - The CSS Selector of the Element to attach to.3446 name - If creating multiple tours at the same time,3447 use this to select the tour you wish to add steps to.3448 title - Additional header text that appears above the message.3449 alignment - Choose from "top", "bottom", "left", and "right".3450 ("top" is the default alignment).3451 duration - (Bootstrap Tours ONLY) The amount of time, in seconds,3452 before automatically advancing to the next tour step.3453 """3454 if selector != "html":3455 selector = self.__make_css_match_first_element_only(selector)3456 element_row = "element: '%s'," % selector3457 else:3458 element_row = ""3459 if not duration:3460 duration = "0"3461 else:3462 duration = str(float(duration) * 1000.0)3463 step = ("""{3464 %s3465 title: '%s',3466 content: '%s',3467 orphan: true,3468 placement: 'auto %s',3469 smartPlacement: true,3470 duration: %s,3471 },""" % (element_row, title, message, alignment, duration))3472 self._tour_steps[name].append(step)3473 def __add_driverjs_tour_step(self, message, selector=None, name=None,3474 title=None, alignment=None):3475 """ Allows the user to add tour steps for a website.3476 @Params3477 message - The message to display.3478 selector - The CSS Selector of the Element to attach to.3479 name - If creating multiple tours at the same time,3480 use this to select the tour you wish to add steps to.3481 title - Additional header text that appears above the message.3482 alignment - Choose from "top", "bottom", "left", and "right".3483 ("top" is the default alignment).3484 """3485 message = (3486 '<font size=\"3\" color=\"#33477B\"><b>' + message + '</b></font>')3487 title_row = ""3488 if not title:3489 title_row = "title: '%s'," % message3490 message = ""3491 else:3492 title_row = "title: '%s'," % title3493 align_row = "position: '%s'," % alignment3494 ani_row = "animate: true,"3495 if not selector or selector == "html" or selector == "body":3496 selector = "body"3497 ani_row = "animate: false,"3498 align_row = "position: '%s'," % "mid-center"3499 element_row = "element: '%s'," % selector3500 desc_row = "description: '%s'," % message3501 step = ("""{3502 %s3503 %s3504 popover: {3505 className: 'popover-class',3506 %s3507 %s3508 %s3509 }3510 },""" % (element_row, ani_row, title_row, desc_row, align_row))3511 self._tour_steps[name].append(step)3512 def __add_hopscotch_tour_step(self, message, selector=None, name=None,3513 title=None, alignment=None):3514 """ Allows the user to add tour steps for a website.3515 @Params3516 message - The message to display.3517 selector - The CSS Selector of the Element to attach to.3518 name - If creating multiple tours at the same time,3519 use this to select the tour you wish to add steps to.3520 title - Additional header text that appears above the message.3521 alignment - Choose from "top", "bottom", "left", and "right".3522 ("bottom" is the default alignment).3523 """3524 arrow_offset_row = None3525 if not selector or selector == "html":3526 selector = "head"3527 alignment = "bottom"3528 arrow_offset_row = "arrowOffset: '200',"3529 else:3530 arrow_offset_row = ""3531 step = ("""{3532 target: '%s',3533 title: '%s',3534 content: '%s',3535 %s3536 showPrevButton: 'true',3537 scrollDuration: '550',3538 placement: '%s'},3539 """ % (selector, title, message, arrow_offset_row, alignment))3540 self._tour_steps[name].append(step)3541 def __add_introjs_tour_step(self, message, selector=None, name=None,3542 title=None, alignment=None):3543 """ Allows the user to add tour steps for a website.3544 @Params3545 message - The message to display.3546 selector - The CSS Selector of the Element to attach to.3547 name - If creating multiple tours at the same time,3548 use this to select the tour you wish to add steps to.3549 title - Additional header text that appears above the message.3550 alignment - Choose from "top", "bottom", "left", and "right".3551 ("top" is the default alignment).3552 """3553 if selector != "html":3554 element_row = "element: '%s'," % selector3555 else:3556 element_row = ""3557 if title:3558 message = "<center><b>" + title + "</b></center><hr>" + message3559 message = '<font size=\"3\" color=\"#33477B\">' + message + '</font>'3560 step = ("""{%s3561 intro: '%s',3562 position: '%s'},3563 """ % (element_row, message, alignment))3564 self._tour_steps[name].append(step)3565 def play_tour(self, name=None, interval=0):3566 """ Plays a tour on the current website.3567 @Params3568 name - If creating multiple tours at the same time,3569 use this to select the tour you wish to add steps to.3570 interval - The delay time between autoplaying tour steps. (Seconds)3571 If set to 0 (default), the tour is fully manual control.3572 """3573 if self.headless:3574 return # Tours should not run in headless mode.3575 if not name:3576 name = "default"3577 if name not in self._tour_steps:3578 raise Exception("Tour {%s} does not exist!" % name)3579 if "Bootstrap" in self._tour_steps[name][0]:3580 tour_helper.play_bootstrap_tour(3581 self.driver, self._tour_steps, self.browser,3582 self.message_duration, name=name, interval=interval)3583 elif "DriverJS" in self._tour_steps[name][0]:3584 tour_helper.play_driverjs_tour(3585 self.driver, self._tour_steps, self.browser,3586 self.message_duration, name=name, interval=interval)3587 elif "Hopscotch" in self._tour_steps[name][0]:3588 tour_helper.play_hopscotch_tour(3589 self.driver, self._tour_steps, self.browser,3590 self.message_duration, name=name, interval=interval)3591 elif "IntroJS" in self._tour_steps[name][0]:3592 tour_helper.play_introjs_tour(3593 self.driver, self._tour_steps, self.browser,3594 self.message_duration, name=name, interval=interval)3595 else:3596 # "Shepherd"3597 tour_helper.play_shepherd_tour(3598 self.driver, self._tour_steps,3599 self.message_duration, name=name, interval=interval)3600 def export_tour(self, name=None, filename="my_tour.js", url=None):3601 """ Exports a tour as a JS file.3602 You can call self.export_tour() anywhere where you would3603 normally use self.play_tour() to play a website tour.3604 It will include necessary resources as well, such as jQuery.3605 You'll be able to copy the tour directly into the Console of3606 any web browser to play the tour outside of SeleniumBase runs.3607 @Params3608 name - If creating multiple tours at the same time,3609 use this to select the tour you wish to add steps to.3610 filename - The name of the JavaScript file that you wish to3611 save the tour to.3612 url - The URL where the tour starts. If not specified, the URL3613 of the current page will be used. """3614 if not url:3615 url = self.get_current_url()3616 tour_helper.export_tour(3617 self._tour_steps, name=name, filename=filename, url=url)3618 def activate_jquery_confirm(self):3619 """ See https://craftpip.github.io/jquery-confirm/ for usage. """3620 js_utils.activate_jquery_confirm(self.driver)3621 self.wait_for_ready_state_complete()3622 def activate_messenger(self):3623 js_utils.activate_messenger(self.driver)3624 self.wait_for_ready_state_complete()3625 def set_messenger_theme(self, theme="default", location="default",3626 max_messages="default"):3627 """ Sets a theme for posting messages.3628 Themes: ["flat", "future", "block", "air", "ice"]3629 Locations: ["top_left", "top_center", "top_right",3630 "bottom_left", "bottom_center", "bottom_right"]3631 max_messages is the limit of concurrent messages to display. """3632 if not theme:3633 theme = "default" # "future"3634 if not location:3635 location = "default" # "bottom_right"3636 if not max_messages:3637 max_messages = "default" # "8"3638 else:3639 max_messages = str(max_messages) # Value must be in string format3640 js_utils.set_messenger_theme(3641 self.driver, theme=theme,3642 location=location, max_messages=max_messages)3643 def post_message(self, message, duration=None, pause=True, style="info"):3644 """ Post a message on the screen with Messenger.3645 Arguments:3646 message: The message to display.3647 duration: The time until the message vanishes. (Default: 2.55s)3648 pause: If True, the program waits until the message completes.3649 style: "info", "success", or "error".3650 You can also post messages by using =>3651 self.execute_script('Messenger().post("My Message")')3652 """3653 if not duration:3654 if not self.message_duration:3655 duration = settings.DEFAULT_MESSAGE_DURATION3656 else:3657 duration = self.message_duration3658 js_utils.post_message(3659 self.driver, message, duration, style=style)3660 if pause:3661 duration = float(duration) + 0.153662 time.sleep(float(duration))3663 def post_success_message(self, message, duration=None, pause=True):3664 """ Post a success message on the screen with Messenger.3665 Arguments:3666 message: The success message to display.3667 duration: The time until the message vanishes. (Default: 2.55s)3668 pause: If True, the program waits until the message completes.3669 """3670 if not duration:3671 if not self.message_duration:3672 duration = settings.DEFAULT_MESSAGE_DURATION3673 else:3674 duration = self.message_duration3675 js_utils.post_message(3676 self.driver, message, duration, style="success")3677 if pause:3678 duration = float(duration) + 0.153679 time.sleep(float(duration))3680 def post_error_message(self, message, duration=None, pause=True):3681 """ Post an error message on the screen with Messenger.3682 Arguments:3683 message: The error message to display.3684 duration: The time until the message vanishes. (Default: 2.55s)3685 pause: If True, the program waits until the message completes.3686 """3687 if not duration:3688 if not self.message_duration:3689 duration = settings.DEFAULT_MESSAGE_DURATION3690 else:3691 duration = self.message_duration3692 js_utils.post_message(3693 self.driver, message, duration, style="error")3694 if pause:3695 duration = float(duration) + 0.153696 time.sleep(float(duration))3697 ############3698 def generate_referral(self, start_page, destination_page):3699 """ This method opens the start_page, creates a referral link there,3700 and clicks on that link, which goes to the destination_page.3701 (This generates real traffic for testing analytics software.) """3702 if not page_utils.is_valid_url(destination_page):3703 raise Exception(3704 "Exception: destination_page {%s} is not a valid URL!"3705 % destination_page)3706 if start_page:3707 if not page_utils.is_valid_url(start_page):3708 raise Exception(3709 "Exception: start_page {%s} is not a valid URL! "3710 "(Use an empty string or None to start from current page.)"3711 % start_page)3712 self.open(start_page)3713 time.sleep(0.08)3714 self.wait_for_ready_state_complete()3715 referral_link = ('''<body>'''3716 '''<a class='analytics referral test' href='%s' '''3717 '''style='font-family: Arial,sans-serif; '''3718 '''font-size: 30px; color: #18a2cd'>'''3719 '''Magic Link Button</a></body>''' % destination_page)3720 self.execute_script(3721 '''document.body.outerHTML = \"%s\"''' % referral_link)3722 self.click(3723 "a.analytics.referral.test", timeout=2) # Clicks generated button3724 time.sleep(0.15)3725 try:3726 self.click("html")3727 time.sleep(0.08)3728 except Exception:3729 pass3730 def generate_traffic(self, start_page, destination_page, loops=1):3731 """ Similar to generate_referral(), but can do multiple loops. """3732 for loop in range(loops):3733 self.generate_referral(start_page, destination_page)3734 time.sleep(0.05)3735 def generate_referral_chain(self, pages):3736 """ Use this method to chain the action of creating button links on3737 one website page that will take you to the next page.3738 (When you want to create a referral to a website for traffic3739 generation without increasing the bounce rate, you'll want to visit3740 at least one additional page on that site with a button click.) """3741 if not type(pages) is tuple and not type(pages) is list:3742 raise Exception(3743 "Exception: Expecting a list of website pages for chaining!")3744 if len(pages) < 2:3745 raise Exception(3746 "Exception: At least two website pages required for chaining!")3747 for page in pages:3748 # Find out if any of the web pages are invalid before continuing3749 if not page_utils.is_valid_url(page):3750 raise Exception(3751 "Exception: Website page {%s} is not a valid URL!" % page)3752 for page in pages:3753 self.generate_referral(None, page)3754 def generate_traffic_chain(self, pages, loops=1):3755 """ Similar to generate_referral_chain(), but for multiple loops. """3756 for loop in range(loops):3757 self.generate_referral_chain(pages)3758 time.sleep(0.05)3759 ############3760 def wait_for_element_present(self, selector, by=By.CSS_SELECTOR,3761 timeout=None):3762 """ Waits for an element to appear in the HTML of a page.3763 The element does not need be visible (it may be hidden). """3764 if not timeout:3765 timeout = settings.LARGE_TIMEOUT3766 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3767 timeout = self.__get_new_timeout(timeout)3768 selector, by = self.__recalculate_selector(selector, by)3769 return page_actions.wait_for_element_present(3770 self.driver, selector, by, timeout)3771 def wait_for_element(self, selector, by=By.CSS_SELECTOR, timeout=None):3772 """ Waits for an element to appear in the HTML of a page.3773 The element must be visible (it cannot be hidden). """3774 if not timeout:3775 timeout = settings.LARGE_TIMEOUT3776 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3777 timeout = self.__get_new_timeout(timeout)3778 selector, by = self.__recalculate_selector(selector, by)3779 return page_actions.wait_for_element_visible(3780 self.driver, selector, by, timeout)3781 def get_element(self, selector, by=By.CSS_SELECTOR, timeout=None):3782 """ Same as wait_for_element_present() - returns the element.3783 The element does not need be visible (it may be hidden). """3784 if not timeout:3785 timeout = settings.SMALL_TIMEOUT3786 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3787 timeout = self.__get_new_timeout(timeout)3788 selector, by = self.__recalculate_selector(selector, by)3789 return self.wait_for_element_present(selector, by=by, timeout=timeout)3790 def assert_element_present(self, selector, by=By.CSS_SELECTOR,3791 timeout=None):3792 """ Similar to wait_for_element_present(), but returns nothing.3793 Waits for an element to appear in the HTML of a page.3794 The element does not need be visible (it may be hidden).3795 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3796 if not timeout:3797 timeout = settings.SMALL_TIMEOUT3798 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3799 timeout = self.__get_new_timeout(timeout)3800 self.wait_for_element_present(selector, by=by, timeout=timeout)3801 return True3802 def find_element(self, selector, by=By.CSS_SELECTOR, timeout=None):3803 """ Same as wait_for_element_visible() - returns the element """3804 if not timeout:3805 timeout = settings.LARGE_TIMEOUT3806 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3807 timeout = self.__get_new_timeout(timeout)3808 return self.wait_for_element_visible(selector, by=by, timeout=timeout)3809 def assert_element(self, selector, by=By.CSS_SELECTOR, timeout=None):3810 """ Similar to wait_for_element_visible(), but returns nothing.3811 As above, will raise an exception if nothing can be found.3812 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3813 if not timeout:3814 timeout = settings.SMALL_TIMEOUT3815 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3816 timeout = self.__get_new_timeout(timeout)3817 self.wait_for_element_visible(selector, by=by, timeout=timeout)3818 if self.demo_mode:3819 selector, by = self.__recalculate_selector(selector, by)3820 a_t = "ASSERT"3821 if self._language != "English":3822 from seleniumbase.fixtures.words import SD3823 a_t = SD.translate_assert(self._language)3824 messenger_post = "%s %s: %s" % (a_t, by.upper(), selector)3825 self.__highlight_with_assert_success(messenger_post, selector, by)3826 return True3827 def assert_element_visible(self, selector, by=By.CSS_SELECTOR,3828 timeout=None):3829 """ Same as self.assert_element()3830 As above, will raise an exception if nothing can be found. """3831 if not timeout:3832 timeout = settings.SMALL_TIMEOUT3833 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3834 timeout = self.__get_new_timeout(timeout)3835 self.assert_element(selector, by=by, timeout=timeout)3836 return True3837 ############3838 def wait_for_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,3839 timeout=None):3840 if not timeout:3841 timeout = settings.LARGE_TIMEOUT3842 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3843 timeout = self.__get_new_timeout(timeout)3844 selector, by = self.__recalculate_selector(selector, by)3845 return page_actions.wait_for_text_visible(3846 self.driver, text, selector, by, timeout)3847 def wait_for_exact_text_visible(self, text, selector="html",3848 by=By.CSS_SELECTOR,3849 timeout=None):3850 if not timeout:3851 timeout = settings.LARGE_TIMEOUT3852 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3853 timeout = self.__get_new_timeout(timeout)3854 selector, by = self.__recalculate_selector(selector, by)3855 return page_actions.wait_for_exact_text_visible(3856 self.driver, text, selector, by, timeout)3857 def wait_for_text(self, text, selector="html", by=By.CSS_SELECTOR,3858 timeout=None):3859 """ The shorter version of wait_for_text_visible() """3860 if not timeout:3861 timeout = settings.LARGE_TIMEOUT3862 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3863 timeout = self.__get_new_timeout(timeout)3864 return self.wait_for_text_visible(3865 text, selector, by=by, timeout=timeout)3866 def find_text(self, text, selector="html", by=By.CSS_SELECTOR,3867 timeout=None):3868 """ Same as wait_for_text_visible() - returns the element """3869 if not timeout:3870 timeout = settings.LARGE_TIMEOUT3871 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3872 timeout = self.__get_new_timeout(timeout)3873 return self.wait_for_text_visible(3874 text, selector, by=by, timeout=timeout)3875 def assert_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,3876 timeout=None):3877 """ Same as assert_text() """3878 if not timeout:3879 timeout = settings.SMALL_TIMEOUT3880 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3881 timeout = self.__get_new_timeout(timeout)3882 return self.assert_text(text, selector, by=by, timeout=timeout)3883 def assert_text(self, text, selector="html", by=By.CSS_SELECTOR,3884 timeout=None):3885 """ Similar to wait_for_text_visible()3886 Raises an exception if the element or the text is not found.3887 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3888 if not timeout:3889 timeout = settings.SMALL_TIMEOUT3890 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3891 timeout = self.__get_new_timeout(timeout)3892 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)3893 if self.demo_mode:3894 selector, by = self.__recalculate_selector(selector, by)3895 a_t = "ASSERT TEXT"3896 i_n = "in"3897 if self._language != "English":3898 from seleniumbase.fixtures.words import SD3899 a_t = SD.translate_assert_text(self._language)3900 i_n = SD.translate_in(self._language)3901 messenger_post = ("%s: {%s} %s %s: %s"3902 % (a_t, text, i_n, by.upper(), selector))3903 self.__highlight_with_assert_success(messenger_post, selector, by)3904 return True3905 def assert_exact_text(self, text, selector="html", by=By.CSS_SELECTOR,3906 timeout=None):3907 """ Similar to assert_text(), but the text must be exact, rather than3908 exist as a subset of the full text.3909 (Extra whitespace at the beginning or the end doesn't count.)3910 Raises an exception if the element or the text is not found.3911 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3912 if not timeout:3913 timeout = settings.SMALL_TIMEOUT3914 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:3915 timeout = self.__get_new_timeout(timeout)3916 self.wait_for_exact_text_visible(3917 text, selector, by=by, timeout=timeout)3918 if self.demo_mode:3919 selector, by = self.__recalculate_selector(selector, by)3920 a_t = "ASSERT EXACT TEXT"3921 i_n = "in"3922 if self._language != "English":3923 from seleniumbase.fixtures.words import SD3924 a_t = SD.translate_assert_exact_text(self._language)3925 i_n = SD.translate_in(self._language)3926 messenger_post = ("%s: {%s} %s %s: %s"3927 % (a_t, text, i_n, by.upper(), selector))3928 self.__highlight_with_assert_success(messenger_post, selector, by)3929 return True3930 ############3931 def wait_for_link_text_present(self, link_text, timeout=None):3932 if not timeout:3933 timeout = settings.SMALL_TIMEOUT3934 start_ms = time.time() * 1000.03935 stop_ms = start_ms + (timeout * 1000.0)3936 for x in range(int(timeout * 5)):3937 shared_utils.check_if_time_limit_exceeded()3938 try:3939 if not self.is_link_text_present(link_text):3940 raise Exception(3941 "Link text {%s} was not found!" % link_text)3942 return3943 except Exception:3944 now_ms = time.time() * 1000.03945 if now_ms >= stop_ms:3946 break3947 time.sleep(0.2)3948 message = (3949 "Link text {%s} was not present after %s seconds!"3950 "" % (link_text, timeout))3951 page_actions.timeout_exception("NoSuchElementException", message)3952 def wait_for_partial_link_text_present(self, link_text, timeout=None):3953 if not timeout:3954 timeout = settings.SMALL_TIMEOUT3955 start_ms = time.time() * 1000.03956 stop_ms = start_ms + (timeout * 1000.0)3957 for x in range(int(timeout * 5)):3958 shared_utils.check_if_time_limit_exceeded()3959 try:3960 if not self.is_partial_link_text_present(link_text):3961 raise Exception(3962 "Partial Link text {%s} was not found!" % link_text)3963 return3964 except Exception:3965 now_ms = time.time() * 1000.03966 if now_ms >= stop_ms:3967 break3968 time.sleep(0.2)3969 message = (3970 "Partial Link text {%s} was not present after %s seconds!"3971 "" % (link_text, timeout))3972 page_actions.timeout_exception("NoSuchElementException", message)3973 def wait_for_link_text_visible(self, link_text, timeout=None):3974 if not timeout:3975 timeout = settings.LARGE_TIMEOUT3976 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3977 timeout = self.__get_new_timeout(timeout)3978 return self.wait_for_element_visible(3979 link_text, by=By.LINK_TEXT, timeout=timeout)3980 def wait_for_link_text(self, link_text, timeout=None):3981 """ The shorter version of wait_for_link_text_visible() """3982 if not timeout:3983 timeout = settings.LARGE_TIMEOUT3984 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3985 timeout = self.__get_new_timeout(timeout)3986 return self.wait_for_link_text_visible(link_text, timeout=timeout)3987 def find_link_text(self, link_text, timeout=None):3988 """ Same as wait_for_link_text_visible() - returns the element """3989 if not timeout:3990 timeout = settings.LARGE_TIMEOUT3991 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:3992 timeout = self.__get_new_timeout(timeout)3993 return self.wait_for_link_text_visible(link_text, timeout=timeout)3994 def assert_link_text(self, link_text, timeout=None):3995 """ Similar to wait_for_link_text_visible(), but returns nothing.3996 As above, will raise an exception if nothing can be found.3997 Returns True if successful. Default timeout = SMALL_TIMEOUT. """3998 if not timeout:3999 timeout = settings.SMALL_TIMEOUT4000 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4001 timeout = self.__get_new_timeout(timeout)4002 self.wait_for_link_text_visible(link_text, timeout=timeout)4003 if self.demo_mode:4004 a_t = "ASSERT LINK TEXT"4005 if self._language != "English":4006 from seleniumbase.fixtures.words import SD4007 a_t = SD.translate_assert_link_text(self._language)4008 messenger_post = ("%s: {%s}" % (a_t, link_text))4009 self.__highlight_with_assert_success(4010 messenger_post, link_text, by=By.LINK_TEXT)4011 return True4012 def wait_for_partial_link_text(self, partial_link_text, timeout=None):4013 if not timeout:4014 timeout = settings.LARGE_TIMEOUT4015 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4016 timeout = self.__get_new_timeout(timeout)4017 return self.wait_for_element_visible(4018 partial_link_text, by=By.PARTIAL_LINK_TEXT, timeout=timeout)4019 def find_partial_link_text(self, partial_link_text, timeout=None):4020 """ Same as wait_for_partial_link_text() - returns the element """4021 if not timeout:4022 timeout = settings.LARGE_TIMEOUT4023 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4024 timeout = self.__get_new_timeout(timeout)4025 return self.wait_for_partial_link_text(4026 partial_link_text, timeout=timeout)4027 def assert_partial_link_text(self, partial_link_text, timeout=None):4028 """ Similar to wait_for_partial_link_text(), but returns nothing.4029 As above, will raise an exception if nothing can be found.4030 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4031 if not timeout:4032 timeout = settings.SMALL_TIMEOUT4033 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4034 timeout = self.__get_new_timeout(timeout)4035 self.wait_for_partial_link_text(partial_link_text, timeout=timeout)4036 if self.demo_mode:4037 a_t = "ASSERT PARTIAL LINK TEXT"4038 if self._language != "English":4039 from seleniumbase.fixtures.words import SD4040 a_t = SD.translate_assert_link_text(self._language)4041 messenger_post = ("%s: {%s}" % (a_t, partial_link_text))4042 self.__highlight_with_assert_success(4043 messenger_post, partial_link_text, by=By.PARTIAL_LINK_TEXT)4044 return True4045 ############4046 def wait_for_element_absent(self, selector, by=By.CSS_SELECTOR,4047 timeout=None):4048 """ Waits for an element to no longer appear in the HTML of a page.4049 A hidden element still counts as appearing in the page HTML.4050 If an element with "hidden" status is acceptable,4051 use wait_for_element_not_visible() instead. """4052 if not timeout:4053 timeout = settings.LARGE_TIMEOUT4054 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4055 timeout = self.__get_new_timeout(timeout)4056 selector, by = self.__recalculate_selector(selector, by)4057 return page_actions.wait_for_element_absent(4058 self.driver, selector, by, timeout)4059 def assert_element_absent(self, selector, by=By.CSS_SELECTOR,4060 timeout=None):4061 """ Similar to wait_for_element_absent() - returns nothing.4062 As above, will raise an exception if the element stays present.4063 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4064 if not timeout:4065 timeout = settings.SMALL_TIMEOUT4066 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4067 timeout = self.__get_new_timeout(timeout)4068 self.wait_for_element_absent(selector, by=by, timeout=timeout)4069 return True4070 ############4071 def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR,4072 timeout=None):4073 """ Waits for an element to no longer be visible on a page.4074 The element can be non-existant in the HTML or hidden on the page4075 to qualify as not visible. """4076 if not timeout:4077 timeout = settings.LARGE_TIMEOUT4078 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4079 timeout = self.__get_new_timeout(timeout)4080 selector, by = self.__recalculate_selector(selector, by)4081 return page_actions.wait_for_element_not_visible(4082 self.driver, selector, by, timeout)4083 def assert_element_not_visible(self, selector, by=By.CSS_SELECTOR,4084 timeout=None):4085 """ Similar to wait_for_element_not_visible() - returns nothing.4086 As above, will raise an exception if the element stays visible.4087 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4088 if not timeout:4089 timeout = settings.SMALL_TIMEOUT4090 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4091 timeout = self.__get_new_timeout(timeout)4092 self.wait_for_element_not_visible(selector, by=by, timeout=timeout)4093 return True4094 ############4095 def wait_for_text_not_visible(self, text, selector="html",4096 by=By.CSS_SELECTOR,4097 timeout=None):4098 if not timeout:4099 timeout = settings.LARGE_TIMEOUT4100 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4101 timeout = self.__get_new_timeout(timeout)4102 selector, by = self.__recalculate_selector(selector, by)4103 return page_actions.wait_for_text_not_visible(4104 self.driver, text, selector, by, timeout)4105 def assert_text_not_visible(self, text, selector="html",4106 by=By.CSS_SELECTOR,4107 timeout=None):4108 """ Similar to wait_for_text_not_visible()4109 Raises an exception if the element or the text is not found.4110 Returns True if successful. Default timeout = SMALL_TIMEOUT. """4111 if not timeout:4112 timeout = settings.SMALL_TIMEOUT4113 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4114 timeout = self.__get_new_timeout(timeout)4115 self.wait_for_text_not_visible(text, selector, by=by, timeout=timeout)4116 ############4117 def wait_for_and_accept_alert(self, timeout=None):4118 if not timeout:4119 timeout = settings.LARGE_TIMEOUT4120 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4121 timeout = self.__get_new_timeout(timeout)4122 return page_actions.wait_for_and_accept_alert(self.driver, timeout)4123 def wait_for_and_dismiss_alert(self, timeout=None):4124 if not timeout:4125 timeout = settings.LARGE_TIMEOUT4126 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4127 timeout = self.__get_new_timeout(timeout)4128 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)4129 def wait_for_and_switch_to_alert(self, timeout=None):4130 if not timeout:4131 timeout = settings.LARGE_TIMEOUT4132 if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:4133 timeout = self.__get_new_timeout(timeout)4134 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)4135 ############4136 def accept_alert(self, timeout=None):4137 """ Same as wait_for_and_accept_alert(), but smaller default T_O """4138 if not timeout:4139 timeout = settings.SMALL_TIMEOUT4140 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4141 timeout = self.__get_new_timeout(timeout)4142 return page_actions.wait_for_and_accept_alert(self.driver, timeout)4143 def dismiss_alert(self, timeout=None):4144 """ Same as wait_for_and_dismiss_alert(), but smaller default T_O """4145 if not timeout:4146 timeout = settings.SMALL_TIMEOUT4147 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4148 timeout = self.__get_new_timeout(timeout)4149 return page_actions.wait_for_and_dismiss_alert(self.driver, timeout)4150 def switch_to_alert(self, timeout=None):4151 """ Same as wait_for_and_switch_to_alert(), but smaller default T_O """4152 if not timeout:4153 timeout = settings.SMALL_TIMEOUT4154 if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:4155 timeout = self.__get_new_timeout(timeout)4156 return page_actions.wait_for_and_switch_to_alert(self.driver, timeout)4157 ############4158 def __assert_eq(self, *args, **kwargs):4159 """ Minified assert_equal() using only the list diff. """4160 minified_exception = None4161 try:4162 self.assertEqual(*args, **kwargs)4163 except Exception as e:4164 str_e = str(e)4165 minified_exception = "\nAssertionError:\n"4166 lines = str_e.split('\n')4167 countdown = 34168 countdown_on = False4169 for line in lines:4170 if countdown_on:4171 minified_exception += line + '\n'4172 countdown = countdown - 14173 if countdown == 0:4174 countdown_on = False4175 elif line.startswith('F'):4176 countdown_on = True4177 countdown = 34178 minified_exception += line + '\n'4179 elif line.startswith('+') or line.startswith('-'):4180 minified_exception += line + '\n'4181 elif line.startswith('?'):4182 minified_exception += line + '\n'4183 elif line.strip().startswith('*'):4184 minified_exception += line + '\n'4185 if minified_exception:4186 raise Exception(minified_exception)4187 def check_window(self, name="default", level=0, baseline=False):4188 """ *** Automated Visual Testing with SeleniumBase ***4189 The first time a test calls self.check_window() for a unique "name"4190 parameter provided, it will set a visual baseline, meaning that it4191 creates a folder, saves the URL to a file, saves the current window4192 screenshot to a file, and creates the following three files4193 with the listed data saved:4194 tags_level1.txt -> HTML tags from the window4195 tags_level2.txt -> HTML tags + attributes from the window4196 tags_level3.txt -> HTML tags + attributes/values from the window4197 Baseline folders are named based on the test name and the name4198 parameter passed to self.check_window(). The same test can store4199 multiple baseline folders.4200 If the baseline is being set/reset, the "level" doesn't matter.4201 After the first run of self.check_window(), it will compare the4202 HTML tags of the latest window to the one from the initial run.4203 Here's how the level system works:4204 * level=0 ->4205 DRY RUN ONLY - Will perform a comparison to the baseline, and4206 print out any differences that are found, but4207 won't fail the test even if differences exist.4208 * level=1 ->4209 HTML tags are compared to tags_level1.txt4210 * level=2 ->4211 HTML tags are compared to tags_level1.txt and4212 HTML tags/attributes are compared to tags_level2.txt4213 * level=3 ->4214 HTML tags are compared to tags_level1.txt and4215 HTML tags + attributes are compared to tags_level2.txt and4216 HTML tags + attributes/values are compared to tags_level3.txt4217 As shown, Level-3 is the most strict, Level-1 is the least strict.4218 If the comparisons from the latest window to the existing baseline4219 don't match, the current test will fail, except for Level-0 tests.4220 You can reset the visual baseline on the command line by using:4221 --visual_baseline4222 As long as "--visual_baseline" is used on the command line while4223 running tests, the self.check_window() method cannot fail because4224 it will rebuild the visual baseline rather than comparing the html4225 tags of the latest run to the existing baseline. If there are any4226 expected layout changes to a website that you're testing, you'll4227 need to reset the baseline to prevent unnecessary failures.4228 self.check_window() will fail with "Page Domain Mismatch Failure"4229 if the page domain doesn't match the domain of the baseline.4230 If you want to use self.check_window() to compare a web page to4231 a later version of itself from within the same test run, you can4232 add the parameter "baseline=True" to the first time you call4233 self.check_window() in a test to use that as the baseline. This4234 only makes sense if you're calling self.check_window() more than4235 once with the same name parameter in the same test.4236 Automated Visual Testing with self.check_window() is not very4237 effective for websites that have dynamic content that changes4238 the layout and structure of web pages. For those, you're much4239 better off using regular SeleniumBase functional testing.4240 Example usage:4241 self.check_window(name="testing", level=0)4242 self.check_window(name="xkcd_home", level=1)4243 self.check_window(name="github_page", level=2)4244 self.check_window(name="wikipedia_page", level=3)4245 """4246 if level == "0":4247 level = 04248 if level == "1":4249 level = 14250 if level == "2":4251 level = 24252 if level == "3":4253 level = 34254 if level != 0 and level != 1 and level != 2 and level != 3:4255 raise Exception('Parameter "level" must be set to 0, 1, 2, or 3!')4256 if self.demo_mode:4257 raise Exception(4258 "WARNING: Using Demo Mode will break layout tests "4259 "that use the check_window() method due to custom "4260 "HTML edits being made on the page!\n"4261 "Please rerun without using Demo Mode!")4262 module = self.__class__.__module__4263 if '.' in module and len(module.split('.')[-1]) > 1:4264 module = module.split('.')[-1]4265 test_id = "%s.%s" % (module, self._testMethodName)4266 if not name or len(name) < 1:4267 name = "default"4268 name = str(name)4269 from seleniumbase.core import visual_helper4270 visual_helper.visual_baseline_folder_setup()4271 baseline_dir = constants.VisualBaseline.STORAGE_FOLDER4272 visual_baseline_path = baseline_dir + "/" + test_id + "/" + name4273 page_url_file = visual_baseline_path + "/page_url.txt"4274 screenshot_file = visual_baseline_path + "/screenshot.png"4275 level_1_file = visual_baseline_path + "/tags_level_1.txt"4276 level_2_file = visual_baseline_path + "/tags_level_2.txt"4277 level_3_file = visual_baseline_path + "/tags_level_3.txt"4278 set_baseline = False4279 if baseline or self.visual_baseline:4280 set_baseline = True4281 if not os.path.exists(visual_baseline_path):4282 set_baseline = True4283 try:4284 os.makedirs(visual_baseline_path)4285 except Exception:4286 pass # Only reachable during multi-threaded test runs4287 if not os.path.exists(page_url_file):4288 set_baseline = True4289 if not os.path.exists(screenshot_file):4290 set_baseline = True4291 if not os.path.exists(level_1_file):4292 set_baseline = True4293 if not os.path.exists(level_2_file):4294 set_baseline = True4295 if not os.path.exists(level_3_file):4296 set_baseline = True4297 page_url = self.get_current_url()4298 soup = self.get_beautiful_soup()4299 html_tags = soup.body.find_all()4300 level_1 = [[tag.name] for tag in html_tags]4301 level_1 = json.loads(json.dumps(level_1)) # Tuples become lists4302 level_2 = [[tag.name, sorted(tag.attrs.keys())] for tag in html_tags]4303 level_2 = json.loads(json.dumps(level_2)) # Tuples become lists4304 level_3 = [[tag.name, sorted(tag.attrs.items())] for tag in html_tags]4305 level_3 = json.loads(json.dumps(level_3)) # Tuples become lists4306 if set_baseline:4307 self.save_screenshot("screenshot.png", visual_baseline_path)4308 out_file = codecs.open(page_url_file, "w+")4309 out_file.writelines(page_url)4310 out_file.close()4311 out_file = codecs.open(level_1_file, "w+")4312 out_file.writelines(json.dumps(level_1))4313 out_file.close()4314 out_file = codecs.open(level_2_file, "w+")4315 out_file.writelines(json.dumps(level_2))4316 out_file.close()4317 out_file = codecs.open(level_3_file, "w+")4318 out_file.writelines(json.dumps(level_3))4319 out_file.close()4320 if not set_baseline:4321 f = open(page_url_file, 'r')4322 page_url_data = f.read().strip()4323 f.close()4324 f = open(level_1_file, 'r')4325 level_1_data = json.loads(f.read())4326 f.close()4327 f = open(level_2_file, 'r')4328 level_2_data = json.loads(f.read())4329 f.close()4330 f = open(level_3_file, 'r')4331 level_3_data = json.loads(f.read())4332 f.close()4333 domain_fail = (4334 "\nPage Domain Mismatch Failure: "4335 "Current Page Domain doesn't match the Page Domain of the "4336 "Baseline! Can't compare two completely different sites! "4337 "Run with --visual_baseline to reset the baseline!")4338 level_1_failure = (4339 "\n*\n*** Exception: <Level 1> Visual Diff Failure:\n"4340 "* HTML tags don't match the baseline!")4341 level_2_failure = (4342 "\n*\n*** Exception: <Level 2> Visual Diff Failure:\n"4343 "* HTML tag attribute names don't match the baseline!")4344 level_3_failure = (4345 "\n*\n*** Exception: <Level 3> Visual Diff Failure:\n"4346 "* HTML tag attribute values don't match the baseline!")4347 page_domain = self.get_domain_url(page_url)4348 page_data_domain = self.get_domain_url(page_url_data)4349 unittest.TestCase.maxDiff = 10004350 if level != 0:4351 self.assertEqual(page_data_domain, page_domain, domain_fail)4352 unittest.TestCase.maxDiff = None4353 if level == 3:4354 self.__assert_eq(level_3_data, level_3, level_3_failure)4355 if level == 2:4356 self.__assert_eq(level_2_data, level_2, level_2_failure)4357 unittest.TestCase.maxDiff = 10004358 if level == 1:4359 self.__assert_eq(level_1_data, level_1, level_1_failure)4360 unittest.TestCase.maxDiff = None4361 if level == 0:4362 try:4363 unittest.TestCase.maxDiff = 10004364 self.assertEqual(4365 page_domain, page_data_domain, domain_fail)4366 unittest.TestCase.maxDiff = None4367 self.__assert_eq(level_3_data, level_3, level_3_failure)4368 except Exception as e:4369 print(e) # Level-0 Dry Run (Only print the differences)4370 ############4371 def __get_new_timeout(self, timeout):4372 """ When using --timeout_multiplier=#.# """4373 try:4374 timeout_multiplier = float(self.timeout_multiplier)4375 if timeout_multiplier <= 0.5:4376 timeout_multiplier = 0.54377 timeout = int(math.ceil(timeout_multiplier * timeout))4378 return timeout4379 except Exception:4380 # Wrong data type for timeout_multiplier (expecting int or float)4381 return timeout4382 ############4383 def __get_exception_message(self):4384 """ This method extracts the message from an exception if there4385 was an exception that occurred during the test, assuming4386 that the exception was in a try/except block and not thrown. """4387 exception_info = sys.exc_info()[1]4388 if hasattr(exception_info, 'msg'):4389 exc_message = exception_info.msg4390 elif hasattr(exception_info, 'message'):4391 exc_message = exception_info.message4392 else:4393 exc_message = sys.exc_info()4394 return exc_message4395 def __get_improved_exception_message(self):4396 """4397 If Chromedriver is out-of-date, make it clear!4398 Given the high popularity of the following StackOverflow article:4399 https://stackoverflow.com/questions/49162667/unknown-error-4400 call-function-result-missing-value-for-selenium-send-keys-even4401 ... the original error message was not helpful. Tell people directly.4402 (Only expected when using driver.send_keys() with an old Chromedriver.)4403 """4404 exc_message = self.__get_exception_message()4405 maybe_using_old_chromedriver = False4406 if "unknown error: call function result missing" in exc_message:4407 maybe_using_old_chromedriver = True4408 if self.browser == 'chrome' and maybe_using_old_chromedriver:4409 update = ("Your version of ChromeDriver may be out-of-date! "4410 "Please go to "4411 "https://sites.google.com/a/chromium.org/chromedriver/ "4412 "and download the latest version to your system PATH! "4413 "Or use: ``seleniumbase install chromedriver`` . "4414 "Original Exception Message: %s" % exc_message)4415 exc_message = update4416 return exc_message4417 def __add_deferred_assert_failure(self):4418 """ Add a deferred_assert failure to a list for future processing. """4419 current_url = self.driver.current_url4420 message = self.__get_exception_message()4421 self.__deferred_assert_failures.append(4422 "CHECK #%s: (%s)\n %s" % (4423 self.__deferred_assert_count, current_url, message))4424 ############4425 def deferred_assert_element(self, selector, by=By.CSS_SELECTOR,4426 timeout=None):4427 """ A non-terminating assertion for an element on a page.4428 Failures will be saved until the process_deferred_asserts()4429 method is called from inside a test, likely at the end of it. """4430 if not timeout:4431 timeout = settings.MINI_TIMEOUT4432 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:4433 timeout = self.__get_new_timeout(timeout)4434 self.__deferred_assert_count += 14435 try:4436 url = self.get_current_url()4437 if url == self.__last_url_of_deferred_assert:4438 timeout = 14439 else:4440 self.__last_url_of_deferred_assert = url4441 except Exception:4442 pass4443 try:4444 self.wait_for_element_visible(selector, by=by, timeout=timeout)4445 return True4446 except Exception:4447 self.__add_deferred_assert_failure()4448 return False4449 def deferred_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,4450 timeout=None):4451 """ A non-terminating assertion for text from an element on a page.4452 Failures will be saved until the process_deferred_asserts()4453 method is called from inside a test, likely at the end of it. """4454 if not timeout:4455 timeout = settings.MINI_TIMEOUT4456 if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:4457 timeout = self.__get_new_timeout(timeout)4458 self.__deferred_assert_count += 14459 try:4460 url = self.get_current_url()4461 if url == self.__last_url_of_deferred_assert:4462 timeout = 14463 else:4464 self.__last_url_of_deferred_assert = url4465 except Exception:4466 pass4467 try:4468 self.wait_for_text_visible(text, selector, by=by, timeout=timeout)4469 return True4470 except Exception:4471 self.__add_deferred_assert_failure()4472 return False4473 def process_deferred_asserts(self, print_only=False):4474 """ To be used with any test that uses deferred_asserts, which are4475 non-terminating verifications that only raise exceptions4476 after this method is called.4477 This is useful for pages with multiple elements to be checked when4478 you want to find as many bugs as possible in a single test run4479 before having all the exceptions get raised simultaneously.4480 Might be more useful if this method is called after processing all4481 the deferred asserts on a single html page so that the failure4482 screenshot matches the location of the deferred asserts.4483 If "print_only" is set to True, the exception won't get raised. """4484 if self.__deferred_assert_failures:4485 exception_output = ''4486 exception_output += "\n*** DEFERRED ASSERTION FAILURES FROM: "4487 exception_output += "%s\n" % self.id()4488 all_failing_checks = self.__deferred_assert_failures4489 self.__deferred_assert_failures = []4490 for tb in all_failing_checks:4491 exception_output += "%s\n" % tb4492 if print_only:4493 print(exception_output)4494 else:4495 raise Exception(exception_output)4496 ############4497 # Alternate naming scheme for the "deferred_assert" methods.4498 def delayed_assert_element(self, selector, by=By.CSS_SELECTOR,4499 timeout=None):4500 """ Same as self.deferred_assert_element() """4501 return self.deferred_assert_element(4502 selector=selector, by=by, timeout=timeout)4503 def delayed_assert_text(self, text, selector="html", by=By.CSS_SELECTOR,4504 timeout=None):4505 """ Same as self.deferred_assert_text() """4506 return self.deferred_assert_text(4507 text=text, selector=selector, by=by, timeout=timeout)4508 def process_delayed_asserts(self, print_only=False):4509 """ Same as self.process_deferred_asserts() """4510 self.process_deferred_asserts(print_only=print_only)4511 ############4512 def __js_click(self, selector, by=By.CSS_SELECTOR):4513 """ Clicks an element using pure JS. Does not use jQuery. """4514 selector, by = self.__recalculate_selector(selector, by)4515 css_selector = self.convert_to_css_selector(selector, by=by)4516 css_selector = re.escape(css_selector)4517 css_selector = self.__escape_quotes_if_needed(css_selector)4518 script = ("""var simulateClick = function (elem) {4519 var evt = new MouseEvent('click', {4520 bubbles: true,4521 cancelable: true,4522 view: window4523 });4524 var canceled = !elem.dispatchEvent(evt);4525 };4526 var someLink = document.querySelector('%s');4527 simulateClick(someLink);"""4528 % css_selector)4529 self.execute_script(script)4530 def __js_click_all(self, selector, by=By.CSS_SELECTOR):4531 """ Clicks all matching elements using pure JS. (No jQuery) """4532 selector, by = self.__recalculate_selector(selector, by)4533 css_selector = self.convert_to_css_selector(selector, by=by)4534 css_selector = re.escape(css_selector)4535 css_selector = self.__escape_quotes_if_needed(css_selector)4536 script = ("""var simulateClick = function (elem) {4537 var evt = new MouseEvent('click', {4538 bubbles: true,4539 cancelable: true,4540 view: window4541 });4542 var canceled = !elem.dispatchEvent(evt);4543 };4544 var $elements = document.querySelectorAll('%s');4545 var index = 0, length = $elements.length;4546 for(; index < length; index++){4547 simulateClick($elements[index]);}"""4548 % css_selector)4549 self.execute_script(script)4550 def __jquery_click(self, selector, by=By.CSS_SELECTOR):4551 """ Clicks an element using jQuery. Different from using pure JS. """4552 selector, by = self.__recalculate_selector(selector, by)4553 self.wait_for_element_present(4554 selector, by=by, timeout=settings.SMALL_TIMEOUT)4555 selector = self.convert_to_css_selector(selector, by=by)4556 selector = self.__make_css_match_first_element_only(selector)4557 click_script = """jQuery('%s')[0].click()""" % selector4558 self.safe_execute_script(click_script)4559 def __get_href_from_link_text(self, link_text, hard_fail=True):4560 href = self.get_link_attribute(link_text, "href", hard_fail)4561 if not href:4562 return None4563 if href.startswith('//'):4564 link = "http:" + href4565 elif href.startswith('/'):4566 url = self.driver.current_url4567 domain_url = self.get_domain_url(url)4568 link = domain_url + href4569 else:4570 link = href4571 return link4572 def __click_dropdown_link_text(self, link_text, link_css):4573 """ When a link may be hidden under a dropdown menu, use this. """4574 soup = self.get_beautiful_soup()4575 drop_down_list = []4576 for item in soup.select('li[class]'):4577 drop_down_list.append(item)4578 csstype = link_css.split('[')[1].split('=')[0]4579 for item in drop_down_list:4580 item_text_list = item.text.split('\n')4581 if link_text in item_text_list and csstype in item.decode():4582 dropdown_css = ""4583 try:4584 for css_class in item['class']:4585 dropdown_css += '.'4586 dropdown_css += css_class4587 except Exception:4588 continue4589 dropdown_css = item.name + dropdown_css4590 matching_dropdowns = self.find_visible_elements(dropdown_css)4591 for dropdown in matching_dropdowns:4592 # The same class names might be used for multiple dropdowns4593 if dropdown.is_displayed():4594 try:4595 try:4596 page_actions.hover_element(4597 self.driver, dropdown)4598 except Exception:4599 # If hovering fails, driver is likely outdated4600 # Time to go directly to the hidden link text4601 self.open(self.__get_href_from_link_text(4602 link_text))4603 return True4604 page_actions.hover_element_and_click(4605 self.driver, dropdown, link_text,4606 click_by=By.LINK_TEXT, timeout=0.12)4607 return True4608 except Exception:4609 pass4610 return False4611 def __get_href_from_partial_link_text(self, link_text, hard_fail=True):4612 href = self.get_partial_link_text_attribute(4613 link_text, "href", hard_fail)4614 if not href:4615 return None4616 if href.startswith('//'):4617 link = "http:" + href4618 elif href.startswith('/'):4619 url = self.driver.current_url4620 domain_url = self.get_domain_url(url)4621 link = domain_url + href4622 else:4623 link = href4624 return link4625 def __click_dropdown_partial_link_text(self, link_text, link_css):4626 """ When a partial link may be hidden under a dropdown, use this. """4627 soup = self.get_beautiful_soup()4628 drop_down_list = []4629 for item in soup.select('li[class]'):4630 drop_down_list.append(item)4631 csstype = link_css.split('[')[1].split('=')[0]4632 for item in drop_down_list:4633 item_text_list = item.text.split('\n')4634 if link_text in item_text_list and csstype in item.decode():4635 dropdown_css = ""4636 try:4637 for css_class in item['class']:4638 dropdown_css += '.'4639 dropdown_css += css_class4640 except Exception:4641 continue4642 dropdown_css = item.name + dropdown_css4643 matching_dropdowns = self.find_visible_elements(dropdown_css)4644 for dropdown in matching_dropdowns:4645 # The same class names might be used for multiple dropdowns4646 if dropdown.is_displayed():4647 try:4648 try:4649 page_actions.hover_element(4650 self.driver, dropdown)4651 except Exception:4652 # If hovering fails, driver is likely outdated4653 # Time to go directly to the hidden link text4654 self.open(4655 self.__get_href_from_partial_link_text(4656 link_text))4657 return True4658 page_actions.hover_element_and_click(4659 self.driver, dropdown, link_text,4660 click_by=By.LINK_TEXT, timeout=0.12)4661 return True4662 except Exception:4663 pass4664 return False4665 def __recalculate_selector(self, selector, by):4666 # Try to determine the type of selector automatically4667 if page_utils.is_xpath_selector(selector):4668 by = By.XPATH4669 if page_utils.is_link_text_selector(selector):4670 selector = page_utils.get_link_text_from_selector(selector)4671 by = By.LINK_TEXT4672 if page_utils.is_partial_link_text_selector(selector):4673 selector = page_utils.get_partial_link_text_from_selector(selector)4674 by = By.PARTIAL_LINK_TEXT4675 if page_utils.is_name_selector(selector):4676 name = page_utils.get_name_from_selector(selector)4677 selector = '[name="%s"]' % name4678 by = By.CSS_SELECTOR4679 return (selector, by)4680 def __looks_like_a_page_url(self, url):4681 """ Returns True if the url parameter looks like a URL. This method4682 is slightly more lenient than page_utils.is_valid_url(url) due to4683 possible typos when calling self.get(url), which will try to4684 navigate to the page if a URL is detected, but will instead call4685 self.get_element(URL_AS_A_SELECTOR) if the input in not a URL. """4686 if (url.startswith("http:") or url.startswith("https:") or (4687 url.startswith("://") or url.startswith("data:") or (4688 url.startswith("about:") or url.startswith("chrome:") or (4689 url.startswith("file:"))))):4690 return True4691 else:4692 return False4693 def __make_css_match_first_element_only(self, selector):4694 # Only get the first match4695 return page_utils.make_css_match_first_element_only(selector)4696 def __demo_mode_pause_if_active(self, tiny=False):4697 if self.demo_mode:4698 wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT4699 if self.demo_sleep:4700 wait_time = float(self.demo_sleep)4701 if not tiny:4702 time.sleep(wait_time)4703 else:4704 time.sleep(wait_time / 3.4)4705 elif self.slow_mode:4706 self.__slow_mode_pause_if_active()4707 def __slow_mode_pause_if_active(self):4708 if self.slow_mode:4709 wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT4710 if self.demo_sleep:4711 wait_time = float(self.demo_sleep)4712 time.sleep(wait_time)4713 def __demo_mode_scroll_if_active(self, selector, by):4714 if self.demo_mode:4715 self.slow_scroll_to(selector, by=by)4716 def __demo_mode_highlight_if_active(self, selector, by):4717 if self.demo_mode:4718 # Includes self.slow_scroll_to(selector, by=by) by default4719 self.highlight(selector, by=by)4720 elif self.slow_mode:4721 # Just do the slow scroll part of the highlight() method4722 selector, by = self.__recalculate_selector(selector, by)4723 element = self.wait_for_element_visible(4724 selector, by=by, timeout=settings.SMALL_TIMEOUT)4725 try:4726 self.__slow_scroll_to_element(element)4727 except (StaleElementReferenceException, ENI_Exception):4728 self.wait_for_ready_state_complete()4729 time.sleep(0.05)4730 element = self.wait_for_element_visible(4731 selector, by=by, timeout=settings.SMALL_TIMEOUT)4732 self.__slow_scroll_to_element(element)4733 def __scroll_to_element(self, element, selector=None, by=By.CSS_SELECTOR):4734 success = js_utils.scroll_to_element(self.driver, element)4735 if not success and selector:4736 self.wait_for_ready_state_complete()4737 element = page_actions.wait_for_element_visible(4738 self.driver, selector, by, timeout=settings.SMALL_TIMEOUT)4739 self.__demo_mode_pause_if_active(tiny=True)4740 def __slow_scroll_to_element(self, element):4741 js_utils.slow_scroll_to_element(self.driver, element, self.browser)4742 def __highlight_with_assert_success(4743 self, message, selector, by=By.CSS_SELECTOR):4744 selector, by = self.__recalculate_selector(selector, by)4745 element = self.wait_for_element_visible(4746 selector, by=by, timeout=settings.SMALL_TIMEOUT)4747 try:4748 selector = self.convert_to_css_selector(selector, by=by)4749 except Exception:4750 # Don't highlight if can't convert to CSS_SELECTOR4751 return4752 try:4753 self.__slow_scroll_to_element(element)4754 except (StaleElementReferenceException, ENI_Exception):4755 self.wait_for_ready_state_complete()4756 time.sleep(0.05)4757 element = self.wait_for_element_visible(4758 selector, by=by, timeout=settings.SMALL_TIMEOUT)4759 self.__slow_scroll_to_element(element)4760 o_bs = '' # original_box_shadow4761 try:4762 style = element.get_attribute('style')4763 except (StaleElementReferenceException, ENI_Exception):4764 self.wait_for_ready_state_complete()4765 time.sleep(0.05)4766 element = self.wait_for_element_visible(4767 selector, by=By.CSS_SELECTOR, timeout=settings.SMALL_TIMEOUT)4768 style = element.get_attribute('style')4769 if style:4770 if 'box-shadow: ' in style:4771 box_start = style.find('box-shadow: ')4772 box_end = style.find(';', box_start) + 14773 original_box_shadow = style[box_start:box_end]4774 o_bs = original_box_shadow4775 if ":contains" not in selector and ":first" not in selector:4776 selector = re.escape(selector)4777 selector = self.__escape_quotes_if_needed(selector)4778 self.__highlight_with_js_2(message, selector, o_bs)4779 else:4780 selector = self.__make_css_match_first_element_only(selector)4781 selector = re.escape(selector)4782 selector = self.__escape_quotes_if_needed(selector)4783 try:4784 self.__highlight_with_jquery_2(message, selector, o_bs)4785 except Exception:4786 pass # JQuery probably couldn't load. Skip highlighting.4787 time.sleep(0.065)4788 def __highlight_with_js_2(self, message, selector, o_bs):4789 js_utils.highlight_with_js_2(4790 self.driver, message, selector, o_bs, self.message_duration)4791 def __highlight_with_jquery_2(self, message, selector, o_bs):4792 js_utils.highlight_with_jquery_2(4793 self.driver, message, selector, o_bs, self.message_duration)4794 ############4795 # Deprecated Methods (Replace these if they're still in your code!)4796 @decorators.deprecated(4797 "jq_format() is deprecated. Use re.escape() instead!")4798 def jq_format(self, code):4799 # DEPRECATED - re.escape() already performs the intended action!4800 return js_utils._jq_format(code)4801 ############4802 def setUp(self, masterqa_mode=False):4803 """4804 Be careful if a subclass of BaseCase overrides setUp()4805 You'll need to add the following line to the subclass setUp() method:4806 super(SubClassOfBaseCase, self).setUp()4807 """4808 self.masterqa_mode = masterqa_mode4809 self.is_pytest = None4810 try:4811 # This raises an exception if the test is not coming from pytest4812 self.is_pytest = sb_config.is_pytest4813 except Exception:4814 # Not using pytest (probably nosetests)4815 self.is_pytest = False4816 if self.is_pytest:4817 # pytest-specific code4818 test_id = self.__get_test_id()4819 self.browser = sb_config.browser4820 self.data = sb_config.data4821 self.var1 = sb_config.var14822 self.var2 = sb_config.var24823 self.var3 = sb_config.var34824 self.slow_mode = sb_config.slow_mode4825 self.demo_mode = sb_config.demo_mode4826 self.demo_sleep = sb_config.demo_sleep4827 self.highlights = sb_config.highlights4828 self.time_limit = sb_config._time_limit4829 sb_config.time_limit = sb_config._time_limit # Reset between tests4830 self.environment = sb_config.environment4831 self.env = self.environment # Add a shortened version4832 self.with_selenium = sb_config.with_selenium # Should be True4833 self.headless = sb_config.headless4834 self.headless_active = False4835 self.headed = sb_config.headed4836 self.start_page = sb_config.start_page4837 self.log_path = sb_config.log_path4838 self.with_testing_base = sb_config.with_testing_base4839 self.with_basic_test_info = sb_config.with_basic_test_info4840 self.with_screen_shots = sb_config.with_screen_shots4841 self.with_page_source = sb_config.with_page_source4842 self.with_db_reporting = sb_config.with_db_reporting4843 self.with_s3_logging = sb_config.with_s3_logging4844 self.servername = sb_config.servername4845 self.port = sb_config.port4846 self.proxy_string = sb_config.proxy_string4847 self.user_agent = sb_config.user_agent4848 self.mobile_emulator = sb_config.mobile_emulator4849 self.device_metrics = sb_config.device_metrics4850 self.cap_file = sb_config.cap_file4851 self.cap_string = sb_config.cap_string4852 self.settings_file = sb_config.settings_file4853 self.database_env = sb_config.database_env4854 self.message_duration = sb_config.message_duration4855 self.js_checking_on = sb_config.js_checking_on4856 self.ad_block_on = sb_config.ad_block_on4857 self.verify_delay = sb_config.verify_delay4858 self.disable_csp = sb_config.disable_csp4859 self.enable_sync = sb_config.enable_sync4860 self.use_auto_ext = sb_config.use_auto_ext4861 self.no_sandbox = sb_config.no_sandbox4862 self.disable_gpu = sb_config.disable_gpu4863 self.incognito = sb_config.incognito4864 self.guest_mode = sb_config.guest_mode4865 self.devtools = sb_config.devtools4866 self.user_data_dir = sb_config.user_data_dir4867 self.extension_zip = sb_config.extension_zip4868 self.extension_dir = sb_config.extension_dir4869 self.maximize_option = sb_config.maximize_option4870 self._reuse_session = sb_config.reuse_session4871 self._crumbs = sb_config.crumbs4872 self.save_screenshot_after_test = sb_config.save_screenshot4873 self.visual_baseline = sb_config.visual_baseline4874 self.timeout_multiplier = sb_config.timeout_multiplier4875 self.pytest_html_report = sb_config.pytest_html_report4876 self.report_on = False4877 if self.pytest_html_report:4878 self.report_on = True4879 self.use_grid = False4880 if self.servername != "localhost":4881 # Use Selenium Grid (Use --server="127.0.0.1" for a local Grid)4882 self.use_grid = True4883 if self.with_db_reporting:4884 import getpass4885 import uuid4886 from seleniumbase.core.application_manager import (4887 ApplicationManager)4888 from seleniumbase.core.testcase_manager import (4889 ExecutionQueryPayload)4890 from seleniumbase.core.testcase_manager import (4891 TestcaseDataPayload)4892 from seleniumbase.core.testcase_manager import (4893 TestcaseManager)4894 self.execution_guid = str(uuid.uuid4())4895 self.testcase_guid = None4896 self.execution_start_time = 04897 self.case_start_time = 04898 self.application = None4899 self.testcase_manager = None4900 self.error_handled = False4901 self.testcase_manager = TestcaseManager(self.database_env)4902 #4903 exec_payload = ExecutionQueryPayload()4904 exec_payload.execution_start_time = int(time.time() * 1000)4905 self.execution_start_time = exec_payload.execution_start_time4906 exec_payload.guid = self.execution_guid4907 exec_payload.username = getpass.getuser()4908 self.testcase_manager.insert_execution_data(exec_payload)4909 #4910 data_payload = TestcaseDataPayload()4911 self.testcase_guid = str(uuid.uuid4())4912 data_payload.guid = self.testcase_guid4913 data_payload.execution_guid = self.execution_guid4914 if self.with_selenium:4915 data_payload.browser = self.browser4916 else:4917 data_payload.browser = "N/A"4918 data_payload.test_address = test_id4919 application = ApplicationManager.generate_application_string(4920 self._testMethodName)4921 data_payload.env = application.split('.')[0]4922 data_payload.start_time = application.split('.')[1]4923 data_payload.state = constants.State.NOTRUN4924 self.testcase_manager.insert_testcase_data(data_payload)4925 self.case_start_time = int(time.time() * 1000)4926 if self.headless:4927 width = settings.HEADLESS_START_WIDTH4928 height = settings.HEADLESS_START_HEIGHT4929 try:4930 # from pyvirtualdisplay import Display # Skip for own lib4931 from seleniumbase.virtual_display.display import Display4932 self.display = Display(visible=0, size=(width, height))4933 self.display.start()4934 self.headless_active = True4935 except Exception:4936 # pyvirtualdisplay might not be necessary anymore because4937 # Chrome and Firefox now have built-in headless displays4938 pass4939 else:4940 # (Nosetests / Not Pytest)4941 pass # Setup performed in plugins4942 # Verify that SeleniumBase is installed successfully4943 if not hasattr(self, "browser"):4944 raise Exception("""SeleniumBase plugins DID NOT load!\n\n"""4945 """*** Please REINSTALL SeleniumBase using: >\n"""4946 """ >>> "pip install -r requirements.txt"\n"""4947 """ >>> "python setup.py install" """)4948 # Parse the settings file4949 if self.settings_file:4950 from seleniumbase.core import settings_parser4951 settings_parser.set_settings(self.settings_file)4952 # Mobile Emulator device metrics: CSS Width, CSS Height, & Pixel-Ratio4953 if self.device_metrics:4954 metrics_string = self.device_metrics4955 metrics_string = metrics_string.replace(' ', '')4956 metrics_list = metrics_string.split(',')4957 exception_string = (4958 'Invalid input for Mobile Emulator device metrics!\n'4959 'Expecting a comma-separated string with three\n'4960 'integer values for Width, Height, and Pixel-Ratio.\n'4961 'Example: --metrics="411,731,3" ')4962 if len(metrics_list) != 3:4963 raise Exception(exception_string)4964 try:4965 self.__device_width = int(metrics_list[0])4966 self.__device_height = int(metrics_list[1])4967 self.__device_pixel_ratio = int(metrics_list[2])4968 self.mobile_emulator = True4969 except Exception:4970 raise Exception(exception_string)4971 if self.mobile_emulator:4972 if not self.user_agent:4973 # Use the Pixel 3 user agent by default if not specified4974 self.user_agent = (4975 "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL) "4976 "AppleWebKit/537.36 (KHTML, like Gecko) "4977 "Chrome/76.0.3809.132 Mobile Safari/537.36")4978 has_url = False4979 if self._reuse_session:4980 if not hasattr(sb_config, 'shared_driver'):4981 sb_config.shared_driver = None4982 if sb_config.shared_driver:4983 try:4984 self._default_driver = sb_config.shared_driver4985 self.driver = sb_config.shared_driver4986 self._drivers_list = [sb_config.shared_driver]4987 url = self.get_current_url()4988 if len(url) > 3:4989 has_url = True4990 if self._crumbs:4991 self.driver.delete_all_cookies()4992 except Exception:4993 pass4994 if self._reuse_session and sb_config.shared_driver and has_url:4995 if self.start_page and len(self.start_page) >= 4:4996 if page_utils.is_valid_url(self.start_page):4997 self.open(self.start_page)4998 else:4999 new_start_page = "http://" + self.start_page5000 if page_utils.is_valid_url(new_start_page):5001 self.open(new_start_page)5002 else:5003 if self.get_current_url() != "data:,":5004 self.open("data:,")5005 else:5006 # Launch WebDriver for both Pytest and Nosetests5007 self.driver = self.get_new_driver(browser=self.browser,5008 headless=self.headless,5009 servername=self.servername,5010 port=self.port,5011 proxy=self.proxy_string,5012 agent=self.user_agent,5013 switch_to=True,5014 cap_file=self.cap_file,5015 cap_string=self.cap_string,5016 disable_csp=self.disable_csp,5017 enable_sync=self.enable_sync,5018 use_auto_ext=self.use_auto_ext,5019 no_sandbox=self.no_sandbox,5020 disable_gpu=self.disable_gpu,5021 incognito=self.incognito,5022 guest_mode=self.guest_mode,5023 devtools=self.devtools,5024 user_data_dir=self.user_data_dir,5025 extension_zip=self.extension_zip,5026 extension_dir=self.extension_dir,5027 is_mobile=self.mobile_emulator,5028 d_width=self.__device_width,5029 d_height=self.__device_height,5030 d_p_r=self.__device_pixel_ratio)5031 self._default_driver = self.driver5032 if self._reuse_session:5033 sb_config.shared_driver = self.driver5034 if self.browser in ["firefox", "ie", "safari"]:5035 # Only Chromium-based browsers have the mobile emulator.5036 # Some actions such as hover-clicking are different on mobile.5037 self.mobile_emulator = False5038 # Configure the test time limit (if used).5039 self.set_time_limit(self.time_limit)5040 # Set the start time for the test (in ms).5041 # Although the pytest clock starts before setUp() begins,5042 # the time-limit clock starts at the end of the setUp() method.5043 sb_config.start_time_ms = int(time.time() * 1000.0)5044 def __set_last_page_screenshot(self):5045 """ self.__last_page_screenshot is only for pytest html report logs5046 self.__last_page_screenshot_png is for all screenshot log files """5047 if not self.__last_page_screenshot and (5048 not self.__last_page_screenshot_png):5049 try:5050 element = self.driver.find_element(5051 by=By.TAG_NAME, value="body")5052 if self.is_pytest and self.report_on:5053 self.__last_page_screenshot_png = (5054 self.driver.get_screenshot_as_png())5055 self.__last_page_screenshot = element.screenshot_as_base645056 else:5057 self.__last_page_screenshot_png = element.screenshot_as_png5058 except Exception:5059 if not self.__last_page_screenshot:5060 if self.is_pytest and self.report_on:5061 try:5062 self.__last_page_screenshot = (5063 self.driver.get_screenshot_as_base64())5064 except Exception:5065 pass5066 if not self.__last_page_screenshot_png:5067 try:5068 self.__last_page_screenshot_png = (5069 self.driver.get_screenshot_as_png())5070 except Exception:5071 pass5072 def __set_last_page_url(self):5073 if not self.__last_page_url:5074 try:5075 self.__last_page_url = log_helper.get_last_page(self.driver)5076 except Exception:5077 self.__last_page_url = None5078 def __set_last_page_source(self):5079 if not self.__last_page_source:5080 try:5081 self.__last_page_source = (5082 log_helper.get_html_source_with_base_href(5083 self.driver, self.driver.page_source))5084 except Exception:5085 self.__last_page_source = None5086 def __insert_test_result(self, state, err):5087 from seleniumbase.core.testcase_manager import TestcaseDataPayload5088 data_payload = TestcaseDataPayload()5089 data_payload.runtime = int(time.time() * 1000) - self.case_start_time5090 data_payload.guid = self.testcase_guid5091 data_payload.execution_guid = self.execution_guid5092 data_payload.state = state5093 if err:5094 import traceback5095 tb_string = traceback.format_exc()5096 if "Message: " in tb_string:5097 data_payload.message = "Message: " + tb_string.split(5098 "Message: ")[-1]5099 elif "Exception: " in tb_string:5100 data_payload.message = tb_string.split("Exception: ")[-1]5101 elif "Error: " in tb_string:5102 data_payload.message = tb_string.split("Error: ")[-1]5103 else:5104 data_payload.message = "Unknown Error: See Stacktrace"5105 self.testcase_manager.update_testcase_data(data_payload)5106 def __add_pytest_html_extra(self):5107 if not self.__added_pytest_html_extra:5108 try:5109 if self.with_selenium:5110 if not self.__last_page_screenshot:5111 self.__set_last_page_screenshot()5112 self.__set_last_page_url()5113 self.__set_last_page_source()5114 if self.report_on:5115 extra_url = {}5116 extra_url['name'] = 'URL'5117 extra_url['format'] = 'url'5118 extra_url['content'] = self.get_current_url()5119 extra_url['mime_type'] = None5120 extra_url['extension'] = None5121 extra_image = {}5122 extra_image['name'] = 'Screenshot'5123 extra_image['format'] = 'image'5124 extra_image['content'] = self.__last_page_screenshot5125 extra_image['mime_type'] = 'image/png'5126 extra_image['extension'] = 'png'5127 self.__added_pytest_html_extra = True5128 self._html_report_extra.append(extra_url)5129 self._html_report_extra.append(extra_image)5130 except Exception:5131 pass5132 def __quit_all_drivers(self):5133 if self._reuse_session and sb_config.shared_driver:5134 if len(self._drivers_list) > 0:5135 sb_config.shared_driver = self._drivers_list[0]5136 self._default_driver = self._drivers_list[0]5137 self.switch_to_default_driver()5138 if len(self._drivers_list) > 1:5139 self._drivers_list = self._drivers_list[1:]5140 else:5141 self._drivers_list = []5142 # Close all open browser windows5143 self._drivers_list.reverse() # Last In, First Out5144 for driver in self._drivers_list:5145 try:5146 driver.quit()5147 except AttributeError:5148 pass5149 except Exception:5150 pass5151 self.driver = None5152 self._default_driver = None5153 self._drivers_list = []5154 def __has_exception(self):5155 has_exception = False5156 if sys.version_info[0] >= 3 and hasattr(self, '_outcome'):5157 if hasattr(self._outcome, 'errors') and self._outcome.errors:5158 has_exception = True5159 else:5160 has_exception = sys.exc_info()[1] is not None5161 return has_exception5162 def __get_test_id(self):5163 test_id = "%s.%s.%s" % (self.__class__.__module__,5164 self.__class__.__name__,5165 self._testMethodName)5166 return test_id5167 def __create_log_path_as_needed(self, test_logpath):5168 if not os.path.exists(test_logpath):5169 try:5170 os.makedirs(test_logpath)5171 except Exception:5172 pass # Only reachable during multi-threaded runs5173 def save_teardown_screenshot(self):5174 """ (Should ONLY be used at the start of custom tearDown() methods.)5175 This method takes a screenshot of the current web page for a5176 failing test (or when running your tests with --save-screenshot).5177 That way your tearDown() method can navigate away from the last5178 page where the test failed, and still get the correct screenshot5179 before performing tearDown() steps on other pages. If this method5180 is not included in your custom tearDown() method, a screenshot5181 will still be taken after the last step of your tearDown(), where5182 you should be calling "super(SubClassOfBaseCase, self).tearDown()"5183 """5184 if self.__has_exception() or self.save_screenshot_after_test:5185 test_id = self.__get_test_id()5186 test_logpath = self.log_path + "/" + test_id5187 self.__create_log_path_as_needed(test_logpath)5188 self.__set_last_page_screenshot()5189 self.__set_last_page_url()5190 self.__set_last_page_source()5191 if self.is_pytest:5192 self.__add_pytest_html_extra()5193 def tearDown(self):5194 """5195 Be careful if a subclass of BaseCase overrides setUp()5196 You'll need to add the following line to the subclass's tearDown():5197 super(SubClassOfBaseCase, self).tearDown()5198 """5199 self.__slow_mode_pause_if_active()5200 has_exception = self.__has_exception()5201 if self.__deferred_assert_failures:5202 print(5203 "\nWhen using self.deferred_assert_*() methods in your tests, "5204 "remember to call self.process_deferred_asserts() afterwards. "5205 "Now calling in tearDown()...\nFailures Detected:")5206 if not has_exception:5207 self.process_deferred_asserts()5208 else:5209 self.process_deferred_asserts(print_only=True)5210 if self.is_pytest:5211 # pytest-specific code5212 test_id = self.__get_test_id()5213 try:5214 with_selenium = self.with_selenium5215 except Exception:5216 sub_class_name = str(5217 self.__class__.__bases__[0]).split('.')[-1].split("'")[0]5218 sub_file_name = str(self.__class__.__bases__[0]).split('.')[-2]5219 sub_file_name = sub_file_name + ".py"5220 class_name = str(self.__class__).split('.')[-1].split("'")[0]5221 file_name = str(self.__class__).split('.')[-2] + ".py"5222 class_name_used = sub_class_name5223 file_name_used = sub_file_name5224 if sub_class_name == "BaseCase":5225 class_name_used = class_name5226 file_name_used = file_name5227 fix_setup = "super(%s, self).setUp()" % class_name_used5228 fix_teardown = "super(%s, self).tearDown()" % class_name_used5229 message = ("You're overriding SeleniumBase's BaseCase setUp() "5230 "method with your own setUp() method, which breaks "5231 "SeleniumBase. You can fix this by going to your "5232 "%s class located in your %s file and adding the "5233 "following line of code AT THE BEGINNING of your "5234 "setUp() method:\n%s\n\nAlso make sure "5235 "you have added the following line of code AT THE "5236 "END of your tearDown() method:\n%s\n"5237 % (class_name_used, file_name_used,5238 fix_setup, fix_teardown))5239 raise Exception(message)5240 if with_selenium:5241 # Save a screenshot if logging is on when an exception occurs5242 if has_exception:5243 self.__add_pytest_html_extra()5244 if self.with_testing_base and not has_exception and (5245 self.save_screenshot_after_test):5246 test_logpath = self.log_path + "/" + test_id5247 self.__create_log_path_as_needed(test_logpath)5248 if not self.__last_page_screenshot_png:5249 self.__set_last_page_screenshot()5250 self.__set_last_page_url()5251 self.__set_last_page_source()5252 log_helper.log_screenshot(5253 test_logpath,5254 self.driver,5255 self.__last_page_screenshot_png)5256 self.__add_pytest_html_extra()5257 if self.with_testing_base and has_exception:5258 test_logpath = self.log_path + "/" + test_id5259 self.__create_log_path_as_needed(test_logpath)5260 if ((not self.with_screen_shots) and (5261 not self.with_basic_test_info) and (5262 not self.with_page_source)):5263 # Log everything if nothing specified (if testing_base)5264 if not self.__last_page_screenshot_png:5265 self.__set_last_page_screenshot()5266 self.__set_last_page_url()5267 self.__set_last_page_source()5268 log_helper.log_screenshot(5269 test_logpath,5270 self.driver,5271 self.__last_page_screenshot_png)5272 log_helper.log_test_failure_data(5273 self, test_logpath, self.driver, self.browser,5274 self.__last_page_url)5275 log_helper.log_page_source(5276 test_logpath, self.driver, self.__last_page_source)5277 else:5278 if self.with_screen_shots:5279 if not self.__last_page_screenshot_png:5280 self.__set_last_page_screenshot()5281 self.__set_last_page_url()5282 self.__set_last_page_source()5283 log_helper.log_screenshot(5284 test_logpath,5285 self.driver,5286 self.__last_page_screenshot_png)5287 if self.with_basic_test_info:5288 log_helper.log_test_failure_data(5289 self, test_logpath, self.driver, self.browser,5290 self.__last_page_url)5291 if self.with_page_source:5292 log_helper.log_page_source(5293 test_logpath, self.driver,5294 self.__last_page_source)5295 # (Pytest) Finally close all open browser windows5296 self.__quit_all_drivers()5297 if self.headless:5298 if self.headless_active:5299 try:5300 self.display.stop()5301 except AttributeError:5302 pass5303 except Exception:5304 pass5305 self.display = None5306 if self.with_db_reporting:5307 if has_exception:5308 self.__insert_test_result(constants.State.ERROR, True)5309 else:5310 self.__insert_test_result(constants.State.PASS, False)5311 runtime = int(time.time() * 1000) - self.execution_start_time5312 self.testcase_manager.update_execution_data(5313 self.execution_guid, runtime)5314 if self.with_s3_logging and has_exception:5315 """ If enabled, upload logs to S3 during test exceptions. """5316 import uuid5317 from seleniumbase.core.s3_manager import S3LoggingBucket5318 s3_bucket = S3LoggingBucket()5319 guid = str(uuid.uuid4().hex)5320 path = "%s/%s" % (self.log_path, test_id)5321 uploaded_files = []5322 for logfile in os.listdir(path):5323 logfile_name = "%s/%s/%s" % (guid,5324 test_id,5325 logfile.split(path)[-1])5326 s3_bucket.upload_file(logfile_name,5327 "%s/%s" % (path, logfile))5328 uploaded_files.append(logfile_name)5329 s3_bucket.save_uploaded_file_names(uploaded_files)5330 index_file = s3_bucket.upload_index_file(test_id, guid)5331 print("\n\n*** Log files uploaded: ***\n%s\n" % index_file)5332 logging.info(5333 "\n\n*** Log files uploaded: ***\n%s\n" % index_file)5334 if self.with_db_reporting:5335 from seleniumbase.core.testcase_manager import (5336 TestcaseDataPayload)5337 from seleniumbase.core.testcase_manager import (5338 TestcaseManager)5339 self.testcase_manager = TestcaseManager(self.database_env)5340 data_payload = TestcaseDataPayload()5341 data_payload.guid = self.testcase_guid5342 data_payload.logURL = index_file5343 self.testcase_manager.update_testcase_log_url(data_payload)5344 else:5345 # (Nosetests)5346 if has_exception:5347 test_id = self.__get_test_id()5348 test_logpath = self.log_path + "/" + test_id5349 self.__create_log_path_as_needed(test_logpath)5350 log_helper.log_test_failure_data(5351 self, test_logpath, self.driver, self.browser,5352 self.__last_page_url)5353 if len(self._drivers_list) > 0:5354 if not self.__last_page_screenshot_png:5355 self.__set_last_page_screenshot()5356 self.__set_last_page_url()5357 self.__set_last_page_source()5358 log_helper.log_screenshot(5359 test_logpath,5360 self.driver,5361 self.__last_page_screenshot_png)5362 log_helper.log_page_source(5363 test_logpath, self.driver, self.__last_page_source)5364 elif self.save_screenshot_after_test:5365 test_id = self.__get_test_id()5366 test_logpath = self.log_path + "/" + test_id5367 self.__create_log_path_as_needed(test_logpath)5368 if not self.__last_page_screenshot_png:5369 self.__set_last_page_screenshot()5370 self.__set_last_page_url()5371 self.__set_last_page_source()5372 log_helper.log_screenshot(5373 test_logpath,5374 self.driver,5375 self.__last_page_screenshot_png)5376 if self.report_on:5377 self._last_page_screenshot = self.__last_page_screenshot_png5378 try:5379 self._last_page_url = self.get_current_url()...
etf_validator.py
Source:etf_validator.py
...27 @staticmethod28 def __fix_url(url):29 return url.rstrip("/") + "/"30 def start_service_test(self, label, test_type, service_endpoint):31 test_type_id = self.__get_test_id(test_type, SERVICE_TEST_IDS)32 body = {33 "label": label,34 "executableTestSuiteIds": [test_type_id],35 "arguments": {"testRunTags": label},36 "testObject": {"resources": {"serviceEndpoint": service_endpoint}},37 }38 return self.__start_test(body)39 def start_service_md_test(self, label, md_test_type, metadata_url):40 test_type_id = self.__get_test_id(md_test_type, METADATA_TEST_IDS)41 body = {42 "label": label,43 "executableTestSuiteIds": [test_type_id],44 "arguments": {"testRunTags": label},45 "testObject": {"resources": {"data": metadata_url}},46 }47 return self.__start_test(body)48 FILTER_RESOURCE_EXCEPTION = "The system has currently insufficient resources to process this request"49 sleep_time = SLEEP_TIME_IN_SECONDS50 retry_count = 051 def __start_test(self, body):52 endpoint = self.__endpoint("TestRuns")53 response = requests.post(endpoint, json=body, headers=self.headers)54 if response.status_code != 201:55 if self.FILTER_RESOURCE_EXCEPTION in str(response.content):56 if self.retry_count > self.max_retry:57 raise EtfValidatorClientException(58 f"ETF validator does not have sufficient resources, tried {self.retry_count} times, we got HTTP status {response.status_code}:\n {response.content}"59 )60 print(f"Test start failed, retry in {self.sleep_time} seconds")61 time.sleep(self.sleep_time)62 self.sleep_time = self.sleep_time * 263 self.retry_count += 164 return self.__start_test(body)65 else:66 raise EtfValidatorClientException(67 f"Something went wrong starting the test, we got HTTP status {response.status_code}:\n {response.content}"68 )69 result = json.loads(response.content)70 return result71 @staticmethod72 def __get_test_id(test_type, test_ids_dictonary):73 if test_type not in test_ids_dictonary:74 raise EtfValidatorClientException(75 f"There is no test id for type `{test_type}`. Available test types are {', '.join(test_ids_dictonary.keys())}."76 )77 return test_ids_dictonary[test_type]78 def is_status_complete(self, test_id):79 endpoint = self.__endpoint(f"TestRuns/{test_id}/progress")80 response = requests.get(endpoint, headers=self.headers)81 if response.status_code != 200:82 raise EtfValidatorClientException(83 f"Something went wrong checking the status of test `{test_id}`, we got HTTP status {response.status_code}:\n {response.content}"84 )85 result = json.loads(response.content)86 return result["val"] == result["max"]...
soft_assert.py
Source:soft_assert.py
...3from typing import Any, Optional4from test_framework.scripts.test_results.expectation_report import ExpectationReport, TestResult5from test_framework.scripts.test_results.test_data import TestData6__FAILED_EXPECTATIONS = {}7def __get_test_id() -> Optional[int]:8 """9 Finds frame of pytest test function that called expect method10 and returns its id to save test results11 :return: current test id12 """13 stack = inspect.stack()14 for call in stack:15 if call[3] == 'runtestprotocol':16 test_id = id(call.frame)17 return test_id18 return None19def expect(expression: Any, test_result: TestResult) -> bool:20 """21 Soft assert realisation22 :param expression: expression to assert23 :param test_result: check message24 :return: boolean value of check25 """26 expression_result = bool(expression)27 if not expression_result:28 test_id = __get_test_id()29 if __FAILED_EXPECTATIONS.get(test_id):30 __FAILED_EXPECTATIONS[test_id].append(test_result)31 else:32 __FAILED_EXPECTATIONS[test_id] = [test_result]33 return expression_result34def assert_expectations(test_data: dict):35 """36 Assert all soft asserts37 :param test_data: test data from yaml file38 """39 test_id = __get_test_id()40 if __FAILED_EXPECTATIONS.get(test_id):41 test_data = TestData(test_data)42 report = __get_report_failures(test_data)43 assert False, report44def __get_report_failures(test_data: TestData) -> str:45 """46 Gets test report with all failed test soft asserts47 :param test_data: test data from yaml file48 :return: str test report with all soft asserts49 """50 test_id = __get_test_id()51 failed_assert_reports = __FAILED_EXPECTATIONS.get(test_id)52 meta_info = inspect.stack()[2][1:4]53 expectation_report = ExpectationReport(test_data.test_name, failed_assert_reports, meta_info)...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!