Files
meteor/tools/run-selenium.js
2015-01-20 18:54:45 -05:00

204 lines
5.2 KiB
JavaScript

var _ = require('underscore');
var Fiber = require('fibers');
var Future = require('fibers/future');
var files = require('./files.js');
var runLog = require('./run-log.js');
var utils = require('./utils.js');
// options: runner url browserId xunitOutputFile
var Selenium = function (options) {
var self = this;
options = options || {};
self.driver = null;
self.server = null;
self.browserId = options.browserId || 'xunit';
self.url = options.url || 'http://localhost:3000/' + self.browserId;
self.xunitOutputFile = options.xunitOutputFile || 'test-results.xml';
self.runner = options.runner;
self.browser = options.browser || 'chrome';
self.xunitLines = null;
};
var _promiseToFuture = function (promise) {
var fut = new Future;
promise.then(function (result) {
fut.isResolved() || fut['return'](result);
}, function (err) {
fut.isResolved() || fut['throw'](err);
});
return fut;
};
// The magic prefix for special log output
// Must match packages/test-in-console/driver.js
var MAGIC_PREFIX = '##_meteor_magic##';
// For some reason, we can't see the console.log output
// unless we 'flush' it by sending another console.log via execute()
// Note that this is actually a magic message, so we get it echoed back to us;
// that's not necessary for this to work, but it keeps the output clean for users.
var DUMMY_FLUSH = MAGIC_PREFIX + "flush: flush";
_.extend(Selenium.prototype, {
// Start the selenium server, block (yield) until it is ready to go
// (actively listening on outer and proxying to inner), and then
// return.
start: function () {
var self = this;
if (self.server)
throw new Error("already running?");
self.xunitLines = [];
var webdriver = require('selenium-webdriver');
var capabilities;
var loggingPrefs;
if (self.browser === 'chrome') {
capabilities = webdriver.Capabilities.chrome();
loggingPrefs = {'browser': 'ALL'};
} else if (self.browser === 'firefox') {
capabilities = webdriver.Capabilities.firefox();
loggingPrefs = {'browser': 'ALL'};
} else {
throw new Error("Unhandled browser: " + self.browser);
}
if (loggingPrefs) {
capabilities = capabilities.set('loggingPrefs', loggingPrefs);
}
var builder = new webdriver.Builder().withCapabilities(capabilities);
self.driver = builder.build();
var fut = _promiseToFuture(self.driver.getSession());
fut.wait();
_promiseToFuture(self.driver.get(self.url)).wait();
Fiber(function () {
try {
self._pollLogs();
} catch (err) {
runLog.log("Log polling exited unexpectedly: " + err);
}
}).run();
},
stop: function () {
var self = this;
if (! self.driver)
return;
_promiseToFuture(self.driver.close()).wait();
_promiseToFuture(self.driver.quit()).wait();
self.driver = null;
},
_flushLogs: function () {
var self = this;
var promise = self.driver.executeScript("console.log('" + DUMMY_FLUSH + "');", []);
_promiseToFuture(promise).wait();
},
_getLogs: function () {
var self = this;
var promise = self.driver.manage().logs().get('browser');
return _promiseToFuture(promise).wait();
},
_gotStateDone: function () {
var self = this;
if (self.xunitOutputFile) {
runLog.log("Writing xunit output to: " + self.xunitOutputFile);
files.writeFile(self.xunitOutputFile, self.xunitLines.join('\n'));
}
if (self.runner) {
runLog.log("Shutting down in response to 'done' state");
self.runner.stop();
process.exit(0);
}
},
_gotState: function (state) {
var self = this;
runLog.log("State -> " + state);
if (state === "done") {
self._gotStateDone();
}
},
_gotMagicLog: function (facility, msg) {
var self = this;
if (facility == 'xunit') {
self.xunitLines.push(msg);
} else if (facility == 'state') {
self._gotState(msg);
} else if (facility == 'flush') {
// Ignore
} else {
runLog.log("Unknown magic: " + facility + ": " + msg);
}
},
_pollLogsOnce: function () {
var self = this;
self._flushLogs();
var logs = self._getLogs();
_.each(logs, function (log) {
var msg = log.message;
var regex = /([^\s]*)\s*([^\s]*)\s*(.*)/i;
var match = regex.exec(msg);
if (!match) {
runLog.log("Unknown console.log message format: " + JSON.stringify(log));
return;
}
msg = match[3];
if (msg === DUMMY_FLUSH) return;
if (msg.indexOf(MAGIC_PREFIX) == 0) {
msg = msg.substring(MAGIC_PREFIX.length);
var colonIndex = msg.indexOf(': ');
if (colonIndex == -1) {
self._gotMagicLog('', msg);
} else {
var facility = msg.substring(0, colonIndex);
msg = msg.substring(colonIndex + 2);
self._gotMagicLog(facility, msg);
}
} else {
runLog.log(msg);
}
});
},
_pollLogs: function () {
var self = this;
while (self.driver) {
try {
self._pollLogsOnce();
} catch (err) {
runLog.log("Error reading console log: " + err);
}
utils.sleepMs(1000);
}
},
});
exports.Selenium = Selenium;