Пример #1
0
class TestProjectLibrary:
    # CONSTANTS #
    ACCEPT = "ACCEPT"
    ROBOT_LIBRARY_SCOPE = "GLOBAL"
    ROBOT_LISTENER_API_VERSION = 3
    CHROME_NAMES = ["googlechrome", "chrome", "gc"]
    FIREFOX_NAMES = ["firefox", "ff"]
    IE_NAMES = ["internetexplorer", "ie"]
    # CONSTANTS END #

    def __init__(self):
        self.ROBOT_LIBRARY_LISTENER = self
        self.__default_screenshot_name = f'TestProject-{datetime.date.today().strftime("%Y_%m_%d_%H_%M_%S")}.png'
        self.__reporter = None
        self.library = SeleniumLibrary()
        os.environ["RFW_SUPPRESS_WARNINGS"] = "true"

    # TESTPROJECT #
    @keyword
    def init_testproject_driver(
        self,
        browser: str = "firefox",
        url: Optional[str] = None,
        timeout: Optional[int] = 5000,
        project_name: Optional[str] = "TestProject Robot",
        job_name: Optional[str] = "Robot Job",
        desired_capabilities: Union[str, dict, None] = None,
        disabled_reports: Optional[bool] = False,
        dev_token: Optional[str] = os.getenv("TP_DEV_TOKEN"),
    ):
        logger.console(f"Initializing TestProject Library for Robot v{definitions.get_lib_version()}...")

        # # Make sure development token is set
        # if not dev_token:
        #     BuiltIn().fatal_error(
        #         "******************************************************************************\n"
        #         "* TestProject Development token not set!                                     *\n"
        #         "* Please use the 'dev_token' argument of the 'init_testproject_driver'       *\n"
        #         "* keyword, or set the 'TP_DEV_TOKEN' environment variable.                   *\n"
        #         "******************************************************************************"
        #     )

        driver = None
        # Check if instance of Options to pass to the driver.
        if not isinstance(desired_capabilities, (ChromeOptions, FirefoxOptions, IeOptions)):
            desired_capabilities = self._build_capabilities(
                WebDriverCreator(os.getcwd())._parse_capabilities(capabilities=desired_capabilities, browser=browser), browser
            )
        # If headless, override and start a clean headless session.
        if "headless" in browser:
            type = browser.split("headless", 1)[1]
            if type == "chrome":
                desired_capabilities = ChromeOptions()
            elif type == "firefox":
                desired_capabilities = FirefoxOptions()
            else:
                raise ValueError("Headless is supported for FireFox and Chrome only")
            desired_capabilities.add_argument("--headless")
            browser = type
        if browser in self.FIREFOX_NAMES:
            driver = webdriver.Firefox(
                firefox_options=desired_capabilities,
                token=dev_token,
                projectname=project_name,
                jobname=job_name,
                disable_reports=disabled_reports,
            )
        elif browser in self.CHROME_NAMES:
            driver = webdriver.Chrome(
                chrome_options=desired_capabilities,
                token=dev_token,
                projectname=project_name,
                jobname=job_name,
                disable_reports=disabled_reports,
            )
        elif browser in self.IE_NAMES:
            driver = webdriver.Ie(
                ie_options=desired_capabilities,
                token=dev_token,
                projectname=project_name,
                jobname=job_name,
                disable_reports=disabled_reports,
            )
        elif browser == "edge":
            driver = webdriver.Edge(
                edge_options=desired_capabilities,
                token=dev_token,
                projectname=project_name,
                jobname=job_name,
                disable_reports=disabled_reports,
            )
        elif browser == "safari":
            driver = webdriver.Safari(
                desired_capabilities=desired_capabilities,
                token=dev_token,
                projectname=project_name,
                jobname=job_name,
                disable_reports=disabled_reports,
            )
        else:
            raise ValueError("Unsupported Browser, please look at the official TestProject library documentation")

        driver.report().disable_command_reports(True)
        driver.set_script_timeout(timeout)
        driver.report().step(message="Set timeout", description=f"Time out was set to {timeout}", passed=True)
        try:
            driver.get(url)
            driver.report().step(message=f"Navigated to {url}", description=f"Successfully navigated to {url}", passed=True)
        except Exception:
            driver.report().step(message=f"Failed to open {url}", description=f"Failed to open {url}", passed=True)
            raise
        self.library.register_driver(driver=driver, alias="testproject_driver")
        self.__reporter = driver.report()
        self.__reporter.exclude_test_names(["run_cli", "main"])

    def _build_capabilities(self, caps, browser_name):
        options = None
        if caps:
            _, value = caps.popitem()
            try:
                browser = value["browserName"]
            except Exception as e:
                logger.console("No browser name capability was set")
                raise e
            if browser in self.FIREFOX_NAMES:
                options = FirefoxOptions()
            elif browser in self.CHROME_NAMES:
                options = ChromeOptions()
            elif browser in self.IE_NAMES:
                options = IeOptions()
            elif browser == "edge":
                options = Options()
            elif browser == "safari":
                options = DesiredCapabilities.SAFARI.copy()
            for k, v in value.items():
                options.set_capability(k, v)
        else:
            if browser_name in self.FIREFOX_NAMES:
                caps = DesiredCapabilities.FIREFOX.copy()
                options = FirefoxOptions()
            elif browser_name in self.CHROME_NAMES:
                caps = DesiredCapabilities.CHROME.copy()
                options = ChromeOptions()
            elif browser_name in self.IE_NAMES:
                caps = DesiredCapabilities.INTERNETEXPLORER.copy()
                options = IeOptions()
            elif browser_name == "edge":
                caps = DesiredCapabilities.EDGE.copy()
                options = Options()
            elif browser_name == "safari":
                options = DesiredCapabilities.SAFARI.copy()
            for k, v in caps.items():
                options.set_capability(k, v)
        return options

    # TESTPROJECT END #

    # SELECT/UNSELECT #

    @keyword
    def select_from_list_by_value(self, xpath, *values):
        self.base(xpath, f'Values selected: "{values}"', f' Values:"{xpath}", List: "{values}"', *values)

    @keyword
    def select_all_from_list(self, locator):
        self.base(locator, f'Selected all values from "{locator}"', f'"{locator}"')

    @keyword
    def select_from_list_by_label(self, locator, *labels):
        self.base(locator, f'Selected labels "{labels}"', f'Labels:"{locator}", List: "{labels}"', *labels)

    @keyword
    def unselect_all_from_list(self, locator):
        self.base(locator, f'Unselected all from "{locator}"', f'"{locator}"')

    @keyword
    def unselect_from_list_by_index(self, locator, *indexes):
        self.base(locator, f'Unselected "{indexes}" from "{locator}"', f"Indexes: {indexes}, List: {locator}", *indexes)

    @keyword
    def unselect_from_list_by_value(self, locator, *values):
        self.base(locator, f'Unselected "{values}" from "{locator}"', f"Values: {values}, List: {locator}", *values)

    @keyword
    def unselect_from_list_by_label(self, locator, *labels):
        self.base(locator, f'Unselected "{labels}" from "{locator}"', f"Labels: {labels}, List: {locator}", *labels)

    @keyword
    def get_selected_list_label(self, locator):
        return self.base(locator, "label", f"List: {locator}")

    @keyword
    def get_selected_list_labels(self, locator):
        return self.base(locator, "labels", f"List: {locator}")

    @keyword
    def get_selected_list_value(self, locator):
        return self.base(locator, "value", f"List: {locator}")

    @keyword
    def get_selected_list_values(self, locator):
        return self.base(locator, "values", f"List: {locator}")

    @keyword
    def get_list_items(self, locator, values=False):
        return self.base(locator, "list items", f"List: {locator}", values)

    @keyword
    def list_selection_should_be(self, locator, *expected):
        self.base(locator, f'Selection is "{expected}"', f"List: {locator}", *expected)

    @keyword
    def list_should_have_no_selections(self, locator):
        self.base(locator, f'List "{locator}" has no selections', f"List: {locator}")

    @keyword
    def page_should_not_contain_list(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, "List is not in page", f"List: {locator}", message, loglevel)

    @keyword
    def page_should_contain_list(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, "List is in page", f"List: {locator}", message, loglevel)

    # SELECT/UNSELECT END #

    # SCREENSHOTS #
    @keyword
    def capture_page_screenshot(self, filename=None):
        if not filename:  # Generate default file name with time stamp
            filename = self.__default_screenshot_name
        return self.base("", "Screenshot captured file", f" {filename}", filename)

    @keyword
    def set_screenshot_directory(self, path):
        self.base("", "previous set directory", f" {path}", path)

    @keyword
    def capture_element_screenshot(self, locator, filename=None):
        if not filename:
            filename = self.__default_screenshot_name
        return self.base(locator, "element screenshot file", f"{filename}", filename)

    # SCREENSHOTS END #

    # ELEMENTS #
    @keyword(name="Get WebElement")
    def get_webelement(self, locator):
        return self.base(locator, "first web element by the given locator", f" {locator}")

    @keyword(name="Get WebElements")
    def get_webelements(self, locator):
        return self.base(locator, "list of web elements by the given locator", f" {locator}")

    @keyword
    def element_should_contain(self, locator, expected, message=None, ignore_case=False):
        self.base(locator, f'Element contains "{expected}"', f" {locator}", expected, message, ignore_case)

    @keyword
    def element_should_not_contain(self, locator, expected, message=None, ignore_case=False):
        self.base(locator, f'Element does not contain "{expected}"', f" {locator}", expected, message, ignore_case)

    @keyword
    def page_should_contain_element(self, locator, message=None, loglevel="TRACE", limit=None):
        return self.base(locator, f'Page contains "{locator}"', f" {locator}", message, loglevel, limit)

    @keyword
    def page_should_contain(self, text, loglevel="TRACE"):
        self.base("", f'Page contains "{text}"', f" {text}", text, loglevel)

    @keyword
    def locator_should_match_x_times(self, locator, x, message=None, loglevel="TRACE"):
        self.base(locator, f'"{locator}" matched "{x}"', f"Locator: {locator}, X: {x}", x, message, loglevel)

    @keyword
    def page_should_not_contain(self, text, loglevel="TRACE"):
        self.base("", f'Page did not contain "{text}"', f" {text}", text, loglevel)

    @keyword
    def page_should_not_contain_element(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f'Page did not contain "{locator}"', f" {locator}", message, loglevel)

    @keyword
    def assign_id_to_element(self, locator, id):
        self.base(locator, f'ID was assigned to "{id}"', f" {locator}", id)

    @keyword
    def element_should_be_disabled(self, locator):
        self.base(locator, "Element is disabled", f" {locator}")

    @keyword
    def element_should_be_enabled(self, locator):
        self.base(locator, "Element is enabled", f" {locator}")

    @keyword
    def element_should_be_focused(self, locator):
        self.base(locator, "Element is focused", f" {locator}")

    @keyword
    def element_should_be_visible(self, locator, message=None):
        self.base(locator, "Element is visible", f" {locator}", message)

    @keyword
    def element_should_not_be_visible(self, locator, message=None):
        self.base(locator, "Element is not visible", f" {locator}", message)

    @keyword
    def element_text_should_be(self, locator, expected, message=None, ignore_case=False):
        self.base(locator, f'Text is "{expected}"', f"Element: {locator}, Text: {expected}", expected, message, ignore_case)

    @keyword
    def element_text_should_not_be(self, locator, not_expected, message=None, ignore_case=False):
        self.base(
            locator,
            f'Text is not "{not_expected}"',
            f"Element: {locator}, Text: {not_expected}",
            not_expected,
            message,
            ignore_case,
        )

    @keyword
    def get_element_attribute(self, locator, attribute):
        return self.base(locator, "attribute", f"Element: {locator}", attribute)

    @keyword
    def element_attribute_value_should_be(self, locator, attribute, expected, message=None):
        self.base(
            locator,
            f'Attribute "{attribute}" contains {expected}',
            f"Element: {locator}, Attribute: {expected}",
            attribute,
            expected,
            message,
        )

    @keyword
    def get_horizontal_position(self, locator):
        return self.base(locator, "Horizontal position", f" {locator}")

    @keyword
    def get_element_size(self, locator):
        return self.base(locator, "size", f"Element: {locator}")

    @keyword
    def cover_element(self, locator):
        self.base(locator, f'Element Covered "{locator}"', f" {locator}")

    @keyword
    def get_value(self, locator):
        return self.base(locator, "value", f" {locator}")

    @keyword
    def get_text(self, locator):
        return self.base(locator, "Text", f" {locator}")

    @keyword
    def clear_element_text(self, locator):
        self.base(locator, "Text cleared", f" {locator}")

    @keyword
    def get_vertical_position(self, locator):
        return self.base(locator, "vertical position", f" {locator}")

    @keyword
    def click_button(self, locator, modifier=False):
        self.base(locator, f'Clicked button "{locator}"', f" {locator}", modifier)

    @keyword
    def click_image(self, locator, modifier=False):
        self.base(locator, f'Clicked image "{locator}"', f" {locator}", modifier)

    @keyword
    def click_link(self, locator, modifier=False):
        self.base(locator, f'Clicked link "{locator}"', f" {locator}", modifier)

    @keyword
    def click_element(self, locator, modifier=False, action_chain=False):
        self.base(locator, f'Clicked "{locator}"', f" {locator}", modifier, action_chain)

    @keyword
    def click_element_at_coordinates(self, locator, xoffset, yoffset):
        self.base(locator, f'Clicked "{locator}" at X:{xoffset} , Y:{yoffset}', f" {locator}", xoffset, yoffset)

    @keyword
    def double_click_element(self, locator):
        self.base(self, f'Double clicked "{locator}"', f" {locator}")

    @keyword
    def set_focus_to_element(self, locator):
        self.base(locator, f'Element "{locator}" was is focused', f" {locator}")

    @keyword
    def scroll_element_into_view(self, locator):
        self.base(locator, f'Element "{locator}" was scrolled into view', f" {locator}")

    @keyword
    def drag_and_drop(self, locator, target):
        self.base(locator, f'Element "{locator}" was dragged to "{target}"', f"Origin: {locator}, Target: {target}", target)

    @keyword
    def drag_and_drop_by_offset(self, locator, xoffset, yoffset):
        self.base(locator, f'Element "{locator}" was dragged to X:{xoffset} , Y:{yoffset}', f" {locator}", xoffset, yoffset)

    @keyword
    def mouse_down(self, locator):
        self.base(locator, f'Mouse down on: "{locator}"', f" {locator}")

    @keyword
    def mouse_out(self, locator):
        self.base(locator, f'Mouse out on: "{locator}"', f" {locator}")

    @keyword
    def mouse_over(self, locator):
        self.base(locator, f'Mouse over on: "{locator}"', f" {locator}")

    @keyword
    def mouse_up(self, locator):
        self.base(locator, f'Mouse up on: "{locator}"', f" {locator}")

    @keyword
    def open_context_menu(self, locator):
        self.base(locator, f'Context menu opened on: "{locator}"', f" {locator}")

    @keyword
    def simulate_event(self, locator, event):
        self.base(locator, f'Event: "{event}" was simulated on {locator}', f"Element: {locator}, Event: {event}", event)

    @keyword
    def press_key(self, locator, key):
        self.base(locator, f'Key pressed: "{key}"', f"Locator: {locator}, Key: {key}", key)

    @keyword
    def press_keys(self, locator=None, *keys):
        self.base(locator, f'Keys pressed: "{keys}"\non element found at "{locator}"', f"{keys}", keys)

    @keyword
    def get_all_links(self):
        return self.base("", "links", "")

    @keyword
    def mouse_down_on_link(self, locator):
        self.base(locator, f'Mouse was pressed on "{locator}"', f" {locator}")

    @keyword
    def page_should_contain_link(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, 'Page did contain link in "{}"', f" {locator}", message, loglevel)

    @keyword
    def page_should_not_contain_link(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f'Page did not contain link in "{locator}"', f" {locator}", message, loglevel)

    @keyword
    def mouse_down_on_image(self, locator):
        self.base(locator, f'Mouse was down on image found at "{locator}"', f" {locator}")

    @keyword
    def page_should_contain_image(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f'Page did contain "{locator}"', f" {locator}", message, loglevel)

    @keyword
    def page_should_not_contain_image(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f'Page did not contain "{locator}"', f" {locator}", message, loglevel)

    @keyword
    def get_element_count(self, locator):
        return self.base(locator, f'element "{locator}" count', f"Element: {locator}")

    @keyword
    def add_location_strategy(self, strategy_name, strategy_keyword, persist=False):
        self.base("", f"Strategy '{strategy_name} was added'", strategy_name, strategy_keyword, persist)

    @keyword
    def remove_location_strategy(self, strategy_name):
        self.base("", f"Strategy '{strategy_name}' was removed", f"Removed {strategy_name}", strategy_name)

    # ELEMENTS END #

    # ALERTS #
    @keyword
    def input_text_into_alert(self, text, action=ACCEPT, timeout=None):
        self.base("", f"Typed {text} into alert\nAction used: {action}", f"Text: {text}", text, action, timeout)

    @keyword
    def alert_should_be_present(self, text="", action=ACCEPT, timeout=None):
        self.base("", f"Action used: {action}", "", text, action, timeout)

    @keyword
    def alert_should_not_be_present(self, action=ACCEPT, timeout=0):
        self.base("", f"Action used: {action}", "", action, timeout)

    @keyword
    def handle_alert(self, action=ACCEPT, timeout=None):
        return self.base("", f"Alert handled with action: {action}", "", action, timeout)

    # ALERTS END #

    # COOKIES #
    @keyword
    def delete_all_cookies(self):
        self.base("", "Deleted all cookies", "")

    @keyword
    def delete_cookie(self, name):
        self.base("", "Deleted all cookies", f"{name}", name)

    @keyword
    def get_cookies(self, as_dict=False):
        return self.base("", "Cookies", "", as_dict)

    @keyword
    def get_cookie(self, name):
        return self.base("", "Cookie", f"{name}", name)

    @keyword
    def add_cookie(self, name, value, path=None, domain=None, secure=None, expiry=None):
        self.base("", f"Added cookie: {name} with value: {value}", f"{name}", name, value, path, domain, secure, expiry)

    # COOKIES END #

    # JAVASCRIPT #
    @keyword
    def execute_javascript(self, *code):
        return self.base("", f"Executed {code}", f"{code}", *code)

    @keyword
    def execute_async_javascript(self, *code):
        return self.base("", f"Executed {code} Asynchronously", f"{code}", *code)

    # JAVASCRIPT END #

    # RUN ON FAILURE #
    @keyword
    def register_keyword_to_run_on_failure(self, keyword):
        return self.base("", "Previous keyword", f"{keyword}", keyword)

    # RUN ON FAILURE END #

    # TABLE ELEMENT #
    @keyword
    def get_table_cell(self, locator, row, column, loglevel="TRACE"):
        return self.base(locator, "Cell text", f"{locator} at Row: {row}, Col: {column}", row, column, loglevel)

    @keyword
    def table_cell_should_contain(self, locator, row, column, expected, loglevel="TRACE"):
        self.base(
            locator,
            f"Cell at row: {row} and column {column} contained {expected}",
            f"{locator} at Row: {row}, Col: {column}",
            row,
            column,
            expected,
            loglevel,
        )

    @keyword
    def table_column_should_contain(self, locator, column, expected, loglevel="TRACE"):
        self.base(locator, f"Column {column} contained {expected}", f"{locator} Col: {column}", column, expected, loglevel)

    @keyword
    def table_footer_should_contain(self, locator, expected, loglevel="TRACE"):
        self.base(locator, f"Footer contained {expected}", f"Footer: {locator}, Expected: {expected}", expected, loglevel)

    @keyword
    def table_header_should_contain(self, locator, expected, loglevel="TRACE"):
        self.base(locator, f"Header contained {expected}", f"Header: {locator}, Expected: {expected}", expected, loglevel)

    @keyword
    def table_row_should_contain(self, locator, row, expected, loglevel="TRACE"):
        self.base(locator, f"Row {row} contained {expected}", f"{locator} Row: {row}", row, expected, loglevel)

    @keyword
    def table_should_contain(self, locator, expected, loglevel="TRACE"):
        self.base(locator, f"Table contained {expected}", f"Table: {locator}, Expected: {expected}", expected, loglevel)

    # TABLE ELEMENT END #

    # WAITING #
    @keyword
    def wait_for_condition(self, condition, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base("", f"Condition: '{condition}' was met {message}", f"{condition}", condition, timeout, error)

    @keyword
    def wait_until_location_is(self, expected, timeout=None, message=None):
        message = self._set_message(timeout)
        self.base("", f"Location was '{expected}' {message}", f"{expected}", expected, timeout, message)

    @keyword
    def wait_until_location_is_not(self, location, timeout=None, message=None):
        message = self._set_message(timeout)
        self.base("", f"Location was not '{location}' {message}", f"{location}", location, timeout, message)

    @keyword
    def wait_until_location_contains(self, expected, timeout=None, message=None):
        message = self._set_message(timeout)
        self.base("", f"Location contained '{expected}' {message}", f"{expected}", expected, timeout, message)

    @keyword
    def wait_until_location_does_not_contain(self, location, timeout=None, message=None):
        message = self._set_message(timeout)
        self.base("", f"Location does not contain '{location}' {message}", f"{location}", location, timeout, message)

    @keyword
    def wait_until_page_contains(self, text, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base("", f"Page contained '{text}' {message}", f" {text}", text, timeout, error)

    @keyword
    def wait_until_page_does_not_contain(self, text, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base("", f"Page does not contain '{text}' {message}", f" {text}", text, timeout, error)

    @keyword
    def wait_until_page_contains_element(self, locator, timeout=None, error=None, limit=None):
        message = self._set_message(timeout)
        self.base(locator, f"Page contained '{locator}' {message}", f" {locator}", timeout, error, limit)

    @keyword
    def wait_until_page_does_not_contain_element(self, locator, timeout=None, error=None, limit=None):
        message = self._set_message(timeout)
        self.base(locator, f"Page does not contain '{locator}' {message}", f" {locator}", timeout, error, limit)

    @keyword
    def wait_until_element_is_visible(self, locator, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base(locator, f"Element '{locator}' was visible {message}", f" {locator}", timeout, error)

    @keyword
    def wait_until_element_is_not_visible(self, locator, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base(locator, f"Element '{locator}' was not visible {message}", f" {locator}", timeout, error)

    @keyword
    def wait_until_element_is_enabled(self, locator, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base(locator, f"Element '{locator}' was enabled {message}", f" {locator}", timeout, error)

    @keyword
    def wait_until_element_contains(self, locator, text, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base(locator, f"Element '{locator}' contained {text} {message}", f" {text}", text, timeout, error)

    @keyword
    def wait_until_element_does_not_contain(self, locator, text, timeout=None, error=None):
        message = self._set_message(timeout)
        self.base(locator, f"Element '{locator}' does not contain {text} {message}", f" {text}", text, timeout, error)

    def _set_message(self, timeout):
        return "" if timeout is None else f"(timeout: {timeout} seconds)"

    # WAITING END #

    # WINDOW #
    @keyword
    def select_window(self, locator="MAIN", timeout=None):
        return self.base(locator, f"Switched to {locator}", "", timeout)

    @keyword
    def switch_window(self, locator="MAIN", timeout=None, browser="CURRENT"):
        return self.base(locator, f"Switched to {locator}", f"{browser}", timeout, browser)

    @keyword
    def close_window(self):
        self.base("", "Window closed", "")

    @keyword
    def get_window_handles(self, browser="CURRENT"):
        return self.base("", "Window Handles", "", browser)

    @keyword
    def get_window_identifiers(self, browser="CURRENT"):
        return self.base("", "Window Identifiers", "", browser)

    @keyword
    def get_window_names(self, browser="CURRENT"):
        return self.base("", "Window Names", "", browser)

    @keyword
    def get_window_titles(self, browser="CURRENT"):
        return self.base("", "Window Titles", "", browser)

    @keyword
    def get_locations(self, browser="CURRENT"):
        return self.base("", "All Locations", "", browser)

    @keyword
    def maximize_browser_window(self):
        self.base("", "Window Maximized", "")

    @keyword
    def get_window_size(self, inner=False):
        return self.base("", "Window size", "", inner)

    @keyword
    def set_window_size(self, width, height, inner=False):
        self.base("", f"Size set to {width}, {height}", f"Width: {width}, Height: {height}", width, height, inner)

    @keyword
    def get_window_position(self):
        return self.base("", "Window position", "")

    @keyword
    def set_window_position(self, x, y):
        self.base("", f"Position set to {x}, {y}", f"X: {x}, Y: {y}", x, y)

    # WINDOW END #

    # FRAMES #
    @keyword
    def select_frame(self, locator):
        self.base(locator, f"Switched from to {locator}", f"{locator}")

    @keyword
    def unselect_frame(self):
        self.base("", "Returned to main frame", "")

    @keyword
    def current_frame_should_contain(self, text, loglevel="TRACE"):
        self.base("", f"Current frame contains {text}", f"{text}", text, loglevel)

    @keyword
    def current_frame_should_not_contain(self, text, loglevel="TRACE"):
        self.base("", f"Current frame does not contain {text}", f"{text}", text, loglevel)

    @keyword
    def frame_should_contain(self, locator, text, loglevel="TRACE"):
        self.base(locator, f"{locator} contains {text}", f"Frame: {locator}, Text: {text}", text, loglevel)

    # FRAMES END #

    # FORM ELEMENT #
    @keyword
    def submit_form(self, locator=None):
        self.base(locator, "Form submitted", f"{locator}")

    @keyword
    def checkbox_should_be_selected(self, locator):
        self.base(locator, f"Checked box {locator} is selected", f"{locator}")

    @keyword
    def checkbox_should_not_be_selected(self, locator):
        self.base(locator, f"Checked box {locator} is not selected", f"{locator}")

    @keyword
    def page_should_contain_checkbox(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page contains checkbox {locator}", f"{locator}", message, loglevel)

    @keyword
    def page_should_not_contain_checkbox(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page does not contain checkbox {locator}", f"{locator}", message, loglevel)

    @keyword
    def select_checkbox(self, locator):
        self.base(locator, "Checkbox selected", f"{locator}")

    @keyword
    def unselect_checkbox(self, locator):
        self.base(locator, "Checkbox unselected", f"{locator}")

    @keyword
    def page_should_contain_radio_button(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page contains radio button {locator}", f"{locator}", message, loglevel)

    @keyword
    def page_should_not_contain_radio_button(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page does not contain radio button {locator}", f"{locator}", message, loglevel)

    @keyword
    def radio_button_should_be_set_to(self, group_name, value):
        self.base("", f"{group_name} was set to {value}", f"{value}", group_name, value)

    @keyword
    def radio_button_should_not_be_selected(self, group_name):
        self.base("", f"{group_name} was not selected", f"{group_name}", group_name)

    @keyword
    def select_radio_button(self, group_name, value):
        self.base("", f"{group_name} was set to {value}", f"{value}", group_name, value)

    @keyword
    def choose_file(self, locator, file_path):
        self.base(locator, f"File {file_path} was uploaded", f"{locator}", file_path)

    @keyword
    def input_password(self, locator, password, clear=True):
        self.base(locator, f"Typed {password} to {locator}", f"{password}", password, clear)

    @keyword
    def input_text(self, locator, text, clear=True):
        self.base(locator, f"Typed {text} to {locator}", f"{text}", text, clear)

    @keyword
    def page_should_contain_textfield(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page contains TextField {locator}", f"{locator}", message, loglevel)

    @keyword
    def page_should_not_contain_textfield(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page does not contain TextField {locator}", f"{locator}", message, loglevel)

    @keyword
    def textfield_should_contain(self, locator, expected, message=None):
        self.base(locator, f"TextField {locator} contains  {expected}", f"{expected}", expected, message)

    @keyword
    def textfield_value_should_be(self, locator, expected, message=None):
        self.base(locator, f"TextField {locator} value is {expected}", f"{expected}", expected, message)

    @keyword
    def textarea_should_contain(self, locator, expected, message=None):
        self.base(locator, f"TextArea {locator} contains {expected}", f"{expected}", expected, message)

    @keyword
    def textarea_value_should_be(self, locator, expected, message=None):
        self.base(locator, f"TextArea {locator} value is {expected}", f"{expected}", expected, message)

    @keyword
    def page_should_contain_button(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page contains button {locator}", f"{locator}", message, loglevel)

    @keyword
    def page_should_not_contain_button(self, locator, message=None, loglevel="TRACE"):
        self.base(locator, f"Page does not contain button {locator}", f"{locator}", message, loglevel)

    # FORM ELEMENT END #

    # BROWSER MANAGEMENT #
    @keyword
    def open_browser(self, url=None):
        if not self.__reporter:
            self.__reporter.step(
                description="Open Browser",
                message="This step is deprecated using TestProject library",
                passed=True,
                screenshot=False,
            )

        warnings.warn(
            "This is deprecated using TestProject Library, please look at the official documentation for examples.",
            DeprecationWarning,
            stacklevel=2,
        )
        logger.console("'Open Browser' is deprecated using TestProject Library, please see official documentation.")

    @keyword
    def switch_browser(self, index_or_alias):
        self.base("", f"Switched to {index_or_alias}", f"{index_or_alias}", index_or_alias)

    @keyword
    def get_browser_ids(self):
        return self.base("", "Browser Ids", "")

    @keyword
    def get_browser_aliases(self):
        return self.base("", "Browser Aliases", "")

    @keyword
    def get_session_id(self):
        return self.base("", "Session ID", "")

    @keyword
    def get_source(self):
        return self.base("", "Page Source", "")

    @keyword
    def get_title(self):
        return self.base("", "Page Title", "")

    @keyword
    def get_location(self):
        return self.base("", "Window URL", "")

    @keyword
    def location_should_be(self, url, message=None):
        self.base("", f"URL is: {url}", f"{url}", url, message)

    @keyword
    def location_should_contain(self, expected, message=None):
        self.base("", f"URL contains: {expected}", f"{expected}", expected, message)

    @keyword
    def log_location(self):
        return self.base("", "Location and logged it", "")

    @keyword
    def log_source(self, loglevel="INFO"):
        return self.base("", "Source and logged it", "", loglevel)

    @keyword
    def log_title(self):
        return self.base("", "Title and logged it", "")

    @keyword
    def title_should_be(self, title, message=None):
        return self.base("", f"Title is {title}", f"{title}", title, message)

    @keyword
    def go_back(self):
        self.base("", "Navigated Back", "")

    @keyword
    def go_to(self, url):
        self.base("", f"Navigated to {url}", f"{url}", url)

    @keyword
    def reload_page(self):
        self.base("", "Reloaded page", "")

    @keyword
    def get_selenium_speed(self):
        return self.base("", "Delay between each selenium command", "")

    @keyword
    def get_selenium_timeout(self):
        return self.base("", "Timeout between variuos keywords", "")

    @keyword
    def get_selenium_implicit_wait(self):
        return self.base("", "Implicit wait value", "")

    @keyword
    def set_selenium_speed(self, value):
        return self.base("", f"Selenium Speed set to {value}", f"{value}", value)

    @keyword
    def set_selenium_timeout(self, value):
        return self.base("", f"Timeout was set to {value}", f"{value}", value)

    @keyword
    def set_selenium_implicit_wait(self, value):
        return self.base("", f"Implicit wait set to {value}", f"{value}", value)

    @keyword
    def set_browser_implicit_wait(self, value):
        self.base("", f"Browser implicit wait set to {value}", f"{value}", value)

    @keyword
    def close_all_browsers(self):
        self.base("", "Closed all open browsers", "")

    @keyword
    def create_webdriver(self, driver_name, alias=None, kwargs={}, **init_kwargs):
        if not self.__reporter:
            self.__reporter.step(
                description="Create Webdriver",
                message="This step is deprecated using TestProject library",
                passed=True,
                screenshot=False,
            )

        warnings.warn(
            "This is deprecated using TestProject Library, please look at the offical documentation for examples.",
            DeprecationWarning,
            stacklevel=2,
        )
        logger.console("'Create WebDriver' is deprecated using TestProject Library, please see offical documentation.")

    # BROWSER MANAGEMENT END #

    # UTIL METHODS #
    def base(self, *args):
        func_name = inspect.stack()[1].function
        locator, message, description = "N/A"
        try:
            locator = args[0]  # Get the locator of the element
            message = args[1]  # Get the message for the __reporter
            description = args[2]  # Get the description for the __reporter
            # Remove message,description and locator
            args_as_list = list(args)
            args = tuple(args_as_list[3:])
            value = self.base_keyword_action(locator, *args, func_name=func_name)
            if not value:
                self.base_report(True, message=message, func_name=func_name, description=description)
            else:
                self.base_report(True, message=f"Returned value: {value}", func_name=func_name, description=description)
            return value
        except Exception as e:
            self.base_report(success=False, exception=e, func_name=func_name, description=description)
            raise

    def build_values(self, locator, *values):
        result_list = []
        if locator:
            result_list.append(locator)

        result_list = result_list + list(values)
        return result_list

    def convert(self, word):
        return " ".join(x.capitalize() for x in word.split("_"))

    def base_report(self, success, message="", func_name="", screenshot=False, exception=None, description=None):
        if self.__reporter is None:
            return

        if not func_name:
            func_name = inspect.stack()[1].function
        func_name = self.convert(func_name)
        if success:
            self.__reporter.step(
                description=f"{func_name}: {description}", message=message, passed=True, screenshot=True
            )
        else:
            if not message:
                message += f"Failure reason:\n'{exception}'"
            self.__reporter.step(description=f"{func_name}: {description}", message=message, passed=False, screenshot=True)

    def base_keyword_action(self, locator, *values, func_name=""):
        try:
            ret_value = ""
            if not func_name:  # Get name of calling Keyword
                func_name = inspect.stack()[1].function
            if not locator and not values:
                ret_value = self.library.run_keyword(func_name, (), {})
            elif not locator:
                ret_value = self.library.run_keyword(func_name, self.build_values(None, *values), {})
            elif not values:
                if func_name == "get_webelements":
                    func_name = "Get WebElements"
                if func_name == "get_webelement":
                    func_name = "Get WebElement"
                ret_value = self.library.run_keyword(func_name, [locator], {})
            else:
                ret_value = self.library.run_keyword(func_name, self.build_values(locator, *values), {})
            return ret_value
        except Exception as e:
            raise e

    # UTIL METHODS END #

    # LISTENERS #
    def _end_test(self, data, result):
        if self.__reporter is None:
            return

        self.__reporter.test(name=result.name, passed=result.passed)
Пример #2
0
class AirSelenium(AlertKeywords, BrowserManagementKeywords, CookieKeywords,
                  ElementKeywords, FormElementKeywords, FrameKeywords,
                  JavaScriptKeywords, RunOnFailureKeywords, ScreenshotKeywords,
                  SelectElementKeywords, TableElementKeywords, WaitingKeywords,
                  WindowKeywords):
    def __init__(self,
                 screenshot_root_directory='logs',
                 remote_url=ST.REMOTE_URL,
                 browser=ST.BROWSER,
                 headless=False,
                 alias=None,
                 device=None,
                 executable_path=None,
                 options=None,
                 service_args=None,
                 desired_capabilities=None):
        """
        启动浏览器类型可选: Firefox, Chrome, Ie, Opera, Safari, PhantomJS, 可模拟移动设备
        """
        if browser not in [
                'Firefox', 'Chrome', 'Ie', 'Opera', 'Safari', 'PhantomJS'
        ]:
            raise Exception(
                '浏览器类型不对, 仅可选: Firefox, Chrome, Ie, Opera, Safari, PhantomJS')
        self.remote_url = remote_url
        self.browser = browser
        self.headless = headless
        self.alias = alias
        self.device = device
        self.executable_path = executable_path
        self.options = options
        self.service_args = service_args
        self.desired_capabilities = desired_capabilities

        self.ctx = SeleniumLibrary(
            screenshot_root_directory=screenshot_root_directory)
        self.screenshot_directory = ST.LOG_DIR = self.ctx.screenshot_root_directory
        super(AirSelenium, self).__init__(self.ctx)

    @logwrap
    @allure.step
    def open_browser(
            self,
            url: Optional[str] = None,
            browser: str = "Chrome",
            alias: Optional[str] = None,
            remote_url: Union[bool, str] = False,
            headless: Optional[bool] = False,
            options: Any = None,
            device: Optional[str] = None,
            executable_path: Optional[str] = None,
            service_args: Union[dict, None, str] = None,
            desired_capabilities: Union[dict, None, str] = None) -> str:
        """
        启动浏览器类型可选: Firefox, Chrome, Ie, Opera, Safari, PhantomJS, 可模拟移动设备
        """
        if browser not in [
                'Firefox', 'Chrome', 'Ie', 'Opera', 'Safari', 'PhantomJS'
        ]:
            raise Exception(
                '浏览器类型不对, 仅可选: Firefox, Chrome, Ie, Opera, Safari, PhantomJS')

        remote_url = remote_url or self.remote_url
        browser = browser or self.browser
        headless = headless or self.headless
        alias = alias or self.alias
        device = device or self.device
        executable_path = executable_path or self.executable_path
        options or self.options
        service_args = service_args or self.service_args
        desired_capabilities = desired_capabilities or self.desired_capabilities

        if remote_url:
            if browser == 'Chrome':
                chrome_options = webdriver.ChromeOptions()
                chrome_options.add_argument('--no-sandbox')
                chrome_options.add_argument('--disable-setuid-sandbox')
                chrome_options.add_argument('--disable-dev-shm-usage')
                if headless:
                    chrome_options.add_argument('--headless')
                    chrome_options.add_argument('--disable-gpu')
                if device:
                    mobile_emulation = {'deviceName': device}
                    chrome_options.add_experimental_option(
                        'mobileEmulation', mobile_emulation)
                browser_options = chrome_options
            elif browser == 'Firefox':
                firefox_options = webdriver.FirefoxOptions()
                firefox_options.add_argument('--disable-dev-shm-usage')
                if headless:
                    firefox_options.add_argument('--headless')
                    firefox_options.add_argument('--disable-gpu')
                browser_options = firefox_options
            else:
                browser_options = options
            desired_capabilities = desired_capabilities or {}
            desired_capabilities['browserName'] = browser.lower()
            driver = WebRemote(command_executor=remote_url,
                               desired_capabilities=desired_capabilities,
                               options=options or browser_options)
            # ctx.create_webdriver(driver_name='Remote', alias=alias, command_executor=remote_url, options=options, desired_capabilities=desired_capabilities)
        elif browser == 'Chrome':
            chrome_options = webdriver.ChromeOptions()
            chrome_options.add_argument('--no-sandbox')
            chrome_options.add_argument('--disable-setuid-sandbox')
            if headless:
                chrome_options.add_argument('--headless')
                chrome_options.add_argument('--disable-gpu')
            if device:
                mobile_emulation = {'deviceName': device}
                chrome_options.add_experimental_option('mobileEmulation',
                                                       mobile_emulation)
            if executable_path:
                driver = WebChrome(executable_path=executable_path,
                                   options=options or chrome_options,
                                   service_args=service_args,
                                   desired_capabilities=desired_capabilities)
                # ctx.create_webdriver(driver_name=browser, alias=alias, executable_path=executable_path, options=options or chrome_options, service_args=service_args, desired_capabilities=desired_capabilities)
            else:
                driver = WebChrome(options=options or chrome_options,
                                   service_args=service_args,
                                   desired_capabilities=desired_capabilities)
                # ctx.create_webdriver(driver_name=browser, alias=alias, options=options or chrome_options, service_args=service_args, desired_capabilities=desired_capabilities)
        elif browser == 'Firefox':
            firefox_options = webdriver.FirefoxOptions()
            if headless:
                firefox_options.add_argument('--headless')
                firefox_options.add_argument('--disable-gpu')
            if executable_path:
                driver = WebFirefox(executable_path=executable_path,
                                    options=options or firefox_options,
                                    service_args=service_args,
                                    desired_capabilities=desired_capabilities)
                # ctx.create_webdriver(driver_name=browser, alias=alias, executable_path=executable_path, options=options or firefox_options, service_args=service_args, desired_capabilities=desired_capabilities)
            else:
                driver = WebFirefox(options=options or firefox_options,
                                    service_args=service_args,
                                    desired_capabilities=desired_capabilities)
                # ctx.create_webdriver(driver_name=browser, alias=alias, options=options or firefox_options, service_args=service_args, desired_capabilities=desired_capabilities)
        else:
            if executable_path:
                self.create_webdriver(
                    driver_name=browser,
                    alias=alias,
                    executable_path=executable_path,
                    service_args=service_args,
                    desired_capabilities=desired_capabilities)
            else:
                self.create_webdriver(
                    driver_name=browser,
                    alias=alias,
                    service_args=service_args,
                    desired_capabilities=desired_capabilities)
            driver = self.driver
        index = self.ctx.register_driver(driver=driver, alias=alias)
        if url: self.go_to(url)
        return index

    @logwrap
    @allure.step
    def close_browser(self):
        return super(AirSelenium, self).close_browser()

    @logwrap
    @allure.step
    def close_all_browsers(self):
        return super(AirSelenium, self).close_all_browsers()

    @logwrap
    @allure.step
    def switch_browser(self, index_or_alias: str):
        return super(AirSelenium, self).switch_browser(index_or_alias)

    @logwrap
    @allure.step
    def switch_window(self,
                      locator: Union[list, str] = "MAIN",
                      timeout: Optional[str] = None,
                      browser: str = 'CURRENT'):
        return super(AirSelenium, self).switch_window(locator=locator,
                                                      timeout=timeout,
                                                      browser=browser)

    @logwrap
    @allure.step
    def set_window_size(self, width: int, height: int, inner: bool = False):
        return super(AirSelenium, self).set_window_size(width,
                                                        height,
                                                        inner=inner)

    @logwrap
    @allure.step
    def choose_file(self, locator: Union[WebElement, str], file_path: str):
        return super(AirSelenium, self).choose_file(locator, file_path)

    @logwrap
    @allure.step
    def go_back(self):
        return super(AirSelenium, self).go_back()

    @logwrap
    @allure.step
    def press_key(self, locator: Union[WebElement, str], key: str):
        return super(AirSelenium, self).press_key(locator, key)

    @logwrap
    @allure.step
    def press_keys(self,
                   locator: Union[WebElement, None, str] = None,
                   *keys: str):
        return super(AirSelenium, self).press_keys(locator=locator, *keys)

    @logwrap
    @allure.step
    def select_checkbox(self, locator: Union[WebElement, str]):
        return super(AirSelenium, self).select_checkbox(locator)

    @logwrap
    @allure.step
    def select_radio_button(self, group_name: str, value: str):
        return super(AirSelenium, self).select_radio_button(group_name, value)

    @logwrap
    @allure.step
    def scroll_element_into_view(self, locator: Union[WebElement, str]):
        return super(AirSelenium, self).scroll_element_into_view(locator)

    @logwrap
    @allure.step
    def unselect_checkbox(self, locator: Union[WebElement, str]):
        return super(AirSelenium, self).unselect_checkbox(locator)

    @logwrap
    @allure.step
    def unselect_all_from_list(self, locator: Union[WebElement, str]):
        return super(AirSelenium, self).unselect_all_from_list(locator)

    @logwrap
    def find_element(self, locator, tag=None, required=True, parent=None):
        web_element = super(AirSelenium, self).find_element(locator=locator,
                                                            tag=tag,
                                                            required=required,
                                                            parent=parent)
        log_res = self._gen_screen_log(web_element)
        return web_element and Element(web_element, log_res)

    @logwrap
    @allure.step
    def air_click(self, v):
        """
        Perform the click action on the current page by image identification.

        Args:
            v: target to click, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be clicked.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(v, Template):
            _pos = loop_find(v, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos = v
        x, y = _pos
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(x, y).click().perform()
        time.sleep(1)
        return _pos

    @logwrap
    @allure.step
    def air_assert(self, v, msg=""):
        """
        Assert target exists on the current page.

        Args:
            v: target to touch, either a Template instance
        Raise:
            AssertionError - if target not found.
        Returns:
            Position of the template.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        return self.driver.assert_template(v=v, msg=msg)

    @logwrap
    @allure.step
    def air_double_click(self, v):
        """
        Perform the double click action on the current page by image identification.

        Args:
            v: target to double click, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be double clicked.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(v, Template):
            _pos = loop_find(v, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos = v
        x, y = _pos
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(x, y).double_click().perform()
        time.sleep(1)
        return _pos

    @logwrap
    @allure.step
    def air_context_click(self, v):
        """
        Perform the right click action on the current page by image identification.

        Args:
            v: target to right click, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be right clicked.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(v, Template):
            _pos = loop_find(v, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos = v
        x, y = _pos
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(x,
                                                 y).context_click().perform()
        time.sleep(1)
        return _pos

    @logwrap
    @allure.step
    def air_mouse_up(self, v):
        """
        Perform the mouse up action on the current page by image identification.

        Args:
            v: target to mouse up, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be mouse up.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(v, Template):
            _pos = loop_find(v, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos = v
        x, y = _pos
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(x, y).release().perform()
        time.sleep(1)
        return _pos

    @logwrap
    @allure.step
    def air_mouse_down(self, v):
        """
        Perform the mouse down action on the current page by image identification.

        Args:
            v: target to mouse down, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be mouse down.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(v, Template):
            _pos = loop_find(v, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos = v
        x, y = _pos
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(x,
                                                 y).click_and_hold().perform()
        time.sleep(1)
        return _pos

    @logwrap
    @allure.step
    def air_mouse_over(self, v):
        """
        Perform the mouse over action on the current page by image identification.

        Args:
            v: target to mouse over, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be mouse over.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(v, Template):
            _pos = loop_find(v, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos = v
        x, y = _pos
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(x, y).perform()
        time.sleep(1)
        return _pos

    @logwrap
    @allure.step
    def air_mouse_out(self, v):
        """
        Perform the mouse out action on the current page by image identification.

        Args:
            v: target to mouse out, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be mouse out.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(v, Template):
            _pos = loop_find(v, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos = v
        x, y = _pos
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(x, y).move_by_offset(
            0, 0).perform()
        time.sleep(1)
        return _pos

    @logwrap
    @allure.step
    def air_drag_and_drop(self, s, t):
        """
        Perform the drag and drop action on the current page by image identification.

        Args:
            v: target to drag and drop, either a Template instance or absolute coordinates (x, y)
        Returns:
            Finial position to be drag and drop.
        """
        if not isinstance(self.driver, (WebChrome, WebFirefox, WebRemote)):
            raise AssertionError(
                'Use this function, the driver is must be WebChrome, WebFirefox or WebRemote'
            )
        if isinstance(s, Template):
            _pos_s = loop_find(s, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos_s = s
        x_s, y_s = _pos_s
        if isinstance(t, Template):
            _pos_t = loop_find(t, timeout=ST.FIND_TIMEOUT, driver=self.driver)
        else:
            _pos_t = t
        x_t, y_t = _pos_t
        # pos = self.driver._get_left_up_offset()
        # pos = (pos[0] + x, pos[1] + y)
        self.driver.action_chains.move_by_offset(
            x_s, y_s).click_and_hold().move_by_offset(x_t,
                                                      y_t).release().perform()
        time.sleep(1)
        return _pos_s, _pos_t

    @logwrap
    @allure.step
    def click_element(self, locator, modifier=False, action_chain=False):
        super(AirSelenium, self).click_element(locator=locator,
                                               modifier=modifier,
                                               action_chain=action_chain)

    @logwrap
    @allure.step
    def click_link(self, locator, modifier=False):
        super(AirSelenium, self).click_link(locator=locator, modifier=modifier)

    @logwrap
    @allure.step
    def click_image(self, locator, modifier=False):
        super(AirSelenium, self).click_image(locator=locator,
                                             modifier=modifier)

    @logwrap
    @allure.step
    def click_button(self, locator, modifier=False):
        super(AirSelenium, self).click_button(locator=locator,
                                              modifier=modifier)

    @logwrap
    @allure.step
    def input_text(self, locator, text, clear=True):
        super(AirSelenium, self).input_text(locator=locator,
                                            text=text,
                                            clear=clear)

    @logwrap
    @allure.step
    def input_password(self, locator, password, clear=True):
        super(AirSelenium, self).input_password(locator=locator,
                                                password=password,
                                                clear=clear)

    @logwrap
    @allure.step
    def double_click_element(self, locator):
        super(AirSelenium, self).double_click_element(locator=locator)

    @logwrap
    @allure.step
    def page_should_contain(self, text, loglevel='TRACE'):
        super(AirSelenium, self).page_should_contain(text=text,
                                                     loglevel=loglevel)

    @logwrap
    @allure.step
    def page_should_not_contain(self, text, loglevel='TRACE'):
        super(AirSelenium, self).page_should_not_contain(text=text,
                                                         loglevel=loglevel)

    @logwrap
    @allure.step
    def open_context_menu(self, locator):
        super(AirSelenium, self).open_context_menu(locator=locator)

    @logwrap
    @allure.step
    def mouse_up(self, locator):
        super(AirSelenium, self).mouse_up(locator=locator)

    @logwrap
    @allure.step
    def mouse_down(self, locator):
        super(AirSelenium, self).mouse_down(locator=locator)

    @logwrap
    @allure.step
    def mouse_over(self, locator):
        super(AirSelenium, self).mouse_over(locator=locator)

    @logwrap
    @allure.step
    def mouse_out(self, locator):
        super(AirSelenium, self).mouse_out(locator=locator)

    @logwrap
    @allure.step
    def drag_and_drop(self, locator, target):
        super(AirSelenium, self).drag_and_drop(locator=locator, target=target)

    @logwrap
    @allure.step
    def drag_and_drop_by_offset(self, locator, xoffset, yoffset):
        super(AirSelenium, self).drag_and_drop_by_offset(locator=locator,
                                                         xoffset=xoffset,
                                                         yoffset=yoffset)

    @logwrap
    @allure.step
    def go_to(self, url):
        super(AirSelenium, self).go_to(url=url)

    def screenshot(self, file_path=None):
        if file_path:
            file = self.capture_page_screenshot(file_path)
            with open(file, 'rb') as fp:
                allure.attach(fp.read(), '截图{}'.format(file_path),
                              allure.attachment_type.PNG)
        else:
            if not self.screenshot_directory:
                file_path = "temp.png"
            else:
                file_path = os.path.join('', "temp.png")
            file = self.capture_page_screenshot(file_path)
            with open(file, 'rb') as fp:
                allure.attach(fp.read(), '截图{}'.format(file_path),
                              allure.attachment_type.PNG)
            screen = aircv.imread(file_path)
            return screen

    def _gen_screen_log(
        self,
        element=None,
        filename=None,
    ):
        if self.screenshot_directory is None:
            return None
        if filename:
            self.screenshot(filename)
        jpg_file_name = str(int(time.time())) + '.png'
        jpg_path = os.path.join('', jpg_file_name)
        # print("this is jpg path:", jpg_path)
        self.screenshot(jpg_path)
        saved = {"screen": jpg_file_name}
        if element:
            size = element.size
            location = element.location
            x = size['width'] / 2 + location['x']
            y = size['height'] / 2 + location['y']
            if "darwin" in sys.platform:
                x, y = x * 2, y * 2
            saved.update({"pos": [[x, y]]})
        return saved

    @property
    def log_dir(self):
        try:
            if os.path.isdir(self.screenshot_directory):
                return os.path.abspath(self.screenshot_directory)
            else:
                os.makedirs(self.screenshot_directory)
                return os.path.abspath(self.screenshot_directory)
        except RobotNotRunningError:
            return os.getcwd() if PY2 else os.getcwd()