예제 #1
0
 def readConfig(self):
     """ Read existing mail.ini file if not exist then will write one with empty values"""
     self.globalSettings = {}
     config = configparser.ConfigParser()
     managedPaths = ManagedPaths()
     config_file = managedPaths.getOrSetIni().joinpath("mail.ini")
     if not os.path.exists(config_file):
         self.write_config(config_file)
     config.read(config_file)
     self.globalSettings["SendMailTo"] = config["Default"].get("SendMailTo"
                                         ) or self.write_config(config_file, "SendMailTo", "")
     self.globalSettings["NotificationWithAttachment"] = config["Default"].get("NotificationWithAttachment"
                                         ) or self.write_config(config_file, "NotificationWithAttachment", False)
     self.globalSettings["MsWebHook"] = config["Default"].get("MsWebHook"
                                         ) or self.write_config(config_file, "MsWebHook", "")
     self.globalSettings["SlackWebHook"] = config["Default"].get("SlackWebHook"
                                         ) or self.write_config(config_file, "SlackWebHook", "")
     self.globalSettings["TelegramBot"] = config["Default"].get("TelegramBot"
                                                                 ) or self.write_config(config_file, "TelegramBot",
                                                                                        "")
     self.globalSettings["TelegramChannel"] = config["Default"].get("TelegramChannel"
                                                                 ) or self.write_config(config_file, "TelegramChannel",
                                                                                        "")
     self.globalSettings["Confluence-Base-Url"] = config["Default"].get("Confluence-Base-Url") or self.write_config(
         config_file, "Confluence-Base-Url", "")
예제 #2
0
    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
예제 #3
0
    def __init__(self,
                 testRunName,
                 globalSettingsFileNameAndPath=None,
                 testRunDict=None,
                 uuid=uuid4(),
                 executeDirect=True,
                 noCloneXls=False):  # -- API support: testRunDict --
        """
        @param testRunName: The name of the TestRun to be executed.
        @param globalSettingsFileNameAndPath: from where to read the <globals>.json
        """

        # Take over importing parameters:
        self.uuid = uuid
        logger.info(f'Init Testrun, uuid is {self.uuid}')
        self.testRunDict = testRunDict
        self.globalSettingsFileNameAndPath = globalSettingsFileNameAndPath
        self.testRunName, self.testRunFileName = \
            self._sanitizeTestRunNameAndFileName(testRunName, executeDirect)

        # Initialize everything else
        self.apiInstance = None
        self.testType = None
        self.networkInfo = None
        self.results = None
        self.browserFactory = None
        self.kwargs = {}
        self.dataRecords = {}
        self.globalSettings = {}
        self.json_dict = {
        }  # Used to maintain records of RLP_ data which will be used will exporting results
        self.managedPaths = ManagedPaths()
        self.classesForObjects = ClassesForObjects(
        )  # Dynamically loaded classes
        self.timing = Timing()
        self.testRunUtils = TestRunUtils()
        self.testCasesEndDateTimes_1D = []  # refer to single execution
        self.testCasesEndDateTimes_2D = [[]]  # refer to parallel execution
        # New way to export additional Tabs to Excel
        # If you want to export additional data, place a Dict with Tabname + Datafields in additionalExportTabs
        # from anywhere within your custom code base.
        self.additionalExportTabs = {}
        self.statistics = Statistic()
        self.noCloneXls = noCloneXls
        signal.signal(signal.SIGINT, self.exit_signal_handler)
        signal.signal(signal.SIGTERM, self.exit_signal_handler)

        # Initialize other values
        self.timing.takeTime(GC.TIMING_TESTRUN)  # Initialize Testrun Duration

        # Usually the Testrun is called without the parameter executeDirect, meaning it default to "Execute"
        # during Unit-Tests we don't want this behaviour:
        if executeDirect:
            self.executeTestRun()
예제 #4
0
    def browserHelper_findBrowserDriverPaths(filename):

        lCurPath = Path(ManagedPaths().getOrSetDriverPath())
        lCurPath = lCurPath.joinpath(filename)

        logger.debug(f"Path for BrowserDrivers: {lCurPath}")
        return str(lCurPath)
예제 #5
0
 def __init__(self, db_url=None):
     # setup db engine
     if db_url:
         engine = create_engine(db_url)
     else:
         engine = create_engine(DATABASE_URL)
     self.db = sessionmaker(bind=engine)()
     # result query set
     self.query_set = QuerySet()
     # query filters
     self.filters = {}
     # path management
     self.managedPaths = ManagedPaths()
     logger.info(
         f'Initiated with DATABASE_URL: {db_url if db_url else DATABASE_URL}'
     )
예제 #6
0
 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"
         )
예제 #7
0
def test_NestedLoops_and_repeat():
    run_file = str(input_dir.joinpath("CompleteBaangtWebdemo_nested.xlsx"))
    execute(run_file,
            globals_file=Path(input_dir).joinpath("globalsNoBrowser.json"))
    managedPaths = ManagedPaths()
    DATABASE_URL = os.getenv('DATABASE_URL') or 'sqlite:///' + str(
        managedPaths.derivePathForOSAndInstallationOption().joinpath(
            'testrun.db'))
    new_file = folder_monitor.getNewFiles()
    assert new_file
    output_file = output_dir.joinpath(new_file[0][0]).as_posix()
    wb = xlrd.open_workbook(output_file)
    sheet1 = wb.sheet_by_name("Test_textarea2")
    sheet2 = wb.sheet_by_name("Test_textarea2.nested")
    assert sheet1.nrows == 3
    assert sheet2.nrows == 3
    TestRunSheet = wb.sheet_by_name("Summary")
    TestRunUUID = TestRunSheet.cell_value(8, 1)
    engine = create_engine(DATABASE_URL)
    Session = sessionmaker(bind=engine)
    s = Session()
    data = s.query(TestrunLog).get(uuid.UUID(TestRunUUID).bytes)
    assert "textarea2" in json.loads(data.RLPJson)
예제 #8
0
    def browserHelper_setBrowserDownloadDirRandom():
        """
        Generate a new Directory for downloads. This needs to be specific for each browser session,
        so that we can know, which documents were created during this test case.
        :return:
        """
        randomValue = str(uuid.uuid4())
        downloadFolder = str(
            Path(ManagedPaths().getOrSetAttachmentDownloadPath()).joinpath(
                randomValue))
        Path(downloadFolder).mkdir(parents=True, exist_ok=True)

        logger.debug(f"Directory for download {downloadFolder}")
        return downloadFolder
예제 #9
0
class Append2BaseXLS:
    """
    If in the globals of the current testrun the parameter AR2BXLS (Append Results to Base XLS) is set,
    we execute baangt Move-Corresponding module accordingly.
    """
    def __init__(self, testRunInstance, resultsFileName: str = None):
        self.testRunInstance = testRunInstance
        self.resultsFileName = resultsFileName
        self.mp = ManagedPaths()

        self._append2BaseXLS()

    def _append2BaseXLS(self):
        lGlobals = self.testRunInstance.globalSettings
        if not lGlobals.get("AR2BXLS"):
            logger.debug(
                "No request to save to further destinations. Exiting.")
            return

        # Format: fileAndPath,Sheet;fileAndPath,sheet
        fileTuples = []
        if ";" in lGlobals.get("AR2BXLS"):
            files = lGlobals.get("AR2BXLS").split(";")
            for file in files:
                fileTuples.append(self.checkAppend(file))
        else:
            fileTuples.append(self.checkAppend(lGlobals["AR2BXLS"]))

        for fileTuple in fileTuples:
            if not fileTuple:
                logger.critical(
                    "File to append results to not found (see message above")
                break
            logger.info(f"Starting to append results to: {str(fileTuple)}")
            lMover = Mover(source_file_path=self.resultsFileName,
                           source_sheet="Output",
                           destination_file_path=fileTuple[0],
                           destination_sheet=fileTuple[1].strip())
            lMover.move(filters={GC.TESTCASESTATUS: GC.TESTCASESTATUS_SUCCESS},
                        add_missing_columns=False)
            logger.debug(f"Appending results to {str(fileTuple)} finished")

    def checkAppend(self, file):
        lFileAndPath = self.mp.findFileInAnyPath(filename=file.split(",")[0])
        if lFileAndPath:
            return [lFileAndPath, file.split(",")[1]]
        else:
            logger.critical(f"File not found anywhere: {file.split(',')[0]}")
            return None
예제 #10
0
    def __init__(self):
        self.configFile = None
        self.tempConfigFile = None
        self.configFiles = []
        self.testRunFile = None
        self.testRunFiles = []
        self.configContents = {}
        self.window = None
        self.toggleAdditionalFieldsVisible = False

        self.iconFileWindow = b"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"
        self.directory = None
        self.mainWindowPosition = (None, None)
        self.managedPaths = ManagedPaths()

        self.readConfig()

        if not self.directory:
            self.directory = self.managedPaths.derivePathForOSAndInstallationOption(
            )  #.getcwd()

        self.getConfigFilesInDirectory()

        self.startWindow()
예제 #11
0
def test_downloadDriver_NonWindows(getdriver, driverName, browserName):
    from pathlib import Path
    import os
    from baangt.base.PathManagement import ManagedPaths

    # Get Remove Driver File
    path = ManagedPaths().getOrSetDriverPath()
    fileName = Path(path).joinpath(driverName)
    try:
        os.remove(fileName)
    except:
        pass
    assert not os.path.isfile(fileName)

    # create browser
    getdriver.downloadDriver(browserName)
    assert os.path.isfile(fileName)
예제 #12
0
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"
            )
예제 #13
0
파일: Utils.py 프로젝트: Athos1972/baangt
    def findFileAndPathFromPath(fileNameAndPath, basePath=None):
        """
        Tries different approaches to locate a file
        lBasePath = the Path where the script is run

        @param fileNameAndPath: Filename and potentially relative path
        @param basePath (optional): Optional basePath to look at
        @return:
        """
        lFileNameAndPath = fileNameAndPath
        if basePath:
            lBasePath = Path(basePath)
            if "~" in str(lBasePath):
                lBasePath = lBasePath.expanduser()
        else:
            lBasePath = Path(sys.argv[0]).parent  # Works in Windows
            logger.debug(f"Main Path to search for files: {lBasePath}")
            if len(str(lBasePath)) < 3:
                # Most probaby we're in pyinstaller. Let's try to find executable path
                lBasePath = Path(sys.executable).parent
                logger.debug(f"New Main Path to search for files: {lBasePath}")

        if not Path(lFileNameAndPath).exists():
            managedPaths = ManagedPaths()
            root_dir = managedPaths.getOrSetRootPath()
            if "~" in str(lFileNameAndPath):
                lFileNameAndPath = Path(lFileNameAndPath).expanduser()
                if not lFileNameAndPath.exists():
                    raise Exception(f"Can't find file {fileNameAndPath}")
            elif Path(lBasePath).joinpath(fileNameAndPath).exists():
                lFileNameAndPath = Path(lBasePath).joinpath(lFileNameAndPath)
                logger.debug(
                    f"Found file via BasePath {str(lFileNameAndPath)}")
            elif len(Path(lFileNameAndPath).parents) == 0:
                # This is only the filename. Try with current path and a bit up
                if Path(utils.__file__).joinpath(lFileNameAndPath).exists():
                    lFileNameAndPath = Path(
                        utils.__file__).joinpath(lFileNameAndPath)
                elif Path(utils.__file__).parent.joinpath(
                        lFileNameAndPath).exists():
                    lFileNameAndPath = Path(
                        utils.__file__).parent.joinpath(lFileNameAndPath)
                elif Path(utils.__file__).parent.parent.joinpath(
                        lFileNameAndPath).exists():
                    lFileNameAndPath = Path(
                        utils.__file__).parent.parent.joinpath(
                            lFileNameAndPath)
                elif Path(root_dir).joinpath(lFileNameAndPath).exists():
                    lFileNameAndPath = Path(root_dir).joinpath(
                        lFileNameAndPath)
                elif Path(root_dir).joinpath("baangt").joinpath(
                        lFileNameAndPath).exists():
                    lFileNameAndPath = Path(root_dir).joinpath(
                        "baangt").joinpath(lFileNameAndPath)
                elif Path(root_dir).joinpath("baangt").joinpath(
                        "base").joinpath(lFileNameAndPath).exists():
                    lFileNameAndPath = Path(root_dir).joinpath(
                        "baangt").joinpath("base").joinpath(lFileNameAndPath)
                else:
                    raise Exception(f"Can't find file {fileNameAndPath}")
            else:
                raise Exception(f"Can't find file {fileNameAndPath}")
        else:
            lFileNameAndPath = Path(lFileNameAndPath)

        return str(lFileNameAndPath.absolute())
예제 #14
0
파일: Cleanup.py 프로젝트: Athos1972/baangt
 def __init__(self, days: float = 31):
     self.managedPaths = ManagedPaths()
     self.threshold = days
예제 #15
0
파일: Cleanup.py 프로젝트: Athos1972/baangt
class Cleanup:
    def __init__(self, days: float = 31):
        self.managedPaths = ManagedPaths()
        self.threshold = days

    def clean_logs(self):
        logger.info(f"Removing logs older than {str(self.threshold)} days")
        logs_dir = self.managedPaths.getLogfilePath()
        files = Path(logs_dir).glob('**/*')
        removed = []
        for file in files:
            timedelta = datetime.now() - datetime.fromtimestamp(DownloadFolderMonitoring.creation_date(file))
            if timedelta.total_seconds()/86400 > self.threshold:
                try:
                    os.remove(str(file))
                    removed.append(file)
                except:
                    logger.debug(f"Cannot remove {str(file)}")
                    continue
                logger.debug(f"{str(file)} deleted")
        if len(removed) == 0:
            logger.info(f"No log file older than {str(self.threshold)} days found")
        else:
            logger.info(f"{str(len(removed))} Log file deleted")

    def clean_screenshots(self):
        logger.info(f"Removing screenshots older than {str(self.threshold)} days")
        ss_dir = self.managedPaths.getOrSetScreenshotsPath()
        files = Path(ss_dir).glob('**/*')
        removed = []
        for file in files:
            timedelta = datetime.now() - datetime.fromtimestamp(DownloadFolderMonitoring.creation_date(file))
            if timedelta.total_seconds()/86400 > self.threshold:
                try:
                    os.remove(str(file))
                    removed.append(file)
                except:
                    logger.debug(f"Cannot remove {str(file)}")
                    continue
                logger.debug(f"{str(file)} deleted")
        if len(removed) == 0:
            logger.info(f"No Screenshot older than {str(self.threshold)} days found")
        else:
            logger.info(f"{str(len(removed))} Screenshot deleted")

    def clean_downloads(self):
        logger.info(f"Removing downloads older than {str(self.threshold)} days")
        downloads_dir = self.managedPaths.getOrSetAttachmentDownloadPath()
        files = Path(downloads_dir).glob('**/*')
        removed = []
        for file in files:
            if file.is_file():
                timedelta = datetime.now() - datetime.fromtimestamp(DownloadFolderMonitoring.creation_date(file))
                if timedelta.total_seconds()/86400 > self.threshold:
                    try:
                        os.remove(str(file))
                        removed.append(file)
                    except:
                        logger.debug(f"Cannot remove {str(file)}")
                        continue
                    logger.debug(f"{str(file)} deleted")
        if len(removed) == 0:
            logger.info(f"No downloads older than {str(self.threshold)} days found")
        else:
            logger.info(f"{str(len(removed))} downloads deleted")
        sleep(1)
        logger.info("Removing empty folders inside download folder.")
        files = Path(downloads_dir).glob('**/*')
        removed = []
        for file in files:
            if file.is_dir():
                if len(os.listdir(str(file))) == 0:
                    try:
                        os.rmdir(str(file))
                        removed.append(file)
                    except:
                        logger.debug(f"Cannot remove folder {str(file)}")
                        continue
                    logger.debug(f"{str(file)} folder deleted")
        if len(removed) == 0:
            logger.info(f"No empty folder found inside downloads")
        else:
            logger.info(f"{str(len(removed))} empty folder deleted")

    def clean_reports(self):
        #
        # cleanup reports
        #

        logger.info(f"Removing reports older than {str(self.threshold)} days")
        reports_dir = self.managedPaths.getOrSetReportPath()
        files = Path(reports_dir).glob('**/*')
        removed = []
        for file in files:
            timedelta = datetime.now() - datetime.fromtimestamp(DownloadFolderMonitoring.creation_date(file))
            if timedelta.total_seconds()/86400 > self.threshold:
                try:
                    os.remove(str(file))
                    removed.append(file)
                except:
                    logger.debug(f"Cannot remove {str(file)}")
                    continue
                logger.debug(f"{str(file)} deleted")
        if len(removed) == 0:
            logger.info(f"No report older than {str(self.threshold)} days found")
        else:
            logger.info(f"{str(len(removed))} reports deleted")

    def clean_all(self):
        self.clean_logs()
        self.clean_screenshots()
        self.clean_downloads()
        self.clean_reports()
예제 #16
0
from sqlalchemy import Column, String, Integer, DateTime, Boolean, Table, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
import os, re, uuid
from datetime import timedelta
from baangt.base.PathManagement import ManagedPaths
from baangt.base.GlobalConstants import EXECUTION_STAGE, TIMING_DURATION, TESTCASESTATUS

managedPaths = ManagedPaths()
DATABASE_URL = os.getenv('DATABASE_URL') or 'sqlite:///' + str(
    managedPaths.derivePathForOSAndInstallationOption().joinpath('testrun.db'))

# handle sqlalchemy dialects
dialect = re.match(r'\w+', DATABASE_URL)
if dialect.group() == 'mysql':
    from sqlalchemy.dialects.mysql import BINARY as Binary
else:
    from sqlalchemy import LargeBinary as Binary

engine = create_engine(DATABASE_URL)
base = declarative_base()


#
# UUID as bytes
#
def uuidAsBytes():
    return uuid.uuid4().bytes

예제 #17
0
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import uuid
from baangt.base.DataBaseORM import TestrunLog
import json

# Will Check for the current directory and change it to baangt root dir
if not os.path.basename(os.getcwd()) == "baangt":
    if os.path.basename(os.path.dirname(os.getcwd())) == "baangt":
        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
예제 #18
0
from baangt.base.TestRun.hookImpls import TestRunHookImpl
from baangt.base.BrowserHandling.hookImpls import BrowserDriverHookImpl
from baangt.base.Timing.hookImpls import TimingHookImpl
from baangt.base.ExportResults.hookImpls import \
    (ExportResultsHookImpl, ExcelSheetHelperFunctionsHookImpl, ExportTimingHookImpl)

plugin_manager.register(plugin=TestRunHookImpl())
plugin_manager.register(plugin=BrowserDriverHookImpl())
plugin_manager.register(plugin=TimingHookImpl())
plugin_manager.register(plugin=ExportResultsHookImpl())
plugin_manager.register(plugin=ExcelSheetHelperFunctionsHookImpl())
plugin_manager.register(plugin=ExportTimingHookImpl())

# fixme: Parameter für Logfile should include stage and browser()
managedPaths = ManagedPaths()
logFilename: pathlib.Path = Path(managedPaths.getLogfilePath())
pathlib.Path(logFilename).mkdir(parents=True, exist_ok=True)
logFilename = logFilename.joinpath(datetime.now().strftime("%Y%m%d_%H%M%S") +
                                   '.log')

print(f"Logfile used: {logFilename}")

# Bit more advanced logging
logger = logging.getLogger('pyC')
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fileHandler = logging.FileHandler(logFilename, encoding="UTF-8")
fileHandler.setLevel(level=logging.DEBUG)
# create console handler with a higher log level
channelHandler = logging.StreamHandler()
예제 #19
0
파일: reports.py 프로젝트: Athos1972/baangt
 def __init__(self):
     self.created = datetime.now()
     self.managedPaths = ManagedPaths()
     self.generate()
예제 #20
0
class UI:
    """
    Provides a simple UI for Testrun-Execution
    """
    def __init__(self):
        self.configFile = None
        self.tempConfigFile = None
        self.configFiles = []
        self.testRunFile = None
        self.testRunFiles = []
        self.configContents = {}
        self.window = None
        self.toggleAdditionalFieldsVisible = False

        self.iconFileWindow = b"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"
        self.directory = None
        self.mainWindowPosition = (None, None)
        self.managedPaths = ManagedPaths()

        self.readConfig()

        if not self.directory:
            self.directory = self.managedPaths.derivePathForOSAndInstallationOption(
            )  #.getcwd()

        self.getConfigFilesInDirectory()

        self.startWindow()

    # def getLayout(self):
    #
    #     lMenu = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']],
    #              ['&Katalon Studio', ['Paste', ['Special', 'Normal', ], 'Undo'], ],
    #              ['&Help', '&About...'], ]
    #     # lMenu doesnt' work. It shows up in the Mac-Menu, but none of the buttons work. Even the
    #     # Mac-Button stops working until the window is closed
    #
    #     lColumnLeft = [[sg.Text("")],
    #                    [sg.Text("Path", size=(10,1), font="Helvetica 10 bold"),
    #                     sg.In(key="-directory-", size=(31,1), enable_events=True, default_text=self.directory, font="Helvetica 12"),
    #                     sg.FolderBrowse(initial_folder=os.getcwd(), font="Helvetica 10", enable_events=True, size=(10,1))]]
    #
    #     lColumnLeft.append([sg.Text("TestRun", size=(10, 1), font="Helvetica 10 bold"),
    #                     sg.InputCombo(self.testRunFiles, key="testRunFile", default_value=self.testRunFile,
    #                                   size=(29, 1), font="Helvetica 12"),
    #                     sg.Button("Execute", size=(10, 1), font="Helvetica 10", button_color=('white', 'darkgreen'))])
    #
    #     lColumnLeft.append([sg.Text("Settings", size=(10, 1), font="Helvetica 10 bold"),
    #                         sg.InputCombo(self.configFiles, key="configFile", default_value=self.configFile,
    #                                       enable_events=True, size=(29, 1), font="Helvetica 12"),
    #                         sg.Button("Details", size=(10, 1), font="Helvetica 10", key="ToggleFields")])
    #
    #     # Baangt Logo
    #     lPathLogo = Path(__file__).parent.parent.parent.joinpath("ressources").joinpath("baangtLogo2020Small.png")
    #     # when in pip-Package, this doesn't work.
    #     if not lPathLogo.exists():
    #         lPathLogo = Path(__file__).parent.parent.joinpath("ressources").joinpath("baangtLogo2020Small.png")
    #
    #     lColumnRight = [[sg.Image(filename=lPathLogo)]]
    #
    #     lLayout = [[sg.Menu(lMenu)],
    #                [sg.Col(lColumnLeft, pad=(0,0)),
    #                 sg.Col(lColumnRight, pad=(0,0), justification="right")]]
    #
    #     # Show the button to provide more details
    #     ttip_Recorder = 'Will start the Katalon Recorder Importer'
    #
    #     if self.configContents:
    #         lLayout.append([sg.Text(f"SETTINGS IN {self.configFile}", font="Helvetica 8 bold",
    #                                 visible=self.toggleAdditionalFieldsVisible)])
    #         for key, value in self.configContents.items():
    #             lLayout.append([sg.In(key, key="-attrib-" + key, size=(28,1), visible=self.toggleAdditionalFieldsVisible),
    #                             sg.In(key="-val-"+key, size=(34,1), default_text=value, visible=self.toggleAdditionalFieldsVisible)])
    #         for i in range(0,4):
    #             lLayout.append([sg.In(key=f"-newField-{i}", size=(28,1), visible=self.toggleAdditionalFieldsVisible),
    #                             sg.In(key=f"-newValue-{i}", size=(34,1), visible=self.toggleAdditionalFieldsVisible)])
    #
    #         lLayout.append([sg.Button('Save', size=(13,1)),
    #                         sg.Button("SaveAs", size=(13,1)),
    #                         sg.Button("Import Recorder", size=(13,1), tooltip=ttip_Recorder),
    #                         sg.Button("Import Katalon", size=(13,1), disabled=True),])
    #         lLayout.append([sg.T()])
    #
    #     return lLayout
    #
    # def startWindow(self):
    #     sg.theme("LightBrown1")
    #
    #     self.window = sg.Window("baangt Interactive Starter", layout=self.getLayout(), location=self.mainWindowPosition,
    #                             icon=self.iconFileWindow)  # size=(750,400)
    #     lWindow = self.window
    #     lWindow.finalize()
    #
    #     while True:
    #         lEvent, lValues = lWindow.read(timeout=200)
    #         if lEvent == "Exit":
    #             break
    #
    #         if not lEvent:       # Window was closed by "X"-Button
    #             break
    #
    #         self.mainWindowPosition = lWindow.CurrentLocation()
    #
    #         if lValues.get('-directory-') != self.directory:
    #             self.directory = lValues.get("-directory-")
    #             self.getConfigFilesInDirectory()
    #
    #         if lValues["testRunFile"]:
    #             self.testRunFile = lValues["testRunFile"]
    #
    #         if lValues["configFile"]:
    #             if lValues["configFile"] != self.configFile:
    #                 self.configFile = lValues['configFile']
    #                 self.readContentsOfGlobals()
    #                 lWindow = self.reopenWindow(lWindow)
    #
    #         if lEvent == 'Save':
    #             lWindow = self.saveConfigFileProcedure(lWindow, lValues)
    #
    #         if lEvent == 'SaveAs':
    #             self.configFile = sg.popup_get_text("New Name of Configfile:")
    #             if len(self.configFile) > 0:
    #                 lWindow = self.saveConfigFileProcedure(lWindow, lValues)
    #
    #         if lEvent == "Execute":
    #             self.modifyValuesOfConfigFileInMemory(lValues=lValues)
    #             self.runTestRun()
    #
    #         if lEvent == "Import Recorder":
    #             lRecorder = ImportKatalonRecorder(self.directory)
    #             self.getConfigFilesInDirectory()   # Refresh display
    #             lWindow['testRunFile'].update(values=self.testRunFiles,
    #                                           value=lRecorder.fileNameExport)
    #
    #         if lEvent == 'ToggleFields':
    #             lWindow = self.toggleAdditionalFieldsExecute(lWindow=lWindow)
    #
    #     self.saveInteractiveGuiConfig()
    #     lWindow.close()

    def saveConfigFileProcedure(self, lWindow, lValues):
        # receive updated fields and values to store in JSON-File
        self.modifyValuesOfConfigFileInMemory(lValues)
        self.saveContentsOfConfigFile()
        lWindow = self.reopenWindow(lWindow)
        return lWindow

    # def reopenWindow(self, lWindow):
    #     lSize = lWindow.Size
    #     lPosition = lWindow.CurrentLocation()
    #     lWindow.close()
    #     self.window = sg.Window("baangt Interactive Starter", layout=self.getLayout(),
    #                             location=lPosition, icon=self.iconFileWindow)
    #     lWindow = self.window
    #     lWindow.finalize()
    #     return lWindow
    #
    # def toggleAdditionalFieldsExecute(self, lWindow):
    #     if self.toggleAdditionalFieldsVisible:
    #         self.toggleAdditionalFieldsVisible = False
    #     else:
    #         self.toggleAdditionalFieldsVisible = True
    #
    #     lWindow = self.reopenWindow(lWindow=lWindow)
    #     return lWindow
    #
    # def runTestRun(self):
    #     if not self.configFile:
    #         sg.popup_cancel("No Config File selected - can't run")
    #         return
    #     if not self.testRunFile:
    #         sg.popup_cancel("No Testrun File selected - can't run")
    #         return
    #     runCmd = self._getRunCommand()
    #     if self.configContents.get("TX.DEBUG"):
    #         from baangt.base.TestRun.TestRun import TestRun
    #
    #         lTestRun = TestRun(f"{Path(self.directory).joinpath(self.testRunFile)}",
    #                            globalSettingsFileNameAndPath=f'{Path(self.directory).joinpath(self.tempConfigFile)}')
    #
    #     else:
    #         logger.info(f"Running command: {runCmd}")
    #         p = subprocess.run(runCmd, shell=True, close_fds=True)
    #
    #
    #     sg.popup_ok("Testrun finished")
    #     # Remove temporary Configfile, that was created only for this run:
    #     try:
    #         os.remove(Path(self.directory).joinpath(self.tempConfigFile))
    #     except Exception as e:
    #         logger.warning(f"Tried to remove temporary file but seems to be not there: "
    #                        f"{self.directory}/{self.tempConfigFile}")

    def _getRunCommand(self):
        """
        If bundled (e.g. in pyinstaller), then the executable is already sys.executable,
        otherwise we need to concatenate executable and Script-Name before we can start
        a subprocess.

        @return: Full path and filename to call Subprocess
        """
        lStart = sys.executable
        if "python" in sys.executable.lower():
            if len(Path(sys.argv[0]).parents) > 1:
                # This is a system where the path the the script is given in sys.argv[0]
                lStart = lStart + f" {sys.argv[0]}"
            else:
                # this is a system where we need to join os.getcwd() and sys.argv[0] because the path is not given in sys.argv[0]
                lStart = lStart + f" {Path(os.getcwd()).joinpath(sys.argv[0])}"

        self.__makeTempConfigFile()

        return f"{lStart} " \
               f"--run='{Path(self.directory).joinpath(self.testRunFile)}' " \
               f"--globals='{Path(self.directory).joinpath(self.tempConfigFile)}'"

    def __makeTempConfigFile(self):
        """
        Add parameters to the Config-File for this Testrun and save the file under a temporary name
        """
        self.configContents[GC.PATH_ROOT] = self.directory

        # self.configContents[GC.PATH_SCREENSHOTS] = str(Path(self.directory).joinpath(GC.PATH_SCREENSHOTS))
        # self.configContents[GC.PATH_EXPORT] = str(Path(self.directory).joinpath(GC.PATH_EXPORT))
        # self.configContents[GC.PATH_IMPORT] = str(Path(self.directory).joinpath(GC.PATH_IMPORT))
        self.tempConfigFile = UI.__makeRandomFileName()
        self.saveContentsOfConfigFile(self.tempConfigFile)

    @staticmethod
    def __makeRandomFileName():
        return "globals_" + utils.datetime_return() + ".json"

    def _getPythonExecutable(self):
        if hasattr(sys, '_MEIPASS'):
            # We're in an executable created by pyinstaller
            return sys.executable

        if platform.system().lower() == 'linux' or platform.system().lower(
        ) == 'darwin':
            lPython = 'python3'
        elif platform.system().lower() == 'windows':
            lPython = 'python'
        else:
            sys.exit(
                f"Unknown platform to run on: {platform.system().lower()}")
        return lPython

    def getConfigFilesInDirectory(self):
        """
        Reads JSON-Files from directory given in self.directory and builds 2 lists (Testrunfiles and ConfiFiles)
        """
        self.configFiles = []
        self.testRunFiles = []
        lcwd = os.getcwd()
        os.chdir(self.directory)
        fileList = glob.glob("*.json")
        fileList.extend(glob.glob("*.xlsx"))
        if not platform.system().lower() == 'windows':
            # On MAC and LINUX there may be also upper/lower-Case versions
            fileList.extend(glob.glob("*.JSON"))
            fileList.extend(glob.glob("*.XLSX"))
        for file in fileList:
            if file[0:4].lower(
            ) == 'glob':  # Global Settings for Testrun must start with global_*
                self.configFiles.append(file)
            else:
                self.testRunFiles.append(file)
            pass

        self.configFiles = sorted(self.configFiles)
        self.testRunFiles = sorted(self.testRunFiles)

        lWindow = self.window
        if lWindow:
            lWindow['configFile'].update(
                values=self.configFiles,
                value=self.configFile if self.configFile else "")
            lWindow['testRunFile'].update(
                values=self.testRunFiles,
                value=self.testRunFile if self.testRunFile else "")

        os.chdir(lcwd)

    def readContentsOfGlobals(self):
        self.configContents = utils.openJson(
            Path(self.directory).joinpath(self.configFile))
        # Prepare some default values, if not filled:
        if not self.configContents.get("TC." + GC.DATABASE_LINES):
            self.configContents["TC." + GC.DATABASE_LINES] = ""
        if not self.configContents.get("TC." + GC.EXECUTION_DONTCLOSEBROWSER):
            self.configContents["TC." + GC.EXECUTION_DONTCLOSEBROWSER] = ""
        if not self.configContents.get("TC." + GC.EXECUTION_SLOW):
            self.configContents["TC." + GC.EXECUTION_SLOW] = ""

    def saveContentsOfConfigFile(self, lFileName=None):
        if not lFileName:
            lFileName = self.configFile

        with open(str(Path(self.directory).joinpath(lFileName)),
                  'w') as outfile:
            json.dump(self.configContents, outfile, indent=4)

    def modifyValuesOfConfigFileInMemory(self, lValues):
        for key, value in lValues.items():
            if not isinstance(key, str):
                continue

            if '-attrib-' in key:
                # Existing field - update value from value
                lSearchKey = key.replace("-attrib-", "")
                if lSearchKey != value:
                    # an existing variable was changed to a new name. Delete the old one:
                    self.configContents.pop(lSearchKey)
                    lSearchKey = value
                    lSearchVal = lValues['-val-' + key.replace("-attrib-", "")]
                else:
                    lSearchVal = lValues['-val-' + lSearchKey]
                if len(lSearchKey) > 0:
                    self.configContents[lSearchKey] = lSearchVal
            elif '-newField-' in key:
                # New field needs to be added to memory:
                lSearchKey = value  # the new fieldname
                if len(lSearchKey) > 0:
                    lSearchVal = lValues['-newValue-' + key[-1]]
                    self.configContents[lSearchKey] = lSearchVal
            elif '-val-' in key or '-newValue-':
                pass  # Values have been used already above
            else:
                logger.critical(
                    f"Program error. Received something with key {key}, value {value} and no "
                    f"idea what to do with it")

    def saveInteractiveGuiConfig(self):
        config = configparser.ConfigParser()
        config["DEFAULT"] = {
            "path": self.directory,
            "testrun": UI.__nonEmptyString(self.testRunFile),
            "globals": UI.__nonEmptyString(self.configFile),
            "position": self.mainWindowPosition
        }
        with open(self.managedPaths.getOrSetIni().joinpath("baangt.ini"),
                  "w") as configFile:
            config.write(configFile)

    @staticmethod
    def __nonEmptyString(stringIn):
        if stringIn:
            return stringIn
        else:
            return ""

    def readConfig(self):
        config = configparser.ConfigParser()
        try:
            config.read(self.managedPaths.getOrSetIni().joinpath("baangt.ini"))
            self.directory = config["DEFAULT"]['path']
            self.testRunFile = config["DEFAULT"]['testrun']
            self.configFile = config["DEFAULT"]['globals']
            self.mainWindowPosition = UI.__convert_configPosition2Tuple(
                config["DEFAULT"]['position'])
            # Value in there is now a string of "(x-coordinates, y-coordinates)". We need a tuple (int:x, int:y)

            self.readContentsOfGlobals()
        except Exception as e:
            # if baangt.ini is not there. Default the directory to /examples.
            if Path(os.getcwd()).joinpath("examples").exists():
                self.directory = Path(os.getcwd()).joinpath("examples")
                self.testRunFile = 'simpleAutomationpractice.xlsx'
                self.configFile = 'globals.json'
                self.mainWindowPosition = (20, 30)
                self.readContentsOfGlobals()

    @staticmethod
    def __convert_configPosition2Tuple(inString):
        x = int(inString.split(",")[0].lstrip("("))
        y = int(inString.split(",")[1].rstrip(")"))

        return (x, y)
예제 #21
0
class ResultsBrowser:
    def __init__(self, db_url=None):
        # setup db engine
        if db_url:
            engine = create_engine(db_url)
        else:
            engine = create_engine(DATABASE_URL)
        self.db = sessionmaker(bind=engine)()
        # result query set
        self.query_set = QuerySet()
        # query filters
        self.filters = {}
        # path management
        self.managedPaths = ManagedPaths()
        logger.info(
            f'Initiated with DATABASE_URL: {db_url if db_url else DATABASE_URL}'
        )

    def __del__(self):
        self.db.close()

    def name_list(self):
        names = self.db.query(TestrunLog.testrunName).group_by(
            TestrunLog.testrunName).order_by(TestrunLog.testrunName)
        return [x[0] for x in names]

    def stage_list(self):
        stages = self.db.query(GlobalAttribute.value).filter_by(name=GC.EXECUTION_STAGE)\
            .group_by(GlobalAttribute.value).order_by(GlobalAttribute.value)
        return [x[0] for x in stages]

    def globals_names(self):
        names = self.db.query(GlobalAttribute.name).group_by(
            GlobalAttribute.name).order_by(GlobalAttribute.name)
        return [x[0] for x in names]

    def globals_values(self, name):
        values = self.db.query(
            GlobalAttribute.value).filter_by(name=name).group_by(
                GlobalAttribute.value).order_by(GlobalAttribute.value)
        return [x[0] for x in values]

    def query(self,
              name=None,
              stage=None,
              start_date=None,
              end_date=None,
              global_settings={}):
        #
        # get TestrunLogs from db
        #

        # store filters
        self.filters = {
            'name': name,
            'stage': stage,
            'start_date': start_date,
            'end_date': end_date,
            'globals': global_settings,
        }

        # get records
        logger.info(
            f'Quering: name={name}, stage={stage}, dates=[{start_date} - {end_date}], globals={global_settings}'
        )
        records = self.db.query(TestrunLog).order_by(TestrunLog.startTime)

        # filter by name
        if name:
            records = records.filter_by(testrunName=name)

        # filter bay dates
        if start_date:
            records = records.filter(TestrunLog.startTime >= start_date)

        if end_date:
            records = records.filter(TestrunLog.endTime <= end_date)

        # filter by globals
        if stage:
            records = records.filter(
                TestrunLog.globalVars.any(
                    and_(
                        GlobalAttribute.name == GC.EXECUTION_STAGE,
                        GlobalAttribute.value == stage,
                    )))

        for key, value in global_settings.items():
            records = records.filter(
                TestrunLog.globalVars.any(
                    and_(
                        GlobalAttribute.name == key,
                        GlobalAttribute.value == value,
                    )))

        self.query_set.set(records)

        logger.info(f'Number of found records: {self.query_set.length}')

    def filter_to_str(self, key):
        #
        # get filter value as str
        #

        value = self.filters.get(key)
        if value:
            return str(value)

        # undefined values
        if key == 'end_date':
            return datetime.now().strftime('%Y-%m-%d')
        if key == 'start_date':
            return 'None'
        return 'All'

    def filename(self, extention):
        #
        # generate filename for exports
        #

        filename = '_'.join([
            'TestrunLogs',
            self.filter_to_str('name'),
            self.filter_to_str('stage'),
            self.filter_to_str('start_date'),
            self.filter_to_str('end_date'),
            'globals' if self.filters.get('globals') else 'None',
        ])
        return f'{filename}.{extention}'

    def export(self):
        #
        # exports the query set to xlsx
        #

        # initialize workbook
        path_to_file = self.managedPaths.getOrSetDBExportPath().joinpath(
            self.filename('xlsx'))
        workbook = Workbook(str(path_to_file))

        # set labels
        labels = {
            'testrun': 'TestRun',
            'testcase_sequence': 'Test Case Sequence',
            'testcase': 'Test Case',
            'stage': 'Stage',
            'duration': 'Avg. Duration',
            'globals': 'Global Settings',
        }

        # set output headers
        base_headers = [
            'Testrun ID',
            'TestCase ID',
            'TestCase Number',
        ]

        base_fields = [
            GC.EXECUTION_STAGE,
            GC.TESTCASESTATUS,
            GC.TIMING_DURATION,
        ]

        # define cell formats
        cformats = {
            'bg_red':
            workbook.add_format({'bg_color': 'red'}),
            'bg_green':
            workbook.add_format({'bg_color': 'green'}),
            'bg_yellow':
            workbook.add_format({'bg_color': 'yellow'}),
            'font_bold':
            workbook.add_format({'bold': True}),
            'font_bold_italic':
            workbook.add_format({
                'bold': True,
                'italic': True
            }),
        }

        # map status styles
        status_style = {
            GC.TESTCASESTATUS_SUCCESS: cformats.get('bg_green'),
            GC.TESTCASESTATUS_ERROR: cformats.get('bg_red'),
            GC.TESTCASESTATUS_WAITING: None,
        }

        # create sheets
        sheets = {
            'summary':
            ExportSheet(workbook.add_worksheet('Summary'),
                        cformats.get('font_bold')),
            'output':
            ExportSheet(workbook.add_worksheet('Output'),
                        cformats.get('font_bold')),
        }

        # write summary titles
        time_start = time.time()  # -------------------------> time tracker
        # title
        sheets['summary'].sheet.set_column(first_col=0, last_col=0, width=18)
        sheets['summary'].sheet.set_column(first_col=1, last_col=1, width=10)
        sheets['summary'].header([f'{labels.get("testrun")}s Summary'])

        # parameters
        for key in self.filters:
            if key != 'globals':
                sheets['summary'].row([
                    {
                        'value': key,
                        'format': cformats.get('font_bold'),
                    },
                    {
                        'value': self.filter_to_str(key),
                    },
                ])
        # global settings
        if self.filters.get('globals'):
            sheets['summary'].hr()
            sheets['summary'].row([
                {
                    'value': f"{labels.get('globals')}:",
                    'format': cformats.get('font_bold'),
                },
            ])
            for name, value in self.filters.get('globals').items():
                sheets['summary'].row([
                    {
                        'value': name,
                    },
                    {
                        'value': value,
                    },
                ])

        # write output titles
        sheets['output'].header(base_headers + base_fields)

        # write items
        # testruns
        for tr_name in self.query_set.names():
            logger.info(
                f'Exporting Tetsrun "{tr_name}": {len(list(self.query_set.filter(tr_name)))} records'
            )
            # testrun name
            sheets['summary'].hr()
            sheets['summary'].row([{
                'value': labels.get('testrun'),
                'format': cformats.get('font_bold'),
            }, {
                'value': tr_name,
            }])

            # average duration
            sheets['summary'].row([{
                'value': labels.get('duration'),
                'format': cformats.get('font_bold'),
            }, {
                'value':
                self.query_set.tr_avg_duration(tr_name),
            }])

            #

            # testcase sequences
            for tcs_index in range(self.query_set.max_size(tr_name)):
                tcs_number = tcs_index + 1
                # testcase sequence
                sheets['summary'].hr()
                sheets['summary'].row([{
                    'value': labels.get('testcase_sequence'),
                    'format': cformats.get('font_bold'),
                }, {
                    'value': tcs_number,
                }])

                # test cases
                # header
                tc_num_max = self.query_set.max_size(tr_name,
                                                     tcs_index=tcs_index)
                sheets['summary'].hr()
                sheets['summary'].row([
                    {
                        'value': 'Run Date',
                        'format': cformats.get('font_bold_italic'),
                    },
                    {
                        'value': labels.get('testcase'),
                        'format': cformats.get('font_bold_italic'),
                    },
                ] + [{} for i in range(1, tc_num_max)] + [
                    {
                        'value': labels.get('stage'),
                        'format': cformats.get('font_bold_italic'),
                    },
                    {
                        'value': f'{labels.get("testrun")} ID',
                        'format': cformats.get('font_bold_italic'),
                    },
                ])
                sheets['summary'].row([{}] + [{
                    'value': i
                } for i in range(tc_num_max)])

                durations = [[.0, 0] for i in range(tc_num_max)]
                for tr_index, tr in enumerate(
                        self.query_set.filter(tr_name, tcs_index)):
                    tc_num = len(tr.testcase_sequences[tcs_index].testcases)
                    status_row = [{
                        'value':
                        tr.startTime.strftime('%Y-%m-%d %H:%M:%S')
                    }]

                    # query fields
                    data = self.db.query(
                        TestCaseField.name,
                        TestCaseField.value,
                        TestCaseLog.number,
                        TestCaseLog.id,
                    ).join(TestCaseField.testcase).join(TestCaseLog.testcase_sequence).join(TestCaseSequenceLog.testrun)\
                    .filter(and_(TestrunLog.id == tr.id, TestCaseSequenceLog.number == tcs_number)).order_by(TestCaseLog.number)
                    tc_id_cur = None
                    tr_stage = None
                    for name, value, tc_index, tc_id in data.yield_per(500):
                        # summary data
                        if name == GC.TESTCASESTATUS:
                            status_row.append({
                                'value':
                                value,
                                'format':
                                status_style.get(value),
                            })
                        elif name == GC.EXECUTION_STAGE:
                            tr_stage = value
                        elif name == GC.TIMING_DURATION:
                            m = re.search(
                                r'(?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d[\.\d+]*)',
                                value)
                            if m:
                                factors = {
                                    'hours': 3600,
                                    'minutes': 60,
                                    'seconds': 1,
                                }
                                durations[tc_index - 1][0] += sum([
                                    factors[key] * float(value)
                                    for key, value in m.groupdict().items()
                                ])
                                durations[tc_index - 1][1] += 1

                        # write to output
                        # write testcase
                        if tc_id != tc_id_cur:
                            tc_id_cur = tc_id
                            sheets['output'].new_row([
                                {
                                    'value': str(tr),
                                },
                                {
                                    'value': str(uuid.UUID(bytes=tc_id)),
                                },
                                {
                                    'value': tc_index,
                                },
                            ])

                        #field
                        sheets['output'].by_header(name, value)

                    # write state row to summary sheet
                    sheets['summary'].row(
                        status_row + [{}
                                      for i in range(tc_num, tc_num_max)] + [
                                          {
                                              'value': tr_stage,
                                          },
                                          {
                                              'value': str(tr),
                                          },
                                      ])

                # avg durations
                sheets['summary'].row([
                    {
                        'value': labels.get('duration'),
                        'format': cformats.get('font_bold_italic'),
                    },
                ] + [{
                    'value': d
                } for d in map(
                    lambda d: round(d[0] / d[1], 2)
                    if d[1] > 0 else .0, durations)])

        # autowidth output columns
        for col in range(len(base_headers) + len(base_fields)):
            ExcelSheetHelperFunctions.set_column_autowidth(
                sheets['output'].sheet, col)

        workbook.close()

        execution_time = round(time.time() - time_start, 2)
        logger.info(
            f'Query successfully exported to {path_to_file} in {execution_time} seconds'
        )

        return path_to_file

    def export_txt(self):
        #
        # export to txt
        #

        path_to_file = self.managedPaths.getOrSetDBExportPath().joinpath(
            self.filename('txt'))

        # set labels
        labels = {
            'testrun': 'TestRun',
            'testcase_sequence': 'Test Case Sequence',
            'testcase': 'Test Case',
            'stage': 'Stage',
            'duration': 'Avg. Duration',
        }

        # write data
        with open(path_to_file, 'w') as f:
            # title
            f.write(f'{labels.get("testrun")}s Summary\n\n')

            # parameters
            for key in self.filters:
                f.write(f'{key}\t{self.filter_to_str(key)}\n')

            # testruns
            for tr_name in self.query_set.names():
                print(f'*** Tetsrun "{tr_name}"')
                # testrun name
                f.write(f'\n{labels.get("testrun")}: {tr_name}\n')

                # average duration
                f.write(
                    f'{labels.get("duration")}: {self.query_set.tr_avg_duration(tr_name)}\n'
                )

                # testcase sequences
                for tcs_index in range(self.query_set.max_size(tr_name)):
                    print(f'**** TestCaseSequence-{tcs_index}')
                    # testcase sequence
                    f.write(
                        f'\n{labels.get("testcase_sequence")}: {tcs_index}\n\n'
                    )

                    # test cases
                    # header
                    tc_num = self.query_set.max_size(tr_name,
                                                     tcs_index=tcs_index)
                    f.write(f'{"Run Date":20}{labels.get("testcase"):8}')
                    f.write(' ' * 7)
                    f.write((' ' * 8) * (tc_num - 2))
                    f.write(
                        f'{labels.get("stage"):8}{labels.get("testcase")} ID\n'
                    )
                    f.write(' ' * 20)
                    for tc_index in range(tc_num):
                        f.write(f'{tc_index:<8}')
                    f.write('\n')

                    # testcase status
                    tr_counter = 1
                    for tr in self.query_set.filter(tr_name, tcs_index):
                        print(
                            f'***** TestRun {tr_counter}: {len(tr.testcase_sequences[tcs_index].testcases)} testcases'
                        )
                        tr_counter += 1
                        f.write(
                            f'{tr.startTime.strftime("%Y-%m-%d %H:%M:%S"):20}')
                        for tc in tr.testcase_sequences[tcs_index].testcases:
                            f.write(f'{tc.status:8}')
                        # tail
                        print(
                            f'{tc_num} - {len(tr.testcase_sequences[tcs_index].testcases)} = {(tc_num-len(tr.testcase_sequences[tcs_index].testcases))}'
                        )
                        f.write(
                            (' ' * 8) *
                            (tc_num -
                             len(tr.testcase_sequences[tcs_index].testcases)))
                        f.write(f'{tr.stage:8}{tr}\n')

                    # average durations
                    f.write(f'{labels.get("duration"):20}')
                    for tc_index in range(tc_num):
                        f.write(
                            f'{self.query_set.tc_avg_duration(tr_name, (tcs_index, tc_index)):8}'
                        )

        logger.info(f'Query successfully exported to {path_to_file}')

        return path_to_file
예제 #22
0
class TestRun:
    """
    This is the main Class of Testexecution in the baangt Framework. It is usually started
    from baangtIA.py
    """
    def __init__(self,
                 testRunName,
                 globalSettingsFileNameAndPath=None,
                 testRunDict=None,
                 uuid=uuid4(),
                 executeDirect=True,
                 noCloneXls=False):  # -- API support: testRunDict --
        """
        @param testRunName: The name of the TestRun to be executed.
        @param globalSettingsFileNameAndPath: from where to read the <globals>.json
        """

        # Take over importing parameters:
        self.uuid = uuid
        logger.info(f'Init Testrun, uuid is {self.uuid}')
        self.testRunDict = testRunDict
        self.globalSettingsFileNameAndPath = globalSettingsFileNameAndPath
        self.testRunName, self.testRunFileName = \
            self._sanitizeTestRunNameAndFileName(testRunName, executeDirect)

        # Initialize everything else
        self.apiInstance = None
        self.testType = None
        self.networkInfo = None
        self.results = None
        self.browserFactory = None
        self.kwargs = {}
        self.dataRecords = {}
        self.globalSettings = {}
        self.json_dict = {
        }  # Used to maintain records of RLP_ data which will be used will exporting results
        self.managedPaths = ManagedPaths()
        self.classesForObjects = ClassesForObjects(
        )  # Dynamically loaded classes
        self.timing = Timing()
        self.testRunUtils = TestRunUtils()
        self.testCasesEndDateTimes_1D = []  # refer to single execution
        self.testCasesEndDateTimes_2D = [[]]  # refer to parallel execution
        # New way to export additional Tabs to Excel
        # If you want to export additional data, place a Dict with Tabname + Datafields in additionalExportTabs
        # from anywhere within your custom code base.
        self.additionalExportTabs = {}
        self.statistics = Statistic()
        self.noCloneXls = noCloneXls
        signal.signal(signal.SIGINT, self.exit_signal_handler)
        signal.signal(signal.SIGTERM, self.exit_signal_handler)

        # Initialize other values
        self.timing.takeTime(GC.TIMING_TESTRUN)  # Initialize Testrun Duration

        # Usually the Testrun is called without the parameter executeDirect, meaning it default to "Execute"
        # during Unit-Tests we don't want this behaviour:
        if executeDirect:
            self.executeTestRun()

    def exit_signal_handler(self, signal, frame):
        self.browserFactory.teardown()

    def executeTestRun(self):
        self._initTestRunSettingsFromFile()  # Loads the globals*.json file

        self._loadJSONTestRunDefinitions()
        self._loadExcelTestRunDefinitions()

        self.browserFactory = BrowserFactory(self)
        self.executeTestSequence()
        self.tearDown()

        try:
            Sender.send_all(self.results, self.globalSettings)
        except Exception as ex:
            logger.error(f"Error from SendAll: {ex}")

    def append1DTestCaseEndDateTimes(self, dt):
        self.testCasesEndDateTimes_1D.append(dt)

    def append2DTestCaseEndDateTimes(self, index, tcAndDt):
        tc = tcAndDt[0]
        dt = tcAndDt[1]
        [
            self.testCasesEndDateTimes_2D.append([])
            for i in range(index + 1 - len(self.testCasesEndDateTimes_2D))
        ] if index + 1 > len(self.testCasesEndDateTimes_2D) else None
        self.testCasesEndDateTimes_2D[index].append([tc, dt])

    def tearDown(self):
        """
        Close browser (unless stated in the Globals to not do so) and API-Instances
        Take overall Time spent for the complete TestRun
        Write results of TestRun to output channel(s)
        """

        self.timing.takeTime(GC.TIMING_TESTRUN)
        self.timing.takeTimeSumOutput()

        if self.apiInstance:
            self.apiInstance.tearDown()

        network_info = self.browserFactory.teardown()
        self.kwargs['networkInfo'] = network_info

        if self.testCasesEndDateTimes_1D:
            self.kwargs[
                'testCasesEndDateTimes_1D'] = self.testCasesEndDateTimes_1D

        if self.testCasesEndDateTimes_2D and self.testCasesEndDateTimes_2D[0]:
            self.kwargs[
                'testCasesEndDateTimes_2D'] = self.testCasesEndDateTimes_2D

        if len(self.additionalExportTabs) > 0:
            self.kwargs[GC.EXPORT_ADDITIONAL_DATA] = self.additionalExportTabs

        self.results = ExportResults(
            **self.kwargs)  # -- API support: self.results --
        successful, error = self.getSuccessAndError()
        waiting = self.getWaiting()
        self.statistics.update_all(successful, error, waiting)
        logger.info(
            f"Finished execution of Testrun {self.testRunName}. "
            f"{successful} Testcases successfully executed, {error} errors")
        print(f"Finished execution of Testrun {self.testRunName}. "
              f"{successful} Testcases successfully executed, {error} errors")

    def getSuccessAndError(self):
        """
        Returns number of successful and number of error test cases of the current test run
        @rtype: object
        """
        lError = 0
        lSuccess = 0
        for value in self.dataRecords.values():
            if value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
                lError += 1
            elif value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_SUCCESS:
                lSuccess += 1
        return lSuccess, lError

    def getWaiting(self):
        lWaiting = 0
        for value in self.dataRecords.values():
            if value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_WAITING:
                lWaiting += 1
        return lWaiting

    def getAllTestRunAttributes(self):
        return self.testRunUtils.getCompleteTestRunAttributes(self.testRunName)

    def getBrowser(self,
                   browserInstance=0,
                   browserName=None,
                   browserAttributes=None,
                   mobileType=None,
                   mobileApp=None,
                   desired_app=None,
                   mobile_app_setting=None,
                   browserWindowSize=None):

        return self.browserFactory.getBrowser(
            browserInstance=browserInstance,
            browserName=browserName,
            browserAttributes=browserAttributes,
            mobileType=mobileType,
            mobileApp=mobileApp,
            desired_app=desired_app,
            mobile_app_setting=mobile_app_setting,
            browserWindowSize=browserWindowSize)

    def getAPI(self):
        if not self.apiInstance:
            self.apiInstance = ApiHandling()
        return self.apiInstance

    def setResult(self, recordNumber, dataRecordResult):
        logger.debug(f"Received new result for Testrecord {recordNumber}")
        self.dataRecords[recordNumber] = dataRecordResult

    def executeTestSequence(self):
        """
        Start TestcaseSequence

        TestCaseSequence is a sequence of Testcases. In the TestcaseSequence there's a sequential List of
        Testcases to be executed.

        Before the loop (executeDictSequenceOfClasses) variables inside the the testrun-definition are replaced
        by values from the globals-file (e.g. if you want to generally run with FF, but in a certain case you want to
        run with Chrome, you'd have FF in the Testrundefinition, but set parameter in globals_chrome.json accordingly
        (in this case {"TC.Browser": "CHROME"}. TC.-Prefix signals the logic to look for this variable ("Browser")
        inside the testcase definitions and replace it with value "CHROME".

        """
        self.testRunUtils.replaceGlobals(self.globalSettings)
        self.testRunUtils.replaceClasses(self.testRunName,
                                         self.classesForObjects)

        kwargs = {
            GC.KWARGS_TESTRUNATTRIBUTES: self.getAllTestRunAttributes(),
            GC.KWARGS_TESTRUNINSTANCE: self,
            GC.KWARGS_TIMING: self.timing
        }

        self.executeDictSequenceOfClasses(
            kwargs[GC.KWARGS_TESTRUNATTRIBUTES][GC.STRUCTURE_TESTCASESEQUENCE],
            counterName=GC.STRUCTURE_TESTCASESEQUENCE,
            **kwargs)

    def executeDictSequenceOfClasses(self, dictSequenceOfClasses, counterName,
                                     **kwargs):
        """
        This is the main loop of the TestCaseSequence, TestCases, TestStepSequences and TestSteps.
        The Sequence of which class instance to create is defined by the TestRunAttributes.

        Before instancgetBrowsering the class it is checked, whether the class was loaded already and if not, will be loaded
        (only if the classname is fully qualified (e.g baangt<projectname>.TestSteps.myTestStep).
        If the testcase-Status is already "error" (GC.TESTCASESTATUS_ERROR) we'll stop the loop.

        @param dictSequenceOfClasses: The list of classes to be instanced. Must be a dict of {Enum, Classname},
        can be also {enum: [classname, <whatEverElse>]}
        @param counterName: Which Structure element we're currently looping, e.g. "TestStep" (GC.STRUCTURE_TESTSTEP)
        @param kwargs: TestrunAttributes, this TestRun, the Timings-Instance, the datarecord

        """
        if not kwargs.get(GC.KWARGS_TESTRUNATTRIBUTES):
            kwargs[
                GC.KWARGS_TESTRUNATTRIBUTES] = self.getAllTestRunAttributes()
        if not kwargs.get(GC.KWARGS_TESTRUNINSTANCE):
            kwargs[GC.KWARGS_TESTRUNINSTANCE] = self
            logger.info(
                'get into not kwargs.getGC.KWARGS_TESTRUNINSTANCE, id is {}'.
                format(id(self)))
        if not kwargs.get(GC.KWARGS_TIMING):
            kwargs[GC.KWARGS_TIMING] = self.timing
        for key, value in dictSequenceOfClasses.items():
            # If any of the previous steps set the testcase to "Error" - exit here.
            if kwargs.get(GC.KWARGS_DATA):
                if kwargs[GC.KWARGS_DATA][
                        GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
                    logger.info(
                        f"TC is already in status Error - not processing steps {counterName}: {key}, {value}"
                        f"and everything behind this step")
                    return
                if kwargs[GC.KWARGS_DATA].get(GC.TESTCASESTATUS_STOP):
                    logger.info(
                        f"TC wanted to stop. Not processing steps {counterName}: {key}, {value}"
                        f"and everything behind this step. TC-Status is "
                        f"{kwargs[GC.KWARGS_DATA][GC.TESTCASESTATUS]}")
                    return
                if kwargs[GC.KWARGS_DATA].get(GC.TESTCASESTATUS_STOPERROR):
                    kwargs[GC.KWARGS_DATA][
                        GC.TESTCASESTATUS] = GC.TESTCASESTATUS_ERROR
                    if not kwargs[GC.KWARGS_DATA].get(GC.TESTCASEERRORLOG):
                        kwargs[GC.KWARGS_DATA][
                            GC.
                            TESTCASEERRORLOG] = "Aborted by command 'TCStopTestCaseError'"
                    else:
                        kwargs[GC.KWARGS_DATA][GC.TESTCASEERRORLOG] = kwargs[GC.KWARGS_DATA][GC.TESTCASEERRORLOG] \
                                                                      + "\nAborted by command 'TCStopTestCaseError'"
                    return

            logger.info(f"Starting {counterName}: {key}")
            kwargs[counterName] = key

            # Get the class reference:
            if isinstance(value, list):
                lFullQualified = value[
                    0]  # First List-Entry must hold the ClassName
            else:
                lFullQualified = value

            l_class = TestRun.__dynamicImportClasses(lFullQualified)

            try:
                l_class(**kwargs)  # Executes the class´es  __init__ method
            except TypeError as e:
                # Damn! The class is wrong.
                l_class = TestRun.__dynamicImportClasses(lFullQualified)
                l_class(**kwargs)

        self.kwargs = kwargs

    def _initTestRunSettingsFromFile(self):
        self.__loadJSONGlobals()
        self.__setPathsIfNotPredefined()
        self.__sanitizeGlobalsValues()

    def __setPathsIfNotPredefined(self):
        if not self.globalSettings.get(GC.PATH_SCREENSHOTS, None):
            self.globalSettings[GC.PATH_SCREENSHOTS] = str(
                Path(self.managedPaths.getOrSetScreenshotsPath()).expanduser())
        else:
            self.managedPaths.getOrSetScreenshotsPath(
                path=self.globalSettings.get(GC.PATH_SCREENSHOTS))
        if not self.globalSettings.get(GC.PATH_EXPORT, None):
            self.globalSettings[GC.PATH_EXPORT] = str(
                Path(self.managedPaths.getOrSetExportPath()).expanduser())
        else:
            self.managedPaths.getOrSetExportPath(
                path=self.globalSettings.get(GC.PATH_EXPORT))
        if not self.globalSettings.get(GC.PATH_IMPORT, None):
            self.globalSettings[GC.PATH_IMPORT] = str(
                Path(self.managedPaths.getOrSetImportPath()).expanduser())
        else:
            self.managedPaths.getOrSetImportPath(
                path=self.globalSettings.get(GC.PATH_IMPORT))
        if not self.globalSettings.get(GC.PATH_ROOT, None):
            self.globalSettings[GC.PATH_ROOT] = str(
                Path(self.managedPaths.getOrSetRootPath()).parent.expanduser())
        else:
            self.managedPaths.getOrSetRootPath(
                path=self.globalSettings.get(GC.PATH_ROOT))

    def __loadJSONGlobals(self):
        if self.globalSettingsFileNameAndPath:
            self.globalSettings = utils.openJson(
                self.globalSettingsFileNameAndPath)

        # Set default execution STAGE
        if not self.globalSettings.get(GC.EXECUTION_STAGE, None):
            logger.debug(
                f"Execution Stage was not set. Setting to default value {GC.EXECUTION_STAGE_TEST}"
            )
            self.globalSettings[GC.EXECUTION_STAGE] = GC.EXECUTION_STAGE_TEST

    def __sanitizeGlobalsValues(self):
        # Support for new dataClass to load different Classes
        for key, value in self.globalSettings.items():
            if "CL." in key:
                self.classesForObjects.__setattr__(key.strip("CL."), value)

            # Change boolean strings into boolean values.
            if isinstance(value, str):
                if value.lower() in ("false", "true", "no", "x"):
                    self.globalSettings[key] = utils.anything2Boolean(value)
                elif "renv_" in value.lower():
                    self.globalSettings[
                        key] = TestDataGenerator.get_env_variable(value[5:])
            if isinstance(value, dict):
                if "default" in value:
                    # This happens in the new UI, if a value was added manually,
                    # but is not part of the globalSetting.json. In this case there's the whole shebang in a dict. We
                    # are only interested in the actual value, which is stored in "default":
                    self.globalSettings[key] = value["default"]
                    if isinstance(self.globalSettings[key], str):
                        if "renv_" in self.globalSettings[key].lower():
                            self.globalSettings[
                                key] = TestDataGenerator.get_env_variable(
                                    self.globalSettings[key][5:])
                    continue
                else:
                    # This could be the "old" way of the globals-file (with {"HEADLESS":"True"})
                    self.globalSettings[key] = value
                    continue

            if isinstance(value, str) and len(value) > 0:
                if value[0] == "{" and value[-1] == "}":
                    # Dict, that is not seen as dict
                    value = value.replace("\'", '"')
                    self.globalSettings[key] = json.loads(value)

        if self.globalSettings.get("TC." + GC.EXECUTION_LOGLEVEL):
            utils.setLogLevel(
                self.globalSettings.get("TC." + GC.EXECUTION_LOGLEVEL))

    def _loadJSONTestRunDefinitions(self):
        if not self.testRunFileName and not self.testRunDict:  # -- API support: testRunDict --
            return

        if self.testRunFileName and ".JSON" in self.testRunFileName.upper(
        ):  # -- API support: self.testRunFileName --
            data = utils.replaceAllGlobalConstantsInDict(
                utils.openJson(self.testRunFileName))
            self.testRunUtils.setCompleteTestRunAttributes(
                testRunName=self.testRunName, testRunAttributes=data)

        # -- API support --
        # load TestRun from dict
        if self.testRunDict:
            data = utils.replaceAllGlobalConstantsInDict(self.testRunDict)
            self.testRunUtils.setCompleteTestRunAttributes(
                testRunName=self.testRunName, testRunAttributes=data)
        # -- END of API support --

    def _loadExcelTestRunDefinitions(self):
        if not self.testRunFileName:
            return

        if ".XLSX" in self.testRunFileName.upper():
            logger.info(f"Reading Definition from {self.testRunFileName}")
            lExcelImport = TestRunExcelImporter(
                FileNameAndPath=self.testRunFileName,
                testRunUtils=self.testRunUtils)
            lExcelImport.importConfig(global_settings=self.globalSettings)

    @staticmethod
    def __dynamicImportClasses(fullQualifiedImportName):
        return utils.dynamicImportOfClasses(
            fullQualifiedImportName=fullQualifiedImportName)

    @staticmethod
    def _sanitizeTestRunNameAndFileName(TestRunNameInput, direct):
        """
        @param TestRunNameInput: The complete File and Path of the TestRun definition (JSON or XLSX).
        @return: TestRunName and FileName (if definition of testrun comes from a file (JSON or XLSX)
        """
        if ".XLSX" in TestRunNameInput.upper():
            cloneXls = CloneXls(TestRunNameInput)
            lFileName = cloneXls.update_or_make_clone(
                ignore_headers=["TestResult", "UseCount"], clone=direct)
            lRunName = utils.extractFileNameFromFullPath(lFileName)
        elif ".JSON" in TestRunNameInput.upper():
            lRunName = utils.extractFileNameFromFullPath(TestRunNameInput)
            lFileName = TestRunNameInput
        else:
            lRunName = TestRunNameInput
            lFileName = None

        return lRunName, lFileName
예제 #23
0
    def __init__(self, testRunInstance, resultsFileName: str = None):
        self.testRunInstance = testRunInstance
        self.resultsFileName = resultsFileName
        self.mp = ManagedPaths()

        self._append2BaseXLS()