@elastic/synthetics 1.7.2 → 1.9.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/dist/cli.js +17 -14
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +13 -4
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +12 -6
- package/dist/core/runner.js.map +1 -1
- package/dist/dsl/monitor.d.ts +4 -0
- package/dist/dsl/monitor.d.ts.map +1 -1
- package/dist/dsl/monitor.js +6 -0
- package/dist/dsl/monitor.js.map +1 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +22 -16
- package/dist/generator/index.js.map +1 -1
- package/dist/options.d.ts +12 -2
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +67 -58
- package/dist/options.js.map +1 -1
- package/dist/push/index.d.ts +1 -1
- package/dist/push/index.d.ts.map +1 -1
- package/dist/push/index.js +20 -15
- package/dist/push/index.js.map +1 -1
- package/dist/push/monitor.d.ts.map +1 -1
- package/dist/push/monitor.js +22 -7
- package/dist/push/monitor.js.map +1 -1
- package/dist/reporters/json.d.ts +1 -0
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +33 -1
- package/dist/reporters/json.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +31 -23
- package/src/common_types.ts +14 -4
- package/src/core/runner.ts +17 -6
- package/src/dsl/monitor.ts +13 -1
- package/src/generator/index.ts +24 -20
- package/src/options.ts +86 -65
- package/src/push/index.ts +37 -15
- package/src/push/monitor.ts +27 -9
- package/src/reporters/json.ts +33 -0
package/src/cli.ts
CHANGED
|
@@ -56,33 +56,38 @@ import { installTransform } from './core/transform';
|
|
|
56
56
|
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
|
|
57
57
|
const { name, version } = require('../package.json');
|
|
58
58
|
|
|
59
|
-
const {
|
|
60
|
-
|
|
59
|
+
const {
|
|
60
|
+
params,
|
|
61
|
+
pattern,
|
|
62
|
+
playwrightOpts,
|
|
63
|
+
auth,
|
|
64
|
+
authMandatory,
|
|
65
|
+
configOpt,
|
|
66
|
+
tags,
|
|
67
|
+
match,
|
|
68
|
+
} = getCommonCommandOpts();
|
|
61
69
|
|
|
62
70
|
program
|
|
63
71
|
.name(`npx ${name}`)
|
|
64
72
|
.usage('[options] [dir] [files] file')
|
|
65
|
-
.
|
|
66
|
-
'-c, --config <path>',
|
|
67
|
-
'configuration path (default: synthetics.config.js)'
|
|
68
|
-
)
|
|
73
|
+
.addOption(configOpt)
|
|
69
74
|
.addOption(pattern)
|
|
75
|
+
.addOption(tags)
|
|
76
|
+
.addOption(match)
|
|
70
77
|
.addOption(params)
|
|
71
|
-
.option('--tags <name...>', 'run tests with a tag that matches the glob')
|
|
72
|
-
.option(
|
|
73
|
-
'--match <name>',
|
|
74
|
-
'run tests with a name or tags that matches the glob'
|
|
75
|
-
)
|
|
76
78
|
.addOption(
|
|
77
79
|
new Option('--reporter <value>', `output reporter format`).choices(
|
|
78
80
|
Object.keys(reporters)
|
|
79
81
|
)
|
|
80
82
|
)
|
|
81
|
-
.option('--inline', '
|
|
83
|
+
.option('--inline', 'read journeys from stdin instead of reading from files')
|
|
82
84
|
.option('-r, --require <modules...>', 'module(s) to preload')
|
|
83
85
|
.option('--sandbox', 'enable chromium sand-boxing')
|
|
84
|
-
.option(
|
|
85
|
-
|
|
86
|
+
.option(
|
|
87
|
+
'--rich-events',
|
|
88
|
+
'preset flag used when running monitors directly via Heartbeat'
|
|
89
|
+
)
|
|
90
|
+
.option('--no-headless', 'run with the browser in headful mode')
|
|
86
91
|
.option(
|
|
87
92
|
'--capability <features...>',
|
|
88
93
|
'Enable capabilities through feature flags'
|
|
@@ -90,7 +95,7 @@ program
|
|
|
90
95
|
.addOption(
|
|
91
96
|
new Option(
|
|
92
97
|
'--screenshots [flag]',
|
|
93
|
-
'
|
|
98
|
+
'Control whether to capture screenshots at the end of each step'
|
|
94
99
|
).choices(['on', 'off', 'only-on-failure'])
|
|
95
100
|
)
|
|
96
101
|
.option(
|
|
@@ -160,6 +165,7 @@ program
|
|
|
160
165
|
.description(
|
|
161
166
|
'Push all journeys in the current directory to create monitors within the Kibana monitor management UI'
|
|
162
167
|
)
|
|
168
|
+
.addOption(authMandatory)
|
|
163
169
|
.option(
|
|
164
170
|
'--schedule <time-in-minutes>',
|
|
165
171
|
"schedule in minutes for the pushed monitors. Setting `10`, for example, configures monitors which don't have an interval defined to run every 10 minutes.",
|
|
@@ -175,7 +181,7 @@ program
|
|
|
175
181
|
'--private-locations <locations...>',
|
|
176
182
|
'default list of private locations from which your monitors will run.'
|
|
177
183
|
)
|
|
178
|
-
.option('--url <url>', 'Kibana URL to upload the monitors')
|
|
184
|
+
.option('--url <url>', 'Kibana URL to upload the project monitors')
|
|
179
185
|
.option(
|
|
180
186
|
'--id <id>',
|
|
181
187
|
'project id that will be used for logically grouping monitors'
|
|
@@ -185,20 +191,23 @@ program
|
|
|
185
191
|
'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.'
|
|
186
192
|
)
|
|
187
193
|
.option('-y, --yes', 'skip all questions and run non-interactively')
|
|
188
|
-
|
|
194
|
+
|
|
189
195
|
.addOption(pattern)
|
|
196
|
+
.addOption(tags)
|
|
197
|
+
.addOption(match)
|
|
190
198
|
.addOption(params)
|
|
191
199
|
.addOption(playwrightOpts)
|
|
192
|
-
.
|
|
200
|
+
.addOption(configOpt)
|
|
201
|
+
.action(async cmdOpts => {
|
|
202
|
+
cmdOpts = { ...cmdOpts, ...program.opts() };
|
|
193
203
|
const workDir = cwd();
|
|
194
|
-
const tearDown = await globalSetup({ inline: false, ...
|
|
204
|
+
const tearDown = await globalSetup({ inline: false, ...cmdOpts }, [
|
|
195
205
|
workDir,
|
|
196
206
|
]);
|
|
197
207
|
try {
|
|
198
|
-
const settings = await loadSettings();
|
|
208
|
+
const settings = await loadSettings(cmdOpts.config);
|
|
199
209
|
const options = (await normalizeOptions(
|
|
200
210
|
{
|
|
201
|
-
...program.opts(),
|
|
202
211
|
...settings,
|
|
203
212
|
...cmdOpts,
|
|
204
213
|
},
|
|
@@ -244,8 +253,7 @@ program
|
|
|
244
253
|
.addOption(auth)
|
|
245
254
|
.action(async (cmdOpts: LocationCmdOptions) => {
|
|
246
255
|
const revert = installTransform();
|
|
247
|
-
const url = cmdOpts.url ?? (await loadSettings(true))?.url;
|
|
248
|
-
|
|
256
|
+
const url = cmdOpts.url ?? (await loadSettings(null, true))?.url;
|
|
249
257
|
try {
|
|
250
258
|
if (url && cmdOpts.auth) {
|
|
251
259
|
const allLocations = await getLocations({
|
package/src/common_types.ts
CHANGED
|
@@ -200,13 +200,18 @@ export type ThrottlingOptions = {
|
|
|
200
200
|
latency?: number;
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
+
type GrepOptions = {
|
|
204
|
+
pattern?: string;
|
|
205
|
+
tags?: Array<string>;
|
|
206
|
+
match?: string;
|
|
207
|
+
};
|
|
208
|
+
|
|
203
209
|
type BaseArgs = {
|
|
204
210
|
params?: Params;
|
|
205
211
|
screenshots?: ScreenshotOptions;
|
|
206
212
|
dryRun?: boolean;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
tags?: Array<string>;
|
|
213
|
+
config?: string;
|
|
214
|
+
auth?: string;
|
|
210
215
|
outfd?: number;
|
|
211
216
|
wsEndpoint?: string;
|
|
212
217
|
pauseOnError?: boolean;
|
|
@@ -219,7 +224,9 @@ type BaseArgs = {
|
|
|
219
224
|
};
|
|
220
225
|
|
|
221
226
|
export type CliArgs = BaseArgs & {
|
|
222
|
-
|
|
227
|
+
pattern?: string;
|
|
228
|
+
match?: string;
|
|
229
|
+
tags?: Array<string>;
|
|
223
230
|
reporter?: BuiltInReporterName;
|
|
224
231
|
inline?: boolean;
|
|
225
232
|
require?: Array<string>;
|
|
@@ -239,6 +246,7 @@ export type RunOptions = BaseArgs & {
|
|
|
239
246
|
environment?: string;
|
|
240
247
|
networkConditions?: NetworkConditions;
|
|
241
248
|
reporter?: BuiltInReporterName | ReporterInstance;
|
|
249
|
+
grepOpts?: GrepOptions;
|
|
242
250
|
};
|
|
243
251
|
|
|
244
252
|
export type PushOptions = Partial<ProjectSettings> &
|
|
@@ -246,9 +254,11 @@ export type PushOptions = Partial<ProjectSettings> &
|
|
|
246
254
|
auth: string;
|
|
247
255
|
kibanaVersion?: string;
|
|
248
256
|
yes?: boolean;
|
|
257
|
+
tags?: Array<string>;
|
|
249
258
|
alert?: AlertConfig;
|
|
250
259
|
retestOnFailure?: MonitorConfig['retestOnFailure'];
|
|
251
260
|
enabled?: boolean;
|
|
261
|
+
grepOpts?: GrepOptions;
|
|
252
262
|
};
|
|
253
263
|
|
|
254
264
|
export type ProjectSettings = {
|
package/src/core/runner.ts
CHANGED
|
@@ -404,8 +404,7 @@ export default class Runner {
|
|
|
404
404
|
|
|
405
405
|
buildMonitors(options: PushOptions) {
|
|
406
406
|
/**
|
|
407
|
-
* Update the global monitor configuration required for
|
|
408
|
-
* setting defaults
|
|
407
|
+
* Update the global monitor configuration required for setting defaults
|
|
409
408
|
*/
|
|
410
409
|
this.updateMonitor({
|
|
411
410
|
throttling: options.throttling,
|
|
@@ -430,11 +429,23 @@ export default class Runner {
|
|
|
430
429
|
);
|
|
431
430
|
}
|
|
432
431
|
/**
|
|
433
|
-
*
|
|
434
|
-
*
|
|
432
|
+
* Before pushing a browser monitor, three things need to be done:
|
|
433
|
+
*
|
|
434
|
+
* - execute callback `monitor.use` in particular to get monitor configurations
|
|
435
|
+
* - update the monitor config with global configuration
|
|
436
|
+
* - filter out monitors based on matched tags and name after applying both
|
|
437
|
+
* global and local monitor configurations
|
|
435
438
|
*/
|
|
436
439
|
journey.callback({ params: options.params } as any);
|
|
437
440
|
journey.monitor.update(this.monitor?.config);
|
|
441
|
+
if (
|
|
442
|
+
!journey.monitor.isMatch(
|
|
443
|
+
options.grepOpts?.match,
|
|
444
|
+
options.grepOpts?.tags
|
|
445
|
+
)
|
|
446
|
+
) {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
438
449
|
journey.monitor.validate();
|
|
439
450
|
monitors.push(journey.monitor);
|
|
440
451
|
}
|
|
@@ -454,7 +465,7 @@ export default class Runner {
|
|
|
454
465
|
params: options.params,
|
|
455
466
|
}).catch(e => (this.hookError = e));
|
|
456
467
|
|
|
457
|
-
const { dryRun,
|
|
468
|
+
const { dryRun, grepOpts } = options;
|
|
458
469
|
/**
|
|
459
470
|
* Skip other journeys when using `.only`
|
|
460
471
|
*/
|
|
@@ -471,7 +482,7 @@ export default class Runner {
|
|
|
471
482
|
this.#reporter.onJourneyRegister?.(journey);
|
|
472
483
|
continue;
|
|
473
484
|
}
|
|
474
|
-
if (!journey.isMatch(match, tags) || journey.skip) {
|
|
485
|
+
if (!journey.isMatch(grepOpts?.match, grepOpts?.tags) || journey.skip) {
|
|
475
486
|
continue;
|
|
476
487
|
}
|
|
477
488
|
const journeyResult: JourneyResult = this.hookError
|
package/src/dsl/monitor.ts
CHANGED
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
Params,
|
|
34
34
|
PlaywrightOptions,
|
|
35
35
|
} from '../common_types';
|
|
36
|
-
import { indent } from '../helpers';
|
|
36
|
+
import { indent, isMatch } from '../helpers';
|
|
37
37
|
import { LocationsMap } from '../locations/public-locations';
|
|
38
38
|
|
|
39
39
|
export type SyntheticsLocationsType = keyof typeof LocationsMap;
|
|
@@ -129,6 +129,18 @@ export class Monitor {
|
|
|
129
129
|
this.filter = filter;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Matches monitors based on the provided args. Proitize tags over match
|
|
134
|
+
*/
|
|
135
|
+
isMatch(matchPattern: string, tagsPattern: Array<string>) {
|
|
136
|
+
return isMatch(
|
|
137
|
+
this.config.tags,
|
|
138
|
+
this.config.name,
|
|
139
|
+
tagsPattern,
|
|
140
|
+
matchPattern
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
132
144
|
/**
|
|
133
145
|
* Hash is used to identify if the monitor has changed since the last time
|
|
134
146
|
* it was pushed to Kibana. Change is based on three factors:
|
package/src/generator/index.ts
CHANGED
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
* THE SOFTWARE.
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
|
+
import { URL } from 'url';
|
|
25
26
|
import { execSync } from 'child_process';
|
|
26
27
|
import { existsSync } from 'fs';
|
|
27
28
|
import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
28
29
|
import { bold, cyan, yellow } from 'kleur/colors';
|
|
29
30
|
import { join, relative, dirname, basename } from 'path';
|
|
30
31
|
// @ts-ignore-next-line: has no exported member 'Input'
|
|
31
|
-
import { prompt, Input } from 'enquirer';
|
|
32
|
+
import { prompt, Input, Password } from 'enquirer';
|
|
32
33
|
import { getProjectApiKeyURL, progress, write as stdWrite } from '../helpers';
|
|
33
34
|
import {
|
|
34
35
|
getPackageManager,
|
|
@@ -60,6 +61,8 @@ export const REGULAR_FILES_PATH = [
|
|
|
60
61
|
];
|
|
61
62
|
export const CONFIG_PATH = 'synthetics.config.ts';
|
|
62
63
|
|
|
64
|
+
const IS_URL = new RegExp('^(https?:\\/\\/)');
|
|
65
|
+
|
|
63
66
|
export class Generator {
|
|
64
67
|
pkgManager = 'npm';
|
|
65
68
|
constructor(public projectDir: string) {}
|
|
@@ -80,29 +83,30 @@ export class Generator {
|
|
|
80
83
|
return JSON.parse(process.env.TEST_QUESTIONS);
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
const { onCloud } = await prompt<{ onCloud: string }>({
|
|
84
|
-
type: 'confirm',
|
|
85
|
-
name: 'onCloud',
|
|
86
|
-
initial: 'y',
|
|
87
|
-
message: 'Do you use Elastic Cloud',
|
|
88
|
-
});
|
|
89
86
|
const url = await new Input({
|
|
90
|
-
header: onCloud
|
|
91
|
-
? yellow(
|
|
92
|
-
'Get cloud.id from your deployment https://www.elastic.co/guide/en/cloud/current/ec-cloud-id.html'
|
|
93
|
-
)
|
|
94
|
-
: '',
|
|
95
|
-
message: onCloud
|
|
96
|
-
? 'What is your cloud.id'
|
|
97
|
-
: 'What is the url of your Kibana instance',
|
|
98
87
|
name: 'url',
|
|
88
|
+
message: 'Enter Elastic Kibana URL or Cloud ID',
|
|
99
89
|
required: true,
|
|
100
|
-
|
|
101
|
-
|
|
90
|
+
validate(value) {
|
|
91
|
+
try {
|
|
92
|
+
if (!IS_URL.test(value)) {
|
|
93
|
+
value = cloudIDToKibanaURL(value);
|
|
94
|
+
}
|
|
95
|
+
new URL(value);
|
|
96
|
+
return true;
|
|
97
|
+
} catch (e) {
|
|
98
|
+
return 'Invalid URL or Cloud ID';
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
result(value: string) {
|
|
102
|
+
if (!IS_URL.test(value)) {
|
|
103
|
+
value = cloudIDToKibanaURL(value);
|
|
104
|
+
}
|
|
105
|
+
return value;
|
|
102
106
|
},
|
|
103
107
|
}).run();
|
|
104
108
|
|
|
105
|
-
const auth = await new
|
|
109
|
+
const auth = await new Password({
|
|
106
110
|
name: 'auth',
|
|
107
111
|
header: yellow(
|
|
108
112
|
`Generate API key from Kibana ${getProjectApiKeyURL(url)}`
|
|
@@ -249,7 +253,7 @@ export class Generator {
|
|
|
249
253
|
}
|
|
250
254
|
// Add push command
|
|
251
255
|
if (!pkgJSON.scripts.push) {
|
|
252
|
-
pkgJSON.scripts.push =
|
|
256
|
+
pkgJSON.scripts.push = `npx @elastic/synthetics push`;
|
|
253
257
|
}
|
|
254
258
|
|
|
255
259
|
await this.createFile(
|
|
@@ -287,7 +291,7 @@ All set, you can run below commands inside: ${this.projectDir}:
|
|
|
287
291
|
)}
|
|
288
292
|
|
|
289
293
|
${yellow(
|
|
290
|
-
'
|
|
294
|
+
'Configure API Key via `SYNTHETICS_API_KEY` env variable or --auth CLI flag.'
|
|
291
295
|
)}
|
|
292
296
|
|
|
293
297
|
Visit https://www.elastic.co/guide/en/observability/current/synthetic-run-tests.html to learn more.
|
package/src/options.ts
CHANGED
|
@@ -26,74 +26,49 @@
|
|
|
26
26
|
import merge from 'deepmerge';
|
|
27
27
|
import { createOption } from 'commander';
|
|
28
28
|
import { readConfig } from './config';
|
|
29
|
-
import type { CliArgs,
|
|
30
|
-
import { THROTTLING_WARNING_MSG,
|
|
29
|
+
import type { CliArgs, RunOptions } from './common_types';
|
|
30
|
+
import { THROTTLING_WARNING_MSG, warn } from './helpers';
|
|
31
31
|
|
|
32
32
|
type Mode = 'run' | 'push';
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Normalize the options passed via CLI and Synthetics config file
|
|
36
|
+
*
|
|
37
|
+
* Order of preference for options:
|
|
38
|
+
* 1. Local options configured via Runner API
|
|
39
|
+
* 2. CLI flags
|
|
40
|
+
* 3. Configuration file
|
|
41
|
+
*/
|
|
34
42
|
export async function normalizeOptions(
|
|
35
43
|
cliArgs: CliArgs,
|
|
36
44
|
mode: Mode = 'run'
|
|
37
45
|
): Promise<RunOptions> {
|
|
46
|
+
/**
|
|
47
|
+
* Move filtering flags from the top level to filter object
|
|
48
|
+
* and delete the old keys
|
|
49
|
+
*/
|
|
50
|
+
const grepOpts = {
|
|
51
|
+
pattern: cliArgs.pattern,
|
|
52
|
+
tags: cliArgs.tags,
|
|
53
|
+
match: cliArgs.match,
|
|
54
|
+
};
|
|
55
|
+
delete cliArgs.pattern;
|
|
56
|
+
delete cliArgs.tags;
|
|
57
|
+
delete cliArgs.match;
|
|
58
|
+
|
|
38
59
|
const options: RunOptions = {
|
|
39
60
|
...cliArgs,
|
|
61
|
+
grepOpts,
|
|
40
62
|
environment: process.env['NODE_ENV'] || 'development',
|
|
41
63
|
};
|
|
42
64
|
/**
|
|
43
|
-
*
|
|
44
|
-
* eventually by the Synthetics UI.
|
|
45
|
-
*/
|
|
46
|
-
if (cliArgs.richEvents) {
|
|
47
|
-
options.reporter = cliArgs.reporter ?? 'json';
|
|
48
|
-
options.ssblocks = true;
|
|
49
|
-
options.network = true;
|
|
50
|
-
options.trace = true;
|
|
51
|
-
options.quietExitCode = true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (cliArgs.capability) {
|
|
55
|
-
const supportedCapabilities = [
|
|
56
|
-
'trace',
|
|
57
|
-
'network',
|
|
58
|
-
'filmstrips',
|
|
59
|
-
'metrics',
|
|
60
|
-
'ssblocks',
|
|
61
|
-
];
|
|
62
|
-
/**
|
|
63
|
-
* trace - record chrome trace events(LCP, FCP, CLS, etc.) for all journeys
|
|
64
|
-
* network - capture network information for all journeys
|
|
65
|
-
* filmstrips - record detailed filmstrips for all journeys
|
|
66
|
-
* metrics - capture performance metrics (DOM Nodes, Heap size, etc.) for each step
|
|
67
|
-
* ssblocks - Dedupes the screenshots in to blocks to save storage space
|
|
68
|
-
*/
|
|
69
|
-
for (const flag of cliArgs.capability) {
|
|
70
|
-
if (supportedCapabilities.includes(flag)) {
|
|
71
|
-
options[flag] = true;
|
|
72
|
-
} else {
|
|
73
|
-
console.warn(
|
|
74
|
-
`Missing capability "${flag}", current supported capabilities are ${supportedCapabilities.join(
|
|
75
|
-
', '
|
|
76
|
-
)}`
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Validate and read synthetics config file
|
|
84
|
-
* based on the environment
|
|
65
|
+
* Validate and read synthetics config file based on the environment
|
|
85
66
|
*/
|
|
86
67
|
const config =
|
|
87
68
|
cliArgs.config || !cliArgs.inline
|
|
88
69
|
? await readConfig(options.environment, cliArgs.config)
|
|
89
70
|
: {};
|
|
90
71
|
|
|
91
|
-
/**
|
|
92
|
-
* Order of preference for options that are used while running are
|
|
93
|
-
* 1. Local options configured via Runner API
|
|
94
|
-
* 2. CLI flags
|
|
95
|
-
* 3. Configuration file
|
|
96
|
-
*/
|
|
97
72
|
options.params = Object.freeze(merge(config.params, cliArgs.params || {}));
|
|
98
73
|
|
|
99
74
|
/**
|
|
@@ -118,10 +93,49 @@ export async function normalizeOptions(
|
|
|
118
93
|
*/
|
|
119
94
|
switch (mode) {
|
|
120
95
|
case 'run':
|
|
96
|
+
if (cliArgs.capability) {
|
|
97
|
+
const supportedCapabilities = [
|
|
98
|
+
'trace',
|
|
99
|
+
'network',
|
|
100
|
+
'filmstrips',
|
|
101
|
+
'metrics',
|
|
102
|
+
'ssblocks',
|
|
103
|
+
];
|
|
104
|
+
/**
|
|
105
|
+
* trace - record chrome trace events(LCP, FCP, CLS, etc.) for all journeys
|
|
106
|
+
* network - capture network information for all journeys
|
|
107
|
+
* filmstrips - record detailed filmstrips for all journeys
|
|
108
|
+
* metrics - capture performance metrics (DOM Nodes, Heap size, etc.) for each step
|
|
109
|
+
* ssblocks - Dedupes the screenshots in to blocks to save storage space
|
|
110
|
+
*/
|
|
111
|
+
for (const flag of cliArgs.capability) {
|
|
112
|
+
if (supportedCapabilities.includes(flag)) {
|
|
113
|
+
options[flag] = true;
|
|
114
|
+
} else {
|
|
115
|
+
console.warn(
|
|
116
|
+
`Missing capability "${flag}", current supported capabilities are ${supportedCapabilities.join(
|
|
117
|
+
', '
|
|
118
|
+
)}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Group all events that can be consumed by heartbeat and
|
|
126
|
+
* eventually by the Synthetics UI.
|
|
127
|
+
*/
|
|
128
|
+
if (cliArgs.richEvents) {
|
|
129
|
+
options.reporter = cliArgs.reporter ?? 'json';
|
|
130
|
+
options.ssblocks = true;
|
|
131
|
+
options.network = true;
|
|
132
|
+
options.trace = true;
|
|
133
|
+
options.quietExitCode = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
121
136
|
options.screenshots = cliArgs.screenshots ?? 'on';
|
|
122
137
|
break;
|
|
123
138
|
case 'push':
|
|
124
|
-
validatePushOptions(options as PushOptions);
|
|
125
139
|
/**
|
|
126
140
|
* Merge the default monitor config from synthetics.config.ts file
|
|
127
141
|
* with the CLI options passed via push command
|
|
@@ -152,14 +166,6 @@ export function getHeadlessFlag(
|
|
|
152
166
|
return configHeadless ?? true;
|
|
153
167
|
}
|
|
154
168
|
|
|
155
|
-
export function validatePushOptions(opts: PushOptions) {
|
|
156
|
-
if (opts.tags || opts.match) {
|
|
157
|
-
throw error(`Aborted. Invalid CLI flags.
|
|
158
|
-
|
|
159
|
-
Tags and Match are not supported in push command.`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
169
|
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
|
164
170
|
function toObject(value: boolean | Record<string, any>): Record<string, any> {
|
|
165
171
|
const defaulVal = {};
|
|
@@ -183,19 +189,17 @@ export function parseThrottling() {
|
|
|
183
189
|
export function getCommonCommandOpts() {
|
|
184
190
|
const params = createOption(
|
|
185
191
|
'-p, --params <jsonstring>',
|
|
186
|
-
'JSON object that
|
|
187
|
-
);
|
|
188
|
-
params.argParser(JSON.parse);
|
|
192
|
+
'JSON object that defines any variables your tests require.'
|
|
193
|
+
).argParser(JSON.parse);
|
|
189
194
|
|
|
190
195
|
const playwrightOpts = createOption(
|
|
191
196
|
'--playwright-options <jsonstring>',
|
|
192
197
|
'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.'
|
|
193
|
-
);
|
|
194
|
-
playwrightOpts.argParser(JSON.parse);
|
|
198
|
+
).argParser(JSON.parse);
|
|
195
199
|
|
|
196
200
|
const pattern = createOption(
|
|
197
201
|
'--pattern <pattern>',
|
|
198
|
-
'RegExp pattern to match journey
|
|
202
|
+
'RegExp pattern to match journey files in the current working directory (default: /*.journey.(ts|js)$/)'
|
|
199
203
|
);
|
|
200
204
|
|
|
201
205
|
const apiDocsLink =
|
|
@@ -208,11 +212,28 @@ export function getCommonCommandOpts() {
|
|
|
208
212
|
.env('SYNTHETICS_API_KEY')
|
|
209
213
|
.makeOptionMandatory(true);
|
|
210
214
|
|
|
215
|
+
const configOpt = createOption(
|
|
216
|
+
'-c, --config <path>',
|
|
217
|
+
'path to the configuration file (default: synthetics.config.(js|ts))'
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const tags = createOption(
|
|
221
|
+
'--tags <name...>',
|
|
222
|
+
'run/push tests with the tag(s) matching a pattern'
|
|
223
|
+
);
|
|
224
|
+
const match = createOption(
|
|
225
|
+
'--match <name>',
|
|
226
|
+
'run/push tests with a name or tags that matches a pattern'
|
|
227
|
+
);
|
|
228
|
+
|
|
211
229
|
return {
|
|
212
230
|
auth,
|
|
213
231
|
authMandatory,
|
|
214
232
|
params,
|
|
215
233
|
playwrightOpts,
|
|
216
234
|
pattern,
|
|
235
|
+
configOpt,
|
|
236
|
+
tags,
|
|
237
|
+
match,
|
|
217
238
|
};
|
|
218
239
|
}
|
package/src/push/index.ts
CHANGED
|
@@ -58,13 +58,16 @@ import {
|
|
|
58
58
|
isLightweightMonitorSupported,
|
|
59
59
|
logDiff,
|
|
60
60
|
} from './utils';
|
|
61
|
+
import { log } from '../core/logger';
|
|
61
62
|
|
|
62
63
|
export async function push(monitors: Monitor[], options: PushOptions) {
|
|
63
64
|
const duplicates = trackDuplicates(monitors);
|
|
64
65
|
if (duplicates.size > 0) {
|
|
65
66
|
throw error(formatDuplicateError(duplicates));
|
|
66
67
|
}
|
|
67
|
-
progress(
|
|
68
|
+
progress(
|
|
69
|
+
`Pushing monitors for '${options.id}' project in kibana '${options.space}' space`
|
|
70
|
+
);
|
|
68
71
|
|
|
69
72
|
/**
|
|
70
73
|
* Legacy API for kibana which does not support bulk operations
|
|
@@ -97,8 +100,17 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
if (removedIDs.size > 0) {
|
|
103
|
+
log(`deleting monitor ids: ${Array.from(removedIDs.keys()).join(', ')}`);
|
|
100
104
|
if (updatedMonitors.size === 0 && unchangedIDs.size === 0) {
|
|
101
|
-
await
|
|
105
|
+
await confirmDelete(
|
|
106
|
+
`Pushing without any monitors will delete all monitors associated with the project.\n Do you want to continue?`,
|
|
107
|
+
options.yes
|
|
108
|
+
);
|
|
109
|
+
} else {
|
|
110
|
+
await confirmDelete(
|
|
111
|
+
`Deleting ${removedIDs.size} monitors. Do you want to continue?`,
|
|
112
|
+
options.yes
|
|
113
|
+
);
|
|
102
114
|
}
|
|
103
115
|
const chunks = getChunks(Array.from(removedIDs), CHUNK_SIZE);
|
|
104
116
|
for (const chunk of chunks) {
|
|
@@ -112,22 +124,22 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
112
124
|
done(`Pushed: ${grey(getMonitorManagementURL(options.url))}`);
|
|
113
125
|
}
|
|
114
126
|
|
|
115
|
-
async function
|
|
116
|
-
|
|
117
|
-
const { deleteAll } = await prompt<{ deleteAll: boolean }>({
|
|
127
|
+
async function confirmDelete(message: string, skip: boolean) {
|
|
128
|
+
const { deleteMonitors } = await prompt<{ deleteMonitors: boolean }>({
|
|
118
129
|
type: 'confirm',
|
|
119
130
|
skip() {
|
|
120
|
-
|
|
131
|
+
write('');
|
|
132
|
+
if (skip || !process.stdout.isTTY) {
|
|
121
133
|
this.initial = process.env.TEST_OVERRIDE ?? true;
|
|
122
134
|
return true;
|
|
123
135
|
}
|
|
124
136
|
return false;
|
|
125
137
|
},
|
|
126
|
-
name: '
|
|
127
|
-
message
|
|
138
|
+
name: 'deleteMonitors',
|
|
139
|
+
message,
|
|
128
140
|
initial: false,
|
|
129
141
|
});
|
|
130
|
-
if (!
|
|
142
|
+
if (!deleteMonitors) {
|
|
131
143
|
throw warn('Push command Aborted');
|
|
132
144
|
}
|
|
133
145
|
}
|
|
@@ -160,9 +172,12 @@ export function formatDuplicateError(monitors: Set<Monitor>) {
|
|
|
160
172
|
|
|
161
173
|
const INSTALLATION_HELP = `Run 'npx @elastic/synthetics init' to create project with default settings.`;
|
|
162
174
|
|
|
163
|
-
export async function loadSettings(ignoreMissing = false) {
|
|
175
|
+
export async function loadSettings(configPath, ignoreMissing = false) {
|
|
164
176
|
try {
|
|
165
|
-
const config = await readConfig(
|
|
177
|
+
const config = await readConfig(
|
|
178
|
+
process.env['NODE_ENV'] || 'development',
|
|
179
|
+
configPath
|
|
180
|
+
);
|
|
166
181
|
// Missing config file, fake throw to capture as missing file
|
|
167
182
|
if (Object.keys(config).length === 0) {
|
|
168
183
|
throw '';
|
|
@@ -216,9 +231,13 @@ ${reason}
|
|
|
216
231
|
${INSTALLATION_HELP}`);
|
|
217
232
|
}
|
|
218
233
|
|
|
219
|
-
async function overrideSettings(
|
|
234
|
+
async function overrideSettings(
|
|
235
|
+
configPath,
|
|
236
|
+
oldValue: string,
|
|
237
|
+
newValue: string
|
|
238
|
+
) {
|
|
220
239
|
const cwd = process.cwd();
|
|
221
|
-
|
|
240
|
+
configPath = configPath ?? (await findSyntheticsConfig(cwd, cwd));
|
|
222
241
|
if (!configPath) {
|
|
223
242
|
throw warn(`Unable to find synthetics config file: ${configPath}`);
|
|
224
243
|
}
|
|
@@ -256,7 +275,7 @@ export async function catchIncorrectSettings(
|
|
|
256
275
|
}
|
|
257
276
|
}
|
|
258
277
|
if (override) {
|
|
259
|
-
await overrideSettings(settings.id, options.id);
|
|
278
|
+
await overrideSettings(options.config, settings.id, options.id);
|
|
260
279
|
}
|
|
261
280
|
}
|
|
262
281
|
|
|
@@ -279,7 +298,10 @@ export async function pushLegacy(monitors: Monitor[], options: PushOptions) {
|
|
|
279
298
|
);
|
|
280
299
|
}
|
|
281
300
|
} else {
|
|
282
|
-
await
|
|
301
|
+
await confirmDelete(
|
|
302
|
+
`Pushing without any monitors will delete all monitors associated with the project.\n Do you want to continue?`,
|
|
303
|
+
options.yes
|
|
304
|
+
);
|
|
283
305
|
}
|
|
284
306
|
await liveProgress(
|
|
285
307
|
createMonitorsLegacy({ schemas, keepStale: false, options }),
|