@elastic/synthetics 1.0.0-beta.15 → 1.0.0-beta.19

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 (52) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/NOTICE.txt +29 -0
  3. package/README.md +1 -1
  4. package/dist/cli.js +34 -21
  5. package/dist/cli.js.map +1 -1
  6. package/dist/common_types.d.ts +10 -40
  7. package/dist/common_types.d.ts.map +1 -1
  8. package/dist/core/gatherer.d.ts.map +1 -1
  9. package/dist/core/gatherer.js +9 -1
  10. package/dist/core/gatherer.js.map +1 -1
  11. package/dist/core/logger.d.ts +0 -2
  12. package/dist/core/logger.d.ts.map +1 -1
  13. package/dist/core/logger.js +2 -15
  14. package/dist/core/logger.js.map +1 -1
  15. package/dist/core/runner.d.ts +19 -20
  16. package/dist/core/runner.d.ts.map +1 -1
  17. package/dist/core/runner.js +14 -5
  18. package/dist/core/runner.js.map +1 -1
  19. package/dist/formatter/javascript.d.ts +2 -0
  20. package/dist/formatter/javascript.d.ts.map +1 -1
  21. package/dist/formatter/javascript.js.map +1 -1
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +0 -6
  25. package/dist/index.js.map +1 -1
  26. package/dist/parse_args.d.ts.map +1 -1
  27. package/dist/parse_args.js +1 -1
  28. package/dist/parse_args.js.map +1 -1
  29. package/dist/plugins/network.d.ts +30 -11
  30. package/dist/plugins/network.d.ts.map +1 -1
  31. package/dist/plugins/network.js +234 -175
  32. package/dist/plugins/network.js.map +1 -1
  33. package/dist/reporters/base.d.ts.map +1 -1
  34. package/dist/reporters/base.js +6 -1
  35. package/dist/reporters/base.js.map +1 -1
  36. package/dist/reporters/json.d.ts +55 -5
  37. package/dist/reporters/json.d.ts.map +1 -1
  38. package/dist/reporters/json.js +45 -14
  39. package/dist/reporters/json.js.map +1 -1
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +7 -7
  42. package/src/cli.ts +37 -25
  43. package/src/common_types.ts +10 -46
  44. package/src/core/gatherer.ts +8 -1
  45. package/src/core/logger.ts +1 -12
  46. package/src/core/runner.ts +42 -28
  47. package/src/formatter/javascript.ts +2 -0
  48. package/src/index.ts +1 -6
  49. package/src/parse_args.ts +1 -5
  50. package/src/plugins/network.ts +280 -213
  51. package/src/reporters/base.ts +6 -1
  52. package/src/reporters/json.ts +52 -16
package/src/cli.ts CHANGED
@@ -126,6 +126,25 @@ async function prepareSuites(inputs: string[]) {
126
126
  }
127
127
 
128
128
  (async () => {
129
+ /**
130
+ * Transform `.ts` files out of the box by invoking
131
+ * the `ts-node` via `transpile-only` mode that compiles
132
+ * TS files without doing any extensive type checks.
133
+ *
134
+ * We must register `ts-node` _before_ loading inline
135
+ * scripts too because otherwise we will not be able to
136
+ * require `.ts` configuration files.
137
+ */
138
+ /* eslint-disable-next-line @typescript-eslint/no-var-requires */
139
+ require('ts-node').register({
140
+ transpileOnly: true,
141
+ compilerOptions: {
142
+ esModuleInterop: true,
143
+ allowJs: true,
144
+ target: 'es2018',
145
+ },
146
+ });
147
+
129
148
  if (options.inline) {
130
149
  const source = await readStdin();
131
150
  loadInlineScript(source);
@@ -141,20 +160,6 @@ async function prepareSuites(inputs: string[]) {
141
160
  throw new Error(`cannot find module '${name}'`);
142
161
  }
143
162
  }
144
- /**
145
- * Transform `.ts` files out of the box by invoking
146
- * the `ts-node` via `transpile-only` mode that compiles
147
- * TS files without doing any extensive type checks
148
- */
149
- /* eslint-disable-next-line @typescript-eslint/no-var-requires */
150
- require('ts-node').register({
151
- transpileOnly: true,
152
- compilerOptions: {
153
- esModuleInterop: true,
154
- allowJs: true,
155
- target: 'es2018',
156
- },
157
- });
158
163
  /**
159
164
  * Handle piped files by reading the STDIN
160
165
  * ex: ls example/suites/*.js | npx @elastic/synthetics
@@ -174,17 +179,24 @@ async function prepareSuites(inputs: string[]) {
174
179
  /**
175
180
  * Validate and handle configs
176
181
  */
177
- const config = readConfig(environment, options.config);
178
- const params = merge(
179
- config.params,
180
- options.suiteParams || {},
181
- options.params || {}
182
- );
183
- const playwrightOptions = merge(config.playwrightOptions, {
184
- headless: options.headless,
185
- chromiumSandbox: options.sandbox,
186
- ignoreHTTPSErrors: options.ignoreHttpsErrors,
187
- });
182
+ const config =
183
+ options.config || !options.inline
184
+ ? readConfig(environment, options.config)
185
+ : {};
186
+ const params = merge(config.params, options.params || {});
187
+
188
+ /**
189
+ * Favor playwright options passed via cli to inline playwright options
190
+ */
191
+ const playwrightOptions = merge.all([
192
+ config.playwrightOptions || {},
193
+ options.playwrightOptions || {},
194
+ {
195
+ headless: options.headless,
196
+ chromiumSandbox: options.sandbox,
197
+ ignoreHTTPSErrors: options.ignoreHttpsErrors,
198
+ },
199
+ ]);
188
200
 
189
201
  const results = await run({
190
202
  params: Object.freeze(params),
@@ -31,6 +31,7 @@ import {
31
31
  ChromiumBrowserContext,
32
32
  Page,
33
33
  } from 'playwright-chromium';
34
+ import { Protocol } from 'playwright-chromium/types/protocol';
34
35
  import { Step } from './dsl';
35
36
  import { reporters } from './reporters';
36
37
 
@@ -93,45 +94,10 @@ export type BrowserInfo = {
93
94
  version: string;
94
95
  };
95
96
 
96
- export type SecurityDetails = {
97
- issuer?: string;
98
- protocol?: string;
99
- subjectName?: string;
100
- validFrom?: number;
101
- validTo?: number;
102
- };
103
-
104
- export type Request = {
105
- method: string;
106
- url: string;
107
- headers: Record<string, string>;
108
- // Total size in bytes of the request (body and headers)
109
- bytes?: number;
110
- body?: {
111
- // Size in bytes of the request body
112
- bytes: number;
113
- };
114
- referrer?: string;
115
- };
116
-
117
- export type Response = {
118
- url?: string;
119
- statusCode: number;
120
- statusText?: string;
121
- mimeType?: string;
122
- httpVersion?: string;
123
- headers: Record<string, string>;
124
- // Total size in bytes of the response (body and headers)
125
- bytes?: number;
126
- body?: {
127
- // Size in bytes of the response body
128
- bytes: number;
129
- };
130
- transferSize?: number;
131
- redirectURL?: string;
132
- securityDetails?: SecurityDetails;
133
- remoteIPAddress?: string;
134
- remotePort?: number;
97
+ export type Screenshot = {
98
+ timestamp: number;
99
+ step: Step;
100
+ data: string;
135
101
  };
136
102
 
137
103
  export type NetworkInfo = {
@@ -139,8 +105,8 @@ export type NetworkInfo = {
139
105
  browser: BrowserInfo;
140
106
  method: string;
141
107
  type: string;
142
- request: Request;
143
- response?: Response;
108
+ request: Protocol.Network.Request;
109
+ response?: Protocol.Network.Response;
144
110
  isNavigationRequest: boolean;
145
111
  requestSentTime: number;
146
112
  loadEndTime: number;
@@ -150,9 +116,10 @@ export type NetworkInfo = {
150
116
  transferSize: number;
151
117
  timings?: {
152
118
  blocked: number;
153
- queueing?: number;
119
+ queueing: number;
154
120
  dns: number;
155
121
  ssl: number;
122
+ proxy: number;
156
123
  connect: number;
157
124
  send: number;
158
125
  wait: number;
@@ -203,10 +170,7 @@ export type CliArgs = {
203
170
  ignoreHttpsErrors?: boolean;
204
171
  params?: Params;
205
172
  throttling?: boolean | string;
206
- /**
207
- * @deprecated use params instead
208
- */
209
- suiteParams?: Params;
173
+ playwrightOptions?: LaunchOptions & BrowserContextOptions;
210
174
  richEvents?: boolean;
211
175
  };
212
176
 
@@ -37,6 +37,7 @@ export class Gatherer {
37
37
  static browser: ChromiumBrowser;
38
38
 
39
39
  static async setupDriver(options: RunOptions): Promise<Driver> {
40
+ log('Gatherer: setup driver');
40
41
  const { wsEndpoint, playwrightOptions, networkConditions } = options;
41
42
 
42
43
  if (Gatherer.browser == null) {
@@ -44,7 +45,13 @@ export class Gatherer {
44
45
  log(`Gatherer: connecting to WS endpoint: ${wsEndpoint}`);
45
46
  Gatherer.browser = await chromium.connect({ wsEndpoint });
46
47
  } else {
47
- Gatherer.browser = await chromium.launch(playwrightOptions);
48
+ Gatherer.browser = await chromium.launch({
49
+ ...playwrightOptions,
50
+ args: [
51
+ ...(playwrightOptions?.headless ? ['--disable-gpu'] : []),
52
+ ...(playwrightOptions?.args ?? []),
53
+ ],
54
+ });
48
55
  }
49
56
  }
50
57
  const context = await Gatherer.browser.newContext({
@@ -23,20 +23,9 @@
23
23
  *
24
24
  */
25
25
 
26
- import SonicBoom from 'sonic-boom';
27
26
  import { grey, cyan, dim, italic } from 'kleur/colors';
28
27
  import { now } from '../helpers';
29
28
 
30
- const defaultFd = process.stdout.fd;
31
- let logger = new SonicBoom({ fd: defaultFd, sync: true });
32
-
33
- export const setLogger = (fd: number) => {
34
- if (fd && fd !== defaultFd) {
35
- logger = new SonicBoom({ fd });
36
- }
37
- return logger;
38
- };
39
-
40
29
  export function log(msg) {
41
30
  if (!process.env.DEBUG || !msg) {
42
31
  return;
@@ -45,5 +34,5 @@ export function log(msg) {
45
34
  msg = JSON.stringify(msg);
46
35
  }
47
36
  const time = dim(cyan(`at ${parseInt(String(now()))} ms `));
48
- logger.write(time + italic(grey(msg)) + '\n');
37
+ console.log(time + italic(grey(msg)) + '\n');
49
38
  }
@@ -48,6 +48,7 @@ import {
48
48
  HooksArgs,
49
49
  PlaywrightOptions,
50
50
  Driver,
51
+ Screenshot,
51
52
  } from '../common_types';
52
53
  import { PageMetrics, PluginManager } from '../plugins';
53
54
  import { PerformanceManager } from '../plugins';
@@ -73,13 +74,12 @@ export type RunOptions = Omit<
73
74
  reporter?: CliArgs['reporter'] | Reporter;
74
75
  };
75
76
 
76
- type BaseContext = {
77
+ type HookType = 'beforeAll' | 'afterAll';
78
+ export type SuiteHooks = Record<HookType, Array<HooksCallback>>;
79
+
80
+ type JourneyContext = {
77
81
  params?: Params;
78
82
  start: number;
79
- end?: number;
80
- };
81
-
82
- type JourneyContext = BaseContext & {
83
83
  driver: Driver;
84
84
  pluginManager: PluginManager;
85
85
  };
@@ -103,8 +103,16 @@ type JourneyResult = {
103
103
 
104
104
  type RunResult = Record<string, JourneyResult>;
105
105
 
106
- type HookType = 'beforeAll' | 'afterAll';
107
- export type SuiteHooks = Record<HookType, Array<HooksCallback>>;
106
+ interface StepEvent {
107
+ step: Step;
108
+ journey: Journey;
109
+ }
110
+
111
+ interface JourneyEvent {
112
+ journey: Journey;
113
+ timestamp: number;
114
+ params?: Params;
115
+ }
108
116
 
109
117
  interface Events {
110
118
  start: {
@@ -114,24 +122,21 @@ interface Events {
114
122
  'journey:register': {
115
123
  journey: Journey;
116
124
  };
117
- 'journey:start': {
118
- journey: Journey;
119
- timestamp: number;
120
- params: Params;
121
- };
122
- 'journey:end': BaseContext &
125
+ 'journey:start': JourneyEvent;
126
+ 'journey:end': JourneyEvent &
123
127
  JourneyResult & {
124
- journey: Journey;
128
+ start: number;
129
+ end: number;
125
130
  options: RunOptions;
131
+ timestamp: number;
126
132
  };
127
133
  'journey:end:reported': unknown;
128
- 'step:start': { journey: Journey; step: Step };
129
- 'step:end': StepResult & {
130
- start: number;
131
- end: number;
132
- journey: Journey;
133
- step: Step;
134
- };
134
+ 'step:start': StepEvent;
135
+ 'step:end': StepEvent &
136
+ StepResult & {
137
+ start: number;
138
+ end: number;
139
+ };
135
140
  end: unknown;
136
141
  }
137
142
 
@@ -149,8 +154,12 @@ export default class Runner extends EventEmitter {
149
154
  static screenshotPath = join(CACHE_PATH, 'screenshots');
150
155
 
151
156
  static async createContext(options: RunOptions): Promise<JourneyContext> {
152
- const start = monotonicTimeInSeconds();
153
157
  const driver = await Gatherer.setupDriver(options);
158
+ /**
159
+ * Do not include browser launch/context creation duration
160
+ * as part of journey duration
161
+ */
162
+ const start = monotonicTimeInSeconds();
154
163
  const pluginManager = await Gatherer.beginRecording(driver, options);
155
164
  /**
156
165
  * For each journey we create the screenshots folder for
@@ -180,12 +189,14 @@ export default class Runner extends EventEmitter {
180
189
  */
181
190
  if (buffer) {
182
191
  const fileName = `${generateUniqueId()}.json`;
192
+ const screenshot: Screenshot = {
193
+ step,
194
+ timestamp: getTimestamp(),
195
+ data: buffer.toString('base64'),
196
+ };
183
197
  await writeFileAsync(
184
198
  join(Runner.screenshotPath, fileName),
185
- JSON.stringify({
186
- step,
187
- data: buffer.toString('base64'),
188
- })
199
+ JSON.stringify(screenshot)
189
200
  );
190
201
  }
191
202
  }
@@ -339,6 +350,7 @@ export default class Runner extends EventEmitter {
339
350
  result: JourneyContext & JourneyResult,
340
351
  options: RunOptions
341
352
  ) {
353
+ const end = monotonicTimeInSeconds();
342
354
  const { pluginManager, start, status, error } = result;
343
355
  const pluginOutput = pluginManager.output();
344
356
  this.emit('journey:end', {
@@ -346,7 +358,8 @@ export default class Runner extends EventEmitter {
346
358
  status,
347
359
  error,
348
360
  start,
349
- end: monotonicTimeInSeconds(),
361
+ end,
362
+ timestamp: getTimestamp(),
350
363
  options,
351
364
  ...pluginOutput,
352
365
  browserconsole: status == 'failed' ? pluginOutput.browserconsole : [],
@@ -378,6 +391,7 @@ export default class Runner extends EventEmitter {
378
391
  };
379
392
  this.emit('journey:end', {
380
393
  journey,
394
+ timestamp: getTimestamp(),
381
395
  start,
382
396
  options,
383
397
  end: monotonicTimeInSeconds(),
@@ -393,8 +407,8 @@ export default class Runner extends EventEmitter {
393
407
  const result: JourneyResult = {
394
408
  status: 'succeeded',
395
409
  };
396
- log(`Runner: start journey (${journey.name})`);
397
410
  const context = await Runner.createContext(options);
411
+ log(`Runner: start journey (${journey.name})`);
398
412
  try {
399
413
  this.registerJourney(journey, context);
400
414
  const hookArgs = {
@@ -35,6 +35,7 @@ export type ActionInContext = {
35
35
  isMainFrame: boolean;
36
36
  action: Action;
37
37
  committed?: boolean;
38
+ modified?: boolean;
38
39
  title?: string;
39
40
  };
40
41
 
@@ -47,6 +48,7 @@ type Action = {
47
48
  modifiers?: number;
48
49
  button?: 'left' | 'middle' | 'right';
49
50
  clickCount?: number;
51
+ text?: string;
50
52
  value?: string;
51
53
  isAssert?: boolean;
52
54
  command?: string;
package/src/index.ts CHANGED
@@ -25,7 +25,6 @@
25
25
 
26
26
  import { runner } from './core';
27
27
  import { RunOptions } from './core/runner';
28
- import { setLogger } from './core/logger';
29
28
  import sourceMapSupport from 'source-map-support';
30
29
 
31
30
  export async function run(options: RunOptions) {
@@ -35,11 +34,6 @@ export async function run(options: RunOptions) {
35
34
  sourceMapSupport.install({
36
35
  environment: 'node',
37
36
  });
38
- /**
39
- * set up logger with appropriate file descriptor
40
- * to capture all the DEBUG logs when run through heartbeat
41
- */
42
- setLogger(options.outfd);
43
37
 
44
38
  try {
45
39
  return await runner.run(options);
@@ -72,3 +66,4 @@ export type { default as Runner } from './core/runner';
72
66
  export type { Reporter, ReporterOptions } from './reporters';
73
67
 
74
68
  export type { SyntheticsConfig } from './common_types';
69
+ export type { ActionInContext } from './formatter/javascript';
package/src/parse_args.ts CHANGED
@@ -43,11 +43,6 @@ program
43
43
  'JSON object that gets injected to all journeys',
44
44
  JSON.parse
45
45
  )
46
- .option(
47
- '-s, --suite-params <jsonstring>',
48
- 'DEPRECATED: Use --params instead',
49
- JSON.parse
50
- )
51
46
  .addOption(
52
47
  new Option('--reporter <value>', `output repoter format`).choices(
53
48
  Object.keys(reporters)
@@ -112,6 +107,7 @@ program
112
107
  ).default(DEFAULT_NETWORK_CONDITIONS_ARG)
113
108
  )
114
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)
115
111
  .version(version)
116
112
  .description('Run synthetic tests');
117
113