@appium/base-driver 8.2.4 → 8.3.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 (51) hide show
  1. package/build/lib/basedriver/commands/find.js +4 -11
  2. package/build/lib/basedriver/commands/log.js +3 -6
  3. package/build/lib/basedriver/commands/session.js +18 -27
  4. package/build/lib/basedriver/commands/settings.js +4 -8
  5. package/build/lib/basedriver/commands/timeout.js +10 -15
  6. package/build/lib/basedriver/device-settings.js +14 -2
  7. package/build/lib/basedriver/driver.js +22 -20
  8. package/build/lib/basedriver/helpers.js +9 -9
  9. package/build/lib/express/express-logging.js +2 -2
  10. package/build/lib/express/idempotency.js +2 -2
  11. package/build/lib/helpers/capabilities.js +39 -0
  12. package/build/lib/index.js +3 -5
  13. package/build/lib/jsonwp-proxy/protocol-converter.js +18 -12
  14. package/build/lib/jsonwp-proxy/proxy.js +19 -12
  15. package/build/lib/protocol/protocol.js +36 -27
  16. package/build/lib/protocol/routes.js +67 -1
  17. package/build/test/basedriver/capabilities-specs.js +43 -1
  18. package/build/test/basedriver/capability-specs.js +126 -167
  19. package/build/test/basedriver/commands/log-specs.js +12 -5
  20. package/build/test/basedriver/driver-tests.js +11 -14
  21. package/build/test/basedriver/timeout-specs.js +7 -9
  22. package/build/test/express/server-e2e-specs.js +10 -5
  23. package/build/test/express/server-specs.js +22 -16
  24. package/build/test/express/static-specs.js +10 -5
  25. package/build/test/protocol/fake-driver.js +12 -15
  26. package/build/test/protocol/protocol-e2e-specs.js +16 -10
  27. package/build/test/protocol/routes-specs.js +2 -2
  28. package/lib/basedriver/commands/find.js +3 -6
  29. package/lib/basedriver/commands/log.js +2 -4
  30. package/lib/basedriver/commands/session.js +21 -22
  31. package/lib/basedriver/commands/settings.js +3 -5
  32. package/lib/basedriver/commands/timeout.js +9 -10
  33. package/lib/basedriver/device-settings.js +10 -1
  34. package/lib/basedriver/driver.js +25 -12
  35. package/lib/basedriver/helpers.js +13 -11
  36. package/lib/express/express-logging.js +1 -1
  37. package/lib/express/idempotency.js +1 -1
  38. package/lib/helpers/capabilities.js +25 -0
  39. package/lib/index.js +2 -2
  40. package/lib/jsonwp-proxy/protocol-converter.js +14 -13
  41. package/lib/jsonwp-proxy/proxy.js +16 -12
  42. package/lib/protocol/protocol.js +34 -29
  43. package/lib/protocol/routes.js +60 -1
  44. package/package.json +29 -22
  45. package/test/basedriver/capabilities-specs.js +34 -2
  46. package/test/basedriver/capability-specs.js +120 -146
  47. package/test/basedriver/commands/log-specs.js +12 -3
  48. package/test/basedriver/driver-tests.js +12 -7
  49. package/test/basedriver/timeout-specs.js +6 -11
  50. package/build/lib/protocol/sessions-cache.js +0 -88
  51. package/lib/protocol/sessions-cache.js +0 -74
@@ -1,5 +1,5 @@
1
1
  import _ from 'lodash';
2
- import { util } from '@appium/support';
2
+ import { util, logger, node } from '@appium/support';
3
3
  import { validators } from './validators';
4
4
  import {
5
5
  errors, isErrorType, getResponseForW3CError,
@@ -7,11 +7,9 @@ import {
7
7
  } from './errors';
8
8
  import { METHOD_MAP, NO_SESSION_ID_COMMANDS } from './routes';
9
9
  import B from 'bluebird';
10
- import {
11
- formatResponseValue, formatStatus,
12
- } from './helpers';
10
+ import { formatResponseValue, formatStatus } from './helpers';
13
11
  import { MAX_LOG_BODY_LENGTH, PROTOCOLS, DEFAULT_BASE_PATH } from '../constants';
14
- import SESSIONS_CACHE from './sessions-cache';
12
+ import { isW3cCaps } from '../helpers/capabilities';
15
13
 
16
14
 
17
15
  const CREATE_SESSION_COMMAND = 'createSession';
@@ -20,10 +18,8 @@ const GET_STATUS_COMMAND = 'getStatus';
20
18
 
21
19
  class Protocol {}
22
20
 
23
- function determineProtocol (desiredCapabilities, requiredCapabilities, capabilities) {
24
- return _.isPlainObject(capabilities) ?
25
- PROTOCOLS.W3C :
26
- PROTOCOLS.MJSONWP;
21
+ function determineProtocol (createSessionArgs) {
22
+ return _.some(createSessionArgs, isW3cCaps) ? PROTOCOLS.W3C : PROTOCOLS.MJSONWP;
27
23
  }
28
24
 
29
25
  function extractProtocol (driver, sessionId = null) {
@@ -38,13 +34,30 @@ function extractProtocol (driver, sessionId = null) {
38
34
  }
39
35
 
40
36
  // Extract the protocol for the current session if the given driver is the umbrella one
41
- return dstDriver ? dstDriver.protocol : SESSIONS_CACHE.getProtocol(sessionId);
37
+ return dstDriver?.protocol ?? PROTOCOLS.W3C;
42
38
  }
43
39
 
44
40
  function isSessionCommand (command) {
45
41
  return !_.includes(NO_SESSION_ID_COMMANDS, command);
46
42
  }
47
43
 
44
+ function getLogger (driver, sessionId = null) {
45
+ const dstDriver = sessionId && _.isFunction(driver.driverForSession)
46
+ ? (driver.driverForSession(sessionId) ?? driver)
47
+ : driver;
48
+ if (_.isFunction(dstDriver.log?.info)) {
49
+ return dstDriver.log;
50
+ }
51
+
52
+ let logPrefix = dstDriver.constructor
53
+ ? `${dstDriver.constructor.name}@${node.getObjectId(dstDriver).substring(0, 8)}`
54
+ : 'AppiumDriver';
55
+ if (sessionId) {
56
+ logPrefix += ` (${sessionId.substring(0, 8)})`;
57
+ }
58
+ return logger.getLogger(logPrefix);
59
+ }
60
+
48
61
  function wrapParams (paramSets, jsonObj) {
49
62
  /* There are commands like performTouch which take a single parameter (primitive type or array).
50
63
  * Some drivers choose to pass this parameter as a value (eg. [action1, action2...]) while others to
@@ -192,11 +205,11 @@ function makeArgs (requestParams, jsonObj, payloadParams, protocol) {
192
205
 
193
206
  function routeConfiguringFunction (driver) {
194
207
  if (!driver.sessionExists) {
195
- throw new Error('Drivers used with MJSONWP must implement `sessionExists`');
208
+ throw new Error('Drivers must implement `sessionExists` property');
196
209
  }
197
210
 
198
211
  if (!(driver.executeCommand || driver.execute)) {
199
- throw new Error('Drivers used with MJSONWP must implement `executeCommand` or `execute`');
212
+ throw new Error('Drivers must implement `executeCommand` or `execute` method');
200
213
  }
201
214
 
202
215
  // return a function which will add all the routes to the driver. Here extraMethods might be
@@ -247,7 +260,7 @@ function buildHandler (app, method, path, spec, driver, isSessCmd) {
247
260
  await doJwpProxy(driver, req, res);
248
261
  return;
249
262
  }
250
- SESSIONS_CACHE.getLogger(req.params.sessionId, currentProtocol).debug(`Would have proxied ` +
263
+ getLogger(driver, req.params.sessionId).debug(`Would have proxied ` +
251
264
  `command directly, but a plugin exists which might require its value, so will let ` +
252
265
  `its value be collected internally and made part of plugin chain`);
253
266
  didPluginOverrideProxy = true;
@@ -272,7 +285,7 @@ function buildHandler (app, method, path, spec, driver, isSessCmd) {
272
285
  if (spec.command === CREATE_SESSION_COMMAND) {
273
286
  // try to determine protocol by session creation args, so we can throw a
274
287
  // properly formatted error if arguments validation fails
275
- currentProtocol = determineProtocol(...makeArgs(req.params, jsonObj, spec.payloadParams || {}));
288
+ currentProtocol = determineProtocol(makeArgs(req.params, jsonObj, spec.payloadParams || {}));
276
289
  }
277
290
 
278
291
  // ensure that the json payload conforms to the spec
@@ -288,7 +301,7 @@ function buildHandler (app, method, path, spec, driver, isSessCmd) {
288
301
  }
289
302
 
290
303
  // run the driver command wrapped inside the argument validators
291
- SESSIONS_CACHE.getLogger(req.params.sessionId, currentProtocol).debug(`Calling ` +
304
+ getLogger(driver, req.params.sessionId).debug(`Calling ` +
292
305
  `${driver.constructor.name}.${spec.command}() with args: ` +
293
306
  _.truncate(JSON.stringify(args), {length: MAX_LOG_BODY_LENGTH}));
294
307
 
@@ -317,8 +330,7 @@ function buildHandler (app, method, path, spec, driver, isSessCmd) {
317
330
  // unpack createSession response
318
331
  if (spec.command === CREATE_SESSION_COMMAND) {
319
332
  newSessionId = driverRes[0];
320
- SESSIONS_CACHE.putSession(newSessionId, currentProtocol);
321
- SESSIONS_CACHE.getLogger(newSessionId, currentProtocol)
333
+ getLogger(driver, newSessionId)
322
334
  .debug(`Cached the protocol value '${currentProtocol}' for the new session ${newSessionId}`);
323
335
  if (currentProtocol === PROTOCOLS.MJSONWP) {
324
336
  driverRes = driverRes[1];
@@ -333,9 +345,9 @@ function buildHandler (app, method, path, spec, driver, isSessCmd) {
333
345
 
334
346
  // delete should not return anything even if successful
335
347
  if (spec.command === DELETE_SESSION_COMMAND) {
336
- SESSIONS_CACHE.getLogger(req.params.sessionId, currentProtocol)
348
+ getLogger(driver, req.params.sessionId)
337
349
  .debug(`Received response: ${_.truncate(JSON.stringify(driverRes), {length: MAX_LOG_BODY_LENGTH})}`);
338
- SESSIONS_CACHE.getLogger(req.params.sessionId, currentProtocol).debug('But deleting session, so not returning');
350
+ getLogger(driver, req.params.sessionId).debug('But deleting session, so not returning');
339
351
  driverRes = null;
340
352
  }
341
353
 
@@ -349,15 +361,8 @@ function buildHandler (app, method, path, spec, driver, isSessCmd) {
349
361
  }
350
362
 
351
363
  httpResBody.value = driverRes;
352
- SESSIONS_CACHE.getLogger(req.params.sessionId || newSessionId, currentProtocol).debug(`Responding ` +
364
+ getLogger(driver, req.params.sessionId || newSessionId).debug(`Responding ` +
353
365
  `to client with driver.${spec.command}() result: ${_.truncate(JSON.stringify(driverRes), {length: MAX_LOG_BODY_LENGTH})}`);
354
-
355
- if (spec.command === DELETE_SESSION_COMMAND) {
356
- // We don't want to keep the logger instance in the cache
357
- // after the session is deleted, because it contains the logging history
358
- // and consumes the memory
359
- SESSIONS_CACHE.resetLogger(req.params.sessionId);
360
- }
361
366
  } catch (err) {
362
367
  // if anything goes wrong, figure out what our response should be
363
368
  // based on the type of error that we encountered
@@ -374,7 +379,7 @@ function buildHandler (app, method, path, spec, driver, isSessCmd) {
374
379
  if (isErrorType(err, errors.ProxyRequestError)) {
375
380
  actualErr = err.getActualError();
376
381
  } else {
377
- SESSIONS_CACHE.getLogger(req.params.sessionId || newSessionId, currentProtocol)
382
+ getLogger(driver, req.params.sessionId || newSessionId)
378
383
  .debug(`Encountered internal error running command: ${errMsg}`);
379
384
  }
380
385
 
@@ -431,7 +436,7 @@ function driverShouldDoJwpProxy (driver, req, command) {
431
436
  }
432
437
 
433
438
  async function doJwpProxy (driver, req, res) {
434
- SESSIONS_CACHE.getLogger(req.params.sessionId, extractProtocol(driver, req.params.sessionId))
439
+ getLogger(driver, req.params.sessionId)
435
440
  .info('Driver proxy active, passing request on via HTTP proxy');
436
441
 
437
442
  // check that the inner driver has a proxy function
@@ -1,6 +1,6 @@
1
1
  import _ from 'lodash';
2
2
  import { util } from '@appium/support';
3
- import { DEFAULT_BASE_PATH } from '../constants';
3
+ import { PROTOCOLS, DEFAULT_BASE_PATH } from '../constants';
4
4
 
5
5
 
6
6
  const SET_ALERT_TEXT_PAYLOAD_PARAMS = {
@@ -33,6 +33,18 @@ const METHOD_MAP = {
33
33
  '/session/:sessionId/timeouts': {
34
34
  GET: {command: 'getTimeouts'}, // W3C route
35
35
  POST: {command: 'timeouts', payloadParams: {
36
+ validate: (jsonObj, protocolName) => {
37
+ if (protocolName === PROTOCOLS.W3C) {
38
+ if (!util.hasValue(jsonObj.script) && !util.hasValue(jsonObj.pageLoad) && !util.hasValue(jsonObj.implicit)) {
39
+ return 'W3C protocol expects any of script, pageLoad or implicit to be set';
40
+ }
41
+ } else {
42
+ // MJSONWP
43
+ if (!util.hasValue(jsonObj.type) || !util.hasValue(jsonObj.ms)) {
44
+ return 'MJSONWP protocol requires type and ms';
45
+ }
46
+ }
47
+ },
36
48
  optional: ['type', 'ms', 'script', 'pageLoad', 'implicit'],
37
49
  }}
38
50
  },
@@ -42,9 +54,19 @@ const METHOD_MAP = {
42
54
  '/session/:sessionId/timeouts/implicit_wait': {
43
55
  POST: {command: 'implicitWait', payloadParams: {required: ['ms']}}
44
56
  },
57
+ // JSONWP
58
+ '/session/:sessionId/window_handle': {
59
+ GET: {command: 'getWindowHandle'}
60
+ },
61
+ // W3C
45
62
  '/session/:sessionId/window/handle': {
46
63
  GET: {command: 'getWindowHandle'}
47
64
  },
65
+ // JSONWP
66
+ '/session/:sessionId/window_handles': {
67
+ GET: {command: 'getWindowHandles'}
68
+ },
69
+ // W3C
48
70
  '/session/:sessionId/window/handles': {
49
71
  GET: {command: 'getWindowHandles'}
50
72
  },
@@ -61,6 +83,14 @@ const METHOD_MAP = {
61
83
  '/session/:sessionId/refresh': {
62
84
  POST: {command: 'refresh'}
63
85
  },
86
+ // MJSONWP
87
+ '/session/:sessionId/execute': {
88
+ POST: {command: 'execute', payloadParams: {required: ['script', 'args']}}
89
+ },
90
+ // MJSONWP
91
+ '/session/:sessionId/execute_async': {
92
+ POST: {command: 'executeAsync', payloadParams: {required: ['script', 'args']}}
93
+ },
64
94
  '/session/:sessionId/screenshot': {
65
95
  GET: {command: 'getScreenshot'}
66
96
  },
@@ -207,6 +237,9 @@ const METHOD_MAP = {
207
237
  '/session/:sessionId/element/:elementId/size': {
208
238
  GET: {command: 'getSize'}
209
239
  },
240
+ '/session/:sessionId/element/:elementId/shadow': {
241
+ GET: {command: 'elementShadowRoot'}
242
+ },
210
243
  '/session/:sessionId/element/:elementId/css/:propertyName': {
211
244
  GET: {command: 'getCssProperty'}
212
245
  },
@@ -582,6 +615,28 @@ const METHOD_MAP = {
582
615
  POST: {command: 'logCustomEvent', payloadParams: {required: ['vendor', 'event']}}
583
616
  },
584
617
 
618
+ /*
619
+ * The W3C spec has some changes to the wire protocol.
620
+ * https://w3c.github.io/webdriver/webdriver-spec.html
621
+ * Begin to add those changes here, keeping the old version
622
+ * since clients still implement them.
623
+ */
624
+ // MJSONWP
625
+ '/session/:sessionId/alert_text': {
626
+ GET: {command: 'getAlertText'},
627
+ POST: {
628
+ command: 'setAlertText',
629
+ payloadParams: SET_ALERT_TEXT_PAYLOAD_PARAMS,
630
+ }
631
+ },
632
+ // MJSONWP
633
+ '/session/:sessionId/accept_alert': {
634
+ POST: {command: 'postAcceptAlert'}
635
+ },
636
+ // MJSONWP
637
+ '/session/:sessionId/dismiss_alert': {
638
+ POST: {command: 'postDismissAlert'}
639
+ },
585
640
  // https://w3c.github.io/webdriver/webdriver-spec.html#user-prompts
586
641
  '/session/:sessionId/alert/text': {
587
642
  GET: {command: 'getAlertText'},
@@ -606,6 +661,10 @@ const METHOD_MAP = {
606
661
  '/session/:sessionId/execute/async': {
607
662
  POST: {command: 'executeAsync', payloadParams: {required: ['script', 'args']}}
608
663
  },
664
+ // Pre-W3C endpoint for element screenshot
665
+ '/session/:sessionId/screenshot/:elementId': {
666
+ GET: {command: 'getElementScreenshot'}
667
+ },
609
668
  '/session/:sessionId/element/:elementId/screenshot': {
610
669
  GET: {command: 'getElementScreenshot'}
611
670
  },
package/package.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "@appium/base-driver",
3
+ "version": "8.3.0",
3
4
  "description": "Base driver class for Appium drivers",
4
5
  "keywords": [
5
6
  "automation",
@@ -11,20 +12,17 @@
11
12
  "firefoxos",
12
13
  "testing"
13
14
  ],
14
- "version": "8.2.4",
15
- "author": "https://github.com/appium",
16
- "license": "Apache-2.0",
17
- "repository": {
18
- "type": "git",
19
- "url": "https://github.com/appium/appium.git"
20
- },
15
+ "homepage": "https://appium.io",
21
16
  "bugs": {
22
17
  "url": "https://github.com/appium/appium/issues"
23
18
  },
24
- "engines": {
25
- "node": ">=12",
26
- "npm": ">=6"
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/appium/appium.git",
22
+ "directory": "packages/base-driver"
27
23
  },
24
+ "license": "Apache-2.0",
25
+ "author": "https://github.com/appium",
28
26
  "directories": {
29
27
  "lib": "lib"
30
28
  },
@@ -38,30 +36,39 @@
38
36
  "build",
39
37
  "!build/test/basedriver/fixtures"
40
38
  ],
39
+ "scripts": {
40
+ "dev": "gulp dev --no-notif",
41
+ "build": "gulp transpile",
42
+ "test": "gulp unit-test:run",
43
+ "test:e2e": "gulp e2e-test:run"
44
+ },
41
45
  "dependencies": {
42
- "@appium/support": "^2.55.4",
43
- "@babel/runtime": "7.16.3",
44
- "@dabh/colors": "1.4.0",
45
- "async-lock": "1.3.0",
46
+ "@appium/support": "^2.56.0",
47
+ "@babel/runtime": "7.17.8",
48
+ "@colors/colors": "1.5.0",
49
+ "async-lock": "1.3.1",
46
50
  "asyncbox": "2.9.2",
47
- "axios": "0.24.0",
51
+ "axios": "0.26.1",
48
52
  "bluebird": "3.7.2",
49
- "body-parser": "1.19.0",
53
+ "body-parser": "1.19.2",
50
54
  "es6-error": "4.1.1",
51
- "express": "4.17.1",
52
- "http-status-codes": "2.1.4",
55
+ "express": "4.17.3",
56
+ "http-status-codes": "2.2.0",
53
57
  "lodash": "4.17.21",
54
- "lru-cache": "6.0.0",
58
+ "lru-cache": "7.7.1",
55
59
  "method-override": "3.0.0",
56
60
  "morgan": "1.10.0",
57
61
  "serve-favicon": "2.5.0",
58
62
  "source-map-support": "0.5.21",
59
63
  "validate.js": "0.13.1",
60
- "ws": "7.5.6"
64
+ "ws": "7.5.7"
65
+ },
66
+ "engines": {
67
+ "node": ">=12",
68
+ "npm": ">=6"
61
69
  },
62
70
  "publishConfig": {
63
71
  "access": "public"
64
72
  },
65
- "homepage": "https://appium.io",
66
- "gitHead": "42f4a2de2d763d57cf0bdb6fb6c9a3bc9c3d232f"
73
+ "gitHead": "f5cce0f29d31699decea63ed94c4506f7af469df"
67
74
  }
@@ -1,7 +1,10 @@
1
- import { parseCaps, validateCaps, mergeCaps, processCapabilities, findNonPrefixedCaps,
2
- promoteAppiumOptions, APPIUM_OPTS_CAP, stripAppiumPrefixes } from '../../lib/basedriver/capabilities';
1
+ import {
2
+ parseCaps, validateCaps, mergeCaps, processCapabilities, findNonPrefixedCaps,
3
+ promoteAppiumOptions, APPIUM_OPTS_CAP, stripAppiumPrefixes
4
+ } from '../../lib/basedriver/capabilities';
3
5
  import _ from 'lodash';
4
6
  import { desiredCapabilityConstraints } from '../../lib/basedriver/desired-caps';
7
+ import { isW3cCaps } from '../../lib/helpers/capabilities';
5
8
 
6
9
 
7
10
  describe('caps', function () {
@@ -502,4 +505,33 @@ describe('caps', function () {
502
505
  });
503
506
  });
504
507
  });
508
+
509
+ describe('#isW3cCaps', function () {
510
+ it('should drop invalid W3C capabilities', function () {
511
+ for (const invalidCaps of [
512
+ null, undefined, [], {},
513
+ {firstMatch: null},
514
+ {firtMatch: [{}]},
515
+ {alwaysMatch: null},
516
+ {firstMatch: [{}], alwaysMatch: null},
517
+ {firstMatch: [], alwaysMatch: {}},
518
+ {firstMatch: []},
519
+ {firstMatch: {}},
520
+ {alwaysMatch: []},
521
+ ]) {
522
+ isW3cCaps(invalidCaps).should.be.false;
523
+ }
524
+ });
525
+
526
+ it('should accept valid W3C capabilities', function () {
527
+ for (const validCaps of [
528
+ {firstMatch: [{}]},
529
+ {firstMatch: [{}], alaysMatch: {}},
530
+ {firtMatch: [{}], alwaysMatch: {}},
531
+ {alwaysMatch: {}},
532
+ ]) {
533
+ isW3cCaps(validCaps).should.be.true;
534
+ }
535
+ });
536
+ });
505
537
  });