A supercharged Python wrapper for Selenium
Full documentation can be located here: https://intuitivewebsolutions.github.io/PyWebRunner
You could use WebRunner to scrape a website, automate web tasks, or anything else you could imagine. It is easy to initialize and use. It's also compatible with BrowserStack using the command_executor and remote_capabilities examples on that page.
(Please note that you will need a subscription, username, and API key to make it work.)
pip install PyWebRunner
# Import WebRunner if you aren't going to assert anything.
# WebTester is a sub-class of WebRunner
from PyWebRunner import WebRunner
# Running headless FireFox is the default.
wr = WebRunner() # Defaults to xvfb=True, driver=FireFox
# If xvfb is not installed, it will be bypassed automatically.
# Start the browser instance.
wr.start()
# Navigate to a page.
wr.go('https://www.google.com/')
# Fill in a text field.
wr.set_value('#lst-ib', 'PyWebRunner')
wr.send_key('#lst-ib', 'ENTER')
# Click the link based on a (gross) CSS selector.
wr.click('#rso > div:nth-child(1) > div:nth-child(1) > div > h3 > a')
# Wait for the page to load.
wr.wait_for_presence('div.document')
# Are we there yet?
wr.is_text_on_page('A helpful wrapper for Selenium') # True
# Take a screenshot!
wr.screenshot('/tmp/screenshot1.png')
# Stop the browser instance.
wr.stop()
PyWebRunner supports running YAML scripts and includes the webrunner
command.
Let's say we made a YAML script for the above example and we called it script.yml
- go: https://www.google.com/
- set_value:
- "#lst-ib"
- PyWebRunner
- send_key:
- "#lst-ib"
- "ENTER"
- click: "#rso > div:nth-child(1) > div:nth-child(1) > div > h3 > a"
- wait_for_presence: div.document
- assert_text_on_page: A helpful wrapper for Selenium
- screenshot: /tmp/screenshot1.png
We can run it like so:
webrunner script.yml
...and it will behave identically to the Python-based example above.
YAML supports the use of the fake-factory library (if it is installed) as well as evals and python function calls. Though the YAML is not intended as a complete replacement for Python scripts, this does enable some pretty flexible scripts to run.
You might be asking yourself, what's the purpose of parsing YAML like this if you can just write Python and have access to all these things by default?
The answer is that I wanted a way to write purely data-driven, front-end tests. The benefits could be summarized as:
- It makes it possible to write a single loader script that grabs all the YAML files in a folder and runs them one at a time (or in parallel).
- The tests themselves could be served up from a single, remote web-server.
- Tests could be written by non-programmers with minimal training and effort.
- GUI tools can easily be created to write tests/automated tasks without needing any programming knowledge.
Consider the following example of registering an account:
# Go to the page.
- go: https://somesite/page.html
# Click the register link.
- click: "#register"
# Wait for the registration form.
- wait_for_presence: "#email"
# Set the email field to a freshly-generated fake email address:
- set_value:
- "#email"
- (( fake-email ))
# Create a fake password string.
- set_value:
- "#password"
- (( fake-password ))
# Reference the fake password we already generated.
- set_value:
- "#password-verify"
- (( vars|password ))
# Click the register button.
- click: "#register"
# Wait for the redirect and the confirmation div.
- wait_for_presence: "#confirmation-div"
# Assert that the registration was successful.
- assert_text_in_page: Your registration was successful!
Fake Data
This example makes use of the fake-factory/faker library to generate a fake email address as well as a password. Any data that is created using the (( )) syntax is automatically assigned to a variable list for reference later.
Installing Faker
To install prior to September 15th, 2016:
pip install fake-factory
To install after September 15th, 2016:
pip install faker
fake-factory / faker need only be installed for the YAML to support (( fake-* ))
tags.
*
can be any of the methods on the faker class. This list is extensive and is under active development. For more information, go to the fake-factory website:
https://faker.readthedocs.io/en/latest/
(( special ))
Items enclosed in double parentheses (( )) will be parsed in this special way upon the execution of the script.
Examples
- include: basic_setup.yml
- import: random.randint
- set_value:
- "#someinput"
- (( randint|1,2 ))
The previous example will import random.randint and use it to generate a value of either 1 or 2 and insert it into the #someinput element.
- import: random.choice
- set_value:
- "#someinput"
- (( choice|['frog','cat','bird'] ))
As you can see, the choice function doesn't take positional arguments like the randint function does. It needs a list of options.
What happens when we run a function more than once and we need to reference the second or third output?
- import: random.choice
- set_value:
- "#someinput-a"
- (( choice|['frog','cat','bird'] )) # bird
- set_value:
- "#someinput-b"
- (( choice|['frog','cat','bird'] )) # cat
- set_value:
- "#someinput-c"
- (( choice|['frog','cat','bird'] )) # cat (again)
- assert_value_of_element:
- "#someinput-a"
- (( vars|choice )) # The "choice" array. Defaults to index 0.
- assert_value_of_element:
- "#someinput-b"
- (( vars|choice|1 )) # The "choice" array index 1.
- assert_value_of_element:
- "#someinput-c"
- (( vars|choice|2 )) # The "choice" array index 2.
OK, how about values on the page itself? Is there any way to obtain and reference them from inside a YAML WebRunner script?
- value_of: "#my-input-element" # Set the value.
- set_value:
- "#someinput-a"
- (( vars|value_of|0 )) # Use the value at index 0.
- text_of: "#my-div-element" # Set the text to a variable.
- set_value:
- "#someinput-b"
- (( vars|text_of|0 )) # Use the text value at index 0.
This library also has first-class support for BrowserStack. Using it is not much different than the examples above.
from PyWebRunner import WebRunner
# Change any of these values to valid ones.
desired = {
'browser': 'Edge',
'browser_version': '13.0',
'os': 'Windows',
'os_version': '10',
'resolution': '1440x900'
}
# Make sure you plug in your own USERNAME and API_KEY values here.
wr = WebRunner(desired_capabilities=desired,
command_executor='http://USERNAME:API_KEY@hub.browserstack.com:80/wd/hub',
driver='Remote')
wr.start()
wr.go('http://google.com')
# ... Etc.
WebTester inherits WebRunner so it has all the same methods that WebRunner has but it adds some additional methods that are useful for testing.
- assert_alert_not_present
- assert_alert_present
- assert_checked
- assert_element_contains_text
- assert_element_has_class
- assert_element_not_has_class
- assert_exists
- assert_found
- assert_not_checked
- assert_not_found
- assert_not_visible
- assert_text_in_element
- assert_text_in_elements
- assert_text_in_page
- assert_text_not_in_page
- assert_url
- assert_value_of_element
- assert_visible