Skip to content

Instantly share code, notes, and snippets.

@starstuck
Last active December 20, 2015 05:19
Show Gist options
  • Save starstuck/6077578 to your computer and use it in GitHub Desktop.
Save starstuck/6077578 to your computer and use it in GitHub Desktop.
Running jasmine tests in phantom while sending results back to mocha on node to have nice console reporter
#!/usr/bin/env node
var server = require('./test-server'),
child_process = require('child_process'),
port = server.port;
console.log('');
console.log('------------------------------------------------------------------------');
console.log('Running jasmine tests in phantomjs');
console.log('------------------------------------------------------------------------');
console.log('');
console.info('Spawning server http://localhost: ' + port);
server.listen(port);
console.info('Spawning phantomjs browser');
var phantom = child_process.spawn('phantomjs', [__dirname + '/test-phantom-main.js']);
// Uncomment to get phantom console beeing printed on screen
//phantom.stdout.on('data', function(data) {
// console.log('Phantom: ' + data);
//});
phantom.stderr.on('data', function (data) {
console.error('ERROR: ' + data);
});
phantom.on('close', function (code) {
if (code !== 0) {
console.error('PHANTOM DIED WITH UNEXPECTED RESULT CODE: ' + code);
}
console.log('Closing server after all tests are done');
server.close();
process.exit(0);
});
/**
* Jasmine reporter that sends tests through socketio
*/
(function(factory) {
//if (typeof define !== undefined) {
// define('socket-reporter', factory);
//} else if (typeof module !== undefined) {
// factory(require, exports, module);
//} else {
window.SocketReporter = factory(null, null, {});
//}
}(function(require, exports, module) {
var proto;
function Reporter(socket) {
this.socket = socket;
socket.on('close', function () {
window.close();
});
}
proto = Reporter.prototype;
proto.reportRunnerStarting = function(runner) {
this.socket.emit('start');
this.lastSuite = null;
};
proto.reportRunnerResults = function(runner) {
this.socket.emit('end');
};
proto.reportSpecStarting = function(spec) {
var socket = this.socket;
if (!this.lastSuite || (spec.suite.id !== this.lastSuite.id)) {
if (this.lastSuite) {
socket.emit('suite end', {
title: this.lastSuite.description,
root: {}
});
}
socket.emit('suite', {
title: spec.suite.description,
root: {}
});
this.lastSuite = spec.suite;
}
socket.emit('test', {
title: spec.description
});
};
proto.reportSpecResults = function(spec) {
var socket = this.socket,
results = spec.results(),
errors = results.getItems(),
test = {
title: spec.description
},
err;
if (results.skipped) {
socket.emit('pending', test);
} else if (results.passed() || errors[0].message == "Passed.") { // From some strange reason results.passed() was not reliable on windows
socket.emit('pass', test);
} else {
if (errors) {
test.err = {
message: errors[0].message,
stack: errors[0].trace.stack
};
}
socket.emit('fail', test);
}
socket.emit('test end', test);
};
return exports = module.exports = Reporter;
}));
/**
* Tests runner intended to be executed in phantomjs
*/
(function () {
var system = require('system'),
page = require('webpage').create();
page.onError = function(msg, trace) {
console.error('ERROR: ' + msg);
};
page.onConsoleMessage = function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
page.onAlert = function(msg) {
console.log('ALERT: ' + msg);
};
page.onLoadStarted = function() {
console.log('Load started');
};
page.onResourceRequested = function(requestData, networkRequest) {
//console.log('Request (#' + requestData.id + '): ' + JSON.stringify(requestData));
console.log('Fetching: ' + requestData.url);
};
page.onClosing = function() {
console.log('Page closing');
// Exit methods apparently needs to be run outside of current event handler,
// so phantom has time to cleanup page object
setTimeout(function() {
phantom.exit(0);
}, 0);
};
page.onLoadFinished = function(status) {
console.log('Load finished: ', status);
console.log("Got page on: ", page.evaluate(function() {
return window.location.toString();
}) );
};
page.open('http://localhost:9091/headless.html');
}());
#!/usr/bin/env node
var http = require('http'),
fs = require('fs'),
express = require('express'),
io = require('socket.io'),
SpecReporter = require('mocha').reporters.Spec,
Test = require('mocha').Test,
app = express(),
server = exports = module.exports = http.createServer(app),
port = exports.port = 9091,
sio = io.listen(server, {log: false}),
tests = [],
rootPath = __dirname + '/../..',
jasmineUrl = '/lib/jasmine-1.3.1',
requirejsConfig = {
baseUrl: 'src/main/js',
paths: {
// External libraries
'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min',
// Internal jQuery plugins
'jquery.c4tooltip': 'plugins/jquery.c4tooltip',
'jquery.c4carousel': 'plugins/jquery.c4carousel',
'jquery.paginate': 'plugins/jquery.paginate',
'jquery.splitlines': 'plugins/jquery.splitlines'
},
shim: {
'jquery': {exports: 'jQuery'}
}
};
function readTests(path) {
var diskPath = rootPath + path,
stat = fs.lstatSync(diskPath);
if (stat.isFile()) {
if (path.match(/\.js$/)) {
tests.push(path);
}
} else {
fs.readdir(diskPath, function(err, files) {
if (files) {
files.forEach(function(f) {
readTests(path + '/' + f);
});
}
});
}
}
readTests('/src/test');
app.get("/", function(req, res, next) {
res.send([
'<!DOCTYPE html>',
'<html lang="en"><head><title>Jasine test suite</title></head>',
'<body>',
' <link rel="stylesheet" href="' + jasmineUrl + '/jasmine.css" type="text/css">',
' <script src="' + jasmineUrl + '/jasmine.js"></script>',
' <script src="' + jasmineUrl + '/jasmine-html.js"></script>',
' <script src="/lib/require-2.1.5.js"></script>',
' <script>',
' requirejs.config(' + JSON.stringify(requirejsConfig) + ');',
'(' + function (tests) {
require(tests, function () {
jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
jasmine.getEnv().execute();
});
}.toString() + '(' + JSON.stringify(tests) + '));',
' </script>',
'</body></html>'
].join('\n'));
});
app.get("/headless.html", function(req, res, next) {
res.send([
'<!DOCTYPE html>',
'<html lang="en"><head><title>Jasmine test suite</title></head>',
'<body>',
' <script src="' + jasmineUrl + '/jasmine.js"></script>',
' <script src="/socket.io/socket.io.js"></script>',
' <script src="/lib/require-2.1.5.js"></script>',
' <script src="/src/scripts/test-jasmine-reporter.js"></script>',
' <script>',
' requirejs.config(' + JSON.stringify(requirejsConfig) + ');',
'(' + function (tests) {
require(tests, function () {
var socket = window.socket = io.connect('http://localhost:9091');
jasmine.getEnv().addReporter(new SocketReporter(socket));
jasmine.getEnv().execute();
});
}.toString() + '(' + JSON.stringify(tests) + '));',
' </script>',
'</body></html>'
].join('\n'));
});
app.use("/src", express.static(__dirname + '/..'));
app.use("/lib", express.static(__dirname + '/../../lib'));
/**
* Remote test stub, which provides mocha compatible test interface
*/
function TestStub(data) {
this.data = data;
this.title = data.title;
this.err = data.err;
};
TestStub.prototype.__proto__ = Test.prototype;
TestStub.prototype.fullTitle = function () {
//TODO: should include suite titles as well, or have suite stub in this.parent
return this.title;
};
/**
* Pseudo runner which is mimicking mocha runner
*/
function SocketRunner(socket) {
var me = this;
this.socket = socket;
this.listeners = {};
['start', 'suite', 'suite end', 'test', 'test end', 'pass', 'fail', 'pending', 'end'].forEach(function (event) {
socket.on(event, function(payload) {
me.emit(event, payload);
});
});
// TODO: do not send close signal if tool called for long running session
me.on('end', function() {
// Give time to reported to print out summary
setTimeout(function() {
socket.emit('close');
}, 0);
});
}
SocketRunner.prototype.emit = function(event, payload) {
var callbacks = this.listeners[event],
i, l;
if (callbacks) {
if (event.match(/^(test|pass|fail|pending)( end)?$/)) {
payload = new TestStub(payload);
}
for (i = 0; i < callbacks.length; i++) {
callbacks[i](payload, payload && payload.err);
}
}
};
SocketRunner.prototype.on = function(event, callback) {
var listeners = this.listeners;
if (! listeners[event]) {
listeners[event] = [];
}
listeners[event].push(callback);
};
sio.on('connection', function(socket) {
var runner = new SocketRunner(socket),
reporter = new SpecReporter(runner);
});
if(require.main === module) {
server.listen(port);
console.log('Test server lisening on http://localhost:' + port + '/');
console.log('Press Ctrl+C to close server');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment