appium-webdriveragent 11.1.4 → 11.1.6
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 +12 -0
- package/WebDriverAgent.xcodeproj/project.pbxproj +14 -5
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationApp.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_1.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_2.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_3.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentLib.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentLib_tvOS.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner-nodebug.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme +1 -1
- package/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner_tvOS.xcscheme +1 -1
- package/WebDriverAgentLib/Info.plist +2 -2
- package/build/lib/check-dependencies.d.ts.map +1 -1
- package/build/lib/check-dependencies.js +6 -3
- package/build/lib/check-dependencies.js.map +1 -1
- package/build/lib/logger.js.map +1 -1
- package/build/lib/no-session-proxy.d.ts.map +1 -1
- package/build/lib/no-session-proxy.js +1 -1
- package/build/lib/no-session-proxy.js.map +1 -1
- package/build/lib/types.d.ts.map +1 -1
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +8 -9
- package/build/lib/utils.js.map +1 -1
- package/build/lib/webdriveragent.d.ts.map +1 -1
- package/build/lib/webdriveragent.js +21 -20
- package/build/lib/webdriveragent.js.map +1 -1
- package/build/lib/xcodebuild.d.ts.map +1 -1
- package/build/lib/xcodebuild.js +16 -19
- package/build/lib/xcodebuild.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/check-dependencies.ts +20 -14
- package/lib/constants.ts +0 -1
- package/lib/logger.ts +1 -1
- package/lib/no-session-proxy.ts +5 -7
- package/lib/types.ts +6 -1
- package/lib/utils.ts +80 -52
- package/lib/webdriveragent.ts +189 -134
- package/lib/xcodebuild.ts +62 -54
- package/package.json +3 -2
package/lib/webdriveragent.ts
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {waitForCondition} from 'asyncbox';
|
|
2
2
|
import _ from 'lodash';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import url from 'node:url';
|
|
5
5
|
import B from 'bluebird';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import type {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
6
|
+
import {JWProxy} from '@appium/base-driver';
|
|
7
|
+
import {fs, util, plist} from '@appium/support';
|
|
8
|
+
import type {AppiumLogger, StringRecord} from '@appium/types';
|
|
9
|
+
import {log as defaultLogger} from './logger';
|
|
10
|
+
import {NoSessionProxy} from './no-session-proxy';
|
|
11
11
|
import {
|
|
12
|
-
getWDAUpgradeTimestamp,
|
|
12
|
+
getWDAUpgradeTimestamp,
|
|
13
|
+
resetTestProcesses,
|
|
14
|
+
getPIDsListeningOnPort,
|
|
15
|
+
BOOTSTRAP_PATH,
|
|
13
16
|
} from './utils';
|
|
14
17
|
import {XcodeBuild} from './xcodebuild';
|
|
15
18
|
import AsyncLock from 'async-lock';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
19
|
+
import {exec} from 'teen_process';
|
|
20
|
+
import {bundleWDASim} from './check-dependencies';
|
|
18
21
|
import {
|
|
19
|
-
WDA_RUNNER_BUNDLE_ID,
|
|
20
|
-
|
|
22
|
+
WDA_RUNNER_BUNDLE_ID,
|
|
23
|
+
WDA_RUNNER_APP,
|
|
24
|
+
WDA_BASE_URL,
|
|
25
|
+
WDA_UPGRADE_TIMESTAMP_PATH,
|
|
26
|
+
DEFAULT_TEST_BUNDLE_SUFFIX,
|
|
21
27
|
} from './constants';
|
|
22
28
|
import {Xctest} from 'appium-ios-device';
|
|
23
29
|
import {strongbox} from '@appium/strongbox';
|
|
24
|
-
import type {
|
|
30
|
+
import type {WebDriverAgentArgs, AppleDevice} from './types';
|
|
25
31
|
|
|
26
32
|
const WDA_LAUNCH_TIMEOUT = 60 * 1000;
|
|
27
33
|
const WDA_AGENT_PORT = 8100;
|
|
@@ -69,7 +75,7 @@ export class WebDriverAgent {
|
|
|
69
75
|
* @param args - Configuration arguments for WebDriverAgent
|
|
70
76
|
* @param log - Optional logger instance
|
|
71
77
|
*/
|
|
72
|
-
constructor
|
|
78
|
+
constructor(args: WebDriverAgentArgs, log: AppiumLogger | null = null) {
|
|
73
79
|
this.args = _.clone(args);
|
|
74
80
|
this.log = log ?? defaultLogger;
|
|
75
81
|
|
|
@@ -84,8 +90,8 @@ export class WebDriverAgent {
|
|
|
84
90
|
this.setWDAPaths(args.bootstrapPath, args.agentPath);
|
|
85
91
|
|
|
86
92
|
this.wdaLocalPort = args.wdaLocalPort;
|
|
87
|
-
this.wdaRemotePort =
|
|
88
|
-
|| WDA_AGENT_PORT;
|
|
93
|
+
this.wdaRemotePort =
|
|
94
|
+
((this.isRealDevice ? args.wdaRemotePort : null) ?? args.wdaLocalPort) || WDA_AGENT_PORT;
|
|
89
95
|
this.wdaBaseUrl = args.wdaBaseUrl || WDA_BASE_URL;
|
|
90
96
|
this.wdaBindingIP = args.wdaBindingIP;
|
|
91
97
|
this.prebuildWDA = args.prebuildWDA;
|
|
@@ -112,40 +118,44 @@ export class WebDriverAgent {
|
|
|
112
118
|
this.updatedWDABundleIdSuffix = args.updatedWDABundleIdSuffix ?? DEFAULT_TEST_BUNDLE_SUFFIX;
|
|
113
119
|
|
|
114
120
|
this._xcodebuild = this.canSkipXcodebuild
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
121
|
+
? null
|
|
122
|
+
: new XcodeBuild(
|
|
123
|
+
this.device,
|
|
124
|
+
{
|
|
125
|
+
platformVersion: this.platformVersion,
|
|
126
|
+
platformName: this.platformName,
|
|
127
|
+
iosSdkVersion: this.iosSdkVersion,
|
|
128
|
+
agentPath: this.agentPath,
|
|
129
|
+
bootstrapPath: this.bootstrapPath,
|
|
130
|
+
realDevice: this.isRealDevice,
|
|
131
|
+
showXcodeLog: args.showXcodeLog,
|
|
132
|
+
xcodeConfigFile: args.xcodeConfigFile,
|
|
133
|
+
xcodeOrgId: args.xcodeOrgId,
|
|
134
|
+
xcodeSigningId: args.xcodeSigningId,
|
|
135
|
+
keychainPath: args.keychainPath,
|
|
136
|
+
keychainPassword: args.keychainPassword,
|
|
137
|
+
useSimpleBuildTest: args.useSimpleBuildTest,
|
|
138
|
+
usePrebuiltWDA: args.usePrebuiltWDA,
|
|
139
|
+
updatedWDABundleId: this.updatedWDABundleId,
|
|
140
|
+
launchTimeout: this.wdaLaunchTimeout,
|
|
141
|
+
wdaRemotePort: this.wdaRemotePort,
|
|
142
|
+
wdaBindingIP: this.wdaBindingIP,
|
|
143
|
+
useXctestrunFile: this.useXctestrunFile,
|
|
144
|
+
derivedDataPath: args.derivedDataPath,
|
|
145
|
+
mjpegServerPort: this.mjpegServerPort,
|
|
146
|
+
allowProvisioningDeviceRegistration: args.allowProvisioningDeviceRegistration,
|
|
147
|
+
resultBundlePath: args.resultBundlePath,
|
|
148
|
+
resultBundleVersion: args.resultBundleVersion,
|
|
149
|
+
},
|
|
150
|
+
this.log,
|
|
151
|
+
);
|
|
142
152
|
}
|
|
143
153
|
|
|
144
154
|
/**
|
|
145
155
|
* Return true if the session does not need xcodebuild.
|
|
146
156
|
* @returns Whether the session needs/has xcodebuild.
|
|
147
157
|
*/
|
|
148
|
-
get canSkipXcodebuild
|
|
158
|
+
get canSkipXcodebuild(): boolean {
|
|
149
159
|
// Use this.args.webDriverAgentUrl to guarantee
|
|
150
160
|
// the capabilities set gave the `appium:webDriverAgentUrl`.
|
|
151
161
|
return this.usePreinstalledWDA || !!this.args.webDriverAgentUrl;
|
|
@@ -156,7 +166,7 @@ export class WebDriverAgent {
|
|
|
156
166
|
* @returns The XcodeBuild instance
|
|
157
167
|
* @throws Error if xcodebuild is not available
|
|
158
168
|
*/
|
|
159
|
-
get xcodebuild
|
|
169
|
+
get xcodebuild(): XcodeBuild {
|
|
160
170
|
if (!this._xcodebuild) {
|
|
161
171
|
throw new Error('xcodebuild is not available');
|
|
162
172
|
}
|
|
@@ -171,7 +181,7 @@ export class WebDriverAgent {
|
|
|
171
181
|
*
|
|
172
182
|
* @returns Bundle ID for Xctest.
|
|
173
183
|
*/
|
|
174
|
-
get bundleIdForXctest
|
|
184
|
+
get bundleIdForXctest(): string {
|
|
175
185
|
return `${this.updatedWDABundleId ? this.updatedWDABundleId : WDA_RUNNER_BUNDLE_ID}${this.updatedWDABundleIdSuffix}`;
|
|
176
186
|
}
|
|
177
187
|
|
|
@@ -179,24 +189,33 @@ export class WebDriverAgent {
|
|
|
179
189
|
* Cleans up obsolete cached processes from previous WDA sessions
|
|
180
190
|
* that are listening on the same port but belong to different devices.
|
|
181
191
|
*/
|
|
182
|
-
async cleanupObsoleteProcesses
|
|
183
|
-
const obsoletePids = await getPIDsListeningOnPort(
|
|
184
|
-
|
|
185
|
-
|
|
192
|
+
async cleanupObsoleteProcesses(): Promise<void> {
|
|
193
|
+
const obsoletePids = await getPIDsListeningOnPort(
|
|
194
|
+
this.url.port as string,
|
|
195
|
+
(cmdLine) =>
|
|
196
|
+
cmdLine.includes('/WebDriverAgentRunner') &&
|
|
197
|
+
!cmdLine.toLowerCase().includes(this.device.udid.toLowerCase()),
|
|
198
|
+
);
|
|
186
199
|
|
|
187
200
|
if (_.isEmpty(obsoletePids)) {
|
|
188
|
-
this.log.debug(
|
|
189
|
-
`
|
|
201
|
+
this.log.debug(
|
|
202
|
+
`No obsolete cached processes from previous WDA sessions ` +
|
|
203
|
+
`listening on port ${this.url.port} have been found`,
|
|
204
|
+
);
|
|
190
205
|
return;
|
|
191
206
|
}
|
|
192
207
|
|
|
193
|
-
this.log.info(
|
|
194
|
-
`
|
|
208
|
+
this.log.info(
|
|
209
|
+
`Detected ${obsoletePids.length} obsolete cached process${obsoletePids.length === 1 ? '' : 'es'} ` +
|
|
210
|
+
`from previous WDA sessions. Cleaning them up`,
|
|
211
|
+
);
|
|
195
212
|
try {
|
|
196
213
|
await exec('kill', obsoletePids);
|
|
197
214
|
} catch (e: any) {
|
|
198
|
-
this.log.warn(
|
|
199
|
-
`
|
|
215
|
+
this.log.warn(
|
|
216
|
+
`Failed to kill obsolete cached process${obsoletePids.length === 1 ? '' : 'es'} '${obsoletePids}'. ` +
|
|
217
|
+
`Original error: ${e.message}`,
|
|
218
|
+
);
|
|
200
219
|
}
|
|
201
220
|
}
|
|
202
221
|
|
|
@@ -204,7 +223,7 @@ export class WebDriverAgent {
|
|
|
204
223
|
* Gets the base path for the WebDriverAgent URL.
|
|
205
224
|
* @returns The base path (empty string if root path)
|
|
206
225
|
*/
|
|
207
|
-
get basePath
|
|
226
|
+
get basePath(): string {
|
|
208
227
|
if (this.url.path === '/') {
|
|
209
228
|
return '';
|
|
210
229
|
}
|
|
@@ -232,7 +251,7 @@ export class WebDriverAgent {
|
|
|
232
251
|
*
|
|
233
252
|
* @param sessionId Launch WDA and establish the session with this sessionId
|
|
234
253
|
*/
|
|
235
|
-
async launch
|
|
254
|
+
async launch(sessionId: string): Promise<StringRecord | null> {
|
|
236
255
|
if (this.webDriverAgentUrl) {
|
|
237
256
|
this.log.info(`Using provided WebdriverAgent at '${this.webDriverAgentUrl}'`);
|
|
238
257
|
this.url = this.webDriverAgentUrl;
|
|
@@ -248,9 +267,11 @@ export class WebDriverAgent {
|
|
|
248
267
|
|
|
249
268
|
this.setupProxies(sessionId);
|
|
250
269
|
|
|
251
|
-
if (!this.useXctestrunFile && !await fs.exists(this.agentPath)) {
|
|
252
|
-
throw new Error(
|
|
253
|
-
|
|
270
|
+
if (!this.useXctestrunFile && !(await fs.exists(this.agentPath))) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`Trying to use WebDriverAgent project at '${this.agentPath}' but the ` +
|
|
273
|
+
'file does not exist',
|
|
274
|
+
);
|
|
254
275
|
}
|
|
255
276
|
|
|
256
277
|
// useXctestrunFile and usePrebuiltWDA use existing dependencies
|
|
@@ -259,8 +280,10 @@ export class WebDriverAgent {
|
|
|
259
280
|
this.log.info('Skipped WDA project cleanup according to the provided capabilities');
|
|
260
281
|
} else {
|
|
261
282
|
const synchronizationKey = path.normalize(this.bootstrapPath);
|
|
262
|
-
await SHARED_RESOURCES_GUARD.acquire(
|
|
263
|
-
|
|
283
|
+
await SHARED_RESOURCES_GUARD.acquire(
|
|
284
|
+
synchronizationKey,
|
|
285
|
+
async () => await this._cleanupProjectIfFresh(),
|
|
286
|
+
);
|
|
264
287
|
}
|
|
265
288
|
|
|
266
289
|
// We need to provide WDA local port, because it might be occupied
|
|
@@ -275,7 +298,7 @@ export class WebDriverAgent {
|
|
|
275
298
|
if (this.prebuildWDA) {
|
|
276
299
|
await this.xcodebuild.prebuild();
|
|
277
300
|
}
|
|
278
|
-
return await this.xcodebuild.start() as StringRecord | null;
|
|
301
|
+
return (await this.xcodebuild.start()) as StringRecord | null;
|
|
279
302
|
}
|
|
280
303
|
|
|
281
304
|
/**
|
|
@@ -283,15 +306,14 @@ export class WebDriverAgent {
|
|
|
283
306
|
* that required resource files exist.
|
|
284
307
|
* @returns `true` if source is fresh (all required files exist), `false` otherwise
|
|
285
308
|
*/
|
|
286
|
-
async isSourceFresh
|
|
287
|
-
const existsPromises = [
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
].map((subPath) => fs.exists(path.resolve(this.bootstrapPath, subPath)));
|
|
309
|
+
async isSourceFresh(): Promise<boolean> {
|
|
310
|
+
const existsPromises = ['Resources', `Resources${path.sep}WebDriverAgent.bundle`].map(
|
|
311
|
+
(subPath) => fs.exists(path.resolve(this.bootstrapPath, subPath)),
|
|
312
|
+
);
|
|
291
313
|
return (await B.all(existsPromises)).some((v) => v === false);
|
|
292
314
|
}
|
|
293
315
|
|
|
294
|
-
private async parseBundleId
|
|
316
|
+
private async parseBundleId(wdaBundlePath: string): Promise<string> {
|
|
295
317
|
const infoPlistPath = path.join(wdaBundlePath, 'Info.plist');
|
|
296
318
|
const infoPlist = await plist.parsePlist(await fs.readFile(infoPlistPath));
|
|
297
319
|
if (!infoPlist.CFBundleIdentifier) {
|
|
@@ -300,7 +322,7 @@ export class WebDriverAgent {
|
|
|
300
322
|
return infoPlist.CFBundleIdentifier as string;
|
|
301
323
|
}
|
|
302
324
|
|
|
303
|
-
private async fetchWDABundle
|
|
325
|
+
private async fetchWDABundle(): Promise<string> {
|
|
304
326
|
if (!this.derivedDataPath) {
|
|
305
327
|
return await bundleWDASim(this.xcodebuild);
|
|
306
328
|
}
|
|
@@ -313,7 +335,7 @@ export class WebDriverAgent {
|
|
|
313
335
|
return wdaBundlePaths[0];
|
|
314
336
|
}
|
|
315
337
|
|
|
316
|
-
private setupProxies
|
|
338
|
+
private setupProxies(sessionId: string): void {
|
|
317
339
|
const proxyOpts: any = {
|
|
318
340
|
log: this.log,
|
|
319
341
|
server: this.url.hostname ?? undefined,
|
|
@@ -338,7 +360,7 @@ export class WebDriverAgent {
|
|
|
338
360
|
* Stops the WebDriverAgent session and cleans up resources.
|
|
339
361
|
* Handles both preinstalled WDA and xcodebuild-based sessions.
|
|
340
362
|
*/
|
|
341
|
-
async quit
|
|
363
|
+
async quit(): Promise<void> {
|
|
342
364
|
if (this.usePreinstalledWDA) {
|
|
343
365
|
this.log.info('Stopping the XCTest session');
|
|
344
366
|
if (this.xctestApiClient) {
|
|
@@ -357,8 +379,10 @@ export class WebDriverAgent {
|
|
|
357
379
|
await this.xcodebuild.quit();
|
|
358
380
|
}
|
|
359
381
|
} else {
|
|
360
|
-
this.log.debug(
|
|
361
|
-
'
|
|
382
|
+
this.log.debug(
|
|
383
|
+
'Do not stop xcodebuild nor XCTest session ' +
|
|
384
|
+
'since the WDA session is managed by outside this driver.',
|
|
385
|
+
);
|
|
362
386
|
}
|
|
363
387
|
|
|
364
388
|
if (this.jwproxy) {
|
|
@@ -380,7 +404,7 @@ export class WebDriverAgent {
|
|
|
380
404
|
* builds it from wdaBaseUrl, wdaBindingIP, and wdaLocalPort.
|
|
381
405
|
* @returns The parsed URL object
|
|
382
406
|
*/
|
|
383
|
-
get url
|
|
407
|
+
get url(): url.UrlWithStringQuery {
|
|
384
408
|
if (!this._url) {
|
|
385
409
|
if (this.webDriverAgentUrl) {
|
|
386
410
|
this._url = url.parse(this.webDriverAgentUrl);
|
|
@@ -397,7 +421,7 @@ export class WebDriverAgent {
|
|
|
397
421
|
* Sets the WebDriverAgent URL.
|
|
398
422
|
* @param _url - The URL string to parse and set
|
|
399
423
|
*/
|
|
400
|
-
set url
|
|
424
|
+
set url(_url: string) {
|
|
401
425
|
this._url = url.parse(_url);
|
|
402
426
|
}
|
|
403
427
|
|
|
@@ -405,7 +429,7 @@ export class WebDriverAgent {
|
|
|
405
429
|
* Gets whether WebDriverAgent has fully started.
|
|
406
430
|
* @returns `true` if WDA has started, `false` otherwise
|
|
407
431
|
*/
|
|
408
|
-
get fullyStarted
|
|
432
|
+
get fullyStarted(): boolean {
|
|
409
433
|
return this.started;
|
|
410
434
|
}
|
|
411
435
|
|
|
@@ -413,7 +437,7 @@ export class WebDriverAgent {
|
|
|
413
437
|
* Sets whether WebDriverAgent has fully started.
|
|
414
438
|
* @param started - `true` if WDA has started, `false` otherwise
|
|
415
439
|
*/
|
|
416
|
-
set fullyStarted
|
|
440
|
+
set fullyStarted(started: boolean) {
|
|
417
441
|
this.started = started ?? false;
|
|
418
442
|
}
|
|
419
443
|
|
|
@@ -421,7 +445,7 @@ export class WebDriverAgent {
|
|
|
421
445
|
* Retrieves the Xcode derived data path for WebDriverAgent.
|
|
422
446
|
* @returns The derived data path, or `undefined` if xcodebuild is skipped
|
|
423
447
|
*/
|
|
424
|
-
async retrieveDerivedDataPath
|
|
448
|
+
async retrieveDerivedDataPath(): Promise<string | undefined> {
|
|
425
449
|
if (this.canSkipXcodebuild) {
|
|
426
450
|
return;
|
|
427
451
|
}
|
|
@@ -433,53 +457,70 @@ export class WebDriverAgent {
|
|
|
433
457
|
* Or reuse it if it has the default id without updatedWDABundleId.
|
|
434
458
|
* Uninstall it if the method faces an exception for the above situation.
|
|
435
459
|
*/
|
|
436
|
-
async setupCaching
|
|
460
|
+
async setupCaching(): Promise<void> {
|
|
437
461
|
const status = await this.getStatus(0);
|
|
438
462
|
if (!status || !status.build) {
|
|
439
463
|
this.log.debug('WDA is currently not running. There is nothing to cache');
|
|
440
464
|
return;
|
|
441
465
|
}
|
|
442
466
|
|
|
443
|
-
const {
|
|
444
|
-
productBundleIdentifier,
|
|
445
|
-
upgradedAt,
|
|
446
|
-
} = status.build as any;
|
|
467
|
+
const {productBundleIdentifier, upgradedAt} = status.build as any;
|
|
447
468
|
// for real device
|
|
448
|
-
if (
|
|
449
|
-
|
|
469
|
+
if (
|
|
470
|
+
util.hasValue(productBundleIdentifier) &&
|
|
471
|
+
util.hasValue(this.updatedWDABundleId) &&
|
|
472
|
+
this.updatedWDABundleId !== productBundleIdentifier
|
|
473
|
+
) {
|
|
474
|
+
this.log.info(
|
|
475
|
+
`Will uninstall running WDA since it has different bundle id. The actual value is '${productBundleIdentifier}'.`,
|
|
476
|
+
);
|
|
450
477
|
return await this.uninstall();
|
|
451
478
|
}
|
|
452
479
|
// for simulator
|
|
453
|
-
if (
|
|
454
|
-
|
|
480
|
+
if (
|
|
481
|
+
util.hasValue(productBundleIdentifier) &&
|
|
482
|
+
!util.hasValue(this.updatedWDABundleId) &&
|
|
483
|
+
WDA_RUNNER_BUNDLE_ID !== productBundleIdentifier
|
|
484
|
+
) {
|
|
485
|
+
this.log.info(
|
|
486
|
+
`Will uninstall running WDA since its bundle id is not equal to the default value ${WDA_RUNNER_BUNDLE_ID}`,
|
|
487
|
+
);
|
|
455
488
|
return await this.uninstall();
|
|
456
489
|
}
|
|
457
490
|
|
|
458
491
|
const actualUpgradeTimestamp = await getWDAUpgradeTimestamp();
|
|
459
492
|
this.log.debug(`Upgrade timestamp of the currently bundled WDA: ${actualUpgradeTimestamp}`);
|
|
460
493
|
this.log.debug(`Upgrade timestamp of the WDA on the device: ${upgradedAt}`);
|
|
461
|
-
if (
|
|
462
|
-
|
|
463
|
-
|
|
494
|
+
if (
|
|
495
|
+
actualUpgradeTimestamp &&
|
|
496
|
+
upgradedAt &&
|
|
497
|
+
_.toLower(`${actualUpgradeTimestamp}`) !== _.toLower(`${upgradedAt}`)
|
|
498
|
+
) {
|
|
499
|
+
this.log.info(
|
|
500
|
+
'Will uninstall running WDA since it has different version in comparison to the one ' +
|
|
501
|
+
`which is bundled with appium-xcuitest-driver module (${actualUpgradeTimestamp} != ${upgradedAt})`,
|
|
502
|
+
);
|
|
464
503
|
return await this.uninstall();
|
|
465
504
|
}
|
|
466
505
|
|
|
467
506
|
const message = util.hasValue(productBundleIdentifier)
|
|
468
507
|
? `Will reuse previously cached WDA instance at '${this.url.href}' with '${productBundleIdentifier}'`
|
|
469
508
|
: `Will reuse previously cached WDA instance at '${this.url.href}'`;
|
|
470
|
-
this.log.info(
|
|
509
|
+
this.log.info(
|
|
510
|
+
`${message}. Set the wdaLocalPort capability to a value different from ${this.url.port} if this is an undesired behavior.`,
|
|
511
|
+
);
|
|
471
512
|
this.webDriverAgentUrl = this.url.href;
|
|
472
513
|
}
|
|
473
514
|
|
|
474
515
|
/**
|
|
475
516
|
* Quit and uninstall running WDA.
|
|
476
517
|
*/
|
|
477
|
-
async quitAndUninstall
|
|
518
|
+
async quitAndUninstall(): Promise<void> {
|
|
478
519
|
await this.quit();
|
|
479
520
|
await this.uninstall();
|
|
480
521
|
}
|
|
481
522
|
|
|
482
|
-
private setWDAPaths
|
|
523
|
+
private setWDAPaths(bootstrapPath?: string, agentPath?: string): void {
|
|
483
524
|
// allow the user to specify a place for WDA. This is undocumented and
|
|
484
525
|
// only here for the purposes of testing development of WDA
|
|
485
526
|
this.bootstrapPath = bootstrapPath || BOOTSTRAP_PATH;
|
|
@@ -490,7 +531,7 @@ export class WebDriverAgent {
|
|
|
490
531
|
this.log.info(`Using WDA agent: '${this.agentPath}'`);
|
|
491
532
|
}
|
|
492
533
|
|
|
493
|
-
private async isRunning
|
|
534
|
+
private async isRunning(): Promise<boolean> {
|
|
494
535
|
return !!(await this.getStatus());
|
|
495
536
|
}
|
|
496
537
|
|
|
@@ -515,7 +556,7 @@ export class WebDriverAgent {
|
|
|
515
556
|
*
|
|
516
557
|
* @param timeoutMs If zero or negative, returns immediately. Otherwise, waits up to timeoutMs.
|
|
517
558
|
*/
|
|
518
|
-
private async getStatus
|
|
559
|
+
private async getStatus(timeoutMs: number = 0): Promise<StringRecord | null> {
|
|
519
560
|
const noSessionProxy = new NoSessionProxy({
|
|
520
561
|
server: this.url.hostname ?? undefined,
|
|
521
562
|
port: parseInt(this.url.port ?? '', 10) || undefined,
|
|
@@ -523,13 +564,16 @@ export class WebDriverAgent {
|
|
|
523
564
|
timeout: 3000,
|
|
524
565
|
});
|
|
525
566
|
|
|
526
|
-
const sendGetStatus = async () =>
|
|
567
|
+
const sendGetStatus = async () =>
|
|
568
|
+
(await noSessionProxy.command('/status', 'GET')) as StringRecord;
|
|
527
569
|
|
|
528
570
|
if (_.isNil(timeoutMs) || timeoutMs <= 0) {
|
|
529
571
|
try {
|
|
530
572
|
return await sendGetStatus();
|
|
531
573
|
} catch (err: any) {
|
|
532
|
-
this.log.debug(
|
|
574
|
+
this.log.debug(
|
|
575
|
+
`WDA is not listening at '${this.url.href}'. Original error:: ${err.message}`,
|
|
576
|
+
);
|
|
533
577
|
return null;
|
|
534
578
|
}
|
|
535
579
|
}
|
|
@@ -537,21 +581,26 @@ export class WebDriverAgent {
|
|
|
537
581
|
let lastError: any = null;
|
|
538
582
|
let status: StringRecord | null = null;
|
|
539
583
|
try {
|
|
540
|
-
await waitForCondition(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
584
|
+
await waitForCondition(
|
|
585
|
+
async () => {
|
|
586
|
+
try {
|
|
587
|
+
status = await sendGetStatus();
|
|
588
|
+
return true;
|
|
589
|
+
} catch (err) {
|
|
590
|
+
lastError = err;
|
|
591
|
+
}
|
|
592
|
+
return false;
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
waitMs: timeoutMs,
|
|
596
|
+
intervalMs: 300,
|
|
597
|
+
},
|
|
598
|
+
);
|
|
552
599
|
} catch (err: any) {
|
|
553
|
-
this.log.debug(
|
|
554
|
-
`
|
|
600
|
+
this.log.debug(
|
|
601
|
+
`Failed to get the status endpoint in ${timeoutMs} ms. ` +
|
|
602
|
+
`The last error while accessing ${this.url.href}: ${lastError}. Original error:: ${err.message}.`,
|
|
603
|
+
);
|
|
555
604
|
throw new Error(`WDA was not ready in ${timeoutMs} ms.`);
|
|
556
605
|
}
|
|
557
606
|
return status;
|
|
@@ -562,7 +611,7 @@ export class WebDriverAgent {
|
|
|
562
611
|
* Over Xcode 11, multiple WDA can be in the device since Xcode 11 generates different WDA.
|
|
563
612
|
* Appium does not expect multiple WDAs are running on a device.
|
|
564
613
|
*/
|
|
565
|
-
private async uninstall
|
|
614
|
+
private async uninstall(): Promise<void> {
|
|
566
615
|
try {
|
|
567
616
|
const bundleIds = await this.device.getUserInstalledBundleIdsByBundleName(WDA_CF_BUNDLE_NAME);
|
|
568
617
|
if (_.isEmpty(bundleIds)) {
|
|
@@ -576,17 +625,21 @@ export class WebDriverAgent {
|
|
|
576
625
|
}
|
|
577
626
|
} catch (e: any) {
|
|
578
627
|
this.log.debug(e);
|
|
579
|
-
this.log.warn(
|
|
580
|
-
`
|
|
628
|
+
this.log.warn(
|
|
629
|
+
`WebDriverAgent uninstall failed. Perhaps, it is already uninstalled? ` +
|
|
630
|
+
`Original error: ${e.message}`,
|
|
631
|
+
);
|
|
581
632
|
}
|
|
582
633
|
}
|
|
583
634
|
|
|
584
|
-
private async _cleanupProjectIfFresh
|
|
635
|
+
private async _cleanupProjectIfFresh(): Promise<void> {
|
|
585
636
|
if (this.canSkipXcodebuild) {
|
|
586
637
|
return;
|
|
587
638
|
}
|
|
588
639
|
|
|
589
|
-
const packageInfo = JSON.parse(
|
|
640
|
+
const packageInfo = JSON.parse(
|
|
641
|
+
await fs.readFile(path.join(BOOTSTRAP_PATH, 'package.json'), 'utf8'),
|
|
642
|
+
);
|
|
590
643
|
const box = strongbox(packageInfo.name);
|
|
591
644
|
let boxItem = box.getItem(RECENT_MODULE_VERSION_ITEM_NAME);
|
|
592
645
|
if (!boxItem) {
|
|
@@ -602,7 +655,9 @@ export class WebDriverAgent {
|
|
|
602
655
|
return;
|
|
603
656
|
}
|
|
604
657
|
} else {
|
|
605
|
-
this.log.info(
|
|
658
|
+
this.log.info(
|
|
659
|
+
'There is no need to perform the project cleanup. A fresh install has been detected',
|
|
660
|
+
);
|
|
606
661
|
try {
|
|
607
662
|
await box.createItemWithValue(RECENT_MODULE_VERSION_ITEM_NAME, packageInfo.version);
|
|
608
663
|
} catch (e: any) {
|
|
@@ -617,7 +672,9 @@ export class WebDriverAgent {
|
|
|
617
672
|
recentModuleVersion = util.coerceVersion(recentModuleVersion, true);
|
|
618
673
|
} catch (e: any) {
|
|
619
674
|
this.log.warn(`The persisted module version string has been damaged: ${e.message}`);
|
|
620
|
-
this.log.info(
|
|
675
|
+
this.log.info(
|
|
676
|
+
`Updating it to '${packageInfo.version}' assuming the project clenup is not needed`,
|
|
677
|
+
);
|
|
621
678
|
await boxItem.write(packageInfo.version);
|
|
622
679
|
return;
|
|
623
680
|
}
|
|
@@ -625,14 +682,14 @@ export class WebDriverAgent {
|
|
|
625
682
|
if (util.compareVersions(recentModuleVersion, '>=', packageInfo.version)) {
|
|
626
683
|
this.log.info(
|
|
627
684
|
`WebDriverAgent does not need a cleanup. The project sources are up to date ` +
|
|
628
|
-
|
|
685
|
+
`(${recentModuleVersion} >= ${packageInfo.version})`,
|
|
629
686
|
);
|
|
630
687
|
return;
|
|
631
688
|
}
|
|
632
689
|
|
|
633
690
|
this.log.info(
|
|
634
691
|
`Cleaning up the WebDriverAgent project after the module upgrade has happened ` +
|
|
635
|
-
|
|
692
|
+
`(${recentModuleVersion} < ${packageInfo.version})`,
|
|
636
693
|
);
|
|
637
694
|
try {
|
|
638
695
|
await this.xcodebuild.cleanProject();
|
|
@@ -653,12 +710,12 @@ export class WebDriverAgent {
|
|
|
653
710
|
*
|
|
654
711
|
* @param opts launching WDA with devicectl command options.
|
|
655
712
|
*/
|
|
656
|
-
private async _launchViaDevicectl(
|
|
713
|
+
private async _launchViaDevicectl(
|
|
714
|
+
opts: {env?: Record<string, string | number>} = {},
|
|
715
|
+
): Promise<void> {
|
|
657
716
|
const {env} = opts;
|
|
658
717
|
|
|
659
|
-
await this.device.devicectl.launchApp(
|
|
660
|
-
this.bundleIdForXctest, { env, terminateExisting: true }
|
|
661
|
-
);
|
|
718
|
+
await this.device.devicectl.launchApp(this.bundleIdForXctest, {env, terminateExisting: true});
|
|
662
719
|
}
|
|
663
720
|
|
|
664
721
|
/**
|
|
@@ -668,7 +725,7 @@ export class WebDriverAgent {
|
|
|
668
725
|
private async launchWithPreinstalledWDA(sessionId: string): Promise<StringRecord | null> {
|
|
669
726
|
const xctestEnv: Record<string, string | number> = {
|
|
670
727
|
USE_PORT: this.wdaLocalPort || WDA_AGENT_PORT,
|
|
671
|
-
WDA_PRODUCT_BUNDLE_IDENTIFIER: this.bundleIdForXctest
|
|
728
|
+
WDA_PRODUCT_BUNDLE_IDENTIFIER: this.bundleIdForXctest,
|
|
672
729
|
};
|
|
673
730
|
if (this.mjpegServerPort) {
|
|
674
731
|
xctestEnv.MJPEG_SERVER_PORT = this.mjpegServerPort;
|
|
@@ -684,16 +741,14 @@ export class WebDriverAgent {
|
|
|
684
741
|
if (this.platformVersion && util.compareVersions(this.platformVersion, '>=', '17.0')) {
|
|
685
742
|
await this._launchViaDevicectl({env: xctestEnv});
|
|
686
743
|
} else {
|
|
687
|
-
this.xctestApiClient = new Xctest(this.device.udid, this.bundleIdForXctest, null, {
|
|
744
|
+
this.xctestApiClient = new Xctest(this.device.udid, this.bundleIdForXctest, null, {
|
|
745
|
+
env: xctestEnv,
|
|
746
|
+
});
|
|
688
747
|
await this.xctestApiClient.start();
|
|
689
748
|
}
|
|
690
749
|
} else {
|
|
691
750
|
await this.device.simctl.exec('launch', {
|
|
692
|
-
args: [
|
|
693
|
-
'--terminate-running-process',
|
|
694
|
-
this.device.udid,
|
|
695
|
-
this.bundleIdForXctest,
|
|
696
|
-
],
|
|
751
|
+
args: ['--terminate-running-process', this.device.udid, this.bundleIdForXctest],
|
|
697
752
|
env: xctestEnv,
|
|
698
753
|
});
|
|
699
754
|
}
|
|
@@ -705,8 +760,8 @@ export class WebDriverAgent {
|
|
|
705
760
|
} catch {
|
|
706
761
|
throw new Error(
|
|
707
762
|
`Failed to start the preinstalled WebDriverAgent in ${this.wdaLaunchTimeout} ms. ` +
|
|
708
|
-
|
|
709
|
-
|
|
763
|
+
`The WebDriverAgent might not be properly built or the device might be locked. ` +
|
|
764
|
+
`The 'appium:wdaLaunchTimeout' capability modifies the timeout.`,
|
|
710
765
|
);
|
|
711
766
|
}
|
|
712
767
|
this.started = true;
|