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,472 +1,414 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.setupNewChromedriver = exports.helpers = exports.commands = void 0;
30
- const lodash_1 = __importDefault(require("lodash"));
31
- const appium_chromedriver_1 = __importDefault(require("appium-chromedriver"));
32
- const portfinder_1 = __importDefault(require("portfinder"));
33
- const bluebird_1 = __importDefault(require("bluebird"));
6
+ exports.setupNewChromedriver = void 0;
7
+ /* eslint-disable require-await */
8
+ // @ts-check
34
9
  const support_1 = require("@appium/support");
10
+ const appium_chromedriver_1 = __importDefault(require("appium-chromedriver"));
35
11
  const driver_1 = require("appium/driver");
36
- const webview_helpers_1 = __importStar(require("../webview-helpers"));
37
- const android_helpers_1 = require("../android-helpers");
12
+ const bluebird_1 = __importDefault(require("bluebird"));
13
+ const lodash_1 = __importDefault(require("lodash"));
14
+ const portfinder_1 = __importDefault(require("portfinder"));
15
+ const helpers_1 = require("../helpers");
16
+ const mixins_1 = require("./mixins");
38
17
  const CHROMEDRIVER_AUTODOWNLOAD_FEATURE = 'chromedriver_autodownload';
39
- let commands = {}, helpers = {}, extensions = {};
40
- exports.commands = commands;
41
- exports.helpers = helpers;
42
- /* -------------------------------
43
- * Actual MJSONWP command handlers
44
- * ------------------------------- */
45
- commands.getCurrentContext = async function getCurrentContext() {
46
- // if the current context is `null`, indicating no context
47
- // explicitly set, it is the default context
48
- return this.curContext || this.defaultContextName();
49
- };
50
- commands.getContexts = async function getContexts() {
51
- const webviewsMapping = await webview_helpers_1.default.getWebViewsMapping(this.adb, this.opts);
52
- return this.assignContexts(webviewsMapping);
53
- };
54
- commands.setContext = async function setContext(name) {
55
- if (!support_1.util.hasValue(name)) {
56
- name = this.defaultContextName();
57
- }
58
- else if (name === webview_helpers_1.WEBVIEW_WIN) {
59
- // handle setContext "WEBVIEW"
60
- name = this.defaultWebviewName();
61
- }
62
- // if we're already in the context we want, do nothing
63
- if (name === this.curContext) {
64
- return;
65
- }
66
- const webviewsMapping = await webview_helpers_1.default.getWebViewsMapping(this.adb, this.opts);
67
- const contexts = this.assignContexts(webviewsMapping);
68
- // if the context we want doesn't exist, fail
69
- if (!lodash_1.default.includes(contexts, name)) {
70
- throw new driver_1.errors.NoSuchContextError();
71
- }
72
- await this.switchContext(name, webviewsMapping);
73
- this.curContext = name;
74
- };
75
18
  /**
76
- * @typedef {Object} WebviewsMapping
77
- * @property {string} proc The name of the Devtools Unix socket
78
- * @property {string} webview The web view alias. Looks like `WEBVIEW_`
79
- * prefix plus PID or package name
80
- * @property {?Object} info Webview information as it is retrieved by
81
- * /json/version CDP endpoint
82
- * @property {?Array<Object>} pages Webview pages list as it is retrieved by
83
- * /json/list CDP endpoint
84
- * @propery {?string} webviewName An actual webview name for switching context.
85
- * This value becomes null when failing to find a PID for a webview.
86
- *
87
- * The following json demonstrates the example of WebviewsMapping object.
88
- * Note that `description` in `page` can be an empty string most likely when it comes to Mobile Chrome)
89
- * {
90
- * "proc": "@webview_devtools_remote_22138",
91
- * "webview": "WEBVIEW_22138",
92
- * "info": {
93
- * "Android-Package": "io.appium.settings",
94
- * "Browser": "Chrome/74.0.3729.185",
95
- * "Protocol-Version": "1.3",
96
- * "User-Agent": "Mozilla/5.0 (Linux; Android 10; Android SDK built for x86 Build/QSR1.190920.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.185 Mobile Safari/537.36",
97
- * "V8-Version": "7.4.288.28",
98
- * "WebKit-Version": "537.36 (@22955682f94ce09336197bfb8dffea991fa32f0d)",
99
- * "webSocketDebuggerUrl": "ws://127.0.0.1:10900/devtools/browser"
100
- * },
101
- * "pages": [
102
- * {
103
- * "description": "{\"attached\":true,\"empty\":false,\"height\":1458,\"screenX\":0,\"screenY\":336,\"visible\":true,\"width\":1080}",
104
- * "devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@22955682f94ce09336197bfb8dffea991fa32f0d/inspector.html?ws=127.0.0.1:10900/devtools/page/27325CC50B600D31B233F45E09487B1F",
105
- * "id": "27325CC50B600D31B233F45E09487B1F",
106
- * "title": "Releases · appium/appium · GitHub",
107
- * "type": "page",
108
- * "url": "https://github.com/appium/appium/releases",
109
- * "webSocketDebuggerUrl": "ws://127.0.0.1:10900/devtools/page/27325CC50B600D31B233F45E09487B1F"
110
- * }
111
- * ],
112
- * "webviewName": "WEBVIEW_com.io.appium.setting"
113
- * }
19
+ * @type {import('./mixins').ContextMixin & ThisType<import('../driver').AndroidDriver>}
20
+ * @satisfies {import('@appium/types').ExternalDriver}
114
21
  */
115
- /**
116
- * Returns a webviewsMapping based on CDP endpoints
117
- *
118
- * @return {Array<WebviewsMapping>} webviewsMapping
119
- */
120
- commands.mobileGetContexts = async function mobileGetContexts() {
121
- const opts = {
122
- androidDeviceSocket: this.opts.androidDeviceSocket,
123
- ensureWebviewsHavePages: true,
124
- webviewDevtoolsPort: this.opts.webviewDevtoolsPort,
125
- enableWebviewDetailsCollection: true
126
- };
127
- return await webview_helpers_1.default.getWebViewsMapping(this.adb, opts);
128
- };
129
- helpers.assignContexts = function assignContexts(webviewsMapping) {
130
- const opts = Object.assign({ isChromeSession: this.isChromeSession }, this.opts);
131
- const webviews = webview_helpers_1.default.parseWebviewNames(webviewsMapping, opts);
132
- this.contexts = [webview_helpers_1.NATIVE_WIN, ...webviews];
133
- this.log.debug(`Available contexts: ${JSON.stringify(this.contexts)}`);
134
- return this.contexts;
135
- };
136
- helpers.switchContext = async function switchContext(name, webviewsMapping) {
137
- // We have some options when it comes to webviews. If we want a
138
- // Chromedriver webview, we can only control one at a time.
139
- if (this.isChromedriverContext(name)) {
140
- // start proxying commands directly to chromedriver
141
- await this.startChromedriverProxy(name, webviewsMapping);
142
- }
143
- else if (this.isChromedriverContext(this.curContext)) {
144
- // if we're moving to a non-chromedriver webview, and our current context
145
- // _is_ a chromedriver webview, if caps recreateChromeDriverSessions is set
146
- // to true then kill chromedriver session using stopChromedriverProxies or
147
- // else simply suspend proxying to the latter
148
- if (this.opts.recreateChromeDriverSessions) {
149
- this.log.debug('recreateChromeDriverSessions set to true; killing existing chromedrivers');
150
- await this.stopChromedriverProxies();
22
+ const ContextMixin = {
23
+ /* -------------------------------
24
+ * Actual MJSONWP command handlers
25
+ * ------------------------------- */
26
+ async getCurrentContext() {
27
+ // if the current context is `null`, indicating no context
28
+ // explicitly set, it is the default context
29
+ return this.curContext || this.defaultContextName();
30
+ },
31
+ async getContexts() {
32
+ const webviewsMapping = await helpers_1.WebviewHelpers.getWebViewsMapping(
33
+ /** @type {ADB} */ (this.adb), this.opts);
34
+ return this.assignContexts(webviewsMapping);
35
+ },
36
+ async setContext(name) {
37
+ if (!support_1.util.hasValue(name)) {
38
+ name = this.defaultContextName();
151
39
  }
152
- else {
153
- await this.suspendChromedriverProxy();
40
+ else if (name === helpers_1.WEBVIEW_WIN) {
41
+ // handle setContext "WEBVIEW"
42
+ name = this.defaultWebviewName();
154
43
  }
155
- }
156
- else {
157
- throw new Error(`Didn't know how to handle switching to context '${name}'`);
158
- }
159
- };
160
- /* ---------------------------------
161
- * On-object context-related helpers
162
- * --------------------------------- */
163
- // The reason this is a function and not just a constant is that both android-
164
- // driver and selendroid-driver use this logic, and each one returns
165
- // a different default context name
166
- helpers.defaultContextName = function defaultContextName() {
167
- return webview_helpers_1.NATIVE_WIN;
168
- };
169
- helpers.defaultWebviewName = function defaultWebviewName() {
170
- return webview_helpers_1.WEBVIEW_BASE + (this.opts.autoWebviewName || this.opts.appPackage);
171
- };
172
- helpers.isWebContext = function isWebContext() {
173
- return this.curContext !== null && this.curContext !== webview_helpers_1.NATIVE_WIN;
174
- };
175
- // Turn on proxying to an existing Chromedriver session or a new one
176
- helpers.startChromedriverProxy = async function startChromedriverProxy(context, webviewsMapping) {
177
- this.log.debug(`Connecting to chrome-backed webview context '${context}'`);
178
- let cd;
179
- if (this.sessionChromedrivers[context]) {
180
- // in the case where we've already set up a chromedriver for a context,
181
- // we want to reconnect to it, not create a whole new one
182
- this.log.debug(`Found existing Chromedriver for context '${context}'. Using it.`);
183
- cd = this.sessionChromedrivers[context];
184
- await setupExistingChromedriver(this.log, cd);
185
- }
186
- else {
187
- let opts = lodash_1.default.cloneDeep(this.opts);
188
- opts.chromeUseRunningApp = true;
189
- // if requested, tell chromedriver to attach to the android package we have
190
- // associated with the context name, rather than the package of the AUT.
191
- // And turn this on by default for chrome--if chrome pops up with a webview
192
- // and someone wants to switch to it, we should let chromedriver connect to
193
- // chrome rather than staying stuck on the AUT
194
- if (opts.extractChromeAndroidPackageFromContextName || context === `${webview_helpers_1.WEBVIEW_BASE}chrome`) {
195
- let androidPackage = context.match(`${webview_helpers_1.WEBVIEW_BASE}(.+)`);
196
- if (androidPackage && androidPackage.length > 0) {
197
- opts.chromeAndroidPackage = androidPackage[1];
44
+ // if we're already in the context we want, do nothing
45
+ if (name === this.curContext) {
46
+ return;
47
+ }
48
+ const webviewsMapping = await helpers_1.WebviewHelpers.getWebViewsMapping(
49
+ /** @type {ADB} */ (this.adb), this.opts);
50
+ const contexts = this.assignContexts(webviewsMapping);
51
+ // if the context we want doesn't exist, fail
52
+ if (!lodash_1.default.includes(contexts, name)) {
53
+ throw new driver_1.errors.NoSuchContextError();
54
+ }
55
+ await this.switchContext(name, webviewsMapping);
56
+ this.curContext = name;
57
+ },
58
+ async mobileGetContexts() {
59
+ const opts = {
60
+ androidDeviceSocket: this.opts.androidDeviceSocket,
61
+ ensureWebviewsHavePages: true,
62
+ webviewDevtoolsPort: this.opts.webviewDevtoolsPort,
63
+ enableWebviewDetailsCollection: true,
64
+ };
65
+ return await helpers_1.WebviewHelpers.getWebViewsMapping(/** @type {ADB} */ (this.adb), opts);
66
+ },
67
+ assignContexts(webviewsMapping) {
68
+ const opts = Object.assign({ isChromeSession: this.isChromeSession }, this.opts);
69
+ const webviews = helpers_1.WebviewHelpers.parseWebviewNames(webviewsMapping, opts);
70
+ this.contexts = [helpers_1.NATIVE_WIN, ...webviews];
71
+ this.log.debug(`Available contexts: ${JSON.stringify(this.contexts)}`);
72
+ return this.contexts;
73
+ },
74
+ async switchContext(name, webviewsMapping) {
75
+ // We have some options when it comes to webviews. If we want a
76
+ // Chromedriver webview, we can only control one at a time.
77
+ if (this.isChromedriverContext(name)) {
78
+ // start proxying commands directly to chromedriver
79
+ await this.startChromedriverProxy(name, webviewsMapping);
80
+ }
81
+ else if (this.isChromedriverContext(this.curContext)) {
82
+ // if we're moving to a non-chromedriver webview, and our current context
83
+ // _is_ a chromedriver webview, if caps recreateChromeDriverSessions is set
84
+ // to true then kill chromedriver session using stopChromedriverProxies or
85
+ // else simply suspend proxying to the latter
86
+ if (this.opts.recreateChromeDriverSessions) {
87
+ this.log.debug('recreateChromeDriverSessions set to true; killing existing chromedrivers');
88
+ await this.stopChromedriverProxies();
198
89
  }
199
- if (!opts.extractChromeAndroidPackageFromContextName) {
200
- if (lodash_1.default.has(this.opts, 'enableWebviewDetailsCollection') && !this.opts.enableWebviewDetailsCollection) {
201
- // When enableWebviewDetailsCollection capability is explicitly disabled, try to identify
202
- // chromeAndroidPackage based on contexts, known chrome variant packages and queryAppState result
203
- // since webviewsMapping does not have info object
204
- const contexts = webviewsMapping.map((wm) => wm.webviewName);
205
- for (const knownPackage of webview_helpers_1.KNOWN_CHROME_PACKAGE_NAMES) {
206
- if (lodash_1.default.includes(contexts, `${webview_helpers_1.WEBVIEW_BASE}${knownPackage}`)) {
207
- continue;
208
- }
209
- const appState = await this.queryAppState(knownPackage);
210
- if (lodash_1.default.includes([android_helpers_1.APP_STATE.RUNNING_IN_BACKGROUND, android_helpers_1.APP_STATE.RUNNING_IN_FOREGROUND], appState)) {
211
- opts.chromeAndroidPackage = knownPackage;
212
- this.log.debug(`Identified chromeAndroidPackage as '${opts.chromeAndroidPackage}' ` +
213
- `for context '${context}' by querying states of Chrome app packages`);
214
- break;
90
+ else {
91
+ this.suspendChromedriverProxy();
92
+ }
93
+ }
94
+ else {
95
+ throw new Error(`Didn't know how to handle switching to context '${name}'`);
96
+ }
97
+ },
98
+ /* ---------------------------------
99
+ * On-object context-related helpers
100
+ * --------------------------------- */
101
+ // The reason this is a function and not just a constant is that both android-
102
+ // driver and selendroid-driver use this logic, and each one returns
103
+ // a different default context name
104
+ defaultContextName() {
105
+ return helpers_1.NATIVE_WIN;
106
+ },
107
+ defaultWebviewName() {
108
+ return helpers_1.WEBVIEW_BASE + (this.opts.autoWebviewName || this.opts.appPackage);
109
+ },
110
+ isWebContext() {
111
+ return this.curContext !== null && this.curContext !== helpers_1.NATIVE_WIN;
112
+ },
113
+ // Turn on proxying to an existing Chromedriver session or a new one
114
+ async startChromedriverProxy(context, webviewsMapping) {
115
+ this.log.debug(`Connecting to chrome-backed webview context '${context}'`);
116
+ let cd;
117
+ if (this.sessionChromedrivers[context]) {
118
+ // in the case where we've already set up a chromedriver for a context,
119
+ // we want to reconnect to it, not create a whole new one
120
+ this.log.debug(`Found existing Chromedriver for context '${context}'. Using it.`);
121
+ cd = this.sessionChromedrivers[context];
122
+ await this.setupExistingChromedriver(this.log, cd);
123
+ }
124
+ else {
125
+ // XXX: this suppresses errors about putting arbitrary stuff on opts
126
+ const opts = /** @type {any} */ (lodash_1.default.cloneDeep(this.opts));
127
+ opts.chromeUseRunningApp = true;
128
+ // if requested, tell chromedriver to attach to the android package we have
129
+ // associated with the context name, rather than the package of the AUT.
130
+ // And turn this on by default for chrome--if chrome pops up with a webview
131
+ // and someone wants to switch to it, we should let chromedriver connect to
132
+ // chrome rather than staying stuck on the AUT
133
+ if (opts.extractChromeAndroidPackageFromContextName || context === `${helpers_1.WEBVIEW_BASE}chrome`) {
134
+ let androidPackage = context.match(`${helpers_1.WEBVIEW_BASE}(.+)`);
135
+ if (androidPackage && androidPackage.length > 0) {
136
+ opts.chromeAndroidPackage = androidPackage[1];
137
+ }
138
+ if (!opts.extractChromeAndroidPackageFromContextName) {
139
+ if (lodash_1.default.has(this.opts, 'enableWebviewDetailsCollection') &&
140
+ !this.opts.enableWebviewDetailsCollection) {
141
+ // When enableWebviewDetailsCollection capability is explicitly disabled, try to identify
142
+ // chromeAndroidPackage based on contexts, known chrome variant packages and queryAppState result
143
+ // since webviewsMapping does not have info object
144
+ const contexts = webviewsMapping.map((wm) => wm.webviewName);
145
+ for (const knownPackage of helpers_1.KNOWN_CHROME_PACKAGE_NAMES) {
146
+ if (lodash_1.default.includes(contexts, `${helpers_1.WEBVIEW_BASE}${knownPackage}`)) {
147
+ continue;
148
+ }
149
+ const appState = await this.queryAppState(knownPackage);
150
+ if (lodash_1.default.includes([helpers_1.APP_STATE.RUNNING_IN_BACKGROUND, helpers_1.APP_STATE.RUNNING_IN_FOREGROUND], appState)) {
151
+ opts.chromeAndroidPackage = knownPackage;
152
+ this.log.debug(`Identified chromeAndroidPackage as '${opts.chromeAndroidPackage}' ` +
153
+ `for context '${context}' by querying states of Chrome app packages`);
154
+ break;
155
+ }
215
156
  }
216
157
  }
217
- }
218
- else {
219
- for (const wm of webviewsMapping) {
220
- if (wm.webviewName === context && lodash_1.default.has(wm?.info, 'Android-Package')) {
221
- opts.chromeAndroidPackage = wm.info['Android-Package'];
222
- this.log.debug(`Identified chromeAndroidPackage as '${opts.chromeAndroidPackage}' ` +
223
- `for context '${context}' by CDP`);
224
- break;
158
+ else {
159
+ for (const wm of webviewsMapping) {
160
+ if (wm.webviewName === context && lodash_1.default.has(wm?.info, 'Android-Package')) {
161
+ // XXX: should be a type guard here
162
+ opts.chromeAndroidPackage =
163
+ /** @type {NonNullable<import('./types').WebviewsMapping['info']>} */ (wm.info)['Android-Package'];
164
+ this.log.debug(`Identified chromeAndroidPackage as '${opts.chromeAndroidPackage}' ` +
165
+ `for context '${context}' by CDP`);
166
+ break;
167
+ }
225
168
  }
226
169
  }
227
170
  }
228
171
  }
172
+ cd = await this.setupNewChromedriver(opts,
173
+ /** @type {string} */ ( /** @type {ADB} */(this.adb).curDeviceId),
174
+ /** @type {ADB} */ (this.adb), context);
175
+ // bind our stop/exit handler, passing in context so we know which
176
+ // one stopped unexpectedly
177
+ cd.on(appium_chromedriver_1.default.EVENT_CHANGED, (msg) => {
178
+ if (msg.state === appium_chromedriver_1.default.STATE_STOPPED) {
179
+ this.onChromedriverStop(context);
180
+ }
181
+ });
182
+ // save the chromedriver object under the context
183
+ this.sessionChromedrivers[context] = cd;
229
184
  }
230
- cd = await this.setupNewChromedriver(opts, this.adb.curDeviceId, this.adb, context);
231
- // bind our stop/exit handler, passing in context so we know which
232
- // one stopped unexpectedly
233
- cd.on(appium_chromedriver_1.default.EVENT_CHANGED, (msg) => {
185
+ // hook up the local variables so we can proxy this biz
186
+ this.chromedriver = cd;
187
+ this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);
188
+ this.proxyCommand = /** @type {import('@appium/types').ExternalDriver['proxyCommand']} */ (this.chromedriver.jwproxy.command.bind(this.chromedriver.jwproxy));
189
+ this.jwpProxyActive = true;
190
+ },
191
+ // Stop proxying to any Chromedriver
192
+ suspendChromedriverProxy() {
193
+ this.chromedriver = undefined;
194
+ this.proxyReqRes = undefined;
195
+ this.proxyCommand = undefined;
196
+ this.jwpProxyActive = false;
197
+ },
198
+ // Handle an out-of-band Chromedriver stop event
199
+ async onChromedriverStop(context) {
200
+ this.log.warn(`Chromedriver for context ${context} stopped unexpectedly`);
201
+ if (context === this.curContext) {
202
+ // we exited unexpectedly while automating the current context and so want
203
+ // to shut down the session and respond with an error
204
+ let err = new Error('Chromedriver quit unexpectedly during session');
205
+ await this.startUnexpectedShutdown(err);
206
+ }
207
+ else {
208
+ // if a Chromedriver in the non-active context barfs, we don't really
209
+ // care, we'll just make a new one next time we need the context.
210
+ this.log.warn("Chromedriver quit unexpectedly, but it wasn't the active " + 'context, ignoring');
211
+ delete this.sessionChromedrivers[context];
212
+ }
213
+ },
214
+ // Intentionally stop all the chromedrivers currently active, and ignore
215
+ // their exit events
216
+ async stopChromedriverProxies() {
217
+ this.suspendChromedriverProxy(); // make sure we turn off the proxy flag
218
+ for (let context of lodash_1.default.keys(this.sessionChromedrivers)) {
219
+ let cd = this.sessionChromedrivers[context];
220
+ this.log.debug(`Stopping chromedriver for context ${context}`);
221
+ // stop listening for the stopped state event
222
+ cd.removeAllListeners(appium_chromedriver_1.default.EVENT_CHANGED);
223
+ try {
224
+ await cd.stop();
225
+ }
226
+ catch (err) {
227
+ this.log.warn(`Error stopping Chromedriver: ${ /** @type {Error} */(err).message}`);
228
+ }
229
+ delete this.sessionChromedrivers[context];
230
+ }
231
+ },
232
+ isChromedriverContext(viewName) {
233
+ return lodash_1.default.includes(viewName, helpers_1.WEBVIEW_WIN) || viewName === helpers_1.CHROMIUM_WIN;
234
+ },
235
+ shouldDismissChromeWelcome() {
236
+ return (!!this.opts.chromeOptions &&
237
+ lodash_1.default.isArray(this.opts.chromeOptions.args) &&
238
+ this.opts.chromeOptions.args.includes('--no-first-run'));
239
+ },
240
+ async dismissChromeWelcome() {
241
+ this.log.info('Trying to dismiss Chrome welcome');
242
+ let activity = await this.getCurrentActivity();
243
+ if (activity !== 'org.chromium.chrome.browser.firstrun.FirstRunActivity') {
244
+ this.log.info('Chrome welcome dialog never showed up! Continuing');
245
+ return;
246
+ }
247
+ let el = await this.findElOrEls('id', 'com.android.chrome:id/terms_accept', false);
248
+ await this.click(/** @type {string} */ (el.ELEMENT));
249
+ try {
250
+ let el = await this.findElOrEls('id', 'com.android.chrome:id/negative_button', false);
251
+ await this.click(/** @type {string} */ (el.ELEMENT));
252
+ }
253
+ catch (e) {
254
+ // DO NOTHING, THIS DEVICE DIDNT LAUNCH THE SIGNIN DIALOG
255
+ // IT MUST BE A NON GMS DEVICE
256
+ this.log.warn(`This device did not show Chrome SignIn dialog, ${ /** @type {Error} */(e).message}`);
257
+ }
258
+ },
259
+ async startChromeSession() {
260
+ this.log.info('Starting a chrome-based browser session');
261
+ // XXX: this suppresses errors about putting arbitrary stuff on opts
262
+ const opts = /** @type {any} */ (lodash_1.default.cloneDeep(this.opts));
263
+ const knownPackages = [
264
+ 'org.chromium.chrome.shell',
265
+ 'com.android.chrome',
266
+ 'com.chrome.beta',
267
+ 'org.chromium.chrome',
268
+ 'org.chromium.webview_shell',
269
+ ];
270
+ if (lodash_1.default.includes(knownPackages, this.opts.appPackage)) {
271
+ opts.chromeBundleId = this.opts.appPackage;
272
+ }
273
+ else {
274
+ opts.chromeAndroidActivity = this.opts.appActivity;
275
+ }
276
+ this.chromedriver = await this.setupNewChromedriver(opts,
277
+ /** @type {string} */ ( /** @type {ADB} */(this.adb).curDeviceId),
278
+ /** @type {ADB} */ (this.adb));
279
+ this.chromedriver.on(appium_chromedriver_1.default.EVENT_CHANGED, (msg) => {
234
280
  if (msg.state === appium_chromedriver_1.default.STATE_STOPPED) {
235
- this.onChromedriverStop(context);
281
+ this.onChromedriverStop(helpers_1.CHROMIUM_WIN);
236
282
  }
237
283
  });
238
- // save the chromedriver object under the context
239
- this.sessionChromedrivers[context] = cd;
240
- }
241
- // hook up the local variables so we can proxy this biz
242
- this.chromedriver = cd;
243
- this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);
244
- this.proxyCommand = this.chromedriver.jwproxy.command.bind(this.chromedriver.jwproxy);
245
- this.jwpProxyActive = true;
246
- };
247
- // Stop proxying to any Chromedriver
248
- helpers.suspendChromedriverProxy = function suspendChromedriverProxy() {
249
- this.chromedriver = null;
250
- this.proxyReqRes = null;
251
- this.proxyCommand = null;
252
- this.jwpProxyActive = false;
253
- };
254
- // Handle an out-of-band Chromedriver stop event
255
- helpers.onChromedriverStop = async function onChromedriverStop(context) {
256
- this.log.warn(`Chromedriver for context ${context} stopped unexpectedly`);
257
- if (context === this.curContext) {
258
- // we exited unexpectedly while automating the current context and so want
259
- // to shut down the session and respond with an error
260
- let err = new Error('Chromedriver quit unexpectedly during session');
261
- await this.startUnexpectedShutdown(err);
262
- }
263
- else {
264
- // if a Chromedriver in the non-active context barfs, we don't really
265
- // care, we'll just make a new one next time we need the context.
266
- this.log.warn("Chromedriver quit unexpectedly, but it wasn't the active " +
267
- 'context, ignoring');
268
- delete this.sessionChromedrivers[context];
269
- }
270
- };
271
- // Intentionally stop all the chromedrivers currently active, and ignore
272
- // their exit events
273
- helpers.stopChromedriverProxies = async function stopChromedriverProxies() {
274
- this.suspendChromedriverProxy(); // make sure we turn off the proxy flag
275
- for (let context of lodash_1.default.keys(this.sessionChromedrivers)) {
276
- let cd = this.sessionChromedrivers[context];
277
- this.log.debug(`Stopping chromedriver for context ${context}`);
278
- // stop listening for the stopped state event
279
- cd.removeAllListeners(appium_chromedriver_1.default.EVENT_CHANGED);
280
- try {
281
- await cd.stop();
284
+ // Now that we have a Chrome session, we ensure that the context is
285
+ // appropriately set and that this chromedriver is added to the list
286
+ // of session chromedrivers so we can switch back and forth
287
+ this.curContext = helpers_1.CHROMIUM_WIN;
288
+ this.sessionChromedrivers[helpers_1.CHROMIUM_WIN] = this.chromedriver;
289
+ this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);
290
+ this.proxyCommand = /** @type {import('@appium/types').ExternalDriver['proxyCommand']} */ (this.chromedriver.jwproxy.command.bind(this.chromedriver.jwproxy));
291
+ this.jwpProxyActive = true;
292
+ if (this.shouldDismissChromeWelcome()) {
293
+ // dismiss Chrome welcome dialog
294
+ await this.dismissChromeWelcome();
282
295
  }
283
- catch (err) {
284
- this.log.warn(`Error stopping Chromedriver: ${err.message}`);
296
+ },
297
+ /* --------------------------
298
+ * Internal library functions
299
+ * -------------------------- */
300
+ async setupExistingChromedriver(log, chromedriver) {
301
+ // check the status by sending a simple window-based command to ChromeDriver
302
+ // if there is an error, we want to recreate the ChromeDriver session
303
+ if (!(await chromedriver.hasWorkingWebview())) {
304
+ log.debug('ChromeDriver is not associated with a window. ' + 'Re-initializing the session.');
305
+ await chromedriver.restart();
285
306
  }
286
- delete this.sessionChromedrivers[context];
287
- }
288
- };
289
- helpers.isChromedriverContext = function isChromedriverContext(viewName) {
290
- return lodash_1.default.includes(viewName, webview_helpers_1.WEBVIEW_WIN) || viewName === webview_helpers_1.CHROMIUM_WIN;
291
- };
292
- helpers.shouldDismissChromeWelcome = function shouldDismissChromeWelcome() {
293
- return !!this.opts.chromeOptions &&
294
- lodash_1.default.isArray(this.opts.chromeOptions.args) &&
295
- this.opts.chromeOptions.args.includes('--no-first-run');
296
- };
297
- helpers.dismissChromeWelcome = async function dismissChromeWelcome() {
298
- this.log.info('Trying to dismiss Chrome welcome');
299
- let activity = await this.getCurrentActivity();
300
- if (activity !== 'org.chromium.chrome.browser.firstrun.FirstRunActivity') {
301
- this.log.info('Chrome welcome dialog never showed up! Continuing');
302
- return;
303
- }
304
- let el = await this.findElOrEls('id', 'com.android.chrome:id/terms_accept', false);
305
- await this.click(el.ELEMENT);
306
- try {
307
- let el = await this.findElOrEls('id', 'com.android.chrome:id/negative_button', false);
308
- await this.click(el.ELEMENT);
309
- }
310
- catch (e) {
311
- // DO NOTHING, THIS DEVICE DIDNT LAUNCH THE SIGNIN DIALOG
312
- // IT MUST BE A NON GMS DEVICE
313
- this.log.warn(`This device did not show Chrome SignIn dialog, ${e.message}`);
314
- }
315
- };
316
- helpers.startChromeSession = async function startChromeSession() {
317
- this.log.info('Starting a chrome-based browser session');
318
- let opts = lodash_1.default.cloneDeep(this.opts);
319
- const knownPackages = [
320
- 'org.chromium.chrome.shell',
321
- 'com.android.chrome',
322
- 'com.chrome.beta',
323
- 'org.chromium.chrome',
324
- 'org.chromium.webview_shell',
325
- ];
326
- if (lodash_1.default.includes(knownPackages, this.opts.appPackage)) {
327
- opts.chromeBundleId = this.opts.appPackage;
328
- }
329
- else {
330
- opts.chromeAndroidActivity = this.opts.appActivity;
331
- }
332
- this.chromedriver = await this.setupNewChromedriver(opts, this.adb.curDeviceId, this.adb);
333
- this.chromedriver.on(appium_chromedriver_1.default.EVENT_CHANGED, (msg) => {
334
- if (msg.state === appium_chromedriver_1.default.STATE_STOPPED) {
335
- this.onChromedriverStop(webview_helpers_1.CHROMIUM_WIN);
307
+ return chromedriver;
308
+ },
309
+ async getChromedriverPort(portSpec, log) {
310
+ const getPort = /** @type {(opts?: import('portfinder').PortFinderOptions) => B<number>} */ (bluebird_1.default.promisify(portfinder_1.default.getPort, { context: portfinder_1.default }));
311
+ // if the user didn't give us any specific information about chromedriver
312
+ // port ranges, just find any free port
313
+ if (!portSpec) {
314
+ const port = await getPort();
315
+ log?.debug(`A port was not given, using random free port: ${port}`);
316
+ return port;
336
317
  }
337
- });
338
- // Now that we have a Chrome session, we ensure that the context is
339
- // appropriately set and that this chromedriver is added to the list
340
- // of session chromedrivers so we can switch back and forth
341
- this.curContext = webview_helpers_1.CHROMIUM_WIN;
342
- this.sessionChromedrivers[webview_helpers_1.CHROMIUM_WIN] = this.chromedriver;
343
- this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);
344
- this.proxyCommand = this.chromedriver.jwproxy.command.bind(this.chromedriver.jwproxy);
345
- this.jwpProxyActive = true;
346
- if (this.shouldDismissChromeWelcome()) {
347
- // dismiss Chrome welcome dialog
348
- await this.dismissChromeWelcome();
349
- }
350
- };
351
- /* --------------------------
352
- * Internal library functions
353
- * -------------------------- */
354
- async function setupExistingChromedriver(log, chromedriver) {
355
- // check the status by sending a simple window-based command to ChromeDriver
356
- // if there is an error, we want to recreate the ChromeDriver session
357
- if (!await chromedriver.hasWorkingWebview()) {
358
- log.debug('ChromeDriver is not associated with a window. ' +
359
- 'Re-initializing the session.');
360
- await chromedriver.restart();
361
- }
362
- return chromedriver;
363
- }
364
- /**
365
- * Find a free port to have Chromedriver listen on.
366
- *
367
- * @param {array} portSpec - Array which is a list of ports. A list item may
368
- * also itself be an array of length 2 specifying a start and end port of
369
- * a range. Some valid port specs:
370
- * - [8000, 8001, 8002]
371
- * - [[8000, 8005]]
372
- * - [8000, [9000, 9100]]
373
- * @param {Object?} log Logger instance
374
- *
375
- * @return {number} A free port
376
- */
377
- async function getChromedriverPort(portSpec, log = null) {
378
- const getPort = bluebird_1.default.promisify(portfinder_1.default.getPort, { context: portfinder_1.default });
379
- // if the user didn't give us any specific information about chromedriver
380
- // port ranges, just find any free port
381
- if (!portSpec) {
382
- const port = await getPort();
383
- log?.debug(`A port was not given, using random free port: ${port}`);
384
- return port;
385
- }
386
- // otherwise find the free port based on a list or range provided by the user
387
- log?.debug(`Finding a free port for chromedriver using spec ${JSON.stringify(portSpec)}`);
388
- let foundPort = null;
389
- for (const potentialPort of portSpec) {
390
- let port, stopPort;
391
- if (lodash_1.default.isArray(potentialPort)) {
392
- ([port, stopPort] = potentialPort);
318
+ // otherwise find the free port based on a list or range provided by the user
319
+ log?.debug(`Finding a free port for chromedriver using spec ${JSON.stringify(portSpec)}`);
320
+ let foundPort = null;
321
+ for (const potentialPort of portSpec) {
322
+ /** @type {number} */
323
+ let port;
324
+ /** @type {number} */
325
+ let stopPort;
326
+ if (lodash_1.default.isArray(potentialPort)) {
327
+ [port, stopPort] = potentialPort.map((p) => parseInt(String(p), 10));
328
+ }
329
+ else {
330
+ port = parseInt(String(potentialPort), 10); // ensure we have a number and not a string
331
+ stopPort = port;
332
+ }
333
+ try {
334
+ log?.debug(`Checking port range ${port}:${stopPort}`);
335
+ foundPort = await getPort({ port, stopPort });
336
+ break;
337
+ }
338
+ catch (e) {
339
+ log?.debug(`Nothing in port range ${port}:${stopPort} was available`);
340
+ }
393
341
  }
394
- else {
395
- port = parseInt(potentialPort, 10); // ensure we have a number and not a string
396
- stopPort = port;
342
+ if (foundPort === null) {
343
+ throw new Error(`Could not find a free port for chromedriver using ` +
344
+ `chromedriverPorts spec ${JSON.stringify(portSpec)}`);
397
345
  }
398
- try {
399
- log?.debug(`Checking port range ${port}:${stopPort}`);
400
- foundPort = await getPort({ port, stopPort });
401
- break;
346
+ log?.debug(`Using free port ${foundPort} for chromedriver`);
347
+ return foundPort;
348
+ },
349
+ isChromedriverAutodownloadEnabled() {
350
+ if (this.isFeatureEnabled(CHROMEDRIVER_AUTODOWNLOAD_FEATURE)) {
351
+ return true;
402
352
  }
403
- catch (e) {
404
- log?.debug(`Nothing in port range ${port}:${stopPort} was available`);
353
+ this?.log?.debug(`Automated Chromedriver download is disabled. ` +
354
+ `Use '${CHROMEDRIVER_AUTODOWNLOAD_FEATURE}' server feature to enable it`);
355
+ return false;
356
+ },
357
+ async setupNewChromedriver(opts, curDeviceId, adb, context) {
358
+ if (opts.chromeDriverPort) {
359
+ this?.log?.warn(`The 'chromeDriverPort' capability is deprecated. Please use 'chromedriverPort' instead`);
360
+ opts.chromedriverPort = opts.chromeDriverPort;
405
361
  }
406
- }
407
- if (foundPort === null) {
408
- throw new Error(`Could not find a free port for chromedriver using ` +
409
- `chromedriverPorts spec ${JSON.stringify(portSpec)}`);
410
- }
411
- log?.debug(`Using free port ${foundPort} for chromedriver`);
412
- return foundPort;
413
- }
414
- helpers.isChromedriverAutodownloadEnabled = function isChromedriverAutodownloadEnabled() {
415
- if (this.isFeatureEnabled(CHROMEDRIVER_AUTODOWNLOAD_FEATURE)) {
416
- return true;
417
- }
418
- this?.log?.debug(`Automated Chromedriver download is disabled. ` +
419
- `Use '${CHROMEDRIVER_AUTODOWNLOAD_FEATURE}' server feature to enable it`);
420
- return false;
421
- };
422
- helpers.setupNewChromedriver = async function setupNewChromedriver(opts, curDeviceId, adb, context = null) {
423
- if (opts.chromeDriverPort) {
424
- this?.log?.warn(`The 'chromeDriverPort' capability is deprecated. Please use 'chromedriverPort' instead`);
425
- opts.chromedriverPort = opts.chromeDriverPort;
426
- }
427
- if (opts.chromedriverPort) {
428
- this?.log?.debug(`Using user-specified port ${opts.chromedriverPort} for chromedriver`);
429
- }
430
- else {
431
- // if a single port wasn't given, we'll look for a free one
432
- opts.chromedriverPort = await getChromedriverPort(opts.chromedriverPorts, this?.log);
433
- }
434
- const details = context ? webview_helpers_1.default.getWebviewDetails(adb, context) : undefined;
435
- if (!lodash_1.default.isEmpty(details)) {
436
- this?.log?.debug('Passing web view details to the Chromedriver constructor: ' +
437
- JSON.stringify(details, null, 2));
438
- }
439
- const chromedriver = new appium_chromedriver_1.default({
440
- port: opts.chromedriverPort,
441
- executable: opts.chromedriverExecutable,
442
- adb,
443
- cmdArgs: opts.chromedriverArgs,
444
- verbose: !!opts.showChromedriverLog,
445
- executableDir: opts.chromedriverExecutableDir,
446
- mappingPath: opts.chromedriverChromeMappingFile,
447
- bundleId: opts.chromeBundleId,
448
- useSystemExecutable: opts.chromedriverUseSystemExecutable,
449
- disableBuildCheck: opts.chromedriverDisableBuildCheck,
450
- details,
451
- isAutodownloadEnabled: this?.isChromedriverAutodownloadEnabled?.()
452
- });
453
- // make sure there are chromeOptions
454
- opts.chromeOptions = opts.chromeOptions || {};
455
- // try out any prefixed chromeOptions,
456
- // and strip the prefix
457
- for (const opt of lodash_1.default.keys(opts)) {
458
- if (opt.endsWith(':chromeOptions')) {
459
- this?.log?.warn(`Merging '${opt}' into 'chromeOptions'. This may cause unexpected behavior`);
460
- lodash_1.default.merge(opts.chromeOptions, opts[opt]);
362
+ if (opts.chromedriverPort) {
363
+ this?.log?.debug(`Using user-specified port ${opts.chromedriverPort} for chromedriver`);
364
+ }
365
+ else {
366
+ // if a single port wasn't given, we'll look for a free one
367
+ opts.chromedriverPort = await this.getChromedriverPort(opts.chromedriverPorts, this?.log);
461
368
  }
462
- }
463
- const caps = webview_helpers_1.default.createChromedriverCaps(opts, curDeviceId, details);
464
- this?.log?.debug(`Before starting chromedriver, androidPackage is '${caps.chromeOptions.androidPackage}'`);
465
- await chromedriver.start(caps);
466
- return chromedriver;
369
+ const details = context ? helpers_1.WebviewHelpers.getWebviewDetails(adb, context) : undefined;
370
+ if (!lodash_1.default.isEmpty(details)) {
371
+ this?.log?.debug('Passing web view details to the Chromedriver constructor: ' +
372
+ JSON.stringify(details, null, 2));
373
+ }
374
+ const chromedriver = new appium_chromedriver_1.default({
375
+ port: String(opts.chromedriverPort),
376
+ executable: opts.chromedriverExecutable,
377
+ // eslint-disable-next-line object-shorthand
378
+ adb: /** @type {any} */ (adb),
379
+ cmdArgs: /** @type {string[]} */ (opts.chromedriverArgs),
380
+ verbose: !!opts.showChromedriverLog,
381
+ executableDir: opts.chromedriverExecutableDir,
382
+ mappingPath: opts.chromedriverChromeMappingFile,
383
+ // @ts-expect-error arbitrary value on opts?
384
+ bundleId: opts.chromeBundleId,
385
+ useSystemExecutable: opts.chromedriverUseSystemExecutable,
386
+ disableBuildCheck: opts.chromedriverDisableBuildCheck,
387
+ // @ts-expect-error FIXME: chromedriver typing are probably too strict
388
+ details,
389
+ isAutodownloadEnabled: this?.isChromedriverAutodownloadEnabled?.(),
390
+ });
391
+ // make sure there are chromeOptions
392
+ opts.chromeOptions = opts.chromeOptions || {};
393
+ // try out any prefixed chromeOptions,
394
+ // and strip the prefix
395
+ for (const opt of lodash_1.default.keys(opts)) {
396
+ if (opt.endsWith(':chromeOptions')) {
397
+ this?.log?.warn(`Merging '${opt}' into 'chromeOptions'. This may cause unexpected behavior`);
398
+ // @ts-expect-error unsafe types
399
+ lodash_1.default.merge(opts.chromeOptions, opts[opt]);
400
+ }
401
+ }
402
+ const caps = /** @type {any} */ (helpers_1.WebviewHelpers.createChromedriverCaps(opts, curDeviceId, details));
403
+ this?.log?.debug(`Before starting chromedriver, androidPackage is '${caps.chromeOptions.androidPackage}'`);
404
+ await chromedriver.start(caps);
405
+ return chromedriver;
406
+ },
467
407
  };
468
- const setupNewChromedriver = helpers.setupNewChromedriver;
469
- exports.setupNewChromedriver = setupNewChromedriver;
470
- Object.assign(extensions, commands, helpers);
471
- exports.default = extensions;
408
+ (0, mixins_1.mixin)(ContextMixin);
409
+ exports.default = ContextMixin;
410
+ exports.setupNewChromedriver = ContextMixin.setupNewChromedriver;
411
+ /**
412
+ * @typedef {import('appium-adb').ADB} ADB
413
+ */
472
414
  //# sourceMappingURL=context.js.map