UNPKG

nightwatch

Version:

Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.

227 lines (180 loc) 6.42 kB
const EventEmitter = require('events'); const BaseCommandLoader = require('./_command-loader.js'); const Utils = require('../../utils'); class CommandLoader extends BaseCommandLoader { static get interfaceMethods() { return { command: 'function' }; } static isDeprecatedCommandStyle(CommandModule) { return Utils.isObject(CommandModule) && Utils.isFunction(CommandModule.command); } /** * This is to support backwards-compatibility for commands defined as objects, * with a command() property * * @param CommandModule */ static createFromObject(CommandModule) { return class CommandClass extends EventEmitter { command(...args) { if (Utils.isES6AsyncFn(CommandModule.command)) { return CommandModule.command.apply(this.api, args); } setImmediate(() => { CommandModule.command.apply(this.api, args); }); return this.api; } }; } static transportActions({actions, api}) { return new Proxy(actions, { get(target, name) { return function(...args) { let callback; let method; let isLastArgFunction = Utils.isFunction(args[args.length-1]); if (isLastArgFunction) { callback = args.pop(); } else if (args.length === 0 || !isLastArgFunction) { callback = function(result) {return result}; } const definition = { args }; if (name in target.session) { // actions that require the current session method = target.session[name]; definition.sessionId = api.sessionId; } else { method = target[name]; } return method(definition).then((result) => Utils.makePromise(callback, api, [result])); }; } }); } static createInstance(nightwatchInstance, CommandModule, opts) { const CommandClass = CommandLoader.isDeprecatedCommandStyle(CommandModule) ? CommandLoader.createFromObject(CommandModule) : CommandModule; class CommandInstance extends CommandClass { get api() { const isES6Async = this.isES6AsyncCommand; return { ...nightwatchInstance.api, get isES6Async() { return isES6Async; } }; } get isES6AsyncCommand() { return Utils.isES6AsyncFn( CommandLoader.isDeprecatedCommandStyle(CommandModule) ? CommandModule.command: this.command ); } get client() { return this.__nightwatchInstance || nightwatchInstance; } get commandFileName() { return opts.commandName; } get commandArgs() { return opts.args; } get transportActions() { return this.client.transportActions; } get driver() { return this.client.transport.driver; } httpRequest(requestOptions) { return this.client.transport.runProtocolAction(requestOptions); } toString() { return `${this.constructor.name} [name=${opts.commandName}]`; } complete(...args) { if (Utils.isFunction(super.complete)) { return super.complete(...args); } this.emit('complete', ...args); } } const instance = new CommandInstance(); Object.keys(CommandLoader.interfaceMethods).forEach(method => { let type = CommandLoader.interfaceMethods[method]; if (!BaseCommandLoader.isTypeImplemented(instance, method, type)) { throw new Error(`Command class must implement method .${method}()`); } }); instance.stackTrace = opts.stackTrace; instance.needsPromise = CommandLoader.isDeprecatedCommandStyle(CommandModule); return instance; } get loadSubDirectories() { return true; } createWrapper() { if (this.module) { this.commandFn = function commandFn({args, stackTrace}) { const instance = CommandLoader.createInstance(this.nightwatchInstance, this.module, { stackTrace, args, commandName: this.commandName }); if (instance.w3c_deprecated) { let extraMessage = instance.deprecationNotice ? `\n ${instance.deprecationNotice}` : ''; // eslint-disable-next-line no-console console.warn(`This command has been deprecated and is removed from the W3C Webdriver standard. It is only working with legacy Selenium JSONWire protocol.${extraMessage}`); } const result = this.resolveElementSelector(args) .then(elementResult => { if (elementResult) { args[0] = elementResult; } this.nightwatchInstance.isES6AsyncCommand = instance.isES6AsyncCommand && this.isUserDefined; return instance.command(...args); }) .catch(err => { if (instance instanceof EventEmitter) { instance.emit('error', err); return; } throw err; }) .then(result => { let reportErrors = instance.client.settings.report_command_errors; let reportNetworkErrors = instance.client.settings.report_network_errors; if (result && result.code && result.error && result.status === -1 && reportNetworkErrors) { // node.js errors, e.g. ECONNRESET reportErrors = true; } if (result && result.status === -1 && instance.reportProtocolErrors(result) && reportErrors) { result.error.registered = true; const error = new Error(`Error while running .${this.commandName}(): ${result.error}`); instance.client.reporter.registerTestError(error); } return result; }); if (instance instanceof EventEmitter) { return instance; } if (result instanceof Promise) { return result; } return result; }; } return this; } getTargetNamespace(parent) { let namespace; if (parent) { namespace = super.getTargetNamespace(parent); } else if (Array.isArray(this.namespace) && this.namespace.length > 0) { namespace = BaseCommandLoader.unflattenNamespace(this.api, this.namespace.slice()); } return namespace; } } module.exports = CommandLoader;