12. Python Selenium and Appium Test Action Scripts

12.1. Introduction

This chapter will address how a user should set about testing a graphical user interface (GUI) using Vitaq with Python scripting. The GUI may be implemented using any one of a large number of different technologies ranging from web technologies (e.g. frameworks such as React that result in HTML) and mobile apps through to interfaces developed using a platform specific (e.g. Windows Forms) or language specific GUI toolkit (e.g. Tcl/Tk, JavaFX). In all cases it is necessary to be able to interact with the GUI elements (i.e. buttons, form fields etc.) through code, so that we can reproduce human interaction with the GUI. If such a mechanism is not already being used for testing, then there are Python packages available to control most GUI frameworks e.g. pywinauto for testing Windows Forms apps.

The focus of this chapter will be on website testing, which serves as a good example because it is a technology that most people are familiar with.

For web and mobile UI testing, we will demonstrate how to use the popular open-source framework of Selenium https://www.seleniumhq.org/ and the same principles apply for its mobile derivative Appium http://appium.io/.. Vitaq has built-in support for both these frameworks and provides simplified access to their main commands such as those to perform ‘click’, ‘write’, ‘read’ and ‘Send Keys’ user actions. Alternatively you can call the native Selenium Python methods https://selenium-python.readthedocs.io/api.html directly as shown in Native Selenium Python methods or a combination of both if preferred. We will initially use the Vitaq Selenium methods to interface to the web-based version of the SplitMe App we have been using as our example in the user guide.

Using Vitaq it is possible to create a single Test Activity diagram that captures many user journeys (the sequence of actions a user makes to navigate through a website to perform a certain task). Having created this Test Activity diagram, Vitaq will randomly create all of the possible user journeys, creating data for each action as it goes.

12.2. Test Interface File for Selenium

In order to make it easier to use Selenium and Appium we have developed some Python utilities. If you are an experienced Selenium user, then you can continue to use native Selenium, or use a mix. The first part of setting up any GUI testing is to specify how the code will find the elements to interact with. Selenium offers multiple methods for finding the UI elements https://selenium-python.readthedocs.io/locating-elements.html, which are typically looking for a specific attribute of the element in the DOM (Document Object Model – the html code you see when you use DevTools in your browser). It is important to find ways to identify the elements that will be as reliable as possible, so that your ‘element finders’ will not need to be changed frequently as the website is developed.

To simplify the setup and improve the robustness of testing with Selenium and Appium we provide a user definable Test Interface File. This file allows all of the element finders for the GUI to be stored in one place. As an example let us consider clicking a button in a web App called Submit using native Selenium, identified by its XPath, the command would look like:

self.parent.driver.click(self.driver.find_element_by_xpath(‘//*button[text()=”Submit”]’)

It can be seen that the code actually uses two Selenium commands to click Submit, one to find its XPath and one to click it. This means that if Submit is changed all its instances in the code have to be changed. This can lead to typo’s (long commands) and bugs as instances may be missed.

Using the Vitaq Selenium interface, we would define Submits XPath in the test interface file, the Selenium command above becomes:

self.parent.test_if.perform_action(“click”, “Submit”)

The test interface file is the only place where Submits XPath needs to be defined, unlike native Selenium where an element finder has to be included in each command. Therefore it is much easier to maintain the test script code.

As stated above the location specifications in the test interface file use the XPath selector https://developer.mozilla.org/en-US/docs/Web/XPath, which may at first seem complicated. To help with this, in the following sections we will go through multiple examples of using XPath to find elements. There is also a wealth of information about XPaths online. We have chosen XPaths as in our experience of developing these interfaces, there are always some elements that can only be found using XPaths and consequently, one has to learn to use them anyway.

The test interface file also provides other user definable parameters for setting up Selenium.

12.3. Creating the Test Interface File

Appendix 3 Test Interface File shows an example test interface file. You can copy and then customise this file to your needs, you just need to focus on the method called user_definitions. To start with there are some settings that specify how Selenium will work, the settings are as follows:

self.browser = "Chrome"

This is the name of the browser to use. Selenium supports Chrome, Edge, Firefox, Ie, Opera and Safari. Whatever browser is chosen the appropriate driver will need to be provided (see: https://selenium-python.readthedocs.io/api.html). If you want to have access to the Selenium driver to use native Selenium, then you will also need to add the following in the Start Action of the Test Activity (or similar for other browsers) like so:

self.driver = webdriver.Chrome()

You will also need to enter the URL of the starting point for the testing, normally the entry point to the web App. This command will need to be included no matter which method for accessing Selenium is used (native or Vitaq). When Selenium starts it will go to this URL.

self.start_url = 'https://splitme.net/en’

Some web Apps may exist within an iframe container within a website and it may therefore be necessary to switch to this iframe before testing can begin. When Selenium starts it will not only go to the URL (line above), but with the inclusion of line below will switch to its starting XPath.

self.start_xpath_string = None

The command below sets the timeout period for the Selenium interface to find an element. The Selenium Interface will continue to search for an element up to the specified time limit. The timeout period is in seconds, so Selenium will search for 5 seconds before returning. This avoids elements not being found due to them not existing or provides some additional time if needed for a slow website response. There is no penalty to use this, as the code will look every 200ms and exit when an element is found.

self.timeout = 5

The reporting verbosity level is set by the command below. Verbosity level goes from 0 to 10, the higher the number the more verbose the reporting output from the interface. Useful to set a high verbosity when developing a test, but best to dial it down again when the test is running. We have opted for a medium verbosity.

self.verbosity = 5

The controls parameter is a Python dictionary (dict) which allows the user to define reusable aliases for each element needed for testing. As stated earlier the search string are always based on XPaths. There are multiple ways that an element can be searched for, a description of some of these can be found in the following sections.

self.controls =

Important

The argument after the keyword is a string. For example below you see the keyword ‘username’: and then this is followed by the argument ‘//*[@id=”txtUsername”]’ In Python you have to be careful about not terminating a string too early, which will cause a Syntax error. The whole string needs to be encloded in quotes but if you have quotes in the inner part of the string then this will cause Python to raise a Syntax error. So make sure you enclose the whole string with single quotes and then use double quotes in the inner part of the argument string as shown here. 'username': '//*[@id="txtUsername"]',

12.4. Search for an element by its id

Searching by id is the best way to find an element, so long as it is unique. If it is unique, then we can unambiguously find the element and this is really what the resource id is for. However, Web Apps will often have non-unique ids or the id may be auto generated by the web development tool being used and may change from one day to the next. We would recommend that in a development situation, you have your developers allocate a unique id to your required test elements (widgets), if possible, as this is by far the most robust way to locate an element.

'username': '//*[@id="txtUsername"]',

This example shows the entry we will make in the Test Interface file. ‘username’ is the meaningful alias that we will use in the Test Action Scripts in Vitaq and the string to the right of the colon is the XPath search string. The individual elements break down as follows:

//* - means start at the very top level of the DOM (indicated by //) (normally the opening <html> tag) and search depth first (i.e. go to the bottom of every tree before moving to the next) in every tag type (indicated by *).

[@id - search for the id attribute

=”txtUsername”] - and return the element where the id is equal to ‘txtUsername’

There are a couple of things to note here. First Selenium offers a method called find_element_by_id and all you need to provide is the id you want to find (e.g. ‘txtUserName’), see below it will give exactly the same result as our approach.

driver.find_element_by_id("txtUsername")

The second point is that both methods will return the first id matching the search text string.

On the face of it looks easier to use the native Selenium method than the Vitaq approach of using XPaths but let us consider the example below:

Example if we have HTML that looks something like below and we want to find the second instance of id=”search_for_this_id”

<div id=”search_for_this_id” …>

<input id=”search_for_this_id” …>

</input>

</div>

Using the methods outlined above it will be the div id element that is returned and not the one we want which is the second instance, input id. This is because ‘//*’ specifies to search from the top of the DOM and look at every element type and return the first instance in the DOM. However, with XPath we have the ability to specify the type of element to look for, so we substitute input for * and the test interface file entry becomes:

'username': '//input[@id="txtUsername"]’,

Then we are saying that the search starts from the top element of the DOM, but only looks at “input” elements. In this way we will find the second instance of id=”search_for_this_id” and not the first that the native Selenium method will find.

Important

If the argument part of your Python dict keyword argument is not being recognised as a string, the Test Action Script editor will highlight the text with red and black. You need to make sure the whole line is shown in red text for all of the control keyword argument lines in the self.controls = { } dictionary otherwise you will get a syntax error because Python will not recognise the complete argument as a string.

12.5. Search for an element by its attribute

We can search through the DOM for any attribute of a tag (an attribute is any value inside the acute brackets (<>) of a tag). For example if we have HTML that looks like this:

<div name=”username” type=”data” class=”user”>

Fred Bloggs

</div>

Then name, type and class are attributes of the div tag and “Fred Bloggs” is the text of the div. In order to search by attribute we simply put the attribute name after the @ symbol e.g.

'source_storage': '//*[@name="storageId"]',

'submit': '//*[@type="submit"]',

In the first example, the search will be from the top of the DOM and it will find the first element (of any type) that has a “name” attribute with a value of “storageId”.

In the second example, the search will be from the top of the DOM in our web App and it will find the first button element that has a “type” attribute with a value of “submit”.

12.6. Search for exact text string match

It is also possible to search by the text of the tag, that is the text between the opening tag and the closing tag. For example if we have HTML that looks like this:

<div>Fred Bloggs</div>

then Fred Bloggs is the text. To search for the text we specify text() instead of using the @attribute syntax.

'No_Leave': '//*[text()="No Records Found"]', 'New_account': '//div[text()="New account"]', 'Web_Version': '//span[text()="Web Version"]',

The first example will search from the top of the DOM and it will find the first element (of any type) that has text that is (exactly) “No Records Found”.

The second example will search from the top of the DOM and it will find the first div element that has text that is (exactly) “New account”.

The third example will search from the top of the DOM and it will find the first span element that has text that is (exactly) “Web Version”.

12.7. Search for attribute type and partial text string

When searching for text or attribute value, it may not always be possible to search for the whole piece of text or value. This may be because we are searching for a paragraph of text that would be unwieldy to have in an XPath expression, or it may be that a part of the text/value changes depending on the context. E.g. if we had the following HTML:

<div>Hello, Fred Bloggs</div>

where, the name changes depending on who has logged in, then we could still search for the part that would remain unchanged. To search for a part of a text string we use the command contains, as the examples below show.

'expense_account':  '//*[contains(@id,"Belongtotheaccount")]',

'Fred_selector':  //*[contains(@text, "Fred")]',

'Check_Leave': '//*[contains(@href, "pim/viewEmployee/empNumber")]',

The first example will search from the top of the DOM and it will find the first element (of any type) that has an id that contains “Belongtotheaccount”.

The second example will search from the top of the DOM and it will find the first element (of any type) that has text that contains “Fred”.

The third example will search from the top of the DOM and it will find the first element that has an href attribute that contains “pim/viewEmployee/empNumber”.

12.8. Search for unknown text using text substitution

Sometimes the text in the element is unknown at the time you write the test interface file maybe because it is the text that a user is expected to enter. In this case we can pass text to the XPath search string from the Test Action Script and have that text value substituted for the placeholder “<text>”.

In the first example, the test interface file includes the line below.

'User_Entered_Text_Selector': '//*[@text="<text>"],

The test action script has the command:

self.parent.test_if.perform_action(“click”, text=”Fred Bloggs”)

In this case the <text> placeholder will be replaced with “Fred Bloggs” and the element that will be found will be the first element (of any type) that has text that is Fred Bloggs, and this element will be clicked.

In the second example the test interface file includes the line

'Account': '//span[contains(text(), "<text>")]',

The test action script has the command:

self.parent.test_if.perform_action(“get_value”, text=”.”)

then the <text> placeholder will be replaced with ”.” (the separator between pounds and pence) and the element that will be found will be the first span element that has text that contains “.”, and the value of this element will be read.

In the third example the test interface file includes:

'balance_by_amount': '//*[contains(text(), "<text>")]',

In the third example, if the test action script had the command:

SplitMe_app_balance = self.parent.test_if.perform_action('get_value', 'balance_by_amount', text='{}'.format(participant_balance))

Then the <text> placeholder will be replaced by the value of a variable called participant_balance and the XPath will search from the top of the DOM and it will find the first element (of any type) that has text that equals the value of the variable participant_balance.

Note: we are using the Python substitution syntax of ‘{}’.format() in this test action script line of text=’{}’.format(participant_balance)). So we substitute the vale of the variable participant_balance into the text=.

12.10. Using the Test Interface file in Vitaq

Having created the Test Interface file, we now need to look at how we use it in Vitaq. The first step is to define the name and location of the test interface file. To specify this, use the Config button on the tool bar. This will open a dialog box that allows you to navigate to the test interface file that you have created and select it.

_images/18_Vitaq_configuration.png

Vitaq uses a generic interface mechanism, which will allow for future developments.

In the examples below we will use the perform_action method of the provided Vitaq Selenium test interface for all of the actions we wish to perform. The perform_action method is part of the test interface which is initialised as part of the parent action, so we access it using:

self.parent.test_if.perform_action(tif_action,alias,args3,args4,..)

tif_action, is the predefined Selenium test interface action to be executed, for a list of these see Section 8.11. A test_action is always the first argument of a perform_action.

alias, is the alias for the XPath that the test_action is to act upon, as defined in the test interface file. An alias is always the second argument of a perform_action args3 and subsequent args are the arguments required by the test _action being executed.

For example:

self.parent.test_if.perform_action('click', 'Submit')

In this example the tif_action (first argument) is click with a predefined XPath (second argument) of Submit.

In order to describe the test interface file in more detail we will refer to the pre-defined Test Interface file Test Interface File that has been created to test the SplitMe web App.

12.11. Vitaq Selenium methods

  • click

  • exists

  • get_value

  • handle_alert

  • send_keys

  • set_value

  • set_window_size

  • set_window_position

  • switch_to_active

  • switch_to_default

  • switch_to_frame

  • switch_to_start_frame

To access these Vitaq Selenium methods we use the generic ‘test_if.perform_action’ method in the Test Action scripts to progress a ‘user journey’ (the path a user may take to reach their goal when using a particular website) through the App under test.

12.12. click (reference)

The click method sends a click event to the selected element and will be used as below in test action scripts:

self.parent.test_if.perform_action('click', <alias>, [text=<text>])

Where:

<alias> - is the required alias to an XPath string as defined in the test interface file [text=<text>] – is an optional keyword argument , that allows us to specify the value for <text> to be substituted into an XPath search string as described in xx above.

There is no return value from this method.

Example:

self.parent.test_if.perform_action('click', 'keypad_button’, text=’7’)

See Vitaq Selenium click method example click (example)

12.13. exists (reference)

The exists method checks if the specified element exists and will be used as follows in test action scripts:

returnVal = self.parent.test_if.perform_action('exists', <alias>, [text=<text>])

Where:

<alias> - is the required alias to an XPath string as defined in the test interface file [text=<text>] – is an optional keyword argument , that allows us to specify the value for <text> to be substituted into an XPath search string as described in xx above.

The return value from this method is True or False.

Example:

self.parent.test_if.perform_action('exists', 'submit_button')

12.14. set_value (reference)

The set_value method sets a value in the specified element and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('set_value', <alias>, value=<value>, [text=<text>])

Where:

<alias> - is the required alias to an XPath string as defined in the test interface file value=<value> - is a required keyword argument to specify the value that the element should be set to [text=<text>] – is an optional keyword argument , that allows us to specify the value for <text> to be substituted into an XPath search string as described in xx above.

There is no return value from this method.

Example:

self.parent.test_if.perform_action('set_value', 'UI_Value', value='{}'. format(self.variables.UI_Value()))

12.15. get_value (reference)

The get_value method gets a value from the specified element and will be used as follows from within Vitaq:

returnVal = self.parent.test_if.perform_action('get_value', <alias>, [text=<text>])

Where:

<alias> - is the required alias to an XPath string as defined in the test interface file value=<value> - is a required keyword argument to specify the value that the element should be set to [text=<text>] – is an optional keyword argument , that allows us to specify the value for <text> to be substituted into an XPath search string as described in xx above.

The return value from this method is the value from the element.

Example:

self.parent.test_if.perform_action('get_value', 'bill_total’)

12.16. send_keys (reference)

The send_keys method allows us to send special keys and key combinations to specified element and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('send_keys', <alias>, <keys>, [text=<text>])

Where:

<alias> - is the required alias to an XPath string as defined in the test interface file <keys> - is a required Python list of keys to be sent to the element e.g. [‘ALT’, ‘CTRL’, ‘DEL’] or [‘CTRL’, ‘a’] [text=<text>] – is an optional keyword argument , that allows us to specify the value for <text> to be substituted into an XPath search string as described in xx above.

There is no return value from this method.

Example:

self.parent.test_if.perform_action('send_keys', 'textarea', [‘CTRL’, ‘a’])

See Vitaq Selenium send_keys method example send_keys (example)

The full list of keys that can be sent and the value to use in the list is shown in the table below. In addition to this, any single character key can be sent e.g. ‘a’ for [‘CTRL’, ‘a’].

Keyboard’s Key

keys_to_send value

Add Key

ADD

Alt Key

ALT

Arrow Key - Down

ARROW_DOWN

Arrow Key - Up

ARROW_LEFT

Arrow Key - Left

ARROW_RIGHT

Arrow Key - Right

ARROW_UP

Backspace Key

BACKSPACE

Cancel Key

CANCEL

Clear Key

CLEAR

Command Key

COMMAND

Control Key

CONTROL

Decimal Key

DECIMAL

Delete Key

DEL

Divide Key

DIVIDE

DOWN Key

DOWN

End Key

END

Enter Key

ENTER

Equals Key

EQUALS

Escape Key

ESCAPE

Function Key F1

F1

Function Key F2

F2

Function Key F3

F3

Function Key F4

F4

Function Key F5

F5

Function Key F6

F6

Function Key F7

F7

Function Key F8

F8

Function Key F9

F9

Function Key F10

F10

Function Key F11

F11

Function Key F12

F12

Help Key

HELP

Home Key

HOME

Insert Key

INSERT

Left Key

LEFT

Left_Alt Key

LEFT_ALT

Left_Control Key

LEFT_CONTROL

Meta Key

META

Multiply Key

MULTIPLY

Null Key

NULL

Numpad0 Key

NUMPAD0

Numpad1 Key

NUMPAD1

Numpad2 Key

NUMPAD2

Numpad3 Key

NUMPAD3

Numpad4 Key

NUMPAD4

Numpad5 Key

NUMPAD5

Numpad6 Key

NUMPAD6

Numpad7 Key

NUMPAD7

Numpad8 Key

NUMPAD8

Numpad9 Key

NUMPAD9

PgDn Key

PAGE_DOWN

PgUp Key

PAGE_UP

Pause Key

PAUSE

Return Key

RETURN

Right Key

RIGHT

Semicolon key

SEMICOLON

Separator Key

SEPARATOR

Shift Key

SHIFT

Spacebar Key

SPACE

Subtract Key

SUBTRACT

Tab Key

TAB

Up Key

UP

12.17. handle_alert (reference)

The handle_alert method allows us to handle an alert dialog, by either dismissing or accepting it and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('handle_alert', [action=<action>])

Where: ``[action=<action>]` – is an optional keyword argument , that allows us to specify the action to take. <action> can either be ‘accept’ or ‘dismiss’. If not provided the default action is to dismiss, however, it is recommended to always provide this argument for code readability i.e. so the action is explicitly stated in the code.

There is no return value from this method.

Example:

self.parent.test_if.perform_action('handle_alert', action=’dismiss’)

12.18. set_window_position (reference)

The set_window_position method allows us to specify the position of the browser window we are interacting with and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('set_window_position', x=<x>, y=<y>)

Where:

x=<x> – is a required keyword argument , that allows us to specify the x coordinate for the top left corner of the browser window y=<y> - is a required keyword argument , that allows us to specify the y coordinate for the top left corner of the browser window

There is no return value from this method.

Example:

self.parent.test_if.perform_action(‘set_window_position’, x=‘0’, y=‘650’)

12.19. set_window_size (reference)

The set_window_size method allows us to specify the size of the browser window we are interacting with and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('set_window_position', width=<width>, height=<height>)

Where:

width=<width> – is a required keyword argument , that allows us to specify the width of the browser window height=<height> - is a required keyword argument , that allows us to specify the height of the browser window

There is no return value from this method.

Example:

self.parent.test_if.perform_action(‘set_window_size’, width=‘640’, height=‘780’)

12.20. switch_to_frame (reference)

An IFrame (Inline Frame) is an HTML element that allows rendering a HTML document within another HTML document on a website. If your web App under test has HTML <iframe></iframe> tags then you have IFrames inside your HTML. A web App can contain multiple IFrames and hence you will need to switch between them.

The switch_to_frame method allows us to switch to a specified frame and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('switch_to_frame', <alias>, [text=<text>])

Where:

<alias> - is the required alias to an XPath string as defined in the test interface file [text=<text>] – is an optional keyword argument , that allows us to specify the value for <text> to be substituted into an XPath search string as described in xx above.

There is no return value from this method.

Example:

self.parent.test_if.perform_action('switch_to_frame', 'frame2')

12.21. switch_to_active (reference)

The switch_to_active method allows us to the active/selected/focussed element and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('switch_to_active')

There are no additional arguments for this command

There is no return value from this method.

Example:

self.parent.test_if.perform_action('switch_to_active')

12.22. switch_to_default(reference)

The switch_to_default method allows us to the default frame (normally the top level) and will be used as follows from within Vitaq:

self.parent.test_if.perform_action('switch_to_default')

There are no additional arguments for this command

There is no return value from this method.

Example:

self.parent.test_if.perform_action('switch_to_default')

12.23. Testing the BillSplit ‘SplitMe’ Web App using Selenium

The first thing we need to do before we can start testing the SplitMe web App is to set up the browser and the test interface we are going to use. We do this by inserting the Python code lines below into the Activity Start Test Action Script.

# Set the web driver to be Chrome self.driver = webdriver.Chrome() # Print out the web driver to the Python Output window print('Driver: {}'.format(self.driver)) # Set the test interface with the SUT, the test interface and the driver self.test_if.create_interface('SUT', TestInterface, driver=self.driver)

As you can see the browser we are going to use is Chrome, Selenium does support all popular browsers.

Important

When-ever you invoke a webdriver, windows will start a process for it. Once the Test Activity is complete you need to make sure you close the webdriver. You can do this in the Activity end Test Action Script.

self.driver.quit()

Important

If the Test Activity stops in error, you will not be able to run the Vitaq Test Activity again until you stop the ‘webdriver’ process. The Python Output log window will show your error in the window log ending with VITAQ TEST ACTIVITY SCRIPT ENCOUNTERED AN ERROR. The error could be because Selenium cannot find the element you have defined in the Test Interface file or your Python Test Script has a mistake. The Test Activity will not have completed and hence will not have ‘quit’ the webdriver. So you need to shut down the process manually from your Task Manager window. If it is a chromedriver then look for chromedriver in the tasks. Most of the time it will be listed with the Vitaq test Activity.

Important

If you have stopped your Test Activity after an error and then clicked Run and the Python output window has these lines in red at the bottom. socket.error: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted then child process exited with code 1 and finally VITAQ TEST ACTIVITY SCRIPT ENCOUNTERED AN ERROR This is probably because you have not ‘stopped’ a running webdriver (chromedriver if you are using Chrome browser) process. You need to start your task manager (CTL-ALT-DEL and select Task Manager) then look for chromedriver process, select it, then click stop.

To test that we have everything set up correctly, we can run the Test Activity, what should happen is that a Chrome web page of SplitMe should pop up and then close.

Now that we have our environment set up we can start to test the SplitMe App, we will focus on the three main Selenium commands of ‘click’, ‘set_value’ and ‘get_value’.

12.24. click (example)

(See click (reference))

The first action in testing the SplitMe bill splitting app, is to click on the Try_SplitMe button on the starting screen of the app, so we need to write this command in to the New_Account test action script. First look in the Test Interface File and you will see the python ‘dictionary’ of self_control with key value pairs. The command for Try_SplitMe is on line 78 and has its XPath value pair defined to search through the DOM (//*) for the html name attribute in a span element called “Try SplitMe”.

This use of aliases like “Try_SplitMe” to identify the html element needed in Selenium (and Appium) makes using this open source framework much easier in Vitaq Test Action Script writing. Once you have created all of the aliases in the user_control dictionary in the Test Interface File, you can reference them in Test Action Scripts without having to think about XPath searches.

Important

We would recommend that you have your developers create the Test Interface File user-control dictionary for you and ensure they use easy to identify unique attributes like id’s and names throughout their html. Which makes creating the XPath much easier and more reliable.

self.parent.test_if.perform_action('click', 'Try_SplitMe')

Then we need to navigate to the New Account information screen. We do this by clicking, Web Version and then testAccountListMore, see below. To give the browser time to render the screen for the next action, we include a 2 second sleep, this ensures that the screen elements are ready for the next Selenium command.

self.parent.test_if.perform_action('click', 'Web_Version') time.sleep(2) self.parent.test_if.perform_action('click', 'testAccountListMore') time.sleep(2)

Then we need to ask Selenium to click on the New account text to create a New Account in the bill splitting app.

self.parent.test_if.perform_action('click', 'New_account') time.sleep(2)

12.25. set_value (example)

(See set_value (reference))

We now need to enter an account name into the user field for new account name text in the bill splitting app. We do this by using the set_value command. This is the command we use to enter values generated by Vitaq into test actions scripts. We will use a Vitaq selected name from the variable, new account_name.

self.parent.test_if.perform_action('set_value', 'new_account_name', \ value='{}'.format(self.parent.new_account_name))

Important

To enter values generated from Vitaq in the Test Action Script we use the set_value argument in the perform_action command. The first argument set_value, tells Vitaq that it will execute the set_value method in the app_specific interface. The second argument, new_account_name is an alias for the XPath search that we wish Selenium to perform. The third argument is the text data value we want to write into the field value =. To do this we use the python text substitution method of ‘{}’.format(text we want to insert into the curly braces).

12.26. get_value (example)

(See get_value (reference))

To get read values from your web (or mobile) App under test we use the get_value command, for example to read the balance from the SplitMe web app.

Balance = self.parent.test_if.perform_action('get_value', \ 'balance_by_amount', text='{}'.format(participant_balance))

12.27. send_keys (example)

(See send_keys (reference))

To send special keys into a field in the browser we use ‘send_keys’, followed by the HTML element alias from our Test Interface file that identifies the XPath of the element where the field is located and then the special key itself, such as shown below ENTER.

self.parent.test_if.perform_action('send_keys', 'new_participant_name', keys=['ENTER'])

If you want to send a combination of special keys such as CTRL-ALT-DEL or a special key and a letter key such as CTRL C, then you continue the python list in the key=[] argument

self.parent.test_if.perform_action('send_keys', 'my_element_alias', keys=['CONTROL', 'ALT', 'DELETE'])

self.parent.test_if.perform_action('send_keys', \ 'another_element_alias_from_my_test_interface_file', keys=['CONTROL','c'])

The full list of special keys supported by the Vitaq send_keys command is show below:

‘ADD’, ‘ALT’, ‘ARROW_DOWN’, ‘ARROW_LEFT’, ‘ARROW_RIGHT’, ‘ARROW_UP’, ‘BACKSPACE’, ‘BACK_SPACE’, ‘CANCEL’, ‘CLEAR’, ‘COMMAND’, ‘CONTROL’, ‘DECIMAL’, ‘DELETE’, ‘DIVIDE’, ‘DOWN’, ‘END’, ‘ENTER’, ‘EQUALS’, ‘ESCAPE’, ‘F1’, ‘F10’, ‘F11’, ‘F12’, ‘F2’, ‘F3’, ‘F4’, ‘F5’, ‘F6’, ‘F7’, ‘F8’, ‘F9’, ‘HELP’, ‘HOME’, ‘INSERT’, ‘LEFT’, ‘LEFT_ALT’, ‘LEFT_CONTROL’, ‘LEFT_SHIFT’, ‘META’, ‘MULTIPLY’, ‘NULL’, ‘NUMPAD0’, ‘NUMPAD1’, ‘NUMPAD2’, ‘NUMPAD3’, ‘NUMPAD4’, ‘NUMPAD5’, ‘NUMPAD6’, ‘NUMPAD7’, ‘NUMPAD8’, ‘NUMPAD9’, ‘PAGE_DOWN’, ‘PAGE_UP’, ‘PAUSE’, ‘RETURN’, ‘RIGHT’, ‘SEMICOLON’, ‘SEPARATOR’, ‘SHIFT’, ‘SPACE’, ‘SUBTRACT’, ‘TAB’, ‘UP’

12.28. set_window_size

To set the window size of the browser after opening, the Test Interface file has a method called set_window_size

self.parent.test_if.perform_action('set_window_size', width='640', height='780')

12.29. set_window_position

To set the window position of the browser after opening, the Test Interface file has a method called set_window_position

self.parent.test_if.perform_action('set_window_position', x='0', y='650')

12.30. How to switch between iFrames

An IFrame (Inline Frame) is an HTML element that allows rendering a HTML document within another HTML document on a website. If your web App under test has HTML <iframe></iframe> tags then you have IFrames inside your HTML. A web App can contain multiple IFrames and hence you will need to switch between them.

The Vitaq Test Interface File for Selenium contains methods to handle this, such as switch_to_start_frame:

self.parent.test_if.perform_action('switch_to_start_frame')

In this method you can see it takes an argument called self.start_xpath_string which is defined in the Test Interface File. In our referenced BillSplit_Test_Interface.py test Interface File we have ‘disabled’ this method by assigning it None. If you wanted to use this method you would define it in the Test Interface file with self.start_xpath_string = '//*[@class="canvas_iframe_util"]' where you would need to use the XPath search string that defines your iframe class rather than this example for ‘canvas_iframe_util’.

To switch to a specified frame:

self.parent.test_if.perform_action('switch_to_frame', 'frame_to_switch_to')

12.31. Native Selenium Python methods

If you want to directly use the Selenium python driver methods (as well as the Vitaq Test Interface methods) you need to add these lines into your Activity start Test Action Script

# This assigns the Chromedriver to our self.driver and then provides access to it from the Test Action Scripts self.driver = webdriver.Chrome() print('Driver: {}'.format(self.driver))

self.test_if.create_interface('SUT', TestInterface, driver=self.driver)

If you do not have driver=self.driver as an argument when you instantiate the self.test_if.create_interface, you will not be able to use native Selenium Python methods in Test Action Scripts. The Vitaq Test interface will automatically create the driver and then you will only be able to use the Vitaq Test Interface methods to control your web App.

As an example, let’s use native Selenium python methods for handling switching between iFrames. First we need to make sure we are in the default frame when entering a web App

# Use the native Selenium driver to move back to the html outside the iframe self.parent.driver.switch_to.default_content()

The switch_to.default_content() is a native Selenium python method which you will find in any Selenium Python tutorial. You can call any native Selenium method this way using the self.parent.driver.__________

Then to switch to the HTML inside the iframe of your web App we use driver.switch_to.iframe(self,frame reference) where the “<frame_reference>” parameter is the locator used to identify the IFrame. For example:

iframe = self.parent.driver.find_element_by_xpath('//*[@class="canvas_iframe_util"]') self.parent.driver.switch_to_frame(iframe)

The test script above first assigns a variable iframe to the locator used to identify the IFrame in the HTML where there is a class element called canvas_iframe_util. Then we use this iframe with the switch_to_frame(iframe) method as shown above.