This was not as easy as the other two. The main issue involves the `BaseDriver` class and is threefold:
1. `BaseDriver` and a valid external subclass thereof (a `Driver`) are not the same, because the former is essentially _abstract_. There is no notion of an abstract class in JS nor in TS for JS.
2. Splitting `BaseDriver` into several modules and "mixing in" some modules is just not well-understood by TS.
3. `AppiumDriver` inherits _most_ of `BaseDriver`, but overrides some functions with differing signatures. This is unsupported by TS.
To work around these problems, I had to:
1. Move the `Driver` interface into `@appium/types`, since other packages may depend on it. We do not want circular dependencies, which is likely to occur otherwise. Further, there is no way to tell TS for JS that a class _implements_ an interface or type.
2. Refactor the mixins (in `basedriver/commands/`) into "mixin classes", as understood by typescript. These are dynamic classes returned by functions which accept a class parameter, and _extend_ that class. The result is marginally better, but the implementation is ... well, see for yourself. It is not possible to use declaration merging to just "trick" TS into thinking that `BaseDriver` has all of these extra methods; classes cannot be merged, and even if you could, you can't use declaration merging to "add" methods afaict.
3. `BaseDriver` is now an empty class which inherits from all mixins applied to `DriverCore`. Due to syntactic restrictions, it must be a `class` statement in order to use the `@implements` tag, which causes TS to check that the result fully implements `Driver`. `AppiumDriver` _also_ inherits from `DriverCore`, since it does not override `BaseDriver` "properly". `BaseDriverCore` itself is pretty small, containing only those functions which `AppiumDriver` defines as differing.
Otherwise:
- `BaseDriver` does not inherit from `Protocol`, which is just an empty class. `Protocol` was removed.
- All of the error classes are exported from `protocol/errors`, because TS cannot see them otherwise.
- Exported missing `GET_STATUS_COMMAND` from `protocol/index`
- Re-exported `normalizeBasePath` from `server` instead of `protocol`, since that's where it's defined
- Removed `index.d.ts`
- Mixins use `@implements` as well. Note that `@implements` does not support `import('..')` syntax.
chore(base-driver): attempted to make types for mixins less awful
fixed a couple issues in the types in `driver.js`
core(base-driver): add capabilities types
- also move the bluebird config into the index
- fix references to `DriverOpts`
- add `assignServer()` to `BaseDriverCore`, used by `AppiumDriver`
chore(base-driver): fix more types
add missing reference to `@appium/types` in `tsconfig.json`
fix(base-driver): ensure getLogEvents is async
fix(base-driver): add some more type checks and fix logCustomEvent to be async
chore(base-driver): reformat
appium-base-driver
This is the parent class that all appium 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.
An appium driver is a module which processes Mobile Json Wire Protocol 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).
The appium Base driver already includes the mjsonwp module, which is the HTTP server that converts incoming requests into json objects that get sent to the driver programmatically.
The appium Base driver already has all the REST api routes, validation, and error codes supplied by mjsonwp.
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.
Writing your own appium driver
Writing your own appium driver starts with inheriting and extending this Base driver module.
Appium Base driver has some properties that all drivers share:
-
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 callingsuper(opts)in your constructor. -
driver.desiredCapConstraints- Base driver sets this property with a customersetterfunction 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. -
driver.createSession(caps)- this is the function which gets desired capabilities and creates a session. Make sure to callsuper.createSession(caps)so that things likethis.sessionIdandthis.capsare populated, and the caps are validated against yourdesiredCapConstraints. -
driver.caps- these are the desired capabilities for the current session. -
driver.sessionId- this is the ID of the current session. It gets populated automaticall bybaseDriver.createSession. -
driver.proxyReqRes()- used by mjsonwp module for proxying http commands to another process (like chromedriver or selendroid) -
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.
Base driver exposes an event called onUnexpectedShutdown which is called when the driver is shut down unexpectedly (usually after invocation of the startUnexpectedShutdown method).
Your driver should also implement a startUnexpectedShutdown method?