appium-android-driver 13.3.2 → 13.3.3
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.
- package/CHANGELOG.md +6 -0
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +13 -12
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/bidi/models.d.ts.map +1 -1
- package/build/lib/commands/bidi/models.js +1 -5
- package/build/lib/commands/bidi/models.js.map +1 -1
- package/build/lib/commands/bluetooth.d.ts.map +1 -1
- package/build/lib/commands/bluetooth.js +1 -5
- package/build/lib/commands/bluetooth.js.map +1 -1
- package/build/lib/commands/context/exports.d.ts.map +1 -1
- package/build/lib/commands/context/exports.js +11 -15
- package/build/lib/commands/context/exports.js.map +1 -1
- package/build/lib/commands/context/helpers.d.ts.map +1 -1
- package/build/lib/commands/context/helpers.js +43 -22
- package/build/lib/commands/context/helpers.js.map +1 -1
- package/build/lib/commands/device/common.d.ts.map +1 -1
- package/build/lib/commands/device/common.js +9 -8
- package/build/lib/commands/device/common.js.map +1 -1
- package/build/lib/commands/device/utils.d.ts.map +1 -1
- package/build/lib/commands/device/utils.js +2 -6
- package/build/lib/commands/device/utils.js.map +1 -1
- package/build/lib/commands/deviceidle.d.ts.map +1 -1
- package/build/lib/commands/deviceidle.js +2 -6
- package/build/lib/commands/deviceidle.js.map +1 -1
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +2 -6
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-actions.d.ts.map +1 -1
- package/build/lib/commands/file-actions.js +2 -3
- package/build/lib/commands/file-actions.js.map +1 -1
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +4 -7
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/geolocation.d.ts.map +1 -1
- package/build/lib/commands/geolocation.js +3 -4
- package/build/lib/commands/geolocation.js.map +1 -1
- package/build/lib/commands/image-injection.d.ts.map +1 -1
- package/build/lib/commands/image-injection.js +1 -2
- package/build/lib/commands/image-injection.js.map +1 -1
- package/build/lib/commands/intent.d.ts.map +1 -1
- package/build/lib/commands/intent.js +14 -18
- package/build/lib/commands/intent.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts.map +1 -1
- package/build/lib/commands/keyboard.js +6 -6
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/lock/exports.d.ts.map +1 -1
- package/build/lib/commands/lock/exports.js +1 -5
- package/build/lib/commands/lock/exports.js.map +1 -1
- package/build/lib/commands/lock/helpers.d.ts.map +1 -1
- package/build/lib/commands/lock/helpers.js +5 -9
- package/build/lib/commands/lock/helpers.js.map +1 -1
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +9 -9
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/media-projection.d.ts.map +1 -1
- package/build/lib/commands/media-projection.js +3 -4
- package/build/lib/commands/media-projection.js.map +1 -1
- package/build/lib/commands/network.d.ts.map +1 -1
- package/build/lib/commands/network.js +12 -16
- package/build/lib/commands/network.js.map +1 -1
- package/build/lib/commands/nfc.d.ts.map +1 -1
- package/build/lib/commands/nfc.js +1 -5
- package/build/lib/commands/nfc.js.map +1 -1
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +7 -12
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts.map +1 -1
- package/build/lib/commands/permissions.js +21 -23
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts.map +1 -1
- package/build/lib/commands/recordscreen.js +10 -11
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/resources.d.ts.map +1 -1
- package/build/lib/commands/resources.js +6 -10
- package/build/lib/commands/resources.js.map +1 -1
- package/build/lib/commands/shell.d.ts.map +1 -1
- package/build/lib/commands/shell.js +7 -6
- package/build/lib/commands/shell.js.map +1 -1
- package/build/lib/commands/streamscreen.d.ts.map +1 -1
- package/build/lib/commands/streamscreen.js +14 -15
- package/build/lib/commands/streamscreen.js.map +1 -1
- package/build/lib/commands/system-bars.d.ts.map +1 -1
- package/build/lib/commands/system-bars.js +19 -21
- package/build/lib/commands/system-bars.js.map +1 -1
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +2 -6
- package/build/lib/driver.js.map +1 -1
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +9 -12
- package/build/lib/utils.js.map +1 -1
- package/lib/commands/app-management.ts +19 -9
- package/lib/commands/bidi/models.ts +1 -2
- package/lib/commands/bluetooth.ts +1 -2
- package/lib/commands/context/exports.ts +12 -14
- package/lib/commands/context/helpers.ts +47 -22
- package/lib/commands/device/common.ts +10 -8
- package/lib/commands/device/utils.ts +2 -3
- package/lib/commands/deviceidle.ts +2 -3
- package/lib/commands/execute.ts +2 -3
- package/lib/commands/file-actions.ts +2 -3
- package/lib/commands/find.ts +4 -5
- package/lib/commands/geolocation.ts +3 -4
- package/lib/commands/image-injection.ts +1 -2
- package/lib/commands/intent.ts +14 -15
- package/lib/commands/keyboard.ts +6 -3
- package/lib/commands/lock/exports.ts +1 -2
- package/lib/commands/lock/helpers.ts +5 -6
- package/lib/commands/log.ts +9 -9
- package/lib/commands/media-projection.ts +3 -4
- package/lib/commands/network.ts +12 -13
- package/lib/commands/nfc.ts +1 -2
- package/lib/commands/performance.ts +7 -11
- package/lib/commands/permissions.ts +23 -20
- package/lib/commands/recordscreen.ts +10 -11
- package/lib/commands/resources.ts +7 -8
- package/lib/commands/shell.ts +7 -3
- package/lib/commands/streamscreen.ts +14 -16
- package/lib/commands/system-bars.ts +20 -20
- package/lib/driver.ts +2 -4
- package/lib/utils.ts +9 -9
- package/package.json +1 -3
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as semver from 'semver';
|
|
2
|
-
import _ from 'lodash';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import {setMockLocationApp} from '../geolocation';
|
|
5
4
|
import {SETTINGS_HELPER_ID} from 'io.appium.settings';
|
|
@@ -38,7 +37,7 @@ export async function getDeviceInfoFromCaps(this: AndroidDriver): Promise<ADBDev
|
|
|
38
37
|
|
|
39
38
|
// udid was given, lets try to init with that device
|
|
40
39
|
if (udid) {
|
|
41
|
-
if (!
|
|
40
|
+
if (!devices?.some((device) => device.udid === udid)) {
|
|
42
41
|
throw this.log.errorWithException(
|
|
43
42
|
`Device ${udid} was not in the list of connected devices`,
|
|
44
43
|
);
|
|
@@ -72,11 +71,13 @@ export async function getDeviceInfoFromCaps(this: AndroidDriver): Promise<ADBDev
|
|
|
72
71
|
const semverDO = deviceOS;
|
|
73
72
|
|
|
74
73
|
const bothVersionsCanBeCoerced = semver.valid(deviceOS) && semver.valid(platformVersion);
|
|
75
|
-
const bothVersionsAreStrings =
|
|
74
|
+
const bothVersionsAreStrings =
|
|
75
|
+
typeof deviceOS === 'string' && typeof platformVersion === 'string';
|
|
76
76
|
if (
|
|
77
77
|
(bothVersionsCanBeCoerced &&
|
|
78
78
|
(semverDO as semver.SemVer).version === (semverPV as semver.SemVer).version) ||
|
|
79
|
-
(bothVersionsAreStrings &&
|
|
79
|
+
(bothVersionsAreStrings &&
|
|
80
|
+
String(deviceOS)?.toLowerCase() === String(platformVersion)?.toLowerCase())
|
|
80
81
|
) {
|
|
81
82
|
// Got an exact match - proceed immediately
|
|
82
83
|
udid = device.udid;
|
|
@@ -91,18 +92,19 @@ export async function getDeviceInfoFromCaps(this: AndroidDriver): Promise<ADBDev
|
|
|
91
92
|
const dvMajor = (semverDO as semver.SemVer).major;
|
|
92
93
|
const dvMinor = (semverDO as semver.SemVer).minor;
|
|
93
94
|
if (
|
|
94
|
-
((!
|
|
95
|
+
((!String(this.opts.platformVersion)?.includes('.') && pvMajor === dvMajor) ||
|
|
95
96
|
(pvMajor === dvMajor && pvMinor === dvMinor)) &&
|
|
96
97
|
// Got a partial match - make sure we consider the most recent
|
|
97
98
|
// device version available on the host system
|
|
98
|
-
((partialMatchCandidate &&
|
|
99
|
+
((partialMatchCandidate &&
|
|
100
|
+
semver.gt(deviceOS, Object.values(partialMatchCandidate)[0])) ||
|
|
99
101
|
!partialMatchCandidate)
|
|
100
102
|
) {
|
|
101
103
|
partialMatchCandidate = {[device.udid]: deviceOS};
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
if (!udid && partialMatchCandidate) {
|
|
105
|
-
udid =
|
|
107
|
+
udid = Object.keys(partialMatchCandidate)[0];
|
|
106
108
|
adb.setDeviceId(udid);
|
|
107
109
|
}
|
|
108
110
|
|
|
@@ -241,7 +243,7 @@ export async function initDevice(this: AndroidDriver): Promise<void> {
|
|
|
241
243
|
if (!this.isEmulator()) {
|
|
242
244
|
setupPromises.push(
|
|
243
245
|
(async () => {
|
|
244
|
-
if (mockLocationApp ||
|
|
246
|
+
if (mockLocationApp || mockLocationApp === undefined) {
|
|
245
247
|
await setMockLocationApp.bind(this)(mockLocationApp || SETTINGS_HELPER_ID);
|
|
246
248
|
} else {
|
|
247
249
|
await this.mobileResetGeolocation();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {util} from '@appium/support';
|
|
3
2
|
import {ADB} from 'appium-adb';
|
|
4
3
|
import {retryInterval} from 'asyncbox';
|
|
@@ -39,7 +38,7 @@ export function ensureNetworkSpeed(this: AndroidDriver, networkSpeed: string): s
|
|
|
39
38
|
}
|
|
40
39
|
this.log.warn(
|
|
41
40
|
`Wrong network speed param '${networkSpeed}', using default: ${this.adb.NETWORK_SPEED.FULL}. ` +
|
|
42
|
-
`Supported values: ${
|
|
41
|
+
`Supported values: ${Object.values(this.adb.NETWORK_SPEED)}`,
|
|
43
42
|
);
|
|
44
43
|
return this.adb.NETWORK_SPEED.FULL;
|
|
45
44
|
}
|
|
@@ -53,7 +52,7 @@ export function prepareAvdArgs(this: AndroidDriver): string[] {
|
|
|
53
52
|
const {networkSpeed, isHeadless, avdArgs} = this.opts;
|
|
54
53
|
const result: string[] = [];
|
|
55
54
|
if (avdArgs) {
|
|
56
|
-
if (
|
|
55
|
+
if (Array.isArray(avdArgs)) {
|
|
57
56
|
result.push(...avdArgs);
|
|
58
57
|
} else {
|
|
59
58
|
result.push(...util.shellParse(`${avdArgs}`));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {errors} from 'appium/driver';
|
|
2
|
-
import _ from 'lodash';
|
|
3
2
|
import type {AndroidDriver} from '../driver';
|
|
4
3
|
|
|
5
4
|
const SUPPORTED_ACTIONS = ['whitelistAdd', 'whitelistRemove'] as const;
|
|
@@ -20,11 +19,11 @@ export async function mobileDeviceidle(
|
|
|
20
19
|
action: (typeof SUPPORTED_ACTIONS)[number],
|
|
21
20
|
packages?: string | string[],
|
|
22
21
|
): Promise<void> {
|
|
23
|
-
if (!(
|
|
22
|
+
if (!(typeof packages === 'string' || Array.isArray(packages))) {
|
|
24
23
|
throw new errors.InvalidArgumentError(`packages argument must be a string or an array`);
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
const packagesArr =
|
|
26
|
+
const packagesArr = Array.isArray(packages) ? packages : [packages];
|
|
28
27
|
const commonArgs = ['dumpsys', 'deviceidle', 'whitelist'];
|
|
29
28
|
let argsGenerator: (pkg: string) => string[];
|
|
30
29
|
switch (action) {
|
package/lib/commands/execute.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {errors, PROTOCOLS} from 'appium/driver';
|
|
3
2
|
import {util} from '@appium/support';
|
|
4
3
|
import type {StringRecord, Element} from '@appium/types';
|
|
@@ -23,7 +22,7 @@ export async function execute(
|
|
|
23
22
|
script: string,
|
|
24
23
|
args?: ExecuteMethodArgs,
|
|
25
24
|
): Promise<any> {
|
|
26
|
-
if (
|
|
25
|
+
if (script?.startsWith(EXECUTE_SCRIPT_PREFIX)) {
|
|
27
26
|
const formattedScript = script.trim().replace(/^mobile:\s*/, `${EXECUTE_SCRIPT_PREFIX} `);
|
|
28
27
|
const executeMethodArgs = preprocessExecuteMethodArgs(args);
|
|
29
28
|
return await this.executeMethod(formattedScript, [executeMethodArgs]);
|
|
@@ -48,7 +47,7 @@ export async function execute(
|
|
|
48
47
|
* @returns Preprocessed arguments as a StringRecord.
|
|
49
48
|
*/
|
|
50
49
|
function preprocessExecuteMethodArgs(args?: ExecuteMethodArgs): StringRecord {
|
|
51
|
-
const executeMethodArgs = (
|
|
50
|
+
const executeMethodArgs = (Array.isArray(args) ? args[0] : args) ?? ({} as StringRecord);
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
53
|
* Renames the deprecated `element` key to `elementId`. Historically,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {fs, util, zip, tempDir} from '@appium/support';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import {errors} from 'appium/driver';
|
|
@@ -98,7 +97,7 @@ export async function pushFile(
|
|
|
98
97
|
}
|
|
99
98
|
const localFile = await tempDir.path({prefix: 'appium', suffix: '.tmp'});
|
|
100
99
|
let base64String: string;
|
|
101
|
-
if (
|
|
100
|
+
if (Array.isArray(base64Data)) {
|
|
102
101
|
// some clients (ahem) java, send a byte array encoding utf8 characters
|
|
103
102
|
// instead of a string, which would be infinitely better!
|
|
104
103
|
base64String = Buffer.from(base64Data).toString('utf8');
|
|
@@ -327,7 +326,7 @@ function createFSTests(adb: ADB) {
|
|
|
327
326
|
const checkCmd = `[ -${op} '${escapePath(p)}' ] && echo ${passFlag}`;
|
|
328
327
|
const fullCmd = runAs ? `run-as ${runAs} ${checkCmd}` : checkCmd;
|
|
329
328
|
try {
|
|
330
|
-
return
|
|
329
|
+
return (await adb.shell([fullCmd])).includes(passFlag);
|
|
331
330
|
} catch {
|
|
332
331
|
return false;
|
|
333
332
|
}
|
package/lib/commands/find.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
|
|
3
|
-
import _ from 'lodash';
|
|
4
1
|
import {errors, isErrorType} from 'appium/driver';
|
|
5
2
|
import type {AndroidDriver} from '../driver';
|
|
6
3
|
import type {Element} from '@appium/types';
|
|
7
4
|
import type {FindElementOpts} from './types';
|
|
5
|
+
import {util} from '@appium/support';
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* @param strategy The element location strategy to use (e.g., 'id', 'xpath', 'class name').
|
|
@@ -70,7 +68,7 @@ export async function findElOrEls(
|
|
|
70
68
|
}
|
|
71
69
|
|
|
72
70
|
// we want to return false if we want to potentially try again
|
|
73
|
-
return !
|
|
71
|
+
return !util.isEmpty(element);
|
|
74
72
|
};
|
|
75
73
|
|
|
76
74
|
try {
|
|
@@ -89,7 +87,7 @@ export async function findElOrEls(
|
|
|
89
87
|
if (mult) {
|
|
90
88
|
return element as Element[];
|
|
91
89
|
}
|
|
92
|
-
if (
|
|
90
|
+
if (util.isEmpty(element)) {
|
|
93
91
|
throw new errors.NoSuchElementError();
|
|
94
92
|
}
|
|
95
93
|
return element as Element;
|
|
@@ -109,5 +107,6 @@ export async function doFindElementOrEls(
|
|
|
109
107
|
this: AndroidDriver,
|
|
110
108
|
params: FindElementOpts,
|
|
111
109
|
): Promise<Element | Element[]> {
|
|
110
|
+
void params;
|
|
112
111
|
throw new errors.NotImplementedError('Not implemented');
|
|
113
112
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {asyncmap} from 'asyncbox';
|
|
3
2
|
import {fs, tempDir} from '@appium/support';
|
|
4
3
|
import path from 'node:path';
|
|
@@ -208,10 +207,10 @@ async function resetMockLocation(this: AndroidDriver): Promise<void> {
|
|
|
208
207
|
pkgIds = JSON.parse(await this.adb.shell(['cat', MOCK_APP_IDS_STORE]));
|
|
209
208
|
} catch {}
|
|
210
209
|
}
|
|
211
|
-
const thirdPartyPkgIds = await thirdPartyPkgIdsPromise;
|
|
210
|
+
const thirdPartyPkgIds = new Set(await thirdPartyPkgIdsPromise);
|
|
212
211
|
// Only include currently installed packages
|
|
213
|
-
const resultPkgs =
|
|
214
|
-
if (
|
|
212
|
+
const resultPkgs = pkgIds.filter((pkg) => thirdPartyPkgIds.has(pkg));
|
|
213
|
+
if (resultPkgs.length <= 1) {
|
|
215
214
|
await this.adb.shell([
|
|
216
215
|
'appops',
|
|
217
216
|
'set',
|
|
@@ -2,7 +2,6 @@ import {errors} from 'appium/driver';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import {fs, tempDir} from '@appium/support';
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
|
-
import _ from 'lodash';
|
|
6
5
|
import type {AndroidDriver} from '../driver';
|
|
7
6
|
|
|
8
7
|
const EMULATOR_RESOURCES_ROOT = ['emulator', 'resources'];
|
|
@@ -92,7 +91,7 @@ export async function mobileInjectEmulatorCameraImage(
|
|
|
92
91
|
throw new Error('The image injection feature is only available on emulators');
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
if (
|
|
94
|
+
if (typeof payload !== 'string' || payload.length <= PNG_MAGIC_LENGTH) {
|
|
96
95
|
throw new errors.InvalidArgumentError(
|
|
97
96
|
`You must provide a valid base64-encoded .PNG data as the 'payload' argument`,
|
|
98
97
|
);
|
package/lib/commands/intent.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {errors} from 'appium/driver';
|
|
3
2
|
import {util} from '@appium/support';
|
|
4
3
|
import type {AndroidDriver} from '../driver';
|
|
@@ -126,7 +125,7 @@ export async function mobileStartActivity(
|
|
|
126
125
|
flags?: string,
|
|
127
126
|
): Promise<string> {
|
|
128
127
|
const cmd = ['am', 'start-activity'];
|
|
129
|
-
if (
|
|
128
|
+
if (util.hasValue(user)) {
|
|
130
129
|
cmd.push('--user', String(user));
|
|
131
130
|
}
|
|
132
131
|
if (wait) {
|
|
@@ -135,13 +134,13 @@ export async function mobileStartActivity(
|
|
|
135
134
|
if (stop) {
|
|
136
135
|
cmd.push('-S');
|
|
137
136
|
}
|
|
138
|
-
if (
|
|
137
|
+
if (util.hasValue(windowingMode)) {
|
|
139
138
|
cmd.push('--windowingMode', String(windowingMode));
|
|
140
139
|
}
|
|
141
|
-
if (
|
|
140
|
+
if (util.hasValue(activityType)) {
|
|
142
141
|
cmd.push('--activityType', String(activityType));
|
|
143
142
|
}
|
|
144
|
-
if (
|
|
143
|
+
if (util.hasValue(display)) {
|
|
145
144
|
cmd.push('--display', String(display));
|
|
146
145
|
}
|
|
147
146
|
cmd.push(
|
|
@@ -200,7 +199,7 @@ export async function mobileBroadcast(
|
|
|
200
199
|
flags?: string,
|
|
201
200
|
): Promise<string> {
|
|
202
201
|
const cmd = ['am', 'broadcast'];
|
|
203
|
-
if (
|
|
202
|
+
if (util.hasValue(user)) {
|
|
204
203
|
cmd.push('--user', String(user));
|
|
205
204
|
}
|
|
206
205
|
if (receiverPermission) {
|
|
@@ -265,7 +264,7 @@ export async function mobileStartService(
|
|
|
265
264
|
): Promise<string> {
|
|
266
265
|
const cmd = ['am'];
|
|
267
266
|
cmd.push(foreground ? 'start-foreground-service' : 'start-service');
|
|
268
|
-
if (
|
|
267
|
+
if (util.hasValue(user)) {
|
|
269
268
|
cmd.push('--user', String(user));
|
|
270
269
|
}
|
|
271
270
|
cmd.push(
|
|
@@ -320,7 +319,7 @@ export async function mobileStopService(
|
|
|
320
319
|
flags?: string,
|
|
321
320
|
): Promise<string> {
|
|
322
321
|
const cmd = ['am', 'stop-service'];
|
|
323
|
-
if (
|
|
322
|
+
if (util.hasValue(user)) {
|
|
324
323
|
cmd.push('--user', String(user));
|
|
325
324
|
}
|
|
326
325
|
cmd.push(
|
|
@@ -366,12 +365,12 @@ function parseIntentSpec(opts: IntentOpts = {}): string[] {
|
|
|
366
365
|
if (mimeType) {
|
|
367
366
|
resultArgs.push('-t', mimeType);
|
|
368
367
|
}
|
|
369
|
-
if (
|
|
368
|
+
if (util.hasValue(identifier)) {
|
|
370
369
|
resultArgs.push('-i', identifier);
|
|
371
370
|
}
|
|
372
371
|
if (categories) {
|
|
373
|
-
if (
|
|
374
|
-
resultArgs.push(...
|
|
372
|
+
if (Array.isArray(categories)) {
|
|
373
|
+
resultArgs.push(...categories.flatMap((cName) => ['-c', cName]));
|
|
375
374
|
} else {
|
|
376
375
|
resultArgs.push('-c', categories);
|
|
377
376
|
}
|
|
@@ -383,11 +382,11 @@ function parseIntentSpec(opts: IntentOpts = {}): string[] {
|
|
|
383
382
|
resultArgs.push('-p', opts.package);
|
|
384
383
|
}
|
|
385
384
|
if (extras) {
|
|
386
|
-
if (!
|
|
385
|
+
if (!Array.isArray(extras)) {
|
|
387
386
|
throw new errors.InvalidArgumentError(`'extras' must be an array`);
|
|
388
387
|
}
|
|
389
388
|
for (const item of extras) {
|
|
390
|
-
if (!
|
|
389
|
+
if (!Array.isArray(item)) {
|
|
391
390
|
throw new errors.InvalidArgumentError(`Extra argument '${item}' must be an array`);
|
|
392
391
|
}
|
|
393
392
|
const [type, key, value] = item;
|
|
@@ -397,14 +396,14 @@ function parseIntentSpec(opts: IntentOpts = {}): string[] {
|
|
|
397
396
|
`Supported intent argument types are: ${SUPPORTED_EXTRA_TYPES.join(', ')}`,
|
|
398
397
|
);
|
|
399
398
|
}
|
|
400
|
-
if (
|
|
399
|
+
if (util.isEmpty(key) || (typeof key === 'string' && key.trim() === '')) {
|
|
401
400
|
throw new errors.InvalidArgumentError(
|
|
402
401
|
`Extra argument's key in '${JSON.stringify(item)}' must be a valid string identifier`,
|
|
403
402
|
);
|
|
404
403
|
}
|
|
405
404
|
if (type === NO_VALUE_ARG_TYPE) {
|
|
406
405
|
resultArgs.push(`--e${type}`, key);
|
|
407
|
-
} else if (
|
|
406
|
+
} else if (value === undefined) {
|
|
408
407
|
throw new errors.InvalidArgumentError(
|
|
409
408
|
`Intent argument type '${type}' in '${JSON.stringify(item)}' requires a ` +
|
|
410
409
|
`valid value to be provided`,
|
package/lib/commands/keyboard.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
import _ from 'lodash';
|
|
3
1
|
import {errors} from 'appium/driver';
|
|
4
2
|
import {UNICODE_IME, EMPTY_IME} from 'io.appium.settings';
|
|
5
3
|
import type {AndroidDriver} from '../driver';
|
|
@@ -32,7 +30,7 @@ export async function isKeyboardShown(this: AndroidDriver): Promise<boolean> {
|
|
|
32
30
|
*/
|
|
33
31
|
export async function keys(this: AndroidDriver, keys: string | string[]): Promise<void> {
|
|
34
32
|
// Protocol sends an array; rethink approach
|
|
35
|
-
const keysStr =
|
|
33
|
+
const keysStr = Array.isArray(keys) ? keys.join('') : keys;
|
|
36
34
|
await this.doSendKeys({
|
|
37
35
|
text: keysStr,
|
|
38
36
|
replace: false,
|
|
@@ -47,6 +45,7 @@ export async function keys(this: AndroidDriver, keys: string | string[]): Promis
|
|
|
47
45
|
* @throws {errors.NotImplementedError} This method is not implemented.
|
|
48
46
|
*/
|
|
49
47
|
export async function doSendKeys(this: AndroidDriver, params: SendKeysOpts): Promise<void> {
|
|
48
|
+
void params;
|
|
50
49
|
throw new errors.NotImplementedError('Not implemented');
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -81,6 +80,8 @@ export async function pressKeyCode(
|
|
|
81
80
|
keycode: string | number,
|
|
82
81
|
metastate?: number,
|
|
83
82
|
): Promise<void> {
|
|
83
|
+
void keycode;
|
|
84
|
+
void metastate;
|
|
84
85
|
throw new errors.NotImplementedError('Not implemented');
|
|
85
86
|
}
|
|
86
87
|
|
|
@@ -97,6 +98,8 @@ export async function longPressKeyCode(
|
|
|
97
98
|
keycode: string | number,
|
|
98
99
|
metastate?: number,
|
|
99
100
|
): Promise<void> {
|
|
101
|
+
void keycode;
|
|
102
|
+
void metastate;
|
|
100
103
|
throw new errors.NotImplementedError('Not implemented');
|
|
101
104
|
}
|
|
102
105
|
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
toCredentialType,
|
|
16
16
|
verifyUnlock,
|
|
17
17
|
} from './helpers';
|
|
18
|
-
import _ from 'lodash';
|
|
19
18
|
import type {AndroidDriver, AndroidDriverCaps} from '../../driver';
|
|
20
19
|
import type {UnlockType} from '../types';
|
|
21
20
|
|
|
@@ -119,7 +118,7 @@ export async function unlockWithOptions(
|
|
|
119
118
|
if (
|
|
120
119
|
unlockKey &&
|
|
121
120
|
unlockType !== FINGERPRINT_UNLOCK &&
|
|
122
|
-
(
|
|
121
|
+
(unlockStrategy == null || String(unlockStrategy)?.toLowerCase() === 'locksettings') &&
|
|
123
122
|
(await this.adb.isLockManagementSupported())
|
|
124
123
|
) {
|
|
125
124
|
await fastUnlock.bind(this)({
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {util} from '@appium/support';
|
|
2
2
|
import {sleep, waitForCondition} from 'asyncbox';
|
|
3
|
-
import _ from 'lodash';
|
|
4
3
|
import type {ADB} from 'appium-adb';
|
|
5
4
|
import type {Position, StringRecord} from '@appium/types';
|
|
6
5
|
import type {AndroidDriver, AndroidDriverCaps} from '../../driver';
|
|
@@ -58,16 +57,16 @@ export function validateUnlockCapabilities<T extends AndroidDriverCaps>(caps: T)
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
if ([PIN_UNLOCK, PIN_UNLOCK_KEY_EVENT, FINGERPRINT_UNLOCK].includes(unlockType)) {
|
|
61
|
-
if (!/^[0-9]+$/.test(
|
|
60
|
+
if (!/^[0-9]+$/.test(String(unlockKey)?.trim())) {
|
|
62
61
|
throw new Error(`Unlock key value '${unlockKey}' must only consist of digits`);
|
|
63
62
|
}
|
|
64
63
|
} else if (unlockType === PATTERN_UNLOCK) {
|
|
65
|
-
if (!/^[1-9]{2,9}$/.test(
|
|
64
|
+
if (!/^[1-9]{2,9}$/.test(String(unlockKey)?.trim())) {
|
|
66
65
|
throw new Error(
|
|
67
66
|
`Unlock key value '${unlockKey}' must only include from two to nine digits in range 1..9`,
|
|
68
67
|
);
|
|
69
68
|
}
|
|
70
|
-
if (/([1-9]).*?\1/.test(
|
|
69
|
+
if (/([1-9]).*?\1/.test(String(unlockKey)?.trim())) {
|
|
71
70
|
throw new Error(
|
|
72
71
|
`Unlock key value '${unlockKey}' must define a valid pattern where repeats are not allowed`,
|
|
73
72
|
);
|
|
@@ -161,7 +160,7 @@ export async function pinUnlock(
|
|
|
161
160
|
await this.adb.dismissKeyguard();
|
|
162
161
|
const keys = stringKeyToArr(String(capabilities.unlockKey));
|
|
163
162
|
const els = await this.findElOrEls('id', 'com.android.systemui:id/digit_text', true);
|
|
164
|
-
if (
|
|
163
|
+
if (util.isEmpty(els)) {
|
|
165
164
|
// fallback to pin with key event
|
|
166
165
|
return await pinUnlockWithKeyEvent.bind(this)(capabilities);
|
|
167
166
|
}
|
|
@@ -267,7 +266,7 @@ export function getPatternActions(
|
|
|
267
266
|
): StringRecord[] {
|
|
268
267
|
// https://www.w3.org/TR/webdriver2/#actions
|
|
269
268
|
const pointerActions: StringRecord[] = [];
|
|
270
|
-
const intKeys = keys.map((key) => (
|
|
269
|
+
const intKeys = keys.map((key) => (typeof key === 'string' ? parseInt(key, 10) : key));
|
|
271
270
|
let lastPos: Position | undefined;
|
|
272
271
|
for (const key of intKeys) {
|
|
273
272
|
const keyPos = getPatternKeyPosition(key, initPos, piece);
|
package/lib/commands/log.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {DEFAULT_WS_PATHNAME_PREFIX, BaseDriver} from 'appium/driver';
|
|
2
|
-
import _ from 'lodash';
|
|
3
2
|
import os from 'node:os';
|
|
4
3
|
import WebSocket from 'ws';
|
|
5
4
|
import type {AppiumServer, WSServer} from '@appium/types';
|
|
@@ -16,6 +15,7 @@ import {NATIVE_WIN} from './context/helpers';
|
|
|
16
15
|
import {BIDI_EVENT_NAME} from './bidi/constants';
|
|
17
16
|
import {makeLogEntryAddedEvent} from './bidi/models';
|
|
18
17
|
import type {AndroidDriver} from '../driver';
|
|
18
|
+
import {util} from '@appium/support';
|
|
19
19
|
|
|
20
20
|
export const supportedLogTypes = {
|
|
21
21
|
logcat: {
|
|
@@ -52,7 +52,7 @@ export const supportedLogTypes = {
|
|
|
52
52
|
export async function mobileStartLogsBroadcast(this: AndroidDriver): Promise<void> {
|
|
53
53
|
const server = this.server as AppiumServer;
|
|
54
54
|
const pathname = WEBSOCKET_ENDPOINT(this.sessionId as string);
|
|
55
|
-
if (!
|
|
55
|
+
if (!util.isEmpty(await server.getWebSocketHandlers(pathname))) {
|
|
56
56
|
this.log.debug(`The logcat broadcasting web socket server is already listening at ${pathname}`);
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
@@ -67,7 +67,7 @@ export async function mobileStartLogsBroadcast(this: AndroidDriver): Promise<voi
|
|
|
67
67
|
});
|
|
68
68
|
wss.on('connection', (ws, req) => {
|
|
69
69
|
if (req) {
|
|
70
|
-
const remoteIp =
|
|
70
|
+
const remoteIp = util.isEmpty(req.headers['x-forwarded-for'])
|
|
71
71
|
? req.socket.remoteAddress
|
|
72
72
|
: req.headers['x-forwarded-for'];
|
|
73
73
|
this.log.debug(`Established a new logcat listener web socket connection from ${remoteIp}`);
|
|
@@ -75,7 +75,7 @@ export async function mobileStartLogsBroadcast(this: AndroidDriver): Promise<voi
|
|
|
75
75
|
this.log.debug('Established a new logcat listener web socket connection');
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
if (
|
|
78
|
+
if (!this._logcatWebsocketListener) {
|
|
79
79
|
this._logcatWebsocketListener = (logRecord: LogEntry) => {
|
|
80
80
|
if (ws?.readyState === WebSocket.OPEN) {
|
|
81
81
|
ws.send(logRecord.message);
|
|
@@ -85,7 +85,7 @@ export async function mobileStartLogsBroadcast(this: AndroidDriver): Promise<voi
|
|
|
85
85
|
this.adb.setLogcatListener(this._logcatWebsocketListener);
|
|
86
86
|
|
|
87
87
|
ws.on('close', (code, reason) => {
|
|
88
|
-
if (
|
|
88
|
+
if (this._logcatWebsocketListener) {
|
|
89
89
|
try {
|
|
90
90
|
this.adb.removeLogcatListener(this._logcatWebsocketListener);
|
|
91
91
|
} catch {}
|
|
@@ -93,10 +93,10 @@ export async function mobileStartLogsBroadcast(this: AndroidDriver): Promise<voi
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
let closeMsg = 'Logcat listener web socket is closed.';
|
|
96
|
-
if (!
|
|
96
|
+
if (!util.isEmpty(code)) {
|
|
97
97
|
closeMsg += ` Code: ${code}.`;
|
|
98
98
|
}
|
|
99
|
-
if (!
|
|
99
|
+
if (!util.isEmpty(reason)) {
|
|
100
100
|
closeMsg += ` Reason: ${reason.toString()}.`;
|
|
101
101
|
}
|
|
102
102
|
this.log.debug(closeMsg);
|
|
@@ -114,7 +114,7 @@ export async function mobileStartLogsBroadcast(this: AndroidDriver): Promise<voi
|
|
|
114
114
|
export async function mobileStopLogsBroadcast(this: AndroidDriver): Promise<void> {
|
|
115
115
|
const pathname = WEBSOCKET_ENDPOINT(this.sessionId as string);
|
|
116
116
|
const server = this.server as AppiumServer;
|
|
117
|
-
if (
|
|
117
|
+
if (util.isEmpty(await server.getWebSocketHandlers(pathname))) {
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -174,7 +174,7 @@ export function assignBiDiLogListener<EE extends EventEmitter>(
|
|
|
174
174
|
* @returns Promise that resolves to the logs for the specified type.
|
|
175
175
|
*/
|
|
176
176
|
export async function getLog(this: AndroidDriver, logType: string): Promise<any> {
|
|
177
|
-
if (this.isWebContext() && !
|
|
177
|
+
if (this.isWebContext() && !Object.keys(this.supportedLogTypes).includes(logType)) {
|
|
178
178
|
return await (this.chromedriver as Chromedriver).jwproxy.command('/log', 'POST', {
|
|
179
179
|
type: logType,
|
|
180
180
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {fs, net, util} from '@appium/support';
|
|
2
2
|
import type {NetOptions, HttpUploadOptions} from '@appium/support';
|
|
3
|
-
import _ from 'lodash';
|
|
4
3
|
import dayjs from 'dayjs';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import type {HTTPMethod, StringRecord} from '@appium/types';
|
|
@@ -128,7 +127,7 @@ export async function mobileStopMediaProjectionRecording(
|
|
|
128
127
|
throw new Error(`No recent media projection recording have been found. Did you start any?`);
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
if (
|
|
130
|
+
if (util.isEmpty(remotePath)) {
|
|
132
131
|
const {size} = await fs.stat(recentRecordingPath);
|
|
133
132
|
this.log.debug(
|
|
134
133
|
`The size of the resulting media projection recording is ${util.toReadableSizeString(size)}`,
|
|
@@ -154,7 +153,7 @@ async function uploadRecordedMedia(
|
|
|
154
153
|
remotePath?: string,
|
|
155
154
|
uploadOptions: UploadOptions = {},
|
|
156
155
|
): Promise<string> {
|
|
157
|
-
if (
|
|
156
|
+
if (util.isEmpty(remotePath)) {
|
|
158
157
|
return (await util.toInMemoryBase64(localFile)).toString();
|
|
159
158
|
}
|
|
160
159
|
|
|
@@ -182,7 +181,7 @@ async function uploadRecordedMedia(
|
|
|
182
181
|
}
|
|
183
182
|
|
|
184
183
|
function adjustMediaExtension(name: string): string {
|
|
185
|
-
return
|
|
184
|
+
return name?.toLowerCase()?.endsWith(DEFAULT_EXT) ? name : `${name}${DEFAULT_EXT}`;
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
async function verifyMediaProjectionRecordingIsSupported(adb: ADB): Promise<void> {
|
package/lib/commands/network.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
1
|
import {errors} from 'appium/driver';
|
|
3
2
|
import {util} from '@appium/support';
|
|
4
3
|
import type {AndroidDriver} from '../driver';
|
|
@@ -69,7 +68,7 @@ export async function mobileSetConnectivity(
|
|
|
69
68
|
data?: boolean,
|
|
70
69
|
airplaneMode?: boolean,
|
|
71
70
|
): Promise<void> {
|
|
72
|
-
if (
|
|
71
|
+
if (wifi === undefined && data === undefined && airplaneMode === undefined) {
|
|
73
72
|
throw new errors.InvalidArgumentError(
|
|
74
73
|
`Either one of ${JSON.stringify(SUPPORTED_SERVICE_NAMES)} options must be provided`,
|
|
75
74
|
);
|
|
@@ -81,20 +80,20 @@ export async function mobileSetConnectivity(
|
|
|
81
80
|
[airplaneMode, AIRPLANE_MODE_KEY_NAME],
|
|
82
81
|
];
|
|
83
82
|
const services = connectivityPairs.reduce<ServiceType[]>((acc, [value, key]) => {
|
|
84
|
-
if (
|
|
83
|
+
if (value !== undefined) {
|
|
85
84
|
acc.push(key);
|
|
86
85
|
}
|
|
87
86
|
return acc;
|
|
88
87
|
}, []);
|
|
89
88
|
const currentState = await this.mobileGetConnectivity(services);
|
|
90
89
|
const setters: Promise<any>[] = [];
|
|
91
|
-
if (
|
|
90
|
+
if (wifi !== undefined && currentState.wifi !== Boolean(wifi)) {
|
|
92
91
|
setters.push(this.setWifiState(wifi));
|
|
93
92
|
}
|
|
94
|
-
if (
|
|
93
|
+
if (data !== undefined && currentState.data !== Boolean(data)) {
|
|
95
94
|
setters.push(this.setDataState(data));
|
|
96
95
|
}
|
|
97
|
-
if (
|
|
96
|
+
if (airplaneMode !== undefined && currentState.airplaneMode !== Boolean(airplaneMode)) {
|
|
98
97
|
setters.push(
|
|
99
98
|
(async () => {
|
|
100
99
|
await this.adb.setAirplaneMode(airplaneMode);
|
|
@@ -104,7 +103,7 @@ export async function mobileSetConnectivity(
|
|
|
104
103
|
})(),
|
|
105
104
|
);
|
|
106
105
|
}
|
|
107
|
-
if (
|
|
106
|
+
if (setters.length > 0) {
|
|
108
107
|
await Promise.all(setters);
|
|
109
108
|
}
|
|
110
109
|
}
|
|
@@ -120,9 +119,9 @@ export async function mobileGetConnectivity(
|
|
|
120
119
|
this: AndroidDriver,
|
|
121
120
|
services: ServiceType[] | ServiceType = SUPPORTED_SERVICE_NAMES,
|
|
122
121
|
): Promise<GetConnectivityResult> {
|
|
123
|
-
const svcs =
|
|
124
|
-
const unsupportedServices =
|
|
125
|
-
if (
|
|
122
|
+
const svcs = Array.isArray(services) ? services : [services];
|
|
123
|
+
const unsupportedServices = svcs.filter((svc) => !SUPPORTED_SERVICE_NAMES.includes(svc));
|
|
124
|
+
if (unsupportedServices.length > 0) {
|
|
126
125
|
throw new errors.InvalidArgumentError(
|
|
127
126
|
`${util.pluralize(
|
|
128
127
|
'Service name',
|
|
@@ -144,13 +143,13 @@ export async function mobileGetConnectivity(
|
|
|
144
143
|
: Promise.resolve(undefined),
|
|
145
144
|
]);
|
|
146
145
|
const result: GetConnectivityResult = {};
|
|
147
|
-
if (
|
|
146
|
+
if (wifi !== undefined) {
|
|
148
147
|
result.wifi = Boolean(wifi);
|
|
149
148
|
}
|
|
150
|
-
if (
|
|
149
|
+
if (data !== undefined) {
|
|
151
150
|
result.data = Boolean(data);
|
|
152
151
|
}
|
|
153
|
-
if (
|
|
152
|
+
if (airplaneMode !== undefined) {
|
|
154
153
|
result.airplaneMode = Boolean(airplaneMode);
|
|
155
154
|
}
|
|
156
155
|
return result;
|
package/lib/commands/nfc.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {errors} from 'appium/driver';
|
|
2
|
-
import _ from 'lodash';
|
|
3
2
|
import type {AndroidDriver} from '../driver';
|
|
4
3
|
import type {NfcAction} from './types';
|
|
5
4
|
|
|
@@ -27,7 +26,7 @@ export async function mobileNfc(this: AndroidDriver, action: NfcAction): Promise
|
|
|
27
26
|
break;
|
|
28
27
|
default:
|
|
29
28
|
throw new errors.InvalidArgumentError(
|
|
30
|
-
`You must provide a valid 'action' argument. Supported actions are: ${
|
|
29
|
+
`You must provide a valid 'action' argument. Supported actions are: ${Object.values(SUPPORTED_ACTIONS)}`,
|
|
31
30
|
);
|
|
32
31
|
}
|
|
33
32
|
}
|