105 lines
2.6 KiB
TypeScript
105 lines
2.6 KiB
TypeScript
import supertest from 'supertest'
|
|
import {
|
|
TMethod,
|
|
IRoutes,
|
|
URLFormatter,
|
|
} from '@rondo.dev/common'
|
|
|
|
// https://stackoverflow.com/questions/48215950/exclude-property-from-type
|
|
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
|
|
|
|
interface ITest extends Omit<Omit<supertest.Test, 'then'>, '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<IResponse<R, P, M>> {
|
|
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<R extends IRoutes> {
|
|
|
|
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<M extends TMethod, P extends keyof R & string>(
|
|
method: M, path: P, options: IRequestOptions<R, P, 'post'> = {},
|
|
)
|
|
: IRequest<R, P, M> {
|
|
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<P extends keyof R & string>(
|
|
path: P,
|
|
options?: IRequestOptions<R, P, 'get'>,
|
|
) {
|
|
return this.request('get', path, options)
|
|
}
|
|
|
|
post<P extends keyof R & string>(
|
|
path: P,
|
|
options?: IRequestOptions<R, P, 'post'>,
|
|
) {
|
|
return this.request('post', path, options)
|
|
}
|
|
|
|
put<P extends keyof R & string>(
|
|
path: P,
|
|
options?: IRequestOptions<R, P, 'put'>,
|
|
) {
|
|
return this.request('put', path, options)
|
|
}
|
|
|
|
delete<P extends keyof R & string>(
|
|
path: P,
|
|
options?: IRequestOptions<R, P, 'delete'>,
|
|
) {
|
|
return this.request('delete', path, options)
|
|
}
|
|
}
|