@devicecloud.dev/dcd 1.0.9 → 1.0.11

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.
@@ -1,12 +1,7 @@
1
1
  import { Command } from '@oclif/core';
2
2
  export declare enum EiOSDevices {
3
3
  'ipad-pro-6th-gen' = "ipad-pro-6th-gen",
4
- 'iphone-12' = "iphone-12",
5
- 'iphone-12-mini' = "iphone-12-mini",
6
- 'iphone-12-pro-max' = "iphone-12-pro-max",
7
4
  'iphone-13' = "iphone-13",
8
- 'iphone-13-mini' = "iphone-13-mini",
9
- 'iphone-13-pro-max' = "iphone-13-pro-max",
10
5
  'iphone-14' = "iphone-14",
11
6
  'iphone-14-plus' = "iphone-14-plus",
12
7
  'iphone-14-pro' = "iphone-14-pro",
@@ -37,6 +32,7 @@ export default class Cloud extends Command {
37
32
  arm64: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
38
33
  async: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
39
34
  'device-locale': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
+ 'download-artifacts': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
40
36
  env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
41
37
  'exclude-flows': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
42
38
  'exclude-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -45,7 +41,6 @@ export default class Cloud extends Command {
45
41
  'include-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
46
42
  'ios-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
47
43
  'ios-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
48
- 'legacy-upload': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
49
44
  'maestro-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
50
45
  name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
51
46
  orientation: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -20,12 +20,7 @@ const mimeTypeLookupByExtension = {
20
20
  var EiOSDevices;
21
21
  (function (EiOSDevices) {
22
22
  EiOSDevices["ipad-pro-6th-gen"] = "ipad-pro-6th-gen";
23
- EiOSDevices["iphone-12"] = "iphone-12";
24
- EiOSDevices["iphone-12-mini"] = "iphone-12-mini";
25
- EiOSDevices["iphone-12-pro-max"] = "iphone-12-pro-max";
26
23
  EiOSDevices["iphone-13"] = "iphone-13";
27
- EiOSDevices["iphone-13-mini"] = "iphone-13-mini";
28
- EiOSDevices["iphone-13-pro-max"] = "iphone-13-pro-max";
29
24
  EiOSDevices["iphone-14"] = "iphone-14";
30
25
  EiOSDevices["iphone-14-plus"] = "iphone-14-plus";
31
26
  EiOSDevices["iphone-14-pro"] = "iphone-14-pro";
@@ -63,14 +58,14 @@ class Cloud extends core_1.Command {
63
58
  }
64
59
  await (0, methods_1.versionCheck)(this.config.version);
65
60
  const { args, flags, raw } = await this.parse(Cloud);
66
- const { 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, arm64, async, 'device-locale': deviceLocale, env, 'exclude-flows': excludeFlows, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ios-device': iOSDevice, 'ios-version': iOSVersion, 'legacy-upload': legacyUpload, 'maestro-version': maestroVersion, name, orientation, quiet, ...rest } = flags;
61
+ const { 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, arm64, async, 'device-locale': deviceLocale, 'download-artifacts': downloadArtifacts, env, 'exclude-flows': excludeFlows, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ios-device': iOSDevice, 'ios-version': iOSVersion, 'maestro-version': maestroVersion, name, orientation, quiet, ...rest } = flags;
67
62
  if (arm64) {
68
63
  (0, cli_ux_1.info)('Contact hello@devicecloud.dev to enquire about arm64 devices');
69
64
  (0, cli_ux_1.exit)();
70
65
  }
71
66
  const { firstFile, secondFile } = args;
72
67
  let finalBinaryId = appBinaryId;
73
- let finalAppFile = appFile ?? firstFile;
68
+ const finalAppFile = appFile ?? firstFile;
74
69
  let flowFile = flows ?? secondFile;
75
70
  let metadata;
76
71
  if (appBinaryId) {
@@ -136,97 +131,75 @@ class Cloud extends core_1.Command {
136
131
  `);
137
132
  if (!finalBinaryId) {
138
133
  core_1.ux.action.start('Uploading binary', 'Initializing', { stdout: true });
139
- if (legacyUpload) {
140
- const binaryFormData = new FormData();
141
- if (finalAppFile?.endsWith('.app')) {
142
- const zippedAppBlob = await (0, methods_1.compressFolderToBlob)(finalAppFile);
143
- finalAppFile += '.zip';
144
- binaryFormData.set('file', zippedAppBlob, finalAppFile);
145
- }
146
- else {
147
- const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
148
- type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
149
- });
150
- binaryFormData.set('file', binaryBlob, finalAppFile);
151
- }
152
- const options = {
153
- body: binaryFormData,
154
- headers: { 'x-app-api-key': apiKey },
155
- };
156
- core_1.ux.action.status = `Uploading`;
157
- const { binaryId, message } = await (0, methods_1.typeSafePost)(apiUrl, '/uploads/binary', options);
158
- if (!binaryId)
159
- throw new Error(message);
160
- finalBinaryId = binaryId;
134
+ const { id, message, path, token } = await (0, methods_1.typeSafePost)(apiUrl, '/uploads/getBinaryUploadUrl', {
135
+ body: JSON.stringify({
136
+ platform: finalAppFile?.endsWith('.apk') ? 'android' : 'ios',
137
+ }),
138
+ headers: {
139
+ 'content-type': 'application/json',
140
+ 'x-app-api-key': apiKey,
141
+ },
142
+ });
143
+ finalBinaryId = id;
144
+ if (!path)
145
+ throw new Error(message);
146
+ let file;
147
+ if (finalAppFile?.endsWith('.app')) {
148
+ const zippedAppBlob = await (0, methods_1.compressFolderToBlob)(finalAppFile);
149
+ const filePath = finalAppFile + '.zip';
150
+ file = new file_1.File([zippedAppBlob], filePath);
161
151
  }
162
152
  else {
163
- const { id, message, path, token } = await (0, methods_1.typeSafePost)(apiUrl, '/uploads/getBinaryUploadUrl', {
164
- body: JSON.stringify({
165
- platform: finalAppFile?.endsWith('.apk') ? 'android' : 'ios',
166
- }),
167
- headers: {
168
- 'content-type': 'application/json',
169
- 'x-app-api-key': apiKey,
170
- },
153
+ const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
154
+ type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
171
155
  });
172
- finalBinaryId = id;
173
- if (!path)
174
- throw new Error(message);
175
- let file;
176
- if (finalAppFile?.endsWith('.app')) {
177
- const zippedAppBlob = await (0, methods_1.compressFolderToBlob)(finalAppFile);
178
- const filePath = finalAppFile + '.zip';
179
- file = new file_1.File([zippedAppBlob], filePath);
180
- }
181
- else {
182
- const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
183
- type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
184
- });
185
- file = new file_1.File([binaryBlob], finalAppFile);
186
- }
187
- try {
188
- metadata = finalAppFile?.endsWith('.apk')
189
- ? await (0, methods_1.extractAppMetadataAndroid)(finalAppFile)
156
+ file = new file_1.File([binaryBlob], finalAppFile);
157
+ }
158
+ try {
159
+ metadata = finalAppFile?.endsWith('.apk')
160
+ ? await (0, methods_1.extractAppMetadataAndroid)(finalAppFile)
161
+ : finalAppFile?.endsWith('.zip')
162
+ ? await (0, methods_1.extractAppMetadataIosZip)(finalAppFile)
190
163
  : await (0, methods_1.extractAppMetadataIos)(finalAppFile);
191
- }
192
- catch {
193
- this.warn('Failed to extact app metadata, please share with support@devicecloud.dev so we can improve our parsing.');
194
- }
195
- // this needs to made nicer by using envs or maybe fetching the keys from the getSignedURL call
196
- const SB = {
197
- dev: {
198
- SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
199
- SUPABASE_URL: 'https://lbmsowehtjwnqlurpemb.supabase.co',
200
- },
201
- prod: {
202
- SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
203
- SUPABASE_URL: 'https://pgydnphbimetinsgfkbo.supabase.co',
204
- },
205
- };
206
- const { SUPABASE_KEY, SUPABASE_URL } = SB[apiUrl === 'https://api.devicecloud.dev' ? 'prod' : 'dev'];
207
- const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_KEY);
208
- const uploadToUrl = await supabase.storage
209
- .from('organizations')
210
- .uploadToSignedUrl(path, token, file);
211
- if (uploadToUrl.error)
212
- throw new Error(uploadToUrl.error);
213
- const { error } = await (0, methods_1.typeSafePost)(apiUrl, '/uploads/finaliseUpload', {
214
- body: JSON.stringify({ id, metadata, path }),
215
- headers: {
216
- 'content-type': 'application/json',
217
- 'x-app-api-key': apiKey,
218
- },
219
- });
220
- if (error)
221
- throw new Error(error);
222
164
  }
165
+ catch {
166
+ this.warn('Failed to extract app metadata, please share with support@devicecloud.dev so we can improve our parsing.');
167
+ }
168
+ // this needs to made nicer by using envs or maybe fetching the keys from the getSignedURL call
169
+ const SB = {
170
+ dev: {
171
+ SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
172
+ SUPABASE_URL: 'https://lbmsowehtjwnqlurpemb.supabase.co',
173
+ },
174
+ prod: {
175
+ SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
176
+ SUPABASE_URL: 'https://pgydnphbimetinsgfkbo.supabase.co',
177
+ },
178
+ };
179
+ const { SUPABASE_KEY, SUPABASE_URL } = SB[apiUrl === 'https://api.devicecloud.dev' ? 'prod' : 'dev'];
180
+ const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_KEY);
181
+ const uploadToUrl = await supabase.storage
182
+ .from('organizations')
183
+ .uploadToSignedUrl(path, token, file);
184
+ if (uploadToUrl.error)
185
+ throw new Error(uploadToUrl.error);
186
+ const { error } = await (0, methods_1.typeSafePost)(apiUrl, '/uploads/finaliseUpload', {
187
+ body: JSON.stringify({ id, metadata, path }),
188
+ headers: {
189
+ 'content-type': 'application/json',
190
+ 'x-app-api-key': apiKey,
191
+ },
192
+ });
193
+ if (error)
194
+ throw new Error(error);
223
195
  core_1.ux.action.stop(`\nBinary uploaded with id: ${finalBinaryId}`);
224
196
  }
225
197
  const testFormData = new FormData();
226
198
  // eslint-disable-next-line unicorn/no-array-reduce
227
199
  const envObject = (env ?? []).reduce((acc, cur) => {
228
- const [key, value] = cur.split('=');
229
- acc[key] = value;
200
+ const [key, ...value] = cur.split('=');
201
+ // handle case where value includes an equals sign
202
+ acc[key] = value.join('=');
230
203
  return acc;
231
204
  }, {});
232
205
  const buffer = await (0, methods_1.compressFilesFromRelativePath)(flowFile?.endsWith('.yaml') || flowFile?.endsWith('.yml')
@@ -267,8 +240,6 @@ class Cloud extends core_1.Command {
267
240
  testFormData.set('iOSDevice', iOSDevice.toString());
268
241
  if (name)
269
242
  testFormData.set('name', name.toString());
270
- if (metadata)
271
- testFormData.set('metadata', JSON.stringify(metadata));
272
243
  for (const [key, value] of Object.entries(rest)) {
273
244
  if (value) {
274
245
  testFormData.set(key, value);
@@ -322,6 +293,22 @@ class Cloud extends core_1.Command {
322
293
  core_1.ux.url(url, url);
323
294
  (0, cli_ux_1.info)('\n');
324
295
  clearInterval(intervalId);
296
+ if (downloadArtifacts) {
297
+ try {
298
+ await (0, methods_1.typeSafePostDownload)(apiUrl, `/results/${results[0].test_upload_id}/download`, {
299
+ body: JSON.stringify({ results: downloadArtifacts }),
300
+ headers: {
301
+ 'content-type': 'application/json',
302
+ 'x-app-api-key': apiKey,
303
+ },
304
+ });
305
+ (0, cli_ux_1.info)('\n');
306
+ (0, cli_ux_1.info)('Test artifacts have been downloaded to ./artifacts.zip');
307
+ }
308
+ catch {
309
+ this.warn('Failed to download artifacts');
310
+ }
311
+ }
325
312
  if (updatedResults.some((result) => result.status === 'FAILED')) {
326
313
  // eslint-disable-next-line no-process-exit, unicorn/no-process-exit
327
314
  process.exit(2);
@@ -9,6 +9,7 @@ export declare const flags: {
9
9
  arm64: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
10
  async: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
11
  'device-locale': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ 'download-artifacts': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
13
  env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
14
  'exclude-flows': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
14
15
  'exclude-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -17,7 +18,6 @@ export declare const flags: {
17
18
  'include-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
18
19
  'ios-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
20
  'ios-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
- 'legacy-upload': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
21
  'maestro-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
22
  name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
23
  orientation: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
package/dist/constants.js CHANGED
@@ -45,6 +45,10 @@ exports.flags = {
45
45
  'device-locale': core_1.Flags.string({
46
46
  description: 'Locale that will be set to a device, ISO-639-1 code and uppercase ISO-3166-1 code e.g. "de_DE" for Germany',
47
47
  }),
48
+ 'download-artifacts': core_1.Flags.string({
49
+ description: 'BETA (API may change) - download a zip containing the logs, screenshots and videos for each result in this run. You will debited a $0.01 egress fee for each result. Use --download-artifacts=FAILED for failures only or --download-artifacts=ALL for every result.',
50
+ options: ['ALL', 'FAILED'],
51
+ }),
48
52
  env: core_1.Flags.file({
49
53
  char: 'e',
50
54
  description: 'One or more environment variables to inject into your flows',
@@ -85,12 +89,7 @@ exports.flags = {
85
89
  'ios-device': core_1.Flags.string({
86
90
  description: '[iOS only] iOS device to run your flow against',
87
91
  options: [
88
- 'iphone-12',
89
- 'iphone-12-mini',
90
- 'iphone-12-pro-max',
91
92
  'iphone-13',
92
- 'iphone-13-mini',
93
- 'iphone-13-pro-max',
94
93
  'iphone-14',
95
94
  'iphone-14-plus',
96
95
  'iphone-14-pro',
@@ -110,10 +109,6 @@ exports.flags = {
110
109
  description: '[iOS only] iOS version to run your flow against',
111
110
  options: ['15', '16', '17', '18'],
112
111
  }),
113
- 'legacy-upload': core_1.Flags.boolean({
114
- default: false,
115
- description: 'Use the legacy direct upload method',
116
- }),
117
112
  'maestro-version': core_1.Flags.string({
118
113
  aliases: ['maestroVersion'],
119
114
  description: '[ALPHA pre-release] - Maestro version to run your flow against',
@@ -146,13 +141,8 @@ exports.flags = {
146
141
  }),
147
142
  };
148
143
  exports.iOSCompatibilityLookup = {
149
- 'ipad-pro-6th-gen': ['16', '17'],
150
- 'iphone-12': ['15', '16', '17'],
151
- 'iphone-12-mini': ['15', '16', '17'],
152
- 'iphone-12-pro-max': ['15', '16', '17'],
153
- 'iphone-13': ['15', '16', '17'],
154
- 'iphone-13-mini': ['15', '16', '17'],
155
- 'iphone-13-pro-max': ['15', '16', '17'],
144
+ 'ipad-pro-6th-gen': ['16', '17', '18'],
145
+ 'iphone-13': ['15'],
156
146
  'iphone-14': ['16', '17', '18'],
157
147
  'iphone-14-plus': ['16', '17', '18'],
158
148
  'iphone-14-pro': ['16', '17', '18'],
package/dist/methods.d.ts CHANGED
@@ -7,6 +7,10 @@ export declare const typeSafePost: <T extends keyof paths>(baseUrl: string, path
7
7
  body?: BodyInit;
8
8
  headers?: HeadersInit;
9
9
  }) => Promise<paths[T]["post"]["responses"]["201"]["content"]["application/json"]>;
10
+ export declare const typeSafePostDownload: (baseUrl: string, path: string, init?: {
11
+ body?: BodyInit;
12
+ headers?: HeadersInit;
13
+ }) => Promise<void>;
10
14
  export declare const typeSafeGet: <T extends keyof paths>(baseUrl: string, path: string, init?: {
11
15
  body?: FormData;
12
16
  headers?: HeadersInit;
@@ -17,4 +21,5 @@ export declare const compressFolderToBlob: (sourceDir: string) => Promise<Blob>;
17
21
  export declare const compressFilesFromRelativePath: (path: string, files: string[]) => Promise<Buffer>;
18
22
  export declare const verifyAppZip: (zipPath: string) => Promise<void>;
19
23
  export declare const extractAppMetadataAndroid: (appFilePath: string) => Promise<TAppMetadata>;
20
- export declare const extractAppMetadataIos: (appFilePath: string) => Promise<TAppMetadata>;
24
+ export declare const extractAppMetadataIosZip: (appFilePath: string) => Promise<TAppMetadata>;
25
+ export declare const extractAppMetadataIos: (appFolderPath: string) => Promise<TAppMetadata>;
package/dist/methods.js CHANGED
@@ -1,15 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractAppMetadataIos = exports.extractAppMetadataAndroid = exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressFolderToBlob = exports.compressDir = exports.toBuffer = exports.typeSafeGet = exports.typeSafePost = exports.versionCheck = void 0;
3
+ exports.extractAppMetadataIos = exports.extractAppMetadataIosZip = exports.extractAppMetadataAndroid = exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressFolderToBlob = exports.compressDir = exports.toBuffer = exports.typeSafeGet = exports.typeSafePostDownload = exports.typeSafePost = exports.versionCheck = void 0;
4
4
  const cli_ux_1 = require("@oclif/core/lib/cli-ux");
5
5
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6
6
  // @ts-ignore
7
7
  const AppInfoParser = require("app-info-parser");
8
8
  const archiver = require("archiver");
9
9
  const bplist_parser_1 = require("bplist-parser");
10
- // import { writeFile } from 'node:fs/promises';
10
+ const node_fs_1 = require("node:fs");
11
+ const promises_1 = require("node:fs/promises");
11
12
  const nodePath = require("node:path");
12
13
  const node_stream_1 = require("node:stream");
14
+ const promises_2 = require("node:stream/promises");
13
15
  const StreamZip = require("node-stream-zip");
14
16
  const plist_1 = require("plist");
15
17
  const PERMITTED_EXTENSIONS = new Set([
@@ -47,6 +49,19 @@ const typeSafePost = async (baseUrl, path, init) => {
47
49
  return res.json();
48
50
  };
49
51
  exports.typeSafePost = typeSafePost;
52
+ const typeSafePostDownload = async (baseUrl, path, init) => {
53
+ const res = await fetch(baseUrl + path, {
54
+ ...init,
55
+ method: 'POST',
56
+ });
57
+ if (!res.ok) {
58
+ throw new Error(await res.text());
59
+ }
60
+ const fileStream = (0, node_fs_1.createWriteStream)('./artifacts.zip', { flags: 'wx' });
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ await (0, promises_2.finished)(node_stream_1.Readable.fromWeb(res.body).pipe(fileStream));
63
+ };
64
+ exports.typeSafePostDownload = typeSafePostDownload;
50
65
  const typeSafeGet = async (baseUrl, path, init) => {
51
66
  const res = await fetch(baseUrl + path, init);
52
67
  if (!res.ok) {
@@ -139,7 +154,23 @@ const extractAppMetadataAndroid = async (appFilePath) => {
139
154
  return { appId: result.package, platform: 'android' };
140
155
  };
141
156
  exports.extractAppMetadataAndroid = extractAppMetadataAndroid;
142
- const extractAppMetadataIos = async (appFilePath) => new Promise((resolve, reject) => {
157
+ const parseInfoPlist = async (buffer) => {
158
+ let data;
159
+ const bufferType = buffer[0];
160
+ if (bufferType === 60 ||
161
+ bufferType === '<' ||
162
+ bufferType === 239) {
163
+ data = (0, plist_1.parse)(buffer.toString());
164
+ }
165
+ else if (bufferType === 98) {
166
+ data = (0, bplist_parser_1.parseBuffer)(buffer)[0];
167
+ }
168
+ else {
169
+ throw new Error('Unknown plist buffer type.');
170
+ }
171
+ return data;
172
+ };
173
+ const extractAppMetadataIosZip = async (appFilePath) => new Promise((resolve, reject) => {
143
174
  const zip = new StreamZip({ file: './' + appFilePath });
144
175
  zip.on('ready', () => {
145
176
  const infoPlist = Object.values(zip.entries()).find((e) => e.name.includes('Info.plist'));
@@ -147,26 +178,25 @@ const extractAppMetadataIos = async (appFilePath) => new Promise((resolve, rejec
147
178
  reject(new Error('Failed to find info plist'));
148
179
  }
149
180
  const buffer = zip.entryDataSync(infoPlist.name);
150
- let data;
151
- const bufferType = buffer[0];
152
- if (bufferType === 60 ||
153
- bufferType === '<' ||
154
- bufferType === 239) {
155
- data = (0, plist_1.parse)(buffer.toString());
156
- }
157
- else if (bufferType === 98) {
158
- data = (0, bplist_parser_1.parseBuffer)(buffer)[0];
159
- }
160
- else {
161
- reject(new Error('Unknown plist buffer type.'));
162
- }
163
- const appId = data.CFBundleIdentifier;
164
- zip.close();
165
- resolve({ appId, platform: 'ios' });
181
+ parseInfoPlist(buffer)
182
+ .then((data) => {
183
+ const appId = data.CFBundleIdentifier;
184
+ zip.close();
185
+ resolve({ appId, platform: 'ios' });
186
+ })
187
+ .catch(reject);
166
188
  });
167
189
  zip.on('error', (err) => {
168
190
  console.error(err);
169
191
  reject(err);
170
192
  });
171
193
  });
194
+ exports.extractAppMetadataIosZip = extractAppMetadataIosZip;
195
+ const extractAppMetadataIos = async (appFolderPath) => {
196
+ const infoPlistPath = nodePath.join(appFolderPath, 'Info.plist');
197
+ const buffer = await (0, promises_1.readFile)(infoPlistPath);
198
+ const data = await parseInfoPlist(buffer);
199
+ const appId = data.CFBundleIdentifier;
200
+ return { appId, platform: 'ios' };
201
+ };
172
202
  exports.extractAppMetadataIos = extractAppMetadataIos;
@@ -106,6 +106,17 @@
106
106
  "multiple": false,
107
107
  "type": "option"
108
108
  },
109
+ "download-artifacts": {
110
+ "description": "BETA (API may change) - download a zip containing the logs, screenshots and videos for each result in this run. You will debited a $0.01 egress fee for each result. Use --download-artifacts=FAILED for failures only or --download-artifacts=ALL for every result.",
111
+ "name": "download-artifacts",
112
+ "hasDynamicHelp": false,
113
+ "multiple": false,
114
+ "options": [
115
+ "ALL",
116
+ "FAILED"
117
+ ],
118
+ "type": "option"
119
+ },
109
120
  "env": {
110
121
  "char": "e",
111
122
  "description": "One or more environment variables to inject into your flows",
@@ -166,12 +177,7 @@
166
177
  "hasDynamicHelp": false,
167
178
  "multiple": false,
168
179
  "options": [
169
- "iphone-12",
170
- "iphone-12-mini",
171
- "iphone-12-pro-max",
172
180
  "iphone-13",
173
- "iphone-13-mini",
174
- "iphone-13-pro-max",
175
181
  "iphone-14",
176
182
  "iphone-14-plus",
177
183
  "iphone-14-pro",
@@ -201,12 +207,6 @@
201
207
  ],
202
208
  "type": "option"
203
209
  },
204
- "legacy-upload": {
205
- "description": "Use the legacy direct upload method",
206
- "name": "legacy-upload",
207
- "allowNo": false,
208
- "type": "boolean"
209
- },
210
210
  "maestro-version": {
211
211
  "aliases": [
212
212
  "maestroVersion"
@@ -275,5 +275,5 @@
275
275
  ]
276
276
  }
277
277
  },
278
- "version": "1.0.9"
278
+ "version": "1.0.11"
279
279
  }
package/package.json CHANGED
@@ -80,7 +80,7 @@
80
80
  "test": "mocha --forbid-only \"test/**/*.test.ts\"",
81
81
  "version": "oclif readme && git add README.md"
82
82
  },
83
- "version": "1.0.9",
83
+ "version": "1.0.11",
84
84
  "bugs": {
85
85
  "url": "https://discord.gg/gm3mJwcNw8"
86
86
  },