Skip to content

Commit

Permalink
feat: add mobileLock to handle an argument properly (#826)
Browse files Browse the repository at this point in the history
* feat: add mobileLock to handle an argument properly

* chore: tweak the default value

* fix docstring
  • Loading branch information
KazuCocoa authored Jun 20, 2023
1 parent 773a864 commit ee90f84
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 54 deletions.
146 changes: 100 additions & 46 deletions lib/commands/actions.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import androidHelpers from '../android-helpers';
import { fs, util, tempDir, imageUtil } from '@appium/support';
import {fs, util, tempDir, imageUtil} from '@appium/support';
import path from 'path';
import B from 'bluebird';
import { exec } from 'teen_process';
import { requireArgs } from '../utils';
import {exec} from 'teen_process';
import {requireArgs} from '../utils';

const swipeStepsPerSec = 28;
const dragStepsPerSec = 40;
Expand All @@ -12,29 +12,29 @@ const commands = {};
const helpers = {};
const extensions = {};

commands.keyevent = async function keyevent (keycode, metastate = null) {
commands.keyevent = async function keyevent(keycode, metastate = null) {
// TODO deprecate keyevent; currently wd only implements keyevent
this.log.warn('keyevent will be deprecated use pressKeyCode');
return await this.pressKeyCode(keycode, metastate);
};

commands.pressKeyCode = async function pressKeyCode (keycode, metastate = null) {
commands.pressKeyCode = async function pressKeyCode(keycode, metastate = null) {
return await this.bootstrap.sendAction('pressKeyCode', {keycode, metastate});
};

commands.longPressKeyCode = async function longPressKeyCode (keycode, metastate = null) {
commands.longPressKeyCode = async function longPressKeyCode(keycode, metastate = null) {
return await this.bootstrap.sendAction('longPressKeyCode', {keycode, metastate});
};

commands.getOrientation = async function getOrientation () {
commands.getOrientation = async function getOrientation() {
let params = {
naturalOrientation: !!this.opts.androidNaturalOrientation,
};
let orientation = await this.bootstrap.sendAction('orientation', params);
return orientation.toUpperCase();
};

commands.setOrientation = async function setOrientation (orientation) {
commands.setOrientation = async function setOrientation(orientation) {
orientation = orientation.toUpperCase();
let params = {
orientation,
Expand All @@ -43,24 +43,23 @@ commands.setOrientation = async function setOrientation (orientation) {
return await this.bootstrap.sendAction('orientation', params);
};

commands.fakeFlick = async function fakeFlick (xSpeed, ySpeed) {
commands.fakeFlick = async function fakeFlick(xSpeed, ySpeed) {
return await this.bootstrap.sendAction('flick', {xSpeed, ySpeed});
};

commands.fakeFlickElement = async function fakeFlickElement (elementId, xoffset, yoffset, speed) {
commands.fakeFlickElement = async function fakeFlickElement(elementId, xoffset, yoffset, speed) {
let params = {xoffset, yoffset, speed, elementId};
return await this.bootstrap.sendAction('element:flick', params);
};

commands.swipe = async function swipe (startX, startY, endX, endY, duration, touchCount, elId) {
commands.swipe = async function swipe(startX, startY, endX, endY, duration, touchCount, elId) {
if (startX === 'null') {
startX = 0.5;
}
if (startY === 'null') {
startY = 0.5;
}
let swipeOpts = {startX, startY, endX, endY,
steps: Math.round(duration * swipeStepsPerSec)};
let swipeOpts = {startX, startY, endX, endY, steps: Math.round(duration * swipeStepsPerSec)};
// going the long way and checking for undefined and null since
// we can't be assured `elId` is a string and not an int
if (util.hasValue(elId)) {
Expand All @@ -69,55 +68,104 @@ commands.swipe = async function swipe (startX, startY, endX, endY, duration, tou
return await this.doSwipe(swipeOpts);
};

commands.doSwipe = async function doSwipe (swipeOpts) {
commands.doSwipe = async function doSwipe(swipeOpts) {
if (util.hasValue(swipeOpts.elementId)) {
return await this.bootstrap.sendAction('element:swipe', swipeOpts);
} else {
return await this.bootstrap.sendAction('swipe', swipeOpts);
}
};

commands.pinchClose = async function pinchClose (startX, startY, endX, endY, duration, percent, steps, elId) {
commands.pinchClose = async function pinchClose(
startX,
startY,
endX,
endY,
duration,
percent,
steps,
elId
) {
let pinchOpts = {
direction: 'in',
elementId: elId,
percent,
steps
steps,
};
return await this.bootstrap.sendAction('element:pinch', pinchOpts);
};

commands.pinchOpen = async function pinchOpen (startX, startY, endX, endY, duration, percent, steps, elId) {
commands.pinchOpen = async function pinchOpen(
startX,
startY,
endX,
endY,
duration,
percent,
steps,
elId
) {
let pinchOpts = {direction: 'out', elementId: elId, percent, steps};
return await this.bootstrap.sendAction('element:pinch', pinchOpts);
};

commands.flick = async function flick (element, xSpeed, ySpeed, xOffset, yOffset, speed) {
commands.flick = async function flick(element, xSpeed, ySpeed, xOffset, yOffset, speed) {
if (element) {
await this.fakeFlickElement(element, xOffset, yOffset, speed);
} else {
await this.fakeFlick(xSpeed, ySpeed);
}
};

commands.drag = async function drag (startX, startY, endX, endY, duration, touchCount, elementId, destElId) {
commands.drag = async function drag(
startX,
startY,
endX,
endY,
duration,
touchCount,
elementId,
destElId
) {
let dragOpts = {
elementId, destElId, startX, startY, endX, endY,
steps: Math.round(duration * dragStepsPerSec)
elementId,
destElId,
startX,
startY,
endX,
endY,
steps: Math.round(duration * dragStepsPerSec),
};
return await this.doDrag(dragOpts);

};

commands.doDrag = async function doDrag (dragOpts) {
commands.doDrag = async function doDrag(dragOpts) {
if (util.hasValue(dragOpts.elementId)) {
return await this.bootstrap.sendAction('element:drag', dragOpts);
} else {
return await this.bootstrap.sendAction('drag', dragOpts);
}
};

commands.lock = async function lock (seconds) {
/**
* @typedef {Object} LockOptions
* @property {number?} seconds The number to keep the locked.
* 0 or empty value will keep the device locked.
*/

/**
* Lock the device (and optionally unlock it after a certain amount of time).
* @param {LockOptions} opts
* @throws {Error} if lock or unlock operation fails
*/

commands.mobileLock = async function mobileLock(opts = {}) {
const {seconds} = opts;
return await this.lock(seconds);
};

commands.lock = async function lock(seconds) {
await this.adb.lock();
if (isNaN(seconds)) {
return;
Expand All @@ -131,23 +179,23 @@ commands.lock = async function lock (seconds) {
await this.unlock();
};

commands.isLocked = async function isLocked () {
commands.isLocked = async function isLocked() {
return await this.adb.isScreenLocked();
};

commands.unlock = async function unlock () {
commands.unlock = async function unlock() {
return await androidHelpers.unlock(this, this.adb, this.caps);
};

commands.openNotifications = async function openNotifications () {
commands.openNotifications = async function openNotifications() {
return await this.bootstrap.sendAction('openNotification');
};

commands.setLocation = async function setLocation (latitude, longitude) {
commands.setLocation = async function setLocation(latitude, longitude) {
return await this.adb.sendTelnetCommand(`geo fix ${longitude} ${latitude}`);
};

commands.fingerprint = async function fingerprint (fingerprintId) {
commands.fingerprint = async function fingerprint(fingerprintId) {
if (!this.isEmulator()) {
this.log.errorAndThrow('fingerprint method is only available for emulators');
}
Expand All @@ -174,7 +222,7 @@ commands.mobileFingerprint = async function mobileFingerprint(opts = {}) {
await this.fingerprint(fingerprintId);
};

commands.sendSMS = async function sendSMS (phoneNumber, message) {
commands.sendSMS = async function sendSMS(phoneNumber, message) {
if (!this.isEmulator()) {
this.log.errorAndThrow('sendSMS method is only available for emulators');
}
Expand All @@ -198,7 +246,7 @@ commands.mobileSendSms = async function mobileSendSms(opts = {}) {
await this.sendSMS(phoneNumber, message);
};

commands.gsmCall = async function gsmCall (phoneNumber, action) {
commands.gsmCall = async function gsmCall(phoneNumber, action) {
if (!this.isEmulator()) {
this.log.errorAndThrow('gsmCall method is only available for emulators');
}
Expand All @@ -222,7 +270,7 @@ commands.mobileGsmCall = async function mobileGsmCall(opts = {}) {
await this.gsmCall(phoneNumber, action);
};

commands.gsmSignal = async function gsmSignal (signalStrengh) {
commands.gsmSignal = async function gsmSignal(signalStrengh) {
if (!this.isEmulator()) {
this.log.errorAndThrow('gsmSignal method is only available for emulators');
}
Expand All @@ -245,7 +293,7 @@ commands.mobileGsmSignal = async function mobileGsmSignal(opts = {}) {
await this.gsmSignal(strength);
};

commands.gsmVoice = async function gsmVoice (state) {
commands.gsmVoice = async function gsmVoice(state) {
if (!this.isEmulator()) {
this.log.errorAndThrow('gsmVoice method is only available for emulators');
}
Expand All @@ -268,7 +316,7 @@ commands.mobileGsmVoice = async function mobileGsmVoice(opts = {}) {
await this.gsmVoice(state);
};

commands.powerAC = async function powerAC (state) {
commands.powerAC = async function powerAC(state) {
if (!this.isEmulator()) {
this.log.errorAndThrow('powerAC method is only available for emulators');
}
Expand All @@ -291,7 +339,7 @@ commands.mobilePowerAc = async function mobilePowerAc(opts = {}) {
await this.powerAC(state);
};

commands.powerCapacity = async function powerCapacity (batteryPercent) {
commands.powerCapacity = async function powerCapacity(batteryPercent) {
if (!this.isEmulator()) {
this.log.errorAndThrow('powerCapacity method is only available for emulators');
}
Expand All @@ -314,7 +362,7 @@ commands.mobilePowerCapacity = async function mobilePowerCapacity(opts = {}) {
await this.powerCapacity(percent);
};

commands.networkSpeed = async function networkSpeed (networkSpeed) {
commands.networkSpeed = async function networkSpeed(networkSpeed) {
if (!this.isEmulator()) {
this.log.errorAndThrow('networkSpeed method is only available for emulators');
}
Expand Down Expand Up @@ -349,7 +397,7 @@ commands.mobileNetworkSpeed = async function mobileNetworkSpeed(opts = {}) {
* @throws {Error} - If value for the se sor is not defined
* @throws {Error} - If deviceType is not an emulator
*/
commands.sensorSet = async function sensorSet (sensor = {}) {
commands.sensorSet = async function sensorSet(sensor = {}) {
const {sensorType, value} = sensor;
if (!util.hasValue(sensorType)) {
this.log.errorAndThrow(`'sensorType' argument is required`);
Expand All @@ -368,7 +416,7 @@ commands.sensorSet = async function sensorSet (sensor = {}) {
* @param {Object} opts
* @returns {Promise<Buffer>}
*/
helpers.getScreenshotDataWithAdbShell = async function getScreenshotDataWithAdbShell (adb, opts) {
helpers.getScreenshotDataWithAdbShell = async function getScreenshotDataWithAdbShell(adb, opts) {
const localFile = await tempDir.path({prefix: 'appium', suffix: '.png'});
if (await fs.exists(localFile)) {
await fs.unlink(localFile);
Expand All @@ -377,7 +425,7 @@ helpers.getScreenshotDataWithAdbShell = async function getScreenshotDataWithAdbS
const pngDir = opts.androidScreenshotPath || '/data/local/tmp/';
const png = path.posix.resolve(pngDir, 'screenshot.png');
await adb.shell(['/system/bin/rm', `${png};`, '/system/bin/screencap', '-p', png]);
if (!await adb.fileSize(png)) {
if (!(await adb.fileSize(png))) {
throw new Error('The size of the taken screenshot equals to zero.');
}
await adb.pull(png, localFile);
Expand All @@ -393,7 +441,7 @@ helpers.getScreenshotDataWithAdbShell = async function getScreenshotDataWithAdbS
* @param {ADB} adb
* @returns {Promise<Buffer>}
*/
helpers.getScreenshotDataWithAdbExecOut = async function getScreenshotDataWithAdbExecOut (adb) {
helpers.getScreenshotDataWithAdbExecOut = async function getScreenshotDataWithAdbExecOut(adb) {
const {stdout, stderr, code} = await exec(
adb.executable.path,
[...adb.executable.defaultArgs, 'exec-out', '/system/bin/screencap', '-p'],
Expand All @@ -411,7 +459,7 @@ helpers.getScreenshotDataWithAdbExecOut = async function getScreenshotDataWithAd
return stdout;
};

commands.getScreenshot = async function getScreenshot () {
commands.getScreenshot = async function getScreenshot() {
const apiLevel = await this.adb.getApiLevel();
let image = null;
if (apiLevel > 20) {
Expand All @@ -420,15 +468,18 @@ commands.getScreenshot = async function getScreenshot () {
// to be executed. Unfortunately, exec-out option is only supported by newer Android/SDK versions (5.0 and later)
image = await this.getScreenshotDataWithAdbExecOut(this.adb);
} catch (e) {
this.log.info(`Cannot get screenshot data with 'adb exec-out' because of '${e.message}'. ` +
`Defaulting to 'adb shell' call`);
this.log.info(
`Cannot get screenshot data with 'adb exec-out' because of '${e.message}'. ` +
`Defaulting to 'adb shell' call`
);
}
}
if (!image) {
try {
image = await this.getScreenshotDataWithAdbShell(this.adb, this.opts);
} catch (e) {
const err = `Cannot get screenshot data because of '${e.message}'. ` +
const err =
`Cannot get screenshot data because of '${e.message}'. ` +
`Make sure the 'LayoutParams.FLAG_SECURE' is not set for ` +
`the current view`;
this.log.errorAndThrow(err);
Expand All @@ -438,7 +489,10 @@ commands.getScreenshot = async function getScreenshot () {
// Android bug 8433742 - rotate screenshot if screen is rotated
let screenOrientation = await this.adb.getScreenOrientation();
try {
image = await imageUtil.requireSharp()(image).rotate(-90 * screenOrientation).toBuffer();
image = await imageUtil
.requireSharp()(image)
.rotate(-90 * screenOrientation)
.toBuffer();
} catch (err) {
this.log.warn(`Could not rotate screenshot due to error: ${err}`);
}
Expand All @@ -447,5 +501,5 @@ commands.getScreenshot = async function getScreenshot () {
};

Object.assign(extensions, commands, helpers);
export { commands, helpers };
export {commands, helpers};
export default extensions;
Loading

0 comments on commit ee90f84

Please sign in to comment.