Skip to content

Commit c44ce74

Browse files
authored
feat(puppeteer): network traffics manipulation (#4263)
1 parent a867294 commit c44ce74

File tree

10 files changed

+622
-706
lines changed

10 files changed

+622
-706
lines changed

docs/helpers/Puppeteer.md

+220-117
Large diffs are not rendered by default.

lib/helper/Playwright.js

+19-140
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
5050
const {
5151
seeElementError, dontSeeElementError, dontSeeElementInDOMError, seeElementInDOMError,
5252
} = require('./errors/ElementAssertion');
53-
const { createAdvancedTestResults, allParameterValuePairsMatchExtreme, extractQueryObjects } = require('./networkTraffics/utils');
54-
const { log } = require('../output');
53+
const {
54+
dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics,
55+
} = require('./network/actions');
5556

5657
const pathSeparator = path.sep;
5758

@@ -3010,37 +3011,6 @@ class Playwright extends Helper {
30103011
});
30113012
}
30123013

3013-
/**
3014-
* {{> grabRecordedNetworkTraffics }}
3015-
*/
3016-
async grabRecordedNetworkTraffics() {
3017-
if (!this.recording || !this.recordedAtLeastOnce) {
3018-
throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.');
3019-
}
3020-
3021-
const promises = this.requests.map(async (request) => {
3022-
const resp = await request.response;
3023-
let body;
3024-
try {
3025-
// There's no 'body' for some requests (redirect etc...)
3026-
body = JSON.parse((await resp.body()).toString());
3027-
} catch (e) {
3028-
// only interested in JSON, not HTML responses.
3029-
}
3030-
3031-
return {
3032-
url: resp.url(),
3033-
response: {
3034-
status: resp.status(),
3035-
statusText: resp.statusText(),
3036-
body,
3037-
},
3038-
};
3039-
});
3040-
3041-
return Promise.all(promises);
3042-
}
3043-
30443014
/**
30453015
* Blocks traffic of a given URL or a list of URLs.
30463016
*
@@ -3120,67 +3090,19 @@ class Playwright extends Helper {
31203090
}
31213091

31223092
/**
3093+
*
31233094
* {{> flushNetworkTraffics }}
31243095
*/
31253096
flushNetworkTraffics() {
3126-
this.requests = [];
3097+
flushNetworkTraffics.call(this);
31273098
}
31283099

31293100
/**
3101+
*
31303102
* {{> stopRecordingTraffic }}
31313103
*/
31323104
stopRecordingTraffic() {
3133-
this.page.removeAllListeners('request');
3134-
this.recording = false;
3135-
}
3136-
3137-
/**
3138-
* {{> seeTraffic }}
3139-
*/
3140-
async seeTraffic({
3141-
name, url, parameters, requestPostData, timeout = 10,
3142-
}) {
3143-
if (!name) {
3144-
throw new Error('Missing required key "name" in object given to "I.seeTraffic".');
3145-
}
3146-
3147-
if (!url) {
3148-
throw new Error('Missing required key "url" in object given to "I.seeTraffic".');
3149-
}
3150-
3151-
if (!this.recording || !this.recordedAtLeastOnce) {
3152-
throw new Error('Failure in test automation. You use "I.seeTraffic", but "I.startRecordingTraffic" was never called before.');
3153-
}
3154-
3155-
for (let i = 0; i <= timeout * 2; i++) {
3156-
const found = this._isInTraffic(url, parameters);
3157-
if (found) {
3158-
return true;
3159-
}
3160-
await new Promise((done) => {
3161-
setTimeout(done, 1000);
3162-
});
3163-
}
3164-
3165-
// check request post data
3166-
if (requestPostData && this._isInTraffic(url)) {
3167-
const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests);
3168-
3169-
assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`);
3170-
} else if (parameters && this._isInTraffic(url)) {
3171-
const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests);
3172-
3173-
assert.fail(
3174-
`Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n`
3175-
+ `${advancedTestResults}`,
3176-
);
3177-
} else {
3178-
assert.fail(
3179-
`Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n`
3180-
+ `Expected url: ${url}.\n`
3181-
+ `Recorded traffic:\n${this._getTrafficDump()}`,
3182-
);
3183-
}
3105+
stopRecordingTraffic.call(this);
31843106
}
31853107

31863108
/**
@@ -3217,73 +3139,30 @@ class Playwright extends Helper {
32173139
}
32183140

32193141
/**
3220-
* {{> dontSeeTraffic }}
32213142
*
3143+
* {{> grabRecordedNetworkTraffics }}
32223144
*/
3223-
dontSeeTraffic({ name, url }) {
3224-
if (!this.recordedAtLeastOnce) {
3225-
throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.');
3226-
}
3227-
3228-
if (!name) {
3229-
throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".');
3230-
}
3231-
3232-
if (!url) {
3233-
throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".');
3234-
}
3235-
3236-
if (this._isInTraffic(url)) {
3237-
assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`);
3238-
}
3145+
async grabRecordedNetworkTraffics() {
3146+
return grabRecordedNetworkTraffics.call(this);
32393147
}
32403148

32413149
/**
3242-
* Checks if URL with parameters is part of network traffic. Returns true or false. Internal method for this helper.
32433150
*
3244-
* @param url URL to look for.
3245-
* @param [parameters] Parameters that this URL needs to contain
3246-
* @return {boolean} Whether or not URL with parameters is part of network traffic.
3247-
* @private
3151+
* {{> seeTraffic }}
32483152
*/
3249-
_isInTraffic(url, parameters) {
3250-
let isInTraffic = false;
3251-
this.requests.forEach((request) => {
3252-
if (isInTraffic) {
3253-
return; // We already found traffic. Continue with next request
3254-
}
3255-
3256-
if (!request.url.match(new RegExp(url))) {
3257-
return; // url not found in this request. continue with next request
3258-
}
3259-
3260-
// URL has matched. Now we check the parameters
3261-
3262-
if (parameters) {
3263-
const advancedReport = allParameterValuePairsMatchExtreme(extractQueryObjects(request.url), parameters);
3264-
if (advancedReport === true) {
3265-
isInTraffic = true;
3266-
}
3267-
} else {
3268-
isInTraffic = true;
3269-
}
3270-
});
3271-
3272-
return isInTraffic;
3153+
async seeTraffic({
3154+
name, url, parameters, requestPostData, timeout = 10,
3155+
}) {
3156+
await seeTraffic.call(this, ...arguments);
32733157
}
32743158

32753159
/**
3276-
* Returns all URLs of all network requests recorded so far during execution of test scenario.
32773160
*
3278-
* @return {string} List of URLs recorded as a string, separated by new lines after each URL
3279-
* @private
3161+
* {{> dontSeeTraffic }}
3162+
*
32803163
*/
3281-
_getTrafficDump() {
3282-
let dumpedTraffic = '';
3283-
this.requests.forEach((request) => {
3284-
dumpedTraffic += `${request.method} - ${request.url}\n`;
3285-
});
3286-
return dumpedTraffic;
3164+
dontSeeTraffic({ name, url }) {
3165+
dontSeeTraffic.call(this, ...arguments);
32873166
}
32883167

32893168
/**

lib/helper/Puppeteer.js

+82-3
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ const ElementNotFound = require('./errors/ElementNotFound');
3636
const RemoteBrowserConnectionRefused = require('./errors/RemoteBrowserConnectionRefused');
3737
const Popup = require('./extras/Popup');
3838
const Console = require('./extras/Console');
39-
const findReact = require('./extras/React');
4039
const { highlightElement } = require('./scripts/highlightElement');
4140
const { blurElement } = require('./scripts/blurElement');
42-
const { focusElement } = require('./scripts/focusElement');
4341
const {
4442
dontSeeElementError, seeElementError, dontSeeElementInDOMError, seeElementInDOMError,
4543
} = require('./errors/ElementAssertion');
44+
const {
45+
dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics,
46+
} = require('./network/actions');
4647

4748
let puppeteer;
4849
let perfTiming;
@@ -226,6 +227,11 @@ class Puppeteer extends Helper {
226227
this.sessionPages = {};
227228
this.activeSessionName = '';
228229

230+
// for network stuff
231+
this.requests = [];
232+
this.recording = false;
233+
this.recordedAtLeastOnce = false;
234+
229235
// for websocket messages
230236
this.webSocketMessages = [];
231237
this.recordingWebSocketMessages = false;
@@ -2514,6 +2520,79 @@ class Puppeteer extends Helper {
25142520
});
25152521
}
25162522

2523+
/**
2524+
*
2525+
* {{> flushNetworkTraffics }}
2526+
*/
2527+
flushNetworkTraffics() {
2528+
flushNetworkTraffics.call(this);
2529+
}
2530+
2531+
/**
2532+
*
2533+
* {{> stopRecordingTraffic }}
2534+
*/
2535+
stopRecordingTraffic() {
2536+
stopRecordingTraffic.call(this);
2537+
}
2538+
2539+
/**
2540+
* {{> startRecordingTraffic }}
2541+
*
2542+
*/
2543+
async startRecordingTraffic() {
2544+
this.flushNetworkTraffics();
2545+
this.recording = true;
2546+
this.recordedAtLeastOnce = true;
2547+
2548+
await this.page.setRequestInterception(true);
2549+
2550+
this.page.on('request', (request) => {
2551+
const information = {
2552+
url: request.url(),
2553+
method: request.method(),
2554+
requestHeaders: request.headers(),
2555+
requestPostData: request.postData(),
2556+
response: request.response(),
2557+
};
2558+
2559+
this.debugSection('REQUEST: ', JSON.stringify(information));
2560+
2561+
if (typeof information.requestPostData === 'object') {
2562+
information.requestPostData = JSON.parse(information.requestPostData);
2563+
}
2564+
request.continue();
2565+
this.requests.push(information);
2566+
});
2567+
}
2568+
2569+
/**
2570+
*
2571+
* {{> grabRecordedNetworkTraffics }}
2572+
*/
2573+
async grabRecordedNetworkTraffics() {
2574+
return grabRecordedNetworkTraffics.call(this);
2575+
}
2576+
2577+
/**
2578+
*
2579+
* {{> seeTraffic }}
2580+
*/
2581+
async seeTraffic({
2582+
name, url, parameters, requestPostData, timeout = 10,
2583+
}) {
2584+
await seeTraffic.call(this, ...arguments);
2585+
}
2586+
2587+
/**
2588+
*
2589+
* {{> dontSeeTraffic }}
2590+
*
2591+
*/
2592+
dontSeeTraffic({ name, url }) {
2593+
dontSeeTraffic.call(this, ...arguments);
2594+
}
2595+
25172596
async getNewCDPSession() {
25182597
const client = await this.page.target().createCDPSession();
25192598
return client;
@@ -2566,7 +2645,7 @@ class Puppeteer extends Helper {
25662645
/**
25672646
* Grab the recording WS messages
25682647
*
2569-
* @return { Array<any> }
2648+
* @return { Array<any>|undefined }
25702649
*
25712650
*/
25722651
grabWebSocketMessages() {

0 commit comments

Comments
 (0)