@elastic/synthetics 1.3.0 → 1.5.0
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/dist/bundles/lib/index.js +432 -0
- package/dist/cli.js +23 -14
- package/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts +51 -40
- package/dist/common_types.d.ts.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -1
- package/dist/config.js.map +1 -1
- package/dist/core/browser-service.js +2 -2
- package/dist/core/browser-service.js.map +1 -1
- package/dist/core/expect.d.ts +1 -152
- package/dist/core/expect.d.ts.map +1 -1
- package/dist/core/expect.js +14 -5
- package/dist/core/expect.js.map +1 -1
- package/dist/core/gatherer.d.ts +1 -1
- package/dist/core/gatherer.d.ts.map +1 -1
- package/dist/core/gatherer.js +5 -5
- package/dist/core/gatherer.js.map +1 -1
- package/dist/core/runner.d.ts +4 -4
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +8 -7
- package/dist/core/runner.js.map +1 -1
- package/dist/core/transform.d.ts +62 -2
- package/dist/core/transform.d.ts.map +1 -1
- package/dist/dsl/journey.d.ts +5 -5
- package/dist/dsl/journey.d.ts.map +1 -1
- package/dist/dsl/journey.js +0 -1
- package/dist/dsl/journey.js.map +1 -1
- package/dist/dsl/monitor.d.ts +8 -3
- package/dist/dsl/monitor.d.ts.map +1 -1
- package/dist/dsl/monitor.js +1 -0
- package/dist/dsl/monitor.js.map +1 -1
- package/dist/formatter/javascript.d.ts +6 -6
- package/dist/formatter/javascript.d.ts.map +1 -1
- package/dist/generator/index.d.ts +1 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +1 -1
- package/dist/generator/index.js.map +1 -1
- package/dist/helpers.d.ts +1 -6
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +1 -24
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/locations/index.d.ts +13 -5
- package/dist/locations/index.d.ts.map +1 -1
- package/dist/locations/index.js +30 -14
- package/dist/locations/index.js.map +1 -1
- package/dist/options.d.ts +5 -2
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +20 -3
- package/dist/options.js.map +1 -1
- package/dist/plugins/browser-console.d.ts +3 -2
- package/dist/plugins/browser-console.d.ts.map +1 -1
- package/dist/plugins/browser-console.js +33 -7
- package/dist/plugins/browser-console.js.map +1 -1
- package/dist/plugins/network.d.ts +1 -0
- package/dist/plugins/network.d.ts.map +1 -1
- package/dist/plugins/network.js +15 -2
- package/dist/plugins/network.js.map +1 -1
- package/dist/plugins/plugin-manager.d.ts +3 -3
- package/dist/plugins/plugin-manager.d.ts.map +1 -1
- package/dist/plugins/tracing.d.ts +1 -1
- package/dist/plugins/tracing.d.ts.map +1 -1
- package/dist/push/index.d.ts +1 -1
- package/dist/push/index.d.ts.map +1 -1
- package/dist/push/index.js +7 -6
- package/dist/push/index.js.map +1 -1
- package/dist/push/kibana_api.d.ts +5 -5
- package/dist/push/kibana_api.d.ts.map +1 -1
- package/dist/push/kibana_api.js +1 -1
- package/dist/push/kibana_api.js.map +1 -1
- package/dist/push/monitor.d.ts +3 -3
- package/dist/push/monitor.d.ts.map +1 -1
- package/dist/push/monitor.js +22 -9
- package/dist/push/monitor.js.map +1 -1
- package/dist/push/request.d.ts +2 -2
- package/dist/push/request.d.ts.map +1 -1
- package/dist/push/utils.d.ts +3 -3
- package/dist/push/utils.d.ts.map +1 -1
- package/dist/push/utils.js +1 -1
- package/dist/push/utils.js.map +1 -1
- package/dist/reporters/base.d.ts +3 -0
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js +20 -22
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/index.d.ts +4 -3
- package/dist/reporters/index.d.ts.map +1 -1
- package/dist/reporters/index.js.map +1 -1
- package/dist/reporters/json.d.ts +5 -5
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +29 -1
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/junit.d.ts.map +1 -1
- package/dist/reporters/junit.js +4 -4
- package/dist/reporters/junit.js.map +1 -1
- package/dist/reporters/reporter-util.d.ts +35 -0
- package/dist/reporters/reporter-util.d.ts.map +1 -0
- package/dist/reporters/reporter-util.js +192 -0
- package/dist/reporters/reporter-util.js.map +1 -0
- package/dist/sdk/trace-processor.d.ts +4 -4
- package/dist/sdk/trace-processor.d.ts.map +1 -1
- package/package.json +20 -11
- package/src/cli.ts +23 -22
- package/src/common_types.ts +17 -3
- package/src/config.ts +12 -1
- package/src/core/browser-service.ts +1 -1
- package/src/core/expect.ts +13 -191
- package/src/core/gatherer.ts +1 -1
- package/src/core/runner.ts +15 -9
- package/src/dsl/journey.ts +1 -2
- package/src/dsl/monitor.ts +5 -0
- package/src/generator/index.ts +2 -1
- package/src/helpers.ts +0 -24
- package/src/index.ts +3 -1
- package/src/locations/index.ts +37 -12
- package/src/options.ts +28 -3
- package/src/plugins/browser-console.ts +39 -8
- package/src/plugins/network.ts +16 -3
- package/src/push/index.ts +9 -6
- package/src/push/kibana_api.ts +4 -3
- package/src/push/monitor.ts +26 -9
- package/src/push/utils.ts +3 -3
- package/src/reporters/base.ts +26 -33
- package/src/reporters/index.ts +5 -1
- package/src/reporters/json.ts +33 -3
- package/src/reporters/junit.ts +5 -5
- package/src/reporters/reporter-util.ts +217 -0
|
@@ -23,13 +23,16 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import type { ConsoleMessage } from 'playwright-core';
|
|
27
|
-
import { BrowserMessage, Driver } from '../common_types';
|
|
26
|
+
import type { ConsoleMessage, WebError } from 'playwright-core';
|
|
27
|
+
import { BrowserMessage, Driver, StatusValue } from '../common_types';
|
|
28
28
|
import { log } from '../core/logger';
|
|
29
29
|
import { Step } from '../dsl';
|
|
30
30
|
import { getTimestamp } from '../helpers';
|
|
31
31
|
|
|
32
|
-
const
|
|
32
|
+
const DEFAULT_MSG_LIMIt = 1000;
|
|
33
|
+
const SUCCESSFUL_MSG_LIMIT = 100;
|
|
34
|
+
|
|
35
|
+
const allowedTypes = ['error', 'warning', 'log'];
|
|
33
36
|
|
|
34
37
|
export class BrowserConsole {
|
|
35
38
|
private messages: BrowserMessage[] = [];
|
|
@@ -42,7 +45,7 @@ export class BrowserConsole {
|
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
44
47
|
const type = msg.type();
|
|
45
|
-
if (type
|
|
48
|
+
if (allowedTypes.includes(type)) {
|
|
46
49
|
const { name, index } = this._currentStep;
|
|
47
50
|
this.messages.push({
|
|
48
51
|
timestamp: getTimestamp(),
|
|
@@ -55,11 +58,12 @@ export class BrowserConsole {
|
|
|
55
58
|
}
|
|
56
59
|
};
|
|
57
60
|
|
|
58
|
-
private
|
|
61
|
+
private webErrorListener = (webErr: WebError) => {
|
|
59
62
|
if (!this._currentStep) {
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
65
|
const { name, index } = this._currentStep;
|
|
66
|
+
const error = webErr.error();
|
|
63
67
|
this.messages.push({
|
|
64
68
|
timestamp: getTimestamp(),
|
|
65
69
|
text: error.message,
|
|
@@ -72,7 +76,7 @@ export class BrowserConsole {
|
|
|
72
76
|
};
|
|
73
77
|
|
|
74
78
|
private enforceMessagesLimit() {
|
|
75
|
-
if (this.messages.length >
|
|
79
|
+
if (this.messages.length > DEFAULT_MSG_LIMIt) {
|
|
76
80
|
this.messages.splice(0, 1);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
@@ -80,13 +84,40 @@ export class BrowserConsole {
|
|
|
80
84
|
start() {
|
|
81
85
|
log(`Plugins: started collecting console events`);
|
|
82
86
|
this.driver.context.on('console', this.consoleEventListener);
|
|
83
|
-
this.driver.
|
|
87
|
+
this.driver.context.on('weberror', this.webErrorListener);
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
stop() {
|
|
87
91
|
this.driver.context.off('console', this.consoleEventListener);
|
|
88
|
-
this.driver.
|
|
92
|
+
this.driver.context.off('weberror', this.webErrorListener);
|
|
89
93
|
log(`Plugins: stopped collecting console events`);
|
|
90
94
|
return this.messages;
|
|
91
95
|
}
|
|
92
96
|
}
|
|
97
|
+
|
|
98
|
+
export function filterBrowserMessages(
|
|
99
|
+
messages: BrowserMessage[],
|
|
100
|
+
status: StatusValue
|
|
101
|
+
) {
|
|
102
|
+
if (status == 'skipped') {
|
|
103
|
+
return [];
|
|
104
|
+
} else if (status === 'failed' || messages.length <= SUCCESSFUL_MSG_LIMIT) {
|
|
105
|
+
return messages;
|
|
106
|
+
}
|
|
107
|
+
// collect 100 messages from the browser console when the test is successful,
|
|
108
|
+
// giving priority to errors and warnings
|
|
109
|
+
const result = messages.filter(msg => msg.type === 'error');
|
|
110
|
+
if (result.length >= SUCCESSFUL_MSG_LIMIT) {
|
|
111
|
+
return result.slice(-SUCCESSFUL_MSG_LIMIT);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// collect warnings
|
|
115
|
+
result.push(...messages.filter(msg => msg.type === 'warning'));
|
|
116
|
+
if (result.length >= SUCCESSFUL_MSG_LIMIT) {
|
|
117
|
+
return result.slice(-SUCCESSFUL_MSG_LIMIT);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// collect logs
|
|
121
|
+
result.push(...messages.filter(msg => msg.type === 'log'));
|
|
122
|
+
return result.slice(-SUCCESSFUL_MSG_LIMIT);
|
|
123
|
+
}
|
package/src/plugins/network.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { Page, Request, Response } from 'playwright-
|
|
26
|
+
import { Frame, Page, Request, Response } from 'playwright-core';
|
|
27
27
|
import { NetworkInfo, BrowserInfo, Driver } from '../common_types';
|
|
28
28
|
import { log } from '../core/logger';
|
|
29
29
|
import { Step } from '../dsl';
|
|
@@ -93,6 +93,17 @@ export class NetworkManager {
|
|
|
93
93
|
race.then(() => this._barrierPromises.delete(race));
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
private _nullableFrameBarrier(req: Request): Frame | null {
|
|
97
|
+
try {
|
|
98
|
+
return req.frame();
|
|
99
|
+
} catch (_) {
|
|
100
|
+
// frame might be unavailable for certain requests if they are issued
|
|
101
|
+
// before the frame is created - true for navigation requests
|
|
102
|
+
// https://playwright.dev/docs/api/class-request#request-frame
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
async start() {
|
|
97
108
|
log(`Plugins: started collecting network events`);
|
|
98
109
|
const { client, context } = this.driver;
|
|
@@ -226,7 +237,8 @@ export class NetworkManager {
|
|
|
226
237
|
};
|
|
227
238
|
this._calcTotalTime(networkEntry, timing);
|
|
228
239
|
|
|
229
|
-
const
|
|
240
|
+
const frame = this._nullableFrameBarrier(request);
|
|
241
|
+
const page = frame?.page();
|
|
230
242
|
this._addBarrier(
|
|
231
243
|
page,
|
|
232
244
|
request.allHeaders().then(reqHeaders => {
|
|
@@ -278,7 +290,8 @@ export class NetworkManager {
|
|
|
278
290
|
return;
|
|
279
291
|
}
|
|
280
292
|
|
|
281
|
-
const
|
|
293
|
+
const frame = this._nullableFrameBarrier(request);
|
|
294
|
+
const page = frame?.page();
|
|
282
295
|
this._addBarrier(
|
|
283
296
|
page,
|
|
284
297
|
request.sizes().then(sizes => {
|
package/src/push/index.ts
CHANGED
|
@@ -73,8 +73,12 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
73
73
|
return await pushLegacy(monitors, options);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
const local = getLocalMonitors(monitors);
|
|
77
76
|
const { monitors: remote } = await bulkGetMonitors(options);
|
|
77
|
+
|
|
78
|
+
progress(`bundling ${monitors.length} monitors`);
|
|
79
|
+
const schemas = await buildMonitorSchema(monitors, true);
|
|
80
|
+
const local = getLocalMonitors(schemas);
|
|
81
|
+
|
|
78
82
|
const { newIDs, changedIDs, removedIDs, unchangedIDs } = diffMonitorHashIDs(
|
|
79
83
|
local,
|
|
80
84
|
remote
|
|
@@ -83,9 +87,6 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
83
87
|
|
|
84
88
|
const updatedMonitors = new Set<string>([...changedIDs, ...newIDs]);
|
|
85
89
|
if (updatedMonitors.size > 0) {
|
|
86
|
-
const toBundle = monitors.filter(m => updatedMonitors.has(m.config.id));
|
|
87
|
-
progress(`bundling ${toBundle.length} monitors`);
|
|
88
|
-
const schemas = await buildMonitorSchema(toBundle, true);
|
|
89
90
|
const chunks = getChunks(schemas, CHUNK_SIZE);
|
|
90
91
|
for (const chunk of chunks) {
|
|
91
92
|
await liveProgress(
|
|
@@ -159,7 +160,7 @@ export function formatDuplicateError(monitors: Set<Monitor>) {
|
|
|
159
160
|
|
|
160
161
|
const INSTALLATION_HELP = `Run 'npx @elastic/synthetics init' to create project with default settings.`;
|
|
161
162
|
|
|
162
|
-
export async function loadSettings() {
|
|
163
|
+
export async function loadSettings(ignoreMissing = false) {
|
|
163
164
|
try {
|
|
164
165
|
const config = await readConfig(process.env['NODE_ENV'] || 'development');
|
|
165
166
|
// Missing config file, fake throw to capture as missing file
|
|
@@ -168,9 +169,11 @@ export async function loadSettings() {
|
|
|
168
169
|
}
|
|
169
170
|
return config.project || ({} as ProjectSettings);
|
|
170
171
|
} catch (e) {
|
|
171
|
-
|
|
172
|
+
if (!ignoreMissing) {
|
|
173
|
+
throw error(`Aborted (missing synthetics config file), Project not set up correctly.
|
|
172
174
|
|
|
173
175
|
${INSTALLATION_HELP}`);
|
|
176
|
+
}
|
|
174
177
|
}
|
|
175
178
|
}
|
|
176
179
|
|
package/src/push/kibana_api.ts
CHANGED
|
@@ -124,8 +124,8 @@ export async function bulkDeleteMonitors(
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
type StatusResponse = {
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
kibana: {
|
|
128
|
+
version: string;
|
|
129
129
|
};
|
|
130
130
|
};
|
|
131
131
|
|
|
@@ -135,7 +135,8 @@ export async function getVersion(options: PushOptions) {
|
|
|
135
135
|
method: 'GET',
|
|
136
136
|
auth: options.auth,
|
|
137
137
|
});
|
|
138
|
-
|
|
138
|
+
|
|
139
|
+
return data.kibana.version;
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
export type LegacyAPISchema = {
|
package/src/push/monitor.ts
CHANGED
|
@@ -110,12 +110,12 @@ export function diffMonitors(
|
|
|
110
110
|
return result;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
export function getLocalMonitors(
|
|
113
|
+
export function getLocalMonitors(schemas: MonitorSchema[]) {
|
|
114
114
|
const data: MonitorHashID[] = [];
|
|
115
|
-
for (const
|
|
115
|
+
for (const schema of schemas) {
|
|
116
116
|
data.push({
|
|
117
|
-
journey_id:
|
|
118
|
-
hash:
|
|
117
|
+
journey_id: schema.id,
|
|
118
|
+
hash: schema.hash,
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
121
|
return data;
|
|
@@ -137,14 +137,20 @@ export async function buildMonitorSchema(monitors: Monitor[], isV2: boolean) {
|
|
|
137
137
|
...config,
|
|
138
138
|
locations: translateLocation(config.locations),
|
|
139
139
|
};
|
|
140
|
-
|
|
141
|
-
schema.hash = monitor.hash();
|
|
142
|
-
}
|
|
140
|
+
|
|
143
141
|
if (type === 'browser') {
|
|
144
142
|
const outPath = join(bundlePath, config.name + '.zip');
|
|
145
143
|
const content = await bundler.build(source.file, outPath);
|
|
144
|
+
monitor.setContent(content);
|
|
146
145
|
Object.assign(schema, { content, filter });
|
|
147
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Generate hash only after the bundled content is created
|
|
149
|
+
* to capture code changes in imported files
|
|
150
|
+
*/
|
|
151
|
+
if (isV2) {
|
|
152
|
+
schema.hash = monitor.hash();
|
|
153
|
+
}
|
|
148
154
|
schemas.push(schema);
|
|
149
155
|
}
|
|
150
156
|
|
|
@@ -236,12 +242,15 @@ export function buildMonitorFromYaml(
|
|
|
236
242
|
config['private_locations'] ||
|
|
237
243
|
config.privateLocations ||
|
|
238
244
|
options.privateLocations;
|
|
239
|
-
|
|
245
|
+
const retestOnFailure =
|
|
246
|
+
config['retest_on_failure'] ?? options.retestOnFailure;
|
|
240
247
|
const alertConfig = parseAlertConfig(config, options.alert);
|
|
248
|
+
|
|
241
249
|
const mon = new Monitor({
|
|
242
250
|
locations: options.locations,
|
|
243
251
|
tags: options.tags,
|
|
244
|
-
...config,
|
|
252
|
+
...normalizeConfig(config),
|
|
253
|
+
retestOnFailure,
|
|
245
254
|
privateLocations,
|
|
246
255
|
schedule: schedule || options.schedule,
|
|
247
256
|
alert: alertConfig,
|
|
@@ -257,6 +266,14 @@ export function buildMonitorFromYaml(
|
|
|
257
266
|
return mon;
|
|
258
267
|
}
|
|
259
268
|
|
|
269
|
+
// Deletes unncessary fields from the lightweight monitor config
|
|
270
|
+
// that is not supported by the Kibana API
|
|
271
|
+
function normalizeConfig(config: MonitorConfig) {
|
|
272
|
+
delete config['private_locations'];
|
|
273
|
+
delete config['retest_on_failure'];
|
|
274
|
+
return config;
|
|
275
|
+
}
|
|
276
|
+
|
|
260
277
|
export const parseAlertConfig = (
|
|
261
278
|
config: MonitorConfig,
|
|
262
279
|
gConfig?: AlertConfig
|
package/src/push/utils.ts
CHANGED
|
@@ -64,7 +64,7 @@ export function generateURL(options: PushOptions, operation: Operation) {
|
|
|
64
64
|
const url = removeTrailingSlash(options.url);
|
|
65
65
|
switch (operation) {
|
|
66
66
|
case 'status':
|
|
67
|
-
return `${url}/s/${options.space}/api/
|
|
67
|
+
return `${url}/s/${options.space}/api/stats`;
|
|
68
68
|
case 'bulk_get':
|
|
69
69
|
case 'bulk_update':
|
|
70
70
|
case 'bulk_delete':
|
|
@@ -81,7 +81,7 @@ export function generateURL(options: PushOptions, operation: Operation) {
|
|
|
81
81
|
/**
|
|
82
82
|
* Bulk API is supported for push monitors only from 8.6.0 and above
|
|
83
83
|
*/
|
|
84
|
-
export function isBulkAPISupported(version:
|
|
84
|
+
export function isBulkAPISupported(version: string) {
|
|
85
85
|
return semver.satisfies(version, '>=8.6.0');
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -103,6 +103,6 @@ export function isLightweightMonitorSupported(
|
|
|
103
103
|
* Lighweight monitor param options are supported only from
|
|
104
104
|
* 8.7.2 and above
|
|
105
105
|
*/
|
|
106
|
-
export function isParamOptionSupported(version:
|
|
106
|
+
export function isParamOptionSupported(version: string) {
|
|
107
107
|
return semver.satisfies(version, '>=8.7.2');
|
|
108
108
|
}
|
package/src/reporters/base.ts
CHANGED
|
@@ -24,14 +24,9 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import SonicBoom from 'sonic-boom';
|
|
27
|
-
import { green, red, cyan } from 'kleur/colors';
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
indent,
|
|
31
|
-
now,
|
|
32
|
-
findPWLogsIndexes,
|
|
33
|
-
rewriteErrorStack,
|
|
34
|
-
} from '../helpers';
|
|
27
|
+
import { green, red, cyan, gray } from 'kleur/colors';
|
|
28
|
+
import { renderError, serializeError } from './reporter-util';
|
|
29
|
+
import { symbols, indent, now } from '../helpers';
|
|
35
30
|
import { Reporter, ReporterOptions } from '.';
|
|
36
31
|
import {
|
|
37
32
|
JourneyEndResult,
|
|
@@ -40,25 +35,6 @@ import {
|
|
|
40
35
|
} from '../common_types';
|
|
41
36
|
import { Journey, Step } from '../dsl';
|
|
42
37
|
|
|
43
|
-
function renderError(error) {
|
|
44
|
-
let output = '';
|
|
45
|
-
const outer = indent('');
|
|
46
|
-
const inner = indent(outer);
|
|
47
|
-
const container = outer + '---\n';
|
|
48
|
-
output += container;
|
|
49
|
-
let stack = error.stack;
|
|
50
|
-
if (stack) {
|
|
51
|
-
output += inner + 'stack: |-\n';
|
|
52
|
-
stack = rewriteErrorStack(stack, findPWLogsIndexes(stack));
|
|
53
|
-
const lines = String(stack).split('\n');
|
|
54
|
-
for (const line of lines) {
|
|
55
|
-
output += inner + ' ' + line + '\n';
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
output += container;
|
|
59
|
-
return red(output);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
38
|
function renderDuration(durationMs) {
|
|
63
39
|
return parseInt(durationMs);
|
|
64
40
|
}
|
|
@@ -66,14 +42,17 @@ function renderDuration(durationMs) {
|
|
|
66
42
|
export default class BaseReporter implements Reporter {
|
|
67
43
|
stream: SonicBoom;
|
|
68
44
|
fd: number;
|
|
45
|
+
dryRun: boolean;
|
|
69
46
|
metrics = {
|
|
70
47
|
succeeded: 0,
|
|
71
48
|
failed: 0,
|
|
72
49
|
skipped: 0,
|
|
50
|
+
registered: 0,
|
|
73
51
|
};
|
|
74
52
|
|
|
75
53
|
constructor(options: ReporterOptions = {}) {
|
|
76
54
|
this.fd = options.fd || process.stdout.fd;
|
|
55
|
+
this.dryRun = options.dryRun ?? false;
|
|
77
56
|
/**
|
|
78
57
|
* minLength is set to 1 byte to make sure we flush the
|
|
79
58
|
* content even if its the last byte on the stream buffer
|
|
@@ -82,17 +61,25 @@ export default class BaseReporter implements Reporter {
|
|
|
82
61
|
this.stream = new SonicBoom({ fd: this.fd, sync: true, minLength: 1 });
|
|
83
62
|
}
|
|
84
63
|
|
|
64
|
+
onJourneyRegister(journey: Journey): void {
|
|
65
|
+
this.write(`\nJourney: ${journey.name}`);
|
|
66
|
+
this.metrics.registered++;
|
|
67
|
+
}
|
|
68
|
+
|
|
85
69
|
onJourneyStart(journey: Journey, {}: JourneyStartResult) {
|
|
86
70
|
this.write(`\nJourney: ${journey.name}`);
|
|
87
71
|
}
|
|
88
72
|
|
|
89
73
|
onStepEnd(_: Journey, step: Step, result: StepEndResult) {
|
|
90
74
|
const { status, end, start, error } = result;
|
|
91
|
-
const message = `${symbols[status]} Step: '${
|
|
92
|
-
|
|
93
|
-
|
|
75
|
+
const message = `${symbols[status]} Step: '${step.name}' ${status} ${gray(
|
|
76
|
+
'(' + renderDuration((end - start) * 1000) + ' ms)'
|
|
77
|
+
)}`;
|
|
94
78
|
this.write(indent(message));
|
|
95
|
-
|
|
79
|
+
if (error) {
|
|
80
|
+
const message = renderError(serializeError(error));
|
|
81
|
+
this.write(indent(message) + '\n');
|
|
82
|
+
}
|
|
96
83
|
this.metrics[status]++;
|
|
97
84
|
}
|
|
98
85
|
|
|
@@ -105,14 +92,20 @@ export default class BaseReporter implements Reporter {
|
|
|
105
92
|
* be executed which means the error happened in one of the hooks
|
|
106
93
|
*/
|
|
107
94
|
if (total === 0 && error) {
|
|
108
|
-
|
|
95
|
+
const message = renderError(serializeError(error));
|
|
96
|
+
this.write(indent(message) + '\n');
|
|
109
97
|
}
|
|
110
98
|
}
|
|
111
99
|
|
|
112
100
|
onEnd() {
|
|
113
|
-
const { failed, succeeded, skipped } = this.metrics;
|
|
101
|
+
const { failed, succeeded, skipped, registered } = this.metrics;
|
|
114
102
|
const total = failed + succeeded + skipped;
|
|
115
103
|
|
|
104
|
+
if (this.dryRun) {
|
|
105
|
+
this.write(`\n${registered} journey(s) registered`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
116
109
|
let message = '\n';
|
|
117
110
|
if (total === 0) {
|
|
118
111
|
message = 'No tests found!';
|
package/src/reporters/index.ts
CHANGED
|
@@ -34,7 +34,11 @@ import {
|
|
|
34
34
|
StepEndResult,
|
|
35
35
|
} from '../common_types';
|
|
36
36
|
|
|
37
|
-
export type ReporterOptions = {
|
|
37
|
+
export type ReporterOptions = {
|
|
38
|
+
fd?: number;
|
|
39
|
+
colors?: boolean;
|
|
40
|
+
dryRun?: boolean;
|
|
41
|
+
};
|
|
38
42
|
export type BuiltInReporterName = 'default' | 'json' | 'junit';
|
|
39
43
|
export type ReporterInstance = new (opts: ReporterOptions) => Reporter;
|
|
40
44
|
export const reporters: {
|
package/src/reporters/json.ts
CHANGED
|
@@ -28,8 +28,8 @@ import { join } from 'path';
|
|
|
28
28
|
import sharp from 'sharp';
|
|
29
29
|
import { createHash } from 'crypto';
|
|
30
30
|
import BaseReporter from './base';
|
|
31
|
+
import { renderError, serializeError, stripAnsiCodes } from './reporter-util';
|
|
31
32
|
import {
|
|
32
|
-
formatError,
|
|
33
33
|
getTimestamp,
|
|
34
34
|
CACHE_PATH,
|
|
35
35
|
totalist,
|
|
@@ -189,6 +189,36 @@ export function formatNetworkFields(network: NetworkInfo) {
|
|
|
189
189
|
return { ecs, payload };
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
/**
|
|
193
|
+
* formatJSONError formats the error in a structured format
|
|
194
|
+
* we restructure the error with code frame and stack trace for test errors
|
|
195
|
+
*/
|
|
196
|
+
function formatJSONError(error: Error | any, type: OutputType) {
|
|
197
|
+
if (error == null) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Do not process browser errors - console.error and unhandled exceptions
|
|
203
|
+
*/
|
|
204
|
+
if (type != null && type === 'journey/browserconsole') {
|
|
205
|
+
return {
|
|
206
|
+
name: error.name,
|
|
207
|
+
message: error.message,
|
|
208
|
+
stack: error.stack,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Do not highlight source for these errors and strip ANSI codes
|
|
214
|
+
*/
|
|
215
|
+
return {
|
|
216
|
+
name: error.name,
|
|
217
|
+
message: stripAnsiCodes(error.message.split('\n')[0]),
|
|
218
|
+
stack: stripAnsiCodes(renderError(serializeError(error, false))),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
192
222
|
function journeyInfo(
|
|
193
223
|
journey: OutputFields['journey'],
|
|
194
224
|
type: OutputFields['type'],
|
|
@@ -317,7 +347,7 @@ export default class JSONReporter extends BaseReporter {
|
|
|
317
347
|
});
|
|
318
348
|
}
|
|
319
349
|
|
|
320
|
-
onJourneyRegister(journey: Journey): void {
|
|
350
|
+
override onJourneyRegister(journey: Journey): void {
|
|
321
351
|
this.writeJSON({
|
|
322
352
|
type: 'journey/register',
|
|
323
353
|
journey,
|
|
@@ -550,7 +580,7 @@ export default class JSONReporter extends BaseReporter {
|
|
|
550
580
|
payload,
|
|
551
581
|
blob,
|
|
552
582
|
blob_mime,
|
|
553
|
-
error:
|
|
583
|
+
error: formatJSONError(error, type),
|
|
554
584
|
url,
|
|
555
585
|
package_version: version,
|
|
556
586
|
});
|
package/src/reporters/junit.ts
CHANGED
|
@@ -31,8 +31,9 @@ import {
|
|
|
31
31
|
StepEndResult,
|
|
32
32
|
} from '../common_types';
|
|
33
33
|
import { Journey, Step } from '../dsl';
|
|
34
|
-
import {
|
|
34
|
+
import { indent, now } from '../helpers';
|
|
35
35
|
import BaseReporter from './base';
|
|
36
|
+
import { serializeError, stripAnsiCodes } from './reporter-util';
|
|
36
37
|
|
|
37
38
|
type XMLEntry = {
|
|
38
39
|
name: string;
|
|
@@ -97,14 +98,13 @@ export default class JUnitReporter extends BaseReporter {
|
|
|
97
98
|
|
|
98
99
|
entry.attributes.tests++;
|
|
99
100
|
if (status === 'failed') {
|
|
100
|
-
const { name, message, stack } = formatError(error);
|
|
101
101
|
caseEntry.children.push({
|
|
102
102
|
name: 'failure',
|
|
103
103
|
attributes: {
|
|
104
|
-
message,
|
|
105
|
-
type: name,
|
|
104
|
+
message: error.message,
|
|
105
|
+
type: error.name,
|
|
106
106
|
},
|
|
107
|
-
text: stack,
|
|
107
|
+
text: stripAnsiCodes(serializeError(error).stack),
|
|
108
108
|
});
|
|
109
109
|
entry.attributes.failures++;
|
|
110
110
|
} else if (status === 'skipped') {
|