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