@elastic/synthetics 1.0.0-beta.21 → 1.0.0-beta.24
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 +23 -0
- package/dist/cli.js +55 -184
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +80 -32
- package/dist/common_types.d.ts.map +1 -1
- package/dist/config.js +5 -5
- package/dist/config.js.map +1 -1
- package/dist/core/browser-service.js +5 -5
- package/dist/core/browser-service.js.map +1 -1
- package/dist/core/gatherer.d.ts +1 -2
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +7 -8
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -11
- package/dist/core/index.js.map +1 -1
- package/dist/core/logger.js +2 -2
- package/dist/core/logger.js.map +1 -1
- package/dist/core/runner.d.ts +1 -8
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +34 -35
- package/dist/core/runner.js.map +1 -1
- package/dist/dsl/journey.d.ts +4 -3
- package/dist/dsl/journey.d.ts.map +1 -1
- package/dist/dsl/journey.js +6 -5
- package/dist/dsl/journey.js.map +1 -1
- package/dist/dsl/step.d.ts +3 -2
- package/dist/dsl/step.d.ts.map +1 -1
- package/dist/dsl/step.js +2 -1
- package/dist/dsl/step.js.map +1 -1
- package/dist/formatter/javascript.d.ts +31 -6
- package/dist/formatter/javascript.d.ts.map +1 -1
- package/dist/formatter/javascript.js +45 -24
- package/dist/formatter/javascript.js.map +1 -1
- package/dist/helpers.d.ts +2 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +48 -19
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -17
- package/dist/index.js.map +1 -1
- package/dist/{parse_args.d.ts → loader.d.ts} +2 -5
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +144 -0
- package/dist/loader.js.map +1 -0
- package/dist/options.d.ts +27 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +108 -0
- package/dist/options.js.map +1 -0
- package/dist/plugins/browser-console.js +2 -2
- package/dist/plugins/browser-console.js.map +1 -1
- package/dist/plugins/network.d.ts +1 -1
- package/dist/plugins/network.d.ts.map +1 -1
- package/dist/plugins/network.js +47 -24
- package/dist/plugins/network.js.map +1 -1
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js +14 -11
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/json.d.ts +4 -53
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +19 -48
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/junit.d.ts.map +1 -1
- package/dist/reporters/junit.js +7 -7
- package/dist/reporters/junit.js.map +1 -1
- package/dist/sdk/lh-trace-processor.js +3 -3
- package/dist/sdk/lh-trace-processor.js.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.map +1 -1
- package/dist/sdk/trace-processor.js +1 -1
- package/dist/sdk/trace-processor.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/cli.ts +114 -188
- package/src/common_types.ts +91 -32
- package/src/core/gatherer.ts +2 -3
- package/src/core/index.ts +22 -16
- package/src/core/runner.ts +2 -22
- package/src/dsl/journey.ts +10 -4
- package/src/dsl/step.ts +9 -2
- package/src/formatter/javascript.ts +59 -22
- package/src/helpers.ts +40 -1
- package/src/index.ts +10 -16
- package/src/loader.ts +162 -0
- package/src/options.ts +116 -0
- package/src/plugins/network.ts +49 -25
- package/src/reporters/base.ts +6 -1
- package/src/reporters/json.ts +8 -39
- package/src/reporters/junit.ts +1 -1
- package/src/sdk/trace-processor.ts +1 -1
- package/dist/parse_args.d.ts.map +0 -1
- package/dist/parse_args.js +0 -103
- package/dist/parse_args.js.map +0 -1
- package/src/parse_args.ts +0 -158
package/src/options.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2020-present, Elastic NV
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
|
14
|
+
* all copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
* THE SOFTWARE.
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import merge from 'deepmerge';
|
|
27
|
+
import { readConfig } from './config';
|
|
28
|
+
import { parseNetworkConditions } from './helpers';
|
|
29
|
+
import type { CliArgs, RunOptions } from './common_types';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Set debug based on DEBUG ENV and -d flags
|
|
33
|
+
* namespace - synthetics
|
|
34
|
+
*/
|
|
35
|
+
const namespace = 'synthetics';
|
|
36
|
+
if (process.env.DEBUG && process.env.DEBUG.includes(namespace)) {
|
|
37
|
+
process.env.DEBUG = '1';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function normalizeOptions(cliArgs: CliArgs): RunOptions {
|
|
41
|
+
const options: RunOptions = {
|
|
42
|
+
...cliArgs,
|
|
43
|
+
environment: process.env['NODE_ENV'] || 'development',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Group all events that can be consumed by heartbeat and
|
|
47
|
+
* eventually by the Synthetics UI.
|
|
48
|
+
*/
|
|
49
|
+
if (cliArgs.richEvents) {
|
|
50
|
+
options.reporter = cliArgs.reporter ?? 'json';
|
|
51
|
+
options.ssblocks = true;
|
|
52
|
+
options.network = true;
|
|
53
|
+
options.trace = true;
|
|
54
|
+
options.quietExitCode = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (cliArgs.capability) {
|
|
58
|
+
const supportedCapabilities = [
|
|
59
|
+
'trace',
|
|
60
|
+
'network',
|
|
61
|
+
'filmstrips',
|
|
62
|
+
'metrics',
|
|
63
|
+
'ssblocks',
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* trace - record chrome trace events(LCP, FCP, CLS, etc.) for all journeys
|
|
67
|
+
* network - capture network information for all journeys
|
|
68
|
+
* filmstrips - record detailed filmstrips for all journeys
|
|
69
|
+
* metrics - capture performance metrics (DOM Nodes, Heap size, etc.) for each step
|
|
70
|
+
* ssblocks - Dedupes the screenshots in to blocks to save storage space
|
|
71
|
+
*/
|
|
72
|
+
for (const flag of cliArgs.capability) {
|
|
73
|
+
if (supportedCapabilities.includes(flag)) {
|
|
74
|
+
options[flag] = true;
|
|
75
|
+
} else {
|
|
76
|
+
console.warn(
|
|
77
|
+
`Missing capability "${flag}", current supported capabilities are ${supportedCapabilities.join(
|
|
78
|
+
', '
|
|
79
|
+
)}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate and read synthetics config file
|
|
87
|
+
* based on the environment
|
|
88
|
+
*/
|
|
89
|
+
const config =
|
|
90
|
+
cliArgs.config || !cliArgs.inline
|
|
91
|
+
? readConfig(options.environment, cliArgs.config)
|
|
92
|
+
: {};
|
|
93
|
+
|
|
94
|
+
options.params = Object.freeze(merge(config.params, cliArgs.params || {}));
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Favor playwright options passed via cli to inline playwright options
|
|
98
|
+
*/
|
|
99
|
+
options.playwrightOptions = merge.all([
|
|
100
|
+
config.playwrightOptions || {},
|
|
101
|
+
cliArgs.playwrightOptions || {},
|
|
102
|
+
{
|
|
103
|
+
headless: cliArgs.headless,
|
|
104
|
+
chromiumSandbox: cliArgs.sandbox,
|
|
105
|
+
ignoreHTTPSErrors: cliArgs.ignoreHttpsErrors,
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
if (cliArgs.throttling) {
|
|
110
|
+
options.networkConditions = parseNetworkConditions(
|
|
111
|
+
cliArgs.throttling as string
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return options;
|
|
116
|
+
}
|
package/src/plugins/network.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { Protocol } from 'playwright-
|
|
26
|
+
import { Protocol } from 'playwright-core/types/protocol';
|
|
27
27
|
import { NetworkInfo, BrowserInfo, Driver } from '../common_types';
|
|
28
28
|
import { Step } from '../dsl';
|
|
29
29
|
import { getTimestamp } from '../helpers';
|
|
@@ -64,14 +64,14 @@ export class NetworkManager {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
_onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload) {
|
|
67
|
-
const {
|
|
68
|
-
const { url, method } = request;
|
|
67
|
+
const { url, method, postData, headers } = event.request;
|
|
69
68
|
/**
|
|
70
69
|
* Data URI should not show up as network requests
|
|
71
70
|
*/
|
|
72
71
|
if (url.startsWith('data:')) {
|
|
73
72
|
return;
|
|
74
73
|
}
|
|
74
|
+
const { requestId, timestamp, type, loaderId } = event;
|
|
75
75
|
const isNavigationRequest = requestId == loaderId && type === 'Document';
|
|
76
76
|
const record = this._findNetworkRecord(requestId);
|
|
77
77
|
/**
|
|
@@ -81,11 +81,13 @@ export class NetworkManager {
|
|
|
81
81
|
*/
|
|
82
82
|
if (record) {
|
|
83
83
|
if (event.redirectResponse) {
|
|
84
|
+
record.response.redirectURL = url;
|
|
84
85
|
const response = event.redirectResponse;
|
|
85
86
|
const data = Object.assign(event, {
|
|
86
87
|
type: event.type,
|
|
87
88
|
response,
|
|
88
89
|
encodedDataLength: response.encodedDataLength,
|
|
90
|
+
hasExtraInfo: false,
|
|
89
91
|
});
|
|
90
92
|
this._onResponseReceived(data);
|
|
91
93
|
this._onLoadingFinished(data);
|
|
@@ -117,15 +119,23 @@ export class NetworkManager {
|
|
|
117
119
|
step: this._currentStep,
|
|
118
120
|
timestamp: getTimestamp(),
|
|
119
121
|
url,
|
|
120
|
-
request,
|
|
121
122
|
type,
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
request: {
|
|
124
|
+
url,
|
|
125
|
+
method,
|
|
126
|
+
headers,
|
|
127
|
+
bytes: postData?.length || 0,
|
|
128
|
+
},
|
|
129
|
+
response: {
|
|
130
|
+
status: -1,
|
|
131
|
+
mimeType: 'x-unknown',
|
|
132
|
+
headers: {},
|
|
133
|
+
timing: null,
|
|
134
|
+
},
|
|
124
135
|
isNavigationRequest,
|
|
125
|
-
|
|
126
|
-
loadEndTime: -1,
|
|
136
|
+
requestSentTime: timestamp,
|
|
127
137
|
responseReceivedTime: -1,
|
|
128
|
-
|
|
138
|
+
loadEndTime: -1,
|
|
129
139
|
resourceSize: 0,
|
|
130
140
|
transferSize: 0,
|
|
131
141
|
timings: null,
|
|
@@ -147,6 +157,7 @@ export class NetworkManager {
|
|
|
147
157
|
...record.request.headers,
|
|
148
158
|
...headers,
|
|
149
159
|
};
|
|
160
|
+
record.request.referrer = record.request.headers?.Referer;
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
_onDataReceived(event: Protocol.Network.dataReceivedPayload) {
|
|
@@ -165,12 +176,31 @@ export class NetworkManager {
|
|
|
165
176
|
if (!record) {
|
|
166
177
|
return;
|
|
167
178
|
}
|
|
168
|
-
|
|
179
|
+
|
|
180
|
+
record.responseReceivedTime = timestamp;
|
|
181
|
+
record.transferSize = response.encodedDataLength;
|
|
182
|
+
record.response = {
|
|
183
|
+
url: response.url,
|
|
169
184
|
status: response.status,
|
|
170
|
-
response,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
185
|
+
protocol: response.protocol,
|
|
186
|
+
statusText: response.statusText,
|
|
187
|
+
headers: response.headers,
|
|
188
|
+
mimeType: response.mimeType,
|
|
189
|
+
remoteIPAddress: response.remoteIPAddress,
|
|
190
|
+
remotePort: response.remotePort,
|
|
191
|
+
fromServiceWorker: response.fromServiceWorker,
|
|
192
|
+
redirectURL: record.response.redirectURL,
|
|
193
|
+
timing: response.timing,
|
|
194
|
+
};
|
|
195
|
+
if (response.securityDetails) {
|
|
196
|
+
record.response.securityDetails = {
|
|
197
|
+
protocol: response.securityDetails.protocol,
|
|
198
|
+
subjectName: response.securityDetails.subjectName,
|
|
199
|
+
issuer: response.securityDetails.issuer,
|
|
200
|
+
validFrom: response.securityDetails.validFrom,
|
|
201
|
+
validTo: response.securityDetails.validTo,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
174
204
|
}
|
|
175
205
|
|
|
176
206
|
_onLoadingFinished(event: Protocol.Network.loadingFinishedPayload) {
|
|
@@ -194,6 +224,9 @@ export class NetworkManager {
|
|
|
194
224
|
}
|
|
195
225
|
if (encodedDataLength >= 0) {
|
|
196
226
|
record.transferSize = encodedDataLength;
|
|
227
|
+
record.response.body = {
|
|
228
|
+
bytes: encodedDataLength,
|
|
229
|
+
};
|
|
197
230
|
}
|
|
198
231
|
record.loadEndTime = endTime;
|
|
199
232
|
record.timings = calculateTimings(record);
|
|
@@ -250,17 +283,6 @@ export function calculateTimings(record: NetworkInfo) {
|
|
|
250
283
|
receive: -1,
|
|
251
284
|
total: -1,
|
|
252
285
|
};
|
|
253
|
-
const { requestSentTime, loadEndTime, responseReceivedTime } = record;
|
|
254
|
-
|
|
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
|
-
};
|
|
264
286
|
|
|
265
287
|
const toMilliseconds = (time: number) => (time === -1 ? -1 : time * 1000);
|
|
266
288
|
const calculateDiffInMs = (
|
|
@@ -293,6 +315,8 @@ export function calculateTimings(record: NetworkInfo) {
|
|
|
293
315
|
calculateDiffInMs(name, startTime + start / 1000, startTime + end / 1000);
|
|
294
316
|
}
|
|
295
317
|
};
|
|
318
|
+
const { requestSentTime, loadEndTime, responseReceivedTime, response } =
|
|
319
|
+
record;
|
|
296
320
|
const timing = response.timing;
|
|
297
321
|
const actResRcvdTime = getResponseReceivedTime(timing, responseReceivedTime);
|
|
298
322
|
const issueTime = requestSentTime;
|
package/src/reporters/base.ts
CHANGED
|
@@ -70,8 +70,13 @@ export default class BaseReporter {
|
|
|
70
70
|
* before destroying the pipe with underlying file descriptor
|
|
71
71
|
*/
|
|
72
72
|
this.stream = new SonicBoom({ fd: this.fd, sync: true, minLength: 1 });
|
|
73
|
+
|
|
74
|
+
// flushSync is used here to make sure all the data from the underlying
|
|
75
|
+
// SonicBoom stream buffer is completely written to the fd before closing
|
|
76
|
+
// the process
|
|
77
|
+
this.runner.on('end', () => this.stream.flushSync());
|
|
78
|
+
|
|
73
79
|
this._registerListeners();
|
|
74
|
-
this.runner.on('end', () => this.stream.flush());
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
_registerListeners() {
|
package/src/reporters/json.ts
CHANGED
|
@@ -40,6 +40,7 @@ import { Journey, Step } from '../dsl';
|
|
|
40
40
|
import snakeCaseKeys from 'snakecase-keys';
|
|
41
41
|
import {
|
|
42
42
|
NetworkInfo,
|
|
43
|
+
SecurityDetails,
|
|
43
44
|
NetworkConditions,
|
|
44
45
|
TraceOutput,
|
|
45
46
|
StatusValue,
|
|
@@ -47,7 +48,6 @@ import {
|
|
|
47
48
|
Params,
|
|
48
49
|
Screenshot,
|
|
49
50
|
} from '../common_types';
|
|
50
|
-
import { Protocol } from 'playwright-chromium/types/protocol';
|
|
51
51
|
import { PageMetrics } from '../plugins';
|
|
52
52
|
|
|
53
53
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
@@ -72,7 +72,7 @@ type Payload = {
|
|
|
72
72
|
start?: number;
|
|
73
73
|
end?: number;
|
|
74
74
|
url?: string;
|
|
75
|
-
status?: StatusValue
|
|
75
|
+
status?: StatusValue;
|
|
76
76
|
pagemetrics?: PageMetrics;
|
|
77
77
|
params?: Params;
|
|
78
78
|
type?: OutputType;
|
|
@@ -94,7 +94,7 @@ type OutputFields = {
|
|
|
94
94
|
};
|
|
95
95
|
error?: Error;
|
|
96
96
|
root_fields?: Record<string, unknown>;
|
|
97
|
-
payload?: Payload
|
|
97
|
+
payload?: Payload;
|
|
98
98
|
blob?: string;
|
|
99
99
|
blob_mime?: string;
|
|
100
100
|
};
|
|
@@ -143,41 +143,12 @@ function formatVersion(protocol: string | undefined) {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
function
|
|
147
|
-
const postData = request.postData ? request.postData : '';
|
|
148
|
-
return {
|
|
149
|
-
...request,
|
|
150
|
-
body: {
|
|
151
|
-
bytes: postData.length,
|
|
152
|
-
content: postData,
|
|
153
|
-
},
|
|
154
|
-
referrer: request.headers?.Referer,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function formatResponse(response: Protocol.Network.Response) {
|
|
159
|
-
if (!response) {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
...response,
|
|
164
|
-
body: {
|
|
165
|
-
bytes: response.encodedDataLength,
|
|
166
|
-
},
|
|
167
|
-
status_code: response.status,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function formatTLS(tls: Protocol.Network.SecurityDetails) {
|
|
146
|
+
function formatTLS(tls: SecurityDetails) {
|
|
172
147
|
if (!tls) {
|
|
173
148
|
return;
|
|
174
149
|
}
|
|
175
|
-
const cipher = `${tls.keyExchange ? tls.keyExchange + '_' : ''}${
|
|
176
|
-
tls.cipher
|
|
177
|
-
}_${tls.keyExchangeGroup}`;
|
|
178
150
|
const [name, version] = tls.protocol.toLowerCase().split(' ');
|
|
179
151
|
return {
|
|
180
|
-
cipher,
|
|
181
152
|
server: {
|
|
182
153
|
x509: {
|
|
183
154
|
issuer: {
|
|
@@ -191,7 +162,7 @@ function formatTLS(tls: Protocol.Network.SecurityDetails) {
|
|
|
191
162
|
},
|
|
192
163
|
},
|
|
193
164
|
version_protocol: name,
|
|
194
|
-
version
|
|
165
|
+
version,
|
|
195
166
|
};
|
|
196
167
|
}
|
|
197
168
|
|
|
@@ -207,16 +178,14 @@ export function formatNetworkFields(network: NetworkInfo) {
|
|
|
207
178
|
},
|
|
208
179
|
http: {
|
|
209
180
|
version: formatVersion(response?.protocol),
|
|
210
|
-
request
|
|
211
|
-
response
|
|
181
|
+
request,
|
|
182
|
+
response,
|
|
212
183
|
},
|
|
213
184
|
tls: formatTLS(response?.securityDetails),
|
|
214
185
|
};
|
|
215
186
|
|
|
216
187
|
const pickItems: Array<keyof NetworkInfo> = [
|
|
217
188
|
'browser',
|
|
218
|
-
'status',
|
|
219
|
-
'method',
|
|
220
189
|
'type',
|
|
221
190
|
'isNavigationRequest',
|
|
222
191
|
'requestSentTime',
|
|
@@ -343,7 +312,7 @@ export async function gatherScreenshots(
|
|
|
343
312
|
}
|
|
344
313
|
|
|
345
314
|
export default class JSONReporter extends BaseReporter {
|
|
346
|
-
_registerListeners() {
|
|
315
|
+
override _registerListeners() {
|
|
347
316
|
/**
|
|
348
317
|
* report the number of journeys that exists on a suite which
|
|
349
318
|
* could be used for better sharding
|
package/src/reporters/junit.ts
CHANGED
|
@@ -52,7 +52,7 @@ export default class JUnitReporter extends BaseReporter {
|
|
|
52
52
|
private totalFailures = 0;
|
|
53
53
|
private totalSkipped = 0;
|
|
54
54
|
|
|
55
|
-
_registerListeners() {
|
|
55
|
+
override _registerListeners() {
|
|
56
56
|
const journeyMap = new Map<string, XMLEntry>();
|
|
57
57
|
|
|
58
58
|
this.runner.on('journey:start', ({ journey }) => {
|
|
@@ -111,7 +111,7 @@ export type LHProcessedTrace = {
|
|
|
111
111
|
* metrics based on multiple navigations instead of a single navigation.
|
|
112
112
|
*/
|
|
113
113
|
export class TraceProcessor extends LighthouseTraceProcessor {
|
|
114
|
-
static _isNavigationStartOfInterest(event) {
|
|
114
|
+
static override _isNavigationStartOfInterest(event) {
|
|
115
115
|
return (
|
|
116
116
|
event.name === 'navigationStart' &&
|
|
117
117
|
(!event.args.data ||
|
package/dist/parse_args.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parse_args.d.ts","sourceRoot":"","sources":["../src/parse_args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAuFzC,QAAA,MAAM,OAAO,6BAA8B,CAAC;AAC5C,QAAA,MAAM,OAAO,SAA4B,CAAC;AA0C1C,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,eAAe,OAAO,CAAC"}
|
package/dist/parse_args.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2020-present, Elastic NV
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in
|
|
15
|
-
* all copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
-
* THE SOFTWARE.
|
|
24
|
-
*
|
|
25
|
-
*/
|
|
26
|
-
var _a;
|
|
27
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.options = void 0;
|
|
29
|
-
const commander_1 = require("commander");
|
|
30
|
-
const reporters_1 = require("./reporters");
|
|
31
|
-
const helpers_1 = require("./helpers");
|
|
32
|
-
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
|
|
33
|
-
const { name, version } = require('../package.json');
|
|
34
|
-
commander_1.program
|
|
35
|
-
.name(`npx ${name}`)
|
|
36
|
-
.usage('[options] [dir] [files] file')
|
|
37
|
-
.option('-c, --config <path>', 'configuration path (default: synthetics.config.js)')
|
|
38
|
-
.option('-p, --params <jsonstring>', 'JSON object that gets injected to all journeys', JSON.parse)
|
|
39
|
-
.addOption(new commander_1.Option('--reporter <value>', `output repoter format`).choices(Object.keys(reporters_1.reporters)))
|
|
40
|
-
.option('-d, --debug', 'print debug logs info')
|
|
41
|
-
.option('--pattern <pattern>', 'RegExp file patterns to search inside directory')
|
|
42
|
-
.option('--inline', 'Run inline journeys from heartbeat')
|
|
43
|
-
.option('-r, --require <modules...>', 'module(s) to preload')
|
|
44
|
-
.option('--no-headless', 'run browser in headful mode')
|
|
45
|
-
.option('--sandbox', 'enable chromium sandboxing', false)
|
|
46
|
-
.option('--rich-events', 'Mimics a heartbeat run')
|
|
47
|
-
.option('--capability <features...>', 'Enable capabilities through feature flags')
|
|
48
|
-
.addOption(new commander_1.Option('--screenshots [flag]', 'take screenshots at end of each step')
|
|
49
|
-
.choices(['on', 'off', 'only-on-failure'])
|
|
50
|
-
.default('on'))
|
|
51
|
-
.option('--dry-run', "don't actually execute anything, report only registered journeys")
|
|
52
|
-
.option('--match <name>', 'run only journeys with a name or tags that matches the glob')
|
|
53
|
-
.option('--tags <name...>', 'run only journeys with the given tag(s), or globs')
|
|
54
|
-
.option('--outfd <fd>', 'specify a file descriptor for logs. Default is stdout', parseInt)
|
|
55
|
-
.option('--ws-endpoint <endpoint>', 'Browser WebSocket endpoint to connect to')
|
|
56
|
-
.option('--pause-on-error', 'pause on error until a keypress is made in the console. Useful during development')
|
|
57
|
-
.option('--ignore-https-errors', 'ignores any HTTPS errors in sites being tested, including ones related to unrecognized certs or signatures. This can be insecure!')
|
|
58
|
-
.option('--quiet-exit-code', 'always return 0 as an exit code status, regardless of test pass / fail. Only return > 0 exit codes on internal errors where the suite could not be run')
|
|
59
|
-
.addOption(new commander_1.Option('--throttling <d/u/l>', 'List of options to throttle network conditions for download throughput (d) in megabits/second, upload throughput (u) in megabits/second and latency (l) in milliseconds.').default(helpers_1.DEFAULT_NETWORK_CONDITIONS_ARG))
|
|
60
|
-
.option('--no-throttling', 'Turns off default network throttling.')
|
|
61
|
-
.option('--playwright-options <jsonstring>', 'JSON object to pass in custom Playwright options for the agent. Options passed will be merged with Playwright options defined in your synthetics.config.js file. Options defined via --playwright-options take precedence.', JSON.parse)
|
|
62
|
-
.version(version)
|
|
63
|
-
.description('Run synthetic tests');
|
|
64
|
-
const command = commander_1.program.parse(process.argv);
|
|
65
|
-
const options = command.opts();
|
|
66
|
-
exports.options = options;
|
|
67
|
-
/**
|
|
68
|
-
* Group all events that can be consumed by heartbeat and
|
|
69
|
-
* eventually by the Synthetics UI.
|
|
70
|
-
*/
|
|
71
|
-
if (options.richEvents) {
|
|
72
|
-
options.reporter = (_a = options.reporter) !== null && _a !== void 0 ? _a : 'json';
|
|
73
|
-
options.ssblocks = true;
|
|
74
|
-
options.network = true;
|
|
75
|
-
options.quietExitCode = true;
|
|
76
|
-
options.trace = true;
|
|
77
|
-
}
|
|
78
|
-
if (options.capability) {
|
|
79
|
-
const supportedCapabilities = [
|
|
80
|
-
'trace',
|
|
81
|
-
'network',
|
|
82
|
-
'filmstrips',
|
|
83
|
-
'metrics',
|
|
84
|
-
'ssblocks',
|
|
85
|
-
];
|
|
86
|
-
/**
|
|
87
|
-
* trace - record chrome trace events(LCP, FCP, CLS, etc.) for all journeys
|
|
88
|
-
* network - capture network information for all journeys
|
|
89
|
-
* filmstrips - record detailed filmstrips for all journeys
|
|
90
|
-
* metrics - capture performance metrics (DOM Nodes, Heap size, etc.) for each step
|
|
91
|
-
* ssblocks - Dedupes the screenshots in to blocks to save storage space
|
|
92
|
-
*/
|
|
93
|
-
for (const flag of options.capability) {
|
|
94
|
-
if (supportedCapabilities.includes(flag)) {
|
|
95
|
-
options[flag] = true;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
console.warn(`Missing capability "${flag}", current supported capabilities are ${supportedCapabilities.join(', ')}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
exports.default = command;
|
|
103
|
-
//# sourceMappingURL=parse_args.js.map
|
package/dist/parse_args.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parse_args.js","sourceRoot":"","sources":["../src/parse_args.ts"],"names":[],"mappings":"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"}
|
package/src/parse_args.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MIT License
|
|
3
|
-
*
|
|
4
|
-
* Copyright (c) 2020-present, Elastic NV
|
|
5
|
-
*
|
|
6
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
* in the Software without restriction, including without limitation the rights
|
|
9
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
* furnished to do so, subject to the following conditions:
|
|
12
|
-
*
|
|
13
|
-
* The above copyright notice and this permission notice shall be included in
|
|
14
|
-
* all copies or substantial portions of the Software.
|
|
15
|
-
*
|
|
16
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
-
* THE SOFTWARE.
|
|
23
|
-
*
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { program, Option } from 'commander';
|
|
27
|
-
import { CliArgs } from './common_types';
|
|
28
|
-
import { reporters } from './reporters';
|
|
29
|
-
import { DEFAULT_NETWORK_CONDITIONS_ARG } from './helpers';
|
|
30
|
-
|
|
31
|
-
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
|
|
32
|
-
const { name, version } = require('../package.json');
|
|
33
|
-
|
|
34
|
-
program
|
|
35
|
-
.name(`npx ${name}`)
|
|
36
|
-
.usage('[options] [dir] [files] file')
|
|
37
|
-
.option(
|
|
38
|
-
'-c, --config <path>',
|
|
39
|
-
'configuration path (default: synthetics.config.js)'
|
|
40
|
-
)
|
|
41
|
-
.option(
|
|
42
|
-
'-p, --params <jsonstring>',
|
|
43
|
-
'JSON object that gets injected to all journeys',
|
|
44
|
-
JSON.parse
|
|
45
|
-
)
|
|
46
|
-
.addOption(
|
|
47
|
-
new Option('--reporter <value>', `output repoter format`).choices(
|
|
48
|
-
Object.keys(reporters)
|
|
49
|
-
)
|
|
50
|
-
)
|
|
51
|
-
.option('-d, --debug', 'print debug logs info')
|
|
52
|
-
.option(
|
|
53
|
-
'--pattern <pattern>',
|
|
54
|
-
'RegExp file patterns to search inside directory'
|
|
55
|
-
)
|
|
56
|
-
.option('--inline', 'Run inline journeys from heartbeat')
|
|
57
|
-
.option('-r, --require <modules...>', 'module(s) to preload')
|
|
58
|
-
.option('--no-headless', 'run browser in headful mode')
|
|
59
|
-
.option('--sandbox', 'enable chromium sandboxing', false)
|
|
60
|
-
.option('--rich-events', 'Mimics a heartbeat run')
|
|
61
|
-
.option(
|
|
62
|
-
'--capability <features...>',
|
|
63
|
-
'Enable capabilities through feature flags'
|
|
64
|
-
)
|
|
65
|
-
.addOption(
|
|
66
|
-
new Option('--screenshots [flag]', 'take screenshots at end of each step')
|
|
67
|
-
.choices(['on', 'off', 'only-on-failure'])
|
|
68
|
-
.default('on')
|
|
69
|
-
)
|
|
70
|
-
.option(
|
|
71
|
-
'--dry-run',
|
|
72
|
-
"don't actually execute anything, report only registered journeys"
|
|
73
|
-
)
|
|
74
|
-
.option(
|
|
75
|
-
'--match <name>',
|
|
76
|
-
'run only journeys with a name or tags that matches the glob'
|
|
77
|
-
)
|
|
78
|
-
.option(
|
|
79
|
-
'--tags <name...>',
|
|
80
|
-
'run only journeys with the given tag(s), or globs'
|
|
81
|
-
)
|
|
82
|
-
.option(
|
|
83
|
-
'--outfd <fd>',
|
|
84
|
-
'specify a file descriptor for logs. Default is stdout',
|
|
85
|
-
parseInt
|
|
86
|
-
)
|
|
87
|
-
.option(
|
|
88
|
-
'--ws-endpoint <endpoint>',
|
|
89
|
-
'Browser WebSocket endpoint to connect to'
|
|
90
|
-
)
|
|
91
|
-
.option(
|
|
92
|
-
'--pause-on-error',
|
|
93
|
-
'pause on error until a keypress is made in the console. Useful during development'
|
|
94
|
-
)
|
|
95
|
-
.option(
|
|
96
|
-
'--ignore-https-errors',
|
|
97
|
-
'ignores any HTTPS errors in sites being tested, including ones related to unrecognized certs or signatures. This can be insecure!'
|
|
98
|
-
)
|
|
99
|
-
.option(
|
|
100
|
-
'--quiet-exit-code',
|
|
101
|
-
'always return 0 as an exit code status, regardless of test pass / fail. Only return > 0 exit codes on internal errors where the suite could not be run'
|
|
102
|
-
)
|
|
103
|
-
.addOption(
|
|
104
|
-
new Option(
|
|
105
|
-
'--throttling <d/u/l>',
|
|
106
|
-
'List of options to throttle network conditions for download throughput (d) in megabits/second, upload throughput (u) in megabits/second and latency (l) in milliseconds.'
|
|
107
|
-
).default(DEFAULT_NETWORK_CONDITIONS_ARG)
|
|
108
|
-
)
|
|
109
|
-
.option('--no-throttling', 'Turns off default network throttling.')
|
|
110
|
-
.option('--playwright-options <jsonstring>', 'JSON object to pass in custom Playwright options for the agent. Options passed will be merged with Playwright options defined in your synthetics.config.js file. Options defined via --playwright-options take precedence.', JSON.parse)
|
|
111
|
-
.version(version)
|
|
112
|
-
.description('Run synthetic tests');
|
|
113
|
-
|
|
114
|
-
const command = program.parse(process.argv);
|
|
115
|
-
const options = command.opts() as CliArgs;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Group all events that can be consumed by heartbeat and
|
|
119
|
-
* eventually by the Synthetics UI.
|
|
120
|
-
*/
|
|
121
|
-
if (options.richEvents) {
|
|
122
|
-
options.reporter = options.reporter ?? 'json';
|
|
123
|
-
options.ssblocks = true;
|
|
124
|
-
options.network = true;
|
|
125
|
-
options.quietExitCode = true;
|
|
126
|
-
options.trace = true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (options.capability) {
|
|
130
|
-
const supportedCapabilities = [
|
|
131
|
-
'trace',
|
|
132
|
-
'network',
|
|
133
|
-
'filmstrips',
|
|
134
|
-
'metrics',
|
|
135
|
-
'ssblocks',
|
|
136
|
-
];
|
|
137
|
-
/**
|
|
138
|
-
* trace - record chrome trace events(LCP, FCP, CLS, etc.) for all journeys
|
|
139
|
-
* network - capture network information for all journeys
|
|
140
|
-
* filmstrips - record detailed filmstrips for all journeys
|
|
141
|
-
* metrics - capture performance metrics (DOM Nodes, Heap size, etc.) for each step
|
|
142
|
-
* ssblocks - Dedupes the screenshots in to blocks to save storage space
|
|
143
|
-
*/
|
|
144
|
-
for (const flag of options.capability) {
|
|
145
|
-
if (supportedCapabilities.includes(flag)) {
|
|
146
|
-
options[flag] = true;
|
|
147
|
-
} else {
|
|
148
|
-
console.warn(
|
|
149
|
-
`Missing capability "${flag}", current supported capabilities are ${supportedCapabilities.join(
|
|
150
|
-
', '
|
|
151
|
-
)}`
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export { options };
|
|
158
|
-
export default command;
|