Skip to content

Commit f90118f

Browse files
chore: Update arguments validation in several mobile extensions (#948)
1 parent ee28266 commit f90118f

File tree

3 files changed

+119
-86
lines changed

3 files changed

+119
-86
lines changed

README.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ Perform swipe action. Invokes Espresso [swipe action](https://developer.android.
570570

571571
Name | Type | Required | Description | Example
572572
--- | --- | --- | --- | ---
573-
element | string | yes | The UDID of the element to perform the swipe on. | 123456-7890-3453-24234243
573+
elementId (element before v2.29) | string | yes | The UDID of the element to perform the swipe on. | 123456-7890-3453-24234243
574574
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
575575
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
576576
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
@@ -600,7 +600,7 @@ Opens the DrawerLayout drawer with the gravity. This method blocks until the dra
600600

601601
Name | Type | Required | Description | Example
602602
--- | --- | --- | --- | ---
603-
element | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
603+
elementId (element before v2.29) | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
604604
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`
605605

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

612612
Name | Type | Required | Description | Example
613613
--- | --- | --- | --- | ---
614-
element | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
614+
elementId (element before v2.29) | string | yes | UDID of the element to perform the action on. | 123456-7890-3453-24234243
615615
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`
616616

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

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

636636
Name | Type | Required | Description | Example
637637
--- | --- | --- | --- | ---
638-
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
638+
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
639639
menuItemId | int | yes | The resource id of the destination menu item | 123
640640

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

647647
Name | Type | Required | Description | Example
648648
--- | --- | --- | --- | ---
649-
element | string | yes | The UDID of the element to perform the click on. | 123456-7890-3453-24234243
649+
elementId (element before v2.29) | string | yes | The UDID of the element to perform the click on. | 123456-7890-3453-24234243
650650
tapper | string | no | Tapper type. Supported types are: `SINGLE` (the default value), `LONG`, `DOUBLE` | `LONG`
651651
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
652652
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
@@ -1070,7 +1070,7 @@ Highlights the given element in the UI by adding flashing to it
10701070

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

lib/commands/general.js

+111-77
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import _ from 'lodash';
22
import { util } from 'appium/support';
3-
import validate from 'validate.js';
43
import { errors } from 'appium/driver';
54
import { qualifyActivityName } from '../utils';
65

76
const commands = {};
87

8+
/**
9+
* @template {Record<string, any>} T
10+
* @param {T} options
11+
* @param {string[]|string} requiredOptionNames
12+
* @returns {T}
13+
*/
914
function requireOptions (options, requiredOptionNames) {
1015
if (!_.isArray(requiredOptionNames)) {
1116
requiredOptionNames = [requiredOptionNames];
@@ -19,6 +24,18 @@ function requireOptions (options, requiredOptionNames) {
1924
`You have only provided: ${JSON.stringify(presentOptionNames)}`);
2025
}
2126

27+
/**
28+
* @param {Record<string, any>} opts
29+
* @returns {string}
30+
*/
31+
function requireElementId (opts) {
32+
const {element, elementId} = opts;
33+
if (!element && !elementId) {
34+
throw new errors.InvalidArgumentError('Element Id must be provided');
35+
}
36+
return util.unwrapElement(elementId || element);
37+
}
38+
2239
/**
2340
* @this {import('../driver').EspressoDriver}
2441
*/
@@ -73,10 +90,20 @@ commands.mobilePerformEditorAction = async function mobilePerformEditorAction (o
7390
* @this {import('../driver').EspressoDriver}
7491
*/
7592
commands.mobileSwipe = async function mobileSwipe (opts = {}) {
76-
const {direction, element, swiper, startCoordinates, endCoordinates, precisionDescriber} = requireOptions(opts, ['element']);
77-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/swipe`, 'POST', {
78-
direction, element, swiper, startCoordinates, endCoordinates, precisionDescriber
79-
});
93+
const {direction, swiper, startCoordinates, endCoordinates, precisionDescriber} = opts;
94+
const element = requireElementId(opts);
95+
return await this.espresso.jwproxy.command(
96+
`/appium/execute_mobile/${element}/swipe`,
97+
'POST',
98+
{
99+
direction,
100+
element,
101+
swiper,
102+
startCoordinates,
103+
endCoordinates,
104+
precisionDescriber,
105+
}
106+
);
80107
};
81108

82109
/**
@@ -148,63 +175,73 @@ commands.mobileIsToastVisible = async function mobileIsToastVisible (opts = {})
148175
* @this {import('../driver').EspressoDriver}
149176
*/
150177
commands.mobileOpenDrawer = async function mobileOpenDrawer (opts = {}) {
151-
const {element, gravity} = requireOptions(opts, ['element']);
152-
153-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/open_drawer`, 'POST', {
154-
gravity
155-
});
178+
const {gravity} = opts;
179+
return await this.espresso.jwproxy.command(
180+
`/appium/execute_mobile/${requireElementId(opts)}/open_drawer`,
181+
'POST',
182+
{gravity}
183+
);
156184
};
157185

158186
/**
159187
* @this {import('../driver').EspressoDriver}
160188
*/
161189
commands.mobileCloseDrawer = async function mobileCloseDrawer (opts = {}) {
162-
const {element, gravity} = requireOptions(opts, ['element']);
163-
164-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/close_drawer`, 'POST', {
165-
gravity
166-
});
190+
const {gravity} = opts;
191+
return await this.espresso.jwproxy.command(
192+
`/appium/execute_mobile/${requireElementId(opts)}/close_drawer`,
193+
'POST',
194+
{gravity}
195+
);
167196
};
168197

169198
/**
170199
* @this {import('../driver').EspressoDriver}
171200
*/
172201
commands.mobileSetDate = async function mobileSetDate (opts = {}) {
173-
const {element, year, monthOfYear, dayOfMonth} = requireOptions(opts, ['element', 'year', 'monthOfYear', 'dayOfMonth']);
174-
175-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/set_date`, 'POST', {
176-
year,
177-
monthOfYear,
178-
dayOfMonth,
179-
});
202+
const {year, monthOfYear, dayOfMonth} = requireOptions(
203+
opts, ['year', 'monthOfYear', 'dayOfMonth']
204+
);
205+
return await this.espresso.jwproxy.command(
206+
`/appium/execute_mobile/${requireElementId(opts)}/set_date`,
207+
'POST', {
208+
year,
209+
monthOfYear,
210+
dayOfMonth,
211+
}
212+
);
180213
};
181214

182215
/**
183216
* @this {import('../driver').EspressoDriver}
184217
*/
185218
commands.mobileSetTime = async function mobileSetTime (opts = {}) {
186-
const {element, hours, minutes} = requireOptions(opts, ['element', 'hours', 'minutes']);
187-
188-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/set_time`, 'POST', {
189-
hours,
190-
minutes,
191-
});
219+
const {hours, minutes} = requireOptions(opts, ['hours', 'minutes']);
220+
return await this.espresso.jwproxy.command(
221+
`/appium/execute_mobile/${requireElementId(opts)}/set_time`,
222+
'POST', {
223+
hours,
224+
minutes,
225+
}
226+
);
192227
};
193228

194229
/**
195230
* @this {import('../driver').EspressoDriver}
196231
*/
197232
commands.mobileNavigateTo = async function mobileNavigateTo (opts = {}) {
198-
let {element, menuItemId} = requireOptions(opts, ['menuItemId', 'element']);
199-
200-
let menuItemIdAsNumber = parseInt(menuItemId, 10);
233+
const {menuItemId} = requireOptions(opts, ['menuItemId']);
234+
const menuItemIdAsNumber = parseInt(menuItemId, 10);
201235
if (_.isNaN(menuItemIdAsNumber) || menuItemIdAsNumber < 0) {
202-
throw new errors.InvalidArgumentError(`'menuItemId' must be a non-negative number. Found ${menuItemId}`);
236+
throw new errors.InvalidArgumentError(
237+
`'menuItemId' must be a non-negative number. Found ${menuItemId}`
238+
);
203239
}
204-
205-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/navigate_to`, 'POST', {
206-
menuItemId
207-
});
240+
return await this.espresso.jwproxy.command(
241+
`/appium/execute_mobile/${requireElementId(opts)}/navigate_to`,
242+
'POST',
243+
{menuItemId}
244+
);
208245
};
209246

210247
/**
@@ -242,40 +279,30 @@ commands.getDisplayDensity = async function getDisplayDensity () {
242279
* @this {import('../driver').EspressoDriver}
243280
*/
244281
commands.mobileScrollToPage = async function mobileScrollToPage (opts = {}) {
245-
246-
// Validate the parameters
282+
const {scrollTo, scrollToPage, smoothScroll} = opts;
247283
const scrollToTypes = ['first', 'last', 'left', 'right'];
248-
const res = validate(opts, {
249-
element: {presence: true},
250-
scrollTo: {
251-
inclusion: {
252-
within: scrollToTypes,
253-
message: `"scrollTo" must be one of "${scrollToTypes.join(', ')}" found '%{value}'`,
254-
}
255-
},
256-
scrollToPage: {
257-
numericality: {
258-
onlyInteger: true,
259-
greaterThanOrEqualTo: 0,
260-
message: `"scrollToPage" must be a non-negative integer. Found '%{value}'`
261-
},
262-
},
263-
});
264-
if (util.hasValue(res)) {
265-
throw new errors.InvalidArgumentError(`Invalid scrollTo parameters: ${JSON.stringify(res)}`);
284+
if (!scrollToTypes.includes(scrollTo)) {
285+
throw new errors.InvalidArgumentError(
286+
`"scrollTo" must be one of "${scrollToTypes.join(', ')}" found '${scrollTo}'`
287+
);
288+
}
289+
if (!_.isInteger(scrollToPage) || scrollToPage < 0) {
290+
throw new errors.InvalidArgumentError(
291+
`"scrollToPage" must be a non-negative integer. Found '${scrollToPage}'`
292+
);
266293
}
267-
268-
const {element, scrollTo, scrollToPage, smoothScroll} = opts;
269-
270294
if (util.hasValue(scrollTo) && util.hasValue(scrollToPage)) {
271295
this.log.warn(`'scrollTo' and 'scrollToPage' where both provided. Defaulting to 'scrollTo'`);
272296
}
273297

274-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/scroll_to_page`, 'POST', {
275-
scrollTo,
276-
scrollToPage,
277-
smoothScroll,
278-
});
298+
return await this.espresso.jwproxy.command(
299+
`/appium/execute_mobile/${requireElementId(opts)}/scroll_to_page`,
300+
'POST', {
301+
scrollTo,
302+
scrollToPage,
303+
smoothScroll,
304+
}
305+
);
279306
};
280307

281308

@@ -322,8 +349,7 @@ commands.mobileScrollToPage = async function mobileScrollToPage (opts = {}) {
322349
*
323350
*/
324351
commands.mobileBackdoor = async function mobileBackdoor (opts = {}) {
325-
requireOptions(opts, ['target', 'methods']);
326-
const {target, methods} = opts;
352+
const {target, methods} = requireOptions(opts, ['target', 'methods']);;
327353
if (target === 'element') {
328354
requireOptions(opts, ['elementId']);
329355
}
@@ -365,24 +391,30 @@ commands.mobileUiautomatorPageSource = async function mobileUiautomatorPageSourc
365391
* @this {import('../driver').EspressoDriver}
366392
*/
367393
commands.mobileFlashElement = async function mobileFlashElement (opts = {}) {
368-
const {element} = requireOptions(opts, ['element']);
369394
const {durationMillis, repeatCount} = opts;
370-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/flash`, 'POST', {
371-
durationMillis,
372-
repeatCount
373-
});
395+
return await this.espresso.jwproxy.command(
396+
`/appium/execute_mobile/${requireElementId(opts)}/flash`,
397+
'POST', {
398+
durationMillis,
399+
repeatCount
400+
}
401+
);
374402
};
375403

376404
/**
377405
* Perform a 'GeneralClickAction' (https://developer.android.com/reference/androidx/test/espresso/action/GeneralClickAction)
378406
* @this {import('../driver').EspressoDriver}
379407
*/
380408
commands.mobileClickAction = async function mobileClickAction (opts = {}) {
381-
const {element, tapper, coordinatesProvider, precisionDescriber,
382-
inputDevice, buttonState} = requireOptions(opts, ['element']);
383-
return await this.espresso.jwproxy.command(`/appium/execute_mobile/${util.unwrapElement(element)}/click_action`, 'POST', {
409+
const {
384410
tapper, coordinatesProvider, precisionDescriber, inputDevice, buttonState
385-
});
411+
} = opts;
412+
return await this.espresso.jwproxy.command(
413+
`/appium/execute_mobile/${requireElementId(opts)}/click_action`,
414+
'POST', {
415+
tapper, coordinatesProvider, precisionDescriber, inputDevice, buttonState
416+
}
417+
);
386418
};
387419

388420
/**
@@ -479,9 +511,11 @@ commands.startActivity = async function startActivity (
479511
* @this {import('../driver').EspressoDriver}
480512
*/
481513
commands.mobileDismissAutofill = async function mobileDismissAutofill (opts = {}) {
482-
const {element} = requireOptions(opts, ['element']);
483514
await this.espresso.jwproxy.command(
484-
`/session/:sessionId/appium/execute_mobile/${util.unwrapElement(element)}/dismiss_autofill`, 'POST', {});
515+
`/session/:sessionId/appium/execute_mobile/${requireElementId(opts)}/dismiss_autofill`,
516+
'POST',
517+
{}
518+
);
485519
};
486520

487521
export { commands };

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@
7878
"lodash": "^4.17.11",
7979
"portscanner": "^2.1.1",
8080
"source-map-support": "^0.x",
81-
"teen_process": "^2.0.0",
82-
"validate.js": "^0.x"
81+
"teen_process": "^2.0.0"
8382
},
8483
"scripts": {
8584
"build": "npm run build:node && npm run build:server",

0 commit comments

Comments
 (0)