From 83030f5bf597d58d82c3202cefc0f1251956da75 Mon Sep 17 00:00:00 2001 From: Samuel SAINT-OMER Date: Mon, 1 Sep 2025 16:47:29 +0200 Subject: [PATCH 1/3] fix: improve traffic recording checks and add test for stopping network recording --- lib/helper/network/actions.js | 81 ++++++++++++++++------------------- test/helper/webapi.js | 10 +++++ 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/lib/helper/network/actions.js b/lib/helper/network/actions.js index e89ba7ed2..c17c799b8 100644 --- a/lib/helper/network/actions.js +++ b/lib/helper/network/actions.js @@ -1,77 +1,68 @@ -const assert = require('assert'); -const { isInTraffic, createAdvancedTestResults, getTrafficDump } = require('./utils'); +const assert = require('assert') +const { isInTraffic, createAdvancedTestResults, getTrafficDump } = require('./utils') function dontSeeTraffic({ name, url }) { if (!this.recordedAtLeastOnce) { - throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.'); + throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.') } if (!name) { - throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".'); + throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".') } if (!url) { - throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".'); + throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".') } if (isInTraffic.call(this, url)) { - assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`); + assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`) } } -async function seeTraffic({ - name, url, parameters, requestPostData, timeout = 10, -}) { +async function seeTraffic({ name, url, parameters, requestPostData, timeout = 10 }) { if (!name) { - throw new Error('Missing required key "name" in object given to "I.seeTraffic".'); + throw new Error('Missing required key "name" in object given to "I.seeTraffic".') } if (!url) { - throw new Error('Missing required key "url" in object given to "I.seeTraffic".'); + throw new Error('Missing required key "url" in object given to "I.seeTraffic".') } - if (!this.recording || !this.recordedAtLeastOnce) { - throw new Error('Failure in test automation. You use "I.seeTraffic", but "I.startRecordingTraffic" was never called before.'); + if (!this.recordedAtLeastOnce) { + throw new Error('Failure in test automation. You use "I.seeTraffic", but "I.startRecordingTraffic" was never called before.') } for (let i = 0; i <= timeout * 2; i++) { - const found = isInTraffic.call(this, url, parameters); + const found = isInTraffic.call(this, url, parameters) if (found) { - return true; + return true } - await new Promise((done) => { - setTimeout(done, 1000); - }); + await new Promise(done => { + setTimeout(done, 1000) + }) } // check request post data if (requestPostData && isInTraffic.call(this, url)) { - const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests); + const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests) - assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`); + assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`) } else if (parameters && isInTraffic.call(this, url)) { - const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests); + const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests) - assert.fail( - `Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n` - + `${advancedTestResults}`, - ); + assert.fail(`Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n` + `${advancedTestResults}`) } else { - assert.fail( - `Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n` - + `Expected url: ${url}.\n` - + `Recorded traffic:\n${getTrafficDump.call(this)}`, - ); + assert.fail(`Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n` + `Expected url: ${url}.\n` + `Recorded traffic:\n${getTrafficDump.call(this)}`) } } async function grabRecordedNetworkTraffics() { - if (!this.recording || !this.recordedAtLeastOnce) { - throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.'); + if (!this.recordedAtLeastOnce) { + throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.') } - const promises = this.requests.map(async (request) => { - const resp = await request.response; + const promises = this.requests.map(async request => { + const resp = await request.response if (!resp) { return { @@ -81,13 +72,13 @@ async function grabRecordedNetworkTraffics() { statusText: '', body: '', }, - }; + } } - let body; + let body try { // There's no 'body' for some requests (redirect etc...) - body = JSON.parse((await resp.body()).toString()); + body = JSON.parse((await resp.body()).toString()) } catch (e) { // only interested in JSON, not HTML responses. } @@ -99,19 +90,21 @@ async function grabRecordedNetworkTraffics() { statusText: resp.statusText(), body, }, - }; - }); - return Promise.all(promises); + } + }) + return Promise.all(promises) } function stopRecordingTraffic() { // @ts-ignore - this.page.removeAllListeners('request'); - this.recording = false; + this.page.removeAllListeners('request') + // @ts-ignore + this.page.removeAllListeners('requestfinished') + this.recording = false } function flushNetworkTraffics() { - this.requests = []; + this.requests = [] } module.exports = { @@ -120,4 +113,4 @@ module.exports = { grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics, -}; +} diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 4705eae67..cd9b58b39 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -1703,6 +1703,16 @@ module.exports.tests = function () { expect(traffics.length).to.equal(0) }) + it('should stop the network recording', async () => { + await I.startRecordingTraffic() + await I.amOnPage('https://codecept.io/') + await I.stopRecordingTraffic() + const traffics1 = await I.grabRecordedNetworkTraffics() + await I.amOnPage('https://codecept.io/') + const traffics2 = await I.grabRecordedNetworkTraffics() + expect(traffics2.length).to.equal(traffics1.length) + }) + it('should see recording traffics', async () => { I.startRecordingTraffic() I.amOnPage('https://codecept.io/') From 9c499a418d25d942fb8d0ce63a22571ad89d4c1b Mon Sep 17 00:00:00 2001 From: Samuel SAINT-OMER Date: Mon, 1 Sep 2025 17:02:26 +0200 Subject: [PATCH 2/3] fix: lint --- lib/helper/network/actions.js | 79 +++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/lib/helper/network/actions.js b/lib/helper/network/actions.js index c17c799b8..6be1c1ebc 100644 --- a/lib/helper/network/actions.js +++ b/lib/helper/network/actions.js @@ -1,68 +1,77 @@ -const assert = require('assert') -const { isInTraffic, createAdvancedTestResults, getTrafficDump } = require('./utils') +const assert = require('assert'); +const { isInTraffic, createAdvancedTestResults, getTrafficDump } = require('./utils'); function dontSeeTraffic({ name, url }) { if (!this.recordedAtLeastOnce) { - throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.') + throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.'); } if (!name) { - throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".') + throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".'); } if (!url) { - throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".') + throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".'); } if (isInTraffic.call(this, url)) { - assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`) + assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`); } } -async function seeTraffic({ name, url, parameters, requestPostData, timeout = 10 }) { +async function seeTraffic({ + name, url, parameters, requestPostData, timeout = 10, +}) { if (!name) { - throw new Error('Missing required key "name" in object given to "I.seeTraffic".') + throw new Error('Missing required key "name" in object given to "I.seeTraffic".'); } if (!url) { - throw new Error('Missing required key "url" in object given to "I.seeTraffic".') + throw new Error('Missing required key "url" in object given to "I.seeTraffic".'); } if (!this.recordedAtLeastOnce) { - throw new Error('Failure in test automation. You use "I.seeTraffic", but "I.startRecordingTraffic" was never called before.') + throw new Error('Failure in test automation. You use "I.seeTraffic", but "I.startRecordingTraffic" was never called before.'); } for (let i = 0; i <= timeout * 2; i++) { - const found = isInTraffic.call(this, url, parameters) + const found = isInTraffic.call(this, url, parameters); if (found) { - return true + return true; } - await new Promise(done => { - setTimeout(done, 1000) - }) + await new Promise((done) => { + setTimeout(done, 1000); + }); } // check request post data if (requestPostData && isInTraffic.call(this, url)) { - const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests) + const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests); - assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`) + assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`); } else if (parameters && isInTraffic.call(this, url)) { - const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests) + const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests); - assert.fail(`Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n` + `${advancedTestResults}`) + assert.fail( + `Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n` + + `${advancedTestResults}`, + ); } else { - assert.fail(`Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n` + `Expected url: ${url}.\n` + `Recorded traffic:\n${getTrafficDump.call(this)}`) + assert.fail( + `Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n` + + `Expected url: ${url}.\n` + + `Recorded traffic:\n${getTrafficDump.call(this)}`, + ); } } async function grabRecordedNetworkTraffics() { if (!this.recordedAtLeastOnce) { - throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.') + throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.'); } - const promises = this.requests.map(async request => { - const resp = await request.response + const promises = this.requests.map(async (request) => { + const resp = await request.response; if (!resp) { return { @@ -72,13 +81,13 @@ async function grabRecordedNetworkTraffics() { statusText: '', body: '', }, - } + }; } - let body + let body; try { // There's no 'body' for some requests (redirect etc...) - body = JSON.parse((await resp.body()).toString()) + body = JSON.parse((await resp.body()).toString()); } catch (e) { // only interested in JSON, not HTML responses. } @@ -90,21 +99,21 @@ async function grabRecordedNetworkTraffics() { statusText: resp.statusText(), body, }, - } - }) - return Promise.all(promises) + }; + }); + return Promise.all(promises); } function stopRecordingTraffic() { // @ts-ignore - this.page.removeAllListeners('request') - // @ts-ignore - this.page.removeAllListeners('requestfinished') - this.recording = false + this.page.removeAllListeners('request'); + // @ts-ignore + this.page.removeAllListeners('requestfinished'); + this.recording = false; } function flushNetworkTraffics() { - this.requests = [] + this.requests = []; } module.exports = { @@ -113,4 +122,4 @@ module.exports = { grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics, -} +}; From 880325ff643d33f9a1fe408035c53b87b5168329 Mon Sep 17 00:00:00 2001 From: Samuel SAINT-OMER Date: Tue, 2 Sep 2025 11:33:55 +0200 Subject: [PATCH 3/3] fix: make stopRecordingTraffic asynchronous and add request interception handling for pupperteer --- lib/helper/Puppeteer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/helper/Puppeteer.js b/lib/helper/Puppeteer.js index 3a23b5f7f..eed4984d2 100644 --- a/lib/helper/Puppeteer.js +++ b/lib/helper/Puppeteer.js @@ -2591,7 +2591,8 @@ class Puppeteer extends Helper { * * {{> stopRecordingTraffic }} */ - stopRecordingTraffic() { + async stopRecordingTraffic() { + await this.page.setRequestInterception(false) stopRecordingTraffic.call(this) }