import dbg from 'debug' ; import { BrowserController , BrowserRequestHandler , BrowserRequestType , } from './controllers/BrowserController' ; import { BrowserControllerFactory , BrowserPage , } from './controllers/BrowserControllerFactory' ; import { Mock } from './mock' ; import { MockedResponse , MockOptions , RequestMatcher , ShorthandRequestMatcher , } from './types' ; import { addMockByPriority , createRequestMatcher , getCorsHeaders , printRequest , printResponse , sanitizeHeaders , } from './utils' ; const debug = dbg ( 'mockiavelli:main' ) ; const interceptedTypes = [ 'xhr' , 'fetch' ] ; export interface MockiavelliOptions { debug : boolean ; baseUrl : string ; } export class Mockiavelli { private readonly baseUrl : string = '' ; private mocks : Mock [ ] = [ ] ; private controller : BrowserController ; constructor ( page , options = { } ) { this . controller = BrowserControllerFactory . createForPage ( page , this . onRequest ) ; if ( options . baseUrl ) { this . baseUrl = options . baseUrl ; } if ( options . debug ) { dbg . enable ( 'mockiavelli:*' ) ; } debug ( 'Initialized' ) ; } public static async setup ( page , options = { } ) { const instance = new Mockiavelli ( page , options ) ; await instance . enable ( ) ; return instance ; } public async enable ( ) { await this . controller . startInterception ( ) ; } public async disable ( ) { await this . controller . stopInterception ( ) ; } public mock < TResponseBody = any > ( matcher , response , options ? ) { const matcherWithBaseUrl = { ... matcher , url : this . baseUrl + matcher . url , } ; const mock = new Mock ( matcherWithBaseUrl , response , { ... options } ) ; addMockByPriority ( this . mocks , mock ) ; return mock ; } public mockGET < TResponseBody = any > ( matcher , response , options ? ) { return this . mock ( createRequestMatcher ( matcher , 'GET' ) , response , options ) ; } public mockPOST < TResponseBody = any > ( matcher , response , options ? ) { return this . mock ( createRequestMatcher ( matcher , 'POST' ) , response , options ) ; } public mockPUT < TResponseBody = any > ( matcher , response , options ? ) { return this . mock ( createRequestMatcher ( matcher , 'PUT' ) , response , options ) ; } public mockDELETE < TResponseBody = any > ( matcher , response , options ? ) { return this . mock ( createRequestMatcher ( matcher , 'DELETE' ) , response , options ) ; } public mockPATCH < TResponseBody = any > ( matcher , response , options ? ) { return this . mock ( createRequestMatcher ( matcher , 'PATCH' ) , response , options ) ; } public removeMock ( mock ) { const index = this . mocks . indexOf ( mock ) ; if ( index > - 1 ) { return this . mocks . splice ( index , 1 ) [ 0 ] ; } } private onRequest : BrowserRequestHandler = async ( request , respond , skip ) : Promise < void > => { debug ( `> req: ${ printRequest ( request ) } ` ) ; if ( request . method === 'OPTIONS' ) { return await respond ( { status : 204 , headers : sanitizeHeaders ( getCorsHeaders ( request ) ) , } ) ; } for ( const mock of this . mocks ) { const response = mock . getResponseForRequest ( request ) ; if ( response ) { const status = response . status || 200 ; let body ; let contentType ; if ( typeof response . body === 'string' ) { body = Buffer . from ( response . body ) ; } else if ( response . body === undefined || response . body === null ) { body = Buffer . alloc ( 0 ) ; } else { try { body = Buffer . from ( JSON . stringify ( response . body ) ) ; contentType = 'application/json; charset=utf-8' ; } catch ( e ) { console . error ( 'Could not serialize response body' , e ) ; throw e ; } } const headers = sanitizeHeaders ( { 'content-length' : String ( body . length ) , 'content-type' : contentType , ... getCorsHeaders ( request ) , ... response . headers , } ) ; try { await respond ( { status , headers , body , } ) ; debug ( `< res: ${ printResponse ( status , headers , body ) }` ) ; return ; } catch ( e ) { console . error ( `Failed to reply with mocked response for ${ printRequest ( request ) }` ) ; console . error ( e ) ; throw e ; } } } const should404 = interceptedTypes . includes ( request . type ) ; if ( should404 ) { debug ( `< res: status=404` ) ; console . error ( `Mock not found for request: ${ printRequest ( request ) }` ) ; return respond ( { status : 404 , body : 'No mock provided for request' , } ) ; } debug ( `< res: continue` ) ; try { return await skip ( ) ; } catch ( e ) { console . error ( e ) ; return ; } } ; }