refactor: break up executeCommand so it is shorter and easier to read
MODIFY
lib/appium.js
lib/appium.js
@@ -346,27 +346,12 @@ class AppiumDriver extends BaseDriver {
}
}
- pluginsToWrapCmd (cmd) {
- return this.plugins.filter((plugin) => plugin.wrapCommands.includes(cmd));
- }
-
pluginsToHandleCmd (cmd) {
- return this.plugins.filter((plugin) => {
- if (plugin.commands === true) {
- return true;
- }
- if (_.isArray(plugin.commands) && plugin.commands.includes(cmd)) {
- return true;
- }
- return false;
- });
+ return this.plugins.filter((p) => p.commands === true || _.includes(p.commands, cmd));
}
async executeCommand (cmd, ...args) {
- // first things first, let's create a result object which we'll ultimately return.
- let res = {};
-
- // now we have basically three cases for how to handle commands:
+ // We have basically three cases for how to handle commands:
// 1. handle getStatus (we do this as a special out of band case so it doesn't get added to an
// execution queue, and can be called while e.g. createSession is in progress)
// 2. handle commands that this umbrella driver should handle, rather than the actual session
@@ -383,14 +368,15 @@ class AppiumDriver extends BaseDriver {
// sure that session actually exists on the session driver, and set the session driver itself
let sessionId = null;
let dstSession = null;
+ let protocol = null;
if (isSessionCmd) {
sessionId = _.last(args);
dstSession = await sessionsListGuard.acquire(AppiumDriver.name, () => this.sessions[sessionId]);
if (!dstSession) {
throw new Error(`The session with id '${sessionId}' does not exist`);
}
- // now reset the response protocol given that the session driver's protocol might differ
- res.protocol = dstSession.protocol;
+ // now save the response protocol given that the session driver's protocol might differ
+ protocol = dstSession.protocol;
}
// now we define a 'handled' object which will keep track of which plugins have handled this
@@ -405,7 +391,7 @@ class AppiumDriver extends BaseDriver {
// if there is more than one plugin that can handle the command. To start off with, the async
// function is defined as calling the default behavior, i.e., whichever of the 3 cases above is
// the appropriate one
- let next = async () => {
+ const defaultBehavior = async () => {
log.info(`Executing default handling behavior for command '${cmd}'`);
handled.default = true; // if we make it here, we know that the default behavior is handled
@@ -423,7 +409,17 @@ class AppiumDriver extends BaseDriver {
return await dstSession.executeCommand(cmd, ...args);
};
- // then we get any plugins which are registered as handling this command
+ // now take our default behavior, wrap it with any number of plugin behaviors, and run it
+ const wrappedCmd = this.wrapCommandWithPlugins({cmd, args, handled, next: defaultBehavior});
+ const res = await this.executeWrappedCommand({wrappedCmd, protocol});
+
+ this.logPluginHandlerReport({cmd, handled});
+
+ return res;
+ }
+
+ wrapCommandWithPlugins ({cmd, args, next, handled}) {
+ // get any plugins which are registered as handling this command
const plugins = this.pluginsToHandleCmd(cmd);
plugins.length && log.info(`Plugins which can handle cmd '${cmd}': ${plugins.map((p) => p.name)}`);
@@ -442,26 +438,10 @@ class AppiumDriver extends BaseDriver {
})(next);
}
- let cmdRes, cmdErr;
- try {
- // ok now we're done with all the plugin wrapping. At this point, `next` defines a whole
- // sequence of plugin handlers, culminating in our default handler. Whatever it returns is
- // what we're going to want to send back to the user.
- cmdRes = await next();
- } catch (e) {
- cmdErr = e;
- }
-
- // Sadly, we don't know exactly what kind of object will be returned. It will either be a bare
- // object, or a protocol-aware object with protocol and error/value keys. So we need to sniff
- // it and make sure we don't double-wrap it if it's the latter kind.
- if (_.isPlainObject(cmdRes) && _.has(cmdRes, 'protocol')) {
- res = cmdRes;
- } else {
- res.value = cmdRes;
- res.error = cmdErr;
+ return next;
}
+ logPluginHandlerReport ({cmd, handled}) {
// at the end of the day, we have an object representing which plugins ended up getting
// their code run as part of handling this command. Because plugins can choose *not* to
// pass control to other plugins or to the default driver behavior, this is information
@@ -475,10 +455,33 @@ class AppiumDriver extends BaseDriver {
`though they were registered to handle it: ${JSON.stringify(didntHandle)}. The ` +
`command *was* handled by these: ${JSON.stringify(didHandle)}.`);
}
+ }
+
+ async executeWrappedCommand ({wrappedCmd, protocol}) {
+ let cmdRes, cmdErr, res = {};
+ try {
+ // At this point, `wrappedCmd` defines a whole sequence of plugin handlers, culminating in
+ // our default handler. Whatever it returns is what we're going to want to send back to the
+ // user.
+ cmdRes = await wrappedCmd();
+ } catch (e) {
+ cmdErr = e;
+ }
+ // Sadly, we don't know exactly what kind of object will be returned. It will either be a bare
+ // object, or a protocol-aware object with protocol and error/value keys. So we need to sniff
+ // it and make sure we don't double-wrap it if it's the latter kind.
+ if (_.isPlainObject(cmdRes) && _.has(cmdRes, 'protocol')) {
+ res = cmdRes;
+ } else {
+ res.value = cmdRes;
+ res.error = cmdErr;
+ res.protocol = protocol;
+ }
return res;
}
+
proxyActive (sessionId) {
const dstSession = this.sessions[sessionId];
return dstSession && _.isFunction(dstSession.proxyActive) && dstSession.proxyActive(sessionId);

MODIFY
lib/cli/args.js
lib/cli/args.js
@@ -403,14 +403,16 @@ function makeUpdateArgs (type) {
[[type], {
type: 'string',
example: type === DRIVER_TYPE ? DRIVER_EXAMPLE : PLUGIN_EXAMPLE,
- help: `Name of the ${type} to update, or the word "installed" to update all installed ${type}s. To see available updates, run "appium ${type} list --installed --updates"`,
+ help: `Name of the ${type} to update, or the word "installed" to update all installed ` +
+ `${type}s. To see available updates, run "appium ${type} list --installed --updates"`,
}],
[['--unsafe'], {
required: false,
defaultValue: false,
action: 'storeTrue',
nargs: 0,
- help: 'Include updates that might have a new major revision, and potentially include breaking changes',
+ help: `Include updates that might have a new major revision, and potentially include ` +
+ `breaking changes`,
}],
];
}

refactor: break up executeCommand so it is shorter and easier to read