From 59c1cdb62711e9e37f4cceda45a8a3cffc4904c8 Mon Sep 17 00:00:00 2001 From: Landon Martin Date: Fri, 13 Nov 2026 15:12:20 -0500 Subject: [PATCH 1/4] feat: include request headers in grabRecordedNetworkTraffics Every helper that records traffic (Playwright, Puppeteer, CDPBrowser) already stores requestHeaders on each recorded request, but grabRecordedNetworkTraffics dropped them, so tests had to reach into helper internals to assert on a request header. --- docs/webapi/grabRecordedNetworkTraffics.mustache | 1 + lib/helper/network/actions.js | 2 ++ test/helper/webapi.js | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/docs/webapi/grabRecordedNetworkTraffics.mustache b/docs/webapi/grabRecordedNetworkTraffics.mustache index 90cc824fb..2d4ee0225 100644 --- a/docs/webapi/grabRecordedNetworkTraffics.mustache +++ b/docs/webapi/grabRecordedNetworkTraffics.mustache @@ -3,6 +3,7 @@ Grab the recording network traffics ```js const traffics = await I.grabRecordedNetworkTraffics(); expect(traffics[0].url).to.equal('https://reqres.in/api/comments/1'); +expect(traffics[0].requestHeaders['x-request-id']).to.equal('abc-123'); expect(traffics[0].response.status).to.equal(200); expect(traffics[0].response.body).to.contain({ name: 'this was mocked' }); ``` diff --git a/lib/helper/network/actions.js b/lib/helper/network/actions.js index 4535fa83d..91c862241 100644 --- a/lib/helper/network/actions.js +++ b/lib/helper/network/actions.js @@ -67,6 +67,7 @@ async function grabRecordedNetworkTraffics() { if (!resp) { return { url: '', + requestHeaders: request.requestHeaders, response: { status: '', statusText: '', @@ -85,6 +86,7 @@ async function grabRecordedNetworkTraffics() { return { url: resp.url(), + requestHeaders: request.requestHeaders, response: { status: resp.status(), statusText: resp.statusText(), diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 08b8138f1..574219c84 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -2469,6 +2469,15 @@ export function tests() { expect(traffics.length).to.equal(0) }) + it('should grab request headers of recorded traffics', async () => { + await I.startRecordingTraffic() + await I.amOnPage('https://codecept.io/') + const traffics = await I.grabRecordedNetworkTraffics() + expect(traffics.length).to.be.above(0) + const headerNames = Object.keys(traffics[0].requestHeaders).map(name => name.toLowerCase()) + expect(headerNames).to.include('user-agent') + }) + it('should stop the network recording', async () => { await I.startRecordingTraffic() await I.amOnPage('https://codecept.io/') From f13a5321f60a60459c7ec29bdcbd967bc57fd2d8 Mon Sep 17 00:00:00 2001 From: Landon Martin Date: Fri, 13 Nov 2026 15:47:05 -0500 Subject: [PATCH 2/4] feat: return the recorded request in grabRecordedNetworkTraffics Expose url, method, headers and postData as a request object alongside response, instead of only the headers. --- docs/webapi/grabRecordedNetworkTraffics.mustache | 3 ++- lib/helper/network/actions.js | 10 ++++++++-- test/helper/webapi.js | 5 +++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/webapi/grabRecordedNetworkTraffics.mustache b/docs/webapi/grabRecordedNetworkTraffics.mustache index 2d4ee0225..d1c497a03 100644 --- a/docs/webapi/grabRecordedNetworkTraffics.mustache +++ b/docs/webapi/grabRecordedNetworkTraffics.mustache @@ -3,7 +3,8 @@ Grab the recording network traffics ```js const traffics = await I.grabRecordedNetworkTraffics(); expect(traffics[0].url).to.equal('https://reqres.in/api/comments/1'); -expect(traffics[0].requestHeaders['x-request-id']).to.equal('abc-123'); +expect(traffics[0].request.method).to.equal('GET'); +expect(traffics[0].request.headers['x-request-id']).to.equal('abc-123'); expect(traffics[0].response.status).to.equal(200); expect(traffics[0].response.body).to.contain({ name: 'this was mocked' }); ``` diff --git a/lib/helper/network/actions.js b/lib/helper/network/actions.js index 91c862241..8258339b1 100644 --- a/lib/helper/network/actions.js +++ b/lib/helper/network/actions.js @@ -63,11 +63,17 @@ async function grabRecordedNetworkTraffics() { const promises = this.requests.map(async request => { const resp = await request.response + const requestInfo = { + url: request.url, + method: request.method, + headers: request.requestHeaders, + postData: request.requestPostData, + } if (!resp) { return { url: '', - requestHeaders: request.requestHeaders, + request: requestInfo, response: { status: '', statusText: '', @@ -86,7 +92,7 @@ async function grabRecordedNetworkTraffics() { return { url: resp.url(), - requestHeaders: request.requestHeaders, + request: requestInfo, response: { status: resp.status(), statusText: resp.statusText(), diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 574219c84..5599a91c8 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -2469,12 +2469,13 @@ export function tests() { expect(traffics.length).to.equal(0) }) - it('should grab request headers of recorded traffics', async () => { + it('should grab the request of recorded traffics', async () => { await I.startRecordingTraffic() await I.amOnPage('https://codecept.io/') const traffics = await I.grabRecordedNetworkTraffics() expect(traffics.length).to.be.above(0) - const headerNames = Object.keys(traffics[0].requestHeaders).map(name => name.toLowerCase()) + expect(traffics[0].request.method).to.equal('GET') + const headerNames = Object.keys(traffics[0].request.headers).map(name => name.toLowerCase()) expect(headerNames).to.include('user-agent') }) From 495302970b62c6fda7beabcc903880af3be0464d Mon Sep 17 00:00:00 2001 From: Landon Martin Date: Fri, 13 Nov 2026 15:50:07 -0500 Subject: [PATCH 3/4] refactor: name the request object req to match resp; use a real header in the docs example --- docs/webapi/grabRecordedNetworkTraffics.mustache | 2 +- lib/helper/network/actions.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/webapi/grabRecordedNetworkTraffics.mustache b/docs/webapi/grabRecordedNetworkTraffics.mustache index d1c497a03..5e9086205 100644 --- a/docs/webapi/grabRecordedNetworkTraffics.mustache +++ b/docs/webapi/grabRecordedNetworkTraffics.mustache @@ -4,7 +4,7 @@ Grab the recording network traffics const traffics = await I.grabRecordedNetworkTraffics(); expect(traffics[0].url).to.equal('https://reqres.in/api/comments/1'); expect(traffics[0].request.method).to.equal('GET'); -expect(traffics[0].request.headers['x-request-id']).to.equal('abc-123'); +expect(traffics[0].request.headers['user-agent']).to.contain('Mozilla'); expect(traffics[0].response.status).to.equal(200); expect(traffics[0].response.body).to.contain({ name: 'this was mocked' }); ``` diff --git a/lib/helper/network/actions.js b/lib/helper/network/actions.js index 8258339b1..8790a18d9 100644 --- a/lib/helper/network/actions.js +++ b/lib/helper/network/actions.js @@ -63,7 +63,7 @@ async function grabRecordedNetworkTraffics() { const promises = this.requests.map(async request => { const resp = await request.response - const requestInfo = { + const req = { url: request.url, method: request.method, headers: request.requestHeaders, @@ -73,7 +73,7 @@ async function grabRecordedNetworkTraffics() { if (!resp) { return { url: '', - request: requestInfo, + request: req, response: { status: '', statusText: '', @@ -92,7 +92,7 @@ async function grabRecordedNetworkTraffics() { return { url: resp.url(), - request: requestInfo, + request: req, response: { status: resp.status(), statusText: resp.statusText(), From 780f9a916f727e9ec4515cfc6066b7516ec0079d Mon Sep 17 00:00:00 2001 From: Landon Martin Date: Fri, 13 Nov 2026 16:13:03 -0500 Subject: [PATCH 4/4] fix: parse JSON post data in grabRecordedNetworkTraffics so every helper returns the same type --- lib/helper/network/actions.js | 11 ++++++++++- test/helper/webapi.js | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/helper/network/actions.js b/lib/helper/network/actions.js index 8790a18d9..34d287833 100644 --- a/lib/helper/network/actions.js +++ b/lib/helper/network/actions.js @@ -56,6 +56,15 @@ async function seeTraffic({ name, url, parameters, requestPostData, timeout = 10 } } +function parsePostData(postData) { + if (typeof postData !== 'string') return postData + try { + return JSON.parse(postData) + } catch (e) { + return postData + } +} + async function grabRecordedNetworkTraffics() { if (!this.recordedAtLeastOnce) { throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.'); @@ -67,7 +76,7 @@ async function grabRecordedNetworkTraffics() { url: request.url, method: request.method, headers: request.requestHeaders, - postData: request.requestPostData, + postData: parsePostData(request.requestPostData), } if (!resp) { diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 5599a91c8..0e06a32c8 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -2479,6 +2479,20 @@ export function tests() { expect(headerNames).to.include('user-agent') }) + it('should grab JSON post data of recorded traffics as an object', async () => { + await I.amOnPage('https://codecept.io/') + await I.startRecordingTraffic() + await I.executeScript(() => + fetch('https://codecept.io/traffic-post-data', { method: 'POST', body: JSON.stringify({ name: 'codecept' }) }) + .then(res => res.status) + .catch(() => 0), + ) + await I.seeTraffic({ name: 'post', url: 'https://codecept.io/traffic-post-data' }) + const traffics = await I.grabRecordedNetworkTraffics() + const post = traffics.find(t => t.request.method === 'POST') + expect(post.request.postData).to.deep.equal({ name: 'codecept' }) + }) + it('should stop the network recording', async () => { await I.startRecordingTraffic() await I.amOnPage('https://codecept.io/')