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

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 (115) hide show
  1. package/CHANGELOG.md +228 -0
  2. package/NOTICE.txt +250 -0
  3. package/README.md +2 -1
  4. package/dist/cli.js +80 -49
  5. package/dist/cli.js.map +1 -1
  6. package/dist/common_types.d.ts +77 -10
  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.map +1 -1
  11. package/dist/core/expect.d.ts +177 -0
  12. package/dist/core/expect.d.ts.map +1 -0
  13. package/dist/core/expect.js +33 -0
  14. package/dist/core/expect.js.map +1 -0
  15. package/dist/core/gatherer.d.ts +4 -7
  16. package/dist/core/gatherer.d.ts.map +1 -1
  17. package/dist/core/gatherer.js +33 -9
  18. package/dist/core/gatherer.js.map +1 -1
  19. package/dist/core/index.d.ts +5 -5
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/core/index.js +1 -1
  22. package/dist/core/index.js.map +1 -1
  23. package/dist/core/logger.d.ts +0 -1
  24. package/dist/core/logger.d.ts.map +1 -1
  25. package/dist/core/logger.js +2 -15
  26. package/dist/core/logger.js.map +1 -1
  27. package/dist/core/runner.d.ts +54 -45
  28. package/dist/core/runner.d.ts.map +1 -1
  29. package/dist/core/runner.js +173 -63
  30. package/dist/core/runner.js.map +1 -1
  31. package/dist/dsl/journey.d.ts +13 -4
  32. package/dist/dsl/journey.d.ts.map +1 -1
  33. package/dist/dsl/journey.js +40 -1
  34. package/dist/dsl/journey.js.map +1 -1
  35. package/dist/formatter/javascript.d.ts +73 -0
  36. package/dist/formatter/javascript.d.ts.map +1 -0
  37. package/dist/formatter/javascript.js +274 -0
  38. package/dist/formatter/javascript.js.map +1 -0
  39. package/dist/helpers.d.ts +33 -12
  40. package/dist/helpers.d.ts.map +1 -1
  41. package/dist/helpers.js +100 -46
  42. package/dist/helpers.js.map +1 -1
  43. package/dist/index.d.ts +8 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +6 -7
  46. package/dist/index.js.map +1 -1
  47. package/dist/parse_args.d.ts +5 -2
  48. package/dist/parse_args.d.ts.map +1 -1
  49. package/dist/parse_args.js +59 -16
  50. package/dist/parse_args.js.map +1 -1
  51. package/dist/plugins/browser-console.d.ts +5 -8
  52. package/dist/plugins/browser-console.d.ts.map +1 -1
  53. package/dist/plugins/browser-console.js +26 -7
  54. package/dist/plugins/browser-console.js.map +1 -1
  55. package/dist/plugins/network.d.ts +20 -20
  56. package/dist/plugins/network.d.ts.map +1 -1
  57. package/dist/plugins/network.js +110 -108
  58. package/dist/plugins/network.js.map +1 -1
  59. package/dist/plugins/performance.d.ts +5 -5
  60. package/dist/plugins/performance.d.ts.map +1 -1
  61. package/dist/plugins/performance.js +5 -5
  62. package/dist/plugins/performance.js.map +1 -1
  63. package/dist/plugins/plugin-manager.d.ts +11 -11
  64. package/dist/plugins/plugin-manager.d.ts.map +1 -1
  65. package/dist/plugins/plugin-manager.js +40 -24
  66. package/dist/plugins/plugin-manager.js.map +1 -1
  67. package/dist/plugins/tracing.d.ts +10 -25
  68. package/dist/plugins/tracing.d.ts.map +1 -1
  69. package/dist/plugins/tracing.js +40 -26
  70. package/dist/plugins/tracing.js.map +1 -1
  71. package/dist/reporters/base.d.ts +0 -1
  72. package/dist/reporters/base.d.ts.map +1 -1
  73. package/dist/reporters/base.js +22 -23
  74. package/dist/reporters/base.js.map +1 -1
  75. package/dist/reporters/json.d.ts +43 -7
  76. package/dist/reporters/json.d.ts.map +1 -1
  77. package/dist/reporters/json.js +181 -35
  78. package/dist/reporters/json.js.map +1 -1
  79. package/dist/sdk/lh-trace-processor.d.ts +304 -0
  80. package/dist/sdk/lh-trace-processor.d.ts.map +1 -0
  81. package/dist/sdk/lh-trace-processor.js +832 -0
  82. package/dist/sdk/lh-trace-processor.js.map +1 -0
  83. package/dist/sdk/trace-metrics.d.ts +61 -0
  84. package/dist/sdk/trace-metrics.d.ts.map +1 -0
  85. package/dist/sdk/trace-metrics.js +252 -0
  86. package/dist/sdk/trace-metrics.js.map +1 -0
  87. package/dist/sdk/trace-processor.d.ts +106 -0
  88. package/dist/sdk/trace-processor.d.ts.map +1 -0
  89. package/dist/sdk/trace-processor.js +74 -0
  90. package/dist/sdk/trace-processor.js.map +1 -0
  91. package/dist/tsconfig.tsbuildinfo +1 -1575
  92. package/package.json +24 -13
  93. package/src/cli.ts +68 -45
  94. package/src/common_types.ts +92 -10
  95. package/src/config.ts +2 -5
  96. package/src/core/expect.ts +187 -0
  97. package/src/core/gatherer.ts +44 -23
  98. package/src/core/index.ts +6 -6
  99. package/src/core/logger.ts +1 -12
  100. package/src/core/runner.ts +259 -112
  101. package/src/dsl/journey.ts +28 -6
  102. package/src/formatter/javascript.ts +330 -0
  103. package/src/helpers.ts +114 -24
  104. package/src/index.ts +7 -6
  105. package/src/parse_args.ts +97 -22
  106. package/src/plugins/browser-console.ts +30 -13
  107. package/src/plugins/network.ts +137 -144
  108. package/src/plugins/performance.ts +7 -7
  109. package/src/plugins/plugin-manager.ts +47 -36
  110. package/src/plugins/tracing.ts +43 -54
  111. package/src/reporters/base.ts +24 -23
  112. package/src/reporters/json.ts +251 -41
  113. package/src/sdk/lh-trace-processor.ts +1033 -0
  114. package/src/sdk/trace-metrics.ts +287 -0
  115. package/src/sdk/trace-processor.ts +147 -0
package/src/parse_args.ts CHANGED
@@ -23,15 +23,14 @@
23
23
  *
24
24
  */
25
25
 
26
- import { program } from 'commander';
26
+ import { program, Option } from 'commander';
27
+ import { CliArgs } from './common_types';
27
28
  import { reporters } from './reporters';
28
-
29
- const availableReporters = Object.keys(reporters)
30
- .map(r => String(r))
31
- .join();
29
+ import { DEFAULT_NETWORK_CONDITIONS_ARG } from './helpers';
32
30
 
33
31
  /* eslint-disable-next-line @typescript-eslint/no-var-requires */
34
32
  const { name, version } = require('../package.json');
33
+
35
34
  program
36
35
  .name(`npx ${name}`)
37
36
  .usage('[options] [dir] [files] file')
@@ -39,12 +38,15 @@ program
39
38
  '-c, --config <path>',
40
39
  'configuration path (default: synthetics.config.js)'
41
40
  )
42
- .option('-s, --suite-params <jsonstring>', 'suite variables', '{}')
43
- .option('-e, --environment <envname>', 'e.g. production', 'development')
44
- .option('-j, --json', 'output newline delimited JSON')
45
41
  .option(
46
- '--reporter <reporter>',
47
- `output repoter format, can be one of ${availableReporters}`
42
+ '-p, --params <jsonstring>',
43
+ 'JSON object that gets injected to all journeys',
44
+ JSON.parse
45
+ )
46
+ .addOption(
47
+ new Option('--reporter <value>', `output repoter format`).choices(
48
+ Object.keys(reporters)
49
+ )
48
50
  )
49
51
  .option('-d, --debug', 'print debug logs info')
50
52
  .option(
@@ -54,30 +56,103 @@ program
54
56
  .option('--inline', 'Run inline journeys from heartbeat')
55
57
  .option('-r, --require <modules...>', 'module(s) to preload')
56
58
  .option('--no-headless', 'run browser in headful mode')
57
- .option('--sandbox', 'enable chromium sandboxing')
59
+ .option('--sandbox', 'enable chromium sandboxing', false)
60
+ .option('--rich-events', 'Mimics a heartbeat run')
58
61
  .option(
59
- '--ws-endpoint <endpoint>',
60
- 'Browser WebSocket endpoint to connect to'
62
+ '--capability <features...>',
63
+ 'Enable capabilities through feature flags'
61
64
  )
62
- .option(
63
- '--pause-on-error',
64
- 'pause on error until a keypress is made in the console. Useful during development'
65
+ .addOption(
66
+ new Option('--screenshots [flag]', 'take screenshots at end of each step')
67
+ .choices(['on', 'off', 'only-on-failure'])
68
+ .default('on')
65
69
  )
66
- .option('--screenshots', 'take screenshot for each step')
67
- .option('--filmstrips', 'record detailed filmstrip info for all journeys')
68
- .option('--network', 'capture network information for all journeys')
69
- .option('--metrics', 'capture performance metrics for each step')
70
70
  .option(
71
71
  '--dry-run',
72
72
  "don't actually execute anything, report only registered journeys"
73
73
  )
74
- .option('--journey-name <name>', 'only run the journey with the given name')
74
+ .option(
75
+ '--match <name>',
76
+ 'run only journeys with a name or tags that matches the glob'
77
+ )
78
+ .option(
79
+ '--tags <name...>',
80
+ 'run only journeys with the given tag(s), or globs'
81
+ )
75
82
  .option(
76
83
  '--outfd <fd>',
77
84
  'specify a file descriptor for logs. Default is stdout',
78
85
  parseInt
79
86
  )
87
+ .option(
88
+ '--ws-endpoint <endpoint>',
89
+ 'Browser WebSocket endpoint to connect to'
90
+ )
91
+ .option(
92
+ '--pause-on-error',
93
+ 'pause on error until a keypress is made in the console. Useful during development'
94
+ )
95
+ .option(
96
+ '--ignore-https-errors',
97
+ 'ignores any HTTPS errors in sites being tested, including ones related to unrecognized certs or signatures. This can be insecure!'
98
+ )
99
+ .option(
100
+ '--quiet-exit-code',
101
+ 'always return 0 as an exit code status, regardless of test pass / fail. Only return > 0 exit codes on internal errors where the suite could not be run'
102
+ )
103
+ .addOption(
104
+ new Option(
105
+ '--throttling <d/u/l>',
106
+ 'List of options to throttle network conditions for download throughput (d) in megabits/second, upload throughput (u) in megabits/second and latency (l) in milliseconds.'
107
+ ).default(DEFAULT_NETWORK_CONDITIONS_ARG)
108
+ )
109
+ .option('--no-throttling', 'Turns off default network throttling.')
110
+ .option('--playwright-options <jsonstring>', 'JSON object to pass in custom Playwright options for the agent. Options passed will be merged with Playwright options defined in your synthetics.config.js file. Options defined via --playwright-options take precedence.', JSON.parse)
80
111
  .version(version)
81
112
  .description('Run synthetic tests');
82
113
 
83
- export default program.parse(process.argv);
114
+ const command = program.parse(process.argv);
115
+ const options = command.opts() as CliArgs;
116
+
117
+ /**
118
+ * Group all events that can be consumed by heartbeat and
119
+ * eventually by the Synthetics UI.
120
+ */
121
+ if (options.richEvents) {
122
+ options.reporter = options.reporter ?? 'json';
123
+ options.ssblocks = true;
124
+ options.network = true;
125
+ options.quietExitCode = true;
126
+ options.trace = true;
127
+ }
128
+
129
+ if (options.capability) {
130
+ const supportedCapabilities = [
131
+ 'trace',
132
+ 'network',
133
+ 'filmstrips',
134
+ 'metrics',
135
+ 'ssblocks',
136
+ ];
137
+ /**
138
+ * trace - record chrome trace events(LCP, FCP, CLS, etc.) for all journeys
139
+ * network - capture network information for all journeys
140
+ * filmstrips - record detailed filmstrips for all journeys
141
+ * metrics - capture performance metrics (DOM Nodes, Heap size, etc.) for each step
142
+ * ssblocks - Dedupes the screenshots in to blocks to save storage space
143
+ */
144
+ for (const flag of options.capability) {
145
+ if (supportedCapabilities.includes(flag)) {
146
+ options[flag] = true;
147
+ } else {
148
+ console.warn(
149
+ `Missing capability "${flag}", current supported capabilities are ${supportedCapabilities.join(
150
+ ', '
151
+ )}`
152
+ );
153
+ }
154
+ }
155
+ }
156
+
157
+ export { options };
158
+ export default command;
@@ -23,22 +23,18 @@
23
23
  *
24
24
  */
25
25
 
26
- import { Page } from 'playwright-chromium';
27
- import { DefaultPluginOutput } from '../common_types';
26
+ import { BrowserMessage, Driver } from '../common_types';
28
27
  import { Step } from '../dsl';
29
28
  import { getTimestamp } from '../helpers';
30
29
 
31
- export type BrowserMessage = {
32
- text: string;
33
- type: string;
34
- } & DefaultPluginOutput;
35
-
36
30
  const defaultMessageLimit = 1000;
37
31
 
38
32
  export class BrowserConsole {
39
33
  private messages: BrowserMessage[] = [];
40
34
  _currentStep: Partial<Step> = null;
41
35
 
36
+ constructor(private driver: Driver) {}
37
+
42
38
  private consoleEventListener = msg => {
43
39
  if (!this._currentStep) {
44
40
  return;
@@ -52,20 +48,41 @@ export class BrowserConsole {
52
48
  type,
53
49
  step: { name, index },
54
50
  });
55
- if (this.messages.length > defaultMessageLimit) {
56
- this.messages.splice(0, 1);
57
- }
51
+
52
+ this.enforceMessagesLimit();
58
53
  }
59
54
  };
60
55
 
61
- constructor(private page: Page) {}
56
+ private pageErrorEventListener = (error: Error) => {
57
+ if (!this._currentStep) {
58
+ return;
59
+ }
60
+ const { name, index } = this._currentStep;
61
+ this.messages.push({
62
+ timestamp: getTimestamp(),
63
+ text: error.message,
64
+ type: 'error',
65
+ step: { name, index },
66
+ error,
67
+ });
68
+
69
+ this.enforceMessagesLimit();
70
+ };
71
+
72
+ private enforceMessagesLimit() {
73
+ if (this.messages.length > defaultMessageLimit) {
74
+ this.messages.splice(0, 1);
75
+ }
76
+ }
62
77
 
63
78
  start() {
64
- this.page.on('console', this.consoleEventListener);
79
+ this.driver.page.on('console', this.consoleEventListener);
80
+ this.driver.page.on('pageerror', this.pageErrorEventListener);
65
81
  }
66
82
 
67
83
  stop() {
68
- this.page.off('console', this.consoleEventListener);
84
+ this.driver.page.off('console', this.consoleEventListener);
85
+ this.driver.page.off('pageerror', this.pageErrorEventListener);
69
86
  return this.messages;
70
87
  }
71
88
  }
@@ -23,9 +23,8 @@
23
23
  *
24
24
  */
25
25
 
26
- import { CDPSession } from 'playwright-chromium';
27
26
  import { Protocol } from 'playwright-chromium/types/protocol';
28
- import { NetworkInfo, BrowserInfo } from '../common_types';
27
+ import { NetworkInfo, BrowserInfo, Driver } from '../common_types';
29
28
  import { Step } from '../dsl';
30
29
  import { getTimestamp } from '../helpers';
31
30
 
@@ -35,7 +34,10 @@ export class NetworkManager {
35
34
  results: Array<NetworkInfo> = [];
36
35
  _currentStep: Partial<Step> = null;
37
36
 
38
- async start(client: CDPSession) {
37
+ constructor(private driver: Driver) {}
38
+
39
+ async start() {
40
+ const { client } = this.driver;
39
41
  const { product } = await client.send('Browser.getVersion');
40
42
  const [name, version] = product.split('/');
41
43
  this._browser = { name, version };
@@ -194,7 +196,7 @@ export class NetworkManager {
194
196
  record.transferSize = encodedDataLength;
195
197
  }
196
198
  record.loadEndTime = endTime;
197
- record.timings = this.calculateTimings(record);
199
+ record.timings = calculateTimings(record);
198
200
  this.results.push(record);
199
201
  /**
200
202
  * When there is DNS failure, browsers keeps retrying requests which
@@ -207,159 +209,150 @@ export class NetworkManager {
207
209
  * Account for missing response received event and also adjust the
208
210
  * response received event based on when first byte event was recorded
209
211
  */
210
- getResponseReceivedTime(
211
- timing: Protocol.Network.Response['timing'],
212
- responseReceivedTime: number
213
- ) {
214
- if (timing == null) {
215
- return responseReceivedTime;
216
- }
217
- const startTime = timing.requestTime;
218
- const headersReceivedTime = startTime + timing.receiveHeadersEnd / 1000;
219
- if (
220
- responseReceivedTime < 0 ||
221
- responseReceivedTime > headersReceivedTime
222
- ) {
223
- responseReceivedTime = headersReceivedTime;
224
- }
225
- if (startTime > responseReceivedTime) {
226
- responseReceivedTime = startTime;
227
- }
212
+
213
+ stop() {
214
+ return this.results;
215
+ }
216
+ }
217
+
218
+ function getResponseReceivedTime(
219
+ timing: Protocol.Network.Response['timing'],
220
+ responseReceivedTime: number
221
+ ) {
222
+ if (timing == null) {
228
223
  return responseReceivedTime;
229
224
  }
225
+ const startTime = timing.requestTime;
226
+ const headersReceivedTime = startTime + timing.receiveHeadersEnd / 1000;
227
+ if (responseReceivedTime < 0 || responseReceivedTime > headersReceivedTime) {
228
+ responseReceivedTime = headersReceivedTime;
229
+ }
230
+ if (startTime > responseReceivedTime) {
231
+ responseReceivedTime = startTime;
232
+ }
233
+ return responseReceivedTime;
234
+ }
235
+
236
+ /**
237
+ * The timing calculations are based on the chrome devtools frontend
238
+ * https://github.com/ChromeDevTools/devtools-frontend/blob/7f5478d8ceb7586f23f4073ab5c2085dac1ec26a/front_end/network/RequestTimingView.js#L98-L193
239
+ */
240
+ export function calculateTimings(record: NetworkInfo) {
241
+ const result: NetworkInfo['timings'] = {
242
+ blocked: -1,
243
+ queueing: -1,
244
+ proxy: -1,
245
+ dns: -1,
246
+ ssl: -1,
247
+ connect: -1,
248
+ send: -1,
249
+ wait: -1,
250
+ receive: -1,
251
+ total: -1,
252
+ };
253
+ const { requestSentTime, loadEndTime, responseReceivedTime } = record;
230
254
 
231
255
  /**
232
- * The timing calculations are based on the chrome devtools frontend
233
- * https://github.com/ChromeDevTools/devtools-frontend/blob/7f5478d8ceb7586f23f4073ab5c2085dac1ec26a/front_end/network/RequestTimingView.js#L98-L193
256
+ * Handle when request failed and no responseReceived event was
257
+ * fired for that particular request
258
+ * Eg: connection refused by remote host
234
259
  */
235
- calculateTimings(record: NetworkInfo) {
236
- const result: NetworkInfo['timings'] = {
237
- blocked: -1,
238
- queueing: -1,
239
- proxy: -1,
240
- dns: -1,
241
- ssl: -1,
242
- connect: -1,
243
- send: -1,
244
- wait: -1,
245
- receive: -1,
246
- total: -1,
247
- };
248
- const { requestSentTime, loadEndTime, responseReceivedTime } = record;
249
-
250
- /**
251
- * Handle when request failed and no responseReceived event was
252
- * fired for that particular request
253
- * Eg: connection refused by remote host
254
- */
255
- const response = record.response || {
256
- timing: null,
257
- fromServiceWorker: false,
258
- };
260
+ const response = record.response || {
261
+ timing: null,
262
+ fromServiceWorker: false,
263
+ };
259
264
 
260
- const toMilliseconds = (time: number) => (time === -1 ? -1 : time * 1000);
261
- const calculateDiffInMs = (
262
- name: keyof NetworkInfo['timings'],
263
- start: number,
264
- end: number
265
- ) => {
266
- if (start < Number.MAX_VALUE && start <= end) {
267
- result[name] = toMilliseconds(end - start);
268
- }
269
- };
270
- const firstPositive = (numbers: number[]) => {
271
- for (let i = 0; i < numbers.length; ++i) {
272
- if (numbers[i] > 0) {
273
- return numbers[i];
274
- }
275
- }
276
- return null;
277
- };
278
- /**
279
- * requestTime is baseline in seconds, rest of the timing data are ticks in milliseconds
280
- * from the requestTime, so we calculate the offset from that
281
- */
282
- const addOffsetRange = (
283
- name: keyof NetworkInfo['timings'],
284
- start: number,
285
- end: number
286
- ) => {
287
- if (start >= 0 && end >= 0) {
288
- calculateDiffInMs(
289
- name,
290
- startTime + start / 1000,
291
- startTime + end / 1000
292
- );
293
- }
294
- };
295
- const timing = response.timing;
296
- const actResRcvdTime = this.getResponseReceivedTime(
297
- timing,
298
- responseReceivedTime
299
- );
300
- const issueTime = requestSentTime;
301
- const startTime = timing == null ? -1 : timing.requestTime;
302
- const endTime = firstPositive([loadEndTime, actResRcvdTime]) || startTime;
303
-
304
- if (timing == null) {
305
- const start =
306
- issueTime !== -1 ? issueTime : startTime !== -1 ? startTime : 0;
307
- const middle = actResRcvdTime === -1 ? Number.MAX_VALUE : actResRcvdTime;
308
- const end = endTime === -1 ? Number.MAX_VALUE : endTime;
309
- calculateDiffInMs('total', start, end);
310
- calculateDiffInMs('blocked', start, middle);
311
- calculateDiffInMs('receive', middle, end);
312
- // Check if the request is blocked/stalled for the whole timeframe and
313
- // calculate timings appropriately for that request
314
- if (!isFinite(result['blocked'])) {
315
- result['blocked'] = result['total'];
265
+ const toMilliseconds = (time: number) => (time === -1 ? -1 : time * 1000);
266
+ const calculateDiffInMs = (
267
+ name: keyof NetworkInfo['timings'],
268
+ start: number,
269
+ end: number
270
+ ) => {
271
+ if (start < Number.MAX_VALUE && start <= end) {
272
+ result[name] = toMilliseconds(end - start);
273
+ }
274
+ };
275
+ const firstPositive = (numbers: number[]) => {
276
+ for (let i = 0; i < numbers.length; ++i) {
277
+ if (numbers[i] > 0) {
278
+ return numbers[i];
316
279
  }
317
- return result;
318
280
  }
319
- calculateDiffInMs(
320
- 'total',
321
- issueTime < startTime ? issueTime : startTime,
322
- endTime
323
- );
324
- if (issueTime < startTime) {
325
- calculateDiffInMs('queueing', issueTime, startTime);
281
+ return null;
282
+ };
283
+ /**
284
+ * requestTime is baseline in seconds, rest of the timing data are ticks in milliseconds
285
+ * from the requestTime, so we calculate the offset from that
286
+ */
287
+ const addOffsetRange = (
288
+ name: keyof NetworkInfo['timings'],
289
+ start: number,
290
+ end: number
291
+ ) => {
292
+ if (start >= 0 && end >= 0) {
293
+ calculateDiffInMs(name, startTime + start / 1000, startTime + end / 1000);
326
294
  }
327
- const responseReceived = toMilliseconds(actResRcvdTime - startTime);
295
+ };
296
+ const timing = response.timing;
297
+ const actResRcvdTime = getResponseReceivedTime(timing, responseReceivedTime);
298
+ const issueTime = requestSentTime;
299
+ const startTime = timing == null ? -1 : timing.requestTime;
300
+ const endTime = firstPositive([loadEndTime, actResRcvdTime]) || startTime;
328
301
 
329
- if (response.fromServiceWorker) {
330
- addOffsetRange('blocked', 0, timing.workerStart);
331
- addOffsetRange('wait', timing.sendEnd, responseReceived);
332
- } else if (!timing.pushStart) {
333
- const blockingEnd =
334
- firstPositive([
335
- timing.dnsStart,
336
- timing.connectStart,
337
- timing.sendStart,
338
- responseReceived,
339
- ]) || 0;
340
- addOffsetRange('blocked', 0, blockingEnd);
341
- addOffsetRange('proxy', timing.proxyStart, timing.proxyEnd);
342
- addOffsetRange('dns', timing.dnsStart, timing.dnsEnd);
343
- addOffsetRange('connect', timing.connectStart, timing.connectEnd);
344
- addOffsetRange('ssl', timing.sslStart, timing.sslEnd);
345
- addOffsetRange('send', timing.sendStart, timing.sendEnd);
346
- addOffsetRange(
347
- 'wait',
348
- Math.max(
349
- timing.sendEnd,
350
- timing.connectEnd,
351
- timing.dnsEnd,
352
- timing.proxyEnd,
353
- blockingEnd
354
- ),
355
- responseReceived
356
- );
302
+ if (timing == null) {
303
+ const start =
304
+ issueTime !== -1 ? issueTime : startTime !== -1 ? startTime : 0;
305
+ const middle = actResRcvdTime === -1 ? Number.MAX_VALUE : actResRcvdTime;
306
+ const end = endTime === -1 ? Number.MAX_VALUE : endTime;
307
+ calculateDiffInMs('total', start, end);
308
+ calculateDiffInMs('blocked', start, middle);
309
+ calculateDiffInMs('receive', middle, end);
310
+ // Check if the request is blocked/stalled for the whole timeframe and
311
+ // calculate timings appropriately for that request
312
+ if (!isFinite(result['blocked'])) {
313
+ result['blocked'] = result['total'];
357
314
  }
358
- calculateDiffInMs('receive', actResRcvdTime, endTime);
359
315
  return result;
360
316
  }
317
+ calculateDiffInMs(
318
+ 'total',
319
+ issueTime < startTime ? issueTime : startTime,
320
+ endTime
321
+ );
322
+ if (issueTime < startTime) {
323
+ calculateDiffInMs('queueing', issueTime, startTime);
324
+ }
325
+ const responseReceived = toMilliseconds(actResRcvdTime - startTime);
361
326
 
362
- stop() {
363
- return this.results;
327
+ if (response.fromServiceWorker) {
328
+ addOffsetRange('blocked', 0, timing.workerStart);
329
+ addOffsetRange('wait', timing.sendEnd, responseReceived);
330
+ } else if (!timing.pushStart) {
331
+ const blockingEnd =
332
+ firstPositive([
333
+ timing.dnsStart,
334
+ timing.connectStart,
335
+ timing.sendStart,
336
+ responseReceived,
337
+ ]) || 0;
338
+ addOffsetRange('blocked', 0, blockingEnd);
339
+ addOffsetRange('proxy', timing.proxyStart, timing.proxyEnd);
340
+ addOffsetRange('dns', timing.dnsStart, timing.dnsEnd);
341
+ addOffsetRange('connect', timing.connectStart, timing.connectEnd);
342
+ addOffsetRange('ssl', timing.sslStart, timing.sslEnd);
343
+ addOffsetRange('send', timing.sendStart, timing.sendEnd);
344
+ addOffsetRange(
345
+ 'wait',
346
+ Math.max(
347
+ timing.sendEnd,
348
+ timing.connectEnd,
349
+ timing.dnsEnd,
350
+ timing.proxyEnd,
351
+ blockingEnd
352
+ ),
353
+ responseReceived
354
+ );
364
355
  }
356
+ calculateDiffInMs('receive', actResRcvdTime, endTime);
357
+ return result;
365
358
  }
@@ -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;