UNPKG

digdug

Version:

Dig Dug. A simple abstraction library for downloading and launching WebDriver service tunnels.

271 lines (232 loc) 6.88 kB
/** * @module digdug/TestingBotTunnel */ var fs = require('fs'); var ioQuery = require('dojo/io-query'); var os = require('os'); var pathUtil = require('path'); var request = require('dojo/request'); var Tunnel = require('./Tunnel'); var urlUtil = require('url'); var util = require('./util'); /** * A TestingBot tunnel. * * @constructor module:digdug/TestingBotTunnel * @extends module:digdug/Tunnel */ function TestingBotTunnel() { this.apiKey = process.env.TESTINGBOT_KEY; this.apiSecret = process.env.TESTINGBOT_SECRET; this.fastFailDomains = []; Tunnel.apply(this, arguments); } var _super = Tunnel.prototype; TestingBotTunnel.prototype = util.mixin(Object.create(_super), /** @lends module:digdug/TestingBotTunnel# */ { constructor: TestingBotTunnel, /** * The TestingBot API key. * * @type {string} * @default the value of the TESTINGBOT_API_KEY environment variable */ apiKey: null, /** * The TestingBot API secret. * * @type {string} * @default the value of the TESTINGBOT_API_SECRET environment variable */ apiSecret: null, directory: pathUtil.join(__dirname, 'testingbot'), executable: 'java', /** * A list of regular expressions corresponding to domains whose connections should fail immediately if the VM * attempts to make a connection to them. * * @type {string[]} */ fastFailDomains: null, /** * A filename where additional logs from the tunnel should be output. * * @type {string} */ logFile: null, port: 4445, url: 'https://testingbot.com/downloads/testingbot-tunnel.zip', /** * Whether or not to use rabbIT compression for the tunnel connection. * * @type {boolean} * @default */ useCompression: false, /** * Whether or not to use the default local Jetty proxy for the tunnel. * * @type {boolean} * @default */ useJettyProxy: true, /** * Whether or not to use the default remote Squid proxy for the VM. * * @type {boolean} * @default */ useSquidProxy: true, /** * Whether or not to re-encrypt data encrypted by self-signed certificates. * * @type {boolean} * @default */ useSsl: false, /** * The URL of a service that provides a list of environments supported by TestingBot. */ environmentUrl: 'https://api.testingbot.com/v1/browsers', get auth() { return (this.apiKey || '') + ':' + (this.apiSecret || ''); }, get isDownloaded() { return util.fileExists(pathUtil.join(this.directory, 'testingbot-tunnel/testingbot-tunnel.jar')); }, _makeArgs: function (readyFile) { var args = [ '-jar', 'testingbot-tunnel/testingbot-tunnel.jar', this.apiKey, this.apiSecret, '-P', this.port, '-f', readyFile ]; this.fastFailDomains.length && args.push('-F', this.fastFailDomains.join(',')); this.logFile && args.push('-l', this.logFile); this.useJettyProxy || args.push('-x'); this.useSquidProxy || args.push('-q'); this.useCompression && args.push('-b'); this.useSsl && args.push('-s'); this.verbose && args.push('-d'); if (this.proxy) { var proxy = urlUtil.parse(this.proxy); proxy.hostname && args.unshift('-Dhttp.proxyHost=', proxy.hostname); proxy.port && args.unshift('-Dhttp.proxyPort=', proxy.port); } return args; }, sendJobState: function (jobId, data) { var payload = {}; data.success != null && (payload['test[success]'] = data.success ? 1 : 0); data.status && (payload['test[status_message]'] = data.status); data.name && (payload['test[name]'] = data.name); data.extra && (payload['test[extra]'] = JSON.stringify(data.extra)); data.tags && data.tags.length && (payload.groups = data.tags.join(',')); payload = ioQuery.objectToQuery(payload); return request.put('https://api.testingbot.com/v1/tests/' + jobId, { data: payload, handleAs: 'text', headers: { 'Content-Length': Buffer.byteLength(payload, 'utf8'), 'Content-Type': 'application/x-www-form-urlencoded' }, password: this.apiSecret, user: this.apiKey, proxy: this.proxy }).then(function (response) { if (response.data) { var data = JSON.parse(response.data); if (data.error) { throw new Error(data.error); } else if (!data.success) { throw new Error('Job data failed to save.'); } else if (response.statusCode !== 200) { throw new Error('Server reported ' + response.statusCode + ' with: ' + response.data); } } else { throw new Error('Server reported ' + response.statusCode + ' with no other data.'); } }); }, _start: function () { var readyFile = pathUtil.join(os.tmpdir(), 'testingbot-' + Date.now()); var child = this._makeChild(readyFile); var childProcess = child.process; var dfd = child.deferred; // Polling API is used because we are only watching for one file, so efficiency is not a big deal, and the // `fs.watch` API has extra restrictions which are best avoided fs.watchFile(readyFile, { persistent: false, interval: 1007 }, function (current, previous) { if (Number(current.mtime) === Number(previous.mtime)) { // readyFile hasn't been modified, so ignore the event return; } fs.unwatchFile(readyFile); dfd.resolve(); }); var self = this; var lastMessage; this._handles.push( util.on(childProcess.stderr, 'data', function (data) { data.split('\n').forEach(function (message) { if (message.indexOf('INFO: ') === 0) { message = message.slice('INFO: '.length); // the tunnel produces a lot of repeating messages during setup when the status is pending; // deduplicate them for sanity if ( message !== lastMessage && message.indexOf('>> [') === -1 && message.indexOf('<< [') === -1 ) { self.emit('status', message); lastMessage = message; } } else if (message.indexOf('SEVERE: ') === 0) { dfd.reject(message); } }); }) ); return child; }, /** * Attempt to normalize a TestingBot described environment with the standard Selenium capabilities * * TestingBot returns a list of environments that looks like: * * { * "selenium_name": "Chrome36", * "name": "googlechrome", * "platform": "CAPITAN", * "version":"36" * } * * @param {Object} environment a TestingBot environment descriptor * @returns a normalized descriptor * @private */ _normalizeEnvironment: function (environment) { var browserMap = { googlechrome: 'chrome', iexplore: 'internet explorer' }; var platform = environment.platform; var browserName = browserMap[environment.name] || environment.name; var version = environment.version; return { platform: platform, browserName: browserName, version: version, descriptor: environment, intern: { platform: platform, browserName: browserName, version: version } }; } }); module.exports = TestingBotTunnel;