Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adb-commands module refactoring #256

Merged
merged 8 commits into from
Sep 12, 2017
55 changes: 39 additions & 16 deletions lib/tools/adb-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { sleep, retryInterval } from 'asyncbox';
import { SubProcess } from 'teen_process';
import B from 'bluebird';


const SETTINGS_HELPER_ID = 'io.appium.settings';
const MAX_SHELL_BUFFER_LENGTH = 1000;

Expand Down Expand Up @@ -54,7 +53,7 @@ methods.initZipAlign = async function () {
methods.getApiLevel = async function () {
if (!this._apiLevel) {
try {
this._apiLevel = await this.shell(['getprop', 'ro.build.version.sdk']);
this._apiLevel = await this.getDeviceProperty('ro.build.version.sdk');
} catch (e) {
log.errorAndThrow(`Error getting device API level. Original error: ${e.message}`);
}
Expand All @@ -72,7 +71,7 @@ methods.getApiLevel = async function () {
methods.getPlatformVersion = async function () {
log.info("Getting device platform version");
try {
return await this.shell(['getprop', 'ro.build.version.release']);
return await this.getDeviceProperty('ro.build.version.release');
} catch (e) {
log.errorAndThrow(`Error getting device platform version. Original error: ${e.message}`);
}
Expand Down Expand Up @@ -307,7 +306,7 @@ methods.getReqPermissions = async function (pkg, cmdOutput = null) {
* @return {Array.<String>} The list of available location providers or an empty list.
*/
methods.getLocationProviders = async function () {
let stdout = await this.shell(['settings', 'get', 'secure', 'location_providers_allowed']);
let stdout = await this.getSetting('secure', 'location_providers_allowed');
return stdout.trim().split(',')
.map((p) => p.trim())
.filter(Boolean);
Expand All @@ -319,7 +318,7 @@ methods.getLocationProviders = async function () {
* @param {boolean} enabled - Whether to enable (true) or disable (false) the GPS provider.
*/
methods.toggleGPSLocationProvider = async function (enabled) {
await this.shell(['settings', 'put', 'secure', 'location_providers_allowed', `${enabled ? "+" : "-"}gps`]);
await this.setSetting('secure', 'location_providers_allowed', `${enabled ? "+" : "-"}gps`);
};

/**
Expand Down Expand Up @@ -408,7 +407,7 @@ methods.setIME = async function (imeId) {
*/
methods.defaultIME = async function () {
try {
let engine = await this.shell(['settings', 'get', 'secure', 'default_input_method']);
let engine = await this.getSetting('secure', 'default_input_method');
return engine.trim();
} catch (e) {
log.errorAndThrow(`Error getting default IME. Original error: ${e.message}`);
Expand Down Expand Up @@ -480,7 +479,6 @@ methods.clearTextField = async function (length = 100) {
*/
methods.lock = async function () {
let locked = await this.isScreenLocked();
locked = await this.isScreenLocked();
if (!locked) {
log.debug("Pressing the KEYCODE_POWER button to lock screen");
await this.keyevent(26);
Expand Down Expand Up @@ -631,7 +629,7 @@ methods.sendTelnetCommand = async function (command) {
* @return {boolean} True if Airplane mode is enabled.
*/
methods.isAirplaneModeOn = async function () {
let stdout = await this.shell(['settings', 'get', 'global', 'airplane_mode_on']);
let stdout = await this.getSetting('global', 'airplane_mode_on');
return parseInt(stdout, 10) !== 0;
};

Expand All @@ -641,7 +639,7 @@ methods.isAirplaneModeOn = async function () {
* @param {boolean} on - True to enable the Airplane mode in Settings and false to disable it.
*/
methods.setAirplaneMode = async function (on) {
await this.shell(['settings', 'put', 'global', 'airplane_mode_on', on ? 1 : 0]);
await this.setSetting('global', 'airplane_mode_on', on ? 1 : 0);
};

/**
Expand All @@ -663,7 +661,7 @@ methods.broadcastAirplaneMode = async function (on) {
* @return {boolean} True if WiFi is enabled.
*/
methods.isWifiOn = async function () {
let stdout = await this.shell(['settings', 'get', 'global', 'wifi_on']);
let stdout = await this.getSetting('global', 'wifi_on');
return (parseInt(stdout, 10) !== 0);
};

Expand All @@ -689,7 +687,7 @@ methods.setWifiState = async function (on, isEmulator = false) {
* @return {boolean} True if Data transfer is enabled.
*/
methods.isDataOn = async function () {
let stdout = await this.shell(['settings', 'get', 'global', 'mobile_data']);
let stdout = await this.getSetting('global', 'mobile_data');
return (parseInt(stdout, 10) !== 0);
};

Expand Down Expand Up @@ -1270,11 +1268,36 @@ methods.setHttpProxy = async function (proxyHost, proxyPort) {
if (_.isUndefined(proxyPort)) {
log.errorAndThrow(`Call to setHttpProxy method with undefined proxy_port ${proxy}`);
}
await this.shell(['settings', 'put', 'global', 'http_proxy', proxy]);
await this.shell(['settings', 'put', 'secure', 'http_proxy', proxy]);
await this.shell(['settings', 'put', 'system', 'http_proxy', proxy]);
await this.shell(['settings', 'put', 'system', 'global_http_proxy_host', proxyHost]);
await this.shell(['settings', 'put', 'system', 'global_http_proxy_port', proxyPort]);
await this.setSetting('global', 'http_proxy', proxy);
await this.setSetting('secure', 'http_proxy', proxy);
await this.setSetting('system', 'http_proxy', proxy);
await this.setSetting('system', 'global_http_proxy_host', proxyHost);
await this.setSetting('system', 'global_http_proxy_port', proxyPort);
};

/**
* Set device property.
* [android.provider.Settings]{@link https://developer.android.com/reference/android/provider/Settings.html}
*
* @param {string} namespace - one of {system, secure, global}, case-insensitive.
* @param {string} setting - property name.
* @param {string|number} value - property value.
* @return {string} command output.
*/
methods.setSetting = async function (namespace, setting, value) {
return await this.shell(['settings', 'put', namespace, setting, value]);
};

/**
* Get device property.
* [android.provider.Settings]{@link https://developer.android.com/reference/android/provider/Settings.html}
*
* @param {string} namespace - one of {system, secure, global}, case-insensitive.
* @param {string} setting - property name.
* @return {string} property value.
*/
methods.getSetting = async function (namespace, setting) {
return await this.shell(['settings', 'get', namespace, setting]);
};

export default methods;
25 changes: 24 additions & 1 deletion test/functional/adb-commands-e2e-specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { apiLevel, platformVersion, MOCHA_TIMEOUT } from './setup';
import { fs, mkdirp } from 'appium-support';
import temp from 'temp';


const should = chai.should();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the way that i usually get around lint errors here is simply:

chai.should();

i.e., you don't need to assign it to a variable for it to do the thing you need it to do.

chai.use(chaiAsPromised);
let expect = chai.expect;

Expand All @@ -25,6 +25,11 @@ describe('adb commands', function () {

let adb;
before(async () => {
/*
* TODO: Update https://github.com/appium/eslint-config-appium
* to ignore no-unused-vars for chai `should|expect`
*/
should;
adb = await ADB.createADB();
});
it('getApiLevel should get correct api level', async () => {
Expand Down Expand Up @@ -122,6 +127,24 @@ describe('adb commands', function () {
it('should get screen size', async () => {
(await adb.getScreenSize()).should.not.be.null;
});
it('should be able to toggle gps location provider', async () => {
await adb.toggleGPSLocationProvider(true);
(await adb.getLocationProviders()).should.include('gps');
await adb.toggleGPSLocationProvider(false);
(await adb.getLocationProviders()).should.not.include('gps');
});
it('should be able to toogle airplane mode', async () => {
await adb.setAirplaneMode(true);
(await adb.isAirplaneModeOn()).should.be.true;
await adb.setAirplaneMode(false);
(await adb.isAirplaneModeOn()).should.be.false;
});
it('should be able to toogle wifi', async () => {
await adb.setWifiState(true);
(await adb.isWifiOn()).should.be.true;
await adb.setWifiState(false);
(await adb.isWifiOn()).should.be.false;
});
describe('app permissions', async () => {
before(async function () {
let deviceApiLevel = await adb.getApiLevel();
Expand Down
89 changes: 53 additions & 36 deletions test/unit/adb-commands-specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,17 @@ describe('adb commands', () => {
describe('shell', () => {
describe('getApiLevel', withMocks({adb}, (mocks) => {
it('should call shell with correct args', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['getprop', 'ro.build.version.sdk'])
mocks.adb.expects("getDeviceProperty")
.once().withExactArgs('ro.build.version.sdk')
.returns(apiLevel);
(await adb.getApiLevel()).should.equal(apiLevel);
mocks.adb.verify();
});
}));
describe('getPlatformVersion', withMocks({adb}, (mocks) => {
it('should call shell with correct args', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['getprop', 'ro.build.version.release'])
mocks.adb.expects("getDeviceProperty")
.once().withExactArgs('ro.build.version.release')
.returns(platformVersion);
(await adb.getPlatformVersion()).should.equal(platformVersion);
mocks.adb.verify();
Expand Down Expand Up @@ -91,17 +91,17 @@ describe('adb commands', () => {
}));
describe('getLocationProviders', withMocks({adb}, (mocks) => {
it('should call shell with correct args and return empty location_providers_allowed', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'secure', 'location_providers_allowed'])
mocks.adb.expects("getSetting")
.once().withExactArgs('secure', 'location_providers_allowed')
.returns('');
let providers = await adb.getLocationProviders();
providers.should.be.an('array');
providers.length.should.equal(0);
mocks.adb.verify();
});
it('should return one location_providers_allowed', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'secure', 'location_providers_allowed'])
mocks.adb.expects("getSetting")
.once().withExactArgs('secure', 'location_providers_allowed')
.returns('gps');
let providers = await adb.getLocationProviders();
providers.should.be.an('array');
Expand All @@ -110,8 +110,8 @@ describe('adb commands', () => {
mocks.adb.verify();
});
it('should return both location_providers_allowed', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'secure', 'location_providers_allowed'])
mocks.adb.expects("getSetting")
.once().withExactArgs('secure', 'location_providers_allowed')
.returns('gps ,wifi');
let providers = await adb.getLocationProviders();
providers.should.be.an('array');
Expand All @@ -123,10 +123,10 @@ describe('adb commands', () => {
}));
describe('toggleGPSLocationProvider', withMocks({adb}, (mocks) => {
it('should call shell with correct args on gps enabled', async () => {
mocks.adb.expects("shell")
.withExactArgs(['settings', 'put', 'secure', 'location_providers_allowed', '+gps']);
mocks.adb.expects("shell")
.withExactArgs(['settings', 'put', 'secure', 'location_providers_allowed', '-gps']);
mocks.adb.expects("setSetting")
.withExactArgs('secure', 'location_providers_allowed', '+gps');
mocks.adb.expects("setSetting")
.withExactArgs('secure', 'location_providers_allowed', '-gps');
await adb.toggleGPSLocationProvider(true);
await adb.toggleGPSLocationProvider(false);
mocks.adb.verify();
Expand Down Expand Up @@ -231,8 +231,8 @@ describe('adb commands', () => {
describe('defaultIME', withMocks({adb}, (mocks) => {
let defaultIME = 'com.android.inputmethod.latin/.LatinIME';
it('should call shell with correct args', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'secure', 'default_input_method'])
mocks.adb.expects("getSetting")
.once().withExactArgs('secure', 'default_input_method')
.returns(defaultIME);
(await adb.defaultIME()).should.equal(defaultIME);
mocks.adb.verify();
Expand Down Expand Up @@ -349,24 +349,24 @@ describe('adb commands', () => {
}));
describe('isAirplaneModeOn', withMocks({adb}, (mocks) => {
it('should call shell with correct args and should be true', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'global', 'airplane_mode_on'])
mocks.adb.expects("getSetting")
.once().withExactArgs('global', 'airplane_mode_on')
.returns("1");
(await adb.isAirplaneModeOn()).should.be.true;
mocks.adb.verify();
});
it('should call shell with correct args and should be false', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'global', 'airplane_mode_on'])
mocks.adb.expects("getSetting")
.once().withExactArgs('global', 'airplane_mode_on')
.returns("0");
(await adb.isAirplaneModeOn()).should.be.false;
mocks.adb.verify();
});
}));
describe('setAirplaneMode', withMocks({adb}, (mocks) => {
it('should call shell with correct args', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'put', 'global', 'airplane_mode_on', 1])
mocks.adb.expects("setSetting")
.once().withExactArgs('global', 'airplane_mode_on', 1)
.returns("");
await adb.setAirplaneMode(1);
mocks.adb.verify();
Expand All @@ -383,15 +383,15 @@ describe('adb commands', () => {
}));
describe('isWifiOn', withMocks({adb}, (mocks) => {
it('should call shell with correct args and should be true', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'global', 'wifi_on'])
mocks.adb.expects("getSetting")
.once().withExactArgs('global', 'wifi_on')
.returns("1");
(await adb.isWifiOn()).should.be.true;
mocks.adb.verify();
});
it('should call shell with correct args and should be false', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'global', 'wifi_on'])
mocks.adb.expects("getSetting")
.once().withExactArgs('global', 'wifi_on')
.returns("0");
(await adb.isWifiOn()).should.be.false;
mocks.adb.verify();
Expand All @@ -415,15 +415,15 @@ describe('adb commands', () => {
}));
describe('isDataOn', withMocks({adb}, (mocks) => {
it('should call shell with correct args and should be true', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'global', 'mobile_data'])
mocks.adb.expects("getSetting")
.once().withExactArgs('global', 'mobile_data')
.returns("1");
(await adb.isDataOn()).should.be.true;
mocks.adb.verify();
});
it('should call shell with correct args and should be false', async () => {
mocks.adb.expects("shell")
.once().withExactArgs(['settings', 'get', 'global', 'mobile_data'])
mocks.adb.expects("getSetting")
.once().withExactArgs('global', 'mobile_data')
.returns("0");
(await adb.isDataOn()).should.be.false;
mocks.adb.verify();
Expand Down Expand Up @@ -929,16 +929,33 @@ describe('adb commands', () => {
it('should throw an error on undefined proxy_port', async () => {
await adb.setHttpProxy("http://localhost").should.eventually.be.rejected;
});
it('should call shell settings methods with correct args', async () => {
it('should call setSetting method with correct args', async () => {
let proxyHost = "http://localhost";
let proxyPort = 4723;
mocks.adb.expects('shell').once().withExactArgs(['settings', 'put', 'global', 'http_proxy', `${proxyHost}:${proxyPort}`]);
mocks.adb.expects('shell').once().withExactArgs(['settings', 'put', 'secure', 'http_proxy', `${proxyHost}:${proxyPort}`]);
mocks.adb.expects('shell').once().withExactArgs(['settings', 'put', 'system', 'http_proxy', `${proxyHost}:${proxyPort}`]);
mocks.adb.expects('shell').once().withExactArgs(['settings', 'put', 'system', 'global_http_proxy_host', proxyHost]);
mocks.adb.expects('shell').once().withExactArgs(['settings', 'put', 'system', 'global_http_proxy_port', proxyPort]);
mocks.adb.expects('setSetting').once().withExactArgs('global', 'http_proxy', `${proxyHost}:${proxyPort}`);
mocks.adb.expects('setSetting').once().withExactArgs('secure', 'http_proxy', `${proxyHost}:${proxyPort}`);
mocks.adb.expects('setSetting').once().withExactArgs('system', 'http_proxy', `${proxyHost}:${proxyPort}`);
mocks.adb.expects('setSetting').once().withExactArgs('system', 'global_http_proxy_host', proxyHost);
mocks.adb.expects('setSetting').once().withExactArgs('system', 'global_http_proxy_port', proxyPort);
await adb.setHttpProxy(proxyHost, proxyPort);
mocks.adb.verify();
});
}));
describe('setSetting', withMocks({adb}, (mocks) => {
it('should call shell settings put', async () => {
mocks.adb.expects('shell').once()
.withExactArgs(['settings', 'put', 'namespace', 'setting', 'value']);
await adb.setSetting('namespace', 'setting', 'value');
mocks.adb.verify();
});
}));
describe('getSetting', withMocks({adb}, (mocks) => {
it('should call shell settings get', async () => {
mocks.adb.expects('shell').once()
.withExactArgs(['settings', 'get', 'namespace', 'setting'])
.returns('value');
(await adb.getSetting('namespace', 'setting')).should.be.equal('value');
mocks.adb.verify();
});
}));
});