Skip to content

Commit

Permalink
Add Fetch API types as accepted mock parameters (#291)
Browse files Browse the repository at this point in the history
* Add Fetch API types as accepted mock parameters

* Ran the formatter

---------

Co-authored-by: Eugene Fidelin <eugene.fidelin@gmail.com>
  • Loading branch information
mhaligowski and eugef authored Sep 9, 2024
1 parent 73487d1 commit 845ca93
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 13 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ it('should handle expressjs requests', () => {
```

The expected type parameter in the mock request and response expects any type that extends the NodeJS
`http.IncomingRequest` interface. This means you can also mock requests coming from other frameworks
too. An example for NextJS request will look like this:
`http.IncomingRequest` interface or Fetch API `Request` class. This means you can also mock requests
coming from other frameworks too. An example for NextJS request will look like this:

```ts
it('should handle nextjs requests', () => {
Expand All @@ -123,6 +123,23 @@ it('should handle nextjs requests', () => {
});
```

It is also possible to mock requests from the NextJS new AppRouter:

```ts
it('should handle nextjs app reouter requests', () => {
const mockExpressRequest = httpMocks.createRequest<NextRequest>({
method: 'GET',
url: '/user/42',
params: {
id: 42
}
});
const mockExpressResponse = httpMocks.createResponse<NextResponse>();

// ... the rest of the test as above.
});
```

## API

### .createRequest()
Expand Down
15 changes: 9 additions & 6 deletions lib/http-mock.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Request, Response, CookieOptions } from 'express';
import { IncomingMessage, OutgoingMessage } from 'http';

export type RequestType = IncomingMessage | globalThis.Request;
export type ResponseType = OutgoingMessage | globalThis.Response;

export type RequestMethod = 'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE';

export interface Params {
Expand Down Expand Up @@ -105,7 +108,7 @@ export interface RequestOptions {
[key: string]: any;
}

export type MockRequest<T extends IncomingMessage> = T & {
export type MockRequest<T extends RequestType> = T & {
_setParameter: (key: string, value?: string) => void;
_setSessionVariable: (variable: string, value?: string) => void;
_setCookiesVariable: (variable: string, value?: string) => void;
Expand Down Expand Up @@ -134,7 +137,7 @@ export type ResponseCookie = {
options: CookieOptions;
};

export type MockResponse<T extends OutgoingMessage> = T & {
export type MockResponse<T extends ResponseType> = T & {
_isEndCalled: () => boolean;
_getHeaders: () => Headers;
_getData: () => any;
Expand All @@ -153,16 +156,16 @@ export type MockResponse<T extends OutgoingMessage> = T & {
cookies: { [name: string]: ResponseCookie };
};

export function createRequest<T extends IncomingMessage = Request>(options?: RequestOptions): MockRequest<T>;
export function createRequest<T extends RequestType = Request>(options?: RequestOptions): MockRequest<T>;

export function createResponse<T extends OutgoingMessage = Response>(options?: ResponseOptions): MockResponse<T>;
export function createResponse<T extends ResponseType = Response>(options?: ResponseOptions): MockResponse<T>;

export interface Mocks<T1 extends IncomingMessage, T2 extends OutgoingMessage> {
export interface Mocks<T1 extends RequestType, T2 extends ResponseType> {
req: MockRequest<T1>;
res: MockResponse<T2>;
}

export function createMocks<T1 extends IncomingMessage = Request, T2 extends OutgoingMessage = Response>(
export function createMocks<T1 extends RequestType = Request, T2 extends ResponseType = Response>(
reqOptions?: RequestOptions,
resOptions?: ResponseOptions
): Mocks<T1, T2>;
2 changes: 2 additions & 0 deletions test/lib/http-mock.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ expectAssignable<NodeResponse>(createResponse<NodeResponse>());
expectNotAssignable<ExpressResponse>(createResponse<NodeResponse>());

expectType<Mocks<ExpressRequest, ExpressResponse>>(createMocks());
// eslint-disable-next-line no-undef
expectType<Mocks<globalThis.Request, globalThis.Response>>(createMocks<globalThis.Request, globalThis.Response>());
7 changes: 6 additions & 1 deletion test/lib/mockRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as url from 'url';
import * as querystring from 'querystring';
import parseRange from 'range-parser';
import { EventEmitter } from 'events';

import { IncomingMessage } from 'http';

import * as mockRequest from '../../lib/http-mock';

describe('mockRequest', () => {
Expand Down Expand Up @@ -281,6 +281,11 @@ describe('mockRequest', () => {
expect(request.ips).to.deep.equal([options.ip]);
});
});

it('should be able to create a Fetch API Request object', () => {
const request = mockRequest.createRequest<Request>();
expect(request.bodyUsed).to.be.undefined;
});
});

describe('.get()/.header()', () => {
Expand Down
7 changes: 3 additions & 4 deletions test/lib/mockResponse.spec.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
const chai = require('chai');

const { expect } = chai;
const sinon = require('sinon');
const sinonChai = require('sinon-chai');

chai.use(sinonChai);

const mockResponse = require('../../lib/mockResponse');
const mockRequest = require('../../lib/mockRequest');

const { expect } = chai;
chai.use(sinonChai);

describe('mockResponse', () => {
it('should expose .createResponse()', () => {
expect(mockResponse.createResponse).to.be.a('function');
Expand Down

0 comments on commit 845ca93

Please sign in to comment.