availcheck.selenium

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