import {Http} from './http'; import {HttpResponse} from './httpResponse'; import {HttpError} from './httpError'; class HttpMock implements Http { public static ForAll = '__all'; constructor() { this.reset(); } _replyFunctions: object = HttpMock.initialReplyFunctions(); private static initialReplyFunctions(): object { return { get: {}, post: {}, put: {}, delete: {} }; } private createReplyFn(status: number, data: any): () => Promise { return () => Promise.resolve(new HttpResponse(status, data)); } private createErrorFn(requestUrl: string, status: number, message: string, data: any = null): () => {} { return () => { throw new HttpError(requestUrl, status, message, data); }; } public reset(): void { this._replyFunctions = HttpMock.initialReplyFunctions(); } public registerResponse(method: string, url: string, status: number, data: any) : void { // @ts-ignore this._replyFunctions[method][url] = this.createReplyFn(status, data); } public registerError(method: string, url: string, status: number, message: string, data: any) : void { // @ts-ignore this._replyFunctions[method][url] = this.createErrorFn(url, status, message, data); } public get(url: string): Promise { return this.request('get', url); } public delete(url: string): Promise { return this.request('delete', url); } public post(url: string, payload: any): Promise { return this.request('post', url); // ignore payload... } public put(url: string, payload: any): Promise { return this.request('put', url); // ignore payload... } private request(method: string, url: string): Promise { // @ts-ignore const replyFn = this._replyFunctions[method][url] || this._replyFunctions[method][HttpMock.ForAll]; if (!replyFn) { throw new Error(`Could not find any mocked function for method ${method.toUpperCase()} url ${url}`); } return replyFn(); } } /** * Http Mocker Builder for easy to http testing * * Example: * ``` const mockedHttp = HttpMockBuilder .create() .onGetReply(200, {foo: 'get'}) .onPostThrowError(500, 'Post Error', {e: 'post'}) .onPutThrowError(404, 'Put Error', {e: 'put'}) .onDeleteThrowError(403, 'Delete Error', {e: 'delete'}) .build(); const response = await mockedHttp.get('/url'); await mockedHttp.post('/url/post', {faz: 'post'}); // will throw exception * ``` * * @module http */ export class HttpMockBuilder { private readonly _httpMock: HttpMock; private constructor() { this._httpMock = new HttpMock(); } /** * Creates a builder instance * @return {HttpMockBuilder} the builder */ public static create(): HttpMockBuilder { return new HttpMockBuilder(); } private onReply(method: string, status: number, data: any, url: string = HttpMock.ForAll): HttpMockBuilder { this._httpMock.registerResponse(method, url, status, data); return this; } private onThrowError(method: string, status: number, errorMessage: string, data: any, url: string = HttpMock.ForAll): HttpMockBuilder { this._httpMock.registerError(method, url, status, errorMessage, data); return this; } /** * Mocks responses for get methods * You may pass a specific endpoint as parameter to mock only selected endpoints. * This is very useful, when having methods that do several Http requests, * so you can mock them one on one. * * The following code returns the same content on _every_ get call * ``` * HttpMockBuilder * .create() * .onGetReply(200, {response: 'foo}) // mocks all get requests * .onPostReply(201, {response: 'foo}) // mocks all post requests * .build() * ``` * * The next code returns the different content depending on the passed endpoint * ``` * HttpMockBuilder * .create() * .onGetReply(200, {response: 'foo}, '/url/specific') // mocks get request for '/url/specific' * .build() * ``` * @param status {number} The status to be returned * @param data The data to be returned * @param url {string?} If given, the mock applies for that specific url, other for all method calls * @return {HttpMockBuilder} The builder instance (Fluent API) */ public onGetReply(status: number, data: any, url?: string): HttpMockBuilder { return this.onReply('get', status, data, url); } /** * Mocks response exceptions for get methods. It works like onGetReply(), but throws an HttpError instead * @param status {number} The status to be returned in exception object * @param errorMessage {string} The error message * @param data {any?} Eventual data carried with the error object * @param url {string?} The specific url for which the exception should be thrown * @return {HttpMockBuilder} The builder instance (Fluent API) */ public onGetThrowError(status: number, errorMessage: string, data: any = null, url: string = HttpMock.ForAll): HttpMockBuilder { return this.onThrowError('get', status, errorMessage, data, url); } /** * Mocks post requests. Works analog to onGetReply(). */ public onPostReply(status: number, data: any, url?: string): HttpMockBuilder { return this.onReply('post', status, data, url); } /** * Mocks response exceptions for post methods. It works like onPostReply(), but throws an HttpError instead */ public onPostThrowError(status: number, errorMessage: string, data: any, url: string = HttpMock.ForAll): HttpMockBuilder { return this.onThrowError('post', status, errorMessage, data, url); } /** * Mocks put requests. Works analog to onGetReply(). */ public onPutReply(status: number, data: any, url?: string): HttpMockBuilder { return this.onReply('put', status, data, url); } /** * Mocks response exceptions for put methods. It works like onPutReply(), but throws an HttpError instead */ public onPutThrowError(status: number, errorMessage: string, data: any, url: string = HttpMock.ForAll): HttpMockBuilder { return this.onThrowError('put', status, errorMessage, data, url); } /** * Mocks delete requests. Works analog to onGetReply(). */ public onDeleteReply(status: number, data: any, url?: string): HttpMockBuilder { return this.onReply('delete', status, data, url); } /** * Mocks response exceptions for delete methods. It works like onDeleteReply(), but throws an HttpError instead */ public onDeleteThrowError(status: number, errorMessage: string, data: any, url: string = HttpMock.ForAll): HttpMockBuilder { return this.onThrowError('delete', status, errorMessage, data, url); } /** * Builds the Http mock. * @return {Http} The mocked Http implementation */ public build(): Http { return this._httpMock; } }