Skip to content

Commit 9700afa

Browse files
committed
fix: reset generator state on .resetHandlers()/.restoreHandlers()
1 parent edeb058 commit 9700afa

6 files changed

Lines changed: 78 additions & 58 deletions

File tree

src/core/experimental/define-network.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ export interface DefineNetworkOptions<
4747
onUnhandledFrame?: UnhandledFrameHandle
4848
}
4949

50-
export interface NetworkApi<Sources extends Array<NetworkSource<any>>>
51-
extends NetworkHandlersApi {
50+
export interface NetworkApi<
51+
Sources extends Array<NetworkSource<any>>,
52+
> extends NetworkHandlersApi {
5253
readyState: NetworkReadyState
5354
/**
5455
* Enable the network interception and handling.
@@ -210,11 +211,7 @@ export function defineNetwork<Sources extends Array<NetworkSource<any>>>(
210211
handlersController.reset(handlers)
211212
},
212213
restoreHandlers() {
213-
for (const handler of handlersController.currentHandlers()) {
214-
if ('isUsed' in handler) {
215-
handler.isUsed = false
216-
}
217-
}
214+
handlersController.restore()
218215
},
219216
listHandlers() {
220217
return toReadonlyArray(handlersController.currentHandlers())

src/core/experimental/handlers-controller.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ export abstract class HandlersController {
9090
),
9191
)
9292

93+
for (const handler of this.currentHandlers()) {
94+
if ('reset' in handler) {
95+
handler['reset']()
96+
}
97+
}
98+
9399
const { initialHandlers } = this.getState()
94100

95101
if (nextHandlers.length === 0) {
@@ -108,6 +114,14 @@ export abstract class HandlersController {
108114
})
109115
}
110116

117+
public restore(): void {
118+
for (const handler of this.currentHandlers()) {
119+
if ('restore' in handler) {
120+
handler['restore']()
121+
}
122+
}
123+
}
124+
111125
#validateHandlers(handlers: Array<AnyHandler>): boolean {
112126
return handlers.every((handler) => !Array.isArray(handler))
113127
}

src/core/experimental/setup-api.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@ export abstract class SetupApi<
4242
}
4343

4444
public restoreHandlers(): void {
45-
this.handlersController.currentHandlers().forEach((handler) => {
46-
if ('isUsed' in handler) {
47-
handler.isUsed = false
48-
}
49-
})
45+
this.handlersController.restore()
5046
}
5147

5248
public resetHandlers(...nextHandlers: Array<AnyHandler>): void {

src/core/handlers/RequestHandler.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,27 @@ export abstract class RequestHandler<
171171
this.isUsed = false
172172
}
173173

174+
/**
175+
* Reset the runtime state accumulated during response resolution,
176+
* such as generator iterator progress. Called when this handler is
177+
* removed from the active handlers list so re-adding it later starts
178+
* from a clean state.
179+
*/
180+
protected reset(): void {
181+
this.resolverIterator = undefined
182+
this.resolverIteratorResult = undefined
183+
}
184+
185+
/**
186+
* Restore this handler so it can match requests again after being
187+
* exhausted (e.g. via `{ once: true }`). Also clears any accumulated
188+
* resolution state.
189+
*/
190+
protected restore(): void {
191+
this.reset()
192+
this.isUsed = false
193+
}
194+
174195
/**
175196
* Determine if the intercepted request should be mocked.
176197
*/

src/core/utils/internal/requestHandlerUtils.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

test/node/rest-api/response/generator.test.ts

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
/**
2-
* @vitest-environment node
3-
*/
1+
// @vitest-environment node
42
import { http, HttpResponse, delay } from 'msw'
53
import { setupServer } from 'msw/node'
64

@@ -24,7 +22,7 @@ afterAll(() => {
2422

2523
it('supports generator function as response resolver', async () => {
2624
server.use(
27-
http.get('https://example.com/weather', function* () {
25+
http.get('http://localhost/weather', function* () {
2826
let degree = 10
2927

3028
while (degree < 13) {
@@ -38,18 +36,18 @@ it('supports generator function as response resolver', async () => {
3836
)
3937

4038
// Must respond with yielded responses.
41-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(11)
42-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(12)
43-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(13)
39+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(11)
40+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(12)
41+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(13)
4442
// Must respond with the final "done" response.
45-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(14)
43+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(14)
4644
// Must keep responding with the final "done" response.
47-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(14)
45+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(14)
4846
})
4947

5048
it('supports async generator function as response resolver', async () => {
5149
server.use(
52-
http.get('https://example.com/weather', async function* () {
50+
http.get('http://localhost/weather', async function* () {
5351
await delay(20)
5452

5553
let degree = 10
@@ -64,17 +62,17 @@ it('supports async generator function as response resolver', async () => {
6462
}),
6563
)
6664

67-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(11)
68-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(12)
69-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(13)
70-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(14)
71-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(14)
65+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(11)
66+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(12)
67+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(13)
68+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(14)
69+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(14)
7270
})
7371

7472
it('supports generator function as one-time response resolver', async () => {
7573
server.use(
7674
http.get(
77-
'https://example.com/weather',
75+
'http://localhost/weather',
7876
function* () {
7977
let degree = 10
8078

@@ -94,16 +92,31 @@ it('supports generator function as one-time response resolver', async () => {
9492
)
9593

9694
// Must respond with the yielded incrementing responses.
97-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(11)
98-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(12)
99-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(13)
95+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(11)
96+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(12)
97+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(13)
10098
// Must respond with the "done" final response from the iterator.
101-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(14)
99+
await expect(fetchJson('http://localhost/weather')).resolves.toBe(14)
102100
// Must respond with the other handler since the generator one is used.
103-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(
104-
'fallback',
105-
)
106-
await expect(fetchJson('https://example.com/weather')).resolves.toEqual(
107-
'fallback',
101+
await expect(fetchJson('http://localhost/weather')).resolves.toBe('fallback')
102+
await expect(fetchJson('http://localhost/weather')).resolves.toBe('fallback')
103+
})
104+
105+
it('resets the generator state after the handlers are reset', async () => {
106+
server.use(
107+
http.get('http://localhost/resource', function* () {
108+
yield HttpResponse.json('Yield')
109+
return HttpResponse.json('Stable')
110+
}),
108111
)
112+
113+
await server.boundary(async () => {
114+
await expect(fetchJson('http://localhost/resource')).resolves.toBe('Yield')
115+
await expect(fetchJson('http://localhost/resource')).resolves.toBe('Stable')
116+
117+
server.resetHandlers()
118+
119+
await expect(fetchJson('http://localhost/resource')).resolves.toBe('Yield')
120+
await expect(fetchJson('http://localhost/resource')).resolves.toBe('Stable')
121+
})()
109122
})

0 commit comments

Comments
 (0)