appium 3.2.2 → 3.3.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.
Files changed (120) hide show
  1. package/build/lib/cli/args.d.ts +16 -12
  2. package/build/lib/cli/args.d.ts.map +1 -1
  3. package/build/lib/cli/args.js +15 -35
  4. package/build/lib/cli/args.js.map +1 -1
  5. package/build/lib/cli/driver-command.d.ts +51 -93
  6. package/build/lib/cli/driver-command.d.ts.map +1 -1
  7. package/build/lib/cli/driver-command.js +11 -66
  8. package/build/lib/cli/driver-command.js.map +1 -1
  9. package/build/lib/cli/extension-command.d.ts +211 -415
  10. package/build/lib/cli/extension-command.d.ts.map +1 -1
  11. package/build/lib/cli/extension-command.js +384 -653
  12. package/build/lib/cli/extension-command.js.map +1 -1
  13. package/build/lib/cli/extension.d.ts +11 -16
  14. package/build/lib/cli/extension.d.ts.map +1 -1
  15. package/build/lib/cli/extension.js +10 -28
  16. package/build/lib/cli/extension.js.map +1 -1
  17. package/build/lib/cli/parser.d.ts +40 -69
  18. package/build/lib/cli/parser.d.ts.map +1 -1
  19. package/build/lib/cli/parser.js +24 -59
  20. package/build/lib/cli/parser.js.map +1 -1
  21. package/build/lib/cli/plugin-command.d.ts +50 -90
  22. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  23. package/build/lib/cli/plugin-command.js +11 -63
  24. package/build/lib/cli/plugin-command.js.map +1 -1
  25. package/build/lib/cli/setup-command.d.ts +21 -26
  26. package/build/lib/cli/setup-command.d.ts.map +1 -1
  27. package/build/lib/cli/setup-command.js +13 -55
  28. package/build/lib/cli/setup-command.js.map +1 -1
  29. package/build/lib/cli/utils.d.ts +27 -29
  30. package/build/lib/cli/utils.d.ts.map +1 -1
  31. package/build/lib/cli/utils.js +29 -31
  32. package/build/lib/cli/utils.js.map +1 -1
  33. package/build/lib/config-file.d.ts +24 -67
  34. package/build/lib/config-file.d.ts.map +1 -1
  35. package/build/lib/config-file.js +56 -115
  36. package/build/lib/config-file.js.map +1 -1
  37. package/build/lib/config.d.ts +42 -44
  38. package/build/lib/config.d.ts.map +1 -1
  39. package/build/lib/config.js +75 -107
  40. package/build/lib/config.js.map +1 -1
  41. package/build/lib/constants.d.ts +23 -23
  42. package/build/lib/constants.d.ts.map +1 -1
  43. package/build/lib/constants.js +10 -15
  44. package/build/lib/constants.js.map +1 -1
  45. package/build/lib/doctor/doctor.d.ts +40 -57
  46. package/build/lib/doctor/doctor.d.ts.map +1 -1
  47. package/build/lib/doctor/doctor.js +29 -60
  48. package/build/lib/doctor/doctor.js.map +1 -1
  49. package/build/lib/grid-register.d.ts +32 -7
  50. package/build/lib/grid-register.d.ts.map +1 -1
  51. package/build/lib/grid-register.js +84 -48
  52. package/build/lib/grid-register.js.map +1 -1
  53. package/build/lib/logsink.d.ts +13 -22
  54. package/build/lib/logsink.d.ts.map +1 -1
  55. package/build/lib/logsink.js +48 -103
  56. package/build/lib/logsink.js.map +1 -1
  57. package/build/lib/main.js +1 -1
  58. package/build/lib/main.js.map +1 -1
  59. package/build/lib/schema/arg-spec.d.ts +32 -107
  60. package/build/lib/schema/arg-spec.d.ts.map +1 -1
  61. package/build/lib/schema/arg-spec.js +11 -107
  62. package/build/lib/schema/arg-spec.js.map +1 -1
  63. package/build/lib/schema/cli-args.d.ts +3 -15
  64. package/build/lib/schema/cli-args.d.ts.map +1 -1
  65. package/build/lib/schema/cli-args.js +15 -105
  66. package/build/lib/schema/cli-args.js.map +1 -1
  67. package/build/lib/schema/cli-transformers.d.ts +15 -12
  68. package/build/lib/schema/cli-transformers.d.ts.map +1 -1
  69. package/build/lib/schema/cli-transformers.js +15 -45
  70. package/build/lib/schema/cli-transformers.js.map +1 -1
  71. package/build/lib/schema/index.d.ts +2 -2
  72. package/build/lib/schema/index.d.ts.map +1 -1
  73. package/build/lib/schema/index.js.map +1 -1
  74. package/build/lib/schema/keywords.d.ts +12 -20
  75. package/build/lib/schema/keywords.d.ts.map +1 -1
  76. package/build/lib/schema/keywords.js +6 -51
  77. package/build/lib/schema/keywords.js.map +1 -1
  78. package/build/lib/schema/schema.d.ts +106 -231
  79. package/build/lib/schema/schema.d.ts.map +1 -1
  80. package/build/lib/schema/schema.js +75 -345
  81. package/build/lib/schema/schema.js.map +1 -1
  82. package/build/lib/utils.d.ts +59 -238
  83. package/build/lib/utils.d.ts.map +1 -1
  84. package/build/lib/utils.js +55 -207
  85. package/build/lib/utils.js.map +1 -1
  86. package/lib/cli/{args.js → args.ts} +40 -51
  87. package/lib/cli/driver-command.ts +122 -0
  88. package/lib/cli/{extension-command.js → extension-command.ts} +610 -689
  89. package/lib/cli/extension.ts +65 -0
  90. package/lib/cli/{parser.js → parser.ts} +48 -71
  91. package/lib/cli/plugin-command.ts +117 -0
  92. package/lib/cli/{setup-command.js → setup-command.ts} +57 -72
  93. package/lib/cli/utils.ts +97 -0
  94. package/lib/config-file.ts +212 -0
  95. package/lib/{config.js → config.ts} +129 -141
  96. package/lib/{constants.js → constants.ts} +30 -41
  97. package/lib/doctor/{doctor.js → doctor.ts} +81 -91
  98. package/lib/grid-register.ts +250 -0
  99. package/lib/{logsink.js → logsink.ts} +91 -137
  100. package/lib/main.js +1 -1
  101. package/lib/schema/arg-spec.ts +131 -0
  102. package/lib/schema/cli-args.ts +171 -0
  103. package/lib/schema/cli-transformers.ts +83 -0
  104. package/lib/schema/keywords.ts +96 -0
  105. package/lib/schema/schema.ts +449 -0
  106. package/lib/utils.ts +404 -0
  107. package/package.json +16 -16
  108. package/lib/cli/driver-command.js +0 -174
  109. package/lib/cli/extension.js +0 -74
  110. package/lib/cli/plugin-command.js +0 -164
  111. package/lib/cli/utils.js +0 -91
  112. package/lib/config-file.js +0 -228
  113. package/lib/grid-register.js +0 -146
  114. package/lib/schema/arg-spec.js +0 -229
  115. package/lib/schema/cli-args.js +0 -254
  116. package/lib/schema/cli-transformers.js +0 -113
  117. package/lib/schema/keywords.js +0 -136
  118. package/lib/schema/schema.js +0 -725
  119. package/lib/utils.js +0 -512
  120. /package/lib/schema/{index.js → index.ts} +0 -0
@@ -0,0 +1,97 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import ora from 'ora';
4
+
5
+ export const JSON_SPACES = 4;
6
+
7
+ type ErrorLike = Error & {stderr?: unknown};
8
+
9
+ /**
10
+ * Log an error to the console and exit the process.
11
+ *
12
+ * @param json - whether we should log json or text
13
+ * @param msg - error message, object, Error instance, etc.
14
+ */
15
+ export function errAndQuit(json: boolean, msg: unknown): never {
16
+ if (json) {
17
+ console.log(JSON.stringify({error: String(msg)}, null, JSON_SPACES));
18
+ } else {
19
+ console.error((String(msg) as any).red);
20
+ if ((msg as ErrorLike)?.stderr) {
21
+ console.error((String((msg as ErrorLike).stderr) as any).red);
22
+ }
23
+ }
24
+ process.exit(1);
25
+ }
26
+
27
+ /**
28
+ * Conditionally log something to the console.
29
+ *
30
+ * @param json - whether we are in json mode (and should therefore not log)
31
+ * @param msg - string to log
32
+ */
33
+ export function log(json: boolean, msg: string): void {
34
+ if (!json) {
35
+ console.log(msg);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Start a spinner, execute an async function, and then stop the spinner.
41
+ *
42
+ * @param json - whether we are in json mode (and should therefore not log)
43
+ * @param msg - string to log
44
+ * @param fn - function to wrap with spinning
45
+ * @returns result of `fn`
46
+ */
47
+ export async function spinWith<T>(
48
+ json: boolean,
49
+ msg: string,
50
+ fn: () => T | Promise<T>
51
+ ): Promise<T> {
52
+ if (json) {
53
+ return await fn();
54
+ }
55
+ const spinner = ora(msg).start();
56
+ try {
57
+ const res = await fn();
58
+ spinner.succeed();
59
+ return res;
60
+ } catch (err) {
61
+ spinner.fail();
62
+ throw err;
63
+ }
64
+ }
65
+
66
+ export class RingBuffer<T = any> {
67
+ private readonly size: number;
68
+ private readonly buffer: T[] = [];
69
+
70
+ constructor(size = 50) {
71
+ this.size = size;
72
+ }
73
+
74
+ /**
75
+ * Get the current buffer contents.
76
+ */
77
+ getBuff(): T[] {
78
+ return this.buffer;
79
+ }
80
+
81
+ /**
82
+ * Add an item to the buffer.
83
+ */
84
+ enqueue(item: T): void {
85
+ if (this.buffer.length >= this.size) {
86
+ this.dequeue();
87
+ }
88
+ this.buffer.push(item);
89
+ }
90
+
91
+ /**
92
+ * Remove the oldest item from the buffer.
93
+ */
94
+ dequeue(): void {
95
+ this.buffer.shift();
96
+ }
97
+ }
@@ -0,0 +1,212 @@
1
+ import betterAjvErrors, {type IOutputError} from '@sidvind/better-ajv-errors';
2
+ import type {ErrorObject, SchemaObject} from 'ajv';
3
+ import {lilconfig, type LoaderSync, type LilconfigResult} from 'lilconfig';
4
+ import _ from 'lodash';
5
+ import * as yaml from 'yaml';
6
+ import type {AppiumConfig, NormalizedAppiumConfig} from '@appium/types';
7
+ import {getSchema, validate} from './schema/schema';
8
+
9
+ /**
10
+ * A cache of the raw config file (a JSON string) at a filepath.
11
+ * This is used for better error reporting.
12
+ * Note that config files needn't be JSON, but it helps if they are.
13
+ */
14
+ const rawConfig = new Map<string, RawJson>();
15
+
16
+ /**
17
+ * Given an array of errors and the result of loading a config file, generate a
18
+ * helpful string for the user.
19
+ *
20
+ * - If `opts` contains a `json` property, this should be the original JSON
21
+ * _string_ of the config file. This is only applicable if the config file
22
+ * was in JSON format. If present, it will associate line numbers with errors.
23
+ * - If `errors` happens to be empty, this will throw.
24
+ *
25
+ * @throws {TypeError} If `errors` is empty
26
+ */
27
+ export function formatErrors(
28
+ errors: ErrorObject[] = [],
29
+ config: ReadConfigFileResult['config'] | Record<string, unknown> | string = {},
30
+ opts: FormatConfigErrorsOptions = {}
31
+ ): string | IOutputError[] {
32
+ if (errors && !errors.length) {
33
+ throw new TypeError('Array of errors must be non-empty');
34
+ }
35
+ return betterAjvErrors(getSchema(opts.schemaId), config, errors, {
36
+ json: opts.json,
37
+ format: opts.pretty === false ? 'js' : 'cli',
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Given an optional path, read a config file. Validates the config file.
43
+ *
44
+ * Call {@link validate} if you already have a config object.
45
+ * @public
46
+ * @returns Contains config and filepath, if found, and any errors
47
+ */
48
+ export async function readConfigFile(
49
+ filepath?: string,
50
+ opts: ReadConfigFileOptions = {}
51
+ ): Promise<ReadConfigFileResult> {
52
+ const lc = lilconfig('appium', {
53
+ loaders: {
54
+ '.yaml': yamlLoader as LoaderSync,
55
+ '.yml': yamlLoader as LoaderSync,
56
+ '.json': jsonLoader as LoaderSync,
57
+ noExt: jsonLoader as LoaderSync,
58
+ },
59
+ packageProp: 'appiumConfig',
60
+ });
61
+
62
+ const result = filepath ? await loadConfigFile(lc, filepath) : await searchConfigFile(lc);
63
+
64
+ if (result?.filepath && !result?.isEmpty) {
65
+ const {pretty = true} = opts;
66
+ try {
67
+ let configResult: ReadConfigFileResult;
68
+ const errors = validate(result.config) as ErrorObject[];
69
+ if (_.isEmpty(errors)) {
70
+ configResult = {...result, errors};
71
+ } else {
72
+ const reason = formatErrors(errors, result.config as Record<string, unknown>, {
73
+ json: rawConfig.get(result.filepath),
74
+ pretty,
75
+ });
76
+ configResult = reason ? {...result, errors, reason} : {...result, errors};
77
+ }
78
+
79
+ // normalize (to camel case) all top-level property names of the config file
80
+ configResult.config = normalizeConfig(configResult.config as AppiumConfig);
81
+
82
+ return configResult;
83
+ } finally {
84
+ // clean up the raw config file cache, which is only kept to better report errors.
85
+ rawConfig.delete(result.filepath);
86
+ }
87
+ }
88
+ return result ?? {};
89
+ }
90
+
91
+ /**
92
+ * Convert schema property names to either a) the value of the `appiumCliDest` property, if any; or b) camel-case
93
+ * @returns New object with camel-cased keys (or `dest` keys).
94
+ */
95
+ export function normalizeConfig(config: AppiumConfig): NormalizedAppiumConfig {
96
+ const schema = getSchema();
97
+
98
+ const isSchemaTypeObject = (schemaObj: SchemaObject | Record<string, unknown> | undefined): boolean =>
99
+ Boolean((schemaObj as SchemaObject | undefined)?.properties || (schemaObj as SchemaObject | undefined)?.type === 'object');
100
+
101
+ const normalize = (rootConfig: AppiumConfig, section?: string): Record<string, unknown> => {
102
+ const obj = _.isUndefined(section)
103
+ ? rootConfig
104
+ : (_.get(rootConfig, section, rootConfig) as Record<string, unknown>);
105
+
106
+ const mappedObj = _.mapKeys(obj, (_v, prop) =>
107
+ _.get(schema, `properties.server.properties[${prop}].appiumCliDest`, _.camelCase(prop))
108
+ );
109
+
110
+ return _.mapValues(mappedObj, (value, property) => {
111
+ const nextSection = section ? `${section}.${property}` : property;
112
+ return isSchemaTypeObject((schema as any).properties?.[property])
113
+ ? normalize(rootConfig, nextSection)
114
+ : value;
115
+ });
116
+ };
117
+
118
+ return normalize(config) as NormalizedAppiumConfig;
119
+ }
120
+
121
+ /**
122
+ * lilconfig loader to handle `.yaml` files
123
+ */
124
+ function yamlLoader(filepath: string, content: string): unknown {
125
+ try {
126
+ return yaml.parse(content);
127
+ } catch (e) {
128
+ throw new Error(
129
+ `The YAML config at '${filepath}' cannot be loaded. Original error: ${(e as Error).message}`
130
+ );
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Custom JSON loader that caches the raw config file (for use with `better-ajv-errors`).
136
+ * If it weren't for this cache, this would be unnecessary.
137
+ */
138
+ function jsonLoader(filepath: string, content: string): unknown {
139
+ rawConfig.set(filepath, content);
140
+ try {
141
+ return JSON.parse(content);
142
+ } catch (e) {
143
+ rawConfig.delete(filepath);
144
+ throw new Error(
145
+ `The JSON config at '${filepath}' cannot be loaded. Original error: ${(e as Error).message}`
146
+ );
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Loads a config file from an explicit path
152
+ */
153
+ async function loadConfigFile(lc: LilconfigAsyncSearcher, filepath: string): Promise<LilconfigResult> {
154
+ try {
155
+ // removing "await" will cause any rejection to _not_ be caught in this block!
156
+ return await lc.load(filepath);
157
+ } catch (err) {
158
+ if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
159
+ (err as NodeJS.ErrnoException).message = `Config file not found at user-provided path: ${filepath}`;
160
+ throw err;
161
+ } else if (err instanceof SyntaxError) {
162
+ // generally invalid JSON
163
+ err.message = `Config file at user-provided path ${filepath} is invalid:\n${err.message}`;
164
+ throw err;
165
+ }
166
+ throw err;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Searches for a config file
172
+ */
173
+ async function searchConfigFile(lc: LilconfigAsyncSearcher): Promise<LilconfigResult> {
174
+ return await lc.search();
175
+ }
176
+
177
+ /**
178
+ * Result of calling {@link readConfigFile}.
179
+ */
180
+ export interface ReadConfigFileResult {
181
+ errors?: ErrorObject[];
182
+ filepath?: string;
183
+ isEmpty?: boolean;
184
+ config?: NormalizedAppiumConfig;
185
+ reason?: string | IOutputError[];
186
+ }
187
+
188
+ /**
189
+ * Options for {@link readConfigFile}.
190
+ */
191
+ export interface ReadConfigFileOptions {
192
+ pretty?: boolean;
193
+ }
194
+
195
+ /**
196
+ * The string should be a raw JSON string.
197
+ */
198
+ export type RawJson = string;
199
+
200
+ /**
201
+ * Options for {@link formatErrors}.
202
+ */
203
+ export interface FormatConfigErrorsOptions {
204
+ json?: RawJson;
205
+ pretty?: boolean;
206
+ schemaId?: string;
207
+ }
208
+
209
+ /**
210
+ * This is an `AsyncSearcher` which is inexplicably _not_ exported by the `lilconfig` type definition.
211
+ */
212
+ type LilconfigAsyncSearcher = ReturnType<typeof lilconfig>;