Skip to content

Commit 70ed466

Browse files
feat: Add mobile wrappers for file actions (#760)
1 parent 961384f commit 70ed466

File tree

4 files changed

+91
-22
lines changed

4 files changed

+91
-22
lines changed

lib/commands/app-management.js

+1-16
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,13 @@ import { waitForCondition } from 'asyncbox';
33
import { util } from 'appium/support';
44
import { APP_STATE } from '../android-helpers';
55
import { errors } from 'appium/driver';
6+
import { requireArgs } from '../utils';
67

78
const APP_EXTENSIONS = ['.apk', '.apks'];
89
const RESOLVER_ACTIVITY_NAME = 'android/com.android.internal.app.ResolverActivity';
910

1011
const commands = {};
1112

12-
/**
13-
* Assert the presence of particular keys in the given object
14-
*
15-
* @param {string|Array<string>} argNames one or more key names
16-
* @param {Object} opts the object to check
17-
* @returns {Object} the same given object
18-
*/
19-
function requireArgs (argNames, opts = {}) {
20-
for (const argName of (_.isArray(argNames) ? argNames : [argNames])) {
21-
if (!_.has(opts, argName)) {
22-
throw new errors.InvalidArgumentError(`'${argName}' argument must be provided`);
23-
}
24-
}
25-
return opts;
26-
}
27-
2813
/**
2914
* Verify whether an application is installed or not
3015
*

lib/commands/execute.js

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ extensions.executeMobile = async function executeMobile (mobileCommand, opts = {
4646

4747
listSms: 'mobileListSms',
4848

49+
pushFile: 'mobilePushFile',
50+
pullFile: 'mobilePullFile',
51+
pullFolder: 'mobilePullFolder',
4952
deleteFile: 'mobileDeleteFile',
5053

5154
isAppInstalled: 'mobileIsAppInstalled',

lib/commands/file-actions.js

+68-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import _ from 'lodash';
22
import { fs, util, zip, tempDir } from 'appium/support';
33
import path from 'path';
44
import { errors } from 'appium/driver';
5+
import { requireArgs } from '../utils';
56

67

78
const CONTAINER_PATH_MARKER = '@';
@@ -115,9 +116,31 @@ commands.pullFile = async function pullFile (remotePath) {
115116
};
116117

117118
/**
118-
* Pushed the given data to a file on the remote device
119+
* @typedef {Object} PullFileOptions
120+
* @property {string} remotePath The full path to the remote file
121+
* or a specially formatted path, which points to an item inside an app bundle,
122+
* for example `@my.app.id/my/path`. It is mandatory for the app bundle to have
123+
* debugging enabled in order to use the latter remotePath format.
124+
*/
125+
126+
/**
127+
* Pulls a remote file from the device.
128+
*
129+
* @param {PullFileOptions} opts
130+
* @returns {string} The same as `pullFile`
131+
*/
132+
commands.mobilePullFile = async function mobilePullFile (opts = {}) {
133+
const { remotePath } = requireArgs('remotePath', opts);
134+
return await this.pullFile(remotePath);
135+
};
136+
137+
/**
138+
* Pushes the given data to a file on the remote device
119139
* It is required, that a package has debugging flag enabled
120140
* in order to access its files.
141+
* After a file is pushed it gets automatically scanned for possible
142+
* media occurrences. If the scan succeeds then the file is added to the
143+
* media library.
121144
*
122145
* @param {string} remotePath The full path to the remote file or
123146
* a file inside a package bundle
@@ -181,6 +204,25 @@ commands.pushFile = async function pushFile (remotePath, base64Data) {
181204
}
182205
};
183206

207+
/**
208+
* @typedef {Object} PushFileOptions
209+
* @property {string} remotePath The full path to the remote file
210+
* or a specially formatted path, which points to an item inside an app bundle,
211+
* for example `@my.app.id/my/path`. It is mandatory for the app bundle to have
212+
* debugging enabled in order to use the latter remotePath format.
213+
* @property {string} payload Base64-encoded content of the file to be pushed.
214+
*/
215+
216+
/**
217+
* Pushes the given data to a file on the remote device.
218+
*
219+
* @param {PushFileOptions} opts
220+
*/
221+
commands.mobilePushFile = async function mobilePushFile (opts = {}) {
222+
const { remotePath, payload } = requireArgs(['remotePath', 'payload'], opts);
223+
return await this.pushFile(remotePath, payload);
224+
};
225+
184226
/**
185227
* Pulls the whole folder from the remote device
186228
*
@@ -190,11 +232,31 @@ commands.pushFile = async function pushFile (remotePath, base64Data) {
190232
* @throws {Error} If there was a failure while getting the folder content
191233
*/
192234
commands.pullFolder = async function pullFolder (remotePath) {
193-
let localFolder = await tempDir.path({prefix: 'appium'});
194-
await this.adb.pull(remotePath, localFolder);
195-
return (await zip.toInMemoryZip(localFolder, {
196-
encodeToBase64: true,
197-
})).toString();
235+
const tmpRoot = await tempDir.openDir();
236+
try {
237+
await this.adb.pull(remotePath, tmpRoot);
238+
return (await zip.toInMemoryZip(tmpRoot, {
239+
encodeToBase64: true,
240+
})).toString();
241+
} finally {
242+
await fs.rimraf(tmpRoot);
243+
}
244+
};
245+
246+
/**
247+
* @typedef {Object} PullFolderOptions
248+
* @property {string} remotePath The full path to the remote folder.
249+
*/
250+
251+
/**
252+
* Pulls the whole folder from the device under test.
253+
*
254+
* @param {PullFolderOptions} opts
255+
* @returns {string} The same as `pullFolder`
256+
*/
257+
commands.mobilePullFolder = async function mobilePullFolder (opts = {}) {
258+
const { remotePath } = requireArgs('remotePath', opts);
259+
return await this.pullFolder(remotePath);
198260
};
199261

200262
/**

lib/utils.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import _ from 'lodash';
2+
import { errors } from 'appium/driver';
3+
4+
5+
/**
6+
* Assert the presence of particular keys in the given object
7+
*
8+
* @param {string|Array<string>} argNames one or more key names
9+
* @param {Object} opts the object to check
10+
* @returns {Object} the same given object
11+
*/
12+
export function requireArgs (argNames, opts = {}) {
13+
for (const argName of (_.isArray(argNames) ? argNames : [argNames])) {
14+
if (!_.has(opts, argName)) {
15+
throw new errors.InvalidArgumentError(`'${argName}' argument must be provided`);
16+
}
17+
}
18+
return opts;
19+
}

0 commit comments

Comments
 (0)