appium 2.3.0 → 2.4.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/README.md +129 -158
- package/build/lib/appium.d.ts +72 -0
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +326 -0
- package/build/lib/appium.js.map +1 -1
- package/build/lib/cli/args.d.ts.map +1 -1
- package/build/lib/cli/args.js +24 -2
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +16 -0
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +16 -0
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +26 -2
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +127 -1
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +11 -7
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.d.ts +16 -0
- package/build/lib/cli/plugin-command.d.ts.map +1 -1
- package/build/lib/cli/plugin-command.js +16 -0
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/utils.d.ts.map +1 -1
- package/build/lib/cli/utils.js +3 -4
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/config.d.ts +1 -1
- package/build/lib/config.d.ts.map +1 -1
- package/build/lib/config.js +25 -29
- package/build/lib/config.js.map +1 -1
- package/build/lib/constants.d.ts +10 -0
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +11 -1
- package/build/lib/constants.js.map +1 -1
- package/build/lib/doctor/doctor.d.ts +55 -0
- package/build/lib/doctor/doctor.d.ts.map +1 -0
- package/build/lib/doctor/doctor.js +201 -0
- package/build/lib/doctor/doctor.js.map +1 -0
- package/build/lib/extension/index.d.ts.map +1 -1
- package/build/lib/extension/index.js +10 -4
- package/build/lib/extension/index.js.map +1 -1
- package/build/lib/grid-register.d.ts.map +1 -1
- package/build/lib/grid-register.js +1 -2
- package/build/lib/grid-register.js.map +1 -1
- package/build/lib/main.d.ts.map +1 -1
- package/build/lib/main.js +6 -0
- package/build/lib/main.js.map +1 -1
- package/build/lib/utils.d.ts +1 -0
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +2 -1
- package/build/lib/utils.js.map +1 -1
- package/build/types/cli.d.ts +1 -1
- package/build/types/cli.d.ts.map +1 -1
- package/lib/appium.js +387 -1
- package/lib/cli/args.js +27 -2
- package/lib/cli/driver-command.js +18 -0
- package/lib/cli/extension-command.js +152 -2
- package/lib/cli/parser.js +22 -8
- package/lib/cli/plugin-command.js +18 -0
- package/lib/cli/utils.js +5 -7
- package/lib/config.js +18 -24
- package/lib/constants.js +12 -0
- package/lib/doctor/doctor.js +209 -0
- package/lib/extension/index.js +10 -4
- package/lib/grid-register.js +1 -2
- package/lib/main.js +6 -0
- package/lib/utils.js +2 -1
- package/package.json +13 -13
- package/types/cli.ts +1 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import '@colors/colors';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import { util, doctor, logger } from '@appium/support';
|
|
4
|
+
|
|
5
|
+
export class Doctor {
|
|
6
|
+
/**
|
|
7
|
+
* @param {DoctorCheck[]} [checks=[]]
|
|
8
|
+
*/
|
|
9
|
+
constructor(checks = []) {
|
|
10
|
+
this.log = logger.getLogger('Doctor');
|
|
11
|
+
/** @type {DoctorCheck[]} */
|
|
12
|
+
this.checks = checks;
|
|
13
|
+
this.checks
|
|
14
|
+
.filter((c) => _.isNil(c.log))
|
|
15
|
+
.forEach((c) => { c.log = this.log; });
|
|
16
|
+
/** @type {DoctorIssue[]} */
|
|
17
|
+
this.foundIssues = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @returns {DoctorIssue[]}
|
|
22
|
+
*/
|
|
23
|
+
get issuesRequiredToFix() {
|
|
24
|
+
return this.foundIssues.filter((f) => !f.check.isOptional());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @returns {DoctorIssue[]}
|
|
29
|
+
*/
|
|
30
|
+
get issuesOptionalToFix() {
|
|
31
|
+
return this.foundIssues.filter((f) => f.check.isOptional());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The doctor shows the report
|
|
36
|
+
*/
|
|
37
|
+
async diagnose() {
|
|
38
|
+
this.log.info(`### Starting doctor diagnostics ###`);
|
|
39
|
+
this.foundIssues = [];
|
|
40
|
+
for (const check of this.checks) {
|
|
41
|
+
const res = await check.diagnose();
|
|
42
|
+
const issue = this.toIssue(res, check);
|
|
43
|
+
if (issue) {
|
|
44
|
+
this.foundIssues.push(issue);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
this.log.info(
|
|
48
|
+
`### Diagnostic completed, ${this.buildFixMessage()}. ###`
|
|
49
|
+
);
|
|
50
|
+
this.log.info('');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @returns {Promise<boolean>}
|
|
55
|
+
*/
|
|
56
|
+
async reportManualIssues() {
|
|
57
|
+
const manualIssues = _.filter(this.issuesRequiredToFix, (f) => !f.check.hasAutofix());
|
|
58
|
+
const manualIssuesOptional = _.filter(this.issuesOptionalToFix, (f) => !f.check.hasAutofix());
|
|
59
|
+
|
|
60
|
+
const handleIssues = async (headerLogs, issues) => {
|
|
61
|
+
if (_.isEmpty(issues)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const logMsg of headerLogs) {
|
|
66
|
+
this.log.info(logMsg);
|
|
67
|
+
}
|
|
68
|
+
/** @type {string[]} */
|
|
69
|
+
const fixMessages = [];
|
|
70
|
+
for (const issue of issues) {
|
|
71
|
+
const message = await issue.check.fix();
|
|
72
|
+
if (message) {
|
|
73
|
+
fixMessages.push(message);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const m of _.uniq(fixMessages)) {
|
|
77
|
+
this.log.warn(` \u279C ${m}`);
|
|
78
|
+
}
|
|
79
|
+
this.log.info('');
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
await handleIssues([
|
|
83
|
+
'### Manual Fixes Needed ###',
|
|
84
|
+
'The configuration cannot be automatically fixed, please do the following first:',
|
|
85
|
+
], manualIssues);
|
|
86
|
+
await handleIssues([
|
|
87
|
+
'### Optional Manual Fixes ###',
|
|
88
|
+
'To fix these optional issues, please do the following manually:',
|
|
89
|
+
], manualIssuesOptional);
|
|
90
|
+
|
|
91
|
+
if (manualIssues.length > 0) {
|
|
92
|
+
this.log.info('###');
|
|
93
|
+
this.log.info('');
|
|
94
|
+
this.log.info('Bye! Run doctor again when all manual fixes have been applied!');
|
|
95
|
+
this.log.info('');
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {DoctorIssue} f
|
|
103
|
+
*/
|
|
104
|
+
async runAutoFix(f) {
|
|
105
|
+
this.log.info(`### Fixing: ${f.error} ###`);
|
|
106
|
+
try {
|
|
107
|
+
await f.check.fix();
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (err.constructor.name === doctor.FixSkippedError.name) {
|
|
110
|
+
this.log.info(`### Skipped fix ###`);
|
|
111
|
+
return;
|
|
112
|
+
} else {
|
|
113
|
+
this.log.warn(`${err}`.replace(/\n$/g, ''));
|
|
114
|
+
this.log.info(`### Fix did not succeed ###`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this.log.info('Checking if this was fixed:');
|
|
119
|
+
const res = await f.check.diagnose();
|
|
120
|
+
if (res.ok) {
|
|
121
|
+
f.fixed = true;
|
|
122
|
+
this.log.info(` ${'\u2714'.green} ${res.message}`);
|
|
123
|
+
this.log.info(`### Fix was successfully applied ###`);
|
|
124
|
+
} else {
|
|
125
|
+
this.log.info(` ${'\u2716'.red} ${res.message}`);
|
|
126
|
+
this.log.info(`### Fix was applied but issue remains ###`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async runAutoFixes() {
|
|
131
|
+
const autoFixes = _.filter(this.foundIssues, (f) => f.check.hasAutofix());
|
|
132
|
+
for (const f of autoFixes) {
|
|
133
|
+
await this.runAutoFix(f);
|
|
134
|
+
this.log.info('');
|
|
135
|
+
}
|
|
136
|
+
if (_.find(autoFixes, (f) => !f.fixed)) {
|
|
137
|
+
// a few issues remain.
|
|
138
|
+
this.log.info('Bye! A few issues remain, fix manually and/or rerun doctor!');
|
|
139
|
+
} else {
|
|
140
|
+
// nothing left to fix.
|
|
141
|
+
this.log.info('Bye! All issues have been fixed!');
|
|
142
|
+
}
|
|
143
|
+
this.log.info('');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async run() {
|
|
147
|
+
await this.diagnose();
|
|
148
|
+
if (this.reportSuccess()) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (await this.reportManualIssues()) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
await this.runAutoFixes();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {DoctorCheckResult} result
|
|
159
|
+
* @param {DoctorCheck} check
|
|
160
|
+
* @returns {DoctorIssue?}
|
|
161
|
+
*/
|
|
162
|
+
toIssue(result, check) {
|
|
163
|
+
if (result.ok) {
|
|
164
|
+
this.log.info(` ${'\u2714'.green} ${result.message}`);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const errorMessage = result.optional
|
|
169
|
+
? ` ${'\u2716'.yellow} ${result.message}`
|
|
170
|
+
: ` ${'\u2716'.red} ${result.message}`;
|
|
171
|
+
this.log.warn(errorMessage);
|
|
172
|
+
return {
|
|
173
|
+
error: errorMessage,
|
|
174
|
+
check,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
buildFixMessage() {
|
|
182
|
+
return `${util.pluralize('required fix', this.issuesRequiredToFix.length, true)} needed, ` +
|
|
183
|
+
`${util.pluralize('optional fix', this.issuesOptionalToFix.length, true)} possible`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @returns {boolean}
|
|
188
|
+
*/
|
|
189
|
+
reportSuccess() {
|
|
190
|
+
if (this.issuesRequiredToFix.length === 0 && this.issuesOptionalToFix.length === 0) {
|
|
191
|
+
this.log.info('Everything looks good, bye!');
|
|
192
|
+
this.log.info('');
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @typedef DoctorIssue
|
|
201
|
+
* @property {DoctorCheck} check
|
|
202
|
+
* @property {string} error
|
|
203
|
+
* @property {boolean} [fixed]
|
|
204
|
+
*/
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @typedef {import('@appium/types').IDoctorCheck} DoctorCheck
|
|
208
|
+
* @typedef {import('@appium/types').DoctorCheckResult} DoctorCheckResult
|
|
209
|
+
*/
|
package/lib/extension/index.js
CHANGED
|
@@ -99,9 +99,12 @@ export async function getActivePlugins(pluginConfig, maxParallelImports, usePlug
|
|
|
99
99
|
`The reserved plugin name '${pluginName}' cannot be combined with other names.`
|
|
100
100
|
);
|
|
101
101
|
} else {
|
|
102
|
+
const suffix = _.isEmpty(pluginConfig.installedExtensions)
|
|
103
|
+
? `You don't have any plugins installed yet.`
|
|
104
|
+
: `Only the following ${_.size(pluginConfig.installedExtensions) === 1 ? `plugin is` : `plugins are`} ` +
|
|
105
|
+
`available: ${_.keys(pluginConfig.installedExtensions)}`;
|
|
102
106
|
throw new Error(
|
|
103
|
-
`Could not load the plugin '${pluginName}' because it is not installed. `
|
|
104
|
-
`Only the following plugins are available: ${_.keys(pluginConfig.installedExtensions)}`
|
|
107
|
+
`Could not load the plugin '${pluginName}' because it is not installed. ${suffix}`
|
|
105
108
|
);
|
|
106
109
|
}
|
|
107
110
|
}
|
|
@@ -131,9 +134,12 @@ export async function getActiveDrivers(driverConfig, maxParallelImports, useDriv
|
|
|
131
134
|
if (driverName in driverConfig.installedExtensions) {
|
|
132
135
|
filteredDriverNames.push(driverName);
|
|
133
136
|
} else {
|
|
137
|
+
const suffix = _.isEmpty(driverConfig.installedExtensions)
|
|
138
|
+
? `You don't have any drivers installed yet.`
|
|
139
|
+
: `Only the following ${_.size(driverConfig.installedExtensions) === 1 ? `driver is` : `drivers are`} ` +
|
|
140
|
+
`available: ${_.keys(driverConfig.installedExtensions)}`;
|
|
134
141
|
throw new Error(
|
|
135
|
-
`Could not load the driver '${driverName}' because it is not installed. `
|
|
136
|
-
`Only the following drivers are available: ${_.keys(driverConfig.installedExtensions)}`
|
|
142
|
+
`Could not load the driver '${driverName}' because it is not installed. ${suffix}`
|
|
137
143
|
);
|
|
138
144
|
}
|
|
139
145
|
}
|
package/lib/grid-register.js
CHANGED
|
@@ -30,10 +30,9 @@ async function registerNode(data, addr, port, basePath) {
|
|
|
30
30
|
try {
|
|
31
31
|
data = JSON.parse(data);
|
|
32
32
|
} catch (err) {
|
|
33
|
-
logger.
|
|
33
|
+
throw logger.errorWithException(
|
|
34
34
|
`Syntax error in node configuration file ${configFilePath}: ${err.message}`
|
|
35
35
|
);
|
|
36
|
-
return;
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
|
package/lib/main.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import {WebSocketServer} from 'ws';
|
|
3
4
|
import {init as logsinkInit} from './logsink'; // this import needs to come first since it sets up global npmlog
|
|
4
5
|
import logger from './logger'; // logger needs to remain second
|
|
5
6
|
import {
|
|
@@ -373,8 +374,13 @@ async function main(args) {
|
|
|
373
374
|
serverOpts.keepAliveTimeout = parsedArgs.keepAliveTimeout * 1000;
|
|
374
375
|
}
|
|
375
376
|
let server;
|
|
377
|
+
const bidiServer = new WebSocketServer({noServer: true});
|
|
378
|
+
bidiServer.on('connection', appiumDriver.onBidiConnection.bind(appiumDriver));
|
|
379
|
+
bidiServer.on('error', appiumDriver.onBidiServerError.bind(appiumDriver));
|
|
376
380
|
try {
|
|
377
381
|
server = await baseServer(serverOpts);
|
|
382
|
+
server.addWebSocketHandler('/bidi', bidiServer);
|
|
383
|
+
server.addWebSocketHandler('/bidi/:sessionId', bidiServer);
|
|
378
384
|
} catch (err) {
|
|
379
385
|
logger.error(
|
|
380
386
|
`Could not configure Appium server. It's possible that a driver or plugin tried ` +
|
package/lib/utils.js
CHANGED
|
@@ -2,7 +2,7 @@ import _ from 'lodash';
|
|
|
2
2
|
import logger from './logger';
|
|
3
3
|
import {processCapabilities, PROTOCOLS, STANDARD_CAPS, errors} from '@appium/base-driver';
|
|
4
4
|
import {inspect as dump} from 'util';
|
|
5
|
-
import {node} from '@appium/support';
|
|
5
|
+
import {node, fs} from '@appium/support';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import {SERVER_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE} from './constants';
|
|
8
8
|
import os from 'node:os';
|
|
@@ -11,6 +11,7 @@ const W3C_APPIUM_PREFIX = 'appium';
|
|
|
11
11
|
const STANDARD_CAPS_LOWERCASE = new Set([...STANDARD_CAPS].map((cap) => cap.toLowerCase()));
|
|
12
12
|
export const V4_BROADCAST_IP = '0.0.0.0';
|
|
13
13
|
export const V6_BROADCAST_IP = '::';
|
|
14
|
+
export const npmPackage = fs.readPackageJsonFrom(__dirname);
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Automation for Apps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@appium/base-driver": "^9.
|
|
64
|
-
"@appium/base-plugin": "^2.2.
|
|
65
|
-
"@appium/docutils": "^1.0.
|
|
66
|
-
"@appium/schema": "
|
|
67
|
-
"@appium/support": "^4.
|
|
68
|
-
"@appium/types": "^0.
|
|
63
|
+
"@appium/base-driver": "^9.5.0",
|
|
64
|
+
"@appium/base-plugin": "^2.2.26",
|
|
65
|
+
"@appium/docutils": "^1.0.2",
|
|
66
|
+
"@appium/schema": "~0.5.0",
|
|
67
|
+
"@appium/support": "^4.2.0",
|
|
68
|
+
"@appium/types": "^0.16.0",
|
|
69
69
|
"@sidvind/better-ajv-errors": "2.1.3",
|
|
70
70
|
"@types/argparse": "2.0.14",
|
|
71
71
|
"@types/bluebird": "3.5.42",
|
|
@@ -76,13 +76,12 @@
|
|
|
76
76
|
"ajv": "8.12.0",
|
|
77
77
|
"ajv-formats": "2.1.1",
|
|
78
78
|
"argparse": "2.0.1",
|
|
79
|
-
"async-lock": "1.4.
|
|
79
|
+
"async-lock": "1.4.1",
|
|
80
80
|
"asyncbox": "3.0.0",
|
|
81
|
-
"axios": "1.6.
|
|
81
|
+
"axios": "1.6.3",
|
|
82
82
|
"bluebird": "3.7.2",
|
|
83
83
|
"cross-env": "7.0.3",
|
|
84
|
-
"
|
|
85
|
-
"lilconfig": "2.1.0",
|
|
84
|
+
"lilconfig": "3.0.0",
|
|
86
85
|
"lodash": "4.17.21",
|
|
87
86
|
"npmlog": "7.0.1",
|
|
88
87
|
"ora": "5.4.1",
|
|
@@ -91,9 +90,10 @@
|
|
|
91
90
|
"semver": "7.5.4",
|
|
92
91
|
"source-map-support": "0.5.21",
|
|
93
92
|
"teen_process": "2.1.1",
|
|
94
|
-
"type-fest": "4.
|
|
93
|
+
"type-fest": "4.9.0",
|
|
95
94
|
"winston": "3.11.0",
|
|
96
95
|
"wrap-ansi": "7.0.0",
|
|
96
|
+
"ws": "8.16.0",
|
|
97
97
|
"yaml": "2.3.4"
|
|
98
98
|
},
|
|
99
99
|
"engines": {
|
|
@@ -103,5 +103,5 @@
|
|
|
103
103
|
"publishConfig": {
|
|
104
104
|
"access": "public"
|
|
105
105
|
},
|
|
106
|
-
"gitHead": "
|
|
106
|
+
"gitHead": "b08210a6f80dcae55bd9e6c415327eba73a8aadd"
|
|
107
107
|
}
|
package/types/cli.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type CliCommand = CliCommandServer | CliExtensionCommand;
|
|
|
19
19
|
* Possible subcommands of {@linkcode CliCommandDriver} or
|
|
20
20
|
* {@linkcode CliCommandPlugin}.
|
|
21
21
|
*/
|
|
22
|
-
export type CliExtensionSubcommand = 'install' | 'list' | 'run' | 'uninstall' | 'update';
|
|
22
|
+
export type CliExtensionSubcommand = 'install' | 'list' | 'run' | 'uninstall' | 'update'| 'doctor';
|
|
23
23
|
|
|
24
24
|
export interface CliExtensionSubcommandListArgs {
|
|
25
25
|
showInstalled?: boolean;
|