availcheck.selenium
1# -*- coding: utf-8 -*- 2# Copyright (C) 2024 TU Dresden 3# ralf.klammer@tu-dresden.de 4 5import logging 6import pathlib 7 8from selenium import webdriver 9from selenium.common import exceptions as selenium_exceptions 10from selenium.webdriver.common.by import By 11from selenium.webdriver.support import expected_conditions as EC 12from selenium.webdriver.support.ui import WebDriverWait 13 14log = logging.getLogger(__name__) 15 16 17class SeleniumAgent(object): 18 """ 19 A simple wrapper around the selenium webdriver to make it easier to use 20 in the context of the availability checker. 21 """ 22 23 def __init__( 24 self, 25 path_of_screenshots=None, 26 header_arguments=[], 27 implicit_wait=None, 28 browser_dir=None, 29 user_agent=""" 30 Mozilla/5.0 (X11; Linux x86_64) 31 AppleWebKit/537.36 (KHTML, like Gecko) 32 Chrome/130.0.0.0 Safari/537.36 33 """, 34 window_size=(1920, 1080), 35 ): 36 # set up the chrome options 37 chrome_options = webdriver.ChromeOptions() 38 chrome_options.add_argument("--headless") 39 chrome_options.add_argument("--no-sandbox") 40 chrome_options.add_argument("--disable-dev-shm-usage") 41 if browser_dir is not None: 42 chrome_options.add_argument(f"--user-data-dir={browser_dir}") 43 if user_agent: 44 chrome_options.add_argument("user-agent=%s" % user_agent) 45 46 for argument in header_arguments: 47 log.info("Add additional header arguments: %s" % argument) 48 chrome_options.add_argument(argument) 49 50 self.path_of_screenshots = path_of_screenshots 51 self.browser = webdriver.Chrome(chrome_options) 52 self.browser.set_window_size(*window_size) 53 54 # we need to add a script to the browser to make it work with shadow 55 # dom which is used by React.js 56 # see: https://stackoverflow.com/a/77243136 57 self.browser.execute_cdp_cmd( 58 "Page.addScriptToEvaluateOnNewDocument", 59 { 60 "source": """ 61 Element.prototype._attachShadow = Element.prototype.attachShadow; 62 Element.prototype.attachShadow = function () { 63 return this._attachShadow( { mode: "open" } ); 64 }; 65 """ 66 }, 67 ) 68 self.implicit_wait = implicit_wait 69 70 @property 71 def implicit_wait(self): 72 return self._implicit_wait 73 74 @implicit_wait.setter 75 def implicit_wait(self, value): 76 self._implicit_wait = value 77 if value is not None: 78 self.browser.implicitly_wait(value) 79 80 def get_shadow_root(self, shadow_root_css): 81 # get the shadow root of the element, this is necessary for 82 # elements that are hidden in the shadow dom 83 # see: https://stackoverflow.com/a/77243136 84 log.debug(f"######get_shadow_root: {shadow_root_css}#######") 85 closed_shadow_host = self.find_by_css(shadow_root_css) 86 log.debug(f"closed_shadow_host: {closed_shadow_host}") 87 shadow_root = self.browser.execute_script( 88 "return arguments[0].shadowRoot", closed_shadow_host 89 ) 90 log.debug(f"shadow_root: {shadow_root}") 91 return shadow_root 92 93 def get(self, url): 94 self.browser.get(url) 95 96 def find_by_css(self, css=None, elem=None, as_list=False): 97 if css is None: 98 return None 99 if elem is None: 100 elem = self.browser 101 try: 102 if as_list: 103 return elem.find_elements(By.CSS_SELECTOR, css) 104 else: 105 return elem.find_element(By.CSS_SELECTOR, css) 106 except selenium_exceptions.WebDriverException as e: 107 log.info( 108 f"Unable to to find element: {css}", 109 ) 110 log.debug(e) 111 112 def find_by_text( 113 self, 114 text=None, 115 css=None, 116 elem=None, 117 as_list=False, 118 case_sensitive=False, 119 ): 120 if all([text, css]): 121 if elem is None: 122 elem = self.browser 123 result_as_list = [] 124 for item in self.find_by_css(css, elem=elem, as_list=True): 125 # convert to lower case if case insensitive is requested 126 item_text = item.text if case_sensitive else item.text.lower() 127 text = text if case_sensitive else text.lower() 128 if item_text == text: 129 if as_list: 130 result_as_list.append(item) 131 else: 132 return item 133 134 def wait_until(self, css=None, seconds=10, elem=None, want_bool=False): 135 if css is not None: 136 if elem is None: 137 elem = self.browser 138 try: 139 WebDriverWait(elem, seconds).until( 140 EC.presence_of_element_located((By.CSS_SELECTOR, css)) 141 ) 142 if want_bool: 143 return True 144 else: 145 return self.find_by_css(css=css, elem=elem) 146 except selenium_exceptions.TimeoutException: 147 log.info(f"Element not found: {css}") 148 return False 149 150 def save_screenshot(self, filename): 151 if self.path_of_screenshots: 152 self.path_of_screenshots = self.path_of_screenshots.rstrip("/") 153 filename = filename.lstrip("/") 154 filename = f"{self.path_of_screenshots}/{filename}" 155 156 if not pathlib.Path(filename).parent.exists(): 157 log.warning( 158 f"Destination for screenshots ({pathlib.Path(filename).parent}) does not exist." 159 ) 160 self.browser.save_screenshot(filename) 161 162 def zoom(self): 163 self.browser.execute_script("document.body.style.zoom='25%'") 164 165 def get_url(self): 166 current_url = self.browser.current_url 167 return current_url 168 169 def amount_of_elements(self, css=None, minimum=10): 170 if css is not None: 171 try: 172 count = self.browser.execute_script( 173 f'return document.querySelectorAll("{css}").length' 174 ) 175 if count >= minimum: 176 return True 177 else: 178 return False 179 except Exception as e: 180 log.error(f"Unable to find Element: {css}") 181 log.error(e) 182 return False 183 else: 184 log.error("CSS selector is None") 185 return False 186 187 def quit(self): 188 self.browser.quit()
log =
<Logger availcheck.selenium (WARNING)>
class
SeleniumAgent:
18class SeleniumAgent(object): 19 """ 20 A simple wrapper around the selenium webdriver to make it easier to use 21 in the context of the availability checker. 22 """ 23 24 def __init__( 25 self, 26 path_of_screenshots=None, 27 header_arguments=[], 28 implicit_wait=None, 29 browser_dir=None, 30 user_agent=""" 31 Mozilla/5.0 (X11; Linux x86_64) 32 AppleWebKit/537.36 (KHTML, like Gecko) 33 Chrome/130.0.0.0 Safari/537.36 34 """, 35 window_size=(1920, 1080), 36 ): 37 # set up the chrome options 38 chrome_options = webdriver.ChromeOptions() 39 chrome_options.add_argument("--headless") 40 chrome_options.add_argument("--no-sandbox") 41 chrome_options.add_argument("--disable-dev-shm-usage") 42 if browser_dir is not None: 43 chrome_options.add_argument(f"--user-data-dir={browser_dir}") 44 if user_agent: 45 chrome_options.add_argument("user-agent=%s" % user_agent) 46 47 for argument in header_arguments: 48 log.info("Add additional header arguments: %s" % argument) 49 chrome_options.add_argument(argument) 50 51 self.path_of_screenshots = path_of_screenshots 52 self.browser = webdriver.Chrome(chrome_options) 53 self.browser.set_window_size(*window_size) 54 55 # we need to add a script to the browser to make it work with shadow 56 # dom which is used by React.js 57 # see: https://stackoverflow.com/a/77243136 58 self.browser.execute_cdp_cmd( 59 "Page.addScriptToEvaluateOnNewDocument", 60 { 61 "source": """ 62 Element.prototype._attachShadow = Element.prototype.attachShadow; 63 Element.prototype.attachShadow = function () { 64 return this._attachShadow( { mode: "open" } ); 65 }; 66 """ 67 }, 68 ) 69 self.implicit_wait = implicit_wait 70 71 @property 72 def implicit_wait(self): 73 return self._implicit_wait 74 75 @implicit_wait.setter 76 def implicit_wait(self, value): 77 self._implicit_wait = value 78 if value is not None: 79 self.browser.implicitly_wait(value) 80 81 def get_shadow_root(self, shadow_root_css): 82 # get the shadow root of the element, this is necessary for 83 # elements that are hidden in the shadow dom 84 # see: https://stackoverflow.com/a/77243136 85 log.debug(f"######get_shadow_root: {shadow_root_css}#######") 86 closed_shadow_host = self.find_by_css(shadow_root_css) 87 log.debug(f"closed_shadow_host: {closed_shadow_host}") 88 shadow_root = self.browser.execute_script( 89 "return arguments[0].shadowRoot", closed_shadow_host 90 ) 91 log.debug(f"shadow_root: {shadow_root}") 92 return shadow_root 93 94 def get(self, url): 95 self.browser.get(url) 96 97 def find_by_css(self, css=None, elem=None, as_list=False): 98 if css is None: 99 return None 100 if elem is None: 101 elem = self.browser 102 try: 103 if as_list: 104 return elem.find_elements(By.CSS_SELECTOR, css) 105 else: 106 return elem.find_element(By.CSS_SELECTOR, css) 107 except selenium_exceptions.WebDriverException as e: 108 log.info( 109 f"Unable to to find element: {css}", 110 ) 111 log.debug(e) 112 113 def find_by_text( 114 self, 115 text=None, 116 css=None, 117 elem=None, 118 as_list=False, 119 case_sensitive=False, 120 ): 121 if all([text, css]): 122 if elem is None: 123 elem = self.browser 124 result_as_list = [] 125 for item in self.find_by_css(css, elem=elem, as_list=True): 126 # convert to lower case if case insensitive is requested 127 item_text = item.text if case_sensitive else item.text.lower() 128 text = text if case_sensitive else text.lower() 129 if item_text == text: 130 if as_list: 131 result_as_list.append(item) 132 else: 133 return item 134 135 def wait_until(self, css=None, seconds=10, elem=None, want_bool=False): 136 if css is not None: 137 if elem is None: 138 elem = self.browser 139 try: 140 WebDriverWait(elem, seconds).until( 141 EC.presence_of_element_located((By.CSS_SELECTOR, css)) 142 ) 143 if want_bool: 144 return True 145 else: 146 return self.find_by_css(css=css, elem=elem) 147 except selenium_exceptions.TimeoutException: 148 log.info(f"Element not found: {css}") 149 return False 150 151 def save_screenshot(self, filename): 152 if self.path_of_screenshots: 153 self.path_of_screenshots = self.path_of_screenshots.rstrip("/") 154 filename = filename.lstrip("/") 155 filename = f"{self.path_of_screenshots}/{filename}" 156 157 if not pathlib.Path(filename).parent.exists(): 158 log.warning( 159 f"Destination for screenshots ({pathlib.Path(filename).parent}) does not exist." 160 ) 161 self.browser.save_screenshot(filename) 162 163 def zoom(self): 164 self.browser.execute_script("document.body.style.zoom='25%'") 165 166 def get_url(self): 167 current_url = self.browser.current_url 168 return current_url 169 170 def amount_of_elements(self, css=None, minimum=10): 171 if css is not None: 172 try: 173 count = self.browser.execute_script( 174 f'return document.querySelectorAll("{css}").length' 175 ) 176 if count >= minimum: 177 return True 178 else: 179 return False 180 except Exception as e: 181 log.error(f"Unable to find Element: {css}") 182 log.error(e) 183 return False 184 else: 185 log.error("CSS selector is None") 186 return False 187 188 def quit(self): 189 self.browser.quit()
A simple wrapper around the selenium webdriver to make it easier to use in the context of the availability checker.
SeleniumAgent( path_of_screenshots=None, header_arguments=[], implicit_wait=None, browser_dir=None, user_agent='\n Mozilla/5.0 (X11; Linux x86_64)\n AppleWebKit/537.36 (KHTML, like Gecko)\n Chrome/130.0.0.0 Safari/537.36\n ', window_size=(1920, 1080))
24 def __init__( 25 self, 26 path_of_screenshots=None, 27 header_arguments=[], 28 implicit_wait=None, 29 browser_dir=None, 30 user_agent=""" 31 Mozilla/5.0 (X11; Linux x86_64) 32 AppleWebKit/537.36 (KHTML, like Gecko) 33 Chrome/130.0.0.0 Safari/537.36 34 """, 35 window_size=(1920, 1080), 36 ): 37 # set up the chrome options 38 chrome_options = webdriver.ChromeOptions() 39 chrome_options.add_argument("--headless") 40 chrome_options.add_argument("--no-sandbox") 41 chrome_options.add_argument("--disable-dev-shm-usage") 42 if browser_dir is not None: 43 chrome_options.add_argument(f"--user-data-dir={browser_dir}") 44 if user_agent: 45 chrome_options.add_argument("user-agent=%s" % user_agent) 46 47 for argument in header_arguments: 48 log.info("Add additional header arguments: %s" % argument) 49 chrome_options.add_argument(argument) 50 51 self.path_of_screenshots = path_of_screenshots 52 self.browser = webdriver.Chrome(chrome_options) 53 self.browser.set_window_size(*window_size) 54 55 # we need to add a script to the browser to make it work with shadow 56 # dom which is used by React.js 57 # see: https://stackoverflow.com/a/77243136 58 self.browser.execute_cdp_cmd( 59 "Page.addScriptToEvaluateOnNewDocument", 60 { 61 "source": """ 62 Element.prototype._attachShadow = Element.prototype.attachShadow; 63 Element.prototype.attachShadow = function () { 64 return this._attachShadow( { mode: "open" } ); 65 }; 66 """ 67 }, 68 ) 69 self.implicit_wait = implicit_wait
def
get_shadow_root(self, shadow_root_css):
81 def get_shadow_root(self, shadow_root_css): 82 # get the shadow root of the element, this is necessary for 83 # elements that are hidden in the shadow dom 84 # see: https://stackoverflow.com/a/77243136 85 log.debug(f"######get_shadow_root: {shadow_root_css}#######") 86 closed_shadow_host = self.find_by_css(shadow_root_css) 87 log.debug(f"closed_shadow_host: {closed_shadow_host}") 88 shadow_root = self.browser.execute_script( 89 "return arguments[0].shadowRoot", closed_shadow_host 90 ) 91 log.debug(f"shadow_root: {shadow_root}") 92 return shadow_root
def
find_by_css(self, css=None, elem=None, as_list=False):
97 def find_by_css(self, css=None, elem=None, as_list=False): 98 if css is None: 99 return None 100 if elem is None: 101 elem = self.browser 102 try: 103 if as_list: 104 return elem.find_elements(By.CSS_SELECTOR, css) 105 else: 106 return elem.find_element(By.CSS_SELECTOR, css) 107 except selenium_exceptions.WebDriverException as e: 108 log.info( 109 f"Unable to to find element: {css}", 110 ) 111 log.debug(e)
def
find_by_text( self, text=None, css=None, elem=None, as_list=False, case_sensitive=False):
113 def find_by_text( 114 self, 115 text=None, 116 css=None, 117 elem=None, 118 as_list=False, 119 case_sensitive=False, 120 ): 121 if all([text, css]): 122 if elem is None: 123 elem = self.browser 124 result_as_list = [] 125 for item in self.find_by_css(css, elem=elem, as_list=True): 126 # convert to lower case if case insensitive is requested 127 item_text = item.text if case_sensitive else item.text.lower() 128 text = text if case_sensitive else text.lower() 129 if item_text == text: 130 if as_list: 131 result_as_list.append(item) 132 else: 133 return item
def
wait_until(self, css=None, seconds=10, elem=None, want_bool=False):
135 def wait_until(self, css=None, seconds=10, elem=None, want_bool=False): 136 if css is not None: 137 if elem is None: 138 elem = self.browser 139 try: 140 WebDriverWait(elem, seconds).until( 141 EC.presence_of_element_located((By.CSS_SELECTOR, css)) 142 ) 143 if want_bool: 144 return True 145 else: 146 return self.find_by_css(css=css, elem=elem) 147 except selenium_exceptions.TimeoutException: 148 log.info(f"Element not found: {css}") 149 return False
def
save_screenshot(self, filename):
151 def save_screenshot(self, filename): 152 if self.path_of_screenshots: 153 self.path_of_screenshots = self.path_of_screenshots.rstrip("/") 154 filename = filename.lstrip("/") 155 filename = f"{self.path_of_screenshots}/{filename}" 156 157 if not pathlib.Path(filename).parent.exists(): 158 log.warning( 159 f"Destination for screenshots ({pathlib.Path(filename).parent}) does not exist." 160 ) 161 self.browser.save_screenshot(filename)
def
amount_of_elements(self, css=None, minimum=10):
170 def amount_of_elements(self, css=None, minimum=10): 171 if css is not None: 172 try: 173 count = self.browser.execute_script( 174 f'return document.querySelectorAll("{css}").length' 175 ) 176 if count >= minimum: 177 return True 178 else: 179 return False 180 except Exception as e: 181 log.error(f"Unable to find Element: {css}") 182 log.error(e) 183 return False 184 else: 185 log.error("CSS selector is None") 186 return False