paint-brush
Writing Better Tests With Cypress' Page Elementsby@bormando
2,846 reads
2,846 reads

Writing Better Tests With Cypress' Page Elements

by Dmitrii BormotovJanuary 30th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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.
featured image - Writing Better Tests With Cypress' Page Elements
Dmitrii Bormotov HackerNoon profile picture

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:


  1. A lot of elements in web applications are usually very similar, for example:

    1. Modal dialogs/windows.
    2. Forms.
    3. Cards




  2. Page Element is represented as a class.

    1. Sub-element (like button/input inside of a card) = DOM tree element, class attribute.
    2. 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.


  1. Create a folder for your Page Elements, preferably name it elements and place it in your cypress directory, like this:


Project view


  1. Create navbar.js inside of elements folder - as the name says, it’s a top navigation bar from the app:


SWAG LABS top navigation bar


  1. Create 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()


  1. 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…


SWAG LABS sign in page

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.