@devicecloud.dev/dcd 0.0.4 → 0.0.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.
- package/dist/commands/cloud.js +15 -7
- package/dist/methods.d.ts +2 -0
- package/dist/methods.js +28 -1
- package/dist/plan.js +20 -45
- package/dist/planMethods.d.ts +11 -0
- package/dist/planMethods.js +49 -2
- package/oclif.manifest.json +2 -2
- package/package.json +1 -1
package/dist/commands/cloud.js
CHANGED
|
@@ -36,7 +36,7 @@ var EiOSDevices;
|
|
|
36
36
|
class Cloud extends core_1.Command {
|
|
37
37
|
static args = {
|
|
38
38
|
firstFile: core_1.Args.string({
|
|
39
|
-
description: 'The binary file of the app to run your flow against, e.g.
|
|
39
|
+
description: 'The binary file of the app to run your flow against, e.g. test.apk for android or test.app/.zip for ios',
|
|
40
40
|
hidden: true,
|
|
41
41
|
name: 'App file',
|
|
42
42
|
}),
|
|
@@ -51,6 +51,7 @@ class Cloud extends core_1.Command {
|
|
|
51
51
|
static flags = constants_1.flags;
|
|
52
52
|
async run() {
|
|
53
53
|
try {
|
|
54
|
+
await (0, methods_1.versionCheck)(this.config.version);
|
|
54
55
|
const { args, flags, raw } = await this.parse(Cloud);
|
|
55
56
|
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;
|
|
56
57
|
if (arm64) {
|
|
@@ -98,8 +99,8 @@ class Cloud extends core_1.Command {
|
|
|
98
99
|
if (!(flowFile && finalAppFile)) {
|
|
99
100
|
throw new Error('You must provide a flow file and an app binary id');
|
|
100
101
|
}
|
|
101
|
-
if (!
|
|
102
|
-
throw new Error('App file must be a .apk or .zip file');
|
|
102
|
+
if (!['apk', '.app', '.zip'].some((ext) => finalAppFile.endsWith(ext))) {
|
|
103
|
+
throw new Error('App file must be a .apk for android or .app/.zip file for iOS');
|
|
103
104
|
}
|
|
104
105
|
if (finalAppFile.endsWith('.zip')) {
|
|
105
106
|
await (0, methods_1.verifyAppZip)(finalAppFile);
|
|
@@ -125,10 +126,16 @@ class Cloud extends core_1.Command {
|
|
|
125
126
|
if (!finalBinaryId) {
|
|
126
127
|
core_1.ux.action.start('Uploading binary', 'Initializing', { stdout: true });
|
|
127
128
|
const binaryFormData = new FormData();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
if (finalAppFile?.endsWith('.app')) {
|
|
130
|
+
const zippedAppBlob = await (0, methods_1.compressFolderToBlob)(finalAppFile);
|
|
131
|
+
binaryFormData.set('file', zippedAppBlob, finalAppFile + '.zip');
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
|
|
135
|
+
type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
|
|
136
|
+
});
|
|
137
|
+
binaryFormData.set('file', binaryBlob, finalAppFile);
|
|
138
|
+
}
|
|
132
139
|
const options = {
|
|
133
140
|
body: binaryFormData,
|
|
134
141
|
headers: { 'x-app-api-key': apiKey },
|
|
@@ -171,6 +178,7 @@ class Cloud extends core_1.Command {
|
|
|
171
178
|
continueOnFailure,
|
|
172
179
|
orientation,
|
|
173
180
|
raw,
|
|
181
|
+
version: this.config.version,
|
|
174
182
|
}));
|
|
175
183
|
if (androidApiLevel)
|
|
176
184
|
testFormData.set('androidApiLevel', androidApiLevel.toString());
|
package/dist/methods.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import * as archiver from 'archiver';
|
|
3
3
|
import { paths } from '../../api/schema.types';
|
|
4
|
+
export declare const versionCheck: (currentVersion: string) => Promise<void>;
|
|
4
5
|
export declare const typeSafePost: <T extends keyof paths>(baseUrl: string, path: string, init?: {
|
|
5
6
|
body?: FormData;
|
|
6
7
|
headers?: HeadersInit;
|
|
@@ -11,5 +12,6 @@ export declare const typeSafeGet: <T extends keyof paths>(baseUrl: string, path:
|
|
|
11
12
|
}) => Promise<paths[T]["get"]["responses"]["200"]["content"]["application/json"]>;
|
|
12
13
|
export declare const toBuffer: (archive: archiver.Archiver) => Promise<Buffer>;
|
|
13
14
|
export declare const compressDir: (sourceDir: string) => Promise<Buffer>;
|
|
15
|
+
export declare const compressFolderToBlob: (sourceDir: string) => Promise<Blob>;
|
|
14
16
|
export declare const compressFilesFromRelativePath: (path: string, files: string[]) => Promise<Buffer>;
|
|
15
17
|
export declare const verifyAppZip: (zipPath: string) => Promise<void>;
|
package/dist/methods.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressDir = exports.toBuffer = exports.typeSafeGet = exports.typeSafePost = void 0;
|
|
3
|
+
exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressFolderToBlob = exports.compressDir = exports.toBuffer = exports.typeSafeGet = exports.typeSafePost = exports.versionCheck = void 0;
|
|
4
|
+
const cli_ux_1 = require("@oclif/core/lib/cli-ux");
|
|
4
5
|
const archiver = require("archiver");
|
|
5
6
|
// import { writeFile } from 'node:fs/promises';
|
|
6
7
|
const nodePath = require("node:path");
|
|
@@ -16,6 +17,20 @@ const PERMITTED_EXTENSIONS = new Set([
|
|
|
16
17
|
'mp4',
|
|
17
18
|
'js',
|
|
18
19
|
]);
|
|
20
|
+
const versionCheck = async (currentVersion) => {
|
|
21
|
+
const versionResponse = await fetch('https://registry.npmjs.org/@devicecloud.dev/dcd/latest');
|
|
22
|
+
const versionResponseJson = await versionResponse.json();
|
|
23
|
+
const latestVersion = versionResponseJson.version;
|
|
24
|
+
if (latestVersion !== currentVersion) {
|
|
25
|
+
(0, cli_ux_1.warn)(`
|
|
26
|
+
-------------------
|
|
27
|
+
A new version of the devicecloud.dev CLI is available: ${latestVersion}
|
|
28
|
+
Run 'npm install -g @devicecloud.dev/dcd' to update to the latest version
|
|
29
|
+
-------------------
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.versionCheck = versionCheck;
|
|
19
34
|
const typeSafePost = async (baseUrl, path, init) => {
|
|
20
35
|
const res = await fetch(baseUrl + path, { ...init, method: 'POST' });
|
|
21
36
|
if (!res.ok) {
|
|
@@ -68,6 +83,18 @@ const compressDir = async (sourceDir) => {
|
|
|
68
83
|
return buffer;
|
|
69
84
|
};
|
|
70
85
|
exports.compressDir = compressDir;
|
|
86
|
+
const compressFolderToBlob = async (sourceDir) => {
|
|
87
|
+
const archive = archiver('zip', {
|
|
88
|
+
zlib: { level: 9 },
|
|
89
|
+
});
|
|
90
|
+
archive.on('error', (err) => {
|
|
91
|
+
throw err;
|
|
92
|
+
});
|
|
93
|
+
archive.directory(sourceDir, sourceDir.split('/').pop());
|
|
94
|
+
const buffer = await (0, exports.toBuffer)(archive);
|
|
95
|
+
return new Blob([buffer], { type: 'application/zip' });
|
|
96
|
+
};
|
|
97
|
+
exports.compressFolderToBlob = compressFolderToBlob;
|
|
71
98
|
const compressFilesFromRelativePath = async (path, files) => {
|
|
72
99
|
const archive = archiver('zip', {
|
|
73
100
|
zlib: { level: 9 },
|
package/dist/plan.js
CHANGED
|
@@ -1,35 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.plan = void 0;
|
|
4
|
-
/* eslint-disable complexity */
|
|
5
4
|
const glob_1 = require("glob");
|
|
6
5
|
const fs = require("node:fs");
|
|
7
6
|
const path = require("node:path");
|
|
8
7
|
const planMethods_1 = require("./planMethods");
|
|
9
|
-
const commandsThatRequireFiles = new Set(['addMedia', 'runFlow', 'runScript']);
|
|
10
8
|
async function plan(input, includeTags, excludeTags) {
|
|
11
9
|
if (!fs.existsSync(input)) {
|
|
12
10
|
throw new Error(`Flow path does not exist: ${path.resolve(input)}`);
|
|
13
11
|
}
|
|
14
12
|
if (fs.lstatSync(input).isFile()) {
|
|
15
|
-
const directory = path.dirname(input);
|
|
16
|
-
const { testSteps } = (0, planMethods_1.readTestYamlFileAsJson)(input);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const { errors: newErrors, files } = (0, planMethods_1.checkIfFilesExistInWorkspace)(commandName, commandValue, path.normalize(input), directory + '/');
|
|
25
|
-
errors = [...errors, ...newErrors];
|
|
26
|
-
allFiles = [...allFiles, ...files];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if (errors.length > 0) {
|
|
13
|
+
const directory = path.dirname(input) + '/';
|
|
14
|
+
const { config, testSteps } = (0, planMethods_1.readTestYamlFileAsJson)(input);
|
|
15
|
+
const { allErrors, allFiles } = (0, planMethods_1.processDependencies)({
|
|
16
|
+
config,
|
|
17
|
+
directory,
|
|
18
|
+
input,
|
|
19
|
+
testSteps,
|
|
20
|
+
});
|
|
21
|
+
if (allErrors.length > 0) {
|
|
31
22
|
throw new Error('The following flow files are not present in the provided directory: \n' +
|
|
32
|
-
|
|
23
|
+
allErrors.join('\n'));
|
|
33
24
|
}
|
|
34
25
|
return {
|
|
35
26
|
flowsToRun: [input.split('/').pop() ?? input],
|
|
@@ -84,20 +75,17 @@ async function plan(input, includeTags, excludeTags) {
|
|
|
84
75
|
});
|
|
85
76
|
let errors = [];
|
|
86
77
|
let allFiles = [];
|
|
87
|
-
for (const [filePath,
|
|
88
|
-
if (!
|
|
78
|
+
for (const [filePath, testSteps] of Object.entries(testStepsPerFlowFile)) {
|
|
79
|
+
if (!testSteps)
|
|
89
80
|
break;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
81
|
+
const { allErrors: errs, allFiles: deps } = (0, planMethods_1.processDependencies)({
|
|
82
|
+
config: configPerFlowFile[path.relative(cleanPath, filePath)],
|
|
83
|
+
directory: cleanPath,
|
|
84
|
+
input: filePath,
|
|
85
|
+
testSteps,
|
|
86
|
+
});
|
|
87
|
+
allFiles = [...allFiles, ...deps];
|
|
88
|
+
errors = [...errors, ...errs];
|
|
101
89
|
}
|
|
102
90
|
if (errors.length > 0) {
|
|
103
91
|
throw new Error('The following flow files are not present in the provided directory: \n' +
|
|
@@ -132,19 +120,6 @@ async function plan(input, includeTags, excludeTags) {
|
|
|
132
120
|
?.map((flowOrder) => (0, planMethods_1.getFlowsToRunInSequence)(pathsByName, [flowOrder]))
|
|
133
121
|
.flat() || [];
|
|
134
122
|
const normalFlows = allFlows.filter((flow) => !flowsToRunInSequence.includes(flow));
|
|
135
|
-
// for (const filePath of allFlows) {
|
|
136
|
-
// const commands = YamlCommandReader.readCommands(filePath).filter(
|
|
137
|
-
// (command) => command.addMediaCommand,
|
|
138
|
-
// );
|
|
139
|
-
// const mediaPaths = commands.flatMap(
|
|
140
|
-
// (command) => command.addMediaCommand.mediaPaths,
|
|
141
|
-
// );
|
|
142
|
-
// YamlCommandsPathValidator.validatePathsExistInWorkspace(
|
|
143
|
-
// input,
|
|
144
|
-
// filePath,
|
|
145
|
-
// mediaPaths,
|
|
146
|
-
// );
|
|
147
|
-
// }
|
|
148
123
|
return {
|
|
149
124
|
allExcludeTags,
|
|
150
125
|
allIncludeTags,
|
package/dist/planMethods.d.ts
CHANGED
|
@@ -15,3 +15,14 @@ export declare const checkIfFilesExistInWorkspace: (commandName: string, command
|
|
|
15
15
|
errors: string[];
|
|
16
16
|
files: string[];
|
|
17
17
|
};
|
|
18
|
+
interface IProcessDependencies {
|
|
19
|
+
config?: Record<string, unknown> | null;
|
|
20
|
+
directory: string;
|
|
21
|
+
input: string;
|
|
22
|
+
testSteps: Record<string, unknown>[];
|
|
23
|
+
}
|
|
24
|
+
export declare const processDependencies: ({ config, directory, input, testSteps, }: IProcessDependencies) => {
|
|
25
|
+
allErrors: string[];
|
|
26
|
+
allFiles: string[];
|
|
27
|
+
};
|
|
28
|
+
export {};
|
package/dist/planMethods.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkIfFilesExistInWorkspace = exports.walk = exports.readTestYamlFileAsJson = exports.readYamlFileAsJson = exports.isFlowFile = exports.getFlowsToRunInSequence = void 0;
|
|
3
|
+
exports.processDependencies = exports.checkIfFilesExistInWorkspace = exports.walk = exports.readTestYamlFileAsJson = exports.readYamlFileAsJson = exports.isFlowFile = exports.getFlowsToRunInSequence = void 0;
|
|
4
4
|
/* eslint-disable unicorn/filename-case */
|
|
5
5
|
const yaml = require("js-yaml");
|
|
6
6
|
const fs = require("node:fs");
|
|
7
7
|
const path = require("node:path");
|
|
8
|
+
const commandsThatRequireFiles = new Set(['addMedia', 'runFlow', 'runScript']);
|
|
8
9
|
function getFlowsToRunInSequence(paths, flowOrder) {
|
|
9
10
|
if (flowOrder.length === 0)
|
|
10
11
|
return [];
|
|
@@ -77,7 +78,7 @@ const checkIfFilesExistInWorkspace = (commandName, command, filePath, cleanPath)
|
|
|
77
78
|
const errors = [];
|
|
78
79
|
const files = [];
|
|
79
80
|
const directory = path.dirname(filePath);
|
|
80
|
-
const buildError = (error) => `Flow file "${filePath}" has a command "${commandName}" that references a ${error}
|
|
81
|
+
const buildError = (error) => `Flow file "${filePath.replace(cleanPath, './')}" has a command "${commandName}" that references a ${error} ${JSON.stringify(command)}`;
|
|
81
82
|
const processFilePath = (relativePath) => {
|
|
82
83
|
const absoluteFilePath = path.resolve(directory, relativePath);
|
|
83
84
|
const error = checkFile(absoluteFilePath, cleanPath);
|
|
@@ -107,3 +108,49 @@ const checkFile = (filePath, cleanPath) => {
|
|
|
107
108
|
if (!filePath.startsWith(cleanPath))
|
|
108
109
|
return `file outside the workspace`;
|
|
109
110
|
};
|
|
111
|
+
const checkStepsArray = (steps, input, directory) => {
|
|
112
|
+
let errors = [];
|
|
113
|
+
let files = [];
|
|
114
|
+
for (const command of steps) {
|
|
115
|
+
if (typeof command === 'string')
|
|
116
|
+
continue;
|
|
117
|
+
for (const [commandName, commandValue] of Object.entries(command)) {
|
|
118
|
+
if (commandsThatRequireFiles.has(commandName)) {
|
|
119
|
+
const { errors: newErrors, files: newFiles } = (0, exports.checkIfFilesExistInWorkspace)(commandName, commandValue, path.normalize(input), directory);
|
|
120
|
+
errors = [...errors, ...newErrors];
|
|
121
|
+
files = [...files, ...newFiles];
|
|
122
|
+
}
|
|
123
|
+
const nestedCommands = typeof commandValue === 'object' &&
|
|
124
|
+
commandValue.commands;
|
|
125
|
+
if (nestedCommands) {
|
|
126
|
+
const { errors: newErrors, files: newFiles } = checkStepsArray(nestedCommands, input, directory);
|
|
127
|
+
errors = [...errors, ...newErrors];
|
|
128
|
+
files = [...files, ...newFiles];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { errors, files };
|
|
133
|
+
};
|
|
134
|
+
const processDependencies = ({ config, directory, input, testSteps, }) => {
|
|
135
|
+
let allErrors = [];
|
|
136
|
+
let allFiles = [];
|
|
137
|
+
const { onFlowComplete, onFlowStart } = config ?? {};
|
|
138
|
+
const stepsArray = [testSteps];
|
|
139
|
+
if (onFlowStart)
|
|
140
|
+
stepsArray.push(onFlowStart);
|
|
141
|
+
if (onFlowComplete)
|
|
142
|
+
stepsArray.push(onFlowComplete);
|
|
143
|
+
for (const [index, steps] of stepsArray.entries()) {
|
|
144
|
+
try {
|
|
145
|
+
const { errors, files } = checkStepsArray(steps, input, directory);
|
|
146
|
+
allErrors = [...allErrors, ...errors];
|
|
147
|
+
allFiles = [...allFiles, ...files];
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
const location = ['Test Steps', 'On Flow Start', 'On Flow Complete'][index];
|
|
151
|
+
throw new Error(`Error processing dependencies for file ${input} in the ${location} section. Expected an array of steps but received: ${JSON.stringify(steps)}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return { allErrors, allFiles };
|
|
155
|
+
};
|
|
156
|
+
exports.processDependencies = processDependencies;
|
package/oclif.manifest.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"aliases": [],
|
|
5
5
|
"args": {
|
|
6
6
|
"firstFile": {
|
|
7
|
-
"description": "The binary file of the app to run your flow against, e.g.
|
|
7
|
+
"description": "The binary file of the app to run your flow against, e.g. test.apk for android or test.app/.zip for ios",
|
|
8
8
|
"hidden": true,
|
|
9
9
|
"name": "firstFile"
|
|
10
10
|
},
|
|
@@ -220,5 +220,5 @@
|
|
|
220
220
|
]
|
|
221
221
|
}
|
|
222
222
|
},
|
|
223
|
-
"version": "0.0.
|
|
223
|
+
"version": "0.0.6"
|
|
224
224
|
}
|