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,24 +1,34 @@
1
- /* eslint-disable require-await */
2
- import { errors } from 'appium/driver';
1
+ // @ts-check
3
2
 
4
- let commands = {}, helpers = {}, extensions = {};
3
+ import {errors} from 'appium/driver';
4
+ import {mixin} from './mixins';
5
5
 
6
- commands.getAlertText = async function getAlertText () {
7
- throw new errors.NotYetImplementedError();
8
- };
6
+ /**
7
+ * @type {AlertMixin & ThisType<import('../driver').AndroidDriver>}
8
+ * @satisfies {import('@appium/types').ExternalDriver}
9
+ */
10
+ const AlertMixin = {
11
+ getAlertText() {
12
+ throw new errors.NotYetImplementedError();
13
+ },
9
14
 
10
- commands.setAlertText = async function setAlertText () {
11
- throw new errors.NotYetImplementedError();
12
- };
15
+ setAlertText() {
16
+ throw new errors.NotYetImplementedError();
17
+ },
13
18
 
14
- commands.postAcceptAlert = async function postAcceptAlert () {
15
- throw new errors.NotYetImplementedError();
16
- };
19
+ postAcceptAlert() {
20
+ throw new errors.NotYetImplementedError();
21
+ },
17
22
 
18
- commands.postDismissAlert = async function postDismissAlert () {
19
- throw new errors.NotYetImplementedError();
23
+ postDismissAlert() {
24
+ throw new errors.NotYetImplementedError();
25
+ },
20
26
  };
21
27
 
22
- Object.assign(extensions, commands, helpers);
23
- export { commands, helpers };
24
- export default extensions;
28
+ mixin(AlertMixin);
29
+
30
+ export default AlertMixin;
31
+
32
+ /**
33
+ * @typedef {import('./mixins').AlertMixin} AlertMixin
34
+ */
@@ -1,337 +1,179 @@
1
+ // @ts-check
2
+
3
+ import {util} from '@appium/support';
4
+ import {waitForCondition} from 'asyncbox';
1
5
  import _ from 'lodash';
2
- import { waitForCondition } from 'asyncbox';
3
- import { util } from '@appium/support';
4
- import { APP_STATE } from '../android-helpers';
5
- import { errors } from 'appium/driver';
6
- import { requireArgs } from '../utils';
6
+ import {APP_STATE} from '../helpers';
7
+ import {requireArgs} from '../utils';
8
+ import {mixin} from './mixins';
7
9
 
8
10
  const APP_EXTENSIONS = ['.apk', '.apks'];
9
11
  const RESOLVER_ACTIVITY_NAME = 'android/com.android.internal.app.ResolverActivity';
10
12
 
11
- const commands = {};
12
-
13
13
  /**
14
- * Verify whether an application is installed or not
15
- *
16
- * @param {string} appId - Application package identifier
17
- * @returns {boolean} true if the app is installed
14
+ * @type {import('./mixins').AppManagementMixin & ThisType<import('../driver').AndroidDriver>}
15
+ * @satisfies {import('@appium/types').ExternalDriver}
18
16
  */
19
- commands.isAppInstalled = async function isAppInstalled (appId) {
20
- return await this.adb.isAppInstalled(appId);
21
- };
17
+ const AppManagementMixin = {
18
+ async isAppInstalled(appId) {
19
+ return await /** @type {ADB} */ (this.adb).isAppInstalled(appId);
20
+ },
22
21
 
23
- /**
24
- * @typedef {Object} MobileAppInstalledOptions
25
- * @property {string} appId - Application package identifier. Must be always provided.
26
- */
22
+ async mobileIsAppInstalled(opts) {
23
+ const {appId} = requireArgs('appId', opts);
24
+ return await this.isAppInstalled(appId);
25
+ },
27
26
 
28
- /**
29
- * Verify whether an application is installed or not
30
- *
31
- * @param {MobileAppInstalledOptions} opts
32
- * @returns {boolean} Same as in `isAppInstalled`
33
- */
34
- commands.mobileIsAppInstalled = async function mobileIsAppInstalled (opts = {}) {
35
- const { appId } = requireArgs('appId', opts);
36
- return await this.isAppInstalled(appId);
37
- };
38
-
39
- /**
40
- * Queries the current state of the app.
41
- *
42
- * @param {string} appId - Application package identifier
43
- * @returns {number} The corresponding constant, which describes
44
- * the current application state:
45
- * 0 - is the app is not installed
46
- * 1 - if the app is installed, but is not running
47
- * 3 - if the app is running in the background
48
- * 4 - if the app is running in the foreground
49
- */
50
- commands.queryAppState = async function queryAppState (appId) {
51
- this.log.info(`Querying the state of '${appId}'`);
52
- if (!await this.adb.isAppInstalled(appId)) {
53
- return APP_STATE.NOT_INSTALLED;
54
- }
55
- if (!await this.adb.processExists(appId)) {
56
- return APP_STATE.NOT_RUNNING;
57
- }
58
- const appIdRe = new RegExp(`\\b${_.escapeRegExp(appId)}/`);
59
- for (const line of (await this.adb.dumpWindows()).split('\n')) {
60
- if (appIdRe.test(line) && ['mCurrentFocus', 'mFocusedApp'].some((x) => line.includes(x))) {
61
- return APP_STATE.RUNNING_IN_FOREGROUND;
27
+ async queryAppState(appId) {
28
+ const adb = /** @type {ADB} */ (this.adb);
29
+ this.log.info(`Querying the state of '${appId}'`);
30
+ if (!(await adb.isAppInstalled(appId))) {
31
+ return APP_STATE.NOT_INSTALLED;
32
+ }
33
+ if (!(await adb.processExists(appId))) {
34
+ return APP_STATE.NOT_RUNNING;
35
+ }
36
+ const appIdRe = new RegExp(`\\b${_.escapeRegExp(appId)}/`);
37
+ for (const line of (await adb.dumpWindows()).split('\n')) {
38
+ if (appIdRe.test(line) && ['mCurrentFocus', 'mFocusedApp'].some((x) => line.includes(x))) {
39
+ return APP_STATE.RUNNING_IN_FOREGROUND;
40
+ }
41
+ }
42
+ return APP_STATE.RUNNING_IN_BACKGROUND;
43
+ },
44
+
45
+ async mobileQueryAppState(opts) {
46
+ const {appId} = requireArgs('appId', opts);
47
+ return await this.queryAppState(appId);
48
+ },
49
+
50
+ async activateApp(appId) {
51
+ const adb = /** @type {ADB} */ (this.adb);
52
+ this.log.debug(`Activating '${appId}'`);
53
+ const apiLevel = await adb.getApiLevel();
54
+ // Fallback to Monkey in older APIs
55
+ if (apiLevel < 24) {
56
+ // 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
57
+ // but '--pct-syskeys 0' could cause another background process issue. https://github.com/appium/appium/issues/16941#issuecomment-1129837285
58
+ const cmd = ['monkey', '-p', appId, '-c', 'android.intent.category.LAUNCHER', '1'];
59
+ let output = '';
60
+ try {
61
+ output = await adb.shell(cmd);
62
+ this.log.debug(`Command stdout: ${output}`);
63
+ } catch (e) {
64
+ this.log.errorAndThrow(
65
+ `Cannot activate '${appId}'. Original error: ${/** @type {Error} */ (e).message}`
66
+ );
67
+ }
68
+ if (output.includes('monkey aborted')) {
69
+ this.log.errorAndThrow(`Cannot activate '${appId}'. Are you sure it is installed?`);
70
+ }
71
+ return;
62
72
  }
63
- }
64
- return APP_STATE.RUNNING_IN_BACKGROUND;
65
- };
66
73
 
67
- /**
68
- * @typedef {Object} MobileQueryAppStateOptions
69
- * @property {string} appId - Application package identifier. Must be always provided.
70
- */
74
+ let activityName = await adb.resolveLaunchableActivity(appId);
75
+ if (activityName === RESOLVER_ACTIVITY_NAME) {
76
+ // https://github.com/appium/appium/issues/17128
77
+ this.log.debug(
78
+ `The launchable activity name of '${appId}' was resolved to '${activityName}'. ` +
79
+ `Switching the resolver to not use cmd`
80
+ );
81
+ activityName = await adb.resolveLaunchableActivity(appId, {preferCmd: false});
82
+ }
71
83
 
72
- /**
73
- * Queries the current state of the app.
74
- *
75
- * @param {MobileQueryAppStateOptions} opts
76
- * @returns {number} Same as in `queryAppState`
77
- */
78
- commands.mobileQueryAppState = async function mobileQueryAppState (opts = {}) {
79
- const { appId } = requireArgs('appId', opts);
80
- return await this.queryAppState(appId);
81
- };
84
+ const stdout = await adb.shell([
85
+ 'am',
86
+ apiLevel < 26 ? 'start' : 'start-activity',
87
+ '-a',
88
+ 'android.intent.action.MAIN',
89
+ '-c',
90
+ 'android.intent.category.LAUNCHER',
91
+ // FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
92
+ // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_NEW_TASK
93
+ // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
94
+ '-f',
95
+ '0x10200000',
96
+ '-n',
97
+ activityName,
98
+ ]);
99
+ this.log.debug(stdout);
100
+ if (/^error:/im.test(stdout)) {
101
+ throw new Error(`Cannot activate '${appId}'. Original error: ${stdout}`);
102
+ }
103
+ },
104
+
105
+ async mobileActivateApp(opts) {
106
+ const {appId} = requireArgs('appId', opts);
107
+ return await this.activateApp(appId);
108
+ },
109
+
110
+ async removeApp(appId, opts = {}) {
111
+ return await /** @type {ADB} */ (this.adb).uninstallApk(appId, opts);
112
+ },
113
+
114
+ async mobileRemoveApp(opts) {
115
+ const {appId} = requireArgs('appId', opts);
116
+ return await this.removeApp(appId, opts);
117
+ },
118
+
119
+ async terminateApp(appId, options = {}) {
120
+ const adb = /** @type {ADB} */ (this.adb);
121
+ this.log.info(`Terminating '${appId}'`);
122
+ if (!(await adb.processExists(appId))) {
123
+ this.log.info(`The app '${appId}' is not running`);
124
+ return false;
125
+ }
126
+ await adb.forceStop(appId);
127
+ const timeout =
128
+ util.hasValue(options.timeout) && !Number.isNaN(options.timeout)
129
+ ? parseInt(String(options.timeout), 10)
130
+ : 500;
131
+
132
+ if (timeout <= 0) {
133
+ this.log.info(
134
+ `'${appId}' has been terminated. Skip checking the application process state ` +
135
+ `since the timeout was set as ${timeout}ms`
136
+ );
137
+ return true;
138
+ }
82
139
 
83
- /**
84
- * Activates the given application or launches it if necessary.
85
- * The action literally simulates
86
- * clicking the corresponding application icon on the dashboard.
87
- *
88
- * @param {string} appId - Application package identifier
89
- * @throws {Error} If the app cannot be activated
90
- */
91
- commands.activateApp = async function activateApp (appId) {
92
- this.log.debug(`Activating '${appId}'`);
93
- const apiLevel = await this.adb.getApiLevel();
94
- // Fallback to Monkey in older APIs
95
- if (apiLevel < 24) {
96
- // 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
97
- // but '--pct-syskeys 0' could cause another background process issue. https://github.com/appium/appium/issues/16941#issuecomment-1129837285
98
- const cmd = ['monkey',
99
- '-p', appId,
100
- '-c', 'android.intent.category.LAUNCHER',
101
- '1'];
102
- let output = '';
103
140
  try {
104
- output = await this.adb.shell(cmd);
105
- this.log.debug(`Command stdout: ${output}`);
141
+ await waitForCondition(
142
+ async () => (await this.queryAppState(appId)) <= APP_STATE.NOT_RUNNING,
143
+ {waitMs: timeout, intervalMs: 100}
144
+ );
106
145
  } catch (e) {
107
- this.log.errorAndThrow(`Cannot activate '${appId}'. Original error: ${e.message}`);
108
- }
109
- if (output.includes('monkey aborted')) {
110
- this.log.errorAndThrow(`Cannot activate '${appId}'. Are you sure it is installed?`);
146
+ this.log.errorAndThrow(`'${appId}' is still running after ${timeout}ms timeout`);
111
147
  }
112
- return;
113
- }
114
-
115
- let activityName = await this.adb.resolveLaunchableActivity(appId);
116
- if (activityName === RESOLVER_ACTIVITY_NAME) {
117
- // https://github.com/appium/appium/issues/17128
118
- this.log.debug(
119
- `The launchable activity name of '${appId}' was resolved to '${activityName}'. ` +
120
- `Switching the resolver to not use cmd`
121
- );
122
- activityName = await this.adb.resolveLaunchableActivity(appId, {preferCmd: false});
123
- }
124
-
125
- const stdout = await this.adb.shell([
126
- 'am', (apiLevel < 26) ? 'start' : 'start-activity',
127
- '-a', 'android.intent.action.MAIN',
128
- '-c', 'android.intent.category.LAUNCHER',
129
- // FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
130
- // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_NEW_TASK
131
- // https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
132
- '-f', '0x10200000',
133
- '-n', activityName,
134
- ]);
135
- this.log.debug(stdout);
136
- if (/^error:/mi.test(stdout)) {
137
- throw new Error(`Cannot activate '${appId}'. Original error: ${stdout}`);
138
- }
139
- };
140
-
141
- /**
142
- * @typedef {Object} MobileActivateAppOptions
143
- * @property {string} appId - Application package identifier. Must be always provided.
144
- */
145
-
146
- /**
147
- * Activates the given application or launches it if necessary.
148
- * The action literally simulates
149
- * clicking the corresponding application icon on the dashboard.
150
- *
151
- * @param {MobileActivateAppOptions} opts
152
- * @throws {Error} If the app cannot be activated
153
- */
154
- commands.mobileActivateApp = async function mobileActivateApp (opts = {}) {
155
- const { appId } = requireArgs('appId', opts);
156
- return await this.activateApp(appId);
157
- };
158
-
159
- /**
160
- * @typedef {Object} UninstallOptions
161
- * @property {number} timeout [20000] - The count of milliseconds to wait until the
162
- * app is uninstalled.
163
- * @property {boolean} keepData [false] - Set to true in order to keep the
164
- * application data and cache folders after uninstall.
165
- */
166
-
167
- /**
168
- * Remove the corresponding application if is installed.
169
- * The call is ignored if the app is not installed.
170
- *
171
- * @param {string} appId - Application package identifier
172
- * @param {?UninstallOptions} options - The set of removal options
173
- * @returns {boolean} True if the package was found on the device and
174
- * successfully uninstalled.
175
- */
176
- commands.removeApp = async function removeApp (appId, options = {}) {
177
- return await this.adb.uninstallApk(appId, options);
178
- };
179
-
180
- /**
181
- * @typedef {Object} MobileRemoveAppOptions
182
- * @property {string} appId - Application package identifier. Must be always provided.
183
- */
184
-
185
- /**
186
- * Remove the corresponding application if is installed.
187
- * The call is ignored if the app is not installed.
188
- *
189
- * @param {MobileRemoveAppOptions} opts
190
- * @returns {boolean} Same as in `removeApp`
191
- */
192
- commands.mobileRemoveApp = async function mobileRemoveApp (opts = {}) {
193
- const { appId } = requireArgs('appId', opts);
194
- return await this.removeApp(appId, opts);
195
- };
196
-
197
- /**
198
- * @typedef {Object} TerminateOptions
199
- * @property {number|string} timeout [500] - The count of milliseconds to wait until the
200
- * app is terminated. The method will skip
201
- * checking the app state check if the timeout
202
- * was lower or equal to zero. Then, the return
203
- * value will be true.
204
- */
205
-
206
- /**
207
- * Terminates the app if it is running. If the given timeout was lower or equal to zero,
208
- * it returns true after terminating the app without checking the app state.
209
- *
210
- * @param {string} appId - Application package identifier
211
- * @param {?TerminateOptions} options - The set of application termination options
212
- * @returns {boolean} True if the app has been successfully terminated.
213
- * @throws {Error} if the app has not been terminated within the given timeout.
214
- */
215
- commands.terminateApp = async function terminateApp (appId, options = {}) {
216
- this.log.info(`Terminating '${appId}'`);
217
- if (!(await this.adb.processExists(appId))) {
218
- this.log.info(`The app '${appId}' is not running`);
219
- return false;
220
- }
221
- await this.adb.forceStop(appId);
222
- const timeout = util.hasValue(options.timeout) && !isNaN(options.timeout) ? parseInt(options.timeout, 10) : 500;
223
-
224
- if (timeout <= 0) {
225
- this.log.info(`'${appId}' has been terminated. Skip checking the application process state ` +
226
- `since the timeout was set as ${timeout}ms`);
148
+ this.log.info(`'${appId}' has been successfully terminated`);
227
149
  return true;
228
- }
229
-
230
- try {
231
- await waitForCondition(async () => await this.queryAppState(appId) <= APP_STATE.NOT_RUNNING,
232
- {waitMs: timeout, intervalMs: 100});
233
- } catch (e) {
234
- this.log.errorAndThrow(`'${appId}' is still running after ${timeout}ms timeout`);
235
- }
236
- this.log.info(`'${appId}' has been successfully terminated`);
237
- return true;
150
+ },
151
+
152
+ async mobileTerminateApp(opts) {
153
+ const {appId} = requireArgs('appId', opts);
154
+ return await this.terminateApp(appId, opts);
155
+ },
156
+
157
+ async installApp(appPath, opts) {
158
+ const localPath = await this.helpers.configureApp(appPath, APP_EXTENSIONS);
159
+ await /** @type {ADB} */ (this.adb).install(localPath, opts);
160
+ },
161
+
162
+ async mobileInstallApp(opts) {
163
+ const {appPath} = requireArgs('appPath', opts);
164
+ return await this.installApp(appPath, opts);
165
+ },
166
+
167
+ async mobileClearApp(opts) {
168
+ const {appId} = requireArgs('appId', opts);
169
+ await /** @type {ADB} */ (this.adb).clear(appId);
170
+ },
238
171
  };
239
172
 
240
- /**
241
- * @typedef {Object} MobileTerminateAppOptions
242
- * @property {string} appId - Application package identifier. Must be always provided.
243
- * @property {number|string} timeout [500] - The count of milliseconds to wait until the
244
- * app is terminated.
245
- */
173
+ mixin(AppManagementMixin);
246
174
 
247
- /**
248
- * Terminates the app if it is running.
249
- *
250
- * @param {MobileTerminateAppOptions} opts
251
- * @returns {boolean} Same as in `terminateApp`
252
- * @throws {Error} if the app has not been terminated within the given timeout.
253
- */
254
- commands.mobileTerminateApp = async function mobileTerminateApp (opts = {}) {
255
- const { appId } = requireArgs('appId', opts);
256
- return await this.terminateApp(appId, opts);
257
- };
258
-
259
- /**
260
- * @typedef {Object} InstallOptions
261
- * @property {number} timeout [60000] - The count of milliseconds to wait until the
262
- * app is installed.
263
- * @property {boolean} allowTestPackages [false] - Set to true in order to allow test
264
- * packages installation.
265
- * @property {boolean} useSdcard [false] - Set to true to install the app on sdcard
266
- * instead of the device memory.
267
- * @property {boolean} grantPermissions [false] - Set to true in order to grant all the
268
- * permissions requested in the application's manifest
269
- * automatically after the installation is completed
270
- * under Android 6+.
271
- * @property {boolean} replace [true] - Set it to false if you don't want
272
- * the application to be upgraded/reinstalled
273
- * if it is already present on the device.
274
- */
275
-
276
- /**
277
- * Installs the given application to the device under test
278
- *
279
- * @param {string} appPath - The local apk path or a remote url
280
- * @param {?InstallOptions} options - The set of installation options
281
- * @throws {Error} if the given apk does not exist or is not reachable
282
- */
283
- commands.installApp = async function installApp (appPath, options = {}) {
284
- const localPath = await this.helpers.configureApp(appPath, APP_EXTENSIONS);
285
- await this.adb.install(localPath, options);
286
- };
287
-
288
- /**
289
- * @typedef {Object} MobileInstallAppOptions
290
- * @property {string} appPath - The local apk path or a remote url. Must be always provided.
291
- * @property {number} timeout [60000] - The count of milliseconds to wait until the
292
- * app is installed.
293
- * @property {boolean} allowTestPackages [false] - Set to true in order to allow test
294
- * packages installation.
295
- * @property {boolean} useSdcard [false] - Set to true to install the app on sdcard
296
- * instead of the device memory.
297
- * @property {boolean} grantPermissions [false] - Set to true in order to grant all the
298
- * permissions requested in the application's manifest
299
- * automatically after the installation is completed
300
- * under Android 6+.
301
- * @property {boolean} replace [true] - Set it to false if you don't want
302
- * the application to be upgraded/reinstalled
303
- * if it is already present on the device.
304
- */
305
-
306
- /**
307
- * Installs the given application to the device under test
308
- *
309
- * @param {MobileInstallAppOptions} opts
310
- * @throws {Error} if the given apk does not exist or is not reachable
311
- */
312
- commands.mobileInstallApp = async function mobileInstallApp (opts = {}) {
313
- const { appPath } = requireArgs('appPath', opts);
314
- return await this.installApp(appPath, opts);
315
- };
175
+ export default AppManagementMixin;
316
176
 
317
177
  /**
318
- * @typedef {Object} ClearAppOptions
319
- * @property {!string} appId The identifier of the application package to be cleared
178
+ * @typedef {import('appium-adb').ADB} ADB
320
179
  */
321
-
322
- /**
323
- * Deletes all data associated with a package.
324
- *
325
- * @param {ClearAppOptions} opts
326
- * @throws {Error} If cleaning of the app data fails
327
- */
328
- commands.mobileClearApp = async function mobileClearApp (opts = {}) {
329
- const {appId} = opts;
330
- if (!appId) {
331
- throw new errors.InvalidArgumentError(`The 'appId' argument is required`);
332
- }
333
- await this.adb.clear(appId);
334
- };
335
-
336
- export { commands };
337
- export default commands;