How to use this.adb.rimraf method in Appium Android Driver

Best JavaScript code snippet using appium-android-driver

Run Appium Android Driver automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

coverage.js

Source: coverage.js Github

copy
1import temp from 'temp';
2import { fs } from 'appium-support';
3import log from '../logger';
4
5
6let commands = {}, helpers = {}, extensions = {};
7
8async function unlinkFile (file) {
9  if (await fs.exists(file)) {
10    await fs.unlink(file);
11  }
12}
13
14commands.endCoverage = async function (intentToBroadcast, ecOnDevicePath) {
15  let localFile = temp.path({prefix: 'appium', suffix: '.ec'});
16  await unlinkFile(localFile);
17
18  let b64data = '';
19  try {
20    // ensure the ec we're pulling is newly created as a result of the intent.
21    await this.adb.rimraf(ecOnDevicePath);
22
23    await this.adb.broadcastProcessEnd(intentToBroadcast, this.appProcess);
24
25    await this.adb.pull(ecOnDevicePath, localFile);
26    let data = await fs.readFile(localFile);
27    b64data = new Buffer(data).toString('base64');
28    await unlinkFile(localFile);
29  } catch (err) {
30    log.debug(`Error ending test coverage: ${err.message}`);
31  }
32  return b64data;
33};
34
35
36Object.assign(extensions, commands, helpers);
37export { commands, helpers };
38export default extensions;
39
Full Screen

recordscreen.js

Source: recordscreen.js Github

copy
1import _ from 'lodash';
2import _fs from 'fs';
3import url from 'url';
4import { waitForCondition } from 'asyncbox';
5import { util, fs, net, tempDir, system } from 'appium-support';
6import log from '../logger';
7import { exec } from 'teen_process';
8import path from 'path';
9import v8 from 'v8';
10
11
12let commands = {}, extensions = {};
13
14const RETRY_PAUSE = 300;
15const RETRY_TIMEOUT = 5000;
16const MAX_RECORDING_TIME_SEC = 60 * 3;
17const MAX_TIME_SEC = 60 * 30;
18const DEFAULT_RECORDING_TIME_SEC = MAX_RECORDING_TIME_SEC;
19const PROCESS_SHUTDOWN_TIMEOUT = 10 * 1000;
20const SCREENRECORD_BINARY = 'screenrecord';
21const DEFAULT_EXT = '.mp4';
22const MIN_EMULATOR_API_LEVEL = 27;
23const FFMPEG_BINARY = `ffmpeg${system.isWindows() ? '.exe' : ''}`;
24
25async function uploadRecordedMedia (adb, localFile, remotePath = null, uploadOptions = {}) {
26  const {size} = await fs.stat(localFile);
27  log.debug(`The size of the resulting screen recording is ${util.toReadableSizeString(size)}`);
28  if (_.isEmpty(remotePath)) {
29    const maxMemoryLimit = v8.getHeapStatistics().total_available_size / 2;
30    if (size >= maxMemoryLimit) {
31      log.info(`The file might be too large to fit into the process memory ` +
32        `(${util.toReadableSizeString(size)} >= ${util.toReadableSizeString(maxMemoryLimit)}). ` +
33        `Provide a link to a remote writable location for video upload ` +
34        `(http(s) and ftp protocols are supported) if you experience Out Of Memory errors`);
35    }
36    return (await fs.readFile(localFile)).toString('base64');
37  }
38
39  const remoteUrl = url.parse(remotePath);
40  let options = {};
41  const {user, pass, method} = uploadOptions;
42  if (remoteUrl.protocol.startsWith('http')) {
43    options = {
44      url: remoteUrl.href,
45      method: method || 'PUT',
46      multipart: [{ body: _fs.createReadStream(localFile) }],
47    };
48    if (user && pass) {
49      options.auth = {user, pass};
50    }
51  } else if (remoteUrl.protocol.startsWith('ftp')) {
52    options = {
53      host: remoteUrl.hostname,
54      port: remoteUrl.port || 21,
55    };
56    if (user && pass) {
57      options.user = user;
58      options.pass = pass;
59    }
60  }
61  await net.uploadFile(localFile, remotePath, options);
62  return '';
63}
64
65async function verifyScreenRecordIsSupported (adb, isEmulator) {
66  const apiLevel = await adb.getApiLevel();
67  if (isEmulator && apiLevel < MIN_EMULATOR_API_LEVEL) {
68    throw new Error(`Screen recording does not work on emulators running Android API level less than ${MIN_EMULATOR_API_LEVEL}`);
69  }
70  if (apiLevel < 19) {
71    throw new Error(`Screen recording not available on API Level ${apiLevel}. Minimum API Level is 19.`);
72  }
73}
74
75async function scheduleScreenRecord (adb, recordingProperties) {
76  if (recordingProperties.stopped) {
77    return;
78  }
79
80  const {
81    startTimestamp,
82    videoSize,
83    bitRate,
84    timeLimit,
85    bugReport,
86  } = recordingProperties;
87
88  let currentTimeLimit = MAX_RECORDING_TIME_SEC;
89  if (util.hasValue(recordingProperties.currentTimeLimit)) {
90    const currentTimeLimitInt = parseInt(recordingProperties.currentTimeLimit, 10);
91    if (!isNaN(currentTimeLimitInt) && currentTimeLimitInt < MAX_RECORDING_TIME_SEC) {
92      currentTimeLimit = currentTimeLimitInt;
93    }
94  }
95  const pathOnDevice = `/sdcard/${Math.floor(new Date())}${DEFAULT_EXT}`;
96  const recordingProc = adb.screenrecord(pathOnDevice, {
97    videoSize,
98    bitRate,
99    timeLimit: currentTimeLimit,
100    bugReport,
101  });
102
103  recordingProc.on('end', () => {
104    if (recordingProperties.stopped || !util.hasValue(timeLimit)) {
105      return;
106    }
107    const currentDuration = process.hrtime(startTimestamp)[0];
108    log.debug(`The overall screen recording duration is ${currentDuration}s so far`);
109    const timeLimitInt = parseInt(timeLimit, 10);
110    if (isNaN(timeLimitInt) || currentDuration >= timeLimitInt) {
111      log.debug('There is no need to start the next recording chunk');
112      return;
113    }
114
115    recordingProperties.currentTimeLimit = timeLimitInt - currentDuration;
116    const chunkDuration = recordingProperties.currentTimeLimit < MAX_RECORDING_TIME_SEC
117      ? recordingProperties.currentTimeLimit
118      : MAX_RECORDING_TIME_SEC;
119    log.debug(`Starting the next ${chunkDuration}s-chunk ` +
120      `of screen recording in order to achieve ${timeLimitInt}s total duration`);
121    scheduleScreenRecord(adb, recordingProperties)
122      .catch((e) => {
123        log.error(e.stack);
124        recordingProperties.stopped = true;
125      });
126  });
127
128  await recordingProc.start(0);
129  try {
130    await waitForCondition(async () => await adb.fileExists(pathOnDevice),
131      {waitMs: RETRY_TIMEOUT, intervalMs: RETRY_PAUSE});
132  } catch (e) {
133    throw new Error(`The expected screen record file '${pathOnDevice}' does not exist after ${RETRY_TIMEOUT}ms. ` +
134      `Is ${SCREENRECORD_BINARY} utility available and operational on the device under test?`);
135  }
136
137  recordingProperties.records.push(pathOnDevice);
138  recordingProperties.recordingProcess = recordingProc;
139}
140
141async function mergeScreenRecords (mediaFiles) {
142  try {
143    await fs.which(FFMPEG_BINARY);
144  } catch (e) {
145    throw new Error(`${FFMPEG_BINARY} utility is not available in PATH. Please install it from https://www.ffmpeg.org/`);
146  }
147  const configContent = mediaFiles
148    .map((x) => `file '${x}'`)
149    .join('\n');
150  const configFile = path.resolve(path.dirname(mediaFiles[0]), 'config.txt');
151  await fs.writeFile(configFile, configContent, 'utf8');
152  log.debug(`Generated ffmpeg merging config '${configFile}' with items:\n${configContent}`);
153  const result = path.resolve(path.dirname(mediaFiles[0]), `merge_${Math.floor(new Date())}${DEFAULT_EXT}`);
154  const args = ['-safe', '0', '-f', 'concat', '-i', configFile, '-c', 'copy', result];
155  log.info(`Initiating screen records merging using the command '${FFMPEG_BINARY} ${args.join(' ')}'`);
156  await exec(FFMPEG_BINARY, args);
157  return result;
158}
159
160async function terminateBackgroundScreenRecording (adb, force = true) {
161  const pids = (await adb.getPIDsByName(SCREENRECORD_BINARY))
162    .map((p) => `${p}`);
163  if (_.isEmpty(pids)) {
164    return false;
165  }
166
167  try {
168    await adb.shell(['kill', force ? '-15' : '-2', ...pids]);
169    await waitForCondition(async () => _.isEmpty(await adb.getPIDsByName(SCREENRECORD_BINARY)), {
170      waitMs: PROCESS_SHUTDOWN_TIMEOUT,
171      intervalMs: 500,
172    });
173    return true;
174  } catch (err) {
175    throw new Error(`Unable to stop the background screen recording: ${err.message}`);
176  }
177}
178
179
180/**
181 * @typedef {Object} StartRecordingOptions
182 *
183 * @property {?string} remotePath - The path to the remote location, where the captured video should be uploaded.
184 *                                  The following protocols are supported: http/https, ftp.
185 *                                  Null or empty string value (the default setting) means the content of resulting
186 *                                  file should be encoded as Base64 and passed as the endpount response value.
187 *                                  An exception will be thrown if the generated media file is too big to
188 *                                  fit into the available process memory.
189 *                                  This option only has an effect if there is screen recording process in progreess
190 *                                  and `forceRestart` parameter is not set to `true`.
191 * @property {?string} user - The name of the user for the remote authentication. Only works if `remotePath` is provided.
192 * @property {?string} pass - The password for the remote authentication. Only works if `remotePath` is provided.
193 * @property {?string} method - The http multipart upload method name. The 'PUT' one is used by default.
194 *                              Only works if `remotePath` is provided.
195 * @property {?string} videoSize - The format is widthxheight.
196 *                  The default value is the device's native display resolution (if supported),
197 *                  1280x720 if not. For best results,
198 *                  use a size supported by your device's Advanced Video Coding (AVC) encoder.
199 *                  For example, "1280x720"
200 * @property {?boolean} bugReport - Set it to `true` in order to display additional information on the video overlay,
201 *                                  such as a timestamp, that is helpful in videos captured to illustrate bugs.
202 *                                  This option is only supported since API level 27 (Android P).
203 * @property {?string|number} timeLimit - The maximum recording time, in seconds. The default value is 180 (3 minutes).
204 *                                        The maximum value is 1800 (30 minutes). If the passed value is greater than 180 then
205 *                                        the algorithm will try to schedule multiple screen recording chunks and merge the
206 *                                        resulting videos into a single media file using `ffmpeg` utility.
207 *                                        If the utility is not available in PATH then the most recent screen recording chunk is
208 *                                        going to be returned.
209 * @property {?string|number} bitRate - The video bit rate for the video, in megabits per second.
210 *                The default value is 4. You can increase the bit rate to improve video quality,
211 *                but doing so results in larger movie files.
212 * @property {?boolean} forceRestart - Whether to try to catch and upload/return the currently running screen recording
213 *                                     (`false`, the default setting) or ignore the result of it and start a new recording
214 *                                     immediately (`true`).
215 */
216
217/**
218 * Record the display of a real devices running Android 4.4 (API level 19) and higher.
219 * Emulators are supported since API level 27 (Android P).
220 * It records screen activity to an MPEG-4 file. Audio is not recorded with the video file.
221 * If screen recording has been already started then the command will stop it forcefully and start a new one.
222 * The previously recorded video file will be deleted.
223 *
224 * @param {?StartRecordingOptions} options - The available options.
225 * @returns {string} Base64-encoded content of the recorded media file if
226 *                   any screen recording is currently running or an empty string.
227 * @throws {Error} If screen recording has failed to start or is not supported on the device under test.
228 */
229commands.startRecordingScreen = async function startRecordingScreen (options = {}) {
230  await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
231
232  let result = '';
233  const {videoSize, timeLimit = DEFAULT_RECORDING_TIME_SEC, bugReport, bitRate, forceRestart} = options;
234  if (!forceRestart) {
235    result = await this.stopRecordingScreen(options);
236  }
237
238  if (await terminateBackgroundScreenRecording(this.adb, true)) {
239    log.warn(`There were some ${SCREENRECORD_BINARY} process leftovers running ` +
240      `in the background. Make sure you stop screen recording each time after it is started, ` +
241      `otherwise the recorded media might quickly exceed all the free space on the device under test.`);
242  }
243
244  if (!_.isEmpty(this._screenRecordingProperties)) {
245    for (const record of (this._screenRecordingProperties.records || [])) {
246      await this.adb.rimraf(record);
247    }
248    this._screenRecordingProperties = null;
249  }
250
251  const timeout = parseFloat(timeLimit);
252  if (isNaN(timeout) || timeout > MAX_TIME_SEC || timeout <= 0) {
253    throw new Error(`The timeLimit value must be in range [1, ${MAX_TIME_SEC}] seconds. ` +
254      `The value of '${timeLimit}' has been passed instead.`);
255  }
256
257  this._screenRecordingProperties = {
258    startTimestamp: process.hrtime(),
259    videoSize,
260    timeLimit,
261    currentTimeLimit: timeLimit,
262    bitRate,
263    bugReport,
264    records: [],
265    recordingProcess: null,
266    stopped: false,
267  };
268  await scheduleScreenRecord(this.adb, this._screenRecordingProperties);
269  return result;
270};
271
272/**
273 * @typedef {Object} StopRecordingOptions
274 *
275 * @property {?string} remotePath - The path to the remote location, where the resulting video should be uploaded.
276 *                                  The following protocols are supported: http/https, ftp.
277 *                                  Null or empty string value (the default setting) means the content of resulting
278 *                                  file should be encoded as Base64 and passed as the endpount response value.
279 *                                  An exception will be thrown if the generated media file is too big to
280 *                                  fit into the available process memory.
281 * @property {?string} user - The name of the user for the remote authentication.
282 * @property {?string} pass - The password for the remote authentication.
283 * @property {?string} method - The http multipart upload method name. The 'PUT' one is used by default.
284 */
285
286/**
287 * Stop recording the screen.
288 * If no screen recording has been started before then the method returns an empty string.
289 *
290 * @param {?StopRecordingOptions} options - The available options.
291 * @returns {string} Base64-encoded content of the recorded media file if 'remotePath'
292 *                   parameter is falsy or an empty string.
293 * @throws {Error} If there was an error while getting the name of a media file
294 *                 or the file content cannot be uploaded to the remote location
295 *                 or screen recording is not supported on the device under test.
296 */
297commands.stopRecordingScreen = async function stopRecordingScreen (options = {}) {
298  await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
299
300  if (!_.isEmpty(this._screenRecordingProperties)) {
301    this._screenRecordingProperties.stopped = true;
302  }
303
304  try {
305    await terminateBackgroundScreenRecording(this.adb, false);
306  } catch (err) {
307    log.warn(err.message);
308    if (!_.isEmpty(this._screenRecordingProperties)) {
309      log.warn('The resulting video might be corrupted');
310    }
311  }
312
313  if (_.isEmpty(this._screenRecordingProperties)) {
314    log.info(`Screen recording has not been previously started by Appium. There is nothing to stop`);
315    return '';
316  }
317
318  if (this._screenRecordingProperties.recordingProcess && this._screenRecordingProperties.recordingProcess.isRunning) {
319    try {
320      await this._screenRecordingProperties.recordingProcess.stop('SIGINT', PROCESS_SHUTDOWN_TIMEOUT);
321    } catch (e) {
322      log.errorAndThrow(`Unable to stop screen recording within ${PROCESS_SHUTDOWN_TIMEOUT}ms`);
323    }
324    this._screenRecordingProperties.recordingProcess = null;
325  }
326
327  if (_.isEmpty(this._screenRecordingProperties.records)) {
328    log.errorAndThrow(`No screen recordings have been stored on the device so far. ` +
329      `Are you sure the ${SCREENRECORD_BINARY} utility works as expected?`);
330  }
331
332  const tmpRoot = await tempDir.openDir();
333  try {
334    const localRecords = [];
335    for (const pathOnDevice of this._screenRecordingProperties.records) {
336      localRecords.push(path.resolve(tmpRoot, path.posix.basename(pathOnDevice)));
337      await this.adb.pull(pathOnDevice, _.last(localRecords));
338      await this.adb.rimraf(pathOnDevice);
339    }
340    let resultFilePath = _.last(localRecords);
341    if (localRecords.length > 1) {
342      log.info(`Got ${localRecords.length} screen recordings. Trying to merge them`);
343      try {
344        resultFilePath = await mergeScreenRecords(localRecords);
345      } catch (e) {
346        log.warn(`Cannot merge the recorded files. The most recent screen recording is going to be returned as the result. ` +
347          `Original error: ${e.message}`);
348      }
349    }
350    const {remotePath, user, pass, method} = options;
351    return await uploadRecordedMedia(this.adb, resultFilePath, remotePath, {user, pass, method});
352  } finally {
353    await fs.rimraf(tmpRoot);
354    this._screenRecordingProperties = null;
355  }
356};
357
358
359Object.assign(extensions, commands);
360export { commands };
361export default extensions;
362
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Appium Android Driver on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)