appium-android-driver 5.14.7 → 6.0.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/CHANGELOG.md +7 -0
- package/build/lib/commands/actions.d.ts +6 -224
- package/build/lib/commands/actions.d.ts.map +1 -1
- package/build/lib/commands/actions.js +306 -405
- package/build/lib/commands/actions.js.map +1 -1
- package/build/lib/commands/alert.d.ts +7 -9
- package/build/lib/commands/alert.d.ts.map +1 -1
- package/build/lib/commands/alert.js +24 -18
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/app-management.d.ts +7 -313
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +135 -293
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/context.d.ts +8 -92
- package/build/lib/commands/context.d.ts.map +1 -1
- package/build/lib/commands/context.js +381 -439
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/element.d.ts +8 -35
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js +153 -136
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/emu-console.d.ts +6 -48
- package/build/lib/commands/emu-console.d.ts.map +1 -1
- package/build/lib/commands/emu-console.js +19 -34
- package/build/lib/commands/emu-console.js.map +1 -1
- package/build/lib/commands/execute.d.ts +6 -5
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +77 -66
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-actions.d.ts +7 -128
- package/build/lib/commands/file-actions.d.ts.map +1 -1
- package/build/lib/commands/file-actions.js +183 -219
- package/build/lib/commands/file-actions.js.map +1 -1
- package/build/lib/commands/find.d.ts +8 -12
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +19 -23
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +9 -132
- package/build/lib/commands/general.d.ts.map +1 -1
- package/build/lib/commands/general.js +281 -312
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/ime.d.ts +7 -10
- package/build/lib/commands/ime.d.ts.map +1 -1
- package/build/lib/commands/ime.js +47 -35
- package/build/lib/commands/ime.js.map +1 -1
- package/build/lib/commands/index.d.ts +27 -2
- package/build/lib/commands/index.d.ts.map +1 -1
- package/build/lib/commands/index.js +41 -19
- package/build/lib/commands/index.js.map +1 -1
- package/build/lib/commands/intent.d.ts +7 -417
- package/build/lib/commands/intent.d.ts.map +1 -1
- package/build/lib/commands/intent.js +104 -216
- package/build/lib/commands/intent.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts +6 -5
- package/build/lib/commands/keyboard.d.ts.map +1 -1
- package/build/lib/commands/keyboard.js +16 -8
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/log.d.ts +7 -44
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +146 -108
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/media-projection.d.ts +7 -143
- package/build/lib/commands/media-projection.d.ts.map +1 -1
- package/build/lib/commands/media-projection.js +113 -140
- package/build/lib/commands/media-projection.js.map +1 -1
- package/build/lib/commands/mixins.d.ts +740 -0
- package/build/lib/commands/mixins.d.ts.map +1 -0
- package/build/lib/commands/mixins.js +19 -0
- package/build/lib/commands/mixins.js.map +1 -0
- package/build/lib/commands/network.d.ts +7 -138
- package/build/lib/commands/network.d.ts.map +1 -1
- package/build/lib/commands/network.js +212 -254
- package/build/lib/commands/network.js.map +1 -1
- package/build/lib/commands/performance.d.ts +24 -70
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +144 -100
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts +8 -92
- package/build/lib/commands/permissions.d.ts.map +1 -1
- package/build/lib/commands/permissions.js +75 -87
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts +7 -193
- package/build/lib/commands/recordscreen.d.ts.map +1 -1
- package/build/lib/commands/recordscreen.js +151 -182
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/shell.d.ts +7 -7
- package/build/lib/commands/shell.d.ts.map +1 -1
- package/build/lib/commands/shell.js +40 -33
- package/build/lib/commands/shell.js.map +1 -1
- package/build/lib/commands/streamscreen.d.ts +9 -103
- package/build/lib/commands/streamscreen.d.ts.map +1 -1
- package/build/lib/commands/streamscreen.js +261 -218
- package/build/lib/commands/streamscreen.js.map +1 -1
- package/build/lib/commands/system-bars.d.ts +22 -90
- package/build/lib/commands/system-bars.d.ts.map +1 -1
- package/build/lib/commands/system-bars.js +76 -74
- package/build/lib/commands/system-bars.js.map +1 -1
- package/build/lib/commands/touch.d.ts +10 -29
- package/build/lib/commands/touch.d.ts.map +1 -1
- package/build/lib/commands/touch.js +301 -285
- package/build/lib/commands/touch.js.map +1 -1
- package/build/lib/commands/types.d.ts +978 -0
- package/build/lib/commands/types.d.ts.map +1 -0
- package/build/lib/commands/types.js +3 -0
- package/build/lib/commands/types.js.map +1 -0
- package/build/lib/constraints.d.ts +291 -0
- package/build/lib/constraints.d.ts.map +1 -0
- package/build/lib/{desired-caps.js → constraints.js} +103 -102
- package/build/lib/constraints.js.map +1 -0
- package/build/lib/driver.d.ts +68 -37
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +123 -80
- package/build/lib/driver.js.map +1 -1
- package/build/lib/helpers/android.d.ts +164 -0
- package/build/lib/helpers/android.d.ts.map +1 -0
- package/build/lib/helpers/android.js +819 -0
- package/build/lib/helpers/android.js.map +1 -0
- package/build/lib/helpers/index.d.ts +7 -0
- package/build/lib/helpers/index.d.ts.map +1 -0
- package/build/lib/helpers/index.js +29 -0
- package/build/lib/helpers/index.js.map +1 -0
- package/build/lib/helpers/types.d.ts +121 -0
- package/build/lib/helpers/types.d.ts.map +1 -0
- package/build/lib/helpers/types.js +3 -0
- package/build/lib/helpers/types.js.map +1 -0
- package/build/lib/helpers/unlock.d.ts +32 -0
- package/build/lib/helpers/unlock.d.ts.map +1 -0
- package/build/lib/helpers/unlock.js +273 -0
- package/build/lib/helpers/unlock.js.map +1 -0
- package/build/lib/helpers/webview.d.ts +74 -0
- package/build/lib/helpers/webview.d.ts.map +1 -0
- package/build/lib/helpers/webview.js +421 -0
- package/build/lib/helpers/webview.js.map +1 -0
- package/build/lib/index.d.ts +9 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +37 -0
- package/build/lib/index.js.map +1 -0
- package/build/lib/method-map.d.ts +0 -8
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js +63 -74
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/stubs.d.ts +0 -1
- package/build/lib/stubs.d.ts.map +1 -1
- package/build/lib/stubs.js +1 -0
- package/build/lib/stubs.js.map +1 -1
- package/build/lib/utils.d.ts +1 -1
- package/build/lib/utils.d.ts.map +1 -1
- package/lib/commands/actions.js +351 -464
- package/lib/commands/alert.js +27 -17
- package/lib/commands/app-management.js +156 -314
- package/lib/commands/context.js +457 -441
- package/lib/commands/element.js +201 -157
- package/lib/commands/emu-console.js +25 -45
- package/lib/commands/execute.js +106 -90
- package/lib/commands/file-actions.js +222 -240
- package/lib/commands/find.ts +103 -0
- package/lib/commands/general.js +327 -339
- package/lib/commands/ime.js +50 -34
- package/lib/commands/{index.js → index.ts} +20 -24
- package/lib/commands/intent.js +108 -249
- package/lib/commands/keyboard.js +20 -8
- package/lib/commands/log.js +172 -116
- package/lib/commands/media-projection.js +134 -161
- package/lib/commands/mixins.ts +966 -0
- package/lib/commands/network.js +252 -281
- package/lib/commands/performance.js +203 -132
- package/lib/commands/permissions.js +108 -109
- package/lib/commands/recordscreen.js +212 -209
- package/lib/commands/shell.js +51 -40
- package/lib/commands/streamscreen.js +355 -289
- package/lib/commands/system-bars.js +92 -83
- package/lib/commands/touch.js +357 -294
- package/lib/commands/types.ts +1097 -0
- package/lib/{desired-caps.js → constraints.ts} +106 -103
- package/lib/{driver.js → driver.ts} +278 -132
- package/lib/helpers/android.ts +1143 -0
- package/lib/helpers/index.ts +6 -0
- package/lib/helpers/types.ts +134 -0
- package/lib/helpers/unlock.ts +329 -0
- package/lib/helpers/webview.ts +582 -0
- package/lib/index.ts +18 -0
- package/lib/method-map.js +87 -98
- package/lib/stubs.ts +0 -1
- package/package.json +26 -19
- package/build/index.js +0 -51
- package/build/lib/android-helpers.d.ts +0 -136
- package/build/lib/android-helpers.d.ts.map +0 -1
- package/build/lib/android-helpers.js +0 -855
- package/build/lib/android-helpers.js.map +0 -1
- package/build/lib/commands/coverage.d.ts +0 -5
- package/build/lib/commands/coverage.d.ts.map +0 -1
- package/build/lib/commands/coverage.js +0 -19
- package/build/lib/commands/coverage.js.map +0 -1
- package/build/lib/desired-caps.d.ts +0 -353
- package/build/lib/desired-caps.d.ts.map +0 -1
- package/build/lib/desired-caps.js.map +0 -1
- package/build/lib/unlock-helpers.d.ts +0 -38
- package/build/lib/unlock-helpers.d.ts.map +0 -1
- package/build/lib/unlock-helpers.js +0 -266
- package/build/lib/unlock-helpers.js.map +0 -1
- package/build/lib/webview-helpers.d.ts +0 -224
- package/build/lib/webview-helpers.d.ts.map +0 -1
- package/build/lib/webview-helpers.js +0 -528
- package/build/lib/webview-helpers.js.map +0 -1
- package/index.js +0 -24
- package/lib/android-helpers.js +0 -983
- package/lib/commands/coverage.js +0 -18
- package/lib/commands/find.js +0 -82
- package/lib/unlock-helpers.js +0 -278
- package/lib/webview-helpers.js +0 -602
|
@@ -0,0 +1,819 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.prepareAvdArgs = exports.ensureNetworkSpeed = exports.SETTINGS_HELPER_PKG_ID = exports.APP_STATE = exports.helpers = void 0;
|
|
30
|
+
const support_1 = require("@appium/support");
|
|
31
|
+
const appium_adb_1 = require("appium-adb");
|
|
32
|
+
const asyncbox_1 = require("asyncbox");
|
|
33
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
34
|
+
const io_appium_settings_1 = require("io.appium.settings");
|
|
35
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
36
|
+
const node_os_1 = require("node:os");
|
|
37
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
38
|
+
const semver_1 = __importDefault(require("semver"));
|
|
39
|
+
const bootstrap_1 = __importDefault(require("../bootstrap"));
|
|
40
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
41
|
+
const unlock_1 = __importStar(require("./unlock"));
|
|
42
|
+
const MOCK_APP_IDS_STORE = '/data/local/tmp/mock_apps.json';
|
|
43
|
+
const PACKAGE_INSTALL_TIMEOUT_MS = 90000;
|
|
44
|
+
const HELPER_APP_INSTALL_RETRIES = 3;
|
|
45
|
+
const HELPER_APP_INSTALL_RETRY_DELAY_MS = 5000;
|
|
46
|
+
// https://cs.chromium.org/chromium/src/chrome/browser/devtools/device/android_device_info_query.cc
|
|
47
|
+
const CHROME_BROWSER_PACKAGE_ACTIVITY = {
|
|
48
|
+
chrome: {
|
|
49
|
+
pkg: 'com.android.chrome',
|
|
50
|
+
activity: 'com.google.android.apps.chrome.Main',
|
|
51
|
+
},
|
|
52
|
+
chromium: {
|
|
53
|
+
pkg: 'org.chromium.chrome.shell',
|
|
54
|
+
activity: '.ChromeShellActivity',
|
|
55
|
+
},
|
|
56
|
+
chromebeta: {
|
|
57
|
+
pkg: 'com.chrome.beta',
|
|
58
|
+
activity: 'com.google.android.apps.chrome.Main',
|
|
59
|
+
},
|
|
60
|
+
browser: {
|
|
61
|
+
pkg: 'com.android.browser',
|
|
62
|
+
activity: 'com.android.browser.BrowserActivity',
|
|
63
|
+
},
|
|
64
|
+
'chromium-browser': {
|
|
65
|
+
pkg: 'org.chromium.chrome',
|
|
66
|
+
activity: 'com.google.android.apps.chrome.Main',
|
|
67
|
+
},
|
|
68
|
+
'chromium-webview': {
|
|
69
|
+
pkg: 'org.chromium.webview_shell',
|
|
70
|
+
activity: 'org.chromium.webview_shell.WebViewBrowserActivity',
|
|
71
|
+
},
|
|
72
|
+
default: {
|
|
73
|
+
pkg: 'com.android.chrome',
|
|
74
|
+
activity: 'com.google.android.apps.chrome.Main',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const SETTINGS_HELPER_PKG_ID = 'io.appium.settings';
|
|
78
|
+
exports.SETTINGS_HELPER_PKG_ID = SETTINGS_HELPER_PKG_ID;
|
|
79
|
+
const SETTING_NOTIFICATIONS_LISTENER_SERVICE = `${SETTINGS_HELPER_PKG_ID}/.NLService`;
|
|
80
|
+
const EMULATOR_PATTERN = /\bemulator\b/i;
|
|
81
|
+
// These constants are in sync with
|
|
82
|
+
// https://developer.apple.com/documentation/xctest/xcuiapplicationstate/xcuiapplicationstaterunningbackground?language=objc
|
|
83
|
+
const APP_STATE = {
|
|
84
|
+
NOT_INSTALLED: 0,
|
|
85
|
+
NOT_RUNNING: 1,
|
|
86
|
+
RUNNING_IN_BACKGROUND: 3,
|
|
87
|
+
RUNNING_IN_FOREGROUND: 4,
|
|
88
|
+
};
|
|
89
|
+
exports.APP_STATE = APP_STATE;
|
|
90
|
+
function ensureNetworkSpeed(adb, networkSpeed) {
|
|
91
|
+
if (networkSpeed.toUpperCase() in adb.NETWORK_SPEED) {
|
|
92
|
+
return networkSpeed;
|
|
93
|
+
}
|
|
94
|
+
logger_1.default.warn(`Wrong network speed param '${networkSpeed}', using default: ${adb.NETWORK_SPEED.FULL}. ` +
|
|
95
|
+
`Supported values: ${lodash_1.default.values(adb.NETWORK_SPEED)}`);
|
|
96
|
+
return adb.NETWORK_SPEED.FULL;
|
|
97
|
+
}
|
|
98
|
+
exports.ensureNetworkSpeed = ensureNetworkSpeed;
|
|
99
|
+
function prepareAvdArgs(adb, opts) {
|
|
100
|
+
const { networkSpeed, isHeadless, avdArgs } = opts;
|
|
101
|
+
const result = [];
|
|
102
|
+
if (avdArgs) {
|
|
103
|
+
if (lodash_1.default.isArray(avdArgs)) {
|
|
104
|
+
result.push(...avdArgs);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
result.push(...support_1.util.shellParse(`${avdArgs}`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (networkSpeed) {
|
|
111
|
+
result.push('-netspeed', ensureNetworkSpeed(adb, networkSpeed));
|
|
112
|
+
}
|
|
113
|
+
if (isHeadless) {
|
|
114
|
+
result.push('-no-window');
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
exports.prepareAvdArgs = prepareAvdArgs;
|
|
119
|
+
function toCredentialType(unlockType) {
|
|
120
|
+
const result = {
|
|
121
|
+
[unlock_1.PIN_UNLOCK]: 'pin',
|
|
122
|
+
[unlock_1.PIN_UNLOCK_KEY_EVENT]: 'pin',
|
|
123
|
+
[unlock_1.PASSWORD_UNLOCK]: 'password',
|
|
124
|
+
[unlock_1.PATTERN_UNLOCK]: 'pattern',
|
|
125
|
+
}[unlockType];
|
|
126
|
+
if (result) {
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
throw new Error(`Unlock type '${unlockType}' is not known`);
|
|
130
|
+
}
|
|
131
|
+
const AndroidHelpers = {
|
|
132
|
+
async createBaseADB(opts) {
|
|
133
|
+
// filter out any unwanted options sent in
|
|
134
|
+
// this list should be updated as ADB takes more arguments
|
|
135
|
+
const { adbPort, suppressKillServer, remoteAdbHost, clearDeviceLogsOnStart, adbExecTimeout, useKeystore, keystorePath, keystorePassword, keyAlias, keyPassword, remoteAppsCacheLimit, buildToolsVersion, allowOfflineDevices, allowDelayAdb, } = opts ?? {};
|
|
136
|
+
return await appium_adb_1.ADB.createADB({
|
|
137
|
+
adbPort,
|
|
138
|
+
suppressKillServer,
|
|
139
|
+
remoteAdbHost,
|
|
140
|
+
clearDeviceLogsOnStart,
|
|
141
|
+
adbExecTimeout,
|
|
142
|
+
useKeystore,
|
|
143
|
+
keystorePath,
|
|
144
|
+
keystorePassword,
|
|
145
|
+
keyAlias,
|
|
146
|
+
keyPassword,
|
|
147
|
+
remoteAppsCacheLimit,
|
|
148
|
+
buildToolsVersion,
|
|
149
|
+
allowOfflineDevices,
|
|
150
|
+
allowDelayAdb,
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
async prepareEmulator(adb, opts) {
|
|
154
|
+
const { avd, avdEnv: env, language, locale: country, avdLaunchTimeout: launchTimeout, avdReadyTimeout: readyTimeout, } = opts;
|
|
155
|
+
if (!avd) {
|
|
156
|
+
throw new Error('Cannot launch AVD without AVD name');
|
|
157
|
+
}
|
|
158
|
+
const avdName = avd.replace('@', '');
|
|
159
|
+
let isEmulatorRunning = true;
|
|
160
|
+
try {
|
|
161
|
+
await adb.getRunningAVDWithRetry(avdName, 5000);
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
logger_1.default.debug(`Emulator '${avdName}' is not running: ${e.message}`);
|
|
165
|
+
isEmulatorRunning = false;
|
|
166
|
+
}
|
|
167
|
+
const args = prepareAvdArgs(adb, opts);
|
|
168
|
+
if (isEmulatorRunning) {
|
|
169
|
+
if (args.includes('-wipe-data')) {
|
|
170
|
+
logger_1.default.debug(`Killing '${avdName}' because it needs to be wiped at start.`);
|
|
171
|
+
await adb.killEmulator(avdName);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
logger_1.default.debug('Not launching AVD because it is already running.');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
await adb.launchAVD(avd, {
|
|
179
|
+
args,
|
|
180
|
+
env,
|
|
181
|
+
language,
|
|
182
|
+
country,
|
|
183
|
+
launchTimeout,
|
|
184
|
+
readyTimeout,
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
async ensureDeviceLocale(adb, language, country, script) {
|
|
188
|
+
if (!lodash_1.default.isString(language) && !lodash_1.default.isString(country)) {
|
|
189
|
+
logger_1.default.warn(`setDeviceLanguageCountry requires language or country.`);
|
|
190
|
+
logger_1.default.warn(`Got language: '${language}' and country: '${country}'`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
await adb.setDeviceLanguageCountry(language, country, script);
|
|
194
|
+
if (!(await adb.ensureCurrentLocale(language, country, script))) {
|
|
195
|
+
const message = script
|
|
196
|
+
? `language: ${language}, country: ${country} and script: ${script}`
|
|
197
|
+
: `language: ${language} and country: ${country}`;
|
|
198
|
+
throw new Error(`Failed to set ${message}`);
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
async getDeviceInfoFromCaps(opts) {
|
|
202
|
+
// we can create a throwaway ADB instance here, so there is no dependency
|
|
203
|
+
// on instantiating on earlier (at this point, we have no udid)
|
|
204
|
+
// we can only use this ADB object for commands that would not be confused
|
|
205
|
+
// if multiple devices are connected
|
|
206
|
+
const adb = await AndroidHelpers.createBaseADB(opts);
|
|
207
|
+
let udid = opts?.udid;
|
|
208
|
+
let emPort;
|
|
209
|
+
// a specific avd name was given. try to initialize with that
|
|
210
|
+
if (opts?.avd) {
|
|
211
|
+
await AndroidHelpers.prepareEmulator(adb, opts);
|
|
212
|
+
udid = adb.curDeviceId;
|
|
213
|
+
emPort = adb.emulatorPort;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// no avd given. lets try whatever's plugged in devices/emulators
|
|
217
|
+
logger_1.default.info('Retrieving device list');
|
|
218
|
+
const devices = await adb.getDevicesWithRetry();
|
|
219
|
+
// udid was given, lets try to init with that device
|
|
220
|
+
if (udid) {
|
|
221
|
+
if (!lodash_1.default.includes(lodash_1.default.map(devices, 'udid'), udid)) {
|
|
222
|
+
logger_1.default.errorAndThrow(`Device ${udid} was not in the list of connected devices`);
|
|
223
|
+
}
|
|
224
|
+
emPort = adb.getPortFromEmulatorString(udid);
|
|
225
|
+
}
|
|
226
|
+
else if (opts?.platformVersion) {
|
|
227
|
+
opts.platformVersion = `${opts.platformVersion}`.trim();
|
|
228
|
+
// a platform version was given. lets try to find a device with the same os
|
|
229
|
+
const platformVersion = semver_1.default.coerce(opts.platformVersion) || opts.platformVersion;
|
|
230
|
+
logger_1.default.info(`Looking for a device with Android '${platformVersion}'`);
|
|
231
|
+
// in case we fail to find something, give the user a useful log that has
|
|
232
|
+
// the device udids and os versions so they know what's available
|
|
233
|
+
const availDevices = [];
|
|
234
|
+
let partialMatchCandidate;
|
|
235
|
+
// first try started devices/emulators
|
|
236
|
+
for (const device of devices) {
|
|
237
|
+
// direct adb calls to the specific device
|
|
238
|
+
adb.setDeviceId(device.udid);
|
|
239
|
+
const rawDeviceOS = await adb.getPlatformVersion();
|
|
240
|
+
// The device OS could either be a number, like `6.0`
|
|
241
|
+
// or an abbreviation, like `R`
|
|
242
|
+
availDevices.push(`${device.udid} (${rawDeviceOS})`);
|
|
243
|
+
const deviceOS = semver_1.default.coerce(rawDeviceOS) || rawDeviceOS;
|
|
244
|
+
if (!deviceOS) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const semverPV = platformVersion;
|
|
248
|
+
const semverDO = deviceOS;
|
|
249
|
+
const bothVersionsCanBeCoerced = semver_1.default.valid(deviceOS) && semver_1.default.valid(platformVersion);
|
|
250
|
+
const bothVersionsAreStrings = lodash_1.default.isString(deviceOS) && lodash_1.default.isString(platformVersion);
|
|
251
|
+
if ((bothVersionsCanBeCoerced && semverDO.version === semverPV.version) ||
|
|
252
|
+
(bothVersionsAreStrings && lodash_1.default.toLower(deviceOS) === lodash_1.default.toLower(platformVersion))) {
|
|
253
|
+
// Got an exact match - proceed immediately
|
|
254
|
+
udid = device.udid;
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
else if (!bothVersionsCanBeCoerced) {
|
|
258
|
+
// There is no point to check for partial match if either of version numbers is not coercible
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (((!lodash_1.default.includes(opts.platformVersion, '.') && semverPV.major === semverDO.major) ||
|
|
262
|
+
(semverPV.major === semverDO.major && semverPV.minor === semverDO.minor)) &&
|
|
263
|
+
// Got a partial match - make sure we consider the most recent
|
|
264
|
+
// device version available on the host system
|
|
265
|
+
((partialMatchCandidate && semver_1.default.gt(deviceOS, lodash_1.default.values(partialMatchCandidate)[0])) ||
|
|
266
|
+
!partialMatchCandidate)) {
|
|
267
|
+
partialMatchCandidate = { [device.udid]: deviceOS };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!udid && partialMatchCandidate) {
|
|
271
|
+
udid = lodash_1.default.keys(partialMatchCandidate)[0];
|
|
272
|
+
adb.setDeviceId(udid);
|
|
273
|
+
}
|
|
274
|
+
if (!udid) {
|
|
275
|
+
// we couldn't find anything! quit
|
|
276
|
+
logger_1.default.errorAndThrow(`Unable to find an active device or emulator ` +
|
|
277
|
+
`with OS ${opts.platformVersion}. The following are available: ` +
|
|
278
|
+
availDevices.join(', '));
|
|
279
|
+
throw new Error(); // unreachable; for TS
|
|
280
|
+
}
|
|
281
|
+
emPort = adb.getPortFromEmulatorString(udid);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// a udid was not given, grab the first device we see
|
|
285
|
+
udid = devices[0].udid;
|
|
286
|
+
emPort = adb.getPortFromEmulatorString(udid);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
logger_1.default.info(`Using device: ${udid}`);
|
|
290
|
+
return { udid: udid, emPort: emPort };
|
|
291
|
+
},
|
|
292
|
+
async createADB(opts) {
|
|
293
|
+
// @ts-expect-error do not put arbitrary properties on opts
|
|
294
|
+
const { udid, emPort } = opts ?? {};
|
|
295
|
+
const adb = await AndroidHelpers.createBaseADB(opts);
|
|
296
|
+
adb.setDeviceId(udid);
|
|
297
|
+
if (emPort) {
|
|
298
|
+
adb.setEmulatorPort(emPort);
|
|
299
|
+
}
|
|
300
|
+
return adb;
|
|
301
|
+
},
|
|
302
|
+
validatePackageActivityNames(opts) {
|
|
303
|
+
for (const key of ['appPackage', 'appActivity', 'appWaitPackage', 'appWaitActivity']) {
|
|
304
|
+
const name = opts[key];
|
|
305
|
+
if (!name) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const match = /([^\w.*,])+/.exec(String(name));
|
|
309
|
+
if (!match) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
logger_1.default.warn(`Capability '${key}' is expected to only include latin letters, digits, underscore, dot, comma and asterisk characters.`);
|
|
313
|
+
logger_1.default.warn(`Current value '${name}' has non-matching character at index ${match.index}: '${String(name).substring(0, match.index + 1)}'`);
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
async getLaunchInfo(adb, opts) {
|
|
317
|
+
if (!opts.app) {
|
|
318
|
+
logger_1.default.warn('No app sent in, not parsing package/activity');
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
let { appPackage, appActivity, appWaitPackage, appWaitActivity } = opts;
|
|
322
|
+
const { app } = opts;
|
|
323
|
+
AndroidHelpers.validatePackageActivityNames(opts);
|
|
324
|
+
if (appPackage && appActivity) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
logger_1.default.debug('Parsing package and activity from app manifest');
|
|
328
|
+
const { apkPackage, apkActivity } = await adb.packageAndLaunchActivityFromManifest(app);
|
|
329
|
+
if (apkPackage && !appPackage) {
|
|
330
|
+
appPackage = apkPackage;
|
|
331
|
+
}
|
|
332
|
+
if (!appWaitPackage) {
|
|
333
|
+
appWaitPackage = appPackage;
|
|
334
|
+
}
|
|
335
|
+
if (apkActivity && !appActivity) {
|
|
336
|
+
appActivity = apkActivity;
|
|
337
|
+
}
|
|
338
|
+
if (!appWaitActivity) {
|
|
339
|
+
appWaitActivity = appActivity;
|
|
340
|
+
}
|
|
341
|
+
logger_1.default.debug(`Parsed package and activity are: ${apkPackage}/${apkActivity}`);
|
|
342
|
+
return { appPackage, appWaitPackage, appActivity, appWaitActivity };
|
|
343
|
+
},
|
|
344
|
+
async resetApp(adb, opts) {
|
|
345
|
+
const { app, appPackage, fastReset, fullReset, androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS, autoGrantPermissions, allowTestPackages, } = opts ?? {};
|
|
346
|
+
if (!appPackage) {
|
|
347
|
+
throw new Error("'appPackage' option is required");
|
|
348
|
+
}
|
|
349
|
+
const isInstalled = await adb.isAppInstalled(appPackage);
|
|
350
|
+
if (isInstalled) {
|
|
351
|
+
try {
|
|
352
|
+
await adb.forceStop(appPackage);
|
|
353
|
+
}
|
|
354
|
+
catch (ign) { }
|
|
355
|
+
// fullReset has priority over fastReset
|
|
356
|
+
if (!fullReset && fastReset) {
|
|
357
|
+
const output = await adb.clear(appPackage);
|
|
358
|
+
if (lodash_1.default.isString(output) && output.toLowerCase().includes('failed')) {
|
|
359
|
+
throw new Error(`Cannot clear the application data of '${appPackage}'. Original error: ${output}`);
|
|
360
|
+
}
|
|
361
|
+
// executing `shell pm clear` resets previously assigned application permissions as well
|
|
362
|
+
if (autoGrantPermissions) {
|
|
363
|
+
try {
|
|
364
|
+
await adb.grantAllPermissions(appPackage);
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
logger_1.default.error(`Unable to grant permissions requested. Original error: ${error.message}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
logger_1.default.debug(`Performed fast reset on the installed '${appPackage}' application (stop and clear)`);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (!app) {
|
|
375
|
+
throw new Error(`Either provide 'app' option to install '${appPackage}' or ` +
|
|
376
|
+
`consider setting 'noReset' to 'true' if '${appPackage}' is supposed to be preinstalled.`);
|
|
377
|
+
}
|
|
378
|
+
logger_1.default.debug(`Running full reset on '${appPackage}' (reinstall)`);
|
|
379
|
+
if (isInstalled) {
|
|
380
|
+
await adb.uninstallApk(appPackage);
|
|
381
|
+
}
|
|
382
|
+
await adb.install(app, {
|
|
383
|
+
grantPermissions: autoGrantPermissions,
|
|
384
|
+
timeout: androidInstallTimeout,
|
|
385
|
+
allowTestPackages,
|
|
386
|
+
});
|
|
387
|
+
},
|
|
388
|
+
async installApk(adb, opts) {
|
|
389
|
+
const { app, appPackage, fastReset, fullReset, androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS, autoGrantPermissions, allowTestPackages, enforceAppInstall, } = opts ?? {};
|
|
390
|
+
if (!app || !appPackage) {
|
|
391
|
+
throw new Error("'app' and 'appPackage' options are required");
|
|
392
|
+
}
|
|
393
|
+
if (fullReset) {
|
|
394
|
+
await AndroidHelpers.resetApp(adb, opts);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const { appState, wasUninstalled } = await adb.installOrUpgrade(app, appPackage, {
|
|
398
|
+
grantPermissions: autoGrantPermissions,
|
|
399
|
+
timeout: androidInstallTimeout,
|
|
400
|
+
allowTestPackages,
|
|
401
|
+
enforceCurrentBuild: enforceAppInstall,
|
|
402
|
+
});
|
|
403
|
+
// There is no need to reset the newly installed app
|
|
404
|
+
const isInstalledOverExistingApp = !wasUninstalled && appState !== adb.APP_INSTALL_STATE.NOT_INSTALLED;
|
|
405
|
+
if (fastReset && isInstalledOverExistingApp) {
|
|
406
|
+
logger_1.default.info(`Performing fast reset on '${appPackage}'`);
|
|
407
|
+
await AndroidHelpers.resetApp(adb, opts);
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
async installOtherApks(otherApps, adb, opts) {
|
|
411
|
+
const { androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS, autoGrantPermissions, allowTestPackages, } = opts;
|
|
412
|
+
// Install all of the APK's asynchronously
|
|
413
|
+
await bluebird_1.default.all(otherApps.map((otherApp) => {
|
|
414
|
+
logger_1.default.debug(`Installing app: ${otherApp}`);
|
|
415
|
+
return adb.installOrUpgrade(otherApp, undefined, {
|
|
416
|
+
grantPermissions: autoGrantPermissions,
|
|
417
|
+
timeout: androidInstallTimeout,
|
|
418
|
+
allowTestPackages,
|
|
419
|
+
});
|
|
420
|
+
}));
|
|
421
|
+
},
|
|
422
|
+
async uninstallOtherPackages(adb, appPackages, filterPackages = []) {
|
|
423
|
+
if (appPackages.includes('*')) {
|
|
424
|
+
logger_1.default.debug('Uninstall third party packages');
|
|
425
|
+
appPackages = await AndroidHelpers.getThirdPartyPackages(adb, filterPackages);
|
|
426
|
+
}
|
|
427
|
+
logger_1.default.debug(`Uninstalling packages: ${appPackages}`);
|
|
428
|
+
await bluebird_1.default.all(appPackages.map((appPackage) => adb.uninstallApk(appPackage)));
|
|
429
|
+
},
|
|
430
|
+
async getThirdPartyPackages(adb, filterPackages = []) {
|
|
431
|
+
try {
|
|
432
|
+
const packagesString = await adb.shell(['pm', 'list', 'packages', '-3']);
|
|
433
|
+
const appPackagesArray = packagesString
|
|
434
|
+
.trim()
|
|
435
|
+
.replace(/package:/g, '')
|
|
436
|
+
.split(node_os_1.EOL);
|
|
437
|
+
logger_1.default.debug(`'${appPackagesArray}' filtered with '${filterPackages}'`);
|
|
438
|
+
return lodash_1.default.difference(appPackagesArray, filterPackages);
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
logger_1.default.warn(`Unable to get packages with 'adb shell pm list packages -3': ${err.message}`);
|
|
442
|
+
return [];
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
async initUnicodeKeyboard(adb) {
|
|
446
|
+
logger_1.default.debug('Enabling Unicode keyboard support');
|
|
447
|
+
// get the default IME so we can return back to it later if we want
|
|
448
|
+
const defaultIME = await adb.defaultIME();
|
|
449
|
+
logger_1.default.debug(`Unsetting previous IME ${defaultIME}`);
|
|
450
|
+
const appiumIME = `${SETTINGS_HELPER_PKG_ID}/.UnicodeIME`;
|
|
451
|
+
logger_1.default.debug(`Setting IME to '${appiumIME}'`);
|
|
452
|
+
await adb.enableIME(appiumIME);
|
|
453
|
+
await adb.setIME(appiumIME);
|
|
454
|
+
return defaultIME;
|
|
455
|
+
},
|
|
456
|
+
async setMockLocationApp(adb, app) {
|
|
457
|
+
try {
|
|
458
|
+
if ((await adb.getApiLevel()) < 23) {
|
|
459
|
+
await adb.shell(['settings', 'put', 'secure', 'mock_location', '1']);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
await adb.shell(['appops', 'set', app, 'android:mock_location', 'allow']);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch (err) {
|
|
466
|
+
logger_1.default.warn(`Unable to set mock location for app '${app}': ${err.message}`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
let pkgIds = [];
|
|
471
|
+
if (await adb.fileExists(MOCK_APP_IDS_STORE)) {
|
|
472
|
+
try {
|
|
473
|
+
pkgIds = JSON.parse(await adb.shell(['cat', MOCK_APP_IDS_STORE]));
|
|
474
|
+
}
|
|
475
|
+
catch (ign) { }
|
|
476
|
+
}
|
|
477
|
+
if (pkgIds.includes(app)) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
pkgIds.push(app);
|
|
481
|
+
const tmpRoot = await support_1.tempDir.openDir();
|
|
482
|
+
const srcPath = node_path_1.default.posix.join(tmpRoot, node_path_1.default.posix.basename(MOCK_APP_IDS_STORE));
|
|
483
|
+
try {
|
|
484
|
+
await support_1.fs.writeFile(srcPath, JSON.stringify(pkgIds), 'utf8');
|
|
485
|
+
await adb.push(srcPath, MOCK_APP_IDS_STORE);
|
|
486
|
+
}
|
|
487
|
+
finally {
|
|
488
|
+
await support_1.fs.rimraf(tmpRoot);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
logger_1.default.warn(`Unable to persist mock location app id '${app}': ${e.message}`);
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
async resetMockLocation(adb) {
|
|
496
|
+
try {
|
|
497
|
+
if ((await adb.getApiLevel()) < 23) {
|
|
498
|
+
await adb.shell(['settings', 'put', 'secure', 'mock_location', '0']);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const thirdPartyPkgIdsPromise = AndroidHelpers.getThirdPartyPackages(adb);
|
|
502
|
+
let pkgIds = [];
|
|
503
|
+
if (await adb.fileExists(MOCK_APP_IDS_STORE)) {
|
|
504
|
+
try {
|
|
505
|
+
pkgIds = JSON.parse(await adb.shell(['cat', MOCK_APP_IDS_STORE]));
|
|
506
|
+
}
|
|
507
|
+
catch (ign) { }
|
|
508
|
+
}
|
|
509
|
+
const thirdPartyPkgIds = await thirdPartyPkgIdsPromise;
|
|
510
|
+
// Only include currently installed packages
|
|
511
|
+
const resultPkgs = lodash_1.default.intersection(pkgIds, thirdPartyPkgIds);
|
|
512
|
+
if (lodash_1.default.size(resultPkgs) <= 1) {
|
|
513
|
+
await adb.shell([
|
|
514
|
+
'appops',
|
|
515
|
+
'set',
|
|
516
|
+
resultPkgs[0] ?? SETTINGS_HELPER_PKG_ID,
|
|
517
|
+
'android:mock_location',
|
|
518
|
+
'deny',
|
|
519
|
+
]);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
logger_1.default.debug(`Resetting mock_location permission for the following apps: ${resultPkgs}`);
|
|
523
|
+
await bluebird_1.default.all(resultPkgs.map((pkgId) => (async () => {
|
|
524
|
+
try {
|
|
525
|
+
await adb.shell(['appops', 'set', pkgId, 'android:mock_location', 'deny']);
|
|
526
|
+
}
|
|
527
|
+
catch (ign) { }
|
|
528
|
+
})()));
|
|
529
|
+
}
|
|
530
|
+
catch (err) {
|
|
531
|
+
logger_1.default.warn(`Unable to reset mock location: ${err.message}`);
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
async installHelperApp(adb, apkPath, packageId) {
|
|
535
|
+
// Sometimes adb push or adb instal take more time than expected to install an app
|
|
536
|
+
// e.g. https://github.com/appium/io.appium.settings/issues/40#issuecomment-476593174
|
|
537
|
+
await (0, asyncbox_1.retryInterval)(HELPER_APP_INSTALL_RETRIES, HELPER_APP_INSTALL_RETRY_DELAY_MS, async function retryInstallHelperApp() {
|
|
538
|
+
await adb.installOrUpgrade(apkPath, packageId, { grantPermissions: true });
|
|
539
|
+
});
|
|
540
|
+
},
|
|
541
|
+
async pushSettingsApp(adb, throwError, opts) {
|
|
542
|
+
logger_1.default.debug('Pushing settings apk to device...');
|
|
543
|
+
try {
|
|
544
|
+
await AndroidHelpers.installHelperApp(adb, io_appium_settings_1.path, SETTINGS_HELPER_PKG_ID);
|
|
545
|
+
}
|
|
546
|
+
catch (err) {
|
|
547
|
+
if (throwError) {
|
|
548
|
+
throw err;
|
|
549
|
+
}
|
|
550
|
+
logger_1.default.warn(`Ignored error while installing '${io_appium_settings_1.path}': ` +
|
|
551
|
+
`'${err.message}'. Features that rely on this helper ` +
|
|
552
|
+
'require the apk such as toggle WiFi and getting location ' +
|
|
553
|
+
'will raise an error if you try to use them.');
|
|
554
|
+
}
|
|
555
|
+
// Reinstall would stop the settings helper process anyway, so
|
|
556
|
+
// there is no need to continue if the application is still running
|
|
557
|
+
if (await adb.processExists(SETTINGS_HELPER_PKG_ID)) {
|
|
558
|
+
logger_1.default.debug(`${SETTINGS_HELPER_PKG_ID} is already running. ` +
|
|
559
|
+
`There is no need to reset its permissions.`);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const apiLevel = await adb.getApiLevel();
|
|
563
|
+
if (apiLevel >= 29) {
|
|
564
|
+
// https://github.com/appium/io.appium.settings#internal-audio--video-recording
|
|
565
|
+
try {
|
|
566
|
+
await adb.shell(['appops', 'set', SETTINGS_HELPER_PKG_ID, 'PROJECT_MEDIA', 'allow']);
|
|
567
|
+
}
|
|
568
|
+
catch (err) {
|
|
569
|
+
logger_1.default.debug(err.message);
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
await adb.shell([
|
|
573
|
+
'cmd',
|
|
574
|
+
'notification',
|
|
575
|
+
'allow_listener',
|
|
576
|
+
SETTING_NOTIFICATIONS_LISTENER_SERVICE,
|
|
577
|
+
]);
|
|
578
|
+
}
|
|
579
|
+
catch (err) {
|
|
580
|
+
logger_1.default.debug(err.message);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (apiLevel <= 23) {
|
|
584
|
+
// Android 6- devices should have granted permissions
|
|
585
|
+
// https://github.com/appium/appium/pull/11640#issuecomment-438260477
|
|
586
|
+
const perms = ['SET_ANIMATION_SCALE', 'CHANGE_CONFIGURATION', 'ACCESS_FINE_LOCATION'];
|
|
587
|
+
logger_1.default.info(`Granting permissions ${perms} to '${SETTINGS_HELPER_PKG_ID}'`);
|
|
588
|
+
await adb.grantPermissions(SETTINGS_HELPER_PKG_ID, perms.map((x) => `android.permission.${x}`));
|
|
589
|
+
}
|
|
590
|
+
// launch io.appium.settings app due to settings failing to be set
|
|
591
|
+
// if the app is not launched prior to start the session on android 7+
|
|
592
|
+
// see https://github.com/appium/appium/issues/8957
|
|
593
|
+
try {
|
|
594
|
+
await adb.requireRunningSettingsApp({
|
|
595
|
+
timeout: AndroidHelpers.isEmulator(adb, opts) ? 30000 : 5000,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
logger_1.default.debug(err);
|
|
600
|
+
if (throwError) {
|
|
601
|
+
throw err;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
async pushStrings(language, adb, opts) {
|
|
606
|
+
const remoteDir = '/data/local/tmp';
|
|
607
|
+
const stringsJson = 'strings.json';
|
|
608
|
+
const remoteFile = node_path_1.default.posix.resolve(remoteDir, stringsJson);
|
|
609
|
+
// clean up remote string.json if present
|
|
610
|
+
await adb.rimraf(remoteFile);
|
|
611
|
+
let app;
|
|
612
|
+
try {
|
|
613
|
+
app = opts.app || (await adb.pullApk(opts.appPackage, opts.tmpDir));
|
|
614
|
+
}
|
|
615
|
+
catch (err) {
|
|
616
|
+
logger_1.default.info(`Failed to pull an apk from '${opts.appPackage}' to '${opts.tmpDir}'. Original error: ${err.message}`);
|
|
617
|
+
}
|
|
618
|
+
if (lodash_1.default.isEmpty(opts.appPackage) || !(await support_1.fs.exists(app))) {
|
|
619
|
+
logger_1.default.debug(`No app or package specified. Returning empty strings`);
|
|
620
|
+
return {};
|
|
621
|
+
}
|
|
622
|
+
const stringsTmpDir = node_path_1.default.resolve(opts.tmpDir, opts.appPackage);
|
|
623
|
+
try {
|
|
624
|
+
logger_1.default.debug('Extracting strings from apk', app, language, stringsTmpDir);
|
|
625
|
+
const { apkStrings, localPath } = await adb.extractStringsFromApk(app, language, stringsTmpDir);
|
|
626
|
+
await adb.push(localPath, remoteDir);
|
|
627
|
+
return apkStrings;
|
|
628
|
+
}
|
|
629
|
+
catch (err) {
|
|
630
|
+
logger_1.default.warn(`Could not get strings, continuing anyway. Original error: ${err.message}`);
|
|
631
|
+
await adb.shell(['echo', `'{}' > ${remoteFile}`]);
|
|
632
|
+
}
|
|
633
|
+
finally {
|
|
634
|
+
await support_1.fs.rimraf(stringsTmpDir);
|
|
635
|
+
}
|
|
636
|
+
return {};
|
|
637
|
+
},
|
|
638
|
+
async unlock(driver, adb, capabilities) {
|
|
639
|
+
if (!(await adb.isScreenLocked())) {
|
|
640
|
+
logger_1.default.info('Screen already unlocked, doing nothing');
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
logger_1.default.debug('Screen is locked, trying to unlock');
|
|
644
|
+
if (!capabilities.unlockType && !capabilities.unlockKey) {
|
|
645
|
+
logger_1.default.info(`Neither 'unlockType' nor 'unlockKey' capability is provided. ` +
|
|
646
|
+
`Assuming the device is locked with a simple lock screen.`);
|
|
647
|
+
await adb.dismissKeyguard();
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const { unlockType, unlockKey, unlockStrategy, unlockSuccessTimeout } = unlock_1.default.validateUnlockCapabilities(capabilities);
|
|
651
|
+
if (unlockKey &&
|
|
652
|
+
unlockType !== unlock_1.FINGERPRINT_UNLOCK &&
|
|
653
|
+
(lodash_1.default.isNil(unlockStrategy) || lodash_1.default.toLower(unlockStrategy) === 'locksettings') &&
|
|
654
|
+
(await adb.isLockManagementSupported())) {
|
|
655
|
+
await unlock_1.default.fastUnlock(adb, {
|
|
656
|
+
credential: unlockKey,
|
|
657
|
+
credentialType: toCredentialType(unlockType),
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
const unlockMethod = {
|
|
662
|
+
[unlock_1.PIN_UNLOCK]: unlock_1.default.pinUnlock,
|
|
663
|
+
[unlock_1.PIN_UNLOCK_KEY_EVENT]: unlock_1.default.pinUnlockWithKeyEvent,
|
|
664
|
+
[unlock_1.PASSWORD_UNLOCK]: unlock_1.default.passwordUnlock,
|
|
665
|
+
[unlock_1.PATTERN_UNLOCK]: unlock_1.default.patternUnlock,
|
|
666
|
+
[unlock_1.FINGERPRINT_UNLOCK]: unlock_1.default.fingerprintUnlock,
|
|
667
|
+
}[unlockType];
|
|
668
|
+
await unlockMethod(adb, driver, capabilities);
|
|
669
|
+
}
|
|
670
|
+
await AndroidHelpers.verifyUnlock(adb, unlockSuccessTimeout);
|
|
671
|
+
},
|
|
672
|
+
async verifyUnlock(adb, timeoutMs = null) {
|
|
673
|
+
try {
|
|
674
|
+
await (0, asyncbox_1.waitForCondition)(async () => !(await adb.isScreenLocked()), {
|
|
675
|
+
waitMs: timeoutMs ?? 2000,
|
|
676
|
+
intervalMs: 500,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
catch (ign) {
|
|
680
|
+
throw new Error('The device has failed to be unlocked');
|
|
681
|
+
}
|
|
682
|
+
logger_1.default.info('The device has been successfully unlocked');
|
|
683
|
+
},
|
|
684
|
+
async initDevice(adb, opts) {
|
|
685
|
+
const { skipDeviceInitialization, locale, language, localeScript, unicodeKeyboard, disableWindowAnimation, skipUnlock, mockLocationApp, skipLogcatCapture, logcatFormat, logcatFilterSpecs, } = opts;
|
|
686
|
+
if (skipDeviceInitialization) {
|
|
687
|
+
logger_1.default.info(`'skipDeviceInitialization' is set. Skipping device initialization.`);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
if (AndroidHelpers.isEmulator(adb, opts)) {
|
|
691
|
+
// Check if the device wake up only for an emulator.
|
|
692
|
+
// It takes 1 second or so even when the device is already awake in a real device.
|
|
693
|
+
await adb.waitForDevice();
|
|
694
|
+
}
|
|
695
|
+
// pushSettingsApp required before calling ensureDeviceLocale for API Level 24+
|
|
696
|
+
// Some feature such as location/wifi are not necessary for all users,
|
|
697
|
+
// but they require the settings app. So, try to configure it while Appium
|
|
698
|
+
// does not throw error even if they fail.
|
|
699
|
+
const shouldThrowError = Boolean(language ||
|
|
700
|
+
locale ||
|
|
701
|
+
localeScript ||
|
|
702
|
+
unicodeKeyboard ||
|
|
703
|
+
disableWindowAnimation ||
|
|
704
|
+
!skipUnlock);
|
|
705
|
+
await AndroidHelpers.pushSettingsApp(adb, shouldThrowError, opts);
|
|
706
|
+
}
|
|
707
|
+
if (!AndroidHelpers.isEmulator(adb, opts)) {
|
|
708
|
+
if (mockLocationApp || lodash_1.default.isUndefined(mockLocationApp)) {
|
|
709
|
+
await AndroidHelpers.setMockLocationApp(adb, mockLocationApp || SETTINGS_HELPER_PKG_ID);
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
await AndroidHelpers.resetMockLocation(adb);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (language || locale) {
|
|
716
|
+
await AndroidHelpers.ensureDeviceLocale(adb, language, locale, localeScript);
|
|
717
|
+
}
|
|
718
|
+
if (skipLogcatCapture) {
|
|
719
|
+
logger_1.default.info(`'skipLogcatCapture' is set. Skipping starting logcat capture.`);
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
await adb.startLogcat({
|
|
723
|
+
format: logcatFormat,
|
|
724
|
+
filterSpecs: logcatFilterSpecs,
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
if (unicodeKeyboard) {
|
|
728
|
+
return await AndroidHelpers.initUnicodeKeyboard(adb);
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
removeNullProperties(obj) {
|
|
732
|
+
for (const key of lodash_1.default.keys(obj)) {
|
|
733
|
+
if (lodash_1.default.isNull(obj[key]) || lodash_1.default.isUndefined(obj[key])) {
|
|
734
|
+
delete obj[key];
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
truncateDecimals(number, digits) {
|
|
739
|
+
const multiplier = Math.pow(10, digits), adjustedNum = number * multiplier, truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
|
|
740
|
+
return truncatedNum / multiplier;
|
|
741
|
+
},
|
|
742
|
+
isChromeBrowser(browser) {
|
|
743
|
+
return lodash_1.default.includes(Object.keys(CHROME_BROWSER_PACKAGE_ACTIVITY), (browser || '').toLowerCase());
|
|
744
|
+
},
|
|
745
|
+
getChromePkg(browser) {
|
|
746
|
+
return (CHROME_BROWSER_PACKAGE_ACTIVITY[browser.toLowerCase()] || CHROME_BROWSER_PACKAGE_ACTIVITY.default);
|
|
747
|
+
},
|
|
748
|
+
async removeAllSessionWebSocketHandlers(server, sessionId) {
|
|
749
|
+
if (!server || !lodash_1.default.isFunction(server.getWebSocketHandlers)) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
const activeHandlers = await server.getWebSocketHandlers(sessionId);
|
|
753
|
+
for (const pathname of lodash_1.default.keys(activeHandlers)) {
|
|
754
|
+
await server.removeWebSocketHandler(pathname);
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
parseArray(cap) {
|
|
758
|
+
let parsedCaps;
|
|
759
|
+
try {
|
|
760
|
+
parsedCaps = JSON.parse(cap);
|
|
761
|
+
}
|
|
762
|
+
catch (ign) { }
|
|
763
|
+
if (lodash_1.default.isArray(parsedCaps)) {
|
|
764
|
+
return parsedCaps;
|
|
765
|
+
}
|
|
766
|
+
else if (lodash_1.default.isString(cap)) {
|
|
767
|
+
return [cap];
|
|
768
|
+
}
|
|
769
|
+
throw new Error(`must provide a string or JSON Array; received ${cap}`);
|
|
770
|
+
},
|
|
771
|
+
validateDesiredCaps(caps) {
|
|
772
|
+
if (caps.browserName) {
|
|
773
|
+
if (caps.app) {
|
|
774
|
+
// warn if the capabilities have both `app` and `browser, although this is common with selenium grid
|
|
775
|
+
logger_1.default.warn(`The desired capabilities should generally not include both an 'app' and a 'browserName'`);
|
|
776
|
+
}
|
|
777
|
+
if (caps.appPackage) {
|
|
778
|
+
logger_1.default.errorAndThrow(`The desired should not include both of an 'appPackage' and a 'browserName'`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (caps.uninstallOtherPackages) {
|
|
782
|
+
try {
|
|
783
|
+
AndroidHelpers.parseArray(caps.uninstallOtherPackages);
|
|
784
|
+
}
|
|
785
|
+
catch (e) {
|
|
786
|
+
logger_1.default.errorAndThrow(`Could not parse "uninstallOtherPackages" capability: ${e.message}`);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return true;
|
|
790
|
+
},
|
|
791
|
+
adjustBrowserSessionCaps(caps) {
|
|
792
|
+
const { browserName } = caps;
|
|
793
|
+
logger_1.default.info(`The current session is considered browser-based`);
|
|
794
|
+
logger_1.default.info(`Supported browser names: ${JSON.stringify(lodash_1.default.keys(CHROME_BROWSER_PACKAGE_ACTIVITY))}`);
|
|
795
|
+
if (caps.appPackage || caps.appActivity) {
|
|
796
|
+
logger_1.default.info(`Not overriding appPackage/appActivity capability values for '${browserName}' ` +
|
|
797
|
+
'because some of them have been already provided');
|
|
798
|
+
return caps;
|
|
799
|
+
}
|
|
800
|
+
const { pkg, activity } = AndroidHelpers.getChromePkg(String(browserName));
|
|
801
|
+
caps.appPackage = pkg;
|
|
802
|
+
caps.appActivity = activity;
|
|
803
|
+
logger_1.default.info(`appPackage/appActivity capabilities have been automatically set to ${pkg}/${activity} ` +
|
|
804
|
+
`for '${browserName}'`);
|
|
805
|
+
logger_1.default.info(`Consider changing the browserName to the one from the list of supported browser names ` +
|
|
806
|
+
`or provide custom appPackage/appActivity capability values if the automatically assigned ones do ` +
|
|
807
|
+
`not make sense`);
|
|
808
|
+
return caps;
|
|
809
|
+
},
|
|
810
|
+
isEmulator(adb, opts) {
|
|
811
|
+
const possibleNames = [opts?.udid, adb?.curDeviceId];
|
|
812
|
+
return !!opts?.avd || possibleNames.some((x) => EMULATOR_PATTERN.test(String(x)));
|
|
813
|
+
},
|
|
814
|
+
bootstrap: bootstrap_1.default,
|
|
815
|
+
unlocker: unlock_1.default,
|
|
816
|
+
};
|
|
817
|
+
exports.helpers = AndroidHelpers;
|
|
818
|
+
exports.default = AndroidHelpers;
|
|
819
|
+
//# sourceMappingURL=android.js.map
|