Skip to content

Commit 2e7dbf2

Browse files
committed
add overrideResponse helper
1 parent 65cef62 commit 2e7dbf2

File tree

3 files changed

+118
-59
lines changed

3 files changed

+118
-59
lines changed

docs/mocks.md

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,12 @@ interface AutoAPIMockInstance<T> {
8787
// The default fixture data
8888
defaultValue: T;
8989

90-
// Override the response for the current test
90+
// Override the response for the current test (full control)
9191
override(fn: (data: T, info: ResponseResolverInfo) => Response): this;
9292

93+
// Override just the response data (simpler API)
94+
overrideResponse(fn: (data: T, info: ResponseResolverInfo) => T): this;
95+
9396
// Reset to default behavior (called automatically before each test)
9497
reset(): this;
9598

@@ -98,13 +101,44 @@ interface AutoAPIMockInstance<T> {
98101
}
99102
```
100103

101-
### Override Function
104+
### Choosing Between `override` and `overrideResponse`
105+
106+
Use **`overrideResponse`** (simpler) when you just want to change the response data:
107+
108+
```typescript
109+
// Simple - just return the data you want
110+
mockedGetRegistryV01Servers.overrideResponse(() => ({
111+
servers: [],
112+
metadata: { count: 0 },
113+
}));
114+
115+
// With default data modifications
116+
mockedGetRegistryV01Servers.overrideResponse((data) => ({
117+
...data,
118+
servers: data.servers?.slice(0, 3),
119+
}));
120+
```
121+
122+
Use **`override`** (full control) when you need:
123+
- Custom HTTP status codes (errors)
124+
- Non-JSON responses
125+
- Network errors
126+
- Invalid/malformed data for edge case testing
127+
128+
```typescript
129+
// Return error status
130+
mockedGetRegistryV01Servers.override(() =>
131+
HttpResponse.json({ error: "Server error" }, { status: 500 })
132+
);
102133

103-
The override function receives:
104-
1. `data: T` - The default fixture data (useful for partial modifications)
105-
2. `info: ResponseResolverInfo` - MSW request info (request, params, cookies)
134+
// Network error
135+
mockedGetRegistryV01Servers.override(() => HttpResponse.error());
106136

107-
Return a `Response` object (use `HttpResponse.json()` from MSW).
137+
// Testing invalid data shapes
138+
mockedGetRegistryV01Servers.override(() =>
139+
HttpResponse.json({ servers: [{ server: null }] })
140+
);
141+
```
108142

109143
### Automatic Reset
110144

@@ -115,23 +149,39 @@ Overrides are automatically reset before each test via `beforeEach()` in `src/mo
115149
Access the default fixture data to make partial modifications:
116150

117151
```typescript
152+
// With overrideResponse (cleaner)
153+
mockedGetRegistryV01Servers.overrideResponse((data) => ({
154+
...data,
155+
servers: data.servers?.slice(0, 1),
156+
}));
157+
158+
// With override (when you need the Response wrapper)
118159
mockedGetRegistryV01Servers.override((data) =>
119160
HttpResponse.json({
120161
...data,
121-
servers: data.servers?.slice(0, 1), // Keep only first server
162+
servers: data.servers?.slice(0, 1),
122163
})
123164
);
124165
```
125166

126167
### Accessing Request Info
127168

128-
Use the `info` parameter to vary responses based on request:
169+
Both methods receive request info as the second parameter:
129170

130171
```typescript
131-
mockedGetRegistryV01Servers.override((data, info) => {
172+
// With overrideResponse
173+
mockedGetRegistryV01Servers.overrideResponse((data, info) => {
132174
const cursor = new URL(info.request.url).searchParams.get("cursor");
133-
if (cursor === "page2") {
134-
return HttpResponse.json({ servers: [], metadata: { count: 0 } });
175+
return cursor === "page2"
176+
? { servers: [], metadata: { count: 0 } }
177+
: data;
178+
});
179+
180+
// With override (when you need different status codes based on request)
181+
mockedGetRegistryV01Servers.override((data, info) => {
182+
const authHeader = info.request.headers.get("Authorization");
183+
if (!authHeader) {
184+
return HttpResponse.json({ error: "Unauthorized" }, { status: 401 });
135185
}
136186
return HttpResponse.json(data);
137187
});

src/app/catalog/actions.test.ts

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,19 @@ describe("getServers", () => {
2828
expect(servers[0].title).toBe("AWS Nova Canvas");
2929
});
3030

31+
// Using overrideResponse - simpler API when you just want to change the data
3132
it("returns empty array when API returns no servers", async () => {
32-
mockedGetRegistryV01Servers.override(() =>
33-
HttpResponse.json({ servers: [], metadata: { count: 0 } }),
34-
);
33+
mockedGetRegistryV01Servers.overrideResponse(() => ({
34+
servers: [],
35+
metadata: { count: 0 },
36+
}));
3537

3638
const servers = await getServers();
3739

3840
expect(servers).toEqual([]);
3941
});
4042

43+
// Using override - needed when returning non-JSON responses like null
4144
it("returns empty array when API returns null data", async () => {
4245
mockedGetRegistryV01Servers.override(() => HttpResponse.json(null));
4346

@@ -48,6 +51,7 @@ describe("getServers", () => {
4851
});
4952

5053
describe("error handling", () => {
54+
// Using override - needed for error status codes
5155
it("throws on 500 server error", async () => {
5256
mockedGetRegistryV01Servers.override(() =>
5357
HttpResponse.json({ error: "Internal Server Error" }, { status: 500 }),
@@ -72,6 +76,7 @@ describe("getServers", () => {
7276
});
7377

7478
describe("data transformation", () => {
79+
// Using override - needed when testing invalid data shapes (null servers)
7580
it("filters out null servers from response", async () => {
7681
mockedGetRegistryV01Servers.override((data) =>
7782
HttpResponse.json({
@@ -94,6 +99,7 @@ describe("getServers", () => {
9499
]);
95100
});
96101

102+
// Using override - needed when testing invalid data shapes (undefined servers)
97103
it("filters out undefined servers from response", async () => {
98104
mockedGetRegistryV01Servers.override(() =>
99105
HttpResponse.json({
@@ -112,23 +118,21 @@ describe("getServers", () => {
112118
expect(servers[0].name).toBe("valid/server");
113119
});
114120

121+
// Using overrideResponse - valid response structure
115122
it("extracts server objects from nested response structure", async () => {
116-
mockedGetRegistryV01Servers.override(() =>
117-
HttpResponse.json({
118-
servers: [
119-
{
120-
server: {
121-
name: "test/server",
122-
title: "Test Server",
123-
description: "A test server",
124-
version: "2.0.0",
125-
},
126-
_meta: { "some/key": { status: "active" } },
123+
mockedGetRegistryV01Servers.overrideResponse(() => ({
124+
servers: [
125+
{
126+
server: {
127+
name: "test/server",
128+
title: "Test Server",
129+
description: "A test server",
130+
version: "2.0.0",
127131
},
128-
],
129-
metadata: { count: 1 },
130-
}),
131-
);
132+
},
133+
],
134+
metadata: { count: 1 },
135+
}));
132136

133137
const servers = await getServers();
134138

@@ -143,48 +147,43 @@ describe("getServers", () => {
143147
});
144148

145149
describe("using default data in overrides", () => {
150+
// Using overrideResponse - cleaner syntax for data modifications
146151
it("can modify specific server titles", async () => {
147-
mockedGetRegistryV01Servers.override((data) =>
148-
HttpResponse.json({
149-
...data,
150-
servers: data.servers?.map((item) => ({
151-
...item,
152-
server: {
153-
...item.server,
154-
title: `Modified: ${item.server?.title}`,
155-
},
156-
})),
157-
}),
158-
);
152+
mockedGetRegistryV01Servers.overrideResponse((data) => ({
153+
...data,
154+
servers: data.servers?.map((item) => ({
155+
...item,
156+
server: {
157+
...item.server,
158+
title: `Modified: ${item.server?.title}`,
159+
},
160+
})),
161+
}));
159162

160163
const servers = await getServers();
161164

162165
expect(servers[0].title).toBe("Modified: AWS Nova Canvas");
163166
});
164167

165168
it("can limit the number of returned servers", async () => {
166-
mockedGetRegistryV01Servers.override((data) =>
167-
HttpResponse.json({
168-
...data,
169-
servers: data.servers?.slice(0, 3),
170-
metadata: { count: 3 },
171-
}),
172-
);
169+
mockedGetRegistryV01Servers.overrideResponse((data) => ({
170+
...data,
171+
servers: data.servers?.slice(0, 3),
172+
metadata: { count: 3 },
173+
}));
173174

174175
const servers = await getServers();
175176

176177
expect(servers).toHaveLength(3);
177178
});
178179

179180
it("can filter servers by criteria", async () => {
180-
mockedGetRegistryV01Servers.override((data) =>
181-
HttpResponse.json({
182-
...data,
183-
servers: data.servers?.filter((item) =>
184-
item.server?.name?.includes("google"),
185-
),
186-
}),
187-
);
181+
mockedGetRegistryV01Servers.overrideResponse((data) => ({
182+
...data,
183+
servers: data.servers?.filter((item) =>
184+
item.server?.name?.includes("google"),
185+
),
186+
}));
188187

189188
const servers = await getServers();
190189

@@ -193,19 +192,21 @@ describe("getServers", () => {
193192
});
194193

195194
describe("request-aware overrides", () => {
196-
it("can access request info in override", async () => {
195+
// Using overrideResponse with request info
196+
it("can access request info in overrideResponse", async () => {
197197
let capturedUrl: string | undefined;
198198

199-
mockedGetRegistryV01Servers.override((data, info) => {
199+
mockedGetRegistryV01Servers.overrideResponse((data, info) => {
200200
capturedUrl = info.request.url;
201-
return HttpResponse.json(data);
201+
return data;
202202
});
203203

204204
await getServers();
205205

206206
expect(capturedUrl).toContain("/registry/v0.1/servers");
207207
});
208208

209+
// Using override - needed when response depends on request and may return different status codes
209210
it("can vary response based on request headers", async () => {
210211
mockedGetRegistryV01Servers.override((data, info) => {
211212
const authHeader = info.request.headers.get("Authorization");

src/mocks/autoAPIMock.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { HttpResponse } from "msw";
44
type ResponseResolverInfo = Parameters<HttpResponseResolver>[0];
55

66
type OverrideFn<T> = (data: T, info: ResponseResolverInfo) => Response;
7+
type OverrideResponseFn<T> = (data: T, info: ResponseResolverInfo) => T;
78

89
export interface AutoAPIMockInstance<T> {
910
generatedHandler: HttpResponseResolver;
1011
override: (fn: OverrideFn<T>) => AutoAPIMockInstance<T>;
12+
overrideResponse: (fn: OverrideResponseFn<T>) => AutoAPIMockInstance<T>;
1113
reset: () => AutoAPIMockInstance<T>;
1214
defaultValue: T;
1315
}
@@ -33,6 +35,12 @@ export function AutoAPIMock<T>(defaultValue: T): AutoAPIMockInstance<T> {
3335
return instance;
3436
},
3537

38+
overrideResponse(fn: OverrideResponseFn<T>) {
39+
return instance.override((data, info) =>
40+
HttpResponse.json(fn(data, info) as JsonBodyType),
41+
);
42+
},
43+
3644
reset() {
3745
overrideFn = null;
3846
return instance;

0 commit comments

Comments
 (0)