appium 2.0.0-beta.23 → 2.0.0-beta.24

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.
@@ -73,6 +73,11 @@ export class ArgSpec {
73
73
  */
74
74
  dest;
75
75
 
76
+ /**
77
+ * The same as {@link ArgSpec.dest} but without the leading `<extType>.<extName>.` prefix.
78
+ */
79
+ rawDest;
80
+
76
81
  /**
77
82
  * Whatever the default value of this argument is, as specified by the
78
83
  * `default` property of the schema.
@@ -100,10 +105,10 @@ export class ArgSpec {
100
105
 
101
106
  // if no explicit `dest` provided, just camelCase the name to avoid needing
102
107
  // to use bracket syntax when accessing props on the parsed args object.
103
- const baseDest = _.camelCase(dest ?? name);
108
+ const rawDest = _.camelCase(dest ?? name);
104
109
 
105
110
  const destKeypath =
106
- extType && extName ? [extType, extName, baseDest].join('.') : baseDest;
111
+ extType && extName ? [extType, extName, rawDest].join('.') : rawDest;
107
112
 
108
113
  this.defaultValue = defaultValue;
109
114
  this.name = name;
@@ -112,6 +117,7 @@ export class ArgSpec {
112
117
  this.arg = arg;
113
118
  this.dest = destKeypath;
114
119
  this.ref = ref;
120
+ this.rawDest = rawDest;
115
121
  }
116
122
 
117
123
  /**
@@ -98,11 +98,9 @@ function makeDescription (schema) {
98
98
  * as understood by `argparse`.
99
99
  * @param {AppiumJSONSchema} subSchema - JSON schema for the option
100
100
  * @param {ArgSpec} argSpec - Argument spec tuple
101
- * @param {SubSchemaToArgDefOptions} [opts] - Options
102
101
  * @returns {[string[], import('argparse').ArgumentOptions]} Tuple of flag and options
103
102
  */
104
- function subSchemaToArgDef (subSchema, argSpec, opts = {}) {
105
- const {overrides = {}} = opts;
103
+ function subSchemaToArgDef (subSchema, argSpec) {
106
104
  let {
107
105
  type,
108
106
  appiumCliAliases,
@@ -110,7 +108,7 @@ function subSchemaToArgDef (subSchema, argSpec, opts = {}) {
110
108
  enum: enumValues,
111
109
  } = subSchema;
112
110
 
113
- const {name, arg, dest} = argSpec;
111
+ const {name, arg} = argSpec;
114
112
 
115
113
  const aliases = [
116
114
  aliasToFlag(argSpec),
@@ -142,7 +140,8 @@ function subSchemaToArgDef (subSchema, argSpec, opts = {}) {
142
140
  switch (type) {
143
141
  // booleans do not have a type per `ArgumentOptions`, just an "action"
144
142
  case TYPENAMES.BOOLEAN: {
145
- argOpts.action = 'store_true';
143
+ argOpts.action = 'store_const';
144
+ argOpts.const = true;
146
145
  break;
147
146
  }
148
147
 
@@ -227,16 +226,6 @@ function subSchemaToArgDef (subSchema, argSpec, opts = {}) {
227
226
  }
228
227
  }
229
228
 
230
- // overrides override anything we computed here. usually this involves "custom types",
231
- // which are really just transform functions.
232
- argOpts = _.merge(
233
- argOpts,
234
- /** should the override keys correspond to the prop name or the prop dest?
235
- * the prop dest is computed by {@link aliasToDest}.
236
- */
237
- overrides[dest] ?? {},
238
- );
239
-
240
229
  return [aliases, argOpts];
241
230
  }
242
231
 
@@ -244,32 +233,19 @@ function subSchemaToArgDef (subSchema, argSpec, opts = {}) {
244
233
  * Converts the finalized, flattened schema representation into
245
234
  * ArgumentDefinitions for handoff to `argparse`.
246
235
  *
247
- * @param {ToParserArgsOptions} opts - Options
248
236
  * @throws If schema has not been added to ajv (via `finalizeSchema()`)
249
237
  * @returns {import('../cli/args').ArgumentDefinitions} A map of arryas of
250
238
  * aliases to `argparse` arguments; empty if no schema found
251
239
  */
252
- export function toParserArgs (opts = {}) {
240
+ export function toParserArgs () {
253
241
  const flattened = flattenSchema().filter(({schema}) => !schema.appiumCliIgnored);
254
242
  return new Map(
255
243
  _.map(flattened, ({schema, argSpec}) =>
256
- subSchemaToArgDef(schema, argSpec, opts),
244
+ subSchemaToArgDef(schema, argSpec),
257
245
  ),
258
246
  );
259
247
  }
260
248
 
261
- /**
262
- * Options for {@link toParserArgs}
263
- * @typedef {SubSchemaToArgDefOptions} ToParserArgsOptions
264
- */
265
-
266
- /**
267
- * Options for {@link subSchemaToArgDef}.
268
- * @typedef {Object} SubSchemaToArgDefOptions
269
- * @property {string} [prefix] - The prefix to use for the flag, if any
270
- * @property {{[key: string]: import('argparse').ArgumentOptions}} [overrides] - An object of key/value pairs to override the default values
271
- */
272
-
273
249
  /**
274
250
  * @template T
275
251
  * @typedef {import('ajv/dist/types').FormatValidator<T>} FormatValidator<T>
@@ -4,11 +4,11 @@ import Ajv from 'ajv';
4
4
  import addFormats from 'ajv-formats';
5
5
  import _ from 'lodash';
6
6
  import path from 'path';
7
- import {DRIVER_TYPE, PLUGIN_TYPE} from '../extension-config';
8
- import {ReadonlyMap} from '../utils';
7
+ import { DRIVER_TYPE, PLUGIN_TYPE } from '../extension-config';
8
+ import { ReadonlyMap } from '../utils';
9
9
  import appiumConfigSchema from './appium-config-schema';
10
- import {ArgSpec, SERVER_PROP_NAME, APPIUM_CONFIG_SCHEMA_ID} from './arg-spec';
11
- import {keywords} from './keywords';
10
+ import { APPIUM_CONFIG_SCHEMA_ID, ArgSpec, SERVER_PROP_NAME } from './arg-spec';
11
+ import { keywords } from './keywords';
12
12
 
13
13
  /**
14
14
  * Extensions that an extension schema file can have.
@@ -91,6 +91,7 @@ class AppiumSchema {
91
91
  'flatten',
92
92
  'getArgSpec',
93
93
  'getDefaults',
94
+ 'getDefaultsForExtension',
94
95
  'getSchema',
95
96
  'hasArgSpec',
96
97
  'isFinalized',
@@ -152,7 +153,7 @@ class AppiumSchema {
152
153
  const ajv = this._ajv;
153
154
 
154
155
  // Ajv will _mutate_ the schema, so we need to clone it.
155
- const baseSchema = _.cloneDeep(/** @type {StrictSchemaObject} */(appiumConfigSchema));
156
+ const baseSchema = _.cloneDeep(appiumConfigSchema);
156
157
 
157
158
  /**
158
159
  *
@@ -202,7 +203,7 @@ class AppiumSchema {
202
203
  ajv.validateSchema(schema, true);
203
204
  addArgSpecs(schema.properties, extType, extName);
204
205
  ajv.addSchema(schema, $ref);
205
- finalizedSchemas[$ref] = /** @type {StrictSchemaObject} */(schema);
206
+ finalizedSchemas[$ref] = /** @type {StrictSchemaObject} */ (schema);
206
207
  });
207
208
  return baseSchema;
208
209
  },
@@ -277,12 +278,14 @@ class AppiumSchema {
277
278
  * @returns {void}
278
279
  */
279
280
  registerSchema (extType, extName, schema) {
280
- if (!(extType && extName && !_.isEmpty(schema))) {
281
+ if (!(extType && extName) || _.isUndefined(schema)) {
281
282
  throw new TypeError(
282
- 'Expected nonempty extension type, extension name and schema parameters',
283
+ 'Expected extension type, extension name, and a defined schema',
283
284
  );
284
285
  }
285
-
286
+ if (!AppiumSchema.isSupportedSchemaType(schema)) {
287
+ throw new SchemaUnsupportedSchemaError(schema, extType, extName);
288
+ }
286
289
  const normalizedExtName = _.kebabCase(extName);
287
290
  if (this.hasRegisteredSchema(extType, normalizedExtName)) {
288
291
  if (this._registeredSchemas[extType].get(normalizedExtName) === schema) {
@@ -320,22 +323,67 @@ class AppiumSchema {
320
323
  /**
321
324
  * Returns a `Record` of argument "dest" strings to default values.
322
325
  *
323
- * The "dest" string is the property name in object returned by `argparse.ArgumentParser['parse_args']`.
324
- * @returns {Record<string,ArgSpec['defaultValue']>}
326
+ * The "dest" string is the property name in object returned by
327
+ * `argparse.ArgumentParser['parse_args']`.
328
+ * @template {boolean|undefined} Flattened
329
+ * @param {Flattened} [flatten=true] - If `true`, flattens the returned object
330
+ * using "keypath"-style keys of the format `<extType>.<extName>.<argName>`.
331
+ * Otherwise, returns a nested object using `extType` and `extName` as
332
+ * properties. Base arguments (server arguments) are always at the top level.
333
+ * @returns {DefaultValues<Flattened>}
325
334
  */
326
- getDefaults () {
335
+ getDefaults (flatten = /** @type {Flattened} */ (true)) {
327
336
  if (!this.isFinalized()) {
328
337
  throw new SchemaFinalizationError();
329
338
  }
330
- return [...this._argSpecs.values()].reduce(
331
- (defaults, {defaultValue, dest}) => {
339
+
340
+ /**
341
+ * @private
342
+ * @callback DefaultReducer
343
+ * @param {DefaultValues<Flattened>} defaults
344
+ * @param {ArgSpec} argSpec
345
+ * @returns {DefaultValues<Flattened>}
346
+ */
347
+ /** @type {DefaultReducer} */
348
+ const reducer = flatten
349
+ ? (defaults, {defaultValue, dest}) => {
332
350
  if (!_.isUndefined(defaultValue)) {
333
351
  defaults[dest] = defaultValue;
334
352
  }
335
353
  return defaults;
336
- },
337
- {},
354
+ }
355
+ : (defaults, {defaultValue, dest}) => {
356
+ if (!_.isUndefined(defaultValue)) {
357
+ _.set(defaults, dest, defaultValue);
358
+ }
359
+ return defaults;
360
+ };
361
+
362
+ /** @type {DefaultValues<Flattened>} */
363
+ const retval = {};
364
+ return [...this._argSpecs.values()].reduce(reducer, retval);
365
+ }
366
+
367
+ /**
368
+ * Returns a flattened Record of defaults for a specific extension. Keys will
369
+ * be of format `<argName>`.
370
+ * @param {ExtensionType} extType - Extension type
371
+ * @param {string} extName - Extension name
372
+ * @returns {Record<string,ArgSpecDefaultValue>}
373
+ */
374
+ getDefaultsForExtension (extType, extName) {
375
+ if (!this.isFinalized()) {
376
+ throw new SchemaFinalizationError();
377
+ }
378
+ const specs = [...this._argSpecs.values()].filter(
379
+ (spec) => spec.extType === extType && spec.extName === extName,
338
380
  );
381
+ return specs.reduce((defaults, {defaultValue, rawDest}) => {
382
+ if (!_.isUndefined(defaultValue)) {
383
+ defaults[rawDest] = defaultValue;
384
+ }
385
+ return defaults;
386
+ }, {});
339
387
  }
340
388
 
341
389
  /**
@@ -416,7 +464,7 @@ class AppiumSchema {
416
464
  * Retrieves the schema itself
417
465
  * @public
418
466
  * @param {string} [ref] - Schema ID
419
- * @throws If the schema has not yet been _finalized_
467
+ * @throws If the schema has not yet been finalized
420
468
  * @returns {SchemaObject}
421
469
  */
422
470
  getSchema (ref = APPIUM_CONFIG_SCHEMA_ID) {
@@ -450,9 +498,7 @@ class AppiumSchema {
450
498
  * @returns {import('ajv').ErrorObject[]} Array of errors, if any.
451
499
  */
452
500
  validate (value, ref = APPIUM_CONFIG_SCHEMA_ID) {
453
- const validator = /** @type {import('ajv').ValidateFunction} */ (
454
- this._getValidator(ref)
455
- );
501
+ const validator = this._getValidator(ref);
456
502
  return !validator(value) && _.isArray(validator.errors)
457
503
  ? [...validator.errors]
458
504
  : [];
@@ -466,6 +512,15 @@ class AppiumSchema {
466
512
  static isAllowedSchemaFileExtension (filename) {
467
513
  return ALLOWED_SCHEMA_EXTENSIONS.has(path.extname(filename));
468
514
  }
515
+
516
+ /**
517
+ * Returns `true` if `schema` is a plain object with a non-true `$async` property.
518
+ * @param {any} schema - Schema to check
519
+ * @returns {schema is SchemaObject}
520
+ */
521
+ static isSupportedSchemaType (schema) {
522
+ return _.isPlainObject(schema) && schema.$async !== true;
523
+ }
469
524
  }
470
525
 
471
526
  /**
@@ -534,6 +589,54 @@ export class SchemaUnknownSchemaError extends ReferenceError {
534
589
  }
535
590
  }
536
591
 
592
+ /**
593
+ * Thrown when a schema is provided, but it's of an unsupported type.
594
+ *
595
+ * "Valid" schemas which are unsupported include boolean schemas and async schemas
596
+ * (having a `true` `$async` property).
597
+ */
598
+ export class SchemaUnsupportedSchemaError extends TypeError {
599
+ /**
600
+ * @type {Readonly<string>}
601
+ */
602
+ code = 'APPIUMERR_SCHEMA_UNSUPPORTED_SCHEMA';
603
+
604
+ /**
605
+ * @type {Readonly<{schema: any, extType: ExtensionType, extName: string}>}
606
+ */
607
+ data;
608
+
609
+ /**
610
+ * @param {any} schema
611
+ * @param {ExtensionType} extType
612
+ * @param {string} extName
613
+ */
614
+ constructor (schema, extType, extName) {
615
+ // https://github.com/Microsoft/TypeScript/issues/8277
616
+ super(
617
+ (() => {
618
+ let msg = `Unsupported schema from ${extType} "${extName}":`;
619
+ if (_.isBoolean(schema)) {
620
+ return `${msg} schema cannot be a boolean`;
621
+ }
622
+ if (_.isPlainObject(schema)) {
623
+ if (schema.$async) {
624
+ return `${msg} schema cannot be an async schema`;
625
+ }
626
+ /* istanbul ignore next */
627
+ throw new TypeError(
628
+ `schema IS supported; this error should not be thrown (this is a bug). value of schema: ${JSON.stringify(
629
+ schema,
630
+ )}`,
631
+ );
632
+ }
633
+ return `${msg} schema must be a plain object without a true "$async" property`;
634
+ })(),
635
+ );
636
+ this.data = {schema, extType, extName};
637
+ }
638
+ }
639
+
537
640
  const appiumSchema = AppiumSchema.create();
538
641
 
539
642
  export const {
@@ -546,12 +649,14 @@ export const {
546
649
  validate,
547
650
  getSchema,
548
651
  flatten: flattenSchema,
549
- getDefaults: getDefaultsFromSchema,
652
+ getDefaults: getDefaultsForSchema,
653
+ getDefaultsForExtension,
550
654
  } = appiumSchema;
551
655
  export const {isAllowedSchemaFileExtension} = AppiumSchema;
552
656
 
553
657
  /**
554
- * @typedef {import('ajv').SchemaObject} SchemaObject
658
+ * Appium only supports schemas that are plain objects; not arrays.
659
+ * @typedef {import('ajv').SchemaObject & {[key: number]: never}} SchemaObject
555
660
  */
556
661
 
557
662
  /**
@@ -575,3 +680,18 @@ export const {isAllowedSchemaFileExtension} = AppiumSchema;
575
680
  * Intermediate data structure used when converting the entire schema down to CLI arguments.
576
681
  * @typedef {{schema: SchemaObject, argSpec: ArgSpec}[]} FlattenedSchema
577
682
  */
683
+
684
+ /**
685
+ * @typedef {ArgSpec['defaultValue']} ArgSpecDefaultValue
686
+ */
687
+
688
+ /**
689
+ * e.g. `{driver: {foo: 'bar'}}` where `foo` is the arg name and `bar` is the default value.
690
+ * @typedef {Record<string,Record<string,ArgSpecDefaultValue>>} NestedArgSpecDefaultValue
691
+ */
692
+
693
+ /**
694
+ * Helper type for the return value of {@link AppiumSchema.getDefaults}
695
+ * @template {boolean|undefined} Flattened
696
+ * @typedef {Record<string,Flattened extends true ? ArgSpecDefaultValue : ArgSpecDefaultValue | NestedArgSpecDefaultValue>} DefaultValues
697
+ */
package/lib/utils.js CHANGED
@@ -1,37 +1,36 @@
1
+ // @ts-check
2
+
1
3
  import _ from 'lodash';
2
4
  import logger from './logger';
5
+ // @ts-ignore
3
6
  import { processCapabilities, PROTOCOLS } from '@appium/base-driver';
4
7
  import { fs } from '@appium/support';
8
+ import { inspect as dump } from 'util';
5
9
 
6
10
  const W3C_APPIUM_PREFIX = 'appium';
7
11
 
8
- function inspectObject (args) {
9
- function getValueArray (obj, indent = ' ') {
10
- if (!_.isObject(obj)) {
11
- return [obj];
12
- }
12
+ /**
13
+ *
14
+ * If `stdout` is a TTY, this is `true`.
15
+ *
16
+ * Used for tighter control over log output.
17
+ * @type {boolean}
18
+ */
19
+ const isStdoutTTY = process.stdout.isTTY;
13
20
 
14
- let strArr = ['{'];
15
- for (let [arg, value] of _.toPairs(obj)) {
16
- if (!_.isObject(value)) {
17
- strArr.push(`${indent} ${arg}: ${value}`);
18
- } else {
19
- value = getValueArray(value, `${indent} `);
20
- strArr.push(`${indent} ${arg}: ${value.shift()}`);
21
- strArr.push(...value);
22
- }
23
- }
24
- strArr.push(`${indent}}`);
25
- return strArr;
26
- }
27
- for (let [arg, value] of _.toPairs(args)) {
28
- value = getValueArray(value);
29
- logger.info(` ${arg}: ${value.shift()}`);
30
- for (let val of value) {
31
- logger.info(val);
32
- }
33
- }
34
- }
21
+ /**
22
+ * Dumps to value to the console using `info` logger.
23
+ *
24
+ * @todo May want to force color to be `false` if {@link isStdoutTTY} is `false`.
25
+ */
26
+ const inspect = _.flow(
27
+ _.partialRight(
28
+ /** @type {(object: any, options: import('util').InspectOptions) => string} */(dump),
29
+ {colors: true, depth: null, compact: !isStdoutTTY}
30
+ ),
31
+ (...args) => {
32
+ logger.info(...args);
33
+ });
35
34
 
36
35
  /**
37
36
  * Takes the caps that were provided in the request and translates them
@@ -236,8 +235,8 @@ class ReadonlyMap extends Map {
236
235
  return super.set(key, value);
237
236
  }
238
237
 
239
- delete (key) {
240
- throw new Error(`${key} cannot be deleted`);
238
+ delete () {
239
+ return false;
241
240
  }
242
241
 
243
242
  clear () {
@@ -246,6 +245,6 @@ class ReadonlyMap extends Map {
246
245
  }
247
246
 
248
247
  export {
249
- inspectObject, parseCapsForInnerDriver, insertAppiumPrefixes, rootDir,
248
+ inspect, parseCapsForInnerDriver, insertAppiumPrefixes, rootDir,
250
249
  getPackageVersion, pullSettings, removeAppiumPrefixes, ReadonlyMap
251
250
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium",
3
- "version": "2.0.0-beta.23",
3
+ "version": "2.0.0-beta.24",
4
4
  "description": "Automation for Apps.",
5
5
  "keywords": [
6
6
  "automation",
@@ -43,9 +43,9 @@
43
43
  "zip-and-upload": "npm run zip && npm run upload"
44
44
  },
45
45
  "dependencies": {
46
- "@appium/base-driver": "^8.2.2",
46
+ "@appium/base-driver": "^8.2.3",
47
47
  "@appium/base-plugin": "1.8.0",
48
- "@appium/support": "^2.55.2",
48
+ "@appium/support": "^2.55.3",
49
49
  "@babel/runtime": "7.16.3",
50
50
  "@sidvind/better-ajv-errors": "0.9.2",
51
51
  "ajv": "8.8.2",
@@ -79,5 +79,5 @@
79
79
  "tag": "next"
80
80
  },
81
81
  "homepage": "https://appium.io",
82
- "gitHead": "280d409df6c02d36b4ffc4d02a95ab3f4649b08c"
82
+ "gitHead": "ca90a11813546ab4851e5b1f0406f420a53227e6"
83
83
  }
package/types/types.d.ts CHANGED
@@ -160,6 +160,11 @@ interface MoreArgs {
160
160
  /**
161
161
  * If true, show the build info and exit
162
162
  */
163
+ showBuildInfo: boolean;
164
+
165
+ /**
166
+ * If true, show config and exit
167
+ */
163
168
  showConfig: boolean;
164
169
 
165
170
  /**