appium-android-driver 12.6.0 → 12.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,23 @@
1
1
  import {util} from '@appium/support';
2
2
  import {sleep, waitForCondition} from 'asyncbox';
3
3
  import _ from 'lodash';
4
+ import type {ADB} from 'appium-adb';
5
+ import type {Position, StringRecord} from '@appium/types';
6
+ import type {AndroidDriver, AndroidDriverCaps} from '../../driver';
7
+ import type {UnlockType, FastUnlockOptions} from '../types';
4
8
 
5
9
  export const PIN_UNLOCK = 'pin';
6
10
  export const PIN_UNLOCK_KEY_EVENT = 'pinWithKeyEvent';
7
11
  export const PASSWORD_UNLOCK = 'password';
8
12
  export const PATTERN_UNLOCK = 'pattern';
9
13
  export const FINGERPRINT_UNLOCK = 'fingerprint';
10
- const UNLOCK_TYPES = /** @type {const} */ ([
14
+ const UNLOCK_TYPES = [
11
15
  PIN_UNLOCK,
12
16
  PIN_UNLOCK_KEY_EVENT,
13
17
  PASSWORD_UNLOCK,
14
18
  PATTERN_UNLOCK,
15
19
  FINGERPRINT_UNLOCK,
16
- ]);
20
+ ] as const;
17
21
  export const KEYCODE_NUMPAD_ENTER = 66;
18
22
  export const UNLOCK_WAIT_TIME = 100;
19
23
  export const INPUT_KEYS_WAIT_TIME = 100;
@@ -21,40 +25,13 @@ const NUMBER_ZERO_KEYCODE = 7;
21
25
  const TOUCH_DELAY_MS = 1000;
22
26
 
23
27
  /**
28
+ * Converts an unlock type to a credential type string.
24
29
  *
25
- * @param {any} value
26
- * @returns {value is string}
30
+ * @param unlockType - The unlock type
31
+ * @returns The credential type string
32
+ * @throws {Error} If the unlock type is not known
27
33
  */
28
- function isNonEmptyString(value) {
29
- return typeof value === 'string' && value !== '';
30
- }
31
-
32
- /**
33
- * Wait for the display to be unlocked.
34
- * Some devices automatically accept typed 'pin' and 'password' code
35
- * without pressing the Enter key. But some devices need it.
36
- * This method waits a few seconds first for such automatic acceptance case.
37
- * If the device is still locked, then this method will try to send
38
- * the enter key code.
39
- *
40
- * @param {import('appium-adb').ADB} adb The instance of ADB
41
- */
42
- async function waitForUnlock(adb) {
43
- await sleep(UNLOCK_WAIT_TIME);
44
- if (!(await adb.isScreenLocked())) {
45
- return;
46
- }
47
-
48
- await adb.keyevent(KEYCODE_NUMPAD_ENTER);
49
- await sleep(UNLOCK_WAIT_TIME);
50
- }
51
-
52
- /**
53
- *
54
- * @param {import('../types').UnlockType} unlockType
55
- * @returns {string}
56
- */
57
- export function toCredentialType(unlockType) {
34
+ export function toCredentialType(unlockType: UnlockType): string {
58
35
  const result = {
59
36
  [PIN_UNLOCK]: 'pin',
60
37
  [PIN_UNLOCK_KEY_EVENT]: 'pin',
@@ -68,11 +45,13 @@ export function toCredentialType(unlockType) {
68
45
  }
69
46
 
70
47
  /**
71
- * @template {AndroidDriverCaps} T
72
- * @param {T} caps
73
- * @returns {T}
48
+ * Validates unlock capabilities and returns them if valid.
49
+ *
50
+ * @param caps - The capabilities to validate
51
+ * @returns The validated capabilities
52
+ * @throws {Error} If the capabilities are invalid
74
53
  */
75
- export function validateUnlockCapabilities(caps) {
54
+ export function validateUnlockCapabilities<T extends AndroidDriverCaps>(caps: T): T {
76
55
  const {unlockKey, unlockType} = caps ?? {};
77
56
  if (!isNonEmptyString(unlockType)) {
78
57
  throw new Error('A non-empty unlock key value must be provided');
@@ -111,10 +90,11 @@ export function validateUnlockCapabilities(caps) {
111
90
  }
112
91
 
113
92
  /**
114
- * @this {AndroidDriver}
115
- * @param {import('../types').FastUnlockOptions} opts
93
+ * Performs a fast unlock using ADB commands.
94
+ *
95
+ * @param opts - Fast unlock options with credential and credential type
116
96
  */
117
- export async function fastUnlock(opts) {
97
+ export async function fastUnlock(this: AndroidDriver, opts: FastUnlockOptions): Promise<void> {
118
98
  const {credential, credentialType} = opts;
119
99
  this.log.info(`Unlocking the device via ADB using ${credentialType} credential '${credential}'`);
120
100
  const wasLockEnabled = await this.adb.isLockEnabled();
@@ -136,39 +116,47 @@ export async function fastUnlock(opts) {
136
116
  }
137
117
 
138
118
  /**
119
+ * Encodes a password by replacing spaces with %s.
139
120
  *
140
- * @param {string} key
141
- * @returns {string}
121
+ * @param key - The password key
122
+ * @returns The encoded password
142
123
  */
143
- export function encodePassword(key) {
124
+ export function encodePassword(key: string): string {
144
125
  return `${key}`.replace(/\s/gi, '%s');
145
126
  }
146
127
 
147
128
  /**
129
+ * Converts a string key to an array of characters.
148
130
  *
149
- * @param {string} key
150
- * @returns {string[]}
131
+ * @param key - The key string
132
+ * @returns An array of characters
151
133
  */
152
- export function stringKeyToArr(key) {
134
+ export function stringKeyToArr(key: string): string[] {
153
135
  return `${key}`.trim().replace(/\s+/g, '').split(/\s*/);
154
136
  }
155
137
 
156
138
  /**
157
- * @this {AndroidDriver}
158
- * @param {AndroidDriverCaps} capabilities
159
- * @returns {Promise<void>}
139
+ * Unlocks the device using fingerprint.
140
+ *
141
+ * @param capabilities - Driver capabilities containing unlockKey
160
142
  */
161
- export async function fingerprintUnlock(capabilities) {
143
+ export async function fingerprintUnlock(
144
+ this: AndroidDriver,
145
+ capabilities: AndroidDriverCaps,
146
+ ): Promise<void> {
162
147
  await this.adb.fingerprint(String(capabilities.unlockKey));
163
148
  await sleep(UNLOCK_WAIT_TIME);
164
149
  }
165
150
 
166
151
  /**
167
- * @this {AndroidDriver}
168
- * @param {AndroidDriverCaps} capabilities
169
- * @returns {Promise<void>}
152
+ * Unlocks the device using PIN by clicking on-screen buttons.
153
+ *
154
+ * @param capabilities - Driver capabilities containing unlockKey
170
155
  */
171
- export async function pinUnlock(capabilities) {
156
+ export async function pinUnlock(
157
+ this: AndroidDriver,
158
+ capabilities: AndroidDriverCaps,
159
+ ): Promise<void> {
172
160
  this.log.info(`Trying to unlock device using pin ${capabilities.unlockKey}`);
173
161
  await this.adb.dismissKeyguard();
174
162
  const keys = stringKeyToArr(String(capabilities.unlockKey));
@@ -177,10 +165,12 @@ export async function pinUnlock(capabilities) {
177
165
  // fallback to pin with key event
178
166
  return await pinUnlockWithKeyEvent.bind(this)(capabilities);
179
167
  }
180
- const pins = {};
168
+ const pins: Record<string, any> = {};
181
169
  for (const el of els) {
182
170
  const text = await this.getAttribute('text', util.unwrapElement(el));
183
- pins[text] = el;
171
+ if (text) {
172
+ pins[text] = el;
173
+ }
184
174
  }
185
175
  for (const pin of keys) {
186
176
  const el = pins[pin];
@@ -190,11 +180,14 @@ export async function pinUnlock(capabilities) {
190
180
  }
191
181
 
192
182
  /**
193
- * @this {AndroidDriver}
194
- * @param {AndroidDriverCaps} capabilities
195
- * @returns {Promise<void>}
183
+ * Unlocks the device using PIN by sending key events.
184
+ *
185
+ * @param capabilities - Driver capabilities containing unlockKey
196
186
  */
197
- export async function pinUnlockWithKeyEvent(capabilities) {
187
+ export async function pinUnlockWithKeyEvent(
188
+ this: AndroidDriver,
189
+ capabilities: AndroidDriverCaps,
190
+ ): Promise<void> {
198
191
  this.log.info(`Trying to unlock device using pin with keycode ${capabilities.unlockKey}`);
199
192
  await this.adb.dismissKeyguard();
200
193
  const keys = stringKeyToArr(String(capabilities.unlockKey));
@@ -210,11 +203,14 @@ export async function pinUnlockWithKeyEvent(capabilities) {
210
203
  }
211
204
 
212
205
  /**
213
- * @this {AndroidDriver}
214
- * @param {AndroidDriverCaps} capabilities
215
- * @returns {Promise<void>}
206
+ * Unlocks the device using password.
207
+ *
208
+ * @param capabilities - Driver capabilities containing unlockKey
216
209
  */
217
- export async function passwordUnlock(capabilities) {
210
+ export async function passwordUnlock(
211
+ this: AndroidDriver,
212
+ capabilities: AndroidDriverCaps,
213
+ ): Promise<void> {
218
214
  const {unlockKey} = capabilities;
219
215
  this.log.info(`Trying to unlock device using password ${unlockKey}`);
220
216
  await this.adb.dismissKeyguard();
@@ -230,13 +226,14 @@ export async function passwordUnlock(capabilities) {
230
226
  }
231
227
 
232
228
  /**
229
+ * Calculates the position of a pattern key based on the initial position and piece size.
233
230
  *
234
- * @param {number} key
235
- * @param {import('@appium/types').Position} initPos
236
- * @param {number} piece
237
- * @returns {import('@appium/types').Position}
231
+ * @param key - The pattern key number (1-9)
232
+ * @param initPos - The initial position of the pattern view
233
+ * @param piece - The size of each pattern piece
234
+ * @returns The calculated position for the key
238
235
  */
239
- export function getPatternKeyPosition(key, initPos, piece) {
236
+ export function getPatternKeyPosition(key: number, initPos: Position, piece: number): Position {
240
237
  /*
241
238
  How the math works:
242
239
  We have 9 buttons divided in 3 columns and 3 rows inside the lockPatternView,
@@ -245,8 +242,9 @@ export function getPatternKeyPosition(key, initPos, piece) {
245
242
  */
246
243
  const cols = 3;
247
244
  const pins = 9;
248
- const xPos = (key, x, piece) => Math.round(x + (key % cols || cols) * piece - piece / 2);
249
- const yPos = (key, y, piece) =>
245
+ const xPos = (key: number, x: number, piece: number) =>
246
+ Math.round(x + (key % cols || cols) * piece - piece / 2);
247
+ const yPos = (key: number, y: number, piece: number) =>
250
248
  Math.round(y + (Math.ceil((key % pins || pins) / cols) * piece - piece / 2));
251
249
  return {
252
250
  x: xPos(key, initPos.x, piece),
@@ -255,19 +253,22 @@ export function getPatternKeyPosition(key, initPos, piece) {
255
253
  }
256
254
 
257
255
  /**
258
- * @param {string[]|number[]} keys
259
- * @param {import('@appium/types').Position} initPos
260
- * @param {number} piece
261
- * @returns {import('@appium/types').StringRecord[]}
256
+ * Generates pointer actions for pattern unlock gesture.
257
+ *
258
+ * @param keys - Array of pattern keys (string or number)
259
+ * @param initPos - The initial position of the pattern view
260
+ * @param piece - The size of each pattern piece
261
+ * @returns An array of W3C action objects for pattern unlock
262
262
  */
263
- export function getPatternActions(keys, initPos, piece) {
264
- /** @type {import('@appium/types').StringRecord[]} */
263
+ export function getPatternActions(
264
+ keys: string[] | number[],
265
+ initPos: Position,
266
+ piece: number,
267
+ ): StringRecord[] {
265
268
  // https://www.w3.org/TR/webdriver2/#actions
266
- const pointerActions = [];
267
- /** @type {number[]} */
269
+ const pointerActions: StringRecord[] = [];
268
270
  const intKeys = keys.map((key) => (_.isString(key) ? _.parseInt(key) : key));
269
- /** @type {import('@appium/types').Position|undefined} */
270
- let lastPos;
271
+ let lastPos: Position | undefined;
271
272
  for (const key of intKeys) {
272
273
  const keyPos = getPatternKeyPosition(key, initPos, piece);
273
274
  if (!lastPos) {
@@ -307,26 +308,30 @@ export function getPatternActions(keys, initPos, piece) {
307
308
  type: 'pointerMove',
308
309
  duration: TOUCH_DELAY_MS,
309
310
  x: moveTo.x + lastPos.x,
310
- y: moveTo.y + lastPos.y
311
+ y: moveTo.y + lastPos.y,
311
312
  });
312
313
  lastPos = keyPos;
313
314
  }
314
315
  pointerActions.push({type: 'pointerUp', button: 0});
315
- return [{
316
- type: 'pointer',
317
- id: 'patternUnlock',
318
- parameters: {
319
- pointerType: 'touch',
316
+ return [
317
+ {
318
+ type: 'pointer',
319
+ id: 'patternUnlock',
320
+ parameters: {
321
+ pointerType: 'touch',
322
+ },
323
+ actions: pointerActions,
320
324
  },
321
- actions: pointerActions,
322
- }];
325
+ ];
323
326
  }
324
327
 
325
328
  /**
326
- * @this {AndroidDriver}
327
- * @param {number?} [timeoutMs=null]
329
+ * Verifies that the device has been unlocked.
330
+ *
331
+ * @param timeoutMs - Optional timeout in milliseconds (default: 2000)
332
+ * @throws {Error} If the device fails to unlock within the timeout
328
333
  */
329
- export async function verifyUnlock(timeoutMs = null) {
334
+ export async function verifyUnlock(this: AndroidDriver, timeoutMs: number | null = null): Promise<void> {
330
335
  try {
331
336
  await waitForCondition(async () => !(await this.adb.isScreenLocked()), {
332
337
  waitMs: timeoutMs ?? 2000,
@@ -339,10 +344,14 @@ export async function verifyUnlock(timeoutMs = null) {
339
344
  }
340
345
 
341
346
  /**
342
- * @this {AndroidDriver}
343
- * @param {AndroidDriverCaps} capabilities
347
+ * Unlocks the device using pattern gesture.
348
+ *
349
+ * @param capabilities - Driver capabilities containing unlockKey
344
350
  */
345
- export async function patternUnlock(capabilities) {
351
+ export async function patternUnlock(
352
+ this: AndroidDriver,
353
+ capabilities: AndroidDriverCaps,
354
+ ): Promise<void> {
346
355
  const {unlockKey} = capabilities;
347
356
  this.log.info(`Trying to unlock device using pattern ${unlockKey}`);
348
357
  await this.adb.dismissKeyguard();
@@ -372,7 +381,33 @@ export async function patternUnlock(capabilities) {
372
381
  await sleep(UNLOCK_WAIT_TIME);
373
382
  }
374
383
 
384
+ // #region Private Functions
385
+
375
386
  /**
376
- * @typedef {import('@appium/types').Capabilities<import('../../constraints').AndroidDriverConstraints>} AndroidDriverCaps
377
- * @typedef {import('../../driver').AndroidDriver} AndroidDriver
387
+ * Type guard to check if a value is a non-empty string.
378
388
  */
389
+ function isNonEmptyString(value: any): value is string {
390
+ return typeof value === 'string' && value !== '';
391
+ }
392
+
393
+ /**
394
+ * Waits for the display to be unlocked.
395
+ * Some devices automatically accept typed 'pin' and 'password' code
396
+ * without pressing the Enter key. But some devices need it.
397
+ * This method waits a few seconds first for such automatic acceptance case.
398
+ * If the device is still locked, then this method will try to send
399
+ * the enter key code.
400
+ *
401
+ * @param adb - The instance of ADB
402
+ */
403
+ async function waitForUnlock(adb: ADB): Promise<void> {
404
+ await sleep(UNLOCK_WAIT_TIME);
405
+ if (!(await adb.isScreenLocked())) {
406
+ return;
407
+ }
408
+
409
+ await adb.keyevent(KEYCODE_NUMPAD_ENTER);
410
+ await sleep(UNLOCK_WAIT_TIME);
411
+ }
412
+
413
+ // #endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-android-driver",
3
- "version": "12.6.0",
3
+ "version": "12.6.2",
4
4
  "description": "Android UiAutomator and Chrome support for Appium",
5
5
  "keywords": [
6
6
  "appium",
@@ -62,7 +62,7 @@
62
62
  "moment-timezone": "^0.x",
63
63
  "portscanner": "^2.2.0",
64
64
  "semver": "^7.0.0",
65
- "teen_process": "^3.0.0",
65
+ "teen_process": "^4.0.7",
66
66
  "ws": "^8.0.0"
67
67
  },
68
68
  "devDependencies": {
@@ -1,137 +0,0 @@
1
- import B from 'bluebird';
2
- import {
3
- validateUnlockCapabilities,
4
- FINGERPRINT_UNLOCK,
5
- fastUnlock,
6
- PIN_UNLOCK,
7
- pinUnlock,
8
- PIN_UNLOCK_KEY_EVENT,
9
- pinUnlockWithKeyEvent,
10
- PASSWORD_UNLOCK,
11
- passwordUnlock,
12
- PATTERN_UNLOCK,
13
- patternUnlock,
14
- fingerprintUnlock,
15
- toCredentialType,
16
- verifyUnlock,
17
- } from './helpers';
18
- import _ from 'lodash';
19
-
20
- /**
21
- * @this {AndroidDriver}
22
- * @param {number} [seconds]
23
- * @returns {Promise<void>}
24
- */
25
- export async function lock(seconds) {
26
- await this.adb.lock();
27
- if (Number.isNaN(seconds)) {
28
- return;
29
- }
30
-
31
- const floatSeconds = parseFloat(String(seconds));
32
- if (floatSeconds <= 0) {
33
- return;
34
- }
35
- await B.delay(1000 * floatSeconds);
36
- await this.unlock();
37
- }
38
-
39
- /**
40
- * @this {AndroidDriver}
41
- * @returns {Promise<boolean>}
42
- */
43
- export async function isLocked() {
44
- return await this.adb.isScreenLocked();
45
- }
46
-
47
- /**
48
- * @this {AndroidDriver}
49
- * @returns {Promise<void>}
50
- */
51
- export async function unlock() {
52
- await unlockWithOptions.bind(this)();
53
- }
54
-
55
- /**
56
- * @this {AndroidDriver}
57
- * @param {string} [key] The unlock key. The value of this key depends on the actual unlock type and
58
- * could be a pin/password/pattern value or a biometric finger id.
59
- * If not provided then the corresponding value from session capabilities is
60
- * used.
61
- * @param {import('../types').UnlockType} [type] The unlock type.
62
- * If not provided then the corresponding value from session capabilities is used.
63
- * @param {string} [strategy] Setting it to 'uiautomator' will enforce the driver to avoid using special
64
- * ADB shortcuts in order to speed up the unlock procedure.
65
- * 'uiautomator' by default.
66
- * @param {number} [timeoutMs] The maximum time in milliseconds to wait until the screen gets unlocked
67
- * 2000ms byde fault.
68
- * @returns {Promise<void>}
69
- */
70
- export async function mobileUnlock(key, type, strategy, timeoutMs) {
71
- if (!key && !type) {
72
- await this.unlock();
73
- } else {
74
- await unlockWithOptions.bind(this)({
75
- unlockKey: key,
76
- unlockType: type,
77
- unlockStrategy: strategy,
78
- unlockSuccessTimeout: timeoutMs,
79
- });
80
- }
81
- }
82
-
83
- // #region Internal Helpers
84
-
85
- /**
86
- * @this {AndroidDriver}
87
- * @param {AndroidDriverCaps?} [caps=null]
88
- * @returns {Promise<void>}
89
- */
90
- export async function unlockWithOptions(caps = null) {
91
- if (!(await this.adb.isScreenLocked())) {
92
- this.log.info('Screen already unlocked, doing nothing');
93
- return;
94
- }
95
-
96
- const capabilities = caps ?? this.opts;
97
- this.log.debug('Screen is locked, trying to unlock');
98
- if (!capabilities.unlockType && !capabilities.unlockKey) {
99
- this.log.info(
100
- `Neither 'unlockType' nor 'unlockKey' capability is provided. ` +
101
- `Assuming the device is locked with a simple lock screen.`,
102
- );
103
- await this.adb.dismissKeyguard();
104
- return;
105
- }
106
-
107
- const {unlockType, unlockKey, unlockStrategy, unlockSuccessTimeout} =
108
- validateUnlockCapabilities(capabilities);
109
- if (
110
- unlockKey &&
111
- unlockType !== FINGERPRINT_UNLOCK &&
112
- (_.isNil(unlockStrategy) || _.toLower(unlockStrategy) === 'locksettings') &&
113
- (await this.adb.isLockManagementSupported())
114
- ) {
115
- await fastUnlock.bind(this)({
116
- credential: unlockKey,
117
- credentialType: toCredentialType(/** @type {import('../types').UnlockType} */ (unlockType)),
118
- });
119
- } else {
120
- const unlockMethod = {
121
- [PIN_UNLOCK]: pinUnlock,
122
- [PIN_UNLOCK_KEY_EVENT]: pinUnlockWithKeyEvent,
123
- [PASSWORD_UNLOCK]: passwordUnlock,
124
- [PATTERN_UNLOCK]: patternUnlock,
125
- [FINGERPRINT_UNLOCK]: fingerprintUnlock,
126
- }[unlockType];
127
- await unlockMethod.bind(this)(capabilities);
128
- }
129
- await verifyUnlock.bind(this)(unlockSuccessTimeout);
130
- }
131
-
132
- // #endregion
133
-
134
- /**
135
- * @typedef {import('@appium/types').Capabilities<import('../../constraints').AndroidDriverConstraints>} AndroidDriverCaps
136
- * @typedef {import('../../driver').AndroidDriver} AndroidDriver
137
- */