@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 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;AA2oB9B,eAAe,OAAO,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":"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"}
1
+ {"version":3,"file":"maestro_options.d.ts","sourceRoot":"","sources":["../../src/models/maestro_options.ts"],"names":[],"mappings":"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"}
@@ -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":"AAAA,OAAO,cAAiC,MAAM,2BAA2B,CAAC;AAE1E,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAahD,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAO3C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAChD,YAAY,EAAE;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,cAAc,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,YAAY,CAAC,cAAc,CAAC;IAC/D,SAAS,CAAC,QAAQ,CAAC,GAAG,wDAAwD;IAE9E,OAAO,CAAC,gBAAgB,CAA4C;IACpE,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,wBAAwB,CAAK;gBAElB,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc;IAIpE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAM9C;YAEY,QAAQ;IA4EtB;;OAEG;YACW,cAAc;IAOf,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;YA+I5B,SAAS;IA4EvB;;OAEG;YACW,YAAY;YAwBZ,gBAAgB;IAkC9B;;;;;;OAMG;IACG,YAAY,IAAI,OAAO,CAAC;QAC5B,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,GAAG,IAAI,CAAC;YAsJK,WAAW;IA4CzB;;;;OAIG;YACW,sBAAsB;YAwBtB,aAAa;YAmEb,oBAAoB;IA0ClC;;OAEG;IACH,OAAO,CAAC,YAAY;YAWN,YAAY;IAoB1B;;;;OAIG;YACW,cAAc;YAgCd,iBAAiB;IA6D/B;;OAEG;IACH,OAAO,CAAC,aAAa;IA6BrB;;OAEG;YACW,gBAAgB;IAsC9B;;OAEG;YACW,qBAAqB;IAgMnC;;;;OAIG;IACU,qBAAqB,CAChC,SAAS,EAAE,MAAM,EAAE,EACnB,gBAAgB,EAAE,MAAM,EAAE,GACzB,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAmClC;;OAEG;YACW,kBAAkB;IAoKhC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,gBAAgB;YAmDV,cAAc;IA+B5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;YAsBhB,QAAQ;YA6DR,SAAS;YAkCT,iBAAiB;IAsM/B,OAAO,CAAC,gBAAgB;IAwCxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,oBAAoB;IA6B5B,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,mBAAmB;IA6C3B,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,cAAc;IA6CtB,OAAO,CAAC,iBAAiB;IA0BzB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,iBAAiB;IAOzB,SAAS,CAAC,cAAc,IAAI,IAAI;IAIhC,OAAO,CAAC,kBAAkB;YA2CZ,YAAY;YA2EZ,aAAa;YAiCb,oBAAoB;YAoBpB,YAAY;YA0DZ,uBAAuB;YAqBvB,iBAAiB;YAwLjB,sBAAsB;IAkBpC,OAAO,CAAC,qBAAqB;IA6C7B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,kBAAkB;CAa3B"}
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 (hasFailures && isFailed && errorMessages.length > 0) {
1589
- row += ` ${picocolors_1.default.red(errorMessages[0])}`;
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[K${row}\n`);
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(` Waiting for artifacts sync for run ${run.id}...`);
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
- const runDir = node_path_1.default.join(tempDir, `run_${run.id}`);
1887
- await node_fs_1.default.promises.mkdir(runDir, { recursive: true });
1888
- if (runDetails.assets.logs &&
1889
- Object.keys(runDetails.assets.logs).length > 0) {
1890
- const logsDir = node_path_1.default.join(runDir, 'logs');
1891
- await node_fs_1.default.promises.mkdir(logsDir, { recursive: true });
1892
- for (const [logName, logUrl] of Object.entries(runDetails.assets.logs)) {
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
- if (runDetails.assets.video &&
1907
- typeof runDetails.assets.video === 'string') {
1908
- const videoDir = node_path_1.default.join(runDir, 'video');
1909
- await node_fs_1.default.promises.mkdir(videoDir, { recursive: true });
1910
- const videoUrl = runDetails.assets.video;
1911
- const videoFileName = 'video.mp4';
1912
- const videoPath = node_path_1.default.join(videoDir, videoFileName);
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 this.downloadFile(videoUrl, videoPath);
2041
+ await node_fs_1.default.promises.writeFile(flowReportPath, flow.report, 'utf-8');
1915
2042
  if (!this.options.quiet) {
1916
- logger_1.default.info(` Downloaded video: ${videoFileName}`);
2043
+ logger_1.default.info(` Saved ${flowDirName}/report.xml`);
1917
2044
  }
1918
2045
  }
1919
2046
  catch (error) {
1920
- logger_1.default.error(` Failed to download video: ${error instanceof Error ? error.message : error}`);
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
- if (runDetails.report) {
1943
- const reportPath = node_path_1.default.join(runDir, 'report.xml');
1944
- try {
1945
- await node_fs_1.default.promises.writeFile(reportPath, runDetails.report, 'utf-8');
1946
- if (!this.options.quiet) {
1947
- logger_1.default.info(` Saved report.xml`);
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
- if (!this.options.quiet) {
1955
- logger_1.default.info(` Artifacts for run ${run.id} downloaded`);
2060
+ catch (error) {
2061
+ logger_1.default.error(` Failed to save ${reportName}: ${error instanceof Error ? error.message : error}`);
1956
2062
  }
1957
2063
  }
1958
- catch (error) {
1959
- logger_1.default.error(`Failed to download artifacts for run ${run.id}: ${error instanceof Error ? error.message : error}`);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testingbot/cli",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "CLI tool to run Espresso, XCUITest and Maestro tests on TestingBot's cloud infrastructure",
5
5
  "main": "dist/index.js",
6
6
  "bin": {