diff --git a/docs/change-log.md b/docs/change-log.md index ad28942ae..891107fa1 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -32,6 +32,7 @@ #### Ospec improvements: - Added support for async functions and promises in tests - ([#1928](https://github.com/MithrilJS/mithril.js/pull/1928)) +- Added support for custom reporters ([#2009](https://github.com/MithrilJS/mithril.js/pull/2020)) - Error handling for async tests with `done` callbacks supports error as first argument - Error messages which include newline characters do not swallow the stack trace ([#1495](https://github.com/MithrilJS/mithril.js/issues/1495)) diff --git a/ospec/README.md b/ospec/README.md index dc3d923ed..a72e3f73c 100644 --- a/ospec/README.md +++ b/ospec/README.md @@ -19,7 +19,7 @@ Noiseless testing framework - `before`/`after`/`beforeEach`/`afterEach` hooks - test exclusivity (i.e. `.only`) - async tests and hooks -- explicitly disallows test-space configuration to encourage focus on testing, and to provide uniform test suites across projects +- explicitly regulates test-space configuration to encourage focus on testing, and to provide uniform test suites across projects ## Usage @@ -437,9 +437,17 @@ The arguments that were passed to the function in the last time it was called --- -### void o.run() +### void o.run([Function reporter]) -Runs the test suite +Runs the test suite. By default passing test results are printed using +`console.log` and failing test results are printed using `console.error`. + +If you have custom continuous integration needs then you can use a +reporter to process [test result data](#result-data) yourself. + +If running in Node.js, ospec will call `process.exit` after reporting +results by default. If you specify a reporter, ospec will not do this +and allow your reporter to respond to results in its own way. --- @@ -455,6 +463,74 @@ $o("a test", function() { $o.run() ``` +--- + +## Result data + +Test results are available by reference for integration purposes. You +can use custom reporters in `o.run()` to process these results. + +```javascript +o.run(function(results) { + // results is an array + + results.forEach(function(result) { + // ... + }) +}) +``` + +--- + +### Boolean result.pass + +True if the test passed. **No other keys will exist on the result if this value is true.** + +--- + +### Error result.error + +The value of the stack property from the `Error` object explaining the reason behind a failure. + +--- + +### String result.message + +If an exception was thrown inside the corresponding test, this will equal that Error's `message`. Otherwise, this will be a preformatted message in [SVO form](https://en.wikipedia.org/wiki/Subject%E2%80%93verb%E2%80%93object). More specifically, `${subject}\n${verb}\n${object}`. + +As an example, the following test's result message will be `"true\nshould equal\nfalse"`. + +```javascript +o.spec("message", function() { + o(false).equals(true) +}) +``` + +If you specify an assertion description, that description will appear two lines above the subject. + +```javascript +o.spec("message", function() { + o(false).equals(true)("Candyland") // result.message === "Candyland\n\ntrue\nshould equal\nfalse" +}) +``` + +--- + +### String result.context + +A `>`-separated string showing the structure of the test specification. +In the below example, `result.context` would be `testing > rocks`. + +```javascript +o.spec("testing", function() { + o.spec("rocks", function() { + o(false).equals(true) + }) +}) +``` + + + --- ## Goals @@ -462,8 +538,8 @@ $o.run() - Do the most common things that the mocha/chai/sinon triad does without having to install 3 different libraries and several dozen dependencies - Disallow configuration in test-space: - Disallow ability to pick between API styles (BDD/TDD/Qunit, assert/should/expect, etc) - - Disallow ability to pick between different reporters - Disallow ability to add custom assertion types + - Provide a default simple reporter - Make assertion code terse, readable and self-descriptive - Have as few assertion types as possible for a workable usage pattern diff --git a/ospec/ospec.js b/ospec/ospec.js index b10367432..a812068e7 100644 --- a/ospec/ospec.js +++ b/ospec/ospec.js @@ -2,7 +2,7 @@ "use strict" module.exports = new function init(name) { - 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", reporter, hasOwn = ({}).hasOwnProperty if (name != null) spec[name] = ctx = {} @@ -52,9 +52,10 @@ module.exports = new function init(name) { o.cleanStackTrace = function(stack) { return stack.match(/^(?:(?!Error|[\/\\]ospec[\/\\]ospec\.js).)*$/gm).pop() } - o.run = function() { + o.run = function(_reporter) { results = [] start = new Date + reporter = _reporter test(spec, [], [], report) function test(spec, pre, post, finalize) { @@ -236,6 +237,13 @@ module.exports = new function init(name) { function report() { var status = 0 + + if (typeof reporter === "function") { + // Reporter gains flow control + reporter(results) + return + } + for (var i = 0, r; r = results[i]; i++) { if (!r.pass) { var stackTrace = o.cleanStackTrace(r.error) diff --git a/ospec/tests/test-ospec.js b/ospec/tests/test-ospec.js index 4ef4614dd..3d50936cb 100644 --- a/ospec/tests/test-ospec.js +++ b/ospec/tests/test-ospec.js @@ -18,7 +18,42 @@ new function(o) { 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) + }) + }) + + // Predicate test passing on clone results + o.spec("reporting", function() { + o("reports per instance", function(done, timeout) { + timeout(100) // Waiting on clone + + clone.run(function(results) { + o(typeof results).equals("object") + o("length" in results).equals(true) + 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") + + done() + }) + }) + }) +}(o) + o.spec("ospec", function() { + o.spec("sync", function() { var a = 0, b = 0, illegalAssertionThrows = false