appium-mac2-driver 3.2.0 → 3.2.2
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/CHANGELOG.md +14 -0
- package/build/lib/commands/record-screen.d.ts +11 -12
- package/build/lib/commands/record-screen.d.ts.map +1 -1
- package/build/lib/commands/record-screen.js +2 -0
- package/build/lib/commands/record-screen.js.map +1 -1
- package/build/lib/constraints.d.ts +56 -0
- package/build/lib/constraints.d.ts.map +1 -0
- package/build/lib/constraints.js +57 -0
- package/build/lib/constraints.js.map +1 -0
- package/build/lib/driver.d.ts +30 -90
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +57 -46
- package/build/lib/driver.js.map +1 -1
- package/build/lib/method-map.d.ts +1 -1
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js +6 -6
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/wda-mac.d.ts +29 -71
- package/build/lib/wda-mac.d.ts.map +1 -1
- package/build/lib/wda-mac.js +141 -159
- package/build/lib/wda-mac.js.map +1 -1
- package/lib/commands/record-screen.js +1 -1
- package/lib/constraints.ts +60 -0
- package/lib/{driver.js → driver.ts} +118 -65
- package/lib/{method-map.js → method-map.ts} +7 -6
- package/lib/{wda-mac.js → wda-mac.ts} +205 -190
- package/npm-shrinkwrap.json +346 -296
- package/package.json +1 -1
- package/build/lib/desired-caps.d.ts +0 -66
- package/build/lib/desired-caps.d.ts.map +0 -1
- package/build/lib/desired-caps.js +0 -58
- package/build/lib/desired-caps.js.map +0 -1
- package/lib/desired-caps.js +0 -55
|
@@ -10,6 +10,12 @@ import { SubProcess, exec } from 'teen_process';
|
|
|
10
10
|
import { waitForCondition } from 'asyncbox';
|
|
11
11
|
import { checkPortStatus } from 'portscanner';
|
|
12
12
|
import { execSync } from 'child_process';
|
|
13
|
+
import type {
|
|
14
|
+
HTTPMethod,
|
|
15
|
+
HTTPBody,
|
|
16
|
+
ProxyResponse,
|
|
17
|
+
ProxyOptions,
|
|
18
|
+
} from '@appium/types';
|
|
13
19
|
import { listChildrenProcessIds, getModuleRoot } from './utils';
|
|
14
20
|
|
|
15
21
|
const log = logger.getLogger('WebDriverAgentMac');
|
|
@@ -24,69 +30,55 @@ const STARTUP_TIMEOUT_MS = 120000;
|
|
|
24
30
|
const DEFAULT_SYSTEM_PORT = 10100;
|
|
25
31
|
const DEFAULT_SYSTEM_HOST = '127.0.0.1';
|
|
26
32
|
const DEFAULT_SHOW_SERVER_LOGS = false;
|
|
27
|
-
const RUNNING_PROCESS_IDS = [];
|
|
33
|
+
const RUNNING_PROCESS_IDS: (string | number)[] = [];
|
|
28
34
|
const RECENT_UPGRADE_TIMESTAMP_PATH = path.join('.appium', 'webdriveragent_mac', 'upgrade.time');
|
|
29
35
|
const RECENT_MODULE_VERSION_ITEM_NAME = 'recentWdaModuleVersion';
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!_.isEmpty(RUNNING_PROCESS_IDS)) {
|
|
34
|
-
log.debug(`Cleaning up ${RUNNING_PROCESS_IDS.length} obsolete ` +
|
|
35
|
-
util.pluralize('process', RUNNING_PROCESS_IDS.length, false));
|
|
36
|
-
try {
|
|
37
|
-
await exec('kill', ['-9', ...RUNNING_PROCESS_IDS]);
|
|
38
|
-
} catch {}
|
|
39
|
-
_.pullAll(RUNNING_PROCESS_IDS, RUNNING_PROCESS_IDS);
|
|
40
|
-
}
|
|
37
|
+
export interface SessionOptions {
|
|
38
|
+
reqBasePath?: string;
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
process.once('exit', () => {
|
|
44
|
-
if (!_.isEmpty(RUNNING_PROCESS_IDS)) {
|
|
45
|
-
try {
|
|
46
|
-
execSync(`kill -9 ${RUNNING_PROCESS_IDS.join(' ')}`);
|
|
47
|
-
} catch {}
|
|
48
|
-
_.pullAll(RUNNING_PROCESS_IDS, RUNNING_PROCESS_IDS);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
|
|
53
41
|
export class WDAMacProxy extends JWProxy {
|
|
54
|
-
|
|
55
|
-
didProcessExit;
|
|
42
|
+
public didProcessExit: boolean = false;
|
|
56
43
|
|
|
57
|
-
async proxyCommand
|
|
44
|
+
override async proxyCommand(
|
|
45
|
+
url: string,
|
|
46
|
+
method: HTTPMethod,
|
|
47
|
+
body: HTTPBody = null,
|
|
48
|
+
): Promise<[ProxyResponse, HTTPBody]> {
|
|
58
49
|
if (this.didProcessExit) {
|
|
59
50
|
throw new errors.InvalidContextError(
|
|
60
51
|
`'${method} ${url}' cannot be proxied to Mac2 Driver server because ` +
|
|
61
|
-
|
|
52
|
+
'its process is not running (probably crashed). Check the Appium log for more details',
|
|
53
|
+
);
|
|
62
54
|
}
|
|
63
55
|
return await super.proxyCommand(url, method, body);
|
|
64
56
|
}
|
|
65
57
|
}
|
|
66
58
|
|
|
67
59
|
class WDAMacProcess {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.proc = null;
|
|
74
|
-
}
|
|
60
|
+
public port: number = DEFAULT_SYSTEM_PORT;
|
|
61
|
+
public host: string = DEFAULT_SYSTEM_HOST;
|
|
62
|
+
public bootstrapRoot: string = DEFAULT_WDA_ROOT;
|
|
63
|
+
public proc: SubProcess | null = null;
|
|
64
|
+
private _showServerLogs: boolean = DEFAULT_SHOW_SERVER_LOGS;
|
|
75
65
|
|
|
76
|
-
get isRunning () {
|
|
66
|
+
get isRunning (): boolean {
|
|
77
67
|
return !!(this.proc?.isRunning);
|
|
78
68
|
}
|
|
79
69
|
|
|
80
|
-
get pid () {
|
|
70
|
+
get pid (): number | null {
|
|
81
71
|
return this.isRunning && this.proc ? this.proc.pid : null;
|
|
82
72
|
}
|
|
83
73
|
|
|
84
|
-
async listChildrenPids () {
|
|
74
|
+
async listChildrenPids (): Promise<string[]> {
|
|
85
75
|
return this.pid ? (await listChildrenProcessIds(this.pid)) : [];
|
|
86
76
|
}
|
|
87
77
|
|
|
88
|
-
async cleanupProjectIfFresh () {
|
|
89
|
-
const packageInfo = JSON.parse(
|
|
78
|
+
async cleanupProjectIfFresh (): Promise<void> {
|
|
79
|
+
const packageInfo = JSON.parse(
|
|
80
|
+
await fs.readFile(path.join(getModuleRoot(), 'package.json'), 'utf8')
|
|
81
|
+
) as { name: string; version: string };
|
|
90
82
|
const box = strongbox(packageInfo.name);
|
|
91
83
|
let boxItem = box.getItem(RECENT_MODULE_VERSION_ITEM_NAME);
|
|
92
84
|
if (!boxItem) {
|
|
@@ -97,7 +89,7 @@ class WDAMacProcess {
|
|
|
97
89
|
// TODO: to switch from a hardcoded file path to the strongbox usage.
|
|
98
90
|
try {
|
|
99
91
|
boxItem = await box.createItemWithValue(RECENT_MODULE_VERSION_ITEM_NAME, '1.5.4');
|
|
100
|
-
} catch (e) {
|
|
92
|
+
} catch (e: any) {
|
|
101
93
|
log.warn(`The actual module version cannot be persisted: ${e.message}`);
|
|
102
94
|
return;
|
|
103
95
|
}
|
|
@@ -105,7 +97,7 @@ class WDAMacProcess {
|
|
|
105
97
|
log.info('There is no need to perform the project cleanup. A fresh install has been detected');
|
|
106
98
|
try {
|
|
107
99
|
await box.createItemWithValue(RECENT_MODULE_VERSION_ITEM_NAME, packageInfo.version);
|
|
108
|
-
} catch (e) {
|
|
100
|
+
} catch (e: any) {
|
|
109
101
|
log.warn(`The actual module version cannot be persisted: ${e.message}`);
|
|
110
102
|
}
|
|
111
103
|
return;
|
|
@@ -115,7 +107,7 @@ class WDAMacProcess {
|
|
|
115
107
|
let recentModuleVersion = await boxItem.read();
|
|
116
108
|
try {
|
|
117
109
|
recentModuleVersion = util.coerceVersion(recentModuleVersion, true);
|
|
118
|
-
} catch (e) {
|
|
110
|
+
} catch (e: any) {
|
|
119
111
|
log.warn(`The persisted module version string has been damaged: ${e.message}`);
|
|
120
112
|
log.info(`Updating it to '${packageInfo.version}' assuming the project cleanup is not needed`);
|
|
121
113
|
await boxItem.write(packageInfo.version);
|
|
@@ -139,39 +131,17 @@ class WDAMacProcess {
|
|
|
139
131
|
];
|
|
140
132
|
await exec(XCODEBUILD, args, {cwd: this.bootstrapRoot});
|
|
141
133
|
await boxItem.write(packageInfo.version);
|
|
142
|
-
} catch (e) {
|
|
134
|
+
} catch (e: any) {
|
|
143
135
|
log.warn(`Cannot perform project cleanup. Original error: ${e.stderr || e.message}`);
|
|
144
136
|
}
|
|
145
137
|
}
|
|
146
138
|
|
|
147
|
-
|
|
148
|
-
if (_.isBoolean(showServerLogs) && this.showServerLogs !== showServerLogs
|
|
149
|
-
|| _.isNil(showServerLogs) && this.showServerLogs !== DEFAULT_SHOW_SERVER_LOGS) {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
if (systemPort && this.port !== systemPort
|
|
153
|
-
|| !systemPort && this.port !== DEFAULT_SYSTEM_PORT) {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
if (systemHost && this.host !== systemHost
|
|
157
|
-
|| !systemHost && this.host !== DEFAULT_SYSTEM_HOST) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
if (bootstrapRoot && this.bootstrapRoot !== bootstrapRoot
|
|
161
|
-
|| !bootstrapRoot && this.bootstrapRoot !== DEFAULT_WDA_ROOT) {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async init (opts = {}) {
|
|
169
|
-
// @ts-ignore TODO: Make opts typed
|
|
139
|
+
async init (opts: WDAMacProcessInitOptions = {}): Promise<boolean> {
|
|
170
140
|
if (this.isRunning && this.hasSameOpts(opts)) {
|
|
171
141
|
return false;
|
|
172
142
|
}
|
|
173
143
|
|
|
174
|
-
this.
|
|
144
|
+
this._showServerLogs = opts.showServerLogs ?? this._showServerLogs;
|
|
175
145
|
this.port = opts.systemPort ?? this.port;
|
|
176
146
|
this.host = opts.systemHost ?? this.host;
|
|
177
147
|
this.bootstrapRoot = opts.bootstrapRoot ?? this.bootstrapRoot;
|
|
@@ -185,7 +155,7 @@ class WDAMacProcess {
|
|
|
185
155
|
await this.kill();
|
|
186
156
|
await cleanupObsoleteProcesses();
|
|
187
157
|
|
|
188
|
-
let xcodebuild;
|
|
158
|
+
let xcodebuild: string;
|
|
189
159
|
try {
|
|
190
160
|
xcodebuild = await fs.which(XCODEBUILD);
|
|
191
161
|
} catch {
|
|
@@ -198,7 +168,7 @@ class WDAMacProcess {
|
|
|
198
168
|
|
|
199
169
|
log.debug(`Using ${this.host} as server host`);
|
|
200
170
|
log.debug(`Using port ${this.port}`);
|
|
201
|
-
const isPortBusy = async () => (await checkPortStatus(this.port, this.host)) === 'open';
|
|
171
|
+
const isPortBusy = async (): Promise<boolean> => (await checkPortStatus(this.port, this.host)) === 'open';
|
|
202
172
|
if (await isPortBusy()) {
|
|
203
173
|
log.warn(`The port #${this.port} at ${this.host} is busy. ` +
|
|
204
174
|
`Assuming it is an obsolete WDA server instance and ` +
|
|
@@ -214,7 +184,7 @@ class WDAMacProcess {
|
|
|
214
184
|
waitMs: 3000,
|
|
215
185
|
intervalMs: 100,
|
|
216
186
|
});
|
|
217
|
-
} catch (e) {
|
|
187
|
+
} catch (e: any) {
|
|
218
188
|
log.warn(`Did not know how to terminate the process at ${this.host}:${this.port}: ${e.message}. ` +
|
|
219
189
|
`Perhaps, it is not a WDA server, which is hogging the port?`);
|
|
220
190
|
throw new Error(`The port #${this.port} at ${this.host} is busy. ` +
|
|
@@ -239,13 +209,13 @@ class WDAMacProcess {
|
|
|
239
209
|
cwd: this.bootstrapRoot,
|
|
240
210
|
env,
|
|
241
211
|
});
|
|
242
|
-
if (!this.
|
|
212
|
+
if (!this._showServerLogs) {
|
|
243
213
|
log.info(`Mac2Driver host process logging is disabled. ` +
|
|
244
214
|
`All the ${XCODEBUILD} output is going to be suppressed. ` +
|
|
245
215
|
`Set the 'showServerLogs' capability to 'true' if this is an undesired behavior`);
|
|
246
216
|
}
|
|
247
217
|
this.proc.on('output', (stdout, stderr) => {
|
|
248
|
-
if (!this.
|
|
218
|
+
if (!this._showServerLogs) {
|
|
249
219
|
return;
|
|
250
220
|
}
|
|
251
221
|
|
|
@@ -262,7 +232,7 @@ class WDAMacProcess {
|
|
|
262
232
|
return true;
|
|
263
233
|
}
|
|
264
234
|
|
|
265
|
-
async stop () {
|
|
235
|
+
async stop (): Promise<void> {
|
|
266
236
|
if (!this.isRunning) {
|
|
267
237
|
return;
|
|
268
238
|
}
|
|
@@ -276,7 +246,7 @@ class WDAMacProcess {
|
|
|
276
246
|
await this.proc?.stop('SIGTERM', 3000);
|
|
277
247
|
}
|
|
278
248
|
|
|
279
|
-
async kill () {
|
|
249
|
+
async kill (): Promise<void> {
|
|
280
250
|
if (!this.isRunning) {
|
|
281
251
|
return;
|
|
282
252
|
}
|
|
@@ -291,115 +261,66 @@ class WDAMacProcess {
|
|
|
291
261
|
await this.proc?.stop('SIGKILL');
|
|
292
262
|
} catch {}
|
|
293
263
|
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export class WDAMacServer {
|
|
297
|
-
/** @type {WDAMacProxy} */
|
|
298
|
-
proxy;
|
|
299
|
-
|
|
300
|
-
constructor () {
|
|
301
|
-
this.process = null;
|
|
302
|
-
this.serverStartupTimeoutMs = STARTUP_TIMEOUT_MS;
|
|
303
|
-
// @ts-ignore this is ok
|
|
304
|
-
this.proxy = null;
|
|
305
264
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
async isProxyReady (throwOnExit = true) {
|
|
311
|
-
if (!this.proxy) {
|
|
265
|
+
private hasSameOpts (opts: WDAMacProcessInitOptions): boolean {
|
|
266
|
+
const { showServerLogs, systemPort, systemHost, bootstrapRoot } = opts;
|
|
267
|
+
if (_.isBoolean(showServerLogs) && this._showServerLogs !== showServerLogs
|
|
268
|
+
|| _.isNil(showServerLogs) && this._showServerLogs !== DEFAULT_SHOW_SERVER_LOGS) {
|
|
312
269
|
return false;
|
|
313
270
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
await this.proxy.command('/status', 'GET');
|
|
317
|
-
return true;
|
|
318
|
-
} catch (err) {
|
|
319
|
-
if (throwOnExit && this.proxy.didProcessExit) {
|
|
320
|
-
throw new Error(err.message);
|
|
321
|
-
}
|
|
271
|
+
if (systemPort && this.port !== systemPort
|
|
272
|
+
|| !systemPort && this.port !== DEFAULT_SYSTEM_PORT) {
|
|
322
273
|
return false;
|
|
323
274
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* @typedef {Object} ProxyProperties
|
|
329
|
-
*
|
|
330
|
-
* @property {string} scheme - The scheme proxy to.
|
|
331
|
-
* @property {string} host - The host name proxy to.
|
|
332
|
-
* @property {number} port - The port number proxy to.
|
|
333
|
-
* @property {string} path - The path proxy to.
|
|
334
|
-
*/
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Returns proxy information where WDAMacServer proxy to.
|
|
338
|
-
*
|
|
339
|
-
* @param {Object} caps - The capabilities in the session.
|
|
340
|
-
* @return {ProxyProperties}
|
|
341
|
-
* @throws Error if 'webDriverAgentMacUrl' had invalid URL
|
|
342
|
-
*/
|
|
343
|
-
parseProxyProperties (caps) {
|
|
344
|
-
let scheme = 'http';
|
|
345
|
-
if (!caps.webDriverAgentMacUrl) {
|
|
346
|
-
return {
|
|
347
|
-
scheme,
|
|
348
|
-
host: (this.process?.host ?? caps.systemHost) ?? DEFAULT_SYSTEM_HOST,
|
|
349
|
-
port: (this.process?.port ?? caps.systemPort) ?? DEFAULT_SYSTEM_PORT,
|
|
350
|
-
path: ''
|
|
351
|
-
};
|
|
275
|
+
if (systemHost && this.host !== systemHost
|
|
276
|
+
|| !systemHost && this.host !== DEFAULT_SYSTEM_HOST) {
|
|
277
|
+
return false;
|
|
352
278
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
parsedUrl = new url.URL(caps.webDriverAgentMacUrl);
|
|
357
|
-
} catch (e) {
|
|
358
|
-
throw new Error(`webDriverAgentMacUrl, '${caps.webDriverAgentMacUrl}', ` +
|
|
359
|
-
`in the capabilities is invalid. ${e.message}`);
|
|
279
|
+
if (bootstrapRoot && this.bootstrapRoot !== bootstrapRoot
|
|
280
|
+
|| !bootstrapRoot && this.bootstrapRoot !== DEFAULT_WDA_ROOT) {
|
|
281
|
+
return false;
|
|
360
282
|
}
|
|
361
283
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export class WDAMacServer {
|
|
289
|
+
private _proxy: WDAMacProxy | null = null;
|
|
290
|
+
private _process: WDAMacProcess | null = null;
|
|
291
|
+
private _serverStartupTimeoutMs: number = STARTUP_TIMEOUT_MS;
|
|
292
|
+
private _isProxyingToRemoteServer: boolean = false;
|
|
293
|
+
|
|
294
|
+
get proxy (): WDAMacProxy {
|
|
295
|
+
if (!this._proxy) {
|
|
296
|
+
throw new Error('Proxy is not initialized. Did you call startSession()?');
|
|
365
297
|
}
|
|
366
|
-
return
|
|
367
|
-
scheme,
|
|
368
|
-
host: hostname ?? DEFAULT_SYSTEM_HOST,
|
|
369
|
-
port: _.isEmpty(port) ? DEFAULT_SYSTEM_PORT : _.parseInt(port),
|
|
370
|
-
path: pathname === '/' ? '' : pathname
|
|
371
|
-
};
|
|
298
|
+
return this._proxy;
|
|
372
299
|
}
|
|
373
300
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
* @param {import('@appium/types').StringRecord} caps
|
|
377
|
-
* @param {SessionOptions} [opts={}]
|
|
378
|
-
*/
|
|
379
|
-
async startSession (caps, opts = {}) {
|
|
380
|
-
this.serverStartupTimeoutMs = caps.serverStartupTimeout ?? this.serverStartupTimeoutMs;
|
|
301
|
+
async startSession (caps: StartSessionCapabilities, opts: SessionOptions = {}): Promise<void> {
|
|
302
|
+
this._serverStartupTimeoutMs = caps.serverStartupTimeout ?? this._serverStartupTimeoutMs;
|
|
381
303
|
|
|
382
|
-
this.
|
|
304
|
+
this._isProxyingToRemoteServer = !!caps.webDriverAgentMacUrl;
|
|
383
305
|
|
|
384
|
-
let wasProcessInitNecessary;
|
|
385
|
-
if (this.
|
|
386
|
-
if (this.
|
|
387
|
-
await this.
|
|
306
|
+
let wasProcessInitNecessary: boolean;
|
|
307
|
+
if (this._isProxyingToRemoteServer) {
|
|
308
|
+
if (this._process) {
|
|
309
|
+
await this._process.kill();
|
|
388
310
|
await cleanupObsoleteProcesses();
|
|
389
|
-
this.
|
|
311
|
+
this._process = null;
|
|
390
312
|
}
|
|
391
|
-
|
|
392
313
|
wasProcessInitNecessary = false;
|
|
393
314
|
} else {
|
|
394
|
-
if (!this.
|
|
395
|
-
this.
|
|
315
|
+
if (!this._process) {
|
|
316
|
+
this._process = new WDAMacProcess();
|
|
396
317
|
}
|
|
397
|
-
wasProcessInitNecessary = await this.
|
|
318
|
+
wasProcessInitNecessary = await this._process.init(caps);
|
|
398
319
|
}
|
|
399
320
|
|
|
400
|
-
if (wasProcessInitNecessary || this.
|
|
321
|
+
if (wasProcessInitNecessary || this._isProxyingToRemoteServer || !this._proxy) {
|
|
401
322
|
const {scheme, host, port, path} = this.parseProxyProperties(caps);
|
|
402
|
-
const proxyOpts = {
|
|
323
|
+
const proxyOpts: ProxyOptions = {
|
|
403
324
|
scheme,
|
|
404
325
|
server: host,
|
|
405
326
|
port,
|
|
@@ -409,13 +330,13 @@ export class WDAMacServer {
|
|
|
409
330
|
if (caps.reqBasePath) {
|
|
410
331
|
proxyOpts.reqBasePath = opts.reqBasePath;
|
|
411
332
|
}
|
|
412
|
-
this.
|
|
413
|
-
this.
|
|
333
|
+
this._proxy = new WDAMacProxy(proxyOpts);
|
|
334
|
+
this._proxy.didProcessExit = false;
|
|
414
335
|
|
|
415
|
-
if (this.
|
|
416
|
-
this.
|
|
417
|
-
if (this.
|
|
418
|
-
this.
|
|
336
|
+
if (this._process?.proc) {
|
|
337
|
+
this._process.proc.on('exit', () => {
|
|
338
|
+
if (this._proxy) {
|
|
339
|
+
this._proxy.didProcessExit = true;
|
|
419
340
|
}
|
|
420
341
|
});
|
|
421
342
|
}
|
|
@@ -423,19 +344,19 @@ export class WDAMacServer {
|
|
|
423
344
|
const timer = new timing.Timer().start();
|
|
424
345
|
try {
|
|
425
346
|
await waitForCondition(async () => await this.isProxyReady(), {
|
|
426
|
-
waitMs: this.
|
|
347
|
+
waitMs: this._serverStartupTimeoutMs,
|
|
427
348
|
intervalMs: 1000,
|
|
428
349
|
});
|
|
429
|
-
} catch (e) {
|
|
430
|
-
if (this.
|
|
350
|
+
} catch (e: any) {
|
|
351
|
+
if (this._process?.isRunning) {
|
|
431
352
|
// avoid "frozen" processes,
|
|
432
|
-
await this.
|
|
353
|
+
await this._process.kill();
|
|
433
354
|
}
|
|
434
355
|
if (/Condition unmet/.test(e.message)) {
|
|
435
|
-
const msg = this.
|
|
436
|
-
? `No response from '${scheme}://${host}:${port}${path}' within ${this.
|
|
356
|
+
const msg = this._isProxyingToRemoteServer
|
|
357
|
+
? `No response from '${scheme}://${host}:${port}${path}' within ${this._serverStartupTimeoutMs}ms timeout.` +
|
|
437
358
|
`Please make sure the remote server is running and accessible by Appium`
|
|
438
|
-
: `Mac2Driver server is not listening within ${this.
|
|
359
|
+
: `Mac2Driver server is not listening within ${this._serverStartupTimeoutMs}ms timeout. ` +
|
|
439
360
|
`Try to increase the value of 'serverStartupTimeout' capability, check the server logs ` +
|
|
440
361
|
`and make sure the ${XCODEBUILD} host process could be started manually from a terminal`;
|
|
441
362
|
throw new Error(msg);
|
|
@@ -443,18 +364,20 @@ export class WDAMacServer {
|
|
|
443
364
|
throw e;
|
|
444
365
|
}
|
|
445
366
|
|
|
446
|
-
if (this.
|
|
447
|
-
const pid = this.
|
|
448
|
-
const childrenPids = await this.
|
|
449
|
-
|
|
450
|
-
|
|
367
|
+
if (this._process) {
|
|
368
|
+
const pid = this._process.pid;
|
|
369
|
+
const childrenPids = await this._process.listChildrenPids();
|
|
370
|
+
if (pid !== null) {
|
|
371
|
+
RUNNING_PROCESS_IDS.push(...childrenPids, pid);
|
|
372
|
+
this._process.proc?.on('exit', () => void _.pull(RUNNING_PROCESS_IDS, pid));
|
|
373
|
+
}
|
|
451
374
|
log.info(`The host process is ready within ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
452
375
|
}
|
|
453
376
|
} else {
|
|
454
377
|
log.info('The host process has already been listening. Proceeding with session creation');
|
|
455
378
|
}
|
|
456
379
|
|
|
457
|
-
await this.
|
|
380
|
+
await this._proxy.command('/session', 'POST', {
|
|
458
381
|
capabilities: {
|
|
459
382
|
firstMatch: [{}],
|
|
460
383
|
alwaysMatch: caps,
|
|
@@ -462,27 +385,119 @@ export class WDAMacServer {
|
|
|
462
385
|
});
|
|
463
386
|
}
|
|
464
387
|
|
|
465
|
-
async stopSession () {
|
|
466
|
-
if (!this.
|
|
388
|
+
async stopSession (): Promise<void> {
|
|
389
|
+
if (!this._isProxyingToRemoteServer && !(this._process?.isRunning)) {
|
|
467
390
|
log.info(`Mac2Driver session cannot be stopped, because the server is not running`);
|
|
468
391
|
return;
|
|
469
392
|
}
|
|
470
393
|
|
|
471
|
-
if (this.
|
|
394
|
+
if (this._proxy?.sessionId) {
|
|
472
395
|
try {
|
|
473
|
-
await this.
|
|
474
|
-
} catch (e) {
|
|
396
|
+
await this._proxy.command(`/session/${this._proxy.sessionId}`, 'DELETE');
|
|
397
|
+
} catch (e: any) {
|
|
475
398
|
log.info(`Mac2Driver session cannot be deleted. Original error: ${e.message}`);
|
|
476
399
|
}
|
|
477
400
|
}
|
|
478
401
|
}
|
|
402
|
+
|
|
403
|
+
private async isProxyReady (throwOnExit = true): Promise<boolean> {
|
|
404
|
+
if (!this._proxy) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
await this._proxy.command('/status', 'GET');
|
|
410
|
+
return true;
|
|
411
|
+
} catch (err: any) {
|
|
412
|
+
if (throwOnExit && this._proxy.didProcessExit) {
|
|
413
|
+
throw new Error(err.message);
|
|
414
|
+
}
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Returns proxy information where WDAMacServer proxy to.
|
|
421
|
+
*
|
|
422
|
+
* @param caps - The capabilities in the session.
|
|
423
|
+
* @return ProxyProperties
|
|
424
|
+
* @throws Error if 'webDriverAgentMacUrl' had invalid URL
|
|
425
|
+
*/
|
|
426
|
+
private parseProxyProperties (caps: StartSessionCapabilities): ProxyProperties {
|
|
427
|
+
let scheme = 'http';
|
|
428
|
+
if (!caps.webDriverAgentMacUrl) {
|
|
429
|
+
return {
|
|
430
|
+
scheme,
|
|
431
|
+
host: (this._process?.host ?? caps.systemHost) ?? DEFAULT_SYSTEM_HOST,
|
|
432
|
+
port: (this._process?.port ?? caps.systemPort) ?? DEFAULT_SYSTEM_PORT,
|
|
433
|
+
path: ''
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let parsedUrl: url.URL;
|
|
438
|
+
try {
|
|
439
|
+
parsedUrl = new url.URL(caps.webDriverAgentMacUrl);
|
|
440
|
+
} catch (e: any) {
|
|
441
|
+
throw new Error(`webDriverAgentMacUrl, '${caps.webDriverAgentMacUrl}', ` +
|
|
442
|
+
`in the capabilities is invalid. ${e.message}`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const { protocol, hostname, port, pathname } = parsedUrl;
|
|
446
|
+
if (_.isString(protocol)) {
|
|
447
|
+
scheme = protocol.split(':')[0];
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
scheme,
|
|
451
|
+
host: hostname ?? DEFAULT_SYSTEM_HOST,
|
|
452
|
+
port: _.isEmpty(port) ? DEFAULT_SYSTEM_PORT : _.parseInt(port),
|
|
453
|
+
path: pathname === '/' ? '' : pathname
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export const WDA_MAC_SERVER = new WDAMacServer();
|
|
459
|
+
|
|
460
|
+
// Private functions
|
|
461
|
+
async function cleanupObsoleteProcesses (): Promise<void> {
|
|
462
|
+
if (!_.isEmpty(RUNNING_PROCESS_IDS)) {
|
|
463
|
+
log.debug(`Cleaning up ${RUNNING_PROCESS_IDS.length} obsolete ` +
|
|
464
|
+
util.pluralize('process', RUNNING_PROCESS_IDS.length, false));
|
|
465
|
+
try {
|
|
466
|
+
await exec('kill', ['-9', ...RUNNING_PROCESS_IDS.map(String)]);
|
|
467
|
+
} catch {}
|
|
468
|
+
_.pullAll(RUNNING_PROCESS_IDS, RUNNING_PROCESS_IDS);
|
|
469
|
+
}
|
|
479
470
|
}
|
|
480
471
|
|
|
481
|
-
|
|
472
|
+
process.once('exit', () => {
|
|
473
|
+
if (!_.isEmpty(RUNNING_PROCESS_IDS)) {
|
|
474
|
+
try {
|
|
475
|
+
execSync(`kill -9 ${RUNNING_PROCESS_IDS.map(String).join(' ')}`);
|
|
476
|
+
} catch {}
|
|
477
|
+
_.pullAll(RUNNING_PROCESS_IDS, RUNNING_PROCESS_IDS);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Private type definitions
|
|
482
|
+
interface WDAMacProcessInitOptions {
|
|
483
|
+
showServerLogs?: boolean;
|
|
484
|
+
systemPort?: number;
|
|
485
|
+
systemHost?: string;
|
|
486
|
+
bootstrapRoot?: string;
|
|
487
|
+
}
|
|
482
488
|
|
|
483
|
-
|
|
489
|
+
interface ProxyProperties {
|
|
490
|
+
scheme: string;
|
|
491
|
+
host: string;
|
|
492
|
+
port: number;
|
|
493
|
+
path: string;
|
|
494
|
+
}
|
|
484
495
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
496
|
+
interface StartSessionCapabilities {
|
|
497
|
+
webDriverAgentMacUrl?: string;
|
|
498
|
+
systemHost?: string;
|
|
499
|
+
systemPort?: number;
|
|
500
|
+
serverStartupTimeout?: number;
|
|
501
|
+
reqBasePath?: string;
|
|
502
|
+
[key: string]: unknown;
|
|
503
|
+
}
|