| #!/usr/bin/env node |
| 'use strict'; |
| |
| var wd = require('wd'); |
| wd.configureHttp({timeout: 180000}); // 3 minutes |
| |
| var sauceConnectLauncher = require('sauce-connect-launcher'); |
| var selenium = require('selenium-standalone'); |
| var querystring = require("querystring"); |
| |
| var MochaSpecReporter = require('mocha').reporters.Spec; |
| |
| var devserver = require('./dev-server.js'); |
| |
| var testTimeout = 30 * 60 * 1000; |
| |
| var username = process.env.SAUCE_USERNAME; |
| var accessKey = process.env.SAUCE_ACCESS_KEY; |
| |
| var SELENIUM_VERSION = process.env.SELENIUM_VERSION || '3.141.0'; |
| var CHROME_BIN = process.env.CHROME_BIN; |
| var FIREFOX_BIN = process.env.FIREFOX_BIN; |
| |
| // BAIL=0 to disable bailing |
| var bail = process.env.BAIL !== '0'; |
| |
| // process.env.CLIENT is a colon seperated list of |
| // (saucelabs|selenium):browserName:browserVerion:platform |
| var tmp = (process.env.CLIENT || 'selenium:firefox').split(':'); |
| var client = { |
| runner: tmp[0] || 'selenium', |
| browser: tmp[1] || 'firefox', |
| version: tmp[2] || null, // Latest |
| platform: tmp[3] || null |
| }; |
| |
| var testRoot = 'http://127.0.0.1:8000/tests/'; |
| var testUrl; |
| if (process.env.PERF) { |
| testUrl = testRoot + 'performance/index.html'; |
| } else if (process.env.TYPE === 'fuzzy') { |
| testUrl = testRoot + 'fuzzy/index.html'; |
| } else if (process.env.TYPE === 'mapreduce') { |
| testUrl = testRoot + 'mapreduce/index.html'; |
| } else if (process.env.TYPE === 'find') { |
| testUrl = testRoot + 'find/index.html'; |
| } else { |
| testUrl = testRoot + 'integration/index.html'; |
| } |
| |
| var qs = { remote: 1 }; |
| |
| var sauceClient; |
| var sauceConnectProcess; |
| var tunnelId = process.env.TRAVIS_JOB_NUMBER || 'tunnel-' + Date.now(); |
| |
| if (client.runner === 'saucelabs') { |
| qs.saucelabs = true; |
| } |
| if (process.env.INVERT) { |
| qs.invert = process.env.INVERT; |
| } |
| if (process.env.GREP) { |
| qs.grep = process.env.GREP; |
| } |
| if (process.env.ADAPTERS) { |
| qs.adapters = process.env.ADAPTERS; |
| } |
| if (process.env.AUTO_COMPACTION) { |
| qs.autoCompaction = true; |
| } |
| if (process.env.SERVER) { |
| qs.SERVER = process.env.SERVER; |
| } |
| if (process.env.SKIP_MIGRATION) { |
| qs.SKIP_MIGRATION = process.env.SKIP_MIGRATION; |
| } |
| if (process.env.POUCHDB_SRC) { |
| qs.src = process.env.POUCHDB_SRC; |
| } |
| if (process.env.PLUGINS) { |
| qs.plugins = process.env.PLUGINS; |
| } |
| if (process.env.COUCH_HOST) { |
| qs.couchHost = process.env.COUCH_HOST; |
| } |
| if (process.env.ADAPTER) { |
| qs.adapter = process.env.ADAPTER; |
| } |
| if (process.env.ITERATIONS) { |
| qs.iterations = process.env.ITERATIONS; |
| } |
| if (process.env.NEXT) { |
| qs.NEXT = '1'; |
| } |
| |
| testUrl += '?'; |
| testUrl += querystring.stringify(qs); |
| |
| function testError(e) { |
| console.error(e); |
| console.error('Doh, tests failed'); |
| |
| closeClient(function () { |
| process.exit(3); |
| }); |
| } |
| |
| function startSelenium(callback) { |
| // Start selenium |
| var opts = {version: SELENIUM_VERSION}; |
| selenium.install(opts, function (err) { |
| if (err) { |
| console.error('Failed to install selenium'); |
| process.exit(1); |
| } |
| selenium.start(opts, function () { |
| sauceClient = wd.promiseChainRemote(); |
| callback(); |
| }); |
| }); |
| } |
| |
| function startSauceConnect(callback) { |
| |
| var options = { |
| username: username, |
| accessKey: accessKey, |
| tunnelIdentifier: tunnelId |
| }; |
| |
| sauceConnectLauncher(options, function (err, sauceProcess) { |
| if (err) { |
| console.error('Failed to connect to saucelabs'); |
| console.error(err); |
| return process.exit(1); |
| } |
| sauceConnectProcess = sauceProcess; |
| sauceClient = wd.promiseChainRemote("localhost", 4445, username, accessKey); |
| callback(); |
| }); |
| } |
| |
| function closeClient(callback) { |
| sauceClient.quit().then(function () { |
| if (sauceConnectProcess) { |
| sauceConnectProcess.close(function () { |
| callback(); |
| }); |
| } else { |
| callback(); |
| } |
| }); |
| } |
| |
| function RemoteRunner() { |
| this.handlers = {}; |
| this.completed = false; |
| this.failed = false; |
| } |
| |
| RemoteRunner.prototype.on = function (name, handler) { |
| var handlers = this.handlers; |
| |
| if (!handlers[name]) { |
| handlers[name] = []; |
| } |
| handlers[name].push(handler); |
| }; |
| |
| RemoteRunner.prototype.handleEvents = function (events) { |
| var self = this; |
| var handlers = this.handlers; |
| |
| events.forEach(function (event) { |
| self.completed = self.completed || event.name === 'end'; |
| self.failed = self.failed || event.name === 'fail'; |
| |
| var additionalProps = ['pass', 'fail', 'pending'].indexOf(event.name) === -1 ? {} : { |
| slow: event.obj.slow ? function () { return event.obj.slow; } : function () { return 60; }, |
| fullTitle: event.obj.fullTitle ? function () { return event.obj.fullTitle; } : undefined |
| }; |
| var obj = Object.assign({}, event.obj, additionalProps); |
| |
| handlers[event.name].forEach(function (handler) { |
| handler(obj, event.err); |
| }); |
| |
| if (event.logs && event.logs.length > 0) { |
| event.logs.forEach(function (line) { |
| if (line.type === 'log') { |
| console.log(line.content); |
| } else if (line.type === 'error') { |
| console.error(line.content); |
| } else { |
| console.error('Invalid log line', line); |
| } |
| }); |
| console.log(); |
| } |
| }); |
| }; |
| |
| RemoteRunner.prototype.bail = function () { |
| var handlers = this.handlers; |
| |
| handlers['end'].forEach(function (handler) { |
| handler(); |
| }); |
| |
| this.completed = true; |
| }; |
| |
| function BenchmarkReporter(runner) { |
| runner.on('benchmark:result', function (obj) { |
| console.log(' ', obj); |
| }); |
| } |
| |
| function startTest() { |
| |
| console.log('Starting', client, 'on', testUrl); |
| |
| var opts = { |
| browserName: client.browser, |
| version: client.version, |
| platform: client.platform, |
| tunnelTimeout: testTimeout, |
| name: client.browser + ' - ' + tunnelId, |
| 'max-duration': 60 * 45, |
| 'command-timeout': 599, |
| 'idle-timeout': 599, |
| 'tunnel-identifier': tunnelId |
| }; |
| if (CHROME_BIN) { |
| opts.chromeOptions = { |
| binary: CHROME_BIN, |
| args: ['--headless', '--disable-gpu', '--no-sandbox', '--disable-setuid-sandbox'] |
| }; |
| } |
| if (FIREFOX_BIN) { |
| opts.firefox_binary = FIREFOX_BIN; |
| } |
| |
| var runner = new RemoteRunner(); |
| new MochaSpecReporter(runner); |
| new BenchmarkReporter(runner); |
| |
| sauceClient.init(opts, function () { |
| console.log('Initialized'); |
| |
| sauceClient.get(testUrl, function () { |
| console.log('Successfully started'); |
| |
| sauceClient.eval('navigator.userAgent', function (err, userAgent) { |
| if (err) { |
| testError(err); |
| } else { |
| console.log('Testing on:', userAgent); |
| |
| /* jshint evil: true */ |
| var interval = setInterval(function () { |
| sauceClient.eval('window.testEvents()', function (err, events) { |
| if (err) { |
| clearInterval(interval); |
| testError(err); |
| } else if (events) { |
| runner.handleEvents(events); |
| |
| if (runner.completed || (runner.failed && bail)) { |
| if (!runner.completed && runner.failed) { |
| try { |
| runner.bail(); |
| } catch (e) { |
| // Temporary debugging of bailing failure |
| console.log('An error occurred while bailing:'); |
| console.log(e); |
| } |
| } |
| |
| clearInterval(interval); |
| |
| closeClient(function () { |
| process.exit(!process.env.PERF && runner.failed ? 1 : 0); |
| }); |
| } |
| } |
| }); |
| }, 10 * 1000); |
| |
| } |
| }); |
| }); |
| }); |
| } |
| |
| devserver.start(function () { |
| if (client.runner === 'saucelabs') { |
| startSauceConnect(startTest); |
| } else { |
| startSelenium(startTest); |
| } |
| }); |