"use strict";
/**
* Creates new instance of `Steps` class.
*
* @class
* @classdesc Contains collection of steps which may be called inside tests via
* its instance [$](global.html#$). It mixes steps from plugins too.
* @name Steps
* @mixes TimerSteps
* @prop {object} ctx - Storage to share some data between steps.
*/
var util = require("util");
require("colors");
var _ = require("lodash");
var U = require("glace-utils");
var CONF = require("../config");
var plugins = require("../plugins");
var tools = require("../tools");
const utils = require("../utils");
var Steps = function () {
this.ctx = {};
};
module.exports = Steps;
Steps.prototype.resetCtx = function () {
/**
* Helper to reset steps context.
*
* @memberOf Steps
* @method resetCtx
* @instance
*/
this.ctx = {};
};
Steps.prototype.isTestFailed = function () {
/**
* Helper to check whether test was failed before current step.
*
* @memberOf Steps
* @method isTestFailed
* @instance
* @return {undefined|boolean} `undefined` if test is absent,
* `true` if test was failed, `false` otherwise.
*/
if (!CONF.test.curCase) return;
return !!CONF.test.curCase.errors.length;
};
Steps.prototype.debug = async function () {
/**
* Step to enter to interactive debugging mode. May be used inside test if you
* need to debug test in runtime.
*
* @async
* @memberOf Steps
* @method debug
* @instance
* @return {Promise<void>}
* @example
*
* test("my test", () => {
* chunk(async () => {
* await $.debug();
* });
* });
*/
var onFail = CONF.session.debugOnFail;
CONF.session.debugOnFail = false;
await U.debug(setupDebug());
CONF.session.debugOnFail = onFail;
};
Steps.prototype.listSteps = function (filter, namesOnly) {
/**
* Step to list available steps [debug mode].
*
* @memberOf Steps
* @method listSteps
* @instance
* @arg {string} filter - Steps filter.
* @arg {boolean} [namesOnly=false] - Search among step names only. By default
* full-text search is used.
*/
tools.printSteps(filter, namesOnly);
};
/**
* Registers steps (mixes them).
*
* @method
* @static
* @arg {...object} steps - Sequence of steps to register.
* @example
*
* var MyStepsMixin = require("./my-steps-mixin");
* var AnotherStepsMixin = require("./another-steps-mixin");
*
* Steps.register(MyStepsMixin, AnotherStepsMixin);
*/
Steps.register = function () {
for (var obj of arguments) {
_.assign(this.prototype, obj);
}
};
/**
* Helper to get steps instance.
*
* It wraps steps class with proxy object. Proxy observes steps call and in
* debug mode if steps is failed it entered test to interactive debug mode.
*
* @method
* @static
* @arg {function} [cls] - Class with steps. By default original glace `Steps`
* will be used.
* @return {Proxy} Wrapped steps instance.
*/
Steps.getInstance = function (cls) {
return new Proxy(
new (cls || Steps),
{
get: (target, property) => {
var func = target[property];
if (!util.isFunction(func)) return func;
if (property === "debug" || !CONF.session.debugOnFail) {
return func;
}
return async function () {
try {
var result = await func.apply(target, arguments);
} catch (e) {
console.log(util.format(e).red);
await target.debug();
throw e;
}
return result;
};
},
});
};
Steps.register(require("./timer"));
/* Load plugins steps */
Steps.register.apply(Steps, plugins.getModules("Steps"));
/**
* Set up debug mode for glace.
* @ignore
*/
const setupDebug = () => {
global.search = $.listSteps.bind($);
global.doc = f => {
const doc = utils.getDoc(f);
if (doc) {
console.log(doc.green);
} else {
console.log("No docs found".yellow);
}
};
const helpMsg = "In interactive mode you can execute any nodejs code or glace step.\n" +
"Available commands:\n" +
"- search([query string]) - Searchs a glace step according to query. " +
"For ex: > search('step to start timer');\n" +
"- doc(function) - Prints function documentation;\n";
return helpMsg;
};