The WebDriver.io Page Object Pattern documentation is fantastic, and it wouldn't hurt to read it over a few times to make sure you really understand the principles.
"The goal behind page objects is to abstract any page information away from the actual tests.
Ideally you should store all selectors or specific instructions that are unique for a certain page in a page object, so that you still can run your test after you’ve completely redesigned your page."
Overview
- Understand why the Page Object Pattern is so useful
- Understand the 2 most important parts of the pattern
- Understand that Components can also use this pattern
- Change to the correct directory
cd exercise-6
- Install dependencies
npm i
Notice the new file structure in the tests/browser
directory. I've further organized those into child directories for pages
and specs
.
- Define element getters
- Define element actions
Consider the following file named tests/browser/pages/Home.page.js
:
'use strict';
/**
* Pages generally contains two parts:
*
* 1. getters
* 2. actions
*
*/
class HomePage {
// --------------------------------------------------------------
// Define element getters.
// --------------------------------------------------------------
get jumbotronPrimaryButton() { return browser.element('.jumbotron .btn-primary'); }
// --------------------------------------------------------------
// Define trusted interactions with elements.
// --------------------------------------------------------------
/**
* Get the element text.
*/
getJumbotronPrimaryButtonText() {
this.jumbotronPrimaryButton.getText();
}
}
module.exports = HomePage;
Let's walk through this using each of the two parts of a Page Object - getters and actions.
We'll use this syntax to encapsulate calls to browser.element()
and browser.elements()
, which will save us code and time later. Also, all of the selectors for each element (or array of elements) are now located in one place.
class HomePage {
// --------------------------------------------------------------
// Define element getters.
// --------------------------------------------------------------
get jumbotronPrimaryButton() { return browser.element('.jumbotron .btn-primary'); }
}
Now that the page elements are easy to access via our getter functions, we can begin to define functions to encapsulate interactions on the page, abstracting away the details so from your test specs.
Examples of common actions you might write functions for include:
- getting an element's text
- clicking on an element
- scrolling the page
- ... and other trusted interactions within this page
class HomePage {
// --------------------------------------------------------------
// Define trusted interactions with elements.
// --------------------------------------------------------------
/**
* Get the element text.
*/
getJumbotronPrimaryButtonText() {
this.jumbotronPrimaryButton.getText();
}
}
We're going to use the solution from the previous exercise-5 to begin with, and we'll transform it to use The Page Object pattern.
I've already created that initial test spec file as tests/browser/specs/homepage.spec.js
, but we must create a Page object for the spec to interact with.
- Open the
Home.page.js
file in thetests/browser/pages
directory, where the getter and action parts of the Page Object Pattern will be implemented. (Look for theTODO
comments.) - For each of the elements used in our
tests/browser/specs/homepage.spec.js
test, create a getter and an action in thetests/browser/pages/Home.page.js
page object.
Hint: look for anywhere we've called browser.element()
, browser.elements()
, browser.getText()
, or other commands to see which elements you'll need to create getters for!
(See the solution directory if you'd like to preview, or simply copy/pasta the code.)
✅ And now you've got a good start towards modeling your pages and components!
- We've learned how the Page Object Pattern abstracts away the accessing of elements from our test spec, making code much more readable, reusable and maintainable.
- We've learned the 2 most important parts of the pattern - getters and actions
- We now know that the "Page Object Pattern" can actually be used for all sorts of component architecture.