@typed/test
Version:
Simple test framework
77 lines (64 loc) • 2.31 kB
text/typescript
import { Assertions, Test, TestFn, TestResult } from '../types'
import { blue } from 'typed-colors'
import { createAssertionsEnvironment } from '../assertions'
export function it(does: string, test: TestFn): Test {
return new It(does, test)
}
class It implements Test {
public name: string
protected test: TestFn
constructor(name: string, test: TestFn) {
this.name = `${blue('it')} ${name}`
this.test = test
}
run(timeout: number = 2000): Promise<TestResult> {
const { test } = this
const { assertions, stats } = createAssertionsEnvironment()
return failAfterGivenTimeout(timeout, runTest(test, assertions))
.then((): TestResult => {
if (stats.count === 0) throw new Error(`No assertions have been made`)
return { passed: true, error: void 0 }
})
.catch((error): TestResult => ({ passed: false, error }))
}
}
function runTest(test: TestFn, assertions: Assertions): Promise<any> {
switch (test.length) {
// not using assertions passed in -- always fail
case 0:
return Promise.reject(new Error(`No assertions have been made`))
// synchronous test or returning promise -- promise will reject if test throws
case 1:
return new Promise(resolve => resolve(test(assertions, Function.prototype as any)))
// is using done() callback to signal test completion
default: {
return new Promise((resolve, reject) => {
try {
const result = test(assertions, (error?: Error) => {
// use promise to ensure test returns first
new Promise(res => setTimeout(res, 0, error)).then((err?: Error) => {
if (err) return reject(err)
resolve()
})
})
if (result && typeof result.then === 'function')
reject(new Error(`A promise-returning test should not use 'done' callback`))
} catch (err) {
reject(err)
}
})
}
}
}
function failAfterGivenTimeout(time: number, promise: Promise<any>): Promise<any> {
return new Promise((resolve, reject) => {
const id = setTimeout(() => {
reject(new Error(`Timed out after ${time} ms`))
clearTimeout(id)
}, time)
promise.then((value: any) => {
clearTimeout(id)
resolve(value)
}, reject)
})
}