diff --git a/lib/commands/execute.js b/lib/commands/execute.js new file mode 100644 index 00000000..e4fcfb07 --- /dev/null +++ b/lib/commands/execute.js @@ -0,0 +1,27 @@ +import _ from 'lodash'; +import { errors } from 'appium-base-driver'; + +let extensions = {}; + +extensions.execute = async function (script, args) { + if (script.match(/^mobile\:/)) { + script = script.replace(/^mobile\:/, '').trim(); + return await this.executeMobile(script, _.isArray(args) ? args[0] : args); + } + + throw new errors.NotImplementedError(); +}; + +extensions.executeMobile = async function (mobileCommand, opts = {}) { + const mobileCommandsMapping = { + shell: async (x) => await this.mobileShell(x), + }; + + if (!_.has(mobileCommandsMapping, mobileCommand)) { + throw new errors.UnknownCommandError(`Unknown mobile command "${mobileCommand}". ` + + `Only ${_.keys(mobileCommandsMapping)} commands are supported.`); + } + return await mobileCommandsMapping[mobileCommand](opts); +}; + +export default extensions; diff --git a/lib/commands/index.js b/lib/commands/index.js index a77acee2..51e7755d 100644 --- a/lib/commands/index.js +++ b/lib/commands/index.js @@ -10,6 +10,8 @@ import networkCmds from './network'; import coverageCmds from './coverage'; import recordscreenCmds from './recordscreen'; import performanceCmds from './performance'; +import executeCmds from "./execute"; +import shellCmds from "./shell"; let commands = {}; Object.assign( @@ -25,7 +27,9 @@ Object.assign( networkCmds, coverageCmds, recordscreenCmds, - performanceCmds + performanceCmds, + executeCmds, + shellCmds, // add other command types here ); diff --git a/lib/commands/shell.js b/lib/commands/shell.js new file mode 100644 index 00000000..88fcf54f --- /dev/null +++ b/lib/commands/shell.js @@ -0,0 +1,28 @@ +import log from '../logger'; +import _ from 'lodash'; +import { util } from 'appium-support'; + +let commands = {}; + +commands.mobileShell = async function (opts = {}) { + if (!this.relaxedSecurityEnabled) { + log.errorAndThrow(`Appium server must have relaxed security flag set in order to run any shell commands`); + } + + if (!_.isString(opts.command)) { + log.errorAndThrow(`The 'command' argument is mandatory'`); + } + let args = opts.args; + if (util.hasValue(args)) { + if (!_.isArray(args)) { + args = [args]; + } + } else { + args = []; + } + + return await this.adb.shell([opts.command, ...args]); +}; + +export { commands }; +export default commands; diff --git a/test/functional/commands/execute-e2e-specs.js b/test/functional/commands/execute-e2e-specs.js new file mode 100644 index 00000000..a08b0a55 --- /dev/null +++ b/test/functional/commands/execute-e2e-specs.js @@ -0,0 +1,58 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import AndroidDriver from '../../..'; +import _ from 'lodash'; +import DEFAULT_CAPS from '../desired'; + + +chai.should(); +chai.use(chaiAsPromised); + +let driver; +let caps = _.defaults({ + appPackage: 'io.appium.android.apis', + appActivity: '.view.TextFields' +}, DEFAULT_CAPS); + +describe('execute', function () { + before(async function () { + driver = new AndroidDriver(); + await driver.createSession(caps); + }); + after(async function () { + await driver.deleteSession(); + }); + + it('should fail if one tries to execute non-mobile command in native context', async function () { + await driver.execute('blabla').should.eventually.be.rejected; + }); + + it('should fail if one tries to execute an unknown mobile command in native context', async function () { + await driver.execute('mobile: blabla').should.eventually.be.rejectedWith(/Unknown mobile command/); + }); + + it('should fail if one tries to execute a shell command without relaxed security flag set', async function () { + await driver.execute('mobile: shell', {command: 'pm', args: ['list']}) + .should.eventually.be.rejectedWith(/must have relaxed security flag set/); + }); + + it('should fail if no command argument is provided to shell call', async function () { + driver.relaxedSecurityEnabled = true; + try { + await driver.execute('mobile: shell', {comand: 'pm', args: ['list']}) + .should.eventually.be.rejectedWith(/argument is mandatory/); + } finally { + driver.relaxedSecurityEnabled = undefined; + } + }); + + it('should return a result if correct shell command is provided', async function () { + driver.relaxedSecurityEnabled = true; + try { + (await driver.execute('mobile: shell', {command: 'echo', args: 'hello'})) + .should.not.be.empty; + } finally { + driver.relaxedSecurityEnabled = undefined; + } + }); +});