@@ -27,9 +27,7 @@ Let’s say we want to write our mocks in the following object structure, so we
27
27
```
28
28
{
29
29
[pathname]: {
30
- [HTTP method]: {
31
- [HTTP status code]: { [some mock data] }
32
- }
30
+ [HTTP method]: { status: [status], body: { …[some mock data] } };
33
31
}
34
32
}
35
33
```
@@ -44,23 +42,22 @@ describe("My API test", () => {
44
42
it (" mocks correctly" , async () => {
45
43
mockResponses ({
46
44
" /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 " } },
51
49
},
52
50
" /users" : {
53
- put: {
54
- 201 : { status: " success" }, // ✅ Correct 201 response
55
- },
51
+ // ✅ Correct 201 response
52
+ put: { 201 : { status: " success" } },
56
53
},
57
54
});
58
55
59
- // test 1: GET /users/{user_id}: 200 returned by default
56
+ // test 1: GET /users/{user_id}: 200
60
57
await fetch (" /users/user-123" );
61
58
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 " });
64
61
65
62
// test 3: PUT /users: 200
66
63
await fetch (" /users" , {
@@ -95,15 +92,22 @@ type FilterKeys<Obj, Matchers> = { [K in keyof Obj]: K extends Matchers ? Obj[K]
95
92
type PathResponses <T > = T extends { responses: any } ? T [" responses" ] : unknown ;
96
93
type OperationContent <T > = T extends { content: any } ? T [" content" ] : unknown ;
97
94
type 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
+ };
98
104
99
105
/**
100
106
* Mock fetch() calls and type against OpenAPI schema
101
107
*/
102
108
export function mockResponses(responses : {
103
109
[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 ]>>;
107
111
};
108
112
}) {
109
113
fetchMock .mockResponse ((req ) => {
@@ -112,13 +116,11 @@ export function mockResponses(responses: {
112
116
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)
113
117
const method = req .method .toLowerCase ();
114
118
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 ) };
122
124
}
123
125
124
126
// 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
146
148
` ` ` ts
147
149
export function mockResponses(responses : {
148
150
[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 ]>>;
152
152
};
153
153
});
154
154
` ` `
0 commit comments