appium-adb 14.1.2 → 14.1.4
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/tools/aab-utils.d.ts +5 -4
- package/build/lib/tools/aab-utils.d.ts.map +1 -1
- package/build/lib/tools/aab-utils.js +13 -11
- package/build/lib/tools/aab-utils.js.map +1 -1
- package/build/lib/tools/android-manifest.d.ts +26 -30
- package/build/lib/tools/android-manifest.d.ts.map +1 -1
- package/build/lib/tools/android-manifest.js +33 -47
- package/build/lib/tools/android-manifest.js.map +1 -1
- package/build/lib/tools/apks-utils.d.ts +25 -29
- package/build/lib/tools/apks-utils.d.ts.map +1 -1
- package/build/lib/tools/apks-utils.js +41 -52
- package/build/lib/tools/apks-utils.js.map +1 -1
- package/build/lib/tools/emulator-commands.d.ts.map +1 -1
- package/build/lib/tools/emulator-commands.js +2 -1
- package/build/lib/tools/emulator-commands.js.map +1 -1
- package/build/lib/tools/fs-commands.d.ts +25 -30
- package/build/lib/tools/fs-commands.d.ts.map +1 -1
- package/build/lib/tools/fs-commands.js +19 -26
- package/build/lib/tools/fs-commands.js.map +1 -1
- package/build/lib/tools/general-commands.d.ts +36 -58
- package/build/lib/tools/general-commands.d.ts.map +1 -1
- package/build/lib/tools/general-commands.js +41 -47
- package/build/lib/tools/general-commands.js.map +1 -1
- package/build/lib/tools/keyboard-commands.d.ts +27 -36
- package/build/lib/tools/keyboard-commands.d.ts.map +1 -1
- package/build/lib/tools/keyboard-commands.js +24 -34
- package/build/lib/tools/keyboard-commands.js.map +1 -1
- package/build/lib/tools/lockmgmt.d.ts +25 -36
- package/build/lib/tools/lockmgmt.d.ts.map +1 -1
- package/build/lib/tools/lockmgmt.js +34 -38
- package/build/lib/tools/lockmgmt.js.map +1 -1
- package/build/lib/tools/logcat-commands.d.ts +11 -30
- package/build/lib/tools/logcat-commands.d.ts.map +1 -1
- package/build/lib/tools/logcat-commands.js +16 -17
- package/build/lib/tools/logcat-commands.js.map +1 -1
- package/build/lib/tools/network-commands.d.ts +24 -31
- package/build/lib/tools/network-commands.d.ts.map +1 -1
- package/build/lib/tools/network-commands.js +14 -24
- package/build/lib/tools/network-commands.js.map +1 -1
- package/lib/tools/{aab-utils.js → aab-utils.ts} +24 -15
- package/lib/tools/{android-manifest.js → android-manifest.ts} +66 -59
- package/lib/tools/{apks-utils.js → apks-utils.ts} +76 -68
- package/lib/tools/emulator-commands.ts +3 -2
- package/lib/tools/{fs-commands.js → fs-commands.ts} +41 -35
- package/lib/tools/{general-commands.js → general-commands.ts} +71 -68
- package/lib/tools/{keyboard-commands.js → keyboard-commands.ts} +42 -49
- package/lib/tools/{lockmgmt.js → lockmgmt.ts} +71 -56
- package/lib/tools/{logcat-commands.js → logcat-commands.ts} +24 -22
- package/lib/tools/{network-commands.js → network-commands.ts} +42 -34
- package/package.json +2 -2
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import type {ADB} from '../adb.js';
|
|
4
|
+
import type {TeenProcessExecOptions} from 'teen_process';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Verify whether a remote path exists on the device under test.
|
|
6
8
|
*
|
|
7
|
-
* @
|
|
8
|
-
* @
|
|
9
|
-
* @return {Promise<boolean>} True if the given path exists on the device.
|
|
9
|
+
* @param remotePath - The remote path to verify.
|
|
10
|
+
* @return True if the given path exists on the device.
|
|
10
11
|
*/
|
|
11
|
-
export async function fileExists(remotePath) {
|
|
12
|
+
export async function fileExists(this: ADB, remotePath: string): Promise<boolean> {
|
|
12
13
|
const passFlag = '__PASS__';
|
|
13
14
|
const checkCmd = `[ -e '${remotePath.replace(/'/g, `\\'`)}' ] && echo ${passFlag}`;
|
|
14
15
|
try {
|
|
@@ -21,24 +22,23 @@ export async function fileExists(remotePath) {
|
|
|
21
22
|
/**
|
|
22
23
|
* Get the output of _ls_ command on the device under test.
|
|
23
24
|
*
|
|
24
|
-
* @
|
|
25
|
-
* @param
|
|
26
|
-
* @
|
|
27
|
-
* @return {Promise<string[]>} The _ls_ output as an array of split lines.
|
|
25
|
+
* @param remotePath - The remote path (the first argument to the _ls_ command).
|
|
26
|
+
* @param opts - Additional _ls_ options.
|
|
27
|
+
* @return The _ls_ output as an array of split lines.
|
|
28
28
|
* An empty array is returned of the given _remotePath_
|
|
29
29
|
* does not exist.
|
|
30
30
|
*/
|
|
31
|
-
export async function ls(remotePath, opts = []) {
|
|
31
|
+
export async function ls(this: ADB, remotePath: string, opts: string[] = []): Promise<string[]> {
|
|
32
32
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const args = ['ls', ...opts, remotePath];
|
|
34
|
+
const stdout = await this.shell(args);
|
|
35
|
+
const lines = stdout.split('\n');
|
|
36
36
|
return lines
|
|
37
37
|
.map((l) => l.trim())
|
|
38
38
|
.filter(Boolean)
|
|
39
39
|
.filter((l) => l.indexOf('No such file') === -1);
|
|
40
40
|
} catch (err) {
|
|
41
|
-
if (err.message.indexOf('No such file or directory') === -1) {
|
|
41
|
+
if ((err as Error).message.indexOf('No such file or directory') === -1) {
|
|
42
42
|
throw err;
|
|
43
43
|
}
|
|
44
44
|
return [];
|
|
@@ -48,12 +48,11 @@ export async function ls(remotePath, opts = []) {
|
|
|
48
48
|
/**
|
|
49
49
|
* Get the size of the particular file located on the device under test.
|
|
50
50
|
*
|
|
51
|
-
* @
|
|
52
|
-
* @
|
|
53
|
-
* @return {Promise<number>} File size in bytes.
|
|
51
|
+
* @param remotePath - The remote path to the file.
|
|
52
|
+
* @return File size in bytes.
|
|
54
53
|
* @throws {Error} If there was an error while getting the size of the given file.
|
|
55
54
|
*/
|
|
56
|
-
export async function fileSize(remotePath) {
|
|
55
|
+
export async function fileSize(this: ADB, remotePath: string): Promise<number> {
|
|
57
56
|
try {
|
|
58
57
|
const files = await this.ls(remotePath, ['-la']);
|
|
59
58
|
if (files.length !== 1) {
|
|
@@ -66,7 +65,7 @@ export async function fileSize(remotePath) {
|
|
|
66
65
|
}
|
|
67
66
|
return parseInt(match[1], 10);
|
|
68
67
|
} catch (err) {
|
|
69
|
-
throw new Error(`Unable to get file size for '${remotePath}': ${err.message}`);
|
|
68
|
+
throw new Error(`Unable to get file size for '${remotePath}': ${(err as Error).message}`);
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
|
|
@@ -74,25 +73,28 @@ export async function fileSize(remotePath) {
|
|
|
74
73
|
* Forcefully recursively remove a path on the device under test.
|
|
75
74
|
* Be careful while calling this method.
|
|
76
75
|
*
|
|
77
|
-
* @
|
|
78
|
-
* @param {string} path - The path to be removed recursively.
|
|
76
|
+
* @param path - The path to be removed recursively.
|
|
79
77
|
*/
|
|
80
|
-
export async function rimraf(path) {
|
|
78
|
+
export async function rimraf(this: ADB, path: string): Promise<void> {
|
|
81
79
|
await this.shell(['rm', '-rf', path]);
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
/**
|
|
85
83
|
* Send a file to the device under test.
|
|
86
84
|
*
|
|
87
|
-
* @
|
|
88
|
-
* @param
|
|
89
|
-
* @param
|
|
90
|
-
* @param {object} [opts] - Additional options mapping. See
|
|
85
|
+
* @param localPath - The path to the file on the local file system.
|
|
86
|
+
* @param remotePath - The destination path on the remote device.
|
|
87
|
+
* @param opts - Additional options mapping. See
|
|
91
88
|
* https://github.com/appium/node-teen_process,
|
|
92
89
|
* _exec_ method options, for more information about available
|
|
93
90
|
* options.
|
|
94
91
|
*/
|
|
95
|
-
export async function push(
|
|
92
|
+
export async function push(
|
|
93
|
+
this: ADB,
|
|
94
|
+
localPath: string,
|
|
95
|
+
remotePath: string,
|
|
96
|
+
opts?: TeenProcessExecOptions,
|
|
97
|
+
): Promise<void> {
|
|
96
98
|
await this.mkdir(path.posix.dirname(remotePath));
|
|
97
99
|
await this.adbExec(['push', localPath, remotePath], opts);
|
|
98
100
|
}
|
|
@@ -100,15 +102,19 @@ export async function push(localPath, remotePath, opts) {
|
|
|
100
102
|
/**
|
|
101
103
|
* Receive a file from the device under test.
|
|
102
104
|
*
|
|
103
|
-
* @
|
|
104
|
-
* @param
|
|
105
|
-
* @param
|
|
106
|
-
* @param {import('teen_process').TeenProcessExecOptions} [opts={}] - Additional options mapping. See
|
|
105
|
+
* @param remotePath - The source path on the remote device.
|
|
106
|
+
* @param localPath - The destination path to the file on the local file system.
|
|
107
|
+
* @param opts - Additional options mapping. See
|
|
107
108
|
* https://github.com/appium/node-teen_process,
|
|
108
109
|
* _exec_ method options, for more information about available
|
|
109
110
|
* options.
|
|
110
111
|
*/
|
|
111
|
-
export async function pull(
|
|
112
|
+
export async function pull(
|
|
113
|
+
this: ADB,
|
|
114
|
+
remotePath: string,
|
|
115
|
+
localPath: string,
|
|
116
|
+
opts: TeenProcessExecOptions = {},
|
|
117
|
+
): Promise<void> {
|
|
112
118
|
// pull folder can take more time, increasing time out to 60 secs
|
|
113
119
|
await this.adbExec(['pull', remotePath, localPath], {...opts, timeout: opts.timeout ?? 60000});
|
|
114
120
|
}
|
|
@@ -116,12 +122,12 @@ export async function pull(remotePath, localPath, opts = {}) {
|
|
|
116
122
|
/**
|
|
117
123
|
* Recursively create a new folder on the device under test.
|
|
118
124
|
*
|
|
119
|
-
* @
|
|
120
|
-
* @
|
|
121
|
-
* @return {Promise<string>} mkdir command output.
|
|
125
|
+
* @param remotePath - The new path to be created.
|
|
126
|
+
* @return mkdir command output.
|
|
122
127
|
*/
|
|
123
|
-
export async function mkdir(remotePath) {
|
|
128
|
+
export async function mkdir(this: ADB, remotePath: string): Promise<string> {
|
|
124
129
|
return /\s+/.test(remotePath)
|
|
125
130
|
? await this.shell([`mkdir -p '${remotePath.replace(/'/g, `\\'`)}'`])
|
|
126
131
|
: await this.shell(['mkdir', '-p', remotePath]);
|
|
127
132
|
}
|
|
133
|
+
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {log} from '../logger.js';
|
|
2
2
|
import _ from 'lodash';
|
|
3
3
|
import {fs, util} from '@appium/support';
|
|
4
|
-
import {SubProcess, exec} from 'teen_process';
|
|
4
|
+
import {SubProcess, exec, type ExecError} from 'teen_process';
|
|
5
|
+
import type {ADB} from '../adb.js';
|
|
6
|
+
import type {ScreenrecordOptions, StringRecord} from './types.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Get the path to adb executable amd assign it
|
|
8
10
|
* to this.executable.path and this.binaries.adb properties.
|
|
9
11
|
*
|
|
10
|
-
* @
|
|
11
|
-
* @return {Promise<import('../adb.js').ADB>} ADB instance.
|
|
12
|
+
* @return ADB instance.
|
|
12
13
|
*/
|
|
13
|
-
export async function getAdbWithCorrectAdbPath() {
|
|
14
|
+
export async function getAdbWithCorrectAdbPath(this: ADB): Promise<ADB> {
|
|
14
15
|
this.executable.path = await this.getSdkBinaryPath('adb');
|
|
15
16
|
return this;
|
|
16
17
|
}
|
|
@@ -18,39 +19,35 @@ export async function getAdbWithCorrectAdbPath() {
|
|
|
18
19
|
/**
|
|
19
20
|
* Get the full path to aapt tool and assign it to
|
|
20
21
|
* this.binaries.aapt property
|
|
21
|
-
* @this {import('../adb.js').ADB}
|
|
22
22
|
*/
|
|
23
|
-
export async function initAapt() {
|
|
23
|
+
export async function initAapt(this: ADB): Promise<void> {
|
|
24
24
|
await this.getSdkBinaryPath('aapt');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Get the full path to aapt2 tool and assign it to
|
|
29
29
|
* this.binaries.aapt2 property
|
|
30
|
-
* @this {import('../adb.js').ADB}
|
|
31
30
|
*/
|
|
32
|
-
export async function initAapt2() {
|
|
31
|
+
export async function initAapt2(this: ADB): Promise<void> {
|
|
33
32
|
await this.getSdkBinaryPath('aapt2');
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
36
|
* Get the full path to zipalign tool and assign it to
|
|
38
37
|
* this.binaries.zipalign property
|
|
39
|
-
* @this {import('../adb.js').ADB}
|
|
40
38
|
*/
|
|
41
|
-
export async function initZipAlign() {
|
|
39
|
+
export async function initZipAlign(this: ADB): Promise<void> {
|
|
42
40
|
await this.getSdkBinaryPath('zipalign');
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
/**
|
|
46
44
|
* Get the full path to bundletool binary and assign it to
|
|
47
45
|
* this.binaries.bundletool property
|
|
48
|
-
* @this {import('../adb.js').ADB}
|
|
49
46
|
*/
|
|
50
|
-
export async function initBundletool() {
|
|
47
|
+
export async function initBundletool(this: ADB): Promise<void> {
|
|
51
48
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
const binaries = this.binaries as StringRecord;
|
|
50
|
+
binaries.bundletool = await fs.which('bundletool.jar');
|
|
54
51
|
} catch {
|
|
55
52
|
throw new Error(
|
|
56
53
|
'bundletool.jar binary is expected to be present in PATH. ' +
|
|
@@ -62,12 +59,11 @@ export async function initBundletool() {
|
|
|
62
59
|
/**
|
|
63
60
|
* Retrieve the API level of the device under test.
|
|
64
61
|
*
|
|
65
|
-
* @
|
|
66
|
-
* @return {Promise<number>} The API level as integer number, for example 21 for
|
|
62
|
+
* @return The API level as integer number, for example 21 for
|
|
67
63
|
* Android Lollipop. The result of this method is cached, so all the further
|
|
68
64
|
* calls return the same value as the first one.
|
|
69
65
|
*/
|
|
70
|
-
export async function getApiLevel() {
|
|
66
|
+
export async function getApiLevel(this: ADB): Promise<number> {
|
|
71
67
|
if (!_.isInteger(this._apiLevel)) {
|
|
72
68
|
try {
|
|
73
69
|
const strOutput = await this.getDeviceProperty('ro.build.version.sdk');
|
|
@@ -92,21 +88,20 @@ export async function getApiLevel() {
|
|
|
92
88
|
}
|
|
93
89
|
} catch (e) {
|
|
94
90
|
throw new Error(
|
|
95
|
-
`Error getting device API level. Original error: ${
|
|
91
|
+
`Error getting device API level. Original error: ${(e as Error).message}`,
|
|
96
92
|
);
|
|
97
93
|
}
|
|
98
94
|
}
|
|
99
|
-
return
|
|
95
|
+
return this._apiLevel as number;
|
|
100
96
|
}
|
|
101
97
|
|
|
102
98
|
/**
|
|
103
99
|
* Verify whether a device is connected.
|
|
104
100
|
*
|
|
105
|
-
* @
|
|
106
|
-
* @return {Promise<boolean>} True if at least one device is visible to adb.
|
|
101
|
+
* @return True if at least one device is visible to adb.
|
|
107
102
|
*/
|
|
108
|
-
export async function isDeviceConnected() {
|
|
109
|
-
|
|
103
|
+
export async function isDeviceConnected(this: ADB): Promise<boolean> {
|
|
104
|
+
const devices = await this.getConnectedDevices();
|
|
110
105
|
return devices.length > 0;
|
|
111
106
|
}
|
|
112
107
|
|
|
@@ -114,16 +109,15 @@ export async function isDeviceConnected() {
|
|
|
114
109
|
* Clear the active text field on the device under test by sending
|
|
115
110
|
* special keyevents to it.
|
|
116
111
|
*
|
|
117
|
-
* @
|
|
118
|
-
* @param {number} [length=100] - The maximum length of the text in the field to be cleared.
|
|
112
|
+
* @param length - The maximum length of the text in the field to be cleared.
|
|
119
113
|
*/
|
|
120
|
-
export async function clearTextField(length = 100) {
|
|
114
|
+
export async function clearTextField(this: ADB, length: number = 100): Promise<void> {
|
|
121
115
|
// assumes that the EditText field already has focus
|
|
122
116
|
log.debug(`Clearing up to ${length} characters`);
|
|
123
117
|
if (length === 0) {
|
|
124
118
|
return;
|
|
125
119
|
}
|
|
126
|
-
|
|
120
|
+
const args = ['input', 'keyevent'];
|
|
127
121
|
for (let i = 0; i < length; i++) {
|
|
128
122
|
// we cannot know where the cursor is in the text field, so delete both before
|
|
129
123
|
// and after so that we get rid of everything
|
|
@@ -137,9 +131,8 @@ export async function clearTextField(length = 100) {
|
|
|
137
131
|
/**
|
|
138
132
|
* Send the special keycode to the device under test in order to emulate
|
|
139
133
|
* Back button tap.
|
|
140
|
-
* @this {import('../adb.js').ADB}
|
|
141
134
|
*/
|
|
142
|
-
export async function back() {
|
|
135
|
+
export async function back(this: ADB): Promise<void> {
|
|
143
136
|
log.debug('Pressing the BACK button');
|
|
144
137
|
await this.keyevent(4);
|
|
145
138
|
}
|
|
@@ -147,35 +140,32 @@ export async function back() {
|
|
|
147
140
|
/**
|
|
148
141
|
* Send the special keycode to the device under test in order to emulate
|
|
149
142
|
* Home button tap.
|
|
150
|
-
* @this {import('../adb.js').ADB}
|
|
151
143
|
*/
|
|
152
|
-
export async function goToHome() {
|
|
144
|
+
export async function goToHome(this: ADB): Promise<void> {
|
|
153
145
|
log.debug('Pressing the HOME button');
|
|
154
146
|
await this.keyevent(3);
|
|
155
147
|
}
|
|
156
148
|
|
|
157
149
|
/**
|
|
158
|
-
* @
|
|
159
|
-
* @return {string} the actual path to adb executable.
|
|
150
|
+
* @return the actual path to adb executable.
|
|
160
151
|
*/
|
|
161
|
-
export function getAdbPath() {
|
|
152
|
+
export function getAdbPath(this: ADB): string {
|
|
162
153
|
return this.executable.path;
|
|
163
154
|
}
|
|
164
155
|
|
|
165
156
|
/**
|
|
166
157
|
* Restart the device under test using adb commands.
|
|
167
158
|
*
|
|
168
|
-
* @this {import('../adb.js').ADB}
|
|
169
159
|
* @throws {Error} If start fails.
|
|
170
160
|
*/
|
|
171
|
-
export async function restart() {
|
|
161
|
+
export async function restart(this: ADB): Promise<void> {
|
|
172
162
|
try {
|
|
173
163
|
await this.stopLogcat();
|
|
174
164
|
await this.restartAdb();
|
|
175
165
|
await this.waitForDevice(60);
|
|
176
166
|
await this.startLogcat(this._logcatStartupParams);
|
|
177
167
|
} catch (e) {
|
|
178
|
-
const err =
|
|
168
|
+
const err = e as Error;
|
|
179
169
|
throw new Error(`Restart failed. Original error: ${err.message}`);
|
|
180
170
|
}
|
|
181
171
|
}
|
|
@@ -184,25 +174,27 @@ export async function restart() {
|
|
|
184
174
|
* Retrieve the `adb bugreport` command output. This
|
|
185
175
|
* operation may take up to several minutes.
|
|
186
176
|
*
|
|
187
|
-
* @
|
|
188
|
-
* @
|
|
189
|
-
* @returns {Promise<string>} Command stdout
|
|
177
|
+
* @param timeout - Command timeout in milliseconds
|
|
178
|
+
* @returns Command stdout
|
|
190
179
|
*/
|
|
191
|
-
export async function bugreport(timeout = 120000) {
|
|
180
|
+
export async function bugreport(this: ADB, timeout: number = 120000): Promise<string> {
|
|
192
181
|
return await this.adbExec(['bugreport'], {timeout});
|
|
193
182
|
}
|
|
194
183
|
|
|
195
184
|
/**
|
|
196
185
|
* Initiate screenrecord utility on the device
|
|
197
186
|
*
|
|
198
|
-
* @
|
|
199
|
-
* @param {string} destination - Full path to the writable media file destination
|
|
187
|
+
* @param destination - Full path to the writable media file destination
|
|
200
188
|
* on the device file system.
|
|
201
|
-
* @param
|
|
202
|
-
* @returns
|
|
189
|
+
* @param options - Screenrecord options
|
|
190
|
+
* @returns screenrecord process, which can be then controlled by the client code
|
|
203
191
|
*/
|
|
204
|
-
export function screenrecord(
|
|
205
|
-
|
|
192
|
+
export function screenrecord(
|
|
193
|
+
this: ADB,
|
|
194
|
+
destination: string,
|
|
195
|
+
options: ScreenrecordOptions = {},
|
|
196
|
+
): SubProcess {
|
|
197
|
+
const cmd: string[] = ['screenrecord'];
|
|
206
198
|
const {videoSize, bitRate, timeLimit, bugReport} = options;
|
|
207
199
|
if (util.hasValue(videoSize)) {
|
|
208
200
|
cmd.push('--size', videoSize);
|
|
@@ -226,8 +218,7 @@ export function screenrecord(destination, options = {}) {
|
|
|
226
218
|
/**
|
|
227
219
|
* Retrieves the list of features supported by the device under test
|
|
228
220
|
*
|
|
229
|
-
* @
|
|
230
|
-
* @returns {Promise<string[]>} the list of supported feature names or an empty list.
|
|
221
|
+
* @returns the list of supported feature names or an empty list.
|
|
231
222
|
* An example adb command output:
|
|
232
223
|
* ```
|
|
233
224
|
* cmd
|
|
@@ -243,7 +234,7 @@ export function screenrecord(destination, options = {}) {
|
|
|
243
234
|
* ```
|
|
244
235
|
* @throws {Error} if there was an error while retrieving the list
|
|
245
236
|
*/
|
|
246
|
-
export async function listFeatures() {
|
|
237
|
+
export async function listFeatures(this: ADB): Promise<string[]> {
|
|
247
238
|
this._memoizedFeatures =
|
|
248
239
|
this._memoizedFeatures ||
|
|
249
240
|
_.memoize(
|
|
@@ -251,12 +242,16 @@ export async function listFeatures() {
|
|
|
251
242
|
() => this.curDeviceId,
|
|
252
243
|
);
|
|
253
244
|
try {
|
|
254
|
-
|
|
245
|
+
const memoizedFeatures = this._memoizedFeatures;
|
|
246
|
+
if (!memoizedFeatures) {
|
|
247
|
+
throw new Error('Memoized features function is not initialized');
|
|
248
|
+
}
|
|
249
|
+
return (await memoizedFeatures())
|
|
255
250
|
.split(/\s+/)
|
|
256
251
|
.map((x) => x.trim())
|
|
257
252
|
.filter(Boolean);
|
|
258
253
|
} catch (e) {
|
|
259
|
-
const err =
|
|
254
|
+
const err = e as ExecError;
|
|
260
255
|
if (_.includes(err.stderr, 'unknown command')) {
|
|
261
256
|
return [];
|
|
262
257
|
}
|
|
@@ -274,11 +269,10 @@ export async function listFeatures() {
|
|
|
274
269
|
* See https://github.com/aosp-mirror/platform_system_core/blob/master/adb/client/adb_install.cpp
|
|
275
270
|
* for more details
|
|
276
271
|
*
|
|
277
|
-
* @
|
|
278
|
-
* @returns {Promise<boolean>} `true` if the feature is supported by both adb and the
|
|
272
|
+
* @returns `true` if the feature is supported by both adb and the
|
|
279
273
|
* device under test
|
|
280
274
|
*/
|
|
281
|
-
export async function isStreamedInstallSupported() {
|
|
275
|
+
export async function isStreamedInstallSupported(this: ADB): Promise<boolean> {
|
|
282
276
|
const proto = Object.getPrototypeOf(this);
|
|
283
277
|
proto._helpOutput = proto._helpOutput || (await this.adbExec(['help']));
|
|
284
278
|
return proto._helpOutput.includes('--streaming') && (await this.listFeatures()).includes('cmd');
|
|
@@ -289,11 +283,10 @@ export async function isStreamedInstallSupported() {
|
|
|
289
283
|
* Read https://developer.android.com/preview/features#incremental
|
|
290
284
|
* for more details on it.
|
|
291
285
|
*
|
|
292
|
-
* @
|
|
293
|
-
* @returns {Promise<boolean>} `true` if the feature is supported by both adb and the
|
|
286
|
+
* @returns `true` if the feature is supported by both adb and the
|
|
294
287
|
* device under test
|
|
295
288
|
*/
|
|
296
|
-
export async function isIncrementalInstallSupported() {
|
|
289
|
+
export async function isIncrementalInstallSupported(this: ADB): Promise<boolean> {
|
|
297
290
|
const {binary} = await this.getVersion();
|
|
298
291
|
if (!binary) {
|
|
299
292
|
return false;
|
|
@@ -307,29 +300,38 @@ export async function isIncrementalInstallSupported() {
|
|
|
307
300
|
/**
|
|
308
301
|
* Takes a screenshot of the given display or the default display.
|
|
309
302
|
*
|
|
310
|
-
* @
|
|
311
|
-
* @param {number|string?} displayId A valid display identifier. If
|
|
303
|
+
* @param displayId A valid display identifier. If
|
|
312
304
|
* no identifier is provided then the screenshot of the default display is returned.
|
|
313
305
|
* Note that only recent Android APIs provide multi-screen support.
|
|
314
|
-
* @returns
|
|
306
|
+
* @returns PNG screenshot payload
|
|
315
307
|
*/
|
|
316
|
-
export async function takeScreenshot(
|
|
308
|
+
export async function takeScreenshot(
|
|
309
|
+
this: ADB,
|
|
310
|
+
displayId?: number | string,
|
|
311
|
+
): Promise<Buffer> {
|
|
317
312
|
const args = [...this.executable.defaultArgs, 'exec-out', 'screencap', '-p'];
|
|
318
313
|
// @ts-ignore This validation works as expected
|
|
319
|
-
const displayIdStr = isNaN(displayId) ? null : `${displayId}`;
|
|
314
|
+
const displayIdStr = isNaN(Number(displayId)) ? null : `${displayId}`;
|
|
320
315
|
if (displayIdStr) {
|
|
321
316
|
args.push('-d', displayIdStr);
|
|
322
317
|
}
|
|
323
|
-
const displayDescr = displayIdStr ?
|
|
324
|
-
let stdout;
|
|
318
|
+
const displayDescr = displayIdStr ? `display #${displayIdStr}` : 'default display';
|
|
319
|
+
let stdout: Buffer;
|
|
325
320
|
try {
|
|
326
321
|
({stdout} = await exec(this.executable.path, args, {encoding: 'binary', isBuffer: true}));
|
|
327
322
|
} catch (e) {
|
|
328
|
-
const err =
|
|
323
|
+
const err = e as ExecError;
|
|
324
|
+
let outputStr = '';
|
|
325
|
+
if (err.stderr) {
|
|
326
|
+
const stderr = err.stderr as string | Buffer;
|
|
327
|
+
outputStr = typeof stderr === 'string' ? stderr : stderr.toString('utf-8');
|
|
328
|
+
} else if (err.stdout) {
|
|
329
|
+
const stdout = err.stdout as string | Buffer;
|
|
330
|
+
outputStr = typeof stdout === 'string' ? stdout : stdout.toString('utf-8');
|
|
331
|
+
}
|
|
329
332
|
throw new Error(
|
|
330
333
|
`Screenshot of the ${displayDescr} failed. ` +
|
|
331
|
-
|
|
332
|
-
`Code: '${err.code}', output: '${(err.stderr.length ? err.stderr : err.stdout).toString('utf-8')}'`,
|
|
334
|
+
`Code: '${err.code ?? 'unknown'}', output: '${outputStr}'`,
|
|
333
335
|
);
|
|
334
336
|
}
|
|
335
337
|
if (stdout.length === 0) {
|
|
@@ -337,3 +339,4 @@ export async function takeScreenshot(displayId) {
|
|
|
337
339
|
}
|
|
338
340
|
return stdout;
|
|
339
341
|
}
|
|
342
|
+
|