WebDriver can handle any type of interaction with a web page. However, it is a complex API. Thankfully, it's similar between different programming languages.
If you ever get stuck, I recommend reading the Python docs online.
Here are a few of the WebDriver methods and properties I find myself calling frequently.
The left column shows calls directly from the WebDriver instance, which we named browser
in our code.
current_url
returns the URL of the page currently loaded in the browser
find_element
returns an object representing the first element found by a locator. If no element is found, then this method throws an exception
find_elements
, plural, returns a list of element objects found by a locator. In this case, if no elements are found, then this method returns an empty list
The Python WebDriver also includes a find_elemen_by_*
method for each locator type. Typically, I don't use these methods because I prefer to use 2-part locator tuples, but many people prefer the shorter syntax
get
loads a given URL in the browser
maximize_window
maximizes the browser's window size
quit
closes a browser and then terminates the process
refresh
refreshes the current page in the browser
save_screenshot
will capture the current browser window and save it to an image file
title
returns the page's title
The right column shows calls for element objects returned by the find_element
methods.
clear
removes all text from a text input field
click
clicks an element
find_element*
methods on an element object will locate elements starting with the current element as the root
get_attribute
returns the value of an HTML attribute; while get_property
returns the value of an HTML property
is_displayed
returns true if the element visually appears on the page and isn't hidden
location
returns the X and Y pixel coordinates of the elements
send_keys
sends keystrokes to the elements like typing in a text input
size
returns the pixel dimensions of the elements
text
returns the text value of the elements
The best way to learn the Python WebDriver API is simply to do the code.
I have our project open here, and I've opened it to the search page.
"""
This module contains DuckDuckGoSearchPage,
the page object for the DuckDuckGo search page.
"""
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
class DuckDuckGoSearchPage:
# URL
URL = 'https://www.duckduckgo.com'
# Locators
SEARCH_INPUT = (By.ID, 'search_form_input_homepage')
# Initializer
def __init__(self, browser):
self.browser = browser
# Interaction Methods
def load(self):
self.browser.get(self.URL)
def search(self, phrase):
search_input = self.browser.find_element(*self.SEARCH_INPUT)
search_input.send_keys(phrase + Keys.RETURN)
You'll notice some differences.
First of all, I've added a new Selenium WebDriver import
for this thing called Keys
. We'll need that for one of the methods we implemented below.
Our class should look pretty familiar.
It still has our locator and it still has our initializer, but the interaction methods have now been implemented. Let's look at one at a time.
We know that the load
method should load the page.
So, if you recall from our previous slide, we want to use the get
method for our WebDriver. The get
method takes in a βURLβ, which I've put up here as a class variable.
That's all we need to do to load the webpage.
Next, let's look at the search
method.
We know the search
method should take in a textual phrase and enter that phrase onto the DuckDuckGo search page.
What we'll need to do is we'll need to locate the search_input
element and then send the phrase to it.
To locate the search input elements, we'll need to say browser.find_element
and we'll pass in our SEARCH_INPUT
locator.
Now you may be wondering what this asterisk is for.
Well, the find_element
method takes two arguments β the locator type and then the query β but SEARCH_INPUT
is a tuple
This asterisk is a standard Python thing that will expand tuples into positional arguments that can be passed into methods.
By adding this here, I can essentially pass my tuple, my locator, into the find_element
method. If it's successful, it will return an object representing the search_input
element on the page.
Once I have that element, I can send_keys
to it for my search phrase.
I'll also want to add on the Keys.RETURN
. That comes from that import we saw before. The return key will essentially commit that search and then proceed to start loading the search results page.
Now you may be wondering what happens if this find_element
call for my SEARCH_INPUT
doesn't actually find the search input element.
Maybe the page isn't loaded yet or maybe the locator's bad, or maybe there's some other kind of problem. If this call fails to find the element, then it will raise an exception. We don't necessarily need to handle the exception here. We can just let it bubble up.
Ultimately, pytest will capture that exception at the test case execution level, and it will log a failure for the test. Then when we come and read the reports later, we could find it and try to figure out what went wrong.
Let's look at the result page next.
"""
This module contains DuckDuckGoResultPage,
the page object for the DuckDuckGo search result page.
"""
from selenium.webdriver.common.by import By
class DuckDuckGoResultPage:
# Locators
RESULT_LINKS = (By.CSS_SELECTOR, 'a.result__a')
SEARCH_INPUT = (By.ID, 'search_form_input')
# Initializer
def __init__(self, browser):
self.browser = browser
# Interaction Methods
def result_link_titles(self):
links = self.browser.find_elements(*self.RESULT_LINKS)
titles = [link.text for link in links]
return titles
def search_input_value(self):
search_input = self.browser.find_element(*self.SEARCH_INPUT)
value = search_input.get_attribute('value')
return value
def title(self):
return self.browser.title
Like the search page, the result page code should look familiar.
Again, we have the locators and the same initializer, but we've implemented the interaction methods.
The result_link_titles
method should return a list of all link_titles
for the results.
This call is going to look very similar to the search one in that we have to find some elements. However, this one uses the find_elements
plural. We pass in the locator for my result links and I'll get those links back here in this variable, which will be a list.
Now I don't want to return the elements themselves. What I want to return is the text of each element that was found.
I can use a simple list comprehension here to get the titles β titles = [link.text for link in links]
β and that is the value I want to return. It should be a list of strings, not a list of elements.
Next, let's look at the search_input_value
method.
This method should get the text value of my search input field from the result page. Again, I'll use the singular find_element
method and pass in my search input locator.
Now instead of sending keys to this, I want to get the value from it. To do that, I'll need to use the get_attribute
method and look for the attribute named βvalueβ and ultimately, that's the text value that I'll want to return.
Now you may be wondering, why did we use get_attribute
instead of just the text
property for the search_input
element?
Whenever you're dealing with text input elements where you can type something in, interestingly, the text property of that is going to be the empty string.
You'll need to use the get_attribute
of the value to get the value that the user typed in. Be careful, that usually trips up a lot of people.
Finally, let's look at the title
method here.
This one, we don't need to use a find_element
method because like we said previously, the title is an attribute of the page and not an element of the page.
We simply call self.browser
(our WebDriver instance) .title
.
As we said before, the WebDriver API is long and complex. I can only cover a few methods in this course here, so I encourage you to give some of these other methods and properties a try and to read up on the docs so that you know how to do any interaction.