Skip to content

Commit

Permalink
Make jsdom browserifiable and add browser test runner
Browse files Browse the repository at this point in the history
To run the tests: `node test/browser-runner.js`. The runner accepts the same
options as `test/runner`, as well as some optional arguments that control the
test setup.
  • Loading branch information
lawnsea committed Aug 2, 2014
1 parent 15e3c4f commit fd2bc1a
Show file tree
Hide file tree
Showing 11 changed files with 437 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
gmon.out
v8.log
node_modules
test/worker-bundle.js
7 changes: 7 additions & 0 deletions lib/jsdom.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ exports.debugMode = false;
});
});

exports.debugMode = false;

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

What are the additions to this file for? They seem bad?

This comment has been minimized.

Copy link
@lawnsea

lawnsea Aug 6, 2014

Author Member

This line seems to be an artifact of rebasing on top of e0006d8


defineGetter(exports, 'version', function() {
return pkg.version;
});

var level2Html = require('./jsdom/level2/html');
exports.level = function (level, feature) {
if(!feature) {
feature = 'core';
Expand Down
5 changes: 5 additions & 0 deletions lib/jsdom/contextify-shim.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = function (o) {
o.getGlobal = function () {
return o;
};
};
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,24 @@
"contextify": "~0.1.5"
},
"devDependencies" : {
"browser-request": "~0.3.1",
"cssstyle-browserify": "git://github.com/TreehouseJS/CSSStyleDeclaration.git",
"nodeunit": "~0.8.0",
"optimist": "*",
"urlmaster": ">=0.2.15"
"urlmaster": ">=0.2.15",
"http-browserify": "git://github.com/kumavis/http-browserify.git#dc84f15eb15c58505c0dea29de7ee64ff56dfe4f",
"browserify": "~3.24.1",

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

Browserify is up to 5.x these days ;)

This comment has been minimized.

Copy link
@lawnsea

lawnsea Aug 5, 2014

Author Member

Oh dang. I'll update and see what happens...

"q": "^1.0.1",
"wd": "^0.2.21",
"selenium-standalone": "^2.42.0-2.9.0",
"http-server": "^0.6.1"
},
"browser": {
"canvas": false,
"contextify": "./lib/jsdom/contextify-shim.js",
"cssstyle": "./node_modules/cssstyle-browserify/lib/CSSStyleDeclaration.js",
"http": "./node_modules/http-browserify/index.js",

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

you might be able to just do "http-browserify"? Not sure.

This comment has been minimized.

Copy link
@lawnsea

lawnsea Aug 11, 2014

Author Member

This is no longer necessary thanks to this workaround.

"request": "./node_modules/browser-request/index.js"
},
"scripts": {
"test": "node ./test/runner"
Expand Down
34 changes: 34 additions & 0 deletions test/browser-main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
window._browserRunner = {
events: []
};

var worker = new Worker('./worker-bundle.js');

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

This is so cool

var consoleEl = document.querySelector('.console');

function fire(event, detail) {
window._browserRunner.events.push({
event: event,
detail: detail
});
}

worker.onmessage = function (e) {
if (e.data.method) {
switch (e.data.method) {
case 'fire':
fire(e.data.params.event, e.data.params.data);
break;
case 'console':
fire('console', e.data.params);
console[e.data.params.level].apply(console, e.data.params.message);
break;
case 'ready':
fire('ready');
worker.postMessage(location.search.slice(1));
break;
default:
console.error('Unknown method', e.data.method);
}
}
};

183 changes: 183 additions & 0 deletions test/browser-runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
require('colors');
var EventEmitter = require('events').EventEmitter;
var wd = require('wd');
var Q = require('q');
var browser;

var optimist = require('./runner-options');

optimist.
usage('Run the jsdom test suite in a browser via WebDriver').
describe('http-port', 'port to run test server on (defaults to pid + 20000)').
describe('web-driver-port', 'port to run Selenium on (defaults to pid + 20000)').
describe('verbose-web-driver', 'print verbose output from wd to stdout').
describe('verbose-browser-console', 'print browser console to stdout');

var argv = optimist.argv;

if (argv.help) {
optimist.showHelp();
process.exit();
}

var httpPort = argv['http-port'] || process.pid + 20000;
var wdPort = argv['web-driver-port'] || httpPort + 10000;

/**
* Return the body of a function as a string
*
* wd should do this for us, but it doesn't
*/
function getFnBody(fn) {
var src = fn.toString();
return src.slice(src.indexOf('{') + 1, src.lastIndexOf('}'));
}

function run() {
var passed = false;
browser.init({ browserName: 'chrome' }).
then(function () {
return browser.setAsyncScriptTimeout(5000);
}).
then(function () {
return browser.get([
'http://localhost:',
httpPort,
'/test?',
require('querystring').stringify(argv)
].join(''));
}).
then(function (result) {
function browserPoll() {
var events = window._browserRunner.events;

return events.splice(0, events.length);
}

var deferred = Q.defer();

var runner = new EventEmitter();
require('./runner-display')(runner, argv, function (err) {

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

I prefer keeping requires at the top of the file. There are a lot of them scattered throughout this file...

passed = !err;
deferred.resolve();
});
var nodeunitTypes = require('nodeunit').types;

function poll() {
browser.
execute(getFnBody(browserPoll)).
then(function (events) {
var done = false;

events.forEach(function (event) {
switch (event.event) {
case 'testDone':
case 'moduleDone':
runner.emit(event.event,
event.detail[0],
nodeunitTypes.assertionList(
event.detail[1].map(nodeunitTypes.assertion)));
break;
case 'log':
runner.emit(event.event,
nodeunitTypes.assertion(event.detail[0]));
break;
case 'done':
runner.emit(event.event,
nodeunitTypes.assertionList(
event.detail[0].map(nodeunitTypes.assertion)));
break;
case 'console':
case 'http':
case 'status':
case 'command':
browser.
emit.apply(browser, [event.event].concat(event.detail));
break;
default:
runner.emit.apply(runner, [event.event].concat(event.detail));
}

if (event.detail && event.event === 'done') {
done = true;
}
});

if (!done) {
setTimeout(poll, 50);
}
});
}

poll();
return deferred.promise;
}).
fin(function () {

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

prefer .finally to .fin

return browser.quit();
}).
fin(function () {
process.exit(passed ? 0 : 1);
}).
done();
}

// browserify and run the tests
require('child_process').exec(

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

Hmm it's a bit of a shame we can't use browserify programmatically. Did you try?

This comment has been minimized.

Copy link
@lawnsea

lawnsea Aug 5, 2014

Author Member

I did not. I can take a run at it.

'node_modules/browserify/bin/cmd.js test/worker.js -o test/worker-bundle.js',
function (err, stdout, stderr) {
if (err) {
console.log(stdout.toString());
console.log('Failed to browserify test/worker');
console.log(err);
process.exit(1);
return;
}

// start web server
var httpServer = require('http-server').createServer().listen(httpPort);

// set up webdriver
browser = wd.promiseRemote({
port: wdPort
});

if (argv['verbose-web-driver']) {
// really verbose wd logging
browser.on('status', function (info) {
console.log(info.cyan);
});
browser.on('command', function (eventType, command, response) {
console.log(' > ' + eventType.cyan, command, (response || '').grey);
});
browser.on('http', function (method, path, data) {
console.log(' > ' + method.magenta, path, (data || '').grey);
});
}

if (argv['verbose-browser-console']) {
browser.on('console', function (detail) {
console[detail.level].apply(console, detail.message);
});
}

// start selenium

This comment has been minimized.

Copy link
@domenic

domenic Aug 5, 2014

SO COOL

var selenium = require('selenium-standalone');
var wdServer = selenium({
stdio: 'pipe'
}, ['-port', wdPort]);

// time out after a default of 30 seconds
var h = setTimeout(function () {
console.log('Timed out waiting for selenium server to start');
wdServer.kill();
process.exit(1);
}, argv.wdTimeout || 30 * 1000);

// Wait for selenium server to start.
wdServer.stdout.on('data', function (output) {
if (output.toString().indexOf('Started org.openqa.jetty.jetty.Server') >= 0) {
clearTimeout(h);
run();
}
});
});
10 changes: 10 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>Browserify in worker test</title>
</head>
<body>
<pre class='console'></pre>
<script src="./browser-main.js"></script>
</body>
</html>
2 changes: 1 addition & 1 deletion test/level2/core/files/staff2.xml.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var dom = require(__dirname + "/../../../../lib/jsdom/level2/core").dom.level2.core;
var dom = require("../../../../lib/jsdom/level2/core").dom.level2.core;

exports.staff2 = function() {

Expand Down
1 change: 1 addition & 0 deletions test/level2/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var assert = require('assert');
var http = require('http');
var fs = require('fs');
var path = require('path');

exports.tests = {

HTMLStyleElement01 : function (test) {
Expand Down
Loading

0 comments on commit fd2bc1a

Please sign in to comment.