@testingbot/cli 1.0.3 → 1.0.4
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/providers/espresso.d.ts.map +1 -1
- package/dist/providers/espresso.js +2 -0
- package/dist/providers/maestro.d.ts +8 -0
- package/dist/providers/maestro.d.ts.map +1 -1
- package/dist/providers/maestro.js +105 -44
- package/dist/providers/xcuitest.d.ts.map +1 -1
- package/dist/providers/xcuitest.js +2 -0
- package/dist/upload.d.ts +11 -0
- package/dist/upload.d.ts.map +1 -1
- package/dist/upload.js +57 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +51 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"espresso.d.ts","sourceRoot":"","sources":["../../src/providers/espresso.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAEzD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAOhD,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAE3C,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,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,sBAAsB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,YAAY,CAAC,eAAe,CAAC;IACjE,SAAS,CAAC,QAAQ,CAAC,GAAG,yDACkC;IAExD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,SAAS,CAAuB;gBAErB,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe;YAIvD,QAAQ;IAuCT,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC;YA+D7B,SAAS;
|
|
1
|
+
{"version":3,"file":"espresso.d.ts","sourceRoot":"","sources":["../../src/providers/espresso.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAEzD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAOhD,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAE3C,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,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,sBAAsB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,YAAY,CAAC,eAAe,CAAC;IACjE,SAAS,CAAC,QAAQ,CAAC,GAAG,yDACkC;IAExD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,SAAS,CAAuB;gBAErB,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe;YAIvD,QAAQ;IAuCT,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC;YA+D7B,SAAS;YAcT,aAAa;YAab,QAAQ;YA0DR,SAAS;YA4BT,iBAAiB;IAwE/B,OAAO,CAAC,gBAAgB;IAmCxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,aAAa;YAkBP,YAAY;IAwD1B,OAAO,CAAC,qBAAqB;IAqC7B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,mBAAmB;CAa5B"}
|
|
@@ -104,6 +104,7 @@ class Espresso extends base_provider_1.default {
|
|
|
104
104
|
credentials: this.credentials,
|
|
105
105
|
contentType: 'application/vnd.android.package-archive',
|
|
106
106
|
showProgress: !this.options.quiet,
|
|
107
|
+
validateZipFormat: true,
|
|
107
108
|
});
|
|
108
109
|
this.appId = result.id;
|
|
109
110
|
return true;
|
|
@@ -115,6 +116,7 @@ class Espresso extends base_provider_1.default {
|
|
|
115
116
|
credentials: this.credentials,
|
|
116
117
|
contentType: 'application/vnd.android.package-archive',
|
|
117
118
|
showProgress: !this.options.quiet,
|
|
119
|
+
validateZipFormat: true,
|
|
118
120
|
});
|
|
119
121
|
return true;
|
|
120
122
|
}
|
|
@@ -76,10 +76,18 @@ export default class Maestro extends BaseProvider<MaestroOptions> {
|
|
|
76
76
|
private detectPlatform;
|
|
77
77
|
run(): Promise<MaestroResult>;
|
|
78
78
|
private uploadApp;
|
|
79
|
+
/**
|
|
80
|
+
* Zip a .app bundle directory into a temporary zip file
|
|
81
|
+
*/
|
|
82
|
+
private zipAppBundle;
|
|
79
83
|
private checkAppChecksum;
|
|
80
84
|
private uploadFlows;
|
|
81
85
|
private discoverFlows;
|
|
82
86
|
private discoverDependencies;
|
|
87
|
+
/**
|
|
88
|
+
* Check if a file path is a Maestro config file (config.yaml or config.yml)
|
|
89
|
+
*/
|
|
90
|
+
private isConfigFile;
|
|
83
91
|
/**
|
|
84
92
|
* Check if a string looks like a file path (relative path with extension)
|
|
85
93
|
*/
|
|
@@ -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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"}
|
|
@@ -172,44 +172,84 @@ class Maestro extends base_provider_1.default {
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
async uploadApp() {
|
|
175
|
-
|
|
175
|
+
let appPath = this.options.app;
|
|
176
176
|
const ext = node_path_1.default.extname(appPath).toLowerCase();
|
|
177
|
-
let
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
contentType = 'application/octet-stream';
|
|
183
|
-
}
|
|
184
|
-
else if (ext === '.zip') {
|
|
185
|
-
contentType = 'application/zip';
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
contentType = 'application/octet-stream';
|
|
189
|
-
}
|
|
190
|
-
if (!this.options.ignoreChecksumCheck) {
|
|
191
|
-
const checksum = await this.upload.calculateChecksum(appPath);
|
|
192
|
-
const existingApp = await this.checkAppChecksum(checksum);
|
|
193
|
-
if (existingApp) {
|
|
194
|
-
this.appId = existingApp.id;
|
|
177
|
+
let tempZipPath = null;
|
|
178
|
+
// If .app bundle (directory), zip it first
|
|
179
|
+
if (ext === '.app') {
|
|
180
|
+
const stat = await node_fs_1.default.promises.stat(appPath);
|
|
181
|
+
if (stat.isDirectory()) {
|
|
195
182
|
if (!this.options.quiet) {
|
|
196
|
-
logger_1.default.info('
|
|
183
|
+
logger_1.default.info('Zipping .app bundle for upload');
|
|
197
184
|
}
|
|
198
|
-
|
|
185
|
+
tempZipPath = await this.zipAppBundle(appPath);
|
|
186
|
+
appPath = tempZipPath;
|
|
199
187
|
}
|
|
200
188
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
189
|
+
try {
|
|
190
|
+
let contentType;
|
|
191
|
+
if (ext === '.apk') {
|
|
192
|
+
contentType = 'application/vnd.android.package-archive';
|
|
193
|
+
}
|
|
194
|
+
else if (ext === '.ipa') {
|
|
195
|
+
contentType = 'application/octet-stream';
|
|
196
|
+
}
|
|
197
|
+
else if (ext === '.zip' || ext === '.app') {
|
|
198
|
+
// .app bundles are zipped, so use application/zip
|
|
199
|
+
contentType = 'application/zip';
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
contentType = 'application/octet-stream';
|
|
203
|
+
}
|
|
204
|
+
if (!this.options.ignoreChecksumCheck) {
|
|
205
|
+
const checksum = await this.upload.calculateChecksum(appPath);
|
|
206
|
+
const existingApp = await this.checkAppChecksum(checksum);
|
|
207
|
+
if (existingApp) {
|
|
208
|
+
this.appId = existingApp.id;
|
|
209
|
+
if (!this.options.quiet) {
|
|
210
|
+
logger_1.default.info(' App already uploaded, skipping upload');
|
|
211
|
+
}
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (!this.options.quiet) {
|
|
216
|
+
logger_1.default.info('Uploading Maestro App');
|
|
217
|
+
}
|
|
218
|
+
const result = await this.upload.upload({
|
|
219
|
+
filePath: appPath,
|
|
220
|
+
url: `${this.URL}/app`,
|
|
221
|
+
credentials: this.credentials,
|
|
222
|
+
contentType,
|
|
223
|
+
showProgress: !this.options.quiet,
|
|
224
|
+
validateZipFormat: true,
|
|
225
|
+
});
|
|
226
|
+
this.appId = result.id;
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
// Clean up temporary zip file
|
|
231
|
+
if (tempZipPath) {
|
|
232
|
+
await node_fs_1.default.promises.unlink(tempZipPath).catch(() => { });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Zip a .app bundle directory into a temporary zip file
|
|
238
|
+
*/
|
|
239
|
+
async zipAppBundle(appPath) {
|
|
240
|
+
const appName = node_path_1.default.basename(appPath);
|
|
241
|
+
const tmpDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'testingbot-app-'));
|
|
242
|
+
const zipPath = node_path_1.default.join(tmpDir, `${appName}.zip`);
|
|
243
|
+
return new Promise((resolve, reject) => {
|
|
244
|
+
const output = node_fs_1.default.createWriteStream(zipPath);
|
|
245
|
+
const archive = (0, archiver_1.default)('zip', { zlib: { level: 9 } });
|
|
246
|
+
output.on('close', () => resolve(zipPath));
|
|
247
|
+
archive.on('error', (err) => reject(err));
|
|
248
|
+
archive.pipe(output);
|
|
249
|
+
// Add the .app directory with its name preserved
|
|
250
|
+
archive.directory(appPath, appName);
|
|
251
|
+
archive.finalize();
|
|
210
252
|
});
|
|
211
|
-
this.appId = result.id;
|
|
212
|
-
return true;
|
|
213
253
|
}
|
|
214
254
|
async checkAppChecksum(checksum) {
|
|
215
255
|
try {
|
|
@@ -252,6 +292,7 @@ class Maestro extends base_provider_1.default {
|
|
|
252
292
|
credentials: this.credentials,
|
|
253
293
|
contentType: 'application/zip',
|
|
254
294
|
showProgress: !this.options.quiet,
|
|
295
|
+
validateZipFormat: true,
|
|
255
296
|
});
|
|
256
297
|
return true;
|
|
257
298
|
}
|
|
@@ -303,6 +344,14 @@ class Maestro extends base_provider_1.default {
|
|
|
303
344
|
const baseDir = baseDirs.length === 1 ? baseDirs[0] : undefined;
|
|
304
345
|
if (!this.options.quiet) {
|
|
305
346
|
this.logIncludedFiles(allFlowFiles, baseDir);
|
|
347
|
+
// Show info about potential slow execution on specific real devices
|
|
348
|
+
utils_1.default.showRealDeviceFlowsInfo({
|
|
349
|
+
realDevice: this.options.realDevice,
|
|
350
|
+
device: this.options.device,
|
|
351
|
+
version: this.options.version,
|
|
352
|
+
flowCount: allFlowFiles.filter((f) => f.endsWith('.yaml') || f.endsWith('.yml')).filter((f) => !this.isConfigFile(f)).length,
|
|
353
|
+
shardSplit: this.options.shardSplit,
|
|
354
|
+
});
|
|
306
355
|
}
|
|
307
356
|
// Check for missing file references and warn the user
|
|
308
357
|
const missingReferences = await this.findMissingReferences(allFlowFiles, allFlowFiles, baseDir);
|
|
@@ -332,15 +381,20 @@ class Maestro extends base_provider_1.default {
|
|
|
332
381
|
withFileTypes: true,
|
|
333
382
|
});
|
|
334
383
|
const flowFiles = [];
|
|
335
|
-
|
|
384
|
+
let configPath = null;
|
|
336
385
|
let config = null;
|
|
337
|
-
// Check for config.yaml
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
386
|
+
// Check for config.yaml or config.yml
|
|
387
|
+
for (const configName of ['config.yaml', 'config.yml']) {
|
|
388
|
+
const candidatePath = node_path_1.default.join(directory, configName);
|
|
389
|
+
try {
|
|
390
|
+
const configContent = await node_fs_1.default.promises.readFile(candidatePath, 'utf-8');
|
|
391
|
+
config = yaml.load(configContent);
|
|
392
|
+
configPath = candidatePath;
|
|
393
|
+
break; // Use the first config file found
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// Config file doesn't exist, try next
|
|
397
|
+
}
|
|
344
398
|
}
|
|
345
399
|
// If config specifies flows, use those
|
|
346
400
|
if (config?.flows && config.flows.length > 0) {
|
|
@@ -355,7 +409,7 @@ class Maestro extends base_provider_1.default {
|
|
|
355
409
|
if (entry.isFile()) {
|
|
356
410
|
const ext = node_path_1.default.extname(entry.name).toLowerCase();
|
|
357
411
|
if ((ext === '.yaml' || ext === '.yml') &&
|
|
358
|
-
entry.name
|
|
412
|
+
!this.isConfigFile(entry.name)) {
|
|
359
413
|
flowFiles.push(node_path_1.default.join(directory, entry.name));
|
|
360
414
|
}
|
|
361
415
|
}
|
|
@@ -367,8 +421,8 @@ class Maestro extends base_provider_1.default {
|
|
|
367
421
|
const dependencies = await this.discoverDependencies(flowFile, directory);
|
|
368
422
|
dependencies.forEach((dep) => allFiles.add(dep));
|
|
369
423
|
}
|
|
370
|
-
// Include config
|
|
371
|
-
if (
|
|
424
|
+
// Include config file if it exists
|
|
425
|
+
if (configPath) {
|
|
372
426
|
allFiles.add(configPath);
|
|
373
427
|
}
|
|
374
428
|
return Array.from(allFiles);
|
|
@@ -400,6 +454,13 @@ class Maestro extends base_provider_1.default {
|
|
|
400
454
|
}
|
|
401
455
|
return dependencies;
|
|
402
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* Check if a file path is a Maestro config file (config.yaml or config.yml)
|
|
459
|
+
*/
|
|
460
|
+
isConfigFile(filePath) {
|
|
461
|
+
const basename = node_path_1.default.basename(filePath);
|
|
462
|
+
return basename === 'config.yaml' || basename === 'config.yml';
|
|
463
|
+
}
|
|
403
464
|
/**
|
|
404
465
|
* Check if a string looks like a file path (relative path with extension)
|
|
405
466
|
*/
|
|
@@ -770,7 +831,7 @@ class Maestro extends base_provider_1.default {
|
|
|
770
831
|
for (const filePath of relativePaths) {
|
|
771
832
|
const ext = node_path_1.default.extname(filePath).toLowerCase();
|
|
772
833
|
if (ext === '.yaml' || ext === '.yml') {
|
|
773
|
-
if (
|
|
834
|
+
if (this.isConfigFile(filePath)) {
|
|
774
835
|
groups['Config files'].push(filePath);
|
|
775
836
|
}
|
|
776
837
|
else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xcuitest.d.ts","sourceRoot":"","sources":["../../src/providers/xcuitest.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAEzD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAOhD,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAE3C,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,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,sBAAsB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,YAAY,CAAC,eAAe,CAAC;IACjE,SAAS,CAAC,QAAQ,CAAC,GAAG,yDACkC;IAExD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,SAAS,CAAuB;gBAErB,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe;YAIvD,QAAQ;IAuCT,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC;YA+D7B,SAAS;
|
|
1
|
+
{"version":3,"file":"xcuitest.d.ts","sourceRoot":"","sources":["../../src/providers/xcuitest.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,4BAA4B,CAAC;AAEzD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAOhD,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAE3C,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,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,sBAAsB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,YAAY,CAAC,eAAe,CAAC;IACjE,SAAS,CAAC,QAAQ,CAAC,GAAG,yDACkC;IAExD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,SAAS,CAAuB;gBAErB,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe;YAIvD,QAAQ;IAuCT,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC;YA+D7B,SAAS;YAcT,aAAa;YAab,QAAQ;YA0DR,SAAS;YA4BT,iBAAiB;IAwE/B,OAAO,CAAC,gBAAgB;IAmCxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,aAAa;YAkBP,YAAY;IA2D1B,OAAO,CAAC,qBAAqB;IAqC7B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,mBAAmB;CAa5B"}
|
|
@@ -104,6 +104,7 @@ class XCUITest extends base_provider_1.default {
|
|
|
104
104
|
credentials: this.credentials,
|
|
105
105
|
contentType: 'application/octet-stream',
|
|
106
106
|
showProgress: !this.options.quiet,
|
|
107
|
+
validateZipFormat: true,
|
|
107
108
|
});
|
|
108
109
|
this.appId = result.id;
|
|
109
110
|
return true;
|
|
@@ -115,6 +116,7 @@ class XCUITest extends base_provider_1.default {
|
|
|
115
116
|
credentials: this.credentials,
|
|
116
117
|
contentType: 'application/zip',
|
|
117
118
|
showProgress: !this.options.quiet,
|
|
119
|
+
validateZipFormat: true,
|
|
118
120
|
});
|
|
119
121
|
return true;
|
|
120
122
|
}
|
package/dist/upload.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface UploadOptions {
|
|
|
7
7
|
contentType: ContentType;
|
|
8
8
|
showProgress?: boolean;
|
|
9
9
|
checksum?: string;
|
|
10
|
+
/** Validate that the file is a valid zip-based archive (APK, IPA, ZIP) */
|
|
11
|
+
validateZipFormat?: boolean;
|
|
10
12
|
}
|
|
11
13
|
export interface UploadResult {
|
|
12
14
|
id: number;
|
|
@@ -19,6 +21,15 @@ export default class Upload {
|
|
|
19
21
|
*/
|
|
20
22
|
private formatFileSize;
|
|
21
23
|
private validateFile;
|
|
24
|
+
/**
|
|
25
|
+
* Validate that the file is a valid zip-based archive.
|
|
26
|
+
* ZIP, APK, IPA files all start with the ZIP magic bytes (PK\x03\x04).
|
|
27
|
+
*/
|
|
28
|
+
private validateZipFormat;
|
|
29
|
+
/**
|
|
30
|
+
* Extract error message from server response data
|
|
31
|
+
*/
|
|
32
|
+
private extractErrorMessage;
|
|
22
33
|
/**
|
|
23
34
|
* Calculate MD5 checksum of a file, returning base64-encoded result
|
|
24
35
|
* This matches ActiveStorage's checksum format
|
package/dist/upload.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAMA,OAAO,WAAW,MAAM,sBAAsB,CAAC;AAK/C,MAAM,MAAM,WAAW,GACnB,yCAAyC,GACzC,0BAA0B,GAC1B,iBAAiB,CAAC;AAEtB,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAMA,OAAO,WAAW,MAAM,sBAAsB,CAAC;AAK/C,MAAM,MAAM,WAAW,GACnB,yCAAyC,GACzC,0BAA0B,GAC1B,iBAAiB,CAAC;AAEtB,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,CAAC,OAAO,OAAO,MAAM;IACZ,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAyGlE,OAAO,CAAC,eAAe;IAmBvB;;OAEG;IACH,OAAO,CAAC,cAAc;YAUR,YAAY;IAQ1B;;;OAGG;YACW,iBAAiB;IAqB/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAuB3B;;;OAGG;IACU,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAUlE"}
|
package/dist/upload.js
CHANGED
|
@@ -16,6 +16,9 @@ class Upload {
|
|
|
16
16
|
async upload(options) {
|
|
17
17
|
const { filePath, url, credentials, showProgress = false } = options;
|
|
18
18
|
await this.validateFile(filePath);
|
|
19
|
+
if (options.validateZipFormat) {
|
|
20
|
+
await this.validateZipFormat(filePath);
|
|
21
|
+
}
|
|
19
22
|
const fileName = node_path_1.default.basename(filePath);
|
|
20
23
|
const fileStats = await node_fs_1.default.promises.stat(filePath);
|
|
21
24
|
const totalSize = fileStats.size;
|
|
@@ -85,6 +88,11 @@ class Upload {
|
|
|
85
88
|
throw error;
|
|
86
89
|
}
|
|
87
90
|
if (axios_1.default.isAxiosError(error)) {
|
|
91
|
+
// Handle 400 errors specifically for file uploads
|
|
92
|
+
if (error.response?.status === 400) {
|
|
93
|
+
const serverMessage = this.extractErrorMessage(error.response.data);
|
|
94
|
+
throw new testingbot_error_1.default(`Upload rejected: ${serverMessage || 'The file was not accepted by the server'}`, { cause: error });
|
|
95
|
+
}
|
|
88
96
|
throw (0, error_helpers_1.handleAxiosError)(error, 'Upload failed');
|
|
89
97
|
}
|
|
90
98
|
throw new testingbot_error_1.default(`Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error instanceof Error ? error : undefined });
|
|
@@ -122,6 +130,55 @@ class Upload {
|
|
|
122
130
|
throw new testingbot_error_1.default(`File not found or not readable: ${filePath}`);
|
|
123
131
|
}
|
|
124
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Validate that the file is a valid zip-based archive.
|
|
135
|
+
* ZIP, APK, IPA files all start with the ZIP magic bytes (PK\x03\x04).
|
|
136
|
+
*/
|
|
137
|
+
async validateZipFormat(filePath) {
|
|
138
|
+
const ZIP_MAGIC_BYTES = Buffer.from([0x50, 0x4b, 0x03, 0x04]); // PK\x03\x04
|
|
139
|
+
const fd = await node_fs_1.default.promises.open(filePath, 'r');
|
|
140
|
+
try {
|
|
141
|
+
const buffer = Buffer.alloc(4);
|
|
142
|
+
const { bytesRead } = await fd.read(buffer, 0, 4, 0);
|
|
143
|
+
if (bytesRead < 4 || !buffer.subarray(0, 4).equals(ZIP_MAGIC_BYTES)) {
|
|
144
|
+
const fileName = node_path_1.default.basename(filePath);
|
|
145
|
+
const ext = node_path_1.default.extname(filePath).toLowerCase();
|
|
146
|
+
throw new testingbot_error_1.default(`Invalid file format: "${fileName}" is not a valid ${ext || 'archive'} file. ` +
|
|
147
|
+
`The file does not appear to be a valid zip-based archive (APK, IPA, or ZIP).`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
await fd.close();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Extract error message from server response data
|
|
156
|
+
*/
|
|
157
|
+
extractErrorMessage(data) {
|
|
158
|
+
if (!data)
|
|
159
|
+
return undefined;
|
|
160
|
+
if (typeof data === 'string') {
|
|
161
|
+
try {
|
|
162
|
+
const parsed = JSON.parse(data);
|
|
163
|
+
return parsed.message || parsed.error || parsed.errors || data;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return data;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (typeof data === 'object') {
|
|
170
|
+
const obj = data;
|
|
171
|
+
if (typeof obj.message === 'string')
|
|
172
|
+
return obj.message;
|
|
173
|
+
if (typeof obj.error === 'string')
|
|
174
|
+
return obj.error;
|
|
175
|
+
if (typeof obj.errors === 'string')
|
|
176
|
+
return obj.errors;
|
|
177
|
+
if (Array.isArray(obj.errors))
|
|
178
|
+
return obj.errors.join(', ');
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
125
182
|
/**
|
|
126
183
|
* Calculate MD5 checksum of a file, returning base64-encoded result
|
|
127
184
|
* This matches ActiveStorage's checksum format
|
package/dist/utils.d.ts
CHANGED
|
@@ -6,6 +6,24 @@ declare const _default: {
|
|
|
6
6
|
* Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
7
7
|
*/
|
|
8
8
|
compareVersions(v1: string, v2: string): number;
|
|
9
|
+
/**
|
|
10
|
+
* Check if a device specification is a wildcard or regex pattern
|
|
11
|
+
*/
|
|
12
|
+
isWildcardDevice(device: string | undefined): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Check if a version specification is a wildcard or regex pattern
|
|
15
|
+
*/
|
|
16
|
+
isWildcardVersion(version: string | undefined): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Show info message when running many flows on a specific real device without sharding
|
|
19
|
+
*/
|
|
20
|
+
showRealDeviceFlowsInfo(options: {
|
|
21
|
+
realDevice: boolean;
|
|
22
|
+
device?: string;
|
|
23
|
+
version?: string;
|
|
24
|
+
flowCount: number;
|
|
25
|
+
shardSplit?: number;
|
|
26
|
+
}): void;
|
|
9
27
|
/**
|
|
10
28
|
* Check if a newer version is available and display update notice
|
|
11
29
|
*/
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";oBAQkB,MAAM;yBAID,MAAM;IAI3B;;;OAGG;wBACiB,MAAM,MAAM,MAAM,GAAG,MAAM;IAa/C;;OAEG;6BACsB,MAAM,GAAG,SAAS,GAAG,OAAO;IAMrD;;OAEG;+BACwB,MAAM,GAAG,SAAS,GAAG,OAAO;IAMvD;;OAEG;qCAC8B;QAC/B,UAAU,EAAE,OAAO,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,IAAI;IAuDR;;OAEG;kCAC2B,MAAM,GAAG,SAAS,GAAG,IAAI;;AA/GzD,wBAyIE"}
|
package/dist/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ const package_json_1 = __importDefault(require("../package.json"));
|
|
|
7
7
|
const logger_1 = __importDefault(require("./logger"));
|
|
8
8
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
9
|
let versionCheckDisplayed = false;
|
|
10
|
+
let realDeviceFlowsInfoDisplayed = false;
|
|
10
11
|
exports.default = {
|
|
11
12
|
getUserAgent() {
|
|
12
13
|
return `TestingBot-CTL-${package_json_1.default.version}`;
|
|
@@ -31,6 +32,56 @@ exports.default = {
|
|
|
31
32
|
}
|
|
32
33
|
return 0;
|
|
33
34
|
},
|
|
35
|
+
/**
|
|
36
|
+
* Check if a device specification is a wildcard or regex pattern
|
|
37
|
+
*/
|
|
38
|
+
isWildcardDevice(device) {
|
|
39
|
+
if (!device)
|
|
40
|
+
return true;
|
|
41
|
+
// Check for common wildcard/regex characters
|
|
42
|
+
return device === '*' || device.includes('*') || device.includes('?') || device.includes('.*');
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* Check if a version specification is a wildcard or regex pattern
|
|
46
|
+
*/
|
|
47
|
+
isWildcardVersion(version) {
|
|
48
|
+
if (!version)
|
|
49
|
+
return true;
|
|
50
|
+
// Check for common wildcard/regex characters
|
|
51
|
+
return version === '*' || version.includes('*') || version.includes('?') || version.includes('.*');
|
|
52
|
+
},
|
|
53
|
+
/**
|
|
54
|
+
* Show info message when running many flows on a specific real device without sharding
|
|
55
|
+
*/
|
|
56
|
+
showRealDeviceFlowsInfo(options) {
|
|
57
|
+
// Only show once
|
|
58
|
+
if (realDeviceFlowsInfoDisplayed) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Check conditions: real device, specific device, more than 2 flows, no shards
|
|
62
|
+
if (!options.realDevice ||
|
|
63
|
+
this.isWildcardDevice(options.device) ||
|
|
64
|
+
options.flowCount <= 2 ||
|
|
65
|
+
options.shardSplit) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
realDeviceFlowsInfoDisplayed = true;
|
|
69
|
+
const border = '─'.repeat(80);
|
|
70
|
+
logger_1.default.info('');
|
|
71
|
+
logger_1.default.info(picocolors_1.default.cyan(border));
|
|
72
|
+
logger_1.default.info(picocolors_1.default.cyan('ℹ Performance Tip'));
|
|
73
|
+
logger_1.default.info(picocolors_1.default.cyan(` Running ${options.flowCount} flows on a specific device (${options.device}) in real device mode.`));
|
|
74
|
+
logger_1.default.info(picocolors_1.default.cyan(' Each flow runs in its own session on that device, which may be slow.'));
|
|
75
|
+
logger_1.default.info(picocolors_1.default.cyan(''));
|
|
76
|
+
logger_1.default.info(picocolors_1.default.cyan(' Consider these alternatives for faster execution:'));
|
|
77
|
+
logger_1.default.info(picocolors_1.default.cyan(` • Use ${picocolors_1.default.white('--shard-split <n>')} to run multiple flows in the same session`));
|
|
78
|
+
logger_1.default.info(picocolors_1.default.cyan(` • Use wildcards for device (e.g., ${picocolors_1.default.white('"Pixel.*"')}) to parallelize across devices`));
|
|
79
|
+
if (!this.isWildcardVersion(options.version)) {
|
|
80
|
+
logger_1.default.info(picocolors_1.default.cyan(` • Use wildcards for version (e.g., ${picocolors_1.default.white('"15.*"')}) for broader device selection`));
|
|
81
|
+
}
|
|
82
|
+
logger_1.default.info(picocolors_1.default.cyan(border));
|
|
83
|
+
logger_1.default.info('');
|
|
84
|
+
},
|
|
34
85
|
/**
|
|
35
86
|
* Check if a newer version is available and display update notice
|
|
36
87
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testingbot/cli",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "CLI tool to run Espresso, XCUITest
|
|
3
|
+
"version": "1.0.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": {
|
|
7
7
|
"testingbot": "dist/index.js"
|