Skip to content

Commit 0f506e5

Browse files
authored
fix(toBeRequestedWith): restore postData/response body matching (#2184)
* fix(toBeRequestedWith): restore postData/response body matching * fix(toBeRequestedWith): require webdriverio ^9.28.0 and explain uncollected bodies * test(playgrounds): cover postData/response assertions in mocha e2e
1 parent f4d0af3 commit 0f506e5

7 files changed

Lines changed: 574 additions & 192 deletions

File tree

docs/API.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,18 @@ await expect(mock).toBeRequestedWith({
871871
})
872872
```
873873

874+
> **Note on `postData`/`response` timing:** unlike the other options, the request/response body is
875+
> collected asynchronously (an extra round-trip after the request/response headers are already
876+
> available), so it may not be attached to a call yet at the very first check. Regular assertions
877+
> retry within their `wait` timeout and pick it up once it arrives. `.not.toBeRequestedWith({ postData
878+
> / response })` also retries within `wait` - but only while there's a call that already matches every
879+
> other criterion and is just waiting on its body to attach; if nothing matches at all (wrong URL, no
880+
> call made, etc.) it still resolves immediately, same as any other `.not` assertion. The one residual
881+
> case this can't close: if the body genuinely takes longer to arrive than your configured `wait`, a
882+
> `.not` assertion can still report a false pass. If you rely on `.not` with `postData`/`response` and
883+
> see intermittent false passes, increase `wait` (or await a signal that the request has fully
884+
> completed) before asserting.
885+
874886
## Snapshot Matcher
875887

876888
WebdriverIO supports basic snapshot tests as well as DOM snapshot testing.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"peerDependencies": {
114114
"@wdio/globals": "^9.0.0",
115115
"@wdio/logger": "^9.0.0",
116-
"webdriverio": "^9.0.0"
116+
"webdriverio": "^9.28.0"
117117
},
118118
"peerDependenciesMeta": {
119119
"@wdio/globals": {

playgrounds/jasmine/wdio.conf.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ export const config: WebdriverIO.Config = {
6767
//
6868
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6969
before: function (_capabilities, _specs) {
70-
setOptions({ wait: 500 })
70+
// 500ms wasn't enough headroom for `postData`/`response` collection (an async
71+
setOptions({ wait: 2000 })
7172
},
7273
afterTest: function (_test, _context, { error }) {
7374
if (error) {

playgrounds/mocha/test/specs/network-matchers.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,58 @@ describe('Network Matchers', () => {
9696
it('should throw an error when asserting not be requested', async () => {
9797
await expect(expect(mock).not.toBeRequested()).rejects.toThrow()
9898
})
99+
100+
/**
101+
* `postData`/`response` were silently ignored before #2184 - any assertion on them passed
102+
* regardless of the real payload. These cover the restored comparison end-to-end.
103+
*
104+
* Note: `mock.calls[n].body` is the *upstream* response, not `mock.respond()`'s override
105+
* (respond changes what the page receives; the collector still records what the server sent).
106+
* So `response` is asserted structurally rather than against the mocked value.
107+
*/
108+
it('should assert on postData', async () => {
109+
await expect(mock).toBeRequestedWith({
110+
method: 'POST',
111+
postData: { title: 'foo', description: 'bar' }
112+
})
113+
})
114+
115+
it('should assert on postData with an asymmetric matcher', async () => {
116+
await expect(mock).toBeRequestedWith({
117+
postData: expect.objectContaining({ title: 'foo' })
118+
})
119+
})
120+
121+
it('should assert on postData with a function matcher', async () => {
122+
await expect(mock).toBeRequestedWith({
123+
postData: (postData) => typeof postData === 'string' && postData.includes('description')
124+
})
125+
})
126+
127+
it('should FAIL when postData does not match', async () => {
128+
// the regression guard: this silently passed before the fix
129+
await expect(
130+
expect(mock).toBeRequestedWith({ postData: { title: 'WRONG' } })
131+
).rejects.toThrow()
132+
})
133+
134+
it('should FAIL when postData is expected but the request had none', async () => {
135+
const getMock = await browser.mock('https://webdriver.io/**', { method: 'GET' })
136+
await browser.url('https://webdriver.io/')
137+
await expect(
138+
expect(getMock).toBeRequestedWith({ postData: { any: 'thing' } })
139+
).rejects.toThrow()
140+
})
141+
142+
it('should assert that a response body was collected', async () => {
143+
await expect(mock).toBeRequestedWith({
144+
response: (response) => typeof response === 'string' && response.length > 0
145+
})
146+
})
147+
148+
it('should FAIL when response does not match', async () => {
149+
await expect(
150+
expect(mock).toBeRequestedWith({ response: { definitely: 'not-this' } })
151+
).rejects.toThrow()
152+
})
99153
})

0 commit comments

Comments
 (0)