@elastic/synthetics 1.0.0-beta.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/cli.js +139 -165
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +158 -34
- package/dist/common_types.d.ts.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -6
- package/dist/config.js.map +1 -1
- package/dist/core/browser-service.d.ts +1 -1
- package/dist/core/browser-service.d.ts.map +1 -1
- package/dist/core/browser-service.js +7 -6
- package/dist/core/browser-service.js.map +1 -1
- package/dist/core/expect.d.ts +25 -25
- package/dist/core/expect.d.ts.map +1 -1
- package/dist/core/expect.js.map +1 -1
- package/dist/core/gatherer.d.ts +5 -8
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +75 -17
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/index.d.ts +5 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +23 -11
- package/dist/core/index.js.map +1 -1
- package/dist/core/logger.d.ts +0 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +10 -17
- package/dist/core/logger.js.map +1 -1
- package/dist/core/runner.d.ts +12 -63
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +159 -92
- package/dist/core/runner.js.map +1 -1
- package/dist/core/transform.d.ts +37 -0
- package/dist/core/transform.d.ts.map +1 -0
- package/dist/core/transform.js +148 -0
- package/dist/core/transform.js.map +1 -0
- package/dist/dsl/index.js +5 -1
- package/dist/dsl/index.js.map +1 -1
- package/dist/dsl/journey.d.ts +9 -7
- package/dist/dsl/journey.d.ts.map +1 -1
- package/dist/dsl/journey.js +32 -39
- package/dist/dsl/journey.js.map +1 -1
- package/dist/dsl/monitor.d.ts +93 -0
- package/dist/dsl/monitor.d.ts.map +1 -0
- package/dist/dsl/monitor.js +110 -0
- package/dist/dsl/monitor.js.map +1 -0
- package/dist/dsl/step.d.ts +3 -2
- package/dist/dsl/step.d.ts.map +1 -1
- package/dist/dsl/step.js +6 -1
- package/dist/dsl/step.js.map +1 -1
- package/dist/formatter/javascript.d.ts +115 -0
- package/dist/formatter/javascript.d.ts.map +1 -0
- package/dist/formatter/javascript.js +331 -0
- package/dist/formatter/javascript.js.map +1 -0
- package/dist/generator/index.d.ts +24 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +241 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/{reporters/reporter.js → generator/utils.d.ts} +5 -3
- package/dist/generator/utils.d.ts.map +1 -0
- package/dist/generator/utils.js +84 -0
- package/dist/generator/utils.js.map +1 -0
- package/dist/helpers.d.ts +36 -9
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +170 -24
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -26
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +34 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +153 -0
- package/dist/loader.js.map +1 -0
- package/dist/locations/index.d.ts +45 -0
- package/dist/locations/index.d.ts.map +1 -0
- package/dist/locations/index.js +83 -0
- package/dist/locations/index.js.map +1 -0
- package/dist/{reporters/reporter.d.ts → locations/public-locations.d.ts} +12 -8
- package/dist/locations/public-locations.d.ts.map +1 -0
- package/dist/locations/public-locations.js +41 -0
- package/dist/locations/public-locations.js.map +1 -0
- package/dist/options.d.ts +42 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +146 -0
- package/dist/options.js.map +1 -0
- package/dist/plugins/browser-console.d.ts +5 -4
- package/dist/plugins/browser-console.d.ts.map +1 -1
- package/dist/plugins/browser-console.js +47 -24
- package/dist/plugins/browser-console.js.map +1 -1
- package/dist/plugins/index.js +5 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/network.d.ts +24 -31
- package/dist/plugins/network.d.ts.map +1 -1
- package/dist/plugins/network.js +226 -229
- package/dist/plugins/network.js.map +1 -1
- package/dist/plugins/performance.d.ts +4 -19
- package/dist/plugins/performance.d.ts.map +1 -1
- package/dist/plugins/performance.js +6 -5
- package/dist/plugins/performance.js.map +1 -1
- package/dist/plugins/plugin-manager.d.ts +9 -5
- package/dist/plugins/plugin-manager.d.ts.map +1 -1
- package/dist/plugins/plugin-manager.js +46 -30
- package/dist/plugins/plugin-manager.js.map +1 -1
- package/dist/plugins/tracing.d.ts +6 -6
- package/dist/plugins/tracing.d.ts.map +1 -1
- package/dist/plugins/tracing.js +13 -7
- package/dist/plugins/tracing.js.map +1 -1
- package/dist/push/bundler.d.ts +35 -0
- package/dist/push/bundler.d.ts.map +1 -0
- package/dist/push/bundler.js +131 -0
- package/dist/push/bundler.js.map +1 -0
- package/dist/push/index.d.ts +10 -0
- package/dist/push/index.d.ts.map +1 -0
- package/dist/push/index.js +235 -0
- package/dist/push/index.js.map +1 -0
- package/dist/push/kibana_api.d.ts +56 -0
- package/dist/push/kibana_api.d.ts.map +1 -0
- package/dist/push/kibana_api.js +135 -0
- package/dist/push/kibana_api.js.map +1 -0
- package/dist/push/monitor.d.ts +55 -0
- package/dist/push/monitor.d.ts.map +1 -0
- package/dist/push/monitor.js +268 -0
- package/dist/push/monitor.js.map +1 -0
- package/dist/{parse_args.d.ts → push/plugin.d.ts} +9 -6
- package/dist/push/plugin.d.ts.map +1 -0
- package/dist/push/plugin.js +66 -0
- package/dist/push/plugin.js.map +1 -0
- package/dist/push/request.d.ts +45 -0
- package/dist/push/request.d.ts.map +1 -0
- package/dist/push/request.js +109 -0
- package/dist/push/request.js.map +1 -0
- package/dist/push/utils.d.ts +31 -0
- package/dist/push/utils.d.ts.map +1 -0
- package/dist/push/utils.js +64 -0
- package/dist/push/utils.js.map +1 -0
- package/dist/reporters/base.d.ts +14 -6
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js +58 -50
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/index.d.ts +18 -8
- package/dist/reporters/index.d.ts.map +1 -1
- package/dist/reporters/index.js.map +1 -1
- package/dist/reporters/json.d.ts +17 -62
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +147 -177
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/junit.d.ts +7 -1
- package/dist/reporters/junit.d.ts.map +1 -1
- package/dist/reporters/junit.js +90 -95
- package/dist/reporters/junit.js.map +1 -1
- package/dist/sdk/lh-trace-processor.d.ts +35 -9
- package/dist/sdk/lh-trace-processor.d.ts.map +1 -1
- package/dist/sdk/lh-trace-processor.js +114 -53
- package/dist/sdk/lh-trace-processor.js.map +1 -1
- package/dist/sdk/trace-metrics.d.ts +4 -4
- package/dist/sdk/trace-metrics.d.ts.map +1 -1
- package/dist/sdk/trace-metrics.js +3 -4
- package/dist/sdk/trace-metrics.js.map +1 -1
- package/dist/sdk/trace-processor.d.ts +11 -3
- package/dist/sdk/trace-processor.d.ts.map +1 -1
- package/dist/sdk/trace-processor.js +17 -9
- package/dist/sdk/trace-processor.js.map +1 -1
- package/package.json +38 -30
- package/src/cli.ts +223 -158
- package/src/common_types.ts +191 -34
- package/src/config.ts +1 -1
- package/src/core/expect.ts +58 -24
- package/src/core/gatherer.ts +92 -25
- package/src/core/index.ts +36 -15
- package/src/core/logger.ts +8 -12
- package/src/core/runner.ts +158 -148
- package/src/core/transform.ts +160 -0
- package/src/dsl/journey.ts +41 -21
- package/src/dsl/monitor.ts +152 -0
- package/src/dsl/step.ts +9 -2
- package/src/formatter/javascript.ts +414 -0
- package/src/generator/index.ts +308 -0
- package/src/generator/utils.ts +84 -0
- package/src/helpers.ts +182 -16
- package/src/index.ts +21 -22
- package/src/loader.ts +171 -0
- package/src/locations/index.ts +95 -0
- package/src/{reporters/reporter.ts → locations/public-locations.ts} +12 -9
- package/src/options.ts +175 -0
- package/src/plugins/browser-console.ts +33 -8
- package/src/plugins/network.ts +266 -284
- package/src/plugins/performance.ts +6 -22
- package/src/plugins/plugin-manager.ts +49 -31
- package/src/plugins/tracing.ts +11 -9
- package/src/push/bundler.ts +112 -0
- package/src/push/index.ts +291 -0
- package/src/push/kibana_api.ts +191 -0
- package/src/push/monitor.ts +307 -0
- package/src/push/plugin.ts +66 -0
- package/src/push/request.ts +141 -0
- package/src/push/utils.ts +77 -0
- package/src/reporters/base.ts +63 -52
- package/src/reporters/index.ts +26 -3
- package/src/reporters/json.ts +181 -201
- package/src/reporters/junit.ts +98 -93
- package/src/sdk/lh-trace-processor.ts +132 -68
- package/src/sdk/trace-metrics.ts +8 -4
- package/src/sdk/trace-processor.ts +28 -13
- package/templates/.github/workflows/run-synthetics.yml +27 -0
- package/templates/README.md +21 -0
- package/templates/journeys/advanced-example-helpers.ts +54 -0
- package/templates/journeys/advanced-example.journey.ts +43 -0
- package/templates/journeys/example.journey.ts +18 -0
- package/templates/lightweight/heartbeat.yml +9 -0
- package/templates/synthetics.config.ts +35 -0
- package/CHANGELOG.md +0 -181
- package/NOTICE.txt +0 -221
- package/dist/parse_args.d.ts.map +0 -1
- package/dist/parse_args.js +0 -98
- package/dist/parse_args.js.map +0 -1
- package/dist/reporters/reporter.d.ts.map +0 -1
- package/dist/reporters/reporter.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1895
- package/src/parse_args.ts +0 -146
package/src/plugins/network.ts
CHANGED
|
@@ -23,343 +23,325 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
26
|
+
import { Page, Request, Response } from 'playwright-chromium';
|
|
27
|
+
import { NetworkInfo, BrowserInfo, Driver } from '../common_types';
|
|
28
|
+
import { log } from '../core/logger';
|
|
29
29
|
import { Step } from '../dsl';
|
|
30
30
|
import { getTimestamp } from '../helpers';
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Kibana UI expects the requestStartTime and loadEndTime to be baseline
|
|
34
|
+
* in seconds as they have the logic to convert it to milliseconds before
|
|
35
|
+
* using for offset calculation
|
|
36
|
+
*/
|
|
37
|
+
function epochTimeInSeconds() {
|
|
38
|
+
return getTimestamp() / 1e6;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Find the first positive number in an array for Resource timing data
|
|
43
|
+
*/
|
|
44
|
+
const firstPositive = (numbers: number[]) => {
|
|
45
|
+
for (let i = 0; i < numbers.length; ++i) {
|
|
46
|
+
if (numbers[i] > 0) {
|
|
47
|
+
return numbers[i];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const roundMilliSecs = (value: number): number => {
|
|
54
|
+
return Math.floor(value * 1000) / 1000;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Used as a key in each Network Request to identify the
|
|
59
|
+
* associated request across distinct lifecycle events
|
|
60
|
+
*/
|
|
61
|
+
export const NETWORK_ENTRY_SUMBOL = Symbol.for('NetworkEntry');
|
|
62
|
+
|
|
63
|
+
type RequestWithEntry = Request & {
|
|
64
|
+
NETWORK_ENTRY_SUMBOL?: symbol;
|
|
65
|
+
};
|
|
66
|
+
|
|
32
67
|
export class NetworkManager {
|
|
33
68
|
private _browser: BrowserInfo;
|
|
34
|
-
private
|
|
69
|
+
private _barrierPromises = new Set<Promise<void>>();
|
|
35
70
|
results: Array<NetworkInfo> = [];
|
|
36
71
|
_currentStep: Partial<Step> = null;
|
|
37
72
|
|
|
38
|
-
|
|
73
|
+
constructor(private driver: Driver) {}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Adds a protection barrier aganist all asynchronous extract operations from
|
|
77
|
+
* request/response object that are happening during page lifecycle, If the
|
|
78
|
+
* page is closed during the extraction, the barrier enforces those operations
|
|
79
|
+
* to not result in exception
|
|
80
|
+
*/
|
|
81
|
+
private _addBarrier(page: Page, promise: Promise<void>) {
|
|
82
|
+
if (!page) return;
|
|
83
|
+
const race = Promise.race([
|
|
84
|
+
new Promise<void>(resolve =>
|
|
85
|
+
page.on('close', () => {
|
|
86
|
+
this._barrierPromises.delete(race);
|
|
87
|
+
resolve();
|
|
88
|
+
})
|
|
89
|
+
),
|
|
90
|
+
promise,
|
|
91
|
+
]);
|
|
92
|
+
this._barrierPromises.add(race);
|
|
93
|
+
race.then(() => this._barrierPromises.delete(race));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async start() {
|
|
97
|
+
log(`Plugins: started collecting network events`);
|
|
98
|
+
const { client, context } = this.driver;
|
|
39
99
|
const { product } = await client.send('Browser.getVersion');
|
|
40
100
|
const [name, version] = product.split('/');
|
|
41
101
|
this._browser = { name, version };
|
|
42
|
-
await client.send('Network.enable');
|
|
43
102
|
/**
|
|
44
|
-
* Listen for all network events
|
|
103
|
+
* Listen for all network events from PW context
|
|
45
104
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
client.on(
|
|
51
|
-
'Network.requestWillBeSentExtraInfo',
|
|
52
|
-
this._onRequestWillBeSentExtraInfo.bind(this)
|
|
53
|
-
);
|
|
54
|
-
client.on('Network.dataReceived', this._onDataReceived.bind(this));
|
|
55
|
-
client.on('Network.responseReceived', this._onResponseReceived.bind(this));
|
|
56
|
-
client.on('Network.loadingFinished', this._onLoadingFinished.bind(this));
|
|
57
|
-
client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
|
|
105
|
+
context.on('request', this._onRequest.bind(this));
|
|
106
|
+
context.on('response', this._onResponse.bind(this));
|
|
107
|
+
context.on('requestfinished', this._onRequestCompleted.bind(this));
|
|
108
|
+
context.on('requestfailed', this._onRequestCompleted.bind(this));
|
|
58
109
|
}
|
|
59
110
|
|
|
60
|
-
|
|
61
|
-
|
|
111
|
+
private _findNetworkEntry(
|
|
112
|
+
request: RequestWithEntry
|
|
113
|
+
): NetworkInfo | undefined {
|
|
114
|
+
return request[NETWORK_ENTRY_SUMBOL];
|
|
62
115
|
}
|
|
63
116
|
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const { url, method } = request;
|
|
117
|
+
private _onRequest(request: Request) {
|
|
118
|
+
const url = request.url();
|
|
67
119
|
/**
|
|
68
120
|
* Data URI should not show up as network requests
|
|
69
121
|
*/
|
|
70
122
|
if (url.startsWith('data:')) {
|
|
71
123
|
return;
|
|
72
124
|
}
|
|
73
|
-
const isNavigationRequest = requestId == loaderId && type === 'Document';
|
|
74
|
-
const record = this._findNetworkRecord(requestId);
|
|
75
|
-
/**
|
|
76
|
-
* On redirects, another `requestWillBeSent` event will be fired for the
|
|
77
|
-
* same requestId. We calculate the timings for the redirect request using
|
|
78
|
-
* the `redirectedResponse` from the redirected request.
|
|
79
|
-
*/
|
|
80
|
-
if (record) {
|
|
81
|
-
if (event.redirectResponse) {
|
|
82
|
-
const response = event.redirectResponse;
|
|
83
|
-
const data = Object.assign(event, {
|
|
84
|
-
type: event.type,
|
|
85
|
-
response,
|
|
86
|
-
encodedDataLength: response.encodedDataLength,
|
|
87
|
-
});
|
|
88
|
-
this._onResponseReceived(data);
|
|
89
|
-
this._onLoadingFinished(data);
|
|
90
|
-
} else {
|
|
91
|
-
/**
|
|
92
|
-
* Edge case, we handle it to proceed with next navigation
|
|
93
|
-
*/
|
|
94
|
-
this._onLoadingFailed(
|
|
95
|
-
Object.assign(event, {
|
|
96
|
-
type: event.type,
|
|
97
|
-
errorText: 'redirectResponse data is not available',
|
|
98
|
-
})
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
const redirectedRecord = this._findNetworkRecord(requestId);
|
|
102
|
-
/**
|
|
103
|
-
* Rewrite the map with new redirect id to not reset
|
|
104
|
-
* the redirect request with original request
|
|
105
|
-
*/
|
|
106
|
-
this._InflightRequestMap.delete(requestId);
|
|
107
|
-
this._InflightRequestMap.set(
|
|
108
|
-
`redirect:${timestamp}:${requestId}`,
|
|
109
|
-
redirectedRecord
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
125
|
|
|
113
|
-
|
|
126
|
+
const timestamp = getTimestamp();
|
|
127
|
+
const networkEntry: NetworkInfo = {
|
|
114
128
|
browser: this._browser,
|
|
115
129
|
step: this._currentStep,
|
|
116
|
-
timestamp
|
|
130
|
+
timestamp,
|
|
117
131
|
url,
|
|
118
|
-
request,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
type: request.resourceType(),
|
|
133
|
+
request: {
|
|
134
|
+
url,
|
|
135
|
+
method: request.method(),
|
|
136
|
+
headers: request.headers(),
|
|
137
|
+
body: {
|
|
138
|
+
bytes: request.postDataBuffer()?.length || 0,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
response: {
|
|
142
|
+
status: -1,
|
|
143
|
+
headers: {},
|
|
144
|
+
mimeType: 'x-unknown',
|
|
145
|
+
redirectURL: '',
|
|
146
|
+
},
|
|
147
|
+
isNavigationRequest: request.isNavigationRequest(),
|
|
148
|
+
requestSentTime: epochTimeInSeconds(),
|
|
124
149
|
loadEndTime: -1,
|
|
125
150
|
responseReceivedTime: -1,
|
|
126
|
-
response: null,
|
|
127
151
|
resourceSize: 0,
|
|
128
152
|
transferSize: 0,
|
|
129
|
-
timings:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Enhance request headers with additional information
|
|
143
|
-
*/
|
|
144
|
-
record.request.headers = {
|
|
145
|
-
...record.request.headers,
|
|
146
|
-
...headers,
|
|
153
|
+
timings: {
|
|
154
|
+
blocked: -1,
|
|
155
|
+
dns: -1,
|
|
156
|
+
ssl: -1,
|
|
157
|
+
connect: -1,
|
|
158
|
+
send: -1,
|
|
159
|
+
wait: -1,
|
|
160
|
+
receive: -1,
|
|
161
|
+
total: -1,
|
|
162
|
+
},
|
|
147
163
|
};
|
|
148
|
-
}
|
|
149
164
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (!record) {
|
|
154
|
-
return;
|
|
165
|
+
if (request.redirectedFrom()) {
|
|
166
|
+
const fromEntry = this._findNetworkEntry(request.redirectedFrom());
|
|
167
|
+
if (fromEntry) fromEntry.response.redirectURL = request.url();
|
|
155
168
|
}
|
|
156
|
-
record.resourceSize += dataLength;
|
|
157
|
-
if (encodedDataLength >= 0) record.transferSize += encodedDataLength;
|
|
158
|
-
}
|
|
159
169
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const record = this._findNetworkRecord(requestId);
|
|
163
|
-
if (!record) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
Object.assign(record, {
|
|
167
|
-
status: response.status,
|
|
168
|
-
response,
|
|
169
|
-
responseReceivedTime: timestamp,
|
|
170
|
-
transferSize: response.encodedDataLength,
|
|
171
|
-
});
|
|
170
|
+
request[NETWORK_ENTRY_SUMBOL] = networkEntry;
|
|
171
|
+
this.results.push(networkEntry);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
this.
|
|
177
|
-
|
|
174
|
+
private async _onResponse(response: Response) {
|
|
175
|
+
const request = response.request();
|
|
176
|
+
const networkEntry = this._findNetworkEntry(request);
|
|
177
|
+
if (!networkEntry) return;
|
|
178
|
+
|
|
179
|
+
networkEntry.responseReceivedTime = epochTimeInSeconds();
|
|
180
|
+
networkEntry.response = {
|
|
181
|
+
url: response.url(),
|
|
182
|
+
status: response.status(),
|
|
183
|
+
statusText: response.statusText(),
|
|
184
|
+
headers: {},
|
|
185
|
+
mimeType: 'x-unknown',
|
|
186
|
+
redirectURL: networkEntry.response.redirectURL,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Gather all resource timing information up until the
|
|
190
|
+
// TTFB(Time to first byte) is received
|
|
191
|
+
const timing = request.timing();
|
|
192
|
+
const blocked =
|
|
193
|
+
roundMilliSecs(
|
|
194
|
+
firstPositive([
|
|
195
|
+
timing.domainLookupStart,
|
|
196
|
+
timing.connectStart,
|
|
197
|
+
timing.requestStart,
|
|
198
|
+
])
|
|
199
|
+
) || -1;
|
|
200
|
+
const dns =
|
|
201
|
+
timing.domainLookupEnd !== -1
|
|
202
|
+
? roundMilliSecs(timing.domainLookupEnd - timing.domainLookupStart)
|
|
203
|
+
: -1;
|
|
204
|
+
const connect =
|
|
205
|
+
timing.connectEnd !== -1
|
|
206
|
+
? roundMilliSecs(timing.connectEnd - timing.connectStart)
|
|
207
|
+
: -1;
|
|
208
|
+
const ssl =
|
|
209
|
+
timing.secureConnectionStart !== -1
|
|
210
|
+
? roundMilliSecs(timing.connectEnd - timing.secureConnectionStart)
|
|
211
|
+
: -1;
|
|
212
|
+
const wait =
|
|
213
|
+
timing.responseStart !== -1
|
|
214
|
+
? roundMilliSecs(timing.responseStart - timing.requestStart)
|
|
215
|
+
: -1;
|
|
178
216
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
217
|
+
networkEntry.timings = {
|
|
218
|
+
blocked,
|
|
219
|
+
dns,
|
|
220
|
+
ssl,
|
|
221
|
+
connect,
|
|
222
|
+
send: 0, // not exposed via RT api
|
|
223
|
+
wait,
|
|
224
|
+
receive: -1, // will be available after full response is received
|
|
225
|
+
total: -1,
|
|
226
|
+
};
|
|
227
|
+
this._calcTotalTime(networkEntry, timing);
|
|
228
|
+
|
|
229
|
+
const page = request.frame().page();
|
|
230
|
+
this._addBarrier(
|
|
231
|
+
page,
|
|
232
|
+
request.allHeaders().then(reqHeaders => {
|
|
233
|
+
networkEntry.request.headers = reqHeaders;
|
|
234
|
+
networkEntry.request.referrer = reqHeaders?.referer;
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
this._addBarrier(
|
|
238
|
+
page,
|
|
239
|
+
response.allHeaders().then(resHeaders => {
|
|
240
|
+
networkEntry.response.headers = resHeaders;
|
|
241
|
+
|
|
242
|
+
const contentType = resHeaders['content-type'];
|
|
243
|
+
if (contentType)
|
|
244
|
+
networkEntry.response.mimeType = contentType.split(';')[0];
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
this._addBarrier(
|
|
248
|
+
page,
|
|
249
|
+
response.serverAddr().then(server => {
|
|
250
|
+
networkEntry.response.remoteIPAddress = server?.ipAddress;
|
|
251
|
+
networkEntry.response.remotePort = server?.port;
|
|
252
|
+
})
|
|
253
|
+
);
|
|
254
|
+
this._addBarrier(
|
|
255
|
+
page,
|
|
256
|
+
response.securityDetails().then(details => {
|
|
257
|
+
if (details) networkEntry.response.securityDetails = details;
|
|
258
|
+
})
|
|
259
|
+
);
|
|
182
260
|
}
|
|
183
261
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
262
|
+
private async _onRequestCompleted(request: Request) {
|
|
263
|
+
const networkEntry = this._findNetworkEntry(request);
|
|
264
|
+
if (!networkEntry) return;
|
|
265
|
+
|
|
266
|
+
networkEntry.loadEndTime = epochTimeInSeconds();
|
|
267
|
+
// responseEnd is fired after the last byte of the response is received.
|
|
268
|
+
const timing = request.timing();
|
|
269
|
+
const receive =
|
|
270
|
+
timing.responseEnd !== -1
|
|
271
|
+
? roundMilliSecs(timing.responseEnd - timing.responseStart)
|
|
272
|
+
: -1;
|
|
273
|
+
networkEntry.timings.receive = receive;
|
|
274
|
+
this._calcTotalTime(networkEntry, timing);
|
|
275
|
+
|
|
276
|
+
// For aborted/failed requests sizes will not be present
|
|
277
|
+
if (timing.startTime <= 0) {
|
|
191
278
|
return;
|
|
192
279
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
280
|
+
|
|
281
|
+
const page = request.frame().page();
|
|
282
|
+
this._addBarrier(
|
|
283
|
+
page,
|
|
284
|
+
request.sizes().then(sizes => {
|
|
285
|
+
networkEntry.request.bytes =
|
|
286
|
+
sizes.requestHeadersSize + sizes.requestBodySize;
|
|
287
|
+
networkEntry.request.body = {
|
|
288
|
+
bytes: sizes.requestBodySize,
|
|
289
|
+
};
|
|
290
|
+
const transferSize = sizes.responseHeadersSize + sizes.responseBodySize;
|
|
291
|
+
networkEntry.transferSize = transferSize;
|
|
292
|
+
networkEntry.response.bytes = transferSize;
|
|
293
|
+
networkEntry.response.body = {
|
|
294
|
+
bytes: sizes.responseBodySize,
|
|
295
|
+
};
|
|
296
|
+
})
|
|
297
|
+
);
|
|
204
298
|
}
|
|
205
299
|
|
|
206
300
|
/**
|
|
207
|
-
*
|
|
208
|
-
*
|
|
301
|
+
* Calculates the total time for the network request based on the ResourceTiming
|
|
302
|
+
* data from Playwright. Fallbacks to the event timings if ResourceTiming data
|
|
303
|
+
* is not available.
|
|
209
304
|
*/
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
305
|
+
private _calcTotalTime(
|
|
306
|
+
entry: NetworkInfo,
|
|
307
|
+
rtiming: ReturnType<Request['timing']>
|
|
213
308
|
) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
309
|
+
const timings = entry.timings;
|
|
310
|
+
entry.timings.total = [
|
|
311
|
+
timings.blocked,
|
|
312
|
+
timings.dns,
|
|
313
|
+
timings.connect,
|
|
314
|
+
timings.wait,
|
|
315
|
+
timings.receive,
|
|
316
|
+
].reduce((pre, cur) => ((cur || -1) > 0 ? cur + pre : pre), 0);
|
|
317
|
+
|
|
318
|
+
// fallback when ResourceTiming data is not available
|
|
319
|
+
if (rtiming.startTime <= 0) {
|
|
320
|
+
const end =
|
|
321
|
+
entry.loadEndTime ||
|
|
322
|
+
entry.responseReceivedTime ||
|
|
323
|
+
entry.requestSentTime;
|
|
324
|
+
const total = roundMilliSecs((end - entry.requestSentTime) * 1000);
|
|
325
|
+
entry.timings.total = total <= 0 ? -1 : total;
|
|
227
326
|
}
|
|
228
|
-
return responseReceivedTime;
|
|
229
327
|
}
|
|
230
328
|
|
|
231
|
-
|
|
232
|
-
* The timing calculations are based on the chrome devtools frontend
|
|
233
|
-
* https://github.com/ChromeDevTools/devtools-frontend/blob/7f5478d8ceb7586f23f4073ab5c2085dac1ec26a/front_end/network/RequestTimingView.js#L98-L193
|
|
234
|
-
*/
|
|
235
|
-
calculateTimings(record: NetworkInfo) {
|
|
236
|
-
const result: NetworkInfo['timings'] = {
|
|
237
|
-
blocked: -1,
|
|
238
|
-
queueing: -1,
|
|
239
|
-
proxy: -1,
|
|
240
|
-
dns: -1,
|
|
241
|
-
ssl: -1,
|
|
242
|
-
connect: -1,
|
|
243
|
-
send: -1,
|
|
244
|
-
wait: -1,
|
|
245
|
-
receive: -1,
|
|
246
|
-
total: -1,
|
|
247
|
-
};
|
|
248
|
-
const { requestSentTime, loadEndTime, responseReceivedTime } = record;
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Handle when request failed and no responseReceived event was
|
|
252
|
-
* fired for that particular request
|
|
253
|
-
* Eg: connection refused by remote host
|
|
254
|
-
*/
|
|
255
|
-
const response = record.response || {
|
|
256
|
-
timing: null,
|
|
257
|
-
fromServiceWorker: false,
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const toMilliseconds = (time: number) => (time === -1 ? -1 : time * 1000);
|
|
261
|
-
const calculateDiffInMs = (
|
|
262
|
-
name: keyof NetworkInfo['timings'],
|
|
263
|
-
start: number,
|
|
264
|
-
end: number
|
|
265
|
-
) => {
|
|
266
|
-
if (start < Number.MAX_VALUE && start <= end) {
|
|
267
|
-
result[name] = toMilliseconds(end - start);
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
const firstPositive = (numbers: number[]) => {
|
|
271
|
-
for (let i = 0; i < numbers.length; ++i) {
|
|
272
|
-
if (numbers[i] > 0) {
|
|
273
|
-
return numbers[i];
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return null;
|
|
277
|
-
};
|
|
329
|
+
async stop() {
|
|
278
330
|
/**
|
|
279
|
-
*
|
|
280
|
-
* from
|
|
331
|
+
* Waiting for all network events is error prone and might hang the tests
|
|
332
|
+
* from getting closed forever when there are upstream bugs in browsers or
|
|
333
|
+
* Playwright. So we log and drop these events once the test run is completed
|
|
281
334
|
*/
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
start: number,
|
|
285
|
-
end: number
|
|
286
|
-
) => {
|
|
287
|
-
if (start >= 0 && end >= 0) {
|
|
288
|
-
calculateDiffInMs(
|
|
289
|
-
name,
|
|
290
|
-
startTime + start / 1000,
|
|
291
|
-
startTime + end / 1000
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
const timing = response.timing;
|
|
296
|
-
const actResRcvdTime = this.getResponseReceivedTime(
|
|
297
|
-
timing,
|
|
298
|
-
responseReceivedTime
|
|
299
|
-
);
|
|
300
|
-
const issueTime = requestSentTime;
|
|
301
|
-
const startTime = timing == null ? -1 : timing.requestTime;
|
|
302
|
-
const endTime = firstPositive([loadEndTime, actResRcvdTime]) || startTime;
|
|
303
|
-
|
|
304
|
-
if (timing == null) {
|
|
305
|
-
const start =
|
|
306
|
-
issueTime !== -1 ? issueTime : startTime !== -1 ? startTime : 0;
|
|
307
|
-
const middle = actResRcvdTime === -1 ? Number.MAX_VALUE : actResRcvdTime;
|
|
308
|
-
const end = endTime === -1 ? Number.MAX_VALUE : endTime;
|
|
309
|
-
calculateDiffInMs('total', start, end);
|
|
310
|
-
calculateDiffInMs('blocked', start, middle);
|
|
311
|
-
calculateDiffInMs('receive', middle, end);
|
|
312
|
-
// Check if the request is blocked/stalled for the whole timeframe and
|
|
313
|
-
// calculate timings appropriately for that request
|
|
314
|
-
if (!isFinite(result['blocked'])) {
|
|
315
|
-
result['blocked'] = result['total'];
|
|
316
|
-
}
|
|
317
|
-
return result;
|
|
318
|
-
}
|
|
319
|
-
calculateDiffInMs(
|
|
320
|
-
'total',
|
|
321
|
-
issueTime < startTime ? issueTime : startTime,
|
|
322
|
-
endTime
|
|
323
|
-
);
|
|
324
|
-
if (issueTime < startTime) {
|
|
325
|
-
calculateDiffInMs('queueing', issueTime, startTime);
|
|
326
|
-
}
|
|
327
|
-
const responseReceived = toMilliseconds(actResRcvdTime - startTime);
|
|
328
|
-
|
|
329
|
-
if (response.fromServiceWorker) {
|
|
330
|
-
addOffsetRange('blocked', 0, timing.workerStart);
|
|
331
|
-
addOffsetRange('wait', timing.sendEnd, responseReceived);
|
|
332
|
-
} else if (!timing.pushStart) {
|
|
333
|
-
const blockingEnd =
|
|
334
|
-
firstPositive([
|
|
335
|
-
timing.dnsStart,
|
|
336
|
-
timing.connectStart,
|
|
337
|
-
timing.sendStart,
|
|
338
|
-
responseReceived,
|
|
339
|
-
]) || 0;
|
|
340
|
-
addOffsetRange('blocked', 0, blockingEnd);
|
|
341
|
-
addOffsetRange('proxy', timing.proxyStart, timing.proxyEnd);
|
|
342
|
-
addOffsetRange('dns', timing.dnsStart, timing.dnsEnd);
|
|
343
|
-
addOffsetRange('connect', timing.connectStart, timing.connectEnd);
|
|
344
|
-
addOffsetRange('ssl', timing.sslStart, timing.sslEnd);
|
|
345
|
-
addOffsetRange('send', timing.sendStart, timing.sendEnd);
|
|
346
|
-
addOffsetRange(
|
|
347
|
-
'wait',
|
|
348
|
-
Math.max(
|
|
349
|
-
timing.sendEnd,
|
|
350
|
-
timing.connectEnd,
|
|
351
|
-
timing.dnsEnd,
|
|
352
|
-
timing.proxyEnd,
|
|
353
|
-
blockingEnd
|
|
354
|
-
),
|
|
355
|
-
responseReceived
|
|
356
|
-
);
|
|
335
|
+
if (this._barrierPromises.size > 0) {
|
|
336
|
+
log(`Plugins: dropping ${this._barrierPromises.size} network events`);
|
|
357
337
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
338
|
+
const context = this.driver.context;
|
|
339
|
+
context.off('request', this._onRequest.bind(this));
|
|
340
|
+
context.off('response', this._onResponse.bind(this));
|
|
341
|
+
context.off('requestfinished', this._onRequestCompleted.bind(this));
|
|
342
|
+
context.off('requestfailed', this._onRequestCompleted.bind(this));
|
|
343
|
+
this._barrierPromises.clear();
|
|
344
|
+
log(`Plugins: stopped collecting network events`);
|
|
363
345
|
return this.results;
|
|
364
346
|
}
|
|
365
347
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import { Driver, PageMetrics } from '../common_types';
|
|
27
27
|
|
|
28
28
|
const supportedMetrics = new Set<string>([
|
|
29
29
|
'Timestamp',
|
|
@@ -41,39 +41,23 @@ const supportedMetrics = new Set<string>([
|
|
|
41
41
|
'JSHeapTotalSize',
|
|
42
42
|
]);
|
|
43
43
|
|
|
44
|
-
export interface Metrics {
|
|
45
|
-
Timestamp?: number;
|
|
46
|
-
Documents?: number;
|
|
47
|
-
Frames?: number;
|
|
48
|
-
JSEventListeners?: number;
|
|
49
|
-
Nodes?: number;
|
|
50
|
-
LayoutCount?: number;
|
|
51
|
-
RecalcStyleCount?: number;
|
|
52
|
-
LayoutDuration?: number;
|
|
53
|
-
RecalcStyleDuration?: number;
|
|
54
|
-
ScriptDuration?: number;
|
|
55
|
-
TaskDuration?: number;
|
|
56
|
-
JSHeapUsedSize?: number;
|
|
57
|
-
JSHeapTotalSize?: number;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
44
|
export class PerformanceManager {
|
|
61
|
-
constructor(private
|
|
45
|
+
constructor(private driver: Driver) {}
|
|
62
46
|
|
|
63
47
|
async start() {
|
|
64
|
-
await this.client.send('Performance.enable');
|
|
48
|
+
await this.driver.client.send('Performance.enable');
|
|
65
49
|
}
|
|
66
50
|
|
|
67
51
|
async stop() {
|
|
68
|
-
await this.client.send('Performance.disable');
|
|
52
|
+
await this.driver.client.send('Performance.disable');
|
|
69
53
|
}
|
|
70
54
|
|
|
71
55
|
async getMetrics() {
|
|
72
|
-
const { metrics } = await this.client.send('Performance.getMetrics');
|
|
56
|
+
const { metrics } = await this.driver.client.send('Performance.getMetrics');
|
|
73
57
|
return this.buildMetricsObject(metrics);
|
|
74
58
|
}
|
|
75
59
|
|
|
76
|
-
private buildMetricsObject(metrics = []):
|
|
60
|
+
private buildMetricsObject(metrics = []): PageMetrics {
|
|
77
61
|
const result = {};
|
|
78
62
|
for (const metric of metrics) {
|
|
79
63
|
if (supportedMetrics.has(metric.name)) result[metric.name] = metric.value;
|