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.
WHATāS PAGE ELEMENTS?
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:
- Modal dialogs/windows.
- Forms.
- Cards
-
Page Element is represented as a class.
- Sub-element (like button/input inside of a card) = DOM tree element, class attribute.
- Method = sequence of actions within the element.
IMPLEMENTATION
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.
- Create a folder for your Page Elements, preferably name it
elements
and place it in your cypress directory, like this:
- Create
navbar.js
inside ofelements
folder - as the name says, itās a top navigation bar from the app:
- Create
Navbar
class inside ofnavbar.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()
- Now thereās 2 ways to use such elements in your testsā¦
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 āļø
WHY SHOULD I USE IT?
Big page objects
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
}
Elements are not everywhere
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.