import supertest from 'supertest' import { TMethod, IRoutes, URLFormatter, } from '@rondo.dev/common' // https://stackoverflow.com/questions/48215950/exclude-property-from-type type Omit = Pick> interface ITest extends Omit, 'catch'> {} interface IResponse< R extends IRoutes, P extends keyof R, M extends TMethod, > extends supertest.Response { body: R[P][M]['response'] header: {[key: string]: string} } interface IRequest< R extends IRoutes, P extends keyof R, M extends TMethod, > extends ITest, Promise> { send(value: R[P][M]['body'] | string): this expect(status: number, body?: any): this expect(body: string | RegExp | object | ((res: Response) => any)): this expect(field: string, val: string | RegExp): this // any other method that's called will return "this" from supertest's // or superagent's type definition and afterwards the promise will no longer // contain type definitions. if you use any other methods, add them to this // type definition } interface IRequestOptions< R extends IRoutes, P extends keyof R, M extends TMethod, > { params?: R[P][M]['params'], query?: R[P][M]['query'], } export interface IHeaders { [key: string]: string } export class RequestTester { protected headers: IHeaders = {} protected formatter: URLFormatter = new URLFormatter() constructor( readonly app: Express.Application, readonly baseUrl = '', ) {} setHeaders(headers: IHeaders): this { this.headers = headers return this } request( method: M, path: P, options: IRequestOptions = {}, ) : IRequest { const url = this.formatter.format(path, options.params, options.query) const test = supertest(this.app)[method](`${this.baseUrl}${url}`) Object.keys(this.headers).forEach(key => { test.set(key, this.headers[key]) }) return test } get

( path: P, options?: IRequestOptions, ) { return this.request('get', path, options) } post

( path: P, options?: IRequestOptions, ) { return this.request('post', path, options) } put

( path: P, options?: IRequestOptions, ) { return this.request('put', path, options) } delete

( path: P, options?: IRequestOptions, ) { return this.request('delete', path, options) } }