page-object icon indicating copy to clipboard operation
page-object copied to clipboard

A pattern to test UI Components

Page object pattern for Javascript

This repo contains examples how to implement Page Object pattern for Javascript testing frameworks. Let's assume, that we have UI framework that gives us a component instance, where we can navigate through items:

const component = createComponent()

component.find('.name') // find and return element with class "name"
component.find('.action-button') // find button to trigger action

To avoid repeating this selectors every time, it is recommended to create Page Object as a wrapper:

function Page() {
  this.name = () => component.find('.name');
  this.action = () => component.find('.action-button');

  //getter functions can be parametrized
  this.formField = (name) => component.find(`.form-control[name=${name}]`);
}

Then use this object rather that raw instance to do test actions

it('fill form and submit', () => {
  const component = createComponent();
  const page = new Page(component);

  page.formField('username').val('tester');
  page.formField('email').val('[email protected]');

  page.action().click();
});

It also makes sense to put this setup code in a helper function or beforeEach hook:

function setup() {
  const component = createComponent();
  return new Page(component);
}

ProTip: use ES2015 features to return multiple values from the function if you need so

function setup(data) {
  const component = createComponent(data);
  const page = new Page(component);
  return {component, page}
}

// later in code
const {component, page} = setup(data);

FAQ

Can I use this pattern with my framework?

Yes, you probably can. It works with any framework that uses UI-Components. Then you need to have an ability to traverse through elements tree. See examples section for actual frameworks example

I want to put a method like "clickTheButton" into my page object. Is it correct?

It is not okay. If you have a button in your component, it should be available not only for click but also for checking its content or visibility at all. When you create a method that unconditionally clicks on button, it is not good for reusability. However, you can add two methods: one, that returns button, and second that makes a click:

function Page(component) {
  this.button = () => component.find('button');
  this.clickOnButton = () => this.button().click();
}

I have a complex component and I need to find an element within another element. How can I do that?

If your component framework supports .find() chaining, you can do it with page objects as well:

function Page(component) {
  this.topBar = () => component.find('.top-bar');
  this.topButton = () => this.topBar().find('.button');
}

Ask your question if you have one.

Examples

  • React
  • Backbone
  • Vue