Skip to content

Commit

Permalink
replace vitest-fetch-mock with msw (#1592)
Browse files Browse the repository at this point in the history
* replace vitest-fetch-mock with msw in index.test

This replaces vitest-fetch-mock with msw in the index.test.ts file.

* Rename getCookies to getRequestCookies

* Rename testPath to toAbsoluteURL

* Rename TestRequestHandler to MockRequestHandler

* Move msw utils to separate fixture

* Update v7-beta.test.ts to utilize msw

* Update index.bench.js to utilize msw

Additionally ensures all clients are setup correctly for GET tests.

* remove vitest-fetch-mock dependency

* Update testing.md to include short example of msw

* Fix failing test

* Add MultipleResponse example to api.yaml and run generate-types

* Include 'use the selected content' in v7-beta.test.ts
  • Loading branch information
vipentti authored Mar 29, 2024
1 parent 2a66a64 commit e89573e
Show file tree
Hide file tree
Showing 10 changed files with 1,866 additions and 524 deletions.
48 changes: 48 additions & 0 deletions docs/openapi-fetch/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,51 @@ test("my API call", async () => {
expect(error).toBeUndefined();
});
```

## Mock Service Worker

[Mock Service Worker](https://mswjs.io/) can also be used for testing and mocking actual responses:

```ts
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
import createClient from "openapi-fetch";
import { afterEach, beforeAll, expect, test } from "vitest";
import type { paths } from "./api/v1";

const server = setupServer();

beforeAll(() => {
// NOTE: server.listen must be called before `createClient` is used to ensure
// the msw can inject its version of `fetch` to intercept the requests.
server.listen({
onUnhandledRequest: (request) => {
throw new Error(
`No request handler found for ${request.method} ${request.url}`,
);
},
});
});

afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test("my API call", async () => {
const rawData = { test: { data: "foo" } };

const BASE_URL = "https://my-site.com";

server.use(
http.get(`${BASE_URL}/api/v1/foo`, () => HttpResponse.json(rawData, { status: 200 }))
);

const client = createClient<paths>({
baseUrl: BASE_URL,
});

const { data, error } = await client.GET("/api/v1/foo");

expect(data).toEqual(rawData);
expect(error).toBeUndefined();
});
```
4 changes: 2 additions & 2 deletions packages/openapi-fetch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@
"axios": "^1.6.7",
"del-cli": "^5.1.0",
"esbuild": "^0.20.0",
"msw": "^2.2.3",
"openapi-typescript": "^6.7.4",
"openapi-typescript-codegen": "^0.25.0",
"openapi-typescript-fetch": "^1.1.3",
"superagent": "^8.1.2",
"typescript": "^5.3.3",
"vitest": "^1.3.1",
"vitest-fetch-mock": "^0.2.2"
"vitest": "^1.3.1"
}
}
2 changes: 2 additions & 0 deletions packages/openapi-fetch/test/fixtures/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Do not make direct changes to the file.
*/


export interface paths {
"/comment": {
put: {
Expand Down Expand Up @@ -577,6 +578,7 @@ export type $defs = Record<string, never>;
export type external = Record<string, never>;

export interface operations {

getHeaderParams: {
parameters: {
header: {
Expand Down
33 changes: 33 additions & 0 deletions packages/openapi-fetch/test/fixtures/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,11 @@ paths:
responses:
200:
$ref: '#/components/responses/Contact'
/multiple-response-content:
get:
responses:
200:
$ref: '#/components/responses/MultipleResponse'
components:
schemas:
Post:
Expand Down Expand Up @@ -672,3 +677,31 @@ components:
application/json:
schema:
$ref: '#/components/schemas/User'
MultipleResponse:
content:
application/json:
schema:
type: object
properties:
id:
type: string
email:
type: string
name:
type: string
required:
- id
- email
application/ld+json:
schema:
type: object
properties:
'@id':
type: string
email:
type: string
name:
type: string
required:
- '@id'
- email
124 changes: 124 additions & 0 deletions packages/openapi-fetch/test/fixtures/mock-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
http,
HttpResponse,
type JsonBodyType,
type StrictRequest,
type DefaultBodyType,
type HttpResponseResolver,
type PathParams,
type AsyncResponseResolverReturnType,
} from "msw";
import { setupServer } from "msw/node";

/**
* Mock server instance
*/
export const server = setupServer();

/**
* Default baseUrl for tests
*/
export const baseUrl = "https://api.example.com" as const;

/**
* Test path helper, returns a an absolute URL based on
* the given path and base
*/
export function toAbsoluteURL(path: string, base: string = baseUrl) {
// If we have absolute path
if (URL.canParse(path)) {
return new URL(path).toString();
}

// Otherwise we want to support relative paths
// where base may also contain some part of the path
// e.g.
// base = https://api.foo.bar/v1/
// path = /self
// should result in https://api.foo.bar/v1/self

// Construct base URL
const baseUrlInstance = new URL(base);

// prepend base url url pathname to path and ensure only one slash between the URL parts
const newPath = `${baseUrlInstance.pathname}/${path}`.replace(/\/+/g, "/");

return new URL(newPath, baseUrlInstance).toString();
}

export type MswHttpMethod = keyof typeof http;

export interface MockRequestHandlerOptions<
// Recreate the generic signature of the HTTP resolver
// so the arguments passed to http handlers propagate here.
Params extends PathParams<keyof Params> = PathParams,
RequestBodyType extends DefaultBodyType = DefaultBodyType,
ResponseBodyType extends DefaultBodyType = undefined,
> {
baseUrl?: string;
method: MswHttpMethod;
/**
* Relative or absolute path to match.
* When relative, baseUrl will be used as base.
*/
path: string;
body?: JsonBodyType;
headers?: Record<string, string>;
status?: number;

/**
* Optional handler which will be called instead of using the body, headers and status
*/
handler?: HttpResponseResolver<Params, RequestBodyType, ResponseBodyType>;
}

/**
* Configures a msw request handler using the provided options.
*/
export function useMockRequestHandler<
// Recreate the generic signature of the HTTP resolver
// so the arguments passed to http handlers propagate here.
Params extends PathParams<keyof Params> = PathParams,
RequestBodyType extends DefaultBodyType = DefaultBodyType,
ResponseBodyType extends DefaultBodyType = undefined,
>({
baseUrl: requestBaseUrl,
method,
path,
body,
headers,
status,
handler,
}: MockRequestHandlerOptions<Params, RequestBodyType, ResponseBodyType>) {
let requestUrl = "";
let receivedRequest: null | StrictRequest<DefaultBodyType> = null;
let receivedCookies: null | Record<string, string> = null;

const resolvedPath = toAbsoluteURL(path, requestBaseUrl);

server.use(
http[method]<Params, RequestBodyType, ResponseBodyType>(
resolvedPath,
(args) => {
requestUrl = args.request.url;
receivedRequest = args.request.clone();
receivedCookies = { ...args.cookies };

if (handler) {
return handler(args);
}

return HttpResponse.json(body, {
status: status ?? 200,
headers,
}) as AsyncResponseResolverReturnType<ResponseBodyType>;
},
),
);

return {
getRequestCookies: () => receivedCookies!,
getRequest: () => receivedRequest!,
getRequestUrl: () => new URL(requestUrl),
};
}
44 changes: 44 additions & 0 deletions packages/openapi-fetch/test/fixtures/v7-beta.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,33 @@ export interface paths {
patch?: never;
trace?: never;
};
"/multiple-response-content": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: components["responses"]["MultipleResponse"];
};
};
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
}
export type webhooks = Record<string, never>;
export interface components {
Expand Down Expand Up @@ -859,6 +886,23 @@ export interface components {
"application/json": components["schemas"]["User"];
};
};
MultipleResponse: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
id: string;
email: string;
name?: string;
};
"application/ld+json": {
"@id": string;
email: string;
name?: string;
};
};
};
};
parameters: never;
requestBodies: {
Expand Down
Loading

0 comments on commit e89573e

Please sign in to comment.