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,19 +1,20 @@
1
1
  "use strict";
2
+ // @ts-check
2
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
5
  };
5
6
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const lodash_1 = __importDefault(require("lodash"));
7
7
  const support_1 = require("@appium/support");
8
- const teen_process_1 = require("teen_process");
9
- const portscanner_1 = require("portscanner");
10
- const http_1 = __importDefault(require("http"));
11
- const net_1 = __importDefault(require("net"));
12
- const bluebird_1 = __importDefault(require("bluebird"));
13
8
  const asyncbox_1 = require("asyncbox");
14
- const child_process_1 = require("child_process");
15
- const url_1 = __importDefault(require("url"));
16
- const commands = {};
9
+ const bluebird_1 = __importDefault(require("bluebird"));
10
+ const lodash_1 = __importDefault(require("lodash"));
11
+ const node_child_process_1 = require("node:child_process");
12
+ const node_http_1 = __importDefault(require("node:http"));
13
+ const node_net_1 = __importDefault(require("node:net"));
14
+ const node_url_1 = __importDefault(require("node:url"));
15
+ const portscanner_1 = require("portscanner");
16
+ const teen_process_1 = require("teen_process");
17
+ const mixins_1 = require("./mixins");
17
18
  const RECORDING_INTERVAL_SEC = 5;
18
19
  const STREAMING_STARTUP_TIMEOUT_MS = 5000;
19
20
  const GSTREAMER_BINARY = `gst-launch-1.0${support_1.system.isWindows() ? '.exe' : ''}`;
@@ -34,12 +35,23 @@ const DEFAULT_QUALITY = 70;
34
35
  const DEFAULT_BITRATE = 4000000; // 4 Mbps
35
36
  const BOUNDARY_STRING = '--2ae9746887f170b8cf7c271047ce314c';
36
37
  const ADB_SCREEN_STREAMING_FEATURE = 'adb_screen_streaming';
38
+ /**
39
+ *
40
+ * @param {string} streamName
41
+ * @param {string} udid
42
+ * @returns {AppiumLogger}
43
+ */
37
44
  function createStreamingLogger(streamName, udid) {
38
- return support_1.logger.getLogger(`${streamName}@` + lodash_1.default.truncate(udid, {
39
- length: 8,
40
- omission: '',
41
- }));
45
+ return support_1.logger.getLogger(`${streamName}@` +
46
+ lodash_1.default.truncate(udid, {
47
+ length: 8,
48
+ omission: '',
49
+ }));
42
50
  }
51
+ /**
52
+ *
53
+ * @param {ADB} adb
54
+ */
43
55
  async function verifyStreamingRequirements(adb) {
44
56
  if (!lodash_1.default.trim(await adb.shell(['which', SCREENRECORD_BINARY]))) {
45
57
  throw new Error(`The required '${SCREENRECORD_BINARY}' binary is not available on the device under test`);
@@ -69,14 +81,23 @@ async function verifyStreamingRequirements(adb) {
69
81
  }
70
82
  await bluebird_1.default.all(moduleCheckPromises);
71
83
  }
72
- async function getDeviceInfo(adb, log = null) {
84
+ const deviceInfoRegexes = /** @type {const} */ ([
85
+ ['width', /\bdeviceWidth=(\d+)/],
86
+ ['height', /\bdeviceHeight=(\d+)/],
87
+ ['fps', /\bfps=(\d+)/],
88
+ ]);
89
+ /**
90
+ *
91
+ * @param {ADB} adb
92
+ * @param {AppiumLogger} [log]
93
+ */
94
+ async function getDeviceInfo(adb, log) {
73
95
  const output = await adb.shell(['dumpsys', 'display']);
96
+ /**
97
+ * @type {DeviceInfo}
98
+ */
74
99
  const result = {};
75
- for (const [key, pattern] of [
76
- ['width', /\bdeviceWidth=(\d+)/],
77
- ['height', /\bdeviceHeight=(\d+)/],
78
- ['fps', /\bfps=(\d+)/],
79
- ]) {
100
+ for (const [key, pattern] of deviceInfoRegexes) {
80
101
  const match = pattern.exec(output);
81
102
  if (!match) {
82
103
  log?.debug(output);
@@ -85,14 +106,22 @@ async function getDeviceInfo(adb, log = null) {
85
106
  }
86
107
  result[key] = parseInt(match[1], 10);
87
108
  }
88
- result.udid = adb.curDeviceId;
109
+ result.udid = String(adb.curDeviceId);
89
110
  return result;
90
111
  }
112
+ /**
113
+ *
114
+ * @param {ADB} adb
115
+ * @param {AppiumLogger} log
116
+ * @param {DeviceInfo} deviceInfo
117
+ * @param {{width?: string|number, height?: string|number, bitRate?: string|number}} opts
118
+ * @returns
119
+ */
91
120
  async function initDeviceStreamingProc(adb, log, deviceInfo, opts = {}) {
92
- const { width, height, bitRate, } = opts;
93
- const adjustedWidth = parseInt(width, 10) || deviceInfo.width;
94
- const adjustedHeight = parseInt(height, 10) || deviceInfo.height;
95
- const adjustedBitrate = parseInt(bitRate, 10) || DEFAULT_BITRATE;
121
+ const { width, height, bitRate } = opts;
122
+ const adjustedWidth = lodash_1.default.isUndefined(width) ? deviceInfo.width : parseInt(String(width), 10);
123
+ const adjustedHeight = lodash_1.default.isUndefined(height) ? deviceInfo.height : parseInt(String(height), 10);
124
+ const adjustedBitrate = lodash_1.default.isUndefined(bitRate) ? DEFAULT_BITRATE : parseInt(String(bitRate), 10);
96
125
  let screenRecordCmd = SCREENRECORD_BINARY +
97
126
  ` --output-format=h264` +
98
127
  // 5 seconds is fine to detect rotation changes
@@ -110,12 +139,16 @@ async function initDeviceStreamingProc(adb, log, deviceInfo, opts = {}) {
110
139
  // for screenrecord is always limited
111
140
  `while true; do ${screenRecordCmd} -; done`,
112
141
  ];
113
- const deviceStreaming = (0, child_process_1.spawn)(adb.executable.path, adbArgs);
142
+ const deviceStreaming = (0, node_child_process_1.spawn)(adb.executable.path, adbArgs);
114
143
  deviceStreaming.on('exit', (code, signal) => {
115
144
  log.debug(`Device streaming process exited with code ${code}, signal ${signal}`);
116
145
  });
117
146
  let isStarted = false;
118
147
  const deviceStreamingLogger = createStreamingLogger(SCREENRECORD_BINARY, deviceInfo.udid);
148
+ /**
149
+ *
150
+ * @param {Buffer|string} chunk
151
+ */
119
152
  const errorsListener = (chunk) => {
120
153
  const stderr = chunk.toString();
121
154
  if (lodash_1.default.trim(stderr)) {
@@ -123,6 +156,10 @@ async function initDeviceStreamingProc(adb, log, deviceInfo, opts = {}) {
123
156
  }
124
157
  };
125
158
  deviceStreaming.stderr.on('data', errorsListener);
159
+ /**
160
+ *
161
+ * @param {Buffer|string} chunk
162
+ */
126
163
  const startupListener = (chunk) => {
127
164
  if (!isStarted) {
128
165
  isStarted = !lodash_1.default.isEmpty(chunk);
@@ -137,7 +174,8 @@ async function initDeviceStreamingProc(adb, log, deviceInfo, opts = {}) {
137
174
  });
138
175
  }
139
176
  catch (e) {
140
- log.errorAndThrow(`Cannot start the screen streaming process. Original error: ${e.message}`);
177
+ log.errorAndThrow(`Cannot start the screen streaming process. Original error: ${
178
+ /** @type {Error} */ (e).message}`);
141
179
  }
142
180
  finally {
143
181
  deviceStreaming.stderr.removeListener('data', errorsListener);
@@ -145,32 +183,59 @@ async function initDeviceStreamingProc(adb, log, deviceInfo, opts = {}) {
145
183
  }
146
184
  return deviceStreaming;
147
185
  }
148
- async function initGstreamerPipeline(deviceStreamingProc, deviceInfo, log, opts = {}) {
149
- const { width, height, quality, tcpPort, considerRotation, logPipelineDetails, } = opts;
150
- const adjustedWidth = parseInt(width, 10) || deviceInfo.width;
151
- const adjustedHeight = parseInt(height, 10) || deviceInfo.height;
186
+ /**
187
+ *
188
+ * @param {import('node:child_process').ChildProcess} deviceStreamingProc
189
+ * @param {DeviceInfo} deviceInfo
190
+ * @param {AppiumLogger} log
191
+ * @param {import('./types').InitGStreamerPipelineOpts} opts
192
+ */
193
+ async function initGstreamerPipeline(deviceStreamingProc, deviceInfo, log, opts) {
194
+ const { width, height, quality, tcpPort, considerRotation, logPipelineDetails } = opts;
195
+ const adjustedWidth = parseInt(String(width), 10) || deviceInfo.width;
196
+ const adjustedHeight = parseInt(String(height), 10) || deviceInfo.height;
152
197
  const gstreamerPipeline = new teen_process_1.SubProcess(GSTREAMER_BINARY, [
153
198
  '-v',
154
- 'fdsrc', 'fd=0',
155
- '!', 'video/x-h264,' +
199
+ 'fdsrc',
200
+ 'fd=0',
201
+ '!',
202
+ 'video/x-h264,' +
156
203
  `width=${considerRotation ? Math.max(adjustedWidth, adjustedHeight) : adjustedWidth},` +
157
204
  `height=${considerRotation ? Math.max(adjustedWidth, adjustedHeight) : adjustedHeight},` +
158
205
  `framerate=${deviceInfo.fps}/1,` +
159
206
  'byte-stream=true',
160
- '!', 'h264parse',
161
- '!', 'queue', 'leaky=downstream',
162
- '!', 'avdec_h264',
163
- '!', 'queue', 'leaky=downstream',
164
- '!', 'jpegenc', `quality=${quality}`,
165
- '!', 'multipartmux', `boundary=${BOUNDARY_STRING}`,
166
- '!', 'tcpserversink', `host=${TCP_HOST}`, `port=${tcpPort}`,
207
+ '!',
208
+ 'h264parse',
209
+ '!',
210
+ 'queue',
211
+ 'leaky=downstream',
212
+ '!',
213
+ 'avdec_h264',
214
+ '!',
215
+ 'queue',
216
+ 'leaky=downstream',
217
+ '!',
218
+ 'jpegenc',
219
+ `quality=${quality}`,
220
+ '!',
221
+ 'multipartmux',
222
+ `boundary=${BOUNDARY_STRING}`,
223
+ '!',
224
+ 'tcpserversink',
225
+ `host=${TCP_HOST}`,
226
+ `port=${tcpPort}`,
167
227
  ], {
168
- stdio: [deviceStreamingProc.stdout, 'pipe', 'pipe']
228
+ stdio: [deviceStreamingProc.stdout, 'pipe', 'pipe'],
169
229
  });
170
230
  gstreamerPipeline.on('exit', (code, signal) => {
171
231
  log.debug(`Pipeline streaming process exited with code ${code}, signal ${signal}`);
172
232
  });
173
233
  const gstreamerLogger = createStreamingLogger('gst', deviceInfo.udid);
234
+ /**
235
+ *
236
+ * @param {string} stdout
237
+ * @param {string} stderr
238
+ */
174
239
  const gstOutputListener = (stdout, stderr) => {
175
240
  if (lodash_1.default.trim(stderr || stdout)) {
176
241
  gstreamerLogger.debug(stderr || stdout);
@@ -195,7 +260,8 @@ async function initGstreamerPipeline(deviceStreamingProc, deviceInfo, log, opts
195
260
  }
196
261
  catch (e) {
197
262
  didFail = true;
198
- log.errorAndThrow(`Cannot start the screen streaming pipeline. Original error: ${e.message}`);
263
+ log.errorAndThrow(`Cannot start the screen streaming pipeline. Original error: ${
264
+ /** @type {Error} */ (e).message}`);
199
265
  }
200
266
  finally {
201
267
  if (!logPipelineDetails || didFail) {
@@ -204,205 +270,182 @@ async function initGstreamerPipeline(deviceStreamingProc, deviceInfo, log, opts
204
270
  }
205
271
  return gstreamerPipeline;
206
272
  }
273
+ /**
274
+ * @param {import('node:http').IncomingMessage} req
275
+ * @privateRemarks This may need to be future-proofed, as `IncomingMessage.connection` is deprecated and its `socket` prop is likely private
276
+ */
207
277
  function extractRemoteAddress(req) {
208
- return req.headers['x-forwarded-for']
209
- || req.socket.remoteAddress
210
- || req.connection.remoteAddress
211
- || req.connection.socket.remoteAddress;
278
+ return (req.headers['x-forwarded-for'] ||
279
+ req.socket.remoteAddress ||
280
+ req.connection.remoteAddress ||
281
+ // @ts-expect-error socket may be a private API??
282
+ req.connection.socket.remoteAddress);
212
283
  }
213
284
  /**
214
- * @typedef {Object} StartScreenStreamingOptions
215
- *
216
- * @property {?number} width - The scaled width of the device's screen. If unset then the script will assign it
217
- * to the actual screen width measured in pixels.
218
- * @property {?number} height - The scaled height of the device's screen. If unset then the script will assign it
219
- * to the actual screen height measured in pixels.
220
- * @property {?number} bitRate - The video bit rate for the video, in bits per second.
221
- * The default value is 4000000 (4 Mb/s). You can increase the bit rate to improve video quality,
222
- * but doing so results in larger movie files.
223
- * @property {?string} host [127.0.0.1] - The IP address/host name to start the MJPEG server on.
224
- * You can set it to `0.0.0.0` to trigger the broadcast on all available network interfaces.
225
- * @property {?string} pathname - The HTTP request path the MJPEG server should be available on.
226
- * If unset then any pathname on the given `host`/`port` combination will work. Note that the value
227
- * should always start with a single slash: `/`
228
- * @property {?number} tcpPort [8094] - The port number to start the internal TCP MJPEG broadcast on.
229
- * This type of broadcast always starts on the loopback interface (`127.0.0.1`).
230
- * @property {?number} port [8093] - The port number to start the MJPEG server on.
231
- * @property {?number} quality [70] - The quality value for the streamed JPEG images.
232
- * This number should be in range [1, 100], where 100 is the best quality.
233
- * @property {?boolean} considerRotation [false] - If set to `true` then GStreamer pipeline will
234
- * increase the dimensions of the resulting images to properly fit images in both landscape and
235
- * portrait orientations. Set it to `true` if the device rotation is not going to be the same during the
236
- * broadcasting session.
237
- * @property {?boolean} logPipelineDetails [false] - Whether to log GStreamer pipeline events into
238
- * the standard log output. Might be useful for debugging purposes.
285
+ * @type {import('./mixins').StreamScreenMixin & ThisType<import('../driver').AndroidDriver>}
286
+ * @satisfies {import('@appium/types').ExternalDriver}
239
287
  */
240
- /**
241
- * Starts device screen broadcast by creating MJPEG server.
242
- * Multiple calls to this method have no effect unless the previous streaming
243
- * session is stopped.
244
- * This method only works if the `adb_screen_streaming` feature is
245
- * enabled on the server side.
246
- *
247
- * @param {?StartScreenStreamingOptions} options - The available options.
248
- * @throws {Error} If screen streaming has failed to start or
249
- * is not supported on the host system or
250
- * the corresponding server feature is not enabled.
251
- */
252
- commands.mobileStartScreenStreaming = async function mobileStartScreenStreaming(options = {}) {
253
- this.ensureFeatureEnabled(ADB_SCREEN_STREAMING_FEATURE);
254
- const { width, height, bitRate, host = DEFAULT_HOST, port = DEFAULT_PORT, pathname, tcpPort = DEFAULT_PORT + 1, quality = DEFAULT_QUALITY, considerRotation = false, logPipelineDetails = false, } = options;
255
- if (lodash_1.default.isUndefined(this._screenStreamingProps)) {
256
- await verifyStreamingRequirements(this.adb);
257
- }
258
- if (!lodash_1.default.isEmpty(this._screenStreamingProps)) {
259
- this.log.info(`The screen streaming session is already running. ` +
260
- `Stop it first in order to start a new one.`);
261
- return;
262
- }
263
- if ((await (0, portscanner_1.checkPortStatus)(port, host)) === 'open') {
264
- this.log.info(`The port #${port} at ${host} is busy. ` +
265
- `Assuming the screen streaming is already running`);
266
- return;
267
- }
268
- if ((await (0, portscanner_1.checkPortStatus)(tcpPort, TCP_HOST)) === 'open') {
269
- this.log.errorAndThrow(`The port #${tcpPort} at ${TCP_HOST} is busy. ` +
270
- `Make sure there are no leftovers from previous sessions.`);
271
- }
272
- this._screenStreamingProps = null;
273
- const deviceInfo = await getDeviceInfo(this.adb, this.log);
274
- const deviceStreamingProc = await initDeviceStreamingProc(this.adb, this.log, deviceInfo, {
275
- width,
276
- height,
277
- bitRate,
278
- });
279
- let gstreamerPipeline;
280
- try {
281
- gstreamerPipeline = await initGstreamerPipeline(deviceStreamingProc, deviceInfo, this.log, {
288
+ const StreamScreenMixin = {
289
+ async mobileStartScreenStreaming(options = {}) {
290
+ this.ensureFeatureEnabled(ADB_SCREEN_STREAMING_FEATURE);
291
+ const { width, height, bitRate, host = DEFAULT_HOST, port = DEFAULT_PORT, pathname, tcpPort = DEFAULT_PORT + 1, quality = DEFAULT_QUALITY, considerRotation = false, logPipelineDetails = false, } = options;
292
+ const adb = /** @type {ADB} */ (this.adb);
293
+ if (lodash_1.default.isUndefined(this._screenStreamingProps)) {
294
+ await verifyStreamingRequirements(adb);
295
+ }
296
+ if (!lodash_1.default.isEmpty(this._screenStreamingProps)) {
297
+ this.log.info(`The screen streaming session is already running. ` +
298
+ `Stop it first in order to start a new one.`);
299
+ return;
300
+ }
301
+ if ((await (0, portscanner_1.checkPortStatus)(port, host)) === 'open') {
302
+ this.log.info(`The port #${port} at ${host} is busy. ` +
303
+ `Assuming the screen streaming is already running`);
304
+ return;
305
+ }
306
+ if ((await (0, portscanner_1.checkPortStatus)(tcpPort, TCP_HOST)) === 'open') {
307
+ this.log.errorAndThrow(`The port #${tcpPort} at ${TCP_HOST} is busy. ` +
308
+ `Make sure there are no leftovers from previous sessions.`);
309
+ }
310
+ this._screenStreamingProps = undefined;
311
+ const deviceInfo = await getDeviceInfo(adb, this.log);
312
+ const deviceStreamingProc = await initDeviceStreamingProc(adb, this.log, deviceInfo, {
282
313
  width,
283
314
  height,
284
- quality,
285
- tcpPort,
286
- considerRotation,
287
- logPipelineDetails,
315
+ bitRate,
288
316
  });
289
- }
290
- catch (e) {
291
- if (deviceStreamingProc.kill(0)) {
292
- deviceStreamingProc.kill();
317
+ let gstreamerPipeline;
318
+ try {
319
+ gstreamerPipeline = await initGstreamerPipeline(deviceStreamingProc, deviceInfo, this.log, {
320
+ width,
321
+ height,
322
+ quality,
323
+ tcpPort,
324
+ considerRotation,
325
+ logPipelineDetails,
326
+ });
293
327
  }
294
- throw e;
295
- }
296
- let mjpegSocket;
297
- let mjpegServer;
298
- try {
299
- await new bluebird_1.default((resolve, reject) => {
300
- mjpegSocket = net_1.default.createConnection(tcpPort, TCP_HOST, () => {
301
- this.log.info(`Successfully connected to MJPEG stream at tcp://${TCP_HOST}:${tcpPort}`);
302
- mjpegServer = http_1.default.createServer((req, res) => {
303
- const remoteAddress = extractRemoteAddress(req);
304
- const currentPathname = url_1.default.parse(req.url).pathname;
305
- this.log.info(`Got an incoming screen bradcasting request from ${remoteAddress} ` +
306
- `(${req.headers['user-agent'] || 'User Agent unknown'}) at ${currentPathname}`);
307
- if (pathname && currentPathname !== pathname) {
308
- this.log.info('Rejecting the broadcast request since it does not match the given pathname');
309
- res.writeHead(404, {
328
+ catch (e) {
329
+ if (deviceStreamingProc.kill(0)) {
330
+ deviceStreamingProc.kill();
331
+ }
332
+ throw e;
333
+ }
334
+ /** @type {import('node:net').Socket|undefined} */
335
+ let mjpegSocket;
336
+ /** @type {import('node:http').Server|undefined} */
337
+ let mjpegServer;
338
+ try {
339
+ await new bluebird_1.default((resolve, reject) => {
340
+ mjpegSocket = node_net_1.default.createConnection(tcpPort, TCP_HOST, () => {
341
+ this.log.info(`Successfully connected to MJPEG stream at tcp://${TCP_HOST}:${tcpPort}`);
342
+ mjpegServer = node_http_1.default.createServer((req, res) => {
343
+ const remoteAddress = extractRemoteAddress(req);
344
+ const currentPathname = node_url_1.default.parse(String(req.url)).pathname;
345
+ this.log.info(`Got an incoming screen broadcasting request from ${remoteAddress} ` +
346
+ `(${req.headers['user-agent'] || 'User Agent unknown'}) at ${currentPathname}`);
347
+ if (pathname && currentPathname !== pathname) {
348
+ this.log.info('Rejecting the broadcast request since it does not match the given pathname');
349
+ res.writeHead(404, {
350
+ Connection: 'close',
351
+ 'Content-Type': 'text/plain; charset=utf-8',
352
+ });
353
+ res.write(`'${currentPathname}' did not match any known endpoints`);
354
+ res.end();
355
+ return;
356
+ }
357
+ this.log.info('Starting MJPEG broadcast');
358
+ res.writeHead(200, {
359
+ 'Cache-Control': 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
360
+ Pragma: 'no-cache',
310
361
  Connection: 'close',
311
- 'Content-Type': 'text/plain; charset=utf-8',
362
+ 'Content-Type': `multipart/x-mixed-replace; boundary=${BOUNDARY_STRING}`,
312
363
  });
313
- res.write(`'${currentPathname}' did not match any known endpoints`);
314
- res.end();
315
- return;
316
- }
317
- this.log.info('Starting MJPEG broadcast');
318
- res.writeHead(200, {
319
- 'Cache-Control': 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
320
- Pragma: 'no-cache',
321
- Connection: 'close',
322
- 'Content-Type': `multipart/x-mixed-replace; boundary=${BOUNDARY_STRING}`
364
+ /** @type {import('node:net').Socket} */ (mjpegSocket).pipe(res);
365
+ });
366
+ mjpegServer.on('error', (e) => {
367
+ this.log.warn(e);
368
+ reject(e);
369
+ });
370
+ mjpegServer.on('close', () => {
371
+ this.log.debug(`MJPEG server at http://${host}:${port} has been closed`);
323
372
  });
324
- mjpegSocket.pipe(res);
373
+ mjpegServer.on('listening', () => {
374
+ this.log.info(`Successfully started MJPEG server at http://${host}:${port}`);
375
+ resolve();
376
+ });
377
+ mjpegServer.listen(port, host);
325
378
  });
326
- mjpegServer.on('error', (e) => {
327
- this.log.warn(e);
379
+ mjpegSocket.on('error', (e) => {
380
+ this.log.error(e);
328
381
  reject(e);
329
382
  });
330
- mjpegServer.on('close', () => {
331
- this.log.debug(`MJPEG server at http://${host}:${port} has been closed`);
332
- });
333
- mjpegServer.on('listening', () => {
334
- this.log.info(`Successfully started MJPEG server at http://${host}:${port}`);
335
- resolve();
336
- });
337
- mjpegServer.listen(port, host);
338
- });
339
- mjpegSocket.on('error', (e) => {
340
- this.log.error(e);
341
- reject(e);
342
- });
343
- }).timeout(STREAMING_STARTUP_TIMEOUT_MS, `Cannot connect to the streaming server within ${STREAMING_STARTUP_TIMEOUT_MS}ms`);
344
- }
345
- catch (e) {
346
- if (deviceStreamingProc.kill(0)) {
347
- deviceStreamingProc.kill();
348
- }
349
- if (gstreamerPipeline.isRunning) {
350
- await gstreamerPipeline.stop();
351
- }
352
- if (mjpegSocket) {
353
- mjpegSocket.destroy();
354
- }
355
- if (mjpegServer && mjpegServer.listening) {
356
- mjpegServer.close();
357
- }
358
- throw e;
359
- }
360
- this._screenStreamingProps = {
361
- deviceStreamingProc,
362
- gstreamerPipeline,
363
- mjpegSocket,
364
- mjpegServer,
365
- };
366
- };
367
- /**
368
- * Stop screen streaming.
369
- * If no screen streaming server has been started then nothing is done.
370
- */
371
- commands.mobileStopScreenStreaming = async function mobileStopScreenStreaming( /* options = {} */) {
372
- if (lodash_1.default.isEmpty(this._screenStreamingProps)) {
373
- if (!lodash_1.default.isUndefined(this._screenStreamingProps)) {
374
- this.log.debug(`Screen streaming is not running. There is nothing to stop`);
383
+ }).timeout(STREAMING_STARTUP_TIMEOUT_MS, `Cannot connect to the streaming server within ${STREAMING_STARTUP_TIMEOUT_MS}ms`);
375
384
  }
376
- return;
377
- }
378
- const { deviceStreamingProc, gstreamerPipeline, mjpegSocket, mjpegServer, } = this._screenStreamingProps;
379
- try {
380
- mjpegSocket.end();
381
- if (mjpegServer.listening) {
382
- mjpegServer.close();
385
+ catch (e) {
386
+ if (deviceStreamingProc.kill(0)) {
387
+ deviceStreamingProc.kill();
388
+ }
389
+ if (gstreamerPipeline.isRunning) {
390
+ await gstreamerPipeline.stop();
391
+ }
392
+ if (mjpegSocket) {
393
+ mjpegSocket.destroy();
394
+ }
395
+ if (mjpegServer && mjpegServer.listening) {
396
+ mjpegServer.close();
397
+ }
398
+ throw e;
383
399
  }
384
- if (deviceStreamingProc.kill(0)) {
385
- deviceStreamingProc.kill('SIGINT');
400
+ this._screenStreamingProps = {
401
+ deviceStreamingProc,
402
+ gstreamerPipeline,
403
+ mjpegSocket,
404
+ mjpegServer,
405
+ };
406
+ },
407
+ async mobileStopScreenStreaming() {
408
+ if (lodash_1.default.isEmpty(this._screenStreamingProps)) {
409
+ if (!lodash_1.default.isUndefined(this._screenStreamingProps)) {
410
+ this.log.debug(`Screen streaming is not running. There is nothing to stop`);
411
+ }
412
+ return;
386
413
  }
387
- if (gstreamerPipeline.isRunning) {
388
- try {
389
- await gstreamerPipeline.stop('SIGINT');
414
+ const { deviceStreamingProc, gstreamerPipeline, mjpegSocket, mjpegServer } = this._screenStreamingProps;
415
+ try {
416
+ mjpegSocket.end();
417
+ if (mjpegServer.listening) {
418
+ mjpegServer.close();
390
419
  }
391
- catch (e) {
392
- this.log.warn(e);
420
+ if (deviceStreamingProc.kill(0)) {
421
+ deviceStreamingProc.kill('SIGINT');
422
+ }
423
+ if (gstreamerPipeline.isRunning) {
393
424
  try {
394
- await gstreamerPipeline.stop('SIGKILL');
425
+ await gstreamerPipeline.stop('SIGINT');
395
426
  }
396
- catch (e1) {
397
- this.log.error(e1);
427
+ catch (e) {
428
+ this.log.warn(e);
429
+ try {
430
+ await gstreamerPipeline.stop('SIGKILL');
431
+ }
432
+ catch (e1) {
433
+ this.log.error(e1);
434
+ }
398
435
  }
399
436
  }
437
+ this.log.info(`Successfully terminated the screen streaming MJPEG server`);
400
438
  }
401
- this.log.info(`Successfully terminated the screen streaming MJPEG server`);
402
- }
403
- finally {
404
- this._screenStreamingProps = null;
405
- }
439
+ finally {
440
+ this._screenStreamingProps = undefined;
441
+ }
442
+ },
406
443
  };
407
- exports.default = commands;
444
+ (0, mixins_1.mixin)(StreamScreenMixin);
445
+ exports.default = StreamScreenMixin;
446
+ /**
447
+ * @typedef {import('appium-adb').ADB} ADB
448
+ * @typedef {import('@appium/types').AppiumLogger} AppiumLogger
449
+ * @typedef {import('./types').DeviceInfo} DeviceInfo
450
+ */
408
451
  //# sourceMappingURL=streamscreen.js.map