@devicecloud.dev/dcd 0.0.1-alpha.0

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/README.md ADDED
@@ -0,0 +1,18 @@
1
+ devicecloud.dev CLI
2
+ =================
3
+
4
+ One line swap out for Maestro Cloud
5
+
6
+ Install:
7
+ ```sh-session
8
+ $ npm install -g @devicecloud/dcd
9
+ ```
10
+
11
+ Use:
12
+ ```sh
13
+ # maestro cloud --apiKey <apiKey> <appFile> .myFlows/
14
+
15
+ dcd cloud --apiKey <apiKey> <appFile> .myFlows/
16
+ ```
17
+
18
+ See full documentation: [Docs](https://docs.devicecloud.dev)
package/bin/dev.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\dev" %*
package/bin/dev.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node_modules/.bin/ts-node
2
+ // eslint-disable-next-line node/shebang, unicorn/prefer-top-level-await
3
+ ;(async () => {
4
+ const oclif = await import('@oclif/core')
5
+ await oclif.execute({development: true, dir: __dirname})
6
+ })()
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
package/bin/run.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ // eslint-disable-next-line unicorn/prefer-top-level-await
4
+ (async () => {
5
+ const oclif = await import('@oclif/core');
6
+ await oclif.execute({ development: false, dir: __dirname });
7
+ })();
@@ -0,0 +1,19 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Cloud extends Command {
3
+ static args: {
4
+ firstFile: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
5
+ secondFile: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ androidApiLevel: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ appBinaryId: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
+ appFile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
15
+ env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
16
+ iOSVersion: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
+ };
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const promises_1 = require("node:fs/promises");
5
+ const mimeTypeLookupByExtension = {
6
+ apk: 'application/vnd.android.package-archive',
7
+ yaml: 'application/x-yaml',
8
+ zip: 'application/zip',
9
+ };
10
+ const typeSafeFetch = async (baseUrl, path, init) => {
11
+ const res = await fetch(baseUrl + path, init);
12
+ if (!res.ok) {
13
+ throw new Error(await res.text());
14
+ }
15
+ return res.json();
16
+ };
17
+ class Cloud extends core_1.Command {
18
+ static args = {
19
+ firstFile: core_1.Args.string({
20
+ description: 'The binary file of the app to run your flow against, e.g. app.apk for android or app.zip for ios',
21
+ hidden: true,
22
+ name: 'App file',
23
+ required: true,
24
+ }),
25
+ secondFile: core_1.Args.string({
26
+ description: 'The flow file to run against the app, e.g. test.yaml',
27
+ hidden: true,
28
+ name: 'Flow file',
29
+ }),
30
+ };
31
+ static description = `Test a Flow or set of Flows on devicecloud.dev (https://devicecloud.dev)\nProvide your application file and a folder with Maestro flows to run them in parallel on multiple devices in devicecloud.dev\nThe command will block until all analyses have completed`;
32
+ static examples = ['<%= config.bin %> <%= command.id %>'];
33
+ static flags = {
34
+ androidApiLevel: core_1.Flags.integer({
35
+ aliases: ['android-api-level'],
36
+ description: 'Android API level to run your flow against',
37
+ }),
38
+ apiKey: core_1.Flags.string({ aliases: ['api-key'], description: 'API key' }),
39
+ apiUrl: core_1.Flags.string({
40
+ aliases: ['api-url'],
41
+ default: 'https://api.devicecloud.dev',
42
+ description: 'API base URL',
43
+ }),
44
+ appBinaryId: core_1.Flags.string({
45
+ aliases: ['app-binary-id'],
46
+ description: 'The ID of the app binary previously uploaded to Maestro Cloud',
47
+ }),
48
+ appFile: core_1.Flags.file({
49
+ aliases: ['app-file'],
50
+ description: 'App binary to run your Flows against',
51
+ }),
52
+ env: core_1.Flags.file({
53
+ aliases: ['env'],
54
+ char: 'e',
55
+ description: 'One or more environment variables to inject into your Flows',
56
+ multiple: true,
57
+ }),
58
+ iOSVersion: core_1.Flags.string({
59
+ aliases: ['ios-version'],
60
+ description: 'iOS version to run your flow against',
61
+ }),
62
+ };
63
+ async run() {
64
+ const { args, flags } = await this.parse(Cloud);
65
+ const { apiKey, apiUrl, appBinaryId, ...rest } = flags;
66
+ console.log({ args });
67
+ const { firstFile, secondFile } = args;
68
+ let finalBinaryId = appBinaryId;
69
+ const appFile = firstFile;
70
+ let flowFile = secondFile;
71
+ if (appBinaryId) {
72
+ if (secondFile) {
73
+ throw new Error('You cannot provide both an appBinaryId and a binary file');
74
+ }
75
+ flowFile = firstFile;
76
+ this.log(`you want to run the flow ${flowFile} against the binary with id ${appBinaryId} with the following flags: ${JSON.stringify(flags)}`);
77
+ }
78
+ else {
79
+ if (!appFile.endsWith('.apk') && !appFile.endsWith('.zip')) {
80
+ throw new Error('App file must be a .apk or .zip file');
81
+ }
82
+ if (!(flowFile && appFile)) {
83
+ throw new Error('You must provide a flow file and an app binary id');
84
+ }
85
+ this.log(`you want to run the flow ${flowFile} against the app ${appFile} with the following flags: ${JSON.stringify(flags)}`);
86
+ }
87
+ if (!finalBinaryId) {
88
+ const binaryFormData = new FormData();
89
+ const binaryBlob = new Blob([await (0, promises_1.readFile)(appFile)], {
90
+ type: mimeTypeLookupByExtension[appFile.split('.').pop()],
91
+ });
92
+ console.log(mimeTypeLookupByExtension[appFile.split('.').pop()]);
93
+ binaryFormData.set('file', binaryBlob, appFile);
94
+ const options = {
95
+ body: binaryFormData,
96
+ headers: { 'x-app-api-key': apiKey },
97
+ method: 'POST',
98
+ };
99
+ const { binaryId, message } = await typeSafeFetch(apiUrl, '/uploads/binary', options);
100
+ if (!binaryId)
101
+ throw new Error(message);
102
+ this.log(message);
103
+ finalBinaryId = binaryId;
104
+ }
105
+ const testFormData = new FormData();
106
+ const blob = new Blob([await (0, promises_1.readFile)(flowFile)], {
107
+ type: mimeTypeLookupByExtension[flowFile.split('.').pop()],
108
+ });
109
+ testFormData.set('file', blob, flowFile);
110
+ testFormData.set('appBinaryId', finalBinaryId);
111
+ testFormData.set('testFileName', flowFile);
112
+ for (const [key, value] of Object.entries(rest)) {
113
+ if (value) {
114
+ testFormData.set(key, value);
115
+ }
116
+ }
117
+ const options = {
118
+ body: testFormData,
119
+ headers: { 'x-app-api-key': apiKey },
120
+ method: 'POST',
121
+ };
122
+ const { message } = await typeSafeFetch(apiUrl, '/uploads/flow', options);
123
+ console.log(message);
124
+ }
125
+ }
126
+ exports.default = Cloud;
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = void 0;
4
+ var core_1 = require("@oclif/core");
5
+ Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
@@ -0,0 +1,113 @@
1
+ {
2
+ "commands": {
3
+ "cloud": {
4
+ "aliases": [],
5
+ "args": {
6
+ "firstFile": {
7
+ "description": "The binary file of the app to run your flow against, e.g. app.apk for android or app.zip for ios",
8
+ "hidden": true,
9
+ "name": "firstFile",
10
+ "required": true
11
+ },
12
+ "secondFile": {
13
+ "description": "The flow file to run against the app, e.g. test.yaml",
14
+ "hidden": true,
15
+ "name": "secondFile"
16
+ }
17
+ },
18
+ "description": "Test a Flow or set of Flows on devicecloud.dev (https://devicecloud.dev)\nProvide your application file and a folder with Maestro flows to run them in parallel on multiple devices in devicecloud.dev\nThe command will block until all analyses have completed",
19
+ "examples": [
20
+ "<%= config.bin %> <%= command.id %>"
21
+ ],
22
+ "flags": {
23
+ "androidApiLevel": {
24
+ "aliases": [
25
+ "android-api-level"
26
+ ],
27
+ "description": "Android API level to run your flow against",
28
+ "name": "androidApiLevel",
29
+ "hasDynamicHelp": false,
30
+ "multiple": false,
31
+ "type": "option"
32
+ },
33
+ "apiKey": {
34
+ "aliases": [
35
+ "api-key"
36
+ ],
37
+ "description": "API key",
38
+ "name": "apiKey",
39
+ "hasDynamicHelp": false,
40
+ "multiple": false,
41
+ "type": "option"
42
+ },
43
+ "apiUrl": {
44
+ "aliases": [
45
+ "api-url"
46
+ ],
47
+ "description": "API base URL",
48
+ "name": "apiUrl",
49
+ "default": "https://api.devicecloud.dev",
50
+ "hasDynamicHelp": false,
51
+ "multiple": false,
52
+ "type": "option"
53
+ },
54
+ "appBinaryId": {
55
+ "aliases": [
56
+ "app-binary-id"
57
+ ],
58
+ "description": "The ID of the app binary previously uploaded to Maestro Cloud",
59
+ "name": "appBinaryId",
60
+ "hasDynamicHelp": false,
61
+ "multiple": false,
62
+ "type": "option"
63
+ },
64
+ "appFile": {
65
+ "aliases": [
66
+ "app-file"
67
+ ],
68
+ "description": "App binary to run your Flows against",
69
+ "name": "appFile",
70
+ "hasDynamicHelp": false,
71
+ "multiple": false,
72
+ "type": "option"
73
+ },
74
+ "env": {
75
+ "aliases": [
76
+ "env"
77
+ ],
78
+ "char": "e",
79
+ "description": "One or more environment variables to inject into your Flows",
80
+ "name": "env",
81
+ "hasDynamicHelp": false,
82
+ "multiple": true,
83
+ "type": "option"
84
+ },
85
+ "iOSVersion": {
86
+ "aliases": [
87
+ "ios-version"
88
+ ],
89
+ "description": "iOS version to run your flow against",
90
+ "name": "iOSVersion",
91
+ "hasDynamicHelp": false,
92
+ "multiple": false,
93
+ "type": "option"
94
+ }
95
+ },
96
+ "hasDynamicHelp": false,
97
+ "hiddenAliases": [],
98
+ "id": "cloud",
99
+ "pluginAlias": "@devicecloud.dev/dcd",
100
+ "pluginName": "@devicecloud.dev/dcd",
101
+ "pluginType": "core",
102
+ "strict": true,
103
+ "enableJsonFlag": false,
104
+ "isESM": false,
105
+ "relativePath": [
106
+ "dist",
107
+ "commands",
108
+ "cloud.js"
109
+ ]
110
+ }
111
+ },
112
+ "version": "0.0.1-alpha.0"
113
+ }
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "author": "devicecloud.dev",
3
+ "bin": {
4
+ "dcd": "bin/run.js"
5
+ },
6
+ "dependencies": {
7
+ "@oclif/core": "^3",
8
+ "@oclif/plugin-autocomplete": "^3.0.8",
9
+ "@oclif/plugin-help": "^6",
10
+ "@oclif/plugin-not-found": "^3.0.10",
11
+ "@oclif/plugin-plugins": "^4",
12
+ "@oclif/plugin-update": "^4.1.11",
13
+ "@oclif/plugin-warn-if-update-available": "^3.0.10"
14
+ },
15
+ "description": "Better cloud maestro testing",
16
+ "devDependencies": {
17
+ "@oclif/prettier-config": "^0.2.1",
18
+ "@oclif/test": "^3",
19
+ "@types/chai": "^4",
20
+ "@types/mocha": "^9.0.0",
21
+ "chai": "^4",
22
+ "eslint": "^8.56.0",
23
+ "eslint-config-oclif": "^5",
24
+ "eslint-config-oclif-typescript": "^3",
25
+ "eslint-config-prettier": "^9.1.0",
26
+ "mocha": "^10",
27
+ "oclif": "^4",
28
+ "shx": "^0.3.3",
29
+ "ts-node": "^10.9.2",
30
+ "typescript": "^5"
31
+ },
32
+ "engines": {
33
+ "node": ">=21.0.0"
34
+ },
35
+ "files": [
36
+ "/bin",
37
+ "/dist",
38
+ "/oclif.manifest.json"
39
+ ],
40
+ "homepage": "https://devicecloud.dev",
41
+ "license": "MIT",
42
+ "main": "dist/index.js",
43
+ "name": "@devicecloud.dev/dcd",
44
+ "oclif": {
45
+ "bin": "dcd",
46
+ "dirname": "dcd",
47
+ "commands": "./dist/commands",
48
+ "plugins": [
49
+ "@oclif/plugin-help",
50
+ "@oclif/plugin-not-found",
51
+ "@oclif/plugin-update",
52
+ "@oclif/plugin-warn-if-update-available",
53
+ "@oclif/plugin-autocomplete"
54
+ ]
55
+ },
56
+ "private": false,
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "@devicecloud.dev/dcd"
60
+ },
61
+ "scripts": {
62
+ "build": "shx rm -rf dist && tsc -b",
63
+ "lint": "eslint . --ext .ts",
64
+ "postpack": "shx rm -f oclif.manifest.json",
65
+ "posttest": "yarn lint",
66
+ "prepack": "yarn build && oclif manifest",
67
+ "prepare": "yarn build",
68
+ "test": "mocha --forbid-only \"test/**/*.test.ts\"",
69
+ "version": "oclif readme && git add README.md"
70
+ },
71
+ "version": "0.0.1-alpha.0",
72
+ "bugs": {
73
+ "url": "https://discord.gg/GzZBHcUJ"
74
+ },
75
+ "keywords": [
76
+ "oclif"
77
+ ],
78
+ "types": "dist/index.d.ts",
79
+ "directories": {
80
+ "test": "test"
81
+ }
82
+ }