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
path_of_screenshots
browser
implicit_wait
71    @property
72    def implicit_wait(self):
73        return self._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 get(self, url):
94    def get(self, url):
95        self.browser.get(url)
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 zoom(self):
163    def zoom(self):
164        self.browser.execute_script("document.body.style.zoom='25%'")
def get_url(self):
166    def get_url(self):
167        current_url = self.browser.current_url
168        return current_url
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
def quit(self):
188    def quit(self):
189        self.browser.quit()