Skip to content

Commit 4ba72c9

Browse files
fix: ensure that requests to the same resource will be properly intercepted even if they are sent in rapid succession (#30435)
* fix: ensure that requests to the same resource will be properly intercepted even if they are sent in rapid succession - run ci * add changelog * Update packages/net-stubbing/lib/server/middleware/request.ts * PR comments
1 parent aafac6a commit 4ba72c9

File tree

4 files changed

+73
-4
lines changed

4 files changed

+73
-4
lines changed

cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ _Released 10/1/2024 (PENDING)_
66
**Bugfixes:**
77

88
- Patched [find-process](https://github.com/yibn2008/find-process) to fix an issue where trying to clean up browser profiles can throw an error on Windows. Addresses [#30378](https://github.com/cypress-io/cypress/issues/30378).
9+
- Fixed an issue where requests to the same resource in rapid succession may not have the appropriate static response intercept applied if there are multiple intercepts that apply for that resource. Addresses [#30375](https://github.com/cypress-io/cypress/issues/30375).
910

1011
**Misc:**
1112

packages/net-stubbing/lib/server/intercepted-request.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ export class InterceptedRequest {
5151
this._onResponse = opts.onResponse
5252
this.state = opts.state
5353
this.socket = opts.socket
54-
55-
this.addDefaultSubscriptions()
5654
}
5755

5856
onResponse = (incomingRes: IncomingMessage, resStream: Readable) => {
@@ -65,7 +63,7 @@ export class InterceptedRequest {
6563
this._onResponse(incomingRes, resStream)
6664
}
6765

68-
private addDefaultSubscriptions () {
66+
addDefaultSubscriptions () {
6967
if (this.subscriptionsByRoute.length) {
7068
throw new Error('cannot add default subscriptions to non-empty array')
7169
}
@@ -75,6 +73,10 @@ export class InterceptedRequest {
7573
}
7674

7775
for (const route of this.req.matchingRoutes) {
76+
if (route.disabled) {
77+
continue
78+
}
79+
7880
const subscriptionsByRoute = {
7981
routeId: route.id,
8082
immediateStaticResponse: route.staticResponse,

packages/net-stubbing/lib/server/middleware/request.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ export const InterceptRequest: RequestMiddleware = async function () {
138138

139139
await ensureBody()
140140

141+
// Note that this needs to happen after the `ensureBody` call to ensure that we proceed synchronously to
142+
// where we update the state of the routes:
143+
// https://github.com/cypress-io/cypress/blob/aafac6a6104b689a118f4c4f29f948d7d8a35aef/packages/net-stubbing/lib/server/intercepted-request.ts#L167-L169
144+
request.addDefaultSubscriptions()
145+
141146
if (!_.isString(req.body) && !_.isBuffer(req.body)) {
142147
throw new Error('req.body must be a string or a Buffer')
143148
}

packages/net-stubbing/test/unit/intercepted-request-spec.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import { expect } from 'chai'
1+
import chai, { expect } from 'chai'
22
import _ from 'lodash'
33
import sinon from 'sinon'
4+
import sinonChai from 'sinon-chai'
45
import { InterceptedRequest } from '../../lib/server/intercepted-request'
56
import { state as NetStubbingState } from '../../lib/server/state'
67

8+
chai.use(sinonChai)
9+
710
describe('InterceptedRequest', () => {
811
context('handleSubscriptions', () => {
912
it('handles subscriptions as expected', async () => {
@@ -32,6 +35,8 @@ describe('InterceptedRequest', () => {
3235
socket,
3336
})
3437

38+
interceptedRequest.addDefaultSubscriptions()
39+
3540
interceptedRequest.addSubscription({
3641
routeId: '1',
3742
eventName: 'before:response',
@@ -59,6 +64,62 @@ describe('InterceptedRequest', () => {
5964
data,
6065
mergeChanges: _.merge,
6166
})
67+
68+
expect(socket.toDriver).to.be.calledTwice
69+
})
70+
71+
it('ignores disabled subscriptions', async () => {
72+
const socket = {
73+
toDriver: sinon.stub(),
74+
}
75+
const state = NetStubbingState()
76+
const interceptedRequest = new InterceptedRequest({
77+
req: {
78+
matchingRoutes: [
79+
// @ts-ignore
80+
{
81+
id: '1',
82+
hasInterceptor: true,
83+
routeMatcher: {},
84+
disabled: true,
85+
},
86+
// @ts-ignore
87+
{
88+
id: '2',
89+
hasInterceptor: true,
90+
routeMatcher: {},
91+
},
92+
],
93+
},
94+
state,
95+
socket,
96+
})
97+
98+
interceptedRequest.addDefaultSubscriptions()
99+
100+
const data = { foo: 'bar' }
101+
102+
socket.toDriver.callsFake((eventName, subEventName, frame) => {
103+
expect(eventName).to.eq('net:stubbing:event')
104+
expect(subEventName).to.eq('before:request')
105+
expect(frame).to.deep.include({
106+
subscription: {
107+
eventName: 'before:request',
108+
await: true,
109+
routeId: frame.subscription.routeId,
110+
},
111+
})
112+
113+
state.pendingEventHandlers[frame.eventId](frame.data)
114+
})
115+
116+
await interceptedRequest.handleSubscriptions({
117+
eventName: 'before:request',
118+
data,
119+
mergeChanges: _.merge,
120+
})
121+
122+
expect(socket.toDriver).to.be.calledOnce
62123
})
63124
})
64125
})

0 commit comments

Comments
 (0)