UNPKG

it

Version:

A testing framework for node

437 lines (376 loc) 15.9 kB
"use strict"; var utils = require("../../utils"), splitFilter = utils.splitFilter, setUpCb = utils.setUpCb, _ = require("../../extended"), EventEmitter = require("./emitter"), isEmpty = _.isEmpty, isString = _.isString, merge = _.merge, Promise = _.Promise, Action = require("./action"); EventEmitter.extend({ instance: { sub: false, __timeout: null, parent: null, level: 0, stopOnError: false, __ignoreProcessError: false, constructor: function constructor(description, options) { this._super(arguments); this.Action = this._static.Action; this.description = description; this.__shoulds = []; this.__ba = []; this.__be = []; this.__aa = []; this.__ae = []; merge(this, options); if (!this.sub && !this.filtered) { this._static.tests[description] = this; this._static.trackName(this); } _.bindAll(this, ["_addAction", "ignoreErrors", "_failSiblings", "_addTest", "timeout", "getAction", "beforeAll", "beforeEach", "afterAll", "afterEach", "context", "get", "set", "skip"]); }, ignoreErrors: function (val) { if (_.isBoolean(val)) { this.__ignoreProcessError = val; } else { return this.__ignoreProcessError; } }, timeout: function (num) { this.__timeout = num; }, getAction: function getAction(name) { var matched = _.filter(this.__shoulds, function (should) { if (should instanceof this.Action) { return should.description === name; } else { return false; } }, this); return matched.length !== 0 ? matched[0] : null; }, as: function (mod) { mod.exports = this; return this; }, beforeAll: function (cb) { this.__ba.push(_.partial(setUpCb(cb), this)); return this; }, beforeEach: function (cb) { this.__be.push(_.partial(setUpCb(cb), this)); return this; }, afterAll: function (cb) { this.__aa.push(_.partial(setUpCb(cb), this)); return this; }, afterEach: function (cb) { this.__ae.push(_.partial(setUpCb(cb), this)); return this; }, context: function (cb) { var cloned = this._static.clone(this, null, {sub: true}); this.emit("addTest", cloned); if (cb) { cb(cloned); } this.__shoulds.push(cloned); return cloned; }, skip: function (description) { this._addAction(description, undefined, 'skipped'); }, _addTest: function (description, cb) { var cloned = this._static.clone(this, description, {sub: true, level: this.level + 1, parent: this}, cb), it = cloned._addAction; _(["suite", "test", "should", "describe", "timeout", "ignoreErrors", "getAction", "beforeAll", "beforeEach", "afterAll", "afterEach", "context", "get", "set", "skip"]).forEach(function (key) { if (_.isFunction(cloned[key])) { it[key] = cloned[key]; } }); if (cb) { cb(it); } this.__shoulds.push(cloned); return cloned; }, _addAction: function (description, cb, status) { var action = new this.Action(description, this, this.level + 1, cb, status); this._static.trackName(action); this.__shoulds.push(action); return this; }, __runAction: function (action) { var stopOnError = this.stopOnError; return action.run(this.__be, this.__ae).then( _.bind(this, function actionSuccess() { var ret = new Promise(), summary = action.get("summary"); if (summary.status === "skipped" || summary.status === "passed") { ret.callback(); } else { ret[stopOnError ? "errback" : "callback"](); } return ret; }), _.bind(this, function actionError(err) { this.error = err; this.emit("error", this); }) ); }, _failSiblings: function (err) { return _.serial(_.map(this.__shoulds, function (action) { return _.bind(this, function () { var ret; if (action instanceof Action) { this.emit("action", action); var start = new Date(); action.failed(start, start, err); ret = new Promise().callback(); } else { this.emit("test", action); action.emit("run", action); ret = action._failSiblings(err); } return ret; }); }, this)); }, run: function (filter) { var ret; if (filter) { ret = this.filter(filter).run(); } else { ret = this.__runPromise; if (!ret) { this.emit("run", this); ret = this.__runPromise = _.serial(this.__ba).then( _.bind(this, function () { return _.serial(_.map(this.__shoulds, function (action) { return _.bind(this, function () { var ret; if (action instanceof Action) { this.emit("action", action); ret = this.__runAction(action); } else { this.emit("test", action); ret = action.run(); } return ret; }); }, this)); }), _.bind(this, function (err) { return this._failSiblings(err); }) ).then( _.bind(this, function () { return _.serial(this.__aa); }), _.bind(this, function actionError(err) { this.error = err; this.emit("error", this); }) ).both(_.bind(this, "emit", "done", this)); } } return ret; }, matches: function matches(filter) { return this.description === filter; }, filter: function filter(f) { var ret = this, i, l; if (f.length) { f = isString(f) ? splitFilter(f) : [f]; if (f) { ret = this._static.clone(this, this.description, { sub: this.sub, "filtered": this.sub ? false : true, parent: this.parent, __ba: this.__ba.slice(0), __aa: this.__ba.slice(0), "__shoulds": _(this.__shoulds).map(function (action) { var rest, include = false, ret = null; for (i = 0, l = f.length; i < l && !include; i++) { if (action.description === f[i][0]) { include = true; rest = f[i].slice(1); } } if (include) { if (action instanceof this.Action) { ret = action; } else { ret = action.filter(rest); } } return ret; }, this).compact().value() }); } } return ret; }, getters: { summary: function () { var duration = 0, ret = {description: this.description, summaries: {}}, summaries = ret.summaries; _.map(this.__shoulds, function (action) { var actionSum = action.get("summary"); if (action instanceof Action || action.description) { summaries[action.description] = actionSum; duration += actionSum.duration; } else { merge(summaries, actionSum.summaries); duration += actionSum.duration; } }); ret.duration = duration; return ret; }, fullName: function () { var decription = ""; if (this.parent) { decription += this.parent.get("fullName") + ":"; } decription += " " + this.description; return decription; }, length: function () { return _(this.__shoulds).map(function (instance) { return instance instanceof Action ? 1 : instance.get("length"); }).sum().value(); }, errorCount: function () { return _(this.__shoulds).map(function (instance) { return (instance instanceof Action) ? (instance.get("status") === "failed" ? 1 : 0) : instance.get("errorCount"); }).sum().value(); }, successCount: function () { return _(this.__shoulds).map(function (instance) { return (instance instanceof Action) ? (instance.get("status") === "passed" ? 1 : 0) : instance.get("successCount"); }).sum().value(); }, pendingActions: function () { return _(this.__shoulds).filter(function (instance) { return (instance instanceof Action) && (instance.get("status") === "pending"); }).map(function (action) { return action.get("fullName"); }).value(); }, skippedCount: function () { return _(this.__shoulds).map(function (instance) { return (instance instanceof Action) ? (instance.get("status") === "skipped" ? 1 : 0) : instance.get("skippedCount"); }).sum().value(); } } }, "static": { tests: {}, init: function init() { this.Action = Action; }, testNames: {}, duplicateTestErrors: [], trackName: function(action){ var fullName = action.get("fullName"); if (this.testNames[fullName]) { this.duplicateTestErrors.push({ error: new Error("Duplicate describe block or test name:" + fullName), test: action }); } this.testNames[fullName] = true; }, checkDuplicateNames: function(){ if (this.duplicateTestErrors.length > 0) { _.bus.emit("printDuplicateActions", this.duplicateTestErrors); return true; } }, clone: function (behavior, description, options, cb) { return new this(description, merge({ __timeout: behavior.__timeout, level: behavior.level, __be: behavior.__be.slice(), __ae: behavior.__ae.slice(), parent: behavior, stopOnError: behavior.stopOnError, __ignoreProcessError: behavior.ignoreErrors() }, options), cb); }, __filter: function (filter) { var ret = {}, tests = this.tests, names = _.pluck(filter, "0"); _.forEach(names, function (t, index) { var test = tests[t]; if (test) { var filtered = tests[t].filter(filter[index].slice(1)); if (ret[t]) { ret[t].__shoulds = ret[t].__shoulds.concat(filtered.__shoulds); } else { ret[t] = filtered; } } }); return ret; }, run: function run(filter) { var summaries = {}, tests = this.tests; filter = splitFilter(filter); if (filter.length) { tests = this.__filter(filter); } if (!isEmpty(tests)) { if(this.checkDuplicateNames()){ throw new Error("Found " + this.duplicateTestErrors.length + " duplicate describe block and/or test name. Please use unique descriptions."); } _.bus.emit("start", {tests: tests, numActions: _(tests).values().invoke("get", "length").sum().value()}); return _.serial(_(tests).keys().map(function (k) { return function () { _.bus.emit("test", tests[k]); return tests[k].run().both(function (summary) { summaries[k] = summary; }); }; }).value()).then(_.bindIgnore(this, "printSummary")); } else { console.warn("No Tests found"); return _.resolve(); } }, printSummary: function printSummary() { var tests = this.tests; var summary = {summary: {}, totalCount: 0, errorCount: 0, successCount: 0, pendingCount: 0, pendingActions: [], skippedCount: 0}; if (!isEmpty(tests)) { var keys = _.hash.keys(tests), length = 0; _(tests).forEach(function (test, k) { var testSummary = test.get("summary"), pendingActions = test.get("pendingActions"); summary.totalCount += test.get("length"); summary.errorCount += test.get("errorCount"); summary.successCount += test.get("successCount"); summary.skippedCount += test.get("skippedCount"); summary.pendingCount += pendingActions.length, summary.pendingActions = summary.pendingActions.concat(pendingActions); if (testSummary) { summary.summary[k] = testSummary; length += 1; } }); if (length < keys.length) { _.bus.emit("error", new Error("Async Error")); } _.bus.emit("done", summary); } return summary; } } }).as(module);