appium 2.0.0-beta.2 → 2.0.0-beta.23
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 +9 -9
- package/build/check-npm-pack-files.js +23 -0
- package/build/commands-yml/parse.js +319 -0
- package/build/commands-yml/validator.js +130 -0
- package/build/index.js +19 -0
- package/build/lib/appium-config.schema.json +0 -0
- package/build/lib/appium.js +160 -53
- package/build/lib/cli/args.js +115 -279
- package/build/lib/cli/driver-command.js +11 -1
- package/build/lib/cli/extension-command.js +60 -8
- package/build/lib/cli/extension.js +30 -7
- package/build/lib/cli/npm.js +43 -29
- package/build/lib/cli/parser.js +156 -89
- package/build/lib/cli/plugin-command.js +11 -1
- package/build/lib/cli/utils.js +29 -3
- package/build/lib/config-file.js +141 -0
- package/build/lib/config.js +53 -65
- package/build/lib/driver-config.js +42 -19
- package/build/lib/drivers.js +8 -4
- package/build/lib/ext-config-io.js +165 -0
- package/build/lib/extension-config.js +130 -61
- package/build/lib/grid-register.js +22 -24
- package/build/lib/logger.js +3 -3
- package/build/lib/logsink.js +11 -13
- package/build/lib/main.js +197 -77
- package/build/lib/plugin-config.js +21 -11
- package/build/lib/plugins.js +4 -2
- package/build/lib/schema/appium-config-schema.js +253 -0
- package/build/lib/schema/arg-spec.js +120 -0
- package/build/lib/schema/cli-args.js +188 -0
- package/build/lib/schema/cli-transformers.js +76 -0
- package/build/lib/schema/index.js +36 -0
- package/build/lib/schema/keywords.js +72 -0
- package/build/lib/schema/schema.js +357 -0
- package/build/lib/utils.js +44 -99
- package/build/postinstall.js +90 -0
- package/build/test/cli/cli-e2e-specs.js +221 -0
- package/build/test/cli/cli-helpers.js +86 -0
- package/build/test/cli/cli-specs.js +71 -0
- package/build/test/cli/fixtures/test-driver/package.json +27 -0
- package/build/test/cli/schema-args-specs.js +48 -0
- package/build/test/cli/schema-e2e-specs.js +47 -0
- package/build/test/config-e2e-specs.js +112 -0
- package/build/test/config-file-e2e-specs.js +209 -0
- package/build/test/config-file-specs.js +281 -0
- package/build/test/config-specs.js +159 -0
- package/build/test/driver-e2e-specs.js +435 -0
- package/build/test/driver-specs.js +321 -0
- package/build/test/ext-config-io-specs.js +181 -0
- package/build/test/extension-config-specs.js +365 -0
- package/build/test/fixtures/allow-feat.txt +5 -0
- package/build/test/fixtures/caps.json +3 -0
- package/build/test/fixtures/config/allow-insecure.txt +3 -0
- package/build/test/fixtures/config/appium.config.bad-nodeconfig.json +5 -0
- package/build/test/fixtures/config/appium.config.bad.json +32 -0
- package/build/test/fixtures/config/appium.config.ext-good.json +9 -0
- package/build/test/fixtures/config/appium.config.ext-unknown-props.json +10 -0
- package/build/test/fixtures/config/appium.config.good.js +40 -0
- package/build/test/fixtures/config/appium.config.good.json +33 -0
- package/build/test/fixtures/config/appium.config.good.yaml +30 -0
- package/build/test/fixtures/config/appium.config.invalid.json +31 -0
- package/build/test/fixtures/config/appium.config.security-array.json +5 -0
- package/build/test/fixtures/config/appium.config.security-delimited.json +5 -0
- package/build/test/fixtures/config/appium.config.security-path.json +5 -0
- package/build/test/fixtures/config/driver-fake.config.json +8 -0
- package/build/test/fixtures/config/nodeconfig.json +3 -0
- package/build/test/fixtures/config/plugin-fake.config.json +0 -0
- package/build/test/fixtures/default-args.js +35 -0
- package/build/test/fixtures/deny-feat.txt +5 -0
- package/build/test/fixtures/driver.schema.js +20 -0
- package/build/test/fixtures/extensions.yaml +27 -0
- package/build/test/fixtures/flattened-schema.js +504 -0
- package/build/test/fixtures/plugin.schema.js +20 -0
- package/build/test/fixtures/schema-with-extensions.js +28 -0
- package/build/test/grid-register-specs.js +74 -0
- package/build/test/helpers.js +75 -0
- package/build/test/logger-specs.js +76 -0
- package/build/test/npm-specs.js +20 -0
- package/build/test/parser-specs.js +314 -0
- package/build/test/plugin-e2e-specs.js +316 -0
- package/build/test/schema/arg-spec-specs.js +70 -0
- package/build/test/schema/cli-args-specs.js +431 -0
- package/build/test/schema/schema-specs.js +389 -0
- package/build/test/utils-specs.js +266 -0
- package/index.js +11 -0
- package/lib/appium-config.schema.json +278 -0
- package/lib/appium.js +207 -65
- package/lib/cli/args.js +174 -375
- package/lib/cli/driver-command.js +4 -0
- package/lib/cli/extension-command.js +70 -5
- package/lib/cli/extension.js +25 -5
- package/lib/cli/npm.js +86 -18
- package/lib/cli/parser.js +257 -79
- package/lib/cli/plugin-command.js +4 -0
- package/lib/cli/utils.js +21 -1
- package/lib/config-file.js +227 -0
- package/lib/config.js +84 -63
- package/lib/driver-config.js +66 -11
- package/lib/drivers.js +4 -1
- package/lib/ext-config-io.js +287 -0
- package/lib/extension-config.js +225 -67
- package/lib/grid-register.js +27 -24
- package/lib/logger.js +1 -1
- package/lib/logsink.js +10 -7
- package/lib/main.js +214 -77
- package/lib/plugin-config.js +35 -6
- package/lib/plugins.js +1 -0
- package/lib/schema/appium-config-schema.js +287 -0
- package/lib/schema/arg-spec.js +222 -0
- package/lib/schema/cli-args.js +285 -0
- package/lib/schema/cli-transformers.js +123 -0
- package/lib/schema/index.js +2 -0
- package/lib/schema/keywords.js +135 -0
- package/lib/schema/schema.js +577 -0
- package/lib/utils.js +42 -88
- package/package.json +55 -84
- package/postinstall.js +71 -0
- package/types/appium-config.d.ts +197 -0
- package/types/types.d.ts +201 -0
- package/CHANGELOG.md +0 -3515
- package/build/lib/cli/parser-helpers.js +0 -82
- package/lib/cli/parser-helpers.js +0 -79
package/lib/extension-config.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import _ from 'lodash';
|
|
2
|
-
import
|
|
3
|
-
import { fs, mkdirp } from 'appium-support';
|
|
4
|
+
import os from 'os';
|
|
4
5
|
import path from 'path';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const DEFAULT_APPIUM_HOME = path.resolve(process.env.HOME, '.appium');
|
|
6
|
+
import resolveFrom from 'resolve-from';
|
|
7
|
+
import { getExtConfigIOInstance } from './ext-config-io';
|
|
8
|
+
import log from './logger';
|
|
9
|
+
import { ALLOWED_SCHEMA_EXTENSIONS, isAllowedSchemaFileExtension, registerSchema } from './schema/schema';
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const DEFAULT_APPIUM_HOME = path.resolve(os.homedir(), '.appium');
|
|
12
|
+
const APPIUM_HOME = process.env.APPIUM_HOME || DEFAULT_APPIUM_HOME;
|
|
13
13
|
|
|
14
14
|
const INSTALL_TYPE_NPM = 'npm';
|
|
15
15
|
const INSTALL_TYPE_LOCAL = 'local';
|
|
@@ -22,27 +22,44 @@ const INSTALL_TYPES = [
|
|
|
22
22
|
INSTALL_TYPE_NPM
|
|
23
23
|
];
|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
export default class ExtensionConfig {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @param {string} appiumHome - `APPIUM_HOME`
|
|
29
|
+
* @param {ExtensionType} extensionType - Type of extension
|
|
30
|
+
* @param {(...args: any[]) => void} [logFn]
|
|
31
|
+
*/
|
|
32
|
+
constructor (appiumHome, extensionType, logFn) {
|
|
33
|
+
const logger = _.isFunction(logFn) ? logFn : log.error.bind(log);
|
|
34
|
+
/** @type {string} */
|
|
31
35
|
this.appiumHome = appiumHome;
|
|
32
|
-
|
|
36
|
+
/** @type {Record<string,object>} */
|
|
33
37
|
this.installedExtensions = {};
|
|
38
|
+
/** @type {import('./ext-config-io').ExtensionConfigIO} */
|
|
39
|
+
this.io = getExtConfigIOInstance(appiumHome);
|
|
40
|
+
/** @type {ExtensionType} */
|
|
34
41
|
this.extensionType = extensionType;
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
42
|
+
/** @type {'drivers'|'plugins'} */
|
|
43
|
+
this.configKey = `${extensionType}s`; // todo use template type
|
|
44
|
+
/**
|
|
45
|
+
* @type {(...args: any[])=>void}
|
|
46
|
+
*/
|
|
47
|
+
this.log = logger;
|
|
38
48
|
}
|
|
39
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Checks extensions for problems
|
|
52
|
+
* @template ExtData
|
|
53
|
+
* @param {ExtData[]} exts - Array of extData objects
|
|
54
|
+
* @returns {ExtData[]}
|
|
55
|
+
*/
|
|
40
56
|
validate (exts) {
|
|
41
57
|
const foundProblems = {};
|
|
42
58
|
for (const [extName, extData] of _.toPairs(exts)) {
|
|
43
59
|
foundProblems[extName] = [
|
|
44
|
-
...this.getGenericConfigProblems(extData),
|
|
45
|
-
...this.getConfigProblems(extData)
|
|
60
|
+
...this.getGenericConfigProblems(extData, extName),
|
|
61
|
+
...this.getConfigProblems(extData, extName),
|
|
62
|
+
...this.getSchemaProblems(extData, extName)
|
|
46
63
|
];
|
|
47
64
|
}
|
|
48
65
|
|
|
@@ -63,7 +80,7 @@ export default class ExtensionConfig {
|
|
|
63
80
|
|
|
64
81
|
if (!_.isEmpty(problemSummaries)) {
|
|
65
82
|
this.log(`Appium encountered one or more errors while validating ` +
|
|
66
|
-
`the ${this.configKey} extension file (${this.
|
|
83
|
+
`the ${this.configKey} extension file (${this.io.filepath}):`);
|
|
67
84
|
for (const summary of problemSummaries) {
|
|
68
85
|
this.log(summary);
|
|
69
86
|
}
|
|
@@ -72,8 +89,52 @@ export default class ExtensionConfig {
|
|
|
72
89
|
return exts;
|
|
73
90
|
}
|
|
74
91
|
|
|
75
|
-
|
|
76
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @param {object} extData
|
|
94
|
+
* @param {string} extName
|
|
95
|
+
* @returns {Problem[]}
|
|
96
|
+
*/
|
|
97
|
+
getSchemaProblems (extData, extName) {
|
|
98
|
+
const problems = [];
|
|
99
|
+
const {schema: argSchemaPath} = extData;
|
|
100
|
+
if (argSchemaPath) {
|
|
101
|
+
if (_.isString(argSchemaPath)) {
|
|
102
|
+
if (isAllowedSchemaFileExtension(argSchemaPath)) {
|
|
103
|
+
try {
|
|
104
|
+
this.readExtensionSchema(extName, extData);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
problems.push({err: `Unable to register schema at path ${argSchemaPath}; ${err.message}`, val: argSchemaPath});
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
problems.push({
|
|
110
|
+
err: `Schema file has unsupported extension. Allowed: ${[...ALLOWED_SCHEMA_EXTENSIONS].join(', ')}`,
|
|
111
|
+
val: argSchemaPath
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
} else if (_.isPlainObject(argSchemaPath)) {
|
|
115
|
+
try {
|
|
116
|
+
this.readExtensionSchema(extName, extData);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
problems.push({err: `Unable to register embedded schema; ${err.message}`, val: argSchemaPath});
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
problems.push({
|
|
122
|
+
err: 'Incorrectly formatted schema field; must be a path to a schema file or a schema object.',
|
|
123
|
+
val: argSchemaPath
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return problems;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {object} extData
|
|
132
|
+
* @param {string} extName
|
|
133
|
+
* @returns {Problem[]}
|
|
134
|
+
*/
|
|
135
|
+
// eslint-disable-next-line no-unused-vars
|
|
136
|
+
getGenericConfigProblems (extData, extName) {
|
|
137
|
+
const {version, pkgName, installSpec, installType, installPath, mainClass} = extData;
|
|
77
138
|
const problems = [];
|
|
78
139
|
|
|
79
140
|
if (!_.isString(version)) {
|
|
@@ -103,62 +164,60 @@ export default class ExtensionConfig {
|
|
|
103
164
|
return problems;
|
|
104
165
|
}
|
|
105
166
|
|
|
106
|
-
|
|
167
|
+
/**
|
|
168
|
+
* @param {object} extData
|
|
169
|
+
* @param {string} extName
|
|
170
|
+
* @returns {Problem[]}
|
|
171
|
+
*/
|
|
172
|
+
// eslint-disable-next-line no-unused-vars
|
|
173
|
+
getConfigProblems (extData, extName) {
|
|
107
174
|
// shoud override this method if special validation is necessary for this extension type
|
|
108
175
|
return [];
|
|
109
176
|
}
|
|
110
177
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// so make sure we at least have an empty section for it
|
|
115
|
-
this.yamlData[PLUGIN_TYPE] = {};
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
178
|
+
/**
|
|
179
|
+
* @returns {Promise<typeof this.installedExtensions>}
|
|
180
|
+
*/
|
|
119
181
|
async read () {
|
|
120
|
-
await
|
|
121
|
-
|
|
122
|
-
this.yamlData = YAML.parse(await fs.readFile(this.configFile, 'utf8'));
|
|
123
|
-
this.applySchemaMigrations();
|
|
124
|
-
|
|
125
|
-
// set the list of drivers the user has installed
|
|
126
|
-
this.installedExtensions = this.validate(this.yamlData[this.configKey]);
|
|
127
|
-
} catch (err) {
|
|
128
|
-
if (await fs.exists(this.configFile)) {
|
|
129
|
-
// if the file exists and we couldn't parse it, that's a problem
|
|
130
|
-
throw new Error(`Appium had trouble loading the extension installation ` +
|
|
131
|
-
`cache file (${this.configFile}). Ensure it exists and is ` +
|
|
132
|
-
`readable. Specific error: ${err.message}`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// if the config file doesn't exist, try to write an empty one, to make
|
|
136
|
-
// sure we actually have write privileges, and complain if we don't
|
|
137
|
-
try {
|
|
138
|
-
await this.write();
|
|
139
|
-
} catch {
|
|
140
|
-
throw new Error(`Appium could not read or write from the Appium Home directory ` +
|
|
141
|
-
`(${this.appiumHome}). Please ensure it is writable.`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
182
|
+
const extensions = await this.io.read(this.extensionType);
|
|
183
|
+
this.installedExtensions = this.validate(extensions);
|
|
144
184
|
return this.installedExtensions;
|
|
145
185
|
}
|
|
146
186
|
|
|
147
|
-
|
|
187
|
+
/**
|
|
188
|
+
* @returns {Promise<boolean>}
|
|
189
|
+
*/
|
|
148
190
|
async write () {
|
|
149
|
-
|
|
150
|
-
...this.yamlData,
|
|
151
|
-
schemaRev: CONFIG_SCHEMA_REV,
|
|
152
|
-
[this.configKey]: this.installedExtensions
|
|
153
|
-
};
|
|
154
|
-
await fs.writeFile(this.configFile, YAML.stringify(newYamlData), 'utf8');
|
|
191
|
+
return await this.io.write();
|
|
155
192
|
}
|
|
156
193
|
|
|
194
|
+
/**
|
|
195
|
+
* @param {string} extName
|
|
196
|
+
* @param {object} extData
|
|
197
|
+
* @returns {Promise<void>}
|
|
198
|
+
*/
|
|
157
199
|
async addExtension (extName, extData) {
|
|
158
200
|
this.installedExtensions[extName] = extData;
|
|
159
201
|
await this.write();
|
|
160
202
|
}
|
|
161
203
|
|
|
204
|
+
/**
|
|
205
|
+
* @param {string} extName
|
|
206
|
+
* @param {object} extData
|
|
207
|
+
* @returns {Promise<void>}
|
|
208
|
+
*/
|
|
209
|
+
async updateExtension (extName, extData) {
|
|
210
|
+
this.installedExtensions[extName] = {
|
|
211
|
+
...this.installedExtensions[extName],
|
|
212
|
+
...extData,
|
|
213
|
+
};
|
|
214
|
+
await this.write();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @param {string} extName
|
|
219
|
+
* @returns {Promise<void>}
|
|
220
|
+
*/
|
|
162
221
|
async removeExtension (extName) {
|
|
163
222
|
delete this.installedExtensions[extName];
|
|
164
223
|
await this.write();
|
|
@@ -178,31 +237,130 @@ export default class ExtensionConfig {
|
|
|
178
237
|
}
|
|
179
238
|
}
|
|
180
239
|
|
|
181
|
-
|
|
182
|
-
|
|
240
|
+
/**
|
|
241
|
+
* Returns a string describing the extension. Subclasses must implement.
|
|
242
|
+
* @param {string} extName - Extension name
|
|
243
|
+
* @param {object} extData - Extension data
|
|
244
|
+
* @returns {string}
|
|
245
|
+
* @abstract
|
|
246
|
+
*/
|
|
247
|
+
// eslint-disable-next-line no-unused-vars
|
|
248
|
+
extensionDesc (extName, extData) {
|
|
249
|
+
throw new Error('This must be implemented in a subclass');
|
|
183
250
|
}
|
|
184
251
|
|
|
252
|
+
/**
|
|
253
|
+
* @param {string} extName
|
|
254
|
+
* @returns {string}
|
|
255
|
+
*/
|
|
185
256
|
getExtensionRequirePath (extName) {
|
|
186
257
|
const {pkgName, installPath} = this.installedExtensions[extName];
|
|
187
258
|
return path.resolve(this.appiumHome, installPath, 'node_modules', pkgName);
|
|
188
259
|
}
|
|
189
260
|
|
|
261
|
+
/**
|
|
262
|
+
* @param {string} extName
|
|
263
|
+
* @returns {string}
|
|
264
|
+
*/
|
|
190
265
|
getInstallPath (extName) {
|
|
191
266
|
const {installPath} = this.installedExtensions[extName];
|
|
192
267
|
return path.resolve(this.appiumHome, installPath);
|
|
193
268
|
}
|
|
194
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Loads extension and returns its main class
|
|
272
|
+
* @param {string} extName
|
|
273
|
+
* @returns {(...args: any[]) => object }
|
|
274
|
+
*/
|
|
195
275
|
require (extName) {
|
|
196
276
|
const {mainClass} = this.installedExtensions[extName];
|
|
197
|
-
|
|
277
|
+
const reqPath = this.getExtensionRequirePath(extName);
|
|
278
|
+
const reqResolved = require.resolve(reqPath);
|
|
279
|
+
if (process.env.APPIUM_RELOAD_EXTENSIONS && require.cache[reqResolved]) {
|
|
280
|
+
log.debug(`Removing ${reqResolved} from require cache`);
|
|
281
|
+
delete require.cache[reqResolved];
|
|
282
|
+
}
|
|
283
|
+
return require(reqPath)[mainClass];
|
|
198
284
|
}
|
|
199
285
|
|
|
286
|
+
/**
|
|
287
|
+
* @param {string} extName
|
|
288
|
+
* @returns {boolean}
|
|
289
|
+
*/
|
|
200
290
|
isInstalled (extName) {
|
|
201
291
|
return _.includes(Object.keys(this.installedExtensions), extName);
|
|
202
292
|
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Intended to be called by corresponding instance methods of subclass.
|
|
296
|
+
* @private
|
|
297
|
+
* @param {string} appiumHome
|
|
298
|
+
* @param {ExtensionType} extType
|
|
299
|
+
* @param {string} extName - Extension name (unique to its type)
|
|
300
|
+
* @param {ExtData} extData - Extension config
|
|
301
|
+
* @returns {import('ajv').SchemaObject|undefined}
|
|
302
|
+
*/
|
|
303
|
+
static _readExtensionSchema (appiumHome, extType, extName, extData) {
|
|
304
|
+
const {installPath, pkgName, schema: argSchemaPath} = extData;
|
|
305
|
+
if (!argSchemaPath) {
|
|
306
|
+
throw new TypeError(
|
|
307
|
+
`No \`schema\` property found in config for ${extType} ${pkgName} -- why is this function being called?`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
let moduleObject;
|
|
311
|
+
if (_.isString(argSchemaPath)) {
|
|
312
|
+
const schemaPath = resolveFrom(
|
|
313
|
+
path.resolve(appiumHome, installPath),
|
|
314
|
+
// this path sep is fine because `resolveFrom` uses Node's module resolution
|
|
315
|
+
path.normalize(`${pkgName}/${argSchemaPath}`),
|
|
316
|
+
);
|
|
317
|
+
moduleObject = require(schemaPath);
|
|
318
|
+
} else {
|
|
319
|
+
moduleObject = argSchemaPath;
|
|
320
|
+
}
|
|
321
|
+
// this sucks. default exports should be destroyed
|
|
322
|
+
const schema = moduleObject.__esModule
|
|
323
|
+
? moduleObject.default
|
|
324
|
+
: moduleObject;
|
|
325
|
+
registerSchema(extType, extName, schema);
|
|
326
|
+
return schema;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* If an extension provides a schema, this will load the schema and attempt to
|
|
331
|
+
* register it with the schema registrar.
|
|
332
|
+
* @param {string} extName - Name of extension
|
|
333
|
+
* @param {ExtData} extData - Extension data
|
|
334
|
+
* @returns {import('ajv').SchemaObject|undefined}
|
|
335
|
+
*/
|
|
336
|
+
readExtensionSchema (extName, extData) {
|
|
337
|
+
return ExtensionConfig._readExtensionSchema(this.appiumHome, this.extensionType, extName, extData);
|
|
338
|
+
}
|
|
203
339
|
}
|
|
204
340
|
|
|
341
|
+
export { DRIVER_TYPE, PLUGIN_TYPE } from './ext-config-io';
|
|
205
342
|
export {
|
|
206
343
|
INSTALL_TYPE_NPM, INSTALL_TYPE_GIT, INSTALL_TYPE_LOCAL, INSTALL_TYPE_GITHUB,
|
|
207
|
-
INSTALL_TYPES, DEFAULT_APPIUM_HOME,
|
|
344
|
+
INSTALL_TYPES, DEFAULT_APPIUM_HOME, APPIUM_HOME
|
|
208
345
|
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Config problem
|
|
349
|
+
* @typedef {Object} Problem
|
|
350
|
+
* @property {string} err - Error message
|
|
351
|
+
* @property {any} val - Associated value
|
|
352
|
+
*/
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Alias
|
|
356
|
+
* @typedef {import('./ext-config-io').ExtensionType} ExtensionType
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Extension data (pulled from config YAML)
|
|
361
|
+
* @typedef {Object} ExtData
|
|
362
|
+
* @property {string|import('ajv').SchemaObject} [schema] - Optional schema path if the ext defined it
|
|
363
|
+
* @property {string} pkgName - Package name
|
|
364
|
+
* @property {string} installPath - Actually looks more like a module identifier? Resolved from `APPIUM_HOME`
|
|
365
|
+
*/
|
|
366
|
+
|
package/lib/grid-register.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { fs } from 'appium
|
|
2
|
+
import { fs } from '@appium/support';
|
|
3
3
|
import logger from './logger';
|
|
4
4
|
import _ from 'lodash';
|
|
5
5
|
|
|
@@ -9,21 +9,32 @@ const hubUri = (config) => {
|
|
|
9
9
|
return `${protocol}://${config.hubHost}:${config.hubPort}`;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Registers a new node with a selenium grid
|
|
14
|
+
* @param {string|object} data - Path or object representing selenium grid node config file
|
|
15
|
+
* @param {string} addr - Bind to this address
|
|
16
|
+
* @param {number} port - Bind to this port
|
|
17
|
+
* @param {string} basePath - Base path for the grid
|
|
18
|
+
*/
|
|
19
|
+
async function registerNode (data, addr, port, basePath) {
|
|
20
|
+
let configFilePath;
|
|
21
|
+
if (_.isString(data)) {
|
|
22
|
+
configFilePath = data;
|
|
23
|
+
try {
|
|
24
|
+
data = await fs.readFile(data, 'utf-8');
|
|
25
|
+
} catch (err) {
|
|
26
|
+
logger.error(`Unable to load node configuration file ${configFilePath} to register with grid: ${err.message}`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
data = JSON.parse(data);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
logger.errorAndThrow(`Syntax error in node configuration file ${configFilePath}: ${err.message}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
19
35
|
}
|
|
20
36
|
|
|
21
|
-
|
|
22
|
-
if (!data) {
|
|
23
|
-
logger.error('No data found in the node configuration file to send to the grid');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
postRequest(data, addr, port);
|
|
37
|
+
postRequest(data, addr, port, basePath);
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
async function registerToGrid (postOptions, configHolder) {
|
|
@@ -39,15 +50,7 @@ async function registerToGrid (postOptions, configHolder) {
|
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
52
|
|
|
42
|
-
function postRequest (
|
|
43
|
-
// parse json to get hub host and port
|
|
44
|
-
let configHolder;
|
|
45
|
-
try {
|
|
46
|
-
configHolder = JSON.parse(data);
|
|
47
|
-
} catch (err) {
|
|
48
|
-
logger.errorAndThrow(`Syntax error in node configuration file: ${err.message}`);
|
|
49
|
-
}
|
|
50
|
-
|
|
53
|
+
function postRequest (configHolder, addr, port, basePath) {
|
|
51
54
|
// Move Selenium 3 configuration properties to configuration object
|
|
52
55
|
if (!_.has(configHolder, 'configuration')) {
|
|
53
56
|
let configuration = {};
|
|
@@ -66,7 +69,7 @@ function postRequest (data, addr, port) {
|
|
|
66
69
|
// because we will always set localhost/127.0.0.1. this won't work if your
|
|
67
70
|
// node and grid aren't in the same place
|
|
68
71
|
if (!configHolder.configuration.url || !configHolder.configuration.host || !configHolder.configuration.port) {
|
|
69
|
-
configHolder.configuration.url = `http://${addr}:${port}
|
|
72
|
+
configHolder.configuration.url = `http://${addr}:${port}${basePath}`;
|
|
70
73
|
configHolder.configuration.host = addr;
|
|
71
74
|
configHolder.configuration.port = port;
|
|
72
75
|
}
|
package/lib/logger.js
CHANGED
package/lib/logsink.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import npmlog from 'npmlog';
|
|
2
2
|
import { createLogger, format, transports } from 'winston';
|
|
3
|
-
import { fs, logger } from 'appium
|
|
4
|
-
import dateformat from 'dateformat';
|
|
3
|
+
import { fs, logger } from '@appium/support';
|
|
5
4
|
import _ from 'lodash';
|
|
6
5
|
|
|
7
6
|
|
|
@@ -36,16 +35,20 @@ const npmToWinstonLevels = {
|
|
|
36
35
|
};
|
|
37
36
|
|
|
38
37
|
let log = null;
|
|
39
|
-
let
|
|
38
|
+
let useLocalTimeZone = false;
|
|
40
39
|
|
|
41
40
|
// add the timestamp in the correct format to the log info object
|
|
42
41
|
const timestampFormat = format.timestamp({
|
|
43
42
|
format () {
|
|
44
43
|
let date = new Date();
|
|
45
|
-
if (
|
|
46
|
-
date = new Date(date.valueOf()
|
|
44
|
+
if (useLocalTimeZone) {
|
|
45
|
+
date = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
|
|
47
46
|
}
|
|
48
|
-
|
|
47
|
+
// '2012-11-04T14:51:06.157Z' -> '2012-11-04 14:51:06:157'
|
|
48
|
+
return date.toISOString()
|
|
49
|
+
.replace(/[TZ]/g, ' ')
|
|
50
|
+
.replace(/\./g, ':')
|
|
51
|
+
.trim();
|
|
49
52
|
},
|
|
50
53
|
});
|
|
51
54
|
|
|
@@ -182,7 +185,7 @@ async function createTransports (args) {
|
|
|
182
185
|
|
|
183
186
|
async function init (args) {
|
|
184
187
|
// set de facto param passed to timestamp function
|
|
185
|
-
|
|
188
|
+
useLocalTimeZone = args.localTimezone;
|
|
186
189
|
|
|
187
190
|
// clean up in case we have initiated before since npmlog is a global object
|
|
188
191
|
clear();
|