@appium/base-driver 8.2.4 → 8.4.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 (206) hide show
  1. package/build/lib/basedriver/capabilities.d.ts +80 -0
  2. package/build/lib/basedriver/capabilities.d.ts.map +1 -0
  3. package/build/lib/basedriver/capabilities.js +12 -13
  4. package/build/lib/basedriver/commands/event.d.ts +9 -0
  5. package/build/lib/basedriver/commands/event.d.ts.map +1 -0
  6. package/build/lib/basedriver/commands/event.js +21 -23
  7. package/build/lib/basedriver/commands/find.d.ts +11 -0
  8. package/build/lib/basedriver/commands/find.d.ts.map +1 -0
  9. package/build/lib/basedriver/commands/find.js +41 -43
  10. package/build/lib/basedriver/commands/index.d.ts +8 -0
  11. package/build/lib/basedriver/commands/index.d.ts.map +1 -0
  12. package/build/lib/basedriver/commands/index.js +17 -16
  13. package/build/lib/basedriver/commands/log.d.ts +12 -0
  14. package/build/lib/basedriver/commands/log.d.ts.map +1 -0
  15. package/build/lib/basedriver/commands/log.js +23 -30
  16. package/build/lib/basedriver/commands/session.d.ts +11 -0
  17. package/build/lib/basedriver/commands/session.d.ts.map +1 -0
  18. package/build/lib/basedriver/commands/session.js +21 -158
  19. package/build/lib/basedriver/commands/settings.d.ts +10 -0
  20. package/build/lib/basedriver/commands/settings.d.ts.map +1 -0
  21. package/build/lib/basedriver/commands/settings.js +18 -22
  22. package/build/lib/basedriver/commands/timeout.d.ts +8 -0
  23. package/build/lib/basedriver/commands/timeout.d.ts.map +1 -0
  24. package/build/lib/basedriver/commands/timeout.js +118 -144
  25. package/build/lib/basedriver/core.d.ts +235 -0
  26. package/build/lib/basedriver/core.d.ts.map +1 -0
  27. package/build/lib/basedriver/core.js +283 -0
  28. package/build/lib/basedriver/desired-caps.d.ts +5 -0
  29. package/build/lib/basedriver/desired-caps.d.ts.map +1 -0
  30. package/build/lib/basedriver/desired-caps.js +2 -4
  31. package/build/lib/basedriver/device-settings.d.ts +32 -0
  32. package/build/lib/basedriver/device-settings.d.ts.map +1 -0
  33. package/build/lib/basedriver/device-settings.js +33 -15
  34. package/build/lib/basedriver/driver.d.ts +83 -0
  35. package/build/lib/basedriver/driver.d.ts.map +1 -0
  36. package/build/lib/basedriver/driver.js +104 -257
  37. package/build/lib/basedriver/helpers.d.ts +132 -0
  38. package/build/lib/basedriver/helpers.d.ts.map +1 -0
  39. package/build/lib/basedriver/helpers.js +24 -10
  40. package/build/lib/basedriver/logger.d.ts +3 -0
  41. package/build/lib/basedriver/logger.d.ts.map +1 -0
  42. package/build/lib/basedriver/logger.js +2 -4
  43. package/build/lib/constants.d.ts +9 -0
  44. package/build/lib/constants.d.ts.map +1 -0
  45. package/build/lib/constants.js +2 -4
  46. package/build/lib/express/crash.d.ts +3 -0
  47. package/build/lib/express/crash.d.ts.map +1 -0
  48. package/build/lib/express/crash.js +2 -4
  49. package/build/lib/express/express-logging.d.ts +3 -0
  50. package/build/lib/express/express-logging.d.ts.map +1 -0
  51. package/build/lib/express/express-logging.js +3 -5
  52. package/build/lib/express/idempotency.d.ts +2 -0
  53. package/build/lib/express/idempotency.d.ts.map +1 -0
  54. package/build/lib/express/idempotency.js +3 -5
  55. package/build/lib/express/logger.d.ts +3 -0
  56. package/build/lib/express/logger.d.ts.map +1 -0
  57. package/build/lib/express/logger.js +2 -4
  58. package/build/lib/express/middleware.d.ts +9 -0
  59. package/build/lib/express/middleware.d.ts.map +1 -0
  60. package/build/lib/express/middleware.js +2 -4
  61. package/build/lib/express/server.d.ts +10 -0
  62. package/build/lib/express/server.d.ts.map +1 -0
  63. package/build/lib/express/server.js +2 -4
  64. package/build/lib/express/static.d.ts +6 -0
  65. package/build/lib/express/static.d.ts.map +1 -0
  66. package/build/lib/express/static.js +2 -4
  67. package/build/lib/express/websocket.d.ts +64 -0
  68. package/build/lib/express/websocket.d.ts.map +1 -0
  69. package/build/lib/express/websocket.js +2 -4
  70. package/build/lib/helpers/capabilities.d.ts +13 -0
  71. package/build/lib/helpers/capabilities.d.ts.map +1 -0
  72. package/build/lib/helpers/capabilities.js +77 -0
  73. package/build/lib/index.d.ts +183 -0
  74. package/build/lib/index.d.ts.map +1 -0
  75. package/build/lib/index.js +41 -25
  76. package/build/lib/jsonwp-proxy/protocol-converter.d.ts +48 -0
  77. package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -0
  78. package/build/lib/jsonwp-proxy/protocol-converter.js +19 -15
  79. package/build/lib/jsonwp-proxy/proxy.d.ts +41 -0
  80. package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -0
  81. package/build/lib/jsonwp-proxy/proxy.js +20 -15
  82. package/build/lib/jsonwp-status/status.d.ts +159 -0
  83. package/build/lib/jsonwp-status/status.d.ts.map +1 -0
  84. package/build/lib/jsonwp-status/status.js +2 -4
  85. package/build/lib/protocol/errors.d.ts +310 -0
  86. package/build/lib/protocol/errors.d.ts.map +1 -0
  87. package/build/lib/protocol/errors.js +82 -5
  88. package/build/lib/protocol/helpers.d.ts +22 -0
  89. package/build/lib/protocol/helpers.d.ts.map +1 -0
  90. package/build/lib/protocol/helpers.js +2 -4
  91. package/build/lib/protocol/index.d.ts +16 -0
  92. package/build/lib/protocol/index.d.ts.map +1 -0
  93. package/build/lib/protocol/index.js +8 -10
  94. package/build/lib/protocol/protocol.d.ts +11 -0
  95. package/build/lib/protocol/protocol.d.ts.map +1 -0
  96. package/build/lib/protocol/protocol.js +38 -35
  97. package/build/lib/protocol/routes.d.ts +6 -0
  98. package/build/lib/protocol/routes.d.ts.map +1 -0
  99. package/build/lib/protocol/routes.js +84 -4
  100. package/build/lib/protocol/validators.d.ts +8 -0
  101. package/build/lib/protocol/validators.d.ts.map +1 -0
  102. package/build/lib/protocol/validators.js +2 -4
  103. package/build/test/basedriver/README.md +5 -0
  104. package/build/test/basedriver/driver-e2e-tests.js +2 -4
  105. package/build/test/basedriver/driver-tests.js +41 -19
  106. package/build/test/basedriver/index.js +2 -4
  107. package/build/test/e2e/basedriver/driver.e2e.spec.js +15 -0
  108. package/build/test/e2e/basedriver/helpers.e2e.spec.js +192 -0
  109. package/build/test/e2e/basedriver/websockets.e2e.spec.js +82 -0
  110. package/build/test/e2e/express/server.e2e.spec.js +159 -0
  111. package/build/test/e2e/jsonwp-proxy/proxy.e2e.spec.js +59 -0
  112. package/build/test/e2e/protocol/fake-driver.js +163 -0
  113. package/build/test/e2e/protocol/helpers.js +25 -0
  114. package/build/test/e2e/protocol/protocol.e2e.spec.js +1186 -0
  115. package/build/test/helpers.js +2 -4
  116. package/build/test/unit/basedriver/capabilities.spec.js +672 -0
  117. package/build/test/unit/basedriver/capability.spec.js +353 -0
  118. package/build/test/unit/basedriver/commands/event.spec.js +110 -0
  119. package/build/test/unit/basedriver/commands/log.spec.js +92 -0
  120. package/build/test/unit/basedriver/driver.spec.js +15 -0
  121. package/build/test/unit/basedriver/helpers.spec.js +151 -0
  122. package/build/test/unit/basedriver/timeout.spec.js +135 -0
  123. package/build/test/unit/express/server.spec.js +155 -0
  124. package/build/test/unit/express/static.spec.js +26 -0
  125. package/build/test/unit/jsonwp-proxy/mock-request.js +91 -0
  126. package/build/test/unit/jsonwp-proxy/protocol-converter.spec.js +171 -0
  127. package/build/test/unit/jsonwp-proxy/proxy.spec.js +292 -0
  128. package/build/test/unit/jsonwp-proxy/url.spec.js +165 -0
  129. package/build/test/unit/jsonwp-status/status.spec.js +34 -0
  130. package/build/test/unit/protocol/errors.spec.js +390 -0
  131. package/build/test/unit/protocol/routes.spec.js +80 -0
  132. package/build/test/unit/protocol/validator.spec.js +149 -0
  133. package/build/tsconfig.tsbuildinfo +1 -0
  134. package/lib/basedriver/capabilities.js +49 -10
  135. package/lib/basedriver/commands/event.js +49 -31
  136. package/lib/basedriver/commands/find.js +107 -45
  137. package/lib/basedriver/commands/index.js +25 -19
  138. package/lib/basedriver/commands/log.js +59 -34
  139. package/lib/basedriver/commands/session.js +39 -142
  140. package/lib/basedriver/commands/settings.js +32 -14
  141. package/lib/basedriver/commands/timeout.js +153 -154
  142. package/lib/basedriver/core.js +497 -0
  143. package/lib/basedriver/desired-caps.js +1 -1
  144. package/lib/basedriver/device-settings.js +57 -13
  145. package/lib/basedriver/driver.js +277 -375
  146. package/lib/basedriver/helpers.js +31 -13
  147. package/lib/express/express-logging.js +1 -1
  148. package/lib/express/idempotency.js +1 -1
  149. package/lib/helpers/capabilities.js +84 -0
  150. package/lib/index.js +17 -13
  151. package/lib/jsonwp-proxy/protocol-converter.js +14 -13
  152. package/lib/jsonwp-proxy/proxy.js +16 -12
  153. package/lib/protocol/errors.js +42 -42
  154. package/lib/protocol/index.js +4 -4
  155. package/lib/protocol/protocol.js +35 -32
  156. package/lib/protocol/routes.js +69 -1
  157. package/package.json +37 -24
  158. package/test/basedriver/README.md +5 -0
  159. package/test/basedriver/driver-e2e-tests.js +1 -1
  160. package/test/basedriver/driver-tests.js +43 -9
  161. package/build/lib/protocol/sessions-cache.js +0 -88
  162. package/build/test/basedriver/capabilities-specs.js +0 -632
  163. package/build/test/basedriver/capability-specs.js +0 -396
  164. package/build/test/basedriver/commands/event-specs.js +0 -112
  165. package/build/test/basedriver/commands/log-specs.js +0 -80
  166. package/build/test/basedriver/driver-e2e-specs.js +0 -17
  167. package/build/test/basedriver/driver-specs.js +0 -17
  168. package/build/test/basedriver/helpers-e2e-specs.js +0 -194
  169. package/build/test/basedriver/helpers-specs.js +0 -153
  170. package/build/test/basedriver/timeout-specs.js +0 -139
  171. package/build/test/basedriver/websockets-e2e-specs.js +0 -84
  172. package/build/test/express/server-e2e-specs.js +0 -156
  173. package/build/test/express/server-specs.js +0 -151
  174. package/build/test/express/static-specs.js +0 -23
  175. package/build/test/jsonwp-proxy/mock-request.js +0 -93
  176. package/build/test/jsonwp-proxy/protocol-converter-specs.js +0 -173
  177. package/build/test/jsonwp-proxy/proxy-e2e-specs.js +0 -61
  178. package/build/test/jsonwp-proxy/proxy-specs.js +0 -294
  179. package/build/test/jsonwp-proxy/url-specs.js +0 -167
  180. package/build/test/jsonwp-status/status-specs.js +0 -36
  181. package/build/test/protocol/errors-specs.js +0 -388
  182. package/build/test/protocol/fake-driver.js +0 -168
  183. package/build/test/protocol/helpers.js +0 -27
  184. package/build/test/protocol/protocol-e2e-specs.js +0 -1182
  185. package/build/test/protocol/routes-specs.js +0 -82
  186. package/build/test/protocol/validator-specs.js +0 -151
  187. package/index.d.ts +0 -386
  188. package/lib/protocol/sessions-cache.js +0 -74
  189. package/test/basedriver/capabilities-specs.js +0 -505
  190. package/test/basedriver/capability-specs.js +0 -409
  191. package/test/basedriver/commands/event-specs.js +0 -74
  192. package/test/basedriver/commands/log-specs.js +0 -70
  193. package/test/basedriver/driver-e2e-specs.js +0 -8
  194. package/test/basedriver/driver-specs.js +0 -8
  195. package/test/basedriver/fixtures/BadZippedApp.zip +0 -1
  196. package/test/basedriver/fixtures/FakeAndroidApp.apk +0 -1
  197. package/test/basedriver/fixtures/FakeAndroidApp.asd +0 -0
  198. package/test/basedriver/fixtures/FakeIOSApp.app +0 -1
  199. package/test/basedriver/fixtures/FakeIOSApp.app.zip +0 -0
  200. package/test/basedriver/fixtures/FakeIOSApp.ipa +0 -0
  201. package/test/basedriver/fixtures/custom-element-finder-bad.js +0 -5
  202. package/test/basedriver/fixtures/custom-element-finder.js +0 -29
  203. package/test/basedriver/helpers-e2e-specs.js +0 -187
  204. package/test/basedriver/helpers-specs.js +0 -137
  205. package/test/basedriver/timeout-specs.js +0 -133
  206. package/test/basedriver/websockets-e2e-specs.js +0 -75
@@ -0,0 +1,497 @@
1
+ // @ts-check
2
+ /* eslint-disable no-unused-vars */
3
+ /* eslint-disable require-await */
4
+
5
+ import { fs, logger, node } from '@appium/support';
6
+ import AsyncLock from 'async-lock';
7
+ import { EventEmitter } from 'events';
8
+ import _ from 'lodash';
9
+ import os from 'os';
10
+ import { DEFAULT_BASE_PATH, PROTOCOLS } from '../constants';
11
+ import { errors } from '../protocol';
12
+ import { validateCaps } from './capabilities';
13
+ import { desiredCapabilityConstraints } from './desired-caps';
14
+ import DeviceSettings from './device-settings';
15
+ import helpers from './helpers';
16
+
17
+ // for compat with running tests transpiled and in-place
18
+ const {version: BASEDRIVER_VER} = fs.readPackageJsonFrom(__dirname);
19
+
20
+ const NEW_COMMAND_TIMEOUT_MS = 60 * 1000;
21
+
22
+ const ON_UNEXPECTED_SHUTDOWN_EVENT = 'onUnexpectedShutdown';
23
+ /**
24
+ * @implements {Core}
25
+ */
26
+ class DriverCore {
27
+
28
+ /**
29
+ * Make the basedriver version available so for any driver which inherits from this package, we
30
+ * know which version of basedriver it inherited from
31
+ */
32
+ static baseVersion = BASEDRIVER_VER;
33
+
34
+ /**
35
+ * @type {string?}
36
+ */
37
+ sessionId = null;
38
+
39
+ /**
40
+ * @type {DriverOpts & Capabilities}
41
+ */
42
+ opts;
43
+
44
+ /**
45
+ * @type {DriverOpts}
46
+ */
47
+ initialOpts;
48
+
49
+ /**
50
+ * @type {Capabilities}
51
+ */
52
+ caps;
53
+
54
+ /**
55
+ * @type {W3CCapabilities}
56
+ */
57
+ originalCaps;
58
+
59
+ helpers = helpers;
60
+
61
+ /**
62
+ * basePath is used for several purposes, for example in setting up
63
+ * proxying to other drivers, since we need to know what the base path
64
+ * of any incoming request might look like. We set it to the default
65
+ * initially but it is automatically updated during any actual program
66
+ * execution by the routeConfiguringFunction, which is necessarily run as
67
+ * the entrypoint for any Appium server
68
+ */
69
+ basePath = DEFAULT_BASE_PATH;
70
+
71
+ relaxedSecurityEnabled = false;
72
+
73
+ /** @type {string[]} */
74
+ allowInsecure = [];
75
+
76
+ /** @type {string[]} */
77
+ denyInsecure = [];
78
+
79
+ newCommandTimeoutMs = NEW_COMMAND_TIMEOUT_MS;
80
+
81
+ implicitWaitMs = 0;
82
+
83
+ /** @type {string[]} */
84
+ locatorStrategies = [];
85
+
86
+ /** @type {string[]} */
87
+ webLocatorStrategies = [];
88
+
89
+ /** @type {Driver[]} */
90
+ managedDrivers = [];
91
+
92
+ /** @type {NodeJS.Timeout?} */
93
+ noCommandTimer = null;
94
+
95
+ /** @type {EventHistory} */
96
+ _eventHistory = {commands: []};
97
+
98
+ _constraints = _.cloneDeep(desiredCapabilityConstraints);
99
+
100
+ // used to handle driver events
101
+ /** @type {NodeJS.EventEmitter} */
102
+ eventEmitter = new EventEmitter();
103
+
104
+ /**
105
+ * @type {AppiumLogger}
106
+ */
107
+ _log;
108
+
109
+ /**
110
+ * @protected
111
+ */
112
+ shutdownUnexpectedly = false;
113
+
114
+ /**
115
+ * @type {boolean}
116
+ * @protected
117
+ */
118
+ shouldValidateCaps;
119
+
120
+ /**
121
+ * @protected
122
+ */
123
+ commandsQueueGuard = new AsyncLock();
124
+
125
+ /**
126
+ * settings should be instantiated by drivers which extend BaseDriver, but
127
+ * we set it to an empty DeviceSettings instance here to make sure that the
128
+ * default settings are applied even if an extending driver doesn't utilize
129
+ * the settings functionality itself
130
+ */
131
+ settings = new DeviceSettings();
132
+
133
+ constructor (
134
+ opts = /** @type {DriverOpts} */ ({}),
135
+ shouldValidateCaps = true,
136
+ ) {
137
+ this._log = logger.getLogger(helpers.generateDriverLogPrefix(this));
138
+
139
+ // setup state
140
+ this.opts = opts;
141
+
142
+ // use a custom tmp dir to avoid losing data and app when computer is
143
+ // restarted
144
+ this.opts.tmpDir =
145
+ this.opts.tmpDir || process.env.APPIUM_TMP_DIR || os.tmpdir();
146
+
147
+ // base-driver internals
148
+ this.shouldValidateCaps = shouldValidateCaps;
149
+
150
+ // keeping track of initial opts
151
+ this.initialOpts = _.cloneDeep(this.opts);
152
+
153
+ this.sessionId = null;
154
+ }
155
+
156
+ get log () {
157
+ return this._log;
158
+ }
159
+
160
+ /**
161
+ * Set a callback handler if needed to execute a custom piece of code
162
+ * when the driver is shut down unexpectedly. Multiple calls to this method
163
+ * will cause the handler to be executed mutiple times
164
+ *
165
+ * @param {(...args: any[]) => void} handler The code to be executed on unexpected shutdown.
166
+ * The function may accept one argument, which is the actual error instance, which
167
+ * caused the driver to shut down.
168
+ */
169
+ onUnexpectedShutdown (handler) {
170
+ this.eventEmitter.on(ON_UNEXPECTED_SHUTDOWN_EVENT, handler);
171
+ }
172
+
173
+ /**
174
+ * This property is used by AppiumDriver to store the data of the
175
+ * specific driver sessions. This data can be later used to adjust
176
+ * properties for driver instances running in parallel.
177
+ * Override it in inherited driver classes if necessary.
178
+ *
179
+ * @return {Record<string,unknown>} Driver properties mapping
180
+ */
181
+ get driverData () {
182
+ return {};
183
+ }
184
+
185
+ /**
186
+ * This property controls the way {#executeCommand} method
187
+ * handles new driver commands received from the client.
188
+ * Override it for inherited classes only in special cases.
189
+ *
190
+ * @return {boolean} If the returned value is true (default) then all the commands
191
+ * received by the particular driver instance are going to be put into the queue,
192
+ * so each following command will not be executed until the previous command
193
+ * execution is completed. False value disables that queue, so each driver command
194
+ * is executed independently and does not wait for anything.
195
+ */
196
+ get isCommandsQueueEnabled () {
197
+ return true;
198
+ }
199
+
200
+ /*
201
+ * make eventHistory a property and return a cloned object so a consumer can't
202
+ * inadvertently change data outside of logEvent
203
+ */
204
+ get eventHistory () {
205
+ return _.cloneDeep(this._eventHistory);
206
+ }
207
+
208
+ /**
209
+ * API method for driver developers to log timings for important events
210
+ * @param {string} eventName
211
+ */
212
+ logEvent (eventName) {
213
+ if (eventName === 'commands') {
214
+ throw new Error('Cannot log commands directly');
215
+ }
216
+ if (typeof eventName !== 'string') {
217
+ throw new Error(`Invalid eventName ${eventName}`);
218
+ }
219
+ if (!this._eventHistory[eventName]) {
220
+ this._eventHistory[eventName] = [];
221
+ }
222
+ const ts = Date.now();
223
+ const logTime = new Date(ts).toTimeString();
224
+ this._eventHistory[eventName].push(ts);
225
+ this.log.debug(`Event '${eventName}' logged at ${ts} (${logTime})`);
226
+ }
227
+
228
+ /**
229
+ * Overridden in appium driver, but here so that individual drivers can be
230
+ * tested with clients that poll
231
+ */
232
+ async getStatus () {
233
+ return {};
234
+ }
235
+
236
+ // we only want subclasses to ever extend the contraints
237
+ set desiredCapConstraints (constraints) {
238
+ this._constraints = Object.assign(this._constraints, constraints);
239
+ // 'presence' means different things in different versions of the validator,
240
+ // when we say 'true' we mean that it should not be able to be empty
241
+ for (const [, value] of _.toPairs(this._constraints)) {
242
+ if (value && value.presence === true) {
243
+ value.presence = {
244
+ allowEmpty: false,
245
+ };
246
+ }
247
+ }
248
+ }
249
+
250
+ get desiredCapConstraints () {
251
+ return this._constraints;
252
+ }
253
+
254
+ /**
255
+ * method required by MJSONWP in order to determine whether it should
256
+ * respond with an invalid session response
257
+ * @param {string} [sessionId]
258
+ * @returns {boolean}
259
+ */
260
+ sessionExists (sessionId) {
261
+ if (!sessionId) return false; // eslint-disable-line curly
262
+ return sessionId === this.sessionId;
263
+ }
264
+
265
+ /**
266
+ * method required by MJSONWP in order to determine if the command should
267
+ * be proxied directly to the driver
268
+ * @param {string} sessionId
269
+ * @returns {this | import('@appium/types').Driver}
270
+ */
271
+ driverForSession (sessionId) {
272
+ return this;
273
+ }
274
+
275
+ /**
276
+ *
277
+ * @param {Capabilities} caps
278
+ */
279
+ logExtraCaps (caps) {
280
+ let extraCaps = _.difference(_.keys(caps), _.keys(this._constraints));
281
+ if (extraCaps.length) {
282
+ this.log.warn(
283
+ `The following capabilities were provided, but are not ` +
284
+ `recognized by Appium:`,
285
+ );
286
+ for (const cap of extraCaps) {
287
+ this.log.warn(` ${cap}`);
288
+ }
289
+ }
290
+ }
291
+
292
+ /**
293
+ *
294
+ * @param {Capabilities} caps
295
+ * @returns {boolean}
296
+ */
297
+ validateDesiredCaps (caps) {
298
+ if (!this.shouldValidateCaps) {
299
+ return true;
300
+ }
301
+
302
+ try {
303
+ validateCaps(caps, this._constraints);
304
+ } catch (e) {
305
+ this.log.errorAndThrow(
306
+ new errors.SessionNotCreatedError(
307
+ `The desiredCapabilities object was not valid for the ` +
308
+ `following reason(s): ${e.message}`,
309
+ ),
310
+ );
311
+ }
312
+
313
+ this.logExtraCaps(caps);
314
+
315
+ return true;
316
+ }
317
+
318
+ isMjsonwpProtocol () {
319
+ return this.protocol === PROTOCOLS.MJSONWP;
320
+ }
321
+
322
+ isW3CProtocol () {
323
+ return this.protocol === PROTOCOLS.W3C;
324
+ }
325
+
326
+ setProtocolMJSONWP () {
327
+ this.protocol = PROTOCOLS.MJSONWP;
328
+ }
329
+
330
+ setProtocolW3C () {
331
+ this.protocol = PROTOCOLS.W3C;
332
+ }
333
+
334
+ /**
335
+ * Check whether a given feature is enabled via its name
336
+ *
337
+ * @param {string} name - name of feature/command
338
+ *
339
+ * @returns {Boolean}
340
+ */
341
+ isFeatureEnabled (name) {
342
+ // if we have explicitly denied this feature, return false immediately
343
+ if (this.denyInsecure && _.includes(this.denyInsecure, name)) {
344
+ return false;
345
+ }
346
+
347
+ // if we specifically have allowed the feature, return true
348
+ if (this.allowInsecure && _.includes(this.allowInsecure, name)) {
349
+ return true;
350
+ }
351
+
352
+ // otherwise, if we've globally allowed insecure features and not denied
353
+ // this one, return true
354
+ if (this.relaxedSecurityEnabled) {
355
+ return true;
356
+ }
357
+
358
+ // if we haven't allowed anything insecure, then reject
359
+ return false;
360
+ }
361
+
362
+ /**
363
+ * Assert that a given feature is enabled and throw a helpful error if it's
364
+ * not
365
+ *
366
+ * @param {string} name - name of feature/command
367
+ */
368
+ ensureFeatureEnabled (name) {
369
+ if (!this.isFeatureEnabled(name)) {
370
+ throw new Error(
371
+ `Potentially insecure feature '${name}' has not been ` +
372
+ `enabled. If you want to enable this feature and accept ` +
373
+ `the security ramifications, please do so by following ` +
374
+ `the documented instructions at https://github.com/appium` +
375
+ `/appium/blob/master/docs/en/writing-running-appium/security.md`,
376
+ );
377
+ }
378
+ }
379
+
380
+ /**
381
+ *
382
+ * @param {string} strategy
383
+ * @param {boolean} [webContext]
384
+ */
385
+ validateLocatorStrategy (strategy, webContext = false) {
386
+ let validStrategies = this.locatorStrategies;
387
+ this.log.debug(
388
+ `Valid locator strategies for this request: ${validStrategies.join(
389
+ ', ',
390
+ )}`,
391
+ );
392
+
393
+ if (webContext) {
394
+ validStrategies = validStrategies.concat(this.webLocatorStrategies);
395
+ }
396
+
397
+ if (!_.includes(validStrategies, strategy)) {
398
+ throw new errors.InvalidSelectorError(
399
+ `Locator Strategy '${strategy}' is not supported for this session`,
400
+ );
401
+ }
402
+ }
403
+
404
+ /**
405
+ *
406
+ * @param {string} [sessionId]
407
+ * @returns {boolean}
408
+ */
409
+ proxyActive (sessionId) {
410
+ return false;
411
+ }
412
+
413
+ /**
414
+ *
415
+ * @param {string} sessionId
416
+ * @returns {[string, RegExp][]}
417
+ */
418
+ getProxyAvoidList (sessionId) {
419
+ return [];
420
+ }
421
+
422
+ /**
423
+ *
424
+ * @param {string} [sessionId]
425
+ * @returns {boolean}
426
+ */
427
+ canProxy (sessionId) {
428
+ return false;
429
+ }
430
+
431
+ /**
432
+ * Whether a given command route (expressed as method and url) should not be
433
+ * proxied according to this driver
434
+ *
435
+ * @param {string} sessionId - the current sessionId (in case the driver runs
436
+ * multiple session ids and requires it). This is not used in this method but
437
+ * should be made available to overridden methods.
438
+ * @param {import('@appium/types').HTTPMethod} method - HTTP method of the route
439
+ * @param {string} url - url of the route
440
+ * @param {any} [body] - webdriver request body
441
+ *
442
+ * @returns {boolean} - whether the route should be avoided
443
+ */
444
+ proxyRouteIsAvoided (sessionId, method, url, body) {
445
+ for (let avoidSchema of this.getProxyAvoidList(sessionId)) {
446
+ if (!_.isArray(avoidSchema) || avoidSchema.length !== 2) {
447
+ throw new Error('Proxy avoidance must be a list of pairs');
448
+ }
449
+ let [avoidMethod, avoidPathRegex] = avoidSchema;
450
+ if (!_.includes(['GET', 'POST', 'DELETE'], avoidMethod)) {
451
+ throw new Error(`Unrecognized proxy avoidance method '${avoidMethod}'`);
452
+ }
453
+ if (!_.isRegExp(avoidPathRegex)) {
454
+ throw new Error('Proxy avoidance path must be a regular expression');
455
+ }
456
+ let normalizedUrl = url.replace(
457
+ new RegExp(`^${_.escapeRegExp(this.basePath)}`),
458
+ '',
459
+ );
460
+ if (avoidMethod === method && avoidPathRegex.test(normalizedUrl)) {
461
+ return true;
462
+ }
463
+ }
464
+ return false;
465
+ }
466
+
467
+ /**
468
+ *
469
+ * @param {Driver} driver
470
+ */
471
+ addManagedDriver (driver) {
472
+ this.managedDrivers.push(driver);
473
+ }
474
+
475
+ getManagedDrivers () {
476
+ return this.managedDrivers;
477
+ }
478
+
479
+ async clearNewCommandTimeout () {
480
+ if (this.noCommandTimer) {
481
+ clearTimeout(this.noCommandTimer);
482
+ this.noCommandTimer = null;
483
+ }
484
+ }
485
+ }
486
+
487
+ export {DriverCore};
488
+
489
+ /**
490
+ * @typedef {import('@appium/types').Capabilities} Capabilities
491
+ * @typedef {import('@appium/types').W3CCapabilities} W3CCapabilities
492
+ * @typedef {import('@appium/types').Driver} Driver
493
+ * @typedef {import('@appium/types').Core} Core
494
+ * @typedef {import('@appium/types').DriverOpts} DriverOpts
495
+ * @typedef {import('@appium/types').EventHistory} EventHistory
496
+ * @typedef {import('@appium/types').AppiumLogger} AppiumLogger
497
+ */
@@ -2,7 +2,7 @@ import log from './logger';
2
2
  import validator from 'validate.js';
3
3
  import B from 'bluebird';
4
4
 
5
-
5
+ /** @type {import('@appium/types').Constraints} */
6
6
  let desiredCapabilityConstraints = {
7
7
  platformName: {
8
8
  presence: true,
@@ -1,20 +1,64 @@
1
+ // @ts-check
2
+
1
3
  import _ from 'lodash';
2
4
  import log from './logger';
5
+ import { node, util } from '@appium/support';
6
+ import { errors } from '../protocol/errors';
7
+
8
+ const MAX_SETTINGS_SIZE = 20 * 1024 * 1024; // 20 MB
3
9
 
10
+ /**
11
+ * @template {Record<string,unknown>} T
12
+ * @implements {IDeviceSettings<T>}
13
+ */
4
14
  class DeviceSettings {
5
15
 
6
- constructor (defaultSettings = {}, onSettingsUpdate = null) {
7
- this._settings = Object.assign({}, defaultSettings);
8
- this.onSettingsUpdate = onSettingsUpdate;
16
+ /**
17
+ * @protected
18
+ * @type {T}
19
+ */
20
+ _settings;
21
+
22
+ /**
23
+ * @protected
24
+ * @type {import('@appium/types').SettingsUpdateListener<T>|undefined}
25
+ */
26
+ _onSettingsUpdate;
27
+
28
+ /**
29
+ * `onSettingsUpdate` is _required_ if settings will ever be updated; otherwise
30
+ * an error will occur at runtime.
31
+ * @param {T} [defaultSettings]
32
+ * @param {import('@appium/types').SettingsUpdateListener<T>} [onSettingsUpdate]
33
+ */
34
+ constructor (defaultSettings, onSettingsUpdate) {
35
+ this._settings = /** @type {T} */({...(defaultSettings ?? {})});
36
+ this._onSettingsUpdate = onSettingsUpdate;
9
37
  }
10
38
 
11
- // calls updateSettings from implementing driver every time a setting is changed.
39
+ /**
40
+ * calls updateSettings from implementing driver every time a setting is changed.
41
+ * @param {T} newSettings
42
+ */
12
43
  async update (newSettings) {
13
44
  if (!_.isPlainObject(newSettings)) {
14
- throw new Error(`Settings update should be called with valid JSON. Got ` +
45
+ throw new errors.InvalidArgumentError(`Settings update should be called with valid JSON. Got ` +
15
46
  `${JSON.stringify(newSettings)} instead`);
16
47
  }
17
- for (const prop of _.keys(newSettings)) {
48
+
49
+ if (node.getObjectSize({...this._settings, ...newSettings}) >= MAX_SETTINGS_SIZE) {
50
+ throw new errors.InvalidArgumentError(`New settings cannot be applied, because the overall ` +
51
+ `object size exceeds the allowed limit of ${util.toReadableSizeString(MAX_SETTINGS_SIZE)}`);
52
+ }
53
+
54
+ if (!_.isFunction(this._onSettingsUpdate)) {
55
+ log.errorAndThrow(`Unable to update settings; ` +
56
+ `onSettingsUpdate method not found on '${this.constructor.name}'`);
57
+ return;
58
+ }
59
+
60
+ const props = /** @type {(keyof T & string)[]} */(_.keys(newSettings));
61
+ for (const prop of props) {
18
62
  if (!_.isUndefined(this._settings[prop])) {
19
63
  if (this._settings[prop] === newSettings[prop]) {
20
64
  log.debug(`The value of '${prop}' setting did not change. Skipping the update for it`);
@@ -22,13 +66,8 @@ class DeviceSettings {
22
66
  }
23
67
  }
24
68
  // update setting only when there is updateSettings defined.
25
- if (_.isFunction(this.onSettingsUpdate)) {
26
- await this.onSettingsUpdate(prop, newSettings[prop], this._settings[prop]);
27
- this._settings[prop] = newSettings[prop];
28
- } else {
29
- log.errorAndThrow(`Unable to update settings; ` +
30
- `onSettingsUpdate method not found on '${this.constructor.name}'`);
31
- }
69
+ await this._onSettingsUpdate(prop, newSettings[prop], this._settings[prop]);
70
+ this._settings[prop] = newSettings[prop];
32
71
  }
33
72
  }
34
73
 
@@ -39,3 +78,8 @@ class DeviceSettings {
39
78
 
40
79
  export default DeviceSettings;
41
80
  export { DeviceSettings };
81
+
82
+ /**
83
+ * @template T
84
+ * @typedef {import('@appium/types').DeviceSettings<T>} IDeviceSettings
85
+ */