@elastic/synthetics 1.0.0-beta.2 → 1.0.0-beta.22

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 (122) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/NOTICE.txt +250 -0
  3. package/README.md +2 -1
  4. package/dist/cli.js +87 -56
  5. package/dist/cli.js.map +1 -1
  6. package/dist/common_types.d.ts +116 -14
  7. package/dist/common_types.d.ts.map +1 -1
  8. package/dist/config.d.ts +2 -5
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +5 -5
  11. package/dist/config.js.map +1 -1
  12. package/dist/core/browser-service.js +5 -5
  13. package/dist/core/browser-service.js.map +1 -1
  14. package/dist/core/expect.d.ts +177 -0
  15. package/dist/core/expect.d.ts.map +1 -0
  16. package/dist/core/expect.js +33 -0
  17. package/dist/core/expect.js.map +1 -0
  18. package/dist/core/gatherer.d.ts +4 -7
  19. package/dist/core/gatherer.d.ts.map +1 -1
  20. package/dist/core/gatherer.js +39 -12
  21. package/dist/core/gatherer.js.map +1 -1
  22. package/dist/core/index.d.ts +5 -5
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/index.js +2 -2
  25. package/dist/core/index.js.map +1 -1
  26. package/dist/core/logger.d.ts +0 -1
  27. package/dist/core/logger.d.ts.map +1 -1
  28. package/dist/core/logger.js +3 -16
  29. package/dist/core/logger.js.map +1 -1
  30. package/dist/core/runner.d.ts +54 -45
  31. package/dist/core/runner.d.ts.map +1 -1
  32. package/dist/core/runner.js +181 -71
  33. package/dist/core/runner.js.map +1 -1
  34. package/dist/dsl/journey.d.ts +13 -4
  35. package/dist/dsl/journey.d.ts.map +1 -1
  36. package/dist/dsl/journey.js +40 -1
  37. package/dist/dsl/journey.js.map +1 -1
  38. package/dist/formatter/javascript.d.ts +73 -0
  39. package/dist/formatter/javascript.d.ts.map +1 -0
  40. package/dist/formatter/javascript.js +274 -0
  41. package/dist/formatter/javascript.js.map +1 -0
  42. package/dist/helpers.d.ts +33 -12
  43. package/dist/helpers.d.ts.map +1 -1
  44. package/dist/helpers.js +108 -54
  45. package/dist/helpers.js.map +1 -1
  46. package/dist/index.d.ts +8 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +6 -7
  49. package/dist/index.js.map +1 -1
  50. package/dist/parse_args.d.ts +5 -2
  51. package/dist/parse_args.d.ts.map +1 -1
  52. package/dist/parse_args.js +59 -16
  53. package/dist/parse_args.js.map +1 -1
  54. package/dist/plugins/browser-console.d.ts +5 -8
  55. package/dist/plugins/browser-console.d.ts.map +1 -1
  56. package/dist/plugins/browser-console.js +27 -8
  57. package/dist/plugins/browser-console.js.map +1 -1
  58. package/dist/plugins/network.d.ts +23 -20
  59. package/dist/plugins/network.d.ts.map +1 -1
  60. package/dist/plugins/network.js +149 -124
  61. package/dist/plugins/network.js.map +1 -1
  62. package/dist/plugins/performance.d.ts +5 -5
  63. package/dist/plugins/performance.d.ts.map +1 -1
  64. package/dist/plugins/performance.js +5 -5
  65. package/dist/plugins/performance.js.map +1 -1
  66. package/dist/plugins/plugin-manager.d.ts +11 -11
  67. package/dist/plugins/plugin-manager.d.ts.map +1 -1
  68. package/dist/plugins/plugin-manager.js +40 -24
  69. package/dist/plugins/plugin-manager.js.map +1 -1
  70. package/dist/plugins/tracing.d.ts +10 -25
  71. package/dist/plugins/tracing.d.ts.map +1 -1
  72. package/dist/plugins/tracing.js +40 -26
  73. package/dist/plugins/tracing.js.map +1 -1
  74. package/dist/reporters/base.d.ts +0 -1
  75. package/dist/reporters/base.d.ts.map +1 -1
  76. package/dist/reporters/base.js +34 -32
  77. package/dist/reporters/base.js.map +1 -1
  78. package/dist/reporters/json.d.ts +46 -59
  79. package/dist/reporters/json.d.ts.map +1 -1
  80. package/dist/reporters/json.js +188 -70
  81. package/dist/reporters/json.js.map +1 -1
  82. package/dist/reporters/junit.d.ts.map +1 -1
  83. package/dist/reporters/junit.js +7 -7
  84. package/dist/reporters/junit.js.map +1 -1
  85. package/dist/sdk/lh-trace-processor.d.ts +304 -0
  86. package/dist/sdk/lh-trace-processor.d.ts.map +1 -0
  87. package/dist/sdk/lh-trace-processor.js +832 -0
  88. package/dist/sdk/lh-trace-processor.js.map +1 -0
  89. package/dist/sdk/trace-metrics.d.ts +61 -0
  90. package/dist/sdk/trace-metrics.d.ts.map +1 -0
  91. package/dist/sdk/trace-metrics.js +252 -0
  92. package/dist/sdk/trace-metrics.js.map +1 -0
  93. package/dist/sdk/trace-processor.d.ts +106 -0
  94. package/dist/sdk/trace-processor.d.ts.map +1 -0
  95. package/dist/sdk/trace-processor.js +74 -0
  96. package/dist/sdk/trace-processor.js.map +1 -0
  97. package/dist/tsconfig.tsbuildinfo +1 -1575
  98. package/package.json +27 -16
  99. package/src/cli.ts +68 -45
  100. package/src/common_types.ts +138 -14
  101. package/src/config.ts +2 -5
  102. package/src/core/expect.ts +187 -0
  103. package/src/core/gatherer.ts +48 -24
  104. package/src/core/index.ts +6 -6
  105. package/src/core/logger.ts +1 -12
  106. package/src/core/runner.ts +259 -112
  107. package/src/dsl/journey.ts +28 -6
  108. package/src/formatter/javascript.ts +330 -0
  109. package/src/helpers.ts +114 -24
  110. package/src/index.ts +7 -6
  111. package/src/parse_args.ts +97 -22
  112. package/src/plugins/browser-console.ts +30 -13
  113. package/src/plugins/network.ts +175 -159
  114. package/src/plugins/performance.ts +7 -7
  115. package/src/plugins/plugin-manager.ts +47 -36
  116. package/src/plugins/tracing.ts +43 -54
  117. package/src/reporters/base.ts +29 -23
  118. package/src/reporters/json.ts +259 -80
  119. package/src/reporters/junit.ts +1 -1
  120. package/src/sdk/lh-trace-processor.ts +1033 -0
  121. package/src/sdk/trace-metrics.ts +287 -0
  122. package/src/sdk/trace-processor.ts +147 -0
@@ -23,7 +23,7 @@
23
23
  *
24
24
  */
25
25
 
26
- import { CDPSession } from 'playwright-chromium';
26
+ import { Driver } from '../common_types';
27
27
 
28
28
  const supportedMetrics = new Set<string>([
29
29
  'Timestamp',
@@ -41,7 +41,7 @@ const supportedMetrics = new Set<string>([
41
41
  'JSHeapTotalSize',
42
42
  ]);
43
43
 
44
- export interface Metrics {
44
+ export interface PageMetrics {
45
45
  Timestamp?: number;
46
46
  Documents?: number;
47
47
  Frames?: number;
@@ -58,22 +58,22 @@ export interface Metrics {
58
58
  }
59
59
 
60
60
  export class PerformanceManager {
61
- constructor(private client: CDPSession) {}
61
+ constructor(private driver: Driver) {}
62
62
 
63
63
  async start() {
64
- await this.client.send('Performance.enable');
64
+ await this.driver.client.send('Performance.enable');
65
65
  }
66
66
 
67
67
  async stop() {
68
- await this.client.send('Performance.disable');
68
+ await this.driver.client.send('Performance.disable');
69
69
  }
70
70
 
71
71
  async getMetrics() {
72
- const { metrics } = await this.client.send('Performance.getMetrics');
72
+ const { metrics } = await this.driver.client.send('Performance.getMetrics');
73
73
  return this.buildMetricsObject(metrics);
74
74
  }
75
75
 
76
- private buildMetricsObject(metrics = []): Metrics {
76
+ private buildMetricsObject(metrics = []): PageMetrics {
77
77
  const result = {};
78
78
  for (const metric of metrics) {
79
79
  if (supportedMetrics.has(metric.name)) result[metric.name] = metric.value;
@@ -23,79 +23,90 @@
23
23
  *
24
24
  */
25
25
 
26
- import { NetworkInfo, FilmStrip } from '../common_types';
26
+ import { PluginOutput, Driver } from '../common_types';
27
27
  import {
28
28
  BrowserConsole,
29
- BrowserMessage,
30
29
  NetworkManager,
31
30
  PerformanceManager,
32
31
  Tracing,
33
- filterFilmstrips,
32
+ TraceOptions,
34
33
  } from './';
35
- import { Driver } from '../core/gatherer';
36
34
  import { Step } from '../dsl';
37
35
 
38
36
  type PluginType = 'network' | 'trace' | 'performance' | 'browserconsole';
39
-
40
- type PluginOutput = {
41
- filmstrips?: Array<FilmStrip>;
42
- networkinfo?: Array<NetworkInfo>;
43
- browserconsole?: Array<BrowserMessage>;
44
- };
45
-
46
37
  type Plugin = NetworkManager | Tracing | PerformanceManager | BrowserConsole;
38
+ type PluginOptions = TraceOptions;
47
39
 
48
40
  export class PluginManager {
49
- protected plugins = new Map<string, Plugin>();
50
-
41
+ protected plugins = new Map<PluginType, Plugin>();
42
+ public PLUGIN_TYPES: Array<PluginType> = [
43
+ 'network',
44
+ 'trace',
45
+ 'performance',
46
+ 'browserconsole',
47
+ ];
51
48
  constructor(private driver: Driver) {}
52
49
 
53
- async start(type: PluginType) {
50
+ register(type: PluginType, options: PluginOptions) {
54
51
  let instance: Plugin;
55
52
  switch (type) {
56
53
  case 'network':
57
- instance = new NetworkManager();
58
- await instance.start(this.driver.client);
54
+ instance = new NetworkManager(this.driver);
59
55
  break;
60
56
  case 'trace':
61
- instance = new Tracing();
62
- await instance.start(this.driver.client);
57
+ instance = new Tracing(this.driver, options);
63
58
  break;
64
59
  case 'performance':
65
- instance = new PerformanceManager(this.driver.client);
66
- instance.start();
60
+ instance = new PerformanceManager(this.driver);
67
61
  break;
68
62
  case 'browserconsole':
69
- instance = new BrowserConsole(this.driver.page);
70
- instance.start();
63
+ instance = new BrowserConsole(this.driver);
71
64
  break;
72
65
  }
66
+ instance && this.plugins.set(type, instance);
67
+ return instance;
68
+ }
69
+
70
+ registerAll(options: PluginOptions) {
71
+ for (const type of this.PLUGIN_TYPES) {
72
+ this.register(type, options);
73
+ }
74
+ }
75
+
76
+ unregisterAll() {
77
+ for (const type of this.PLUGIN_TYPES) {
78
+ this.plugins.delete(type);
79
+ }
80
+ }
73
81
 
74
- this.plugins.set(instance.constructor.name, instance);
82
+ async stop(type: PluginType) {
83
+ const instance = this.plugins.get(type);
84
+ if (instance) {
85
+ return await instance.stop();
86
+ }
87
+ return {};
88
+ }
89
+
90
+ async start(type: PluginType) {
91
+ const instance = this.plugins.get(type);
92
+ instance && (await instance.start());
75
93
  return instance;
76
94
  }
77
95
 
78
- get<T extends Plugin>(Type: new (...args: any[]) => T): T {
79
- return this.plugins.get(Type.name) as T;
96
+ get(type: PluginType) {
97
+ return this.plugins.get(type);
80
98
  }
81
99
 
82
100
  onStep(step: Step) {
83
- this.get(BrowserConsole) && (this.get(BrowserConsole)._currentStep = step);
84
- this.get(NetworkManager) && (this.get(NetworkManager)._currentStep = step);
101
+ (this.get('browserconsole') as BrowserConsole)._currentStep = step;
102
+ (this.get('network') as NetworkManager)._currentStep = step;
85
103
  }
86
104
 
87
- async output(): Promise<PluginOutput> {
88
- const data = {
89
- filmstrips: [],
90
- networkinfo: [],
91
- browserconsole: null,
92
- };
105
+ output() {
106
+ const data: PluginOutput = {};
93
107
  for (const [, plugin] of this.plugins) {
94
108
  if (plugin instanceof NetworkManager) {
95
109
  data.networkinfo = plugin.stop();
96
- } else if (plugin instanceof Tracing) {
97
- const result = await plugin.stop(this.driver.client);
98
- data.filmstrips = filterFilmstrips(result);
99
110
  } else if (plugin instanceof BrowserConsole) {
100
111
  data.browserconsole = plugin.stop();
101
112
  }
@@ -23,80 +23,60 @@
23
23
  *
24
24
  */
25
25
 
26
- import { CDPSession } from 'playwright-chromium';
27
- import { FilmStrip } from '../common_types';
28
- import { convertTraceTimestamp } from '../helpers';
26
+ import { Driver, PluginOutput } from '../common_types';
27
+ import { Filmstrips } from '../sdk/trace-metrics';
28
+ import { TraceEvent, TraceProcessor } from '../sdk/trace-processor';
29
29
 
30
- /**
31
- * Trace Event Format
32
- * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
33
- */
34
- type TraceEvent = {
35
- name: string;
36
- // event category
37
- cat: string;
38
- // process id
39
- pid: number;
40
- // thread id
41
- tid: number;
42
- // event phase
43
- ph?: string;
44
- args?: {
45
- snapshot: string;
46
- };
47
- /**
48
- * Platform specific monotonic non decreasing clock time
49
- * https://source.chromium.org/chromium/chromium/src/+/master:base/time/time.h;l=936;bpv=0;bpt=0
50
- */
51
- ts: number;
30
+ export type TraceOptions = {
31
+ filmstrips?: boolean;
32
+ trace?: boolean;
52
33
  };
53
34
 
54
- export function filterFilmstrips(
55
- traceEvents: Array<TraceEvent>
56
- ): Array<FilmStrip> {
57
- const events = traceEvents.filter(event => {
58
- const { args, cat, name } = event;
59
- return (
60
- name === 'Screenshot' &&
61
- cat === 'disabled-by-default-devtools.screenshot' &&
62
- args?.snapshot
63
- );
64
- });
65
-
66
- return events.map(event => ({
67
- snapshot: event.args.snapshot,
68
- ts: event.ts,
69
- startTime: convertTraceTimestamp(event.ts),
70
- }));
71
- }
72
-
73
35
  /**
74
- * Custom Tracer that only traces the filmstrips
36
+ * Custom Tracer that listenes for events from specified categories
75
37
  * https://chromedevtools.github.io/devtools-protocol/tot/Tracing/
76
38
  */
77
39
  export class Tracing {
78
- async start(client: CDPSession) {
40
+ constructor(private driver: Driver, private options: TraceOptions) {}
41
+
42
+ async start() {
79
43
  const includedCategories = [
80
44
  // exclude all default categories
81
45
  '-*',
82
- // capture screenshots
83
- 'disabled-by-default-devtools.screenshot',
84
46
  ];
85
- await client.send('Tracing.start', {
47
+ if (this.options.filmstrips) {
48
+ // capture screenshots - up to 450 max for each trace (https://goo.gl/rBfhn4)
49
+ includedCategories.push('disabled-by-default-devtools.screenshot');
50
+ }
51
+ if (this.options.trace) {
52
+ includedCategories.push(
53
+ // Used instead of 'toplevel' in Chrome 71+
54
+ 'disabled-by-default-lighthouse',
55
+ // Cumulative Layout Shift metric
56
+ 'loading',
57
+ // UserTiming marks/measures
58
+ 'blink.user_timing',
59
+ // Most of the events we need are from these two categories
60
+ // Includes FCP, LCP, Main thread frames, process, etc.
61
+ 'devtools.timeline',
62
+ 'disabled-by-default-devtools.timeline'
63
+ );
64
+ }
65
+ await this.driver.client.send('Tracing.start', {
86
66
  /**
87
67
  * Using `ReportEvents` makes gathering trace events
88
68
  * much faster as opposed to using `ReturnAsStream` mode
89
69
  */
90
70
  transferMode: 'ReportEvents',
91
- traceConfig: {
92
- includedCategories,
93
- },
71
+ categories: includedCategories.join(','),
72
+ options: 'sampling-frequency=10000', // 1000 is default
94
73
  });
95
74
  }
96
75
 
97
- async stop(client: CDPSession) {
76
+ async stop() {
98
77
  const events = [];
99
78
  const collectListener = payload => events.push(...payload.value);
79
+ const { client } = this.driver;
100
80
  client.on('Tracing.dataCollected', collectListener);
101
81
 
102
82
  const [traceEvents] = await Promise.all([
@@ -108,6 +88,15 @@ export class Tracing {
108
88
  ),
109
89
  client.send('Tracing.end'),
110
90
  ]);
111
- return traceEvents as Array<TraceEvent>;
91
+ const output: Partial<PluginOutput> = {};
92
+ if (this.options.filmstrips) {
93
+ output.filmstrips = Filmstrips.compute(traceEvents as Array<TraceEvent>);
94
+ }
95
+ if (this.options.trace) {
96
+ Object.assign(output, {
97
+ ...TraceProcessor.computeTrace(traceEvents as Array<TraceEvent>),
98
+ });
99
+ }
100
+ return output;
112
101
  }
113
102
  }
@@ -64,28 +64,19 @@ export default class BaseReporter {
64
64
 
65
65
  constructor(public runner: Runner, options: ReporterOptions = {}) {
66
66
  this.fd = options.fd || process.stdout.fd;
67
- this.stream = new SonicBoom({ fd: this.fd, sync: true });
68
- this._registerListeners();
69
- this.runner.on('end', () => this.close());
70
- }
67
+ /**
68
+ * minLength is set to 1 byte to make sure we flush the
69
+ * content even if its the last byte on the stream buffer
70
+ * before destroying the pipe with underlying file descriptor
71
+ */
72
+ this.stream = new SonicBoom({ fd: this.fd, sync: true, minLength: 1 });
71
73
 
72
- close() {
73
- if (this.fd <= 2) {
74
- /**
75
- * For stdout/stderr we destroy stream once data is written and run
76
- * it as the last listener giving enough room for
77
- * other reporters to write to stream
78
- */
79
- setImmediate(() => this.stream.end());
80
- } else {
81
- /**
82
- * If the user has passed a custom FD we don't close the FD, but we do flush it
83
- * to give them more control. This is important because FDs should only be closed
84
- * once, and the primary use case for custom FDs is being called by heartbeat, which
85
- * closes the FD after the process exits
86
- */
87
- this.stream.flush();
88
- }
74
+ // flushSync is used here to make sure all the data from the underlying
75
+ // SonicBoom stream buffer is completely written to the fd before closing
76
+ // the process
77
+ this.runner.on('end', () => this.stream.flushSync());
78
+
79
+ this._registerListeners();
89
80
  }
90
81
 
91
82
  _registerListeners() {
@@ -108,16 +99,31 @@ export default class BaseReporter {
108
99
  result[status]++;
109
100
  });
110
101
 
102
+ this.runner.on('journey:end', ({ error }) => {
103
+ const { failed, succeeded, skipped } = result;
104
+ const total = failed + succeeded + skipped;
105
+
106
+ /**
107
+ * Render the error on the terminal only when no steps could
108
+ * be executed which means the error happened in one of the hooks
109
+ */
110
+ if (total === 0 && error) {
111
+ this.write(renderError(error));
112
+ }
113
+ });
114
+
111
115
  this.runner.on('end', () => {
112
116
  const { failed, succeeded, skipped } = result;
113
117
  const total = failed + succeeded + skipped;
114
118
 
119
+ let message = '\n';
115
120
  if (total === 0) {
116
- this.write('No tests found!');
121
+ message = 'No tests found!';
122
+ message += ` (${renderDuration(now())} ms) \n`;
123
+ this.write(message);
117
124
  return;
118
125
  }
119
126
 
120
- let message = '\n';
121
127
  message += succeeded > 0 ? green(` ${succeeded} passed`) : '';
122
128
  message += failed > 0 ? red(` ${failed} failed`) : '';
123
129
  message += skipped > 0 ? cyan(` ${skipped} skipped`) : '';