@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.
Files changed (87) hide show
  1. package/dist/cli.js +23 -4
  2. package/dist/cli.js.map +1 -1
  3. package/dist/common_types.d.ts +9 -16
  4. package/dist/common_types.d.ts.map +1 -1
  5. package/dist/core/gatherer.d.ts +3 -1
  6. package/dist/core/gatherer.d.ts.map +1 -1
  7. package/dist/core/gatherer.js +42 -35
  8. package/dist/core/gatherer.js.map +1 -1
  9. package/dist/core/globals.d.ts +27 -0
  10. package/dist/core/globals.d.ts.map +1 -0
  11. package/dist/core/globals.js +41 -0
  12. package/dist/core/globals.js.map +1 -0
  13. package/dist/core/index.d.ts +0 -2
  14. package/dist/core/index.d.ts.map +1 -1
  15. package/dist/core/index.js +13 -25
  16. package/dist/core/index.js.map +1 -1
  17. package/dist/core/mfa.d.ts +4 -0
  18. package/dist/core/mfa.d.ts.map +1 -1
  19. package/dist/core/mfa.js.map +1 -1
  20. package/dist/core/runner.d.ts +30 -39
  21. package/dist/core/runner.d.ts.map +1 -1
  22. package/dist/core/runner.js +174 -179
  23. package/dist/core/runner.js.map +1 -1
  24. package/dist/dsl/journey.d.ts +24 -16
  25. package/dist/dsl/journey.d.ts.map +1 -1
  26. package/dist/dsl/journey.js +28 -15
  27. package/dist/dsl/journey.js.map +1 -1
  28. package/dist/dsl/monitor.d.ts +1 -0
  29. package/dist/dsl/monitor.d.ts.map +1 -1
  30. package/dist/dsl/monitor.js.map +1 -1
  31. package/dist/dsl/step.d.ts +12 -6
  32. package/dist/dsl/step.d.ts.map +1 -1
  33. package/dist/dsl/step.js +11 -3
  34. package/dist/dsl/step.js.map +1 -1
  35. package/dist/helpers.d.ts +1 -0
  36. package/dist/helpers.d.ts.map +1 -1
  37. package/dist/helpers.js +1 -0
  38. package/dist/helpers.js.map +1 -1
  39. package/dist/index.js +10 -10
  40. package/dist/index.js.map +1 -1
  41. package/dist/options.d.ts +1 -0
  42. package/dist/options.d.ts.map +1 -1
  43. package/dist/options.js +18 -0
  44. package/dist/options.js.map +1 -1
  45. package/dist/push/index.d.ts.map +1 -1
  46. package/dist/push/index.js +1 -1
  47. package/dist/push/index.js.map +1 -1
  48. package/dist/push/monitor.d.ts +1 -0
  49. package/dist/push/monitor.d.ts.map +1 -1
  50. package/dist/push/monitor.js +18 -1
  51. package/dist/push/monitor.js.map +1 -1
  52. package/dist/reporters/base.d.ts +3 -3
  53. package/dist/reporters/base.d.ts.map +1 -1
  54. package/dist/reporters/base.js +6 -6
  55. package/dist/reporters/base.js.map +1 -1
  56. package/dist/reporters/build_kite_cli.d.ts +2 -4
  57. package/dist/reporters/build_kite_cli.d.ts.map +1 -1
  58. package/dist/reporters/build_kite_cli.js +8 -10
  59. package/dist/reporters/build_kite_cli.js.map +1 -1
  60. package/dist/reporters/json.d.ts +4 -9
  61. package/dist/reporters/json.d.ts.map +1 -1
  62. package/dist/reporters/json.js +31 -40
  63. package/dist/reporters/json.js.map +1 -1
  64. package/dist/reporters/junit.d.ts +2 -2
  65. package/dist/reporters/junit.d.ts.map +1 -1
  66. package/dist/reporters/junit.js +7 -7
  67. package/dist/reporters/junit.js.map +1 -1
  68. package/package.json +2 -2
  69. package/src/cli.ts +24 -5
  70. package/src/common_types.ts +9 -16
  71. package/src/core/gatherer.ts +44 -37
  72. package/src/core/globals.ts +37 -0
  73. package/src/core/index.ts +12 -25
  74. package/src/core/mfa.ts +5 -0
  75. package/src/core/runner.ts +201 -202
  76. package/src/dsl/journey.ts +42 -23
  77. package/src/dsl/monitor.ts +1 -0
  78. package/src/dsl/step.ts +16 -7
  79. package/src/helpers.ts +4 -3
  80. package/src/index.ts +2 -2
  81. package/src/options.ts +22 -0
  82. package/src/push/index.ts +3 -4
  83. package/src/push/monitor.ts +20 -0
  84. package/src/reporters/base.ts +8 -12
  85. package/src/reporters/build_kite_cli.ts +13 -18
  86. package/src/reporters/json.ts +35 -52
  87. package/src/reporters/junit.ts +8 -10
@@ -23,51 +23,42 @@
23
23
  *
24
24
  */
25
25
  import { Journey } from '../dsl/journey';
26
- import { Step } from '../dsl/step';
27
- import { HooksCallback, Params, HooksArgs, Driver, RunOptions, JourneyResult, StepResult, PushOptions } from '../common_types';
28
- import { PluginManager } from '../plugins';
26
+ import { HooksCallback, RunOptions, JourneyResult, PushOptions } from '../common_types';
29
27
  import { Monitor, MonitorConfig } from '../dsl/monitor';
30
28
  type HookType = 'beforeAll' | 'afterAll';
31
29
  export type SuiteHooks = Record<HookType, Array<HooksCallback>>;
32
- type JourneyContext = {
33
- params?: Params;
34
- browserDelay: number;
35
- start: number;
36
- driver: Driver;
37
- pluginManager: PluginManager;
38
- };
39
30
  type RunResult = Record<string, JourneyResult>;
40
- export default class Runner {
41
- #private;
42
- journeys: Journey[];
43
- hooks: SuiteHooks;
44
- hookError: Error | undefined;
45
- monitor?: Monitor;
46
- static screenshotPath: string;
47
- static createContext(options: RunOptions): Promise<JourneyContext>;
48
- captureScreenshot(page: Driver['page'], step: Step): Promise<void>;
49
- get currentJourney(): Journey;
50
- addHook(type: HookType, callback: HooksCallback): void;
51
- updateMonitor(config: MonitorConfig): void;
52
- addJourney(journey: Journey): void;
53
- setReporter(options: RunOptions): void;
54
- runBeforeAllHook(args: HooksArgs): Promise<void>;
55
- runAfterAllHook(args: HooksArgs): Promise<void>;
56
- runBeforeHook(journey: Journey, args: HooksArgs): Promise<void>;
57
- runAfterHook(journey: Journey, args: HooksArgs): Promise<void>;
58
- runStep(step: Step, context: JourneyContext, options: RunOptions): Promise<StepResult>;
59
- runSteps(journey: Journey, context: JourneyContext, options: RunOptions): Promise<StepResult[]>;
60
- registerJourney(journey: Journey, context: JourneyContext): void;
61
- endJourney(journey: any, result: JourneyContext & JourneyResult, options: RunOptions): Promise<void>;
31
+ export interface RunnerInfo {
32
+ /**
33
+ * Processed configuration from the CLI args and the config file
34
+ */
35
+ readonly config: RunOptions;
62
36
  /**
63
- * Simulate a journey run to capture errors in the beforeAll hook
37
+ * Currently active journey
64
38
  */
65
- runFakeJourney(journey: Journey, options: RunOptions): Promise<JourneyResult>;
66
- runJourney(journey: Journey, options: RunOptions): Promise<JourneyResult>;
67
- init(options: RunOptions): Promise<void>;
68
- buildMonitors(options: PushOptions): Monitor[];
69
- run(options: RunOptions): Promise<RunResult>;
70
- reset(): Promise<void>;
39
+ readonly journey: Journey | undefined;
40
+ /**
41
+ * All registerd journeys
42
+ */
43
+ readonly journeys: Journey[];
44
+ }
45
+ export default class Runner implements RunnerInfo {
46
+ #private;
47
+ config: RunOptions;
48
+ get journey(): Journey;
49
+ get journeys(): Journey[];
50
+ get hooks(): SuiteHooks;
51
+ private captureScreenshot;
52
+ _addHook(type: HookType, callback: HooksCallback): void;
53
+ private buildHookArgs;
54
+ _updateMonitor(config: MonitorConfig): void;
55
+ _addJourney(journey: Journey): void;
56
+ private setReporter;
57
+ _runJourney(journey: Journey, options: RunOptions): Promise<JourneyResult>;
58
+ _buildMonitors(options: PushOptions): Monitor[];
59
+ _run(options: RunOptions): Promise<RunResult>;
60
+ _runJourneys(options: RunOptions): Promise<RunResult>;
61
+ _reset(): Promise<void>;
71
62
  }
72
63
  export {};
73
64
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AASnC,OAAO,EACL,aAAa,EACb,MAAM,EACN,SAAS,EACT,MAAM,EAEN,UAAU,EACV,aAAa,EACb,UAAU,EACV,WAAW,EACZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EAGd,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAExD,KAAK,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;AAEhE,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE/C,MAAM,CAAC,OAAO,OAAO,MAAM;;IAIzB,QAAQ,EAAE,OAAO,EAAE,CAAM;IACzB,KAAK,EAAE,UAAU,CAAmC;IACpD,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,cAAc,SAAmC;WAE3C,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,CAAC;IAuBlE,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI;IA6BxD,IAAI,cAAc,YAEjB;IAED,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa;IAI/C,aAAa,CAAC,MAAM,EAAE,aAAa;IAQnC,UAAU,CAAC,OAAO,EAAE,OAAO;IAK3B,WAAW,CAAC,OAAO,EAAE,UAAU;IAazB,gBAAgB,CAAC,IAAI,EAAE,SAAS;IAKhC,eAAe,CAAC,IAAI,EAAE,SAAS;IAK/B,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;IAK/C,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;IAK9C,OAAO,CACX,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC;IAkEhB,QAAQ,CACZ,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,UAAU;IAyCrB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc;IAcnD,UAAU,CACd,OAAO,KAAA,EACP,MAAM,EAAE,cAAc,GAAG,aAAa,EACtC,OAAO,EAAE,UAAU;IAuBrB;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU;IAqBpD,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU;IAkChD,IAAI,CAAC,OAAO,EAAE,UAAU;IAY9B,aAAa,CAAC,OAAO,EAAE,WAAW;IAkD5B,GAAG,CAAC,OAAO,EAAE,UAAU;IA+CvB,KAAK;CAWZ"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAUzC,OAAO,EACL,aAAa,EAIb,UAAU,EACV,aAAa,EAEb,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAOzB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAExD,KAAK,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;AAEhE,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE/C,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;CAC9B;AAED,MAAM,CAAC,OAAO,OAAO,MAAO,YAAW,UAAU;;IAW/C,MAAM,EAAE,UAAU,CAAC;IAEnB,IAAI,OAAO,YAEV;IAED,IAAI,QAAQ,cAEX;IAED,IAAI,KAAK,eAER;YAEa,iBAAiB;IA6B/B,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa;IAIhD,OAAO,CAAC,aAAa;IAIrB,cAAc,CAAC,MAAM,EAAE,aAAa;IAQpC,WAAW,CAAC,OAAO,EAAE,OAAO;IAK5B,OAAO,CAAC,WAAW;IAiNb,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU;IAmCvD,cAAc,CAAC,OAAO,EAAE,WAAW;IA+D7B,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;IAiC7C,YAAY,CAAC,OAAO,EAAE,UAAU;IAgBhC,MAAM;CAWb"}
@@ -36,32 +36,22 @@ class Runner {
36
36
  #active = false;
37
37
  #reporter;
38
38
  #currentJourney = null;
39
- journeys = [];
40
- hooks = { beforeAll: [], afterAll: [] };
41
- hookError;
42
- monitor;
43
- static screenshotPath = (0, path_1.join)(helpers_1.CACHE_PATH, 'screenshots');
44
- static async createContext(options) {
45
- const browserStart = (0, helpers_1.monotonicTimeInSeconds)();
46
- const driver = await gatherer_1.Gatherer.setupDriver(options);
47
- /**
48
- * Do not include browser launch/context creation duration
49
- * as part of journey duration
50
- */
51
- const start = (0, helpers_1.monotonicTimeInSeconds)();
52
- const pluginManager = await gatherer_1.Gatherer.beginRecording(driver, options);
53
- /**
54
- * For each journey we create the screenshots folder for
55
- * caching all screenshots and clear them at end of each journey
56
- */
57
- await (0, promises_1.mkdir)(this.screenshotPath, { recursive: true });
58
- return {
59
- start,
60
- browserDelay: start - browserStart,
61
- params: options.params,
62
- driver,
63
- pluginManager,
64
- };
39
+ #journeys = [];
40
+ #hooks = { beforeAll: [], afterAll: [] };
41
+ #screenshotPath = (0, path_1.join)(helpers_1.CACHE_PATH, 'screenshots');
42
+ #driver;
43
+ #browserDelay = -1;
44
+ #hookError;
45
+ #monitor;
46
+ config;
47
+ get journey() {
48
+ return this.#currentJourney;
49
+ }
50
+ get journeys() {
51
+ return this.#journeys;
52
+ }
53
+ get hooks() {
54
+ return this.#hooks;
65
55
  }
66
56
  async captureScreenshot(page, step) {
67
57
  try {
@@ -81,7 +71,7 @@ class Runner {
81
71
  timestamp: (0, helpers_1.getTimestamp)(),
82
72
  data: buffer.toString('base64'),
83
73
  };
84
- await (0, promises_1.writeFile)((0, path_1.join)(Runner.screenshotPath, fileName), JSON.stringify(screenshot));
74
+ await (0, promises_1.writeFile)((0, path_1.join)(this.#screenshotPath, fileName), JSON.stringify(screenshot));
85
75
  (0, logger_1.log)(`Runner: captured screenshot for (${step.name})`);
86
76
  }
87
77
  catch (_) {
@@ -89,21 +79,21 @@ class Runner {
89
79
  (0, logger_1.log)(`Runner: failed to capture screenshot for (${step.name})`);
90
80
  }
91
81
  }
92
- get currentJourney() {
93
- return this.#currentJourney;
82
+ _addHook(type, callback) {
83
+ this.#hooks[type].push(callback);
94
84
  }
95
- addHook(type, callback) {
96
- this.hooks[type].push(callback);
85
+ buildHookArgs() {
86
+ return { env: this.config.environment, params: this.config.params, info: this };
97
87
  }
98
- updateMonitor(config) {
99
- if (!this.monitor) {
100
- this.monitor = new monitor_1.Monitor(config);
88
+ _updateMonitor(config) {
89
+ if (!this.#monitor) {
90
+ this.#monitor = new monitor_1.Monitor(config);
101
91
  return;
102
92
  }
103
- this.monitor.update(config);
93
+ this.#monitor.update(config);
104
94
  }
105
- addJourney(journey) {
106
- this.journeys.push(journey);
95
+ _addJourney(journey) {
96
+ this.#journeys.push(journey);
107
97
  this.#currentJourney = journey;
108
98
  }
109
99
  setReporter(options) {
@@ -117,66 +107,64 @@ class Runner {
117
107
  : reporters_1.reporters[reporter] || reporters_1.reporters['default'];
118
108
  this.#reporter = new Reporter({ fd: outfd, dryRun });
119
109
  }
120
- async runBeforeAllHook(args) {
110
+ async #runBeforeAllHook(args) {
121
111
  (0, logger_1.log)(`Runner: beforeAll hooks`);
122
- await (0, helpers_1.runParallel)(this.hooks.beforeAll, args);
112
+ await (0, helpers_1.runParallel)(this.#hooks.beforeAll, args);
123
113
  }
124
- async runAfterAllHook(args) {
114
+ async #runAfterAllHook(args) {
125
115
  (0, logger_1.log)(`Runner: afterAll hooks`);
126
- await (0, helpers_1.runParallel)(this.hooks.afterAll, args);
116
+ await (0, helpers_1.runParallel)(this.#hooks.afterAll, args);
127
117
  }
128
- async runBeforeHook(journey, args) {
118
+ async #runBeforeHook(journey, args) {
129
119
  (0, logger_1.log)(`Runner: before hooks for (${journey.name})`);
130
- await (0, helpers_1.runParallel)(journey.hooks.before, args);
120
+ await (0, helpers_1.runParallel)(journey._getHook('before'), args);
131
121
  }
132
- async runAfterHook(journey, args) {
122
+ async #runAfterHook(journey, args) {
133
123
  (0, logger_1.log)(`Runner: after hooks for (${journey.name})`);
134
- await (0, helpers_1.runParallel)(journey.hooks.after, args);
124
+ await (0, helpers_1.runParallel)(journey._getHook('after'), args);
135
125
  }
136
- async runStep(step, context, options) {
137
- const data = {
138
- status: 'succeeded',
139
- };
126
+ async #runStep(step, options) {
140
127
  (0, logger_1.log)(`Runner: start step (${step.name})`);
141
128
  const { metrics, screenshots, filmstrips, trace } = options;
142
- const { driver, pluginManager } = context;
143
129
  /**
144
130
  * URL needs to be the first navigation request of any step
145
131
  * Listening for request solves the case where `about:blank` would be
146
132
  * reported for failed navigations
147
133
  */
148
134
  const captureUrl = req => {
149
- if (!data.url && req.isNavigationRequest()) {
150
- data.url = req.url();
135
+ if (!step.url && req.isNavigationRequest()) {
136
+ step.url = req.url();
151
137
  }
152
- driver.context.off('request', captureUrl);
138
+ this.#driver.context.off('request', captureUrl);
153
139
  };
154
- driver.context.on('request', captureUrl);
140
+ this.#driver.context.on('request', captureUrl);
141
+ const data = {};
155
142
  const traceEnabled = trace || filmstrips;
156
143
  try {
157
144
  /**
158
145
  * Set up plugin manager context and also register
159
146
  * step level plugins
160
147
  */
161
- pluginManager.onStep(step);
162
- traceEnabled && (await pluginManager.start('trace'));
148
+ gatherer_1.Gatherer.pluginManager.onStep(step);
149
+ traceEnabled && (await gatherer_1.Gatherer.pluginManager.start('trace'));
163
150
  // invoke the step callback by extracting to a variable to get better stack trace
164
- const cb = step.callback;
151
+ const cb = step.cb;
165
152
  await cb();
153
+ step.status = "succeeded";
166
154
  }
167
155
  catch (error) {
168
- data.status = 'failed';
169
- data.error = error;
156
+ step.status = 'failed';
157
+ step.error = error;
170
158
  }
171
159
  finally {
172
160
  /**
173
161
  * Collect all step level metrics and trace events
174
162
  */
175
163
  if (metrics) {
176
- data.pagemetrics = await pluginManager.get('performance').getMetrics();
164
+ data.pagemetrics = await gatherer_1.Gatherer.pluginManager.get('performance').getMetrics();
177
165
  }
178
166
  if (traceEnabled) {
179
- const traceOutput = await pluginManager.stop('trace');
167
+ const traceOutput = await gatherer_1.Gatherer.pluginManager.stop('trace');
180
168
  Object.assign(data, traceOutput);
181
169
  }
182
170
  /**
@@ -185,10 +173,10 @@ class Runner {
185
173
  *
186
174
  * Last open page will get us the correct screenshot
187
175
  */
188
- const pages = driver.context.pages();
176
+ const pages = this.#driver.context.pages();
189
177
  const page = pages[pages.length - 1];
190
178
  if (page) {
191
- data.url ??= page.url();
179
+ step.url ??= page.url();
192
180
  if (screenshots && screenshots !== 'off') {
193
181
  await this.captureScreenshot(page, step);
194
182
  }
@@ -197,14 +185,14 @@ class Runner {
197
185
  (0, logger_1.log)(`Runner: end step (${step.name})`);
198
186
  return data;
199
187
  }
200
- async runSteps(journey, context, options) {
188
+ async #runSteps(journey, options) {
201
189
  const results = [];
202
190
  const isOnlyExists = journey.steps.filter(s => s.only).length > 0;
203
191
  let skipStep = false;
204
192
  for (const step of journey.steps) {
205
- const start = (0, helpers_1.monotonicTimeInSeconds)();
193
+ step._startTime = (0, helpers_1.monotonicTimeInSeconds)();
206
194
  this.#reporter?.onStepStart?.(journey, step);
207
- let data = { status: 'succeeded' };
195
+ let data = {};
208
196
  /**
209
197
  * Skip the step
210
198
  * - if the step is marked as skip
@@ -214,133 +202,126 @@ class Runner {
214
202
  if (step.skip ||
215
203
  (skipStep && !step.only) ||
216
204
  (isOnlyExists && !step.only)) {
217
- data.status = 'skipped';
205
+ step.status = 'skipped';
218
206
  }
219
207
  else {
220
- data = await this.runStep(step, context, options);
208
+ data = await this.#runStep(step, options);
221
209
  /**
222
210
  * skip next steps if the previous step returns error
223
211
  */
224
- if (data.error && !step.soft)
212
+ if (step.error && !step.soft)
225
213
  skipStep = true;
226
214
  }
227
- this.#reporter?.onStepEnd?.(journey, step, {
228
- start,
229
- end: (0, helpers_1.monotonicTimeInSeconds)(),
230
- ...data,
231
- });
232
- if (options.pauseOnError && data.error) {
215
+ step.duration = (0, helpers_1.monotonicTimeInSeconds)() - step._startTime;
216
+ this.#reporter?.onStepEnd?.(journey, step, data);
217
+ if (options.pauseOnError && step.error) {
233
218
  await new Promise(r => process.stdin.on('data', r));
234
219
  }
235
220
  results.push(data);
236
221
  }
237
222
  return results;
238
223
  }
239
- registerJourney(journey, context) {
240
- this.#currentJourney = journey;
241
- const timestamp = (0, helpers_1.getTimestamp)();
242
- const { params } = context;
224
+ async #startJourney(journey, options) {
225
+ journey._startTime = (0, helpers_1.monotonicTimeInSeconds)();
226
+ this.#driver = await gatherer_1.Gatherer.setupDriver(options);
227
+ await gatherer_1.Gatherer.beginRecording(this.#driver, options);
228
+ /**
229
+ * For each journey we create the screenshots folder for
230
+ * caching all screenshots and clear them at end of each journey
231
+ */
232
+ await (0, promises_1.mkdir)(this.#screenshotPath, { recursive: true });
233
+ const params = options.params;
243
234
  this.#reporter?.onJourneyStart?.(journey, {
244
- timestamp,
235
+ timestamp: (0, helpers_1.getTimestamp)(),
245
236
  params,
246
237
  });
247
238
  /**
248
239
  * Exeucute the journey callback which registers the steps for current journey
249
240
  */
250
- journey.callback({ ...context.driver, params });
241
+ journey.cb({ ...this.#driver, params, info: this });
251
242
  }
252
- async endJourney(journey, result, options) {
253
- const end = (0, helpers_1.monotonicTimeInSeconds)();
254
- const { pluginManager, start, status, error, browserDelay } = result;
255
- const pluginOutput = await pluginManager.output();
243
+ async #endJourney(journey, result, options) {
244
+ // Enhance the journey results
245
+ const pOutput = await gatherer_1.Gatherer.pluginManager.output();
246
+ const bConsole = (0, plugins_1.filterBrowserMessages)(pOutput.browserconsole, journey.status);
256
247
  await this.#reporter?.onJourneyEnd?.(journey, {
257
- status,
258
- error,
259
- start,
260
- end,
261
- browserDelay,
248
+ browserDelay: this.#browserDelay,
262
249
  timestamp: (0, helpers_1.getTimestamp)(),
263
250
  options,
264
- ...pluginOutput,
265
- browserconsole: (0, plugins_1.filterBrowserMessages)(pluginOutput.browserconsole, status),
251
+ networkinfo: pOutput.networkinfo,
252
+ browserconsole: bConsole,
266
253
  });
254
+ await gatherer_1.Gatherer.endRecording();
255
+ await gatherer_1.Gatherer.dispose(this.#driver);
267
256
  // clear screenshots cache after each journey
268
- await (0, promises_1.rm)(Runner.screenshotPath, { recursive: true, force: true });
257
+ await (0, promises_1.rm)(this.#screenshotPath, { recursive: true, force: true });
258
+ return Object.assign(result, {
259
+ networkinfo: pOutput.networkinfo,
260
+ browserconsole: bConsole,
261
+ ...journey,
262
+ });
269
263
  }
270
264
  /**
271
265
  * Simulate a journey run to capture errors in the beforeAll hook
272
266
  */
273
- async runFakeJourney(journey, options) {
267
+ async #runFakeJourney(journey, options) {
274
268
  const start = (0, helpers_1.monotonicTimeInSeconds)();
275
269
  this.#reporter.onJourneyStart?.(journey, {
276
270
  timestamp: (0, helpers_1.getTimestamp)(),
277
271
  params: options.params,
278
272
  });
279
- const result = {
280
- status: 'failed',
281
- error: this.hookError,
282
- };
273
+ // Mark the journey as failed and report the hook error as journey error
274
+ journey.status = 'failed';
275
+ journey.error = this.#hookError;
276
+ journey.duration = (0, helpers_1.monotonicTimeInSeconds)() - start;
283
277
  await this.#reporter.onJourneyEnd?.(journey, {
284
278
  timestamp: (0, helpers_1.getTimestamp)(),
285
- start,
286
279
  options,
287
- browserDelay: 0,
288
- end: (0, helpers_1.monotonicTimeInSeconds)(),
289
- ...result,
280
+ browserDelay: this.#browserDelay,
290
281
  });
291
- return result;
282
+ return journey;
292
283
  }
293
- async runJourney(journey, options) {
294
- const result = { status: 'succeeded' };
295
- const context = await Runner.createContext(options);
284
+ async _runJourney(journey, options) {
285
+ this.#currentJourney = journey;
296
286
  (0, logger_1.log)(`Runner: start journey (${journey.name})`);
287
+ let result = {};
288
+ const hookArgs = { env: options.environment, params: options.params, info: this };
297
289
  try {
298
- this.registerJourney(journey, context);
299
- const hookArgs = {
300
- env: options.environment,
301
- params: options.params,
302
- };
303
- await this.runBeforeHook(journey, hookArgs);
304
- const stepResults = await this.runSteps(journey, context, options);
305
- /**
306
- * Mark journey as failed if any intermediate step fails
307
- */
308
- for (const stepResult of stepResults) {
309
- if (stepResult.status === 'failed') {
310
- result.status = stepResult.status;
311
- result.error = stepResult.error;
290
+ await this.#startJourney(journey, options);
291
+ await this.#runBeforeHook(journey, hookArgs);
292
+ const stepResults = await this.#runSteps(journey, options);
293
+ journey.status = "succeeded";
294
+ // Mark journey as failed if any one of the step fails
295
+ for (const step of journey.steps) {
296
+ if (step.status === 'failed') {
297
+ journey.status = step.status;
298
+ journey.error = step.error;
312
299
  }
313
300
  }
314
- result.steps = stepResults;
315
- await this.runAfterHook(journey, hookArgs);
301
+ result.stepsresults = stepResults;
316
302
  }
317
303
  catch (e) {
318
- result.status = 'failed';
319
- result.error = e;
304
+ journey.status = 'failed';
305
+ journey.error = e;
320
306
  }
321
307
  finally {
322
- await this.endJourney(journey, { ...context, ...result }, options);
323
- await gatherer_1.Gatherer.dispose(context.driver);
308
+ journey.duration = (0, helpers_1.monotonicTimeInSeconds)() - journey._startTime;
309
+ // Run after hook on journey failure and capture the uncaught error as
310
+ // journey error, hook is purposely run before to capture errors during reporting
311
+ await this.#runAfterHook(journey, hookArgs).catch(e => {
312
+ journey.status = 'failed';
313
+ journey.error = e;
314
+ });
315
+ result = await this.#endJourney(journey, result, options);
324
316
  }
325
317
  (0, logger_1.log)(`Runner: end journey (${journey.name})`);
326
318
  return result;
327
319
  }
328
- async init(options) {
329
- this.setReporter(options);
330
- this.#reporter.onStart?.({
331
- numJourneys: this.journeys.length,
332
- networkConditions: options.networkConditions,
333
- });
334
- /**
335
- * Set up the directory for caching screenshots
336
- */
337
- await (0, promises_1.mkdir)(helpers_1.CACHE_PATH, { recursive: true });
338
- }
339
- buildMonitors(options) {
320
+ _buildMonitors(options) {
340
321
  /**
341
322
  * Update the global monitor configuration required for setting defaults
342
323
  */
343
- this.updateMonitor({
324
+ this._updateMonitor({
344
325
  throttling: options.throttling,
345
326
  schedule: options.schedule,
346
327
  locations: options.locations,
@@ -354,7 +335,7 @@ class Runner {
354
335
  enabled: options.enabled,
355
336
  });
356
337
  const monitors = [];
357
- for (const journey of this.journeys) {
338
+ for (const journey of this.#journeys) {
358
339
  this.#currentJourney = journey;
359
340
  if (journey.skip) {
360
341
  throw new Error(`Journey ${journey.name} is skipped. Please remove the journey.skip annotation and try again.`);
@@ -367,63 +348,77 @@ class Runner {
367
348
  * - filter out monitors based on matched tags and name after applying both
368
349
  * global and local monitor configurations
369
350
  */
370
- journey.callback({ params: options.params });
371
- journey.monitor.update(this.monitor?.config);
372
- if (!journey.monitor.isMatch(options.grepOpts?.match, options.grepOpts?.tags)) {
351
+ journey.cb({ params: options.params });
352
+ const monitor = journey._getMonitor();
353
+ monitor.update(this.#monitor?.config);
354
+ if (!monitor.isMatch(options.grepOpts?.match, options.grepOpts?.tags)) {
373
355
  continue;
374
356
  }
375
- journey.monitor.validate();
376
- monitors.push(journey.monitor);
357
+ monitor.validate();
358
+ monitors.push(monitor);
377
359
  }
378
360
  return monitors;
379
361
  }
380
- async run(options) {
381
- const result = {};
362
+ async #init(options) {
363
+ this.setReporter(options);
364
+ this.#reporter.onStart?.({
365
+ numJourneys: this.#journeys.length,
366
+ networkConditions: options.networkConditions,
367
+ });
368
+ /**
369
+ * Set up the directory for caching screenshots
370
+ */
371
+ await (0, promises_1.mkdir)(helpers_1.CACHE_PATH, { recursive: true });
372
+ }
373
+ async _run(options) {
374
+ this.config = options;
375
+ let result = {};
382
376
  if (this.#active) {
383
377
  return result;
384
378
  }
385
379
  this.#active = true;
386
- (0, logger_1.log)(`Runner: run ${this.journeys.length} journeys`);
387
- this.init(options);
388
- await this.runBeforeAllHook({
389
- env: options.environment,
390
- params: options.params,
391
- }).catch(e => (this.hookError = e));
380
+ (0, logger_1.log)(`Runner: run ${this.#journeys.length} journeys`);
381
+ this.#init(options);
382
+ const hookArgs = this.buildHookArgs();
383
+ await this.#runBeforeAllHook(hookArgs).catch(e => (this.#hookError = e));
392
384
  const { dryRun, grepOpts } = options;
393
- /**
394
- * Skip other journeys when using `.only`
395
- */
396
- const onlyJournerys = this.journeys.filter(j => j.only);
385
+ // collect all journeys with `.only` annotation and skip the rest
386
+ const onlyJournerys = this.#journeys.filter(j => j.only);
397
387
  if (onlyJournerys.length > 0) {
398
- this.journeys = onlyJournerys;
388
+ this.#journeys = onlyJournerys;
399
389
  }
400
- for (const journey of this.journeys) {
401
- /**
402
- * Used by heartbeat to gather all registered journeys
403
- */
404
- if (dryRun) {
405
- this.#reporter.onJourneyRegister?.(journey);
406
- continue;
407
- }
408
- if (!journey.isMatch(grepOpts?.match, grepOpts?.tags) || journey.skip) {
409
- continue;
410
- }
411
- const journeyResult = this.hookError
412
- ? await this.runFakeJourney(journey, options)
413
- : await this.runJourney(journey, options);
390
+ else {
391
+ // filter journeys based on tags and skip annotations
392
+ this.#journeys = this.#journeys.filter(j => j._isMatch(grepOpts?.match, grepOpts?.tags) && !j.skip);
393
+ }
394
+ // Used by heartbeat to gather all registered journeys
395
+ if (dryRun) {
396
+ this.#journeys.forEach(journey => this.#reporter.onJourneyRegister?.(journey));
397
+ }
398
+ else if (this.#journeys.length > 0) {
399
+ result = await this._runJourneys(options);
400
+ }
401
+ await this.#runAfterAllHook(hookArgs).catch(async () => await this._reset());
402
+ await this._reset();
403
+ return result;
404
+ }
405
+ async _runJourneys(options) {
406
+ const result = {};
407
+ const browserStart = (0, helpers_1.monotonicTimeInSeconds)();
408
+ await gatherer_1.Gatherer.launchBrowser(options);
409
+ this.#browserDelay = (0, helpers_1.monotonicTimeInSeconds)() - browserStart;
410
+ for (const journey of this.#journeys) {
411
+ const journeyResult = this.#hookError
412
+ ? await this.#runFakeJourney(journey, options)
413
+ : await this._runJourney(journey, options);
414
414
  result[journey.name] = journeyResult;
415
415
  }
416
416
  await gatherer_1.Gatherer.stop();
417
- await this.runAfterAllHook({
418
- env: options.environment,
419
- params: options.params,
420
- });
421
- await this.reset();
422
417
  return result;
423
418
  }
424
- async reset() {
419
+ async _reset() {
425
420
  this.#currentJourney = null;
426
- this.journeys = [];
421
+ this.#journeys = [];
427
422
  this.#active = false;
428
423
  /**
429
424
  * Clear all cache data stored for post processing by