@elastic/synthetics 1.0.0-beta.21 → 1.0.0-beta.24

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 (97) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cli.js +55 -184
  3. package/dist/cli.js.map +1 -1
  4. package/dist/common_types.d.ts +80 -32
  5. package/dist/common_types.d.ts.map +1 -1
  6. package/dist/config.js +5 -5
  7. package/dist/config.js.map +1 -1
  8. package/dist/core/browser-service.js +5 -5
  9. package/dist/core/browser-service.js.map +1 -1
  10. package/dist/core/gatherer.d.ts +1 -2
  11. package/dist/core/gatherer.d.ts.map +1 -1
  12. package/dist/core/gatherer.js +7 -8
  13. package/dist/core/gatherer.js.map +1 -1
  14. package/dist/core/index.d.ts +1 -1
  15. package/dist/core/index.d.ts.map +1 -1
  16. package/dist/core/index.js +9 -11
  17. package/dist/core/index.js.map +1 -1
  18. package/dist/core/logger.js +2 -2
  19. package/dist/core/logger.js.map +1 -1
  20. package/dist/core/runner.d.ts +1 -8
  21. package/dist/core/runner.d.ts.map +1 -1
  22. package/dist/core/runner.js +34 -35
  23. package/dist/core/runner.js.map +1 -1
  24. package/dist/dsl/journey.d.ts +4 -3
  25. package/dist/dsl/journey.d.ts.map +1 -1
  26. package/dist/dsl/journey.js +6 -5
  27. package/dist/dsl/journey.js.map +1 -1
  28. package/dist/dsl/step.d.ts +3 -2
  29. package/dist/dsl/step.d.ts.map +1 -1
  30. package/dist/dsl/step.js +2 -1
  31. package/dist/dsl/step.js.map +1 -1
  32. package/dist/formatter/javascript.d.ts +31 -6
  33. package/dist/formatter/javascript.d.ts.map +1 -1
  34. package/dist/formatter/javascript.js +45 -24
  35. package/dist/formatter/javascript.js.map +1 -1
  36. package/dist/helpers.d.ts +2 -1
  37. package/dist/helpers.d.ts.map +1 -1
  38. package/dist/helpers.js +48 -19
  39. package/dist/helpers.js.map +1 -1
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -17
  43. package/dist/index.js.map +1 -1
  44. package/dist/{parse_args.d.ts → loader.d.ts} +2 -5
  45. package/dist/loader.d.ts.map +1 -0
  46. package/dist/loader.js +144 -0
  47. package/dist/loader.js.map +1 -0
  48. package/dist/options.d.ts +27 -0
  49. package/dist/options.d.ts.map +1 -0
  50. package/dist/options.js +108 -0
  51. package/dist/options.js.map +1 -0
  52. package/dist/plugins/browser-console.js +2 -2
  53. package/dist/plugins/browser-console.js.map +1 -1
  54. package/dist/plugins/network.d.ts +1 -1
  55. package/dist/plugins/network.d.ts.map +1 -1
  56. package/dist/plugins/network.js +47 -24
  57. package/dist/plugins/network.js.map +1 -1
  58. package/dist/reporters/base.d.ts.map +1 -1
  59. package/dist/reporters/base.js +14 -11
  60. package/dist/reporters/base.js.map +1 -1
  61. package/dist/reporters/json.d.ts +4 -53
  62. package/dist/reporters/json.d.ts.map +1 -1
  63. package/dist/reporters/json.js +19 -48
  64. package/dist/reporters/json.js.map +1 -1
  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/dist/sdk/lh-trace-processor.js +3 -3
  69. package/dist/sdk/lh-trace-processor.js.map +1 -1
  70. package/dist/sdk/trace-metrics.js +3 -4
  71. package/dist/sdk/trace-metrics.js.map +1 -1
  72. package/dist/sdk/trace-processor.d.ts.map +1 -1
  73. package/dist/sdk/trace-processor.js +1 -1
  74. package/dist/sdk/trace-processor.js.map +1 -1
  75. package/dist/tsconfig.tsbuildinfo +1 -1
  76. package/package.json +8 -7
  77. package/src/cli.ts +114 -188
  78. package/src/common_types.ts +91 -32
  79. package/src/core/gatherer.ts +2 -3
  80. package/src/core/index.ts +22 -16
  81. package/src/core/runner.ts +2 -22
  82. package/src/dsl/journey.ts +10 -4
  83. package/src/dsl/step.ts +9 -2
  84. package/src/formatter/javascript.ts +59 -22
  85. package/src/helpers.ts +40 -1
  86. package/src/index.ts +10 -16
  87. package/src/loader.ts +162 -0
  88. package/src/options.ts +116 -0
  89. package/src/plugins/network.ts +49 -25
  90. package/src/reporters/base.ts +6 -1
  91. package/src/reporters/json.ts +8 -39
  92. package/src/reporters/junit.ts +1 -1
  93. package/src/sdk/trace-processor.ts +1 -1
  94. package/dist/parse_args.d.ts.map +0 -1
  95. package/dist/parse_args.js +0 -103
  96. package/dist/parse_args.js.map +0 -1
  97. package/src/parse_args.ts +0 -158
@@ -25,9 +25,8 @@
25
25
 
26
26
  import { chromium, ChromiumBrowser, BrowserContext } from 'playwright-chromium';
27
27
  import { PluginManager } from '../plugins';
28
- import { RunOptions } from './runner';
29
28
  import { log } from './logger';
30
- import { Driver } from '../common_types';
29
+ import { Driver, RunOptions } from '../common_types';
31
30
 
32
31
  /**
33
32
  * Purpose of the Gatherer is to set up the necessary browser driver
@@ -43,7 +42,7 @@ export class Gatherer {
43
42
  if (Gatherer.browser == null) {
44
43
  if (wsEndpoint) {
45
44
  log(`Gatherer: connecting to WS endpoint: ${wsEndpoint}`);
46
- Gatherer.browser = await chromium.connect({ wsEndpoint });
45
+ Gatherer.browser = await chromium.connect(wsEndpoint);
47
46
  } else {
48
47
  Gatherer.browser = await chromium.launch({
49
48
  ...playwrightOptions,
package/src/core/index.ts CHANGED
@@ -25,7 +25,8 @@
25
25
 
26
26
  import { Journey, JourneyCallback, JourneyOptions } from '../dsl';
27
27
  import Runner from './runner';
28
- import { VoidCallback, HooksCallback } from '../common_types';
28
+ import { VoidCallback, HooksCallback, Location } from '../common_types';
29
+ import { wrapFnWithLocation } from '../helpers';
29
30
  import { log } from './logger';
30
31
 
31
32
  /**
@@ -39,23 +40,28 @@ if (!global[SYNTHETICS_RUNNER]) {
39
40
 
40
41
  export const runner: Runner = global[SYNTHETICS_RUNNER];
41
42
 
42
- export const journey = (
43
- options: JourneyOptions | string,
44
- callback: JourneyCallback
45
- ) => {
46
- log(`register journey: ${JSON.stringify(options)}`);
47
- if (typeof options === 'string') {
48
- options = { name: options, id: options };
43
+ export const journey = wrapFnWithLocation(
44
+ (
45
+ location: Location,
46
+ options: JourneyOptions | string,
47
+ callback: JourneyCallback
48
+ ) => {
49
+ log(`register journey: ${JSON.stringify(options)}`);
50
+ if (typeof options === 'string') {
51
+ options = { name: options, id: options };
52
+ }
53
+ const j = new Journey(options, callback, location);
54
+ runner.addJourney(j);
55
+ return j;
49
56
  }
50
- const j = new Journey(options, callback);
51
- runner.addJourney(j);
52
- return j;
53
- };
57
+ );
54
58
 
55
- export const step = (name: string, callback: VoidCallback) => {
56
- log(`register step: ${name}`);
57
- return runner.currentJourney?.addStep(name, callback);
58
- };
59
+ export const step = wrapFnWithLocation(
60
+ (location: Location, name: string, callback: VoidCallback) => {
61
+ log(`register step: ${name}`);
62
+ return runner.currentJourney?.addStep(name, callback, location);
63
+ }
64
+ );
59
65
 
60
66
  export const beforeAll = (callback: HooksCallback) => {
61
67
  runner.addHook('beforeAll', callback);
@@ -28,7 +28,7 @@ import { join } from 'path';
28
28
  import { rm } from 'fs/promises';
29
29
  import { Journey } from '../dsl/journey';
30
30
  import { Step } from '../dsl/step';
31
- import { reporters, Reporter } from '../reporters';
31
+ import { reporters } from '../reporters';
32
32
  import {
33
33
  CACHE_PATH,
34
34
  monotonicTimeInSeconds,
@@ -44,36 +44,16 @@ import {
44
44
  Params,
45
45
  NetworkConditions,
46
46
  PluginOutput,
47
- CliArgs,
48
47
  HooksArgs,
49
- PlaywrightOptions,
50
48
  Driver,
51
49
  Screenshot,
50
+ RunOptions,
52
51
  } from '../common_types';
53
52
  import { PageMetrics, PluginManager } from '../plugins';
54
53
  import { PerformanceManager } from '../plugins';
55
54
  import { Gatherer } from './gatherer';
56
55
  import { log } from './logger';
57
56
 
58
- export type RunOptions = Omit<
59
- CliArgs,
60
- | 'debug'
61
- | 'pattern'
62
- | 'inline'
63
- | 'require'
64
- | 'reporter'
65
- | 'richEvents'
66
- | 'capability'
67
- | 'sandbox'
68
- | 'headless'
69
- | 'throttling'
70
- > & {
71
- environment?: string;
72
- playwrightOptions?: PlaywrightOptions;
73
- networkConditions?: NetworkConditions;
74
- reporter?: CliArgs['reporter'] | Reporter;
75
- };
76
-
77
57
  type HookType = 'beforeAll' | 'afterAll';
78
58
  export type SuiteHooks = Record<HookType, Array<HooksCallback>>;
79
59
 
@@ -26,7 +26,7 @@
26
26
  import { Browser, Page, BrowserContext, CDPSession } from 'playwright-chromium';
27
27
  import micromatch, { isMatch } from 'micromatch';
28
28
  import { Step } from './step';
29
- import { VoidCallback, HooksCallback, Params } from '../common_types';
29
+ import { VoidCallback, HooksCallback, Params, Location } from '../common_types';
30
30
 
31
31
  export type JourneyOptions = {
32
32
  name: string;
@@ -49,18 +49,24 @@ export class Journey {
49
49
  id?: string;
50
50
  tags?: string[];
51
51
  callback: JourneyCallback;
52
+ location?: Location;
52
53
  steps: Step[] = [];
53
54
  hooks: Hooks = { before: [], after: [] };
54
55
 
55
- constructor(options: JourneyOptions, callback: JourneyCallback) {
56
+ constructor(
57
+ options: JourneyOptions,
58
+ callback: JourneyCallback,
59
+ location?: Location
60
+ ) {
56
61
  this.name = options.name;
57
62
  this.id = options.id || options.name;
58
63
  this.tags = options.tags;
59
64
  this.callback = callback;
65
+ this.location = location;
60
66
  }
61
67
 
62
- addStep(name: string, callback: VoidCallback) {
63
- const step = new Step(name, this.steps.length + 1, callback);
68
+ addStep(name: string, callback: VoidCallback, location?: Location) {
69
+ const step = new Step(name, this.steps.length + 1, callback, location);
64
70
  this.steps.push(step);
65
71
  return step;
66
72
  }
package/src/dsl/step.ts CHANGED
@@ -23,16 +23,23 @@
23
23
  *
24
24
  */
25
25
 
26
- import { VoidCallback } from '../common_types';
26
+ import { Location, VoidCallback } from '../common_types';
27
27
 
28
28
  export class Step {
29
29
  name: string;
30
30
  index: number;
31
31
  callback: VoidCallback;
32
+ location?: Location;
32
33
 
33
- constructor(name: string, index: number, callback: VoidCallback) {
34
+ constructor(
35
+ name: string,
36
+ index: number,
37
+ callback: VoidCallback,
38
+ location: Location
39
+ ) {
34
40
  this.name = name;
35
41
  this.index = index;
36
42
  this.callback = callback;
43
+ this.location = location;
37
44
  }
38
45
  }
@@ -26,7 +26,14 @@
26
26
  import {
27
27
  JavaScriptLanguageGenerator,
28
28
  JavaScriptFormatter,
29
- } from 'playwright-chromium/lib/server/supplements/recorder/javascript';
29
+ } from 'playwright-core/lib/server/supplements/recorder/javascript';
30
+
31
+ export type Step = {
32
+ actions: ActionInContext[];
33
+ name?: string;
34
+ };
35
+
36
+ export type Steps = Step[];
30
37
 
31
38
  export type ActionInContext = {
32
39
  pageAlias: string;
@@ -39,7 +46,7 @@ export type ActionInContext = {
39
46
  title?: string;
40
47
  };
41
48
 
42
- type Action = {
49
+ export type Action = {
43
50
  name: string;
44
51
  selector?: string;
45
52
  url?: string;
@@ -52,9 +59,11 @@ type Action = {
52
59
  value?: string;
53
60
  isAssert?: boolean;
54
61
  command?: string;
62
+ files?: string[];
63
+ options?: string[];
55
64
  };
56
65
 
57
- type Signal = {
66
+ export type Signal = {
58
67
  name: string;
59
68
  url?: string;
60
69
  isAsync?: boolean;
@@ -73,8 +82,13 @@ export class SyntheticsGenerator extends JavaScriptLanguageGenerator {
73
82
  this.previousContext = undefined;
74
83
  }
75
84
 
85
+ /**
86
+ * Generate code for an action.
87
+ * @param actionInContext The action to create code for.
88
+ * @returns the strings generated for the action.
89
+ */
76
90
  generateAction(actionInContext: ActionInContext) {
77
- const { action, pageAlias, title } = actionInContext;
91
+ const { action, pageAlias } = actionInContext;
78
92
  if (action.name === 'openPage') {
79
93
  return '';
80
94
  }
@@ -84,17 +98,9 @@ export class SyntheticsGenerator extends JavaScriptLanguageGenerator {
84
98
  return '';
85
99
  }
86
100
 
87
- let formatter = new JavaScriptFormatter(this.isSuite ? 2 : 0);
88
- // Check if it's a new step
89
- const isNewStep = this.isNewStep(actionInContext);
90
- if (isNewStep) {
91
- if (this.insideStep) {
92
- formatter.add(this.generateStepEnd());
93
- }
94
- formatter.add(this.generateStepStart(title || actionTitle(action)));
95
- } else {
96
- formatter = new JavaScriptFormatter(this.isSuite ? 4 : 2);
97
- }
101
+ const stepIndent = this.insideStep ? 2 : 0;
102
+ const offset = this.isSuite ? 2 + stepIndent : 0 + stepIndent;
103
+ const formatter = new JavaScriptFormatter(offset);
98
104
 
99
105
  const subject = actionInContext.isMainFrame
100
106
  ? pageAlias
@@ -185,7 +191,9 @@ export class SyntheticsGenerator extends JavaScriptLanguageGenerator {
185
191
 
186
192
  generateStepStart(name) {
187
193
  this.insideStep = true;
188
- return `step(${quote(name)}, async () => {`;
194
+ const formatter = new JavaScriptFormatter(this.isSuite ? 2 : 0);
195
+ formatter.add(`step(${quote(name)}, async () => {`);
196
+ return formatter.format();
189
197
  }
190
198
 
191
199
  generateStepEnd() {
@@ -211,19 +219,41 @@ export class SyntheticsGenerator extends JavaScriptLanguageGenerator {
211
219
  return `});`;
212
220
  }
213
221
 
214
- generateText(actions: Array<ActionInContext>) {
222
+ /**
223
+ * Generates JavaScript code from a custom set of steps and nested actions.
224
+ *
225
+ * This function makes no assumptions about where steps should be created,
226
+ * and instead follows the step definitions the caller has defined.
227
+ * @param steps IR to use for code generation
228
+ * @returns a list of the code strings outputted by the generator
229
+ */
230
+ generateFromSteps(steps: Steps): string {
215
231
  const text = [];
216
232
  if (this.isSuite) {
217
233
  text.push(this.generateHeader());
218
234
  }
219
- for (let i = 0; i < actions.length; i++) {
220
- text.push(this.generateAction(actions[i]));
221
- if (i === actions.length - 1) text.push(this.generateStepEnd());
235
+ for (const step of steps) {
236
+ if (step.actions.length === 0)
237
+ throw Error('Cannot process an empty step');
238
+ text.push(
239
+ this.generateStepStart(
240
+ step.name ??
241
+ step.actions[0].title ??
242
+ actionTitle(step.actions[0].action)
243
+ )
244
+ );
245
+
246
+ for (const action of step.actions) {
247
+ const actionText = this.generateAction(action);
248
+ if (actionText.length) text.push(actionText);
249
+ }
250
+
251
+ text.push(this.generateStepEnd());
222
252
  }
223
253
  if (this.isSuite) {
224
254
  text.push(this.generateFooter());
225
255
  }
226
- return text.filter(t => Boolean(t)).join('\n');
256
+ return text.filter(s => !!s).join('\n');
227
257
  }
228
258
  }
229
259
 
@@ -297,7 +327,12 @@ function formatObject(value, indent = ' ') {
297
327
  return String(value);
298
328
  }
299
329
 
300
- function actionTitle(action) {
330
+ /**
331
+ * Generates an appropriate title string based on the action type/data.
332
+ * @param action Playwright action IR
333
+ * @returns title string
334
+ */
335
+ export function actionTitle(action: Action) {
301
336
  switch (action.name) {
302
337
  case 'openPage':
303
338
  return `Open new page`;
@@ -326,5 +361,7 @@ function actionTitle(action) {
326
361
  );
327
362
  case 'select':
328
363
  return `Select ${action.options.join(', ')}`;
364
+ case 'assert':
365
+ return `Assert ${action.selector} ${action.command}`;
329
366
  }
330
367
  }
package/src/helpers.ts CHANGED
@@ -29,7 +29,13 @@ import { resolve, join, dirname } from 'path';
29
29
  import fs from 'fs';
30
30
  import { promisify } from 'util';
31
31
  import { performance } from 'perf_hooks';
32
- import { HooksArgs, HooksCallback, NetworkConditions } from './common_types';
32
+ import sourceMapSupport from '@cspotcode/source-map-support';
33
+ import {
34
+ HooksArgs,
35
+ HooksCallback,
36
+ NetworkConditions,
37
+ Location,
38
+ } from './common_types';
33
39
 
34
40
  const lstatAsync = promisify(fs.lstat);
35
41
  const readdirAsync = promisify(fs.readdir);
@@ -315,3 +321,36 @@ export function parseNetworkConditions(args: string): NetworkConditions {
315
321
 
316
322
  return networkConditions;
317
323
  }
324
+
325
+ // default stack trace limit
326
+ const dstackTraceLimit = 10;
327
+
328
+ // Uses the V8 Stacktrace API to get the function location
329
+ // information - https://v8.dev/docs/stack-trace-api#customizing-stack-traces
330
+ export function wrapFnWithLocation<A extends unknown[], R>(
331
+ func: (location: Location, ...args: A) => R
332
+ ): (...args: A) => R {
333
+ return (...args) => {
334
+ const _prepareStackTrace = Error.prepareStackTrace;
335
+ Error.prepareStackTrace = (_, stackFrames) => {
336
+ // Deafult CallSite would not map to the original transpiled source
337
+ // from ts-node properly, So we wrap it with the library that knows
338
+ // how to retrive those source-map for the transpiled code
339
+ const frame: NodeJS.CallSite = sourceMapSupport.wrapCallSite(
340
+ stackFrames[1]
341
+ );
342
+ return {
343
+ file: frame.getFileName(),
344
+ line: frame.getLineNumber(),
345
+ column: frame.getColumnNumber(),
346
+ };
347
+ };
348
+ Error.stackTraceLimit = 2;
349
+ const obj: { stack: Location } = {} as any;
350
+ Error.captureStackTrace(obj);
351
+ const location = obj.stack;
352
+ Error.stackTraceLimit = dstackTraceLimit;
353
+ Error.prepareStackTrace = _prepareStackTrace;
354
+ return func(location, ...args);
355
+ };
356
+ }
package/src/index.ts CHANGED
@@ -24,23 +24,10 @@
24
24
  */
25
25
 
26
26
  import { runner } from './core';
27
- import { RunOptions } from './core/runner';
28
- import sourceMapSupport from 'source-map-support';
27
+ import { RunOptions } from './common_types';
29
28
 
30
29
  export async function run(options: RunOptions) {
31
- /**
32
- * Install source map support
33
- */
34
- sourceMapSupport.install({
35
- environment: 'node',
36
- });
37
-
38
- try {
39
- return await runner.run(options);
40
- } catch (e) {
41
- console.error('Failed to run the test', e);
42
- process.exit(1);
43
- }
30
+ return runner.run(options);
44
31
  }
45
32
 
46
33
  /**
@@ -66,4 +53,11 @@ export type { default as Runner } from './core/runner';
66
53
  export type { Reporter, ReporterOptions } from './reporters';
67
54
 
68
55
  export type { SyntheticsConfig } from './common_types';
69
- export type { ActionInContext } from './formatter/javascript';
56
+ export type {
57
+ Action,
58
+ ActionInContext,
59
+ Signal,
60
+ Step,
61
+ Steps,
62
+ actionTitle,
63
+ } from './formatter/javascript';
package/src/loader.ts ADDED
@@ -0,0 +1,162 @@
1
+ /**
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2020-present, Elastic NV
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ *
24
+ */
25
+
26
+ import { stdin, cwd } from 'process';
27
+ import { resolve } from 'path';
28
+ import { CliArgs } from './common_types';
29
+ import { step, journey } from './core';
30
+ import { log } from './core/logger';
31
+ import { expect } from './core/expect';
32
+ import {
33
+ isDepInstalled,
34
+ isDirectory,
35
+ totalist,
36
+ findPkgJsonByTraversing,
37
+ } from './helpers';
38
+
39
+ const resolvedCwd = cwd();
40
+
41
+ export async function loadTestFiles(options: CliArgs, args: string[]) {
42
+ /**
43
+ * Transform `.ts` files out of the box by invoking
44
+ * the `ts-node` via `transpile-only` mode that compiles
45
+ * TS files without doing any extensive type checks.
46
+ *
47
+ * We must register `ts-node` _before_ loading inline
48
+ * scripts too because otherwise we will not be able to
49
+ * require `.ts` configuration files.
50
+ */
51
+ /* eslint-disable-next-line @typescript-eslint/no-var-requires */
52
+ require('ts-node').register({
53
+ transpileOnly: true,
54
+ compilerOptions: {
55
+ esModuleInterop: true,
56
+ allowJs: true,
57
+ target: 'es2020',
58
+ },
59
+ });
60
+
61
+ /**
62
+ * Preload modules before running the tests
63
+ */
64
+ const modules = [].concat(options.require || []).filter(Boolean);
65
+ for (const name of modules) {
66
+ if (isDepInstalled(name)) {
67
+ require(name);
68
+ } else {
69
+ throw new Error(`cannot find module '${name}'`);
70
+ }
71
+ }
72
+
73
+ if (options.inline) {
74
+ const source = await readStdin();
75
+ loadInlineScript(source);
76
+ return;
77
+ }
78
+ /**
79
+ * Handle piped files by reading the STDIN
80
+ * ex: ls example/suites/*.js | npx @elastic/synthetics
81
+ */
82
+ const files =
83
+ args.length > 0 ? args : (await readStdin()).split('\n').filter(Boolean);
84
+ const suites = await prepareSuites(files, options);
85
+ requireSuites(suites);
86
+ }
87
+
88
+ const loadInlineScript = source => {
89
+ const scriptFn = new Function(
90
+ 'step',
91
+ 'page',
92
+ 'context',
93
+ 'browser',
94
+ 'params',
95
+ 'expect',
96
+ source
97
+ );
98
+ journey('inline', ({ page, context, browser, params }) => {
99
+ scriptFn.apply(null, [step, page, context, browser, params, expect]);
100
+ });
101
+ };
102
+
103
+ /**
104
+ * Read the input from STDIN and run it as inline journey
105
+ */
106
+ async function readStdin() {
107
+ const chunks = [];
108
+ stdin.resume();
109
+ stdin.setEncoding('utf-8');
110
+ for await (const chunk of stdin) {
111
+ chunks.push(chunk);
112
+ }
113
+ return chunks.join();
114
+ }
115
+
116
+ function requireSuites(suites: Iterable<string>) {
117
+ for (const suite of suites) {
118
+ require(suite);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Handle both directory and files that are passed through TTY
124
+ * and add them to suites
125
+ */
126
+ async function prepareSuites(inputs: string[], cliArgs: CliArgs) {
127
+ const suites = new Set<string>();
128
+ const addSuite = absPath => {
129
+ log(`Processing file: ${absPath}`);
130
+ suites.add(require.resolve(absPath));
131
+ };
132
+ /**
133
+ * Match all files inside the directory with the
134
+ * .journey.{mjs|cjs|js|ts) extensions
135
+ */
136
+ const pattern = cliArgs.pattern
137
+ ? new RegExp(cliArgs.pattern, 'i')
138
+ : /.+\.journey\.([mc]js|[jt]s?)$/;
139
+ /**
140
+ * Ignore node_modules by default when running suites
141
+ */
142
+ const ignored = /node_modules/i;
143
+
144
+ for (const input of inputs) {
145
+ const absPath = resolve(resolvedCwd, input);
146
+ /**
147
+ * Validate for package.json file before running
148
+ * the suites
149
+ */
150
+ findPkgJsonByTraversing(absPath, resolvedCwd);
151
+ if (isDirectory(absPath)) {
152
+ await totalist(absPath, (rel, abs) => {
153
+ if (pattern.test(rel) && !ignored.test(rel)) {
154
+ addSuite(abs);
155
+ }
156
+ });
157
+ } else {
158
+ addSuite(absPath);
159
+ }
160
+ }
161
+ return suites.values();
162
+ }