Files
Jonathan Lipps 8df1c217a1 feat(appium,base-driver,base-plugin): allow plugins to define custom bidi commands and emit bidi events (#20876)
* feat: allow drivers to define their own bidi commands

* test(appium): refactor plugin tests server startup

* feat: allow plugins to define their own bidi commands and events

* fix(fake-driver,fake-plugin): ensure custom bidi commands events have vendor prefixes

* feat(appium): link plugin and driver objects in log lines when possible

* fix(appium,base-driver): ensure new methods for plugins aren't called without existence checks
2025-01-08 13:45:36 -08:00

108 lines
3.4 KiB
JavaScript

import {ExtensionCore, generateDriverLogPrefix, validateExecuteMethodParams} from '@appium/base-driver';
/**
* @implements {Plugin}
*/
class BasePlugin extends ExtensionCore {
/**
* @deprecated Use this.log instead of this.logger
* @type {import('@appium/types').AppiumLogger}
*/
logger;
/**
* Subclasses should use type `import('@appium/types').MethodMap<SubclassName>`.
*
* This will verify that the commands in the `newMethodMap` property are
* valid. It is impossible to use a generic type param here; the type of this should really
* be something like `MethodMap<T extends BasePlugin>` but that isn't a thing TS does.
*
* ```ts
* static newMethodMap = {
* '/session/:sessionId/fake_data': {
* GET: {command: 'getFakeSessionData', neverProxy: true},
* }
* } as const;
* ```
*/
static newMethodMap = {};
/**
* Subclasses should use type `import('@appium/types').ExecuteMethodMap<SubclassName>`.
*
* Building up this map allows the use of the convenience function `executeMethod`, which
* basically does verification of names and parameters for execute methods implemented by this
* plugin.
*
* ```ts
* static executeMethodMap = {
* 'foo: bar': {
* command: 'commandName',
* params: {required: ['thing1', 'thing2'], optional: ['thing3']},
* },
* } as const;
* ```
*/
static executeMethodMap = {};
/**
* @param {string} name
* @param {Record<string,unknown>} [cliArgs]
* @param {string|null} driverId
*/
constructor(name, cliArgs = {}, driverId = null) {
super();
if (driverId) {
this.updateLogPrefix(`${generateDriverLogPrefix(this)} <${driverId}>`);
}
this.name = name;
this.cliArgs = cliArgs;
this.logger = this.log;
}
/**
* A convenience method that can be called by plugins who implement their own `executeMethodMap`.
* Only useful if your plugin has defined `executeMethodMap`. This helper requires passing in the
* `next` and `driver` objects since naturally we'd want to make sure to trigger the driver's own
* `executeMethod` call if an execute method is not found on the plugin itself.
*
* @template {Constraints} C
* @param {NextPluginCallback} next
* @param {Driver<C>} driver
* @param {string} script
* @param {readonly [import('@appium/types').StringRecord<unknown>] | readonly any[]} protoArgs
*/
async executeMethod(next, driver, script, protoArgs) {
const Plugin = /** @type {import('@appium/types').PluginClass<Plugin>} */ (this.constructor);
const commandMetadata = {...Plugin.executeMethodMap?.[script]};
if (!commandMetadata.command || !(commandMetadata.command in this)) {
this.logger.info(
`Plugin did not know how to handle method '${script}'. Passing control to next`
);
return await next();
}
const command = /** @type {import('@appium/types').PluginCommand<Driver<C>>} */ (
this[commandMetadata.command]
);
const args = validateExecuteMethodParams(protoArgs, commandMetadata.params);
return await command.call(this, next, driver, ...args);
}
}
export default BasePlugin;
export {BasePlugin};
/**
* @typedef {import('@appium/types').Plugin} Plugin
* @typedef {import('@appium/types').NextPluginCallback} NextPluginCallback
* @typedef {import('@appium/types').Constraints} Constraints
*/
/**
* @template {Constraints} C
* @typedef {import('@appium/types').Driver<C>} Driver
*/