@elastic/synthetics 1.24.3 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/dist/common_types.d.ts +17 -0
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/gatherer.d.ts +4 -4
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +28 -7
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/globals.js +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +16 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/runner.d.ts +1 -1
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +86 -58
- package/dist/core/runner.js.map +1 -1
- package/dist/dsl/api-journey.d.ts +62 -0
- package/dist/dsl/api-journey.d.ts.map +1 -0
- package/dist/dsl/api-journey.js +55 -0
- package/dist/dsl/api-journey.js.map +1 -0
- package/dist/dsl/index.d.ts +1 -0
- package/dist/dsl/index.d.ts.map +1 -1
- package/dist/dsl/index.js +1 -0
- package/dist/dsl/index.js.map +1 -1
- package/dist/dsl/journey.d.ts +10 -2
- package/dist/dsl/journey.d.ts.map +1 -1
- package/dist/dsl/journey.js +11 -2
- package/dist/dsl/journey.js.map +1 -1
- package/dist/formatter/codegen.d.ts +69 -0
- package/dist/formatter/codegen.d.ts.map +1 -0
- package/dist/formatter/codegen.js +210 -0
- package/dist/formatter/codegen.js.map +1 -0
- package/dist/formatter/javascript.d.ts +6 -6
- package/dist/formatter/javascript.d.ts.map +1 -1
- package/dist/formatter/javascript.js +7 -7
- package/dist/formatter/javascript.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +66 -1
- package/dist/loader.js.map +1 -1
- package/dist/network-timings.d.ts +55 -0
- package/dist/network-timings.d.ts.map +1 -0
- package/dist/network-timings.js +107 -0
- package/dist/network-timings.js.map +1 -0
- package/dist/plugins/api-network.d.ts +39 -0
- package/dist/plugins/api-network.d.ts.map +1 -0
- package/dist/plugins/api-network.js +235 -0
- package/dist/plugins/api-network.js.map +1 -0
- package/dist/plugins/network.d.ts +0 -6
- package/dist/plugins/network.d.ts.map +1 -1
- package/dist/plugins/network.js +5 -70
- package/dist/plugins/network.js.map +1 -1
- package/dist/plugins/plugin-manager.d.ts +11 -4
- package/dist/plugins/plugin-manager.d.ts.map +1 -1
- package/dist/plugins/plugin-manager.js +36 -16
- package/dist/plugins/plugin-manager.js.map +1 -1
- package/dist/push/index.d.ts.map +1 -1
- package/dist/push/index.js +4 -0
- package/dist/push/index.js.map +1 -1
- package/dist/push/monitor.d.ts.map +1 -1
- package/dist/push/monitor.js +1 -1
- package/dist/push/monitor.js.map +1 -1
- package/dist/push/run-local.d.ts.map +1 -1
- package/dist/push/run-local.js +2 -1
- package/dist/push/run-local.js.map +1 -1
- package/dist/push/utils.d.ts +7 -0
- package/dist/push/utils.d.ts.map +1 -1
- package/dist/push/utils.js +15 -1
- package/dist/push/utils.js.map +1 -1
- package/dist/reporters/base.d.ts +2 -2
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/index.d.ts +2 -2
- package/dist/reporters/index.d.ts.map +1 -1
- package/dist/reporters/index.js.map +1 -1
- package/dist/reporters/json.d.ts +6 -2
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +68 -33
- package/dist/reporters/json.js.map +1 -1
- package/package.json +7 -7
- package/src/common_types.ts +22 -0
- package/src/core/gatherer.ts +41 -10
- package/src/core/globals.ts +1 -1
- package/src/core/index.ts +28 -0
- package/src/core/runner.ts +93 -67
- package/src/dsl/api-journey.ts +93 -0
- package/src/dsl/index.ts +1 -0
- package/src/dsl/journey.ts +21 -9
- package/src/formatter/codegen.ts +299 -0
- package/src/formatter/javascript.ts +1 -4
- package/src/index.ts +6 -0
- package/src/loader.ts +78 -2
- package/src/network-timings.ts +130 -0
- package/src/plugins/api-network.ts +252 -0
- package/src/plugins/network.ts +5 -87
- package/src/plugins/plugin-manager.ts +60 -19
- package/src/push/index.ts +11 -0
- package/src/push/monitor.ts +6 -2
- package/src/push/run-local.ts +2 -1
- package/src/push/utils.ts +15 -0
- package/src/reporters/base.ts +2 -1
- package/src/reporters/index.ts +2 -1
- package/src/reporters/json.ts +74 -41
- package/templates/README.md +3 -1
- package/templates/journeys/advanced-api-example-helpers.ts +79 -0
- package/templates/journeys/advanced-api-example.journey.ts +52 -0
- package/templates/journeys/api-example.journey.ts +35 -0
- package/templates/synthetics.config.ts +2 -0
package/src/core/gatherer.ts
CHANGED
|
@@ -32,7 +32,12 @@ import {
|
|
|
32
32
|
} from 'playwright-core';
|
|
33
33
|
import { PluginManager } from '../plugins';
|
|
34
34
|
import { log } from './logger';
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
APIDriver,
|
|
37
|
+
Driver,
|
|
38
|
+
NetworkConditions,
|
|
39
|
+
RunOptions,
|
|
40
|
+
} from '../common_types';
|
|
36
41
|
|
|
37
42
|
// Default timeout for Playwright actions and Navigations
|
|
38
43
|
const DEFAULT_TIMEOUT = 50000;
|
|
@@ -71,9 +76,19 @@ export class Gatherer {
|
|
|
71
76
|
});
|
|
72
77
|
}
|
|
73
78
|
|
|
74
|
-
static async setupDriver
|
|
75
|
-
|
|
79
|
+
static async setupDriver<T extends 'browser' | 'api' = 'browser'>(
|
|
80
|
+
options: RunOptions,
|
|
81
|
+
journeyType: T = 'browser' as T
|
|
82
|
+
): Promise<T extends 'api' ? APIDriver : Driver> {
|
|
76
83
|
const { playwrightOptions } = options;
|
|
84
|
+
|
|
85
|
+
if (journeyType === 'api') {
|
|
86
|
+
const request = await apiRequest.newContext({ ...playwrightOptions });
|
|
87
|
+
return { request } as T extends 'api' ? APIDriver : Driver;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await Gatherer.launchBrowser(options);
|
|
91
|
+
|
|
77
92
|
const context = await Gatherer.browser.newContext({
|
|
78
93
|
...playwrightOptions,
|
|
79
94
|
userAgent: await Gatherer.getUserAgent(playwrightOptions?.userAgent),
|
|
@@ -96,7 +111,13 @@ export class Gatherer {
|
|
|
96
111
|
const page = await context.newPage();
|
|
97
112
|
const client = await context.newCDPSession(page);
|
|
98
113
|
const request = await apiRequest.newContext({ ...playwrightOptions });
|
|
99
|
-
return {
|
|
114
|
+
return {
|
|
115
|
+
browser: Gatherer.browser,
|
|
116
|
+
context,
|
|
117
|
+
page,
|
|
118
|
+
client,
|
|
119
|
+
request,
|
|
120
|
+
} as T extends 'api' ? APIDriver : Driver;
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
static async getUserAgent(userAgent?: string) {
|
|
@@ -136,14 +157,22 @@ export class Gatherer {
|
|
|
136
157
|
* Starts recording all events related to the v8 devtools protocol
|
|
137
158
|
* https://chromedevtools.github.io/devtools-protocol/v8/
|
|
138
159
|
*/
|
|
139
|
-
static async beginRecording(driver: Driver, options: RunOptions) {
|
|
160
|
+
static async beginRecording(driver: Driver | APIDriver, options: RunOptions) {
|
|
140
161
|
log('Gatherer: started recording');
|
|
141
162
|
const { network, metrics } = options;
|
|
142
163
|
Gatherer.pluginManager = new PluginManager(driver);
|
|
143
164
|
Gatherer.pluginManager.registerAll(options);
|
|
144
|
-
const plugins = [
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
const plugins = [];
|
|
166
|
+
if ('browser' in driver) {
|
|
167
|
+
plugins.push(await Gatherer.pluginManager.start('browserconsole'));
|
|
168
|
+
network && plugins.push(await Gatherer.pluginManager.start('network'));
|
|
169
|
+
metrics &&
|
|
170
|
+
plugins.push(await Gatherer.pluginManager.start('performance'));
|
|
171
|
+
} else {
|
|
172
|
+
// Network is the only data source for API journeys, so always capture
|
|
173
|
+
// it regardless of `options.network`.
|
|
174
|
+
plugins.push(await Gatherer.pluginManager.start('network'));
|
|
175
|
+
}
|
|
147
176
|
await Promise.all(plugins);
|
|
148
177
|
return Gatherer.pluginManager;
|
|
149
178
|
}
|
|
@@ -153,10 +182,12 @@ export class Gatherer {
|
|
|
153
182
|
await Gatherer.pluginManager.unregisterAll();
|
|
154
183
|
}
|
|
155
184
|
|
|
156
|
-
static async dispose(driver: Driver) {
|
|
185
|
+
static async dispose(driver: Driver | APIDriver) {
|
|
157
186
|
log(`Gatherer: closing all contexts`);
|
|
158
187
|
await driver.request.dispose();
|
|
159
|
-
|
|
188
|
+
if ('context' in driver) {
|
|
189
|
+
await driver.context.close();
|
|
190
|
+
}
|
|
160
191
|
}
|
|
161
192
|
|
|
162
193
|
static async stop() {
|
package/src/core/globals.ts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
import Runner from './runner';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Use a
|
|
29
|
+
* Use a global Runner which would be accessed by the runtime and
|
|
30
30
|
* required to handle the local vs global invocation through CLI
|
|
31
31
|
*/
|
|
32
32
|
const SYNTHETICS_RUNNER = Symbol.for('SYNTHETICS_RUNNER');
|
package/src/core/index.ts
CHANGED
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import {
|
|
27
|
+
APIJourney,
|
|
28
|
+
APIJourneyCallback,
|
|
29
|
+
APIJourneyOptions,
|
|
30
|
+
APIJourneyWithAnnotations,
|
|
27
31
|
Journey,
|
|
28
32
|
JourneyCallback,
|
|
29
33
|
JourneyOptions,
|
|
@@ -60,6 +64,30 @@ export const journey = createJourney() as JourneyWithAnnotations;
|
|
|
60
64
|
journey.skip = createJourney('skip');
|
|
61
65
|
journey.only = createJourney('only');
|
|
62
66
|
|
|
67
|
+
const createAPIJourney = (type?: 'skip' | 'only') =>
|
|
68
|
+
wrapFnWithLocation(
|
|
69
|
+
(
|
|
70
|
+
location: Location,
|
|
71
|
+
options: APIJourneyOptions | string,
|
|
72
|
+
callback: APIJourneyCallback
|
|
73
|
+
) => {
|
|
74
|
+
log(`API Journey register: ${JSON.stringify(options)}`);
|
|
75
|
+
if (typeof options === 'string') {
|
|
76
|
+
options = { name: options, id: options };
|
|
77
|
+
}
|
|
78
|
+
const j = new APIJourney(options, callback, location);
|
|
79
|
+
if (type) {
|
|
80
|
+
j[type] = true;
|
|
81
|
+
}
|
|
82
|
+
runner._addJourney(j);
|
|
83
|
+
return j;
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
export const apiJourney = createAPIJourney() as APIJourneyWithAnnotations;
|
|
88
|
+
apiJourney.skip = createAPIJourney('skip');
|
|
89
|
+
apiJourney.only = createAPIJourney('only');
|
|
90
|
+
|
|
63
91
|
const createStep = (type?: 'skip' | 'soft' | 'only') =>
|
|
64
92
|
wrapFnWithLocation(
|
|
65
93
|
(location: Location, name: string, callback: VoidCallback) => {
|
package/src/core/runner.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import { join } from 'path';
|
|
27
27
|
import { mkdir, rm, writeFile } from 'fs/promises';
|
|
28
|
-
import { Journey } from '../dsl/journey';
|
|
28
|
+
import { Journey, JourneyCallbackOpts } from '../dsl/journey';
|
|
29
29
|
import { Step } from '../dsl/step';
|
|
30
30
|
import { reporters, Reporter } from '../reporters';
|
|
31
31
|
import {
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
JourneyResult,
|
|
45
45
|
StepResult,
|
|
46
46
|
PushOptions,
|
|
47
|
+
APIDriver,
|
|
47
48
|
} from '../common_types';
|
|
48
49
|
import { PerformanceManager, filterBrowserMessages } from '../plugins';
|
|
49
50
|
import { Gatherer } from './gatherer';
|
|
@@ -66,7 +67,7 @@ export interface RunnerInfo {
|
|
|
66
67
|
*/
|
|
67
68
|
readonly currentJourney: Journey | undefined;
|
|
68
69
|
/**
|
|
69
|
-
* All
|
|
70
|
+
* All registered journeys
|
|
70
71
|
*/
|
|
71
72
|
readonly journeys: Journey[];
|
|
72
73
|
}
|
|
@@ -78,7 +79,7 @@ export default class Runner implements RunnerInfo {
|
|
|
78
79
|
#journeys: Journey[] = [];
|
|
79
80
|
#hooks: SuiteHooks = { beforeAll: [], afterAll: [] };
|
|
80
81
|
#screenshotPath = join(CACHE_PATH, 'screenshots');
|
|
81
|
-
#driver?: Driver;
|
|
82
|
+
#driver?: Driver | APIDriver;
|
|
82
83
|
#browserDelay = -1;
|
|
83
84
|
#hookError: Error | undefined;
|
|
84
85
|
#monitor?: Monitor;
|
|
@@ -210,18 +211,22 @@ export default class Runner implements RunnerInfo {
|
|
|
210
211
|
async #runStep(step: Step, options: RunOptions): Promise<StepResult> {
|
|
211
212
|
log(`Runner: start step (${step.name})`);
|
|
212
213
|
const { metrics, screenshots, filmstrips, trace } = options;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
214
|
+
|
|
215
|
+
if ('context' in this.#driver) {
|
|
216
|
+
/**
|
|
217
|
+
* URL needs to be the first navigation request of any step
|
|
218
|
+
* Listening for request solves the case where `about:blank` would be
|
|
219
|
+
* reported for failed navigations
|
|
220
|
+
*/
|
|
221
|
+
const captureUrl = req => {
|
|
222
|
+
if (!step.url && req.isNavigationRequest()) {
|
|
223
|
+
step.url = req.url();
|
|
224
|
+
}
|
|
225
|
+
'context' in this.#driver &&
|
|
226
|
+
this.#driver.context.off('request', captureUrl);
|
|
227
|
+
};
|
|
228
|
+
this.#driver.context.on('request', captureUrl);
|
|
229
|
+
}
|
|
225
230
|
|
|
226
231
|
const data: StepResult = {};
|
|
227
232
|
const traceEnabled = trace || filmstrips;
|
|
@@ -240,30 +245,32 @@ export default class Runner implements RunnerInfo {
|
|
|
240
245
|
step.status = 'failed';
|
|
241
246
|
step.error = error;
|
|
242
247
|
} finally {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
248
|
+
if ('context' in this.#driver) {
|
|
249
|
+
/**
|
|
250
|
+
* Collect all step level metrics and trace events
|
|
251
|
+
*/
|
|
252
|
+
if (metrics) {
|
|
253
|
+
data.pagemetrics = await (
|
|
254
|
+
Gatherer.pluginManager.get('performance') as PerformanceManager
|
|
255
|
+
).getMetrics();
|
|
256
|
+
}
|
|
257
|
+
if (traceEnabled) {
|
|
258
|
+
const traceOutput = await Gatherer.pluginManager.stop('trace');
|
|
259
|
+
Object.assign(data, traceOutput);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Capture screenshot for the newly created pages
|
|
263
|
+
* via popup or new windows/tabs
|
|
264
|
+
*
|
|
265
|
+
* Last open page will get us the correct screenshot
|
|
266
|
+
*/
|
|
267
|
+
const pages = this.#driver.context.pages();
|
|
268
|
+
const page = pages[pages.length - 1];
|
|
269
|
+
if (page) {
|
|
270
|
+
step.url ??= page.url();
|
|
271
|
+
if (screenshots && screenshots !== 'off') {
|
|
272
|
+
await this.captureScreenshot(page, step);
|
|
273
|
+
}
|
|
267
274
|
}
|
|
268
275
|
}
|
|
269
276
|
}
|
|
@@ -310,22 +317,24 @@ export default class Runner implements RunnerInfo {
|
|
|
310
317
|
|
|
311
318
|
async #startJourney(journey: Journey, options: RunOptions) {
|
|
312
319
|
journey._startTime = monotonicTimeInSeconds();
|
|
313
|
-
this.#driver = await Gatherer.setupDriver(options);
|
|
320
|
+
this.#driver = await Gatherer.setupDriver(options, journey.type);
|
|
314
321
|
await Gatherer.beginRecording(this.#driver, options);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
322
|
+
if (journey.type === 'browser') {
|
|
323
|
+
/**
|
|
324
|
+
* For each journey we create the screenshots folder for
|
|
325
|
+
* caching all screenshots and clear them at end of each journey
|
|
326
|
+
*/
|
|
327
|
+
await mkdir(this.#screenshotPath, { recursive: true });
|
|
328
|
+
}
|
|
320
329
|
const params = options.params;
|
|
321
330
|
this.#reporter?.onJourneyStart?.(journey, {
|
|
322
331
|
timestamp: getTimestamp(),
|
|
323
332
|
params,
|
|
324
333
|
});
|
|
325
334
|
/**
|
|
326
|
-
*
|
|
335
|
+
* Execute the journey callback which registers the steps for current journey
|
|
327
336
|
*/
|
|
328
|
-
journey.cb({ ...this.#driver, params, info: this });
|
|
337
|
+
journey.cb({ ...this.#driver, params, info: this } as JourneyCallbackOpts);
|
|
329
338
|
}
|
|
330
339
|
|
|
331
340
|
async #endJourney(
|
|
@@ -335,24 +344,34 @@ export default class Runner implements RunnerInfo {
|
|
|
335
344
|
) {
|
|
336
345
|
// Enhance the journey results
|
|
337
346
|
const pOutput = await Gatherer.pluginManager.output();
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
journey.status
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
const isBrowserJourney = journey.type === 'browser';
|
|
348
|
+
const bConsole = isBrowserJourney
|
|
349
|
+
? filterBrowserMessages(pOutput.browserconsole, journey.status)
|
|
350
|
+
: undefined;
|
|
351
|
+
if (isBrowserJourney) {
|
|
352
|
+
await this.#reporter?.onJourneyEnd?.(journey, {
|
|
353
|
+
browserDelay: this.#browserDelay,
|
|
354
|
+
timestamp: getTimestamp(),
|
|
355
|
+
options,
|
|
356
|
+
networkinfo: pOutput.networkinfo,
|
|
357
|
+
browserconsole: bConsole,
|
|
358
|
+
});
|
|
359
|
+
} else {
|
|
360
|
+
await this.#reporter?.onJourneyEnd?.(journey, {
|
|
361
|
+
timestamp: getTimestamp(),
|
|
362
|
+
options,
|
|
363
|
+
networkinfo: pOutput.networkinfo,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
349
366
|
await Gatherer.endRecording();
|
|
350
367
|
await Gatherer.dispose(this.#driver);
|
|
351
368
|
// clear screenshots cache after each journey
|
|
352
|
-
|
|
369
|
+
if (isBrowserJourney) {
|
|
370
|
+
await rm(this.#screenshotPath, { recursive: true, force: true });
|
|
371
|
+
}
|
|
353
372
|
return Object.assign(result, {
|
|
354
373
|
networkinfo: pOutput.networkinfo,
|
|
355
|
-
browserconsole: bConsole,
|
|
374
|
+
...(isBrowserJourney ? { browserconsole: bConsole } : {}),
|
|
356
375
|
...journey,
|
|
357
376
|
});
|
|
358
377
|
}
|
|
@@ -516,9 +535,9 @@ export default class Runner implements RunnerInfo {
|
|
|
516
535
|
const { dryRun, grepOpts } = options;
|
|
517
536
|
|
|
518
537
|
// collect all journeys with `.only` annotation and skip the rest
|
|
519
|
-
const
|
|
520
|
-
if (
|
|
521
|
-
this.#journeys =
|
|
538
|
+
const onlyJourneys = this.#journeys.filter(j => j.only);
|
|
539
|
+
if (onlyJourneys.length > 0) {
|
|
540
|
+
this.#journeys = onlyJourneys;
|
|
522
541
|
} else {
|
|
523
542
|
// filter journeys based on tags and skip annotations
|
|
524
543
|
this.#journeys = this.#journeys.filter(
|
|
@@ -551,9 +570,16 @@ export default class Runner implements RunnerInfo {
|
|
|
551
570
|
|
|
552
571
|
async _runJourneys(options: RunOptions) {
|
|
553
572
|
const result: RunResult = {};
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
573
|
+
/**
|
|
574
|
+
* Only spin up a Chromium browser if at least one browser journey is
|
|
575
|
+
* scheduled to run. Pure API-journey suites skip launch entirely.
|
|
576
|
+
*/
|
|
577
|
+
const hasBrowserJourneys = this.#journeys.some(j => j.type === 'browser');
|
|
578
|
+
if (hasBrowserJourneys) {
|
|
579
|
+
const browserStart = monotonicTimeInSeconds();
|
|
580
|
+
await Gatherer.launchBrowser(options);
|
|
581
|
+
this.#browserDelay = monotonicTimeInSeconds() - browserStart;
|
|
582
|
+
}
|
|
557
583
|
|
|
558
584
|
for (const journey of this.#journeys) {
|
|
559
585
|
const journeyResult: JourneyResult = this.#hookError
|
|
@@ -578,7 +604,7 @@ export default class Runner implements RunnerInfo {
|
|
|
578
604
|
this.#journeys = [];
|
|
579
605
|
this.#active = false;
|
|
580
606
|
/**
|
|
581
|
-
* Clear all cache data stored for post
|
|
607
|
+
* Clear all cache data stored for post-processing by
|
|
582
608
|
* the current synthetic agent run
|
|
583
609
|
*/
|
|
584
610
|
await rm(CACHE_PATH, { recursive: true, force: true });
|
|
@@ -0,0 +1,93 @@
|
|
|
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 { APIRequestContext } from 'playwright-core';
|
|
27
|
+
import { Location, Params } from '../common_types';
|
|
28
|
+
|
|
29
|
+
import { Journey } from './journey';
|
|
30
|
+
import { Monitor, MonitorConfig } from './monitor';
|
|
31
|
+
import { RunnerInfo } from '../core/runner';
|
|
32
|
+
|
|
33
|
+
export type APIJourneyOptions = {
|
|
34
|
+
name: string;
|
|
35
|
+
id?: string;
|
|
36
|
+
tags?: string[];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type APIJourneyCallbackOpts = {
|
|
40
|
+
params: Params;
|
|
41
|
+
request: APIRequestContext;
|
|
42
|
+
info: RunnerInfo;
|
|
43
|
+
};
|
|
44
|
+
export type APIJourneyCallback = (options: APIJourneyCallbackOpts) => void;
|
|
45
|
+
|
|
46
|
+
type APIJourneyType = (
|
|
47
|
+
options: string | APIJourneyOptions,
|
|
48
|
+
callback: APIJourneyCallback
|
|
49
|
+
) => APIJourney;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Same step-based model as a browser `Journey` but without a Chromium
|
|
53
|
+
* context. `type === 'api'` lets the runner/gatherer/plugin-manager skip
|
|
54
|
+
* browser-only setup.
|
|
55
|
+
*/
|
|
56
|
+
export class APIJourney extends Journey {
|
|
57
|
+
constructor(
|
|
58
|
+
options: APIJourneyOptions | string,
|
|
59
|
+
cb: APIJourneyCallback,
|
|
60
|
+
location?: Location
|
|
61
|
+
) {
|
|
62
|
+
const opts =
|
|
63
|
+
typeof options === 'string'
|
|
64
|
+
? { name: options, id: options }
|
|
65
|
+
: { ...options };
|
|
66
|
+
// Runner narrows callback args by `journey.type`, so the cast is sound.
|
|
67
|
+
super({ ...opts, type: 'api' }, cb as any, location);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Register as an `api` project monitor on `synthetics push`.
|
|
71
|
+
override _updateMonitor(config: MonitorConfig) {
|
|
72
|
+
this._setMonitor(
|
|
73
|
+
new Monitor({
|
|
74
|
+
name: this.name,
|
|
75
|
+
id: this.id,
|
|
76
|
+
type: 'api',
|
|
77
|
+
tags: this.tags ?? [],
|
|
78
|
+
...config,
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type APIJourneyWithAnnotations = APIJourneyType & {
|
|
85
|
+
/**
|
|
86
|
+
* Skip this journey and all its steps
|
|
87
|
+
*/
|
|
88
|
+
skip: APIJourneyType;
|
|
89
|
+
/**
|
|
90
|
+
* Run only this journey and skip rest of the journeys
|
|
91
|
+
*/
|
|
92
|
+
only: APIJourneyType;
|
|
93
|
+
};
|
package/src/dsl/index.ts
CHANGED
package/src/dsl/journey.ts
CHANGED
|
@@ -50,7 +50,7 @@ export type JourneyOptions = {
|
|
|
50
50
|
|
|
51
51
|
type HookType = 'before' | 'after';
|
|
52
52
|
export type Hooks = Record<HookType, Array<HooksCallback>>;
|
|
53
|
-
type JourneyCallbackOpts = {
|
|
53
|
+
export type JourneyCallbackOpts = {
|
|
54
54
|
page: Page;
|
|
55
55
|
context: BrowserContext;
|
|
56
56
|
browser: Browser;
|
|
@@ -62,6 +62,7 @@ type JourneyCallbackOpts = {
|
|
|
62
62
|
export type JourneyCallback = (options: JourneyCallbackOpts) => void;
|
|
63
63
|
|
|
64
64
|
export class Journey {
|
|
65
|
+
readonly type: 'browser' | 'api' = 'browser';
|
|
65
66
|
readonly name: string;
|
|
66
67
|
readonly id?: string;
|
|
67
68
|
readonly tags?: string[];
|
|
@@ -78,10 +79,11 @@ export class Journey {
|
|
|
78
79
|
error?: Error;
|
|
79
80
|
|
|
80
81
|
constructor(
|
|
81
|
-
options: JourneyOptions,
|
|
82
|
+
options: JourneyOptions & { type?: 'browser' | 'api' },
|
|
82
83
|
cb: JourneyCallback,
|
|
83
84
|
location?: Location
|
|
84
85
|
) {
|
|
86
|
+
this.type = options.type ?? 'browser';
|
|
85
87
|
this.name = options.name;
|
|
86
88
|
this.id = options.id || options.name;
|
|
87
89
|
this.tags = options.tags;
|
|
@@ -144,13 +146,23 @@ export class Journey {
|
|
|
144
146
|
/**
|
|
145
147
|
* Use defaults values from journey for monitor object (id, name and tags)
|
|
146
148
|
*/
|
|
147
|
-
this
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
this._setMonitor(
|
|
150
|
+
new Monitor({
|
|
151
|
+
name: this.name,
|
|
152
|
+
id: this.id,
|
|
153
|
+
type: 'browser',
|
|
154
|
+
tags: this.tags ?? [],
|
|
155
|
+
...config,
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Subclasses use this to swap in a monitor with a different default
|
|
162
|
+
* `type` while reusing the source-location and name-filter wiring.
|
|
163
|
+
*/
|
|
164
|
+
protected _setMonitor(monitor: Monitor) {
|
|
165
|
+
this.#monitor = monitor;
|
|
154
166
|
this.#monitor.setSource(this.location);
|
|
155
167
|
this.#monitor.setFilter({ match: this.name });
|
|
156
168
|
}
|