Skip to content

Instantly share code, notes, and snippets.

@NullVoxPopuli
Last active January 11, 2020 23:47
Show Gist options
  • Save NullVoxPopuli/245917f5d9b1ca7271b9d1a04ad90378 to your computer and use it in GitHub Desktop.
Save NullVoxPopuli/245917f5d9b1ca7271b9d1a04ad90378 to your computer and use it in GitHub Desktop.
Generic Adaptable Page Objects

Goals:

  • page objects should be cross-ecosystem, cross-technology
  • simple API
  • interop with Ember, and WebDriver (via adapters, which means testing-library would be supported, if anyone wanted to use that).
  • all async/awaitable
  • componentizable -- encourage separation of structure from the actual page object creation.
  • all properties lazily evaluated

Nomenclature:

  • Adapter: Converts meaning in to behavior -- "click" means different things to ember and webdriver
  • Page Object: a literal object that represents how one would interact with the page

Proposed API:

import { setAdapter, create } from 'unknown package';
import { EmberAdapter } from 'unknown package/ember';

setAdapter(EmberAdapter);

const definition = {
  login: {
    username: '[data-test-username]',
    password: '[data-test-password]',
    submit: '[data-test-submit]',
  },

  list: {
    scope: '[data-test-list]',

    rows: collection('[data-test-row]', {
      click: '[data-test-row-clicker]'
    });
  }
};

const page = create(definition);

// Usage
await page.login.username.click();
page.login.username.isFocussed; // true
await page.login.username.fillIn('text'); // username is now "text"
page.login.username.dom // <input type='text' data-test-username ... >

page.login.submit.fillIn('text'); // Error!: buttons are not fillable
await page.login.submit.click();

// for typescript, types are optional
// by default, all methods and properties will be available, and will throw runtime errors
// but the type of the interactable will still auto-complete for all methods and properties.
interface Definition {
  login: {
    submit: ButtonInteractor;
  }
};
const typedPage = create<Definition>(definition);
// by default, every entry in the page object would be of type "OmniInteractor"

Usage with qunit-dom

assert.dom(page.login.username.dom).hasClass(...);
@ro0gr
Copy link

ro0gr commented Jan 11, 2020

@sukima your sinon framework agnostic idea looks interesting. I think it's worth to be explored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment