Source: steps/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"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;
};