This repository has been archived by the owner on May 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
225 additions
and
381 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// tslint:disable callable-types | ||
// tslint:disable no-unused | ||
|
||
import * as mocha from 'mocha' | ||
|
||
export interface Next<O> { | ||
(output: O): Promise<void> | ||
} | ||
|
||
export interface Plugin<O, A1 = undefined, A2 = undefined, A3 = undefined, A4 = undefined> { | ||
(next: Next<O>, input: any, arg1?: A1, arg2?: A2, arg3?: A3, arg4?: A4): Promise<any> | ||
} | ||
|
||
export interface Plugins {[k: string]: [object]} | ||
|
||
export interface Base<T extends Plugins> { | ||
(): Fancy<{}, T> | ||
register<K extends string, O, A1, A2, A3, A4>(k: K, v: Plugin<O, A1, A2, A3, A4>): Base<T & {[P in K]: [O, A1, A2, A3, A4]}> | ||
} | ||
|
||
export interface Callback<T, U> { | ||
(this: T, context: U): any | ||
} | ||
export interface MochaCallback<U> extends Callback<mocha.ITestCallbackContext, U> {} | ||
|
||
export type Fancy<I extends object, T extends Plugins> = { | ||
it: { | ||
(expectation: string, callback?: MochaCallback<I>): mocha.ITest | ||
only(expectation: string, callback?: MochaCallback<I>): mocha.ITest | ||
skip(expectation: string, callback?: MochaCallback<I>): void | ||
} | ||
run<O extends object>(opts: {addToContext: true}, cb: (context: I) => Promise<O> | O): Fancy<I & O, T> | ||
run(cb: (context: I) => any): Fancy<I, T> | ||
} & {[P in keyof T]: (arg1?: T[P][1], arg2?: T[P][2], arg3?: T[P][3], arg4?: T[P][4]) => Fancy<I & T[P][0], T>} | ||
|
||
const fancy = <I extends object, T extends Plugins>(plugins: any, chain: Chain = []): Fancy<I, T> => { | ||
const __it = (fn: typeof it) => (expectation: string, callback: MochaCallback<I>): any => { | ||
return fn(expectation, async function () { | ||
let ctx = {plugins} | ||
const run = (extra = {}): any => { | ||
ctx = assignWithProps({}, ctx, extra) | ||
const [next, args] = chain.shift() || [null, null] | ||
if (next) return next(run, ctx, ...args as any[]) | ||
if (callback) callback.call(this, ctx) | ||
} | ||
await run() | ||
}) | ||
} | ||
const _it = __it(it) as Fancy<I, T>['it'] | ||
_it.only = __it(it.only as any) | ||
_it.skip = __it(it.skip as any) | ||
return { | ||
...Object.entries(plugins) | ||
.reduce((fns, [k, v]) => { | ||
fns[k] = (...args: any[]) => { | ||
return fancy(plugins, [...chain, [v, args]]) | ||
} | ||
return fns | ||
}, {} as any), | ||
run(opts: any, cb: any) { | ||
if (!cb) { | ||
cb = opts | ||
opts = {} | ||
} | ||
return fancy(plugins, [...chain, [async (input: any, next: any) => { | ||
let output = await cb(input) | ||
if (opts.addToContext) next(output) | ||
else next() | ||
}, []]]) | ||
}, | ||
it: _it, | ||
} | ||
} | ||
|
||
function base<T extends Plugins>(plugins: any): Base<T> { | ||
const f = (() => fancy(plugins)) as any | ||
f.register = (k: string, v: any) => base({...plugins as any, [k]: v}) | ||
return f | ||
} | ||
|
||
export type Chain = [Plugin<any, any, any, any, any>, any[]][] | ||
|
||
function assignWithProps(target: any, ...sources: any[]) { | ||
sources.forEach(source => { | ||
if (!source) return | ||
let descriptors = Object.keys(source).reduce((descriptors: any, key) => { | ||
descriptors[key] = Object.getOwnPropertyDescriptor(source, key) | ||
return descriptors | ||
}, {}) | ||
// by default, Object.assign copies enumerable Symbols too | ||
Object.getOwnPropertySymbols(source).forEach(sym => { | ||
let descriptor = Object.getOwnPropertyDescriptor(source, sym) as any | ||
if (descriptor.enumerable) { | ||
descriptors[sym] = descriptor | ||
} | ||
}) | ||
Object.defineProperties(target, descriptors) | ||
}) | ||
return target | ||
} | ||
|
||
export default base<{}>({}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import * as _ from 'lodash' | ||
|
||
import {Plugin} from '.' | ||
import {expect} from './chai' | ||
|
||
export default (async (next, __, arg) => { | ||
try { | ||
await next({}) | ||
} catch (err) { | ||
if (_.isRegExp(arg)) { | ||
expect(err.message).to.match(arg) | ||
} else if (_.isString(arg)) { | ||
expect(err.message).to.equal(arg) | ||
} else if (arg) { | ||
arg(err) | ||
} else { | ||
throw new Error('no arg provided to catch') | ||
} | ||
} | ||
}) as Plugin<{}, RegExp | string | ((err: Error) => any)> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
const original = process.env | ||
import {Plugin} from '.' | ||
|
||
export default (env: {[k: string]: string}) => ({ | ||
before() { | ||
process.env = {...env} | ||
}, | ||
after() { | ||
export default (async (next, _, env) => { | ||
const original = process.env | ||
process.env = {...env} | ||
try { | ||
await next({}) | ||
} finally { | ||
process.env = original | ||
} | ||
}) | ||
}) as Plugin<{}, {[k: string]: string}> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,21 @@ | ||
export * from './chai' | ||
import base, {Base, Fancy, Next, Plugin} from './base' | ||
import _catch from './catch' | ||
import env from './env' | ||
import mock from './mock' | ||
import {stderr, stdout} from './stdmock' | ||
|
||
import test, {Test, TestBase} from './test' | ||
export const fancy = base | ||
.register('stdout', stdout) | ||
.register('stderr', stderr) | ||
.register('mock', mock) | ||
.register('env', env) | ||
.register('catch', _catch) | ||
|
||
export { | ||
Test, | ||
TestBase, | ||
test, | ||
Base, | ||
Fancy, | ||
Plugin, | ||
Next, | ||
} | ||
export default test | ||
export default fancy | ||
export * from './chai' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,16 @@ | ||
import * as _ from 'lodash' | ||
|
||
import {Next} from './base' | ||
|
||
/** | ||
* mocks an object's property | ||
*/ | ||
export default (object: any, path: string, value: any) => { | ||
export default async (next: Next<{}>, __: any, object: any, path: string, value: any) => { | ||
let original = _.get(object, path) | ||
return { | ||
before() { | ||
original = _.get(object, path) | ||
_.set(object, path, value) | ||
}, | ||
finally() { | ||
_.set(object, path, original) | ||
} | ||
try { | ||
_.set(object, path, value) | ||
await next({}) | ||
} finally { | ||
_.set(object, path, original) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,21 @@ | ||
import * as mock from 'stdout-stderr' | ||
|
||
export interface Stdout { | ||
before(): {stdout: string} | ||
after(): void | ||
catch(): void | ||
finally(): void | ||
} | ||
import {Plugin} from './base' | ||
|
||
export interface Stderr { | ||
before(): {stderr: string} | ||
after(): void | ||
catch(): void | ||
finally(): void | ||
export type Return<T extends 'stdout' | 'stderr'> = { | ||
readonly [P in T]: string | ||
} | ||
|
||
const create = <T extends 'stdout' | 'stderr'>(std: T) => () => { | ||
const _finally = () => mock[std].stop() | ||
return { | ||
before() { | ||
mock[std].start() | ||
if (std === 'stdout') { | ||
return { | ||
get stdout() { return mock.stdout.output } | ||
} | ||
} | ||
return { | ||
get stderr() { return mock.stderr.output } | ||
} | ||
}, | ||
after: _finally, | ||
catch: _finally, | ||
finally: _finally, | ||
const create = <T extends 'stdout' | 'stderr'>(std: T) => (async next => { | ||
mock[std].start() | ||
try { | ||
await next({ | ||
get [std]() { return mock[std].output } | ||
} as Return<T>) | ||
} finally { | ||
mock[std].stop() | ||
} | ||
} | ||
}) as Plugin<Return<T>> | ||
|
||
export const stdout = create('stdout') as () => Stdout | ||
export const stderr = create('stderr') as () => Stderr | ||
export const stdout = create('stdout') | ||
export const stderr = create('stderr') |
Oops, something went wrong.