nightwatch
Version:
Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.
198 lines (156 loc) • 4.6 kB
JavaScript
const Utils = require('../utils');
const CommandWrapper = require('./command-wrapper.js');
const BaseObject = require('./base-object.js');
const ScopedElementAPILoader = require('../api/_loaders/element-api');
const shouldReturnPromise = function() {
return this.client.isES6AsyncTestcase || this.client.isES6AsyncCommand;
};
const validateUrl = function(url) {
let goToUrl = this.getUrl(url);
if (!goToUrl) {
goToUrl = this.client.settings.launchUrl;
} else if (Utils.isString(goToUrl) && Utils.relativeUrl(goToUrl)) {
if (!this.client.settings.launchUrl) {
const err = new Error(`Invalid URL ${goToUrl} in "${this.name}" page object. When using relative uris, you must ` +
'define a "baseUrl" in your nightwatch config.');
throw err;
}
goToUrl = Utils.uriJoin(this.client.settings.launchUrl, goToUrl);
}
if (goToUrl === null) {
const err = new Error(`Invalid URL: You must either add a url property to the "${this.name}" or provide a url as an argument`);
throw err;
}
return goToUrl;
};
/**
* Class that all pages subclass from
*
* @param {Object} options Page options defined in page object
* @constructor
*/
class Page extends BaseObject {
constructor(options, commandLoader, client) {
super();
this.commandLoader = commandLoader;
this.__options = options;
this.__client = client;
this.__api = Object.assign({}, client.api);
this.__props = Page.createProps(this, options.props);
this.__elements = Page.createElements(this, options.elements);
this.__sections = Page.createSections(this, options.sections);
const pageCommands = options.commands || [];
Page.addCommands(this, pageCommands);
CommandWrapper.addWrappedCommands(this, this.commandLoader);
CommandWrapper.wrapProtocolCommands(this, this.api, BaseObject.WrappedProtocolCommands);
CommandWrapper.wrapScopedElementApi(this, this.api, ScopedElementAPILoader.availableElementCommands);
Object.defineProperty(this, 'element', {
get() {
return this.api.element;
},
enumerable: true,
configurable: false
});
}
get api() {
return this.__api;
}
get client() {
return this.__client;
}
get name() {
return this.__options.name;
}
/**
* @return {object}
*/
get props() {
return this.__props;
}
/**
* @return {object}
*/
get elements() {
return this.__elements;
}
/**
* @return {object}
*/
get section() {
return this.__sections;
}
get url() {
return this.__options.url;
}
set url(val) {
this.__options.url = val;
}
/**
* Returns the url passed as an argument (or null if no arguments are passed).
* If the supplied url is a function, it invokes that function with the page as its context.
*
* @method getUrl
* @param {function|string|object} url
* @return {function}
*/
get getUrl() {
/**
* @returns {string|null}
*/
return (url) => {
if (Utils.isFunction(url)) {
return url.call(this);
}
if (Utils.isString(url)) {
return url;
}
if (Utils.isObject(url) && this.url) {
return Utils.replaceParams(this.url, url);
}
return null;
};
}
/**
* This command is an alias to url and also a convenience method because when called without any arguments
* it performs a call to .url() with passing the value of `url` property on the page object.
* Uses `url` protocol command.
*
* @method navigate
* @param {Object} [url=this.url] Url to navigate to.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @returns {*}
*/
get navigate() {
return function(url, callback) {
let goToUrl;
if (typeof url == 'function' && !callback) {
callback = url;
url = this.url;
}
if (!url) {
url = this.url;
}
if (!shouldReturnPromise.call(this)) {
goToUrl = validateUrl.call(this, url, callback);
this.api.url(goToUrl, callback);
return this;
}
const promise = new Promise((resolve, reject) => {
(async () => {
let goToUrl;
try {
goToUrl = validateUrl.call(this, url, callback);
} catch (err) {
reject(err);
return;
}
await this.api.url(goToUrl, callback);
resolve();
})();
});
Object.assign(promise, this);
return promise;
};
}
}
module.exports = Page;