diff --git a/ospec/ospec.js b/ospec/ospec.js index a8d7b306..eadb4150 100644 --- a/ospec/ospec.js +++ b/ospec/ospec.js @@ -7,17 +7,21 @@ else window.o = m() var spec = {}, subjects = [], results, only = null, ctx = spec, start, stack = 0, nextTickish, hasProcess = typeof process === "object", hasOwn = ({}).hasOwnProperty var ospecFileName = getStackName(ensureStackTrace(new Error), /[\/\\](.*?):\d+:\d+/), timeoutStackName var globalTimeout = noTimeoutRightNow + var hooks = { + __before: true, + __beforeEach: true, + __after: true, + __afterEach: true + } if (name != null) spec[name] = ctx = {} function o(subject, predicate) { - if (predicate === undefined) { - if (results == null) throw new Error("Assertions should not occur outside test definitions") - return new Assert(subject) - } - else if (results == null) { + if (predicate === undefined) return new Assert(subject) + else { + subject = String(subject) + if (hasOwn.call(hooks, subject)) throw new Error("'" + subject + "' is a reserved test name") + if (subject.slice(0, 2) === "__") console.warn("test names starting with '__' are reserved for internal use\n" + o.cleanStackTrace(ensureStackTrace(new Error))) ctx[unique(subject)] = new Task(predicate, ensureStackTrace(new Error)) - } else { - throw new Error("Test definition shouldn't be nested. To group tests use `o.spec()`") } } o.before = hook("__before") @@ -36,7 +40,6 @@ else window.o = m() highlight("/!\\ WARNING /!\\ o.only() mode") + "\n" + o.cleanStackTrace(ensureStackTrace(new Error)) + "\n", cStyle("red"), "" ) - o(subject, only = predicate) } o.spy = function(fn) { @@ -93,23 +96,25 @@ else window.o = m() function test(spec, pre, post, finalize) { pre = [].concat(pre, spec["__beforeEach"] || []) post = [].concat(spec["__afterEach"] || [], post) - series([].concat(spec["__before"] || [], Object.keys(spec).map(function(key) { - return new Task(function(done, timeout) { - timeout(Infinity) - if (key.slice(0, 2) === "__") return done() - if (only !== null && spec[key].fn !== only && spec[key] instanceof Task) return done() + series([].concat(spec["__before"] || [], Object.keys(spec).reduce(function(tasks, key) { + if (!hasOwn.call(hooks, key)) { + tasks.push(new Task(function(done, timeout) { + timeout(Infinity) + if (only !== null && spec[key].fn !== only && spec[key] instanceof Task) return done() - subjects.push(key) - var pop = new Task(function pop() { - subjects.pop() - done() - }, null) + subjects.push(key) + var pop = new Task(function pop() { + subjects.pop() + done() + }, null) - if (spec[key] instanceof Task) series([].concat(pre, spec[key], post, pop)) - else test(spec[key], pre, post, pop) + if (spec[key] instanceof Task) series([].concat(pre, spec[key], post, pop)) + else test(spec[key], pre, post, pop) - }, null) - }), spec["__after"] || [], finalize)) + }, null)) + } + return tasks + }, []), spec["__after"] || [], finalize)) } function series(tasks) { @@ -162,7 +167,8 @@ else window.o = m() fn(done, setDelay) } catch (e) { - finalizeAsync(e) + if (task.err != null) finalizeAsync(e) + else throw e } if (timeout === 0) { startTimer() @@ -239,8 +245,16 @@ else window.o = m() return false } - function Assert(value) {this.value = value} - function Task(fn, err) {this.fn = fn, this.err = err} + function isRunning() {return results != null} + function Assert(value) { + if (!isRunning()) throw new Error("Assertions should not occur outside test definitions") + this.value = value + } + function Task(fn, err) { + if (err != null && isRunning()) throw new Error("Test definitions and hooks shouldn't be nested. To group tests use `o.spec()`") + this.fn = fn + this.err = err + } function define(name, verb, compare) { Assert.prototype[name] = function assert(value) { if (compare(this.value, value)) record(null) diff --git a/ospec/tests/test-ospec.js b/ospec/tests/test-ospec.js index 753acdf8..2d949a8a 100644 --- a/ospec/tests/test-ospec.js +++ b/ospec/tests/test-ospec.js @@ -3,91 +3,96 @@ var callAsync = require("../../test-utils/callAsync") var o = require("../ospec") -new function(o) { - o = o.new() +o("o.only", function(done) { + var oo = o.new() - o.spec("ospec", function() { - o("skipped", function() { - o(true).equals(false) + oo.spec("ospec", function() { + oo("skipped", function() { + oo(true).equals(false) }) - o.only(".only()", function() { - o(2).equals(2) + oo.only(".only()", function() { + oo(2).equals(2) }, true) }) - o.run() -}(o) - -new function(o) { - var clone = o.new() - - clone.spec("clone", function() { - clone("fail", function() { - clone(true).equals(false) - }) - - clone("pass", function() { - clone(true).equals(true) - }) + oo.run(function(results){ + o(results.length).equals(1) + o(results[0].pass).equals(true) + done() }) +}) - // Predicate test passing on clone results - o.spec("reporting", function() { - o("reports per instance", function(done, timeout) { - timeout(100) // Waiting on clone +// Predicate test passing on clone results +o.spec("reporting", function() { + var oo + o.beforeEach(function(){ + oo = o.new() - clone.run(function(results) { - o(typeof results).equals("object") - o("length" in results).equals(true) - o(results.length).equals(2)("Two results") + oo.spec("clone", function() { + oo("fail", function() { + oo(true).equals(false) + }) - o("error" in results[0] && "pass" in results[0]).equals(true)("error and pass keys present in failing result") - o(!("error" in results[1]) && "pass" in results[1]).equals(true)("only pass key present in passing result") - o(results[0].pass).equals(false)("Test meant to fail has failed") - o(results[1].pass).equals(true)("Test meant to pass has passed") - - done() + oo("pass", function() { + oo(true).equals(true) }) }) - o("o.report() returns the number of failures", function () { - var log = console.log, error = console.error - console.log = o.spy() - console.error = o.spy() + }) + o("reports per instance", function(done, timeout) { + timeout(100) // Waiting on clone - function makeError(msg) {try{throw msg ? new Error(msg) : new Error} catch(e){return e}} - try { - var errCount = o.report([{pass: true}, {pass: true}]) + oo.run(function(results) { + o(typeof results).equals("object") + o("length" in results).equals(true) + o(results.length).equals(2)("Two results") - o(errCount).equals(0) - o(console.log.callCount).equals(1) - o(console.error.callCount).equals(0) + o("error" in results[0] && "pass" in results[0]).equals(true)("error and pass keys present in failing result") + o(!("error" in results[1]) && "pass" in results[1]).equals(true)("only pass key present in passing result") + o(results[0].pass).equals(false)("Test meant to fail has failed") + o(results[1].pass).equals(true)("Test meant to pass has passed") - errCount = o.report([ - {pass: false, error: makeError("hey"), message: "hey"} - ]) - - o(errCount).equals(1) - o(console.log.callCount).equals(2) - o(console.error.callCount).equals(1) - - errCount = o.report([ - {pass: false, error: makeError("hey"), message: "hey"}, - {pass: true}, - {pass: false, error: makeError("ho"), message: "ho"} - ]) - - o(errCount).equals(2) - o(console.log.callCount).equals(3) - o(console.error.callCount).equals(3) - } catch (e) { - o(1).equals(0)("Error while testing the reporter") - } - - console.log = log - console.error = error + done() }) }) -}(o) + o("o.report() returns the number of failures", function () { + var log = console.log, error = console.error + console.log = o.spy() + console.error = o.spy() + + function makeError(msg) {try{throw msg ? new Error(msg) : new Error} catch(e){return e}} + try { + var errCount = o.report([{pass: true}, {pass: true}]) + + o(errCount).equals(0) + o(console.log.callCount).equals(1) + o(console.error.callCount).equals(0) + + errCount = o.report([ + {pass: false, error: makeError("hey"), message: "hey"} + ]) + + o(errCount).equals(1) + o(console.log.callCount).equals(2) + o(console.error.callCount).equals(1) + + errCount = o.report([ + {pass: false, error: makeError("hey"), message: "hey"}, + {pass: true}, + {pass: false, error: makeError("ho"), message: "ho"} + ]) + + o(errCount).equals(2) + o(console.log.callCount).equals(3) + o(console.error.callCount).equals(3) + } catch (e) { + o(1).equals(0)("Error while testing the reporter") + } + + console.log = log + console.error = error + }) +}) + o.spec("ospec", function() { o.spec("sync", function() { @@ -197,7 +202,7 @@ o.spec("ospec", function() { done() }) }) - + o.beforeEach(function(done) { o(b).equals(0) callAsync(function() { @@ -211,15 +216,15 @@ o.spec("ospec", function() { done() }) }) - + o("hooks work as intended the first time", function(done) { callAsync(function() { var spy = o.spy() spy(a) - + o(a).equals(1) o(b).equals(1) - + done() }) }) @@ -227,10 +232,10 @@ o.spec("ospec", function() { callAsync(function() { var spy = o.spy() spy(a) - + o(a).equals(1) o(b).equals(1) - + done() }) })