Skip to content

Commit 9971514

Browse files
authored
feat(playwright): grab WS messages (#3789)
1 parent d9839aa commit 9971514

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

docs/helpers/Playwright.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,10 @@ I.fillField({css: 'form#login input[name=username]'}, 'John');
861861

862862
Resets all recorded network requests.
863863

864+
### flushWebSocketMessages
865+
866+
Resets all recorded WS messages.
867+
864868
### focus
865869

866870
Calls [focus][10] on the matching element.
@@ -1257,6 +1261,12 @@ let inputs = await I.grabValueFromAll('//form/input');
12571261

12581262
Returns **[Promise][20]<[Array][22]<[string][8]>>** attribute value
12591263

1264+
### grabWebSocketMessages
1265+
1266+
Grab the recording WS messages
1267+
1268+
Returns **[Array][22]<any>**
1269+
12601270
### handleDownloads
12611271

12621272
Handles a file download. A file name is required to save the file on disk.
@@ -1960,6 +1970,15 @@ I.startRecordingTraffic();
19601970

19611971
Returns **[Promise][20]<void>**
19621972

1973+
### startRecordingWebSocketMessages
1974+
1975+
Starts recording of websocket messages.
1976+
This also resets recorded websocket messages.
1977+
1978+
```js
1979+
await I.startRecordingWebSocketMessages();
1980+
```
1981+
19631982
### stopMockingRoute
19641983

19651984
Stops network mocking created by `mockRoute`.
@@ -1984,6 +2003,14 @@ Stops recording of network traffic. Recorded traffic is not flashed.
19842003
I.stopRecordingTraffic();
19852004
```
19862005

2006+
### stopRecordingWebSocketMessages
2007+
2008+
Stops recording WS messages. Recorded WS messages is not flashed.
2009+
2010+
```js
2011+
await I.stopRecordingWebSocketMessages();
2012+
```
2013+
19872014
### switchTo
19882015

19892016
Switches frame or in case of null locator reverts to parent.

lib/helper/Playwright.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,12 @@ class Playwright extends Helper {
318318
this.recording = false;
319319
this.recordedAtLeastOnce = false;
320320

321+
// for websocket messages
322+
this.webSocketMessages = [];
323+
this.recordingWebSocketMessages = false;
324+
this.recordedWebSocketMessagesAtLeastOnce = false;
325+
this.cdpSession = null;
326+
321327
// override defaults with config
322328
this._setConfig(config);
323329
}
@@ -2981,6 +2987,102 @@ class Playwright extends Helper {
29812987
});
29822988
return dumpedTraffic;
29832989
}
2990+
2991+
/**
2992+
* Starts recording of websocket messages.
2993+
* This also resets recorded websocket messages.
2994+
*
2995+
* ```js
2996+
* await I.startRecordingWebSocketMessages();
2997+
* ```
2998+
*
2999+
*/
3000+
async startRecordingWebSocketMessages() {
3001+
this.flushWebSocketMessages();
3002+
this.recordingWebSocketMessages = true;
3003+
this.recordedWebSocketMessagesAtLeastOnce = true;
3004+
3005+
this.cdpSession = await this.getNewCDPSession();
3006+
await this.cdpSession.send('Network.enable');
3007+
await this.cdpSession.send('Page.enable');
3008+
3009+
this.cdpSession.on(
3010+
'Network.webSocketFrameReceived',
3011+
(payload) => {
3012+
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload));
3013+
},
3014+
);
3015+
3016+
this.cdpSession.on(
3017+
'Network.webSocketFrameSent',
3018+
(payload) => {
3019+
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload));
3020+
},
3021+
);
3022+
3023+
this.cdpSession.on(
3024+
'Network.webSocketFrameError',
3025+
(payload) => {
3026+
this._logWebsocketMessages(this._getWebSocketLog('ERROR', payload));
3027+
},
3028+
);
3029+
}
3030+
3031+
/**
3032+
* Stops recording WS messages. Recorded WS messages is not flashed.
3033+
*
3034+
* ```js
3035+
* await I.stopRecordingWebSocketMessages();
3036+
* ```
3037+
*/
3038+
async stopRecordingWebSocketMessages() {
3039+
await this.cdpSession.send('Network.disable');
3040+
await this.cdpSession.send('Page.disable');
3041+
this.page.removeAllListeners('Network');
3042+
this.recordingWebSocketMessages = false;
3043+
}
3044+
3045+
/**
3046+
* Grab the recording WS messages
3047+
*
3048+
* @return { Array<any> }
3049+
*
3050+
*/
3051+
grabWebSocketMessages() {
3052+
if (!this.recordingWebSocketMessages) {
3053+
if (!this.recordedWebSocketMessagesAtLeastOnce) {
3054+
throw new Error('Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.');
3055+
}
3056+
}
3057+
return this.webSocketMessages;
3058+
}
3059+
3060+
/**
3061+
* Resets all recorded WS messages.
3062+
*/
3063+
flushWebSocketMessages() {
3064+
this.webSocketMessages = [];
3065+
}
3066+
3067+
_getWebSocketMessage(payload) {
3068+
if (payload.errorMessage) {
3069+
return payload.errorMessage;
3070+
}
3071+
3072+
return payload.response.payloadData;
3073+
}
3074+
3075+
_getWebSocketLog(prefix, payload) {
3076+
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n`;
3077+
}
3078+
3079+
async getNewCDPSession() {
3080+
return this.page.context().newCDPSession(this.page);
3081+
}
3082+
3083+
_logWebsocketMessages(message) {
3084+
this.webSocketMessages += message;
3085+
}
29843086
}
29853087

29863088
module.exports = Playwright;

test/helper/Playwright_test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,47 @@ describe('Playwright', function () {
938938
});
939939
});
940940

941+
describe('#startRecordingWebSocketMessages, #grabWebSocketMessages, #stopRecordingWebSocketMessages', () => {
942+
it('should throw error when calling grabWebSocketMessages before startRecordingWebSocketMessages', () => {
943+
try {
944+
I.amOnPage('https://websocketstest.com/');
945+
I.waitForText('Work for You!');
946+
I.grabWebSocketMessages();
947+
} catch (e) {
948+
expect(e.message).to.equal('Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.');
949+
}
950+
});
951+
952+
it('should flush the WS messages', async () => {
953+
await I.startRecordingWebSocketMessages();
954+
I.amOnPage('https://websocketstest.com/');
955+
I.waitForText('Work for You!');
956+
I.flushNetworkTraffics();
957+
const wsMessages = I.grabWebSocketMessages();
958+
expect(wsMessages.length).to.equal(0);
959+
});
960+
961+
it('should see recording WS messages', async () => {
962+
await I.startRecordingWebSocketMessages();
963+
await I.amOnPage('https://websocketstest.com/');
964+
I.waitForText('Work for You!');
965+
const wsMessages = I.grabWebSocketMessages();
966+
expect(wsMessages.length).to.greaterThan(0);
967+
});
968+
969+
it('should not see recording WS messages', async () => {
970+
await I.startRecordingWebSocketMessages();
971+
await I.amOnPage('https://websocketstest.com/');
972+
I.waitForText('Work for You!');
973+
const wsMessages = I.grabWebSocketMessages();
974+
await I.stopRecordingWebSocketMessages();
975+
await I.amOnPage('https://websocketstest.com/');
976+
I.waitForText('Work for You!');
977+
const afterWsMessages = I.grabWebSocketMessages();
978+
expect(wsMessages.length).to.equal(afterWsMessages.length);
979+
});
980+
});
981+
941982
describe('#makeApiRequest', () => {
942983
it('should make 3rd party API request', async () => {
943984
const response = await I.makeApiRequest('get', 'https://reqres.in/api/users?page=2');

0 commit comments

Comments
 (0)