@appium/support 2.55.3 → 2.56.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/build/lib/env.js +102 -0
- package/build/lib/fs.js +74 -56
- package/build/lib/image-util.js +4 -339
- package/build/lib/index.js +18 -6
- package/build/lib/log-internal.js +2 -4
- package/build/lib/logger.js +2 -4
- package/build/lib/logging.js +2 -4
- package/build/lib/mjpeg.js +2 -4
- package/build/lib/mkdirp.js +7 -11
- package/build/lib/net.js +2 -4
- package/build/lib/node.js +99 -2
- package/build/lib/npm.js +240 -0
- package/build/lib/plist.js +2 -4
- package/build/lib/process.js +2 -4
- package/build/lib/system.js +2 -4
- package/build/lib/tempdir.js +2 -4
- package/build/lib/timing.js +2 -4
- package/build/lib/util.js +6 -8
- package/build/lib/zip.js +4 -8
- package/lib/env.js +162 -0
- package/lib/fs.js +193 -69
- package/lib/image-util.js +3 -578
- package/lib/index.js +8 -2
- package/lib/log-internal.js +2 -2
- package/lib/logging.js +1 -1
- package/lib/mkdirp.js +3 -6
- package/lib/net.js +4 -4
- package/lib/node.js +104 -1
- package/lib/npm.js +335 -0
- package/lib/tempdir.js +6 -6
- package/lib/util.js +28 -24
- package/lib/zip.js +7 -8
- package/package.json +21 -9
- package/build/test/assets/sample_binary.plist +0 -0
- package/build/test/assets/sample_text.plist +0 -28
- package/build/test/fs-specs.js +0 -264
- package/build/test/helpers.js +0 -35
- package/build/test/image-util-e2e-specs.js +0 -227
- package/build/test/index-specs.js +0 -49
- package/build/test/log-internals-specs.js +0 -97
- package/build/test/logger/helpers.js +0 -71
- package/build/test/logger/logger-force-specs.js +0 -41
- package/build/test/logger/logger-normal-specs.js +0 -113
- package/build/test/logger/logger-test-specs.js +0 -40
- package/build/test/mjpeg-e2e-specs.js +0 -96
- package/build/test/net-e2e-specs.js +0 -32
- package/build/test/node-e2e-specs.js +0 -22
- package/build/test/plist-specs.js +0 -54
- package/build/test/process-specs.js +0 -104
- package/build/test/system-specs.js +0 -136
- package/build/test/tempdir-specs.js +0 -86
- package/build/test/timing-specs.js +0 -125
- package/build/test/util-e2e-specs.js +0 -136
- package/build/test/util-specs.js +0 -537
- package/build/test/zip-e2e-specs.js +0 -233
package/lib/node.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { isWindows } from './system';
|
|
2
2
|
import log from './logger';
|
|
3
|
+
import _ from 'lodash';
|
|
3
4
|
import { exec } from 'teen_process';
|
|
4
5
|
import path from 'path';
|
|
6
|
+
import { v4 as uuidV4 } from 'uuid';
|
|
7
|
+
|
|
8
|
+
const ECMA_SIZES = Object.freeze({
|
|
9
|
+
STRING: 2,
|
|
10
|
+
BOOLEAN: 4,
|
|
11
|
+
NUMBER: 8,
|
|
12
|
+
});
|
|
5
13
|
|
|
6
14
|
/**
|
|
7
15
|
* Internal utility to link global package to local context
|
|
@@ -63,4 +71,99 @@ async function requirePackage (packageName) {
|
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
function extractAllProperties (obj) {
|
|
75
|
+
const stringProperties = [];
|
|
76
|
+
for (const prop in obj) {
|
|
77
|
+
stringProperties.push(prop);
|
|
78
|
+
}
|
|
79
|
+
if (_.isFunction(Object.getOwnPropertySymbols)) {
|
|
80
|
+
stringProperties.push(...(Object.getOwnPropertySymbols(obj)));
|
|
81
|
+
}
|
|
82
|
+
return stringProperties;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function _getSizeOfObject (seen, object) {
|
|
86
|
+
if (_.isNil(object)) {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let bytes = 0;
|
|
91
|
+
const properties = extractAllProperties(object);
|
|
92
|
+
for (const key of properties) {
|
|
93
|
+
// Do not recalculate circular references
|
|
94
|
+
if (typeof object[key] === 'object' && !_.isNil(object[key])) {
|
|
95
|
+
if (seen.has(object[key])) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
seen.add(object[key]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
bytes += getCalculator(seen)(key);
|
|
102
|
+
try {
|
|
103
|
+
bytes += getCalculator(seen)(object[key]);
|
|
104
|
+
} catch (ex) {
|
|
105
|
+
if (ex instanceof RangeError) {
|
|
106
|
+
// circular reference detected, final result might be incorrect
|
|
107
|
+
// let's be nice and not throw an exception
|
|
108
|
+
bytes = 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return bytes;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getCalculator (seen) {
|
|
117
|
+
return function calculator (obj) {
|
|
118
|
+
if (_.isBuffer(obj)) {
|
|
119
|
+
return obj.length;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
switch (typeof (obj)) {
|
|
123
|
+
case 'string':
|
|
124
|
+
return obj.length * ECMA_SIZES.STRING;
|
|
125
|
+
case 'boolean':
|
|
126
|
+
return ECMA_SIZES.BOOLEAN;
|
|
127
|
+
case 'number':
|
|
128
|
+
return ECMA_SIZES.NUMBER;
|
|
129
|
+
case 'symbol':
|
|
130
|
+
return _.isFunction(Symbol.keyFor) && Symbol.keyFor(obj)
|
|
131
|
+
? Symbol.keyFor(obj).length * ECMA_SIZES.STRING
|
|
132
|
+
: (obj.toString().length - 8) * ECMA_SIZES.STRING;
|
|
133
|
+
case 'object':
|
|
134
|
+
return _.isArray(obj)
|
|
135
|
+
? obj.map(getCalculator(seen)).reduce((acc, curr) => acc + curr, 0)
|
|
136
|
+
: _getSizeOfObject(seen, obj);
|
|
137
|
+
default:
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Calculate the in-depth size in memory of the provided object.
|
|
145
|
+
* The original implementation is borrowed from https://github.com/miktam/sizeof.
|
|
146
|
+
*
|
|
147
|
+
* @param {*} obj An object whose size should be calculated
|
|
148
|
+
* @returns {number} Object size in bytes.
|
|
149
|
+
*/
|
|
150
|
+
function getObjectSize (obj) {
|
|
151
|
+
return getCalculator(new WeakSet())(obj);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const OBJECTS_MAPPING = new WeakMap();
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Calculates a unique object identifier
|
|
158
|
+
*
|
|
159
|
+
* @param {object} object Any valid ECMA object
|
|
160
|
+
* @returns {string} A uuidV4 string that uniquely identifies given object
|
|
161
|
+
*/
|
|
162
|
+
function getObjectId (object) {
|
|
163
|
+
if (!OBJECTS_MAPPING.has(object)) {
|
|
164
|
+
OBJECTS_MAPPING.set(object, uuidV4());
|
|
165
|
+
}
|
|
166
|
+
return OBJECTS_MAPPING.get(object);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { requirePackage, getObjectSize, getObjectId };
|
package/lib/npm.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
import { exec } from 'teen_process';
|
|
6
|
+
import { fs } from './fs';
|
|
7
|
+
import * as util from './util';
|
|
8
|
+
import * as system from './system';
|
|
9
|
+
import resolveFrom from 'resolve-from';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Relative path to directory containing any Appium internal files
|
|
14
|
+
* XXX: this is duplicated in `appium/lib/constants.js`.
|
|
15
|
+
*/
|
|
16
|
+
export const CACHE_DIR_RELATIVE_PATH = path.join(
|
|
17
|
+
'node_modules',
|
|
18
|
+
'.cache',
|
|
19
|
+
'appium',
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Relative path to lockfile used when installing an extension via `appium`
|
|
24
|
+
*/
|
|
25
|
+
export const INSTALL_LOCKFILE_RELATIVE_PATH = path.join(
|
|
26
|
+
CACHE_DIR_RELATIVE_PATH,
|
|
27
|
+
'.install.lock',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Relative path to lockfile used when linking an extension via `appium`
|
|
32
|
+
*/
|
|
33
|
+
export const LINK_LOCKFILE_RELATIVE_PATH = path.join(
|
|
34
|
+
CACHE_DIR_RELATIVE_PATH,
|
|
35
|
+
'.link.lock',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* XXX: This should probably be a singleton, but it isn't. Maybe this module should just export functions?
|
|
40
|
+
*/
|
|
41
|
+
export class NPM {
|
|
42
|
+
/**
|
|
43
|
+
* Returns path to "install" lockfile
|
|
44
|
+
* @private
|
|
45
|
+
* @param {string} cwd
|
|
46
|
+
*/
|
|
47
|
+
_getInstallLockfilePath (cwd) {
|
|
48
|
+
return path.join(cwd, INSTALL_LOCKFILE_RELATIVE_PATH);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns path to "link" lockfile
|
|
53
|
+
* @private
|
|
54
|
+
* @param {string} cwd
|
|
55
|
+
*/
|
|
56
|
+
_getLinkLockfilePath (cwd) {
|
|
57
|
+
return path.join(cwd, LINK_LOCKFILE_RELATIVE_PATH);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Execute `npm` with given args.
|
|
62
|
+
*
|
|
63
|
+
* If the process exits with a nonzero code, the contents of `STDOUT` and `STDERR` will be in the
|
|
64
|
+
* `message` of the {@link TeenProcessExecError} rejected.
|
|
65
|
+
* @param {string} cmd
|
|
66
|
+
* @param {string[]} args
|
|
67
|
+
* @param {ExecOpts} opts
|
|
68
|
+
* @param {TeenProcessExecOpts} [execOpts]
|
|
69
|
+
*/
|
|
70
|
+
async exec (cmd, args, opts, execOpts = {}) {
|
|
71
|
+
let { cwd, json, lockFile } = opts;
|
|
72
|
+
|
|
73
|
+
// make sure we perform the current operation in cwd
|
|
74
|
+
execOpts = {...execOpts, cwd};
|
|
75
|
+
|
|
76
|
+
args.unshift(cmd);
|
|
77
|
+
if (json) {
|
|
78
|
+
args.push('--json');
|
|
79
|
+
}
|
|
80
|
+
const npmCmd = system.isWindows() ? 'npm.cmd' : 'npm';
|
|
81
|
+
let runner = async () => await exec(npmCmd, args, execOpts);
|
|
82
|
+
if (lockFile) {
|
|
83
|
+
const acquireLock = util.getLockFileGuard(lockFile);
|
|
84
|
+
const _runner = runner;
|
|
85
|
+
runner = async () => await acquireLock(_runner);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let ret;
|
|
89
|
+
try {
|
|
90
|
+
const {stdout, stderr, code} = await runner();
|
|
91
|
+
ret = /** @type {TeenProcessExecResult} */({stdout, stderr, code});
|
|
92
|
+
// if possible, parse NPM's json output. During NPM install 3rd-party
|
|
93
|
+
// packages can write to stdout, so sometimes the json output can't be
|
|
94
|
+
// guaranteed to be parseable
|
|
95
|
+
try {
|
|
96
|
+
ret.json = JSON.parse(stdout);
|
|
97
|
+
} catch (ign) {}
|
|
98
|
+
} catch (e) {
|
|
99
|
+
const {stdout = '', stderr = '', code = null} = /** @type {TeenProcessExecError} */(e);
|
|
100
|
+
const err = new Error(`npm command '${args.join(' ')}' failed with code ${code}.\n\nSTDOUT:\n${stdout.trim()}\n\nSTDERR:\n${stderr.trim()}`);
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
return ret;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {string} cwd
|
|
108
|
+
* @param {string} pkg
|
|
109
|
+
*/
|
|
110
|
+
async getLatestVersion (cwd, pkg) {
|
|
111
|
+
return (await this.exec('view', [pkg, 'dist-tags'], {
|
|
112
|
+
json: true,
|
|
113
|
+
cwd
|
|
114
|
+
})).json?.latest;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {string} cwd
|
|
119
|
+
* @param {string} pkg
|
|
120
|
+
* @param {string} curVersion
|
|
121
|
+
*/
|
|
122
|
+
async getLatestSafeUpgradeVersion (cwd, pkg, curVersion) {
|
|
123
|
+
const allVersions = (await this.exec('view', [pkg, 'versions'], {
|
|
124
|
+
json: true,
|
|
125
|
+
cwd
|
|
126
|
+
})).json;
|
|
127
|
+
return this.getLatestSafeUpgradeFromVersions(curVersion, allVersions);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Runs `npm ls`, optionally for a particular package.
|
|
132
|
+
* @param {string} cwd
|
|
133
|
+
* @param {string} [pkg]
|
|
134
|
+
*/
|
|
135
|
+
async list (cwd, pkg) {
|
|
136
|
+
return (await this.exec('list', pkg ? [pkg] : [], {cwd, json: true})).json;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Given a current version and a list of all versions for a package, return the version which is
|
|
141
|
+
* the highest safely-upgradable version (meaning not crossing any major revision boundaries, and
|
|
142
|
+
* not including any alpha/beta/rc versions)
|
|
143
|
+
*
|
|
144
|
+
* @param {string} curVersion - the current version of a package
|
|
145
|
+
* @param {Array<string>} allVersions - a list of version strings
|
|
146
|
+
*
|
|
147
|
+
* @return {string|null} - the highest safely-upgradable version, or null if there isn't one
|
|
148
|
+
*/
|
|
149
|
+
getLatestSafeUpgradeFromVersions (curVersion, allVersions) {
|
|
150
|
+
let safeUpgradeVer = null;
|
|
151
|
+
const curSemver = semver.parse(curVersion);
|
|
152
|
+
if (curSemver === null) {
|
|
153
|
+
throw new Error(`Could not parse current version '${curVersion}'`);
|
|
154
|
+
}
|
|
155
|
+
for (const testVer of allVersions) {
|
|
156
|
+
const testSemver = semver.parse(testVer);
|
|
157
|
+
if (testSemver === null) {
|
|
158
|
+
throw new Error(`Could not parse version to test against: '${testVer}'`);
|
|
159
|
+
}
|
|
160
|
+
// if the test version is a prerelease, ignore it
|
|
161
|
+
if (testSemver.prerelease.length > 0) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
// if the current version is later than the test version, skip this test version
|
|
165
|
+
if (curSemver.compare(testSemver) === 1) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
// if the test version is newer, but crosses a major revision boundary, also skip it
|
|
169
|
+
if (testSemver.major > curSemver.major) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// otherwise this version is safe to upgrade to. But there might be multiple ones of this
|
|
173
|
+
// kind, so keep iterating and keeping the highest
|
|
174
|
+
if (safeUpgradeVer === null || testSemver.compare(safeUpgradeVer) === 1) {
|
|
175
|
+
safeUpgradeVer = testSemver;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (safeUpgradeVer) {
|
|
179
|
+
safeUpgradeVer = safeUpgradeVer.format();
|
|
180
|
+
}
|
|
181
|
+
return safeUpgradeVer;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Installs a package w/ `npm`
|
|
186
|
+
* @param {string} cwd
|
|
187
|
+
* @param {string} pkgName
|
|
188
|
+
* @param {InstallPackageOpts} [opts]
|
|
189
|
+
* @returns {Promise<import('type-fest').PackageJson>}
|
|
190
|
+
*/
|
|
191
|
+
async installPackage (cwd, pkgName, {pkgVer} = {}) {
|
|
192
|
+
// not only this, this directory needs a 'package.json' inside of it, otherwise, if any
|
|
193
|
+
// directory in the filesystem tree ABOVE cwd happens to have a package.json or a node_modules
|
|
194
|
+
// dir in it, NPM will install the module up there instead (silly NPM)
|
|
195
|
+
const dummyPkgJson = path.resolve(cwd, 'package.json');
|
|
196
|
+
if (!await fs.exists(dummyPkgJson)) {
|
|
197
|
+
await fs.writeFile(dummyPkgJson, '{}');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const res = await this.exec('install', [
|
|
201
|
+
'--no-save',
|
|
202
|
+
'--global-style',
|
|
203
|
+
'--no-package-lock',
|
|
204
|
+
pkgVer ? `${pkgName}@${pkgVer}` : pkgName
|
|
205
|
+
], {
|
|
206
|
+
cwd,
|
|
207
|
+
json: true,
|
|
208
|
+
lockFile: this._getInstallLockfilePath(cwd)
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (res.json) {
|
|
212
|
+
// we parsed a valid json response, so if we got an error here, return that
|
|
213
|
+
// message straightaway
|
|
214
|
+
if (res.json.error) {
|
|
215
|
+
throw new Error(res.json.error);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Now read package data from the installed package to return, and make sure
|
|
220
|
+
// everything got installed ok. Remember, pkgName might end up with a / in it due to an npm
|
|
221
|
+
// org, so if so, that will get correctly exploded into multiple directories, by path.resolve here
|
|
222
|
+
// (even on Windows!)
|
|
223
|
+
const pkgJsonPath = resolveFrom(cwd, `${pkgName}/package.json`);
|
|
224
|
+
try {
|
|
225
|
+
return require(pkgJsonPath);
|
|
226
|
+
} catch {
|
|
227
|
+
throw new Error('The package was not downloaded correctly; its package.json ' +
|
|
228
|
+
'did not exist or was unreadable. We looked for it at ' +
|
|
229
|
+
pkgJsonPath);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @todo: I think this can be an `install` instead of a `link`.
|
|
235
|
+
* @param {string} cwd
|
|
236
|
+
* @param {string} pkgPath
|
|
237
|
+
*/
|
|
238
|
+
async linkPackage (cwd, pkgPath) {
|
|
239
|
+
// from the path alone we don't know the npm package name, so we need to
|
|
240
|
+
// look in package.json
|
|
241
|
+
let pkgName;
|
|
242
|
+
try {
|
|
243
|
+
pkgName = require(path.resolve(pkgPath, 'package.json')).name;
|
|
244
|
+
} catch {
|
|
245
|
+
throw new Error('Could not find package.json inside the package path ' +
|
|
246
|
+
`provided: ${pkgPath}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// this is added to handle commands with relative paths
|
|
250
|
+
// ie: "node . driver install --source=local ../fake-driver"
|
|
251
|
+
pkgPath = path.resolve(process.cwd(), pkgPath);
|
|
252
|
+
|
|
253
|
+
// call link with --no-package-lock to ensure no corruption while installing local packages
|
|
254
|
+
const args = [
|
|
255
|
+
'--global-style',
|
|
256
|
+
'--no-package-lock',
|
|
257
|
+
pkgPath
|
|
258
|
+
];
|
|
259
|
+
const res = await this.exec('link', args, {cwd, lockFile: this._getLinkLockfilePath(cwd)});
|
|
260
|
+
if (res.json && res.json.error) {
|
|
261
|
+
throw new Error(res.json.error);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// now ensure it was linked to the correct place
|
|
265
|
+
try {
|
|
266
|
+
return require(resolveFrom(cwd, `${pkgName}/package.json`));
|
|
267
|
+
} catch {
|
|
268
|
+
throw new Error('The package was not linked correctly; its package.json ' +
|
|
269
|
+
'did not exist or was unreadable');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* @param {string} cwd
|
|
275
|
+
* @param {string} pkg
|
|
276
|
+
*/
|
|
277
|
+
async uninstallPackage (cwd, pkg) {
|
|
278
|
+
await this.exec('uninstall', [pkg], {
|
|
279
|
+
cwd,
|
|
280
|
+
lockFile: this._getInstallLockfilePath(cwd)
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const npm = new NPM();
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Options for {@link NPM.installPackage}
|
|
289
|
+
* @typedef InstallPackageOpts
|
|
290
|
+
* @property {string} [pkgVer] - the version of the package to install
|
|
291
|
+
*/
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Options for {@link NPM.exec}
|
|
295
|
+
* @typedef ExecOpts
|
|
296
|
+
* @property {string} cwd - Current working directory
|
|
297
|
+
* @property {boolean} [json] - If `true`, supply `--json` flag to npm and resolve w/ parsed JSON
|
|
298
|
+
* @property {string} [lockFile] - Path to lockfile to use
|
|
299
|
+
*/
|
|
300
|
+
|
|
301
|
+
// THESE TYPES SHOULD BE IN TEEN PROCESS, NOT HERE
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Result from a non-zero-exit execution of `appium`
|
|
305
|
+
* @typedef TeenProcessExecResult
|
|
306
|
+
* @property {string} stdout - Stdout
|
|
307
|
+
* @property {string} stderr - Stderr
|
|
308
|
+
* @property {number?} code - Exit code
|
|
309
|
+
* @property {any} json - JSON parsed from stdout
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Extra props `teen_process.exec` adds to its error objects
|
|
314
|
+
* @typedef TeenProcessExecErrorProps
|
|
315
|
+
* @property {string} stdout - STDOUT
|
|
316
|
+
* @property {string} stderr - STDERR
|
|
317
|
+
* @property {number?} code - Exit code
|
|
318
|
+
*/
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Options unique to `teen_process.exec`. I probably missed some
|
|
322
|
+
* @typedef TeenProcessExecExtraOpts
|
|
323
|
+
* @property {number} [maxStdoutBufferSize]
|
|
324
|
+
* @property {number} [maxStderrBufferSize]
|
|
325
|
+
*/
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* All options for `teen_process.exec`
|
|
329
|
+
* @typedef {import('child_process').SpawnOptions & TeenProcessExecExtraOpts} TeenProcessExecOpts
|
|
330
|
+
*/
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Error thrown by `teen_process.exec`
|
|
334
|
+
* @typedef {Error & TeenProcessExecErrorProps} TeenProcessExecError
|
|
335
|
+
*/
|
package/lib/tempdir.js
CHANGED
|
@@ -13,7 +13,7 @@ const RDWR_EXCL = cnst.O_CREAT | cnst.O_TRUNC | cnst.O_RDWR | cnst.O_EXCL;
|
|
|
13
13
|
* - No `process.env.APPIUM_TMP_DIR`: `/var/folders/34/2222sh8n27d6rcp7jqlkw8km0000gn/T/xxxxxxxx.yyyy`
|
|
14
14
|
* - With `process.env.APPIUM_TMP_DIR = '/path/to/root'`: `/path/to/root/xxxxxxxx.yyyy`
|
|
15
15
|
*
|
|
16
|
-
* @returns {string} A path to the temporary directory
|
|
16
|
+
* @returns {Promise<string>} A path to the temporary directory
|
|
17
17
|
*/
|
|
18
18
|
async function tempDir () {
|
|
19
19
|
const now = new Date();
|
|
@@ -33,7 +33,7 @@ async function tempDir () {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* @typedef
|
|
36
|
+
* @typedef Affixes
|
|
37
37
|
* @property {string} prefix - prefix of the temp directory name
|
|
38
38
|
* @property {string} suffix - suffix of the temp directory name
|
|
39
39
|
*/
|
|
@@ -44,7 +44,7 @@ async function tempDir () {
|
|
|
44
44
|
*
|
|
45
45
|
* @param {string|Affixes} rawAffixes
|
|
46
46
|
* @param {?string} defaultPrefix
|
|
47
|
-
* @returns {string} A path to the temporary directory with rawAffixes and defaultPrefix
|
|
47
|
+
* @returns {Promise<string>} A path to the temporary directory with rawAffixes and defaultPrefix
|
|
48
48
|
*/
|
|
49
49
|
async function path (rawAffixes, defaultPrefix) {
|
|
50
50
|
const affixes = parseAffixes(rawAffixes, defaultPrefix);
|
|
@@ -54,7 +54,7 @@ async function path (rawAffixes, defaultPrefix) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* @typedef
|
|
57
|
+
* @typedef OpenedAffixes
|
|
58
58
|
* @property {string} path - The path to file
|
|
59
59
|
* @property {integer} fd - The file descriptor opened
|
|
60
60
|
*/
|
|
@@ -64,7 +64,7 @@ async function path (rawAffixes, defaultPrefix) {
|
|
|
64
64
|
* with arbitrary prefix/suffix for the directory name and return it as open.
|
|
65
65
|
*
|
|
66
66
|
* @param {Affixes} affixes
|
|
67
|
-
* @returns {OpenedAffixes}
|
|
67
|
+
* @returns {Promise<OpenedAffixes>}
|
|
68
68
|
*/
|
|
69
69
|
async function open (affixes) {
|
|
70
70
|
const filePath = await path(affixes, 'f-');
|
|
@@ -116,7 +116,7 @@ const openDir = tempDir;
|
|
|
116
116
|
/**
|
|
117
117
|
* Returns a path to a temporary directory whcih is defined as static in the same process
|
|
118
118
|
*
|
|
119
|
-
* @returns {string} A temp directory path whcih is defined as static in the same process
|
|
119
|
+
* @returns {Promise<string>} A temp directory path whcih is defined as static in the same process
|
|
120
120
|
*/
|
|
121
121
|
async function staticDir () { // eslint-disable-line require-await
|
|
122
122
|
return _static;
|
package/lib/util.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import B from 'bluebird';
|
|
2
4
|
import _ from 'lodash';
|
|
3
5
|
import os from 'os';
|
|
@@ -75,6 +77,7 @@ function localIp () {
|
|
|
75
77
|
let ip = _.chain(os.networkInterfaces())
|
|
76
78
|
.values()
|
|
77
79
|
.flatten()
|
|
80
|
+
// @ts-ignore
|
|
78
81
|
.filter(function (val) {
|
|
79
82
|
return (val.family === 'IPv4' && val.internal === false);
|
|
80
83
|
})
|
|
@@ -217,16 +220,16 @@ function filterObject (obj, predicate) {
|
|
|
217
220
|
* if it is less than zero.
|
|
218
221
|
*/
|
|
219
222
|
function toReadableSizeString (bytes) {
|
|
220
|
-
const intBytes = parseInt(bytes, 10);
|
|
223
|
+
const intBytes = parseInt(String(bytes), 10);
|
|
221
224
|
if (isNaN(intBytes) || intBytes < 0) {
|
|
222
225
|
throw new Error(`Cannot convert '${bytes}' to a readable size format`);
|
|
223
226
|
}
|
|
224
227
|
if (intBytes >= GiB) {
|
|
225
|
-
return `${
|
|
228
|
+
return `${(intBytes / (GiB * 1.0)).toFixed(2)} GB`;
|
|
226
229
|
} else if (intBytes >= MiB) {
|
|
227
|
-
return `${
|
|
230
|
+
return `${(intBytes / (MiB * 1.0)).toFixed(2)} MB`;
|
|
228
231
|
} else if (intBytes >= KiB) {
|
|
229
|
-
return `${
|
|
232
|
+
return `${(intBytes / (KiB * 1.0)).toFixed(2)} KB`;
|
|
230
233
|
}
|
|
231
234
|
return `${intBytes} B`;
|
|
232
235
|
}
|
|
@@ -260,7 +263,7 @@ function isSubPath (originalPath, root, forcePosix = null) {
|
|
|
260
263
|
* @param {string} path1 - Absolute or relative path to a file/folder
|
|
261
264
|
* @param {string} path2 - Absolute or relative path to a file/folder
|
|
262
265
|
* @param {...string} pathN - Zero or more absolute or relative paths to files/folders
|
|
263
|
-
* @returns {boolean} true if all paths are pointing to the same file system item
|
|
266
|
+
* @returns {Promise<boolean>} true if all paths are pointing to the same file system item
|
|
264
267
|
*/
|
|
265
268
|
async function isSameDestination (path1, path2, ...pathN) {
|
|
266
269
|
const allPaths = [path1, path2, ...pathN];
|
|
@@ -288,19 +291,20 @@ async function isSameDestination (path1, path2, ...pathN) {
|
|
|
288
291
|
/**
|
|
289
292
|
* Coerces the given number/string to a valid version string
|
|
290
293
|
*
|
|
291
|
-
* @
|
|
292
|
-
* @param {
|
|
294
|
+
* @template {boolean} [Strict=true]
|
|
295
|
+
* @param {string} ver - Version string to coerce
|
|
296
|
+
* @param {Strict} [strict] - If `true` then an exception will be thrown
|
|
293
297
|
* if `ver` cannot be coerced
|
|
294
|
-
* @returns {string} Coerced version number or null if the string cannot be
|
|
298
|
+
* @returns {Strict extends true ? string : string|null} Coerced version number or null if the string cannot be
|
|
295
299
|
* coerced and strict mode is disabled
|
|
296
300
|
* @throws {Error} if strict mode is enabled and `ver` cannot be coerced
|
|
297
301
|
*/
|
|
298
|
-
function coerceVersion (ver, strict = true) {
|
|
302
|
+
function coerceVersion (ver, strict = /** @type {Strict} */(true)) {
|
|
299
303
|
const result = semver.valid(semver.coerce(`${ver}`));
|
|
300
304
|
if (strict && !result) {
|
|
301
305
|
throw new Error(`'${ver}' cannot be coerced to a valid version number`);
|
|
302
306
|
}
|
|
303
|
-
return result;
|
|
307
|
+
return /** @type {Strict extends true ? string : string|null} */(result);
|
|
304
308
|
}
|
|
305
309
|
|
|
306
310
|
const SUPPORTED_OPERATORS = ['==', '!=', '>', '<', '>=', '<=', '='];
|
|
@@ -308,9 +312,9 @@ const SUPPORTED_OPERATORS = ['==', '!=', '>', '<', '>=', '<=', '='];
|
|
|
308
312
|
/**
|
|
309
313
|
* Compares two version strings
|
|
310
314
|
*
|
|
311
|
-
* @param {string
|
|
315
|
+
* @param {string} ver1 - The first version number to compare. Should be a valid
|
|
312
316
|
* version number supported by semver parser.
|
|
313
|
-
* @param {string
|
|
317
|
+
* @param {string} ver2 - The second version number to compare. Should be a valid
|
|
314
318
|
* version number supported by semver parser.
|
|
315
319
|
* @param {string} operator - One of supported version number operators:
|
|
316
320
|
* ==, !=, >, <, <=, >=, =
|
|
@@ -333,7 +337,7 @@ function compareVersions (ver1, operator, ver2) {
|
|
|
333
337
|
* Add appropriate quotes to command arguments. See https://github.com/substack/node-shell-quote
|
|
334
338
|
* for more details
|
|
335
339
|
*
|
|
336
|
-
* @param {string|
|
|
340
|
+
* @param {string|string[]} args - The arguments that will be parsed
|
|
337
341
|
* @returns {string} - The arguments, quoted
|
|
338
342
|
*/
|
|
339
343
|
function quote (args) {
|
|
@@ -354,8 +358,8 @@ function unleakString (s) {
|
|
|
354
358
|
|
|
355
359
|
|
|
356
360
|
/**
|
|
357
|
-
* @typedef
|
|
358
|
-
* @property {
|
|
361
|
+
* @typedef PluralizeOptions
|
|
362
|
+
* @property {boolean} [inclusive=false] - Whether to prefix with the number (e.g., 3 ducks)
|
|
359
363
|
*/
|
|
360
364
|
|
|
361
365
|
/**
|
|
@@ -363,7 +367,7 @@ function unleakString (s) {
|
|
|
363
367
|
*
|
|
364
368
|
* @param {string} word - The word to pluralize
|
|
365
369
|
* @param {number} count - How many of the word exist
|
|
366
|
-
* @param {
|
|
370
|
+
* @param {PluralizeOptions|boolean} options - options for word pluralization,
|
|
367
371
|
* or a boolean indicating the options.inclusive property
|
|
368
372
|
* @returns {string} The word pluralized according to the number
|
|
369
373
|
*/
|
|
@@ -380,8 +384,8 @@ function pluralize (word, count, options = {}) {
|
|
|
380
384
|
}
|
|
381
385
|
|
|
382
386
|
/**
|
|
383
|
-
* @typedef
|
|
384
|
-
* @property {number} maxSize
|
|
387
|
+
* @typedef EncodingOptions
|
|
388
|
+
* @property {number} [maxSize=1073741824] The maximum size of
|
|
385
389
|
* the resulting buffer in bytes. This is set to 1GB by default, because
|
|
386
390
|
* Appium limits the maximum HTTP body size to 1GB. Also, the NodeJS heap
|
|
387
391
|
* size must be enough to keep the resulting object (usually this size is
|
|
@@ -395,7 +399,7 @@ function pluralize (word, count, options = {}) {
|
|
|
395
399
|
*
|
|
396
400
|
* @param {string} srcPath The full path to the file being encoded
|
|
397
401
|
* @param {EncodingOptions} opts
|
|
398
|
-
* @returns {Buffer} base64-encoded content of the source file as memory buffer
|
|
402
|
+
* @returns {Promise<Buffer>} base64-encoded content of the source file as memory buffer
|
|
399
403
|
* @throws {Error} if there was an error while reading the source file
|
|
400
404
|
* or the source file is too
|
|
401
405
|
*/
|
|
@@ -445,9 +449,9 @@ async function toInMemoryBase64 (srcPath, opts = {}) {
|
|
|
445
449
|
}
|
|
446
450
|
|
|
447
451
|
/**
|
|
448
|
-
* @typedef
|
|
449
|
-
* @property {number} timeout
|
|
450
|
-
* @property {boolean} tryRecovery
|
|
452
|
+
* @typedef LockFileOptions
|
|
453
|
+
* @property {number} [timeout=120] The max time in seconds to wait for the lock
|
|
454
|
+
* @property {boolean} [tryRecovery=false] Whether to try lock recovery if
|
|
451
455
|
* the first attempt to acquire it timed out.
|
|
452
456
|
*/
|
|
453
457
|
|
|
@@ -458,7 +462,7 @@ async function toInMemoryBase64 (srcPath, opts = {}) {
|
|
|
458
462
|
*
|
|
459
463
|
* @param {string} lockFile The full path to the file used for the lock
|
|
460
464
|
* @param {LockFileOptions} opts
|
|
461
|
-
* @returns {
|
|
465
|
+
* @returns {(behavior: () => Promise<void>) => Promise<void>} async function that takes another async function defining the locked
|
|
462
466
|
* behavior
|
|
463
467
|
*/
|
|
464
468
|
function getLockFileGuard (lockFile, opts = {}) {
|
|
@@ -467,7 +471,7 @@ function getLockFileGuard (lockFile, opts = {}) {
|
|
|
467
471
|
tryRecovery = false,
|
|
468
472
|
} = opts;
|
|
469
473
|
|
|
470
|
-
const lock = B.promisify(_lockfile.lock);
|
|
474
|
+
const lock = /** @type {(lockfile: string, opts: import('lockfile').Options)=>B<void>} */(B.promisify(_lockfile.lock));
|
|
471
475
|
const check = B.promisify(_lockfile.check);
|
|
472
476
|
const unlock = B.promisify(_lockfile.unlock);
|
|
473
477
|
|