@devicecloud.dev/dcd 3.6.1 → 3.6.3

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.
@@ -34,6 +34,7 @@ export default class Cloud extends Command {
34
34
  'additional-app-files': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
35
35
  'android-api-level': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
36
36
  'android-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
37
+ 'skip-chrome-onboarding': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
37
38
  apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
38
39
  apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
39
40
  'app-binary-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -61,6 +62,7 @@ export default class Cloud extends Command {
61
62
  'runner-type': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
62
63
  report: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
63
64
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
65
+ 'json-file': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
64
66
  };
65
67
  static enableJsonFlag: boolean;
66
68
  private versionCheck;
@@ -6,6 +6,7 @@ const core_1 = require("@oclif/core");
6
6
  const cli_ux_1 = require("@oclif/core/lib/cli-ux");
7
7
  const errors_1 = require("@oclif/core/lib/errors");
8
8
  const path = require("node:path");
9
+ const fs = require("node:fs");
9
10
  const constants_1 = require("../constants");
10
11
  const methods_1 = require("../methods");
11
12
  const plan_1 = require("../plan");
@@ -46,6 +47,10 @@ process.on('warning', (warning) => {
46
47
  return;
47
48
  }
48
49
  });
50
+ const escapeShellValue = (value) => {
51
+ // Escape special characters that could cause shell interpretation issues
52
+ return value.replace(/(["\\'$`!\s\[\]{}()&|;<>*?#^~])/g, '\\$1');
53
+ };
49
54
  class Cloud extends core_1.Command {
50
55
  static args = {
51
56
  firstFile: core_1.Args.string({
@@ -80,8 +85,27 @@ class Cloud extends core_1.Command {
80
85
  let output = null;
81
86
  try {
82
87
  const { args, flags, raw } = await this.parse(Cloud);
88
+ // Log the full command that was run
89
+ const commandParts = ['dcd cloud'];
90
+ if (args.firstFile)
91
+ commandParts.push(escapeShellValue(args.firstFile));
92
+ if (args.secondFile)
93
+ commandParts.push(escapeShellValue(args.secondFile));
94
+ for (const [key, value] of Object.entries(flags)) {
95
+ if (value && value.toString().length > 0) {
96
+ if (typeof value === 'boolean') {
97
+ commandParts.push(`--${key}`);
98
+ }
99
+ else if (Array.isArray(value)) {
100
+ commandParts.push(`--${key}=${value.map((v) => escapeShellValue(v)).join(',')}`);
101
+ }
102
+ else {
103
+ commandParts.push(`--${key}=${escapeShellValue(value.toString())}`);
104
+ }
105
+ }
106
+ }
107
+ this.log(`\nCommand ran: ${commandParts.join(' ')}\n`);
83
108
  let { 'additional-app-binary-ids': nonFlatAdditionalAppBinaryIds, 'additional-app-files': nonFlatAdditionalAppFiles, 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey: apiKeyFlag, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, 'artifacts-path': artifactsPath, async, config: configFile, 'device-locale': deviceLocale, 'download-artifacts': downloadArtifacts, env, 'exclude-flows': excludeFlows, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ignore-sha-check': ignoreShaCheck, 'ios-device': iOSDevice, 'ios-version': iOSVersion, 'maestro-version': maestroVersion, name, orientation, quiet, retry, report, 'runner-type': runnerType, 'x86-arch': x86Arch, json, ...rest } = flags;
84
- // If in JSON mode, temporarily intercept stdout to suppress the warning
85
109
  if (json) {
86
110
  const originalStdoutWrite = process.stdout.write;
87
111
  process.stdout.write = function (chunk, encodingOrCallback, cb) {
@@ -281,6 +305,7 @@ class Cloud extends core_1.Command {
281
305
  raw: JSON.stringify(raw),
282
306
  uploadedBinaryIds,
283
307
  version: this.config.version,
308
+ skipChromeOnboarding: flags['skip-chrome-onboarding'],
284
309
  };
285
310
  if (finalAdditionalBinaryIds?.length > 0) {
286
311
  config.additionalAppBinaryIds = finalAdditionalBinaryIds;
@@ -326,16 +351,24 @@ class Cloud extends core_1.Command {
326
351
  this.log(`Your upload ID is: ${results[0].test_upload_id}`);
327
352
  this.log(`Poll upload status using: dcd status --api-key ... --upload-id ${results[0].test_upload_id}`);
328
353
  if (async) {
354
+ const jsonOutput = {
355
+ uploadId: results[0].test_upload_id,
356
+ consoleUrl: url,
357
+ status: 'PENDING',
358
+ tests: results.map((r) => ({
359
+ name: r.test_file_name,
360
+ status: r.status,
361
+ })),
362
+ };
363
+ if (flags['json-file']) {
364
+ const jsonFilePath = name
365
+ ? `${name}_dcd.json`
366
+ : `${results[0].test_upload_id}_dcd.json`;
367
+ fs.writeFileSync(jsonFilePath, JSON.stringify(jsonOutput, null, 2));
368
+ this.log(`JSON output written to: ${path.resolve(jsonFilePath)}`);
369
+ }
329
370
  if (json) {
330
- return {
331
- uploadId: results[0].test_upload_id,
332
- consoleUrl: url,
333
- status: 'PENDING',
334
- tests: results.map((r) => ({
335
- name: r.test_file_name,
336
- status: r.status,
337
- })),
338
- };
371
+ return jsonOutput;
339
372
  }
340
373
  this.log('Not waiting for results as async flag is set to true');
341
374
  return;
@@ -402,30 +435,46 @@ class Cloud extends core_1.Command {
402
435
  return result.id === Math.max(...tries.map((t) => t.id));
403
436
  });
404
437
  if (resultsWithoutEarlierTries.some((result) => result.status === 'FAILED')) {
438
+ const jsonOutput = {
439
+ uploadId: results[0].test_upload_id,
440
+ consoleUrl: url,
441
+ status: 'FAILED',
442
+ tests: resultsWithoutEarlierTries.map((r) => ({
443
+ name: r.test_file_name,
444
+ status: r.status,
445
+ })),
446
+ };
447
+ if (flags['json-file']) {
448
+ const jsonFilePath = name
449
+ ? `${name}_dcd.json`
450
+ : `${results[0].test_upload_id}_dcd.json`;
451
+ fs.writeFileSync(jsonFilePath, JSON.stringify(jsonOutput, null, 2));
452
+ this.log(`JSON output written to: ${path.resolve(jsonFilePath)}`);
453
+ }
405
454
  if (json) {
406
- output = {
407
- uploadId: results[0].test_upload_id,
408
- consoleUrl: url,
409
- status: 'FAILED',
410
- tests: resultsWithoutEarlierTries.map((r) => ({
411
- name: r.test_file_name,
412
- status: r.status,
413
- })),
414
- };
455
+ output = jsonOutput;
415
456
  }
416
457
  reject(new Error('RUN_FAILED'));
417
458
  }
418
459
  else {
460
+ const jsonOutput = {
461
+ uploadId: results[0].test_upload_id,
462
+ consoleUrl: url,
463
+ status: 'PASSED',
464
+ tests: resultsWithoutEarlierTries.map((r) => ({
465
+ name: r.test_file_name,
466
+ status: r.status,
467
+ })),
468
+ };
469
+ if (flags['json-file']) {
470
+ const jsonFilePath = name
471
+ ? `${name}_dcd.json`
472
+ : `${results[0].test_upload_id}_dcd.json`;
473
+ fs.writeFileSync(jsonFilePath, JSON.stringify(jsonOutput, null, 2));
474
+ this.log(`JSON output written to: ${path.resolve(jsonFilePath)}`);
475
+ }
419
476
  if (json) {
420
- output = {
421
- uploadId: results[0].test_upload_id,
422
- consoleUrl: url,
423
- status: 'PASSED',
424
- tests: resultsWithoutEarlierTries.map((r) => ({
425
- name: r.test_file_name,
426
- status: r.status,
427
- })),
428
- };
477
+ output = jsonOutput;
429
478
  }
430
479
  }
431
480
  sequentialPollFaillures = 0;
@@ -4,6 +4,7 @@ export declare const flags: {
4
4
  'additional-app-files': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
5
5
  'android-api-level': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
6
  'android-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ 'skip-chrome-onboarding': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
8
  apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
9
  apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
10
  'app-binary-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -31,6 +32,7 @@ export declare const flags: {
31
32
  'runner-type': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
32
33
  report: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
34
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
35
+ 'json-file': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
34
36
  };
35
37
  export declare const iOSCompatibilityLookup: {
36
38
  [k in EiOSDevices]: string[];
package/dist/constants.js CHANGED
@@ -31,6 +31,10 @@ exports.flags = {
31
31
  'generic-tablet',
32
32
  ],
33
33
  }),
34
+ 'skip-chrome-onboarding': core_1.Flags.boolean({
35
+ description: '[Android only] Skip Chrome browser onboarding screens when running tests',
36
+ default: false,
37
+ }),
34
38
  apiKey: core_1.Flags.string({
35
39
  aliases: ['api-key'],
36
40
  description: 'API key for devicecloud.dev (find this in the console UI). You can also set the DEVICE_CLOUD_API_KEY environment variable.',
@@ -181,6 +185,10 @@ exports.flags = {
181
185
  json: core_1.Flags.boolean({
182
186
  description: 'Output results in JSON format - note: will always provide exit code 0',
183
187
  }),
188
+ 'json-file': core_1.Flags.boolean({
189
+ description: 'Write JSON output to a file with name <run_name>_dcd.json or <upload_id>_dcd.json if no name is provided',
190
+ required: false,
191
+ }),
184
192
  };
185
193
  exports.iOSCompatibilityLookup = {
186
194
  'ipad-pro-6th-gen': ['16', '17', '18'],
@@ -71,6 +71,12 @@
71
71
  ],
72
72
  "type": "option"
73
73
  },
74
+ "skip-chrome-onboarding": {
75
+ "description": "[Android only] Skip Chrome browser onboarding screens when running tests",
76
+ "name": "skip-chrome-onboarding",
77
+ "allowNo": false,
78
+ "type": "boolean"
79
+ },
74
80
  "apiKey": {
75
81
  "aliases": [
76
82
  "api-key"
@@ -342,6 +348,13 @@
342
348
  "html"
343
349
  ],
344
350
  "type": "option"
351
+ },
352
+ "json-file": {
353
+ "description": "Write JSON output to a file with name <run_name>_dcd.json or <upload_id>_dcd.json if no name is provided",
354
+ "name": "json-file",
355
+ "required": false,
356
+ "allowNo": false,
357
+ "type": "boolean"
345
358
  }
346
359
  },
347
360
  "hasDynamicHelp": false,
@@ -501,5 +514,5 @@
501
514
  ]
502
515
  }
503
516
  },
504
- "version": "3.6.1"
517
+ "version": "3.6.3"
505
518
  }
package/package.json CHANGED
@@ -79,7 +79,7 @@
79
79
  "prepare": "yarn build",
80
80
  "version": "oclif readme && git add README.md"
81
81
  },
82
- "version": "3.6.1",
82
+ "version": "3.6.3",
83
83
  "bugs": {
84
84
  "url": "https://discord.gg/gm3mJwcNw8"
85
85
  },