-
Notifications
You must be signed in to change notification settings - Fork 46
rx page objects
Page objects aid in performing actions in the UI in the same manner as a user would perform them.
Page objects should accept and return simple values ([arrays of] strings, numbers, booleans), because a user isn't going to construct a Date
or moment
object to set a date picker, and they probably aren't going to be looking at the value of a dropdown through an object
. By writing your page object functions/properties in a manner in which they accept and return these simple values, the page object API becomes much simpler to understand.
- Use standard formats, when applicable (date, time, currency, telephone, etc.)
NOTE: The examples below show how to avoid problems using date/time values, but the concept can be applied to any type of value.
- The properties and params below all deal with date/time values in one form or another.
- They accept/return three different forms of date/time values (
Date
,moment
, and 'String'). - The returned String value is not in a typical format for a date and time.
This API is confusing and unintuitive for the consumer of the page objects.
/**
* @property {Date} startDate - beginning of date range
* @property {Date} endDate - end of date range
*/
/**
* @param {moment} date - selected date
* @param {moment} time - selected time
*/
/**
* @returns {String} - full date/time value in format of DD-MM-YY-HH:mm
*/
- Standardize on Strings as the least common denominator.
- Use accepted standards when formatting the values (ISO8601 in the case of date/time strings)
Making this simple change, we've simplified the interface, and standardized the output. A benefit of doing this is that a date string return value can be used directly as an input value for a date property/param without any unnecessary conversion.
/**
* @property {String} startDate - beginning of date range (ISO8601 Format: YYYY-MM-DD)
* @property {String} endDate - end of date range (ISO8601 Format: YYYY-MM-DD)
*/
/**
* @param {String} date - selected date (ISO8601 Format: YYYY-MM-DD)
* @param {String} time - selected time (ISO8601 Format: HH:mmZ)
*/
/**
* @returns {String} - full date/time value (ISO8601 Format: YYYY-MM-DDTHH:mmZ)
*/
Why is console.log outputting an object?
Protractor is based on promises, so all page object functions and properties that return a then-able promise should be documented as such.
/**
* @returns {Boolean} Can do stuff?
* @property {Number} x - Horizontal, X value
*/
/**
* @returns {Promise<Boolean>} Can do stuff?
* @property {Promise<Number>} x - Horizontal, X value
*/
... because time zones.
All dates and times are to be presented to page objects as string literals. A property that accepts a date/time string when setting, should return a date/time string when getting.
var fooDate = new Date('2016-12-09');
encore.rxComponent.date = fooDate;
encore.rxComponent.date; // Date object
var fooDate = '2016-12-09';
encore.rxComponent.date = fooDate;
encore.rxComponent.date; // '2016-12-09'
Protractor doesn't do it, why should we?
Only provide the positive predicate function for a value. Consumers can determine the correct boolean to expect when used.
isEnabled: function () { /* ... */ },
isDisabled: function () {
return this.isEnabled().then(encore.rxMisc.negate);
},
isOpen: function () { /* ... */ },
isClosed: function () {
return this.isOpen().then(encore.rxMisc.negate);
},
isValid: function () { /* ... */ },
isInvalid: function () {
return this.isValid().then(encore.rxMisc.negate);
},
isDisplayed: function () { /* ... */ },
isInvisible: function () {
return this.isVisible().then(encore.rxMisc.negate);
}
isEnabled: function () { /* ... */ },
isOpen: function () { /* ... */ },
isValid: function () { /* ... */ },
isDisplayed: function () { /* ... */ },
Don't rename the wheel.
When defining a predicate function in your page objects, try to use the following names to match what protractor provides.
- isPresent()
- isEnabled()
- isSelected()
- isDisplayed()
This reduces confusion when testers consume your page object.
Y U NO pending?
Avoid testing intermittent element states (between true/false). Protractor does not have a straightforward way to test state as an element is transitioning, so just save yourself the trouble.
ES6 is the future... and page objects run on Node.
To be continued...
Use native JS get
and set
syntax.
Object.defineProperty(myObject, 'myProperty', {
get: function () { return this.myProperty; },
set: function (val) { this.myProperty = val; }
});
Action functions should begin with a verb to denote that they perform some sort of functionality.
// BAD
obj.previousMonth();
obj.nextMonth();
// GOOD
obj.goToPreviousMonth();
obj.goToNextMonth();
Predicate functions are those that return boolean values. These functions should begin with "is", "can", or "has".
- "is"
- used to query state of an object (e.g.
isOpen()
)
- used to query state of an object (e.g.
- "can"
- used to query capability of an object (e.g.
canOpen()
)
- used to query capability of an object (e.g.
- "has"
- used to query possession (e.g.
hasItem(foo)
,hasAction(bar)
)
- used to query possession (e.g.
// BAD
obj.foo();
// GOOD
obj.isBar();
obj.canFoo();
obj.hasThing(someThing);