@elastic/synthetics 1.14.0 → 1.16.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 +23 -4
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +9 -16
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/gatherer.d.ts +3 -1
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +42 -35
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/globals.d.ts +27 -0
- package/dist/core/globals.d.ts.map +1 -0
- package/dist/core/globals.js +41 -0
- package/dist/core/globals.js.map +1 -0
- package/dist/core/index.d.ts +0 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +13 -25
- package/dist/core/index.js.map +1 -1
- package/dist/core/mfa.d.ts +4 -0
- package/dist/core/mfa.d.ts.map +1 -1
- package/dist/core/mfa.js.map +1 -1
- package/dist/core/runner.d.ts +30 -39
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +174 -179
- package/dist/core/runner.js.map +1 -1
- package/dist/dsl/journey.d.ts +24 -16
- package/dist/dsl/journey.d.ts.map +1 -1
- package/dist/dsl/journey.js +28 -15
- package/dist/dsl/journey.js.map +1 -1
- package/dist/dsl/monitor.d.ts +1 -0
- package/dist/dsl/monitor.d.ts.map +1 -1
- package/dist/dsl/monitor.js.map +1 -1
- package/dist/dsl/step.d.ts +12 -6
- package/dist/dsl/step.d.ts.map +1 -1
- package/dist/dsl/step.js +11 -3
- package/dist/dsl/step.js.map +1 -1
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +1 -0
- package/dist/helpers.js.map +1 -1
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +1 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +18 -0
- package/dist/options.js.map +1 -1
- package/dist/push/index.d.ts.map +1 -1
- package/dist/push/index.js +1 -1
- package/dist/push/index.js.map +1 -1
- package/dist/push/monitor.d.ts +1 -0
- package/dist/push/monitor.d.ts.map +1 -1
- package/dist/push/monitor.js +18 -1
- package/dist/push/monitor.js.map +1 -1
- package/dist/reporters/base.d.ts +3 -3
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js +6 -6
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/build_kite_cli.d.ts +2 -4
- package/dist/reporters/build_kite_cli.d.ts.map +1 -1
- package/dist/reporters/build_kite_cli.js +8 -10
- package/dist/reporters/build_kite_cli.js.map +1 -1
- package/dist/reporters/json.d.ts +4 -9
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +31 -40
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/junit.d.ts +2 -2
- 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/package.json +2 -2
- package/src/cli.ts +24 -5
- package/src/common_types.ts +9 -16
- package/src/core/gatherer.ts +44 -37
- package/src/core/globals.ts +37 -0
- package/src/core/index.ts +12 -25
- package/src/core/mfa.ts +5 -0
- package/src/core/runner.ts +201 -202
- package/src/dsl/journey.ts +42 -23
- package/src/dsl/monitor.ts +1 -0
- package/src/dsl/step.ts +16 -7
- package/src/helpers.ts +4 -3
- package/src/index.ts +2 -2
- package/src/options.ts +22 -0
- package/src/push/index.ts +3 -4
- package/src/push/monitor.ts +20 -0
- package/src/reporters/base.ts +8 -12
- package/src/reporters/build_kite_cli.ts +13 -18
- package/src/reporters/json.ts +35 -52
- package/src/reporters/junit.ts +8 -10
package/src/core/runner.ts
CHANGED
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
} from '../helpers';
|
|
38
38
|
import {
|
|
39
39
|
HooksCallback,
|
|
40
|
-
Params,
|
|
41
40
|
HooksArgs,
|
|
42
41
|
Driver,
|
|
43
42
|
Screenshot,
|
|
@@ -47,7 +46,6 @@ import {
|
|
|
47
46
|
PushOptions,
|
|
48
47
|
} from '../common_types';
|
|
49
48
|
import {
|
|
50
|
-
PluginManager,
|
|
51
49
|
PerformanceManager,
|
|
52
50
|
filterBrowserMessages,
|
|
53
51
|
} from '../plugins';
|
|
@@ -58,50 +56,49 @@ import { Monitor, MonitorConfig } from '../dsl/monitor';
|
|
|
58
56
|
type HookType = 'beforeAll' | 'afterAll';
|
|
59
57
|
export type SuiteHooks = Record<HookType, Array<HooksCallback>>;
|
|
60
58
|
|
|
61
|
-
type JourneyContext = {
|
|
62
|
-
params?: Params;
|
|
63
|
-
browserDelay: number;
|
|
64
|
-
start: number;
|
|
65
|
-
driver: Driver;
|
|
66
|
-
pluginManager: PluginManager;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
59
|
type RunResult = Record<string, JourneyResult>;
|
|
70
60
|
|
|
71
|
-
export
|
|
61
|
+
export interface RunnerInfo {
|
|
62
|
+
/**
|
|
63
|
+
* Processed configuration from the CLI args and the config file
|
|
64
|
+
*/
|
|
65
|
+
readonly config: RunOptions;
|
|
66
|
+
/**
|
|
67
|
+
* Currently active journey
|
|
68
|
+
*/
|
|
69
|
+
readonly journey: Journey | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* All registerd journeys
|
|
72
|
+
*/
|
|
73
|
+
readonly journeys: Journey[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default class Runner implements RunnerInfo {
|
|
72
77
|
#active = false;
|
|
73
78
|
#reporter: Reporter;
|
|
74
79
|
#currentJourney?: Journey = null;
|
|
75
|
-
journeys: Journey[] = [];
|
|
76
|
-
hooks: SuiteHooks = { beforeAll: [], afterAll: [] };
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
#journeys: Journey[] = [];
|
|
81
|
+
#hooks: SuiteHooks = { beforeAll: [], afterAll: [] };
|
|
82
|
+
#screenshotPath = join(CACHE_PATH, 'screenshots');
|
|
83
|
+
#driver?: Driver;
|
|
84
|
+
#browserDelay = -1;
|
|
85
|
+
#hookError: Error | undefined;
|
|
86
|
+
#monitor?: Monitor;
|
|
87
|
+
config: RunOptions;
|
|
80
88
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
* For each journey we create the screenshots folder for
|
|
92
|
-
* caching all screenshots and clear them at end of each journey
|
|
93
|
-
*/
|
|
94
|
-
await mkdir(this.screenshotPath, { recursive: true });
|
|
95
|
-
return {
|
|
96
|
-
start,
|
|
97
|
-
browserDelay: start - browserStart,
|
|
98
|
-
params: options.params,
|
|
99
|
-
driver,
|
|
100
|
-
pluginManager,
|
|
101
|
-
};
|
|
89
|
+
get journey() {
|
|
90
|
+
return this.#currentJourney;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get journeys() {
|
|
94
|
+
return this.#journeys;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get hooks() {
|
|
98
|
+
return this.#hooks;
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
async captureScreenshot(page: Driver['page'], step: Step) {
|
|
101
|
+
private async captureScreenshot(page: Driver['page'], step: Step) {
|
|
105
102
|
try {
|
|
106
103
|
const buffer = await page.screenshot({
|
|
107
104
|
type: 'jpeg',
|
|
@@ -120,7 +117,7 @@ export default class Runner {
|
|
|
120
117
|
data: buffer.toString('base64'),
|
|
121
118
|
};
|
|
122
119
|
await writeFile(
|
|
123
|
-
join(
|
|
120
|
+
join(this.#screenshotPath, fileName),
|
|
124
121
|
JSON.stringify(screenshot)
|
|
125
122
|
);
|
|
126
123
|
log(`Runner: captured screenshot for (${step.name})`);
|
|
@@ -130,28 +127,28 @@ export default class Runner {
|
|
|
130
127
|
}
|
|
131
128
|
}
|
|
132
129
|
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
_addHook(type: HookType, callback: HooksCallback) {
|
|
131
|
+
this.#hooks[type].push(callback);
|
|
135
132
|
}
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
this.
|
|
134
|
+
private buildHookArgs() {
|
|
135
|
+
return { env: this.config.environment, params: this.config.params, info: this as RunnerInfo };
|
|
139
136
|
}
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
if (!this
|
|
143
|
-
this
|
|
138
|
+
_updateMonitor(config: MonitorConfig) {
|
|
139
|
+
if (!this.#monitor) {
|
|
140
|
+
this.#monitor = new Monitor(config);
|
|
144
141
|
return;
|
|
145
142
|
}
|
|
146
|
-
this
|
|
143
|
+
this.#monitor.update(config);
|
|
147
144
|
}
|
|
148
145
|
|
|
149
|
-
|
|
150
|
-
this
|
|
146
|
+
_addJourney(journey: Journey) {
|
|
147
|
+
this.#journeys.push(journey);
|
|
151
148
|
this.#currentJourney = journey;
|
|
152
149
|
}
|
|
153
150
|
|
|
154
|
-
setReporter(options: RunOptions) {
|
|
151
|
+
private setReporter(options: RunOptions) {
|
|
155
152
|
/**
|
|
156
153
|
* Set up the corresponding reporter and fallback
|
|
157
154
|
* to default reporter if not provided
|
|
@@ -164,75 +161,72 @@ export default class Runner {
|
|
|
164
161
|
this.#reporter = new Reporter({ fd: outfd, dryRun });
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
async runBeforeAllHook(args: HooksArgs) {
|
|
164
|
+
async #runBeforeAllHook(args: HooksArgs) {
|
|
168
165
|
log(`Runner: beforeAll hooks`);
|
|
169
|
-
await runParallel(this
|
|
166
|
+
await runParallel(this.#hooks.beforeAll, args);
|
|
170
167
|
}
|
|
171
168
|
|
|
172
|
-
async runAfterAllHook(args: HooksArgs) {
|
|
169
|
+
async #runAfterAllHook(args: HooksArgs) {
|
|
173
170
|
log(`Runner: afterAll hooks`);
|
|
174
|
-
await runParallel(this
|
|
171
|
+
await runParallel(this.#hooks.afterAll, args);
|
|
175
172
|
}
|
|
176
173
|
|
|
177
|
-
async runBeforeHook(journey: Journey, args: HooksArgs) {
|
|
174
|
+
async #runBeforeHook(journey: Journey, args: HooksArgs) {
|
|
178
175
|
log(`Runner: before hooks for (${journey.name})`);
|
|
179
|
-
await runParallel(journey.
|
|
176
|
+
await runParallel(journey._getHook('before'), args);
|
|
180
177
|
}
|
|
181
178
|
|
|
182
|
-
async runAfterHook(journey: Journey, args: HooksArgs) {
|
|
179
|
+
async #runAfterHook(journey: Journey, args: HooksArgs) {
|
|
183
180
|
log(`Runner: after hooks for (${journey.name})`);
|
|
184
|
-
await runParallel(journey.
|
|
181
|
+
await runParallel(journey._getHook('after'), args);
|
|
185
182
|
}
|
|
186
183
|
|
|
187
|
-
async runStep(
|
|
184
|
+
async #runStep(
|
|
188
185
|
step: Step,
|
|
189
|
-
context: JourneyContext,
|
|
190
186
|
options: RunOptions
|
|
191
187
|
): Promise<StepResult> {
|
|
192
|
-
const data: StepResult = {
|
|
193
|
-
status: 'succeeded',
|
|
194
|
-
};
|
|
195
188
|
log(`Runner: start step (${step.name})`);
|
|
196
189
|
const { metrics, screenshots, filmstrips, trace } = options;
|
|
197
|
-
const { driver, pluginManager } = context;
|
|
198
190
|
/**
|
|
199
191
|
* URL needs to be the first navigation request of any step
|
|
200
192
|
* Listening for request solves the case where `about:blank` would be
|
|
201
193
|
* reported for failed navigations
|
|
202
194
|
*/
|
|
203
195
|
const captureUrl = req => {
|
|
204
|
-
if (!
|
|
205
|
-
|
|
196
|
+
if (!step.url && req.isNavigationRequest()) {
|
|
197
|
+
step.url = req.url();
|
|
206
198
|
}
|
|
207
|
-
driver.context.off('request', captureUrl);
|
|
199
|
+
this.#driver.context.off('request', captureUrl);
|
|
208
200
|
};
|
|
209
|
-
driver.context.on('request', captureUrl);
|
|
201
|
+
this.#driver.context.on('request', captureUrl);
|
|
210
202
|
|
|
203
|
+
const data: StepResult = {};
|
|
211
204
|
const traceEnabled = trace || filmstrips;
|
|
212
205
|
try {
|
|
213
206
|
/**
|
|
214
207
|
* Set up plugin manager context and also register
|
|
215
208
|
* step level plugins
|
|
216
209
|
*/
|
|
217
|
-
pluginManager.onStep(step);
|
|
218
|
-
traceEnabled && (await pluginManager.start('trace'));
|
|
210
|
+
Gatherer.pluginManager.onStep(step);
|
|
211
|
+
traceEnabled && (await Gatherer.pluginManager.start('trace'));
|
|
219
212
|
// invoke the step callback by extracting to a variable to get better stack trace
|
|
220
|
-
const cb = step.
|
|
213
|
+
const cb = step.cb;
|
|
221
214
|
await cb();
|
|
215
|
+
step.status = "succeeded"
|
|
222
216
|
} catch (error) {
|
|
223
|
-
|
|
224
|
-
|
|
217
|
+
step.status = 'failed';
|
|
218
|
+
step.error = error;
|
|
225
219
|
} finally {
|
|
226
220
|
/**
|
|
227
221
|
* Collect all step level metrics and trace events
|
|
228
222
|
*/
|
|
229
223
|
if (metrics) {
|
|
230
224
|
data.pagemetrics = await (
|
|
231
|
-
pluginManager.get('performance') as PerformanceManager
|
|
225
|
+
Gatherer.pluginManager.get('performance') as PerformanceManager
|
|
232
226
|
).getMetrics();
|
|
233
227
|
}
|
|
234
228
|
if (traceEnabled) {
|
|
235
|
-
const traceOutput = await pluginManager.stop('trace');
|
|
229
|
+
const traceOutput = await Gatherer.pluginManager.stop('trace');
|
|
236
230
|
Object.assign(data, traceOutput);
|
|
237
231
|
}
|
|
238
232
|
/**
|
|
@@ -241,31 +235,30 @@ export default class Runner {
|
|
|
241
235
|
*
|
|
242
236
|
* Last open page will get us the correct screenshot
|
|
243
237
|
*/
|
|
244
|
-
const pages = driver.context.pages();
|
|
238
|
+
const pages = this.#driver.context.pages();
|
|
245
239
|
const page = pages[pages.length - 1];
|
|
246
240
|
if (page) {
|
|
247
|
-
|
|
241
|
+
step.url ??= page.url();
|
|
248
242
|
if (screenshots && screenshots !== 'off') {
|
|
249
243
|
await this.captureScreenshot(page, step);
|
|
250
244
|
}
|
|
251
245
|
}
|
|
252
246
|
}
|
|
253
247
|
log(`Runner: end step (${step.name})`);
|
|
254
|
-
return data
|
|
248
|
+
return data
|
|
255
249
|
}
|
|
256
250
|
|
|
257
|
-
async runSteps(
|
|
251
|
+
async #runSteps(
|
|
258
252
|
journey: Journey,
|
|
259
|
-
context: JourneyContext,
|
|
260
253
|
options: RunOptions
|
|
261
254
|
) {
|
|
262
255
|
const results: Array<StepResult> = [];
|
|
263
256
|
const isOnlyExists = journey.steps.filter(s => s.only).length > 0;
|
|
264
257
|
let skipStep = false;
|
|
265
258
|
for (const step of journey.steps) {
|
|
266
|
-
|
|
259
|
+
step._startTime = monotonicTimeInSeconds();
|
|
267
260
|
this.#reporter?.onStepStart?.(journey, step);
|
|
268
|
-
let data: StepResult = {
|
|
261
|
+
let data: StepResult = {};
|
|
269
262
|
/**
|
|
270
263
|
* Skip the step
|
|
271
264
|
* - if the step is marked as skip
|
|
@@ -277,20 +270,17 @@ export default class Runner {
|
|
|
277
270
|
(skipStep && !step.only) ||
|
|
278
271
|
(isOnlyExists && !step.only)
|
|
279
272
|
) {
|
|
280
|
-
|
|
273
|
+
step.status = 'skipped';
|
|
281
274
|
} else {
|
|
282
|
-
data = await this
|
|
275
|
+
data = await this.#runStep(step, options);
|
|
283
276
|
/**
|
|
284
277
|
* skip next steps if the previous step returns error
|
|
285
278
|
*/
|
|
286
|
-
if (
|
|
279
|
+
if (step.error && !step.soft) skipStep = true;
|
|
287
280
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
...data,
|
|
292
|
-
});
|
|
293
|
-
if (options.pauseOnError && data.error) {
|
|
281
|
+
step.duration = monotonicTimeInSeconds() - step._startTime;
|
|
282
|
+
this.#reporter?.onStepEnd?.(journey, step, data);
|
|
283
|
+
if (options.pauseOnError && step.error) {
|
|
294
284
|
await new Promise(r => process.stdin.on('data', r));
|
|
295
285
|
}
|
|
296
286
|
results.push(data);
|
|
@@ -298,121 +288,115 @@ export default class Runner {
|
|
|
298
288
|
return results;
|
|
299
289
|
}
|
|
300
290
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
291
|
+
async #startJourney(journey: Journey, options: RunOptions) {
|
|
292
|
+
journey._startTime = monotonicTimeInSeconds();
|
|
293
|
+
this.#driver = await Gatherer.setupDriver(options);
|
|
294
|
+
await Gatherer.beginRecording(this.#driver, options);
|
|
295
|
+
/**
|
|
296
|
+
* For each journey we create the screenshots folder for
|
|
297
|
+
* caching all screenshots and clear them at end of each journey
|
|
298
|
+
*/
|
|
299
|
+
await mkdir(this.#screenshotPath, { recursive: true });
|
|
300
|
+
const params = options.params
|
|
305
301
|
this.#reporter?.onJourneyStart?.(journey, {
|
|
306
|
-
timestamp,
|
|
302
|
+
timestamp: getTimestamp(),
|
|
307
303
|
params,
|
|
308
304
|
});
|
|
309
305
|
/**
|
|
310
306
|
* Exeucute the journey callback which registers the steps for current journey
|
|
311
307
|
*/
|
|
312
|
-
journey.
|
|
308
|
+
journey.cb({ ...this.#driver, params, info: this });
|
|
313
309
|
}
|
|
314
310
|
|
|
315
|
-
async endJourney(
|
|
316
|
-
journey,
|
|
317
|
-
result:
|
|
311
|
+
async #endJourney(
|
|
312
|
+
journey: Journey,
|
|
313
|
+
result: JourneyResult,
|
|
318
314
|
options: RunOptions
|
|
319
315
|
) {
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
const
|
|
316
|
+
// Enhance the journey results
|
|
317
|
+
const pOutput = await Gatherer.pluginManager.output();
|
|
318
|
+
const bConsole = filterBrowserMessages(pOutput.browserconsole, journey.status);
|
|
323
319
|
await this.#reporter?.onJourneyEnd?.(journey, {
|
|
324
|
-
|
|
325
|
-
error,
|
|
326
|
-
start,
|
|
327
|
-
end,
|
|
328
|
-
browserDelay,
|
|
320
|
+
browserDelay: this.#browserDelay,
|
|
329
321
|
timestamp: getTimestamp(),
|
|
330
322
|
options,
|
|
331
|
-
|
|
332
|
-
browserconsole:
|
|
333
|
-
pluginOutput.browserconsole,
|
|
334
|
-
status
|
|
335
|
-
),
|
|
323
|
+
networkinfo: pOutput.networkinfo,
|
|
324
|
+
browserconsole: bConsole,
|
|
336
325
|
});
|
|
326
|
+
await Gatherer.endRecording();
|
|
327
|
+
await Gatherer.dispose(this.#driver)
|
|
337
328
|
// clear screenshots cache after each journey
|
|
338
|
-
await rm(
|
|
329
|
+
await rm(this.#screenshotPath, { recursive: true, force: true });
|
|
330
|
+
return Object.assign(result, {
|
|
331
|
+
networkinfo: pOutput.networkinfo,
|
|
332
|
+
browserconsole: bConsole,
|
|
333
|
+
...journey,
|
|
334
|
+
});
|
|
339
335
|
}
|
|
340
336
|
|
|
341
337
|
/**
|
|
342
338
|
* Simulate a journey run to capture errors in the beforeAll hook
|
|
343
339
|
*/
|
|
344
|
-
async runFakeJourney(journey: Journey, options: RunOptions) {
|
|
340
|
+
async #runFakeJourney(journey: Journey, options: RunOptions) {
|
|
345
341
|
const start = monotonicTimeInSeconds();
|
|
346
342
|
this.#reporter.onJourneyStart?.(journey, {
|
|
347
343
|
timestamp: getTimestamp(),
|
|
348
344
|
params: options.params,
|
|
349
345
|
});
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
346
|
+
|
|
347
|
+
// Mark the journey as failed and report the hook error as journey error
|
|
348
|
+
journey.status = 'failed';
|
|
349
|
+
journey.error = this.#hookError;
|
|
350
|
+
journey.duration = monotonicTimeInSeconds() - start;
|
|
351
|
+
|
|
354
352
|
await this.#reporter.onJourneyEnd?.(journey, {
|
|
355
353
|
timestamp: getTimestamp(),
|
|
356
|
-
start,
|
|
357
354
|
options,
|
|
358
|
-
browserDelay:
|
|
359
|
-
end: monotonicTimeInSeconds(),
|
|
360
|
-
...result,
|
|
355
|
+
browserDelay: this.#browserDelay,
|
|
361
356
|
});
|
|
362
|
-
return
|
|
357
|
+
return journey;
|
|
363
358
|
}
|
|
364
359
|
|
|
365
|
-
async
|
|
366
|
-
|
|
367
|
-
const context = await Runner.createContext(options);
|
|
360
|
+
async _runJourney(journey: Journey, options: RunOptions) {
|
|
361
|
+
this.#currentJourney = journey;
|
|
368
362
|
log(`Runner: start journey (${journey.name})`);
|
|
363
|
+
let result: JourneyResult = {};
|
|
364
|
+
const hookArgs = { env: options.environment, params: options.params, info: this };
|
|
369
365
|
try {
|
|
370
|
-
this
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
*/
|
|
380
|
-
for (const stepResult of stepResults) {
|
|
381
|
-
if (stepResult.status === 'failed') {
|
|
382
|
-
result.status = stepResult.status;
|
|
383
|
-
result.error = stepResult.error;
|
|
366
|
+
await this.#startJourney(journey, options);
|
|
367
|
+
await this.#runBeforeHook(journey, hookArgs);
|
|
368
|
+
const stepResults = await this.#runSteps(journey, options);
|
|
369
|
+
journey.status = "succeeded";
|
|
370
|
+
// Mark journey as failed if any one of the step fails
|
|
371
|
+
for (const step of journey.steps) {
|
|
372
|
+
if (step.status === 'failed') {
|
|
373
|
+
journey.status = step.status;
|
|
374
|
+
journey.error = step.error;
|
|
384
375
|
}
|
|
385
376
|
}
|
|
386
|
-
result.
|
|
387
|
-
await this.runAfterHook(journey, hookArgs);
|
|
377
|
+
result.stepsresults = stepResults;
|
|
388
378
|
} catch (e) {
|
|
389
|
-
|
|
390
|
-
|
|
379
|
+
journey.status = 'failed';
|
|
380
|
+
journey.error = e;
|
|
391
381
|
} finally {
|
|
392
|
-
|
|
393
|
-
|
|
382
|
+
journey.duration = monotonicTimeInSeconds() - journey._startTime;
|
|
383
|
+
// Run after hook on journey failure and capture the uncaught error as
|
|
384
|
+
// journey error, hook is purposely run before to capture errors during reporting
|
|
385
|
+
await this.#runAfterHook(journey, hookArgs).catch(e => {
|
|
386
|
+
journey.status = 'failed';
|
|
387
|
+
journey.error = e;
|
|
388
|
+
})
|
|
389
|
+
result = await this.#endJourney(journey, result, options);
|
|
394
390
|
}
|
|
395
391
|
log(`Runner: end journey (${journey.name})`);
|
|
396
392
|
return result;
|
|
397
393
|
}
|
|
398
394
|
|
|
399
|
-
|
|
400
|
-
this.setReporter(options);
|
|
401
|
-
this.#reporter.onStart?.({
|
|
402
|
-
numJourneys: this.journeys.length,
|
|
403
|
-
networkConditions: options.networkConditions,
|
|
404
|
-
});
|
|
405
|
-
/**
|
|
406
|
-
* Set up the directory for caching screenshots
|
|
407
|
-
*/
|
|
408
|
-
await mkdir(CACHE_PATH, { recursive: true });
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
buildMonitors(options: PushOptions) {
|
|
395
|
+
_buildMonitors(options: PushOptions) {
|
|
412
396
|
/**
|
|
413
397
|
* Update the global monitor configuration required for setting defaults
|
|
414
398
|
*/
|
|
415
|
-
this.
|
|
399
|
+
this._updateMonitor({
|
|
416
400
|
throttling: options.throttling,
|
|
417
401
|
schedule: options.schedule,
|
|
418
402
|
locations: options.locations,
|
|
@@ -427,7 +411,7 @@ export default class Runner {
|
|
|
427
411
|
});
|
|
428
412
|
|
|
429
413
|
const monitors: Monitor[] = [];
|
|
430
|
-
for (const journey of this
|
|
414
|
+
for (const journey of this.#journeys) {
|
|
431
415
|
this.#currentJourney = journey;
|
|
432
416
|
if (journey.skip) {
|
|
433
417
|
throw new Error(
|
|
@@ -442,72 +426,87 @@ export default class Runner {
|
|
|
442
426
|
* - filter out monitors based on matched tags and name after applying both
|
|
443
427
|
* global and local monitor configurations
|
|
444
428
|
*/
|
|
445
|
-
journey.
|
|
446
|
-
journey.
|
|
429
|
+
journey.cb({ params: options.params } as any);
|
|
430
|
+
const monitor = journey._getMonitor();
|
|
431
|
+
monitor.update(this.#monitor?.config);
|
|
447
432
|
if (
|
|
448
|
-
!
|
|
433
|
+
!monitor.isMatch(
|
|
449
434
|
options.grepOpts?.match,
|
|
450
435
|
options.grepOpts?.tags
|
|
451
436
|
)
|
|
452
437
|
) {
|
|
453
438
|
continue;
|
|
454
439
|
}
|
|
455
|
-
|
|
456
|
-
monitors.push(
|
|
440
|
+
monitor.validate();
|
|
441
|
+
monitors.push(monitor);
|
|
457
442
|
}
|
|
458
443
|
return monitors;
|
|
459
444
|
}
|
|
460
445
|
|
|
461
|
-
async
|
|
462
|
-
|
|
446
|
+
async #init(options: RunOptions) {
|
|
447
|
+
this.setReporter(options);
|
|
448
|
+
this.#reporter.onStart?.({
|
|
449
|
+
numJourneys: this.#journeys.length,
|
|
450
|
+
networkConditions: options.networkConditions,
|
|
451
|
+
});
|
|
452
|
+
/**
|
|
453
|
+
* Set up the directory for caching screenshots
|
|
454
|
+
*/
|
|
455
|
+
await mkdir(CACHE_PATH, { recursive: true });
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async _run(options: RunOptions): Promise<RunResult> {
|
|
459
|
+
this.config = options;
|
|
460
|
+
let result: RunResult = {};
|
|
463
461
|
if (this.#active) {
|
|
464
462
|
return result;
|
|
465
463
|
}
|
|
466
464
|
this.#active = true;
|
|
467
|
-
log(`Runner: run ${this
|
|
468
|
-
this
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
params: options.params,
|
|
472
|
-
}).catch(e => (this.hookError = e));
|
|
473
|
-
|
|
465
|
+
log(`Runner: run ${this.#journeys.length} journeys`);
|
|
466
|
+
this.#init(options);
|
|
467
|
+
const hookArgs = this.buildHookArgs();
|
|
468
|
+
await this.#runBeforeAllHook(hookArgs).catch(e => (this.#hookError = e));
|
|
474
469
|
const { dryRun, grepOpts } = options;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const onlyJournerys = this.journeys.filter(j => j.only);
|
|
470
|
+
|
|
471
|
+
// collect all journeys with `.only` annotation and skip the rest
|
|
472
|
+
const onlyJournerys = this.#journeys.filter(j => j.only);
|
|
479
473
|
if (onlyJournerys.length > 0) {
|
|
480
|
-
this
|
|
474
|
+
this.#journeys = onlyJournerys;
|
|
475
|
+
} else {
|
|
476
|
+
// filter journeys based on tags and skip annotations
|
|
477
|
+
this.#journeys = this.#journeys.filter(j => j._isMatch(grepOpts?.match, grepOpts?.tags) && !j.skip);
|
|
481
478
|
}
|
|
482
479
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
480
|
+
// Used by heartbeat to gather all registered journeys
|
|
481
|
+
if (dryRun) {
|
|
482
|
+
this.#journeys.forEach(journey => this.#reporter.onJourneyRegister?.(journey))
|
|
483
|
+
} else if (this.#journeys.length > 0) {
|
|
484
|
+
result = await this._runJourneys(options);
|
|
485
|
+
}
|
|
486
|
+
await this.#runAfterAllHook(hookArgs).catch(async () => await this._reset());
|
|
487
|
+
await this._reset();
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
async _runJourneys(options: RunOptions) {
|
|
492
|
+
const result: RunResult = {};
|
|
493
|
+
const browserStart = monotonicTimeInSeconds();
|
|
494
|
+
await Gatherer.launchBrowser(options);
|
|
495
|
+
this.#browserDelay = monotonicTimeInSeconds() - browserStart;
|
|
496
|
+
|
|
497
|
+
for (const journey of this.#journeys) {
|
|
498
|
+
const journeyResult: JourneyResult = this.#hookError
|
|
499
|
+
? await this.#runFakeJourney(journey, options)
|
|
500
|
+
: await this._runJourney(journey, options);
|
|
497
501
|
result[journey.name] = journeyResult;
|
|
498
502
|
}
|
|
499
503
|
await Gatherer.stop();
|
|
500
|
-
await this.runAfterAllHook({
|
|
501
|
-
env: options.environment,
|
|
502
|
-
params: options.params,
|
|
503
|
-
});
|
|
504
|
-
await this.reset();
|
|
505
504
|
return result;
|
|
506
505
|
}
|
|
507
506
|
|
|
508
|
-
async
|
|
507
|
+
async _reset() {
|
|
509
508
|
this.#currentJourney = null;
|
|
510
|
-
this
|
|
509
|
+
this.#journeys = [];
|
|
511
510
|
this.#active = false;
|
|
512
511
|
/**
|
|
513
512
|
* Clear all cache data stored for post processing by
|