@testingbot/cli 1.0.8 → 1.0.9
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/README.md +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/models/maestro_options.d.ts +8 -0
- package/dist/models/maestro_options.d.ts.map +1 -1
- package/dist/models/maestro_options.js +14 -0
- package/dist/providers/maestro.d.ts +18 -0
- package/dist/providers/maestro.d.ts.map +1 -1
- package/dist/providers/maestro.js +181 -75
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,7 @@ testingbot maestro <app> <flows...> [options]
|
|
|
110
110
|
|--------|-------------|
|
|
111
111
|
| `--name <name>` | Test name for dashboard identification |
|
|
112
112
|
| `--build <build>` | Build identifier for grouping test runs |
|
|
113
|
+
| `--groups <names>` | Tag the test session with one or more groups (comma-separated). Groups appear on the test in the TestingBot dashboard |
|
|
113
114
|
| `--include-tags <tags>` | Only run flows with these tags (comma-separated) |
|
|
114
115
|
| `--exclude-tags <tags>` | Exclude flows with these tags (comma-separated) |
|
|
115
116
|
| `-e, --env <KEY=VALUE>` | Environment variable for flows (can be repeated) |
|
|
@@ -172,6 +173,9 @@ testingbot maestro app.apk ./flows --device "Pixel 8" --deviceVersion "14"
|
|
|
172
173
|
# Android app on real device with tags
|
|
173
174
|
testingbot maestro app.apk ./flows --device "Samsung Galaxy S24" --real-device --include-tags "smoke,regression"
|
|
174
175
|
|
|
176
|
+
# Tag the test session with groups (visible in the dashboard)
|
|
177
|
+
testingbot maestro app.apk ./flows --groups "smoke,critical"
|
|
178
|
+
|
|
175
179
|
# With environment variables
|
|
176
180
|
testingbot maestro app.apk ./flows -e API_URL=https://staging.example.com -e API_KEY=secret
|
|
177
181
|
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,QAAA,MAAM,OAAO,SAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAspB9B,eAAe,OAAO,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -190,11 +190,13 @@ program
|
|
|
190
190
|
.option('--platform <platform>', 'Platform name: Android or iOS.', (val) => val)
|
|
191
191
|
.option('--deviceVersion <version>', 'OS version (e.g., "14", "17.2").')
|
|
192
192
|
.option('--real-device', 'Use a real device instead of an emulator/simulator.')
|
|
193
|
+
.option('--google-play', 'Use the Google Play Store-enabled version (Android emulator only).')
|
|
193
194
|
.option('--orientation <orientation>', 'Screen orientation: PORTRAIT or LANDSCAPE.', (val) => val.toUpperCase())
|
|
194
195
|
.option('--device-locale <locale>', 'Device locale (e.g., "en_US", "de_DE").')
|
|
195
196
|
.option('--timezone <timezone>', 'Device timezone (e.g., "America/New_York", "Europe/London").')
|
|
196
197
|
// Test metadata
|
|
197
198
|
.option('--name <name>', 'Name for this Maestro run.')
|
|
199
|
+
.option('--groups <names>', 'Tag the test session with one or more groups (comma-separated).', (val) => val.split(',').map((g) => g.trim()).filter((g) => g.length > 0))
|
|
198
200
|
// Network and geo
|
|
199
201
|
.option('--throttle-network <speed>', 'Network throttling: 4G, 3G, Edge, airplane, or disable.', (val) => val)
|
|
200
202
|
.option('--geo-country-code <code>', 'Geographic IP location (ISO country code, e.g., "US", "DE").')
|
|
@@ -314,7 +316,9 @@ program
|
|
|
314
316
|
ignoreChecksumCheck: args.ignoreChecksumCheck,
|
|
315
317
|
shardSplit: args.shardSplit,
|
|
316
318
|
debug: args.debug,
|
|
319
|
+
googlePlayStore: args.googlePlay,
|
|
317
320
|
configFile: args.config,
|
|
321
|
+
groups: args.groups,
|
|
318
322
|
metadata,
|
|
319
323
|
});
|
|
320
324
|
if (args.debug) {
|
|
@@ -26,6 +26,8 @@ export interface MaestroCapabilities {
|
|
|
26
26
|
'testingbot.geoCountryCode'?: string;
|
|
27
27
|
tunnelIdentifier?: string;
|
|
28
28
|
realDevice?: string;
|
|
29
|
+
groups?: string[];
|
|
30
|
+
googlePlayStore?: boolean;
|
|
29
31
|
}
|
|
30
32
|
export interface MaestroRunOptions {
|
|
31
33
|
includeTags?: string[];
|
|
@@ -64,6 +66,8 @@ export default class MaestroOptions {
|
|
|
64
66
|
private _shardSplit?;
|
|
65
67
|
private _debug;
|
|
66
68
|
private _configFile?;
|
|
69
|
+
private _groups?;
|
|
70
|
+
private _googlePlayStore;
|
|
67
71
|
private _metadata?;
|
|
68
72
|
constructor(app: string, flows: string | string[], device?: string, options?: {
|
|
69
73
|
includeTags?: string[];
|
|
@@ -92,6 +96,8 @@ export default class MaestroOptions {
|
|
|
92
96
|
shardSplit?: number;
|
|
93
97
|
debug?: boolean;
|
|
94
98
|
configFile?: string;
|
|
99
|
+
groups?: string[];
|
|
100
|
+
googlePlayStore?: boolean;
|
|
95
101
|
metadata?: RunMetadata;
|
|
96
102
|
});
|
|
97
103
|
get app(): string;
|
|
@@ -124,6 +130,8 @@ export default class MaestroOptions {
|
|
|
124
130
|
get debug(): boolean;
|
|
125
131
|
get configFile(): string | undefined;
|
|
126
132
|
get metadata(): RunMetadata | undefined;
|
|
133
|
+
get groups(): string[] | undefined;
|
|
134
|
+
get googlePlayStore(): boolean;
|
|
127
135
|
getMaestroOptions(): MaestroRunOptions | undefined;
|
|
128
136
|
getCapabilities(detectedPlatform?: 'Android' | 'iOS'): MaestroCapabilities;
|
|
129
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maestro_options.d.ts","sourceRoot":"","sources":["../../src/models/maestro_options.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,WAAW,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAC5E,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,OAAO,CAAC;AAC9D,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"maestro_options.d.ts","sourceRoot":"","sources":["../../src/models/maestro_options.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,WAAW,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAC5E,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,eAAe,GAAG,OAAO,CAAC;AAC9D,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,CAAC,MAAM,CAAC,SAAS;IAIxB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,YAAY,CAAC,CAAW;IAChC,OAAO,CAAC,YAAY,CAAC,CAAW;IAChC,OAAO,CAAC,aAAa,CAAC,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,IAAI,CAAC,CAAyB;IACtC,OAAO,CAAC,eAAe,CAAC,CAAS;IAEjC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,kBAAkB,CAAC,CAAuB;IAClD,OAAO,CAAC,mBAAmB,CAAC,CAAS;IACrC,OAAO,CAAC,oBAAoB,CAAU;IACtC,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAW;IAC3B,OAAO,CAAC,gBAAgB,CAAU;IAElC,OAAO,CAAC,SAAS,CAAC,CAAc;gBAG9B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,YAAY,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;QACjC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,eAAe,CAAC;QAClC,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,YAAY,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;QACzC,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,EAAE,WAAW,CAAC;KACxB;IA0CH,IAAW,GAAG,IAAI,MAAM,CAEvB;IAED,IAAW,KAAK,IAAI,MAAM,EAAE,CAE3B;IAED,IAAW,MAAM,IAAI,MAAM,GAAG,SAAS,CAEtC;IAED,IAAW,WAAW,IAAI,MAAM,EAAE,GAAG,SAAS,CAE7C;IAED,IAAW,WAAW,IAAI,MAAM,EAAE,GAAG,SAAS,CAE7C;IAED,IAAW,YAAY,IAAI,SAAS,GAAG,KAAK,GAAG,SAAS,CAEvD;IAED,IAAW,OAAO,IAAI,MAAM,GAAG,SAAS,CAEvC;IAED,IAAW,IAAI,IAAI,MAAM,GAAG,SAAS,CAEpC;IAED,IAAW,WAAW,IAAI,WAAW,GAAG,SAAS,CAEhD;IAED,IAAW,MAAM,IAAI,MAAM,GAAG,SAAS,CAEtC;IAED,IAAW,QAAQ,IAAI,MAAM,GAAG,SAAS,CAExC;IAED,IAAW,eAAe,IAAI,eAAe,GAAG,SAAS,CAExD;IAED,IAAW,cAAc,IAAI,MAAM,GAAG,SAAS,CAE9C;IAED,IAAW,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAEnD;IAED,IAAW,cAAc,IAAI,MAAM,GAAG,SAAS,CAE9C;IAED,IAAW,MAAM,IAAI,OAAO,CAE3B;IAED,IAAW,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAEhD;IAED,IAAW,KAAK,IAAI,OAAO,CAE1B;IAED,IAAW,KAAK,IAAI,OAAO,CAE1B;IAED,IAAW,MAAM,IAAI,OAAO,CAE3B;IAED,IAAW,MAAM,IAAI,YAAY,GAAG,SAAS,CAE5C;IAED,IAAW,eAAe,IAAI,MAAM,GAAG,SAAS,CAE/C;IAED,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED,IAAW,iBAAiB,IAAI,oBAAoB,GAAG,SAAS,CAE/D;IAED,IAAW,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAElD;IAED,IAAW,mBAAmB,IAAI,OAAO,CAExC;IAED,IAAW,UAAU,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED,IAAW,KAAK,IAAI,OAAO,CAE1B;IAED,IAAW,UAAU,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED,IAAW,QAAQ,IAAI,WAAW,GAAG,SAAS,CAE7C;IAED,IAAW,MAAM,IAAI,MAAM,EAAE,GAAG,SAAS,CAExC;IAED,IAAW,eAAe,IAAI,OAAO,CAEpC;IAEM,iBAAiB,IAAI,iBAAiB,GAAG,SAAS;IAmBlD,eAAe,CACpB,gBAAgB,CAAC,EAAE,SAAS,GAAG,KAAK,GACnC,mBAAmB;CAqCvB"}
|
|
@@ -35,6 +35,8 @@ class MaestroOptions {
|
|
|
35
35
|
_shardSplit;
|
|
36
36
|
_debug;
|
|
37
37
|
_configFile;
|
|
38
|
+
_groups;
|
|
39
|
+
_googlePlayStore;
|
|
38
40
|
// Metadata
|
|
39
41
|
_metadata;
|
|
40
42
|
constructor(app, flows, device, options) {
|
|
@@ -73,6 +75,8 @@ class MaestroOptions {
|
|
|
73
75
|
this._shardSplit = options?.shardSplit;
|
|
74
76
|
this._debug = options?.debug ?? false;
|
|
75
77
|
this._configFile = options?.configFile;
|
|
78
|
+
this._groups = options?.groups;
|
|
79
|
+
this._googlePlayStore = options?.googlePlayStore ?? false;
|
|
76
80
|
this._metadata = options?.metadata;
|
|
77
81
|
}
|
|
78
82
|
get app() {
|
|
@@ -165,6 +169,12 @@ class MaestroOptions {
|
|
|
165
169
|
get metadata() {
|
|
166
170
|
return this._metadata;
|
|
167
171
|
}
|
|
172
|
+
get groups() {
|
|
173
|
+
return this._groups;
|
|
174
|
+
}
|
|
175
|
+
get googlePlayStore() {
|
|
176
|
+
return this._googlePlayStore;
|
|
177
|
+
}
|
|
168
178
|
getMaestroOptions() {
|
|
169
179
|
const opts = {};
|
|
170
180
|
if (this._includeTags && this._includeTags.length > 0) {
|
|
@@ -217,6 +227,10 @@ class MaestroOptions {
|
|
|
217
227
|
caps.tunnelIdentifier = this._tunnelIdentifier;
|
|
218
228
|
if (this._realDevice)
|
|
219
229
|
caps.realDevice = 'true';
|
|
230
|
+
if (this._groups && this._groups.length > 0)
|
|
231
|
+
caps.groups = this._groups;
|
|
232
|
+
if (this._googlePlayStore)
|
|
233
|
+
caps.googlePlayStore = true;
|
|
220
234
|
return caps;
|
|
221
235
|
}
|
|
222
236
|
}
|
|
@@ -17,6 +17,7 @@ export interface MaestroFlowInfo {
|
|
|
17
17
|
success?: number;
|
|
18
18
|
test_case_id?: number;
|
|
19
19
|
error_messages?: string[];
|
|
20
|
+
assets?: MaestroRunAssets;
|
|
20
21
|
}
|
|
21
22
|
export interface MaestroRunEnvironment {
|
|
22
23
|
device?: string;
|
|
@@ -165,6 +166,20 @@ export default class Maestro extends BaseProvider<MaestroOptions> {
|
|
|
165
166
|
private calculateFlowDuration;
|
|
166
167
|
private getTerminalHeight;
|
|
167
168
|
private getMaxDisplayableFlows;
|
|
169
|
+
private getTerminalWidth;
|
|
170
|
+
/**
|
|
171
|
+
* Returns the maximum length of `flow.name` that keeps the rendered row
|
|
172
|
+
* within the current terminal width, so the row does not visually wrap.
|
|
173
|
+
* Wrapped rows break the `\x1b[NA` cursor-up math used by in-place updates,
|
|
174
|
+
* which is what causes the table to repeat instead of refresh in place
|
|
175
|
+
* (e.g. with --shard-split where the API returns long comma-joined names).
|
|
176
|
+
*
|
|
177
|
+
* Row layout is: " {duration:10} {status:10} {name}[ {error}]" — overhead
|
|
178
|
+
* is 23 plain-width chars before `name`. `extra` reserves room for trailing
|
|
179
|
+
* content like a fail-reason suffix.
|
|
180
|
+
*/
|
|
181
|
+
private getMaxNameLength;
|
|
182
|
+
private truncateForRow;
|
|
168
183
|
private getRemainingSummary;
|
|
169
184
|
private displayFlowsWithLimit;
|
|
170
185
|
private displayFlowsTableHeader;
|
|
@@ -184,6 +199,9 @@ export default class Maestro extends BaseProvider<MaestroOptions> {
|
|
|
184
199
|
private waitForArtifactsSync;
|
|
185
200
|
private downloadFile;
|
|
186
201
|
private generateArtifactZipName;
|
|
202
|
+
private sanitizeFlowDirName;
|
|
203
|
+
private buildFlowDirNames;
|
|
204
|
+
private downloadAssetBundle;
|
|
187
205
|
private downloadArtifacts;
|
|
188
206
|
private createZipFromDirectory;
|
|
189
207
|
private connectToUpdateServer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maestro.d.ts","sourceRoot":"","sources":["../../src/providers/maestro.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"maestro.d.ts","sourceRoot":"","sources":["../../src/providers/maestro.ts"],"names":[],"mappings":"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"}
|
|
@@ -1504,6 +1504,31 @@ class Maestro extends base_provider_1.default {
|
|
|
1504
1504
|
const reservedLines = 6;
|
|
1505
1505
|
return Math.max(5, terminalHeight - reservedLines);
|
|
1506
1506
|
}
|
|
1507
|
+
getTerminalWidth() {
|
|
1508
|
+
return process.stdout.columns || 200;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Returns the maximum length of `flow.name` that keeps the rendered row
|
|
1512
|
+
* within the current terminal width, so the row does not visually wrap.
|
|
1513
|
+
* Wrapped rows break the `\x1b[NA` cursor-up math used by in-place updates,
|
|
1514
|
+
* which is what causes the table to repeat instead of refresh in place
|
|
1515
|
+
* (e.g. with --shard-split where the API returns long comma-joined names).
|
|
1516
|
+
*
|
|
1517
|
+
* Row layout is: " {duration:10} {status:10} {name}[ {error}]" — overhead
|
|
1518
|
+
* is 23 plain-width chars before `name`. `extra` reserves room for trailing
|
|
1519
|
+
* content like a fail-reason suffix.
|
|
1520
|
+
*/
|
|
1521
|
+
getMaxNameLength(extra = 0) {
|
|
1522
|
+
const overhead = 23 + extra + 1;
|
|
1523
|
+
return Math.max(10, this.getTerminalWidth() - overhead);
|
|
1524
|
+
}
|
|
1525
|
+
truncateForRow(name, max) {
|
|
1526
|
+
if (name.length <= max)
|
|
1527
|
+
return name;
|
|
1528
|
+
if (max <= 1)
|
|
1529
|
+
return name.slice(0, max);
|
|
1530
|
+
return name.slice(0, max - 1) + '…';
|
|
1531
|
+
}
|
|
1507
1532
|
getRemainingSummary(flows, displayedCount) {
|
|
1508
1533
|
const remaining = flows.slice(displayedCount);
|
|
1509
1534
|
if (remaining.length === 0) {
|
|
@@ -1578,15 +1603,20 @@ class Maestro extends base_provider_1.default {
|
|
|
1578
1603
|
// Pad based on display text length, add extra for color codes
|
|
1579
1604
|
const statusPadded = statusDisplay.colored +
|
|
1580
1605
|
' '.repeat(Math.max(0, 10 - statusDisplay.text.length));
|
|
1581
|
-
const name = flow.name.padEnd(30);
|
|
1582
1606
|
let linesWritten = 0;
|
|
1583
1607
|
const isFailed = flow.status === 'DONE' && flow.success !== 1;
|
|
1584
1608
|
const errorMessages = flow.error_messages || [];
|
|
1609
|
+
const firstError = hasFailures && isFailed && errorMessages.length > 0
|
|
1610
|
+
? errorMessages[0]
|
|
1611
|
+
: '';
|
|
1612
|
+
const errorReserve = firstError ? firstError.length + 1 : 0;
|
|
1613
|
+
const maxName = this.getMaxNameLength(errorReserve);
|
|
1614
|
+
const name = this.truncateForRow(flow.name, maxName).padEnd(Math.min(30, maxName));
|
|
1585
1615
|
// Build the main row
|
|
1586
1616
|
let row = ` ${duration} ${statusPadded} ${name}`;
|
|
1587
1617
|
// Add first error message on the same line if failed and has errors
|
|
1588
|
-
if (
|
|
1589
|
-
row += ` ${picocolors_1.default.red(
|
|
1618
|
+
if (firstError) {
|
|
1619
|
+
row += ` ${picocolors_1.default.red(firstError)}`;
|
|
1590
1620
|
}
|
|
1591
1621
|
if (isUpdate) {
|
|
1592
1622
|
process.stdout.write(`\r${row}`);
|
|
@@ -1658,14 +1688,15 @@ class Maestro extends base_provider_1.default {
|
|
|
1658
1688
|
}
|
|
1659
1689
|
let linesWritten = 0;
|
|
1660
1690
|
// Redraw displayed flows
|
|
1691
|
+
const maxName = this.getMaxNameLength();
|
|
1661
1692
|
for (const flow of displayFlows) {
|
|
1662
1693
|
const duration = this.calculateFlowDuration(flow).padEnd(10);
|
|
1663
1694
|
const statusDisplay = this.getFlowStatusDisplay(flow);
|
|
1664
1695
|
const statusPadded = statusDisplay.colored +
|
|
1665
1696
|
' '.repeat(Math.max(0, 10 - statusDisplay.text.length));
|
|
1666
|
-
const name = flow.name;
|
|
1697
|
+
const name = this.truncateForRow(flow.name, maxName);
|
|
1667
1698
|
const row = ` ${duration} ${statusPadded} ${name}`;
|
|
1668
|
-
process.stdout.write(`\r\x1b[
|
|
1699
|
+
process.stdout.write(`\r\x1b[2K${row}\n`);
|
|
1669
1700
|
previousFlowStatus.set(flow.id, flow.status);
|
|
1670
1701
|
linesWritten++;
|
|
1671
1702
|
}
|
|
@@ -1841,6 +1872,88 @@ class Maestro extends base_provider_1.default {
|
|
|
1841
1872
|
return fileName;
|
|
1842
1873
|
}
|
|
1843
1874
|
}
|
|
1875
|
+
sanitizeFlowDirName(name) {
|
|
1876
|
+
if (!name)
|
|
1877
|
+
return '';
|
|
1878
|
+
let s = name.replace(/[^A-Za-z0-9._-]+/g, '_');
|
|
1879
|
+
s = s.replace(/_+/g, '_');
|
|
1880
|
+
s = s.replace(/^[_.-]+|[_.-]+$/g, '');
|
|
1881
|
+
if (s.length > 64)
|
|
1882
|
+
s = s.slice(0, 64).replace(/[_.-]+$/, '');
|
|
1883
|
+
return s;
|
|
1884
|
+
}
|
|
1885
|
+
buildFlowDirNames(entries, reserved = new Set()) {
|
|
1886
|
+
const baseNames = new Map();
|
|
1887
|
+
const counts = new Map();
|
|
1888
|
+
for (const r of reserved) {
|
|
1889
|
+
counts.set(r, 1);
|
|
1890
|
+
}
|
|
1891
|
+
for (const { runId, flow } of entries) {
|
|
1892
|
+
const sanitized = this.sanitizeFlowDirName(flow.name);
|
|
1893
|
+
const base = sanitized || `flow_${flow.id}`;
|
|
1894
|
+
baseNames.set(`${runId}:${flow.id}`, base);
|
|
1895
|
+
counts.set(base, (counts.get(base) || 0) + 1);
|
|
1896
|
+
}
|
|
1897
|
+
const result = new Map();
|
|
1898
|
+
for (const { runId, flow } of entries) {
|
|
1899
|
+
const key = `${runId}:${flow.id}`;
|
|
1900
|
+
const base = baseNames.get(key);
|
|
1901
|
+
const collides = (counts.get(base) || 0) > 1;
|
|
1902
|
+
result.set(key, collides ? `${base}_${runId}_${flow.id}` : base);
|
|
1903
|
+
}
|
|
1904
|
+
return result;
|
|
1905
|
+
}
|
|
1906
|
+
async downloadAssetBundle(assets, targetDir) {
|
|
1907
|
+
if (assets.logs && Object.keys(assets.logs).length > 0) {
|
|
1908
|
+
const logsDir = node_path_1.default.join(targetDir, 'logs');
|
|
1909
|
+
await node_fs_1.default.promises.mkdir(logsDir, { recursive: true });
|
|
1910
|
+
for (const [logName, logUrl] of Object.entries(assets.logs)) {
|
|
1911
|
+
const logFileName = `${logName}.txt`;
|
|
1912
|
+
const logPath = node_path_1.default.join(logsDir, logFileName);
|
|
1913
|
+
try {
|
|
1914
|
+
await this.downloadFile(logUrl, logPath);
|
|
1915
|
+
if (!this.options.quiet) {
|
|
1916
|
+
logger_1.default.info(` Downloaded log: ${logFileName}`);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
catch (error) {
|
|
1920
|
+
logger_1.default.error(` Failed to download log ${logFileName}: ${error instanceof Error ? error.message : error}`);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
if (assets.video && typeof assets.video === 'string') {
|
|
1925
|
+
const videoDir = node_path_1.default.join(targetDir, 'video');
|
|
1926
|
+
await node_fs_1.default.promises.mkdir(videoDir, { recursive: true });
|
|
1927
|
+
const videoPath = node_path_1.default.join(videoDir, 'video.mp4');
|
|
1928
|
+
try {
|
|
1929
|
+
await this.downloadFile(assets.video, videoPath);
|
|
1930
|
+
if (!this.options.quiet) {
|
|
1931
|
+
logger_1.default.info(` Downloaded video: video.mp4`);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
catch (error) {
|
|
1935
|
+
logger_1.default.error(` Failed to download video: ${error instanceof Error ? error.message : error}`);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
if (assets.screenshots && assets.screenshots.length > 0) {
|
|
1939
|
+
const screenshotsDir = node_path_1.default.join(targetDir, 'screenshots');
|
|
1940
|
+
await node_fs_1.default.promises.mkdir(screenshotsDir, { recursive: true });
|
|
1941
|
+
for (let i = 0; i < assets.screenshots.length; i++) {
|
|
1942
|
+
const screenshotUrl = assets.screenshots[i];
|
|
1943
|
+
const screenshotFileName = `screenshot_${i}.png`;
|
|
1944
|
+
const screenshotPath = node_path_1.default.join(screenshotsDir, screenshotFileName);
|
|
1945
|
+
try {
|
|
1946
|
+
await this.downloadFile(screenshotUrl, screenshotPath);
|
|
1947
|
+
if (!this.options.quiet) {
|
|
1948
|
+
logger_1.default.info(` Downloaded screenshot: ${screenshotFileName}`);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
catch (error) {
|
|
1952
|
+
logger_1.default.error(` Failed to download screenshot ${screenshotFileName}: ${error instanceof Error ? error.message : error}`);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1844
1957
|
async downloadArtifacts(runs) {
|
|
1845
1958
|
if (!this.options.downloadArtifacts)
|
|
1846
1959
|
return;
|
|
@@ -1871,92 +1984,85 @@ class Maestro extends base_provider_1.default {
|
|
|
1871
1984
|
const outputDir = this.options.artifactsOutputDir || process.cwd();
|
|
1872
1985
|
const tempDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'testingbot-maestro-artifacts-'));
|
|
1873
1986
|
try {
|
|
1987
|
+
const runDetailsList = [];
|
|
1874
1988
|
for (const run of runsToDownload) {
|
|
1989
|
+
if (!this.options.quiet) {
|
|
1990
|
+
logger_1.default.info(` Waiting for artifacts sync for run ${run.id}...`);
|
|
1991
|
+
}
|
|
1875
1992
|
try {
|
|
1993
|
+
const details = await this.waitForArtifactsSync(run.id);
|
|
1994
|
+
runDetailsList.push({ run, details });
|
|
1995
|
+
}
|
|
1996
|
+
catch (error) {
|
|
1997
|
+
logger_1.default.error(`Failed to download artifacts for run ${run.id}: ${error instanceof Error ? error.message : error}`);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
const multiRun = runDetailsList.length > 1;
|
|
2001
|
+
const runReportName = (runId) => multiRun ? `report_${runId}.xml` : 'report.xml';
|
|
2002
|
+
const runAssetsDirName = (runId) => multiRun ? `run_${runId}` : '';
|
|
2003
|
+
const reservedNames = new Set();
|
|
2004
|
+
for (const { run, details } of runDetailsList) {
|
|
2005
|
+
if (details.report)
|
|
2006
|
+
reservedNames.add(runReportName(run.id));
|
|
2007
|
+
if (details.assets) {
|
|
2008
|
+
const dir = runAssetsDirName(run.id);
|
|
2009
|
+
if (dir)
|
|
2010
|
+
reservedNames.add(dir);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
const flowEntries = runDetailsList.flatMap(({ run, details }) => (details.flows || [])
|
|
2014
|
+
.filter((flow) => flow.assets)
|
|
2015
|
+
.map((flow) => ({ runId: run.id, flow })));
|
|
2016
|
+
const flowDirNames = this.buildFlowDirNames(flowEntries, reservedNames);
|
|
2017
|
+
for (const { run, details } of runDetailsList) {
|
|
2018
|
+
const flowsWithAssets = (details.flows || []).filter((flow) => flow.assets);
|
|
2019
|
+
if (!details.assets && flowsWithAssets.length === 0) {
|
|
1876
2020
|
if (!this.options.quiet) {
|
|
1877
|
-
logger_1.default.info(`
|
|
1878
|
-
}
|
|
1879
|
-
const runDetails = await this.waitForArtifactsSync(run.id);
|
|
1880
|
-
if (!runDetails.assets) {
|
|
1881
|
-
if (!this.options.quiet) {
|
|
1882
|
-
logger_1.default.info(` No artifacts available for run ${run.id}`);
|
|
1883
|
-
}
|
|
1884
|
-
continue;
|
|
2021
|
+
logger_1.default.info(` No artifacts available for run ${run.id}`);
|
|
1885
2022
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
const logFileName = `${logName}.txt`;
|
|
1894
|
-
const logPath = node_path_1.default.join(logsDir, logFileName);
|
|
1895
|
-
try {
|
|
1896
|
-
await this.downloadFile(logUrl, logPath);
|
|
1897
|
-
if (!this.options.quiet) {
|
|
1898
|
-
logger_1.default.info(` Downloaded log: ${logFileName}`);
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
catch (error) {
|
|
1902
|
-
logger_1.default.error(` Failed to download log ${logFileName}: ${error instanceof Error ? error.message : error}`);
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
2023
|
+
continue;
|
|
2024
|
+
}
|
|
2025
|
+
if (details.assets) {
|
|
2026
|
+
const dirName = runAssetsDirName(run.id);
|
|
2027
|
+
const targetDir = dirName ? node_path_1.default.join(tempDir, dirName) : tempDir;
|
|
2028
|
+
if (dirName) {
|
|
2029
|
+
await node_fs_1.default.promises.mkdir(targetDir, { recursive: true });
|
|
1905
2030
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2031
|
+
await this.downloadAssetBundle(details.assets, targetDir);
|
|
2032
|
+
}
|
|
2033
|
+
for (const flow of flowsWithAssets) {
|
|
2034
|
+
const flowDirName = flowDirNames.get(`${run.id}:${flow.id}`);
|
|
2035
|
+
const flowDir = node_path_1.default.join(tempDir, flowDirName);
|
|
2036
|
+
await node_fs_1.default.promises.mkdir(flowDir, { recursive: true });
|
|
2037
|
+
await this.downloadAssetBundle(flow.assets, flowDir);
|
|
2038
|
+
if (flow.report) {
|
|
2039
|
+
const flowReportPath = node_path_1.default.join(flowDir, 'report.xml');
|
|
1913
2040
|
try {
|
|
1914
|
-
await
|
|
2041
|
+
await node_fs_1.default.promises.writeFile(flowReportPath, flow.report, 'utf-8');
|
|
1915
2042
|
if (!this.options.quiet) {
|
|
1916
|
-
logger_1.default.info(`
|
|
2043
|
+
logger_1.default.info(` Saved ${flowDirName}/report.xml`);
|
|
1917
2044
|
}
|
|
1918
2045
|
}
|
|
1919
2046
|
catch (error) {
|
|
1920
|
-
logger_1.default.error(` Failed to
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
if (runDetails.assets.screenshots &&
|
|
1924
|
-
runDetails.assets.screenshots.length > 0) {
|
|
1925
|
-
const screenshotsDir = node_path_1.default.join(runDir, 'screenshots');
|
|
1926
|
-
await node_fs_1.default.promises.mkdir(screenshotsDir, { recursive: true });
|
|
1927
|
-
for (let i = 0; i < runDetails.assets.screenshots.length; i++) {
|
|
1928
|
-
const screenshotUrl = runDetails.assets.screenshots[i];
|
|
1929
|
-
const screenshotFileName = `screenshot_${i}.png`;
|
|
1930
|
-
const screenshotPath = node_path_1.default.join(screenshotsDir, screenshotFileName);
|
|
1931
|
-
try {
|
|
1932
|
-
await this.downloadFile(screenshotUrl, screenshotPath);
|
|
1933
|
-
if (!this.options.quiet) {
|
|
1934
|
-
logger_1.default.info(` Downloaded screenshot: ${screenshotFileName}`);
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
catch (error) {
|
|
1938
|
-
logger_1.default.error(` Failed to download screenshot ${screenshotFileName}: ${error instanceof Error ? error.message : error}`);
|
|
1939
|
-
}
|
|
2047
|
+
logger_1.default.error(` Failed to save report.xml for ${flowDirName}: ${error instanceof Error ? error.message : error}`);
|
|
1940
2048
|
}
|
|
1941
2049
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
catch (error) {
|
|
1951
|
-
logger_1.default.error(` Failed to save report.xml: ${error instanceof Error ? error.message : error}`);
|
|
2050
|
+
}
|
|
2051
|
+
if (details.report) {
|
|
2052
|
+
const reportName = runReportName(run.id);
|
|
2053
|
+
const reportPath = node_path_1.default.join(tempDir, reportName);
|
|
2054
|
+
try {
|
|
2055
|
+
await node_fs_1.default.promises.writeFile(reportPath, details.report, 'utf-8');
|
|
2056
|
+
if (!this.options.quiet) {
|
|
2057
|
+
logger_1.default.info(` Saved ${reportName}`);
|
|
1952
2058
|
}
|
|
1953
2059
|
}
|
|
1954
|
-
|
|
1955
|
-
logger_1.default.
|
|
2060
|
+
catch (error) {
|
|
2061
|
+
logger_1.default.error(` Failed to save ${reportName}: ${error instanceof Error ? error.message : error}`);
|
|
1956
2062
|
}
|
|
1957
2063
|
}
|
|
1958
|
-
|
|
1959
|
-
logger_1.default.
|
|
2064
|
+
if (!this.options.quiet) {
|
|
2065
|
+
logger_1.default.info(` Artifacts for run ${run.id} downloaded`);
|
|
1960
2066
|
}
|
|
1961
2067
|
}
|
|
1962
2068
|
const zipFileName = await this.generateArtifactZipName(outputDir);
|