UNPKG

browserstack-connector

Version:

Helps connect the local machine to BrowserStack and start a remote browser.

484 lines (373 loc) 17.5 kB
'use strict'; var _classCallCheck = require('babel-runtime/helpers/class-call-check').default; var _regeneratorRuntime = require('babel-runtime/regenerator').default; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default').default; exports.__esModule = true; var _os = require('os'); var _os2 = _interopRequireDefault(_os); var _pinkie = require('pinkie'); var _pinkie2 = _interopRequireDefault(_pinkie); var _osFamily = require('os-family'); var _osFamily2 = _interopRequireDefault(_osFamily); var _browserstack = require('browserstack'); var _browserstackLocal = require('browserstack-local'); var _uid = require('uid'); var _uid2 = _interopRequireDefault(_uid); var _utilsWait = require('./utils/wait'); var _utilsWait2 = _interopRequireDefault(_utilsWait); var _hubJs = require('./hub.js'); var _hubJs2 = _interopRequireDefault(_hubJs); var DEFAULT_BROWSER_OPENING_MAX_ATTEMPT = 3; var DEFAULT_BROWSER_OPENING_TIMEOUT = 60 * 1000; var DEFAULT_HUB_PORT = 1000; var BrowserStackConnector = (function () { function BrowserStackConnector(username, accessKey) { var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; _classCallCheck(this, BrowserStackConnector); this.username = username; this.accessKey = accessKey; var _options$connectorLogging = options.connectorLogging; var connectorLogging = _options$connectorLogging === undefined ? true : _options$connectorLogging; var _options$servicePort = options.servicePort; var servicePort = _options$servicePort === undefined ? DEFAULT_HUB_PORT : _options$servicePort; this.options = { connectorLogging: connectorLogging }; this.client = _browserstack.createClient({ username: username, password: accessKey }); this.skipLocalConnection = !!process.env['BROWSERSTACK_NO_LOCAL']; this.localConnection = null; this.tunnelIdentifier = Date.now(); this.hubPort = servicePort || DEFAULT_HUB_PORT; this.hub = new _hubJs2.default(this.hubPort); } BrowserStackConnector.prototype._log = function _log(message) { if (this.options.connectorLogging) process.stdout.write(message + '\n'); }; BrowserStackConnector.prototype._getWorkers = function _getWorkers() { var _this = this; return new _pinkie2.default(function (resolve) { return _this.client.getWorkers(function (err, res) { return resolve(res); }); }); }; BrowserStackConnector.prototype._getWorker = function _getWorker(id) { var getWorker, maxAttempts, requestTimeout, attempts, worker; return _regeneratorRuntime.async(function _getWorker$(context$2$0) { var _this2 = this; while (1) switch (context$2$0.prev = context$2$0.next) { case 0: getWorker = function () { return new _pinkie2.default(function (resolve) { _this2.client.getWorker(id, function (err, worker) { return resolve(worker); }); }); }; maxAttempts = 30; requestTimeout = 10000; attempts = 0; case 4: if (!(attempts++ <= maxAttempts)) { context$2$0.next = 14; break; } context$2$0.next = 7; return _regeneratorRuntime.awrap(getWorker()); case 7: worker = context$2$0.sent; if (!(worker && worker.status === 'running')) { context$2$0.next = 10; break; } return context$2$0.abrupt('return', worker); case 10: context$2$0.next = 12; return _regeneratorRuntime.awrap(_utilsWait2.default(requestTimeout)); case 12: context$2$0.next = 4; break; case 14: case 'end': return context$2$0.stop(); } }, null, this); }; BrowserStackConnector.prototype._getMaxAvailableMachines = function _getMaxAvailableMachines() { return _regeneratorRuntime.async(function _getMaxAvailableMachines$(context$2$0) { var _this3 = this; while (1) switch (context$2$0.prev = context$2$0.next) { case 0: return context$2$0.abrupt('return', new _pinkie2.default(function (resolve, reject) { _this3.client.getApiStatus(function (err, status) { if (err) { _this3._log(err); reject(err); } else resolve(status.sessions_limit); }); })); case 1: case 'end': return context$2$0.stop(); } }, null, this); }; BrowserStackConnector.prototype._getFreeMachineCount = function _getFreeMachineCount() { var _ref, maxMachines, workers; return _regeneratorRuntime.async(function _getFreeMachineCount$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: context$2$0.next = 2; return _regeneratorRuntime.awrap(_pinkie2.default.all([this._getMaxAvailableMachines(), this._getWorkers()])); case 2: _ref = context$2$0.sent; maxMachines = _ref[0]; workers = _ref[1]; return context$2$0.abrupt('return', maxMachines - workers.length); case 6: case 'end': return context$2$0.stop(); } }, null, this); }; BrowserStackConnector.prototype.getSessionUrl = function getSessionUrl(id) { var worker; return _regeneratorRuntime.async(function getSessionUrl$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: context$2$0.next = 2; return _regeneratorRuntime.awrap(this._getWorker(id)); case 2: worker = context$2$0.sent; return context$2$0.abrupt('return', worker && worker.browser_url); case 4: case 'end': return context$2$0.stop(); } }, null, this); }; BrowserStackConnector.prototype._startBrowser = function _startBrowser(browserSettings, url, _ref2, _ref3) { var jobName = _ref2.jobName; var build = _ref2.build; var workingTimeout = _ref3.workingTimeout; var openingTimeout = _ref3.openingTimeout; var browserId, worker, createWorker, waitForUrlOpened, workerId; return _regeneratorRuntime.async(function _startBrowser$(context$2$0) { var _this4 = this; while (1) switch (context$2$0.prev = context$2$0.next) { case 0: browserId = _uid2.default(10); worker = null; createWorker = function () { return new _pinkie2.default(function (resolve, reject) { var settings = { os: browserSettings.os, os_version: browserSettings.osVersion, browser: browserSettings.name || null, browser_version: browserSettings.version || 'latest', device: browserSettings.device || null, url: 'http://' + _os2.default.hostname() + ':' + _this4.hubPort + '/' + browserId + '?url=' + url, timeout: workingTimeout || 1800, name: jobName, build: build, localIdentifier: _this4.tunnelIdentifier }; if ('realMobile' in browserSettings) settings.realMobile = browserSettings.realMobile; _this4.client.createWorker(settings, function (err, res) { if (err) { _this4._log(err); reject(err); return; } resolve(res.id); }); }); }; waitForUrlOpened = new _pinkie2.default(function (resolve, reject) { var timeoutId = null; var hubHandler = function (id) { if (id === browserId) { _this4.hub.removeListener('browser-opened', hubHandler); clearTimeout(timeoutId); resolve(); } }; timeoutId = setTimeout(function callee$3$0() { return _regeneratorRuntime.async(function callee$3$0$(context$4$0) { while (1) switch (context$4$0.prev = context$4$0.next) { case 0: this.hub.removeListener('browser-opened', hubHandler); context$4$0.next = 3; return _regeneratorRuntime.awrap(this.stopBrowser(worker.id)); case 3: reject('Browser starting timeout expired'); case 4: case 'end': return context$4$0.stop(); } }, null, _this4); }, openingTimeout); _this4.hub.addListener('browser-opened', hubHandler); }); context$2$0.next = 6; return _regeneratorRuntime.awrap(createWorker()); case 6: workerId = context$2$0.sent; context$2$0.next = 9; return _regeneratorRuntime.awrap(this._getWorker(workerId)); case 9: worker = context$2$0.sent; context$2$0.next = 12; return _regeneratorRuntime.awrap(waitForUrlOpened); case 12: this._log(browserSettings.name + ' started. See ' + worker.browser_url); return context$2$0.abrupt('return', worker); case 14: case 'end': return context$2$0.stop(); } }, null, this); }; BrowserStackConnector.prototype.startBrowser = function startBrowser(browserSettings, url) { var _ref4, jobName, build, _ref5, maxAttepts, openingTimeout, workingTimeout, worker, attempt, error, args$2$0 = arguments; return _regeneratorRuntime.async(function startBrowser$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: _ref4 = args$2$0.length <= 2 || args$2$0[2] === undefined ? {} : args$2$0[2]; jobName = _ref4.jobName; build = _ref4.build; _ref5 = args$2$0.length <= 3 || args$2$0[3] === undefined ? {} : args$2$0[3]; maxAttepts = _ref5.maxAttepts; openingTimeout = _ref5.openingTimeout; workingTimeout = _ref5.workingTimeout; worker = null; attempt = 0; error = null; maxAttepts = maxAttepts || DEFAULT_BROWSER_OPENING_MAX_ATTEMPT; openingTimeout = openingTimeout || DEFAULT_BROWSER_OPENING_TIMEOUT; case 12: if (!(attempt < maxAttepts && !worker)) { context$2$0.next = 26; break; } context$2$0.prev = 13; error = null; context$2$0.next = 17; return _regeneratorRuntime.awrap(this._startBrowser(browserSettings, url, { jobName: jobName, build: build }, { workingTimeout: workingTimeout, openingTimeout: openingTimeout })); case 17: worker = context$2$0.sent; context$2$0.next = 24; break; case 20: context$2$0.prev = 20; context$2$0.t0 = context$2$0['catch'](13); error = context$2$0.t0; attempt++; case 24: context$2$0.next = 12; break; case 26: if (!error) { context$2$0.next = 28; break; } throw new Error('Unable to start browser ' + browserSettings.name + ' due to: ' + error); case 28: return context$2$0.abrupt('return', worker); case 29: case 'end': return context$2$0.stop(); } }, null, this, [[13, 20]]); }; BrowserStackConnector.prototype.stopBrowser = function stopBrowser(workerId) { var _this5 = this; return new _pinkie2.default(function (resolve, reject) { _this5.client.terminateWorker(workerId, function (err, data) { if (err) { _this5._log(err); reject(err); return; } resolve(data.time); }); }); }; BrowserStackConnector.prototype.connect = function connect() { var _this6 = this; var opts = { 'key': this.accessKey, 'logfile': _osFamily2.default.win ? 'NUL' : '/dev/null', 'enable-logging-for-api': true, 'localIdentifier': this.tunnelIdentifier }; if (this.skipLocalConnection) return _pinkie2.default.resolve(); this.localConnection = new _browserstackLocal.Local(); return new _pinkie2.default(function (resolve, reject) { _this6.localConnection.start(opts, function (err) { if (err) { _this6._log(err); reject(err); } else resolve(); }); }); }; BrowserStackConnector.prototype.disconnect = function disconnect() { var _this7 = this; this.hub.close(); if (this.skipLocalConnection) return _pinkie2.default.resolve(); return new _pinkie2.default(function (resolve) { return _this7.localConnection.stop(resolve); }); }; BrowserStackConnector.prototype.waitForFreeMachines = function waitForFreeMachines(machineCount, requestInterval, maxAttemptCount) { var attempts, freeMachineCount; return _regeneratorRuntime.async(function waitForFreeMachines$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: attempts = 0; case 1: if (!(attempts < maxAttemptCount)) { context$2$0.next = 13; break; } context$2$0.next = 4; return _regeneratorRuntime.awrap(this._getFreeMachineCount()); case 4: freeMachineCount = context$2$0.sent; if (!(freeMachineCount >= machineCount)) { context$2$0.next = 7; break; } return context$2$0.abrupt('return'); case 7: this._log('The number of free machines (' + freeMachineCount + ') is less than requested (' + machineCount + ').'); context$2$0.next = 10; return _regeneratorRuntime.awrap(_utilsWait2.default(requestInterval)); case 10: attempts++; context$2$0.next = 1; break; case 13: throw new Error('There are no free machines'); case 14: case 'end': return context$2$0.stop(); } }, null, this); }; return BrowserStackConnector; })(); exports.default = BrowserStackConnector; module.exports = exports.default;