A thing we know about the `opts` object passed in to a driver constructor is that it does _not_ contain caps. This value is retained in `this.initialOpts`. Upon session creation, it _does_ contain caps.
This adds a new type, `InitialOpts`, which is the first parameter to a driver. In the future, it may accept a type argument, as currently any extension-specific options (defined via schema in `package.json`) are not typed.
Because `DriverOpts` is a superset of `InitialOpts`, this should not be a breaking change.
- because BaseDriver implements `findElOrEls` (technically), any subclasses must not inherit an interface containing a non-identical `findElOrEls` -- not even narrowing. so to avoid this, we will need to make it as broad as possible; no more generic `Ctx`
- likewise with `getLog`
- fix return type of `setGeoLocation`
- fix return type of `getPerformanceData`
- fix return type of `removeApp`
- fix return type of `hideKeyboard`
- fix return type of `getSystemBars`
- fix signature of various `touch*` methods
- fix return type of `setNetworkConnection`
alternatively, the deprecated methods mentioned here should be removed.
Another handful of fixes.
- remove second "extra" parameter from `Capabilities`, `NSCapabilities` `W3CCapabilities`. we were not using it anywhere to my knowledge. it was an escape hatch anyway.
- never assign `BaseDriverCapConstraints` as the default constraints, because this is confusing as hell. instead, drivers should use the `*DriverCaps` types which automatically pulls the base constraints in. applied across several workspaces
- renamed `DeviceSettings` => `IDeviceSettings` because `DeviceSettings` is a class.
- the session ID param of `Core`'s `sessionExists()` is actually optional (per the implementation)
- allow some modicum of type narrowing at the class level around settings and values returned by `ISessionHandler`'s methods. also, not all values in the raw settings object are of the same type `T`, so fix that
- `server`, `serverHost`, `serverPort` and `serverPath` are not members of `Core` (using `AppiumDriver` as a litmus test--it interacts with `Driver` types but does not have these properties itself)
- changed a few types to interfaces where possible
- moved a lost comment
- further dumbed-down the logdef stuff
- `Driver.driverData` is _always_ defined, since `Core` makes it an empty array.
- `DriverOpts` no longer contains "partial" caps; this it not quite right as the caps are not assigned until a session is created, but there's a conflict between some props which are _both_ server args and defined in constraints. anyway, this is what `initialOpts` is for
- fix some type disagreements in basedriver's "capabilities" module
This change makes it so when running an extension script (e.g., `appium driver foo run some-script`), unless the `--json` flag is present, the child process will do no buffering whatsoever and will inherit `process.stdin`, `process.stdout` and `process.stderr` from Appium.
This means that extension scripts can do stuff like prompt the user for input. It _also_ means they can do stuff like display progress bars and spinners.
If the user wants JSON data, we revert to the old behavior, which is to buffer the output and dump it once the script has completed.
This changes the `fake-driver` implementation to use declaration merging instead of other alternatives to mixins.
History:
The original code simply assigned the mixin methods to `FakeDriver.prototype` after the `class` definition. In TS, this doesn't fly; you need to be explicit about what class members exist.
To get around this, we've tried multiple different approaches:
1. Use a "mixin pattern" as explained in the official TypeScript documentation, which involves _class factories_. This is tedious and difficult to reason about; each mixin must be passed to the next in-line, and we must be explicit about the base class and which _previous_ mixins any given mixin expects.
2. Use public class fields (a recent addition to JS). Basically, these do not work the way you want them to; if a subclass overrides a public field, a method in the superclass which refers to this public field _will use the field as defined in the superclass_--not the subclass. This is different than how methods and accessors work. This inhibits use of inheritance.
3. Add a method _for each method in each mixin_ to the base class, which simply calls `Function.prototype.apply` on the mixin with the context as the base class' `this`. This is too verbose.
There may be a better way to do this, but where we've landed is using _declaration merging_ in TypeScript. It allows us to add (but not remove, and I'm not sure about "modify") properties to the "FakeDriver interface", which seems to be the implicit TS interface created by the `class` keyword. Since the declaration merging syntax is not expressible in JavaScript, this requires a `.ts` file. To avoid creating _n_ extra files, it was easier to just make the mixins `.ts` files (you may note that we can mix-and-match JS and TS in our projects at will).
The implementation of each mixin uses `Object.assign()` to put new methods on the prototype, which are previously defined in the merged declaration above. Finally, a `satisfies` clause (which is a transient type-check; it does not change any types but it _will_ generate a compiler error) ensures the props being added to the prototype match what we've defined.
You'll also note that each method has a `this` parameter, which is not a "real" parameter--it's just an annotation for TS which tells the method what the context is. If we hate that, an alternative is to use `FakeDriver.prototype.method = <method>` instead of the `Object.assign` call. This feels marginally more tedious, but I could go either way.