Skip to content

Commit

Permalink
feat(ospec): Allow custom reporters for CI reasons (#2019)
Browse files Browse the repository at this point in the history
  • Loading branch information
zyrolasting committed Nov 20, 2017
1 parent 684a394 commit 707d639
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 6 deletions.
1 change: 1 addition & 0 deletions docs/change-log.md
Expand Up @@ -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))

Expand Down
84 changes: 80 additions & 4 deletions ospec/README.md
Expand Up @@ -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

Expand Down Expand Up @@ -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.

---

Expand All @@ -455,15 +463,83 @@ $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

- 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

Expand Down
12 changes: 10 additions & 2 deletions ospec/ospec.js
Expand Up @@ -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 = {}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions ospec/tests/test-ospec.js
Expand Up @@ -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

Expand Down

0 comments on commit 707d639

Please sign in to comment.