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.

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 filevalue=<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 filevalue=<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.