initial commit (work in progress)
This commit is contained in:
parent
13fdb60f66
commit
559369016d
83 changed files with 10461 additions and 0 deletions
389
ospec/README.md
Normal file
389
ospec/README.md
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
# ospec
|
||||
|
||||
[About](#about) | [Usage](#usage) | [API](#api) | [Goals](#goals)
|
||||
|
||||
Noiseless testing framework
|
||||
|
||||
Version: 1.0
|
||||
License: MIT
|
||||
|
||||
## About
|
||||
|
||||
- ~180 LOC
|
||||
- terser and faster test code than with mocha, jasmine or tape
|
||||
- test code reads like bullet points
|
||||
- assertion code follows [SVO](https://en.wikipedia.org/wiki/Subject–verb–object) structure in present tense for terseness and readability
|
||||
- supports:
|
||||
- test grouping
|
||||
- assertions
|
||||
- spies
|
||||
- `equals`, `notEquals`, `deepEquals` and `notDeepEquals` assertion types
|
||||
- `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
|
||||
|
||||
## Usage
|
||||
|
||||
### Single tests
|
||||
|
||||
Both tests and assertions are declared via the `o` function. Tests should have a description and a body function. A test may have one or more assertions. Assertions should appear inside a test's body function and compare two values.
|
||||
|
||||
```javascript
|
||||
var o = require("ospec")
|
||||
|
||||
o("addition", function() {
|
||||
o(1 + 1).equals(2)
|
||||
})
|
||||
o("subtraction", function() {
|
||||
o(1 - 1).notEquals(2)
|
||||
})
|
||||
```
|
||||
|
||||
Assertions may have descriptions:
|
||||
|
||||
```javascript
|
||||
o("addition", function() {
|
||||
o(1 + 1).equals(2)("addition should work")
|
||||
|
||||
/* in ES6, the following syntax is also possible
|
||||
o(1 + 1).equals(2) `addition should work`
|
||||
*/
|
||||
})
|
||||
/* for a failing test, an assertion with a description outputs this:
|
||||
|
||||
addition should work
|
||||
|
||||
1 should equal 2
|
||||
|
||||
Error
|
||||
at stacktrace/goes/here.js:1:1
|
||||
*/
|
||||
```
|
||||
|
||||
### Grouping tests
|
||||
|
||||
Tests may be organized into logical groups using `o.spec`
|
||||
|
||||
```javascript
|
||||
o.spec("math", function() {
|
||||
o("addition", function() {
|
||||
o(1 + 1).equals(2)
|
||||
})
|
||||
o("subtraction", function() {
|
||||
o(1 - 1).notEquals(2)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Group names appear as a breadcrumb trail in test descriptions: `math > addition: 2 should equal 2`
|
||||
|
||||
### Nested test groups
|
||||
|
||||
Groups can be nested to further organize test groups. Note that tests cannot be nested inside other tests.
|
||||
|
||||
```javascript
|
||||
o.spec("math", function() {
|
||||
o.spec("arithmetics", function() {
|
||||
o("addition", function() {
|
||||
o(1 + 1).equals(2)
|
||||
})
|
||||
o("subtraction", function() {
|
||||
o(1 - 1).notEquals(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Callback test
|
||||
|
||||
The `o.spy()` method can be used to create a stub function that keeps track of its call count and received parameters
|
||||
|
||||
```javascript
|
||||
//code to be tested
|
||||
function call(cb, arg) {cb(arg)}
|
||||
|
||||
//test suite
|
||||
var o = require("ospec")
|
||||
|
||||
o.spec("call()", function() {
|
||||
o("works", function() {
|
||||
var spy = o.spy()
|
||||
call(spy, 1)
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
o(spy.args[0]).equals(1)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Asynchronous tests
|
||||
|
||||
If a test body function declares a named argument, the test is assumed to be asynchronous, and the argument is a function that must be called exactly one time to signal that the test has completed. As a matter of convention, this argument is typically named `done`.
|
||||
|
||||
```javascript
|
||||
o("setTimeout calls callback", function(done) {
|
||||
setTimeout(done, 10)
|
||||
})
|
||||
```
|
||||
|
||||
By default, asynchronous tests time out after 20ms. This can be changed on a per-test basis using the `timeout` argument:
|
||||
|
||||
```javascript
|
||||
o("setTimeout calls callback", function(done, timeout) {
|
||||
timeout(50) //wait 50ms before bailing out of the test
|
||||
|
||||
setTimeout(done, 30)
|
||||
})
|
||||
```
|
||||
|
||||
Note that the `timeout` function call must be the first statement in its test.
|
||||
|
||||
Asynchronous tests generate an assertion that succeeds upon calling `done` or fails on timeout with the error message `async test timed out`.
|
||||
|
||||
### `before`, `after`, `beforeEach`, `afterEach` hooks
|
||||
|
||||
These hooks can be declared when it's necessary to setup and clean up state for a test or group of tests. The `before` and `after` hooks run once each per test group, whereas the `beforeEach` and `afterEach` hooks run for every test.
|
||||
|
||||
```javascript
|
||||
o.spec("math", function() {
|
||||
var acc
|
||||
o.beforeEach(function() {
|
||||
acc = 0
|
||||
})
|
||||
|
||||
o("addition", function() {
|
||||
acc += 1
|
||||
|
||||
o(acc).equals(1)
|
||||
})
|
||||
o("subtraction", function() {
|
||||
acc -= 1
|
||||
|
||||
o(acc).equals(-1)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
It's strongly recommended to ensure that `beforeEach` hooks always overwrite all shared variables, and avoid `if/else` logic, memoization, undo routines inside `beforeEach` hooks.
|
||||
|
||||
### Asynchronous hooks
|
||||
|
||||
Like tests, hooks can also be asynchronous. Tests that are affected by asynchronous hooks will wait for the hooks to complete before running.
|
||||
|
||||
```javascript
|
||||
o.spec("math", function() {
|
||||
var acc
|
||||
o.beforeEach(function(done) {
|
||||
setTimeout(function() {
|
||||
acc = 0
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
//tests only run after async hooks complete
|
||||
o("addition", function() {
|
||||
acc += 1
|
||||
|
||||
o(acc).equals(1)
|
||||
})
|
||||
o("subtraction", function() {
|
||||
acc -= 1
|
||||
|
||||
o(acc).equals(-1)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Running only one test
|
||||
|
||||
A test can be temporarily made to run exclusively by calling `o.only()` instead of `o`. This is useful when troubleshooting regressions, to zero-in on a failing test, and to avoid saturating console log w/ irrelevant debug information.
|
||||
|
||||
```javascript
|
||||
o.spec("math", function() {
|
||||
o("addition", function() {
|
||||
o(1 + 1).equals(2)
|
||||
})
|
||||
|
||||
//only this test will be run, regardless of how many groups there are
|
||||
o.only("subtraction", function() {
|
||||
o(1 - 1).notEquals(2)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Running the test suite
|
||||
|
||||
```javascript
|
||||
//define a test
|
||||
o("addition", function() {
|
||||
o(1 + 1).equals(2)
|
||||
})
|
||||
|
||||
//run the suite
|
||||
o.run()
|
||||
```
|
||||
|
||||
### Running test suites concurrently
|
||||
|
||||
The `o.new()` method can be used to create new instances of ospec, which can be run in parallel. Note that each instance will report independently, and there's no aggregation of results.
|
||||
|
||||
```javascript
|
||||
var _o = o.new()
|
||||
_o("a test", function() {
|
||||
_o(1).equals(1)
|
||||
})
|
||||
_o.run()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
*Square brackets denote optional arguments
|
||||
|
||||
### void o.spec(String title, Function tests)
|
||||
|
||||
Defines a group of tests. Groups are optional
|
||||
|
||||
---
|
||||
|
||||
### void o(String title, Function([Function done [, Function timeout]]) assertions)
|
||||
|
||||
Defines a test.
|
||||
|
||||
If an argument is defined for the `assertions` function, the test is deemed to be asynchronous, and the argument is required to be called exactly one time.
|
||||
|
||||
---
|
||||
|
||||
### Assertion o(any value)
|
||||
|
||||
Starts an assertion. There are four types of assertion: `equals`, `notEquals`, `deepEquals` and `notDeepEquals`.
|
||||
|
||||
Assertions have this form:
|
||||
|
||||
```
|
||||
o(actualValue).equals(expectedValue)
|
||||
```
|
||||
|
||||
As a matter of convention, the actual value should be the first argument and the expected value should be the second argument in an assertion.
|
||||
|
||||
Assertions can also accept an optional description curried parameter:
|
||||
|
||||
```
|
||||
o(actualValue).equals(expectedValue)("this is a description for this assertion")
|
||||
```
|
||||
|
||||
Assertion descriptions can be simplified using ES6 tagged template string syntax:
|
||||
|
||||
```
|
||||
o(actualValue).equals(expectedValue) `this is a description for this assertion`
|
||||
```
|
||||
|
||||
#### Function(String description) o(any value).equals(any value)
|
||||
|
||||
Asserts that two values are strictly equal (`===`)
|
||||
|
||||
#### Function(String description) o(any value).notEquals(any value)
|
||||
|
||||
Asserts that two values are strictly not equal (`!==`)
|
||||
|
||||
#### Function(String description) o(any value).deepEquals(any value)
|
||||
|
||||
Asserts that two values are recursively equal
|
||||
|
||||
#### Function(String description) o(any value).notDeepEquals(any value)
|
||||
|
||||
Asserts that two values are not recursively equal
|
||||
|
||||
---
|
||||
|
||||
### void o.before(Function([Function done [, Function timeout]]) setup)
|
||||
|
||||
Defines code to be run at the beginning of a test group
|
||||
|
||||
If an argument is defined for the `setup` function, this hook is deemed to be asynchronous, and the argument is required to be called exactly one time.
|
||||
|
||||
---
|
||||
|
||||
### void o.after(Function([Function done [, Function timeout]]) teardown)
|
||||
|
||||
Defines code to be run at the end of a test group
|
||||
|
||||
If an argument is defined for the `teardown` function, this hook is deemed to be asynchronous, and the argument is required to be called exactly one time.
|
||||
|
||||
---
|
||||
|
||||
### void o.beforeEach(Function([Function done [, Function timeout]]) setup)
|
||||
|
||||
Defines code to be run before each test in a group
|
||||
|
||||
If an argument is defined for the `setup` function, this hook is deemed to be asynchronous, and the argument is required to be called exactly one time.
|
||||
|
||||
---
|
||||
|
||||
### void o.afterEach(Function([Function done [, Function timeout]]) teardown)
|
||||
|
||||
Defines code to be run after each test in a group
|
||||
|
||||
If an argument is defined for the `teardown` function, this hook is deemed to be asynchronous, and the argument is required to be called exactly one time.
|
||||
|
||||
---
|
||||
|
||||
### void o.only(String title, Function([Function done [, Function timeout]]) assertions)
|
||||
|
||||
Declares that only a single test should be run, instead of all of them
|
||||
|
||||
---
|
||||
|
||||
### Function o.spy()
|
||||
|
||||
Returns a function that records the number of times it gets called, and its arguments
|
||||
|
||||
#### Number o.spy().callCount
|
||||
|
||||
The number of times the function has been called
|
||||
|
||||
#### Array<any> o.spy().args
|
||||
|
||||
The arguments that were passed to the function in the last time it was called
|
||||
|
||||
---
|
||||
|
||||
### void o.run()
|
||||
|
||||
Runs the test suite
|
||||
|
||||
---
|
||||
|
||||
### Function o.new()
|
||||
|
||||
Returns a new instance of ospec. Useful if you want to run more than one test suite concurrently
|
||||
|
||||
```javascript
|
||||
var $o = o.new()
|
||||
$o("a test", function() {
|
||||
$o(1).equals(1)
|
||||
})
|
||||
$o.run()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
- Make assertion code terse, readable and self-descriptive
|
||||
- Have as few assertion types as possible for a workable usage pattern
|
||||
|
||||
Explicitly disallowing modularity and configuration in test-space has a few benefits:
|
||||
|
||||
- tests always look the same, even across different projects and teams
|
||||
- single source of documentation for entire testing API
|
||||
- no need to hunt down plugins to figure out what they do, especially if they replace common javascript idioms with fuzzy spoken language constructs (e.g. what does `.is()` do?)
|
||||
- no need to pollute project-space with ad-hoc configuration code
|
||||
- discourages side-tracking and yak-shaving
|
||||
46
ospec/bin/ospec
Normal file
46
ospec/bin/ospec
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var fs = require("fs")
|
||||
var path = require("path")
|
||||
|
||||
var o = require("../ospec")
|
||||
|
||||
function traverseDirectory(pathname, callback) {
|
||||
pathname = pathname.replace(/\\/g, "/")
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.lstat(pathname, function(err, stat) {
|
||||
if (err) reject(err)
|
||||
if (stat.isDirectory()) {
|
||||
fs.readdir(pathname, function(err, pathnames) {
|
||||
if (err) reject(err)
|
||||
var promises = []
|
||||
for (var i = 0; i < pathnames.length; i++) {
|
||||
pathnames[i] = path.join(pathname, pathnames[i])
|
||||
promises.push(traverseDirectory(pathnames[i], callback))
|
||||
}
|
||||
callback(pathname, stat, pathnames)
|
||||
resolve(Promise.all(promises))
|
||||
})
|
||||
}
|
||||
else {
|
||||
callback(pathname, stat)
|
||||
resolve(pathname)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
traverseDirectory(".", function(pathname, stat, children) {
|
||||
if (pathname.indexOf("node_modules") > -1) return
|
||||
if (pathname.match(/(?:^|\/)tests\/.*\.js$/)) {
|
||||
require("../../" + pathname)
|
||||
}
|
||||
})
|
||||
.then(o.run)
|
||||
.catch(function(e) {
|
||||
console.log(e.stack)
|
||||
})
|
||||
|
||||
process.on("unhandledRejection", function(e) {
|
||||
console.log("Uncaught (in promise) " + e.stack)
|
||||
})
|
||||
1
ospec/bin/ospec.cmd
Normal file
1
ospec/bin/ospec.cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
node cli
|
||||
187
ospec/ospec.js
Normal file
187
ospec/ospec.js
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
"use strict"
|
||||
|
||||
module.exports = new function init() {
|
||||
var spec = {}, subjects = [], results = [], only = null, ctx = spec, start, stack = 0, hasProcess = typeof process === "object"
|
||||
|
||||
function o(subject, predicate) {
|
||||
ctx[subject] = predicate
|
||||
if (predicate === undefined) return new Assert(subject)
|
||||
}
|
||||
o.before = hook("__before")
|
||||
o.after = hook("__after")
|
||||
o.beforeEach = hook("__beforeEach")
|
||||
o.afterEach = hook("__afterEach")
|
||||
o.new = init
|
||||
o.spec = function(subject, predicate) {
|
||||
var parent = ctx
|
||||
ctx = ctx[subject] = {}
|
||||
predicate()
|
||||
ctx = parent
|
||||
}
|
||||
o.only = function(subject, predicate) {o(subject, only = predicate)}
|
||||
o.spy = function() {
|
||||
var spy = function() {
|
||||
spy.this = this
|
||||
spy.args = [].slice.call(arguments)
|
||||
spy.callCount++
|
||||
}
|
||||
spy.args = []
|
||||
spy.callCount = 0
|
||||
return spy
|
||||
}
|
||||
o.run = function() {
|
||||
start = new Date
|
||||
test(spec, [], [], report)
|
||||
|
||||
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 function(done, timeout) {
|
||||
timeout(Infinity)
|
||||
|
||||
if (key.slice(0, 2) === "__") return done()
|
||||
if (only !== null && spec[key] !== only && typeof only === typeof spec[key]) return done()
|
||||
subjects.push(key)
|
||||
var type = typeof spec[key]
|
||||
if (type === "object") test(spec[key], pre, post, pop)
|
||||
if (type === "function") series([].concat(pre, spec[key], post, pop))
|
||||
|
||||
function pop() {
|
||||
subjects.pop()
|
||||
done()
|
||||
}
|
||||
}
|
||||
}), spec["__after"] || [], finalize))
|
||||
}
|
||||
|
||||
function series(fns) {
|
||||
var cursor = 0
|
||||
next()
|
||||
|
||||
function next() {
|
||||
stack++
|
||||
if (cursor === fns.length) return
|
||||
|
||||
var fn = fns[cursor++]
|
||||
if (fn.length > 0) {
|
||||
var timeout = 0, delay = 20, s = new Date
|
||||
var isDone = false
|
||||
var body = fn.toString()
|
||||
var arg = (body.match(/\(([\w_$]+)/) || body.match(/([\w_$]+)\s*=>/) || []).pop()
|
||||
if (body.indexOf(arg) === body.lastIndexOf(arg)) throw new Error("`" + arg + "()` should be called at least once")
|
||||
fn(function done() {
|
||||
if (timeout !== undefined) {
|
||||
timeout = clearTimeout(timeout)
|
||||
if (delay !== Infinity) record(null)
|
||||
if (!isDone) next()
|
||||
else throw new Error("`" + arg + "()` should only be called once")
|
||||
isDone = true
|
||||
}
|
||||
else console.log("# elapsed: " + Math.round(new Date - s) + "ms, expected under " + delay + "ms")
|
||||
}, function(t) {delay = t})
|
||||
if (timeout === 0) {
|
||||
timeout = setTimeout(function() {
|
||||
timeout = undefined
|
||||
record("async test timed out")
|
||||
next()
|
||||
}, Math.min(delay, 2147483647))
|
||||
}
|
||||
}
|
||||
else {
|
||||
fn()
|
||||
if (stack < 5000) next()
|
||||
else (hasProcess ? process.nextTick : setTimeout)(next, stack = 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function hook(name) {
|
||||
return function(predicate) {
|
||||
if (ctx[name]) throw new Error("This hook should be defined outside of a loop or inside a nested test group:\n" + predicate)
|
||||
ctx[name] = predicate
|
||||
}
|
||||
}
|
||||
|
||||
define("equals", "should equal", function(a, b) {return a === b})
|
||||
define("notEquals", "should not equal", function(a, b) {return a !== b})
|
||||
define("deepEquals", "should deep equal", deepEqual)
|
||||
define("notDeepEquals", "should not deep equal", function(a, b) {return !deepEqual(a, b)})
|
||||
|
||||
function isArguments(a) {
|
||||
if ("callee" in a) {
|
||||
for (var i in a) if (i === "callee") return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
function deepEqual(a, b) {
|
||||
if (a === b) return true
|
||||
if (a === null ^ b === null || a === undefined ^ b === undefined) return false
|
||||
if (typeof a === "object" && typeof b === "object") {
|
||||
var aIsArgs = isArguments(a), bIsArgs = isArguments(b)
|
||||
if (a.constructor === Object && b.constructor === Object && !aIsArgs && !bIsArgs) {
|
||||
for (var i in a) {
|
||||
if (!deepEqual(a[i], b[i])) return false
|
||||
}
|
||||
for (var i in b) {
|
||||
if (!(i in a)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (a.length === b.length && (a instanceof Array && b instanceof Array || aIsArgs && bIsArgs)) {
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (!deepEqual(a[i], b[i])) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime()
|
||||
if (typeof Buffer === "function" && a instanceof Buffer && b instanceof Buffer) {
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if (a.valueOf() === b.valueOf()) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function Assert(value) {this.value = value}
|
||||
function define(name, verb, compare) {
|
||||
Assert.prototype[name] = function assert(value) {
|
||||
if (compare(this.value, value)) record(null)
|
||||
else record(serialize(this.value) + " " + verb + " " + serialize(value))
|
||||
return function(message) {
|
||||
var result = results[results.length - 1]
|
||||
result.message = message + "\n\n" + result.message
|
||||
}
|
||||
}
|
||||
}
|
||||
function record(message) {
|
||||
var result = {pass: message === null}
|
||||
if (result.pass === false) {
|
||||
var error = new Error
|
||||
if (error.stack === undefined) new function() {try {throw error} catch (e) {error = e}}
|
||||
result.context = subjects.join(" > ")
|
||||
result.message = message
|
||||
result.error = error.stack
|
||||
}
|
||||
results.push(result)
|
||||
}
|
||||
function serialize(value) {
|
||||
if (value === null || typeof value === "object") return String(value)
|
||||
try {return JSON.stringify(value)} catch (e) {return String(value)}
|
||||
}
|
||||
function highlight(message) {
|
||||
return hasProcess ? "\x1b[31m" + message + "\x1b[0m" : "%c" + message + "%c "
|
||||
}
|
||||
|
||||
function report() {
|
||||
for (var i = 0, r; r = results[i]; i++) {
|
||||
if (!r.pass) console.info(r.context + ": " + highlight(r.message) + "\n\n" + r.error.match(/^(?:(?!^Error|[\/\\]ospec[\/\\]ospec\.js).)*$/m) + "\n\n", hasProcess ? "" : "color:red", hasProcess ? "" : "color:black")
|
||||
}
|
||||
console.log(results.length + " tests completed in " + Math.round(new Date - start) + "ms")
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
12
ospec/tests/index.html
Normal file
12
ospec/tests/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<script src="../../module/module.js"></script>
|
||||
<script src="../../test-utils/callAsync.js"></script>
|
||||
|
||||
<script src="../../ospec/ospec.js"></script>
|
||||
<script src="test-ospec.js"></script>
|
||||
<script>require("../../ospec/ospec").run()</script>
|
||||
</body>
|
||||
</html>
|
||||
94
ospec/tests/test-ospec.js
Normal file
94
ospec/tests/test-ospec.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
"use strict"
|
||||
|
||||
var callAsync = require("../../test-utils/callAsync")
|
||||
var o = require("../ospec")
|
||||
|
||||
new function(o) {
|
||||
o = o.new()
|
||||
|
||||
o.spec("ospec", function() {
|
||||
o("skipped", function() {
|
||||
o(1).equals(1)
|
||||
})
|
||||
o.only(".only()", function() {
|
||||
o(2).equals(2)
|
||||
})
|
||||
})
|
||||
|
||||
o.run()
|
||||
}(o)
|
||||
|
||||
o.spec("ospec", function() {
|
||||
o.spec("sync", function() {
|
||||
var a = 0, b = 0
|
||||
|
||||
o.before(function test() {a = 1})
|
||||
o.after(function test() {a = 0})
|
||||
|
||||
o.beforeEach(function test() {b = 1})
|
||||
o.afterEach(function test() {b = 0})
|
||||
|
||||
o("assertions", function test() {
|
||||
var spy = o.spy()
|
||||
spy(a)
|
||||
|
||||
o(a).equals(b)
|
||||
o(a).notEquals(2)
|
||||
o({a: [1, 2], b: 3}).deepEquals({a: [1, 2], b: 3})
|
||||
o([{a: 1, b: 2}, {c: 3}]).deepEquals([{a: 1, b: 2}, {c: 3}])
|
||||
|
||||
var values = ["a", "", 1, 0, true, false, null, undefined, Date(0), ["a"], [], function() {return arguments}.call(), new Uint8Array(), {a: 1}, {}]
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
for (var j = 0; j < values.length; j++) {
|
||||
if (i === j) o(values[i]).deepEquals(values[j])
|
||||
else o(values[i]).notDeepEquals(values[j])
|
||||
}
|
||||
}
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
o(spy.args.length).equals(1)
|
||||
o(spy.args[0]).equals(1)
|
||||
})
|
||||
})
|
||||
o.spec("async", function() {
|
||||
var a = 0, b = 0
|
||||
|
||||
o.before(function test(done) {
|
||||
callAsync(function() {
|
||||
a = 1
|
||||
done()
|
||||
})
|
||||
})
|
||||
o.after(function test(done) {
|
||||
callAsync(function() {
|
||||
a = 0
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
o.beforeEach(function test(done) {
|
||||
callAsync(function() {
|
||||
b = 1
|
||||
done()
|
||||
})
|
||||
})
|
||||
o.afterEach(function test(done) {
|
||||
callAsync(function() {
|
||||
b = 0
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
o("async hooks", function test(done) {
|
||||
callAsync(function() {
|
||||
var spy = o.spy()
|
||||
spy(a)
|
||||
|
||||
o(a).equals(b)
|
||||
o(a).equals(1)("a and b should be initialized")
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue