@@ -27,9 +27,7 @@ Let’s say we want to write our mocks in the following object structure, so we
2727```
2828{
2929 [pathname]: {
30- [HTTP method]: {
31- [HTTP status code]: { [some mock data] }
32- }
30+ [HTTP method]: { status: [status], body: { …[some mock data] } };
3331 }
3432}
3533```
@@ -44,23 +42,22 @@ describe("My API test", () => {
4442 it (" mocks correctly" , async () => {
4543 mockResponses ({
4644 " /users/{user_id}" : {
47- get: {
48- 200 : { id: " user-id" , name: " User Name" }, // ✅ Correct 200 response
49- 404 : { code: " 404 " , message: " User not found " }, // ✅ Correct 404 response
50- },
45+ // ✅ Correct 200 response
46+ get: { status: 200 , body : { id: " user-id" , name: " User Name" } },
47+ // ✅ Correct 403 response
48+ delete: { status: 403 , body: { code: " 403 " , message: " Unauthorized " } },
5149 },
5250 " /users" : {
53- put: {
54- 201 : { status: " success" }, // ✅ Correct 201 response
55- },
51+ // ✅ Correct 201 response
52+ put: { 201 : { status: " success" } },
5653 },
5754 });
5855
59- // test 1: GET /users/{user_id}: 200 returned by default
56+ // test 1: GET /users/{user_id}: 200
6057 await fetch (" /users/user-123" );
6158
62- // test 2: GET /users/{user_id}: 404 returned if `x-test-status` header sent
63- await fetch (" /users/user-123" , { headers: { " x-test-status " : 404 } });
59+ // test 2: DELETE /users/{user_id}: 403
60+ await fetch (" /users/user-123" , { method: " DELETE " });
6461
6562 // test 3: PUT /users: 200
6663 await fetch (" /users" , {
@@ -95,15 +92,22 @@ type FilterKeys<Obj, Matchers> = { [K in keyof Obj]: K extends Matchers ? Obj[K]
9592type PathResponses <T > = T extends { responses: any } ? T [" responses" ] : unknown ;
9693type OperationContent <T > = T extends { content: any } ? T [" content" ] : unknown ;
9794type MediaType = ` ${string }/${string } ` ;
95+ type MockedResponse <T , Status extends keyof T = keyof T > = FilterKeys <
96+ OperationContent <T [Status ]>,
97+ MediaType
98+ > extends never
99+ ? { status: Status ; body? : never }
100+ : {
101+ status: Status ;
102+ body: FilterKeys <OperationContent <T [Status ]>, MediaType >;
103+ };
98104
99105/**
100106 * Mock fetch() calls and type against OpenAPI schema
101107 */
102108export function mockResponses(responses : {
103109 [Path in keyof Partial <paths >]: {
104- [Method in keyof Partial <paths [Path ]>]: {
105- [Status in keyof Partial <PathResponses <paths [Path ][Method ]>>]: FilterKeys <OperationContent <PathResponses <paths [Path ][Method ]>[Status ]>, MediaType >;
106- };
110+ [Method in keyof Partial <paths [Path ]>]: MockedResponse <PathResponses <paths [Path ][Method ]>>;
107111 };
108112}) {
109113 fetchMock .mockResponse ((req ) => {
@@ -112,13 +116,11 @@ export function mockResponses(responses: {
112116 if (! mockedPath || (! responses as any )[mockedPath ]) throw new Error (` No mocked response for ${req .url } ` ); // throw error if response not mocked (remove or modify if you’d like different behavior)
113117 const method = req .method .toLowerCase ();
114118 if (! (responses as any )[mockedPath ][method ]) throw new Error (` ${req .method } called but not mocked on ${mockedPath } ` ); // likewise throw error if other parts of response aren’t mocked
115- const desiredStatus = req .headers .get (" x-status-code" );
116- const body = (responses as any )[mockedPath ][method ];
117- return {
118- status: desiredStatus ? parseInt (desiredStatus , 10 ) : 200 ,
119- body: JSON .stringify ((desiredStatus && body [desiredStatus ]) ?? body [200 ]),
120- };
121- });
119+ if (! (responses as any )[mockedPath ][method ]) {
120+ throw new Error (` ${req .method } called but not mocked on ${mockedPath } ` );
121+ }
122+ const { status, body } = (responses as any )[mockedPath ][method ];
123+ return { status , body: JSON .stringify (body ) };
122124}
123125
124126// helper function that matches a realistic URL (/users/123) to an OpenAPI path (/users/{user_id}
@@ -146,9 +148,7 @@ export function findPath(actual: string, testPaths: string[]): string | undefine
146148` ` ` ts
147149export function mockResponses(responses : {
148150 [Path in keyof Partial <paths >]: {
149- [Method in keyof Partial <paths [Path ]>]: {
150- [Status in keyof Partial <PathResponses <paths [Path ][Method ]>>]: FilterKeys <OperationContent <PathResponses <paths [Path ][Method ]>[Status ]>, MediaType >;
151- };
151+ [Method in keyof Partial <paths [Path ]>]: MockedResponse <PathResponses <paths [Path ][Method ]>>;
152152 };
153153});
154154` ` `
0 commit comments