Page Objects is a design pattern which allows test maintenance and reduction of duplicate code.
It's an object-oriented class that serves as an interface to a page or a section of an application. It normally contains locators, or elements, and functions that will interact with these elements. It may also contain other functions that the test will use.
Let us now create a folder called "pages".
In that folder, let us create a file, and we will name that file "internet.page.js".
Let us define our class — class Internet
And then we say — module.exports = new Internet()
— so that this class is accessible by our tests.
Let us put in this class some of the elements that we found in the previous lecture.
So, we can say:
get pageHeader() { return $('h1.heading') }
If we look here, the first header, it has a class of "heading".
To be more specific, we could also say, "h1.heading" and that would give us this header right here [Welcome to the-internet].
We can also add this element [Available Examples] which we'll call "subheader".
We will do it in a similar format
get subHeading() { return $('h2') }
Let us also get the page footer
get pageFooter() { return $('#page-footer') }
Okay, great.
You may be wondering, what's the purpose of the dollar sign right here?
If we just go to the WebdriverIO website, and we go to the API doc, and we look at dollar sign [$] and double dollar sign [$$], we can get some answers.
The $
command is a short way to call the findElement
command in order to fetch a single element on the page.
If we look here, when we ran our test before, it calls a command findElement
for our xpath that we had in our test here.
So that is what the $
command does — it calls a command findElement
and returns that single element to you so that you can do your interactions on it.
Let us take a quick look at the double dollar sign.
The $$
command is a short way to call the findElements
command in order to fetch multiple elements on the page. It will return to you an array of elements that you can use to act on those elements as you see fit.
Let us do a quick example of that.
If we go to “the-internet” page, we realize that they have a wide variety of links here. If we inspect the first link, we realize that they are all in an li
tag, right?
What if we wanted to get all "li's" on a page?
Using the $$
is a great way to get an array of all the elements. So, let's do that.
Let us look at our web page and we realize that we have a lot of li
tags within this ul
right? We can use the ul
, which is the parent, to then go into and get all the li
tags.
So, let us say
get parent() { return $('ul') }
Then we're going to say
get childElements() { return this.parent.$$('li') }
Then let's create a function, we're going to use the filter and that will create a new array.
Remember we had said previously that using the $$
gives us an array of all the elements that match that selector that you are inputting. In this case, it's going to provide us with all of these li
elements. Because we want to use them to get the text of them, to show, we are going to have to manipulate that array that we're getting back.
Okay.
Let's say:
getLiText() {
this.childElements.filter((element) => {
console.log(element.getText())
})
Which is going to manipulate the array for us. So, we're going to be using the keyword elements
to interact with the array that we're getting back. And we are going to use console.log
to do it, and we're going to getText
on it.
class Internet {
get pageHeader() { return $('h1.heading') }
get subHeading() { return $('h2') }
get h3Header() { return $('h3') }
get pageFooter() { return $('#page-footer') }
get parent() { return $('ul') }
get childElements() { return this.parent.$$('li') }
getLiText() {
this.childElements.filter((element) => {
console.log(element.getText())
})
}
module.exports = new Internet()
Let us go into our test. and we are going to import internet.page
.
const internetPage = require("../pages/internet.page")
describe("Interacting with elements", function () {
it("Get text for element", () => {
browser.url('/')
let text = $("//*[@id='page-footer']").getText()
console.log(text)
internetPage.getLiText()
})
Okay, so we have internet.page
, let us now just call internet.page
and use the function that's there, which is getLiText
.
So, if we run that [npm run test
] it navigates to the URL
And if we scroll up just a bit, we realize that it is getting the text for all the li
elements that we have here — Multiple Windows, Nested Frame, Notification Messages… — it returns all of them here.
There is also another way that we could have used the $
and $$
— let's look at that way as well.
So, let's say
specificChildElement(index) { return this.parent.$(`li:nth-child(${index})`) }
And you'll realize that we aren't using a getter here, because we are going to be accepting an index, which in this case, we want it to be the third index [the “Basic Auth” link in the list]. So, we won't be using a getter as a getter does not accept parameters.
It would be a regular function. So, we are accepting the index, and we are going to use the index here.
Okay, great.
Let's have a function now that says:
getSpecificElementText(index) {
console.log (this.specificChildElement(index).getText())
}
And we can console.log
these out.
Okay, so on our test, let us call this function — internetPage.getSpecificElementText(3) — with an index of 3.
And if we run this, it should return the last thing as being " Basic Auth (user and pass: admin) " here.
Okay, so you can use it to get multiple elements or you can use it to get a single element.
Quiz
The quiz for this chapter can be found in Chapter 4.3.