chore: Replace requests usage with axios
MODIFY
lib/config.js
lib/config.js
import _ from 'lodash';
import path from 'path';
-import { mkdirp, fs, system, util } from 'appium-support';
-import request from 'request-promise';
+import { mkdirp, fs, system } from 'appium-support';
+import axios from 'axios';
import { exec } from 'teen_process';
import { rootDir } from './utils';
import logger from './logger';
@@ -51,12 +51,11 @@ async function getGitRev (useGithubApiFallback = false) {
}
try {
- const response = await request.get(`${GITHUB_API}/tags`, {
+ const resBodyObj = (await axios.get(`${GITHUB_API}/tags`, {
headers: {
'User-Agent': `Appium ${APPIUM_VER}`
}
- });
- const resBodyObj = util.safeJsonParse(response);
+ })).data;
if (_.isArray(resBodyObj)) {
for (const {name, commit} of resBodyObj) {
if (name === `v${APPIUM_VER}` && commit && commit.sha) {
@@ -83,12 +82,11 @@ async function getGitTimestamp (commitSha, useGithubApiFallback = false) {
}
try {
- const response = await request.get(`${GITHUB_API}/commits/${commitSha}`, {
+ const resBodyObj = (await axios.get(`${GITHUB_API}/commits/${commitSha}`, {
headers: {
'User-Agent': `Appium ${APPIUM_VER}`
}
- });
- const resBodyObj = util.safeJsonParse(response);
+ })).data;
if (resBodyObj && resBodyObj.commit) {
if (resBodyObj.commit.committer && resBodyObj.commit.committer.date) {
return resBodyObj.commit.committer.date;

MODIFY
lib/grid-register.js
lib/grid-register.js
-import request from 'request-promise';
+import axios from 'axios';
import { fs } from 'appium-support';
import logger from './logger';
import _ from 'lodash';
@@ -26,38 +26,38 @@ async function registerNode (configFile, addr, port) {
postRequest(data, addr, port);
}
-async function registerToGrid (options_post, jsonObject) {
+async function registerToGrid (postOptions, configHolder) {
try {
- let response = await request(options_post);
- if (response === undefined || response.statusCode !== 200) {
- throw new Error('Request failed');
+ const {status} = await axios(postOptions);
+ if (status !== 200) {
+ throw new Error(`Request failed with code ${status}`);
}
- let logMessage = `Appium successfully registered with the grid on ${hubUri(jsonObject.configuration)}`;
- logger.debug(logMessage);
+ logger.debug(`Appium successfully registered with the the grid on ` +
+ hubUri(configHolder.configuration));
} catch (err) {
- logger.error(`Request to register with grid was unsuccessful: ${err.message}`);
+ logger.error(`An attempt to register with the grid was unsuccessful: ${err.message}`);
}
}
function postRequest (data, addr, port) {
// parse json to get hub host and port
- let jsonObject;
+ let configHolder;
try {
- jsonObject = JSON.parse(data);
+ configHolder = JSON.parse(data);
} catch (err) {
logger.errorAndThrow(`Syntax error in node configuration file: ${err.message}`);
}
// Move Selenium 3 configuration properties to configuration object
- if (!_.has(jsonObject, 'configuration')) {
+ if (!_.has(configHolder, 'configuration')) {
let configuration = {};
- for (const property in jsonObject) {
- if (_.has(jsonObject, property) && property !== 'capabilities') {
- configuration[property] = jsonObject[property];
- delete jsonObject[property];
+ for (const property in configHolder) {
+ if (_.has(configHolder, property) && property !== 'capabilities') {
+ configuration[property] = configHolder[property];
+ delete configHolder[property];
}
}
- jsonObject.configuration = configuration;
+ configHolder.configuration = configuration;
}
// if the node config does not have the appium/webdriver url, host, and port,
@@ -65,77 +65,65 @@ function postRequest (data, addr, port) {
// otherwise, we will take whatever the user setup
// because we will always set localhost/127.0.0.1. this won't work if your
// node and grid aren't in the same place
- if (!jsonObject.configuration.url || !jsonObject.configuration.host || !jsonObject.configuration.port) {
- jsonObject.configuration.url = `http://${addr}:${port}/wd/hub`;
- jsonObject.configuration.host = addr;
- jsonObject.configuration.port = port;
+ if (!configHolder.configuration.url || !configHolder.configuration.host || !configHolder.configuration.port) {
+ configHolder.configuration.url = `http://${addr}:${port}/wd/hub`;
+ configHolder.configuration.host = addr;
+ configHolder.configuration.port = port;
}
// if the node config does not have id automatically add it
- if (!jsonObject.configuration.id) {
- jsonObject.configuration.id = `http://${jsonObject.configuration.host}:${jsonObject.configuration.port}`;
+ if (!configHolder.configuration.id) {
+ configHolder.configuration.id = `http://${configHolder.configuration.host}:${configHolder.configuration.port}`;
}
- // re-serialize the configuration with the auto populated data
- data = JSON.stringify(jsonObject);
-
- // prepare the header
- let post_headers = {
- 'Content-Type': 'application/json',
- 'Content-Length': data.length
- };
// the post options
- let post_options = {
- url: `${hubUri(jsonObject.configuration)}/grid/register`,
+ const regRequest = {
+ url: `${hubUri(configHolder.configuration)}/grid/register`,
method: 'POST',
- body: data,
- headers: post_headers,
- resolveWithFullResponse: true // return the full response, not just the body
+ data: configHolder,
};
- if (jsonObject.configuration.register !== true) {
- logger.debug(`No registration sent (${jsonObject.configuration.register} = false)`);
+ if (configHolder.configuration.register !== true) {
+ logger.debug(`No registration sent (${configHolder.configuration.register} = false)`);
return;
}
- let registerCycleTime = jsonObject.configuration.registerCycle;
- if (registerCycleTime !== null && registerCycleTime > 0) {
+ const registerCycleInterval = configHolder.configuration.registerCycle;
+ if (isNaN(registerCycleInterval) || registerCycleInterval <= 0) {
+ logger.warn(`'registerCycle' is not a valid positive number. ` +
+ `No registration request will be sent to the grid.`);
+ return;
+ }
// initiate a new Thread
let first = true;
- logger.debug(`Starting auto register thread for grid. Will try to register every ${registerCycleTime} ms.`);
+ logger.debug(`Starting auto register thread for the grid. ` +
+ `Will try to register every ${registerCycleInterval} ms.`);
setInterval(async function registerRetry () {
- if (first !== true) {
- let isRegistered = await isAlreadyRegistered(jsonObject);
- if (isRegistered !== null && isRegistered !== true) {
- // make the http POST to the grid for registration
- await registerToGrid(post_options, jsonObject);
- }
- } else {
+ if (first) {
first = false;
- await registerToGrid(post_options, jsonObject);
- }
- }, registerCycleTime);
+ await registerToGrid(regRequest, configHolder);
+ } else if (!await isAlreadyRegistered(configHolder)) {
+ // make the http POST to the grid for registration
+ await registerToGrid(regRequest, configHolder);
}
+ }, registerCycleInterval);
}
-async function isAlreadyRegistered (jsonObject) {
+async function isAlreadyRegistered (configHolder) {
//check if node is already registered
- let id = jsonObject.configuration.id;
+ const id = configHolder.configuration.id;
try {
- let response = await request({
- uri: `${hubUri(jsonObject.configuration)}/grid/api/proxy?id=${id}`,
- method: 'GET',
+ const {data, status} = await axios({
+ url: `${hubUri(configHolder.configuration)}/grid/api/proxy?id=${id}`,
timeout: 10000,
- resolveWithFullResponse: true // return the full response, not just the body
});
- if (response === undefined || response.statusCode !== 200) {
- throw new Error(`Request failed`);
+ if (status !== 200) {
+ throw new Error(`Request failed with code ${status}`);
}
- let responseData = JSON.parse(response.body);
- if (responseData.success !== true) {
+ if (!data.success) {
// if register fail, print the debug msg
- logger.debug(`Grid registration error: ${responseData.msg}`);
+ logger.debug(`Grid registration error: ${data.msg}`);
}
- return responseData.success;
+ return data.success;
} catch (err) {
logger.debug(`Hub down or not responding: ${err.message}`);
}

MODIFY
package.json
package.json
"argparse": "^1.0.10",
"async-lock": "^1.0.0",
"asyncbox": "2.x",
+ "axios": "^0.19.2",
"bluebird": "3.x",
"continuation-local-storage": "3.x",
"dateformat": "^3.0.3",
"lodash": "^4.17.11",
"longjohn": "^0.2.12",
"npmlog": "4.x",
- "request": "^2.81.0",
- "request-promise": "4.x",
"semver": "^7.0.0",
"source-map-support": "0.x",
"teen_process": "1.x",

MODIFY
test/config-specs.js
test/config-specs.js
@@ -11,7 +11,7 @@ import { getGitRev, getBuildInfo, checkNodeOk, warnNodeDeprecations,
import getParser from '../lib/parser';
import logger from '../lib/logger';
import { fs } from 'appium-support';
-import request from 'request-promise';
+import axios from 'axios';
let should = chai.should();
chai.use(chaiAsPromised);
@@ -45,7 +45,7 @@ describe('Config', function () {
let getStub;
beforeEach(function () {
mockFs = sinon.mock(fs);
- getStub = sinon.stub(request, 'get');
+ getStub = sinon.stub(axios, 'get');
});
afterEach(function () {
getStub.restore();
@@ -56,7 +56,7 @@ describe('Config', function () {
await verifyBuildInfoUpdate(true);
});
it('should get a configuration object if the local git metadata is not present', async function () {
- getStub.onCall(0).returns([
+ getStub.onCall(0).returns({data: [
{
'name': `v${APPIUM_VER}`,
'zipball_url': 'https://api.github.com/repos/appium/appium/zipball/v1.9.0-beta.1',
@@ -77,8 +77,8 @@ describe('Config', function () {
},
'node_id': 'MDM6UmVmNzUzMDU3MDp2MS44LjItYmV0YQ=='
}
- ]);
- getStub.onCall(1).returns({
+ ]});
+ getStub.onCall(1).returns({data: {
'sha': '3c2752f9f9c56000705a4ae15b3ba68a5d2e644c',
'node_id': 'MDY6Q29tbWl0NzUzMDU3MDozYzI3NTJmOWY5YzU2MDAwNzA1YTRhZTE1YjNiYTY4YTVkMmU2NDRj',
'commit': {
@@ -109,7 +109,7 @@ describe('Config', function () {
'url': 'https://api.github.com/repos/appium/appium/commits/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c',
'html_url': 'https://github.com/appium/appium/commit/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c',
'comments_url': 'https://api.github.com/repos/appium/appium/commits/3c2752f9f9c56000705a4ae15b3ba68a5d2e644c/comments',
- });
+ }});
await verifyBuildInfoUpdate(false);
});
});

MODIFY
test/driver-e2e-specs.js
test/driver-e2e-specs.js
@@ -5,7 +5,7 @@ import B from 'bluebird';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import wd from 'wd';
-import request from 'request-promise';
+import axios from 'axios';
import { main as appiumServer } from '../lib/main';
import { TEST_FAKE_APP, TEST_HOST, TEST_PORT } from './helpers';
import { BaseDriver } from 'appium-base-driver';
@@ -93,7 +93,7 @@ describe('FakeDriver - via HTTP', function () {
};
// Create the session
- const {status, value, sessionId} = await request.post({url: baseUrl, json: w3cCaps});
+ const {status, value, sessionId} = (await axios.post(baseUrl, w3cCaps)).data;
should.not.exist(status); // Test that it's a W3C session by checking that 'status' is not in the response
should.not.exist(sessionId);
value.sessionId.should.be.a.string;
@@ -105,20 +105,17 @@ describe('FakeDriver - via HTTP', function () {
});
// Now use that sessionId to call /screenshot
- const {status: screenshotStatus, value: screenshotValue} = await request({url: `${baseUrl}/${value.sessionId}/screenshot`, json: true});
+ const {status: screenshotStatus, value: screenshotValue} = (await axios({url: `${baseUrl}/${value.sessionId}/screenshot`})).data;
should.not.exist(screenshotStatus);
screenshotValue.should.equal('hahahanotreallyascreenshot');
// Now use that sessionID to call an arbitrary W3C-only endpoint that isn't implemented to see if it responds with correct error
- const {statusCode, error} = await request.post({url: `${baseUrl}/${value.sessionId}/execute/async`, json: {script: '', args: ['a']}}).should.eventually.be.rejected;
- statusCode.should.equal(405);
- const {error: errorMessage, message, stacktrace} = error.value;
- errorMessage.should.match(/unknown method/);
- message.should.match(/Method has not yet been implemented/);
- stacktrace.should.match(/FakeDriver.executeCommand/);
+ await axios.post(
+ `${baseUrl}/${value.sessionId}/execute/async`,
+ {script: '', args: ['a']}).should.eventually.be.rejectedWith(/405/);
// End session
- await request.delete({url: `${baseUrl}/${value.sessionId}`});
+ await axios.delete(`${baseUrl}/${value.sessionId}`);
});
it('should reject invalid W3C capabilities and respond with a 400 Bad Parameters error', async function () {
@@ -129,9 +126,7 @@ describe('FakeDriver - via HTTP', function () {
}
};
- const {statusCode, error} = await request.post({url: baseUrl, json: badW3Ccaps}).should.eventually.be.rejected;
- statusCode.should.equal(400);
- error.value.message.should.match(/can't be blank/);
+ await axios.post(baseUrl, badW3Ccaps).should.eventually.be.rejectedWith(/400/);
});
it('should accept a combo of W3C and JSONWP capabilities but default to W3C', async function () {
@@ -147,7 +142,7 @@ describe('FakeDriver - via HTTP', function () {
}
};
- const {status, value, sessionId} = await request.post({url: baseUrl, json: combinedCaps});
+ const {status, value, sessionId} = (await axios.post(baseUrl, combinedCaps)).data;
should.not.exist(status); // If it's a W3C session, should not respond with 'status'
should.not.exist(sessionId);
value.sessionId.should.exist;
@@ -157,7 +152,7 @@ describe('FakeDriver - via HTTP', function () {
});
// End session
- await request.delete({ url: `${baseUrl}/${value.sessionId}` });
+ await axios.delete(`${baseUrl}/${value.sessionId}`);
});
it('should accept a combo of W3C and JSONWP and if JSONWP has extraneous keys, they should be merged into W3C capabilities', async function () {
@@ -175,7 +170,7 @@ describe('FakeDriver - via HTTP', function () {
}
};
- const {sessionId, status, value} = await request.post({url: baseUrl, json: combinedCaps});
+ const {sessionId, status, value} = (await axios.post(baseUrl, combinedCaps)).data;
should.not.exist(sessionId);
should.not.exist(status);
value.sessionId.should.exist;
@@ -187,7 +182,7 @@ describe('FakeDriver - via HTTP', function () {
});
// End session
- await request.delete({ url: `${baseUrl}/${value.sessionId}` });
+ await axios.delete(`${baseUrl}/${value.sessionId}`);
});
it('should reject bad W3C capabilities with a BadParametersError (400)', async function () {
@@ -199,12 +194,7 @@ describe('FakeDriver - via HTTP', function () {
},
},
};
- const {error, statusCode, response} = await request.post({url: baseUrl, json: w3cCaps}).should.eventually.be.rejected;
- response.headers['content-type'].should.match(/application\/json/);
-
- const {message} = error.value;
- message.should.match(/BadAutomationName not part of/);
- statusCode.should.equal(400);
+ await axios.post(baseUrl, w3cCaps).should.eventually.be.rejectedWith(/400/);
});
it('should accept capabilities that are provided in the firstMatch array', async function () {
@@ -216,13 +206,13 @@ describe('FakeDriver - via HTTP', function () {
}],
},
};
- const {value, sessionId, status} = await request.post({url: baseUrl, json: w3cCaps});
+ const {value, sessionId, status} = (await axios.post(baseUrl, w3cCaps)).data;
should.not.exist(status);
should.not.exist(sessionId);
value.capabilities.should.deep.equal(caps);
// End session
- await request.delete({ url: `${baseUrl}/${value.sessionId}` });
+ await axios.delete(`${baseUrl}/${value.sessionId}`);
});
it('should fall back to MJSONWP if w3c caps are invalid', async function () {
@@ -238,13 +228,13 @@ describe('FakeDriver - via HTTP', function () {
}],
},
};
- const {value, sessionId, status} = await request.post({url: baseUrl, json: combinedCaps});
+ const {value, sessionId, status} = (await axios.post(baseUrl, combinedCaps)).data;
status.should.exist;
sessionId.should.exist;
value.should.deep.equal(caps);
// End session
- await request.delete({ url: `${baseUrl}/${sessionId}` });
+ await axios.delete(`${baseUrl}/${sessionId}`);
});
it('should fall back to MJSONWP if Inner Driver is not ready for W3C', async function () {
@@ -265,7 +255,7 @@ describe('FakeDriver - via HTTP', function () {
return res;
});
- const {value, sessionId, status} = await request.post({url: baseUrl, json: combinedCaps});
+ const {value, sessionId, status} = (await axios.post(baseUrl, combinedCaps)).data;
status.should.exist;
sessionId.should.exist;
value.should.deep.equal(caps);
@@ -273,7 +263,7 @@ describe('FakeDriver - via HTTP', function () {
createSessionStub.restore();
// End session
- await request.delete({ url: `${baseUrl}/${sessionId}` });
+ await axios.delete(`${baseUrl}/${sessionId}`);
});
it('should handle concurrent MJSONWP and W3C sessions', async function () {
@@ -290,30 +280,30 @@ describe('FakeDriver - via HTTP', function () {
};
// Have an MJSONWP and W3C session running concurrently
- const {sessionId: mjsonwpSessId, value: mjsonwpValue, status} = await request.post({url: baseUrl, json: _.omit(combinedCaps, 'capabilities')});
+ const {sessionId: mjsonwpSessId, value: mjsonwpValue, status} = (await axios.post(baseUrl, _.omit(combinedCaps, 'capabilities'))).data;
status.should.exist;
mjsonwpValue.should.eql(caps);
mjsonwpSessId.should.exist;
- const {value} = await request.post({url: baseUrl, json: _.omit(combinedCaps, 'desiredCapabilities')});
+ const {value} = (await axios.post(baseUrl, _.omit(combinedCaps, 'desiredCapabilities'))).data;
const w3cSessId = value.sessionId;
w3cSessId.should.exist;
value.capabilities.should.eql(caps);
// Test that both return the proper payload based on their protocol
- const mjsonwpPayload = await request(`${baseUrl}/${mjsonwpSessId}`, {json: true});
+ const mjsonwpPayload = (await axios.get(`${baseUrl}/${mjsonwpSessId}`)).data;
mjsonwpPayload.sessionId.should.exist;
mjsonwpPayload.status.should.exist;
mjsonwpPayload.value.should.eql(caps);
- const w3cPayload = await request(`${baseUrl}/${w3cSessId}`, {json: true});
+ const w3cPayload = (await axios.get(`${baseUrl}/${w3cSessId}`)).data;
should.not.exist(w3cPayload.sessionId);
should.not.exist(w3cPayload.status);
w3cPayload.value.should.eql(caps);
// End sessions
- await request.delete({url: `${baseUrl}/${mjsonwpSessId}`});
- await request.delete({url: `${baseUrl}/${w3cSessId}`});
+ await axios.delete(`${baseUrl}/${mjsonwpSessId}`);
+ await axios.delete(`${baseUrl}/${w3cSessId}`);
});
});
});

chore: Replace requests usage with axios (#14269)