UNPKG

compound-ex4

Version:

Compound-ex4 - MVC framework for NodeJS (ExpressJs 4 version), fork compoundjs(https://github.com/1602/compound)

227 lines (196 loc) 6.54 kB
// a thing that runs tests. // Every "test" is also a harness. If they do not have a harness, // then they are attached to the defaut "global harness", // which writes its results to stdout. // TODO: // - Bailout should stop running any tests. // - "skip" in the test config obj should skip it. module.exports = Harness var EE = require("events").EventEmitter require("inherits")(Harness, EE) var Results = require("./tap-results") , TapProducer = require("./tap-producer") , assert = require("./tap-assert") function Harness (Test) { if (!(this instanceof Harness)) return new Harness(Test) //console.error("Test in "+this.constructor.name, Test) this._Test = Test this._plan = null this._children = [] this._processing = false this._testCount = 0 this._planSum = 0 this.results = new Results() // emit result events on the harness. //this.results.on("result", function (res) { // console.error("proxying result ev from res to harness") // this.emit("result", res) //}.bind(this)) var me = this this.results.on("result", this.emit.bind(this, "result")) var p = this.process.bind(this) this.process = function () { this._processing = true process.nextTick(p) } this.output = new TapProducer() EE.call(this) } // this function actually only gets called bound to // the Harness object, and on process.nextTick. Even if // passed as an event handler, everything *else* will // happen before it gets called. Harness.prototype.process = function () { //console.error("harness process") // "end" can emit multiple times, so only actually move on // to the next test if the current one is actually over. // TODO: multiple in-process tests, if all are marked "async" if (this._current) { if (!this._current._ended) return // handle the current one before moving onto the next. this.childEnd(this._current) } var skip = true while (skip) { //console.error("checking for skips") var current = this._current = this._children.shift() if (current) { skip = current.conf.skip if (skip) { //console.error("add a failure for the skipping") this.results.add(assert.fail(current.conf.name ,{skip:true, diag:false})) } } else skip = false } // keep processing through skipped tests, instead of running them. if (current && this._bailedOut) { return this.process() } //console.error("got current?", !!current) if (current) { current.on("end", this.process) current.emit("ready") //console.error("emitted ready") //console.error("_plan", this._plan, this.constructor.name) } else if (!this._plan || (this._plan && this._plan === this._testCount)) { //console.error("Harness process: no more left. ending") if (this._endNice) { this._endNice() } else { this.end() } } else { this._processing = false; } } Harness.prototype.end = function () { if (this._children.length) { return this.process() } //console.error("harness end", this.constructor.name) if (this._bailedOut) return // can't call .end() more than once. if (this._ended) { //console.error("adding failure for end calling") this.results.add(assert.fail("end called more than once")) } // see if the plan is completed properly, if there was one. if (this._plan !== null) { var total = this._testCount if (total !== this._plan) { this.results.add(assert.equal(total, this._plan, "test count != plan")) } this._plan = total } //console.error("setting ended true", this.constructor.name) this._ended = true this.emit("end") } Harness.prototype.plan = function (p) { //console.error("setting plan", new Error().stack) if (this._plan !== null) { //console.error("about to add failure for calling plan") return this.results.add(assert.fail("plan set multiple times")) } this._plan = p if (p === 0 || this.results.testsTotal) { this.end() } } Harness.prototype.childEnd = function (child) { //console.error("childEnd") this._testCount ++ this._planSum += child._plan //console.error("adding set of child.results") this.results.add(child.conf.name || "(unnamed test)") this.results.addSet(child.results) this.emit("childEnd", child) // was this planned? if (this._plan === this._testCount) { //console.error("plan", [this._plan, this._testCount]) return this.end() } } function copyObj(o) { var copied = {} Object.keys(o).forEach(function (k) { copied[k] = o[k] }) return copied } Harness.prototype.test = function test (name, conf, cb) { if (this._bailedOut) return if (typeof conf === "function") cb = conf, conf = null if (typeof name === "object") conf = name, name = null if (typeof name === "function") cb = name, name = null conf = (conf ? copyObj(conf) : {}) name = name || "" //console.error("making test", [name, conf, cb]) // timeout: value in milliseconds. Defaults to 30s // Set to Infinity to have no timeout. if (isNaN(conf.timeout)) conf.timeout = 30000 var t = new this._Test(this, name, conf) var self = this if (cb) { //console.error("attaching cb to ready event") t.on("ready", function () { if (!isNaN(conf.timeout) && isFinite(conf.timeout)) { var timer = setTimeout(this.timeout.bind(this), conf.timeout) var clear = function () { clearTimeout(timer) } t.on("end", clear) t.on("bailout", function (message) { self.bailout(message) clear() }) } }) t.on("ready", cb.bind(t, t)) // proxy the child results to this object. //t.on("result", function (res) { // console.error("in harness, proxying result up") // t.results.add(res) //}) } return t } Harness.prototype.bailout = function (message) { // console.error("Harness bailout", this.constructor.name) message = message || "" //console.error("adding bailout message result") this.results.add({bailout: message}) // console.error(">>> results after bailout" , this.results) this._bailedOut = true this.emit("bailout", message) this.output.end({bailout: message}) } Harness.prototype.add = function (child) { //console.error("adding child") this._children.push(child) if (!this._processing) this.process() } // the tearDown function is *always* guaranteed to happen. // Even if there's a bailout. Harness.prototype.tearDown = function (fn) { this.on("end", fn) }