appium-android-driver 14.0.1 → 14.0.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 +12 -0
- package/build/lib/commands/app-management.d.ts +1 -1
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +2 -4
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/appearance.d.ts.map +1 -1
- package/build/lib/commands/appearance.js.map +1 -1
- package/build/lib/commands/bidi/models.d.ts +1 -1
- package/build/lib/commands/bidi/models.d.ts.map +1 -1
- package/build/lib/commands/bidi/models.js +1 -1
- 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.map +1 -1
- package/build/lib/commands/context/cache.d.ts +1 -1
- package/build/lib/commands/context/cache.d.ts.map +1 -1
- package/build/lib/commands/context/cache.js.map +1 -1
- package/build/lib/commands/context/exports.d.ts.map +1 -1
- package/build/lib/commands/context/exports.js +3 -4
- 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 +21 -26
- package/build/lib/commands/context/helpers.js.map +1 -1
- package/build/lib/commands/device/common.d.ts +1 -1
- package/build/lib/commands/device/common.d.ts.map +1 -1
- package/build/lib/commands/device/common.js +8 -16
- package/build/lib/commands/device/common.js.map +1 -1
- package/build/lib/commands/device/emulator-actions.d.ts.map +1 -1
- package/build/lib/commands/device/emulator-actions.js +1 -1
- package/build/lib/commands/device/emulator-actions.js.map +1 -1
- package/build/lib/commands/device/emulator-console.d.ts.map +1 -1
- package/build/lib/commands/device/emulator-console.js.map +1 -1
- package/build/lib/commands/device/utils.d.ts.map +1 -1
- package/build/lib/commands/device/utils.js +5 -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.map +1 -1
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +2 -4
- 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 +6 -20
- package/build/lib/commands/file-actions.js.map +1 -1
- package/build/lib/commands/find.d.ts +1 -1
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +1 -1
- 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 +5 -12
- package/build/lib/commands/geolocation.js.map +1 -1
- package/build/lib/commands/gestures.d.ts.map +1 -1
- package/build/lib/commands/gestures.js.map +1 -1
- package/build/lib/commands/image-injection.d.ts.map +1 -1
- package/build/lib/commands/image-injection.js +2 -2
- package/build/lib/commands/image-injection.js.map +1 -1
- package/build/lib/commands/ime.d.ts.map +1 -1
- package/build/lib/commands/ime.js +1 -7
- package/build/lib/commands/ime.js.map +1 -1
- package/build/lib/commands/intent.d.ts.map +1 -1
- package/build/lib/commands/intent.js +2 -3
- 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.map +1 -1
- package/build/lib/commands/legacy.d.ts.map +1 -1
- package/build/lib/commands/legacy.js.map +1 -1
- package/build/lib/commands/lock/exports.d.ts.map +1 -1
- 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 +2 -9
- package/build/lib/commands/lock/helpers.js.map +1 -1
- package/build/lib/commands/log.d.ts +1 -1
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +6 -8
- 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 +2 -2
- package/build/lib/commands/media-projection.js.map +1 -1
- package/build/lib/commands/memory.d.ts.map +1 -1
- package/build/lib/commands/memory.js.map +1 -1
- package/build/lib/commands/misc.d.ts.map +1 -1
- package/build/lib/commands/misc.js.map +1 -1
- package/build/lib/commands/network.d.ts.map +1 -1
- package/build/lib/commands/network.js +4 -11
- 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.map +1 -1
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +4 -21
- 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 +4 -7
- 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 +2 -2
- 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.map +1 -1
- package/build/lib/commands/shell.d.ts.map +1 -1
- package/build/lib/commands/shell.js +1 -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 +6 -9
- 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 +1 -1
- package/build/lib/commands/system-bars.js.map +1 -1
- package/build/lib/commands/time.d.ts.map +1 -1
- package/build/lib/commands/time.js +1 -1
- package/build/lib/commands/time.js.map +1 -1
- package/build/lib/commands/types.d.ts +4 -4
- package/build/lib/commands/types.d.ts.map +1 -1
- package/build/lib/doctor/checks.d.ts.map +1 -1
- package/build/lib/doctor/checks.js +2 -2
- package/build/lib/doctor/checks.js.map +1 -1
- package/build/lib/driver.d.ts +8 -8
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +14 -20
- package/build/lib/driver.js.map +1 -1
- package/build/lib/execute-method-map.d.ts.map +1 -1
- package/build/lib/execute-method-map.js +1 -10
- package/build/lib/execute-method-map.js.map +1 -1
- package/build/lib/index.d.ts +1 -1
- package/build/lib/index.d.ts.map +1 -1
- package/build/lib/index.js +1 -1
- package/build/lib/index.js.map +1 -1
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +1 -1
- package/build/lib/utils.js.map +1 -1
- package/lib/commands/app-management.ts +15 -43
- package/lib/commands/appearance.ts +1 -5
- package/lib/commands/bidi/models.ts +5 -12
- package/lib/commands/bluetooth.ts +1 -0
- package/lib/commands/context/cache.ts +2 -1
- package/lib/commands/context/exports.ts +17 -40
- package/lib/commands/context/helpers.ts +37 -88
- package/lib/commands/device/common.ts +16 -29
- package/lib/commands/device/emulator-actions.ts +12 -49
- package/lib/commands/device/emulator-console.ts +1 -0
- package/lib/commands/device/utils.ts +7 -16
- package/lib/commands/deviceidle.ts +1 -0
- package/lib/commands/element.ts +6 -20
- package/lib/commands/execute.ts +6 -13
- package/lib/commands/file-actions.ts +17 -50
- package/lib/commands/find.ts +4 -6
- package/lib/commands/geolocation.ts +9 -17
- package/lib/commands/gestures.ts +2 -1
- package/lib/commands/image-injection.ts +9 -19
- package/lib/commands/ime.ts +2 -7
- package/lib/commands/intent.ts +3 -3
- package/lib/commands/keyboard.ts +4 -14
- package/lib/commands/legacy.ts +1 -0
- package/lib/commands/lock/exports.ts +5 -10
- package/lib/commands/lock/helpers.ts +16 -51
- package/lib/commands/log.ts +12 -22
- package/lib/commands/media-projection.ts +9 -21
- package/lib/commands/memory.ts +2 -5
- package/lib/commands/misc.ts +3 -5
- package/lib/commands/network.ts +9 -27
- package/lib/commands/nfc.ts +1 -0
- package/lib/commands/performance.ts +12 -48
- package/lib/commands/permissions.ts +10 -22
- package/lib/commands/recordscreen.ts +16 -41
- package/lib/commands/resources.ts +3 -9
- package/lib/commands/shell.ts +3 -7
- package/lib/commands/streamscreen.ts +17 -37
- package/lib/commands/system-bars.ts +7 -13
- package/lib/commands/time.ts +4 -8
- package/lib/commands/types.ts +9 -10
- package/lib/doctor/checks.ts +11 -18
- package/lib/driver.ts +43 -65
- package/lib/execute-method-map.ts +1 -10
- package/lib/index.ts +1 -1
- package/lib/method-map.ts +1 -0
- package/lib/utils.ts +4 -6
- package/package.json +27 -37
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import {util} from '@appium/support';
|
|
2
2
|
import {ADB} from 'appium-adb';
|
|
3
3
|
import {retryInterval} from 'asyncbox';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
SETTINGS_HELPER_ID,
|
|
7
|
-
UNICODE_IME,
|
|
8
|
-
EMPTY_IME,
|
|
9
|
-
} from 'io.appium.settings';
|
|
10
|
-
import {prepareEmulatorForImageInjection} from '../image-injection.js';
|
|
11
|
-
import {ADB_LISTEN_ALL_NETWORK_FEATURE} from '../../utils.js';
|
|
4
|
+
import {path as SETTINGS_APK_PATH, SETTINGS_HELPER_ID, UNICODE_IME, EMPTY_IME} from 'io.appium.settings';
|
|
5
|
+
|
|
12
6
|
import type {AndroidDriver} from '../../driver.js';
|
|
7
|
+
import {ADB_LISTEN_ALL_NETWORK_FEATURE} from '../../utils.js';
|
|
8
|
+
import {prepareEmulatorForImageInjection} from '../image-injection.js';
|
|
13
9
|
|
|
14
10
|
const HELPER_APP_INSTALL_RETRIES = 3;
|
|
15
11
|
const HELPER_APP_INSTALL_RETRY_DELAY_MS = 5000;
|
|
@@ -55,7 +51,7 @@ export function prepareAvdArgs(this: AndroidDriver): string[] {
|
|
|
55
51
|
if (Array.isArray(avdArgs)) {
|
|
56
52
|
result.push(...avdArgs);
|
|
57
53
|
} else {
|
|
58
|
-
result.push(...util.shellParse(`${avdArgs}`));
|
|
54
|
+
result.push(...util.shellParse(`${avdArgs}`).filter((entry): entry is string => typeof entry === 'string'));
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
if (networkSpeed) {
|
|
@@ -97,10 +93,7 @@ export async function prepareEmulator(this: AndroidDriver, adb: ADB): Promise<vo
|
|
|
97
93
|
}
|
|
98
94
|
const args = prepareAvdArgs.bind(this)();
|
|
99
95
|
if (isEmulatorRunning) {
|
|
100
|
-
if (
|
|
101
|
-
(await prepareEmulatorForImageInjection.bind(this)(adb.sdkRoot as string)) ||
|
|
102
|
-
args.includes('-wipe-data')
|
|
103
|
-
) {
|
|
96
|
+
if ((await prepareEmulatorForImageInjection.bind(this)(adb.sdkRoot as string)) || args.includes('-wipe-data')) {
|
|
104
97
|
this.log.debug(`Killing '${avdName}'`);
|
|
105
98
|
await adb.killEmulator(avdName);
|
|
106
99
|
} else {
|
|
@@ -203,9 +196,7 @@ export async function pushSettingsApp(this: AndroidDriver, throwIfError: boolean
|
|
|
203
196
|
// Reinstall would stop the settings helper process anyway, so
|
|
204
197
|
// there is no need to continue if the application is still running
|
|
205
198
|
if (await this.settingsApp.isRunningInForeground()) {
|
|
206
|
-
this.log.debug(
|
|
207
|
-
`${SETTINGS_HELPER_ID} is already running. ` + `There is no need to reset its permissions.`,
|
|
208
|
-
);
|
|
199
|
+
this.log.debug(`${SETTINGS_HELPER_ID} is already running. There is no need to reset its permissions.`);
|
|
209
200
|
return;
|
|
210
201
|
}
|
|
211
202
|
|
package/lib/commands/element.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
|
|
3
|
-
import {errors} from 'appium/driver.js';
|
|
4
3
|
import type {Position, Size} from '@appium/types';
|
|
4
|
+
import {errors} from 'appium/driver.js';
|
|
5
|
+
|
|
5
6
|
import type {AndroidDriver} from '../driver.js';
|
|
6
7
|
import type {DoSetElementValueOpts} from './types.js';
|
|
7
8
|
|
|
@@ -13,11 +14,7 @@ import type {DoSetElementValueOpts} from './types.js';
|
|
|
13
14
|
* @returns Promise that resolves to the attribute value, or `null` if not found.
|
|
14
15
|
* @throws {errors.NotImplementedError} This method is not implemented.
|
|
15
16
|
*/
|
|
16
|
-
export async function getAttribute(
|
|
17
|
-
this: AndroidDriver,
|
|
18
|
-
attribute: string,
|
|
19
|
-
elementId: string,
|
|
20
|
-
): Promise<string | null> {
|
|
17
|
+
export async function getAttribute(this: AndroidDriver, attribute: string, elementId: string): Promise<string | null> {
|
|
21
18
|
throw new errors.NotImplementedError('Not implemented');
|
|
22
19
|
}
|
|
23
20
|
|
|
@@ -139,10 +136,7 @@ export async function setElementValue(
|
|
|
139
136
|
* @returns Promise that resolves when the value is set.
|
|
140
137
|
* @throws {errors.NotImplementedError} This method is not implemented.
|
|
141
138
|
*/
|
|
142
|
-
export async function doSetElementValue(
|
|
143
|
-
this: AndroidDriver,
|
|
144
|
-
params: DoSetElementValueOpts,
|
|
145
|
-
): Promise<void> {
|
|
139
|
+
export async function doSetElementValue(this: AndroidDriver, params: DoSetElementValueOpts): Promise<void> {
|
|
146
140
|
throw new errors.NotImplementedError('Not implemented');
|
|
147
141
|
}
|
|
148
142
|
|
|
@@ -153,11 +147,7 @@ export async function doSetElementValue(
|
|
|
153
147
|
* @param elementId The element identifier.
|
|
154
148
|
* @returns Promise that resolves when the value is set.
|
|
155
149
|
*/
|
|
156
|
-
export async function setValue(
|
|
157
|
-
this: AndroidDriver,
|
|
158
|
-
keys: string | string[],
|
|
159
|
-
elementId: string,
|
|
160
|
-
): Promise<void> {
|
|
150
|
+
export async function setValue(this: AndroidDriver, keys: string | string[], elementId: string): Promise<void> {
|
|
161
151
|
return await this.setElementValue(keys, elementId, false);
|
|
162
152
|
}
|
|
163
153
|
|
|
@@ -168,11 +158,7 @@ export async function setValue(
|
|
|
168
158
|
* @param elementId The element identifier.
|
|
169
159
|
* @returns Promise that resolves when the value is replaced.
|
|
170
160
|
*/
|
|
171
|
-
export async function replaceValue(
|
|
172
|
-
this: AndroidDriver,
|
|
173
|
-
keys: string | string[],
|
|
174
|
-
elementId: string,
|
|
175
|
-
): Promise<void> {
|
|
161
|
+
export async function replaceValue(this: AndroidDriver, keys: string | string[], elementId: string): Promise<void> {
|
|
176
162
|
return await this.setElementValue(keys, elementId, true);
|
|
177
163
|
}
|
|
178
164
|
|
package/lib/commands/execute.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {errors, PROTOCOLS} from 'appium/driver.js';
|
|
2
1
|
import {util} from '@appium/support';
|
|
3
2
|
import type {StringRecord, Element} from '@appium/types';
|
|
4
|
-
import type {AndroidDriver} from '../driver.js';
|
|
5
3
|
import type {Chromedriver} from 'appium-chromedriver';
|
|
4
|
+
import {errors, PROTOCOLS} from 'appium/driver.js';
|
|
5
|
+
|
|
6
|
+
import type {AndroidDriver} from '../driver.js';
|
|
6
7
|
|
|
7
8
|
const EXECUTE_SCRIPT_PREFIX = 'mobile:';
|
|
8
9
|
|
|
@@ -17,11 +18,7 @@ type ExecuteMethodArgs = readonly any[] | readonly [StringRecord] | Readonly<Str
|
|
|
17
18
|
* @returns Promise that resolves to the script execution result.
|
|
18
19
|
* @throws {errors.NotImplementedError} If not in a web context and script doesn't start with 'mobile:'.
|
|
19
20
|
*/
|
|
20
|
-
export async function execute(
|
|
21
|
-
this: AndroidDriver,
|
|
22
|
-
script: string,
|
|
23
|
-
args?: ExecuteMethodArgs,
|
|
24
|
-
): Promise<any> {
|
|
21
|
+
export async function execute(this: AndroidDriver, script: string, args?: ExecuteMethodArgs): Promise<any> {
|
|
25
22
|
if (script?.startsWith(EXECUTE_SCRIPT_PREFIX)) {
|
|
26
23
|
const formattedScript = script.trim().replace(/^mobile:\s*/, `${EXECUTE_SCRIPT_PREFIX} `);
|
|
27
24
|
const executeMethodArgs = preprocessExecuteMethodArgs(args);
|
|
@@ -31,9 +28,7 @@ export async function execute(
|
|
|
31
28
|
throw new errors.NotImplementedError();
|
|
32
29
|
}
|
|
33
30
|
const endpoint =
|
|
34
|
-
(this.chromedriver as Chromedriver).jwproxy.downstreamProtocol === PROTOCOLS.MJSONWP
|
|
35
|
-
? '/execute'
|
|
36
|
-
: '/execute/sync';
|
|
31
|
+
(this.chromedriver as Chromedriver).jwproxy.downstreamProtocol === PROTOCOLS.MJSONWP ? '/execute' : '/execute/sync';
|
|
37
32
|
return await (this.chromedriver as Chromedriver).jwproxy.command(endpoint, 'POST', {
|
|
38
33
|
script,
|
|
39
34
|
args,
|
|
@@ -62,9 +57,7 @@ function preprocessExecuteMethodArgs(args?: ExecuteMethodArgs): StringRecord {
|
|
|
62
57
|
* Automatically unwraps the `elementId` prop _if and only if_ the execute method expects it.
|
|
63
58
|
*/
|
|
64
59
|
if ('elementId' in executeMethodArgs) {
|
|
65
|
-
executeMethodArgs.elementId = util.unwrapElement(
|
|
66
|
-
executeMethodArgs.elementId as Element | string,
|
|
67
|
-
);
|
|
60
|
+
executeMethodArgs.elementId = util.unwrapElement(executeMethodArgs.elementId as Element | string);
|
|
68
61
|
}
|
|
69
62
|
|
|
70
63
|
return executeMethodArgs;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {fs, util, zip, tempDir} from '@appium/support';
|
|
2
1
|
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import {fs, util, zip, tempDir} from '@appium/support';
|
|
4
|
+
import type {ADB} from 'appium-adb';
|
|
3
5
|
import {errors} from 'appium/driver.js';
|
|
6
|
+
|
|
4
7
|
import type {AndroidDriver} from '../driver.js';
|
|
5
|
-
import type {ADB} from 'appium-adb';
|
|
6
8
|
|
|
7
9
|
const CONTAINER_PATH_MARKER = '@';
|
|
8
10
|
// https://regex101.com/r/PLdB0G/2
|
|
@@ -27,8 +29,7 @@ const ANDROID_MEDIA_RESCAN_INTENT = 'android.intent.action.MEDIA_SCANNER_SCAN_FI
|
|
|
27
29
|
export async function pullFile(this: AndroidDriver, remotePath: string): Promise<string> {
|
|
28
30
|
if (remotePath.endsWith('/')) {
|
|
29
31
|
throw new errors.InvalidArgumentError(
|
|
30
|
-
`It is expected that remote path points to a file and not to a folder.
|
|
31
|
-
`'${remotePath}' is given instead`,
|
|
32
|
+
`It is expected that remote path points to a file and not to a folder. '${remotePath}' is given instead`,
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
35
|
let tmpDestination: string | null = null;
|
|
@@ -84,15 +85,10 @@ export async function pullFile(this: AndroidDriver, remotePath: string): Promise
|
|
|
84
85
|
* @returns Promise that resolves when the file is pushed.
|
|
85
86
|
* @throws {errors.InvalidArgumentError} If the remote path points to a folder instead of a file.
|
|
86
87
|
*/
|
|
87
|
-
export async function pushFile(
|
|
88
|
-
this: AndroidDriver,
|
|
89
|
-
remotePath: string,
|
|
90
|
-
base64Data: string | number[],
|
|
91
|
-
): Promise<void> {
|
|
88
|
+
export async function pushFile(this: AndroidDriver, remotePath: string, base64Data: string | number[]): Promise<void> {
|
|
92
89
|
if (remotePath.endsWith('/')) {
|
|
93
90
|
throw new errors.InvalidArgumentError(
|
|
94
|
-
`It is expected that remote path points to a file and not to a folder.
|
|
95
|
-
`'${remotePath}' is given instead`,
|
|
91
|
+
`It is expected that remote path points to a file and not to a folder. '${remotePath}' is given instead`,
|
|
96
92
|
);
|
|
97
93
|
}
|
|
98
94
|
const localFile = await tempDir.path({prefix: 'appium', suffix: '.tmp'});
|
|
@@ -116,11 +112,7 @@ export async function pushFile(
|
|
|
116
112
|
);
|
|
117
113
|
tmpDestination = `/data/local/tmp/${path.posix.basename(pathInContainer)}`;
|
|
118
114
|
try {
|
|
119
|
-
await this.adb.shell([
|
|
120
|
-
'run-as',
|
|
121
|
-
packageId,
|
|
122
|
-
`mkdir -p '${escapePath(path.posix.dirname(pathInContainer))}'`,
|
|
123
|
-
]);
|
|
115
|
+
await this.adb.shell(['run-as', packageId, `mkdir -p '${escapePath(path.posix.dirname(pathInContainer))}'`]);
|
|
124
116
|
await this.adb.shell(['run-as', packageId, `touch '${escapePath(pathInContainer)}'`]);
|
|
125
117
|
await this.adb.shell(['run-as', packageId, `chmod 777 '${escapePath(pathInContainer)}'`]);
|
|
126
118
|
await this.adb.push(localFile, tmpDestination);
|
|
@@ -185,8 +177,7 @@ export async function pullFolder(this: AndroidDriver, remotePath: string): Promi
|
|
|
185
177
|
export async function mobileDeleteFile(this: AndroidDriver, remotePath: string): Promise<boolean> {
|
|
186
178
|
if (remotePath.endsWith('/')) {
|
|
187
179
|
throw new errors.InvalidArgumentError(
|
|
188
|
-
`It is expected that remote path points to a folder and not to a file.
|
|
189
|
-
`'${remotePath}' is given instead`,
|
|
180
|
+
`It is expected that remote path points to a folder and not to a file. '${remotePath}' is given instead`,
|
|
190
181
|
);
|
|
191
182
|
}
|
|
192
183
|
return await deleteFileOrFolder.call(this, this.adb, remotePath);
|
|
@@ -201,11 +192,7 @@ export async function mobileDeleteFile(this: AndroidDriver, remotePath: string):
|
|
|
201
192
|
* If the remote path is valid, but the remote path does not exist
|
|
202
193
|
* this function return `false`.
|
|
203
194
|
*/
|
|
204
|
-
async function deleteFileOrFolder(
|
|
205
|
-
this: AndroidDriver,
|
|
206
|
-
adb: ADB,
|
|
207
|
-
remotePath: string,
|
|
208
|
-
): Promise<boolean> {
|
|
195
|
+
async function deleteFileOrFolder(this: AndroidDriver, adb: ADB, remotePath: string): Promise<boolean> {
|
|
209
196
|
const {isDir, isPresent, isFile} = createFSTests(adb);
|
|
210
197
|
let dstPath = remotePath;
|
|
211
198
|
let pkgId: string | undefined;
|
|
@@ -246,9 +233,7 @@ async function deleteFileOrFolder(
|
|
|
246
233
|
await adb.shell(['rm', `-f${expectsFile ? '' : 'r'}`, dstPath]);
|
|
247
234
|
}
|
|
248
235
|
if (await isPresent(dstPath, pkgId)) {
|
|
249
|
-
throw this.log.errorWithException(
|
|
250
|
-
`The item at '${dstPath}' still exists after being deleted. Is it writable?`,
|
|
251
|
-
);
|
|
236
|
+
throw this.log.errorWithException(`The item at '${dstPath}' still exists after being deleted. Is it writable?`);
|
|
252
237
|
}
|
|
253
238
|
return true;
|
|
254
239
|
}
|
|
@@ -285,23 +270,12 @@ async function scanMedia(this: AndroidDriver, remotePath: string): Promise<void>
|
|
|
285
270
|
if ((await this.adb.getApiLevel()) >= 29) {
|
|
286
271
|
await this.settingsApp.scanMedia(remotePath);
|
|
287
272
|
} else {
|
|
288
|
-
await this.adb.shell([
|
|
289
|
-
'am',
|
|
290
|
-
'broadcast',
|
|
291
|
-
'-a',
|
|
292
|
-
ANDROID_MEDIA_RESCAN_INTENT,
|
|
293
|
-
'-d',
|
|
294
|
-
`file://${remotePath}`,
|
|
295
|
-
]);
|
|
273
|
+
await this.adb.shell(['am', 'broadcast', '-a', ANDROID_MEDIA_RESCAN_INTENT, '-d', `file://${remotePath}`]);
|
|
296
274
|
}
|
|
297
275
|
} catch (e) {
|
|
298
276
|
const err = e as {stderr?: string; message?: string};
|
|
299
277
|
// FIXME: what has a `stderr` prop?
|
|
300
|
-
this.log.warn(
|
|
301
|
-
`Ignoring an unexpected error upon media scanning of '${remotePath}': ${
|
|
302
|
-
err.stderr ?? err.message
|
|
303
|
-
}`,
|
|
304
|
-
);
|
|
278
|
+
this.log.warn(`Ignoring an unexpected error upon media scanning of '${remotePath}': ${err.stderr ?? err.message}`);
|
|
305
279
|
}
|
|
306
280
|
}
|
|
307
281
|
|
|
@@ -317,11 +291,7 @@ function escapePath(p: string): string {
|
|
|
317
291
|
* Factory providing filesystem test functions using ADB
|
|
318
292
|
*/
|
|
319
293
|
function createFSTests(adb: ADB) {
|
|
320
|
-
const performRemoteFsCheck = async (
|
|
321
|
-
p: string,
|
|
322
|
-
op: 'd' | 'f' | 'e',
|
|
323
|
-
runAs?: string,
|
|
324
|
-
): Promise<boolean> => {
|
|
294
|
+
const performRemoteFsCheck = async (p: string, op: 'd' | 'f' | 'e', runAs?: string): Promise<boolean> => {
|
|
325
295
|
const passFlag = '__PASS__';
|
|
326
296
|
const checkCmd = `[ -${op} '${escapePath(p)}' ] && echo ${passFlag}`;
|
|
327
297
|
const fullCmd = runAs ? `run-as ${runAs} ${checkCmd}` : checkCmd;
|
|
@@ -332,12 +302,9 @@ function createFSTests(adb: ADB) {
|
|
|
332
302
|
}
|
|
333
303
|
};
|
|
334
304
|
|
|
335
|
-
const isFile = async (p: string, runAs?: string): Promise<boolean> =>
|
|
336
|
-
|
|
337
|
-
const
|
|
338
|
-
await performRemoteFsCheck(p, 'd', runAs);
|
|
339
|
-
const isPresent = async (p: string, runAs?: string): Promise<boolean> =>
|
|
340
|
-
await performRemoteFsCheck(p, 'e', runAs);
|
|
305
|
+
const isFile = async (p: string, runAs?: string): Promise<boolean> => await performRemoteFsCheck(p, 'f', runAs);
|
|
306
|
+
const isDir = async (p: string, runAs?: string): Promise<boolean> => await performRemoteFsCheck(p, 'd', runAs);
|
|
307
|
+
const isPresent = async (p: string, runAs?: string): Promise<boolean> => await performRemoteFsCheck(p, 'e', runAs);
|
|
341
308
|
|
|
342
309
|
return {isFile, isDir, isPresent};
|
|
343
310
|
}
|
package/lib/commands/find.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {errors, isErrorType} from 'appium/driver.js';
|
|
2
1
|
import {util} from '@appium/support';
|
|
3
|
-
import type {AndroidDriver} from '../driver.js';
|
|
4
2
|
import type {Element} from '@appium/types';
|
|
3
|
+
import {errors, isErrorType} from 'appium/driver.js';
|
|
4
|
+
|
|
5
|
+
import type {AndroidDriver} from '../driver.js';
|
|
5
6
|
import type {FindElementOpts} from './types.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -103,10 +104,7 @@ export async function findElOrEls(
|
|
|
103
104
|
* @returns A single `Element` if `params.multiple` is `false`, or an array of `Element` objects if `true`.
|
|
104
105
|
* @throws {errors.NotImplementedError} This method must be implemented by the specific context handler.
|
|
105
106
|
*/
|
|
106
|
-
export async function doFindElementOrEls(
|
|
107
|
-
this: AndroidDriver,
|
|
108
|
-
params: FindElementOpts,
|
|
109
|
-
): Promise<Element | Element[]> {
|
|
107
|
+
export async function doFindElementOrEls(this: AndroidDriver, params: FindElementOpts): Promise<Element | Element[]> {
|
|
110
108
|
void params;
|
|
111
109
|
throw new errors.NotImplementedError('Not implemented');
|
|
112
110
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {asyncmap} from 'asyncbox';
|
|
2
|
-
import {fs, tempDir} from '@appium/support';
|
|
3
1
|
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import {fs, tempDir} from '@appium/support';
|
|
4
4
|
import type {Location} from '@appium/types';
|
|
5
|
-
import {
|
|
6
|
-
import type {AndroidDriver} from '../driver.js';
|
|
5
|
+
import {asyncmap} from 'asyncbox';
|
|
7
6
|
import {SETTINGS_HELPER_ID} from 'io.appium.settings';
|
|
8
7
|
|
|
8
|
+
import type {AndroidDriver} from '../driver.js';
|
|
9
|
+
import {getThirdPartyPackages} from './app-management.js';
|
|
10
|
+
|
|
9
11
|
// The value close to zero, but not zero, is needed
|
|
10
12
|
// to trick JSON generation and send a float value instead of an integer,
|
|
11
13
|
// This allows strictly-typed clients, like Java, to properly
|
|
@@ -87,10 +89,7 @@ export async function mobileSetGeolocation(
|
|
|
87
89
|
* 20000ms by default.
|
|
88
90
|
* @returns Promise that resolves when the GPS cache refresh is initiated.
|
|
89
91
|
*/
|
|
90
|
-
export async function mobileRefreshGpsCache(
|
|
91
|
-
this: AndroidDriver,
|
|
92
|
-
timeoutMs?: number,
|
|
93
|
-
): Promise<void> {
|
|
92
|
+
export async function mobileRefreshGpsCache(this: AndroidDriver, timeoutMs?: number): Promise<void> {
|
|
94
93
|
await this.settingsApp.refreshGeoLocationCache(timeoutMs);
|
|
95
94
|
}
|
|
96
95
|
|
|
@@ -135,8 +134,7 @@ export async function toggleLocationServices(this: AndroidDriver): Promise<void>
|
|
|
135
134
|
this.log.info('Toggling location services');
|
|
136
135
|
const isGpsEnabled = await this.isLocationServicesEnabled();
|
|
137
136
|
this.log.debug(
|
|
138
|
-
`Current GPS state: ${isGpsEnabled}.
|
|
139
|
-
`The service is going to be ${isGpsEnabled ? 'disabled' : 'enabled'}`,
|
|
137
|
+
`Current GPS state: ${isGpsEnabled}. The service is going to be ${isGpsEnabled ? 'disabled' : 'enabled'}`,
|
|
140
138
|
);
|
|
141
139
|
await this.adb.toggleGPSLocationProvider(!isGpsEnabled);
|
|
142
140
|
}
|
|
@@ -211,13 +209,7 @@ async function resetMockLocation(this: AndroidDriver): Promise<void> {
|
|
|
211
209
|
// Only include currently installed packages
|
|
212
210
|
const resultPkgs = pkgIds.filter((pkg) => thirdPartyPkgIds.has(pkg));
|
|
213
211
|
if (resultPkgs.length <= 1) {
|
|
214
|
-
await this.adb.shell([
|
|
215
|
-
'appops',
|
|
216
|
-
'set',
|
|
217
|
-
resultPkgs[0] ?? SETTINGS_HELPER_ID,
|
|
218
|
-
'android:mock_location',
|
|
219
|
-
'deny',
|
|
220
|
-
]);
|
|
212
|
+
await this.adb.shell(['appops', 'set', resultPkgs[0] ?? SETTINGS_HELPER_ID, 'android:mock_location', 'deny']);
|
|
221
213
|
return;
|
|
222
214
|
}
|
|
223
215
|
|
package/lib/commands/gestures.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
|
|
3
4
|
import {fs, tempDir} from '@appium/support';
|
|
4
|
-
import
|
|
5
|
+
import {errors} from 'appium/driver.js';
|
|
6
|
+
|
|
5
7
|
import type {AndroidDriver} from '../driver.js';
|
|
6
8
|
|
|
7
9
|
const EMULATOR_RESOURCES_ROOT = ['emulator', 'resources'];
|
|
@@ -30,10 +32,7 @@ const PNG_MAGIC_LENGTH = 4;
|
|
|
30
32
|
* to load the updated config or `false` if the current config is already up to date
|
|
31
33
|
* or does not need to be updated
|
|
32
34
|
*/
|
|
33
|
-
export async function prepareEmulatorForImageInjection(
|
|
34
|
-
this: AndroidDriver,
|
|
35
|
-
sdkRoot: string,
|
|
36
|
-
): Promise<boolean> {
|
|
35
|
+
export async function prepareEmulatorForImageInjection(this: AndroidDriver, sdkRoot: string): Promise<boolean> {
|
|
37
36
|
const {injectedImageProperties: props} = this.opts;
|
|
38
37
|
if (!props) {
|
|
39
38
|
return false;
|
|
@@ -47,9 +46,7 @@ export async function prepareEmulatorForImageInjection(
|
|
|
47
46
|
if (await fs.exists(configPath)) {
|
|
48
47
|
const configPayload = await fs.readFile(configPath, 'utf8');
|
|
49
48
|
if (configPayload.includes(strProps)) {
|
|
50
|
-
this.log.info(
|
|
51
|
-
`The image injection config at '${configPath}' is already up to date. Doing nothing`,
|
|
52
|
-
);
|
|
49
|
+
this.log.info(`The image injection config at '${configPath}' is already up to date. Doing nothing`);
|
|
53
50
|
return false;
|
|
54
51
|
}
|
|
55
52
|
const updatedPayload = `${DEFAULT_CONFIG_PAYLOAD_PREFIX}
|
|
@@ -83,10 +80,7 @@ export async function prepareEmulatorForImageInjection(
|
|
|
83
80
|
* @throws {Error} If called on a non-emulator device.
|
|
84
81
|
* @throws {errors.InvalidArgumentError} If the payload is not a valid base64-encoded PNG.
|
|
85
82
|
*/
|
|
86
|
-
export async function mobileInjectEmulatorCameraImage(
|
|
87
|
-
this: AndroidDriver,
|
|
88
|
-
payload: string,
|
|
89
|
-
): Promise<void> {
|
|
83
|
+
export async function mobileInjectEmulatorCameraImage(this: AndroidDriver, payload: string): Promise<void> {
|
|
90
84
|
if (!this.isEmulator()) {
|
|
91
85
|
throw new Error('The image injection feature is only available on emulators');
|
|
92
86
|
}
|
|
@@ -99,9 +93,7 @@ export async function mobileInjectEmulatorCameraImage(
|
|
|
99
93
|
const pngBuffer = Buffer.from(payload, 'base64');
|
|
100
94
|
const magic = pngBuffer.toString('hex', 0, PNG_MAGIC_LENGTH);
|
|
101
95
|
if (magic !== PNG_MAGIC) {
|
|
102
|
-
throw new errors.InvalidArgumentError(
|
|
103
|
-
'The provided image payload must contain a valid base64-encoded .PNG data',
|
|
104
|
-
);
|
|
96
|
+
throw new errors.InvalidArgumentError('The provided image payload must contain a valid base64-encoded .PNG data');
|
|
105
97
|
}
|
|
106
98
|
|
|
107
99
|
const tmpImagePath = await tempDir.path({
|
|
@@ -111,9 +103,7 @@ export async function mobileInjectEmulatorCameraImage(
|
|
|
111
103
|
});
|
|
112
104
|
await fs.writeFile(tmpImagePath, pngBuffer);
|
|
113
105
|
await this.adb.execEmuConsoleCommand(`virtualscene-image table ${tmpImagePath}`);
|
|
114
|
-
this.log.info(
|
|
115
|
-
`The provided image has been successully injected to the ${this.adb.curDeviceId} emulator`,
|
|
116
|
-
);
|
|
106
|
+
this.log.info(`The provided image has been successully injected to the ${this.adb.curDeviceId} emulator`);
|
|
117
107
|
}
|
|
118
108
|
|
|
119
109
|
// #region Internal helpers
|
package/lib/commands/ime.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {errors} from 'appium/driver.js';
|
|
2
|
+
|
|
2
3
|
import type {AndroidDriver} from '../driver.js';
|
|
3
4
|
import {SET_STYLUS_HANDWRITING_FEATURE} from '../utils.js';
|
|
4
5
|
|
|
@@ -76,11 +77,5 @@ export async function deactivateIMEEngine(this: AndroidDriver): Promise<void> {
|
|
|
76
77
|
*/
|
|
77
78
|
export async function setStylusHandwriting(this: AndroidDriver, enabled: boolean): Promise<void> {
|
|
78
79
|
this.assertFeatureEnabled(SET_STYLUS_HANDWRITING_FEATURE);
|
|
79
|
-
await this.adb.shell([
|
|
80
|
-
'settings',
|
|
81
|
-
'put',
|
|
82
|
-
'secure',
|
|
83
|
-
'stylus_handwriting_enabled',
|
|
84
|
-
enabled ? '1' : '0',
|
|
85
|
-
]);
|
|
80
|
+
await this.adb.shell(['settings', 'put', 'secure', 'stylus_handwriting_enabled', enabled ? '1' : '0']);
|
|
86
81
|
}
|
package/lib/commands/intent.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {errors} from 'appium/driver.js';
|
|
2
1
|
import {util} from '@appium/support';
|
|
2
|
+
import {errors} from 'appium/driver.js';
|
|
3
|
+
|
|
3
4
|
import type {AndroidDriver} from '../driver.js';
|
|
4
5
|
import type {IntentOpts} from './types.js';
|
|
5
6
|
|
|
@@ -405,8 +406,7 @@ function parseIntentSpec(opts: IntentOpts = {}): string[] {
|
|
|
405
406
|
resultArgs.push(`--e${type}`, key);
|
|
406
407
|
} else if (value === undefined) {
|
|
407
408
|
throw new errors.InvalidArgumentError(
|
|
408
|
-
`Intent argument type '${type}' in '${JSON.stringify(item)}' requires a
|
|
409
|
-
`valid value to be provided`,
|
|
409
|
+
`Intent argument type '${type}' in '${JSON.stringify(item)}' requires a valid value to be provided`,
|
|
410
410
|
);
|
|
411
411
|
} else {
|
|
412
412
|
resultArgs.push(`--e${type}`, key, value);
|
package/lib/commands/keyboard.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {errors} from 'appium/driver.js';
|
|
2
2
|
import {UNICODE_IME, EMPTY_IME} from 'io.appium.settings';
|
|
3
|
+
|
|
3
4
|
import type {AndroidDriver} from '../driver.js';
|
|
4
5
|
import type {SendKeysOpts} from './types.js';
|
|
5
6
|
|
|
@@ -57,11 +58,7 @@ export async function doSendKeys(this: AndroidDriver, params: SendKeysOpts): Pro
|
|
|
57
58
|
* @param metastate Optional meta state flags.
|
|
58
59
|
* @returns Promise that resolves when the key event is sent.
|
|
59
60
|
*/
|
|
60
|
-
export async function keyevent(
|
|
61
|
-
this: AndroidDriver,
|
|
62
|
-
keycode: string | number,
|
|
63
|
-
metastate?: number,
|
|
64
|
-
): Promise<void> {
|
|
61
|
+
export async function keyevent(this: AndroidDriver, keycode: string | number, metastate?: number): Promise<void> {
|
|
65
62
|
// TODO deprecate keyevent; currently wd only implements keyevent
|
|
66
63
|
this.log.warn('keyevent will be deprecated use pressKeyCode');
|
|
67
64
|
return await this.pressKeyCode(keycode, metastate);
|
|
@@ -75,11 +72,7 @@ export async function keyevent(
|
|
|
75
72
|
* @returns Promise that resolves when the key code is pressed.
|
|
76
73
|
* @throws {errors.NotImplementedError} This method is not implemented.
|
|
77
74
|
*/
|
|
78
|
-
export async function pressKeyCode(
|
|
79
|
-
this: AndroidDriver,
|
|
80
|
-
keycode: string | number,
|
|
81
|
-
metastate?: number,
|
|
82
|
-
): Promise<void> {
|
|
75
|
+
export async function pressKeyCode(this: AndroidDriver, keycode: string | number, metastate?: number): Promise<void> {
|
|
83
76
|
void keycode;
|
|
84
77
|
void metastate;
|
|
85
78
|
throw new errors.NotImplementedError('Not implemented');
|
|
@@ -109,10 +102,7 @@ export async function longPressKeyCode(
|
|
|
109
102
|
* @param action The editor action to perform (e.g., 'done', 'go', 'next').
|
|
110
103
|
* @returns Promise that resolves when the editor action is performed.
|
|
111
104
|
*/
|
|
112
|
-
export async function mobilePerformEditorAction(
|
|
113
|
-
this: AndroidDriver,
|
|
114
|
-
action: string | number,
|
|
115
|
-
): Promise<void> {
|
|
105
|
+
export async function mobilePerformEditorAction(this: AndroidDriver, action: string | number): Promise<void> {
|
|
116
106
|
await this.settingsApp.performEditorAction(action);
|
|
117
107
|
}
|
|
118
108
|
|
package/lib/commands/legacy.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import {sleep} from 'asyncbox';
|
|
2
|
+
|
|
3
|
+
import type {AndroidDriver, AndroidDriverCaps} from '../../driver.js';
|
|
4
|
+
import type {UnlockType} from '../types.js';
|
|
2
5
|
import {
|
|
3
6
|
validateUnlockCapabilities,
|
|
4
7
|
FINGERPRINT_UNLOCK,
|
|
@@ -15,8 +18,6 @@ import {
|
|
|
15
18
|
toCredentialType,
|
|
16
19
|
verifyUnlock,
|
|
17
20
|
} from './helpers.js';
|
|
18
|
-
import type {AndroidDriver, AndroidDriverCaps} from '../../driver.js';
|
|
19
|
-
import type {UnlockType} from '../types.js';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Locks the device and optionally unlocks it after a specified number of seconds.
|
|
@@ -90,10 +91,7 @@ export async function mobileUnlock(
|
|
|
90
91
|
*
|
|
91
92
|
* @param caps - Optional capabilities to use for unlocking. If not provided, uses session capabilities.
|
|
92
93
|
*/
|
|
93
|
-
export async function unlockWithOptions(
|
|
94
|
-
this: AndroidDriver,
|
|
95
|
-
caps: AndroidDriverCaps | null = null,
|
|
96
|
-
): Promise<void> {
|
|
94
|
+
export async function unlockWithOptions(this: AndroidDriver, caps: AndroidDriverCaps | null = null): Promise<void> {
|
|
97
95
|
if (!(await this.adb.isScreenLocked())) {
|
|
98
96
|
this.log.info('Screen already unlocked, doing nothing');
|
|
99
97
|
return;
|
|
@@ -126,10 +124,7 @@ export async function unlockWithOptions(
|
|
|
126
124
|
credentialType: toCredentialType(unlockType as UnlockType),
|
|
127
125
|
});
|
|
128
126
|
} else {
|
|
129
|
-
const unlockMethodMap: Record<
|
|
130
|
-
string,
|
|
131
|
-
(this: AndroidDriver, caps: AndroidDriverCaps) => Promise<void>
|
|
132
|
-
> = {
|
|
127
|
+
const unlockMethodMap: Record<string, (this: AndroidDriver, caps: AndroidDriverCaps) => Promise<void>> = {
|
|
133
128
|
[PIN_UNLOCK]: pinUnlock,
|
|
134
129
|
[PIN_UNLOCK_KEY_EVENT]: pinUnlockWithKeyEvent,
|
|
135
130
|
[PASSWORD_UNLOCK]: passwordUnlock,
|