Creating a Page Model¶
We’ve been working on a test to open https:/www.google.com and to query for the word “example”. For the querying part, we need to identify the search input field and the button to submit the search form.
"query 'example'":
actions:
- set $".gLFyf.gsfi" to "example"
- click $".FPdoLc.VlcLAe input[name=btnK]"
assertions:
- $page.title is "example - Google Search"
The test step works for now so that’s fine, right?
Well, those selectors are a bit messy and that’s not so fine. The selectors are hard to read (.gLFyf.gsfi
doesn’t really shout “search input”) and may well need to be updated in many places when the page being tested changes.
Let’s create a page model to store all of these page properties. We can refer to the page model in our tests, making the tests clearer to read. And we can make sure that if something needs updating it only needs to be changed in one place.
Creating and Using a Page Model¶
A page model is a YAML object with url
and elements
properties. The url
property holds the page URL. The
elements
section maps convenience names to ways of identifying elements.
# examples/page/google.com.yml
url: "https://www.google.com"
elements:
search_input: $".gLFyf.gsfi"
search_button: $".FPdoLc.VlcLAe input[name=btnK]"
For the time being those selectors are a little hard on the eyes but at least we need to only update them in one place.
# examples/step/google-assert-open-literal.yml
assertions:
- $page.url is "https://www.google.com"
- $page.title is "Google"
# examples/test/google-import-assert-open-with-page-model.yml
config:
browsers:
- chrome
url: google_com.url
imports:
steps:
google_assert_open: "../step/google-assert-open-literal.yml"
pages:
google_com: "../page/google.com.yml"
"verify Google is open":
use: google_assert_open
"query 'example'":
actions:
- set $google_com.elements.search_input to "example"
- click $google_com.elements.search_button
assertions:
- $page.title is "example - Google Search"
We’ve added a pages
section to the imports
section to reference the page model we created. We’ve chosen an import
name (google_com
) and provided an import path (page/google.com.yml
).
Within our assertions, we use the name of the page import to reference the elements that the page model defines.
See how click $google_com.elements.search_button
is a lot easier on the brain than click $".FPdoLc.VlcLAe input[name=btnK]"
?
Scoping Elements Within a Page Model¶
Both the search input field and the search button are within a form. And both elements have some properties other than class names that are probably less prone to change.
We can make our page model more robust.
# examples/page/google.com-selector-scoped.yml
url: "https://www.google.com"
elements:
# finds "[name=q]" within the context of "form[action='/search']"
# using CSS syntax
search_input: $"form[action='/search'] input[name=q]"
# finds "[type=submit]" within the context of "form[action='/search']"
# using CSS syntax
search_button: $"form[action='/search'] input[type=submit]"
That makes my brain hurt just a little bit less. But we’re still repeating the form[action='/search']
part of each
of the selectors. We can do better!
We have given the $"form[action=/search]"
identifier the name search_form
. We can reference this element via the
name we chose by prefixing the name with a dollar sign. Within our page model, $search_form
is the same as saying
$"form[action=/search]"
.
Combine this with the >>
operator for a parent-child relationship and everything is much easier to read.
# examples/page/google.com-element-scoped-reference.yml
url: "https://www.google.com"
elements:
search_form: $"form[action=/search]"
# finds "[name=q]" within the context of search_form
# using basil parent-child syntax
search_input: $search_form >> $"[name=q]"
# finds "[type=submit]" within the context of search_form
# using basil parent-child syntax
search_button: $search_form >> $"[type=submit]"