@devicecloud.dev/dcd 0.0.1-alpha.9 → 0.0.1
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/commands/cloud.d.ts +6 -3
- package/dist/commands/cloud.js +242 -130
- package/oclif.manifest.json +58 -22
- package/package.json +3 -6
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -8,18 +8,21 @@ export default class Cloud extends Command {
|
|
|
8
8
|
static examples: string[];
|
|
9
9
|
static flags: {
|
|
10
10
|
'android-api-level': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
-
'
|
|
12
|
-
|
|
11
|
+
'android-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
14
|
'app-binary-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
15
|
'app-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
16
|
arm64: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
-
async: import("@oclif/core/lib/interfaces").
|
|
17
|
+
async: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
17
18
|
env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
19
|
'exclude-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
20
|
flows: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
21
|
'google-play': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
22
|
'include-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
|
+
'ios-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
24
|
'ios-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
25
|
+
name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
26
|
orientation: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
27
|
};
|
|
25
28
|
run(): Promise<void>;
|
package/dist/commands/cloud.js
CHANGED
|
@@ -80,6 +80,24 @@ const compressFile = async (sourceFile) => {
|
|
|
80
80
|
// await writeFile('./my-zip.zip', buffer);
|
|
81
81
|
return buffer;
|
|
82
82
|
};
|
|
83
|
+
var EiOSDevices;
|
|
84
|
+
(function (EiOSDevices) {
|
|
85
|
+
EiOSDevices["ipad-pro-6th-gen"] = "ipad-pro-6th-gen";
|
|
86
|
+
EiOSDevices["iphone-12"] = "iphone-12";
|
|
87
|
+
EiOSDevices["iphone-12-mini"] = "iphone-12-mini";
|
|
88
|
+
EiOSDevices["iphone-12-pro-max"] = "iphone-12-pro-max";
|
|
89
|
+
EiOSDevices["iphone-13"] = "iphone-13";
|
|
90
|
+
EiOSDevices["iphone-13-mini"] = "iphone-13-mini";
|
|
91
|
+
EiOSDevices["iphone-13-pro-max"] = "iphone-13-pro-max";
|
|
92
|
+
EiOSDevices["iphone-14"] = "iphone-14";
|
|
93
|
+
EiOSDevices["iphone-14-plus"] = "iphone-14-plus";
|
|
94
|
+
EiOSDevices["iphone-14-pro"] = "iphone-14-pro";
|
|
95
|
+
EiOSDevices["iphone-14-pro-max"] = "iphone-14-pro-max";
|
|
96
|
+
EiOSDevices["iphone-15"] = "iphone-15";
|
|
97
|
+
EiOSDevices["iphone-15-plus"] = "iphone-15-plus";
|
|
98
|
+
EiOSDevices["iphone-15-pro"] = "iphone-15-pro";
|
|
99
|
+
EiOSDevices["iphone-15-pro-max"] = "iphone-15-pro-max";
|
|
100
|
+
})(EiOSDevices || (EiOSDevices = {}));
|
|
83
101
|
class Cloud extends core_1.Command {
|
|
84
102
|
static args = {
|
|
85
103
|
firstFile: core_1.Args.string({
|
|
@@ -97,16 +115,26 @@ class Cloud extends core_1.Command {
|
|
|
97
115
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
98
116
|
static flags = {
|
|
99
117
|
'android-api-level': core_1.Flags.integer({
|
|
100
|
-
aliases: ['android-api-level'],
|
|
101
118
|
description: '[Android only] Android API level to run your flow against',
|
|
102
119
|
options: ['32', '33', '34'],
|
|
103
120
|
}),
|
|
104
|
-
'
|
|
121
|
+
'android-device': core_1.Flags.string({
|
|
122
|
+
description: '[Android only] Android device to run your flow against',
|
|
123
|
+
options: [
|
|
124
|
+
'pixel-6',
|
|
125
|
+
'pixel-6a',
|
|
126
|
+
'pixel-6-pro',
|
|
127
|
+
'pixel-7',
|
|
128
|
+
'pixel-7-pro',
|
|
129
|
+
'generic-tablet',
|
|
130
|
+
],
|
|
131
|
+
}),
|
|
132
|
+
apiKey: core_1.Flags.string({
|
|
105
133
|
aliases: ['api-key'],
|
|
106
134
|
description: 'API key',
|
|
107
135
|
}),
|
|
108
|
-
|
|
109
|
-
aliases: ['api-url'],
|
|
136
|
+
apiUrl: core_1.Flags.string({
|
|
137
|
+
aliases: ['api-url', 'apiURL'],
|
|
110
138
|
default: 'https://api.devicecloud.dev',
|
|
111
139
|
description: 'API base URL',
|
|
112
140
|
hidden: true,
|
|
@@ -123,10 +151,8 @@ class Cloud extends core_1.Command {
|
|
|
123
151
|
default: false,
|
|
124
152
|
description: '[Android only] Run your flow against arm64 devices',
|
|
125
153
|
}),
|
|
126
|
-
async: core_1.Flags.
|
|
127
|
-
default: 'true',
|
|
154
|
+
async: core_1.Flags.boolean({
|
|
128
155
|
description: 'Wait for the results of the run',
|
|
129
|
-
options: ['true', 'false'],
|
|
130
156
|
}),
|
|
131
157
|
env: core_1.Flags.file({
|
|
132
158
|
char: 'e',
|
|
@@ -155,10 +181,32 @@ class Cloud extends core_1.Command {
|
|
|
155
181
|
multiple: true,
|
|
156
182
|
parse: (input) => input.split(','),
|
|
157
183
|
}),
|
|
184
|
+
'ios-device': core_1.Flags.string({
|
|
185
|
+
description: '[iOS only] iOS device to run your flow against',
|
|
186
|
+
options: [
|
|
187
|
+
'iphone-12',
|
|
188
|
+
'iphone-12-mini',
|
|
189
|
+
'iphone-12-pro-max',
|
|
190
|
+
'iphone-13',
|
|
191
|
+
'iphone-13-mini',
|
|
192
|
+
'iphone-13-pro-max',
|
|
193
|
+
'iphone-14',
|
|
194
|
+
'iphone-14-plus',
|
|
195
|
+
'iphone-14-pro',
|
|
196
|
+
'iphone-14-pro-max',
|
|
197
|
+
'iphone-15',
|
|
198
|
+
'iphone-15-plus',
|
|
199
|
+
'iphone-15-pro',
|
|
200
|
+
'iphone-15-pro-max',
|
|
201
|
+
'ipad-pro-6th-gen',
|
|
202
|
+
],
|
|
203
|
+
}),
|
|
158
204
|
'ios-version': core_1.Flags.string({
|
|
159
|
-
aliases: ['ios-version'],
|
|
160
205
|
description: '[iOS only] iOS version to run your flow against',
|
|
161
|
-
options: ['16
|
|
206
|
+
options: ['15', '16', '17'],
|
|
207
|
+
}),
|
|
208
|
+
name: core_1.Flags.string({
|
|
209
|
+
description: 'A custom name for your upload (useful for tagging commits etc)',
|
|
162
210
|
}),
|
|
163
211
|
orientation: core_1.Flags.string({
|
|
164
212
|
description: '[Android only] The orientation of the device to run your flow against in degrees',
|
|
@@ -166,137 +214,201 @@ class Cloud extends core_1.Command {
|
|
|
166
214
|
}),
|
|
167
215
|
};
|
|
168
216
|
async run() {
|
|
169
|
-
const { args, flags } = await this.parse(Cloud);
|
|
170
|
-
const { 'api-key': apiKey, 'api-url': apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, arm64, async, env, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, orientation, ...rest } = flags;
|
|
171
|
-
if (arm64) {
|
|
172
|
-
(0, cli_ux_1.info)('Contact hello@devicecloud.dev to enquire about arm64 devices');
|
|
173
|
-
(0, cli_ux_1.exit)();
|
|
174
|
-
}
|
|
175
|
-
const { firstFile, secondFile } = args;
|
|
176
|
-
let finalBinaryId = appBinaryId;
|
|
177
|
-
const finalAppFile = appFile ?? firstFile;
|
|
178
|
-
let flowFile = flows ?? secondFile;
|
|
179
|
-
if (appBinaryId) {
|
|
180
|
-
if (secondFile) {
|
|
181
|
-
throw new Error('You cannot provide both an appBinaryId and a binary file');
|
|
182
|
-
}
|
|
183
|
-
flowFile = flows ?? firstFile;
|
|
184
|
-
}
|
|
185
|
-
if (!flowFile) {
|
|
186
|
-
throw new Error('You must provide a flow file');
|
|
187
|
-
}
|
|
188
|
-
let testFileNames = [];
|
|
189
|
-
let continueOnFailure = true;
|
|
190
|
-
let sequentialFlows = [];
|
|
191
|
-
if (!flowFile?.endsWith('.yaml') &&
|
|
192
|
-
!flowFile?.endsWith('.yml') &&
|
|
193
|
-
!flowFile?.endsWith('/')) {
|
|
194
|
-
flowFile += '/';
|
|
195
|
-
}
|
|
196
217
|
try {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
catch (error) {
|
|
203
|
-
console.error(error);
|
|
204
|
-
}
|
|
205
|
-
if (!appBinaryId) {
|
|
206
|
-
if (!(flowFile && finalAppFile)) {
|
|
207
|
-
throw new Error('You must provide a flow file and an app binary id');
|
|
218
|
+
const { args, flags } = await this.parse(Cloud);
|
|
219
|
+
const { 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, arm64, async, env, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ios-device': iOSDevice, 'ios-version': iOSVersion, name, orientation, ...rest } = flags;
|
|
220
|
+
if (arm64) {
|
|
221
|
+
(0, cli_ux_1.info)('Contact hello@devicecloud.dev to enquire about arm64 devices');
|
|
222
|
+
(0, cli_ux_1.exit)();
|
|
208
223
|
}
|
|
209
|
-
|
|
210
|
-
|
|
224
|
+
const { firstFile, secondFile } = args;
|
|
225
|
+
let finalBinaryId = appBinaryId;
|
|
226
|
+
const finalAppFile = appFile ?? firstFile;
|
|
227
|
+
let flowFile = flows ?? secondFile;
|
|
228
|
+
if (appBinaryId) {
|
|
229
|
+
if (secondFile) {
|
|
230
|
+
throw new Error('You cannot provide both an appBinaryId and a binary file');
|
|
231
|
+
}
|
|
232
|
+
flowFile = flows ?? firstFile;
|
|
211
233
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
234
|
+
if (!flowFile) {
|
|
235
|
+
throw new Error('You must provide a flow file');
|
|
236
|
+
}
|
|
237
|
+
if (iOSVersion) {
|
|
238
|
+
const iOSCompatibilityLookup = {
|
|
239
|
+
'ipad-pro-6th-gen': ['16', '17'],
|
|
240
|
+
'iphone-12': ['15', '16', '17'],
|
|
241
|
+
'iphone-12-mini': ['15', '16', '17'],
|
|
242
|
+
'iphone-12-pro-max': ['15', '16', '17'],
|
|
243
|
+
'iphone-13': ['15', '16', '17'],
|
|
244
|
+
'iphone-13-mini': ['15', '16', '17'],
|
|
245
|
+
'iphone-13-pro-max': ['15', '16', '17'],
|
|
246
|
+
'iphone-14': ['16', '17'],
|
|
247
|
+
'iphone-14-plus': ['16', '17'],
|
|
248
|
+
'iphone-14-pro': ['16', '17'],
|
|
249
|
+
'iphone-14-pro-max': ['16', '17'],
|
|
250
|
+
'iphone-15': ['17'],
|
|
251
|
+
'iphone-15-plus': ['17'],
|
|
252
|
+
'iphone-15-pro': ['17'],
|
|
253
|
+
'iphone-15-pro-max': ['17'],
|
|
254
|
+
};
|
|
255
|
+
const iOSDeviceID = iOSDevice || 'iphone-14';
|
|
256
|
+
const supportediOSVersions = iOSCompatibilityLookup[iOSDeviceID];
|
|
257
|
+
if (!supportediOSVersions.includes(iOSVersion)) {
|
|
258
|
+
throw new Error(`${iOSDeviceID} only supports these iOS versions: ${supportediOSVersions.join(',')}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
let testFileNames = [];
|
|
262
|
+
let continueOnFailure = true;
|
|
263
|
+
let sequentialFlows = [];
|
|
264
|
+
if (!flowFile?.endsWith('.yaml') &&
|
|
265
|
+
!flowFile?.endsWith('.yml') &&
|
|
266
|
+
!flowFile?.endsWith('/')) {
|
|
267
|
+
flowFile += '/';
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
const executionPlan = await (0, plan_1.plan)(flowFile, includeTags.flat(), excludeTags.flat());
|
|
271
|
+
testFileNames = executionPlan.flowsToRun;
|
|
272
|
+
continueOnFailure = executionPlan.sequence?.continueOnFailure ?? true;
|
|
273
|
+
sequentialFlows = executionPlan.sequence?.flows ?? [];
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
console.error(error);
|
|
277
|
+
}
|
|
278
|
+
if (!appBinaryId) {
|
|
279
|
+
if (!(flowFile && finalAppFile)) {
|
|
280
|
+
throw new Error('You must provide a flow file and an app binary id');
|
|
281
|
+
}
|
|
282
|
+
if (!finalAppFile.endsWith('.apk') && !finalAppFile.endsWith('.zip')) {
|
|
283
|
+
throw new Error('App file must be a .apk or .zip file');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const flagLogs = [];
|
|
287
|
+
for (const [k, v] of Object.entries(flags)) {
|
|
288
|
+
if (v && v.toString().length > 0) {
|
|
289
|
+
flagLogs.push(`${k}: ${v}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
this.log(`
|
|
293
|
+
|
|
294
|
+
Submitting new job
|
|
295
|
+
→ Flow(s): ${flowFile}
|
|
296
|
+
→ App: ${appBinaryId || finalAppFile}
|
|
297
|
+
|
|
298
|
+
With options
|
|
299
|
+
→ ${flagLogs.join(`
|
|
300
|
+
→ `)}
|
|
301
|
+
|
|
302
|
+
`);
|
|
303
|
+
if (!finalBinaryId) {
|
|
304
|
+
core_1.ux.action.start('Uploading binary', 'Initializing', { stdout: true });
|
|
305
|
+
const binaryFormData = new FormData();
|
|
306
|
+
const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
|
|
307
|
+
type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
|
|
308
|
+
});
|
|
309
|
+
binaryFormData.set('file', binaryBlob, finalAppFile);
|
|
310
|
+
const options = {
|
|
311
|
+
body: binaryFormData,
|
|
312
|
+
headers: { 'x-app-api-key': apiKey },
|
|
313
|
+
};
|
|
314
|
+
core_1.ux.action.status = `Uploading`;
|
|
315
|
+
const { binaryId, message } = await typeSafePost(apiUrl, '/uploads/binary', options);
|
|
316
|
+
if (!binaryId)
|
|
317
|
+
throw new Error(message);
|
|
318
|
+
core_1.ux.action.stop(`\nBinary uploaded with id: ${binaryId}`);
|
|
319
|
+
finalBinaryId = binaryId;
|
|
320
|
+
}
|
|
321
|
+
const testFormData = new FormData();
|
|
322
|
+
// eslint-disable-next-line unicorn/no-array-reduce
|
|
323
|
+
const envObject = (env ?? []).reduce((acc, cur) => {
|
|
324
|
+
const [key, value] = cur.split('=');
|
|
325
|
+
acc[key] = value;
|
|
326
|
+
return acc;
|
|
327
|
+
}, {});
|
|
328
|
+
const buffer = [...sequentialFlows, ...testFileNames]?.length > 1
|
|
329
|
+
? await compressDir(flowFile.split('/').slice(0, -1).join('/'))
|
|
330
|
+
: await compressFile(flowFile);
|
|
331
|
+
const blob = new Blob([buffer], {
|
|
332
|
+
type: mimeTypeLookupByExtension.zip,
|
|
219
333
|
});
|
|
220
|
-
|
|
334
|
+
testFormData.set('file', blob, 'flowFile.zip');
|
|
335
|
+
testFormData.set('appBinaryId', finalBinaryId);
|
|
336
|
+
testFormData.set('testFileNames', JSON.stringify(testFileNames));
|
|
337
|
+
testFormData.set('sequentialFlows', JSON.stringify(sequentialFlows));
|
|
338
|
+
testFormData.set('env', JSON.stringify(envObject));
|
|
339
|
+
testFormData.set('googlePlay', googlePlay ? 'true' : 'false');
|
|
340
|
+
testFormData.set('config', JSON.stringify({ continueOnFailure, orientation }));
|
|
341
|
+
if (androidApiLevel) {
|
|
342
|
+
testFormData.set('androidApiLevel', androidApiLevel.toString());
|
|
343
|
+
}
|
|
344
|
+
if (androidDevice) {
|
|
345
|
+
testFormData.set('androidDevice', androidDevice.toString());
|
|
346
|
+
}
|
|
347
|
+
if (iOSVersion) {
|
|
348
|
+
testFormData.set('iOSVersion', iOSVersion.toString());
|
|
349
|
+
}
|
|
350
|
+
if (iOSDevice) {
|
|
351
|
+
testFormData.set('iOSDevice', iOSDevice.toString());
|
|
352
|
+
}
|
|
353
|
+
if (name) {
|
|
354
|
+
testFormData.set('name', name.toString());
|
|
355
|
+
}
|
|
356
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
357
|
+
if (value) {
|
|
358
|
+
testFormData.set(key, value);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
221
361
|
const options = {
|
|
222
|
-
body:
|
|
362
|
+
body: testFormData,
|
|
223
363
|
headers: { 'x-app-api-key': apiKey },
|
|
224
364
|
};
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return acc;
|
|
238
|
-
}, {});
|
|
239
|
-
const buffer = [...sequentialFlows, ...testFileNames]?.length > 1
|
|
240
|
-
? await compressDir(flowFile.split('/').slice(0, -1).join('/'))
|
|
241
|
-
: await compressFile(flowFile);
|
|
242
|
-
const blob = new Blob([buffer], {
|
|
243
|
-
type: mimeTypeLookupByExtension.zip,
|
|
244
|
-
});
|
|
245
|
-
testFormData.set('file', blob, 'flowFile.zip');
|
|
246
|
-
testFormData.set('appBinaryId', finalBinaryId);
|
|
247
|
-
testFormData.set('testFileNames', JSON.stringify(testFileNames));
|
|
248
|
-
testFormData.set('sequentialFlows', JSON.stringify(sequentialFlows));
|
|
249
|
-
testFormData.set('env', JSON.stringify(envObject));
|
|
250
|
-
testFormData.set('googlePlay', googlePlay ? 'true' : 'false');
|
|
251
|
-
testFormData.set('config', JSON.stringify({ continueOnFailure, orientation }));
|
|
252
|
-
for (const [key, value] of Object.entries(rest)) {
|
|
253
|
-
if (value) {
|
|
254
|
-
testFormData.set(key, value);
|
|
365
|
+
const { message, results } = await typeSafePost(apiUrl, '/uploads/flow', options);
|
|
366
|
+
if (!results?.length)
|
|
367
|
+
(0, errors_1.error)('No tests created: ' + message);
|
|
368
|
+
(0, cli_ux_1.info)(`\nCreated ${results.length} tests: ${results
|
|
369
|
+
.map((r) => r.test_file_name)
|
|
370
|
+
.join(', ')}\n`);
|
|
371
|
+
(0, cli_ux_1.info)('Run triggered, you can access the results at:');
|
|
372
|
+
const url = `https://console.devicecloud.dev/results/${results[0].test_upload_id}/${results[0].id}`;
|
|
373
|
+
core_1.ux.url(url, url);
|
|
374
|
+
if (async) {
|
|
375
|
+
(0, cli_ux_1.info)('Not waiting for results as async flag is set to true');
|
|
376
|
+
(0, cli_ux_1.exit)(0);
|
|
255
377
|
}
|
|
378
|
+
// poll for the run status every 5 seconds
|
|
379
|
+
core_1.ux.action.start('Waiting for results', 'Initializing', { stdout: true });
|
|
380
|
+
(0, cli_ux_1.info)('\nYou can safely close this terminal and the tests will continue\n');
|
|
381
|
+
const intervalId = setInterval(async () => {
|
|
382
|
+
const { results: updatedResults } = await typeSafeGet(apiUrl, `/results/${results[0].test_upload_id}`, {
|
|
383
|
+
headers: { 'x-app-api-key': apiKey },
|
|
384
|
+
});
|
|
385
|
+
if (!updatedResults) {
|
|
386
|
+
clearInterval(intervalId);
|
|
387
|
+
(0, errors_1.error)('No results found');
|
|
388
|
+
}
|
|
389
|
+
core_1.ux.action.status = '\nStatus | Test\n─────────── ───────────────';
|
|
390
|
+
for (const { status, test_file_name: test } of updatedResults) {
|
|
391
|
+
core_1.ux.action.status += `\n${status.padEnd(10, ' ')} | ${test}`;
|
|
392
|
+
}
|
|
393
|
+
if (updatedResults.every((result) => !['PENDING', 'RUNNING'].includes(result.status))) {
|
|
394
|
+
core_1.ux.action.stop('completed');
|
|
395
|
+
(0, cli_ux_1.info)('\n');
|
|
396
|
+
(0, cli_ux_1.table)(updatedResults, {
|
|
397
|
+
status: { get: (row) => row.status },
|
|
398
|
+
test: { get: (row) => row.test_file_name },
|
|
399
|
+
}, { printLine: this.log.bind(this) });
|
|
400
|
+
(0, cli_ux_1.info)('\n');
|
|
401
|
+
clearInterval(intervalId);
|
|
402
|
+
if (updatedResults.some((result) => result.status === 'FAILED')) {
|
|
403
|
+
(0, cli_ux_1.exit)(1);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}, 5000);
|
|
256
407
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
const { message, results } = await typeSafePost(apiUrl, '/uploads/flow', options);
|
|
262
|
-
if (!results?.length)
|
|
263
|
-
(0, errors_1.error)('No tests created: ' + message);
|
|
264
|
-
(0, cli_ux_1.info)(`\nCreated ${results.length} tests: ${results
|
|
265
|
-
.map((r) => r.test_file_name)
|
|
266
|
-
.join(', ')}\n`);
|
|
267
|
-
(0, cli_ux_1.info)('Run triggered, you can access the results at:');
|
|
268
|
-
(0, cli_ux_1.info)(`https://console.devicecloud.dev/results/${results[0].test_upload_id}/${results[0].id}/\n`);
|
|
269
|
-
if (async === 'false') {
|
|
270
|
-
(0, cli_ux_1.info)('Not waiting for results as async flag is set to false');
|
|
271
|
-
(0, cli_ux_1.exit)(0);
|
|
408
|
+
catch (error) {
|
|
409
|
+
console.error(error);
|
|
410
|
+
(0, cli_ux_1.exit)(1);
|
|
272
411
|
}
|
|
273
|
-
// poll for the run status every 5 seconds
|
|
274
|
-
core_1.ux.action.start('Waiting for results', 'Initializing', { stdout: true });
|
|
275
|
-
(0, cli_ux_1.info)('You can safely close this terminal and the tests will continue\n');
|
|
276
|
-
const intervalId = setInterval(async () => {
|
|
277
|
-
const { results: updatedResults } = await typeSafeGet(apiUrl, `/results/${results[0].test_upload_id}`, {
|
|
278
|
-
headers: { 'x-app-api-key': apiKey },
|
|
279
|
-
});
|
|
280
|
-
if (!updatedResults) {
|
|
281
|
-
clearInterval(intervalId);
|
|
282
|
-
(0, errors_1.error)('No results found');
|
|
283
|
-
}
|
|
284
|
-
core_1.ux.action.status = `\nStatus | Test
|
|
285
|
-
────────── ─────────── `;
|
|
286
|
-
for (const { status, test_file_name: test } of updatedResults) {
|
|
287
|
-
core_1.ux.action.status += `\n${status.padEnd(10, ' ')} | ${test}`;
|
|
288
|
-
}
|
|
289
|
-
if (updatedResults.every((result) => !['PENDING', 'RUNNING'].includes(result.status))) {
|
|
290
|
-
core_1.ux.action.stop('completed');
|
|
291
|
-
(0, cli_ux_1.info)('\n');
|
|
292
|
-
(0, cli_ux_1.table)(updatedResults, {
|
|
293
|
-
status: { get: (row) => row.status },
|
|
294
|
-
test: { get: (row) => row.test_file_name },
|
|
295
|
-
}, { printLine: this.log.bind(this) });
|
|
296
|
-
(0, cli_ux_1.info)('\n');
|
|
297
|
-
clearInterval(intervalId);
|
|
298
|
-
}
|
|
299
|
-
}, 5000);
|
|
300
412
|
}
|
|
301
413
|
}
|
|
302
414
|
exports.default = Cloud;
|
package/oclif.manifest.json
CHANGED
|
@@ -20,9 +20,6 @@
|
|
|
20
20
|
],
|
|
21
21
|
"flags": {
|
|
22
22
|
"android-api-level": {
|
|
23
|
-
"aliases": [
|
|
24
|
-
"android-api-level"
|
|
25
|
-
],
|
|
26
23
|
"description": "[Android only] Android API level to run your flow against",
|
|
27
24
|
"name": "android-api-level",
|
|
28
25
|
"hasDynamicHelp": false,
|
|
@@ -34,23 +31,39 @@
|
|
|
34
31
|
],
|
|
35
32
|
"type": "option"
|
|
36
33
|
},
|
|
37
|
-
"
|
|
34
|
+
"android-device": {
|
|
35
|
+
"description": "[Android only] Android device to run your flow against",
|
|
36
|
+
"name": "android-device",
|
|
37
|
+
"hasDynamicHelp": false,
|
|
38
|
+
"multiple": false,
|
|
39
|
+
"options": [
|
|
40
|
+
"pixel-6",
|
|
41
|
+
"pixel-6a",
|
|
42
|
+
"pixel-6-pro",
|
|
43
|
+
"pixel-7",
|
|
44
|
+
"pixel-7-pro",
|
|
45
|
+
"generic-tablet"
|
|
46
|
+
],
|
|
47
|
+
"type": "option"
|
|
48
|
+
},
|
|
49
|
+
"apiKey": {
|
|
38
50
|
"aliases": [
|
|
39
51
|
"api-key"
|
|
40
52
|
],
|
|
41
53
|
"description": "API key",
|
|
42
|
-
"name": "
|
|
54
|
+
"name": "apiKey",
|
|
43
55
|
"hasDynamicHelp": false,
|
|
44
56
|
"multiple": false,
|
|
45
57
|
"type": "option"
|
|
46
58
|
},
|
|
47
|
-
"
|
|
59
|
+
"apiUrl": {
|
|
48
60
|
"aliases": [
|
|
49
|
-
"api-url"
|
|
61
|
+
"api-url",
|
|
62
|
+
"apiURL"
|
|
50
63
|
],
|
|
51
64
|
"description": "API base URL",
|
|
52
65
|
"hidden": true,
|
|
53
|
-
"name": "
|
|
66
|
+
"name": "apiUrl",
|
|
54
67
|
"default": "https://api.devicecloud.dev",
|
|
55
68
|
"hasDynamicHelp": false,
|
|
56
69
|
"multiple": false,
|
|
@@ -85,14 +98,8 @@
|
|
|
85
98
|
"async": {
|
|
86
99
|
"description": "Wait for the results of the run",
|
|
87
100
|
"name": "async",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"multiple": false,
|
|
91
|
-
"options": [
|
|
92
|
-
"true",
|
|
93
|
-
"false"
|
|
94
|
-
],
|
|
95
|
-
"type": "option"
|
|
101
|
+
"allowNo": false,
|
|
102
|
+
"type": "boolean"
|
|
96
103
|
},
|
|
97
104
|
"env": {
|
|
98
105
|
"char": "e",
|
|
@@ -140,20 +147,49 @@
|
|
|
140
147
|
"multiple": true,
|
|
141
148
|
"type": "option"
|
|
142
149
|
},
|
|
143
|
-
"ios-
|
|
144
|
-
"
|
|
145
|
-
|
|
150
|
+
"ios-device": {
|
|
151
|
+
"description": "[iOS only] iOS device to run your flow against",
|
|
152
|
+
"name": "ios-device",
|
|
153
|
+
"hasDynamicHelp": false,
|
|
154
|
+
"multiple": false,
|
|
155
|
+
"options": [
|
|
156
|
+
"iphone-12",
|
|
157
|
+
"iphone-12-mini",
|
|
158
|
+
"iphone-12-pro-max",
|
|
159
|
+
"iphone-13",
|
|
160
|
+
"iphone-13-mini",
|
|
161
|
+
"iphone-13-pro-max",
|
|
162
|
+
"iphone-14",
|
|
163
|
+
"iphone-14-plus",
|
|
164
|
+
"iphone-14-pro",
|
|
165
|
+
"iphone-14-pro-max",
|
|
166
|
+
"iphone-15",
|
|
167
|
+
"iphone-15-plus",
|
|
168
|
+
"iphone-15-pro",
|
|
169
|
+
"iphone-15-pro-max",
|
|
170
|
+
"ipad-pro-6th-gen"
|
|
146
171
|
],
|
|
172
|
+
"type": "option"
|
|
173
|
+
},
|
|
174
|
+
"ios-version": {
|
|
147
175
|
"description": "[iOS only] iOS version to run your flow against",
|
|
148
176
|
"name": "ios-version",
|
|
149
177
|
"hasDynamicHelp": false,
|
|
150
178
|
"multiple": false,
|
|
151
179
|
"options": [
|
|
152
|
-
"
|
|
153
|
-
"
|
|
180
|
+
"15",
|
|
181
|
+
"16",
|
|
182
|
+
"17"
|
|
154
183
|
],
|
|
155
184
|
"type": "option"
|
|
156
185
|
},
|
|
186
|
+
"name": {
|
|
187
|
+
"description": "A custom name for your upload (useful for tagging commits etc)",
|
|
188
|
+
"name": "name",
|
|
189
|
+
"hasDynamicHelp": false,
|
|
190
|
+
"multiple": false,
|
|
191
|
+
"type": "option"
|
|
192
|
+
},
|
|
157
193
|
"orientation": {
|
|
158
194
|
"description": "[Android only] The orientation of the device to run your flow against in degrees",
|
|
159
195
|
"name": "orientation",
|
|
@@ -184,5 +220,5 @@
|
|
|
184
220
|
]
|
|
185
221
|
}
|
|
186
222
|
},
|
|
187
|
-
"version": "0.0.1
|
|
223
|
+
"version": "0.0.1"
|
|
188
224
|
}
|
package/package.json
CHANGED
|
@@ -53,10 +53,7 @@
|
|
|
53
53
|
"commands": "./dist/commands",
|
|
54
54
|
"plugins": [
|
|
55
55
|
"@oclif/plugin-help",
|
|
56
|
-
"@oclif/plugin-not-found"
|
|
57
|
-
"@oclif/plugin-update",
|
|
58
|
-
"@oclif/plugin-warn-if-update-available",
|
|
59
|
-
"@oclif/plugin-autocomplete"
|
|
56
|
+
"@oclif/plugin-not-found"
|
|
60
57
|
]
|
|
61
58
|
},
|
|
62
59
|
"private": false,
|
|
@@ -75,7 +72,7 @@
|
|
|
75
72
|
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
|
76
73
|
"version": "oclif readme && git add README.md"
|
|
77
74
|
},
|
|
78
|
-
"version": "0.0.1
|
|
75
|
+
"version": "0.0.1",
|
|
79
76
|
"bugs": {
|
|
80
77
|
"url": "https://discord.gg/GzZBHcUJ"
|
|
81
78
|
},
|
|
@@ -88,4 +85,4 @@
|
|
|
88
85
|
"testing"
|
|
89
86
|
],
|
|
90
87
|
"types": "dist/index.d.ts"
|
|
91
|
-
}
|
|
88
|
+
}
|