Files
appium/packages/base-driver/lib/basedriver/commands/execute.js
T
Christopher Hiller 0bd4e40b38 chore(base-driver,types): implement better types for custom log formats
Previously, the value of any `Driver#supportedLogTypes` prop would ultimately be unaware of what format the log data is actually stored in (and thus, what the `GET /session/:sessionId/log` cmd responds with).  Now this can be declared by using the `LogDef<C extends Constraints, LogEntry = string>` type.

Example:

```js
  supportedLogTypes = {
    debug: /** @type {import('@appium/types').LogDef<RokuDriverCapConstraints, string>} */ ({
      description: 'Roku debug logs',
      /** @param {RokuDriver} driver */
      async getter(driver) {
        return await ['foo'];
      },
    }),
  };
```

This is a log named `debug` which stores strings.  The `getter` function is always expected to return a type of `LogEntry[]` and receives a type of `Driver<C>`. In this case, it's the default log entry type (`string`) and `RokuDriver`, which implements `Driver<RokuDriverCapConstraints>` where `RokuDriverCapConstraints` extends `Constraints`.

It was kind of tricky since `supportedLogTypes` is an instance field and we don't have access to `this`, so cannot derive the type of the `driver` parameter from it, nor can we use `this` in the `ILogCommands` interface.

Renamed these various mixin interfaces to be prefixed with `I`. All of the mixins themselves (the implementations) are now driver-aware via the `C` type parameter and thus can reference driver-specific caps or options, if needed.
2022-10-24 15:07:59 -07:00

71 lines
2.8 KiB
JavaScript

import _ from 'lodash';
import {errors, makeArgs, checkParams} from '../../protocol';
/**
* @template {Constraints} C
* @param {import('./session').SessionBase<C>} Base
* @returns {ExecuteBase<C>}
*/
export function ExecuteMixin(Base) {
/**
* @implements {IExecuteCommands}
*/
class ExecuteCommands extends Base {
/**
* @param {string} script
* @param {[Record<string, any>]|[]} protoArgs
*/
async executeMethod(script, protoArgs) {
// the w3c protocol will give us an array of arguments to apply to a javascript function.
// that's not what we're doing. we're going to look for a JS object as the first arg, so we
// can perform validation on it. we'll ignore everything else.
if (!protoArgs || !_.isArray(protoArgs) || protoArgs.length > 1) {
throw new errors.InvalidArgumentError(
`Did not get correct format of arguments for execute method. Expected zero or one ` +
`arguments to execute script and instead received: ${JSON.stringify(protoArgs)}`
);
}
let args = protoArgs[0] ?? {};
if (!_.isPlainObject(args)) {
throw new errors.InvalidArgumentError(
`Did not receive an appropriate execute method parameters object. It needs to be ` +
`deserializable as a plain JS object`
);
}
const Driver = /** @type {DriverClass} */ (this.constructor);
const availableScripts = _.keys(Driver.executeMethodMap);
const commandMetadata = Driver.executeMethodMap[script];
if (!commandMetadata) {
throw new errors.UnsupportedOperationError(
`Unsupported execute method '${script}'. Available methods ` +
`are: ${availableScripts.join(', ')}`
);
}
let argsToApply = [];
if (!commandMetadata.params) {
commandMetadata.params = {required: [], optional: []};
} else {
commandMetadata.params.required ??= [];
commandMetadata.params.optional ??= [];
checkParams(commandMetadata.params, args, null);
}
argsToApply = makeArgs({}, args, commandMetadata.params, null);
return await this[Driver.executeMethodMap[script].command](...argsToApply);
}
}
return ExecuteCommands;
}
/**
* @typedef {import('@appium/types').IExecuteCommands} IExecuteCommands
* @typedef {import('@appium/types').Driver} Driver
* @typedef {import('@appium/types').DriverClass} DriverClass
* @typedef {import('@appium/types').Constraints} Constraints
*/
/**
* @template {Constraints} C
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & import('@appium/types').ILogCommands<C> & import('@appium/types').ISettingsCommands & import('@appium/types').SessionCommands & IExecuteCommands>} ExecuteBase
*/