2
\$\begingroup\$

I'm looking for optimization between these files, and simplification. If something already exists to do what I have a keyword do, I'd like to know about it. Loops.py is located in one of my other posts (Python - Robot Framework Loops). o_o.py is down below. The idea behind this is to shorten the amount of code required to write Robot Framework commands and have universal waits after most actions (clears up a lot of timing issues before they start and can be set to 0 for speed).

*** Settings ***
Library     ExtendedSelenium2Library
Library     Collections
Library     OperatingSystem
Library     Loops.py
Library     o_o.py

*** Variables ***
${ABS_PATH}     C:/Users/user1/PycharmProjects
${BROWSER}      chrome
${PROJECT}      ProjectName
${SLOW_TIME}    0.5s

*** Keywords ***
Check and Cancel
    #   EXAMPLE USAGE
    #   Check and Cancel    id=elementId    blank   Ok
    #   Check and Cancel    id=firstId  id=secondId
    #   NOTE:   Make sure that if you're only clicking one element you put "blank" in the second cell.
    [Arguments]     ${click_one}    ${click_two}='blank'    ${cancel}='Cancel'
    Click Element   ${click_one}
        Slow Down   ${SLOW_TIME}
    Run Keyword Unless  '${click_two}'=='blank'     Click Element   ${click_two}
    Run Keyword Unless  '${click_two}'=='blank'         Slow Down   ${SLOW_TIME}
    Click Button    ${cancel}
        Slow Down   ${SLOW_TIME}

Click Dom List
    #   EXAMPLE USAGE
    #   Click Dom List  "select"    1   "option"
    #   NOTE:   Since this uses the dom, you may need to use the browser's console feature in order to find the index.
    #   NOTE:   This allows injections on purpose for more advanced users
    [Arguments]     ${first_tag}    ${first_index}    ${second_tag}
    ${max_num}=     Execute Javascript  return document.getElementsByTagName(${first_tag})[${first_index}].getElementsByTagName(${second_tag}).length;
    :FOR    ${INDEX}    IN RANGE    0   ${max_num}
    \  Click Element   dom=document.getElementsByTagName(${first_tag})[${first_index}].getElementsByTagName(${second_tag})[${INDEX}]

Click Dom Tag
    #   EXAMPLE USAGE
    #   Click Dom Tag   "button"    1
    #   NOTE:   Since this uses the dom, you may need to use the browser's console feature in order to find the index.
    #   NOTE:   This allows injections on purpose for more advanced users
    [Arguments]     ${tag}      ${index}
    Click Element   dom=document.getElementsByTagName(${tag})[${index}]

Click Element by Text
    #   EXAMPLE USAGE
    #   Click Element by Text   "text on the element"   id="midlevellocationoftext"     button
    #   NOTE:   Does not account for extra spaces at the beginning or end, text must be exact
    #   NOTE:   Allows for injections on purpose to allow user to be more exact with their location
    [Arguments]     ${text}     ${location}=*   ${elementtype}=*
    Click Element   xpath=//*[@${location}]//${elementtype}[text()=${text}]

Click First HREF
    #   EXAMPLE USAGE
    #   Click First HREF    hreftext
    #   NOTE:   This clicks the first instance of the given HREF on the page
    [Arguments]     ${href}
    Click Link      ${href}

Dismiss Alert If Present
    #   EXAMPLE USAGE
    #   Dismiss Alert If Present
    #   NOTE:   This keyword never fails
    Run Keyword and Ignore Error    Dismiss Alert

Dom Tag
    #   EXAMPLE USAGE
    #   ${usable_variable} =    Dom Tag     li      1
    [Arguments]     ${tag}      ${index}
    ${dom} =    Convert to String   dom=document.getElementsByTagName(${tag})[${index}]
    [Return]    ${dom}

Open Chrome
    #   EXAMPLE USAGE
    #   Open Chrome     www.google.com
    [Arguments]     ${HOMEPAGE}
    ${options}  Evaluate    sys.modules['selenium.webdriver'].ChromeOptions()   sys
    Call Method     ${options}  add_argument    --start-maximized
    Create WebDriver    Chrome  chrome_options=${options}
    Go To   ${HOMEPAGE}

Pause
    #   EXAMPLE USAGE
    #   Pause   ${SLOW_TIME}
    [Arguments]     ${SLOW_TIME}
    Slow Down       ${SLOW_TIME}
    Slow Down       ${SLOW_TIME}
    Slow Down       ${SLOW_TIME}
    Slow Down       ${SLOW_TIME}

Reset Test Suite
    #   EXAMPLE USAGE
    #   REQUIREMENTS:   Must have the local resource file imported first with ${HOMEPAGE} defined
    #   Reset Test Suite    ${HOMEPAGE}     ${BROWSER}
    [Arguments]     ${HOMEPAGE}     ${BROWSER}
    Close All Browsers
    Register Keyword to Run on Failure  Nothing
    ${chrome_check} =   Run Keyword and Return Status   Should Be Equal As Strings  ${BROWSER}  chrome
    Run Keyword If      ${chrome_check}     Open Chrome     ${HOMEPAGE}
    Run Keyword Unless  ${chrome_check}     Open Browser    ${HOMEPAGE}     ${BROWSER}
    Wait Until Keyword Succeeds     1 minute    0.01s   Verify Location     ${HOMEPAGE}


Slow Down
    #   EXAMPLE USAGE
    #   Slow Down   ${SLOW_TIME}
    [Arguments]     ${SLOW_TIME}
    Sleep           ${SLOW_TIME}

Stop
    #   EXAMPLE USAGE
    #   Stop
    Sleep   9999s

Suite Auto-Setup
    #   EXAMPLE USAGE
    #   REQUIREMENTS:   Must have the local resource file imported first with ${HOMEPAGE} defined
    #   Suite Auto-Setup    ${HOMEPAGE}     ${BROWSER}
    [Arguments]     ${HOMEPAGE}     ${BROWSER}
    Register Keyword to Run on Failure  Nothing
    ${chrome_check} =   Run Keyword and Return Status   Should Be Equal As Strings  ${BROWSER}  chrome
    Run Keyword If      ${chrome_check}     Open Chrome     ${HOMEPAGE}
    Run Keyword Unless  ${chrome_check}     Open Browser    ${HOMEPAGE}     ${BROWSER}
    Wait Until Keyword Succeeds     1 minute    0.01s   Verify Location     ${HOMEPAGE}

Suite Auto-Teardown
    #   EXAMPLE USAGE
    #   Suite Auto-Teardown
    Log to Console  Test Suite Complete
    Log to Console  ==============================================================================
    Close All Browsers

Toggle
    #   EXAMPLE USAGE
    #   Toggle  id=toggleElement
    #   Toggle  id=dropdownId   id=toggleElement
    #   NOTE:   This can be used in any circumstance where you want to toggle an option in a dropdown list.
    #   NOTE:   This can be used in any circumstance where you want to toggle two options simultaneously.
    [Arguments]     ${ele1}     ${ele2}='blank'
    Click Element   ${ele1}
        Slow Down   ${SLOW_TIME}
    Run Keyword Unless  ${ele2}=='blank'    Click Element   ${ele2}
    Run Keyword Unless  ${ele2}=='blank'    Slow Down   ${SLOW_TIME}
    Click Element   ${ele1}
        Slow Down   ${SLOW_TIME}
    Run Keyword Unless  ${ele2}=='blank'    Click Element   ${ele2}
    Run Keyword Unless  ${ele2}=='blank'    Slow Down   ${SLOW_TIME}

Verify Element Is Ready
    #   EXAMPLE USAGE
    #   Verify Element Is Ready     id=myElementId
    #   Click Element               id=myElementId
    #   NOTE: Use before every single click.
    [Arguments]                     ${element}
    Variable should exist           ${element}
    Wait until element is visible   ${element}
    Element should be visible       ${element}
    Wait until element is enabled   ${element}

Verify Location
    #   EXAMPLE USAGE
    #   Verify Location     www.google.com
    [Arguments]                 ${desired_url}
    ${current_url}=             Get Location
    Should Be Equal As Strings  ${current_url}  ${desired_url}

o_o.py

# robot.libraries.BuiltIn is needed to execute javascript
# pyautogui is what is used to click on screen coordinates after calculation
# so, "pip install robot" and "pip install pyautogui" are both needed before this script can run
from robot.libraries.BuiltIn import BuiltIn
import pyautogui as pag


class o_o(object):
    """
        This class manages utilities for robot framework tests

        Dependencies: robot, pyautogui
    """

    def __init__(self):
        # Instantiate ExtendedSelenium2Library
        self.selenium_lib = BuiltIn().get_library_instance('ExtendedSelenium2Library')

    def click_by_image(self, image_name):
        """
        Click the center of a rectangle on the screen given the
        image_name of the image rectangle to search for
        :param image_name: Path to image
        """
        pag.click(pag.locateCenterOnScreen(str(image_name)))

    def click_by_locator(self, locator):
        """
        Click the center of web element giv
        :param locator: The locator for a web element conforming to Robot Framework
                        syntax.
                        See http://robotframework.org/ExtendedSelenium2Library/ExtendedSelenium2Library.html#Locating%20or%20specifying%20elements
        """
        element_coordinates = self._get_element_screen_coordinates(str(locator))

        pag.click(element_coordinates['x'], element_coordinates['y'])

    def double_click_by_image(self, image_name):
        """
        Double-click the center of a rectangle on the screen given the
        image_name of the image rectangle to search for
        :param image_name: Path to image
        """
        pag.doubleClick(pag.locateCenterOnScreen(str(image_name)))

    def _get_element_screen_coordinates(self, locator):
        #     Find coordinates of HTML element on the computer screen by
        #     factoring in the position of element in browser and the position
        #     of browser in screen.
        # :param locator: The locator of the HTML element, ex: id=theId
        # :return: HTML element coordinates relative to computer screen
        gui_screen_size = pag.size()
        javascript_screen_size = (self.selenium_lib.execute_javascript('return screen.width'),
                                  self.selenium_lib.execute_javascript('return screen.height'))
        screen_size_multiple = (gui_screen_size[0] / float(javascript_screen_size[0]),
                                gui_screen_size[1] / float(javascript_screen_size[1]))
        browser_toolbar_height = 'window.outerHeight - window.innerHeight'

        # Use screenX/screenY or screenLeft/screenTop based on browser support
        window_position = {
                            'x': self.selenium_lib.execute_javascript('return window.screenX ? \
                                                                        window.screenX : window.screenLeft'),
                            'y': self.selenium_lib.execute_javascript('return (window.screenY ? \
                                                                        window.screenY : window.screenTop) + ' +
                                                                      browser_toolbar_height)
                           }
        element_position_on_browser = {
                                        'x': self.selenium_lib.get_horizontal_position(str(locator)),
                                        'y': self.selenium_lib.get_vertical_position(str(locator))
                                       }
        element_size = self.selenium_lib.get_element_size(str(locator))
        return {
            'x': (window_position['x'] + element_position_on_browser['x']
                  + element_size[0]/2.0) * float(screen_size_multiple[0]),
            'y': (window_position['y'] + element_position_on_browser['y']
                  + element_size[1]/2.0) * float(screen_size_multiple[1])
        }

Any advice/comments/criticism on readability, optimization, durability, portability, or usefulness (or lack thereof) is more than welcome. Again, some of these just make the Robot Framework code in the regular Resource files shorter.

As an additional note, if you happen to have the main resource called, you have automatic access to the libraries and resources that it calls.

\$\endgroup\$
  • \$\begingroup\$ Why don't you get 'ExtendedSelenium2Library' when initiating your class ? \$\endgroup\$ – keepAlive Oct 11 '17 at 14:20
  • \$\begingroup\$ I'm not sure... must have been a ghost from a previous iteration. Fixed now. \$\endgroup\$ – Brandon Olson Oct 11 '17 at 23:50
  • \$\begingroup\$ Note that since __init__ is called just after the instantiation, you are (almost) sure that self.selenium_lib will never be None. Do you plan to use o_o as ancestor to other classes (without calling super, which would not be a traditional/good practice though) ? If not, you can simply remove if self.selenium_lib is None:. \$\endgroup\$ – keepAlive Oct 12 '17 at 2:26
  • \$\begingroup\$ Okay, I was thinking that might be possible, but I wasn't entirely sure. \$\endgroup\$ – Brandon Olson Oct 12 '17 at 2:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.