@elastic/synthetics 1.0.0-beta.15 → 1.0.0-beta.19
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/CHANGELOG.md +55 -0
- package/NOTICE.txt +29 -0
- package/README.md +1 -1
- package/dist/cli.js +34 -21
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +10 -40
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +9 -1
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/logger.d.ts +0 -2
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +2 -15
- package/dist/core/logger.js.map +1 -1
- package/dist/core/runner.d.ts +19 -20
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +14 -5
- package/dist/core/runner.js.map +1 -1
- package/dist/formatter/javascript.d.ts +2 -0
- package/dist/formatter/javascript.d.ts.map +1 -1
- package/dist/formatter/javascript.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +1 -1
- package/dist/parse_args.d.ts.map +1 -1
- package/dist/parse_args.js +1 -1
- package/dist/parse_args.js.map +1 -1
- package/dist/plugins/network.d.ts +30 -11
- package/dist/plugins/network.d.ts.map +1 -1
- package/dist/plugins/network.js +234 -175
- package/dist/plugins/network.js.map +1 -1
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js +6 -1
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/json.d.ts +55 -5
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +45 -14
- package/dist/reporters/json.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/cli.ts +37 -25
- package/src/common_types.ts +10 -46
- package/src/core/gatherer.ts +8 -1
- package/src/core/logger.ts +1 -12
- package/src/core/runner.ts +42 -28
- package/src/formatter/javascript.ts +2 -0
- package/src/index.ts +1 -6
- package/src/parse_args.ts +1 -5
- package/src/plugins/network.ts +280 -213
- package/src/reporters/base.ts +6 -1
- package/src/reporters/json.ts +52 -16
package/src/plugins/network.ts
CHANGED
|
@@ -23,269 +23,336 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import { Protocol } from 'playwright-chromium/types/protocol';
|
|
27
27
|
import { NetworkInfo, BrowserInfo, Driver } from '../common_types';
|
|
28
28
|
import { Step } from '../dsl';
|
|
29
29
|
import { getTimestamp } from '../helpers';
|
|
30
30
|
|
|
31
|
-
/**
|
|
32
|
-
* Kibana UI expects the requestStartTime and loadEndTime to be baseline
|
|
33
|
-
* in seconds as they have the logic to convert it to milliseconds before
|
|
34
|
-
* using for offset calculation
|
|
35
|
-
*/
|
|
36
|
-
function epochTimeInSeconds() {
|
|
37
|
-
return getTimestamp() / 1e6;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Used as a key in each Network Request to identify the
|
|
42
|
-
* associated request across distinct lifecycle events
|
|
43
|
-
*/
|
|
44
|
-
export const NETWORK_ENTRY_SUMBOL = Symbol.for('NetworkEntry');
|
|
45
|
-
|
|
46
|
-
type RequestWithEntry = Request & {
|
|
47
|
-
NETWORK_ENTRY_SUMBOL?: symbol;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
31
|
export class NetworkManager {
|
|
51
32
|
private _browser: BrowserInfo;
|
|
52
|
-
private
|
|
33
|
+
private _InflightRequestMap = new Map<string, NetworkInfo>();
|
|
53
34
|
results: Array<NetworkInfo> = [];
|
|
54
35
|
_currentStep: Partial<Step> = null;
|
|
55
36
|
|
|
56
37
|
constructor(private driver: Driver) {}
|
|
57
38
|
|
|
58
|
-
private _addBarrier(page: Page, promise: Promise<void>) {
|
|
59
|
-
const race = Promise.race([
|
|
60
|
-
new Promise<void>(resolve =>
|
|
61
|
-
page.on('close', () => {
|
|
62
|
-
this._barrierPromises.delete(race);
|
|
63
|
-
resolve();
|
|
64
|
-
})
|
|
65
|
-
),
|
|
66
|
-
promise,
|
|
67
|
-
]) as Promise<void>;
|
|
68
|
-
this._barrierPromises.add(race);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
39
|
async start() {
|
|
72
|
-
const { client
|
|
40
|
+
const { client } = this.driver;
|
|
73
41
|
const { product } = await client.send('Browser.getVersion');
|
|
74
42
|
const [name, version] = product.split('/');
|
|
75
43
|
this._browser = { name, version };
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
44
|
+
await client.send('Network.enable');
|
|
45
|
+
/**
|
|
46
|
+
* Listen for all network events
|
|
47
|
+
*/
|
|
48
|
+
client.on(
|
|
49
|
+
'Network.requestWillBeSent',
|
|
50
|
+
this._onRequestWillBeSent.bind(this)
|
|
51
|
+
);
|
|
52
|
+
client.on(
|
|
53
|
+
'Network.requestWillBeSentExtraInfo',
|
|
54
|
+
this._onRequestWillBeSentExtraInfo.bind(this)
|
|
55
|
+
);
|
|
56
|
+
client.on('Network.dataReceived', this._onDataReceived.bind(this));
|
|
57
|
+
client.on('Network.responseReceived', this._onResponseReceived.bind(this));
|
|
58
|
+
client.on('Network.loadingFinished', this._onLoadingFinished.bind(this));
|
|
59
|
+
client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
|
|
80
60
|
}
|
|
81
61
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
): NetworkInfo | undefined {
|
|
85
|
-
return request[NETWORK_ENTRY_SUMBOL];
|
|
62
|
+
_findNetworkRecord(requestId: string) {
|
|
63
|
+
return this._InflightRequestMap.get(requestId);
|
|
86
64
|
}
|
|
87
65
|
|
|
88
|
-
|
|
89
|
-
const
|
|
66
|
+
_onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload) {
|
|
67
|
+
const { requestId, request, timestamp, type, loaderId } = event;
|
|
68
|
+
const { url, method } = request;
|
|
90
69
|
/**
|
|
91
70
|
* Data URI should not show up as network requests
|
|
92
71
|
*/
|
|
93
72
|
if (url.startsWith('data:')) {
|
|
94
73
|
return;
|
|
95
74
|
}
|
|
75
|
+
const isNavigationRequest = requestId == loaderId && type === 'Document';
|
|
76
|
+
const record = this._findNetworkRecord(requestId);
|
|
77
|
+
/**
|
|
78
|
+
* On redirects, another `requestWillBeSent` event will be fired for the
|
|
79
|
+
* same requestId. We calculate the timings for the redirect request using
|
|
80
|
+
* the `redirectedResponse` from the redirected request.
|
|
81
|
+
*/
|
|
82
|
+
if (record) {
|
|
83
|
+
if (event.redirectResponse) {
|
|
84
|
+
const response = event.redirectResponse;
|
|
85
|
+
const data = Object.assign(event, {
|
|
86
|
+
type: event.type,
|
|
87
|
+
response,
|
|
88
|
+
encodedDataLength: response.encodedDataLength,
|
|
89
|
+
});
|
|
90
|
+
this._onResponseReceived(data);
|
|
91
|
+
this._onLoadingFinished(data);
|
|
92
|
+
} else {
|
|
93
|
+
/**
|
|
94
|
+
* Edge case, we handle it to proceed with next navigation
|
|
95
|
+
*/
|
|
96
|
+
this._onLoadingFailed(
|
|
97
|
+
Object.assign(event, {
|
|
98
|
+
type: event.type,
|
|
99
|
+
errorText: 'redirectResponse data is not available',
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const redirectedRecord = this._findNetworkRecord(requestId);
|
|
104
|
+
/**
|
|
105
|
+
* Rewrite the map with new redirect id to not reset
|
|
106
|
+
* the redirect request with original request
|
|
107
|
+
*/
|
|
108
|
+
this._InflightRequestMap.delete(requestId);
|
|
109
|
+
this._InflightRequestMap.set(
|
|
110
|
+
`redirect:${timestamp}:${requestId}`,
|
|
111
|
+
redirectedRecord
|
|
112
|
+
);
|
|
113
|
+
}
|
|
96
114
|
|
|
97
|
-
|
|
98
|
-
const networkEntry: NetworkInfo = {
|
|
115
|
+
this._InflightRequestMap.set(requestId, {
|
|
99
116
|
browser: this._browser,
|
|
100
117
|
step: this._currentStep,
|
|
101
|
-
timestamp,
|
|
118
|
+
timestamp: getTimestamp(),
|
|
102
119
|
url,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
headers: {},
|
|
110
|
-
},
|
|
111
|
-
response: {
|
|
112
|
-
statusCode: -1,
|
|
113
|
-
headers: {},
|
|
114
|
-
redirectURL: '',
|
|
115
|
-
},
|
|
116
|
-
isNavigationRequest: request.isNavigationRequest(),
|
|
117
|
-
status: -1,
|
|
120
|
+
request,
|
|
121
|
+
type,
|
|
122
|
+
method,
|
|
123
|
+
requestSentTime: timestamp,
|
|
124
|
+
isNavigationRequest,
|
|
125
|
+
status: 0,
|
|
118
126
|
loadEndTime: -1,
|
|
119
127
|
responseReceivedTime: -1,
|
|
128
|
+
response: null,
|
|
120
129
|
resourceSize: 0,
|
|
121
130
|
transferSize: 0,
|
|
122
131
|
timings: null,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_onRequestWillBeSentExtraInfo(
|
|
136
|
+
event: Protocol.Network.requestWillBeSentExtraInfoPayload
|
|
137
|
+
) {
|
|
138
|
+
const { requestId, headers } = event;
|
|
139
|
+
const record = this._findNetworkRecord(requestId);
|
|
140
|
+
if (!record) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Enhance request headers with additional information
|
|
145
|
+
*/
|
|
146
|
+
record.request.headers = {
|
|
147
|
+
...record.request.headers,
|
|
148
|
+
...headers,
|
|
123
149
|
};
|
|
150
|
+
}
|
|
124
151
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
152
|
+
_onDataReceived(event: Protocol.Network.dataReceivedPayload) {
|
|
153
|
+
const { requestId, dataLength, encodedDataLength } = event;
|
|
154
|
+
const record = this._findNetworkRecord(requestId);
|
|
155
|
+
if (!record) {
|
|
156
|
+
return;
|
|
128
157
|
}
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
record.resourceSize += dataLength;
|
|
159
|
+
if (encodedDataLength >= 0) record.transferSize += encodedDataLength;
|
|
131
160
|
}
|
|
132
161
|
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
if (!
|
|
162
|
+
_onResponseReceived(event: Protocol.Network.responseReceivedPayload) {
|
|
163
|
+
const { requestId, response, timestamp } = event;
|
|
164
|
+
const record = this._findNetworkRecord(requestId);
|
|
165
|
+
if (!record) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
Object.assign(record, {
|
|
169
|
+
status: response.status,
|
|
170
|
+
response,
|
|
171
|
+
responseReceivedTime: timestamp,
|
|
172
|
+
transferSize: response.encodedDataLength,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
137
175
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
statusCode: response.status(),
|
|
143
|
-
statusText: response.statusText(),
|
|
144
|
-
headers: {},
|
|
145
|
-
redirectURL: networkEntry.response.redirectURL,
|
|
146
|
-
};
|
|
176
|
+
_onLoadingFinished(event: Protocol.Network.loadingFinishedPayload) {
|
|
177
|
+
const { requestId, timestamp, encodedDataLength } = event;
|
|
178
|
+
this._requestCompleted(requestId, timestamp, encodedDataLength);
|
|
179
|
+
}
|
|
147
180
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
networkEntry.request.headers = reqHeaders;
|
|
153
|
-
networkEntry.request.referrer = reqHeaders?.referer;
|
|
154
|
-
})
|
|
155
|
-
);
|
|
156
|
-
this._addBarrier(
|
|
157
|
-
page,
|
|
158
|
-
response.serverAddr().then(server => {
|
|
159
|
-
networkEntry.response.remoteIPAddress = server?.ipAddress;
|
|
160
|
-
networkEntry.response.remotePort = server?.port;
|
|
161
|
-
})
|
|
162
|
-
);
|
|
163
|
-
this._addBarrier(
|
|
164
|
-
page,
|
|
165
|
-
response.securityDetails().then(details => {
|
|
166
|
-
networkEntry.response.securityDetails = details;
|
|
167
|
-
})
|
|
168
|
-
);
|
|
169
|
-
this._addBarrier(
|
|
170
|
-
page,
|
|
171
|
-
response.allHeaders().then(resHeaders => {
|
|
172
|
-
networkEntry.response.headers = resHeaders;
|
|
181
|
+
_onLoadingFailed(event: Protocol.Network.loadingFailedPayload) {
|
|
182
|
+
const { requestId, timestamp } = event;
|
|
183
|
+
this._requestCompleted(requestId, timestamp);
|
|
184
|
+
}
|
|
173
185
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
);
|
|
186
|
+
_requestCompleted(
|
|
187
|
+
requestId: string,
|
|
188
|
+
endTime: number,
|
|
189
|
+
encodedDataLength?: number
|
|
190
|
+
) {
|
|
191
|
+
const record = this._findNetworkRecord(requestId);
|
|
192
|
+
if (!record) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (encodedDataLength >= 0) {
|
|
196
|
+
record.transferSize = encodedDataLength;
|
|
197
|
+
}
|
|
198
|
+
record.loadEndTime = endTime;
|
|
199
|
+
record.timings = calculateTimings(record);
|
|
200
|
+
this.results.push(record);
|
|
201
|
+
/**
|
|
202
|
+
* When there is DNS failure, browsers keeps retrying requests which
|
|
203
|
+
* might result in incorrect timings so we get remove them to measure only once
|
|
204
|
+
*/
|
|
205
|
+
this._InflightRequestMap.delete(requestId);
|
|
180
206
|
}
|
|
181
207
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Account for missing response received event and also adjust the
|
|
210
|
+
* response received event based on when first byte event was recorded
|
|
211
|
+
*/
|
|
186
212
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
dns: -1,
|
|
192
|
-
ssl: -1,
|
|
193
|
-
connect: -1,
|
|
194
|
-
send: -1,
|
|
195
|
-
wait: -1,
|
|
196
|
-
receive: -1,
|
|
197
|
-
total: -1,
|
|
198
|
-
};
|
|
213
|
+
stop() {
|
|
214
|
+
return this.results;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
199
217
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
function getResponseReceivedTime(
|
|
219
|
+
timing: Protocol.Network.Response['timing'],
|
|
220
|
+
responseReceivedTime: number
|
|
221
|
+
) {
|
|
222
|
+
if (timing == null) {
|
|
223
|
+
return responseReceivedTime;
|
|
224
|
+
}
|
|
225
|
+
const startTime = timing.requestTime;
|
|
226
|
+
const headersReceivedTime = startTime + timing.receiveHeadersEnd / 1000;
|
|
227
|
+
if (responseReceivedTime < 0 || responseReceivedTime > headersReceivedTime) {
|
|
228
|
+
responseReceivedTime = headersReceivedTime;
|
|
229
|
+
}
|
|
230
|
+
if (startTime > responseReceivedTime) {
|
|
231
|
+
responseReceivedTime = startTime;
|
|
232
|
+
}
|
|
233
|
+
return responseReceivedTime;
|
|
234
|
+
}
|
|
211
235
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
236
|
+
/**
|
|
237
|
+
* The timing calculations are based on the chrome devtools frontend
|
|
238
|
+
* https://github.com/ChromeDevTools/devtools-frontend/blob/7f5478d8ceb7586f23f4073ab5c2085dac1ec26a/front_end/network/RequestTimingView.js#L98-L193
|
|
239
|
+
*/
|
|
240
|
+
export function calculateTimings(record: NetworkInfo) {
|
|
241
|
+
const result: NetworkInfo['timings'] = {
|
|
242
|
+
blocked: -1,
|
|
243
|
+
queueing: -1,
|
|
244
|
+
proxy: -1,
|
|
245
|
+
dns: -1,
|
|
246
|
+
ssl: -1,
|
|
247
|
+
connect: -1,
|
|
248
|
+
send: -1,
|
|
249
|
+
wait: -1,
|
|
250
|
+
receive: -1,
|
|
251
|
+
total: -1,
|
|
252
|
+
};
|
|
253
|
+
const { requestSentTime, loadEndTime, responseReceivedTime } = record;
|
|
218
254
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
timing.domainLookupEnd !== -1
|
|
229
|
-
? roundMilliSecs(timing.domainLookupEnd - timing.domainLookupStart)
|
|
230
|
-
: -1;
|
|
231
|
-
const connect =
|
|
232
|
-
timing.connectEnd !== -1
|
|
233
|
-
? roundMilliSecs(timing.connectEnd - timing.connectStart)
|
|
234
|
-
: -1;
|
|
235
|
-
const ssl =
|
|
236
|
-
timing.secureConnectionStart !== -1
|
|
237
|
-
? roundMilliSecs(timing.connectEnd - timing.secureConnectionStart)
|
|
238
|
-
: -1;
|
|
239
|
-
const wait =
|
|
240
|
-
timing.responseStart !== -1
|
|
241
|
-
? roundMilliSecs(timing.responseStart - timing.requestStart)
|
|
242
|
-
: -1;
|
|
243
|
-
const receive =
|
|
244
|
-
timing.responseEnd !== -1
|
|
245
|
-
? roundMilliSecs(timing.responseEnd - timing.responseStart)
|
|
246
|
-
: -1;
|
|
247
|
-
const total = [blocked, dns, connect, wait, receive].reduce(
|
|
248
|
-
(pre, cur) => (cur > 0 ? cur + pre : pre),
|
|
249
|
-
0
|
|
250
|
-
);
|
|
251
|
-
networkEntry.timings = {
|
|
252
|
-
blocked,
|
|
253
|
-
dns,
|
|
254
|
-
connect,
|
|
255
|
-
ssl,
|
|
256
|
-
wait,
|
|
257
|
-
send: 0, // not exposed via RT api
|
|
258
|
-
receive,
|
|
259
|
-
total: roundMilliSecs(total),
|
|
260
|
-
};
|
|
255
|
+
/**
|
|
256
|
+
* Handle when request failed and no responseReceived event was
|
|
257
|
+
* fired for that particular request
|
|
258
|
+
* Eg: connection refused by remote host
|
|
259
|
+
*/
|
|
260
|
+
const response = record.response || {
|
|
261
|
+
timing: null,
|
|
262
|
+
fromServiceWorker: false,
|
|
263
|
+
};
|
|
261
264
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
265
|
+
const toMilliseconds = (time: number) => (time === -1 ? -1 : time * 1000);
|
|
266
|
+
const calculateDiffInMs = (
|
|
267
|
+
name: keyof NetworkInfo['timings'],
|
|
268
|
+
start: number,
|
|
269
|
+
end: number
|
|
270
|
+
) => {
|
|
271
|
+
if (start < Number.MAX_VALUE && start <= end) {
|
|
272
|
+
result[name] = toMilliseconds(end - start);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const firstPositive = (numbers: number[]) => {
|
|
276
|
+
for (let i = 0; i < numbers.length; ++i) {
|
|
277
|
+
if (numbers[i] > 0) {
|
|
278
|
+
return numbers[i];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* requestTime is baseline in seconds, rest of the timing data are ticks in milliseconds
|
|
285
|
+
* from the requestTime, so we calculate the offset from that
|
|
286
|
+
*/
|
|
287
|
+
const addOffsetRange = (
|
|
288
|
+
name: keyof NetworkInfo['timings'],
|
|
289
|
+
start: number,
|
|
290
|
+
end: number
|
|
291
|
+
) => {
|
|
292
|
+
if (start >= 0 && end >= 0) {
|
|
293
|
+
calculateDiffInMs(name, startTime + start / 1000, startTime + end / 1000);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
const timing = response.timing;
|
|
297
|
+
const actResRcvdTime = getResponseReceivedTime(timing, responseReceivedTime);
|
|
298
|
+
const issueTime = requestSentTime;
|
|
299
|
+
const startTime = timing == null ? -1 : timing.requestTime;
|
|
300
|
+
const endTime = firstPositive([loadEndTime, actResRcvdTime]) || startTime;
|
|
301
|
+
|
|
302
|
+
if (timing == null) {
|
|
303
|
+
const start =
|
|
304
|
+
issueTime !== -1 ? issueTime : startTime !== -1 ? startTime : 0;
|
|
305
|
+
const middle = actResRcvdTime === -1 ? Number.MAX_VALUE : actResRcvdTime;
|
|
306
|
+
const end = endTime === -1 ? Number.MAX_VALUE : endTime;
|
|
307
|
+
calculateDiffInMs('total', start, end);
|
|
308
|
+
calculateDiffInMs('blocked', start, middle);
|
|
309
|
+
calculateDiffInMs('receive', middle, end);
|
|
310
|
+
// Check if the request is blocked/stalled for the whole timeframe and
|
|
311
|
+
// calculate timings appropriately for that request
|
|
312
|
+
if (!isFinite(result['blocked'])) {
|
|
313
|
+
result['blocked'] = result['total'];
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
calculateDiffInMs(
|
|
318
|
+
'total',
|
|
319
|
+
issueTime < startTime ? issueTime : startTime,
|
|
320
|
+
endTime
|
|
321
|
+
);
|
|
322
|
+
if (issueTime < startTime) {
|
|
323
|
+
calculateDiffInMs('queueing', issueTime, startTime);
|
|
280
324
|
}
|
|
325
|
+
const responseReceived = toMilliseconds(actResRcvdTime - startTime);
|
|
281
326
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
327
|
+
if (response.fromServiceWorker) {
|
|
328
|
+
addOffsetRange('blocked', 0, timing.workerStart);
|
|
329
|
+
addOffsetRange('wait', timing.sendEnd, responseReceived);
|
|
330
|
+
} else if (!timing.pushStart) {
|
|
331
|
+
const blockingEnd =
|
|
332
|
+
firstPositive([
|
|
333
|
+
timing.dnsStart,
|
|
334
|
+
timing.connectStart,
|
|
335
|
+
timing.sendStart,
|
|
336
|
+
responseReceived,
|
|
337
|
+
]) || 0;
|
|
338
|
+
addOffsetRange('blocked', 0, blockingEnd);
|
|
339
|
+
addOffsetRange('proxy', timing.proxyStart, timing.proxyEnd);
|
|
340
|
+
addOffsetRange('dns', timing.dnsStart, timing.dnsEnd);
|
|
341
|
+
addOffsetRange('connect', timing.connectStart, timing.connectEnd);
|
|
342
|
+
addOffsetRange('ssl', timing.sslStart, timing.sslEnd);
|
|
343
|
+
addOffsetRange('send', timing.sendStart, timing.sendEnd);
|
|
344
|
+
addOffsetRange(
|
|
345
|
+
'wait',
|
|
346
|
+
Math.max(
|
|
347
|
+
timing.sendEnd,
|
|
348
|
+
timing.connectEnd,
|
|
349
|
+
timing.dnsEnd,
|
|
350
|
+
timing.proxyEnd,
|
|
351
|
+
blockingEnd
|
|
352
|
+
),
|
|
353
|
+
responseReceived
|
|
354
|
+
);
|
|
290
355
|
}
|
|
356
|
+
calculateDiffInMs('receive', actResRcvdTime, endTime);
|
|
357
|
+
return result;
|
|
291
358
|
}
|
package/src/reporters/base.ts
CHANGED
|
@@ -64,7 +64,12 @@ export default class BaseReporter {
|
|
|
64
64
|
|
|
65
65
|
constructor(public runner: Runner, options: ReporterOptions = {}) {
|
|
66
66
|
this.fd = options.fd || process.stdout.fd;
|
|
67
|
-
|
|
67
|
+
/**
|
|
68
|
+
* minLength is set to 1 byte to make sure we flush the
|
|
69
|
+
* content even if its the last byte on the stream buffer
|
|
70
|
+
* before destroying the pipe with underlying file descriptor
|
|
71
|
+
*/
|
|
72
|
+
this.stream = new SonicBoom({ fd: this.fd, sync: true, minLength: 1 });
|
|
68
73
|
this._registerListeners();
|
|
69
74
|
this.runner.on('end', () => this.stream.flush());
|
|
70
75
|
}
|