Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Appium-specific commands. #3492

Merged
merged 17 commits into from Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 14 additions & 2 deletions lib/api/client-commands/setGeolocation.js
Expand Up @@ -4,6 +4,10 @@ const {Logger} = require('../../utils');
/**
* Mock the geolocation of the browser. Call without any arguments to reset the geolocation back to original.
*
* This command also supports mocking the geolocation of mobile devices (when automated using Appium), with a few caveats:
* - It accepts `{latitude: number, longitude: number, altitude: number}` as the first argument with `altitude` as optional.
* - Calling the command without any argument does not reset the geolocation of the mobile device.
*
garg3133 marked this conversation as resolved.
Show resolved Hide resolved
* @example
* describe('mock geolocation', function() {
* it('sets the geolocation to Tokyo, Japan and then resets it', () => {
Expand All @@ -22,8 +26,8 @@ const {Logger} = require('../../utils');
* });
*
* @method setGeolocation
* @syntax .setGeolocation({latitude, longitude, accuracy}, [callback])
* @param {object} [coordinates] Latitude, longitude, and accuracy.
* @syntax .setGeolocation({latitude, longitude, accuracy|altitude}, [callback])
* @param {object} [coordinates] `latitude` and `longitude` are required; `accuracy` is optional for desktop browsers, and `altitude` is optional for mobile devices.
* @param {function} [callback] Callback function to be called when the command finishes.
* @api protocol.cdp
* @since 2.2.0
Expand All @@ -35,6 +39,14 @@ class SetGeolocation extends ClientCommand {
}

performAction(callback) {
// TODO: use better predicate to find out if Appium is being used.
if (this.api.browserName === null) {
if (!('latitude' in this.coordinates && 'longitude' in this.coordinates)) {
throw new Error('Please provide both latitude and longitude while using setGeolocation.');
}

return this.transportActions.setDeviceGeolocation(this.coordinates, callback);
}

if (!this.api.isChrome() && !this.api.isEdge()) {
const error = new Error('The command .setGeolocation() is only supported in Chrome and Edge drivers');
Expand Down
18 changes: 18 additions & 0 deletions lib/api/protocol/getCurrentActivity.js
@@ -0,0 +1,18 @@
const ProtocolAction = require('./_base-action.js');

/**
* Get the name of the current Android activity.
*
* @syntax .getCurrentActivity([callback])
* @method getCurrentActivity
* @param {function} [callback] Callback function which is called with the result value.
* @returns {string} Name of the current activity.
* @see getCurrentPackage
* @see startActivity
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(callback) {
return this.transportActions.getCurrentActivity(callback);
}
};
18 changes: 18 additions & 0 deletions lib/api/protocol/getCurrentPackage.js
@@ -0,0 +1,18 @@
const ProtocolAction = require('./_base-action.js');

/**
* Get the name of the current Android package.
*
* @syntax .getCurrentPackage([callback])
* @method getCurrentPackage
* @param {function} [callback] Callback function which is called with the result value.
* @returns {string} Name of the current package.
* @see getCurrentActivity
* @see startActivity
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(callback) {
return this.transportActions.getCurrentPackage(callback);
}
};
19 changes: 19 additions & 0 deletions lib/api/protocol/getGeolocation.js
@@ -0,0 +1,19 @@
const ProtocolAction = require('./_base-action.js');

/**
* Get the current geolocation of the mobile device.
*
* This command works with Appium only. Getting the current geolocation for desktop browsers is not supported.
*
* @syntax .getGeolocation([callback])
* @method getGeolocation
* @param {function} [callback] Callback function which is called with the result value.
* @returns {object} The current geolocation: `{latitude: number, longitude: number, altitude: number}`.
* @see setGeolocation
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(callback) {
return this.transportActions.getDeviceGeolocation(callback);
}
};
7 changes: 5 additions & 2 deletions lib/api/protocol/getOrientation.js
@@ -1,10 +1,13 @@
const ProtocolAction = require('./_base-action.js');

/**
* Get the current browser orientation.
* Get the current browser/device orientation.
*
* @syntax .getOrientation([callback])
* @method getOrientation
* @param {function} callback Callback function which is called with the result value.
* @returns {string} The current browser orientation: {LANDSCAPE|PORTRAIT}
* @returns {string} The current browser/device orientation: `LANDSCAPE` or `PORTRAIT`.
* @see setOrientation
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
Expand Down
18 changes: 18 additions & 0 deletions lib/api/protocol/hideKeyboard.js
@@ -0,0 +1,18 @@
const ProtocolAction = require('./_base-action.js');

/**
* Hide soft keyboard.
*
* This command works with Appium only.
*
* @syntax .hideKeyboard([callback])
* @method hideKeyboard
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see isKeyboardShown
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(callback) {
return this.transportActions.hideDeviceKeyboard({}, callback);
}
};
19 changes: 19 additions & 0 deletions lib/api/protocol/isKeyboardShown.js
@@ -0,0 +1,19 @@
const ProtocolAction = require('./_base-action.js');

/**
* Whether or not the soft keyboard is shown.
*
* This command works with Appium only.
*
* @syntax .isKeyboardShown([callback])
* @method isKeyboardShown
* @param {function} [callback] Callback function which is called with the result value.
* @returns {boolean} True if the keyboard is shown.
* @see hideKeyboard
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(callback) {
return this.transportActions.isDeviceKeyboardShown(callback);
}
};
42 changes: 42 additions & 0 deletions lib/api/protocol/longPressKeyCode.js
@@ -0,0 +1,42 @@
const ProtocolAction = require('./_base-action.js');

/**
* Press and hold a particular key on an Android Device.
*
* This command works with Appium only. See [official Android Developers docs](https://developer.android.com/reference/android/view/KeyEvent.html) for reference of available Android key code values.
*
* @syntax .longPressKeyCode(keycode, [callback])
* @syntax .longPressKeyCode(keycode, metastate, flags, [callback])
* @method longPressKeyCode
* @param {number} keycode Key code to press on the device.
* @param {number} [metastate] Meta state to press the keycode with.
* @param {number} [flags] Flags for the keypress.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see pressKeyCode
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(keycode, ...args) {
let metastate;
let flags;
let callback;

if (typeof keycode !== 'number') {
throw new Error('The first argument to longPressKeyCode is mandatory and must be a number.');
}

if (typeof args[0] === 'function') {
callback = args[0];
} else {
[metastate, flags, callback] = args;
}

const opts = {
keycode,
...(metastate && {metastate}),
...(flags && {flags})
};

return this.transportActions.longPressDeviceKeyCode(opts, callback);
}
};
42 changes: 42 additions & 0 deletions lib/api/protocol/pressKeyCode.js
@@ -0,0 +1,42 @@
const ProtocolAction = require('./_base-action.js');
garg3133 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Press a particular key on an Android Device.
*
* This command works with Appium only. See [official Android Developers docs](https://developer.android.com/reference/android/view/KeyEvent.html) for reference of available Android key code values.
*
* @syntax .pressKeyCode(keycode, [callback])
* @syntax .pressKeyCode(keycode, metastate, flags, [callback])
* @method pressKeyCode
* @param {number} keycode Key code to press on the device.
* @param {number} [metastate] Meta state to press the keycode with.
* @param {number} [flags] Flags for the keypress.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see longPressKeyCode
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(keycode, ...args) {
let metastate;
let flags;
let callback;

if (typeof keycode !== 'number') {
throw new Error('The first argument to pressKeyCode is mandatory and must be a number.');
}

if (typeof args[0] === 'function') {
callback = args[0];
} else {
[metastate, flags, callback] = args;
}

const opts = {
keycode,
...(metastate && {metastate}),
...(flags && {flags})
};

return this.transportActions.pressDeviceKeyCode(opts, callback);
}
};
7 changes: 5 additions & 2 deletions lib/api/protocol/setOrientation.js
@@ -1,10 +1,13 @@
const ProtocolAction = require('./_base-action.js');

/**
* Sets the browser orientation.
* Set the current browser/device orientation.
*
* @param {string} orientation The new browser orientation: {LANDSCAPE|PORTRAIT}
* @syntax .setOrientation(orientation, [callback])
* @method setOrientation
* @param {string} orientation The new browser/device orientation: `LANDSCAPE` or `PORTRAIT`.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see getOrientation
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
Expand Down
42 changes: 42 additions & 0 deletions lib/api/protocol/startActivity.js
@@ -0,0 +1,42 @@
const ProtocolAction = require('./_base-action.js');

/**
* Start an Android activity by providing package name, activity name and other optional parameters.
*
* More info here: https://appium.io/docs/en/commands/device/activity/start-activity/
*
* @example
* module.exports = {
* 'start an android activity': function (client) {
* client.startActivity({
* appPackage: 'com.android.chrome',
* appActivity: 'com.google.android.apps.chrome.Main'
* });
* },
*
* 'start the main Android activity and wait for onboarding activity to start': function (client) {
* client.startActivity({
* appPackage: 'org.wikipedia',
* appActivity: 'org.wikipedia.main.MainActivity',
* appWaitActivity: 'org.wikipedia.onboarding.InitialOnboardingActivity'
* });
* }
* };
*
* @syntax .startActivity(opts, [callback])
* @method startActivity
* @param {string} opts Options to start the activity with. `appPackage` and `appActivity` are required, [others](https://appium.io/docs/en/commands/device/activity/start-activity/#json-parameters) are optional.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see getCurrentActivity
* @see getCurrentPackage
* @api protocol.mobile
*/
module.exports = class Session extends ProtocolAction {
command(opts = {}, callback) {
if (!('appPackage' in opts && 'appActivity' in opts)) {
throw new Error('Please provide both appPackage and appActivity options while using startActivity.');
}

return this.transportActions.startActivity(opts, callback);
}
};
60 changes: 60 additions & 0 deletions lib/transport/selenium-webdriver/method-mappings.js
Expand Up @@ -902,6 +902,10 @@ module.exports = class MethodMappings {
};
},

///////////////////////////////////////////////////////////
// Appium
///////////////////////////////////////////////////////////

getAvailableContexts() {
return '/contexts';
},
Expand All @@ -920,6 +924,62 @@ module.exports = class MethodMappings {
};
},

startActivity(opts) {
return {
method: 'POST',
path: '/appium/device/start_activity',
data: opts
};
},

getCurrentActivity() {
return '/appium/device/current_activity';
},

getCurrentPackage() {
return '/appium/device/current_package';
},

getDeviceGeolocation() {
return '/location';
},

setDeviceGeolocation(location) {
return {
method: 'POST',
path: '/location',
data: {location}
};
},

pressDeviceKeyCode(opts) {
return {
method: 'POST',
path: '/appium/device/press_keycode',
data: opts
};
},

longPressDeviceKeyCode(opts) {
return {
method: 'POST',
path: '/appium/device/long_press_keycode',
data: opts
};
},

hideDeviceKeyboard(opts) {
return {
method: 'POST',
path: '/appium/device/hide_keyboard',
data: opts
};
},

isDeviceKeyboardShown() {
return '/appium/device/is_keyboard_shown';
},

///////////////////////////////////////////////////////////////////////////
// Selenium Webdriver
///////////////////////////////////////////////////////////////////////////
Expand Down