@devicecloud.dev/dcd 4.0.3-beta.1 → 4.0.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.
package/bin/dev.js CHANGED
File without changes
@@ -54,15 +54,4 @@ export default class Cloud extends Command {
54
54
  };
55
55
  private versionCheck;
56
56
  run(): Promise<any>;
57
- /**
58
- * Handle downloading reports based on the report type specified
59
- * @param reportType The type of report to download ('junit', 'allure', or 'all')
60
- * @param apiUrl The base URL for the API server
61
- * @param apiKey The API key for authentication
62
- * @param uploadId The unique identifier for the test upload
63
- * @param junitPath Optional file path where the JUnit report should be saved
64
- * @param debug Whether debug logging is enabled
65
- * @returns Promise that resolves when all reports have been downloaded
66
- */
67
- private handleReportDownloads;
68
57
  }
@@ -684,9 +684,33 @@ class Cloud extends core_1.Command {
684
684
  this.warn('Failed to download artifacts');
685
685
  }
686
686
  }
687
- // Handle report downloads based on --report flag
688
- if (report && ['allure', 'html', 'junit'].includes(report)) {
689
- await this.handleReportDownloads(report, apiUrl, apiKey, results[0].test_upload_id, junitPath, debug);
687
+ // Handle report download separately if --report junit is specified
688
+ if (report === 'junit') {
689
+ try {
690
+ if (debug) {
691
+ this.log('DEBUG: Downloading JUNIT report');
692
+ }
693
+ const reportPath = path.resolve(process.cwd(), junitPath || 'report.xml');
694
+ await api_gateway_1.ApiGateway.downloadReport(apiUrl, apiKey, results[0].test_upload_id, reportPath);
695
+ this.log('\n');
696
+ this.log(`JUNIT test report has been downloaded to ${reportPath}`);
697
+ }
698
+ catch (error) {
699
+ if (debug) {
700
+ this.log(`DEBUG: Error downloading JUNIT report: ${error}`);
701
+ }
702
+ const errorMessage = error instanceof Error ? error.message : String(error);
703
+ this.warn(`Failed to download JUNIT report: ${errorMessage}`);
704
+ if (errorMessage.includes('404')) {
705
+ this.warn('No JUNIT reports found for this upload. Make sure your tests were run with --report junit flag.');
706
+ }
707
+ else if (errorMessage.includes('EACCES') || errorMessage.includes('EPERM')) {
708
+ this.warn('Permission denied. Check write permissions for the current directory.');
709
+ }
710
+ else if (errorMessage.includes('ENOENT')) {
711
+ this.warn('Directory does not exist. Make sure you have write access to the current directory.');
712
+ }
713
+ }
690
714
  }
691
715
  const resultsWithoutEarlierTries = updatedResults.filter((result) => {
692
716
  const originalTryId = result.retry_of || result.id;
@@ -787,63 +811,5 @@ class Cloud extends core_1.Command {
787
811
  }
788
812
  }
789
813
  }
790
- /**
791
- * Handle downloading reports based on the report type specified
792
- * @param reportType The type of report to download ('junit', 'allure', or 'all')
793
- * @param apiUrl The base URL for the API server
794
- * @param apiKey The API key for authentication
795
- * @param uploadId The unique identifier for the test upload
796
- * @param junitPath Optional file path where the JUnit report should be saved
797
- * @param debug Whether debug logging is enabled
798
- * @returns Promise that resolves when all reports have been downloaded
799
- */
800
- // eslint-disable-next-line max-params
801
- async handleReportDownloads(reportType, apiUrl, apiKey, uploadId, junitPath, debug) {
802
- const downloadReport = async (type, filePath) => {
803
- try {
804
- if (debug) {
805
- this.log(`DEBUG: Downloading ${type.toUpperCase()} report`);
806
- }
807
- await api_gateway_1.ApiGateway.downloadReportGeneric(apiUrl, apiKey, uploadId, type, filePath);
808
- this.log(`${type.toUpperCase()} test report has been downloaded to ${filePath}`);
809
- }
810
- catch (error) {
811
- if (debug) {
812
- this.log(`DEBUG: Error downloading ${type.toUpperCase()} report: ${error}`);
813
- }
814
- const errorMessage = error instanceof Error ? error.message : String(error);
815
- this.warn(`Failed to download ${type.toUpperCase()} report: ${errorMessage}`);
816
- if (errorMessage.includes('404')) {
817
- this.warn(`No ${type.toUpperCase()} reports found for this upload. Make sure your tests generated results.`);
818
- }
819
- else if (errorMessage.includes('EACCES') || errorMessage.includes('EPERM')) {
820
- this.warn('Permission denied. Check write permissions for the current directory.');
821
- }
822
- else if (errorMessage.includes('ENOENT')) {
823
- this.warn('Directory does not exist. Make sure you have write access to the current directory.');
824
- }
825
- }
826
- };
827
- switch (reportType) {
828
- case 'junit': {
829
- const reportPath = path.resolve(process.cwd(), junitPath || 'report.xml');
830
- await downloadReport('junit', reportPath);
831
- break;
832
- }
833
- case 'allure': {
834
- const allureReportPath = path.resolve(process.cwd(), `allure-report-${uploadId}.zip`);
835
- await downloadReport('allure', allureReportPath);
836
- break;
837
- }
838
- case 'html': {
839
- const htmlReportPath = path.resolve(process.cwd(), `html-report-${uploadId}.zip`);
840
- await downloadReport('html', htmlReportPath);
841
- break;
842
- }
843
- default: {
844
- this.warn(`Unknown report type: ${reportType}`);
845
- }
846
- }
847
- }
848
814
  }
849
815
  exports.default = Cloud;
package/dist/constants.js CHANGED
@@ -8,15 +8,12 @@ exports.SUPPORTED_MAESTRO_VERSIONS = [
8
8
  '1.39.0',
9
9
  '1.39.1',
10
10
  '1.39.2',
11
- '1.39.4',
12
11
  '1.39.5',
13
12
  '1.39.7',
14
- '1.39.13',
15
- '1.40.0',
16
- '1.40.1',
17
- '1.40.2',
18
13
  '1.40.3',
19
14
  '1.41.0',
15
+ '2.0.2',
16
+ '2.0.3',
20
17
  ];
21
18
  exports.DEFAULT_MAESTRO_VERSION = '1.41.0';
22
19
  const getLatestMaestroVersion = () => exports.SUPPORTED_MAESTRO_VERSIONS.at(-1);
@@ -194,8 +191,8 @@ exports.flags = {
194
191
  }),
195
192
  report: core_1.Flags.string({
196
193
  aliases: ['format'],
197
- description: 'Generate and download test reports in the specified format. HTML reports will be returned in the Allure format, please see the documentation for more details.',
198
- options: ['junit', 'allure', 'html'],
194
+ description: 'Runs Maestro with the --format flag, this will generate a report in the specified format',
195
+ options: ['junit', 'html'],
199
196
  }),
200
197
  retry: core_1.Flags.integer({
201
198
  description: 'Automatically retry the run up to the number of times specified (same as pressing retry in the UI) - this is free of charge',
@@ -1,16 +1,10 @@
1
1
  import { TAppMetadata } from '../types';
2
2
  export declare const ApiGateway: {
3
- /**
4
- * Standardized error handling for API responses
5
- * @param res - The fetch response object
6
- * @param operation - Description of the operation that failed
7
- * @returns Never returns, always throws
8
- */
9
- handleApiError(res: Response, operation: string): Promise<never>;
10
3
  checkForExistingUpload(baseUrl: string, apiKey: string, sha: string): Promise<{
11
4
  appBinaryId: string;
12
5
  exists: boolean;
13
6
  }>;
7
+ downloadReport(baseUrl: string, apiKey: string, uploadId: string, reportPath?: string): Promise<void>;
14
8
  downloadArtifactsZip(baseUrl: string, apiKey: string, uploadId: string, results: "ALL" | "FAILED", artifactsPath?: string): Promise<void>;
15
9
  finaliseUpload(baseUrl: string, apiKey: string, id: string, metadata: TAppMetadata, path: string, sha: string): Promise<Record<string, never>>;
16
10
  getBinaryUploadUrl(baseUrl: string, apiKey: string, platform: "android" | "ios"): Promise<{
@@ -39,14 +33,4 @@ export declare const ApiGateway: {
39
33
  message?: string;
40
34
  results?: import("../types/schema.types").components["schemas"]["IDBResult"][];
41
35
  }>;
42
- /**
43
- * Generic report download method that handles both junit and allure reports
44
- * @param baseUrl - API base URL
45
- * @param apiKey - API key for authentication
46
- * @param uploadId - Upload ID to download report for
47
- * @param reportType - Type of report to download ('junit' or 'allure')
48
- * @param reportPath - Optional custom path for the downloaded report
49
- * @returns Promise that resolves when download is complete
50
- */
51
- downloadReportGeneric(baseUrl: string, apiKey: string, uploadId: string, reportType: "allure" | "junit" | "html", reportPath?: string): Promise<void>;
52
36
  };
@@ -1,50 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApiGateway = void 0;
4
+ const fs = require("node:fs/promises");
4
5
  const path = require("node:path");
5
6
  exports.ApiGateway = {
6
- /**
7
- * Standardized error handling for API responses
8
- * @param res - The fetch response object
9
- * @param operation - Description of the operation that failed
10
- * @returns Never returns, always throws
11
- */
12
- async handleApiError(res, operation) {
13
- const errorText = await res.text();
14
- let userMessage;
15
- // Parse common API error formats
16
- try {
17
- const errorData = JSON.parse(errorText);
18
- userMessage = errorData.message || errorData.error || errorText;
19
- }
20
- catch {
21
- userMessage = errorText;
22
- }
23
- // Add context and improve readability
24
- switch (res.status) {
25
- case 400: {
26
- throw new Error(`Invalid request: ${userMessage}`);
27
- }
28
- case 401: {
29
- throw new Error(`Authentication failed. Please check your API key.`);
30
- }
31
- case 403: {
32
- throw new Error(`Access denied. ${userMessage}`);
33
- }
34
- case 404: {
35
- throw new Error(`Resource not found. ${userMessage}`);
36
- }
37
- case 429: {
38
- throw new Error(`Rate limit exceeded. Please try again later.`);
39
- }
40
- case 500: {
41
- throw new Error(`Server error occurred. Please try again or contact support.`);
42
- }
43
- default: {
44
- throw new Error(`${operation} failed: ${userMessage} (HTTP ${res.status})`);
45
- }
46
- }
47
- },
48
7
  async checkForExistingUpload(baseUrl, apiKey, sha) {
49
8
  const res = await fetch(`${baseUrl}/uploads/checkForExistingUpload`, {
50
9
  body: JSON.stringify({ sha }),
@@ -56,6 +15,25 @@ exports.ApiGateway = {
56
15
  });
57
16
  return res.json();
58
17
  },
18
+ async downloadReport(baseUrl, apiKey, uploadId, reportPath) {
19
+ const finalReportPath = reportPath || path.resolve(process.cwd(), `report-${uploadId}.xml`);
20
+ const url = `${baseUrl}/results/${uploadId}/report`;
21
+ const res = await fetch(url, {
22
+ headers: {
23
+ 'x-app-api-key': apiKey,
24
+ },
25
+ method: 'GET',
26
+ });
27
+ if (!res.ok) {
28
+ const errorText = await res.text();
29
+ if (res.status === 404) {
30
+ throw new Error(`Upload ID '${uploadId}' not found or no results available for this upload`);
31
+ }
32
+ throw new Error(`Failed to download report: ${res.status} ${errorText}`);
33
+ }
34
+ const buffer = await res.arrayBuffer();
35
+ await fs.writeFile(finalReportPath, Buffer.from(buffer));
36
+ },
59
37
  async downloadArtifactsZip(baseUrl, apiKey, uploadId, results, artifactsPath = './artifacts.zip') {
60
38
  const res = await fetch(`${baseUrl}/results/${uploadId}/download`, {
61
39
  body: JSON.stringify({ results }),
@@ -66,7 +44,7 @@ exports.ApiGateway = {
66
44
  method: 'POST',
67
45
  });
68
46
  if (!res.ok) {
69
- await this.handleApiError(res, 'Failed to download artifacts');
47
+ throw new Error(await res.text());
70
48
  }
71
49
  // Handle tilde expansion for home directory
72
50
  if (artifactsPath.startsWith('~/') || artifactsPath === '~') {
@@ -109,7 +87,7 @@ exports.ApiGateway = {
109
87
  method: 'POST',
110
88
  });
111
89
  if (!res.ok) {
112
- await this.handleApiError(res, 'Failed to finalize upload');
90
+ throw new Error(await res.text());
113
91
  }
114
92
  return res.json();
115
93
  },
@@ -123,7 +101,7 @@ exports.ApiGateway = {
123
101
  method: 'POST',
124
102
  });
125
103
  if (!res.ok) {
126
- await this.handleApiError(res, 'Failed to get upload URL');
104
+ throw new Error(await res.text());
127
105
  }
128
106
  return res.json();
129
107
  },
@@ -133,7 +111,7 @@ exports.ApiGateway = {
133
111
  headers: { 'x-app-api-key': apiKey },
134
112
  });
135
113
  if (!res.ok) {
136
- await this.handleApiError(res, 'Failed to get results');
114
+ throw new Error(await res.text());
137
115
  }
138
116
  return res.json();
139
117
  },
@@ -151,7 +129,7 @@ exports.ApiGateway = {
151
129
  },
152
130
  });
153
131
  if (!response.ok) {
154
- await this.handleApiError(response, 'Failed to get upload status');
132
+ throw new Error(`Failed to fetch status: ${response.statusText}`);
155
133
  }
156
134
  return response.json();
157
135
  },
@@ -164,88 +142,8 @@ exports.ApiGateway = {
164
142
  method: 'POST',
165
143
  });
166
144
  if (!res.ok) {
167
- await this.handleApiError(res, 'Failed to upload test flows');
145
+ throw new Error(await res.text());
168
146
  }
169
147
  return res.json();
170
148
  },
171
- /**
172
- * Generic report download method that handles both junit and allure reports
173
- * @param baseUrl - API base URL
174
- * @param apiKey - API key for authentication
175
- * @param uploadId - Upload ID to download report for
176
- * @param reportType - Type of report to download ('junit' or 'allure')
177
- * @param reportPath - Optional custom path for the downloaded report
178
- * @returns Promise that resolves when download is complete
179
- */
180
- async downloadReportGeneric(baseUrl, apiKey, uploadId, reportType, reportPath) {
181
- // Define endpoint and default filename mappings
182
- const config = {
183
- junit: {
184
- endpoint: `/results/${uploadId}/report`,
185
- defaultFilename: `report-${uploadId}.xml`,
186
- notFoundMessage: `Upload ID '${uploadId}' not found or no results available for this upload`,
187
- errorPrefix: 'Failed to download report',
188
- },
189
- allure: {
190
- endpoint: `/allure/${uploadId}/download`,
191
- defaultFilename: `allure-report-${uploadId}.zip`,
192
- notFoundMessage: `Upload ID '${uploadId}' not found or no Allure report available for this upload`,
193
- errorPrefix: 'Failed to download Allure report',
194
- },
195
- html: {
196
- endpoint: `/results/${uploadId}/html-report`,
197
- defaultFilename: `html-report-${uploadId}.zip`,
198
- notFoundMessage: `Upload ID '${uploadId}' not found or no HTML report available for this upload`,
199
- errorPrefix: 'Failed to download HTML report',
200
- },
201
- };
202
- const { endpoint, defaultFilename, notFoundMessage, errorPrefix } = config[reportType];
203
- const finalReportPath = reportPath || path.resolve(process.cwd(), defaultFilename);
204
- const url = `${baseUrl}${endpoint}`;
205
- // Make the download request
206
- const res = await fetch(url, {
207
- headers: {
208
- 'x-app-api-key': apiKey,
209
- },
210
- method: 'GET',
211
- });
212
- if (!res.ok) {
213
- const errorText = await res.text();
214
- if (res.status === 404) {
215
- throw new Error(notFoundMessage);
216
- }
217
- throw new Error(`${errorPrefix}: ${res.status} ${errorText}`);
218
- }
219
- // Handle tilde expansion for home directory (applies to all report types)
220
- let expandedPath = finalReportPath;
221
- if (finalReportPath.startsWith('~/') || finalReportPath === '~') {
222
- expandedPath = finalReportPath.replace(/^~/,
223
- // eslint-disable-next-line unicorn/prefer-module
224
- require('node:os').homedir());
225
- }
226
- // Create directory structure if it doesn't exist
227
- // eslint-disable-next-line unicorn/prefer-module
228
- const { dirname } = require('node:path');
229
- // eslint-disable-next-line unicorn/prefer-module
230
- const { createWriteStream, mkdirSync } = require('node:fs');
231
- // eslint-disable-next-line unicorn/prefer-module
232
- const { finished } = require('node:stream/promises');
233
- // eslint-disable-next-line unicorn/prefer-module
234
- const { Readable } = require('node:stream');
235
- const directory = dirname(expandedPath);
236
- if (directory !== '.') {
237
- try {
238
- mkdirSync(directory, { recursive: true });
239
- }
240
- catch (error) {
241
- // Ignore EEXIST errors (directory already exists)
242
- if (error.code !== 'EEXIST') {
243
- throw error;
244
- }
245
- }
246
- }
247
- // Write the file using streaming for better memory efficiency
248
- const fileStream = createWriteStream(expandedPath, { flags: 'wx' });
249
- await finished(Readable.fromWeb(res.body).pipe(fileStream));
250
- },
251
149
  };
@@ -297,15 +297,12 @@
297
297
  "1.39.0",
298
298
  "1.39.1",
299
299
  "1.39.2",
300
- "1.39.4",
301
300
  "1.39.5",
302
301
  "1.39.7",
303
- "1.39.13",
304
- "1.40.0",
305
- "1.40.1",
306
- "1.40.2",
307
302
  "1.40.3",
308
303
  "1.41.0",
304
+ "2.0.2",
305
+ "2.0.3",
309
306
  "latest"
310
307
  ],
311
308
  "type": "option"
@@ -374,13 +371,12 @@
374
371
  "aliases": [
375
372
  "format"
376
373
  ],
377
- "description": "Generate and download test reports in the specified format. HTML reports will be returned in the Allure format, please see the documentation for more details.",
374
+ "description": "Runs Maestro with the --format flag, this will generate a report in the specified format",
378
375
  "name": "report",
379
376
  "hasDynamicHelp": false,
380
377
  "multiple": false,
381
378
  "options": [
382
379
  "junit",
383
- "allure",
384
380
  "html"
385
381
  ],
386
382
  "type": "option"
@@ -582,5 +578,5 @@
582
578
  ]
583
579
  }
584
580
  },
585
- "version": "4.0.3-beta.1"
581
+ "version": "4.0.3"
586
582
  }
package/package.json CHANGED
@@ -65,22 +65,14 @@
65
65
  ]
66
66
  },
67
67
  "private": false,
68
+ "publishConfig": {
69
+ "access": "public"
70
+ },
68
71
  "repository": {
69
72
  "type": "git",
70
73
  "url": "https://devicecloud.dev"
71
74
  },
72
- "scripts": {
73
- "dcd": "./bin/dev.js",
74
- "prod": "./bin/run.js",
75
- "build": "shx rm -rf dist && tsc -b",
76
- "lint": "NODE_OPTIONS='--no-deprecation' eslint . --ext .ts",
77
- "postpack": "shx rm -f oclif.manifest.json",
78
- "prepack": "yarn build && oclif manifest",
79
- "prepare": "yarn build",
80
- "version": "oclif readme && git add README.md",
81
- "test": "node scripts/test-runner.mjs"
82
- },
83
- "version": "4.0.3-beta.1",
75
+ "version": "4.0.3",
84
76
  "bugs": {
85
77
  "url": "https://discord.gg/gm3mJwcNw8"
86
78
  },
@@ -93,5 +85,12 @@
93
85
  "testing"
94
86
  ],
95
87
  "types": "dist/index.d.ts",
96
- "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
97
- }
88
+ "scripts": {
89
+ "dcd": "./bin/dev.js",
90
+ "prod": "./bin/run.js",
91
+ "build": "shx rm -rf dist && tsc -b",
92
+ "lint": "NODE_OPTIONS='--no-deprecation' eslint . --ext .ts",
93
+ "version": "oclif readme && git add README.md",
94
+ "test": "node scripts/test-runner.mjs"
95
+ }
96
+ }