@zoroaster/reducer v1.3.0
@zoroaster/reducer
@zoroaster/reducer
is a recursive reducer of tests and tests suites that can also focus on particular ones; and a test runner that supports contexts evaluation and timeouts and collecting the data from streams returned by tests. It is part of the Zoroaster Context Testing Framework.
yarn add @zoroaster/reducer
Table Of Contents
- Table Of Contents
- API
async reducer(tests: Array<Test|TestSuite>, config?: ReducerConfig): Tree
async runTest(options: RunTestOptions): RunTestResult
- Copyright
API
The package is available by importing its default reducer and the runTest
function:
import reducer, { runTest } from '@zoroaster/reducer'
async reducer(
tests: Array<Test|TestSuite>,
config?: ReducerConfig,
): Tree
Runs tests and test suites in the array with the runTest
and runTestSuite
methods and returns an object representing the tree structure in which tests were run. The runTest
method can be imported from this library, and the runTestSuite
can be implemented as a recursive reducer. Whether an object is a test is determined by the presence of the fn
property.
import reducer, { runTest } from '@zoroaster/reducer'
const runInSequence = async (testSuite, level = 0) => {
const indent = ' '.repeat(level)
return await reducer(testSuite, {
async runTest(test) {
const result = await runTest(test)
if (result.error) {
console.log('%s[x] %s: %s', indent, test.name, result.error.message)
} else {
console.log('%s[+] %s: %s', indent, test.name, result.result)
}
if (result.error) result.error = result.error.message // for display
return result
},
async runTestSuite(ts) {
console.log('%s %s', indent, ts.name)
return await runInSequence(ts.tests, level + 1)
},
})
}
(async () => {
const tree = await runInSequence([
{
name: 'test',
fn() { return 'ok' },
},
{
name: 'test with context',
context: class Context {
async _init() {
await new Promise(r => setTimeout(r, 10))
this.hello = 'world'
}
},
fn({ hello }) { return `ok - ${hello}` },
},
{
name: 'test-suite',
tests: [
{
name: 'test1',
fn() { throw new Error('fail') },
},
],
},
])
console.error(tree)
})()
[+] test: ok
[+] test with context: ok - world
test-suite
[x] test1: fail
{ test:
{ name: 'test',
fn: [Function: fn],
started: 2019-05-02T14:37:00.789Z,
finished: 2019-05-02T14:37:00.789Z,
error: null,
result: 'ok',
destroyResult: [] },
'test with context':
{ name: 'test with context',
context: [Function: Context],
fn: [Function: fn],
started: 2019-05-02T14:37:00.796Z,
finished: 2019-05-02T14:37:00.810Z,
error: null,
result: 'ok - world',
destroyResult: [ undefined ] },
'test-suite':
{ test1:
{ name: 'test1',
fn: [Function: fn],
started: 2019-05-02T14:37:00.811Z,
finished: 2019-05-02T14:37:00.811Z,
error: 'fail',
result: null,
destroyResult: [] } } }
The main interfaces that @Zoroaster/Reducer uses are the Test and TestSuite, which contain the minimal properties required to decide whether to run a test using its fn
function, or to reduce a test suite further using its tests
property. These types are shared with zoroaster
binary and come from the @zoroaster/types
package, and are provided via externs to be able to compile the project with Google Closure Compiler via Depack.
import('@zoroaster/types').Test
_contextTesting.Test
: The test interface.
import('@zoroaster/types').TestSuite
_contextTesting.TestSuite
: The test sutie interface.
_contextTesting.ReducerConfig
: The options for the reducer.
Name | Type | Description | Default |
---|---|---|---|
onlyFocused | boolean | Run only focused tests. | false |
runTest* | function(!_contextTesting.Test): !Promise | The function that wraps around @zoroaster/reducer.runTest method. | - |
runTestSuite* | function(!_contextTesting.TestSuite, boolean): !Promise<!_contextTesting.TestSuite> | The function used to run a test suite. The second argument receives whether only focused tests should be run within this test suite. | - |
The reducer iterates through the array of provided tests (which can either be test cases or test suites) and passes them one-by-one to the given runTest
and runTestSuite
methods which in turn must call @zoroaster/reducer.runTest
method. All additional operations such as logging of results must be done by those methods, therefore this library provides the means of iterating through items and running them serially, while also evaluating the contexts. Tests can have a raw context
property which is either a context constructor or a context object. If it is a constructor, it should return an object or an instance that stores a state. The package can evaluate the context of the class, function and object types. Tests then receive evaluated context to access the testing API and the state.
A recursive tree is returned by the reducer containing nested test suites with tests updated with the outcome of the runTest
method (therefore, the reducer is not pure since the passed tests are mutated).
async runTest(
options: RunTestOptions,
): RunTestResult
Asynchronously runs the test within a time-out limit. Evaluates the contexts beforehand and destroys them after (using the same time-out). Returns the started
, finished
, error
, result
and destroyResult
properties.
The persistentContext
property can contain either an array or a single evaluated context instance. They are passed to the tests in the argument list before any of the non-persistent test contexts.
In the example below, the reducer
is given and array of tests and the runTest
function. The test has the fn
property and 2 contexts: one as an object and another one as a class. They are evaluated and passed to the test. The _destroy
method of the class context is used to calculate the time taken to run the test. Finally, the result of the runTest
is assigned to the tests in the array.
import reducer, { runTest } from '@zoroaster/run-test'
import { Readable } from 'stream'
(async () => {
const persistentContext = await Promise.resolve('EXAMPLE')
const tree = await reducer([
{
name: 'test',
context: [
{ TEST: 'hello' },
class Context {
async _init() {
this.data = 'world'
this._started = new Date()
}
async _destroy() {
const dt = new Date().getTime() - this._started.getTime()
return `${dt}ms`
}
},
],
persistentContext,
async fn(pc, { TEST }, { data }) {
await new Promise(r => setTimeout(r, 100))
return `[${pc}] ${TEST}-${data}: ok`
},
},
{
name: 'test with stream',
fn() {
return new Readable({
read() {
this.push('data')
this.push(null)
},
})
},
},
], {
runTest,
})
console.log(tree)
})()
{ test:
{ name: 'test',
context: [ [Object], [Function: Context] ],
persistentContext: 'EXAMPLE',
fn: [AsyncFunction: fn],
started: 2019-05-02T14:37:01.235Z,
finished: 2019-05-02T14:37:01.366Z,
error: null,
result: '[EXAMPLE] hello-world: ok',
destroyResult: [ undefined, '129ms' ] },
'test with stream':
{ name: 'test with stream',
fn: [Function: fn],
started: 2019-05-02T14:37:01.382Z,
finished: 2019-05-02T14:37:01.493Z,
error: null,
result: 'data',
destroyResult: [] } }
import('stream').Writable
stream.Writable
: An interface to the Catchment class. Has an additional promise
property resolved on stream finish.
_contextTesting.RunTestOptions
: Options for the runTest
method.
Name | Type | Description | Default |
---|---|---|---|
context | !Array<*> | The contexts to evaluate. | - |
fn* | !Function | The function to execute. | - |
persistentContext | !Array<*> | Evaluated persistent contexts that will come before other contexts. | - |
timeout | ?number | The timeout to run the test and evaluate/destroy contexts within. | null |
onCatchment | function(!stream.Writable) | The callback that will be called with the Catchment stream if the test returned a stream. The stream's data will be collected into the catchment to create the result as a string. The callback can be used to emit errors on the Catchment stream. | - |
_contextTesting.RunTestResult
: The result of the runTest function.
Name | Type | Description | Default |
---|---|---|---|
started* | Date | The date when the test started. | - |
finished* | Date | The date when the test finished. | - |
error | Error | The error which happened during the test. | null |
result | * | The result which the test returned. | null |
destroyResult | * | The result which the destroy method on the context returned. | null |
Copyright
(c) Context Testing 2019