appium-uiautomator2-driver 2.0.5 → 2.0.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/README.md +1 -0
- package/build/lib/commands/element.js +2 -6
- package/build/lib/commands/general.js +8 -12
- package/build/lib/commands/screenshot.js +3 -7
- package/build/lib/commands/touch.js +4 -11
- package/build/lib/driver.js +51 -72
- package/build/lib/uiautomator2.js +29 -39
- package/lib/commands/element.js +1 -3
- package/lib/commands/general.js +7 -8
- package/lib/commands/screenshot.js +3 -5
- package/lib/commands/touch.js +3 -5
- package/lib/driver.js +50 -51
- package/lib/uiautomator2.js +28 -27
- package/npm-shrinkwrap.json +20 -34
- package/package.json +3 -3
package/lib/driver.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
import { fs, util, mjpeg } from '@appium/support';
|
|
7
7
|
import { retryInterval } from 'asyncbox';
|
|
8
8
|
import B from 'bluebird';
|
|
9
|
-
import logger from './logger';
|
|
10
9
|
import commands from './commands/index';
|
|
11
10
|
import { DEFAULT_ADB_PORT } from 'appium-adb';
|
|
12
11
|
import uiautomator2Helpers from './helpers';
|
|
@@ -212,11 +211,11 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
212
211
|
_.defaults(this.opts, defaultOpts);
|
|
213
212
|
|
|
214
213
|
if (this.isChromeSession) {
|
|
215
|
-
|
|
214
|
+
this.log.info("We're going to run a Chrome-based session");
|
|
216
215
|
let {pkg, activity} = helpers.getChromePkg(this.opts.browserName);
|
|
217
216
|
this.opts.appPackage = this.caps.appPackage = pkg;
|
|
218
217
|
this.opts.appActivity = this.caps.appActivity = activity;
|
|
219
|
-
|
|
218
|
+
this.log.info(`Chrome-type package and activity are ${pkg} and ${activity}`);
|
|
220
219
|
}
|
|
221
220
|
|
|
222
221
|
if (this.opts.reboot) {
|
|
@@ -230,9 +229,9 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
230
229
|
} else if (this.opts.appPackage) {
|
|
231
230
|
// the app isn't an actual app file but rather something we want to
|
|
232
231
|
// assume is on the device and just launch via the appPackage
|
|
233
|
-
|
|
232
|
+
this.log.info(`Starting '${this.opts.appPackage}' directly on the device`);
|
|
234
233
|
} else {
|
|
235
|
-
|
|
234
|
+
this.log.info(`Neither 'app' nor 'appPackage' was set. Starting UiAutomator2 ` +
|
|
236
235
|
'without the target application');
|
|
237
236
|
}
|
|
238
237
|
this.opts.adbPort = this.opts.adbPort || DEFAULT_ADB_PORT;
|
|
@@ -240,7 +239,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
240
239
|
await this.startUiAutomator2Session();
|
|
241
240
|
await this.fillDeviceDetails();
|
|
242
241
|
if (this.opts.mjpegScreenshotUrl) {
|
|
243
|
-
|
|
242
|
+
this.log.info(`Starting MJPEG stream reading URL: '${this.opts.mjpegScreenshotUrl}'`);
|
|
244
243
|
this.mjpegStream = new mjpeg.MJpegStream(this.opts.mjpegScreenshotUrl);
|
|
245
244
|
await this.mjpegStream.start();
|
|
246
245
|
}
|
|
@@ -264,7 +263,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
264
263
|
|
|
265
264
|
async getSession () {
|
|
266
265
|
let sessionData = await super.getSession();
|
|
267
|
-
|
|
266
|
+
this.log.debug('Getting session details from server to mix in');
|
|
268
267
|
let uia2Data = await this.uiautomator2.jwproxy.command('/', 'GET', {});
|
|
269
268
|
return Object.assign({}, sessionData, uia2Data);
|
|
270
269
|
}
|
|
@@ -275,13 +274,13 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
275
274
|
|
|
276
275
|
setAvdFromCapabilities (caps) {
|
|
277
276
|
if (this.opts.avd) {
|
|
278
|
-
|
|
277
|
+
this.log.info('avd name defined, ignoring device name and platform version');
|
|
279
278
|
} else {
|
|
280
279
|
if (!caps.deviceName) {
|
|
281
|
-
|
|
280
|
+
this.log.errorAndThrow('avd or deviceName should be specified when reboot option is enables');
|
|
282
281
|
}
|
|
283
282
|
if (!caps.platformVersion) {
|
|
284
|
-
|
|
283
|
+
this.log.errorAndThrow('avd or platformVersion should be specified when reboot option is enabled');
|
|
285
284
|
}
|
|
286
285
|
let avdDevice = caps.deviceName.replace(/[^a-zA-Z0-9_.]/g, '-');
|
|
287
286
|
this.opts.avd = `${avdDevice}__${caps.platformVersion}`;
|
|
@@ -290,9 +289,9 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
290
289
|
|
|
291
290
|
async allocateSystemPort () {
|
|
292
291
|
const forwardPort = async (localPort) => {
|
|
293
|
-
|
|
292
|
+
this.log.debug(`Forwarding UiAutomator2 Server port ${DEVICE_PORT} to local port ${localPort}`);
|
|
294
293
|
if ((await checkPortStatus(localPort, LOCALHOST_IP4)) === 'open') {
|
|
295
|
-
|
|
294
|
+
this.log.errorAndThrow(`UiAutomator2 Server cannot start because the local port #${localPort} is busy. ` +
|
|
296
295
|
`Make sure the port you provide via 'systemPort' capability is not occupied. ` +
|
|
297
296
|
`This situation might often be a result of an inaccurate sessions management, e.g. ` +
|
|
298
297
|
`old automation sessions on the same device must always be closed before starting new ones.`);
|
|
@@ -310,7 +309,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
310
309
|
try {
|
|
311
310
|
this.opts.systemPort = await findAPortNotInUse(startPort, endPort);
|
|
312
311
|
} catch (e) {
|
|
313
|
-
|
|
312
|
+
this.log.errorAndThrow(
|
|
314
313
|
`Cannot find any free port in range ${startPort}..${endPort}}. ` +
|
|
315
314
|
`Please set the available port number by providing the systemPort capability or ` +
|
|
316
315
|
`double check the processes that are locking ports within this range and terminate ` +
|
|
@@ -357,22 +356,22 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
357
356
|
const apiLevel = await this.adb.getApiLevel();
|
|
358
357
|
|
|
359
358
|
if (apiLevel < 21) {
|
|
360
|
-
|
|
359
|
+
this.log.errorAndThrow('UIAutomator2 is only supported since Android 5.0 (Lollipop). ' +
|
|
361
360
|
'You could still use other supported backends in order to automate older Android versions.');
|
|
362
361
|
}
|
|
363
362
|
|
|
364
363
|
if (apiLevel >= 28) { // Android P
|
|
365
|
-
|
|
364
|
+
this.log.info('Relaxing hidden api policy');
|
|
366
365
|
await this.adb.setHiddenApiPolicy('1', !!this.opts.ignoreHiddenApiPolicyError);
|
|
367
366
|
}
|
|
368
367
|
|
|
369
368
|
// check if we have to enable/disable gps before running the application
|
|
370
369
|
if (util.hasValue(this.opts.gpsEnabled)) {
|
|
371
370
|
if (this.isEmulator()) {
|
|
372
|
-
|
|
371
|
+
this.log.info(`Trying to ${this.opts.gpsEnabled ? 'enable' : 'disable'} gps location provider`);
|
|
373
372
|
await this.adb.toggleGPSLocationProvider(this.opts.gpsEnabled);
|
|
374
373
|
} else {
|
|
375
|
-
|
|
374
|
+
this.log.warn(`Sorry! 'gpsEnabled' capability is only available for emulators`);
|
|
376
375
|
}
|
|
377
376
|
}
|
|
378
377
|
|
|
@@ -406,11 +405,11 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
406
405
|
// Read https://github.com/appium/appium/pull/11640#issuecomment-438260477
|
|
407
406
|
// `--no-window-animation` works over Android 8 to disable all of animations
|
|
408
407
|
if (await this.adb.isAnimationOn()) {
|
|
409
|
-
|
|
408
|
+
this.log.info('Disabling animation via io.appium.settings');
|
|
410
409
|
await this.adb.setAnimationState(false);
|
|
411
410
|
this._wasWindowAnimationDisabled = true;
|
|
412
411
|
} else {
|
|
413
|
-
|
|
412
|
+
this.log.info('Window animation is already disabled');
|
|
414
413
|
}
|
|
415
414
|
}
|
|
416
415
|
|
|
@@ -433,7 +432,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
433
432
|
// unlock the device to prepare it for testing
|
|
434
433
|
await helpers.unlock(this, this.adb, this.caps);
|
|
435
434
|
} else {
|
|
436
|
-
|
|
435
|
+
this.log.debug(`'skipUnlock' capability set, so skipping device unlock`);
|
|
437
436
|
}
|
|
438
437
|
|
|
439
438
|
if (this.isChromeSession) { // start a chromedriver session
|
|
@@ -444,7 +443,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
444
443
|
|
|
445
444
|
// if the initial orientation is requested, set it
|
|
446
445
|
if (util.hasValue(this.opts.orientation)) {
|
|
447
|
-
|
|
446
|
+
this.log.debug(`Setting initial orientation to '${this.opts.orientation}'`);
|
|
448
447
|
await this.setOrientation(this.opts.orientation);
|
|
449
448
|
}
|
|
450
449
|
|
|
@@ -453,7 +452,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
453
452
|
if (this.opts.autoWebview) {
|
|
454
453
|
const viewName = this.defaultWebviewName();
|
|
455
454
|
const timeout = this.opts.autoWebviewTimeout || 2000;
|
|
456
|
-
|
|
455
|
+
this.log.info(`Setting auto webview to context '${viewName}' with timeout ${timeout}ms`);
|
|
457
456
|
await retryInterval(timeout / 500, 500, this.setContext.bind(this), viewName);
|
|
458
457
|
}
|
|
459
458
|
|
|
@@ -496,12 +495,12 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
496
495
|
};
|
|
497
496
|
// now that we have package and activity, we can create an instance of
|
|
498
497
|
// uiautomator2 with the appropriate options
|
|
499
|
-
this.uiautomator2 = new UiAutomator2Server(uiautomator2Opts);
|
|
498
|
+
this.uiautomator2 = new UiAutomator2Server(this.log, uiautomator2Opts);
|
|
500
499
|
this.proxyReqRes = this.uiautomator2.proxyReqRes.bind(this.uiautomator2);
|
|
501
500
|
this.proxyCommand = this.uiautomator2.proxyCommand.bind(this.uiautomator2);
|
|
502
501
|
|
|
503
502
|
if (this.opts.skipServerInstallation) {
|
|
504
|
-
|
|
503
|
+
this.log.info(`'skipServerInstallation' is set. Skipping UIAutomator2 server installation.`);
|
|
505
504
|
} else {
|
|
506
505
|
await this.uiautomator2.installServerApk(this.opts.uiautomator2ServerInstallTimeout);
|
|
507
506
|
try {
|
|
@@ -509,7 +508,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
509
508
|
SETTINGS_HELPER_PKG_ID, SERVER_PACKAGE_ID, SERVER_TEST_PACKAGE_ID,
|
|
510
509
|
);
|
|
511
510
|
} catch (e) {
|
|
512
|
-
|
|
511
|
+
this.log.warn(`Cannot add server packages to the Doze whitelist. Original error: ` +
|
|
513
512
|
(e.stderr || e.message));
|
|
514
513
|
}
|
|
515
514
|
}
|
|
@@ -531,7 +530,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
531
530
|
try {
|
|
532
531
|
otherApps = helpers.parseArray(this.opts.otherApps);
|
|
533
532
|
} catch (e) {
|
|
534
|
-
|
|
533
|
+
this.log.errorAndThrow(`Could not parse "otherApps" capability: ${e.message}`);
|
|
535
534
|
}
|
|
536
535
|
otherApps = await B.all(otherApps
|
|
537
536
|
.map((app) => this.helpers.configureApp(app, [APK_EXTENSION, APKS_EXTENSION])));
|
|
@@ -551,13 +550,13 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
551
550
|
}
|
|
552
551
|
await helpers.installApk(this.adb, this.opts);
|
|
553
552
|
} else {
|
|
554
|
-
|
|
553
|
+
this.log.debug('noReset has been requested and the app is already installed. Doing nothing');
|
|
555
554
|
}
|
|
556
555
|
} else {
|
|
557
556
|
if (this.opts.fullReset) {
|
|
558
|
-
|
|
557
|
+
this.log.errorAndThrow('Full reset requires an app capability, use fastReset if app is not provided');
|
|
559
558
|
}
|
|
560
|
-
|
|
559
|
+
this.log.debug('No app capability. Assuming it is already on the device');
|
|
561
560
|
if (this.opts.fastReset && this.opts.appPackage) {
|
|
562
561
|
await helpers.resetApp(this.adb, this.opts);
|
|
563
562
|
}
|
|
@@ -569,11 +568,11 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
569
568
|
const appWaitPackage = this.opts.appWaitPackage || this.opts.appPackage;
|
|
570
569
|
const appWaitActivity = this.opts.appWaitActivity || this.opts.appActivity;
|
|
571
570
|
|
|
572
|
-
|
|
571
|
+
this.log.info(`Starting '${this.opts.appPackage}/${this.opts.appActivity} ` +
|
|
573
572
|
`and waiting for '${appWaitPackage}/${appWaitActivity}'`);
|
|
574
573
|
|
|
575
574
|
if (this.caps.androidCoverage) {
|
|
576
|
-
|
|
575
|
+
this.log.info(`androidCoverage is configured. ` +
|
|
577
576
|
` Starting instrumentation of '${this.caps.androidCoverage}'...`);
|
|
578
577
|
await this.adb.androidCoverage(this.caps.androidCoverage, appWaitPackage, appWaitActivity);
|
|
579
578
|
} else {
|
|
@@ -597,7 +596,7 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
597
596
|
}
|
|
598
597
|
|
|
599
598
|
async deleteSession () {
|
|
600
|
-
|
|
599
|
+
this.log.debug('Deleting UiAutomator2 session');
|
|
601
600
|
|
|
602
601
|
try {
|
|
603
602
|
if (!_.isEmpty(this._screenRecordingProperties)) {
|
|
@@ -613,13 +612,13 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
613
612
|
try {
|
|
614
613
|
await this.stopChromedriverProxies();
|
|
615
614
|
} catch (err) {
|
|
616
|
-
|
|
615
|
+
this.log.warn(`Unable to stop ChromeDriver proxies: ${err.message}`);
|
|
617
616
|
}
|
|
618
617
|
if (this.jwpProxyActive) {
|
|
619
618
|
try {
|
|
620
619
|
await this.uiautomator2.deleteSession();
|
|
621
620
|
} catch (err) {
|
|
622
|
-
|
|
621
|
+
this.log.warn(`Unable to proxy deleteSession to UiAutomator2: ${err.message}`);
|
|
623
622
|
}
|
|
624
623
|
}
|
|
625
624
|
this.uiautomator2 = null;
|
|
@@ -628,22 +627,22 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
628
627
|
|
|
629
628
|
if (this.adb) {
|
|
630
629
|
if (this.opts.unicodeKeyboard && this.opts.resetKeyboard && this.defaultIME) {
|
|
631
|
-
|
|
630
|
+
this.log.debug(`Resetting IME to '${this.defaultIME}'`);
|
|
632
631
|
try {
|
|
633
632
|
await this.adb.setIME(this.defaultIME);
|
|
634
633
|
} catch (err) {
|
|
635
|
-
|
|
634
|
+
this.log.warn(`Unable to reset IME: ${err.message}`);
|
|
636
635
|
}
|
|
637
636
|
}
|
|
638
637
|
if (this.caps.androidCoverage) {
|
|
639
|
-
|
|
638
|
+
this.log.info('Shutting down the adb process of instrumentation...');
|
|
640
639
|
await this.adb.endAndroidCoverage();
|
|
641
640
|
// Use this broadcast intent to notify it's time to dump coverage to file
|
|
642
641
|
if (this.caps.androidCoverageEndIntent) {
|
|
643
|
-
|
|
642
|
+
this.log.info(`Sending intent broadcast '${this.caps.androidCoverageEndIntent}' at the end of instrumenting.`);
|
|
644
643
|
await this.adb.broadcast(this.caps.androidCoverageEndIntent);
|
|
645
644
|
} else {
|
|
646
|
-
|
|
645
|
+
this.log.warn('No androidCoverageEndIntent is configured in caps. Possibly you cannot get coverage file.');
|
|
647
646
|
}
|
|
648
647
|
}
|
|
649
648
|
if (this.opts.appPackage) {
|
|
@@ -653,65 +652,65 @@ class AndroidUiautomator2Driver extends BaseDriver {
|
|
|
653
652
|
try {
|
|
654
653
|
await this.adb.forceStop(this.opts.appPackage);
|
|
655
654
|
} catch (err) {
|
|
656
|
-
|
|
655
|
+
this.log.warn(`Unable to force stop app: ${err.message}`);
|
|
657
656
|
}
|
|
658
657
|
}
|
|
659
658
|
if (this.opts.fullReset && !this.opts.skipUninstall) {
|
|
660
|
-
|
|
659
|
+
this.log.debug(`Capability 'fullReset' set to 'true', Uninstalling '${this.opts.appPackage}'`);
|
|
661
660
|
try {
|
|
662
661
|
await this.adb.uninstallApk(this.opts.appPackage);
|
|
663
662
|
} catch (err) {
|
|
664
|
-
|
|
663
|
+
this.log.warn(`Unable to uninstall app: ${err.message}`);
|
|
665
664
|
}
|
|
666
665
|
}
|
|
667
666
|
}
|
|
668
667
|
// This value can be true if test target device is <= 26
|
|
669
668
|
if (this._wasWindowAnimationDisabled) {
|
|
670
|
-
|
|
669
|
+
this.log.info('Restoring window animation state');
|
|
671
670
|
await this.adb.setAnimationState(true);
|
|
672
671
|
}
|
|
673
672
|
await this.adb.stopLogcat();
|
|
674
673
|
try {
|
|
675
674
|
await this.releaseSystemPort();
|
|
676
675
|
} catch (error) {
|
|
677
|
-
|
|
676
|
+
this.log.warn(`Unable to remove system port forward: ${error.message}`);
|
|
678
677
|
// Ignore, this block will also be called when we fall in catch block
|
|
679
678
|
// and before even port forward.
|
|
680
679
|
}
|
|
681
680
|
try {
|
|
682
681
|
await this.releaseMjpegServerPort();
|
|
683
682
|
} catch (error) {
|
|
684
|
-
|
|
683
|
+
this.log.warn(`Unable to remove MJPEG server port forward: ${error.message}`);
|
|
685
684
|
// Ignore, this block will also be called when we fall in catch block
|
|
686
685
|
// and before even port forward.
|
|
687
686
|
}
|
|
688
687
|
|
|
689
688
|
if (await this.adb.getApiLevel() >= 28) { // Android P
|
|
690
|
-
|
|
689
|
+
this.log.info('Restoring hidden api policy to the device default configuration');
|
|
691
690
|
await this.adb.setDefaultHiddenApiPolicy(!!this.opts.ignoreHiddenApiPolicyError);
|
|
692
691
|
}
|
|
693
692
|
|
|
694
693
|
if (this.opts.reboot) {
|
|
695
694
|
let avdName = this.opts.avd.replace('@', '');
|
|
696
|
-
|
|
695
|
+
this.log.debug(`Closing emulator '${avdName}'`);
|
|
697
696
|
try {
|
|
698
697
|
await this.adb.killEmulator(avdName);
|
|
699
698
|
} catch (err) {
|
|
700
|
-
|
|
699
|
+
this.log.warn(`Unable to close emulator: ${err.message}`);
|
|
701
700
|
}
|
|
702
701
|
}
|
|
703
702
|
}
|
|
704
703
|
if (this.mjpegStream) {
|
|
705
|
-
|
|
704
|
+
this.log.info('Closing MJPEG stream');
|
|
706
705
|
this.mjpegStream.stop();
|
|
707
706
|
}
|
|
708
707
|
await super.deleteSession();
|
|
709
708
|
}
|
|
710
709
|
|
|
711
710
|
async checkAppPresent () {
|
|
712
|
-
|
|
711
|
+
this.log.debug('Checking whether app is actually present');
|
|
713
712
|
if (!(await fs.exists(this.opts.app))) {
|
|
714
|
-
|
|
713
|
+
this.log.errorAndThrow(`Could not find app apk at '${this.opts.app}'`);
|
|
715
714
|
}
|
|
716
715
|
}
|
|
717
716
|
|
package/lib/uiautomator2.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import { JWProxy, errors } from '@appium/base-driver';
|
|
3
3
|
import { waitForCondition } from 'asyncbox';
|
|
4
|
-
import log from './logger';
|
|
5
4
|
import {
|
|
6
5
|
SERVER_APK_PATH as apkPath,
|
|
7
6
|
TEST_APK_PATH as testApkPath,
|
|
@@ -37,15 +36,17 @@ class UIA2Proxy extends JWProxy {
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
class UiAutomator2Server {
|
|
40
|
-
constructor (opts = {}) {
|
|
39
|
+
constructor (log, opts = {}) {
|
|
41
40
|
for (let req of REQD_PARAMS) {
|
|
42
41
|
if (!opts || !util.hasValue(opts[req])) {
|
|
43
42
|
throw new Error(`Option '${req}' is required!`);
|
|
44
43
|
}
|
|
45
44
|
this[req] = opts[req];
|
|
46
45
|
}
|
|
46
|
+
this.log = log;
|
|
47
47
|
this.disableSuppressAccessibilityService = opts.disableSuppressAccessibilityService;
|
|
48
48
|
const proxyOpts = {
|
|
49
|
+
log,
|
|
49
50
|
server: this.host,
|
|
50
51
|
port: this.systemPort,
|
|
51
52
|
keepAlive: true,
|
|
@@ -71,7 +72,7 @@ class UiAutomator2Server {
|
|
|
71
72
|
return { appPath, appId };
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
log.info(`Server package at '${appPath}' is not writeable. ` +
|
|
75
|
+
this.log.info(`Server package at '${appPath}' is not writeable. ` +
|
|
75
76
|
`Will copy it into the temporary location at '${tmpRoot}' as a workaround. ` +
|
|
76
77
|
`Consider making this file writeable manually in order to improve the performance of session startup.`);
|
|
77
78
|
const dstPath = path.resolve(tmpRoot, path.basename(appPath));
|
|
@@ -114,7 +115,7 @@ class UiAutomator2Server {
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
const appState = await this.adb.getApplicationInstallState(appPath, appId);
|
|
117
|
-
log.debug(`${appId} installation state: ${appState}`);
|
|
118
|
+
this.log.debug(`${appId} installation state: ${appState}`);
|
|
118
119
|
if (await this.adb.checkApkCert(appPath, appId)) {
|
|
119
120
|
shouldUninstallServerPackages = shouldUninstallServerPackages || [
|
|
120
121
|
this.adb.APP_INSTALL_STATE.OLDER_VERSION_INSTALLED,
|
|
@@ -130,16 +131,16 @@ class UiAutomator2Server {
|
|
|
130
131
|
this.adb.APP_INSTALL_STATE.NOT_INSTALLED,
|
|
131
132
|
].includes(appState);
|
|
132
133
|
}
|
|
133
|
-
log.info(`Server packages are ${shouldInstallServerPackages ? '' : 'not '}going to be (re)installed`);
|
|
134
|
+
this.log.info(`Server packages are ${shouldInstallServerPackages ? '' : 'not '}going to be (re)installed`);
|
|
134
135
|
if (shouldInstallServerPackages && shouldUninstallServerPackages) {
|
|
135
|
-
log.info('Full packages reinstall is going to be performed');
|
|
136
|
+
this.log.info('Full packages reinstall is going to be performed');
|
|
136
137
|
}
|
|
137
138
|
for (const {appId, appPath} of packagesInfo) {
|
|
138
139
|
if (shouldUninstallServerPackages) {
|
|
139
140
|
try {
|
|
140
141
|
await this.adb.uninstallApk(appId);
|
|
141
142
|
} catch (err) {
|
|
142
|
-
log.warn(`Error uninstalling '${appId}': ${err.message}`);
|
|
143
|
+
this.log.warn(`Error uninstalling '${appId}': ${err.message}`);
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
if (shouldInstallServerPackages) {
|
|
@@ -159,7 +160,7 @@ class UiAutomator2Server {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
async verifyServicesAvailability () {
|
|
162
|
-
log.debug(`Waiting up to ${SERVICES_LAUNCH_TIMEOUT}ms for services to be available`);
|
|
163
|
+
this.log.debug(`Waiting up to ${SERVICES_LAUNCH_TIMEOUT}ms for services to be available`);
|
|
163
164
|
let isPmServiceAvailable = false;
|
|
164
165
|
let pmOutput = '';
|
|
165
166
|
let pmError = null;
|
|
@@ -178,7 +179,7 @@ class UiAutomator2Server {
|
|
|
178
179
|
pmOutput = ''; // remove output, so it is not printed below
|
|
179
180
|
} else if (pmOutput.includes(INSTRUMENTATION_TARGET)) {
|
|
180
181
|
pmOutput = ''; // remove output, so it is not printed below
|
|
181
|
-
log.debug(`Instrumentation target '${INSTRUMENTATION_TARGET}' is available`);
|
|
182
|
+
this.log.debug(`Instrumentation target '${INSTRUMENTATION_TARGET}' is available`);
|
|
182
183
|
// eslint-disable-next-line require-atomic-updates
|
|
183
184
|
isPmServiceAvailable = true;
|
|
184
185
|
} else if (!pmError) {
|
|
@@ -191,11 +192,11 @@ class UiAutomator2Server {
|
|
|
191
192
|
intervalMs: 1000,
|
|
192
193
|
});
|
|
193
194
|
} catch (err) {
|
|
194
|
-
log.error(`Unable to find instrumentation target '${INSTRUMENTATION_TARGET}': ${(pmError || {}).message}`);
|
|
195
|
+
this.log.error(`Unable to find instrumentation target '${INSTRUMENTATION_TARGET}': ${(pmError || {}).message}`);
|
|
195
196
|
if (pmOutput) {
|
|
196
|
-
log.debug('Available targets:');
|
|
197
|
+
this.log.debug('Available targets:');
|
|
197
198
|
for (const line of pmOutput.split('\n')) {
|
|
198
|
-
log.debug(` ${line.replace('instrumentation:', '')}`);
|
|
199
|
+
this.log.debug(` ${line.replace('instrumentation:', '')}`);
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
}
|
|
@@ -204,10 +205,10 @@ class UiAutomator2Server {
|
|
|
204
205
|
async startSession (caps) {
|
|
205
206
|
await this.cleanupAutomationLeftovers();
|
|
206
207
|
if (caps.skipServerInstallation) {
|
|
207
|
-
log.info(`'skipServerInstallation' is set. Attempting to use UIAutomator2 server from the device`);
|
|
208
|
+
this.log.info(`'skipServerInstallation' is set. Attempting to use UIAutomator2 server from the device`);
|
|
208
209
|
} else {
|
|
209
|
-
log.info(`Starting UIAutomator2 server ${serverVersion}`);
|
|
210
|
-
log.info(`Using UIAutomator2 server from '${apkPath}' and test from '${testApkPath}'`);
|
|
210
|
+
this.log.info(`Starting UIAutomator2 server ${serverVersion}`);
|
|
211
|
+
this.log.info(`Using UIAutomator2 server from '${apkPath}' and test from '${testApkPath}'`);
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
const timeout = caps.uiautomator2ServerLaunchTimeout || SERVER_LAUNCH_TIMEOUT;
|
|
@@ -216,7 +217,7 @@ class UiAutomator2Server {
|
|
|
216
217
|
const maxRetries = 2;
|
|
217
218
|
const delayBetweenRetries = 3000;
|
|
218
219
|
while (retries < maxRetries) {
|
|
219
|
-
log.info(`Waiting up to ${timeout}ms for UiAutomator2 to be online...`);
|
|
220
|
+
this.log.info(`Waiting up to ${timeout}ms for UiAutomator2 to be online...`);
|
|
220
221
|
this.jwproxy.didInstrumentationExit = false;
|
|
221
222
|
await this.startInstrumentationProcess();
|
|
222
223
|
if (!this.jwproxy.didInstrumentationExit) {
|
|
@@ -234,7 +235,7 @@ class UiAutomator2Server {
|
|
|
234
235
|
intervalMs: 1000,
|
|
235
236
|
});
|
|
236
237
|
} catch (err) {
|
|
237
|
-
log.errorAndThrow(`The instrumentation process cannot be initialized within ${timeout}ms timeout. `
|
|
238
|
+
this.log.errorAndThrow(`The instrumentation process cannot be initialized within ${timeout}ms timeout. `
|
|
238
239
|
+ 'Make sure the application under test does not crash and investigate the logcat output. '
|
|
239
240
|
+ `You could also try to increase the value of 'uiautomator2ServerLaunchTimeout' capability`);
|
|
240
241
|
}
|
|
@@ -245,16 +246,16 @@ class UiAutomator2Server {
|
|
|
245
246
|
|
|
246
247
|
retries++;
|
|
247
248
|
if (retries >= maxRetries) {
|
|
248
|
-
log.errorAndThrow('The instrumentation process cannot be initialized. '
|
|
249
|
+
this.log.errorAndThrow('The instrumentation process cannot be initialized. '
|
|
249
250
|
+ 'Make sure the application under test does not crash and investigate the logcat output.');
|
|
250
251
|
}
|
|
251
|
-
log.warn(`The instrumentation process has been unexpectedly terminated. `
|
|
252
|
+
this.log.warn(`The instrumentation process has been unexpectedly terminated. `
|
|
252
253
|
+ `Retrying UiAutomator2 startup (#${retries} of ${maxRetries - 1})`);
|
|
253
254
|
await this.cleanupAutomationLeftovers(true);
|
|
254
255
|
await B.delay(delayBetweenRetries);
|
|
255
256
|
}
|
|
256
257
|
|
|
257
|
-
log.debug(`The initialization of the instrumentation process took `
|
|
258
|
+
this.log.debug(`The initialization of the instrumentation process took `
|
|
258
259
|
+ `${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
259
260
|
await this.jwproxy.command('/session', 'POST', {
|
|
260
261
|
capabilities: {
|
|
@@ -290,19 +291,19 @@ class UiAutomator2Server {
|
|
|
290
291
|
}
|
|
291
292
|
|
|
292
293
|
async deleteSession () {
|
|
293
|
-
log.debug('Deleting UiAutomator2 server session');
|
|
294
|
+
this.log.debug('Deleting UiAutomator2 server session');
|
|
294
295
|
// rely on jwproxy's intelligence to know what we're talking about and
|
|
295
296
|
// delete the current session
|
|
296
297
|
try {
|
|
297
298
|
await this.jwproxy.command('/', 'DELETE');
|
|
298
299
|
} catch (err) {
|
|
299
|
-
log.warn(`Did not get confirmation UiAutomator2 deleteSession worked; ` +
|
|
300
|
+
this.log.warn(`Did not get confirmation UiAutomator2 deleteSession worked; ` +
|
|
300
301
|
`Error was: ${err}`);
|
|
301
302
|
}
|
|
302
303
|
}
|
|
303
304
|
|
|
304
305
|
async cleanupAutomationLeftovers (strictCleanup = false) {
|
|
305
|
-
log.debug(`Performing ${strictCleanup ? 'strict' : 'shallow'} cleanup of automation leftovers`);
|
|
306
|
+
this.log.debug(`Performing ${strictCleanup ? 'strict' : 'shallow'} cleanup of automation leftovers`);
|
|
306
307
|
|
|
307
308
|
try {
|
|
308
309
|
const {value} = (await axios({
|
|
@@ -311,18 +312,18 @@ class UiAutomator2Server {
|
|
|
311
312
|
})).data;
|
|
312
313
|
const activeSessionIds = value.map(({id}) => id).filter(Boolean);
|
|
313
314
|
if (activeSessionIds.length) {
|
|
314
|
-
log.debug(`The following obsolete sessions are still running: ${JSON.stringify(activeSessionIds)}`);
|
|
315
|
-
log.debug(`Cleaning up ${util.pluralize('obsolete session', activeSessionIds.length, true)}`);
|
|
315
|
+
this.log.debug(`The following obsolete sessions are still running: ${JSON.stringify(activeSessionIds)}`);
|
|
316
|
+
this.log.debug(`Cleaning up ${util.pluralize('obsolete session', activeSessionIds.length, true)}`);
|
|
316
317
|
await B.all(activeSessionIds
|
|
317
318
|
.map((id) => axios.delete(`http://${this.host}:${this.systemPort}/session/${id}`))
|
|
318
319
|
);
|
|
319
320
|
// Let all sessions to be properly terminated before continuing
|
|
320
321
|
await B.delay(1000);
|
|
321
322
|
} else {
|
|
322
|
-
log.debug('No obsolete sessions have been detected');
|
|
323
|
+
this.log.debug('No obsolete sessions have been detected');
|
|
323
324
|
}
|
|
324
325
|
} catch (e) {
|
|
325
|
-
log.debug(`No obsolete sessions have been detected (${e.message})`);
|
|
326
|
+
this.log.debug(`No obsolete sessions have been detected (${e.message})`);
|
|
326
327
|
}
|
|
327
328
|
|
|
328
329
|
try {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-uiautomator2-driver",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"lockfileVersion": 1,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"dependencies": {
|
|
@@ -447,9 +447,9 @@
|
|
|
447
447
|
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="
|
|
448
448
|
},
|
|
449
449
|
"@xmldom/xmldom": {
|
|
450
|
-
"version": "0.8.
|
|
451
|
-
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.
|
|
452
|
-
"integrity": "sha512
|
|
450
|
+
"version": "0.8.2",
|
|
451
|
+
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.2.tgz",
|
|
452
|
+
"integrity": "sha512-+R0juSseERyoPvnBQ/cZih6bpF7IpCXlWbHRoCRzYzqpz6gWHOgf8o4MOEf6KBVuOyqU+gCNLkCWVIJAro8XyQ=="
|
|
453
453
|
},
|
|
454
454
|
"accepts": {
|
|
455
455
|
"version": "1.3.8",
|
|
@@ -519,11 +519,11 @@
|
|
|
519
519
|
}
|
|
520
520
|
},
|
|
521
521
|
"appium-android-driver": {
|
|
522
|
-
"version": "5.0.
|
|
523
|
-
"resolved": "https://registry.npmjs.org/appium-android-driver/-/appium-android-driver-5.0.
|
|
524
|
-
"integrity": "sha512-
|
|
522
|
+
"version": "5.0.8",
|
|
523
|
+
"resolved": "https://registry.npmjs.org/appium-android-driver/-/appium-android-driver-5.0.8.tgz",
|
|
524
|
+
"integrity": "sha512-dYVr7rezJCJVRP7sQ+l82eigzi4UNTlM7tFoLXKACwBe5ccPqEw3VUNfBC7yZKCNyVuy+zPZKYgDkR7BVlcogg==",
|
|
525
525
|
"requires": {
|
|
526
|
-
"@appium/base-driver": "^8.
|
|
526
|
+
"@appium/base-driver": "^8.3.0",
|
|
527
527
|
"@appium/support": "^2.55.3",
|
|
528
528
|
"@babel/runtime": "^7.0.0",
|
|
529
529
|
"appium-adb": "^9.0.0",
|
|
@@ -575,9 +575,9 @@
|
|
|
575
575
|
}
|
|
576
576
|
},
|
|
577
577
|
"appium-uiautomator2-server": {
|
|
578
|
-
"version": "5.
|
|
579
|
-
"resolved": "https://registry.npmjs.org/appium-uiautomator2-server/-/appium-uiautomator2-server-5.
|
|
580
|
-
"integrity": "sha512-
|
|
578
|
+
"version": "5.5.1",
|
|
579
|
+
"resolved": "https://registry.npmjs.org/appium-uiautomator2-server/-/appium-uiautomator2-server-5.5.1.tgz",
|
|
580
|
+
"integrity": "sha512-mA5ydwyC1VAIaEUrIy4DmmqcPsUmhGFFyKWACdKH0Y1RYDcBVsB/O2giwNEJI42o2yIEkFfevisixs3Z3oEpWg=="
|
|
581
581
|
},
|
|
582
582
|
"aproba": {
|
|
583
583
|
"version": "2.0.0",
|
|
@@ -912,13 +912,9 @@
|
|
|
912
912
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
|
913
913
|
},
|
|
914
914
|
"crc-32": {
|
|
915
|
-
"version": "1.2.
|
|
916
|
-
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.
|
|
917
|
-
"integrity": "sha512-
|
|
918
|
-
"requires": {
|
|
919
|
-
"exit-on-epipe": "~1.0.1",
|
|
920
|
-
"printj": "~1.3.1"
|
|
921
|
-
}
|
|
915
|
+
"version": "1.2.2",
|
|
916
|
+
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
|
917
|
+
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
|
|
922
918
|
},
|
|
923
919
|
"crc32-stream": {
|
|
924
920
|
"version": "4.0.2",
|
|
@@ -1036,11 +1032,6 @@
|
|
|
1036
1032
|
"resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
|
|
1037
1033
|
"integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI="
|
|
1038
1034
|
},
|
|
1039
|
-
"exit-on-epipe": {
|
|
1040
|
-
"version": "1.0.1",
|
|
1041
|
-
"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
|
|
1042
|
-
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
|
|
1043
|
-
},
|
|
1044
1035
|
"express": {
|
|
1045
1036
|
"version": "4.17.3",
|
|
1046
1037
|
"resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
|
|
@@ -1229,9 +1220,9 @@
|
|
|
1229
1220
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
|
1230
1221
|
},
|
|
1231
1222
|
"gauge": {
|
|
1232
|
-
"version": "4.0.
|
|
1233
|
-
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.
|
|
1234
|
-
"integrity": "sha512-
|
|
1223
|
+
"version": "4.0.4",
|
|
1224
|
+
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
|
|
1225
|
+
"integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
|
|
1235
1226
|
"requires": {
|
|
1236
1227
|
"aproba": "^1.0.3 || ^2.0.0",
|
|
1237
1228
|
"color-support": "^1.1.3",
|
|
@@ -1280,9 +1271,9 @@
|
|
|
1280
1271
|
}
|
|
1281
1272
|
},
|
|
1282
1273
|
"graceful-fs": {
|
|
1283
|
-
"version": "4.2.
|
|
1284
|
-
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.
|
|
1285
|
-
"integrity": "sha512-
|
|
1274
|
+
"version": "4.2.10",
|
|
1275
|
+
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
|
|
1276
|
+
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
|
|
1286
1277
|
},
|
|
1287
1278
|
"has": {
|
|
1288
1279
|
"version": "1.0.3",
|
|
@@ -1992,11 +1983,6 @@
|
|
|
1992
1983
|
}
|
|
1993
1984
|
}
|
|
1994
1985
|
},
|
|
1995
|
-
"printj": {
|
|
1996
|
-
"version": "1.3.1",
|
|
1997
|
-
"resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz",
|
|
1998
|
-
"integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg=="
|
|
1999
|
-
},
|
|
2000
1986
|
"process": {
|
|
2001
1987
|
"version": "0.11.10",
|
|
2002
1988
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|