[ospec] Detect incomplete assertions
This commit is contained in:
parent
21a6a857ef
commit
285d00742d
2 changed files with 66 additions and 28 deletions
|
|
@ -7,6 +7,7 @@ else window.o = m()
|
||||||
var spec = {}, subjects = [], results, only = null, ctx = spec, start, stack = 0, nextTickish, hasProcess = typeof process === "object", hasOwn = ({}).hasOwnProperty
|
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 ospecFileName = getStackName(ensureStackTrace(new Error), /[\/\\](.*?):\d+:\d+/), timeoutStackName
|
||||||
var globalTimeout = noTimeoutRightNow
|
var globalTimeout = noTimeoutRightNow
|
||||||
|
var currentTestError = null
|
||||||
var hooks = {
|
var hooks = {
|
||||||
__before: true,
|
__before: true,
|
||||||
__beforeEach: true,
|
__beforeEach: true,
|
||||||
|
|
@ -17,8 +18,11 @@ else window.o = m()
|
||||||
if (name != null) spec[name] = ctx = {}
|
if (name != null) spec[name] = ctx = {}
|
||||||
|
|
||||||
function o(subject, predicate) {
|
function o(subject, predicate) {
|
||||||
if (predicate === undefined) return new Assert(subject)
|
if (predicate === undefined) {
|
||||||
else {
|
if (!isRunning()) throw new Error("Assertions should not occur outside test definitions")
|
||||||
|
return new Assert(subject)
|
||||||
|
} else {
|
||||||
|
if (isRunning()) throw new Error("Test definitions and hooks shouldn't be nested. To group tests use `o.spec()`")
|
||||||
subject = String(subject)
|
subject = String(subject)
|
||||||
if (hasOwn.call(hooks, subject)) throw new Error("'" + subject + "' is a reserved test name")
|
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)))
|
if (subject.slice(0, 2) === "__") console.warn("test names starting with '__' are reserved for internal use\n" + o.cleanStackTrace(ensureStackTrace(new Error)))
|
||||||
|
|
@ -133,9 +137,10 @@ else window.o = m()
|
||||||
if (cursor === tasks.length) return
|
if (cursor === tasks.length) return
|
||||||
|
|
||||||
var task = tasks[cursor++]
|
var task = tasks[cursor++]
|
||||||
var current = cursor
|
|
||||||
var fn = task.fn
|
var fn = task.fn
|
||||||
|
currentTestError = task.err
|
||||||
var timeout = 0, delay = defaultDelay, s = new Date
|
var timeout = 0, delay = defaultDelay, s = new Date
|
||||||
|
var current = cursor
|
||||||
var arg
|
var arg
|
||||||
|
|
||||||
globalTimeout = setDelay
|
globalTimeout = setDelay
|
||||||
|
|
@ -150,9 +155,9 @@ else window.o = m()
|
||||||
}
|
}
|
||||||
// for internal use only
|
// for internal use only
|
||||||
function finalizeAsync(err) {
|
function finalizeAsync(err) {
|
||||||
if (err) {
|
if (err != null) {
|
||||||
if (err instanceof Error) record(err.message, err, task.err)
|
if (err instanceof Error) fail(new Assert, err.message, err)
|
||||||
else record(String(err), null, task.err)
|
else fail(new Assert, String(err), null)
|
||||||
}
|
}
|
||||||
if (timeout !== undefined) timeout = clearTimeout(timeout)
|
if (timeout !== undefined) timeout = clearTimeout(timeout)
|
||||||
if (current === cursor) next()
|
if (current === cursor) next()
|
||||||
|
|
@ -256,34 +261,32 @@ else window.o = m()
|
||||||
|
|
||||||
function isRunning() {return results != null}
|
function isRunning() {return results != null}
|
||||||
function Assert(value) {
|
function Assert(value) {
|
||||||
if (!isRunning()) throw new Error("Assertions should not occur outside test definitions")
|
|
||||||
this.value = value
|
this.value = value
|
||||||
|
this.i = results.length
|
||||||
|
results.push({pass: null, context: "", message: "Incomplete assertion in the test definition starting at...", error: currentTestError, testError: currentTestError})
|
||||||
}
|
}
|
||||||
function Task(fn, err) {
|
function Task(fn, err) {
|
||||||
// Tasks defined internally don't have an `err`, and may be created at run-time.
|
|
||||||
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.fn = fn
|
||||||
this.err = err
|
this.err = err
|
||||||
}
|
}
|
||||||
function define(name, verb, compare) {
|
function define(name, verb, compare) {
|
||||||
Assert.prototype[name] = function assert(value) {
|
Assert.prototype[name] = function assert(value) {
|
||||||
if (compare(this.value, value)) record(null)
|
if (compare(this.value, value)) succeed(this)
|
||||||
else record(serialize(this.value) + "\n " + verb + "\n" + serialize(value))
|
else fail(this, serialize(this.value) + "\n " + verb + "\n" + serialize(value))
|
||||||
|
var self = this
|
||||||
return function(message) {
|
return function(message) {
|
||||||
var result = results[results.length - 1]
|
if (!self.pass) self.message = message + "\n\n" + self.message
|
||||||
result.message = message + "\n\n" + result.message
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function record(message, error, fallbackError) {
|
function succeed(assertion) {
|
||||||
var result = {pass: message === null}
|
results[assertion.i].pass = true
|
||||||
if (result.pass === false) {
|
}
|
||||||
result.context = subjects.join(" > ")
|
function fail(assertion, message, error) {
|
||||||
result.message = message
|
results[assertion.i].pass = false
|
||||||
result.error = error != null ? error : ensureStackTrace(new Error)
|
results[assertion.i].context = subjects.join(" > ")
|
||||||
result.fallbackError = fallbackError
|
results[assertion.i].message = message
|
||||||
}
|
results[assertion.i].error = error != null ? error : ensureStackTrace(new Error)
|
||||||
results.push(result)
|
|
||||||
}
|
}
|
||||||
function serialize(value) {
|
function serialize(value) {
|
||||||
if (hasProcess) return require("util").inspect(value)
|
if (hasProcess) return require("util").inspect(value)
|
||||||
|
|
@ -318,10 +321,15 @@ else window.o = m()
|
||||||
o.report = function (results) {
|
o.report = function (results) {
|
||||||
var errCount = 0
|
var errCount = 0
|
||||||
for (var i = 0, r; r = results[i]; i++) {
|
for (var i = 0, r; r = results[i]; i++) {
|
||||||
|
if (r.pass == null) {
|
||||||
|
r.testError.stack = r.message + "\n" + o.cleanStackTrace(r.testError)
|
||||||
|
r.testError.message = r.message
|
||||||
|
throw r.testError
|
||||||
|
}
|
||||||
if (!r.pass) {
|
if (!r.pass) {
|
||||||
var stackTrace = o.cleanStackTrace(r.error)
|
var stackTrace = o.cleanStackTrace(r.error)
|
||||||
var couldHaveABetterStackTrace = !stackTrace || timeoutStackName != null && stackTrace.indexOf(timeoutStackName) !== -1
|
var couldHaveABetterStackTrace = !stackTrace || timeoutStackName != null && stackTrace.indexOf(timeoutStackName) !== -1
|
||||||
if (couldHaveABetterStackTrace) stackTrace = r.fallbackError != null ? o.cleanStackTrace(r.fallbackError) : r.error.stack || ""
|
if (couldHaveABetterStackTrace) stackTrace = r.testError != null ? o.cleanStackTrace(r.testError) : r.error.stack || ""
|
||||||
console.error(
|
console.error(
|
||||||
(hasProcess ? "\n" : "") +
|
(hasProcess ? "\n" : "") +
|
||||||
highlight(r.context + ":", "red2") + "\n" +
|
highlight(r.context + ":", "red2") + "\n" +
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,37 @@
|
||||||
var callAsync = require("../../test-utils/callAsync")
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
var o = require("../ospec")
|
var o = require("../ospec")
|
||||||
|
|
||||||
|
|
||||||
|
// this throws an async error that can't be caught in browsers
|
||||||
|
if (typeof process !== "undefined") {
|
||||||
|
o("incomplete assertion", function(done) {
|
||||||
|
var stackMatcher = /([\w\.\\\/\-]+):(\d+):/
|
||||||
|
// /!\ this test relies on the `new Error` expression being six lines
|
||||||
|
// above the `oo("test", function(){...})` call.
|
||||||
|
var matches = (new Error).stack.match(stackMatcher)
|
||||||
|
if (matches != null) {
|
||||||
|
var name = matches[1]
|
||||||
|
var num = Number(matches[2])
|
||||||
|
}
|
||||||
|
var oo = o.new()
|
||||||
|
oo("test", function() {
|
||||||
|
oo("incomplete")
|
||||||
|
})
|
||||||
|
oo.run(function(results) {
|
||||||
|
o(results.length).equals(1)
|
||||||
|
o(results[0].message).equals("Incomplete assertion in the test definition starting at...")
|
||||||
|
o(results[0].pass).equals(null)
|
||||||
|
var stack = o.cleanStackTrace(results[0].testError)
|
||||||
|
var matches2 = stack && stack.match(stackMatcher)
|
||||||
|
if (matches != null && matches2 != null) {
|
||||||
|
o(matches[1]).equals(name)
|
||||||
|
o(Number(matches2[2])).equals(num + 6)
|
||||||
|
}
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
o("o.only", function(done) {
|
o("o.only", function(done) {
|
||||||
var oo = o.new()
|
var oo = o.new()
|
||||||
|
|
||||||
|
|
@ -47,7 +78,6 @@ o.spec("reporting", function() {
|
||||||
o(results.length).equals(2)("Two results")
|
o(results.length).equals(2)("Two results")
|
||||||
|
|
||||||
o("error" in results[0] && "pass" in results[0]).equals(true)("error and pass keys present in failing result")
|
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[0].pass).equals(false)("Test meant to fail has failed")
|
||||||
o(results[1].pass).equals(true)("Test meant to pass has passed")
|
o(results[1].pass).equals(true)("Test meant to pass has passed")
|
||||||
|
|
||||||
|
|
@ -260,8 +290,8 @@ o.spec("ospec", function() {
|
||||||
o(results[0].pass).equals(false)
|
o(results[0].pass).equals(false)
|
||||||
// todo test cleaned up results[0].error stack trace for the presence
|
// todo test cleaned up results[0].error stack trace for the presence
|
||||||
// of the timeout stack entry
|
// of the timeout stack entry
|
||||||
o(results[0].fallbackError instanceof Error).equals(true)
|
o(results[0].testError instanceof Error).equals(true)
|
||||||
o(o.cleanStackTrace(results[0].fallbackError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
o(o.cleanStackTrace(results[0].testError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}))
|
}))
|
||||||
|
|
@ -284,8 +314,8 @@ o.spec("ospec", function() {
|
||||||
oo.run((function(results) {
|
oo.run((function(results) {
|
||||||
o(results.length).equals(1)
|
o(results.length).equals(1)
|
||||||
o(results[0].pass).equals(false)
|
o(results[0].pass).equals(false)
|
||||||
o(results[0].fallbackError instanceof Error).equals(true)
|
o(results[0].testError instanceof Error).equals(true)
|
||||||
o(o.cleanStackTrace(results[0].fallbackError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
o(o.cleanStackTrace(results[0].testError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue