appium 3.2.2 → 3.3.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/appium.d.ts +147 -205
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +169 -282
- package/build/lib/appium.js.map +1 -1
- package/build/lib/bidi-commands.d.ts.map +1 -1
- package/build/lib/bidi-commands.js +11 -11
- package/build/lib/bidi-commands.js.map +1 -1
- package/build/lib/bootstrap/appium-initializer.d.ts +21 -0
- package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -0
- package/build/lib/bootstrap/appium-initializer.js +146 -0
- package/build/lib/bootstrap/appium-initializer.js.map +1 -0
- package/build/lib/bootstrap/appium-main-runner.d.ts +22 -0
- package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -0
- package/build/lib/bootstrap/appium-main-runner.js +109 -0
- package/build/lib/bootstrap/appium-main-runner.js.map +1 -0
- package/build/lib/bootstrap/config-file.d.ts +37 -0
- package/build/lib/bootstrap/config-file.d.ts.map +1 -0
- package/build/lib/{config-file.js → bootstrap/config-file.js} +62 -138
- package/build/lib/bootstrap/config-file.js.map +1 -0
- package/build/lib/bootstrap/grid-v3-register.d.ts +20 -0
- package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -0
- package/build/lib/bootstrap/grid-v3-register.js +185 -0
- package/build/lib/bootstrap/grid-v3-register.js.map +1 -0
- package/build/lib/bootstrap/init-types.d.ts +16 -0
- package/build/lib/bootstrap/init-types.d.ts.map +1 -0
- package/build/lib/bootstrap/init-types.js +3 -0
- package/build/lib/bootstrap/init-types.js.map +1 -0
- package/build/lib/bootstrap/main-helpers.d.ts +55 -0
- package/build/lib/bootstrap/main-helpers.d.ts.map +1 -0
- package/build/lib/bootstrap/main-helpers.js +187 -0
- package/build/lib/bootstrap/main-helpers.js.map +1 -0
- package/build/lib/bootstrap/node-helpers.d.ts +32 -0
- package/build/lib/bootstrap/node-helpers.d.ts.map +1 -0
- package/build/lib/bootstrap/node-helpers.js +201 -0
- package/build/lib/bootstrap/node-helpers.js.map +1 -0
- package/build/lib/bootstrap/startup-config.d.ts +22 -0
- package/build/lib/bootstrap/startup-config.d.ts.map +1 -0
- package/build/lib/bootstrap/startup-config.js +111 -0
- package/build/lib/bootstrap/startup-config.js.map +1 -0
- package/build/lib/cli/args.d.ts +16 -12
- package/build/lib/cli/args.d.ts.map +1 -1
- package/build/lib/cli/args.js +20 -40
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +51 -93
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +11 -66
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +173 -377
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +387 -656
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts +10 -15
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js +15 -33
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts +37 -66
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +69 -104
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.d.ts +50 -90
- package/build/lib/cli/plugin-command.d.ts.map +1 -1
- package/build/lib/cli/plugin-command.js +11 -63
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/setup-command.d.ts +21 -26
- package/build/lib/cli/setup-command.d.ts.map +1 -1
- package/build/lib/cli/setup-command.js +19 -61
- package/build/lib/cli/setup-command.js.map +1 -1
- package/build/lib/cli/utils.d.ts +33 -35
- package/build/lib/cli/utils.d.ts.map +1 -1
- package/build/lib/cli/utils.js +48 -50
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/constants.d.ts +23 -23
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +10 -15
- package/build/lib/constants.js.map +1 -1
- package/build/lib/doctor/doctor.d.ts +40 -57
- package/build/lib/doctor/doctor.d.ts.map +1 -1
- package/build/lib/doctor/doctor.js +31 -62
- package/build/lib/doctor/doctor.js.map +1 -1
- package/build/lib/extension/driver-config.d.ts +18 -77
- package/build/lib/extension/driver-config.d.ts.map +1 -1
- package/build/lib/extension/driver-config.js +37 -125
- package/build/lib/extension/driver-config.js.map +1 -1
- package/build/lib/extension/extension-config.d.ts +103 -210
- package/build/lib/extension/extension-config.d.ts.map +1 -1
- package/build/lib/extension/extension-config.js +180 -342
- package/build/lib/extension/extension-config.js.map +1 -1
- package/build/lib/extension/index.d.ts +12 -29
- package/build/lib/extension/index.d.ts.map +1 -1
- package/build/lib/extension/index.js +33 -75
- package/build/lib/extension/index.js.map +1 -1
- package/build/lib/extension/manifest-migrations.d.ts +3 -20
- package/build/lib/extension/manifest-migrations.d.ts.map +1 -1
- package/build/lib/extension/manifest-migrations.js +20 -101
- package/build/lib/extension/manifest-migrations.js.map +1 -1
- package/build/lib/extension/manifest.d.ts +61 -107
- package/build/lib/extension/manifest.d.ts.map +1 -1
- package/build/lib/extension/manifest.js +181 -356
- package/build/lib/extension/manifest.js.map +1 -1
- package/build/lib/extension/package-changed.d.ts +1 -3
- package/build/lib/extension/package-changed.d.ts.map +1 -1
- package/build/lib/extension/package-changed.js +8 -15
- package/build/lib/extension/package-changed.js.map +1 -1
- package/build/lib/extension/plugin-config.d.ts +10 -52
- package/build/lib/extension/plugin-config.d.ts.map +1 -1
- package/build/lib/extension/plugin-config.js +11 -63
- package/build/lib/extension/plugin-config.js.map +1 -1
- package/build/lib/helpers/build.d.ts +22 -0
- package/build/lib/helpers/build.d.ts.map +1 -0
- package/build/lib/helpers/build.js +109 -0
- package/build/lib/helpers/build.js.map +1 -0
- package/build/lib/helpers/capability.d.ts +38 -0
- package/build/lib/helpers/capability.d.ts.map +1 -0
- package/build/lib/helpers/capability.js +128 -0
- package/build/lib/helpers/capability.js.map +1 -0
- package/build/lib/helpers/network.d.ts +14 -0
- package/build/lib/helpers/network.d.ts.map +1 -0
- package/build/lib/helpers/network.js +35 -0
- package/build/lib/helpers/network.js.map +1 -0
- package/build/lib/insecure-features.js +6 -6
- package/build/lib/insecure-features.js.map +1 -1
- package/build/lib/inspector-commands.d.ts +6 -0
- package/build/lib/inspector-commands.d.ts.map +1 -1
- package/build/lib/inspector-commands.js +6 -0
- package/build/lib/inspector-commands.js.map +1 -1
- package/build/lib/logger.d.ts +2 -3
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +2 -3
- package/build/lib/logger.js.map +1 -1
- package/build/lib/logsink.d.ts +13 -22
- package/build/lib/logsink.d.ts.map +1 -1
- package/build/lib/logsink.js +48 -103
- package/build/lib/logsink.js.map +1 -1
- package/build/lib/main.d.ts +15 -58
- package/build/lib/main.d.ts.map +1 -1
- package/build/lib/main.js +25 -425
- package/build/lib/main.js.map +1 -1
- package/build/lib/schema/arg-spec.d.ts +32 -107
- package/build/lib/schema/arg-spec.d.ts.map +1 -1
- package/build/lib/schema/arg-spec.js +11 -107
- package/build/lib/schema/arg-spec.js.map +1 -1
- package/build/lib/schema/cli-args-guards.d.ts +34 -0
- package/build/lib/schema/cli-args-guards.d.ts.map +1 -0
- package/build/lib/schema/cli-args-guards.js +49 -0
- package/build/lib/schema/cli-args-guards.js.map +1 -0
- package/build/lib/schema/cli-args.d.ts +3 -15
- package/build/lib/schema/cli-args.d.ts.map +1 -1
- package/build/lib/schema/cli-args.js +17 -107
- package/build/lib/schema/cli-args.js.map +1 -1
- package/build/lib/schema/cli-transformers.d.ts +15 -12
- package/build/lib/schema/cli-transformers.d.ts.map +1 -1
- package/build/lib/schema/cli-transformers.js +15 -45
- package/build/lib/schema/cli-transformers.js.map +1 -1
- package/build/lib/schema/format-errors.d.ts +28 -0
- package/build/lib/schema/format-errors.d.ts.map +1 -0
- package/build/lib/schema/format-errors.js +29 -0
- package/build/lib/schema/format-errors.js.map +1 -0
- package/build/lib/schema/index.d.ts +4 -2
- package/build/lib/schema/index.d.ts.map +1 -1
- package/build/lib/schema/index.js +2 -0
- package/build/lib/schema/index.js.map +1 -1
- package/build/lib/schema/keywords.d.ts +12 -20
- package/build/lib/schema/keywords.d.ts.map +1 -1
- package/build/lib/schema/keywords.js +6 -51
- package/build/lib/schema/keywords.js.map +1 -1
- package/build/lib/schema/schema.d.ts +106 -231
- package/build/lib/schema/schema.d.ts.map +1 -1
- package/build/lib/schema/schema.js +88 -358
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils.d.ts +7 -267
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +10 -409
- package/build/lib/utils.js.map +1 -1
- package/lib/{appium.js → appium.ts} +297 -341
- package/lib/bidi-commands.ts +10 -14
- package/lib/bootstrap/appium-initializer.ts +212 -0
- package/lib/bootstrap/appium-main-runner.ts +172 -0
- package/lib/bootstrap/config-file.ts +178 -0
- package/lib/bootstrap/grid-v3-register.ts +250 -0
- package/lib/bootstrap/init-types.ts +31 -0
- package/lib/bootstrap/main-helpers.ts +223 -0
- package/lib/bootstrap/node-helpers.ts +180 -0
- package/lib/bootstrap/startup-config.ts +143 -0
- package/lib/cli/{args.js → args.ts} +45 -56
- package/lib/cli/driver-command.ts +122 -0
- package/lib/cli/{extension-command.js → extension-command.ts} +827 -906
- package/lib/cli/extension.ts +65 -0
- package/lib/cli/{parser.js → parser.ts} +93 -116
- package/lib/cli/plugin-command.ts +117 -0
- package/lib/cli/{setup-command.js → setup-command.ts} +59 -74
- package/lib/cli/utils.ts +97 -0
- package/lib/{constants.js → constants.ts} +30 -41
- package/lib/doctor/{doctor.js → doctor.ts} +82 -92
- package/lib/extension/driver-config.ts +165 -0
- package/lib/extension/{extension-config.js → extension-config.ts} +291 -405
- package/lib/extension/index.ts +143 -0
- package/lib/extension/manifest-migrations.ts +57 -0
- package/lib/extension/manifest.ts +369 -0
- package/lib/extension/{package-changed.js → package-changed.ts} +9 -18
- package/lib/extension/plugin-config.ts +62 -0
- package/lib/helpers/build.ts +111 -0
- package/lib/helpers/capability.ts +171 -0
- package/lib/helpers/network.ts +30 -0
- package/lib/insecure-features.ts +1 -1
- package/lib/inspector-commands.ts +6 -1
- package/lib/{logger.js → logger.ts} +1 -2
- package/lib/{logsink.js → logsink.ts} +91 -137
- package/lib/main.ts +60 -0
- package/lib/schema/arg-spec.ts +131 -0
- package/lib/schema/cli-args-guards.ts +67 -0
- package/lib/schema/cli-args.ts +171 -0
- package/lib/schema/cli-transformers.ts +83 -0
- package/lib/schema/format-errors.ts +43 -0
- package/lib/schema/index.ts +4 -0
- package/lib/schema/keywords.ts +96 -0
- package/lib/schema/schema.ts +448 -0
- package/lib/utils.ts +73 -0
- package/package.json +17 -18
- package/scripts/autoinstall-extensions.js +3 -0
- package/build/lib/config-file.d.ts +0 -100
- package/build/lib/config-file.d.ts.map +0 -1
- package/build/lib/config-file.js.map +0 -1
- package/build/lib/config.d.ts +0 -70
- package/build/lib/config.d.ts.map +0 -1
- package/build/lib/config.js +0 -390
- package/build/lib/config.js.map +0 -1
- package/build/lib/grid-register.d.ts +0 -10
- package/build/lib/grid-register.d.ts.map +0 -1
- package/build/lib/grid-register.js +0 -134
- package/build/lib/grid-register.js.map +0 -1
- package/lib/cli/driver-command.js +0 -174
- package/lib/cli/extension.js +0 -74
- package/lib/cli/plugin-command.js +0 -164
- package/lib/cli/utils.js +0 -91
- package/lib/config-file.js +0 -228
- package/lib/config.js +0 -389
- package/lib/extension/driver-config.js +0 -245
- package/lib/extension/index.js +0 -169
- package/lib/extension/manifest-migrations.js +0 -136
- package/lib/extension/manifest.js +0 -550
- package/lib/extension/plugin-config.js +0 -112
- package/lib/grid-register.js +0 -146
- package/lib/main.js +0 -545
- package/lib/schema/arg-spec.js +0 -229
- package/lib/schema/cli-args.js +0 -254
- package/lib/schema/cli-transformers.js +0 -113
- package/lib/schema/index.js +0 -2
- package/lib/schema/keywords.js +0 -136
- package/lib/schema/schema.js +0 -725
- package/lib/utils.js +0 -512
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import {fs} from '@appium/support';
|
|
3
|
+
import type {StringRecord} from '@appium/types';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
import {log as logger} from '../logger';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Selenium **Grid 3** (legacy hub) node integration.
|
|
9
|
+
*
|
|
10
|
+
* Registers this Appium process as a node with a Selenium Grid 3 hub using the classic
|
|
11
|
+
* `/grid/register` endpoint and `/grid/api/proxy` for re-registration checks.
|
|
12
|
+
* This does **not** apply to Selenium Grid 4 (which uses a different topology and APIs).
|
|
13
|
+
*
|
|
14
|
+
* @see https://www.selenium.dev/documentation/legacy/grid_3/
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** REST paths exposed by a Selenium Grid 3 hub. */
|
|
18
|
+
const GRID_V3_REGISTER_PATH = '/grid/register';
|
|
19
|
+
const GRID_V3_PROXY_API_PATH = '/grid/api/proxy';
|
|
20
|
+
|
|
21
|
+
interface Grid3HubConfiguration {
|
|
22
|
+
hubProtocol?: string;
|
|
23
|
+
hubHost?: string;
|
|
24
|
+
hubPort?: number;
|
|
25
|
+
url?: string;
|
|
26
|
+
host?: string;
|
|
27
|
+
port?: number;
|
|
28
|
+
id?: string;
|
|
29
|
+
register?: boolean;
|
|
30
|
+
registerCycle?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Grid3NodeConfig extends StringRecord {
|
|
34
|
+
configuration?: Grid3HubConfiguration;
|
|
35
|
+
capabilities?: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface Grid3ProxyApiResponse {
|
|
39
|
+
success: boolean;
|
|
40
|
+
msg?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default async function registerNode(
|
|
44
|
+
data: string,
|
|
45
|
+
addr: string,
|
|
46
|
+
port: number,
|
|
47
|
+
basePath: string
|
|
48
|
+
): Promise<void>;
|
|
49
|
+
export default async function registerNode(
|
|
50
|
+
data: Grid3NodeConfig,
|
|
51
|
+
addr?: string,
|
|
52
|
+
port?: number,
|
|
53
|
+
basePath?: string
|
|
54
|
+
): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Registers this server as a node with a **Selenium Grid 3** hub.
|
|
57
|
+
*
|
|
58
|
+
* When `data` is a path to a JSON config file, `addr`, `port`, and `basePath` are required (they define
|
|
59
|
+
* how the hub reaches this Appium server). When `data` is an inline config object, those parameters are
|
|
60
|
+
* optional and only used to fill missing node URL fields in the config.
|
|
61
|
+
*
|
|
62
|
+
* @deprecated Selenium Grid 3 registration is legacy and slated for removal.
|
|
63
|
+
* Use Selenium Grid 4 integration (node relay) instead.
|
|
64
|
+
*
|
|
65
|
+
* @param data - Path to a JSON file or an inline parsed config object
|
|
66
|
+
* @param addr - Bind to this address
|
|
67
|
+
* @param port - Bind to this port
|
|
68
|
+
* @param basePath - Base path for the Appium server (used in the node URL sent to the hub; may be `''`)
|
|
69
|
+
*/
|
|
70
|
+
export default async function registerNode(
|
|
71
|
+
data: string | Grid3NodeConfig,
|
|
72
|
+
addr?: string,
|
|
73
|
+
port?: number,
|
|
74
|
+
basePath?: string
|
|
75
|
+
): Promise<void> {
|
|
76
|
+
if (typeof data === 'string') {
|
|
77
|
+
if (addr === undefined || port === undefined || basePath === undefined) {
|
|
78
|
+
throw logger.errorWithException(
|
|
79
|
+
'When the first argument is a Selenium Grid 3 node config file path, address, port, and basePath ' +
|
|
80
|
+
'are required (e.g. match your Appium `--address`, `--port`, and base path).'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (typeof port !== 'number' || !Number.isFinite(port)) {
|
|
84
|
+
throw logger.errorWithException(
|
|
85
|
+
'When registering from a node config file path, port must be a finite number.'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let configHolder: Grid3NodeConfig;
|
|
91
|
+
if (_.isString(data)) {
|
|
92
|
+
const configFilePath = data;
|
|
93
|
+
let fileContent: string;
|
|
94
|
+
try {
|
|
95
|
+
fileContent = await fs.readFile(data, 'utf-8');
|
|
96
|
+
} catch (err) {
|
|
97
|
+
logger.error(
|
|
98
|
+
`Unable to load Selenium Grid 3 node configuration file ${configFilePath} to ` +
|
|
99
|
+
`register with the hub: ${(err as Error).message}`
|
|
100
|
+
);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
configHolder = JSON.parse(fileContent) as Grid3NodeConfig;
|
|
105
|
+
} catch (err) {
|
|
106
|
+
throw logger.errorWithException(
|
|
107
|
+
`Syntax error in Selenium Grid 3 node configuration file ${configFilePath}: ` +
|
|
108
|
+
(err as Error).message
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
configHolder = data;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
postRequest(configHolder, addr, port, basePath);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Base URL for the Selenium Grid 3 hub (protocol + host + port). */
|
|
119
|
+
function hubUri(config: Grid3HubConfiguration): string {
|
|
120
|
+
const protocol = config.hubProtocol || 'http';
|
|
121
|
+
return `${protocol}://${config.hubHost}:${config.hubPort}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** POST registration payload to the Selenium Grid 3 hub. */
|
|
125
|
+
async function registerToGrid(
|
|
126
|
+
postOptions: {url: string; method: string; data: Grid3NodeConfig},
|
|
127
|
+
configHolder: Grid3NodeConfig
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
const hubCfg = configHolder.configuration;
|
|
130
|
+
if (!hubCfg) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const {status} = await axios(postOptions);
|
|
135
|
+
if (status !== 200) {
|
|
136
|
+
throw new Error(`Request failed with code ${status}`);
|
|
137
|
+
}
|
|
138
|
+
logger.debug(
|
|
139
|
+
`Appium successfully registered with the Selenium Grid 3 hub at ` + hubUri(hubCfg)
|
|
140
|
+
);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
logger.error(
|
|
143
|
+
`An attempt to register with the Selenium Grid 3 hub was unsuccessful: ` +
|
|
144
|
+
(err as Error).message
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function postRequest(
|
|
150
|
+
configHolder: Grid3NodeConfig,
|
|
151
|
+
addr?: string,
|
|
152
|
+
port?: number,
|
|
153
|
+
basePath?: string
|
|
154
|
+
): void {
|
|
155
|
+
// Move Selenium Grid 3 (flat) configuration properties into `configuration`
|
|
156
|
+
if (!_.has(configHolder, 'configuration')) {
|
|
157
|
+
const configuration: StringRecord = {};
|
|
158
|
+
const holder = configHolder as StringRecord;
|
|
159
|
+
for (const property in holder) {
|
|
160
|
+
if (_.has(holder, property) && property !== 'capabilities') {
|
|
161
|
+
configuration[property] = holder[property];
|
|
162
|
+
delete holder[property];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
holder.configuration = configuration as Grid3HubConfiguration;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const cfg = configHolder.configuration;
|
|
169
|
+
if (!cfg) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// if the node config does not have the appium/webdriver url, host, and port,
|
|
174
|
+
// automatically add it based on how appium was initialized
|
|
175
|
+
// otherwise, we will take whatever the user setup
|
|
176
|
+
// because we will always set localhost/127.0.0.1. this won't work if your
|
|
177
|
+
// node and hub aren't in the same place
|
|
178
|
+
if (!cfg.url || !cfg.host || !cfg.port) {
|
|
179
|
+
cfg.url = `http://${addr}:${port}${basePath}`;
|
|
180
|
+
cfg.host = addr;
|
|
181
|
+
cfg.port = port;
|
|
182
|
+
}
|
|
183
|
+
// if the node config does not have id automatically add it
|
|
184
|
+
if (!cfg.id) {
|
|
185
|
+
cfg.id = `http://${cfg.host}:${cfg.port}`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// the post options
|
|
189
|
+
const regRequest = {
|
|
190
|
+
url: `${hubUri(cfg)}${GRID_V3_REGISTER_PATH}`,
|
|
191
|
+
method: 'POST',
|
|
192
|
+
data: configHolder,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
if (cfg.register !== true) {
|
|
196
|
+
logger.debug(`No Selenium Grid 3 hub registration sent (${cfg.register} = false)`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const registerCycleInterval = cfg.registerCycle;
|
|
201
|
+
if (registerCycleInterval === undefined || isNaN(registerCycleInterval) || registerCycleInterval <= 0) {
|
|
202
|
+
logger.warn(
|
|
203
|
+
`'registerCycle' is not a valid positive number. ` +
|
|
204
|
+
`No registration request will be sent to the Selenium Grid 3 hub.`
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// initiate a new Thread
|
|
209
|
+
let first = true;
|
|
210
|
+
logger.debug(
|
|
211
|
+
`Starting auto-register thread for Selenium Grid 3. ` +
|
|
212
|
+
`Will try to register every ${registerCycleInterval} ms.`
|
|
213
|
+
);
|
|
214
|
+
setInterval(async function registerRetry() {
|
|
215
|
+
if (first) {
|
|
216
|
+
first = false;
|
|
217
|
+
await registerToGrid(regRequest, configHolder);
|
|
218
|
+
} else if (!(await isAlreadyRegistered(configHolder))) {
|
|
219
|
+
// make the http POST to the Selenium Grid 3 hub for registration
|
|
220
|
+
await registerToGrid(regRequest, configHolder);
|
|
221
|
+
}
|
|
222
|
+
}, registerCycleInterval);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Query the Selenium Grid 3 hub to see if this node id is already registered. */
|
|
226
|
+
async function isAlreadyRegistered(configHolder: Grid3NodeConfig): Promise<boolean | undefined> {
|
|
227
|
+
//check if node is already registered
|
|
228
|
+
const hubCfg = configHolder.configuration;
|
|
229
|
+
if (!hubCfg?.id) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const id = hubCfg.id;
|
|
233
|
+
try {
|
|
234
|
+
const {data, status} = await axios<Grid3ProxyApiResponse>({
|
|
235
|
+
url: `${hubUri(hubCfg)}${GRID_V3_PROXY_API_PATH}`,
|
|
236
|
+
params: {id},
|
|
237
|
+
timeout: 10000,
|
|
238
|
+
});
|
|
239
|
+
if (status !== 200) {
|
|
240
|
+
throw new Error(`Request failed with code ${status}`);
|
|
241
|
+
}
|
|
242
|
+
if (!data.success) {
|
|
243
|
+
// if register fail, print the debug msg
|
|
244
|
+
logger.debug(`Selenium Grid 3 hub registration check: ${data.msg}`);
|
|
245
|
+
}
|
|
246
|
+
return data.success;
|
|
247
|
+
} catch (err) {
|
|
248
|
+
logger.debug(`Selenium Grid 3 hub down or not responding: ${(err as Error).message}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type {AppiumDriver} from '../appium';
|
|
2
|
+
import type {
|
|
3
|
+
Args,
|
|
4
|
+
CliCommand,
|
|
5
|
+
CliCommandServer,
|
|
6
|
+
CliCommandSetupSubcommand,
|
|
7
|
+
CliExtensionSubcommand,
|
|
8
|
+
ParsedArgs,
|
|
9
|
+
} from 'appium/types';
|
|
10
|
+
import type {ExtensionConfigs} from '../extension';
|
|
11
|
+
|
|
12
|
+
/** Empty object returned when `init` completes for non-server CLI flows (extension/setup). */
|
|
13
|
+
export type ExtCommandInitResult = Record<string, never>;
|
|
14
|
+
|
|
15
|
+
/** Driver/plugin config plus server instance and merged server args after successful server `init`. */
|
|
16
|
+
export type ServerInitData = ExtensionConfigs & {
|
|
17
|
+
appiumDriver: AppiumDriver;
|
|
18
|
+
parsedArgs: ParsedArgs<CliCommandServer>;
|
|
19
|
+
appiumHome: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Discriminated by CLI command: server subcommand yields {@link ServerInitData}; otherwise {@link ExtCommandInitResult}. */
|
|
23
|
+
export type InitResult<Cmd extends CliCommand = CliCommandServer> = Cmd extends CliCommandServer
|
|
24
|
+
? ServerInitData
|
|
25
|
+
: ExtCommandInitResult;
|
|
26
|
+
|
|
27
|
+
/** CLI + programmatic args before `init` narrows by `subcommand`. */
|
|
28
|
+
export type PreConfigArgs = Args<
|
|
29
|
+
CliCommand,
|
|
30
|
+
CliExtensionSubcommand | CliCommandSetupSubcommand | void
|
|
31
|
+
>;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import {inspect as dump, type InspectOptions} from 'node:util';
|
|
2
|
+
import {log as logger} from '../logger';
|
|
3
|
+
import {
|
|
4
|
+
routeConfiguringFunction as makeRouter,
|
|
5
|
+
normalizeBasePath,
|
|
6
|
+
server as baseServer,
|
|
7
|
+
type ServerOpts,
|
|
8
|
+
} from '@appium/base-driver';
|
|
9
|
+
import _ from 'lodash';
|
|
10
|
+
import type {AppiumServer, Driver, MethodMap, UpdateServerCallback} from '@appium/types';
|
|
11
|
+
import {WebSocketServer} from 'ws';
|
|
12
|
+
import type {NetworkInterfaceInfo} from 'node:os';
|
|
13
|
+
import type {AppiumDriver} from '../appium';
|
|
14
|
+
import {APPIUM_VER, getBuildInfo, getGitRev, updateBuildInfo} from '../helpers/build';
|
|
15
|
+
import {checkNodeOk, requireDir} from './node-helpers';
|
|
16
|
+
import {getNonDefaultServerArgs} from './startup-config';
|
|
17
|
+
import {validate as validateSchema} from '../schema/schema';
|
|
18
|
+
import {fetchInterfaces, V4_BROADCAST_IP, isBroadcastIp} from '../helpers/network';
|
|
19
|
+
import {LONG_STACKTRACE_LIMIT, BIDI_BASE_PATH} from '../constants';
|
|
20
|
+
import type {Args, ParsedArgs, CliCommandServer} from 'appium/types';
|
|
21
|
+
import type {DriverNameMap, PluginNameMap} from '../extension';
|
|
22
|
+
|
|
23
|
+
const isStdoutTTY = process.stdout.isTTY;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Logs a value to the console using the info logger (with util.inspect formatting).
|
|
27
|
+
*/
|
|
28
|
+
export const inspect = _.flow(
|
|
29
|
+
_.partialRight(dump as (object: unknown, options: InspectOptions) => string, {
|
|
30
|
+
colors: true,
|
|
31
|
+
depth: null,
|
|
32
|
+
compact: !isStdoutTTY,
|
|
33
|
+
}),
|
|
34
|
+
(...args: unknown[]) => {
|
|
35
|
+
logger.info(...args);
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Prints the current build info JSON to stdout.
|
|
41
|
+
*
|
|
42
|
+
* This updates build metadata first (using GitHub fallback) and then logs the
|
|
43
|
+
* resulting {@link BuildInfo} object.
|
|
44
|
+
*/
|
|
45
|
+
export async function showBuildInfo(): Promise<void> {
|
|
46
|
+
await updateBuildInfo(true);
|
|
47
|
+
// eslint-disable-next-line no-console -- CLI output for --build-info
|
|
48
|
+
console.log(JSON.stringify(getBuildInfo()));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Human-readable label for where Appium resolved `APPIUM_HOME` from (CLI, env, or autodetect).
|
|
53
|
+
*/
|
|
54
|
+
export function determineAppiumHomeSource(appiumHomeFromArgs?: string | null): string {
|
|
55
|
+
if (!_.isNil(appiumHomeFromArgs)) {
|
|
56
|
+
return 'appiumHome config value';
|
|
57
|
+
}
|
|
58
|
+
if (process.env.APPIUM_HOME) {
|
|
59
|
+
return 'APPIUM_HOME environment variable';
|
|
60
|
+
}
|
|
61
|
+
return 'autodetected Appium home path';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Logs the REST listener URL; if the bind address is a broadcast address, lists concrete interface URLs.
|
|
66
|
+
*/
|
|
67
|
+
export function logServerAddress(url: string): void {
|
|
68
|
+
const urlObj = new URL(url);
|
|
69
|
+
logger.info(`Appium REST http interface listener started on ${url}`);
|
|
70
|
+
if (!isBroadcastIp(urlObj.hostname)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const interfaces = fetchInterfaces(urlObj.hostname === V4_BROADCAST_IP ? 4 : 6);
|
|
75
|
+
const toLabel = (iface: NetworkInterfaceInfo) => {
|
|
76
|
+
const href = urlObj.href.replace(urlObj.hostname, iface.address);
|
|
77
|
+
return iface.internal ? `${href} (only accessible from the same host)` : href;
|
|
78
|
+
};
|
|
79
|
+
logger.info(
|
|
80
|
+
`You can provide the following ${interfaces.length === 1 ? 'URL' : 'URLs'} ` +
|
|
81
|
+
`in your client code to connect to this server:\n` +
|
|
82
|
+
interfaces.map((iface) => `\t${toLabel(iface)}`).join('\n'),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validates Node version, optional long stack traces, schema, tmp dir, and `--build-info` early exit.
|
|
88
|
+
*
|
|
89
|
+
* @param args - Parsed server CLI args
|
|
90
|
+
* @param throwInsteadOfExit - When true, rethrows failures instead of calling `process.exit(1)`
|
|
91
|
+
*/
|
|
92
|
+
export async function preflightChecks(
|
|
93
|
+
args: ParsedArgs<CliCommandServer>,
|
|
94
|
+
throwInsteadOfExit = false,
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
try {
|
|
97
|
+
checkNodeOk();
|
|
98
|
+
if (args.longStacktrace) {
|
|
99
|
+
Error.stackTraceLimit = LONG_STACKTRACE_LIMIT;
|
|
100
|
+
}
|
|
101
|
+
if (args.showBuildInfo) {
|
|
102
|
+
await showBuildInfo();
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
validateSchema(args);
|
|
107
|
+
|
|
108
|
+
if (args.tmpDir) {
|
|
109
|
+
await requireDir(args.tmpDir, !args.noPermsCheck, 'tmpDir argument value');
|
|
110
|
+
}
|
|
111
|
+
} catch (err: unknown) {
|
|
112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
113
|
+
logger.error((message as string & {red: string}).red);
|
|
114
|
+
if (throwInsteadOfExit) {
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Prints welcome line (version + optional git rev), non-default server args, and default capabilities.
|
|
124
|
+
*/
|
|
125
|
+
export async function logStartupInfo(args: ParsedArgs<CliCommandServer>): Promise<void> {
|
|
126
|
+
let welcome = `Welcome to Appium v${APPIUM_VER}`;
|
|
127
|
+
const appiumRev = await getGitRev();
|
|
128
|
+
if (appiumRev) {
|
|
129
|
+
welcome += ` (REV ${appiumRev})`;
|
|
130
|
+
}
|
|
131
|
+
logger.info(welcome);
|
|
132
|
+
|
|
133
|
+
const showArgs = getNonDefaultServerArgs(args);
|
|
134
|
+
if (_.size(showArgs)) {
|
|
135
|
+
logNonDefaultArgsWarning(showArgs);
|
|
136
|
+
}
|
|
137
|
+
if (!_.isEmpty(args.defaultCapabilities)) {
|
|
138
|
+
logDefaultCapabilitiesWarning(args.defaultCapabilities);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Collects `updateServer` hooks from active driver and plugin classes for HTTP server customization.
|
|
144
|
+
*/
|
|
145
|
+
export function getServerUpdaters(
|
|
146
|
+
driverClasses: DriverNameMap,
|
|
147
|
+
pluginClasses: PluginNameMap,
|
|
148
|
+
): UpdateServerCallback[] {
|
|
149
|
+
return _.compact(_.map([...driverClasses.keys(), ...pluginClasses.keys()], 'updateServer'));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Merges `newMethodMap` contributions from all active drivers and plugins into one method map.
|
|
154
|
+
*/
|
|
155
|
+
export function getExtraMethodMap(driverClasses: DriverNameMap, pluginClasses: PluginNameMap): MethodMap<Driver> {
|
|
156
|
+
return [...driverClasses.keys(), ...pluginClasses.keys()].reduce<MethodMap<Driver>>(
|
|
157
|
+
(map, klass) => ({
|
|
158
|
+
...map,
|
|
159
|
+
...(klass.newMethodMap ?? {}),
|
|
160
|
+
}),
|
|
161
|
+
{},
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Builds {@link ServerOpts} and normalized base path for the Appium HTTP server from CLI args and extensions.
|
|
167
|
+
*/
|
|
168
|
+
export function buildServerOpts(
|
|
169
|
+
appiumDriver: AppiumDriver,
|
|
170
|
+
parsedArgs: ParsedArgs<CliCommandServer>,
|
|
171
|
+
driverClasses: DriverNameMap,
|
|
172
|
+
pluginClasses: PluginNameMap,
|
|
173
|
+
): {serverOpts: ServerOpts; normalizedBasePath: string} {
|
|
174
|
+
const routeConfiguringFunction = makeRouter(appiumDriver);
|
|
175
|
+
const serverOpts: ServerOpts = {
|
|
176
|
+
routeConfiguringFunction,
|
|
177
|
+
port: parsedArgs.port,
|
|
178
|
+
hostname: parsedArgs.address,
|
|
179
|
+
allowCors: parsedArgs.allowCors,
|
|
180
|
+
basePath: parsedArgs.basePath,
|
|
181
|
+
serverUpdaters: getServerUpdaters(driverClasses, pluginClasses),
|
|
182
|
+
extraMethodMap: getExtraMethodMap(driverClasses, pluginClasses),
|
|
183
|
+
cliArgs: parsedArgs,
|
|
184
|
+
};
|
|
185
|
+
const normalizedBasePath = normalizeBasePath(parsedArgs.basePath);
|
|
186
|
+
for (const timeoutArgName of ['keepAliveTimeout', 'requestTimeout'] as const) {
|
|
187
|
+
if (_.isInteger(parsedArgs[timeoutArgName])) {
|
|
188
|
+
serverOpts[timeoutArgName] = parsedArgs[timeoutArgName] * 1000;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return {serverOpts, normalizedBasePath};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Creates the Appium HTTP server and attaches WebSocket handlers for BiDi under the normalized base path.
|
|
196
|
+
*/
|
|
197
|
+
export async function createAppiumServer(
|
|
198
|
+
serverOpts: ServerOpts,
|
|
199
|
+
appiumDriver: AppiumDriver,
|
|
200
|
+
normalizedBasePath: string,
|
|
201
|
+
): Promise<AppiumServer> {
|
|
202
|
+
const bidiServer = new WebSocketServer({noServer: true});
|
|
203
|
+
bidiServer.on('connection', appiumDriver.onBidiConnection.bind(appiumDriver));
|
|
204
|
+
bidiServer.on('error', appiumDriver.onBidiServerError.bind(appiumDriver));
|
|
205
|
+
const server = await baseServer(serverOpts);
|
|
206
|
+
const bidiBasePath = `${normalizedBasePath}${BIDI_BASE_PATH}`;
|
|
207
|
+
await server.addWebSocketHandler(bidiBasePath, bidiServer);
|
|
208
|
+
await server.addWebSocketHandler(`${bidiBasePath}/:sessionId`, bidiServer);
|
|
209
|
+
return server;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function logNonDefaultArgsWarning(args: Args): void {
|
|
213
|
+
logger.info('Non-default server args:');
|
|
214
|
+
inspect(args);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function logDefaultCapabilitiesWarning(caps: ParsedArgs<CliCommandServer>['defaultCapabilities']): void {
|
|
218
|
+
logger.info(
|
|
219
|
+
'Default capabilities, which will be added to each request ' +
|
|
220
|
+
'unless overridden by desired capabilities:',
|
|
221
|
+
);
|
|
222
|
+
inspect(caps);
|
|
223
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import * as semver from 'semver';
|
|
6
|
+
import B from 'bluebird';
|
|
7
|
+
import {system, fs, npm} from '@appium/support';
|
|
8
|
+
import {log as logger} from '../logger';
|
|
9
|
+
import {getAppiumModuleRoot, npmPackage} from '../utils';
|
|
10
|
+
import {rootDir, updateBuildInfo, getBuildInfo} from '../helpers/build';
|
|
11
|
+
|
|
12
|
+
const MIN_NODE_VERSION = (npmPackage.engines as Record<string, string>).node;
|
|
13
|
+
|
|
14
|
+
interface NodeModuleWithInitPaths {
|
|
15
|
+
Module: {_initPaths(): void};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface DebugInfoInput {
|
|
19
|
+
driverConfig: {installedExtensions: unknown};
|
|
20
|
+
pluginConfig: {installedExtensions: unknown};
|
|
21
|
+
appiumHome: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @throws {Error} If Node version is outside of the supported range
|
|
26
|
+
*/
|
|
27
|
+
export function checkNodeOk(): void {
|
|
28
|
+
const version = getNodeVersion();
|
|
29
|
+
if (!semver.satisfies(version, MIN_NODE_VERSION)) {
|
|
30
|
+
throw new Error(`Node version must be at least ${MIN_NODE_VERSION}; current is ${version.version}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Adjusts NODE_PATH so CJS drivers/plugins can load peer deps. Does not work with ESM.
|
|
36
|
+
*/
|
|
37
|
+
export function adjustNodePath(): void {
|
|
38
|
+
let appiumModuleSearchRoot: string;
|
|
39
|
+
try {
|
|
40
|
+
appiumModuleSearchRoot = path.dirname(getAppiumModuleRoot());
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.warn((error as Error).message);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const refreshRequirePaths = (): boolean => {
|
|
47
|
+
try {
|
|
48
|
+
// Private API; see https://gist.github.com/branneman/8048520#7-the-hack
|
|
49
|
+
(require('node:module') as NodeModuleWithInitPaths).Module._initPaths();
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (!process.env.NODE_PATH) {
|
|
57
|
+
process.env.NODE_PATH = appiumModuleSearchRoot;
|
|
58
|
+
if (refreshRequirePaths()) {
|
|
59
|
+
process.env.APPIUM_OMIT_PEER_DEPS = '1';
|
|
60
|
+
} else {
|
|
61
|
+
delete process.env.NODE_PATH;
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const nodePathParts = process.env.NODE_PATH.split(path.delimiter);
|
|
67
|
+
if (nodePathParts.includes(appiumModuleSearchRoot)) {
|
|
68
|
+
process.env.APPIUM_OMIT_PEER_DEPS = '1';
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
nodePathParts.push(appiumModuleSearchRoot);
|
|
73
|
+
process.env.NODE_PATH = nodePathParts.join(path.delimiter);
|
|
74
|
+
if (refreshRequirePaths()) {
|
|
75
|
+
process.env.APPIUM_OMIT_PEER_DEPS = '1';
|
|
76
|
+
} else {
|
|
77
|
+
process.env.NODE_PATH = _.without(nodePathParts, appiumModuleSearchRoot).join(path.delimiter);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Prints JSON debug info (OS, Node/npm, Appium build, installed drivers/plugins) to stdout.
|
|
83
|
+
* Input combines extension config snapshots with the resolved Appium home path.
|
|
84
|
+
*/
|
|
85
|
+
export async function showDebugInfo({driverConfig, pluginConfig, appiumHome}: DebugInfoInput): Promise<void> {
|
|
86
|
+
const getNpmVersion = async (): Promise<string> => {
|
|
87
|
+
const {stdout} = await npm.exec('--version', [], {cwd: process.cwd()});
|
|
88
|
+
return _.trim(stdout);
|
|
89
|
+
};
|
|
90
|
+
const findNpmLocation = async (): Promise<string> =>
|
|
91
|
+
await fs.which(system.isWindows() ? 'npm.cmd' : 'npm');
|
|
92
|
+
|
|
93
|
+
const [npmVersion, npmLocation] = await B.all([
|
|
94
|
+
...[getNpmVersion, findNpmLocation].map((f) => getSafeResult(f, 'unknown')),
|
|
95
|
+
updateBuildInfo() as Promise<unknown>,
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
const debugInfo = {
|
|
99
|
+
os: {
|
|
100
|
+
platform: os.platform(),
|
|
101
|
+
release: os.release(),
|
|
102
|
+
arch: os.arch(),
|
|
103
|
+
homedir: os.homedir(),
|
|
104
|
+
username: os.userInfo().username,
|
|
105
|
+
},
|
|
106
|
+
node: {
|
|
107
|
+
version: process.version,
|
|
108
|
+
arch: process.arch,
|
|
109
|
+
cwd: process.cwd(),
|
|
110
|
+
argv: process.argv,
|
|
111
|
+
env: process.env,
|
|
112
|
+
npm: {
|
|
113
|
+
location: npmLocation,
|
|
114
|
+
version: npmVersion,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
appium: {
|
|
118
|
+
location: rootDir,
|
|
119
|
+
homedir: appiumHome,
|
|
120
|
+
build: getBuildInfo(),
|
|
121
|
+
drivers: driverConfig.installedExtensions,
|
|
122
|
+
plugins: pluginConfig.installedExtensions,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
console.log(JSON.stringify(debugInfo, null, 2));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Ensures a directory exists and (optionally) is writeable.
|
|
130
|
+
*
|
|
131
|
+
* If the directory does not exist, this attempts to create it recursively.
|
|
132
|
+
*
|
|
133
|
+
* @throws {Error}
|
|
134
|
+
*/
|
|
135
|
+
export async function requireDir(
|
|
136
|
+
root: string,
|
|
137
|
+
requireWriteable = true,
|
|
138
|
+
displayName = 'folder path'
|
|
139
|
+
): Promise<void> {
|
|
140
|
+
let stat;
|
|
141
|
+
try {
|
|
142
|
+
stat = await fs.stat(root);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
if ((e as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
145
|
+
try {
|
|
146
|
+
await fs.mkdir(root, {recursive: true});
|
|
147
|
+
return;
|
|
148
|
+
} catch {}
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`The ${displayName} '${root}' must exist and be a valid directory`);
|
|
151
|
+
}
|
|
152
|
+
if (stat && !stat.isDirectory()) {
|
|
153
|
+
throw new Error(`The ${displayName} '${root}' must be a valid directory`);
|
|
154
|
+
}
|
|
155
|
+
if (requireWriteable) {
|
|
156
|
+
try {
|
|
157
|
+
await fs.access(root, fs.constants.W_OK);
|
|
158
|
+
} catch {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`The ${displayName} '${root}' must be writeable for the current user account '${os.userInfo().username}'`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getNodeVersion(): semver.SemVer {
|
|
167
|
+
return semver.coerce(process.version) as semver.SemVer;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Calculates the result of the given function and return its value
|
|
172
|
+
* or the default one if there was an exception.
|
|
173
|
+
*/
|
|
174
|
+
async function getSafeResult<T>(f: () => Promise<T>, defaultValue: T): Promise<T> {
|
|
175
|
+
try {
|
|
176
|
+
return await f();
|
|
177
|
+
} catch {
|
|
178
|
+
return defaultValue;
|
|
179
|
+
}
|
|
180
|
+
}
|