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(...);
I'm not sure. Previously it did make sense in Ember (when there were different DOM APIs in acceptance vs. integration tests). I don't know enough about other frameworks where this would be useful to know if that still does/might apply anywhere. If not, then yeah, probably a single global adapter should be enough.
I was thinking pretty fuzzily, but I think that makes sense -- an optional wrapper library (addon in Ember's case) to integrate it with frameworks with specific testing environments.
Is there an easy/convenient way for users to extend adapters then? In my experience, custom properties are an extremely common use case, and if the page sub-object expose any properties they will likely need to support custom user-defined (not framework-adapter-defined) properties.
And why can't the definition support custom actions? If the custom actions are moved to the page object (rather than the definition), then we'd need re-usable sub-objects to be page objects themselves rather than definitions, because they'd need to carry their custom actions with them.
re:
ember-cli-page-object
, back a couple of years ago when I was contributing heavily to the project, they were talking about a major release that cleans a whole bunch of stuff up and simplifies things -- see this tracking issue. I've found the maintainers fairly responsive on GitHub, and I imagine contributions would be welcome (and breaking changes in the context of a v2 as well). I'd float the idea over there of scooping the core out into a standalone/framework-independent library, since I think it makes a lot of sense.However, I think you might need to get clearer on the goals here, or maybe you are but I'm just not getting them. I see a spectrum here with two extremes.
One extreme is just representing the DOM structure so selectors can be maintained in one place and nested in a structure so parent selectors don't have to be repeated. Then the page object would expose its tree structure, and outside of that it would only expose the
dom
property, which could be passed toqunit-dom
and directly to@ember/test-helpers
DOM helpers. So it's just a convenient API for defining the structure of a page and being able to access DOM elements in a rendered page.The other extreme is full behavior-driven testing support where all the actions and properties and everything are abstracted. The happy path would be never using
qunit-dom
(but just "low-level"qunit
assertions) or@ember/test-helpers
or any DOM APIs at all, but of course it would need the "escape-hatch" of thedom
property since this abstraction is never going to perfectly meet everyone's needs.It's unclear to me where on that spectrum you're aiming to fall. I think
ember-cli-page-object
was aiming close to the latter, while you're aiming closer to the former, but not really all the way to the former...so I'm still unclear exactly what the vision is here. If I had to take a stab at what I think the best course of action would it, it would be to:ember-cli-page-object
a whole bunch (but still keep all of the primary functionality of components, custom properties, custom actions, etc.)ember-cli-page-object
's collections to have a more familiar array APIqunit-dom
(probably by just exposing adom
property on all elements, which is a pretty minor code change inember-cli-page-object
)