refactor app directory structure and add tests
This commit is contained in:
238
tests/node_modules/nightwatch/lib/page-object/command-wrapper.js
generated
vendored
Normal file
238
tests/node_modules/nightwatch/lib/page-object/command-wrapper.js
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
module.exports = new (function() {
|
||||
|
||||
/**
|
||||
* Given an element name, returns that element object
|
||||
*
|
||||
* @param {Object} parent The parent page or section
|
||||
* @param {string} elementName Name of element
|
||||
* @returns {Object} The element object
|
||||
*/
|
||||
function getElement(parent, elementName) {
|
||||
elementName = elementName.substring(1);
|
||||
if (!(elementName in parent.elements)) {
|
||||
throw new Error(elementName + ' was not found in "' + parent.name +
|
||||
'". Available elements: ' + Object.keys(parent.elements));
|
||||
}
|
||||
return parent.elements[elementName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a section name, returns that section object
|
||||
*
|
||||
* @param {Object} parent The parent page or section
|
||||
* @param {string} sectionName Name of section
|
||||
* @returns {Object} The section object
|
||||
*/
|
||||
function getSection(parent, sectionName) {
|
||||
sectionName = sectionName.substring(1);
|
||||
if (!(sectionName in parent.section)) {
|
||||
throw new Error(sectionName + ' was not found in "' + parent.name +
|
||||
'". Available sections: ' + Object.keys(parent.sections));
|
||||
}
|
||||
return parent.section[sectionName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls use(Css|Xpath|Recursion) command
|
||||
*
|
||||
* Uses `useXpath`, `useCss`, and `useRecursion` commands.
|
||||
*
|
||||
* @param {Object} client The Nightwatch instance
|
||||
* @param {string} desiredStrategy (css selector|xpath|recursion)
|
||||
* @returns {null}
|
||||
*/
|
||||
function setLocateStrategy(client, desiredStrategy) {
|
||||
var methodMap = {
|
||||
xpath : 'useXpath',
|
||||
'css selector' : 'useCss',
|
||||
recursion : 'useRecursion'
|
||||
};
|
||||
|
||||
if (desiredStrategy in methodMap) {
|
||||
client.api[methodMap[desiredStrategy]]();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a closure that enables calling commands and assertions on the page or section.
|
||||
* For all element commands and assertions, it fetches element's selector and locate strategy
|
||||
* For elements nested under sections, it sets 'recursion' as the locate strategy and passes as its first argument to the command an array of its ancestors + self
|
||||
* If the command or assertion is not on an element, it calls it with the untouched passed arguments
|
||||
*
|
||||
* @param {Object} parent The parent page or section
|
||||
* @param {function} commandFn The actual command function
|
||||
* @param {string} commandName The name of the command ("click", "containsText", etc)
|
||||
* @param {Boolean} [isChaiAssertion]
|
||||
* @returns {function}
|
||||
*/
|
||||
function makeWrappedCommand(parent, commandFn, commandName, isChaiAssertion) {
|
||||
return function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var prevLocateStrategy = parent.client.locateStrategy;
|
||||
var elementCommand = isElementCommand(args);
|
||||
|
||||
if (elementCommand) {
|
||||
var firstArg;
|
||||
var desiredStrategy;
|
||||
var callbackIndex;
|
||||
var originalCallback;
|
||||
var elementOrSectionName = args.shift();
|
||||
var getter = (isChaiAssertion && commandName === 'section') ? getSection : getElement;
|
||||
var elementOrSection = getter(parent, elementOrSectionName);
|
||||
var ancestors = getAncestorsWithElement(elementOrSection);
|
||||
|
||||
if (ancestors.length === 1) {
|
||||
firstArg = elementOrSection.selector;
|
||||
desiredStrategy = elementOrSection.locateStrategy;
|
||||
} else {
|
||||
firstArg = ancestors;
|
||||
desiredStrategy = 'recursion';
|
||||
}
|
||||
|
||||
setLocateStrategy(parent.client, desiredStrategy);
|
||||
args.unshift(firstArg);
|
||||
|
||||
// if a callback is being used with this command, wrap it in
|
||||
// a function that allows us to restore the locate strategy
|
||||
// to its original value before the callback is called
|
||||
|
||||
callbackIndex = findCallbackIndex(args);
|
||||
if (callbackIndex !== -1) {
|
||||
originalCallback = args[callbackIndex];
|
||||
|
||||
args[callbackIndex] = function callbackWrapper() {
|
||||
|
||||
// restore the locate strategy directly through client.locateStrategy.
|
||||
// setLocateStrategy() can't be used since it uses the api commands
|
||||
// which get added to the command queue and will not update the
|
||||
// strategy in time for the callback which is getting immediately
|
||||
// called after
|
||||
|
||||
parent.client.locateStrategy = prevLocateStrategy;
|
||||
return originalCallback.apply(parent.client.api, arguments);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var c = commandFn.apply(parent.client, args);
|
||||
if (elementCommand) {
|
||||
setLocateStrategy(parent.client, prevLocateStrategy);
|
||||
}
|
||||
return (isChaiAssertion ? c : parent);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Array} args
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isElementCommand(args) {
|
||||
return (args.length > 0) && (args[0].toString().indexOf('@') === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the location of a callback function within an arguments array.
|
||||
*
|
||||
* @param {Array} args Arguments array in which to find the location of a callback.
|
||||
* @returns {number} Index location of the callback in the args array. If not found, -1 is returned.
|
||||
*/
|
||||
function findCallbackIndex(args) {
|
||||
|
||||
if (args.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// callbacks will usually be the last argument. waitFor methods allow an additional
|
||||
// message argument to follow the callback which will also need to be checked for.
|
||||
|
||||
// last argument
|
||||
|
||||
var index = args.length - 1;
|
||||
if (typeof args[index] === 'function') {
|
||||
return index;
|
||||
}
|
||||
|
||||
// second to last argument (waitfor calls)
|
||||
|
||||
index--;
|
||||
if (typeof args[index] === 'function') {
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an array of ancestors of the supplied element. The last element in the array is the element object itself
|
||||
*
|
||||
* @param {Object} element The element
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getAncestorsWithElement(element) {
|
||||
var elements = [];
|
||||
function addElement(e) {
|
||||
elements.unshift(e);
|
||||
if (e.parent && e.parent.selector) {
|
||||
addElement(e.parent);
|
||||
}
|
||||
}
|
||||
addElement(element);
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds commands (elements commands, assertions, etc) to the page or section
|
||||
*
|
||||
* @param {Object} parent The parent page or section
|
||||
* @param {Object} target What the command is added to (parent|section or assertion object on parent|section)
|
||||
* @param {Object} commands
|
||||
* @returns {null}
|
||||
*/
|
||||
function applyCommandsToTarget(parent, target, commands) {
|
||||
|
||||
Object.keys(commands).forEach(function(commandName) {
|
||||
if (isValidAssertion(commandName)) {
|
||||
target[commandName] = target[commandName] || {};
|
||||
|
||||
var isChaiAssertion = commandName === 'expect';
|
||||
var assertions = commands[commandName];
|
||||
|
||||
Object.keys(assertions).forEach(function(assertionName) {
|
||||
target[commandName][assertionName] = addCommand(target[commandName], assertions[assertionName], assertionName, parent, isChaiAssertion);
|
||||
});
|
||||
} else {
|
||||
target[commandName] = addCommand(target, commands[commandName], commandName, parent, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addCommand(target, commandFn, commandName, parent, isChaiAssertion) {
|
||||
if (target[commandName]) {
|
||||
parent.client.results.errors++;
|
||||
var error = new Error('The command "' + commandName + '" is already defined!');
|
||||
parent.client.errors.push(error.stack);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return makeWrappedCommand(parent, commandFn, commandName, isChaiAssertion);
|
||||
}
|
||||
|
||||
function isValidAssertion(commandName) {
|
||||
return ['assert', 'verify', 'expect'].indexOf(commandName) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entrypoint to add commands (elements commands, assertions, etc) to the page or section
|
||||
*
|
||||
* @param {Object} parent The parent page or section
|
||||
* @param {function} commandLoader function that retrieves commands
|
||||
* @returns {null}
|
||||
*/
|
||||
this.addWrappedCommands = function (parent, commandLoader) {
|
||||
var commands = {};
|
||||
commands = commandLoader(commands);
|
||||
applyCommandsToTarget(parent, parent, commands);
|
||||
};
|
||||
|
||||
})();
|
24
tests/node_modules/nightwatch/lib/page-object/element.js
generated
vendored
Normal file
24
tests/node_modules/nightwatch/lib/page-object/element.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Class that all elements subclass from
|
||||
*
|
||||
* @param {Object} options Element options defined in page object
|
||||
* @constructor
|
||||
*/
|
||||
function Element(options) {
|
||||
this.parent = options.parent;
|
||||
|
||||
if (!options.selector) {
|
||||
throw new Error('No selector property for element "' + options.name +
|
||||
'" Instead found properties: ' + Object.keys(options));
|
||||
}
|
||||
|
||||
this.name = options.name;
|
||||
this.selector = options.selector;
|
||||
this.locateStrategy = options.locateStrategy || 'css selector';
|
||||
}
|
||||
|
||||
Element.prototype.toString = function() {
|
||||
return 'Element[name=@' + this.name + ']';
|
||||
};
|
||||
|
||||
module.exports = Element;
|
89
tests/node_modules/nightwatch/lib/page-object/page-utils.js
generated
vendored
Normal file
89
tests/node_modules/nightwatch/lib/page-object/page-utils.js
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
module.exports = new (function() {
|
||||
/**
|
||||
* Returns the properties object passed as an argument (or null if no arguments are passed).
|
||||
* If the supplied properties argument is a function, it invokes that function with the page as its context.
|
||||
*
|
||||
* @method createProps
|
||||
* @param {Page|Section} parent The page object or section instance
|
||||
* @param {Object|Function} props Object or Function that returns an object
|
||||
* @returns {null}
|
||||
*/
|
||||
this.createProps = function(parent, props) {
|
||||
parent.props = typeof props === 'function' ? props.call(parent) : props;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assigns the `elements` property for a page or section object.
|
||||
* For each object in the passed array, it creates a new element object by instantiating Element with its options
|
||||
*
|
||||
* @param {Page|Section} parent The page object or section instance
|
||||
* @param {Object|Array} elements Object or array of objects to become element objects
|
||||
* @returns {null}
|
||||
*/
|
||||
this.createElements = function(parent, elements) {
|
||||
var Element = require('./element.js');
|
||||
var elementObjects = {};
|
||||
var el;
|
||||
|
||||
if (!Array.isArray(elements)) {
|
||||
elements = [elements];
|
||||
}
|
||||
|
||||
elements.forEach(function(els) {
|
||||
Object.keys(els).forEach(function(e) {
|
||||
el = typeof els[e] === 'string' ? { selector: els[e] } : els[e];
|
||||
el.parent = parent;
|
||||
el.name = e;
|
||||
elementObjects[el.name] = new Element(el);
|
||||
});
|
||||
});
|
||||
|
||||
parent.elements = elementObjects;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assigns the `section` property for a page or section object.
|
||||
* For each object in the passed array, it creates a new section object by instantiating Section with its options
|
||||
*
|
||||
* @param {Page|Section} parent The page object or section instance
|
||||
* @param {Array} sections Array of objects to become section objects
|
||||
* @returns {null}
|
||||
*/
|
||||
this.createSections = function(parent, sections) {
|
||||
var Section = require('./section.js');
|
||||
var sectionObjects = {};
|
||||
var sec;
|
||||
|
||||
Object.keys(sections).forEach(function(s) {
|
||||
sec = sections[s];
|
||||
sec.parent = parent;
|
||||
sec.name = s;
|
||||
sectionObjects[sec.name] = new Section(sec);
|
||||
});
|
||||
|
||||
parent.section = sectionObjects;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixes in the passed functions to the page or section object.
|
||||
*
|
||||
* @param {Page|Section} parent The page object or section instance
|
||||
* @param {Object} command Array of commands that will be added to the age or section
|
||||
* @returns {null}
|
||||
*/
|
||||
this.addCommands = function(parent, commands) {
|
||||
commands.forEach(function(m) {
|
||||
Object.keys(m).forEach(function(k) {
|
||||
parent[k] = m[k];
|
||||
});
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
})();
|
66
tests/node_modules/nightwatch/lib/page-object/page.js
generated
vendored
Normal file
66
tests/node_modules/nightwatch/lib/page-object/page.js
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
var PageUtils = require('./page-utils.js');
|
||||
var CommandWrapper = require('./command-wrapper.js');
|
||||
|
||||
/**
|
||||
* Class that all pages subclass from
|
||||
*
|
||||
* @param {Object} options Page options defined in page object
|
||||
* @constructor
|
||||
*/
|
||||
function Page(options, commandLoader, api, client) {
|
||||
this.commandLoader = commandLoader;
|
||||
this.api = api;
|
||||
this.client = client;
|
||||
this.name = options.name;
|
||||
this.url = options.url;
|
||||
|
||||
PageUtils
|
||||
.createProps(this, options.props || {})
|
||||
.createElements(this, options.elements || {})
|
||||
.createSections(this, options.sections || {})
|
||||
.addCommands(this, options.commands || []);
|
||||
|
||||
CommandWrapper.addWrappedCommands(this, this.commandLoader);
|
||||
}
|
||||
|
||||
Page.prototype = {
|
||||
/**
|
||||
* 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 {string} url
|
||||
* @returns {string|null}
|
||||
*/
|
||||
getUrl: function(url) {
|
||||
if (typeof url === 'function') {
|
||||
return url.call(this);
|
||||
} else if (typeof url === 'string') {
|
||||
return 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 {*}
|
||||
*/
|
||||
navigate: function(url, callback) {
|
||||
var goToUrl = this.getUrl(url || this.url);
|
||||
if (goToUrl === null) {
|
||||
throw new Error('Invalid URL: You must either add a url property to "' +
|
||||
this.name + '" or provide a url as an argument');
|
||||
}
|
||||
this.api.url(goToUrl, callback);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Page;
|
38
tests/node_modules/nightwatch/lib/page-object/section.js
generated
vendored
Normal file
38
tests/node_modules/nightwatch/lib/page-object/section.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
var PageUtils = require('./page-utils.js');
|
||||
var CommandWrapper = require('./command-wrapper.js');
|
||||
|
||||
/**
|
||||
* Class that all sections subclass from
|
||||
*
|
||||
* @param {Object} options Section options defined in page object
|
||||
* @constructor
|
||||
*/
|
||||
function Section(options) {
|
||||
this.parent = options.parent;
|
||||
this.client = this.parent.client;
|
||||
|
||||
if(!options.selector) {
|
||||
throw new Error('No selector property for section "' + options.name +
|
||||
'" Instead found properties: ' + Object.keys(options));
|
||||
}
|
||||
|
||||
this.name = options.name;
|
||||
this.selector = options.selector;
|
||||
this.locateStrategy = options.locateStrategy || 'css selector';
|
||||
this.api = this.parent.api;
|
||||
this.commandLoader = this.parent.commandLoader;
|
||||
|
||||
PageUtils
|
||||
.createProps(this, options.props || {})
|
||||
.createElements(this, options.elements || {})
|
||||
.createSections(this, options.sections || {})
|
||||
.addCommands(this, options.commands || []);
|
||||
|
||||
CommandWrapper.addWrappedCommands(this, this.commandLoader);
|
||||
}
|
||||
|
||||
Section.prototype.toString = function() {
|
||||
return 'Section[name=' + this.name + ']';
|
||||
};
|
||||
|
||||
module.exports = Section;
|
Reference in New Issue
Block a user