@appium/base-driver 9.16.2 → 9.16.3

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.
@@ -43,6 +43,11 @@ const {MJSONWP, W3C} = PROTOCOLS;
43
43
  const DEFAULT_LOG = logger.getLogger('Protocol Converter');
44
44
 
45
45
  class ProtocolConverter {
46
+ /**
47
+ *
48
+ * @param {ProxyFunction} proxyFunc
49
+ * @param {import('@appium/types').AppiumLogger | null} [log=null]
50
+ */
46
51
  constructor(proxyFunc, log = null) {
47
52
  this.proxyFunc = proxyFunc;
48
53
  this._downstreamProtocol = null;
@@ -67,7 +72,7 @@ class ProtocolConverter {
67
72
  * provided in the request, we need to do 3 proxies and combine the result
68
73
  *
69
74
  * @param {Object} body Request body
70
- * @return {Array} Array of W3C + MJSONWP compatible timeout objects
75
+ * @return {Object[]} Array of W3C + MJSONWP compatible timeout objects
71
76
  */
72
77
  getTimeoutRequestObjects(body) {
73
78
  if (this.downstreamProtocol === W3C && _.has(body, 'ms') && _.has(body, 'type')) {
@@ -99,9 +104,10 @@ class ProtocolConverter {
99
104
 
100
105
  /**
101
106
  * Proxy an array of timeout objects and merge the result
102
- * @param {String} url Endpoint url
103
- * @param {String} method Endpoint method
104
- * @param {Object} body Request body
107
+ * @param {string} url Endpoint url
108
+ * @param {string} method Endpoint method
109
+ * @param {import('@appium/types').HTTPBody} body Request body
110
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
105
111
  */
106
112
  async proxySetTimeouts(url, method, body) {
107
113
  let response, resBody;
@@ -127,9 +133,16 @@ class ProtocolConverter {
127
133
 
128
134
  // ...Otherwise, continue to the next timeouts call
129
135
  }
130
- return [response, resBody];
136
+ return [/** @type {import('@appium/types').ProxyResponse} */(response), resBody];
131
137
  }
132
138
 
139
+ /**
140
+ *
141
+ * @param {string} url
142
+ * @param {string} method
143
+ * @param {import('@appium/types').HTTPBody} body
144
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
145
+ */
133
146
  async proxySetWindow(url, method, body) {
134
147
  const bodyObj = util.safeJsonParse(body);
135
148
  if (_.isPlainObject(bodyObj)) {
@@ -160,6 +173,13 @@ class ProtocolConverter {
160
173
  return await this.proxyFunc(url, method, body);
161
174
  }
162
175
 
176
+ /**
177
+ *
178
+ * @param {string} url
179
+ * @param {string} method
180
+ * @param {import('@appium/types').HTTPBody} body
181
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
182
+ */
163
183
  async proxySetValue(url, method, body) {
164
184
  const bodyObj = util.safeJsonParse(body);
165
185
  if (_.isPlainObject(bodyObj) && (util.hasValue(bodyObj.text) || util.hasValue(bodyObj.value))) {
@@ -186,6 +206,13 @@ class ProtocolConverter {
186
206
  return await this.proxyFunc(url, method, body);
187
207
  }
188
208
 
209
+ /**
210
+ *
211
+ * @param {string} url
212
+ * @param {string} method
213
+ * @param {import('@appium/types').HTTPBody} body
214
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
215
+ */
189
216
  async proxySetFrame(url, method, body) {
190
217
  const bodyObj = util.safeJsonParse(body);
191
218
  return _.has(bodyObj, 'id') && _.isPlainObject(bodyObj.id)
@@ -196,6 +223,13 @@ class ProtocolConverter {
196
223
  : await this.proxyFunc(url, method, body);
197
224
  }
198
225
 
226
+ /**
227
+ *
228
+ * @param {string} url
229
+ * @param {string} method
230
+ * @param {import('@appium/types').HTTPBody} body
231
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
232
+ */
199
233
  async proxyPerformActions(url, method, body) {
200
234
  const bodyObj = util.safeJsonParse(body);
201
235
  return _.isPlainObject(bodyObj)
@@ -207,6 +241,12 @@ class ProtocolConverter {
207
241
  : await this.proxyFunc(url, method, body);
208
242
  }
209
243
 
244
+ /**
245
+ *
246
+ * @param {string} url
247
+ * @param {string} method
248
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
249
+ */
210
250
  async proxyReleaseActions(url, method) {
211
251
  return await this.proxyFunc(url, method);
212
252
  }
@@ -218,8 +258,8 @@ class ProtocolConverter {
218
258
  * @param {string} commandName
219
259
  * @param {string} url
220
260
  * @param {string} method
221
- * @param {?string|object} body
222
- * @returns The proxyfying result as [response, responseBody] tuple
261
+ * @param {import('@appium/types').HTTPBody} [body]
262
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
223
263
  */
224
264
  async convertAndProxy(commandName, url, method, body) {
225
265
  if (!this.downstreamProtocol) {
@@ -272,3 +312,7 @@ class ProtocolConverter {
272
312
  }
273
313
 
274
314
  export default ProtocolConverter;
315
+
316
+ /**
317
+ * @typedef {(url: string, method: string, body?: import('@appium/types').HTTPBody) => Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>} ProxyFunction
318
+ */
@@ -0,0 +1,52 @@
1
+ import axios from 'axios';
2
+ import { CancellationError } from 'bluebird';
3
+ import EventEmitter from 'node:events';
4
+
5
+ const CANCEL_EVENT = 'cancel';
6
+ const FINISH_EVENT = 'finish';
7
+
8
+ export class ProxyRequest {
9
+ private readonly _requestConfig: axios.RawAxiosRequestConfig;
10
+ private readonly _ee: EventEmitter;
11
+ private _resultPromise: Promise<any> | null;
12
+
13
+ constructor(requestConfig: axios.RawAxiosRequestConfig<any>) {
14
+ this._requestConfig = requestConfig;
15
+ this._ee = new EventEmitter();
16
+ this._resultPromise = null;
17
+ }
18
+
19
+ async execute(): Promise<axios.AxiosResponse> {
20
+ if (this._resultPromise) {
21
+ return await this._resultPromise;
22
+ }
23
+
24
+ try {
25
+ this._resultPromise = Promise.race([
26
+ this._makeRacingTimer(),
27
+ this._makeRequest(),
28
+ ]);
29
+ return await this._resultPromise;
30
+ } finally {
31
+ this._ee.emit(FINISH_EVENT);
32
+ this._ee.removeAllListeners();
33
+ }
34
+ }
35
+
36
+ cancel(): void {
37
+ this._ee.emit(CANCEL_EVENT);
38
+ }
39
+
40
+ private async _makeRequest(): Promise<axios.AxiosResponse> {
41
+ return await axios(this._requestConfig);
42
+ }
43
+
44
+ private async _makeRacingTimer(): Promise<void> {
45
+ return await new Promise((resolve, reject) => {
46
+ this._ee.once(FINISH_EVENT, resolve);
47
+ this._ee.once(CANCEL_EVENT, () => reject(new CancellationError(
48
+ 'The request has been cancelled'
49
+ )));
50
+ });
51
+ }
52
+ }
@@ -1,6 +1,5 @@
1
1
  import _ from 'lodash';
2
2
  import {logger, util} from '@appium/support';
3
- import axios from 'axios';
4
3
  import {getSummaryByCode} from '../jsonwp-status/status';
5
4
  import {
6
5
  errors,
@@ -17,7 +16,7 @@ import http from 'http';
17
16
  import https from 'https';
18
17
  import { match as pathToRegexMatch } from 'path-to-regexp';
19
18
  import nodeUrl from 'node:url';
20
-
19
+ import { ProxyRequest } from './proxy-request';
21
20
 
22
21
  const DEFAULT_LOG = logger.getLogger('WD Proxy');
23
22
  const DEFAULT_REQUEST_TIMEOUT = 240000;
@@ -37,7 +36,7 @@ const ALLOWED_OPTS = [
37
36
  'keepAlive',
38
37
  ];
39
38
 
40
- class JWProxy {
39
+ export class JWProxy {
41
40
  /** @type {string} */
42
41
  scheme;
43
42
  /** @type {string} */
@@ -52,13 +51,20 @@ class JWProxy {
52
51
  sessionId;
53
52
  /** @type {number} */
54
53
  timeout;
54
+ /** @type {Protocol | null | undefined} */
55
+ _downstreamProtocol;
56
+ /** @type {ProxyRequest[]} */
57
+ _activeRequests;
55
58
 
59
+ /**
60
+ * @param {import('@appium/types').ProxyOptions} [opts={}]
61
+ */
56
62
  constructor(opts = {}) {
57
- opts = _.pick(opts, ALLOWED_OPTS);
58
-
63
+ const filteredOpts = _.pick(opts, ALLOWED_OPTS);
59
64
  // omit 'log' in the defaults assignment here because 'log' is a getter and we are going to set
60
65
  // it to this._log (which lies behind the getter) further down
61
- const options = _.defaults(_.omit(opts, 'log'), {
66
+ /** @type {import('@appium/types').ProxyOptions} */
67
+ const options = _.defaults(_.omit(filteredOpts, 'log'), {
62
68
  scheme: 'http',
63
69
  server: 'localhost',
64
70
  port: 4444,
@@ -67,7 +73,7 @@ class JWProxy {
67
73
  sessionId: null,
68
74
  timeout: DEFAULT_REQUEST_TIMEOUT,
69
75
  });
70
- options.scheme = options.scheme.toLowerCase();
76
+ options.scheme = /** @type {string} */ (options.scheme).toLowerCase();
71
77
  Object.assign(this, options);
72
78
 
73
79
  this._activeRequests = [];
@@ -81,6 +87,8 @@ class JWProxy {
81
87
  this.httpsAgent = new https.Agent(agentOpts);
82
88
  this.protocolConverter = new ProtocolConverter(this.proxy.bind(this), opts.log);
83
89
  this._log = opts.log;
90
+
91
+ this.log.debug(`${this.constructor.name} options: ${JSON.stringify(options)}`);
84
92
  }
85
93
 
86
94
  get log() {
@@ -97,23 +105,32 @@ class JWProxy {
97
105
  * @returns {Promise<import('axios').AxiosResponse>}
98
106
  */
99
107
  async request(requestConfig) {
100
- const reqPromise = axios(requestConfig);
101
- this._activeRequests.push(reqPromise);
108
+ const req = new ProxyRequest(requestConfig);
109
+ this._activeRequests.push(req);
102
110
  try {
103
- return await reqPromise;
111
+ return await req.execute();
104
112
  } finally {
105
- _.pull(this._activeRequests, reqPromise);
113
+ _.pull(this._activeRequests, req);
106
114
  }
107
115
  }
108
116
 
117
+ /**
118
+ * @returns {number}
119
+ */
109
120
  getActiveRequestsCount() {
110
121
  return this._activeRequests.length;
111
122
  }
112
123
 
113
124
  cancelActiveRequests() {
125
+ for (const ar of this._activeRequests) {
126
+ ar.cancel();
127
+ }
114
128
  this._activeRequests = [];
115
129
  }
116
130
 
131
+ /**
132
+ * @param {Protocol | null | undefined} value
133
+ */
117
134
  set downstreamProtocol(value) {
118
135
  this._downstreamProtocol = value;
119
136
  this.protocolConverter.downstreamProtocol = value;
@@ -130,32 +147,8 @@ class JWProxy {
130
147
  * @returns {string}
131
148
  */
132
149
  getUrlForProxy(url, method) {
133
- const parsedUrl = nodeUrl.parse(url || '/');
134
- if (
135
- !parsedUrl.href || !parsedUrl.pathname
136
- || (parsedUrl.protocol && !['http:', 'https:'].includes(parsedUrl.protocol))
137
- ) {
138
- throw new Error(`Did not know how to proxy the url '${url}'`);
139
- }
140
- let pathname = this.reqBasePath && parsedUrl.pathname.startsWith(this.reqBasePath)
141
- ? parsedUrl.pathname.replace(this.reqBasePath, '')
142
- : parsedUrl.pathname;
143
- const match = COMMAND_WITH_SESSION_ID_MATCHER(pathname);
144
- // This is needed for the backward compatibility
145
- // if drivers don't set reqBasePath properly
146
- if (!this.reqBasePath) {
147
- if (match && _.isArray(match.params?.prefix)) {
148
- pathname = pathname.replace(`/${match.params?.prefix.join('/')}`, '');
149
- } else if (_.startsWith(pathname, '/wd/hub')) {
150
- pathname = pathname.replace('/wd/hub', '');
151
- }
152
- }
153
- const normalizedPathname = _.trimEnd(
154
- match && _.isArray(match.params?.command)
155
- ? `/${match.params.command.join('/')}`
156
- : pathname,
157
- '/'
158
- );
150
+ const parsedUrl = this._parseUrl(url);
151
+ const normalizedPathname = this._toNormalizedPathname(parsedUrl);
159
152
  const commandName = normalizedPathname
160
153
  ? routeToCommandName(
161
154
  normalizedPathname,
@@ -181,8 +174,8 @@ class JWProxy {
181
174
  *
182
175
  * @param {string} url
183
176
  * @param {string} method
184
- * @param {any} body
185
- * @returns {Promise<any>}
177
+ * @param {import('@appium/types').HTTPBody} [body=null]
178
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
186
179
  */
187
180
  async proxy(url, method, body = null) {
188
181
  method = method.toUpperCase();
@@ -255,8 +248,12 @@ class JWProxy {
255
248
  // Some servers, like chromedriver may return response code 200 for non-zero JSONWP statuses
256
249
  throwProxyError(data);
257
250
  }
258
- const res = {statusCode: status, headers, body: data};
259
- return [res, data];
251
+ const headersMap = /** @type {import('@appium/types').HTTPHeaders} */ (headers);
252
+ return [{
253
+ statusCode: status,
254
+ headers: headersMap,
255
+ body: data,
256
+ }, data];
260
257
  } catch (e) {
261
258
  // We only consider an error unexpected if this was not
262
259
  // an async request module error or if the response cannot be cast to
@@ -279,6 +276,11 @@ class JWProxy {
279
276
  }
280
277
  }
281
278
 
279
+ /**
280
+ *
281
+ * @param {Record<string, any>} resObj
282
+ * @returns {Protocol | undefined}
283
+ */
282
284
  getProtocolFromResBody(resObj) {
283
285
  if (_.isInteger(resObj.status)) {
284
286
  return MJSONWP;
@@ -289,6 +291,7 @@ class JWProxy {
289
291
  }
290
292
 
291
293
  /**
294
+ * @deprecated This method is not used anymore and will be removed
292
295
  *
293
296
  * @param {string} url
294
297
  * @param {import('@appium/types').HTTPMethod} method
@@ -322,10 +325,13 @@ class JWProxy {
322
325
  *
323
326
  * @param {string} url
324
327
  * @param {import('@appium/types').HTTPMethod} method
325
- * @param {any?} body
328
+ * @param {import('@appium/types').HTTPBody} [body=null]
329
+ * @returns {Promise<[import('@appium/types').ProxyResponse, import('@appium/types').HTTPBody]>}
326
330
  */
327
331
  async proxyCommand(url, method, body = null) {
328
- const commandName = this.requestToCommandName(url, method);
332
+ const parsedUrl = this._parseUrl(url);
333
+ const normalizedPathname = this._toNormalizedPathname(parsedUrl);
334
+ const commandName = normalizedPathname ? routeToCommandName(normalizedPathname, method) : '';
329
335
  if (!commandName) {
330
336
  return await this.proxy(url, method, body);
331
337
  }
@@ -338,8 +344,8 @@ class JWProxy {
338
344
  *
339
345
  * @param {string} url
340
346
  * @param {import('@appium/types').HTTPMethod} method
341
- * @param {any?} body
342
- * @returns {Promise<unknown>}
347
+ * @param {import('@appium/types').HTTPBody} [body=null]
348
+ * @returns {Promise<import('@appium/types').HTTPBody>}
343
349
  */
344
350
  async command(url, method, body = null) {
345
351
  let response;
@@ -393,20 +399,40 @@ class JWProxy {
393
399
  );
394
400
  }
395
401
 
402
+ /**
403
+ *
404
+ * @param {string} url
405
+ * @returns {string | null}
406
+ */
396
407
  getSessionIdFromUrl(url) {
397
408
  const match = url.match(/\/session\/([^/]+)/);
398
409
  return match ? match[1] : null;
399
410
  }
400
411
 
412
+ /**
413
+ *
414
+ * @param {import('express').Request} req
415
+ * @param {import('express').Response} res
416
+ */
401
417
  async proxyReqRes(req, res) {
402
418
  // ! this method must not throw any exceptions
403
419
  // ! make sure to call res.send before return
420
+ /** @type {number} */
404
421
  let statusCode;
422
+ /** @type {import('@appium/types').HTTPBody} */
405
423
  let resBodyObj;
406
424
  try {
407
425
  let response;
408
- [response, resBodyObj] = await this.proxyCommand(req.originalUrl, req.method, req.body);
409
- res.headers = response.headers;
426
+ [response, resBodyObj] = await this.proxyCommand(
427
+ req.originalUrl,
428
+ /** @type {import('@appium/types').HTTPMethod} */ (req.method),
429
+ req.body
430
+ );
431
+ for (const [name, value] of _.toPairs(response.headers)) {
432
+ if (!_.isNil(value)) {
433
+ res.setHeader(name, _.isBoolean(value) ? String(value) : value);
434
+ }
435
+ }
410
436
  statusCode = response.statusCode;
411
437
  } catch (err) {
412
438
  [statusCode, resBodyObj] = getResponseForW3CError(
@@ -438,11 +464,57 @@ class JWProxy {
438
464
  resBodyObj.value = formatResponseValue(resBodyObj.value);
439
465
  res.status(statusCode).send(JSON.stringify(formatStatus(resBodyObj)));
440
466
  }
467
+
468
+ /**
469
+ *
470
+ * @param {string} url
471
+ * @returns {ParsedUrl}
472
+ */
473
+ _parseUrl(url) {
474
+ const parsedUrl = nodeUrl.parse(url || '/');
475
+ if (
476
+ _.isNil(parsedUrl.href) || _.isNil(parsedUrl.pathname)
477
+ || (parsedUrl.protocol && !['http:', 'https:'].includes(parsedUrl.protocol))
478
+ ) {
479
+ throw new Error(`Did not know how to proxy the url '${url}'`);
480
+ }
481
+ return parsedUrl;
482
+ }
483
+
484
+ /**
485
+ *
486
+ * @param {ParsedUrl} parsedUrl
487
+ * @returns {string}
488
+ */
489
+ _toNormalizedPathname(parsedUrl) {
490
+ if (!_.isString(parsedUrl.pathname)) {
491
+ return '';
492
+ }
493
+ let pathname = this.reqBasePath && parsedUrl.pathname.startsWith(this.reqBasePath)
494
+ ? parsedUrl.pathname.replace(this.reqBasePath, '')
495
+ : parsedUrl.pathname;
496
+ const match = COMMAND_WITH_SESSION_ID_MATCHER(pathname);
497
+ // This is needed for the backward compatibility
498
+ // if drivers don't set reqBasePath properly
499
+ if (!this.reqBasePath) {
500
+ if (match && _.isArray(match.params?.prefix)) {
501
+ pathname = pathname.replace(`/${match.params?.prefix.join('/')}`, '');
502
+ } else if (_.startsWith(pathname, '/wd/hub')) {
503
+ pathname = pathname.replace('/wd/hub', '');
504
+ }
505
+ }
506
+ let result = pathname;
507
+ if (match) {
508
+ result = _.isArray(match.params?.command) ? `/${match.params.command.join('/')}` : '';
509
+ }
510
+ return _.trimEnd(result, '/');
511
+ }
441
512
  }
442
513
 
443
- export {JWProxy};
444
514
  export default JWProxy;
445
515
 
446
516
  /**
447
517
  * @typedef {Error & {response: {data: import('type-fest').JsonObject, status: import('http-status-codes').StatusCodes}}} ProxyError
518
+ * @typedef {nodeUrl.UrlWithStringQuery} ParsedUrl
519
+ * @typedef {typeof PROTOCOLS[keyof typeof PROTOCOLS]} Protocol
448
520
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appium/base-driver",
3
- "version": "9.16.2",
3
+ "version": "9.16.3",
4
4
  "description": "Base driver class for Appium drivers",
5
5
  "keywords": [
6
6
  "automation",
@@ -44,12 +44,12 @@
44
44
  "test:types": "tsd"
45
45
  },
46
46
  "dependencies": {
47
- "@appium/support": "^6.0.6",
48
- "@appium/types": "^0.25.1",
47
+ "@appium/support": "^6.0.7",
48
+ "@appium/types": "^0.25.2",
49
49
  "@colors/colors": "1.6.0",
50
50
  "async-lock": "1.4.1",
51
51
  "asyncbox": "3.0.0",
52
- "axios": "1.7.9",
52
+ "axios": "1.8.2",
53
53
  "bluebird": "3.7.2",
54
54
  "body-parser": "1.20.3",
55
55
  "express": "4.21.2",
@@ -62,7 +62,7 @@
62
62
  "path-to-regexp": "8.2.0",
63
63
  "serve-favicon": "2.5.0",
64
64
  "source-map-support": "0.5.21",
65
- "type-fest": "4.35.0",
65
+ "type-fest": "4.37.0",
66
66
  "validate.js": "0.13.1"
67
67
  },
68
68
  "optionalDependencies": {
@@ -75,7 +75,7 @@
75
75
  "publishConfig": {
76
76
  "access": "public"
77
77
  },
78
- "gitHead": "e473b05130bb90518a9bdb843c3f93ef74d5b874",
78
+ "gitHead": "7c05f5c213e284b6be0f1ecf5550a40d84e0899e",
79
79
  "tsd": {
80
80
  "directory": "test/types"
81
81
  }
@@ -1,36 +0,0 @@
1
- appium-base-driver
2
- ===================
3
- This is the parent class that all [appium](appium.io) drivers inherit from. Appium drivers themselves can either be started from the command line as standalone appium servers, or can be included by another module (appium) which then proxies commands to the appropriate driver based on [Desired Capabilities](https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md).
4
-
5
- An appium driver is a module which processes [Mobile Json Wire Protocol](https://code.google.com/p/selenium/source/browse/spec-draft.md?repo=mobile) commands and controls a device accordingly. The commands can either come in over HTTP as json api requests, or they can be passed to the driver object programmatically as already-parsed json object (without the HTTP headers and junk).
6
-
7
- The appium Base driver already includes the [mjsonwp](https://github.com/appium/appium-base-driver/blob/master/lib/mjsonwp/README.md) module, which is the HTTP server that converts incoming requests into json objects that get sent to the driver programmatically.
8
-
9
- The appium Base driver already has all the REST api routes, validation, and error codes supplied by [mjsonwp](https://github.com/appium/appium-base-driver/blob/master/lib/mjsonwp/README.md).
10
-
11
- Appium drivers are designed to have a *single testing session* per instantiation. This means that one Driver object should be attached to a single device and handle commands from a single client. The main appium driver handles multiple sessions and instantiates a new instance of the desired driver for each new session.
12
-
13
- ## Writing your own appium driver
14
-
15
- Writing your own appium driver starts with inheriting and extending this Base driver module.
16
-
17
- Appium Base driver has some properties that all drivers share:
18
-
19
- - `driver.opts` - these are the options passed into the driver constructor. Your driver's constructor should take an object of options and pass it on the the Base driver by calling `super(opts)` in your constructor.
20
-
21
- - `driver.desiredCapConstraints` - Base driver sets this property with a customer `setter` function so that when you create a driver, you can add an object which defines the validation contraints of which desired capabilities your new driver can handle. Of course each driver will have it's own specific desired capabilities. Look for examples on our other drivers.
22
-
23
- - `driver.createSession(caps)` - this is the function which gets desired capabilities and creates a session. Make sure to call `super.createSession(caps)` so that things like `this.sessionId` and `this.caps` are populated, and the caps are validated against your `desiredCapConstraints`.
24
-
25
- - `driver.caps` - these are the desired capabilities for the current session.
26
-
27
- - `driver.sessionId` - this is the ID of the current session. It gets populated automaticall by `baseDriver.createSession`.
28
-
29
- - `driver.proxyReqRes()` - used by mjsonwp module for proxying http commands to another process (like chromedriver or selendroid)
30
-
31
- - `driver.jwpProxyAvoid` - used by mjsonwp module. You can specify what REST api routes which you want to SKIP the automatic proxy to another server (which is optional) and instead be handled by your driver.
32
-
33
-
34
- Base driver exposes an event called `onUnexpectedShutdown` which is called when the driver is shut down unexpectedly (usually after invocation of the `startUnexpectedShutdown` method).
35
-
36
- Your driver should also implement a startUnexpectedShutdown method?
@@ -1,59 +0,0 @@
1
- ## appium-express
2
-
3
- [Express](http://expressjs.com/) server tuned for to serve [Appium](http://appium.io/).
4
-
5
-
6
- ### Configuration
7
-
8
- The `appium-express` server comes configured with:
9
-
10
- 1. appropriate logging formats
11
- 2. service of necessary static assets
12
- 3. allowance of cross-domain requests
13
- 4. default error handling
14
- 5. fix for invalid content types sent by certain clients
15
-
16
- To configure routes, a function that takes an Express server is passed into the
17
- server. This function can add whatever routes are wanted.
18
-
19
-
20
- ### Usage
21
-
22
- ```js
23
- import { server } from 'appium-base-driver';
24
-
25
-
26
- // configure the routes
27
- function configureRoutes (app) {
28
- app.get('/hello', (req, res) => {
29
- res.header['content-type'] = 'text/html';
30
- res.status(200).send('Hello');
31
- });
32
- app.get('/world', (req, res) => {
33
- res.header['content-type'] = 'text/html';
34
- res.status(200).send('World');
35
- });
36
- }
37
-
38
- const port = 5000;
39
- const host = 'localhost';
40
-
41
- const appiumServer = await server({
42
- routeConfiguringFunction,
43
- port,
44
- host,
45
- });
46
- ```
47
-
48
-
49
- ## Watch
50
-
51
- ```
52
- npm run watch
53
- ```
54
-
55
- ## Test
56
-
57
- ```
58
- npm test
59
- ```
@@ -1,52 +0,0 @@
1
- ## appium-jsonwp-proxy
2
-
3
- Proxy middleware for the Selenium [JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md). Allows
4
-
5
-
6
- ### Usage
7
-
8
- The proxy is used by instantiating with the details of the Selenium server to which to proxy. The options for the constructor are passed as a hash with the following possible members:
9
-
10
- - `scheme` - defaults to 'http'
11
- - `server` - defaults to 'localhost'
12
- - `port` - defaults to `4444`
13
- - `base` - defaults to ''
14
- - `sessionId` - the session id of the session on the remote server
15
- - `reqBasePath` - the base path of the server which the request was originally sent to (defaults to '')
16
-
17
- Once the proxy is created, there are two `async` methods:
18
-
19
- `command (url, method, body)`
20
-
21
- Sends a "command" to the proxied server, using the "url", which is the endpoing, with the HTTP method and optional body.
22
-
23
- ```js
24
- import { JWProxy } from 'appium-base-driver';
25
-
26
- let host = 'my.host.com';
27
- let port = 4445;
28
-
29
- let proxy = new JWProxy({server: host, port: port});
30
-
31
- // get the Selenium server status
32
- let seStatus = await proxy.command('/status', 'GET');
33
- ```
34
-
35
- `proxyReqRes (req, res)`
36
-
37
- Proxies a request and response to the proxied server. Used to handle the entire conversation of a request/response cycle.
38
-
39
- ```js
40
- import { JWProxy } from 'appium-base-driver';
41
- import http from 'http';
42
-
43
- let host = 'my.host.com';
44
- let port = 4445;
45
-
46
- let proxy = new JWProxy({server: host, port: port});
47
-
48
-
49
- http.createServer(function (req, res) {
50
- await proxy.proxyReqRes(res, res);
51
- }).listen(9615);
52
- ```