Skip to content

Commit 4c1d01b

Browse files
committed
allow creating reusable scenarios?
1 parent bc387f0 commit 4c1d01b

File tree

4 files changed

+91
-9
lines changed

4 files changed

+91
-9
lines changed

docs/mocks.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ interface AutoAPIMockInstance<T> {
9494
// Override the full handler (for errors, network failures, invalid data)
9595
overrideHandler(fn: (data: T, info: ResponseResolverInfo) => Response): this;
9696

97+
// Define a reusable named scenario
98+
scenario(name: string, fn: (instance: AutoAPIMockInstance<T>) => AutoAPIMockInstance<T>): this;
99+
100+
// Activate a named scenario for the current test
101+
useScenario(name: string): this;
102+
97103
// Reset to default behavior (called automatically before each test)
98104
reset(): this;
99105

@@ -186,3 +192,51 @@ mockedGetRegistryV01Servers.overrideHandler((data, info) => {
186192
}
187193
return HttpResponse.json(data);
188194
});
195+
```
196+
197+
### Reusable Scenarios
198+
199+
Define named scenarios in your fixture for commonly used test states:
200+
201+
```typescript
202+
// src/mocks/fixtures/registry_v0_1_servers/get.ts
203+
import type { GetRegistryV01ServersResponse } from "@api/types.gen";
204+
import { AutoAPIMock } from "@mocks";
205+
import { HttpResponse } from "msw";
206+
207+
export const mockedGetRegistryV01Servers = AutoAPIMock<GetRegistryV01ServersResponse>({
208+
servers: [...],
209+
metadata: { count: 15 },
210+
})
211+
.scenario("empty-servers", (self) =>
212+
self.override(() => ({
213+
servers: [],
214+
metadata: { count: 0 },
215+
})),
216+
)
217+
.scenario("server-error", (self) =>
218+
self.overrideHandler(() =>
219+
HttpResponse.json({ error: "Internal Server Error" }, { status: 500 }),
220+
),
221+
);
222+
```
223+
224+
Then use them in tests:
225+
226+
```typescript
227+
import { mockedGetRegistryV01Servers } from "@mocks/fixtures/registry_v0_1_servers/get";
228+
229+
describe("getServers", () => {
230+
it("returns empty array when API returns no servers", async () => {
231+
mockedGetRegistryV01Servers.useScenario("empty-servers");
232+
233+
const servers = await getServers();
234+
expect(servers).toEqual([]);
235+
});
236+
237+
it("throws on 500 server error", async () => {
238+
mockedGetRegistryV01Servers.useScenario("server-error");
239+
240+
await expect(getServers()).rejects.toBeDefined();
241+
});
242+
});

src/app/catalog/actions.test.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@ describe("getServers", () => {
2929
});
3030

3131
it("returns empty array when API returns no servers", async () => {
32-
mockedGetRegistryV01Servers.override(() => ({
33-
servers: [],
34-
metadata: { count: 0 },
35-
}));
32+
mockedGetRegistryV01Servers.useScenario("empty-servers");
3633

3734
const servers = await getServers();
3835

@@ -52,11 +49,9 @@ describe("getServers", () => {
5249
});
5350

5451
describe("error handling", () => {
55-
// Using overrideHandler - needed for error status codes
52+
// Using scenario - reusable error scenario defined in fixture
5653
it("throws on 500 server error", async () => {
57-
mockedGetRegistryV01Servers.overrideHandler(() =>
58-
HttpResponse.json({ error: "Internal Server Error" }, { status: 500 }),
59-
);
54+
mockedGetRegistryV01Servers.useScenario("server-error");
6055

6156
await expect(getServers()).rejects.toBeDefined();
6257
});

src/mocks/autoAPIMock.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ type ResponseResolverInfo = Parameters<HttpResponseResolver>[0];
55

66
type OverrideHandlerFn<T> = (data: T, info: ResponseResolverInfo) => Response;
77
type OverrideFn<T> = (data: T, info: ResponseResolverInfo) => T;
8+
type ScenarioFn<T> = (
9+
instance: AutoAPIMockInstance<T>,
10+
) => AutoAPIMockInstance<T>;
811

912
export interface AutoAPIMockInstance<T> {
1013
generatedHandler: HttpResponseResolver;
1114
override: (fn: OverrideFn<T>) => AutoAPIMockInstance<T>;
1215
overrideHandler: (fn: OverrideHandlerFn<T>) => AutoAPIMockInstance<T>;
16+
scenario: (name: string, fn: ScenarioFn<T>) => AutoAPIMockInstance<T>;
17+
useScenario: (name: string) => AutoAPIMockInstance<T>;
1318
reset: () => AutoAPIMockInstance<T>;
1419
defaultValue: T;
1520
}
@@ -19,6 +24,7 @@ const registry: Set<AutoAPIMockInstance<unknown>> = new Set();
1924

2025
export function AutoAPIMock<T>(defaultValue: T): AutoAPIMockInstance<T> {
2126
let overrideHandlerFn: OverrideHandlerFn<T> | null = null;
27+
const scenarios = new Map<string, ScenarioFn<T>>();
2228

2329
const instance: AutoAPIMockInstance<T> = {
2430
defaultValue,
@@ -41,6 +47,21 @@ export function AutoAPIMock<T>(defaultValue: T): AutoAPIMockInstance<T> {
4147
return instance;
4248
},
4349

50+
scenario(name: string, fn: ScenarioFn<T>) {
51+
scenarios.set(name, fn);
52+
return instance;
53+
},
54+
55+
useScenario(name: string) {
56+
const scenarioFn = scenarios.get(name);
57+
if (!scenarioFn) {
58+
throw new Error(
59+
`Scenario "${name}" not found. Available scenarios: ${[...scenarios.keys()].join(", ") || "(none)"}`,
60+
);
61+
}
62+
return scenarioFn(instance);
63+
},
64+
4465
reset() {
4566
overrideHandlerFn = null;
4667
return instance;

src/mocks/fixtures/registry_v0_1_servers/get.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { GetRegistryV01ServersResponse } from "@api/types.gen";
22
import { AutoAPIMock } from "@mocks";
3+
import { HttpResponse } from "msw";
34

45
export const mockedGetRegistryV01Servers =
56
AutoAPIMock<GetRegistryV01ServersResponse>({
@@ -486,4 +487,15 @@ export const mockedGetRegistryV01Servers =
486487
count: 15,
487488
nextCursor: "next-page",
488489
},
489-
});
490+
})
491+
.scenario("empty-servers", (self) =>
492+
self.override(() => ({
493+
servers: [],
494+
metadata: { count: 0 },
495+
})),
496+
)
497+
.scenario("server-error", (self) =>
498+
self.overrideHandler(() =>
499+
HttpResponse.json({ error: "Internal Server Error" }, { status: 500 }),
500+
),
501+
);

0 commit comments

Comments
 (0)