cabbie-sync
Version:
A synchronous webdriver client
708 lines (510 loc) • 24 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _applicationCacheStatuses = require('./enums/application-cache-statuses');
var _browserOrientations = require('./enums/browser-orientations');
var _httpMethod = require('./flow-types/http-method');
var _options4 = require('./flow-types/options');
var _sessionData = require('./flow-types/session-data');
var _logEntry = require('./log-entry');
var _url = require('url');
var _fs = require('fs');
var _dotenv = require('dotenv');
var _depd = require('depd');
var _depd2 = _interopRequireDefault(_depd);
var _availableBrowsers = require('available-browsers');
var _availableBrowsers2 = _interopRequireDefault(_availableBrowsers);
var _crossSpawn = require('cross-spawn');
var _crossSpawn2 = _interopRequireDefault(_crossSpawn);
var _addDebugging = require('./add-debugging');
var _addDebugging2 = _interopRequireDefault(_addDebugging);
var _syncRequest = require('sync-request');
var _syncRequest2 = _interopRequireDefault(_syncRequest);
var _errors = require('./utils/errors');
var _connection = require('./connection');
var _connection2 = _interopRequireDefault(_connection);
var _browser = require('./browser');
var _browser2 = _interopRequireDefault(_browser);
var _debug = require('./debug');
var _debug2 = _interopRequireDefault(_debug);
var _timeOut = require('./time-out');
var _timeOut2 = _interopRequireDefault(_timeOut);
var _status = require('./status');
var _status2 = _interopRequireDefault(_status);
var _parseResponse = require('./utils/parse-response');
var _parseResponse2 = _interopRequireDefault(_parseResponse);
var _activeWindow = require('./active-window');
var _activeWindow2 = _interopRequireDefault(_activeWindow);
var _ime = require('./ime');
var _ime2 = _interopRequireDefault(_ime);
var _cookieStorage = require('./cookie-storage');
var _cookieStorage2 = _interopRequireDefault(_cookieStorage);
var _localStorage = require('./local-storage');
var _localStorage2 = _interopRequireDefault(_localStorage);
var _sessionStorage = require('./session-storage');
var _sessionStorage2 = _interopRequireDefault(_sessionStorage);
var _windowHandle = require('./window-handle');
var _windowHandle2 = _interopRequireDefault(_windowHandle);
var _flowRuntime = require('flow-runtime');
var _flowRuntime2 = _interopRequireDefault(_flowRuntime);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* This file is generated automatically, run npm run build to re-generate.
**/
var LogEntry = _flowRuntime2.default.tdz(function () {
return _logEntry.LogEntry;
});
var Session = _flowRuntime2.default.tdz(function () {
return _sessionData.Session;
});
var Options = _flowRuntime2.default.tdz(function () {
return _options4.Options;
});
var HttpMethod = _flowRuntime2.default.tdz(function () {
return _httpMethod.HttpMethod;
});
var BrowserOrientation = _flowRuntime2.default.tdz(function () {
return _browserOrientations.BrowserOrientation;
});
var ApplicationCacheStatus = _flowRuntime2.default.tdz(function () {
return _applicationCacheStatuses.ApplicationCacheStatus;
});
var deprecate = (0, _depd2.default)('cabbie');
/*
* Create a new Driver session, remember to call `.dispose()`
* at the end to terminate the session.
*/
var Driver = function () {
/*
* Get the Session-Storage object.
*/
/*
* Get the Cookie-Storage object.
*/
/*
* The currently active window. This has most of the methods to interact with
* the the current page.
*/
/*
* The browser object.
*
* @private
*/
/*
* The location of the selenium server
*/
/*
* @private
*/
function Driver(remote /*: string*/) {
var options /*: Options*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
(0, _classCallCheck3.default)(this, Driver);
var _remoteType = _flowRuntime2.default.string();
var _optionsType = _flowRuntime2.default.ref(Options);
_flowRuntime2.default.param('remote', _remoteType).assert(remote);
_flowRuntime2.default.param('options', _optionsType).assert(options);
options = _optionsType.assert(addEnvironment(options));
this.remote = remote;
this.options = options;
this.debug = new _debug2.default(options);
var remoteURI = remote;
var capabilities = (0, _extends3.default)({}, options.browser && (remote === 'saucelabs' || remote === 'browserstack' || remote === 'testingbot') ? (0, _availableBrowsers2.default)(remote, options.browser.name, options.browser.version, options.browser.platform) : {}, options.capabilities || {});
var remoteAliases = {
'chrome-driver': 'chromedriver',
'taxi-rank': 'taxirank',
'sauce-labs': 'saucelabs',
'browser-stack': 'browserstack',
'testing-bot': 'testingbot'
};
if (remote in remoteAliases) {
remote = _remoteType.assert(remoteAliases[remote]);
}
switch (remote) {
case 'chromedriver':
remoteURI = 'http://localhost:9515/';
break;
case 'taxirank':
remoteURI = 'http://localhost:9516';
break;
case 'saucelabs':
var _options = options,
sauceUsername = _options.sauceUsername,
sauceAccessKey = _options.sauceAccessKey;
if (!sauceUsername || !sauceAccessKey) {
throw new Error('To use sauce labs, you must specify SAUCE_USERNAME and SAUCE_ACCESS_KEY in enviornment variables or ' + 'provide sauceUsername and sauceAccessKey as options.');
}
remoteURI = 'http://' + sauceUsername + ':' + sauceAccessKey + '@ondemand.saucelabs.com/wd/hub';
break;
case 'browserstack':
var _options2 = options,
browserStackUsername = _options2.browserStackUsername,
browserStackAccessKey = _options2.browserStackAccessKey;
if (!browserStackUsername || !browserStackAccessKey) {
throw new Error('To use browserstack, you must specify BROWSER_STACK_USERNAME and BROWSER_STACK_ACCESS_KEY in ' + 'enviornment variables or provide browserStackUsername and browserStackAccessKey as options.');
}
remoteURI = 'http://hub-cloud.browserstack.com/wd/hub';
capabilities['browserstack.user'] = browserStackUsername;
capabilities['browserstack.key'] = browserStackAccessKey;
break;
case 'testingbot':
var _options3 = options,
testingBotKey = _options3.testingBotKey,
testingBotSecret = _options3.testingBotSecret;
if (!testingBotKey || !testingBotSecret) {
throw new Error('To use testingbot, you must specify TESTING_BOT_KEY and TESTING_BOT_SECRET in enviornment ' + 'variables or provide testingBotKey and testingBotSecret as options.');
}
remoteURI = 'http://' + testingBotKey + ':' + testingBotSecret + '@hub.testingbot.com/wd/hub';
break;
}
this._connection = new _connection2.default(remote, remoteURI, this.debug);
this.session = this._createSession(remote, this._connection, (0, _extends3.default)({}, options, { capabilities: capabilities }));
this.browser = new _browser2.default(this, options);
this.timeOut = new _timeOut2.default(this);
this.activeWindow = new _activeWindow2.default(this, options);
this.ime = new _ime2.default(this);
this.cookieStorage = new _cookieStorage2.default(this);
this.localStorage = new _localStorage2.default(this);
this.sessionStorage = new _sessionStorage2.default(this);
deprecate.property(this, 'browser', 'All properties of browser are now directly available on the Driver object');
}
/*
* Get the Local-Storage object.
*/
/*
* Get the IME object.
*/
/*
* Timeout configuration
*/
/*
* The options that were originally passed in to createCabbieDriver
*/
/*
* @private
*/
/*
* The current session. You can pass this session to createCabbieDriver to convert
* between sync and async drivers. e.g.
*
* ```js
* const driver = createCabbieDriver(oldDriver.remote, {...oldDriver.options, session: oldDriver.session});
* ```
*/
(0, _createClass3.default)(Driver, [{
key: '_createSession',
value: function _createSession(remote /*: string*/, connection /*: Connection*/, options /*: Options*/) /*: Session*/ {
var _remoteType2 = _flowRuntime2.default.string();
var _connectionType = _flowRuntime2.default.ref(_connection2.default);
var _optionsType2 = _flowRuntime2.default.ref(Options);
var _returnType = _flowRuntime2.default.return(_flowRuntime2.default.ref(Session));
_flowRuntime2.default.param('remote', _remoteType2).assert(remote);
_flowRuntime2.default.param('connection', _connectionType).assert(connection);
_flowRuntime2.default.param('options', _optionsType2).assert(options);
if (remote === 'taxirank') {
this._closeTaxiRank = startTaxiRank(connection);
}
return _returnType.assert(createSession(remote, this._connection, options));
}
/*
* Performs a context dependent JSON request for the current session.
* The result is parsed for errors.
* @private
*/
}, {
key: 'requestJSON',
value: function requestJSON(method /*: HttpMethod*/, path /*: string*/, body /*:: ?: Object*/) /*: any*/ {
var _methodType = _flowRuntime2.default.ref(HttpMethod);
var _pathType = _flowRuntime2.default.string();
var _bodyType = _flowRuntime2.default.object();
var _returnType2 = _flowRuntime2.default.return(_flowRuntime2.default.any());
_flowRuntime2.default.param('method', _methodType).assert(method);
_flowRuntime2.default.param('path', _pathType).assert(path);
_flowRuntime2.default.param('body', _bodyType, true).assert(body);
var session = this.session;
return _returnType2.assert(this._connection.requestWithSession(session, method, path, { json: body }));
}
/*
* Get an array of windows for all available windows
*/
}, {
key: 'getWindows',
value: function getWindows() /*: Array<WindowHandle>*/ {
var _this = this;
var _returnType3 = _flowRuntime2.default.return(_flowRuntime2.default.array(_flowRuntime2.default.ref(_windowHandle2.default)));
var windowHandles = this.requestJSON('GET', '/window_handles');
return _returnType3.assert(windowHandles.map(function (windowHandle) {
return new _windowHandle2.default(_this, windowHandle);
}));
}
/*
* Get the current browser orientation
*/
}, {
key: 'getOrientation',
value: function getOrientation() /*: BrowserOrientation*/ {
var _returnType4 = _flowRuntime2.default.return(_flowRuntime2.default.ref(BrowserOrientation));
return _returnType4.assert(this.requestJSON('GET', '/orientation'));
}
/*
* Get the current browser orientation
*/
}, {
key: 'setOrientation',
value: function setOrientation(orientation /*: BrowserOrientation*/) /*: void*/ {
var _orientationType = _flowRuntime2.default.ref(BrowserOrientation);
var _returnType5 = _flowRuntime2.default.return(_flowRuntime2.default.void());
_flowRuntime2.default.param('orientation', _orientationType).assert(orientation);
this.requestJSON('POST', '/orientation', { orientation: orientation });
}
/*
* Get the current geo location
*/
}, {
key: 'getGeoLocation',
value: function getGeoLocation() /*: { latitude: number; longitude: number; altitude: number; }*/ {
var _returnType6 = _flowRuntime2.default.return(_flowRuntime2.default.object(_flowRuntime2.default.property('latitude', _flowRuntime2.default.number()), _flowRuntime2.default.property('longitude', _flowRuntime2.default.number()), _flowRuntime2.default.property('altitude', _flowRuntime2.default.number())));
return _returnType6.assert(this.requestJSON('GET', '/location'));
}
/*
* Set the current geo location
*/
}, {
key: 'setGeoLocation',
value: function setGeoLocation(loc /*: { latitude: number; longitude: number; altitude: number; }*/) /*: void*/ {
var _locType = _flowRuntime2.default.object(_flowRuntime2.default.property('latitude', _flowRuntime2.default.number()), _flowRuntime2.default.property('longitude', _flowRuntime2.default.number()), _flowRuntime2.default.property('altitude', _flowRuntime2.default.number()));
var _returnType7 = _flowRuntime2.default.return(_flowRuntime2.default.void());
_flowRuntime2.default.param('loc', _locType).assert(loc);
this.requestJSON('POST', '/location', loc);
}
/*
* Get the status of the html5 application cache
*/
}, {
key: 'getApplicationCacheStatus',
value: function getApplicationCacheStatus() /*: ApplicationCacheStatus*/ {
var _returnType8 = _flowRuntime2.default.return(_flowRuntime2.default.ref(ApplicationCacheStatus));
return _returnType8.assert(this.requestJSON('GET', '/application_cache/status'));
}
/*
* Get the log for a given log type. Log buffer is reset after each request.
*/
}, {
key: 'getLogs',
value: function getLogs(logType /*: string*/) /*: Array<LogEntry>*/ {
var _logTypeType = _flowRuntime2.default.string();
var _returnType9 = _flowRuntime2.default.return(_flowRuntime2.default.array(_flowRuntime2.default.ref(LogEntry)));
_flowRuntime2.default.param('logType', _logTypeType).assert(logType);
return _returnType9.assert(this.requestJSON('POST', '/log', { type: logType }));
}
/*
* Get available log types
*/
}, {
key: 'getLogTypes',
value: function getLogTypes() /*: Array<string>*/ {
var _returnType10 = _flowRuntime2.default.return(_flowRuntime2.default.array(_flowRuntime2.default.string()));
return _returnType10.assert(this.requestJSON('GET', '/log/types'));
}
/*
* End this Driver session
*/
}, {
key: 'dispose',
value: function dispose(status /*:: ?: Object*/) /*: void*/ {
var _statusType = _flowRuntime2.default.object();
var _returnType11 = _flowRuntime2.default.return(_flowRuntime2.default.void());
_flowRuntime2.default.param('status', _statusType, true).assert(status);
if (status) {
this.sauceJobUpdate(status);
}
this.requestJSON('DELETE', '');
if (this._closeTaxiRank) {
this._closeTaxiRank();
}
}
/*
* Sauce Labs Methods
*/
}, {
key: 'sauceJobUpdate',
value: function sauceJobUpdate(body /*: Object*/) /*: boolean*/ {
var _bodyType2 = _flowRuntime2.default.object();
var _returnType12 = _flowRuntime2.default.return(_flowRuntime2.default.boolean());
_flowRuntime2.default.param('body', _bodyType2).assert(body);
var remote = _flowRuntime2.default.string().assert(this._connection.remote);
var session = this.session;
if (remote.indexOf('saucelabs') === -1) {
return _returnType12.assert(false);
}
if (body === undefined) {
return _returnType12.assert(true);
}
var auth = (0, _url.parse)(remote).auth;
if (typeof auth !== 'string') {
throw new Error('Could not find sauce labs authentication in remote');
}
(0, _syncRequest2.default)('PUT', 'http://' + auth + '@saucelabs.com/rest/v1/' + auth.split(':')[0] + '/jobs/' + session.sessionID, { json: body });
return _returnType12.assert(true);
}
// TODO: provide instructions for converting async driver to sync driver and visa versersa
}]);
return Driver;
}();
/*
* End this Driver session
*
* Alias for `dispose`
* @method quit
*/
_flowRuntime2.default.any().assert(Driver.prototype).quit = Driver.prototype.dispose;
function startTaxiRank(connection /*: Connection*/) /*: () => void*/ {
var _connectionType2 = _flowRuntime2.default.ref(_connection2.default);
var _returnType13 = _flowRuntime2.default.return(_flowRuntime2.default.function(_flowRuntime2.default.return(_flowRuntime2.default.void())));
_flowRuntime2.default.param('connection', _connectionType2).assert(connection);
var started = false;
try {
var res = connection.request('GET', '/version', { noRetries: true });
if (res.statusCode === 200) {
var version = JSON.parse(res.body.toString('utf8'));
var expectedVersion = require('taxi-rank/package.json').version;
started = version === expectedVersion;
}
} catch (ex) {}
if (process.env.CI) {
if (started) return _returnType13.assert(function () {});
var taxiRank = (0, _crossSpawn2.default)(process.execPath, [require.resolve('./taxi-rank-ci.js')], {
stdio: ['inherit', 'inherit', 'inherit']
});
return _returnType13.assert(function () {
return taxiRank.kill();
});
}
if (!started) {
console.log('starting taxi rank');
(0, _crossSpawn2.default)(process.execPath, [require.resolve('./taxi-rank.js')], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore']
}).unref();
var startTime = Date.now();
try {
connection.request('GET', '/version');
} catch (ex) {
var endTime = Date.now();
console.log('Failed to start a detached server after ' + Math.round((endTime - startTime) / 1000) + ' seconds.');
console.log('Starting slower, attached server.');
(0, _crossSpawn2.default)(process.execPath, [require.resolve('./taxi-rank.js')], {
stdio: ['inherit', 'inherit', 'inherit']
});
}
}
// this could be done in process for async mode, but must be a separate process for sync mode
var logger = (0, _crossSpawn2.default)(process.execPath, [require.resolve('./taxi-rank-log.js')], {
stdio: ['inherit', 'inherit', 'inherit']
});
return _returnType13.assert(function () {
logger.kill();
});
}
_flowRuntime2.default.annotate(startTaxiRank, _flowRuntime2.default.function(_flowRuntime2.default.param('connection', _flowRuntime2.default.ref(_connection2.default)), _flowRuntime2.default.return(_flowRuntime2.default.function(_flowRuntime2.default.return(_flowRuntime2.default.void())))));
function createSession(remote /*: string*/, connection /*: Connection*/, options /*: Options*/) /*: Session*/ {
var _remoteType3 = _flowRuntime2.default.string();
var _connectionType3 = _flowRuntime2.default.ref(_connection2.default);
var _optionsType3 = _flowRuntime2.default.ref(Options);
var _returnType14 = _flowRuntime2.default.return(_flowRuntime2.default.ref(Session));
_flowRuntime2.default.param('remote', _remoteType3).assert(remote);
_flowRuntime2.default.param('connection', _connectionType3).assert(connection);
_flowRuntime2.default.param('options', _optionsType3).assert(options);
if (options.session !== undefined) {
return _returnType14.assert(options.session);
}
var capabilities = _flowRuntime2.default.object().assert({});
capabilities.desiredCapabilities = options.capabilities || {};
if (options.requiredCapabilities) {
capabilities.requiredCapabilities = options.requiredCapabilities;
}
var res = connection.request('POST', '/session', { json: capabilities });
if (res.statusCode !== 200) {
switch (remote) {
case 'saucelabs':
if (res.statusCode === 401) {
var err = new Error('Sauce Labs Authentication Failed.\r\nCheck the value of the SAUCE_USERNAME and SAUCE_ACCESS_KEY ' + 'environment variables exactly match your credentials, you passed in: ' + (0, _stringify2.default)(options.sauceUsername) + ' and ' + (0, _stringify2.default)(options.sauceAccessKey) + '.');
throw err;
}
break;
case 'browserstack':
if (res.statusCode === 401) {
var _err = new Error('Browser Stack Authentication Failed.\r\nCheck the value of the BROWSER_STACK_USERNAME and BROWSER_STACK_ACCESS_KEY ' + 'environment variables exactly match your credentials, you passed in: ' + (0, _stringify2.default)(options.browserStackUsername) + ' and ' + (0, _stringify2.default)(options.browserStackAccessKey) + '.');
throw _err;
}
break;
case 'testingbot':
if (res.statusCode === 401) {
var _err2 = new Error('Testing Bot Authentication Failed.\r\nCheck the value of the TESTING_BOT_KEY and TESTING_BOT_SECRET ' + 'environment variables exactly match your credentials, you passed in: ' + (0, _stringify2.default)(options.testingBotKey) + ' and ' + (0, _stringify2.default)(options.testingBotSecret) + '.');
throw _err2;
}
break;
}
throw new Error('Failed to start a Selenium session. ' + remote + ' responded with status code ' + res.statusCode + ':\n' + res.body.toString('utf8'));
}
var body = JSON.parse(res.body.toString('utf8'));
if (body.status === 0) {
return _returnType14.assert({ sessionID: body.sessionId, capabilities: body.value });
} else {
throw (0, _errors.fromBody)(body);
}
}
_flowRuntime2.default.annotate(createSession, _flowRuntime2.default.function(_flowRuntime2.default.param('remote', _flowRuntime2.default.string()), _flowRuntime2.default.param('connection', _flowRuntime2.default.ref(_connection2.default)), _flowRuntime2.default.param('options', _flowRuntime2.default.ref(Options)), _flowRuntime2.default.return(_flowRuntime2.default.ref(Session))));
var environmentAliases = {
SAUCE_USERNAME: 'sauceUsername',
SAUCE_ACCESS_KEY: 'sauceAccessKey',
BROWSER_STACK_USERNAME: 'browserStackUsername',
BROWSER_STACK_ACCESS_KEY: 'browserStackAccessKey',
TESTING_BOT_KEY: 'testingBotkey',
TESTING_BOT_SECRET: 'testingBotSecret',
// deprecated aliases
TESTINGBOT_KEY: 'testingBotkey',
TESTINGBOT_SECRET: 'testingBotSecret'
};
function addEnvironment(options /*: Options*/) /*: Options*/ {
var _optionsType4 = _flowRuntime2.default.ref(Options);
var _returnType15 = _flowRuntime2.default.return(_flowRuntime2.default.ref(Options));
_flowRuntime2.default.param('options', _optionsType4).assert(options);
var result = (0, _extends3.default)({}, options);
(0, _keys2.default)(environmentAliases).forEach(function (key) {
if (result[environmentAliases[key]] === undefined && process.env[key]) {
result[environmentAliases[key]] = process.env[key];
}
});
try {
var parsedObj = (0, _dotenv.parse)((0, _fs.readFileSync)('.env.local', 'utf8'));
(0, _keys2.default)(environmentAliases).forEach(function (key) {
if (result[environmentAliases[key]] === undefined && parsedObj[key]) {
result[environmentAliases[key]] = parsedObj[key];
}
});
} catch (e) {}
try {
var _parsedObj = (0, _dotenv.parse)((0, _fs.readFileSync)('.env', 'utf8'));
(0, _keys2.default)(environmentAliases).forEach(function (key) {
if (result[environmentAliases[key]] === undefined && _parsedObj[key]) {
result[environmentAliases[key]] = _parsedObj[key];
}
});
} catch (e) {}
return _returnType15.assert(result);
}
_flowRuntime2.default.annotate(addEnvironment, _flowRuntime2.default.function(_flowRuntime2.default.param('options', _flowRuntime2.default.ref(Options)), _flowRuntime2.default.return(_flowRuntime2.default.ref(Options))));
(0, _addDebugging2.default)(Driver);
exports.default = Driver;