UNPKG

nightwatch

Version:

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

179 lines (145 loc) 4.92 kB
const Utils = require('../../utils'); const Element = require('../../element'); const {AssertionRunner} = require('../../assertion'); const {Logger} = Utils; class AssertionScheduler { get instance() { return this.__instance; } get opts() { return this.__opts; } /** * @param {AssertionInstance} instance * @param {{rescheduleInterval, abortOnFailure, stackTrace, timeout, reporter}} opts */ constructor(instance, opts = {}) { this.__instance = instance; this.__opts = opts; this.retries = 0; this.deferred = Utils.createPromise(); this.shouldRetry = this.opts.timeout > 0; this.setOptsFromSelector(); } reschedule() { setTimeout(() => { this.retries++; this.schedule(); }, this.opts.rescheduleInterval); } setOptsFromSelector() { if (this.instance.element instanceof Element) { const {timeout, retryInterval} = this.instance.element; if (!Utils.isUndefined(timeout)) { this.__opts.timeout = timeout; } if (!Utils.isUndefined(retryInterval)) { this.__opts.rescheduleInterval = retryInterval; } } } createAssertionRunner({passed, actual, commandCallback, message, elapsedTime}) { const expected = Utils.isFunction(this.instance.expected) ? this.instance.expected() : this.instance.expected; const {abortOnFailure, stackTrace, reporter} = this.opts; this.runner = new AssertionRunner({ passed, err: { expected, actual }, calleeFn: commandCallback, message, abortOnFailure, stackTrace, reporter, elapsedTime }); } verifyCommandArgs() { if (this.instance.command.length === 0) { throw new Error(`Assertion "command" methods require one "callback" parameter. Check the command method in: ${this.instance.fileName}.`); } return this; } start() { this.startTime = new Date().getTime(); this.schedule(); return this.deferred.promise; } schedule() { this.instance.command(function commandCallback(commandResult, commandInstance) { const {instance, startTime} = this; const elapsedTime = new Date().getTime() - startTime; if (commandInstance) { this.__opts.abortOnFailure = commandInstance.abortOnFailure; } let passed; let value; instance.elapsedTime = elapsedTime; instance.setResult(commandResult); if (instance.hasFailure()) { passed = false; value = null; } else { value = instance.getValue(); passed = instance.isOk(value); } if (!passed && elapsedTime < this.opts.timeout) { this.reschedule(); return null; } const message = this.getFullMessage(passed, elapsedTime); const actual = instance.getActual(); this.createAssertionRunner({passed, actual, commandCallback, message, elapsedTime}); // FIXME: Returning the promise here in the event the it's needed in the assertion callback, // throws an unhandledRejection when using es6 async because there's no catch in the assertion callback // - adding the catch should be done when breaking changes are going to be introduced. let resolveValue = {}; let testError; if (commandResult && commandResult.error instanceof Error) { testError = commandResult.error; } return this.runner .run(commandResult) .then(result => { resolveValue = Object.assign(resolveValue, result); resolveValue.passed = true; }) .catch(err => { resolveValue.passed = false; resolveValue.err = err; return err; }) .then(() => { Utils.makePromise(this.instance.doneCallback, this, [resolveValue, this.instance]); }) .then(_ => { if (resolveValue.passed) { this.deferred.resolve(resolveValue); } else { this.deferred.reject(resolveValue.err); } return resolveValue; }) .then(_ => { if (testError && testError.name !== 'NoSuchElementError') { this.opts.reporter.registerTestError(testError); } }); }.bind(this), this.instance); } getFullMessage(passed, elapsedTime) { if (!this.shouldRetry) { return this.instance.message; } let timeLogged = passed ? elapsedTime : this.opts.timeout; if (this.instance.message.endsWith('.')) { this.instance.message = this.instance.message.substr(0, this.instance.message.length - 1); } return `${this.instance.message} ${(passed ? Logger.colors.stack_trace(`(${timeLogged}ms)`) : `in ${timeLogged}ms`)}`; } } module.exports.create = function(instance, opts) { const scheduler = new AssertionScheduler(instance, opts); scheduler.verifyCommandArgs(); return scheduler.start(); };