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