[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 ospecFileName = getStackName(ensureStackTrace(new Error), /[\/\\](.*?):\d+:\d+/), timeoutStackName
|
||||
var globalTimeout = noTimeoutRightNow
|
||||
var currentTestError = null
|
||||
var hooks = {
|
||||
__before: true,
|
||||
__beforeEach: true,
|
||||
|
|
@ -17,8 +18,11 @@ else window.o = m()
|
|||
if (name != null) spec[name] = ctx = {}
|
||||
|
||||
function o(subject, predicate) {
|
||||
if (predicate === undefined) return new Assert(subject)
|
||||
else {
|
||||
if (predicate === undefined) {
|
||||
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)
|
||||
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)))
|
||||
|
|
@ -133,9 +137,10 @@ else window.o = m()
|
|||
if (cursor === tasks.length) return
|
||||
|
||||
var task = tasks[cursor++]
|
||||
var current = cursor
|
||||
var fn = task.fn
|
||||
currentTestError = task.err
|
||||
var timeout = 0, delay = defaultDelay, s = new Date
|
||||
var current = cursor
|
||||
var arg
|
||||
|
||||
globalTimeout = setDelay
|
||||
|
|
@ -150,9 +155,9 @@ else window.o = m()
|
|||
}
|
||||
// for internal use only
|
||||
function finalizeAsync(err) {
|
||||
if (err) {
|
||||
if (err instanceof Error) record(err.message, err, task.err)
|
||||
else record(String(err), null, task.err)
|
||||
if (err != null) {
|
||||
if (err instanceof Error) fail(new Assert, err.message, err)
|
||||
else fail(new Assert, String(err), null)
|
||||
}
|
||||
if (timeout !== undefined) timeout = clearTimeout(timeout)
|
||||
if (current === cursor) next()
|
||||
|
|
@ -256,34 +261,32 @@ else window.o = m()
|
|||
|
||||
function isRunning() {return results != null}
|
||||
function Assert(value) {
|
||||
if (!isRunning()) throw new Error("Assertions should not occur outside test definitions")
|
||||
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) {
|
||||
// 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.err = err
|
||||
}
|
||||
function define(name, verb, compare) {
|
||||
Assert.prototype[name] = function assert(value) {
|
||||
if (compare(this.value, value)) record(null)
|
||||
else record(serialize(this.value) + "\n " + verb + "\n" + serialize(value))
|
||||
if (compare(this.value, value)) succeed(this)
|
||||
else fail(this, serialize(this.value) + "\n " + verb + "\n" + serialize(value))
|
||||
var self = this
|
||||
return function(message) {
|
||||
var result = results[results.length - 1]
|
||||
result.message = message + "\n\n" + result.message
|
||||
if (!self.pass) self.message = message + "\n\n" + self.message
|
||||
}
|
||||
}
|
||||
}
|
||||
function record(message, error, fallbackError) {
|
||||
var result = {pass: message === null}
|
||||
if (result.pass === false) {
|
||||
result.context = subjects.join(" > ")
|
||||
result.message = message
|
||||
result.error = error != null ? error : ensureStackTrace(new Error)
|
||||
result.fallbackError = fallbackError
|
||||
}
|
||||
results.push(result)
|
||||
function succeed(assertion) {
|
||||
results[assertion.i].pass = true
|
||||
}
|
||||
function fail(assertion, message, error) {
|
||||
results[assertion.i].pass = false
|
||||
results[assertion.i].context = subjects.join(" > ")
|
||||
results[assertion.i].message = message
|
||||
results[assertion.i].error = error != null ? error : ensureStackTrace(new Error)
|
||||
}
|
||||
function serialize(value) {
|
||||
if (hasProcess) return require("util").inspect(value)
|
||||
|
|
@ -318,10 +321,15 @@ else window.o = m()
|
|||
o.report = function (results) {
|
||||
var errCount = 0
|
||||
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) {
|
||||
var stackTrace = o.cleanStackTrace(r.error)
|
||||
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(
|
||||
(hasProcess ? "\n" : "") +
|
||||
highlight(r.context + ":", "red2") + "\n" +
|
||||
|
|
|
|||
|
|
@ -3,6 +3,37 @@
|
|||
var callAsync = require("../../test-utils/callAsync")
|
||||
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) {
|
||||
var oo = o.new()
|
||||
|
||||
|
|
@ -47,7 +78,6 @@ o.spec("reporting", function() {
|
|||
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[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")
|
||||
|
||||
|
|
@ -260,8 +290,8 @@ o.spec("ospec", function() {
|
|||
o(results[0].pass).equals(false)
|
||||
// todo test cleaned up results[0].error stack trace for the presence
|
||||
// of the timeout stack entry
|
||||
o(results[0].fallbackError instanceof Error).equals(true)
|
||||
o(o.cleanStackTrace(results[0].fallbackError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
||||
o(results[0].testError instanceof Error).equals(true)
|
||||
o(o.cleanStackTrace(results[0].testError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
||||
|
||||
done()
|
||||
}))
|
||||
|
|
@ -284,8 +314,8 @@ o.spec("ospec", function() {
|
|||
oo.run((function(results) {
|
||||
o(results.length).equals(1)
|
||||
o(results[0].pass).equals(false)
|
||||
o(results[0].fallbackError instanceof Error).equals(true)
|
||||
o(o.cleanStackTrace(results[0].fallbackError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
||||
o(results[0].testError instanceof Error).equals(true)
|
||||
o(o.cleanStackTrace(results[0].testError).indexOf("test-ospec.js:" + (line + 3) + ":")).notEquals(-1)
|
||||
|
||||
done()
|
||||
}))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue