import { SpyAdapterFactory } from './spy-adapter-factory' ; import { SpyAdapter } from './spy-adapters/spy-adapter' ; export type RecursivePartial < T > = Partial < { [ key in keyof T ] : T [ key ] extends ( ... a : Array < infer U > ) => any ? ( ... a : Array < U > ) => RecursivePartial < ReturnType < T [ key ] > > | ReturnType < T [ key ] > : T [ key ] extends Array < any > ? Array < RecursivePartial < T [ key ] [ number ] > > : RecursivePartial < T [ key ] > | T [ key ] ; } > ; export interface ExtendedWith < T > { with ( stubs ) : T ; } export class Mockery { public static get noop ( ) : ( ) => any { return this . spyAdapter . getSpy ( 'any' ) ; } public static all < T > ( ) { const handler = { get : ( target , prop ) => { if ( ! target [ prop ] ) { target [ prop ] = this . spyAdapter . getSpy ( 'any' ) ; } return target [ prop ] ; } } ; return new Proxy ( { } , handler ) as T ; } public static configure ( spyAdapter ) { this . spyAdapter = typeof spyAdapter === 'string' ? SpyAdapterFactory . get ( spyAdapter ) : spyAdapter ; } public static extend < T > ( object ) { return this . withGenerator < T > ( object ) ; } public static from < T > ( object ) { return object as T ; } public static of < T extends Array < any > > ( stubs ? ) ; public static of < T extends ReadonlyArray < any > > ( stubs ? ) ; public static of < T extends object > ( stubs ? ) ; public static of < T extends object > ( stubs = { } as T ) { if ( Array . isArray ( stubs ) ) { return stubs as T ; } return this . extend < T > ( { } as T ) . with ( stubs ) ; } public static staticMethod < T , K extends keyof T > ( object , key , stub ) { this . spyAdapter . spyAndCallFake ( object , key , stub ) ; } private static spyAdapter : SpyAdapter = SpyAdapterFactory . get ( 'noop' ) ; private static spyOnTheStubbedFunctions < T > ( object , key ) { if ( typeof object [ key ] === typeof Function ) { this . spyAdapter . spyAndCallThrough ( object , key ) ; } else if ( typeof object [ key ] === typeof { } && object [ key ] !== null ) { Object . keys ( object [ key ] as any ) . forEach ( ( subKey ) => { this . spyOnTheStubbedFunctions < any > ( object [ key ] , subKey ) ; } ) ; } } private static withGenerator < T > ( object ) { return { with : ( stubs = { } as T ) : T => { let currentKey = '' ; try { Object . keys ( stubs ) . forEach ( ( key ) => { currentKey = key ; this . spyOnTheStubbedFunctions ( stubs , key as keyof RecursivePartial < T > ) ; } ) ; } catch ( e ) { if ( e instanceof RangeError ) { throw new Error ( `Return value of ${ currentKey } has a circular reference.\nConsider using Mock.from instead.` ) ; } throw e ; } return Object . assign ( object , stubs ) ; } } ; } }