UNPKG

nightwatch

Version:

Easy to use Node.js based End-to-End testing solution for browser based apps and websites, using the W3C WebDriver API.

239 lines (189 loc) 6.2 kB
const EventEmitter = require('events'); const BaseCommandLoader = require('./_base-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(async () => { 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; } 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 }); const result = this.resolveElementSelector(args) .then(elementResult => { if (elementResult) { args[0] = elementResult; } return instance.command(...args); }) .catch(err => { if (instance instanceof EventEmitter) { instance.emit('error', err); return; } throw err; }); if (instance instanceof EventEmitter) { return instance; } if (result instanceof Promise) { return result; } return result; }; } return this; } define(parent = null) { if (!this.commandFn) { return this; } this.validateMethod(parent); const {commandName, api, nightwatchInstance, commandQueue} = this; const commandFn = this.commandFn.bind(this); const args = [function queuedCommandFn(...args) { const stackTrace = CommandLoader.getOriginalStackTrace(queuedCommandFn); const deferred = Utils.createPromise(); // if this command was called from another async (custom) command const isAsyncCommand = this.isES6Async; // if this command was called from an async test case const isES6Async = Utils.isUndefined(this.isES6Async) ? nightwatchInstance.isES6AsyncTestcase : isAsyncCommand; const node = commandQueue.add({ commandName, commandFn, context: this, args, stackTrace, namespace, deferred, isES6Async }); if (isES6Async) { return node.deferred.promise; } return api; }]; let namespace; if (parent) { namespace = this.getTargetNamespace(parent); } else if (Array.isArray(this.namespace) && this.namespace.length > 0) { namespace = BaseCommandLoader.unflattenNamespace(api, this.namespace.slice()); } if (namespace) { args.unshift(namespace); } this.nightwatchInstance.setApiMethod(this.commandName, ...args); if (this.module && this.module.AliasName) { this.nightwatchInstance.setApiMethod(this.module.AliasName, ...args); } return this; } } module.exports = CommandLoader;