@devicecloud.dev/dcd 0.0.1-alpha.5 → 0.0.1-alpha.6

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.
@@ -12,6 +12,7 @@ export default class Cloud extends Command {
12
12
  apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
13
13
  appBinaryId: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
14
  appFile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
15
+ async: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
16
  env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
16
17
  excludeTags: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
17
18
  flows: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  /* eslint-disable complexity */
4
4
  const core_1 = require("@oclif/core");
5
+ const cli_ux_1 = require("@oclif/core/lib/cli-ux");
6
+ const errors_1 = require("@oclif/core/lib/errors");
5
7
  const archiver = require("archiver");
6
8
  const promises_1 = require("node:fs/promises");
7
9
  const node_stream_1 = require("node:stream");
@@ -21,7 +23,14 @@ const PERMITTED_EXTENSIONS = new Set([
21
23
  'mp4',
22
24
  'js',
23
25
  ]);
24
- const typeSafeFetch = async (baseUrl, path, init) => {
26
+ const typeSafePost = async (baseUrl, path, init) => {
27
+ const res = await fetch(baseUrl + path, { ...init, method: 'POST' });
28
+ if (!res.ok) {
29
+ throw new Error(await res.text());
30
+ }
31
+ return res.json();
32
+ };
33
+ const typeSafeGet = async (baseUrl, path, init) => {
25
34
  const res = await fetch(baseUrl + path, init);
26
35
  if (!res.ok) {
27
36
  throw new Error(await res.text());
@@ -105,11 +114,15 @@ class Cloud extends core_1.Command {
105
114
  }),
106
115
  appFile: core_1.Flags.file({
107
116
  aliases: ['app-file'],
108
- description: 'App binary to run your Flows against',
117
+ description: 'App binary to run your flows against',
118
+ }),
119
+ async: core_1.Flags.boolean({
120
+ default: true,
121
+ description: 'Wait for the results of the run',
109
122
  }),
110
123
  env: core_1.Flags.file({
111
124
  char: 'e',
112
- description: 'One or more environment variables to inject into your Flows',
125
+ description: 'One or more environment variables to inject into your flows',
113
126
  multiple: true,
114
127
  }),
115
128
  excludeTags: core_1.Flags.string({
@@ -120,7 +133,7 @@ class Cloud extends core_1.Command {
120
133
  parse: (input) => input.split(','),
121
134
  }),
122
135
  flows: core_1.Flags.string({
123
- description: 'The path to the flow file or folder containing your Flows',
136
+ description: 'The path to the flow file or folder containing your flows',
124
137
  }),
125
138
  iOSVersion: core_1.Flags.string({
126
139
  aliases: ['ios-version'],
@@ -130,15 +143,14 @@ class Cloud extends core_1.Command {
130
143
  includeTags: core_1.Flags.string({
131
144
  aliases: ['include-tags'],
132
145
  default: [],
133
- description: 'Only Flows which have these tags will be included in the run',
146
+ description: 'Only flows which have these tags will be included in the run',
134
147
  multiple: true,
135
148
  parse: (input) => input.split(','),
136
149
  }),
137
150
  };
138
151
  async run() {
139
152
  const { args, flags } = await this.parse(Cloud);
140
- const { apiKey, apiUrl, appBinaryId, appFile, env, excludeTags, flows, includeTags, ...rest } = flags;
141
- console.log({ args });
153
+ const { apiKey, apiUrl, appBinaryId, appFile, async, env, excludeTags, flows, includeTags, ...rest } = flags;
142
154
  const { firstFile, secondFile } = args;
143
155
  let finalBinaryId = appBinaryId;
144
156
  const finalAppFile = appFile ?? firstFile;
@@ -148,7 +160,11 @@ class Cloud extends core_1.Command {
148
160
  throw new Error('You cannot provide both an appBinaryId and a binary file');
149
161
  }
150
162
  flowFile = flows ?? firstFile;
151
- this.log(`you want to run the flow(s) ${flowFile} against the binary with id ${appBinaryId} with the following flags: ${JSON.stringify(flags)}`);
163
+ // this.log(
164
+ // `you want to run the flow(s) ${flowFile} against the binary with id ${appBinaryId} with the following flags: ${JSON.stringify(
165
+ // flags,
166
+ // )}`,
167
+ // );
152
168
  }
153
169
  else {
154
170
  if (!(flowFile && finalAppFile)) {
@@ -167,14 +183,12 @@ class Cloud extends core_1.Command {
167
183
  const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
168
184
  type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
169
185
  });
170
- console.log(mimeTypeLookupByExtension[finalAppFile.split('.').pop()]);
171
186
  binaryFormData.set('file', binaryBlob, finalAppFile);
172
187
  const options = {
173
188
  body: binaryFormData,
174
189
  headers: { 'x-app-api-key': apiKey },
175
- method: 'POST',
176
190
  };
177
- const { binaryId, message } = await typeSafeFetch(apiUrl, '/uploads/binary', options);
191
+ const { binaryId, message } = await typeSafePost(apiUrl, '/uploads/binary', options);
178
192
  if (!binaryId)
179
193
  throw new Error(message);
180
194
  this.log(message);
@@ -192,7 +206,6 @@ class Cloud extends core_1.Command {
192
206
  // todo: handle continueOnFailure and other sequence properties
193
207
  testFileNames.push(file);
194
208
  }
195
- console.log(executionPlan);
196
209
  }
197
210
  catch (error) {
198
211
  console.error(error);
@@ -228,10 +241,46 @@ class Cloud extends core_1.Command {
228
241
  const options = {
229
242
  body: testFormData,
230
243
  headers: { 'x-app-api-key': apiKey },
231
- method: 'POST',
232
244
  };
233
- const { message } = await typeSafeFetch(apiUrl, '/uploads/flow', options);
234
- console.log(message);
245
+ const { message, results } = await typeSafePost(apiUrl, '/uploads/flow', options);
246
+ if (!results?.length)
247
+ (0, errors_1.error)('No tests created: ' + message);
248
+ (0, cli_ux_1.info)(`\nCreated ${results.length} tests: ${results
249
+ .map((r) => r.test_file_name)
250
+ .join(', ')}\n`);
251
+ (0, cli_ux_1.info)('Run triggered, you can access the results at:');
252
+ (0, cli_ux_1.info)(`https://console.devicecloud.dev/results/${results[0].test_upload_id}/${results[0].id}/\n`);
253
+ if (!async) {
254
+ (0, cli_ux_1.info)('completed');
255
+ (0, cli_ux_1.exit)(0);
256
+ }
257
+ if (async) {
258
+ // poll for the run status every 5 seconds
259
+ core_1.ux.action.start('Waiting for results', 'Initializing', { stdout: true });
260
+ (0, cli_ux_1.info)(' ');
261
+ const intervalId = setInterval(async () => {
262
+ const { results: updatedResults } = await typeSafeGet(apiUrl, `/results/${results[0].test_upload_id}`, {
263
+ headers: { 'x-app-api-key': apiKey },
264
+ });
265
+ if (!updatedResults) {
266
+ clearInterval(intervalId);
267
+ (0, errors_1.error)('No results found');
268
+ }
269
+ core_1.ux.action.status = `\nStatus | Test
270
+ ────────── ─────────── `;
271
+ for (const { status, test_file_name: test } of updatedResults) {
272
+ core_1.ux.action.status += `\n${status.padEnd(10, ' ')} | ${test}`;
273
+ }
274
+ if (updatedResults.every((result) => !['PENDING', 'RUNNING'].includes(result.status))) {
275
+ (0, cli_ux_1.info)('\nRun completed\n');
276
+ (0, cli_ux_1.table)(updatedResults, {
277
+ status: { get: (row) => row.status },
278
+ test: { get: (row) => row.test_file_name },
279
+ }, { printLine: this.log.bind(this) });
280
+ clearInterval(intervalId);
281
+ }
282
+ }, 5000);
283
+ }
235
284
  }
236
285
  }
237
286
  exports.default = Cloud;
@@ -70,15 +70,21 @@
70
70
  "aliases": [
71
71
  "app-file"
72
72
  ],
73
- "description": "App binary to run your Flows against",
73
+ "description": "App binary to run your flows against",
74
74
  "name": "appFile",
75
75
  "hasDynamicHelp": false,
76
76
  "multiple": false,
77
77
  "type": "option"
78
78
  },
79
+ "async": {
80
+ "description": "Wait for the results of the run",
81
+ "name": "async",
82
+ "allowNo": false,
83
+ "type": "boolean"
84
+ },
79
85
  "env": {
80
86
  "char": "e",
81
- "description": "One or more environment variables to inject into your Flows",
87
+ "description": "One or more environment variables to inject into your flows",
82
88
  "name": "env",
83
89
  "hasDynamicHelp": false,
84
90
  "multiple": true,
@@ -96,7 +102,7 @@
96
102
  "type": "option"
97
103
  },
98
104
  "flows": {
99
- "description": "The path to the flow file or folder containing your Flows",
105
+ "description": "The path to the flow file or folder containing your flows",
100
106
  "name": "flows",
101
107
  "hasDynamicHelp": false,
102
108
  "multiple": false,
@@ -120,7 +126,7 @@
120
126
  "aliases": [
121
127
  "include-tags"
122
128
  ],
123
- "description": "Only Flows which have these tags will be included in the run",
129
+ "description": "Only flows which have these tags will be included in the run",
124
130
  "name": "includeTags",
125
131
  "default": [],
126
132
  "hasDynamicHelp": false,
@@ -144,5 +150,5 @@
144
150
  ]
145
151
  }
146
152
  },
147
- "version": "0.0.1-alpha.5"
153
+ "version": "0.0.1-alpha.6"
148
154
  }
package/package.json CHANGED
@@ -74,7 +74,7 @@
74
74
  "test": "mocha --forbid-only \"test/**/*.test.ts\"",
75
75
  "version": "oclif readme && git add README.md"
76
76
  },
77
- "version": "0.0.1-alpha.5",
77
+ "version": "0.0.1-alpha.6",
78
78
  "bugs": {
79
79
  "url": "https://discord.gg/GzZBHcUJ"
80
80
  },