Hello everyone!
I was inspired to write this article by the fact that there’s no official documentation on Page Elements (PE) pattern implementation in Cypress. You might find some articles online, but I find them imperfect - I’ve got a thing or two to add.
This one is not so well-known as Page Object Model (POM), so you probably never heard about it before. Usually, Page Elements being implemented in pair with Page Object Model, not separately.
Page Elements (PE) is a test automation programming (or design) pattern. It’s built on top of DRY (don’t repeat yourself) principle in software development.
As it’s a design pattern and it’s not unique for every web automation tool, no wonder there are no official Cypress docs for that as well ☝️
Key concepts of PE are:
A lot of elements in web applications are usually very similar, for example:
Page Element is represented as a class.
Here’s an example GitHub repo with tests for Swag Labs example web app. If you’ve been following my previous article, it’d probably worth take a look at the PR instead to see what’s changed from there.
elements
and place it in your cypress directory, like this:
navbar.js
inside of elements
folder - as the name says, it’s a top navigation bar from the app:
Navbar
class inside of navbar.js
and fill it with selectors:
class Navbar {
get menu() {return cy.get('#react-burger-menu-btn')}
get cart() {return cy.get('#shopping_cart_container')}
}
export default new Navbar()
A) Combine it with your Page Objects - simply add it as a property for your page classes:
import Page from './page'
import Navbar from '../elements/navbar'
class ProductsPage extends Page {
navbar = Navbar
open() {
return super.open('/inventory.html')
}
}
export default new ProductsPage()
And then call it in tests from the page:
import ProductsPage from '../pages/products.page' // import page that includes page element
...
describe('Items', () => {
...
it('Navigate to Cart from the navbar', () => {
ProductsPage.navbar.cart.click() // click page elements on the page
...
})
})
B) Use it directly in tests:
import Navbar from '../elements/navbar' // import page element directly in tests
...
describe('Items', () => {
...
it('Navigate to Cart from the navbar', () => {
Navbar.cart.click() // interact with imported page element
})
})
And that’s pretty much it ☝️
Sometimes elements like navigation bar are big - they have more than a few sub-elements within (links, icons and etc). If you put too many stuff in your page objects - they get big soon, i.e.:
class SomePage {
selector1,
...
selector9001
method1,
...
method1337
}
If we take the same SWAG LABS website for example - you’ll see that navigation bar doesn’t appear on the Sign In
page, but DO appear on other pages…
It means that there’s no place for navigation bar in our base page class (i.e. page.js
), because it’d make this element available on ALL pages.
Page Elements pattern lets us add elements ONLY where we need them by simply calling stored class and not duplicating code.