@elastic/synthetics 1.0.0-beta.3 → 1.0.0-beta.32
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 +4 -3
- package/dist/cli.js +123 -161
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +159 -34
- package/dist/common_types.d.ts.map +1 -1
- package/dist/config.d.ts +2 -5
- package/dist/config.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/expect.d.ts +25 -25
- package/dist/core/expect.d.ts.map +1 -1
- package/dist/core/expect.js.map +1 -1
- package/dist/core/gatherer.d.ts +4 -8
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +49 -15
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/index.d.ts +5 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +23 -11
- package/dist/core/index.js.map +1 -1
- package/dist/core/logger.d.ts +0 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +3 -16
- package/dist/core/logger.js.map +1 -1
- package/dist/core/runner.d.ts +21 -65
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +212 -93
- package/dist/core/runner.js.map +1 -1
- package/dist/dsl/index.js +5 -1
- package/dist/dsl/index.js.map +1 -1
- package/dist/dsl/journey.d.ts +9 -4
- package/dist/dsl/journey.d.ts.map +1 -1
- package/dist/dsl/journey.js +27 -7
- package/dist/dsl/journey.js.map +1 -1
- package/dist/dsl/monitor.d.ts +66 -0
- package/dist/dsl/monitor.d.ts.map +1 -0
- package/dist/dsl/monitor.js +63 -0
- package/dist/dsl/monitor.js.map +1 -0
- 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 +98 -0
- package/dist/formatter/javascript.d.ts.map +1 -0
- package/dist/formatter/javascript.js +295 -0
- package/dist/formatter/javascript.js.map +1 -0
- package/dist/generator/index.d.ts +29 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +240 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/{reporters/reporter.js → generator/utils.d.ts} +5 -3
- package/dist/generator/utils.d.ts.map +1 -0
- package/dist/generator/utils.js +80 -0
- package/dist/generator/utils.js.map +1 -0
- package/dist/helpers.d.ts +24 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +126 -19
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -26
- 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 +152 -0
- package/dist/loader.js.map +1 -0
- package/dist/locations/index.d.ts +45 -0
- package/dist/locations/index.d.ts.map +1 -0
- package/dist/locations/index.js +90 -0
- package/dist/locations/index.js.map +1 -0
- package/dist/{reporters/reporter.d.ts → locations/public-locations.d.ts} +12 -8
- package/dist/locations/public-locations.d.ts.map +1 -0
- package/dist/locations/public-locations.js +41 -0
- package/dist/locations/public-locations.js.map +1 -0
- package/dist/options.d.ts +41 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +182 -0
- package/dist/options.js.map +1 -0
- package/dist/plugins/browser-console.d.ts +5 -4
- package/dist/plugins/browser-console.d.ts.map +1 -1
- package/dist/plugins/browser-console.js +27 -8
- package/dist/plugins/browser-console.js.map +1 -1
- package/dist/plugins/index.js +5 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/network.d.ts +20 -33
- package/dist/plugins/network.d.ts.map +1 -1
- package/dist/plugins/network.js +173 -214
- package/dist/plugins/network.js.map +1 -1
- package/dist/plugins/performance.d.ts +4 -19
- package/dist/plugins/performance.d.ts.map +1 -1
- package/dist/plugins/performance.js +5 -5
- package/dist/plugins/performance.js.map +1 -1
- package/dist/plugins/plugin-manager.d.ts +9 -5
- package/dist/plugins/plugin-manager.d.ts.map +1 -1
- package/dist/plugins/plugin-manager.js +44 -29
- package/dist/plugins/plugin-manager.js.map +1 -1
- package/dist/plugins/tracing.d.ts +6 -6
- package/dist/plugins/tracing.d.ts.map +1 -1
- package/dist/plugins/tracing.js +8 -7
- package/dist/plugins/tracing.js.map +1 -1
- package/dist/push/bundler.d.ts +34 -0
- package/dist/push/bundler.d.ts.map +1 -0
- package/dist/push/bundler.js +123 -0
- package/dist/push/bundler.js.map +1 -0
- package/dist/push/index.d.ts +33 -0
- package/dist/push/index.d.ts.map +1 -0
- package/dist/push/index.js +174 -0
- package/dist/push/index.js.map +1 -0
- package/dist/push/monitor.d.ts +34 -0
- package/dist/push/monitor.d.ts.map +1 -0
- package/dist/push/monitor.js +78 -0
- package/dist/push/monitor.js.map +1 -0
- package/{src/reporters/reporter.ts → dist/push/plugin.d.ts} +8 -10
- package/dist/push/plugin.d.ts.map +1 -0
- package/dist/push/plugin.js +133 -0
- package/dist/push/plugin.js.map +1 -0
- package/dist/push/request.d.ts +43 -0
- package/dist/push/request.d.ts.map +1 -0
- package/dist/push/request.js +83 -0
- package/dist/push/request.js.map +1 -0
- package/dist/reporters/base.d.ts +14 -6
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js +54 -48
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/index.d.ts +18 -8
- package/dist/reporters/index.d.ts.map +1 -1
- package/dist/reporters/index.js.map +1 -1
- package/dist/reporters/json.d.ts +18 -67
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +153 -175
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/junit.d.ts +7 -1
- package/dist/reporters/junit.d.ts.map +1 -1
- package/dist/reporters/junit.js +94 -89
- package/dist/reporters/junit.js.map +1 -1
- package/dist/sdk/lh-trace-processor.d.ts +35 -9
- package/dist/sdk/lh-trace-processor.d.ts.map +1 -1
- package/dist/sdk/lh-trace-processor.js +114 -53
- package/dist/sdk/lh-trace-processor.js.map +1 -1
- package/dist/sdk/trace-metrics.d.ts +4 -4
- package/dist/sdk/trace-metrics.d.ts.map +1 -1
- package/dist/sdk/trace-metrics.js +5 -6
- package/dist/sdk/trace-metrics.js.map +1 -1
- package/dist/sdk/trace-processor.d.ts +11 -3
- package/dist/sdk/trace-processor.d.ts.map +1 -1
- package/dist/sdk/trace-processor.js +17 -9
- package/dist/sdk/trace-processor.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1888
- package/package.json +32 -20
- package/src/cli.ts +205 -152
- package/src/common_types.ts +192 -34
- package/src/config.ts +2 -5
- package/src/core/expect.ts +58 -24
- package/src/core/gatherer.ts +59 -24
- package/src/core/index.ts +36 -15
- package/src/core/logger.ts +1 -12
- package/src/core/runner.ts +215 -152
- package/src/dsl/journey.ts +37 -6
- package/src/dsl/monitor.ts +88 -0
- package/src/dsl/step.ts +9 -2
- package/src/formatter/javascript.ts +367 -0
- package/src/generator/index.ts +316 -0
- package/src/generator/utils.ts +80 -0
- package/src/helpers.ts +132 -12
- package/src/index.ts +22 -22
- package/src/loader.ts +171 -0
- package/src/locations/index.ts +108 -0
- package/src/locations/public-locations.ts +38 -0
- package/src/options.ts +211 -0
- package/src/plugins/browser-console.ts +30 -8
- package/src/plugins/network.ts +208 -267
- package/src/plugins/performance.ts +6 -22
- package/src/plugins/plugin-manager.ts +49 -31
- package/src/plugins/tracing.ts +8 -9
- package/src/push/bundler.ts +100 -0
- package/src/push/index.ts +202 -0
- package/src/push/monitor.ts +96 -0
- package/src/push/plugin.ts +119 -0
- package/src/push/request.ts +103 -0
- package/src/reporters/base.ts +63 -52
- package/src/reporters/index.ts +26 -3
- package/src/reporters/json.ts +203 -206
- package/src/reporters/junit.ts +98 -93
- package/src/sdk/lh-trace-processor.ts +132 -68
- package/src/sdk/trace-metrics.ts +10 -6
- package/src/sdk/trace-processor.ts +28 -13
- package/templates/.github/workflows/run-synthetics.yml +26 -0
- package/templates/README.md +21 -0
- package/templates/journeys/advanced-example-helpers.ts +54 -0
- package/templates/journeys/advanced-example.journey.ts +43 -0
- package/templates/journeys/example.journey.ts +18 -0
- package/templates/synthetics.config.ts +27 -0
- package/CHANGELOG.md +0 -102
- package/NOTICE.txt +0 -221
- package/dist/parse_args.d.ts.map +0 -1
- package/dist/parse_args.js +0 -89
- package/dist/parse_args.js.map +0 -1
- package/dist/reporters/reporter.d.ts.map +0 -1
- package/dist/reporters/reporter.js.map +0 -1
- package/src/parse_args.ts +0 -127
package/src/core/logger.ts
CHANGED
|
@@ -23,20 +23,9 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import SonicBoom from 'sonic-boom';
|
|
27
26
|
import { grey, cyan, dim, italic } from 'kleur/colors';
|
|
28
27
|
import { now } from '../helpers';
|
|
29
28
|
|
|
30
|
-
const defaultFd = process.stdout.fd;
|
|
31
|
-
let logger = new SonicBoom({ fd: defaultFd, sync: true });
|
|
32
|
-
|
|
33
|
-
export const setLogger = (fd: number) => {
|
|
34
|
-
if (fd && fd !== defaultFd) {
|
|
35
|
-
logger = new SonicBoom({ fd });
|
|
36
|
-
}
|
|
37
|
-
return logger;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
29
|
export function log(msg) {
|
|
41
30
|
if (!process.env.DEBUG || !msg) {
|
|
42
31
|
return;
|
|
@@ -45,5 +34,5 @@ export function log(msg) {
|
|
|
45
34
|
msg = JSON.stringify(msg);
|
|
46
35
|
}
|
|
47
36
|
const time = dim(cyan(`at ${parseInt(String(now()))} ms `));
|
|
48
|
-
|
|
37
|
+
console.log(time + italic(grey(msg)) + '\n');
|
|
49
38
|
}
|
package/src/core/runner.ts
CHANGED
|
@@ -23,9 +23,8 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { once, EventEmitter } from 'events';
|
|
27
26
|
import { join } from 'path';
|
|
28
|
-
import {
|
|
27
|
+
import { mkdir, rm, writeFile } from 'fs/promises';
|
|
29
28
|
import { Journey } from '../dsl/journey';
|
|
30
29
|
import { Step } from '../dsl/step';
|
|
31
30
|
import { reporters, Reporter } from '../reporters';
|
|
@@ -33,109 +32,60 @@ import {
|
|
|
33
32
|
CACHE_PATH,
|
|
34
33
|
monotonicTimeInSeconds,
|
|
35
34
|
getTimestamp,
|
|
36
|
-
now,
|
|
37
35
|
runParallel,
|
|
36
|
+
generateUniqueId,
|
|
38
37
|
} from '../helpers';
|
|
39
38
|
import {
|
|
40
|
-
StatusValue,
|
|
41
39
|
HooksCallback,
|
|
42
40
|
Params,
|
|
43
|
-
PluginOutput,
|
|
44
|
-
CliArgs,
|
|
45
41
|
HooksArgs,
|
|
42
|
+
Driver,
|
|
43
|
+
Screenshot,
|
|
44
|
+
RunOptions,
|
|
45
|
+
JourneyResult,
|
|
46
|
+
StepResult,
|
|
46
47
|
} from '../common_types';
|
|
47
48
|
import { PluginManager } from '../plugins';
|
|
48
|
-
import { PerformanceManager
|
|
49
|
-
import {
|
|
49
|
+
import { PerformanceManager } from '../plugins';
|
|
50
|
+
import { Gatherer } from './gatherer';
|
|
50
51
|
import { log } from './logger';
|
|
52
|
+
import { Monitor, MonitorConfig } from '../dsl/monitor';
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
| 'debug'
|
|
55
|
-
| 'json'
|
|
56
|
-
| 'pattern'
|
|
57
|
-
| 'inline'
|
|
58
|
-
| 'require'
|
|
59
|
-
| 'suiteParams'
|
|
60
|
-
| 'reporter'
|
|
61
|
-
| 'richEvents'
|
|
62
|
-
| 'capability'
|
|
63
|
-
> & {
|
|
64
|
-
params?: Params;
|
|
65
|
-
reporter?: CliArgs['reporter'] | Reporter;
|
|
66
|
-
};
|
|
54
|
+
type HookType = 'beforeAll' | 'afterAll';
|
|
55
|
+
export type SuiteHooks = Record<HookType, Array<HooksCallback>>;
|
|
67
56
|
|
|
68
|
-
type
|
|
57
|
+
type JourneyContext = {
|
|
69
58
|
params?: Params;
|
|
70
59
|
start: number;
|
|
71
|
-
end?: number;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
type JourneyContext = BaseContext & {
|
|
75
60
|
driver: Driver;
|
|
76
61
|
pluginManager: PluginManager;
|
|
77
62
|
};
|
|
78
63
|
|
|
79
|
-
type StepResult = {
|
|
80
|
-
status: StatusValue;
|
|
81
|
-
url?: string;
|
|
82
|
-
metrics?: Metrics;
|
|
83
|
-
error?: Error;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
type JourneyResult = {
|
|
87
|
-
status: StatusValue;
|
|
88
|
-
error?: Error;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
64
|
type RunResult = Record<string, JourneyResult>;
|
|
92
65
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
start: { numJourneys: number };
|
|
98
|
-
'journey:register': {
|
|
99
|
-
journey: Journey;
|
|
100
|
-
};
|
|
101
|
-
'journey:start': {
|
|
102
|
-
journey: Journey;
|
|
103
|
-
timestamp: number;
|
|
104
|
-
params: Params;
|
|
105
|
-
};
|
|
106
|
-
'journey:end': BaseContext &
|
|
107
|
-
JourneyResult &
|
|
108
|
-
PluginOutput & {
|
|
109
|
-
journey: Journey;
|
|
110
|
-
ssblocks?: boolean;
|
|
111
|
-
};
|
|
112
|
-
'journey:end:reported': unknown;
|
|
113
|
-
'step:start': { journey: Journey; step: Step };
|
|
114
|
-
'step:end': StepResult & {
|
|
115
|
-
start: number;
|
|
116
|
-
end: number;
|
|
117
|
-
journey: Journey;
|
|
118
|
-
step: Step;
|
|
119
|
-
};
|
|
120
|
-
end: unknown;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export default interface Runner {
|
|
124
|
-
on<K extends keyof Events>(name: K, listener: (v: Events[K]) => void): this;
|
|
125
|
-
emit<K extends keyof Events>(event: K, args: Events[K]): boolean;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export default class Runner extends EventEmitter {
|
|
129
|
-
active = false;
|
|
130
|
-
currentJourney?: Journey = null;
|
|
66
|
+
export default class Runner {
|
|
67
|
+
#active = false;
|
|
68
|
+
#reporter: Reporter;
|
|
69
|
+
#currentJourney?: Journey = null;
|
|
131
70
|
journeys: Journey[] = [];
|
|
132
71
|
hooks: SuiteHooks = { beforeAll: [], afterAll: [] };
|
|
133
|
-
|
|
72
|
+
hookError: Error | undefined;
|
|
73
|
+
monitor?: Monitor;
|
|
74
|
+
static screenshotPath = join(CACHE_PATH, 'screenshots');
|
|
134
75
|
|
|
135
76
|
static async createContext(options: RunOptions): Promise<JourneyContext> {
|
|
136
|
-
const start = monotonicTimeInSeconds();
|
|
137
77
|
const driver = await Gatherer.setupDriver(options);
|
|
78
|
+
/**
|
|
79
|
+
* Do not include browser launch/context creation duration
|
|
80
|
+
* as part of journey duration
|
|
81
|
+
*/
|
|
82
|
+
const start = monotonicTimeInSeconds();
|
|
138
83
|
const pluginManager = await Gatherer.beginRecording(driver, options);
|
|
84
|
+
/**
|
|
85
|
+
* For each journey we create the screenshots folder for
|
|
86
|
+
* caching all screenshots and clear them at end of each journey
|
|
87
|
+
*/
|
|
88
|
+
await mkdir(this.screenshotPath, { recursive: true });
|
|
139
89
|
return {
|
|
140
90
|
start,
|
|
141
91
|
params: options.params,
|
|
@@ -144,13 +94,65 @@ export default class Runner extends EventEmitter {
|
|
|
144
94
|
};
|
|
145
95
|
}
|
|
146
96
|
|
|
97
|
+
async captureScreenshot(page: Driver['page'], step: Step) {
|
|
98
|
+
const buffer = await page
|
|
99
|
+
.screenshot({
|
|
100
|
+
type: 'jpeg',
|
|
101
|
+
quality: 80,
|
|
102
|
+
timeout: 5000,
|
|
103
|
+
})
|
|
104
|
+
.catch(() => {});
|
|
105
|
+
/**
|
|
106
|
+
* Write the screenshot image buffer with additional details (step
|
|
107
|
+
* information) which could be extracted at the end of
|
|
108
|
+
* each journey without impacting the step timing information
|
|
109
|
+
*/
|
|
110
|
+
if (buffer) {
|
|
111
|
+
const fileName = `${generateUniqueId()}.json`;
|
|
112
|
+
const screenshot: Screenshot = {
|
|
113
|
+
step,
|
|
114
|
+
timestamp: getTimestamp(),
|
|
115
|
+
data: buffer.toString('base64'),
|
|
116
|
+
};
|
|
117
|
+
await writeFile(
|
|
118
|
+
join(Runner.screenshotPath, fileName),
|
|
119
|
+
JSON.stringify(screenshot)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get currentJourney() {
|
|
125
|
+
return this.#currentJourney;
|
|
126
|
+
}
|
|
127
|
+
|
|
147
128
|
addHook(type: HookType, callback: HooksCallback) {
|
|
148
129
|
this.hooks[type].push(callback);
|
|
149
130
|
}
|
|
150
131
|
|
|
132
|
+
updateMonitor(config: MonitorConfig) {
|
|
133
|
+
if (!this.monitor) {
|
|
134
|
+
this.monitor = new Monitor(config);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.monitor.update(config);
|
|
138
|
+
}
|
|
139
|
+
|
|
151
140
|
addJourney(journey: Journey) {
|
|
152
141
|
this.journeys.push(journey);
|
|
153
|
-
this
|
|
142
|
+
this.#currentJourney = journey;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setReporter(options: RunOptions) {
|
|
146
|
+
/**
|
|
147
|
+
* Set up the corresponding reporter and fallback
|
|
148
|
+
* to default reporter if not provided
|
|
149
|
+
*/
|
|
150
|
+
const { reporter, outfd } = options;
|
|
151
|
+
const Reporter =
|
|
152
|
+
typeof reporter === 'function'
|
|
153
|
+
? reporter
|
|
154
|
+
: reporters[reporter] || reporters['default'];
|
|
155
|
+
this.#reporter = new Reporter({ fd: outfd });
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
async runBeforeAllHook(args: HooksArgs) {
|
|
@@ -182,7 +184,7 @@ export default class Runner extends EventEmitter {
|
|
|
182
184
|
status: 'succeeded',
|
|
183
185
|
};
|
|
184
186
|
log(`Runner: start step (${step.name})`);
|
|
185
|
-
const { metrics, screenshots } = options;
|
|
187
|
+
const { metrics, screenshots, filmstrips, trace } = options;
|
|
186
188
|
const { driver, pluginManager } = context;
|
|
187
189
|
/**
|
|
188
190
|
* URL needs to be the first navigation request of any step
|
|
@@ -193,39 +195,49 @@ export default class Runner extends EventEmitter {
|
|
|
193
195
|
if (!data.url && req.isNavigationRequest()) {
|
|
194
196
|
data.url = req.url();
|
|
195
197
|
}
|
|
196
|
-
driver.
|
|
198
|
+
driver.context.off('request', captureUrl);
|
|
197
199
|
};
|
|
198
|
-
driver.
|
|
200
|
+
driver.context.on('request', captureUrl);
|
|
201
|
+
|
|
199
202
|
try {
|
|
203
|
+
/**
|
|
204
|
+
* Set up plugin manager context and also register
|
|
205
|
+
* step level plugins
|
|
206
|
+
*/
|
|
207
|
+
const traceEnabled = trace || filmstrips;
|
|
200
208
|
pluginManager.onStep(step);
|
|
209
|
+
traceEnabled && (await pluginManager.start('trace'));
|
|
210
|
+
// call the step definition
|
|
201
211
|
await step.callback();
|
|
212
|
+
/**
|
|
213
|
+
* Collect all step level metrics
|
|
214
|
+
*/
|
|
202
215
|
if (metrics) {
|
|
203
|
-
data.
|
|
216
|
+
data.pagemetrics = await (
|
|
217
|
+
pluginManager.get('performance') as PerformanceManager
|
|
218
|
+
).getMetrics();
|
|
219
|
+
}
|
|
220
|
+
if (traceEnabled) {
|
|
221
|
+
const traceOutput = await pluginManager.stop('trace');
|
|
222
|
+
Object.assign(data, traceOutput);
|
|
204
223
|
}
|
|
205
224
|
} catch (error) {
|
|
206
225
|
data.status = 'failed';
|
|
207
226
|
data.error = error;
|
|
208
227
|
} finally {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
writeFileSync(
|
|
223
|
-
join(this.screenshotPath, fileName),
|
|
224
|
-
JSON.stringify({
|
|
225
|
-
step,
|
|
226
|
-
data: buffer.toString('base64'),
|
|
227
|
-
})
|
|
228
|
-
);
|
|
228
|
+
/**
|
|
229
|
+
* Capture screenshot for the newly created pages
|
|
230
|
+
* via popup or new windows/tabs
|
|
231
|
+
*
|
|
232
|
+
* Last open page will get us the correct screenshot
|
|
233
|
+
*/
|
|
234
|
+
const pages = driver.context.pages();
|
|
235
|
+
const page = pages[pages.length - 1];
|
|
236
|
+
if (page) {
|
|
237
|
+
data.url ??= page.url();
|
|
238
|
+
if (screenshots && screenshots !== 'off') {
|
|
239
|
+
await this.captureScreenshot(page, step);
|
|
240
|
+
}
|
|
229
241
|
}
|
|
230
242
|
}
|
|
231
243
|
log(`Runner: end step (${step.name})`);
|
|
@@ -241,7 +253,7 @@ export default class Runner extends EventEmitter {
|
|
|
241
253
|
let skipStep = false;
|
|
242
254
|
for (const step of journey.steps) {
|
|
243
255
|
const start = monotonicTimeInSeconds();
|
|
244
|
-
this
|
|
256
|
+
this.#reporter?.onStepStart?.(journey, step);
|
|
245
257
|
let data: StepResult = { status: 'succeeded' };
|
|
246
258
|
if (skipStep) {
|
|
247
259
|
data.status = 'skipped';
|
|
@@ -252,9 +264,7 @@ export default class Runner extends EventEmitter {
|
|
|
252
264
|
*/
|
|
253
265
|
if (data.error) skipStep = true;
|
|
254
266
|
}
|
|
255
|
-
this
|
|
256
|
-
journey,
|
|
257
|
-
step,
|
|
267
|
+
this.#reporter?.onStepEnd?.(journey, step, {
|
|
258
268
|
start,
|
|
259
269
|
end: monotonicTimeInSeconds(),
|
|
260
270
|
...data,
|
|
@@ -268,12 +278,16 @@ export default class Runner extends EventEmitter {
|
|
|
268
278
|
}
|
|
269
279
|
|
|
270
280
|
registerJourney(journey: Journey, context: JourneyContext) {
|
|
271
|
-
this
|
|
281
|
+
this.#currentJourney = journey;
|
|
272
282
|
const timestamp = getTimestamp();
|
|
273
283
|
const { params } = context;
|
|
274
|
-
this
|
|
284
|
+
this.#reporter?.onJourneyStart?.(journey, {
|
|
285
|
+
timestamp,
|
|
286
|
+
params,
|
|
287
|
+
});
|
|
275
288
|
/**
|
|
276
|
-
*
|
|
289
|
+
* Exeucute the journey callback which would
|
|
290
|
+
* register the steps for the current journey
|
|
277
291
|
*/
|
|
278
292
|
journey.callback({ ...context.driver, params });
|
|
279
293
|
}
|
|
@@ -283,34 +297,50 @@ export default class Runner extends EventEmitter {
|
|
|
283
297
|
result: JourneyContext & JourneyResult,
|
|
284
298
|
options: RunOptions
|
|
285
299
|
) {
|
|
286
|
-
const
|
|
300
|
+
const end = monotonicTimeInSeconds();
|
|
301
|
+
const { pluginManager, start, status, error } = result;
|
|
287
302
|
const pluginOutput = await pluginManager.output();
|
|
288
|
-
this
|
|
289
|
-
journey,
|
|
303
|
+
await this.#reporter?.onJourneyEnd?.(journey, {
|
|
290
304
|
status,
|
|
291
305
|
error,
|
|
292
|
-
params,
|
|
293
306
|
start,
|
|
294
|
-
end
|
|
295
|
-
|
|
307
|
+
end,
|
|
308
|
+
timestamp: getTimestamp(),
|
|
309
|
+
options,
|
|
296
310
|
...pluginOutput,
|
|
297
311
|
browserconsole: status == 'failed' ? pluginOutput.browserconsole : [],
|
|
298
312
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
* by reporter.
|
|
302
|
-
*/
|
|
303
|
-
if (options.reporter == 'json') {
|
|
304
|
-
await once(this, 'journey:end:reported');
|
|
305
|
-
}
|
|
313
|
+
// clear screenshots cache after each journey
|
|
314
|
+
await rm(Runner.screenshotPath, { recursive: true, force: true });
|
|
306
315
|
}
|
|
307
316
|
|
|
308
|
-
|
|
317
|
+
/**
|
|
318
|
+
* Simulate a journey run to capture errors in the beforeAll hook
|
|
319
|
+
*/
|
|
320
|
+
async runFakeJourney(journey: Journey, options: RunOptions) {
|
|
321
|
+
const start = monotonicTimeInSeconds();
|
|
322
|
+
this.#reporter.onJourneyStart?.(journey, {
|
|
323
|
+
timestamp: getTimestamp(),
|
|
324
|
+
params: options.params,
|
|
325
|
+
});
|
|
309
326
|
const result: JourneyResult = {
|
|
310
|
-
status: '
|
|
327
|
+
status: 'failed',
|
|
328
|
+
error: this.hookError,
|
|
311
329
|
};
|
|
312
|
-
|
|
330
|
+
await this.#reporter.onJourneyEnd?.(journey, {
|
|
331
|
+
timestamp: getTimestamp(),
|
|
332
|
+
start,
|
|
333
|
+
options,
|
|
334
|
+
end: monotonicTimeInSeconds(),
|
|
335
|
+
...result,
|
|
336
|
+
});
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async runJourney(journey: Journey, options: RunOptions) {
|
|
341
|
+
const result: JourneyResult = { status: 'succeeded' };
|
|
313
342
|
const context = await Runner.createContext(options);
|
|
343
|
+
log(`Runner: start journey (${journey.name})`);
|
|
314
344
|
try {
|
|
315
345
|
this.registerJourney(journey, context);
|
|
316
346
|
const hookArgs = {
|
|
@@ -340,68 +370,101 @@ export default class Runner extends EventEmitter {
|
|
|
340
370
|
return result;
|
|
341
371
|
}
|
|
342
372
|
|
|
343
|
-
init(options: RunOptions) {
|
|
344
|
-
|
|
373
|
+
async init(options: RunOptions) {
|
|
374
|
+
this.setReporter(options);
|
|
375
|
+
this.#reporter.onStart?.({
|
|
376
|
+
numJourneys: this.journeys.length,
|
|
377
|
+
networkConditions: options.networkConditions,
|
|
378
|
+
});
|
|
345
379
|
/**
|
|
346
|
-
* Set up the
|
|
380
|
+
* Set up the directory for caching screenshots
|
|
347
381
|
*/
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
new Reporter(this, { fd: outfd });
|
|
353
|
-
this.emit('start', { numJourneys: this.journeys.length });
|
|
382
|
+
await mkdir(CACHE_PATH, { recursive: true });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
buildMonitors(options: RunOptions) {
|
|
354
386
|
/**
|
|
355
|
-
*
|
|
387
|
+
* Update the global monitor configuration required for
|
|
388
|
+
* setting defaults
|
|
356
389
|
*/
|
|
357
|
-
|
|
390
|
+
this.updateMonitor({
|
|
391
|
+
throttling: options.throttling,
|
|
392
|
+
schedule: options.schedule,
|
|
393
|
+
locations: options.locations,
|
|
394
|
+
privateLocations: options.privateLocations,
|
|
395
|
+
params: options.params,
|
|
396
|
+
playwrightOptions: options.playwrightOptions,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const { match, tags } = options;
|
|
400
|
+
|
|
401
|
+
const monitors: Monitor[] = [];
|
|
402
|
+
for (const journey of this.journeys) {
|
|
403
|
+
if (!journey.isMatch(match, tags)) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this.#currentJourney = journey;
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Execute dummy callback to get all monitor specific
|
|
411
|
+
* configurations for the current journey
|
|
412
|
+
*/
|
|
413
|
+
journey.callback({ params: options.params } as any);
|
|
414
|
+
journey.monitor.update(this.monitor?.config);
|
|
415
|
+
monitors.push(journey.monitor);
|
|
416
|
+
}
|
|
417
|
+
return monitors;
|
|
358
418
|
}
|
|
359
419
|
|
|
360
420
|
async run(options: RunOptions) {
|
|
361
421
|
const result: RunResult = {};
|
|
362
|
-
if (this
|
|
422
|
+
if (this.#active) {
|
|
363
423
|
return result;
|
|
364
424
|
}
|
|
365
|
-
this
|
|
425
|
+
this.#active = true;
|
|
366
426
|
log(`Runner: run ${this.journeys.length} journeys`);
|
|
367
427
|
this.init(options);
|
|
368
428
|
await this.runBeforeAllHook({
|
|
369
429
|
env: options.environment,
|
|
370
430
|
params: options.params,
|
|
371
|
-
});
|
|
431
|
+
}).catch(e => (this.hookError = e));
|
|
432
|
+
|
|
372
433
|
const { dryRun, match, tags } = options;
|
|
373
434
|
for (const journey of this.journeys) {
|
|
374
435
|
/**
|
|
375
436
|
* Used by heartbeat to gather all registered journeys
|
|
376
437
|
*/
|
|
377
438
|
if (dryRun) {
|
|
378
|
-
this.
|
|
439
|
+
this.#reporter.onJourneyRegister?.(journey);
|
|
379
440
|
continue;
|
|
380
441
|
}
|
|
381
442
|
if (!journey.isMatch(match, tags)) {
|
|
382
443
|
continue;
|
|
383
444
|
}
|
|
384
|
-
const journeyResult =
|
|
445
|
+
const journeyResult: JourneyResult = this.hookError
|
|
446
|
+
? await this.runFakeJourney(journey, options)
|
|
447
|
+
: await this.runJourney(journey, options);
|
|
385
448
|
result[journey.name] = journeyResult;
|
|
386
|
-
await Gatherer.stop();
|
|
387
449
|
}
|
|
450
|
+
await Gatherer.stop();
|
|
388
451
|
await this.runAfterAllHook({
|
|
389
452
|
env: options.environment,
|
|
390
453
|
params: options.params,
|
|
391
454
|
});
|
|
392
|
-
this.reset();
|
|
455
|
+
await this.reset();
|
|
393
456
|
return result;
|
|
394
457
|
}
|
|
395
458
|
|
|
396
|
-
reset() {
|
|
459
|
+
async reset() {
|
|
460
|
+
this.#currentJourney = null;
|
|
461
|
+
this.journeys = [];
|
|
462
|
+
this.#active = false;
|
|
397
463
|
/**
|
|
398
464
|
* Clear all cache data stored for post processing by
|
|
399
465
|
* the current synthetic agent run
|
|
400
466
|
*/
|
|
401
|
-
|
|
402
|
-
this
|
|
403
|
-
this.journeys = [];
|
|
404
|
-
this.active = false;
|
|
405
|
-
this.emit('end', {});
|
|
467
|
+
await rm(CACHE_PATH, { recursive: true, force: true });
|
|
468
|
+
await this.#reporter?.onEnd?.();
|
|
406
469
|
}
|
|
407
470
|
}
|
package/src/dsl/journey.ts
CHANGED
|
@@ -23,10 +23,17 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
Browser,
|
|
28
|
+
Page,
|
|
29
|
+
BrowserContext,
|
|
30
|
+
CDPSession,
|
|
31
|
+
APIRequestContext,
|
|
32
|
+
} from 'playwright-chromium';
|
|
27
33
|
import micromatch, { isMatch } from 'micromatch';
|
|
28
34
|
import { Step } from './step';
|
|
29
|
-
import { VoidCallback, HooksCallback, Params } from '../common_types';
|
|
35
|
+
import { VoidCallback, HooksCallback, Params, Location } from '../common_types';
|
|
36
|
+
import { Monitor, MonitorConfig } from './monitor';
|
|
30
37
|
|
|
31
38
|
export type JourneyOptions = {
|
|
32
39
|
name: string;
|
|
@@ -42,6 +49,7 @@ export type JourneyCallback = (options: {
|
|
|
42
49
|
browser: Browser;
|
|
43
50
|
client: CDPSession;
|
|
44
51
|
params: Params;
|
|
52
|
+
request: APIRequestContext;
|
|
45
53
|
}) => void;
|
|
46
54
|
|
|
47
55
|
export class Journey {
|
|
@@ -49,18 +57,26 @@ export class Journey {
|
|
|
49
57
|
id?: string;
|
|
50
58
|
tags?: string[];
|
|
51
59
|
callback: JourneyCallback;
|
|
60
|
+
location?: Location;
|
|
52
61
|
steps: Step[] = [];
|
|
53
62
|
hooks: Hooks = { before: [], after: [] };
|
|
63
|
+
monitor: Monitor;
|
|
54
64
|
|
|
55
|
-
constructor(
|
|
65
|
+
constructor(
|
|
66
|
+
options: JourneyOptions,
|
|
67
|
+
callback: JourneyCallback,
|
|
68
|
+
location?: Location
|
|
69
|
+
) {
|
|
56
70
|
this.name = options.name;
|
|
57
|
-
this.id = options.id;
|
|
71
|
+
this.id = options.id || options.name;
|
|
58
72
|
this.tags = options.tags;
|
|
59
73
|
this.callback = callback;
|
|
74
|
+
this.location = location;
|
|
75
|
+
this.updateMonitor({});
|
|
60
76
|
}
|
|
61
77
|
|
|
62
|
-
addStep(name: string, callback: VoidCallback) {
|
|
63
|
-
const step = new Step(name, this.steps.length + 1, callback);
|
|
78
|
+
addStep(name: string, callback: VoidCallback, location?: Location) {
|
|
79
|
+
const step = new Step(name, this.steps.length + 1, callback, location);
|
|
64
80
|
this.steps.push(step);
|
|
65
81
|
return step;
|
|
66
82
|
}
|
|
@@ -68,6 +84,21 @@ export class Journey {
|
|
|
68
84
|
addHook(type: HookType, callback: HooksCallback) {
|
|
69
85
|
this.hooks[type].push(callback);
|
|
70
86
|
}
|
|
87
|
+
|
|
88
|
+
updateMonitor(config: MonitorConfig) {
|
|
89
|
+
/**
|
|
90
|
+
* Use defaults values from journey for monitor object (id, name and tags)
|
|
91
|
+
*/
|
|
92
|
+
this.monitor = new Monitor({
|
|
93
|
+
name: this.name,
|
|
94
|
+
id: this.id,
|
|
95
|
+
tags: this.tags ?? [],
|
|
96
|
+
...config,
|
|
97
|
+
});
|
|
98
|
+
this.monitor.setSource(this.location);
|
|
99
|
+
this.monitor.setFilter({ match: this.name });
|
|
100
|
+
}
|
|
101
|
+
|
|
71
102
|
/**
|
|
72
103
|
* Matches journeys based on the provided args. Proitize tags over match
|
|
73
104
|
* - tags pattern that matches only tags
|