def downloadDriver(browserName): managedPaths = ManagedPaths() path = Path(managedPaths.getOrSetDriverPath()) logger.debug( f"Trying to download browserDriver for {browserName} into {path}") path.mkdir(parents=True, exist_ok=True) if browserName == GC.BROWSER_FIREFOX: url, isTarFile = helper.browserHelper_getFirefoxFileUrl() if isTarFile: helper.browserHelper_extractTarDriverFile( url, path, GC.GECKO_DRIVER) else: helper.browserHelper_unzipDriverFile(url, path, GC.GECKO_DRIVER) elif browserName == GC.BROWSER_CHROME: url = helper.browserHelper_getChromeFileUrl() helper.browserHelper_unzipDriverFile(url, path, GC.CHROME_DRIVER) else: logger.critical( f"Please download driver for {browserName} manually into folder /browserDrivers" )
class BrowserDriver: """ The main class for baangt-Elements to interact with a browser. Main Methods: - createNewBrowser: Create one instance of a Browser - findBy*-Methods: e.g. findByAndClick - URL-Methods: To navigate to an URL - handleIframe and handleWindow: To navigate between Windows (=tabs) and Iframes - javaScript: to pass JS directly to the browser - takeScreenshot: yes, that. """ def __init__(self, timing=None, screenshotPath=None, statistics=None): self.iFrame = None self.element = None self.browserData = BrowserDriverData( locatorType=None, locator=None, driver=webDrv.BROWSER_DRIVERS[GC.BROWSER_FIREFOX]) self.slowExecution = False self.slowExecutionTimeoutInSeconds = 1 self.downloadFolder = None self.downloadFolderMonitoring = None self.randomProxy = None self.zoomFactorDesired = None # Desired zoom factor for this page self.browserName = None # Reference to Selenium "HTML" in order to track page changes. It is set on every interaction with the page self.html = None self.managedPaths = ManagedPaths() self.statistics = Statistic() if timing: self.timing = timing self.takeTime = timing.takeTime else: self.timing = Timing() self.takeTime = self.timing.takeTime if not screenshotPath or screenshotPath == "": self.screenshotPath = self.managedPaths.getOrSetScreenshotsPath() else: self.screenshotPath = screenshotPath def sleep(self, seconds): time.sleep(seconds) def createNewBrowser(self, mobileType=None, mobileApp=None, desired_app=None, mobile_app_setting=None, browserName=GC.BROWSER_FIREFOX, desiredCapabilities={}, randomProxy=None, **kwargs): """ Will find the specified executables of the desired browser and start it with the given capabilities. @param browserName: one of GC_BROWSER_*-Browsernames, e.g. GC_BROWSER_FIREFOX @param desiredCapabilities: DICT of desiredCapabilities for this browser @param kwargs: Currently (Jan2020) not used """ self.takeTime("Browser Start") self.randomProxy = randomProxy self.browserName = browserName self.browserProcessID = [] lCurPath = Path(self.managedPaths.getOrSetDriverPath()) if browserName in webDrv.BROWSER_DRIVERS: browserProxy = kwargs.get('browserProxy') browserInstance = kwargs.get('browserInstance', 'unknown') if utils.anything2Boolean(mobileType): self.browserData.driver = self._mobileConnectAppium( browserName, desired_app, mobileApp, mobile_app_setting) elif GC.BROWSER_FIREFOX == browserName: self.browserData.driver = self._browserFirefoxRun( browserName, lCurPath, browserProxy, randomProxy, desiredCapabilities) helper.browserHelper_startBrowsermobProxy( browserName=browserName, browserInstance=browserInstance, browserProxy=browserProxy) self.browserProcessID.append( self.browserData.driver.capabilities.get("moz:processID")) elif GC.BROWSER_CHROME == browserName: self.browserData.driver = self._browserChromeRun( browserName, lCurPath, browserProxy, randomProxy, desiredCapabilities) helper.browserHelper_startBrowsermobProxy( browserName=browserName, browserInstance=browserInstance, browserProxy=browserProxy) try: port = self.browserData.driver.capabilities[ 'goog:chromeOptions']["debuggerAddress"].split(":")[1] fp = os.popen(f"lsof -nP -iTCP:{port} | grep LISTEN") self.browserProcessID.append( int(fp.readlines()[-1].split()[1])) except Exception as ex: logger.info(ex) elif GC.BROWSER_EDGE == browserName: self.browserData.driver = webDrv.BROWSER_DRIVERS[browserName]( executable_path=helper. browserHelper_findBrowserDriverPaths(GC.EDGE_DRIVER)) elif GC.BROWSER_SAFARI == browserName: # SAFARI doesn't provide any options, but desired_capabilities. # Executable_path = the standard safaridriver path. if len(desiredCapabilities) == 0: desiredCapabilities = {} self.browserData.driver = webDrv.BROWSER_DRIVERS[browserName]( desired_capabilities=desiredCapabilities) elif GC.BROWSER_REMOTE == browserName: self.browserData.driver = webDrv.BROWSER_DRIVERS[browserName]( options=webDrv.webdriver_createBrowserOptions( browserName=browserName, desiredCapabilities=desiredCapabilities), command_executor=GC.REMOTE_EXECUTE_URL, desired_capabilities=desiredCapabilities) else: logger.critical( f"Browsername not found: {browserName}. Cancelling test run" ) raise SystemError( f"Browsername not found: {browserName}. Cancelling test run" ) elif GC.BROWSER_REMOTE_V4 == browserName: desired_capabilities, seleniumGridIp, seleniumGridPort = helper.browserHelper_setSettingsRemoteV4( desiredCapabilities) if desired_capabilities['browserName'] == 'firefox': browserExecutable = helper.browserHelper_getBrowserExecutable( GC.BROWSER_FIREFOX) self._downloadDriverCheck(browserExecutable, lCurPath, GC.BROWSER_FIREFOX) elif desired_capabilities['browserName'] == 'chrome': browserExecutable = helper.browserHelper_getBrowserExecutable( GC.BROWSER_CHROME) self._downloadDriverCheck(browserExecutable, lCurPath, GC.BROWSER_CHROME) serverUrl = 'http://' + seleniumGridIp + ':' + seleniumGridPort self.browserData.driver = webDrv.BROWSER_DRIVERS[ GC.BROWSER_REMOTE](command_executor=serverUrl, desired_capabilities=desiredCapabilities) else: raise SystemExit("Browsername unknown") if self.downloadFolder: self.downloadFolderMonitoring = DownloadFolderMonitoring( self.downloadFolder) self.takeTime("Browser Start") def _downloadDriverCheck(self, executable, lCurPath, browserName): lCurPath = lCurPath.joinpath(executable) if not (os.path.isfile(str(lCurPath))): self.downloadDriver(browserName) def _browserChromeRun(self, browserName, lCurPath, browserProxy, randomProxy, desiredCapabilities): executable = helper.browserHelper_getBrowserExecutable(browserName) self._downloadDriverCheck(executable, lCurPath, browserName) lOptions = webDrv.webdriver_createBrowserOptions( browserName=browserName, desiredCapabilities=desiredCapabilities, browserMobProxy=browserProxy, randomProxy=randomProxy) self.downloadFolder = webDrv.getDownloadFolderFromChromeOptions( options=lOptions) return webDrv.BROWSER_DRIVERS[browserName]( chrome_options=lOptions, executable_path=helper.browserHelper_findBrowserDriverPaths( executable), service_log_path=os.path.join(self.managedPaths.getLogfilePath(), 'chromedriver.log')) def _browserFirefoxRun(self, browserName, lCurPath, browserProxy, randomProxy, desiredCapabilities): executable = helper.browserHelper_getBrowserExecutable(browserName) self._downloadDriverCheck(executable, lCurPath, browserName) profile = webDrv.webdriver_setFirefoxProfile(browserProxy, randomProxy) self.downloadFolder = webDrv.getDownloadFolderFromProfile(profile) logger.debug(f"Firefox Profile as follows:{profile.userPrefs}") return webDrv.BROWSER_DRIVERS[browserName]( options=webDrv.webdriver_createBrowserOptions( browserName=browserName, desiredCapabilities=desiredCapabilities), executable_path=helper.browserHelper_findBrowserDriverPaths( executable), firefox_profile=profile, service_log_path=os.path.join(self.managedPaths.getLogfilePath(), 'geckodriver.log')) @staticmethod def _mobileConnectAppium(browserName, desired_app, mobileApp, mobile_app_setting): validSettings = False desired_cap = desired_app if desired_app[GC.MOBILE_PLATFORM_NAME] == "Android": validSettings = True if utils.anything2Boolean(mobileApp): desired_cap['app'] = mobile_app_setting[GC.MOBILE_APP_URL] desired_cap['appPackage'] = mobile_app_setting[ GC.MOBILE_APP_PACKAGE] desired_cap['appActivity'] = mobile_app_setting[ GC.MOBILE_APP_ACTIVITY] else: desired_cap['browserName'] = browserName desired_cap['chromedriverExecutable'] = mobile_app_setting[ GC.MOBILE_APP_BROWSER_PATH] desired_cap['noReset'] = False elif desired_app[GC.MOBILE_PLATFORM_NAME] == "iOS": validSettings = True if utils.anything2Boolean(mobileApp): desired_cap['automationName'] = 'XCUITest' desired_cap['app'] = mobile_app_setting[GC.MOBILE_APP_URL] else: desired_cap['browserName'] = 'safari' if validSettings: return webDrv.BROWSER_DRIVERS[GC.BROWSER_APPIUM]( GC.REMOTE_EXECUTE_URL, desired_cap) else: return None def closeBrowser(self): self.statistics.update_teststep() try: if self.browserData.driver: try: if len(self.browserProcessID) > 0: for bpid in self.browserProcessID: os.kill(bpid, signal.SIGINT) except: pass self.browserData.driver.close() self.browserData.driver.quit() except Exception as ex: logger.info(ex) pass # If the driver is already dead, it's fine. self.browserData.driver = None def refresh(self): self.browserData.driver.execute_script("window.location.reload()") def takeScreenshot(self, screenShotPath=None): driver = self.browserData.driver # Filename must have ".png" inside lFile = str(uuid.uuid4()) + ".png" if screenShotPath: lFile = Path(screenShotPath).joinpath(lFile) else: lFile = Path(self.screenshotPath).joinpath(lFile) try: lFile = str(lFile) driver.save_screenshot(lFile) helper.browserHelper_log(logging.DEBUG, f"Stored Screenshot: {lFile}", self.browserData) except Exception as e: helper.browserHelper_log(logging.INFO, f"Screenshot not possible. Error: {e}", self.browserData) lFile = None return lFile def handleIframe(self, iframe=None): """ Give an IFRAME and it will try to go into. If you're inside an iframe it will go out of the iframe """ self.statistics.update_teststep() if iframe: self.browserData.locatorType = "XPATH" self.browserData.locator = iframe helper.browserHelper_log(logging.DEBUG, "Going into Iframe: ", self.browserData, **{"iframe": iframe}) # frame_to_be_availble_and_switch_to_it doesn't work. mustEnd = time.time() + 30 while time.time() < mustEnd: try: self.browserData.driver.switch_to.default_content() self.iFrame = self.browserData.driver.switch_to.frame( iframe) break except WebDriverException as e: helper.browserHelper_log( logging.DEBUG, f"IFrame {iframe} not there yet - waiting 1 second", self.browserData) time.sleep(1) if time.time() > mustEnd: raise TimeoutError elif self.iFrame: helper.browserHelper_log(logging.DEBUG, f"Leaving Iframe: {self.iFrame}", self.browserData) self.browserData.driver.switch_to.default_content() self.iFrame = None else: # TODO add exception, this code should never be reached pass def handleWindow(self, windowNumber=None, function=None, timeout=20): """ Interations with Windows (=BrowserTabs). @param windowNumber: Number of the windowHandle inside this browser session (0 = startwindow(=Tab), 1=Next window @param function: "CLOSE", "CLOSEALL" """ self.statistics.update_teststep() if function: if "close" == function.lower(): self.browserData.driver.close() self.browserData.driver.switch_to.window( self.browserData.driver.window_handles[0]) elif "closeall" in function.lower(): exceptHandles = function.lower().replace("closeall", "") exceptHandles = exceptHandles.replace("-", "") # WindowHandles based on 0.. Value "let 2 windows open" means to close everything except 0 and 1: exceptHandles = int(exceptHandles.strip()) - 1 try: len(self.browserData.driver.window_handles) except BaseException as e: logger.error( f"Tried to get amount of windows. Threw error {e}. Most probably browser crashed" ) raise Exceptions.baangtTestStepException( f"Tried to get amount of windows. " f"Threw error {e}. Most probably browser crashed") for windowHandle in self.browserData.driver.window_handles[ -1:exceptHandles:-1]: try: self.browserData.driver.switch_to.window(windowHandle) self.browserData.driver.close() except NoSuchWindowException as e: # If the window is already closed, it's fine. Don't do anything pass try: self.browserData.driver.switch_to.window( self.browserData.driver.window_handles[exceptHandles]) except IndexError as e: raise Exceptions.baangtTestStepException( f"Seems like the browser crashed. Main-Window lost") else: # TODO Wrong function, add exception pass else: success = False duration = 0 while not success and duration < timeout: try: self.browserData.driver.switch_to.window( self.browserData.driver.window_handles[windowNumber]) success = True continue except Exception as e: logger.debug( f"Tried to switch to Window {windowNumber} but it's not there yet" ) time.sleep(1) duration += 1 if not success: raise Exceptions.baangtTestStepException( f"Window {windowNumber} doesn't exist after timeout {timeout}" ) def findByAndWaitForValue(self, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=20, optional=False): """ @param id: ID of the element @param css: CSS-Locator @param xpath: XPATH-Locator @param class_name: Class-Name @param iframe: Iframe to use (use only if changed. If you set an iframe before, you don't need to set it again!) @param timeout: Timeout in Seconds before raising an error or returning back (depending on "optional") @param optional: If set to "True" and the operation can not be executed, just a log entry is written but no error raised @return: the text of the element, if element was found """ self.statistics.update_teststep() self.element = None returnValue = None start = time.time() duration = 0 retry = True while retry and duration < timeout: self.element, self.html = self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout / 3, optional=optional) time.sleep(0.5) duration = time.time() - start if self.element: try: if len(self.element.text) > 0: returnValue = self.element.text.strip() elif self.element.tag_name == 'input': # element is of type <input /> returnValue = self.element.get_property( 'value').strip() except Exception as e: logger.debug( f"Exception during findByAndWaitForValue, but continuing {str(e)}, " f"Locator: {self.browserData.locatorType} = {self.browserData.locator}" ) else: logger.info( f"Couldn't find value for element {self.browserData.locatorType}:{self.browserData.locator}" ) if returnValue and len(returnValue.strip()) > 0: return returnValue return returnValue def findByAndSetText(self, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None, timeout=60, optional=False): """ Please see documentation in findBy and __doSomething """ self.element, self.html = self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout, optional=optional) if not self.element: return False return webDrv.webdriver_doSomething(GC.CMD_SETTEXT, self.element, value=value, timeout=timeout, optional=optional, browserData=self.browserData) def slowExecutionToggle(self, newSlowExecutionWaitTimeInSeconds=None): """ SlowExecution can be set in globals or by the teststep. It's intended use is debugging or showcasing a testcases functionality. @param newSlowExecutionWaitTimeInSeconds: Optional. If set, it will change the default value of WaitTime, when SlowExecution is active @return: Returns the state of sloeExecution toggle after toggling was done. """ if newSlowExecutionWaitTimeInSeconds: self.slowExecutionTimeoutInSeconds = newSlowExecutionWaitTimeInSeconds return not self.slowExecution def findByAndSetTextIf(self, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None, timeout=60, optional=False): """ Helper function to not have to write: If <condition>: findByAndSetText(locator) instead use: findByAndSetTextIf(locator, value). If value is evaluated into "True" the Text is set. """ if self._isValidKeyValue(value): return self.findByAndSetText(id=id, css=css, xpath=xpath, class_name=class_name, value=value, iframe=iframe, timeout=timeout, optional=optional) else: return False def findByAndSetTextValidated(self, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None, timeout=60, retries=5): """ This is a method not recommended to be used regularly. Sometimes (especially with Angular Frontends) it gets pretty hard to set a value into a field. Chrome, but also FF will show the value, but the DOM will not have it. Ths Method should be your last ressort. Here we try <retries> time to set a value. Then we read the element again and compare value to what we'd expect. If value is different and we're less than <retries>-Times, we'll try again. """ tries = 0 self.element, self.html = self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout) while self.element.text != value and self.element.get_property( "value") != value and tries < retries: helper.browserHelper_log( logging.DEBUG, f"Verified trying of SetText - iteration {tries} of {retries}", self.browserData) self.findByAndForceText(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, value=value, timeout=timeout) self.element, self.html = self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout) tries += 1 def submit(self): """ Used for forms to call the standard submit-function (similar to pressing "Enter" in Dialogue) @return: """ self.statistics.update_teststep() self.element.submit() def findByAndClick(self, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=20, optional=False): """ Execute a Click on an element identified by it's locator. @return wasSuccessful says, whether the element was found. """ self.element, self.html = self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout, optional=optional) if not self.element: logger.debug("findBy didn't work in findByAndClick") return False else: return webDrv.webdriver_doSomething(GC.CMD_CLICK, self.element, timeout=timeout, optional=optional, browserData=self.browserData) def confirmAlertIfAny(self): self.statistics.update_teststep() try: self.browserData.driver.switch_to().alert().accept() except Exception as e: pass @staticmethod def _isValidKeyValue(value): isValid = False if not value: pass elif len(str(value)) == 0 or str(value) == "0": pass else: isValid = True return isValid def findByAndClickIf(self, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=60, value=None, optional=False): """ Convenience method to not have to write: if <condition>: findByAndClick(locator) instead write: findByAndClickIf(locator, value). If value is evaluated to "True", the click-event is executed. """ if self._isValidKeyValue(value): return self.findByAndClick(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout, optional=optional) else: return False def findByAndForceText(self, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None, timeout=60, optional=False): """ Convenience Method. Please see documentation in findBy and __doSomething. """ self.element, self.html = self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout, optional=optional) if not self.element: return False return webDrv.webdriver_doSomething(GC.CMD_FORCETEXT, self.element, value=value, timeout=timeout, optional=optional, browserData=self.browserData) def findByAndForceViaJS(self, id=None, css=None, xpath: str = None, class_name=None, value=None, iframe=None, timeout=60, optional=False): """ Identifies the object via JS and set's the desired value via JS """ # element, html = self.findBy(id=id ,css=css, xpath=xpath, class_name=class_name, iframe=iframe, # timeout=timeout, optional=optional) # didn't work to give the element to JavaScript-method xpath = xpath.replace('"', "'") xpath = xpath.replace("'", "\\'") lJSText = "\n".join([ f"var zzbaangt = document.evaluate('{xpath}', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);", "if (zzbaangt.snapshotLength > 0) { ", f"zzbaangt[0].value='{value}';", "};", f"" ]) logger.debug(f"Setting element using JS-Text: {lJSText}") # lJSText = f"arguments[0].value='{value}';" --> Didn't work with Element. self.javaScript(lJSText) def setBrowserWindowSize(self, browserWindowSize: str): """ Resized the browser Window to a fixed size :param browserWindowSize: String with Widht/Height or Width;Height or Width,height or width x height If you want also with leading -- :return: False, if browser wasn't reset, size-Dict when resize worked. """ self.statistics.update_teststep() lIntBrowserWindowSize = browserWindowSize.replace("-", "").strip() lIntBrowserWindowSize = lIntBrowserWindowSize.replace(";", "/") lIntBrowserWindowSize = lIntBrowserWindowSize.replace(",", "/") lIntBrowserWindowSize = lIntBrowserWindowSize.replace("x", "/") lIntBrowserWindowSize = lIntBrowserWindowSize.replace("*", "/") validSize = False try: width = int(lIntBrowserWindowSize.split("/")[0]) height = int(lIntBrowserWindowSize.split("/")[1]) except KeyError as e: logger.warning( f"Called with wrong setting: {browserWindowSize}. Won't resize browser " f"Can't determine Width/Height.") except ValueError as e: logger.warning( f"Called with wrong setting: {browserWindowSize}. Won't resize browser " f"Something seems not numeric before conversion: {lIntBrowserWindowSize}" ) try: if width == 0 or height == 0: logger.warning( f"Called with wrong setting: {browserWindowSize}. Won't resize browser. Can't be 0" ) else: validSize = True except: pass if validSize: self.browserData.driver.set_window_size(width, height) size = self.browserData.driver.get_window_size() logger.debug( f"Resized browser window to width want/is: {width}/{size['width']}, " f"height want/is: {height}/{size['height']}") else: size = False return size def findNewFiles(self): """ Returns a list of new files from downloadFolderMonitoring since the last call :return: List of Files since last call """ self.statistics.update_teststep() l_list = self.downloadFolderMonitoring.getNewFiles() return l_list @staticmethod def _setLocator(id, css, xpath, class_name, browserData): browserData.locatorType = None browserData.locator = None if xpath: browserData.locatorType = By.XPATH browserData.locator = xpath elif css: browserData.locatorType = By.CSS_SELECTOR browserData.locator = css elif class_name: browserData.locatorType = By.CLASS_NAME browserData.locator = class_name elif id: browserData.locatorType = By.ID browserData.locator = id return browserData def findBy(self, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=60, loggingOn=True, optional=False): """ chose any single locator (ID, CSS, XPATH, CLASS_NAME) to identify an element within the page. if slowExectuion is set, we'll pause for slowExecutionTimeoutInSeconds. @param id: ID of the element @param css: CSS-Locator @param xpath: XPATH @param class_name: Class-Name @param iframe: Name of an Iframe. Use only, if you didn't set the Iframe previously already! @param timeout: How many seconds shall we try/retry, default = 60 Seconds @param loggingOn: Shall this request be logged? Default = Yes @param optional: If set to true and within Timeout we can't find the element, we just return this info. If set to False (=default), an Exception is raised @return: True if element was located, False if element couldn't be found. """ self.statistics.update_teststep() if self.slowExecution: time.sleep(self.slowExecutionTimeoutInSeconds) if iframe: self.handleIframe(iframe) # Set class variables for potential logging of problems. self.browserData = self._setLocator(id, css, xpath, class_name, self.browserData) if loggingOn: logger.debug( f"Locating Element {self.browserData.locatorType} = {self.browserData.locator}" ) self.element, self.html = webDrv.webdriver_tryAndRetry( self.browserData, timeout=timeout, optional=optional) if not self.element and not optional: raise Exceptions.baangtTestStepException( f"Element {self.browserData.locatorType} = {self.browserData.locator} could not be found " f"within timeout of {timeout}") return self.element, self.html def getURL(self): """ @return: the current URL/URI of the current Tab of the current Browser """ self.statistics.update_teststep() return self.browserData.driver.current_url def findWaitNotVisible(self, css=None, xpath=None, id=None, timeout=90, optional=False): """ You'd use this method when you wait for an element to disappear, for instance Angular Spinner or a popup to disapear before you continue with your script in the main screen. """ self.statistics.update_teststep() logger.debug( f"Waiting for Element to disappear: XPATH:{xpath}, timeout: {timeout}" ) time.sleep(0.5) stillHere = True elapsed = 0 begin = time.time() while stillHere and elapsed < timeout: try: if xpath: self.element = self.browserData.driver.find_element_by_xpath( xpath) elif id: self.element = self.browserData.driver.find_element_by_id( id) elif css: self.element = self.browserData.driver.find_element_by_css_selector( css) time.sleep(0.2) elapsed = time.time() - begin except Exception as e: # Element gone - exit stillHere = False helper.browserHelper_log( logging.DEBUG, f"Element was gone after {format(elapsed, '.2f')} seconds", self.browserData) if not stillHere: raise Exceptions.baangtTestStepException( f"Element still here after {timeout} seconds. Locator: xpath={xpath}, id={id}" ) return stillHere def checkLinks(self): """ For the current page we'll check all links and return result in format <status_code> <link_that_was_checked> :return: List of checked links """ self.statistics.update_teststep() lResult = [] links = self.browserData.driver.find_elements_by_css_selector("a") logger.debug( f"Checking links on page {self.browserData.driver.current_url}") for link in links: lHref = link.get_attribute("href") if not lHref: continue if lHref.startswith("mailto"): pass else: try: r = requests.head(lHref) lResult.append([r.status_code, lHref]) logger.debug(f"Result was: {r.status_code}, {lHref}") except requests.exceptions.InvalidURL as e: lResult.append([ HTTPStatus.INTERNAL_SERVER_ERROR, f"Invalid URL: {lHref}" ]) except requests.exceptions.ConnectionError as e: lResult.append([ HTTPStatus.INTERNAL_SERVER_ERROR, f"HTTP connection error: {lHref}" ]) except requests.exceptions.MissingSchema as e: lResult.append([ HTTPStatus.INTERNAL_SERVER_ERROR, f"Missing Schema - invalid URL: {lHref}" ]) return lResult def waitForElementChangeAfterButtonClick(self, timeout=5): """ Wait for a stale element (in a good way). Stale means, that the object has changed. old element is in self.element old locator is in self.browserData.locatorType and self.browserData.locator :param timeout: :return: """ self.statistics.update_teststep() lOldElement = self.element.id isValid = False lStartOfWaiting = time.time() elapsed = 0 logger.debug("Starting") xpath, css, id = utils.setLocatorFromLocatorType( self.browserData.locatorType, self.browserData.locator) while not isValid and elapsed < timeout: self.element, self.html = self.findBy(xpath=xpath, css=css, id=id, timeout=0.5, optional=True) if not self.element: # Wonderful. Element is gone logger.debug( "Old object is not in the page any longer, save to continue" ) isValid = True if self.element.id != lOldElement: logger.debug("Old element is stale, save to continue") isValid = True time.sleep(0.2) elapsed = time.time() - lStartOfWaiting if not isValid: # TimeOut Return false logger.debug( "Old element equal to new element after timeout. Staleness not detected using this method" ) return isValid def waitForPageLoadAfterButtonClick(self, timeout=5): """ Problem: If the same XPATH/CSS/ID exists on both pages (the current one, where a button is clicked and the next one, where we now want to interact, then it happens very often, that the element is stale (because it was bound to the current page BEFORE the page-load happened. Solution: Wait deliberately until current self.element is stale. :param timout: Yeah, you guessed it. The timeout :return: True = New page loaded, False = The element didn't get stale within timeout """ # Performance in 5 parallel Runs dropped from 06:50 to 07:51. That's 1 Minute slower # 60 Seconds or 10% time lost. # For now let it as it is. If users report that as a problem, revisit the subject and # e.g. find another way to understand, whether we're still on the same page or not. self.statistics.update_teststep() if not self.html: sys.exit( "Something is very wrong! self.html didn't exist when waitForPageLoadAfterButtonClick was called" ) lStartOfWaiting = time.time() elapsed = 0 logger.debug("Starting") while elapsed < timeout: lHTML = self.browserData.driver.find_element_by_tag_name("html") if lHTML != self.html: logger.debug("Page was reloaded") return True time.sleep(0.2) elapsed = time.time() - lStartOfWaiting logger.debug("No Page reload detected by this method") return False # There was no changed HTML def goToUrl(self, url): self.statistics.update_teststep() helper.browserHelper_log(logging.INFO, f'GoToUrl:{url}', self.browserData) try: if self.browserName == GC.BROWSER_FIREFOX: self.browserData.driver.set_context("content") self.browserData.driver.get(url) self.setZoomFactor() except WebDriverException as e: # Use noScreenshot-Parameter as otherwise we'll try on a dead browser to create a screenshot helper.browserHelper_log( logging.ERROR, f"Webpage {url} not reached. Error was: {e}", self.browserData, cbTakeScreenshot=self.takeScreenshot) helper.browserHelper_setProxyError(self.randomProxy) raise Exceptions.baangtTestStepException except Exception as e: # Use noScreenshot-Parameter as otherwise we'll try on a dead browser to create a screenshot helper.browserHelper_log(logging.ERROR, f"Webpage {url} throws error {e}", self.browserData, cbTakeScreenshot=self.takeScreenshot) helper.browserHelper_setProxyError(self.randomProxy) raise Exceptions.baangtTestStepException(url, e) def goBack(self): """ Method to go 1 step back in current tab's browse history @return: """ self.statistics.update_teststep() try: self.javaScript("window.history.go(-1)") except Exception as e: helper.browserHelper_log( logging.WARNING, f"Tried to go back in history, didn't work with error {e}", self.browserData) def javaScript(self, jsText, *args): """Execute a given JavaScript in the current Session""" self.statistics.update_teststep() self.browserData.driver.execute_script(jsText, *args) def _zoomFirefox(self, lZoomKey, lHitKeyTimes): try: lWindow = self.browserData.driver.find_element_by_tag_name("html") # Reset the browser window to 100%: if platform.system().lower() == "darwin": lWindow.send_keys(keys.Keys.META + "0") else: lWindow.send_keys(keys.Keys.CONTROL + "0") # Now set to desired zoom factor: for counter in range(lHitKeyTimes): if platform.system().lower() == "darwin": lWindow.send_keys(keys.Keys.META + lZoomKey) else: lWindow.send_keys(keys.Keys.CONTROL + lZoomKey) logger.debug( f"Adjusted zoom factor of browserwindow to {self.zoomFactorDesired}" ) except Exception as e: logger.debug(f"Tried to adjust zoom factor and failed: {e}") finally: self.browserData.driver.set_context("content") def setZoomFactor(self, lZoomFactor=None): """ Will try to set the browser's zoom factor. :param lZoomFactor: set with a value. Otherwise existing value will be used (if previously set) :return: """ isZoomed = False if self.zoomFactorDesired and lZoomFactor: self.zoomFactorDesired = int(lZoomFactor) if self.browserName == GC.BROWSER_CHROME: logger.critical( f"Zoom in Chrome doesn't work. Continuing without zoom") return False x = self.getURL() if x[0: 5] == "http:": # He loaded already something. Too late for us logger.debug( "CHROME: Got called to change Zoom level - but already URL loaded. Too late." ) else: self.browserData.driver.get("chrome://settings/") self.browserData.driver.execute_script( f"chrome.settingsPrivate.setDefaultZoom({self.zoomFactorDesired/100});" ) logger.debug( f"CHROME: Set default zoom using JS-Method to {self.zoomFactorDesired/100}" ) isZoomed = True elif self.browserName == GC.BROWSER_FIREFOX: self.browserData.driver.set_context("chrome") lZoomKey = "+" if self.zoomFactorDesired > 100 else "-" # E.g. current = 100. Desired = 67%: 100-67 = 33. 33/10 = 3.3 int(3.3) = 3 --> he'll hit 3 times CTRL+"-" lDifference = abs(100 - self.zoomFactorDesired) lHitKeyTimes = int(lDifference / 10) self._zoomFirefox(lZoomKey, lHitKeyTimes) isZoomed = True else: # statement not matched pass else: # statement not matched pass return isZoomed @staticmethod def downloadDriver(browserName): managedPaths = ManagedPaths() path = Path(managedPaths.getOrSetDriverPath()) logger.debug( f"Trying to download browserDriver for {browserName} into {path}") path.mkdir(parents=True, exist_ok=True) if browserName == GC.BROWSER_FIREFOX: url, isTarFile = helper.browserHelper_getFirefoxFileUrl() if isTarFile: helper.browserHelper_extractTarDriverFile( url, path, GC.GECKO_DRIVER) else: helper.browserHelper_unzipDriverFile(url, path, GC.GECKO_DRIVER) elif browserName == GC.BROWSER_CHROME: url = helper.browserHelper_getChromeFileUrl() helper.browserHelper_unzipDriverFile(url, path, GC.CHROME_DRIVER) else: logger.critical( f"Please download driver for {browserName} manually into folder /browserDrivers" )
os.chdir('..') else: assert 0, "Please run the test from baangt/ or baangt/tests/ directory." # Paths current_dir = os.getcwd() managed_path = ManagedPaths() output_dir = Path(managed_path.getOrSetExportPath()) input_dir = Path(current_dir).joinpath("tests/0TestInput/ServiceTestInput") input_file = str( Path(current_dir).joinpath( "tests/0TestInput/ServiceTestInput/simpleAutomationpractice_small.xlsx" )) input_file_parallel = str( Path(input_dir).joinpath("simpleAutomationpractice.xlsx")) drivers_folder = Path(managed_path.getOrSetDriverPath()) isLinux = True if platform.system().upper() == "LINUX" else False # Creates a copy of your environment my_env = os.environ.copy() # Use if you get error 'java' command not found even after it is installed in your system # Replace the path inside "" to the bin folder of java. my_env[ "PATH"] = r"C:\Program Files (x86)\Java\jre1.8.0_251\bin;" + my_env["PATH"] # To monitor new download files folder_monitor = DownloadFolderMonitoring(str(output_dir)) # Program execution functions