def configure_group_for_test(cls, metafunc): ''' Configures **group** fixture for a test. Acts only if **group** fixture is present in the signature of a test function or signature(s) of any of its fixture(s) in its fixture hierarchy. To be used in **pytest_generate_tests** hook in **conftest.py**. Args: metafunc: **pytest**'s MetaFunc object Note: The **group** fixture yields a **DataRecord** object containing the following keys: - **name**: Group name - **config**: **Configuration** object assigned to the group. - **thread_name**: Thread name for the thread in which the Test Group is running. ''' from arjuna import Arjuna, ArjunaOption, log_debug, C from arjuna.tpi.engine.data.markup import record log_debug("{} {}".format(metafunc.function, metafunc.fixturenames)) group_params = Arjuna.get_group_params() conf = None m = metafunc.function.__module__ group = record(**group_params).build(context='Group').all_records[0] log_debug( "Parameterizing distributor for module: {} with group: {}".format( m, group)) metafunc.parametrize("group", argvalues=[group], ids=["G"], indirect=True)
def wait(self, *, max_wait=60, poll_interval=0.5): from arjuna import log_debug log_debug( "Dynamic wait call triggered with max_wait: {}".format(max_wait)) end_time = time.time() + max_wait e = None etrace = None while (True): try: if self.is_met(): return self.get_call_result() except ConditionException as ce: raise ce except WaitableError as we: e = we import traceback etrace = traceback.format_exc() pass except Exception as f: import traceback raise Exception( "An unexpected exception occured in dynamic wait. Message: {}. Trace: {}" .format(str(f), traceback.format_exc())) time.sleep(poll_interval) ctime = time.time() if (ctime > end_time): break raise ArjunaTimeoutError(self.__class__.__name__, str(e) + etrace)
def __getitem__(self, name): from arjuna import log_debug if self.has(name): return self.__mdict[name] else: log_debug(f"Meta dict does not have {name}. Returning None.") return None
def __getattr__(self, label): wmd = self._get_wmd_for_label(label) from arjuna import log_debug log_debug("Finding element with label: {} and wmd: {}".format(label, wmd)) try: return self._locate_with_wmd(wmd) except ArjunaTimeoutError: raise GuiWidgetForLabelNotPresentError(self.__gui, label)
def add_element_meta_data(self, name, context, raw_locators, meta): wmd = GuiWidgetMetaData.create_wmd(*raw_locators, meta=meta) name = name.lower() if not self.has(name): self.__ns[name] = {} self.__ns[name][context] = wmd from arjuna import log_debug log_debug("Loaded {} label. EMD: {}".format(name, str(wmd)))
def _as_wmd(self): ''' Convert this **GuiWidgetDefinition** to **GuiWidgetMetaData** object ''' wmd = self._as_raw_wmd() from arjuna import log_debug log_debug("Unformatted Widget def is: " + str(wmd)) fmt_wmd = wmd.create_formatted_wmd(**self.__fmt_args) return fmt_wmd
def find_element(cls, container, byType, byValue): from arjuna import log_debug log_debug( f"Finding element in container:{container} with wtype:{byType} and wvalue:{byValue}" ) try: return container.find_element(cls.BY_MAP[byType.upper()], byValue) except Exception as e: raise GuiWidgetNotFoundError("By.{}={}".format(byType, byValue))
def __init__(self, mdict=None): from arjuna import log_debug temp_dict = not mdict and CIStringDict() or CIStringDict(mdict) self.__mdict = CIStringDict() self.__process_type(temp_dict) self.__process_relations(temp_dict) self.__process_filters(temp_dict) self.__process_settings(temp_dict) log_debug("Meta dictionary is: {}".format(repr_dict(self.__mdict)))
def __load(self): from arjuna import log_debug log_debug("Selecting mailbox: {}".format(self.name)) if self._server._ready: MailBoxConditions(self).Select().wait(max_wait=30) _, data = self._server._imap.search(None, 'ALL') self.__count = sum(1 for num in data[0].split()) log_debug("Selected mailbox: {}".format(self.name)) else: raise Exception("A mailbox can be selected only with active connection to IMAP server.")
def _externalize(self): try: self.__guidef = GuiDef(self.__guimgr.name_store, self._automator, self.label, self.def_file_path) except Exception as e: import traceback raise GuiNamespaceLoadingError(self, str(e) + traceback.format_exc()) from arjuna import log_debug log_debug("Gui Namespace loading completed for {}.".format(self.label))
def __create_relative_by(cls, byType, byValue, relations): from arjuna import log_debug relative_by = RelativeBy({byType: byValue}) for k,v in relations.items(): try: getattr(relative_by, cls.RELATION_MAP[k.lower()])(v) except: pass log_debug("Root" + str(relative_by.root)) log_debug("Filters" + str(relative_by.filters)) return relative_by
def locate(self, locator): from arjuna import log_debug from arjuna.core.error import ArjunaTimeoutError from arjuna.tpi.error import GuiWidgetNotPresentError wmd = locator._as_wmd() log_debug("Finding element with wmd: {}.".format(wmd)) try: return getattr(self.__wmd_finder, wmd.meta["type"].name.lower())(wmd) except ArjunaTimeoutError: raise GuiWidgetNotPresentError(self.__gui, wmd)
def email_at(self, ordinal, _reload=True): ''' Email at an ordinal/position - human counted, 1 is the oldest email position. ''' from arjuna import log_debug log_debug("Reading email at ordinal >{}< from mailbox {} at email server >{}<".format( ordinal, self.name, self._server.host )) if _reload: self.__load() _, data = self._server._imap.fetch(str(ordinal), '(RFC822)') return Email(pyemail.message_from_string(data[0][1].decode()))
def _load_anchor_element(self): label = self._gui_def.anchor_element_name from arjuna import log_debug log_debug( "Loading Anchor Element for {} Gui. anchor label in GNS: {}.". format( self.label, self._gui_def.anchor_element_name, )) if label is not None: getattr(self.gns, label)
def _create_firefox(cls, config, driver_path, browser_bin_path, svc_url, proxy=None): from selenium.webdriver import Firefox from selenium.webdriver import FirefoxOptions from selenium.webdriver import FirefoxProfile profile = FirefoxProfile() # if config["arjuna_options"]["BROWSER_PROXY_ON"]: # proxy = Proxy() # proxy_string = "{}.{}".format( # config["arjuna_options"]["BROWSER_PROXY_HOST"], # config["arjuna_options"]["BROWSER_PROXY_PORT"] # ) # proxy.http_proxy = proxy_string # proxy.ssl_proxy = proxy_string # profile.set_proxy(proxy) caps = DesiredCapabilities.FIREFOX caps.update(config["driverCapabilities"]) from arjuna import log_debug log_debug("Is proxy set for Firefox?: {}".format(proxy is not None)) if proxy is not None: proxy.add_to_capabilities(caps) caps['acceptInsecureCerts'] = True options = FirefoxOptions() if browser_bin_path.lower() != "not_set": options.binary_location = browser_bin_path if cls.are_browser_prefs_set(config): for pref, value in config["browserPreferences"].items(): options.set_preference(pref, value) if config["arjuna_options"]["BROWSER_HEADLESS"]: options.add_argument("-headless") if cls.are_browser_args_set(config): for arg in config["browserArgs"]: options.add_argument(arg) from selenium import webdriver return webdriver.Remote(svc_url, browser_profile=profile, options=options)
def _create_chrome(cls, config, driver_path, browser_bin_path, svc_url, proxy=None): from selenium.webdriver import Chrome, ChromeOptions caps = DesiredCapabilities.CHROME caps.update(config["driverCapabilities"]) # if config["arjuna_options"]["BROWSER_PROXY_ON"]: # proxy = Proxy() # proxy_string = "{}.{}".format( # config["arjuna_options"]["BROWSER_PROXY_HOST"], # config["arjuna_options"]["BROWSER_PROXY_PORT"] # ) # proxy.http_proxy = proxy_string # proxy.ssl_proxy = proxy_string # proxy.add_to_capabilities(caps) from arjuna import log_debug log_debug("Is proxy set for Chrome?: {}".format(proxy is not None)) if proxy is not None: proxy.add_to_capabilities(caps) caps['acceptInsecureCerts'] = True options = ChromeOptions() if browser_bin_path.lower() != "not_set": options.binary_location = browser_bin_path if cls.are_browser_prefs_set(config): options.add_experimental_option("prefs", config["browserPreferences"]) if config["arjuna_options"]["BROWSER_HEADLESS"]: options.add_argument("--headless") if cls.are_browser_args_set(config): for arg in config["browserArgs"]: options.add_argument(arg) if cls.are_extensions_set(config): for ext in config["browserExtensions"]: options.add_extension(ext) caps[ChromeOptions.KEY] = options.to_capabilities()[ChromeOptions.KEY] from selenium import webdriver return webdriver.Remote(svc_url, caps)
def find_elements(cls, container, byType, byValue, *, relations=None, filters=None): from arjuna import log_debug log_debug(f"Finding elements in container:{container} with wtype:{byType} and wvalue:{byValue} with relations: {relations} and filters: {filters}") sbyType = cls.BY_MAP[byType.upper()] if not relations: elements = container.find_elements(sbyType, byValue) else: # Currently Selenium supports Relative locator only for find_elements and at driver level # For nested element finding change to WebDriver instance if relations are defined. if isinstance(container, WebElement): container = container.parent rby = cls.__create_relative_by(sbyType, byValue, relations) log_debug("Selenium find_elements call made with RelativeBy: {}".format(rby.to_dict())) elements = container.find_elements(rby) if len(elements) == 0: raise GuiWidgetNotFoundError("By.{}={}".format(byType, byValue), message="No element found for Selenium locator") if filters is not None: if "pos" in filters: log_debug("Filtering elements with filter: {}".format(filters["pos"])) try: extracted_elements = filters["pos"].extract(elements) if type(extracted_elements) is not list: return [extracted_elements] else: return extracted_elements except Exception as e: log_debug("Exception in filters processing: {}".format(e)) raise GuiWidgetNotFoundError("By.{}={}".format(byType, byValue), message="No element found for Selenium locator after applying position filters.") return elements
def __process_widgetdef_in_relations(self, wmd): from arjuna import log_debug from arjuna.core.error import ArjunaTimeoutError from arjuna.tpi.error import GuiWidgetNotPresentError log_debug( "Processing GuiWidgetDefinition relationships for GuiWidgetMetaData: {} in gui: {}" .format(wmd, self.__gui)) for k, v in wmd.meta.relations.items(): from arjuna import GuiWidgetDefinition if isinstance(v, GuiWidgetDefinition): log_debug( "Triggering locating operation for GuiWidgetDefinition {} in relations dict." .format(v)) rwmd = v._as_wmd() rwmd = self.__process_widgetdef_in_relations(rwmd) log_debug("Finding element with wmd: {}.".format(rwmd)) try: wmd.meta.relations[k] = getattr( self.__wmd_finder, rwmd.meta["type"].name.lower())( rwmd).dispatcher.driver_element except ArjunaTimeoutError: raise GuiWidgetNotPresentError(self.__gui, rwmd) # = self.locate(v).dispatcher.driver_element log_debug( "Replaced relation {} with corresponding GuiElement in relations dict." .format(k)) return wmd
def __init__(self, mdict=None): from arjuna import log_debug log_debug("Input Meta Dict for Meta creation: {}".format( repr_dict(mdict))) temp_dict = not mdict and CIStringDict() or CIStringDict(mdict) self.__mdict = CIStringDict() self.__process_type(temp_dict) self.__process_relations(temp_dict) self.__process_filters(temp_dict) self.__process_settings(temp_dict) self.__mdict.update({ k: v for k, v in temp_dict.items() if k.lower() not in {"type", "relations", "settings", "filters"} }) log_debug("Meta dictionary is: {}".format(repr_dict(self.__mdict)))
def translate(cls, locator): from arjuna import log_debug rltype = locator.ltype rlvalue = locator.lvalue glvalue = None try: gltype = GenericLocateWith[rltype.upper()] except Exception as e: raise Exception( "Invalid locator across all automators: {}={}. Error: {}". format(rltype, type(rlvalue), str(e))) else: log_debug("Processing locator: Type: {}; Value: {}".format( str(gltype), rlvalue)) if gltype == GenericLocateWith.ELEMENT: glvalue = rlvalue elif gltype in cls.BASIC_LOCATORS: glvalue = rlvalue elif gltype in cls.NEED_TRANSLATION: glvalue = rlvalue gltype = cls.NEED_TRANSLATION[gltype] elif gltype in cls.XPATH_LOCATORS: glvalue = cls.XPATH_LOCATORS[gltype].format(rlvalue) gltype = GenericLocateWith.XPATH elif gltype == GenericLocateWith.POINT: glvalue = cls.NAMED_ARG_LOCATORS[gltype].format(**rlvalue) elif gltype in cls.NAMED_ARG_LOCATORS: glvalue = cls.NAMED_ARG_LOCATORS[gltype].format(**rlvalue) gltype = GenericLocateWith.CSS_SELECTOR elif gltype == GenericLocateWith.CLASSES: css_string = cls.__process_selector(rlvalue) glvalue = cls.__process_selector(rlvalue) gltype = GenericLocateWith.CSS_SELECTOR elif gltype == GenericLocateWith.TAGS: glvalue = " ".join(cls.__process_tags(rlvalue)) gltype = GenericLocateWith.CSS_SELECTOR elif gltype in { GenericLocateWith.NODE, GenericLocateWith.BNODE, GenericLocateWith.FNODE }: gltype, glvalue = cls.__translate_node(gltype, rlvalue) elif gltype == GenericLocateWith.AXES: gltype, glvalue = cls.__translate_axes(gltype, rlvalue) else: raise Exception("Locator not supported yet by Arjuna: " + rltype) return GuiGenericLocator(gltype, glvalue)
def __process_type(self, temp_dict): from arjuna import log_debug from arjuna.core.constant import GuiWidgetType if "type" in temp_dict: log_debug("Copying provided type from meta dict: {}".format( temp_dict["type"])) try: widget_type = temp_dict["type"] if not isinstance(widget_type, GuiWidgetType): self.__mdict["type"] = GuiWidgetType[widget_type.upper()] else: self.__mdict["type"] = temp_dict["type"] except Exception as e: raise Exception( "{} is not a valid Gui widget type.".format(widget_type)) else: self.__mdict["type"] = GuiWidgetType.ELEMENT
def _load_root_element(self): ''' Loads root element for GuiSection. Root element is always loaded by using GuiPage. Rest of the elements are loaded as nested elements in root. ''' if self.__root_meta: label, locator = self.__root_meta if self.__root_meta[0] != "anonymous": wmd = self._gui_def.get_wmd(label) from arjuna import log_debug log_debug("Loading Root Element {} for GuiSection: {}".format( label, self.label)) self.__root_element = self._wmd_finder.element(wmd) else: from arjuna import log_debug log_debug( "Loading Root Element with Locator {} for GuiSection: {}". format(str(locator), self.label)) self.__root_element = self._finder.locate(locator) self.__container = self.__root_element
def __determine_root(self, root_init): from arjuna import GuiWidgetDefinition root_label = None root_gns = self._gui_def.root_element_name if root_init: # root in __init__ as a Locator instead of GNS Label if isinstance(root_init, GuiWidgetDefinition): root_label = "anonymous" else: root_label = root_init else: root_label = root_gns from arjuna import log_debug log_debug( "Setting Root Element for {} Gui. Label: {}. Root in GNS: {}. Root in __init__: {}." .format(self.label, root_label, root_gns, root_init)) if root_label: root_label = root_label.lower().strip() return root_label, root_init else: return None
def find(self, dispatcher_call, wmd, context="ELEMENT"): from arjuna import log_debug log_debug("Finding with wmd: {}".format(str(wmd))) from arjuna import Arjuna found = False js_call_name = context == "ELEMENT" and "_find_element_with_js" or "_find_multielement_with_js" js_call = getattr(self.container, js_call_name) locators = wmd.locators if context != "ELEMENT": if "POINT" in {l.ltype.name for l in locators}: raise ConditionException( "With.POINT can be used only with GuiElement.") we = None for locator in locators: try: if locator.ltype.name == "POINT": # Assumption here is that this container is automator. size, dispatcher = js_call(locator.lvalue) elif locator.ltype.name == "JS": size, dispatcher = js_call(locator.lvalue) else: lvalue = locator.lvalue if locator.ltype.name == "XPATH": if not lvalue.startswith("."): lvalue = "." + lvalue size, dispatcher = dispatcher_call(locator.ltype.name, lvalue) return locator.ltype.name, locator.lvalue, size, dispatcher except WaitableError as e: we = e except Exception as f: raise f else: we = None if not found: raise GuiWidgetNotFoundError(*wmd.locators, container=self.__container)
def __getattr__(self, name): wmd = self.__gui_def.get_wmd(name) from arjuna import log_debug log_debug( "Finding element with label: {}, wmd: {} and fargs: {}".format( name, wmd, self.__fargs)) fmt_wmd = wmd.create_formatted_wmd(**self.__fargs) for k, v in fmt_wmd.meta.relations.items(): from arjuna.interact.gui.auto.finder.wmd import GuiWidgetMetaData if type(v) is str: log_debug( "Formatting related label {} in relations dict.".format(k)) rwmd = self.__gui_def.get_wmd(v) log_debug( "Retrieved GuiWidgetMetaData {} for label {} in relations dict." .format(rwmd, k)) log_debug("Formatting with args: {}".format(self.__fargs)) frwmd = rwmd.create_formatted_wmd(**self.__fargs) fmt_wmd.meta.relations[k] = self.__gns._locate_with_wmd( frwmd).dispatcher.driver_element log_debug( "Replaced label {} with corresponding GuiElement in relations dict." .format(k, frwmd)) return self.__gns._locate_with_wmd(fmt_wmd)
def __process_labels_in_relations(self, wmd): from arjuna import log_debug log_debug("Processing relationship for GuiWidgetMetaData: {} in gui: {}".format(wmd, self.__gui)) for k,v in wmd.meta.relations.items(): from arjuna.interact.gui.auto.finder.wmd import GuiWidgetMetaData if type(v) is str: log_debug("Triggering locating operation for label {} in relations dict.".format(k)) wmd.meta.relations[k] = getattr(self, v).dispatcher.driver_element log_debug("Replaced label {} with corresponding GuiElement in relations dict.".format(k)) return wmd
def find_element(cls, container, byType, byValue, *, relations=None, filters=None): from arjuna import log_debug log_debug(f"Finding element in container:{container} with wtype:{byType} and wvalue:{byValue} with relations: {relations} and filters: {filters}") elements = cls.find_elements(container, byType, byValue, relations=relations, filters=filters) return elements[0]
def execute_javascript(cls, driver, script, *args): from arjuna import log_debug log_debug("Executing JavaScript {} with args {}.".format(script, args)) return driver.execute_script(script, *args)
def select_tests(cls, pytest_items, pytest_config): ''' Select tests from items collected by pytest, based on Arjuna rules. Arguments: pytest_items: List of pytest `Item` objects. Each item represents a collected test function node. pytest_config: pytest Config object ''' from arjuna import log_debug def process_nodename(item): return item.name.split("[")[0] from arjuna import Arjuna from arjuna.core.error import ExclusionRuleMet, NoInclusionRuleMet from arjuna import C selector = Arjuna.get_test_selector() final_selection = [] deselected = [] qual_names = set() for item in pytest_items: nid = item.nodeid log_debug(f"Processing {nid} as collected by pytest") # if item.name.split("[")[0] == "test": # continue qual_name = None # For a test function # Root dir should be folder containing the module # E.g. check_config_03_create_conf.py::check_create_config[G] temp_full_path = os.path.join(pytest_config.rootdir, item.nodeid.split('::')[0]) full_dotted_notation = temp_full_path.replace("/", ".").replace( '\\', ".").replace(".py", "") full_dotted_notation = full_dotted_notation.replace("..", ".") project_name = C("project.name") project_index = full_dotted_notation.find(project_name + "." + "test") if project_index == -1: deselected.append(item) continue qual_name = full_dotted_notation[ project_index:] + "." + process_nodename(item) # if os.path.exists(temp_full_path): # if os.path.isfile(temp_full_path): # test_path_index = temp_full_path.find(os.path.join(C("project.root.dir"), "test")) # if test_path_index == -1: # continue # else: # dotted_root = str(pytest_config.rootdir).replace("/",".").replace('\\',"") # proj_suffix = dotted_root[dotted_root.find(project_name):] # qual_name = proj_suffix + "." + process_nodeid(item) + "." + process_nodename(item) # else: # qual_name = process_nodeid(item) + "." + process_nodename(item) # start_index = qual_name.find(project_name + "." + "test") # if start_index == -1: # if qual_name.startswith("test."): # qual_name = project_name + "." + qual_name # else: # deselected.append(item) # continue # else: # qual_name = qual_name[start_index:] try: selector.validate(Arjuna.get_test_meta_data(qual_name)) except (ExclusionRuleMet, NoInclusionRuleMet) as e: deselected.append(item) else: final_selection.append(item) if deselected: pytest_config.hook.pytest_deselected(items=deselected) pytest_items[:] = final_selection
def enhance_reports(cls, item, result): ''' Automatically add screenshot to HTML Report File. To be used in **pytest_runtest_makereport** hook in **conftest.py**. Args: item: **pytest**'s Item object result: **pytest**'s TestReport object. Note: - For taking the screenshot, it does a look up for a **screen_shooter** attribute in the object spaces in following order: - Function Space - Module Space - Session Space - The screen_shooter attribute should contain a **ScreenShooter** i.e. an object of a class that inherits from ScreenShooter class and completes its protocol. - This is a lenient hook. This means that if any exception happens in it, it ignores the exception and logs a warning message. ''' try: from arjuna import Arjuna, ArjunaOption, log_debug ignore_passed_for_screenshots = not Arjuna.get_config().value( ArjunaOption.REPORT_SCREENSHOTS_ALWAYS) ignore_passed_for_network = not Arjuna.get_config().value( ArjunaOption.REPORT_NETWORK_ALWAYS) include_images = True include_network = True html_plugin = cls._get_html_report_plugin(item) pytest_html = html_plugin report = result.get_result() log_debug("Node ID: {}".format(report.nodeid), contexts="report") log_debug("Stage: {}".format(report.when), contexts="report") extra = getattr(report, 'extra', []) # if ignore_fixtures: # if report.when == 'call': # return xfail = hasattr(report, 'wasxfail') if ignore_passed_for_screenshots and report.passed: include_images = False else: # if (report.skipped and xfail) and (report.failed and not xfail): # extra.append(pytest_html.extras.url(app.url)) try: screen_shooter = cls._get_protocol_object( item, "screen_shooter") except AttributeError: pass else: try: screen_shooter.take_screenshot(prefix=report.nodeid) except Exception: # any error in auto-screen shot is ignored. pass log_debug("Attempting to get network_recorder from request", contexts="report") try: network_recorder = cls._get_protocol_object( item, "network_recorder") except AttributeError as e: log_debug("No network_recorder", contexts="report") else: try: log_debug("Registering traffic", contexts="report") network_recorder.register() log_debug("Traffic registered.", contexts="report") except Exception as e: log_debug("Exception in registering network traffic: " + str(e), contexts="report") if ignore_passed_for_network and report.passed: include_network = False log_debug("Include images {}".format(include_images), contexts="report") log_debug("Include network {}".format(include_network), contexts="report") # When this place is reached by a resource that failed/erred or a test (irrespective of result) if (report.when in {"setup", "teardown"} and not report.passed) or report.when == "call": test_container = Arjuna.get_report_metadata() if test_container.has_content(): log_debug("Extra Content Found for HTML Report", contexts="report") extra_html = test_container.as_report_html( include_images=include_images, include_network=include_network) if extra_html: extra.append(pytest_html.extras.html(extra_html)) report.extra = extra # For fixtures with errors, failures, clean the resources. if report.when in {"setup", "teardown"}: if not report.passed: log_debug("Clearing report extras.", contexts="report") Arjuna.get_report_metadata().clear() else: log_debug("Clearing report extras.", contexts="report") Arjuna.get_report_metadata().clear() except Exception as e: from arjuna import log_warning log_warning("Error in enhance_reports hook: " + str(e), contexts="report") raise