@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.
- package/CHANGELOG.md +55 -0
- package/NOTICE.txt +29 -0
- package/README.md +1 -1
- package/dist/cli.js +34 -21
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +10 -40
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +9 -1
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/logger.d.ts +0 -2
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +2 -15
- package/dist/core/logger.js.map +1 -1
- package/dist/core/runner.d.ts +19 -20
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +14 -5
- package/dist/core/runner.js.map +1 -1
- package/dist/formatter/javascript.d.ts +2 -0
- package/dist/formatter/javascript.d.ts.map +1 -1
- package/dist/formatter/javascript.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +1 -1
- package/dist/parse_args.d.ts.map +1 -1
- package/dist/parse_args.js +1 -1
- package/dist/parse_args.js.map +1 -1
- package/dist/plugins/network.d.ts +30 -11
- package/dist/plugins/network.d.ts.map +1 -1
- package/dist/plugins/network.js +234 -175
- package/dist/plugins/network.js.map +1 -1
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js +6 -1
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/json.d.ts +55 -5
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +45 -14
- package/dist/reporters/json.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/cli.ts +37 -25
- package/src/common_types.ts +10 -46
- package/src/core/gatherer.ts +8 -1
- package/src/core/logger.ts +1 -12
- package/src/core/runner.ts +42 -28
- package/src/formatter/javascript.ts +2 -0
- package/src/index.ts +1 -6
- package/src/parse_args.ts +1 -5
- package/src/plugins/network.ts +280 -213
- package/src/reporters/base.ts +6 -1
- 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 =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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),
|
package/src/common_types.ts
CHANGED
|
@@ -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
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
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
|
|
package/src/core/gatherer.ts
CHANGED
|
@@ -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(
|
|
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({
|
package/src/core/logger.ts
CHANGED
|
@@ -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
|
-
|
|
37
|
+
console.log(time + italic(grey(msg)) + '\n');
|
|
49
38
|
}
|
package/src/core/runner.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
119
|
-
timestamp: number;
|
|
120
|
-
params: Params;
|
|
121
|
-
};
|
|
122
|
-
'journey:end': BaseContext &
|
|
125
|
+
'journey:start': JourneyEvent;
|
|
126
|
+
'journey:end': JourneyEvent &
|
|
123
127
|
JourneyResult & {
|
|
124
|
-
|
|
128
|
+
start: number;
|
|
129
|
+
end: number;
|
|
125
130
|
options: RunOptions;
|
|
131
|
+
timestamp: number;
|
|
126
132
|
};
|
|
127
133
|
'journey:end:reported': unknown;
|
|
128
|
-
'step:start':
|
|
129
|
-
'step:end':
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
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
|
|