appium-android-driver 7.8.3 → 8.0.0

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 (261) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/build/lib/commands/app-management.d.ts +129 -5
  3. package/build/lib/commands/app-management.d.ts.map +1 -1
  4. package/build/lib/commands/app-management.js +433 -128
  5. package/build/lib/commands/app-management.js.map +1 -1
  6. package/build/lib/commands/appearance.d.ts +17 -4
  7. package/build/lib/commands/appearance.d.ts.map +1 -1
  8. package/build/lib/commands/appearance.js +32 -33
  9. package/build/lib/commands/appearance.js.map +1 -1
  10. package/build/lib/commands/context/cache.d.ts +19 -0
  11. package/build/lib/commands/context/cache.d.ts.map +1 -0
  12. package/build/lib/commands/context/cache.js +32 -0
  13. package/build/lib/commands/context/cache.js.map +1 -0
  14. package/build/lib/commands/context/exports.d.ts +141 -0
  15. package/build/lib/commands/context/exports.d.ts.map +1 -0
  16. package/build/lib/commands/context/exports.js +351 -0
  17. package/build/lib/commands/context/exports.js.map +1 -0
  18. package/build/lib/commands/context/helpers.d.ts +98 -0
  19. package/build/lib/commands/context/helpers.d.ts.map +1 -0
  20. package/build/lib/commands/context/helpers.js +715 -0
  21. package/build/lib/commands/context/helpers.js.map +1 -0
  22. package/build/lib/commands/device/common.d.ts +23 -0
  23. package/build/lib/commands/device/common.d.ts.map +1 -0
  24. package/build/lib/commands/device/common.js +230 -0
  25. package/build/lib/commands/device/common.js.map +1 -0
  26. package/build/lib/commands/device/emulator-actions.d.ts +114 -0
  27. package/build/lib/commands/device/emulator-actions.d.ts.map +1 -0
  28. package/build/lib/commands/device/emulator-actions.js +197 -0
  29. package/build/lib/commands/device/emulator-actions.js.map +1 -0
  30. package/build/lib/commands/device/emulator-console.d.ts +7 -0
  31. package/build/lib/commands/device/emulator-console.d.ts.map +1 -0
  32. package/build/lib/commands/device/emulator-console.js +24 -0
  33. package/build/lib/commands/device/emulator-console.js.map +1 -0
  34. package/build/lib/commands/device/utils.d.ts +50 -0
  35. package/build/lib/commands/device/utils.d.ts.map +1 -0
  36. package/build/lib/commands/device/utils.js +238 -0
  37. package/build/lib/commands/device/utils.js.map +1 -0
  38. package/build/lib/commands/deviceidle.d.ts +8 -5
  39. package/build/lib/commands/deviceidle.d.ts.map +1 -1
  40. package/build/lib/commands/deviceidle.js +31 -37
  41. package/build/lib/commands/deviceidle.js.map +1 -1
  42. package/build/lib/commands/element.d.ts +99 -5
  43. package/build/lib/commands/element.d.ts.map +1 -1
  44. package/build/lib/commands/element.js +152 -116
  45. package/build/lib/commands/element.js.map +1 -1
  46. package/build/lib/commands/execute.d.ts +12 -4
  47. package/build/lib/commands/execute.d.ts.map +1 -1
  48. package/build/lib/commands/execute.js +83 -78
  49. package/build/lib/commands/execute.js.map +1 -1
  50. package/build/lib/commands/file-actions.d.ts +42 -5
  51. package/build/lib/commands/file-actions.d.ts.map +1 -1
  52. package/build/lib/commands/file-actions.js +230 -194
  53. package/build/lib/commands/file-actions.js.map +1 -1
  54. package/build/lib/commands/find.d.ts +5 -4
  55. package/build/lib/commands/find.d.ts.map +1 -1
  56. package/build/lib/commands/find.js +7 -10
  57. package/build/lib/commands/find.js.map +1 -1
  58. package/build/lib/commands/geolocation.d.ts +45 -0
  59. package/build/lib/commands/geolocation.d.ts.map +1 -0
  60. package/build/lib/commands/geolocation.js +182 -0
  61. package/build/lib/commands/geolocation.js.map +1 -0
  62. package/build/lib/commands/ime.d.ts +25 -5
  63. package/build/lib/commands/ime.d.ts.map +1 -1
  64. package/build/lib/commands/ime.js +59 -42
  65. package/build/lib/commands/ime.js.map +1 -1
  66. package/build/lib/commands/intent.d.ts +56 -5
  67. package/build/lib/commands/intent.d.ts.map +1 -1
  68. package/build/lib/commands/intent.js +135 -83
  69. package/build/lib/commands/intent.js.map +1 -1
  70. package/build/lib/commands/keyboard.d.ts +58 -4
  71. package/build/lib/commands/keyboard.d.ts.map +1 -1
  72. package/build/lib/commands/keyboard.js +119 -17
  73. package/build/lib/commands/keyboard.js.map +1 -1
  74. package/build/lib/commands/lock/exports.d.ts +301 -0
  75. package/build/lib/commands/lock/exports.d.ts.map +1 -0
  76. package/build/lib/commands/lock/exports.js +121 -0
  77. package/build/lib/commands/lock/exports.js.map +1 -0
  78. package/build/lib/commands/lock/helpers.d.ts +349 -0
  79. package/build/lib/commands/lock/helpers.d.ts.map +1 -0
  80. package/build/lib/commands/lock/helpers.js +375 -0
  81. package/build/lib/commands/lock/helpers.js.map +1 -0
  82. package/build/lib/commands/log.d.ts +59 -5
  83. package/build/lib/commands/log.d.ts.map +1 -1
  84. package/build/lib/commands/log.js +150 -140
  85. package/build/lib/commands/log.js.map +1 -1
  86. package/build/lib/commands/media-projection.d.ts +16 -5
  87. package/build/lib/commands/media-projection.d.ts.map +1 -1
  88. package/build/lib/commands/media-projection.js +69 -58
  89. package/build/lib/commands/media-projection.js.map +1 -1
  90. package/build/lib/commands/memory.d.ts +9 -5
  91. package/build/lib/commands/memory.d.ts.map +1 -1
  92. package/build/lib/commands/memory.js +19 -24
  93. package/build/lib/commands/memory.js.map +1 -1
  94. package/build/lib/commands/misc.d.ts +42 -0
  95. package/build/lib/commands/misc.d.ts.map +1 -0
  96. package/build/lib/commands/misc.js +100 -0
  97. package/build/lib/commands/misc.js.map +1 -0
  98. package/build/lib/commands/network.d.ts +61 -5
  99. package/build/lib/commands/network.d.ts.map +1 -1
  100. package/build/lib/commands/network.js +196 -189
  101. package/build/lib/commands/network.js.map +1 -1
  102. package/build/lib/commands/performance.d.ts +67 -27
  103. package/build/lib/commands/performance.d.ts.map +1 -1
  104. package/build/lib/commands/performance.js +105 -80
  105. package/build/lib/commands/performance.js.map +1 -1
  106. package/build/lib/commands/permissions.d.ts +12 -6
  107. package/build/lib/commands/permissions.d.ts.map +1 -1
  108. package/build/lib/commands/permissions.js +65 -62
  109. package/build/lib/commands/permissions.js.map +1 -1
  110. package/build/lib/commands/recordscreen.d.ts +44 -5
  111. package/build/lib/commands/recordscreen.d.ts.map +1 -1
  112. package/build/lib/commands/recordscreen.js +131 -126
  113. package/build/lib/commands/recordscreen.js.map +1 -1
  114. package/build/lib/commands/resources.d.ts +16 -0
  115. package/build/lib/commands/resources.d.ts.map +1 -0
  116. package/build/lib/commands/resources.js +91 -0
  117. package/build/lib/commands/resources.js.map +1 -0
  118. package/build/lib/commands/shell.d.ts +8 -5
  119. package/build/lib/commands/shell.d.ts.map +1 -1
  120. package/build/lib/commands/shell.js +29 -33
  121. package/build/lib/commands/shell.js.map +1 -1
  122. package/build/lib/commands/streamscreen.d.ts +34 -6
  123. package/build/lib/commands/streamscreen.d.ts.map +1 -1
  124. package/build/lib/commands/streamscreen.js +166 -162
  125. package/build/lib/commands/streamscreen.js.map +1 -1
  126. package/build/lib/commands/system-bars.d.ts +18 -13
  127. package/build/lib/commands/system-bars.d.ts.map +1 -1
  128. package/build/lib/commands/system-bars.js +68 -64
  129. package/build/lib/commands/system-bars.js.map +1 -1
  130. package/build/lib/commands/time.d.ts +14 -0
  131. package/build/lib/commands/time.d.ts.map +1 -0
  132. package/build/lib/commands/time.js +39 -0
  133. package/build/lib/commands/time.js.map +1 -0
  134. package/build/lib/commands/touch.d.ts +99 -6
  135. package/build/lib/commands/touch.d.ts.map +1 -1
  136. package/build/lib/commands/touch.js +399 -280
  137. package/build/lib/commands/touch.js.map +1 -1
  138. package/build/lib/commands/types.d.ts +110 -2
  139. package/build/lib/commands/types.d.ts.map +1 -1
  140. package/build/lib/doctor/checks.d.ts.map +1 -1
  141. package/build/lib/doctor/checks.js +4 -4
  142. package/build/lib/doctor/checks.js.map +1 -1
  143. package/build/lib/driver.d.ts +224 -27
  144. package/build/lib/driver.d.ts.map +1 -1
  145. package/build/lib/driver.js +232 -7
  146. package/build/lib/driver.js.map +1 -1
  147. package/build/lib/index.d.ts +1 -4
  148. package/build/lib/index.d.ts.map +1 -1
  149. package/build/lib/index.js +1 -13
  150. package/build/lib/index.js.map +1 -1
  151. package/build/lib/logger.js.map +1 -1
  152. package/build/lib/method-map.d.ts +0 -23
  153. package/build/lib/method-map.d.ts.map +1 -1
  154. package/build/lib/method-map.js +0 -11
  155. package/build/lib/method-map.js.map +1 -1
  156. package/build/lib/utils.d.ts +12 -0
  157. package/build/lib/utils.d.ts.map +1 -1
  158. package/build/lib/utils.js +38 -2
  159. package/build/lib/utils.js.map +1 -1
  160. package/lib/commands/app-management.js +470 -145
  161. package/lib/commands/appearance.js +29 -36
  162. package/lib/commands/context/cache.js +29 -0
  163. package/lib/commands/context/exports.js +379 -0
  164. package/lib/commands/context/helpers.js +802 -0
  165. package/lib/commands/device/common.js +264 -0
  166. package/lib/commands/device/emulator-actions.js +194 -0
  167. package/lib/commands/device/emulator-console.js +24 -0
  168. package/lib/commands/device/utils.js +285 -0
  169. package/lib/commands/deviceidle.js +31 -44
  170. package/lib/commands/element.js +149 -142
  171. package/lib/commands/execute.js +86 -87
  172. package/lib/commands/file-actions.js +249 -222
  173. package/lib/commands/find.ts +13 -19
  174. package/lib/commands/geolocation.js +179 -0
  175. package/lib/commands/ime.js +53 -45
  176. package/lib/commands/intent.js +149 -91
  177. package/lib/commands/keyboard.js +114 -17
  178. package/lib/commands/lock/exports.js +139 -0
  179. package/lib/commands/lock/helpers.js +379 -0
  180. package/lib/commands/log.js +170 -166
  181. package/lib/commands/media-projection.js +75 -70
  182. package/lib/commands/memory.js +17 -29
  183. package/lib/commands/misc.js +94 -0
  184. package/lib/commands/network.js +209 -223
  185. package/lib/commands/performance.js +88 -73
  186. package/lib/commands/permissions.js +83 -84
  187. package/lib/commands/recordscreen.js +171 -170
  188. package/lib/commands/resources.js +96 -0
  189. package/lib/commands/shell.js +28 -42
  190. package/lib/commands/streamscreen.js +207 -206
  191. package/lib/commands/system-bars.js +76 -77
  192. package/lib/commands/time.js +36 -0
  193. package/lib/commands/touch.js +442 -346
  194. package/lib/commands/types.ts +123 -2
  195. package/lib/doctor/checks.js +24 -16
  196. package/lib/driver.ts +454 -12
  197. package/lib/index.ts +1 -13
  198. package/lib/logger.js +1 -1
  199. package/lib/method-map.js +0 -11
  200. package/lib/utils.js +40 -3
  201. package/package.json +1 -1
  202. package/build/lib/commands/actions.d.ts +0 -8
  203. package/build/lib/commands/actions.d.ts.map +0 -1
  204. package/build/lib/commands/actions.js +0 -207
  205. package/build/lib/commands/actions.js.map +0 -1
  206. package/build/lib/commands/alert.d.ts +0 -8
  207. package/build/lib/commands/alert.d.ts.map +0 -1
  208. package/build/lib/commands/alert.js +0 -29
  209. package/build/lib/commands/alert.js.map +0 -1
  210. package/build/lib/commands/context.d.ts +0 -10
  211. package/build/lib/commands/context.d.ts.map +0 -1
  212. package/build/lib/commands/context.js +0 -431
  213. package/build/lib/commands/context.js.map +0 -1
  214. package/build/lib/commands/emu-console.d.ts +0 -7
  215. package/build/lib/commands/emu-console.d.ts.map +0 -1
  216. package/build/lib/commands/emu-console.js +0 -27
  217. package/build/lib/commands/emu-console.js.map +0 -1
  218. package/build/lib/commands/general.d.ts +0 -9
  219. package/build/lib/commands/general.d.ts.map +0 -1
  220. package/build/lib/commands/general.js +0 -293
  221. package/build/lib/commands/general.js.map +0 -1
  222. package/build/lib/commands/index.d.ts +0 -28
  223. package/build/lib/commands/index.d.ts.map +0 -1
  224. package/build/lib/commands/index.js +0 -57
  225. package/build/lib/commands/index.js.map +0 -1
  226. package/build/lib/commands/mixins.d.ts +0 -747
  227. package/build/lib/commands/mixins.d.ts.map +0 -1
  228. package/build/lib/commands/mixins.js +0 -19
  229. package/build/lib/commands/mixins.js.map +0 -1
  230. package/build/lib/helpers/android.d.ts +0 -163
  231. package/build/lib/helpers/android.d.ts.map +0 -1
  232. package/build/lib/helpers/android.js +0 -818
  233. package/build/lib/helpers/android.js.map +0 -1
  234. package/build/lib/helpers/index.d.ts +0 -7
  235. package/build/lib/helpers/index.d.ts.map +0 -1
  236. package/build/lib/helpers/index.js +0 -29
  237. package/build/lib/helpers/index.js.map +0 -1
  238. package/build/lib/helpers/types.d.ts +0 -122
  239. package/build/lib/helpers/types.d.ts.map +0 -1
  240. package/build/lib/helpers/types.js +0 -3
  241. package/build/lib/helpers/types.js.map +0 -1
  242. package/build/lib/helpers/unlock.d.ts +0 -32
  243. package/build/lib/helpers/unlock.d.ts.map +0 -1
  244. package/build/lib/helpers/unlock.js +0 -273
  245. package/build/lib/helpers/unlock.js.map +0 -1
  246. package/build/lib/helpers/webview.d.ts +0 -74
  247. package/build/lib/helpers/webview.d.ts.map +0 -1
  248. package/build/lib/helpers/webview.js +0 -448
  249. package/build/lib/helpers/webview.js.map +0 -1
  250. package/lib/commands/actions.js +0 -244
  251. package/lib/commands/alert.js +0 -34
  252. package/lib/commands/context.js +0 -507
  253. package/lib/commands/emu-console.js +0 -31
  254. package/lib/commands/general.js +0 -343
  255. package/lib/commands/index.ts +0 -54
  256. package/lib/commands/mixins.ts +0 -976
  257. package/lib/helpers/android.ts +0 -1153
  258. package/lib/helpers/index.ts +0 -6
  259. package/lib/helpers/types.ts +0 -136
  260. package/lib/helpers/unlock.ts +0 -329
  261. package/lib/helpers/webview.ts +0 -610
@@ -1,176 +1,501 @@
1
- // @ts-check
2
-
3
1
  import {util} from '@appium/support';
4
- import {waitForCondition} from 'asyncbox';
2
+ import {waitForCondition, longSleep} from 'asyncbox';
5
3
  import _ from 'lodash';
6
- import {APP_STATE} from '../helpers';
7
4
  import {requireArgs} from '../utils';
8
- import {mixin} from './mixins';
5
+ import {EOL} from 'node:os';
6
+ import B from 'bluebird';
9
7
 
10
8
  const APP_EXTENSIONS = ['.apk', '.apks'];
11
9
  const RESOLVER_ACTIVITY_NAME = 'android/com.android.internal.app.ResolverActivity';
10
+ const PACKAGE_INSTALL_TIMEOUT_MS = 90000;
11
+ // These constants are in sync with
12
+ // https://developer.apple.com/documentation/xctest/xcuiapplicationstate/xcuiapplicationstaterunningbackground?language=objc
13
+ export const APP_STATE = /** @type {const} */ ({
14
+ NOT_INSTALLED: 0,
15
+ NOT_RUNNING: 1,
16
+ RUNNING_IN_BACKGROUND: 3,
17
+ RUNNING_IN_FOREGROUND: 4,
18
+ });
12
19
 
13
20
  /**
14
- * @type {import('./mixins').AppManagementMixin & ThisType<import('../driver').AndroidDriver>}
15
- * @satisfies {import('@appium/types').ExternalDriver}
21
+ * @this {AndroidDriver}
22
+ * @param {string} appId
23
+ * @returns {Promise<boolean>}
16
24
  */
17
- const AppManagementMixin = {
18
- async isAppInstalled(appId) {
19
- return await this.adb.isAppInstalled(appId);
20
- },
25
+ export async function isAppInstalled(appId) {
26
+ return await this.adb.isAppInstalled(appId);
27
+ }
21
28
 
22
- async mobileIsAppInstalled(opts) {
23
- const {appId} = requireArgs('appId', opts);
24
- return await this.isAppInstalled(appId);
25
- },
29
+ /**
30
+ * @this {AndroidDriver}
31
+ * @param {import('./types').IsAppInstalledOpts} opts
32
+ * @returns {Promise<boolean>}
33
+ */
34
+ export async function mobileIsAppInstalled(opts) {
35
+ const {appId} = requireArgs('appId', opts);
36
+ return await this.isAppInstalled(appId);
37
+ }
26
38
 
27
- async queryAppState(appId) {
28
- this.log.info(`Querying the state of '${appId}'`);
29
- if (!(await this.adb.isAppInstalled(appId))) {
30
- return APP_STATE.NOT_INSTALLED;
39
+ /**
40
+ * @this {AndroidDriver}
41
+ * @param {string} appId
42
+ * @returns {Promise<import('./types').AppState>}
43
+ */
44
+ export async function queryAppState(appId) {
45
+ this.log.info(`Querying the state of '${appId}'`);
46
+ if (!(await this.adb.isAppInstalled(appId))) {
47
+ return APP_STATE.NOT_INSTALLED;
48
+ }
49
+ if (!(await this.adb.processExists(appId))) {
50
+ return APP_STATE.NOT_RUNNING;
51
+ }
52
+ const appIdRe = new RegExp(`\\b${_.escapeRegExp(appId)}/`);
53
+ for (const line of (await this.adb.dumpWindows()).split('\n')) {
54
+ if (appIdRe.test(line) && ['mCurrentFocus', 'mFocusedApp'].some((x) => line.includes(x))) {
55
+ return APP_STATE.RUNNING_IN_FOREGROUND;
31
56
  }
32
- if (!(await this.adb.processExists(appId))) {
33
- return APP_STATE.NOT_RUNNING;
57
+ }
58
+ return APP_STATE.RUNNING_IN_BACKGROUND;
59
+ }
60
+
61
+ /**
62
+ * @this {AndroidDriver}
63
+ * @param {import('./types').QueryAppStateOpts} opts
64
+ * @returns {Promise<import('./types').AppState>}
65
+ */
66
+ export async function mobileQueryAppState(opts) {
67
+ const {appId} = requireArgs('appId', opts);
68
+ return await this.queryAppState(appId);
69
+ }
70
+
71
+ /**
72
+ * @this {AndroidDriver}
73
+ * @param {string} appId
74
+ * @returns {Promise<void>}
75
+ */
76
+ export async function activateApp(appId) {
77
+ this.log.debug(`Activating '${appId}'`);
78
+ const apiLevel = await this.adb.getApiLevel();
79
+ // Fallback to Monkey in older APIs
80
+ if (apiLevel < 24) {
81
+ // The monkey command could raise an issue as https://stackoverflow.com/questions/44860475/how-to-use-the-monkey-command-with-an-android-system-that-doesnt-have-physical
82
+ // but '--pct-syskeys 0' could cause another background process issue. https://github.com/appium/appium/issues/16941#issuecomment-1129837285
83
+ const cmd = ['monkey', '-p', appId, '-c', 'android.intent.category.LAUNCHER', '1'];
84
+ let output = '';
85
+ try {
86
+ output = await this.adb.shell(cmd);
87
+ this.log.debug(`Command stdout: ${output}`);
88
+ } catch (e) {
89
+ this.log.errorAndThrow(
90
+ `Cannot activate '${appId}'. Original error: ${/** @type {Error} */ (e).message}`,
91
+ );
34
92
  }
35
- const appIdRe = new RegExp(`\\b${_.escapeRegExp(appId)}/`);
36
- for (const line of (await this.adb.dumpWindows()).split('\n')) {
37
- if (appIdRe.test(line) && ['mCurrentFocus', 'mFocusedApp'].some((x) => line.includes(x))) {
38
- return APP_STATE.RUNNING_IN_FOREGROUND;
39
- }
93
+ if (output.includes('monkey aborted')) {
94
+ this.log.errorAndThrow(`Cannot activate '${appId}'. Are you sure it is installed?`);
40
95
  }
41
- return APP_STATE.RUNNING_IN_BACKGROUND;
42
- },
43
-
44
- async mobileQueryAppState(opts) {
45
- const {appId} = requireArgs('appId', opts);
46
- return await this.queryAppState(appId);
47
- },
48
-
49
- async activateApp(appId) {
50
- this.log.debug(`Activating '${appId}'`);
51
- const apiLevel = await this.adb.getApiLevel();
52
- // Fallback to Monkey in older APIs
53
- if (apiLevel < 24) {
54
- // The monkey command could raise an issue as https://stackoverflow.com/questions/44860475/how-to-use-the-monkey-command-with-an-android-system-that-doesnt-have-physical
55
- // but '--pct-syskeys 0' could cause another background process issue. https://github.com/appium/appium/issues/16941#issuecomment-1129837285
56
- const cmd = ['monkey', '-p', appId, '-c', 'android.intent.category.LAUNCHER', '1'];
57
- let output = '';
58
- try {
59
- output = await this.adb.shell(cmd);
60
- this.log.debug(`Command stdout: ${output}`);
61
- } catch (e) {
62
- this.log.errorAndThrow(
63
- `Cannot activate '${appId}'. Original error: ${/** @type {Error} */ (e).message}`
96
+ return;
97
+ }
98
+
99
+ let activityName = await this.adb.resolveLaunchableActivity(appId);
100
+ if (activityName === RESOLVER_ACTIVITY_NAME) {
101
+ // https://github.com/appium/appium/issues/17128
102
+ this.log.debug(
103
+ `The launchable activity name of '${appId}' was resolved to '${activityName}'. ` +
104
+ `Switching the resolver to not use cmd`,
105
+ );
106
+ activityName = await this.adb.resolveLaunchableActivity(appId, {preferCmd: false});
107
+ }
108
+
109
+ const stdout = await this.adb.shell([
110
+ 'am',
111
+ apiLevel < 26 ? 'start' : 'start-activity',
112
+ '-a',
113
+ 'android.intent.action.MAIN',
114
+ '-c',
115
+ 'android.intent.category.LAUNCHER',
116
+ // FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
117
+ // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_NEW_TASK
118
+ // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
119
+ '-f',
120
+ '0x10200000',
121
+ '-n',
122
+ activityName,
123
+ ]);
124
+ this.log.debug(stdout);
125
+ if (/^error:/im.test(stdout)) {
126
+ throw new Error(`Cannot activate '${appId}'. Original error: ${stdout}`);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * @this {AndroidDriver}
132
+ * @param {import('./types').ActivateAppOpts} opts
133
+ * @returns {Promise<void>}
134
+ */
135
+ export async function mobileActivateApp(opts) {
136
+ const {appId} = requireArgs('appId', opts);
137
+ return await this.activateApp(appId);
138
+ }
139
+
140
+ /**
141
+ * @this {AndroidDriver}
142
+ * @param {string} appId
143
+ * @param {Omit<import('appium-adb').UninstallOptions, 'appId'>} opts
144
+ * @returns {Promise<boolean>}
145
+ */
146
+ export async function removeApp(appId, opts = {}) {
147
+ return await this.adb.uninstallApk(appId, opts);
148
+ }
149
+
150
+ /**
151
+ * @this {import('../driver').AndroidDriver}
152
+ * @param {import('./types').RemoveAppOpts} opts
153
+ * @returns {Promise<boolean>}
154
+ */
155
+ export async function mobileRemoveApp(opts) {
156
+ const {appId} = requireArgs('appId', opts);
157
+ return await this.removeApp(appId, opts);
158
+ }
159
+
160
+ /**
161
+ * @this {AndroidDriver}
162
+ * @param {string} appId
163
+ * @param {Omit<import('./types').TerminateAppOpts, 'appId'>} [options={}]
164
+ * @returns {Promise<boolean>}
165
+ */
166
+ export async function terminateApp(appId, options = {}) {
167
+ this.log.info(`Terminating '${appId}'`);
168
+ if (!(await this.adb.processExists(appId))) {
169
+ this.log.info(`The app '${appId}' is not running`);
170
+ return false;
171
+ }
172
+ await this.adb.forceStop(appId);
173
+ const timeout =
174
+ util.hasValue(options.timeout) && !Number.isNaN(options.timeout)
175
+ ? parseInt(String(options.timeout), 10)
176
+ : 500;
177
+
178
+ if (timeout <= 0) {
179
+ this.log.info(
180
+ `'${appId}' has been terminated. Skip checking the application process state ` +
181
+ `since the timeout was set as ${timeout}ms`,
182
+ );
183
+ return true;
184
+ }
185
+
186
+ try {
187
+ await waitForCondition(async () => (await this.queryAppState(appId)) <= APP_STATE.NOT_RUNNING, {
188
+ waitMs: timeout,
189
+ intervalMs: 100,
190
+ });
191
+ } catch (e) {
192
+ this.log.errorAndThrow(`'${appId}' is still running after ${timeout}ms timeout`);
193
+ }
194
+ this.log.info(`'${appId}' has been successfully terminated`);
195
+ return true;
196
+ }
197
+
198
+ /**
199
+ * @this {AndroidDriver}
200
+ * @param {import('./types').TerminateAppOpts} opts
201
+ * @returns {Promise<boolean>}
202
+ */
203
+ export async function mobileTerminateApp(opts) {
204
+ const {appId} = requireArgs('appId', opts);
205
+ return await this.terminateApp(appId, opts);
206
+ }
207
+
208
+ /**
209
+ * @this {AndroidDriver}
210
+ * @param {string} appPath
211
+ * @param {Omit<import('appium-adb').InstallOptions, 'appId'>} opts
212
+ * @returns {Promise<void>}
213
+ */
214
+ export async function installApp(appPath, opts) {
215
+ const localPath = await this.helpers.configureApp(appPath, APP_EXTENSIONS);
216
+ await this.adb.install(localPath, opts);
217
+ }
218
+
219
+ /**
220
+ * @this {AndroidDriver}
221
+ * @param {import('./types').InstallAppOpts} opts
222
+ * @returns {Promise<void>}
223
+ */
224
+ export async function mobileInstallApp(opts) {
225
+ const {appPath} = requireArgs('appPath', opts);
226
+ return await this.installApp(appPath, opts);
227
+ }
228
+
229
+ /**
230
+ * @this {AndroidDriver}
231
+ * @param {import('./types').ClearAppOpts} opts
232
+ * @returns {Promise<void>}
233
+ */
234
+ export async function mobileClearApp(opts) {
235
+ const {appId} = requireArgs('appId', opts);
236
+ await this.adb.clear(appId);
237
+ }
238
+
239
+ /**
240
+ * @this {AndroidDriver}
241
+ * @returns {Promise<string>}
242
+ */
243
+ export async function getCurrentActivity() {
244
+ return /** @type {string} */ ((await this.adb.getFocusedPackageAndActivity()).appActivity);
245
+ }
246
+
247
+ /**
248
+ * @this {AndroidDriver}
249
+ * @returns {Promise<string>}
250
+ */
251
+ export async function getCurrentPackage() {
252
+ return /** @type {string} */ ((await this.adb.getFocusedPackageAndActivity()).appPackage);
253
+ }
254
+
255
+ /**
256
+ * @this {AndroidDriver}
257
+ * @param {number} seconds
258
+ * @returns {Promise<string|true>}
259
+ */
260
+ export async function background(seconds) {
261
+ if (seconds < 0) {
262
+ // if user passes in a negative seconds value, interpret that as the instruction
263
+ // to not bring the app back at all
264
+ await this.adb.goToHome();
265
+ return true;
266
+ }
267
+ let {appPackage, appActivity} = await this.adb.getFocusedPackageAndActivity();
268
+ await this.adb.goToHome();
269
+
270
+ // people can wait for a long time, so to be safe let's use the longSleep function and log
271
+ // progress periodically.
272
+ const sleepMs = seconds * 1000;
273
+ const thresholdMs = 30 * 1000; // use the spin-wait for anything over this threshold
274
+ // for our spin interval, use 1% of the total wait time, but nothing bigger than 30s
275
+ const intervalMs = _.min([30 * 1000, parseInt(String(sleepMs / 100), 10)]);
276
+ /**
277
+ *
278
+ * @param {{elapsedMs: number, progress: number}} param0
279
+ */
280
+ const progressCb = ({elapsedMs, progress}) => {
281
+ const waitSecs = (elapsedMs / 1000).toFixed(0);
282
+ const progressPct = (progress * 100).toFixed(2);
283
+ this.log.debug(`Waited ${waitSecs}s so far (${progressPct}%)`);
284
+ };
285
+ await longSleep(sleepMs, {thresholdMs, intervalMs, progressCb});
286
+
287
+ /** @type {import('appium-adb').StartAppOptions} */
288
+ let args;
289
+ if (this._cachedActivityArgs && this._cachedActivityArgs[`${appPackage}/${appActivity}`]) {
290
+ // the activity was started with `startActivity`, so use those args to restart
291
+ args = this._cachedActivityArgs[`${appPackage}/${appActivity}`];
292
+ } else {
293
+ try {
294
+ this.log.debug(`Activating app '${appPackage}' in order to restore it`);
295
+ await this.activateApp(/** @type {string} */ (appPackage));
296
+ return true;
297
+ } catch (ign) {}
298
+ args =
299
+ (appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) ||
300
+ (appPackage === this.opts.appWaitPackage &&
301
+ (this.opts.appWaitActivity || '').split(',').includes(String(appActivity)))
302
+ ? {
303
+ // the activity is the original session activity, so use the original args
304
+ pkg: /** @type {string} */ (this.opts.appPackage),
305
+ activity: this.opts.appActivity ?? undefined,
306
+ action: this.opts.intentAction,
307
+ category: this.opts.intentCategory,
308
+ flags: this.opts.intentFlags,
309
+ waitPkg: this.opts.appWaitPackage ?? undefined,
310
+ waitActivity: this.opts.appWaitActivity ?? undefined,
311
+ waitForLaunch: this.opts.appWaitForLaunch,
312
+ waitDuration: this.opts.appWaitDuration,
313
+ optionalIntentArguments: this.opts.optionalIntentArguments,
314
+ stopApp: false,
315
+ user: this.opts.userProfile,
316
+ }
317
+ : {
318
+ // the activity was started some other way, so use defaults
319
+ pkg: /** @type {string} */ (appPackage),
320
+ activity: appActivity ?? undefined,
321
+ waitPkg: appPackage ?? undefined,
322
+ waitActivity: appActivity ?? undefined,
323
+ stopApp: false,
324
+ };
325
+ }
326
+ args = /** @type {import('appium-adb').StartAppOptions} */ (
327
+ _.pickBy(args, (value) => !_.isUndefined(value))
328
+ );
329
+ this.log.debug(`Bringing application back to foreground with arguments: ${JSON.stringify(args)}`);
330
+ return await this.adb.startApp(args);
331
+ }
332
+
333
+ /**
334
+ * @this {AndroidDriver}
335
+ * @param {import('../driver').AndroidDriverOpts?} [opts=null]
336
+ * @returns {Promise<void>}
337
+ */
338
+ export async function resetApp(opts = null) {
339
+ const {
340
+ app,
341
+ appPackage,
342
+ fastReset,
343
+ fullReset,
344
+ androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
345
+ autoGrantPermissions,
346
+ allowTestPackages,
347
+ } = opts ?? this.opts;
348
+
349
+ if (!appPackage) {
350
+ throw new Error("'appPackage' option is required");
351
+ }
352
+
353
+ const isInstalled = await this.adb.isAppInstalled(appPackage);
354
+
355
+ if (isInstalled) {
356
+ try {
357
+ await this.adb.forceStop(appPackage);
358
+ } catch (ign) {}
359
+ // fullReset has priority over fastReset
360
+ if (!fullReset && fastReset) {
361
+ const output = await this.adb.clear(appPackage);
362
+ if (_.isString(output) && output.toLowerCase().includes('failed')) {
363
+ throw new Error(
364
+ `Cannot clear the application data of '${appPackage}'. Original error: ${output}`,
64
365
  );
65
366
  }
66
- if (output.includes('monkey aborted')) {
67
- this.log.errorAndThrow(`Cannot activate '${appId}'. Are you sure it is installed?`);
367
+ // executing `shell pm clear` resets previously assigned application permissions as well
368
+ if (autoGrantPermissions) {
369
+ try {
370
+ await this.adb.grantAllPermissions(appPackage);
371
+ } catch (error) {
372
+ this.log.error(`Unable to grant permissions requested. Original error: ${error.message}`);
373
+ }
68
374
  }
69
- return;
70
- }
71
-
72
- let activityName = await this.adb.resolveLaunchableActivity(appId);
73
- if (activityName === RESOLVER_ACTIVITY_NAME) {
74
- // https://github.com/appium/appium/issues/17128
75
375
  this.log.debug(
76
- `The launchable activity name of '${appId}' was resolved to '${activityName}'. ` +
77
- `Switching the resolver to not use cmd`
376
+ `Performed fast reset on the installed '${appPackage}' application (stop and clear)`,
78
377
  );
79
- activityName = await this.adb.resolveLaunchableActivity(appId, {preferCmd: false});
378
+ return;
80
379
  }
380
+ }
81
381
 
82
- const stdout = await this.adb.shell([
83
- 'am',
84
- apiLevel < 26 ? 'start' : 'start-activity',
85
- '-a',
86
- 'android.intent.action.MAIN',
87
- '-c',
88
- 'android.intent.category.LAUNCHER',
89
- // FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
90
- // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_NEW_TASK
91
- // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
92
- '-f',
93
- '0x10200000',
94
- '-n',
95
- activityName,
96
- ]);
97
- this.log.debug(stdout);
98
- if (/^error:/im.test(stdout)) {
99
- throw new Error(`Cannot activate '${appId}'. Original error: ${stdout}`);
100
- }
101
- },
102
-
103
- async mobileActivateApp(opts) {
104
- const {appId} = requireArgs('appId', opts);
105
- return await this.activateApp(appId);
106
- },
107
-
108
- async removeApp(appId, opts = {}) {
109
- return await this.adb.uninstallApk(appId, opts);
110
- },
111
-
112
- async mobileRemoveApp(opts) {
113
- const {appId} = requireArgs('appId', opts);
114
- return await this.removeApp(appId, opts);
115
- },
116
-
117
- async terminateApp(appId, options = {}) {
118
- this.log.info(`Terminating '${appId}'`);
119
- if (!(await this.adb.processExists(appId))) {
120
- this.log.info(`The app '${appId}' is not running`);
121
- return false;
122
- }
123
- await this.adb.forceStop(appId);
124
- const timeout =
125
- util.hasValue(options.timeout) && !Number.isNaN(options.timeout)
126
- ? parseInt(String(options.timeout), 10)
127
- : 500;
128
-
129
- if (timeout <= 0) {
130
- this.log.info(
131
- `'${appId}' has been terminated. Skip checking the application process state ` +
132
- `since the timeout was set as ${timeout}ms`
133
- );
134
- return true;
135
- }
382
+ if (!app) {
383
+ throw new Error(
384
+ `Either provide 'app' option to install '${appPackage}' or ` +
385
+ `consider setting 'noReset' to 'true' if '${appPackage}' is supposed to be preinstalled.`,
386
+ );
387
+ }
136
388
 
137
- try {
138
- await waitForCondition(
139
- async () => (await this.queryAppState(appId)) <= APP_STATE.NOT_RUNNING,
140
- {waitMs: timeout, intervalMs: 100}
141
- );
142
- } catch (e) {
143
- this.log.errorAndThrow(`'${appId}' is still running after ${timeout}ms timeout`);
144
- }
145
- this.log.info(`'${appId}' has been successfully terminated`);
146
- return true;
147
- },
389
+ this.log.debug(`Running full reset on '${appPackage}' (reinstall)`);
390
+ if (isInstalled) {
391
+ await this.adb.uninstallApk(appPackage);
392
+ }
393
+ await this.adb.install(app, {
394
+ grantPermissions: autoGrantPermissions,
395
+ timeout: androidInstallTimeout,
396
+ allowTestPackages,
397
+ });
398
+ }
399
+
400
+ export async function installApk(opts = null) {
401
+ const {
402
+ app,
403
+ appPackage,
404
+ fastReset,
405
+ fullReset,
406
+ androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
407
+ autoGrantPermissions,
408
+ allowTestPackages,
409
+ enforceAppInstall,
410
+ } = opts ?? this.opts;
411
+
412
+ if (!app || !appPackage) {
413
+ throw new Error("'app' and 'appPackage' options are required");
414
+ }
148
415
 
149
- async mobileTerminateApp(opts) {
150
- const {appId} = requireArgs('appId', opts);
151
- return await this.terminateApp(appId, opts);
152
- },
416
+ if (fullReset) {
417
+ await this.resetApp(opts);
418
+ return;
419
+ }
153
420
 
154
- async installApp(appPath, opts) {
155
- const localPath = await this.helpers.configureApp(appPath, APP_EXTENSIONS);
156
- await this.adb.install(localPath, opts);
157
- },
421
+ const {appState, wasUninstalled} = await this.adb.installOrUpgrade(app, appPackage, {
422
+ grantPermissions: autoGrantPermissions,
423
+ timeout: androidInstallTimeout,
424
+ allowTestPackages,
425
+ enforceCurrentBuild: enforceAppInstall,
426
+ });
158
427
 
159
- async mobileInstallApp(opts) {
160
- const {appPath} = requireArgs('appPath', opts);
161
- return await this.installApp(appPath, opts);
162
- },
428
+ // There is no need to reset the newly installed app
429
+ const isInstalledOverExistingApp =
430
+ !wasUninstalled && appState !== this.adb.APP_INSTALL_STATE.NOT_INSTALLED;
431
+ if (fastReset && isInstalledOverExistingApp) {
432
+ this.log.info(`Performing fast reset on '${appPackage}'`);
433
+ await this.resetApp(opts);
434
+ }
435
+ }
163
436
 
164
- async mobileClearApp(opts) {
165
- const {appId} = requireArgs('appId', opts);
166
- await this.adb.clear(appId);
167
- },
168
- };
437
+ /**
438
+ * @this {AndroidDriver}
439
+ * @param {string[]} otherApps
440
+ * @param {import('../driver').AndroidDriverOpts?} [opts=null]
441
+ * @returns {Promise<void>}
442
+ */
443
+ export async function installOtherApks(otherApps, opts = null) {
444
+ const {
445
+ androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT_MS,
446
+ autoGrantPermissions,
447
+ allowTestPackages,
448
+ } = opts ?? this.opts;
169
449
 
170
- mixin(AppManagementMixin);
450
+ // Install all of the APK's asynchronously
451
+ await B.all(
452
+ otherApps.map((otherApp) => {
453
+ this.log.debug(`Installing app: ${otherApp}`);
454
+ return this.adb.installOrUpgrade(otherApp, undefined, {
455
+ grantPermissions: autoGrantPermissions,
456
+ timeout: androidInstallTimeout,
457
+ allowTestPackages,
458
+ });
459
+ }),
460
+ );
461
+ }
171
462
 
172
- export default AppManagementMixin;
463
+ /**
464
+ * @this {AndroidDriver}
465
+ * @param {string[]} appPackages
466
+ * @param {string[]} [filterPackages=[]]
467
+ * @returns {Promise<void>}
468
+ */
469
+ export async function uninstallOtherPackages(appPackages, filterPackages = []) {
470
+ if (appPackages.includes('*')) {
471
+ this.log.debug('Uninstall third party packages');
472
+ appPackages = await getThirdPartyPackages.bind(this)(filterPackages);
473
+ }
474
+
475
+ this.log.debug(`Uninstalling packages: ${appPackages}`);
476
+ await B.all(appPackages.map((appPackage) => this.adb.uninstallApk(appPackage)));
477
+ }
478
+
479
+ /**
480
+ * @this {AndroidDriver}
481
+ * @param {string[]} [filterPackages=[]]
482
+ * @returns {Promise<string[]>}
483
+ */
484
+ export async function getThirdPartyPackages(filterPackages = []) {
485
+ try {
486
+ const packagesString = await this.adb.shell(['pm', 'list', 'packages', '-3']);
487
+ const appPackagesArray = packagesString
488
+ .trim()
489
+ .replace(/package:/g, '')
490
+ .split(EOL);
491
+ this.log.debug(`'${appPackagesArray}' filtered with '${filterPackages}'`);
492
+ return _.difference(appPackagesArray, filterPackages);
493
+ } catch (err) {
494
+ this.log.warn(`Unable to get packages with 'adb shell pm list packages -3': ${err.message}`);
495
+ return [];
496
+ }
497
+ }
173
498
 
174
499
  /**
175
- * @typedef {import('appium-adb').ADB} ADB
500
+ * @typedef {import('../driver').AndroidDriver} AndroidDriver
176
501
  */