Skip to content

Commit

Permalink
chore: Update arguments validation in several mobile extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach committed Nov 3, 2023
1 parent ee28266 commit b73c846
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 86 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ Perform swipe action. Invokes Espresso [swipe action](https://developer.android.

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
element | string | yes | The UDID of the element to perform the swipe on. | 123456-7890-3453-24234243
elementId (element before v2.29) | string | yes | The UDID of the element to perform the swipe on. | 123456-7890-3453-24234243
direction | string | no | Swipe direction. Either this argument or `swiper` must be provided, but not both. The following values are supported: `up`, `down`, `left`, `right` | down
swiper | string | no | Swipe speed. Either this argument or `direction` must be provided, but not both. Either `FAST` (Swipes quickly between the co-ordinates) or `SLOW` (Swipes deliberately slowly between the co-ordinates, to aid in visual debugging) | SLOW
startCoordinates | string | no | The starting coordinates for the action. The following values are supported: `TOP_LEFT`, `TOP_CENTER`, `TOP_RIGHT`, `CENTER_LEFT`, `CENTER`, `CENTER_RIGHT`, `BOTTOM_LEFT`, `BOTTOM_CENTER` (the default value), `BOTTOM_RIGHT`, `VISIBLE_CENTER` | CENTER_LEFT
Expand Down Expand Up @@ -600,7 +600,7 @@ Opens the DrawerLayout drawer with the gravity. This method blocks until the dra

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
element | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
elementId (element before v2.29) | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
gravity | int | no | See [GravityCompat](https://developer.android.com/reference/kotlin/androidx/core/view/GravityCompat) and [Gravity](https://developer.android.com/reference/android/view/Gravity) classes documentation | `0x00800000 <bitwise_or> 0x00000003`

### mobile: closeDrawer
Expand All @@ -611,7 +611,7 @@ Closes the DrawerLayout drawer with the gravity. This method blocks until the dr

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
element | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
elementId (element before v2.29) | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
gravity | int | no | See [GravityCompat](https://developer.android.com/reference/kotlin/androidx/core/view/GravityCompat) and [Gravity](https://developer.android.com/reference/android/view/Gravity) classes documentation | `0x00800000 <bitwise_or> 0x00000005`

### mobile: scrollToPage
Expand All @@ -622,7 +622,7 @@ Perform scrolling to the given page. Invokes one of the [ViewPagerActions](https

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
element | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
elementId (element before v2.29) | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
scrollTo | string | no if `scrollToPage` is provided | Shifts ViewPager to the given page. Supported values are: `first`, `last`, `left`, `right` | last
scrollToPage | int | no if `scrollTo` is provided | Moves ViewPager to a specific page number (numbering starts from zero). | 1
smoothScroll | boolean | no | Whether to perform smooth (but slower) scrolling (`true`). The default value is `false` | true
Expand All @@ -635,7 +635,7 @@ Invokes [navigateTo](https://developer.android.com/reference/androidx/test/espre

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
element | string | yes | UDID of the element to perform the action on. View constraints: View must be a child of a DrawerLayout; View must be of type NavigationView; View must be visible on screen; View must be displayed on screen | 123456-7890-3453-24234243
elementId (element before v2.29) | string | yes | UDID of the element to perform the action on. View constraints: View must be a child of a DrawerLayout; View must be of type NavigationView; View must be visible on screen; View must be displayed on screen | 123456-7890-3453-24234243
menuItemId | int | yes | The resource id of the destination menu item | 123

### mobile: clickAction
Expand All @@ -646,7 +646,7 @@ Perform [general click action](https://developer.android.com/reference/androidx/

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
element | string | yes | The UDID of the element to perform the click on. | 123456-7890-3453-24234243
elementId (element before v2.29) | string | yes | The UDID of the element to perform the click on. | 123456-7890-3453-24234243
tapper | string | no | Tapper type. Supported types are: `SINGLE` (the default value), `LONG`, `DOUBLE` | `LONG`
coordinatesProvider | string | no | The coordinates for the action. The following values are supported: `TOP_LEFT`, `TOP_CENTER`, `TOP_RIGHT`, `CENTER_LEFT`, `CENTER`, `CENTER_RIGHT`, `BOTTOM_LEFT`, `BOTTOM_CENTER`, `BOTTOM_RIGHT`, `VISIBLE_CENTER` (the default value) | CENTER_LEFT
precisionDescriber | string | no | Defines the actual click precision. The following values are supported: `PINPOINT` (1px), `FINGER` (average width of the index finger is 16 – 20 mm, the default value), `THUMB` (average width of an adult thumb is 25 mm or 1 inch) | PINPOINT
Expand Down Expand Up @@ -1070,7 +1070,7 @@ Highlights the given element in the UI by adding flashing to it

Name | Type | Required | Description | Example
--- | --- | --- | --- | ---
element | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
elementId (element before v2.29) | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
durationMillis | int | no | Duration of single flashing sequence, 30 ms by default | 50
repeatCount | int | no | Count of repeats, 15 times by default | 10

Expand Down
188 changes: 111 additions & 77 deletions lib/commands/general.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import _ from 'lodash';
import { util } from 'appium/support';
import validate from 'validate.js';
import { errors } from 'appium/driver';
import { qualifyActivityName } from '../utils';

const commands = {};

/**
* @template {Record<string, any>} T
* @param {T} options
* @param {string[]|string} requiredOptionNames
* @returns {T}
*/
function requireOptions (options, requiredOptionNames) {
if (!_.isArray(requiredOptionNames)) {
requiredOptionNames = [requiredOptionNames];
Expand All @@ -19,6 +24,18 @@ function requireOptions (options, requiredOptionNames) {
`You have only provided: ${JSON.stringify(presentOptionNames)}`);
}

/**
* @param {Record<string, any>} opts
* @returns {string}
*/
function requireElementId (opts) {
const {element, elementId} = opts;
if (!element && !elementId) {
throw new errors.InvalidArgumentError('Element Id must be provided');
}
return util.unwrapElement(elementId || element);
}

/**
* @this {import('../driver').EspressoDriver}
*/
Expand Down Expand Up @@ -73,10 +90,20 @@ commands.mobilePerformEditorAction = async function mobilePerformEditorAction (o
* @this {import('../driver').EspressoDriver}
*/
commands.mobileSwipe = async function mobileSwipe (opts = {}) {
const {direction, element, swiper, startCoordinates, endCoordinates, precisionDescriber} = requireOptions(opts, ['element']);
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/swipe`, 'POST', {
direction, element, swiper, startCoordinates, endCoordinates, precisionDescriber
});
const {direction, swiper, startCoordinates, endCoordinates, precisionDescriber} = opts;
const element = requireElementId(opts);
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${element}/swipe`,
'POST',
{
direction,
element,
swiper,
startCoordinates,
endCoordinates,
precisionDescriber,
}
);
};

/**
Expand Down Expand Up @@ -148,63 +175,73 @@ commands.mobileIsToastVisible = async function mobileIsToastVisible (opts = {})
* @this {import('../driver').EspressoDriver}
*/
commands.mobileOpenDrawer = async function mobileOpenDrawer (opts = {}) {
const {element, gravity} = requireOptions(opts, ['element']);

return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/open_drawer`, 'POST', {
gravity
});
const {gravity} = opts;
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/open_drawer`,
'POST',
{gravity}
);
};

/**
* @this {import('../driver').EspressoDriver}
*/
commands.mobileCloseDrawer = async function mobileCloseDrawer (opts = {}) {
const {element, gravity} = requireOptions(opts, ['element']);

return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/close_drawer`, 'POST', {
gravity
});
const {gravity} = opts;
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/close_drawer`,
'POST',
{gravity}
);
};

/**
* @this {import('../driver').EspressoDriver}
*/
commands.mobileSetDate = async function mobileSetDate (opts = {}) {
const {element, year, monthOfYear, dayOfMonth} = requireOptions(opts, ['element', 'year', 'monthOfYear', 'dayOfMonth']);

return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/set_date`, 'POST', {
year,
monthOfYear,
dayOfMonth,
});
const {year, monthOfYear, dayOfMonth} = requireOptions(
opts, ['year', 'monthOfYear', 'dayOfMonth']
);
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/set_date`,
'POST', {
year,
monthOfYear,
dayOfMonth,
}
);
};

/**
* @this {import('../driver').EspressoDriver}
*/
commands.mobileSetTime = async function mobileSetTime (opts = {}) {
const {element, hours, minutes} = requireOptions(opts, ['element', 'hours', 'minutes']);

return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/set_time`, 'POST', {
hours,
minutes,
});
const {hours, minutes} = requireOptions(opts, ['hours', 'minutes']);
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/set_time`,
'POST', {
hours,
minutes,
}
);
};

/**
* @this {import('../driver').EspressoDriver}
*/
commands.mobileNavigateTo = async function mobileNavigateTo (opts = {}) {
let {element, menuItemId} = requireOptions(opts, ['menuItemId', 'element']);

let menuItemIdAsNumber = parseInt(menuItemId, 10);
const {menuItemId} = requireOptions(opts, ['menuItemId']);
const menuItemIdAsNumber = parseInt(menuItemId, 10);
if (_.isNaN(menuItemIdAsNumber) || menuItemIdAsNumber < 0) {
throw new errors.InvalidArgumentError(`'menuItemId' must be a non-negative number. Found ${menuItemId}`);
throw new errors.InvalidArgumentError(
`'menuItemId' must be a non-negative number. Found ${menuItemId}`
);
}

return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/navigate_to`, 'POST', {
menuItemId
});
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/navigate_to`,
'POST',
{menuItemId}
);
};

/**
Expand Down Expand Up @@ -242,40 +279,30 @@ commands.getDisplayDensity = async function getDisplayDensity () {
* @this {import('../driver').EspressoDriver}
*/
commands.mobileScrollToPage = async function mobileScrollToPage (opts = {}) {

// Validate the parameters
const {scrollTo, scrollToPage, smoothScroll} = opts;
const scrollToTypes = ['first', 'last', 'left', 'right'];
const res = validate(opts, {
element: {presence: true},
scrollTo: {
inclusion: {
within: scrollToTypes,
message: `"scrollTo" must be one of "${scrollToTypes.join(', ')}" found '%{value}'`,
}
},
scrollToPage: {
numericality: {
onlyInteger: true,
greaterThanOrEqualTo: 0,
message: `"scrollToPage" must be a non-negative integer. Found '%{value}'`
},
},
});
if (util.hasValue(res)) {
throw new errors.InvalidArgumentError(`Invalid scrollTo parameters: ${JSON.stringify(res)}`);
if (!scrollToTypes.includes(scrollTo)) {
throw new errors.InvalidArgumentError(
`"scrollTo" must be one of "${scrollToTypes.join(', ')}" found '${scrollTo}'`
);
}
if (!_.isInteger(scrollToPage) || scrollToPage < 0) {
throw new errors.InvalidArgumentError(
`"scrollToPage" must be a non-negative integer. Found '${scrollToPage}'`
);
}

const {element, scrollTo, scrollToPage, smoothScroll} = opts;

if (util.hasValue(scrollTo) && util.hasValue(scrollToPage)) {
this.log.warn(`'scrollTo' and 'scrollToPage' where both provided. Defaulting to 'scrollTo'`);
}

return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/scroll_to_page`, 'POST', {
scrollTo,
scrollToPage,
smoothScroll,
});
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/scroll_to_page`,
'POST', {
scrollTo,
scrollToPage,
smoothScroll,
}
);
};


Expand Down Expand Up @@ -322,8 +349,7 @@ commands.mobileScrollToPage = async function mobileScrollToPage (opts = {}) {
*
*/
commands.mobileBackdoor = async function mobileBackdoor (opts = {}) {
requireOptions(opts, ['target', 'methods']);
const {target, methods} = opts;
const {target, methods} = requireOptions(opts, ['target', 'methods']);;
if (target === 'element') {
requireOptions(opts, ['elementId']);
}
Expand Down Expand Up @@ -365,24 +391,30 @@ commands.mobileUiautomatorPageSource = async function mobileUiautomatorPageSourc
* @this {import('../driver').EspressoDriver}
*/
commands.mobileFlashElement = async function mobileFlashElement (opts = {}) {
const {element} = requireOptions(opts, ['element']);
const {durationMillis, repeatCount} = opts;
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/flash`, 'POST', {
durationMillis,
repeatCount
});
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/flash`,
'POST', {
durationMillis,
repeatCount
}
);
};

/**
* Perform a 'GeneralClickAction' (https://developer.android.com/reference/androidx/test/espresso/action/GeneralClickAction)
* @this {import('../driver').EspressoDriver}
*/
commands.mobileClickAction = async function mobileClickAction (opts = {}) {
const {element, tapper, coordinatesProvider, precisionDescriber,
inputDevice, buttonState} = requireOptions(opts, ['element']);
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/click_action`, 'POST', {
const {
tapper, coordinatesProvider, precisionDescriber, inputDevice, buttonState
});
} = opts;
return await this.espresso.jwproxy.command(
`/appium/execute_mobile/${requireElementId(opts)}/click_action`,
'POST', {
tapper, coordinatesProvider, precisionDescriber, inputDevice, buttonState
}
);
};

/**
Expand Down Expand Up @@ -479,9 +511,11 @@ commands.startActivity = async function startActivity (
* @this {import('../driver').EspressoDriver}
*/
commands.mobileDismissAutofill = async function mobileDismissAutofill (opts = {}) {
const {element} = requireOptions(opts, ['element']);
await this.espresso.jwproxy.command(
`/session/:sessionId/appium/execute_mobile/${util.unwrapElement(element)}/dismiss_autofill`, 'POST', {});
`/session/:sessionId/appium/execute_mobile/${requireElementId(opts)}/dismiss_autofill`,
'POST',
{}
);
};

export { commands };
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@
"lodash": "^4.17.11",
"portscanner": "^2.1.1",
"source-map-support": "^0.x",
"teen_process": "^2.0.0",
"validate.js": "^0.x"
"teen_process": "^2.0.0"
},
"scripts": {
"build": "npm run build:node && npm run build:server",
Expand Down

0 comments on commit b73c846

Please sign in to comment.