appium-android-driver 7.8.2 → 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 +32 -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 +115 -3
  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 +142 -10
  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 -442
  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 -604
@@ -1,14 +1,179 @@
1
- // @ts-check
2
-
3
1
  import {DEFAULT_WS_PATHNAME_PREFIX, BaseDriver} from 'appium/driver';
4
2
  import _ from 'lodash';
5
3
  import os from 'node:os';
6
4
  import WebSocket from 'ws';
7
- import log from '../logger';
8
- import {mixin} from './mixins';
9
5
 
10
6
  const GET_SERVER_LOGS_FEATURE = 'get_server_logs';
11
7
 
8
+ export const supportedLogTypes = {
9
+ logcat: {
10
+ description: 'Logs for Android applications on real device and emulators via ADB',
11
+ /**
12
+ *
13
+ * @param {import('../driver').AndroidDriver} self
14
+ * @returns
15
+ */
16
+ getter: (self) => /** @type {ADB} */ (self.adb).getLogcatLogs(),
17
+ },
18
+ bugreport: {
19
+ description: `'adb bugreport' output for advanced issues diagnostic`,
20
+ /**
21
+ *
22
+ * @param {import('../driver').AndroidDriver} self
23
+ * @returns
24
+ */
25
+ getter: async (self) => {
26
+ const output = await /** @type {ADB} */ (self.adb).bugreport();
27
+ const timestamp = Date.now();
28
+ return output.split(os.EOL).map((x) => toLogRecord(timestamp, 'ALL', x));
29
+ },
30
+ },
31
+ server: {
32
+ description: 'Appium server logs',
33
+ /**
34
+ *
35
+ * @param {import('../driver').AndroidDriver} self
36
+ * @returns
37
+ */
38
+ getter: (self) => {
39
+ self.ensureFeatureEnabled(GET_SERVER_LOGS_FEATURE);
40
+ const timestamp = Date.now();
41
+ return self.log
42
+ .unwrap()
43
+ .record.map((x) =>
44
+ toLogRecord(
45
+ timestamp,
46
+ 'ALL',
47
+ _.isEmpty(x.prefix) ? x.message : `[${x.prefix}] ${x.message}`,
48
+ ),
49
+ );
50
+ },
51
+ },
52
+ };
53
+
54
+ /**
55
+ * Starts Android logcat broadcast websocket on the same host and port
56
+ * where Appium server is running at `/ws/session/:sessionId:/appium/logcat` endpoint. The method
57
+ * will return immediately if the web socket is already listening.
58
+ *
59
+ * Each connected websocket listener will receive logcat log lines
60
+ * as soon as they are visible to Appium.
61
+ *
62
+ * @this {import('../driver').AndroidDriver}
63
+ * @returns {Promise<void>}
64
+ */
65
+ export async function mobileStartLogsBroadcast() {
66
+ const server = /** @type {import('@appium/types').AppiumServer} */ (this.server);
67
+ const pathname = WEBSOCKET_ENDPOINT(/** @type {string} */ (this.sessionId));
68
+ if (!_.isEmpty(await server.getWebSocketHandlers(pathname))) {
69
+ this.log.debug(`The logcat broadcasting web socket server is already listening at ${pathname}`);
70
+ return;
71
+ }
72
+
73
+ this.log.info(
74
+ `Starting logcat broadcasting on web socket server ` +
75
+ `${JSON.stringify(server.address())} to ${pathname}`,
76
+ );
77
+ // https://github.com/websockets/ws/blob/master/doc/ws.md
78
+ const wss = new WebSocket.Server({
79
+ noServer: true,
80
+ });
81
+ wss.on('connection', (ws, req) => {
82
+ if (req) {
83
+ const remoteIp = _.isEmpty(req.headers['x-forwarded-for'])
84
+ ? req.connection?.remoteAddress
85
+ : req.headers['x-forwarded-for'];
86
+ this.log.debug(`Established a new logcat listener web socket connection from ${remoteIp}`);
87
+ } else {
88
+ this.log.debug('Established a new logcat listener web socket connection');
89
+ }
90
+
91
+ if (_.isEmpty(this._logcatWebsocketListener)) {
92
+ this._logcatWebsocketListener = (logRecord) => {
93
+ if (ws?.readyState === WebSocket.OPEN) {
94
+ ws.send(logRecord.message);
95
+ }
96
+ };
97
+ }
98
+ this.adb.setLogcatListener(this._logcatWebsocketListener);
99
+
100
+ ws.on('close', (code, reason) => {
101
+ if (!_.isEmpty(this._logcatWebsocketListener)) {
102
+ try {
103
+ this.adb.removeLogcatListener(this._logcatWebsocketListener);
104
+ } catch (ign) {}
105
+ this._logcatWebsocketListener = undefined;
106
+ }
107
+
108
+ let closeMsg = 'Logcat listener web socket is closed.';
109
+ if (!_.isEmpty(code)) {
110
+ closeMsg += ` Code: ${code}.`;
111
+ }
112
+ if (!_.isEmpty(reason)) {
113
+ closeMsg += ` Reason: ${reason.toString()}.`;
114
+ }
115
+ this.log.debug(closeMsg);
116
+ });
117
+ });
118
+ await server.addWebSocketHandler(pathname, /** @type {import('@appium/types').WSServer} */ (wss));
119
+ }
120
+
121
+ /**
122
+ * Stops the previously started logcat broadcasting wesocket server.
123
+ * This method will return immediately if no server is running.
124
+ *
125
+ * @this {import('../driver').AndroidDriver}
126
+ * @returns {Promise<void>}
127
+ */
128
+ export async function mobileStopLogsBroadcast() {
129
+ const pathname = WEBSOCKET_ENDPOINT(/** @type {string} */ (this.sessionId));
130
+ const server = /** @type {import('@appium/types').AppiumServer} */ (this.server);
131
+ if (_.isEmpty(await server.getWebSocketHandlers(pathname))) {
132
+ return;
133
+ }
134
+
135
+ this.log.debug(
136
+ `Stopping logcat broadcasting on web socket server ` +
137
+ `${JSON.stringify(server.address())} to ${pathname}`,
138
+ );
139
+ await server.removeWebSocketHandler(pathname);
140
+ }
141
+
142
+ /**
143
+ * @this {import('../driver').AndroidDriver}
144
+ * @returns {Promise<string[]>}
145
+ */
146
+ export async function getLogTypes() {
147
+ // XXX why doesn't `super` work here?
148
+ const nativeLogTypes = await BaseDriver.prototype.getLogTypes.call(this);
149
+ if (this.isWebContext()) {
150
+ const webLogTypes = /** @type {string[]} */ (
151
+ await /** @type {import('appium-chromedriver').Chromedriver} */ (
152
+ this.chromedriver
153
+ ).jwproxy.command('/log/types', 'GET')
154
+ );
155
+ return [...nativeLogTypes, ...webLogTypes];
156
+ }
157
+ return nativeLogTypes;
158
+ }
159
+
160
+ /**
161
+ * @this {import('../driver').AndroidDriver}
162
+ * @param {string} logType
163
+ * @returns {Promise<any>}
164
+ */
165
+ export async function getLog(logType) {
166
+ if (this.isWebContext() && !_.keys(this.supportedLogTypes).includes(logType)) {
167
+ return await /** @type {import('appium-chromedriver').Chromedriver} */ (
168
+ this.chromedriver
169
+ ).jwproxy.command('/log', 'POST', {type: logType});
170
+ }
171
+ // XXX why doesn't `super` work here?
172
+ return await BaseDriver.prototype.getLog.call(this, logType);
173
+ }
174
+
175
+ // #region Internal helpers
176
+
12
177
  /**
13
178
  * @param {string} sessionId
14
179
  * @returns {string}
@@ -30,169 +195,8 @@ function toLogRecord(timestamp, level, message) {
30
195
  message,
31
196
  };
32
197
  }
33
- /**
34
- * @type {import('./mixins').LogMixin & ThisType<import('../driver').AndroidDriver>}
35
- * @satisfies {import('@appium/types').ExternalDriver}
36
- */
37
- const LogMixin = {
38
- supportedLogTypes: {
39
- logcat: {
40
- description: 'Logs for Android applications on real device and emulators via ADB',
41
- /**
42
- *
43
- * @param {import('../driver').AndroidDriver} self
44
- * @returns
45
- */
46
- getter: (self) => /** @type {ADB} */ (self.adb).getLogcatLogs(),
47
- },
48
- bugreport: {
49
- description: `'adb bugreport' output for advanced issues diagnostic`,
50
- /**
51
- *
52
- * @param {import('../driver').AndroidDriver} self
53
- * @returns
54
- */
55
- getter: async (self) => {
56
- const output = await /** @type {ADB} */ (self.adb).bugreport();
57
- const timestamp = Date.now();
58
- return output.split(os.EOL).map((x) => toLogRecord(timestamp, 'ALL', x));
59
- },
60
- },
61
- server: {
62
- description: 'Appium server logs',
63
- /**
64
- *
65
- * @param {import('../driver').AndroidDriver} self
66
- * @returns
67
- */
68
- getter: (self) => {
69
- self.ensureFeatureEnabled(GET_SERVER_LOGS_FEATURE);
70
- const timestamp = Date.now();
71
- return log
72
- .unwrap()
73
- .record.map((x) =>
74
- toLogRecord(
75
- timestamp,
76
- 'ALL',
77
- _.isEmpty(x.prefix) ? x.message : `[${x.prefix}] ${x.message}`
78
- )
79
- );
80
- },
81
- },
82
- },
83
-
84
- /**
85
- * Starts Android logcat broadcast websocket on the same host and port
86
- * where Appium server is running at `/ws/session/:sessionId:/appium/logcat` endpoint. The method
87
- * will return immediately if the web socket is already listening.
88
- *
89
- * Each connected websocket listener will receive logcat log lines
90
- * as soon as they are visible to Appium.
91
- */
92
- async mobileStartLogsBroadcast() {
93
- const server = /** @type {import('@appium/types').AppiumServer} */ (this.server);
94
- const pathname = WEBSOCKET_ENDPOINT(/** @type {string} */ (this.sessionId));
95
- if (!_.isEmpty(await server.getWebSocketHandlers(pathname))) {
96
- log.debug(`The logcat broadcasting web socket server is already listening at ${pathname}`);
97
- return;
98
- }
99
-
100
- log.info(
101
- `Starting logcat broadcasting on web socket server ` +
102
- `${JSON.stringify(server.address())} to ${pathname}`
103
- );
104
- // https://github.com/websockets/ws/blob/master/doc/ws.md
105
- const wss = new WebSocket.Server({
106
- noServer: true,
107
- });
108
- wss.on('connection', (ws, req) => {
109
- if (req) {
110
- const remoteIp = _.isEmpty(req.headers['x-forwarded-for'])
111
- ? req.connection?.remoteAddress
112
- : req.headers['x-forwarded-for'];
113
- log.debug(`Established a new logcat listener web socket connection from ${remoteIp}`);
114
- } else {
115
- log.debug('Established a new logcat listener web socket connection');
116
- }
117
-
118
- if (_.isEmpty(this._logcatWebsocketListener)) {
119
- this._logcatWebsocketListener = (logRecord) => {
120
- if (ws?.readyState === WebSocket.OPEN) {
121
- ws.send(logRecord.message);
122
- }
123
- };
124
- }
125
- this.adb.setLogcatListener(this._logcatWebsocketListener);
126
-
127
- ws.on('close', (code, reason) => {
128
- if (!_.isEmpty(this._logcatWebsocketListener)) {
129
- try {
130
- this.adb.removeLogcatListener(this._logcatWebsocketListener);
131
- } catch (ign) {}
132
- this._logcatWebsocketListener = undefined;
133
- }
134
-
135
- let closeMsg = 'Logcat listener web socket is closed.';
136
- if (!_.isEmpty(code)) {
137
- closeMsg += ` Code: ${code}.`;
138
- }
139
- if (!_.isEmpty(reason)) {
140
- closeMsg += ` Reason: ${reason.toString()}.`;
141
- }
142
- log.debug(closeMsg);
143
- });
144
- });
145
- await server.addWebSocketHandler(
146
- pathname,
147
- /** @type {import('@appium/types').WSServer} */ (wss)
148
- );
149
- },
150
- /**
151
- * Stops the previously started logcat broadcasting wesocket server.
152
- * This method will return immediately if no server is running.
153
- */
154
- async mobileStopLogsBroadcast() {
155
- const pathname = WEBSOCKET_ENDPOINT(/** @type {string} */ (this.sessionId));
156
- const server = /** @type {import('@appium/types').AppiumServer} */ (this.server);
157
- if (_.isEmpty(await server.getWebSocketHandlers(pathname))) {
158
- return;
159
- }
160
-
161
- log.debug(
162
- `Stopping logcat broadcasting on web socket server ` +
163
- `${JSON.stringify(server.address())} to ${pathname}`
164
- );
165
- await server.removeWebSocketHandler(pathname);
166
- },
167
-
168
- async getLogTypes() {
169
- // XXX why doesn't `super` work here?
170
- const nativeLogTypes = await BaseDriver.prototype.getLogTypes.call(this);
171
- if (this.isWebContext()) {
172
- const webLogTypes = /** @type {string[]} */ (
173
- await /** @type {import('appium-chromedriver').Chromedriver} */ (
174
- this.chromedriver
175
- ).jwproxy.command('/log/types', 'GET')
176
- );
177
- return [...nativeLogTypes, ...webLogTypes];
178
- }
179
- return nativeLogTypes;
180
- },
181
-
182
- async getLog(logType) {
183
- if (this.isWebContext() && !_.keys(this.supportedLogTypes).includes(logType)) {
184
- return await /** @type {import('appium-chromedriver').Chromedriver} */ (
185
- this.chromedriver
186
- ).jwproxy.command('/log', 'POST', {type: logType});
187
- }
188
- // XXX why doesn't `super` work here?
189
- return await BaseDriver.prototype.getLog.call(this, logType);
190
- },
191
- };
192
-
193
- mixin(LogMixin);
194
198
 
195
- export default LogMixin;
199
+ // #endregion
196
200
 
197
201
  /**
198
202
  * @typedef {import('appium-adb').ADB} ADB
@@ -2,13 +2,85 @@ import {fs, net, util} from '@appium/support';
2
2
  import _ from 'lodash';
3
3
  import moment from 'moment';
4
4
  import path from 'node:path';
5
- import {mixin} from './mixins';
6
5
 
7
6
  // https://github.com/appium/io.appium.settings#internal-audio--video-recording
8
7
  const DEFAULT_EXT = '.mp4';
9
8
  const MIN_API_LEVEL = 29;
10
9
  const DEFAULT_FILENAME_FORMAT = 'YYYY-MM-DDTHH-mm-ss';
11
10
 
11
+ /**
12
+ * @this {import('../driver').AndroidDriver}
13
+ * @param {import('./types').StartMediaProjectionRecordingOpts} [options={}]
14
+ * @returns {Promise<boolean>}
15
+ */
16
+ export async function mobileStartMediaProjectionRecording(options = {}) {
17
+ await verifyMediaProjectionRecordingIsSupported(this.adb);
18
+
19
+ const {resolution, priority, maxDurationSec, filename} = options;
20
+ const recorder = this.settingsApp.makeMediaProjectionRecorder();
21
+ const fname = adjustMediaExtension(filename || moment().format(DEFAULT_FILENAME_FORMAT));
22
+ const didStart = await recorder.start({
23
+ resolution,
24
+ priority,
25
+ maxDurationSec,
26
+ filename: fname,
27
+ });
28
+ if (didStart) {
29
+ this.log.info(`A new media projection recording '${fname}' has been successfully started`);
30
+ } else {
31
+ this.log.info(
32
+ 'Another media projection recording is already in progress. There is nothing to start',
33
+ );
34
+ }
35
+ return didStart;
36
+ }
37
+
38
+ /**
39
+ * @this {import('../driver').AndroidDriver}
40
+ * @returns {Promise<boolean>}
41
+ */
42
+ export async function mobileIsMediaProjectionRecordingRunning() {
43
+ await verifyMediaProjectionRecordingIsSupported(this.adb);
44
+
45
+ const recorder = this.settingsApp.makeMediaProjectionRecorder();
46
+ return await recorder.isRunning();
47
+ }
48
+
49
+ /**
50
+ * @this {import('../driver').AndroidDriver}
51
+ * @param {import('./types').StopMediaProjectionRecordingOpts} [options={}]
52
+ * @returns {Promise<string>}
53
+ */
54
+ export async function mobileStopMediaProjectionRecording(options = {}) {
55
+ await verifyMediaProjectionRecordingIsSupported(this.adb);
56
+
57
+ const recorder = this.settingsApp.makeMediaProjectionRecorder();
58
+ if (await recorder.stop()) {
59
+ this.log.info('Successfully stopped a media projection recording. Pulling the recorded media');
60
+ } else {
61
+ this.log.info('Media projection recording is not running. There is nothing to stop');
62
+ }
63
+ const recentRecordingPath = await recorder.pullRecent();
64
+ if (!recentRecordingPath) {
65
+ throw new Error(`No recent media projection recording have been found. Did you start any?`);
66
+ }
67
+
68
+ const {remotePath} = options;
69
+ if (_.isEmpty(remotePath)) {
70
+ const {size} = await fs.stat(recentRecordingPath);
71
+ this.log.debug(
72
+ `The size of the resulting media projection recording is ${util.toReadableSizeString(size)}`,
73
+ );
74
+ }
75
+ try {
76
+ return await uploadRecordedMedia(recentRecordingPath, remotePath, options);
77
+ } finally {
78
+ await fs.rimraf(path.dirname(recentRecordingPath));
79
+ }
80
+ }
81
+
82
+ // #region Internal helpers
83
+
12
84
  /**
13
85
  *
14
86
  * @param {string} localFile
@@ -65,79 +137,12 @@ async function verifyMediaProjectionRecordingIsSupported(adb) {
65
137
  if (apiLevel < MIN_API_LEVEL) {
66
138
  throw new Error(
67
139
  `Media projection-based recording is not available on API Level ${apiLevel}. ` +
68
- `Minimum required API Level is ${MIN_API_LEVEL}.`
140
+ `Minimum required API Level is ${MIN_API_LEVEL}.`,
69
141
  );
70
142
  }
71
143
  }
72
144
 
73
- /**
74
- * @type {import('./mixins').MediaProjectionMixin & ThisType<import('../driver').AndroidDriver>}
75
- * @satisfies {import('@appium/types').ExternalDriver}
76
- */
77
- const MediaProjectionMixin = {
78
- async mobileStartMediaProjectionRecording(options = {}) {
79
- await verifyMediaProjectionRecordingIsSupported(this.adb);
80
-
81
- const {resolution, priority, maxDurationSec, filename} = options;
82
- const recorder = this.settingsApp.makeMediaProjectionRecorder();
83
- const fname = adjustMediaExtension(filename || moment().format(DEFAULT_FILENAME_FORMAT));
84
- const didStart = await recorder.start({
85
- resolution,
86
- priority,
87
- maxDurationSec,
88
- filename: fname,
89
- });
90
- if (didStart) {
91
- this.log.info(`A new media projection recording '${fname}' has been successfully started`);
92
- } else {
93
- this.log.info(
94
- 'Another media projection recording is already in progress. There is nothing to start'
95
- );
96
- }
97
- return didStart;
98
- },
99
-
100
- async mobileIsMediaProjectionRecordingRunning() {
101
- await verifyMediaProjectionRecordingIsSupported(this.adb);
102
-
103
- const recorder = this.settingsApp.makeMediaProjectionRecorder();
104
- return await recorder.isRunning();
105
- },
106
-
107
- async mobileStopMediaProjectionRecording(options = {}) {
108
- await verifyMediaProjectionRecordingIsSupported(this.adb);
109
-
110
- const recorder = this.settingsApp.makeMediaProjectionRecorder();
111
- if (await recorder.stop()) {
112
- this.log.info(
113
- 'Successfully stopped a media projection recording. Pulling the recorded media'
114
- );
115
- } else {
116
- this.log.info('Media projection recording is not running. There is nothing to stop');
117
- }
118
- const recentRecordingPath = await recorder.pullRecent();
119
- if (!recentRecordingPath) {
120
- throw new Error(`No recent media projection recording have been found. Did you start any?`);
121
- }
122
-
123
- const {remotePath} = options;
124
- if (_.isEmpty(remotePath)) {
125
- const {size} = await fs.stat(recentRecordingPath);
126
- this.log.debug(
127
- `The size of the resulting media projection recording is ${util.toReadableSizeString(size)}`
128
- );
129
- }
130
- try {
131
- return await uploadRecordedMedia(recentRecordingPath, remotePath, options);
132
- } finally {
133
- await fs.rimraf(path.dirname(recentRecordingPath));
134
- }
135
- },
136
- };
137
-
138
- mixin(MediaProjectionMixin);
139
-
140
- export default MediaProjectionMixin;
145
+ // #endregion
141
146
 
142
147
  /**
143
148
  * @typedef {import('appium-adb').ADB} ADB
@@ -1,38 +1,26 @@
1
1
  import {errors} from 'appium/driver';
2
- import {mixin} from './mixins';
3
2
 
4
3
  /**
5
- * @type {import('./mixins').MemoryMixin & ThisType<import('../driver').AndroidDriver>}
6
- * @satisfies {import('@appium/types').ExternalDriver}
4
+ * Simulates the onTrimMemory() event for the given package.
5
+ * Read https://developer.android.com/topic/performance/memory
6
+ * for more details.
7
+ *
8
+ * @this {import('../driver').AndroidDriver}
9
+ * @param {import('./types').SendTrimMemoryOpts} opts
10
+ * @returns {Promise<void>}
7
11
  */
8
- const MemoryMixin = {
9
- /**
10
- * Simulates the onTrimMemory() event for the given package.
11
- * Read https://developer.android.com/topic/performance/memory
12
- * for more details.
13
- *
14
- * @param {import('./types').SendTrimMemoryOpts} opts
15
- */
16
- async mobileSendTrimMemory(opts) {
17
- const {
18
- pkg,
19
- level,
20
- } = opts;
12
+ export async function mobileSendTrimMemory(opts) {
13
+ const {pkg, level} = opts;
21
14
 
22
- if (!pkg) {
23
- throw new errors.InvalidArgumentError(`The 'pkg' argument must be provided`);
24
- }
25
- if (!level) {
26
- throw new errors.InvalidArgumentError(`The 'level' argument must be provided`);
27
- }
15
+ if (!pkg) {
16
+ throw new errors.InvalidArgumentError(`The 'pkg' argument must be provided`);
17
+ }
18
+ if (!level) {
19
+ throw new errors.InvalidArgumentError(`The 'level' argument must be provided`);
20
+ }
28
21
 
29
- await this.adb.shell(['am', 'send-trim-memory', pkg, level]);
30
- },
31
- };
32
-
33
- mixin(MemoryMixin);
34
-
35
- export default MemoryMixin;
22
+ await this.adb.shell(['am', 'send-trim-memory', pkg, level]);
23
+ }
36
24
 
37
25
  /**
38
26
  * @typedef {import('appium-adb').ADB} ADB
@@ -0,0 +1,94 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import _ from 'lodash';
3
+ import {errors} from 'appium/driver';
4
+
5
+ /**
6
+ * @this {import('../driver').AndroidDriver}
7
+ * @returns {Promise<import('@appium/types').Size>}
8
+ */
9
+ export async function getWindowSize() {
10
+ throw new errors.NotImplementedError('Not implemented');
11
+ }
12
+
13
+ /**
14
+ * @this {import('../driver').AndroidDriver}
15
+ * @returns {Promise<import('@appium/types').Rect>}
16
+ */
17
+ export async function getWindowRect() {
18
+ const {width, height} = await this.getWindowSize();
19
+ return {
20
+ width,
21
+ height,
22
+ x: 0,
23
+ y: 0,
24
+ };
25
+ }
26
+
27
+ /**
28
+ * we override setUrl to take an android URI which can be used for deep-linking
29
+ * inside an app, similar to starting an intent
30
+ *
31
+ * @this {import('../driver').AndroidDriver}
32
+ * @param {string} uri
33
+ * @returns {Promise<void>}
34
+ */
35
+ export async function setUrl(uri) {
36
+ await this.adb.startUri(uri, /** @type {string} */ (this.opts.appPackage));
37
+ }
38
+
39
+ /**
40
+ * @this {import('../driver').AndroidDriver}
41
+ * @returns {Promise<number>}
42
+ */
43
+ export async function getDisplayDensity() {
44
+ // first try the property for devices
45
+ let out = await this.adb.shell(['getprop', 'ro.sf.lcd_density']);
46
+ if (out) {
47
+ let val = parseInt(out, 10);
48
+ // if the value is NaN, try getting the emulator property
49
+ if (!isNaN(val)) {
50
+ return val;
51
+ }
52
+ this.log.debug(`Parsed density value was NaN: "${out}"`);
53
+ }
54
+ // fallback to trying property for emulators
55
+ out = await this.adb.shell(['getprop', 'qemu.sf.lcd_density']);
56
+ if (out) {
57
+ let val = parseInt(out, 10);
58
+ if (!isNaN(val)) {
59
+ return val;
60
+ }
61
+ this.log.debug(`Parsed density value was NaN: "${out}"`);
62
+ }
63
+ // couldn't get anything, so error out
64
+ throw this.log.errorAndThrow('Failed to get display density property.');
65
+ }
66
+
67
+ /**
68
+ * @this {import('../driver').AndroidDriver}
69
+ * @returns {Promise<StringRecord>}
70
+ */
71
+ export async function mobileGetNotifications() {
72
+ return await this.settingsApp.getNotifications();
73
+ }
74
+
75
+ /**
76
+ * @this {import('../driver').AndroidDriver}
77
+ * @returns {Promise<import('./types').SmsListResult>}
78
+ */
79
+ export async function mobileListSms(opts) {
80
+ return await this.settingsApp.getSmsList(opts);
81
+ }
82
+
83
+ /**
84
+ * @this {import('../driver').AndroidDriver}
85
+ * @returns {Promise<void>}
86
+ */
87
+ export async function openNotifications() {
88
+ throw new errors.NotImplementedError('Not implemented');
89
+ }
90
+
91
+ /**
92
+ * @typedef {import('appium-adb').ADB} ADB
93
+ * @typedef {import('@appium/types').StringRecord} StringRecord
94
+ */