Merge pull request #1 from benjamn/run-tests-in-iframes

Run each test in its own <iframe>
This commit is contained in:
Ben Newman
2013-06-03 10:58:01 -07:00
24 changed files with 241 additions and 79 deletions
+2
View File
@@ -53,7 +53,9 @@ module.exports = function(grunt) {
grunt.registerTask('build:min', ['jsx:release', 'browserify:min']);
grunt.registerTask('build:test', [
'jsx:debug',
'jsx:jasmine',
'jsx:test',
'browserify:jasmine',
'browserify:test'
]);
+15 -1
View File
@@ -76,10 +76,23 @@ var transformer = {
after: [simpleBannerify]
};
var jasmine = {
entries: [
"./build/jasmine/all.js"
],
requires: {
"jasmine": "./build/jasmine/all.js"
},
outfile: "./build/jasmine.js",
debug: false
};
var test = {
entries: [
"./build/modules/test/all.js",
"./build/modules/**/__tests__/*-test.js"
],
requires: [
"**/__tests__/*-test.js"
],
outfile: './build/react-test.js',
debug: false,
@@ -88,6 +101,7 @@ var test = {
module.exports = {
basic: basic,
jasmine: jasmine,
test: test,
min: min,
transformer: transformer
+19 -3
View File
@@ -6,7 +6,18 @@ var rootIDs = [
var debug = {
rootIDs: rootIDs,
configFile: "grunt/config/jsx/debug.json"
configFile: "grunt/config/jsx/debug.json",
sourceDir: "src",
outputDir: "build/modules"
};
var jasmine = {
rootIDs: [
"all"
],
configFile: debug.configFile,
sourceDir: "vendor/jasmine",
outputDir: "build/jasmine"
};
var test = {
@@ -14,16 +25,21 @@ var test = {
"test/all.js",
"**/__tests__/*.js"
]),
configFile: debug.configFile
configFile: debug.configFile,
sourceDir: "src",
outputDir: "build/modules"
};
var release = {
rootIDs: rootIDs,
configFile: "grunt/config/jsx/release.json"
configFile: "grunt/config/jsx/release.json",
sourceDir: "src",
outputDir: "build/modules"
};
module.exports = {
debug: debug,
jasmine: jasmine,
test: test,
release: release
};
+4 -1
View File
@@ -4,5 +4,8 @@ exports.run = {
port: 8080,
harness: "test/phantom-harness.js",
// Run `grunt test --debug` to enable in-browser testing.
debug: !!grunt.option("debug")
debug: !!grunt.option("debug"),
tests: [
"**/__tests__/*-test.js"
]
};
+14 -4
View File
@@ -12,7 +12,6 @@ module.exports = function() {
// More/better assertions
// grunt.config.requires('outfile');
// grunt.config.requires('entries');
config.requires = config.requires || {};
config.transforms = config.transforms || [];
config.after = config.after || [];
if (typeof config.after === 'function') {
@@ -24,9 +23,20 @@ module.exports = function() {
var bundle = browserify(entries);
// Make sure the things that need to be exposed are.
// TODO: support a blob pattern maybe?
for (var name in config.requires) {
bundle.require(config.requires[name], { expose: name });
var requires = config.requires || {};
if (requires instanceof Array) {
grunt.file.expand({
nonull: true, // Keep IDs that don't expand to anything.
cwd: "src"
}, requires).forEach(function(name) {
bundle.require("./build/modules/" + name, {
expose: name.replace(/\.js$/i, "")
});
});
} else if (typeof requires === "object") {
Object.keys(requires).forEach(function(name) {
bundle.require(requires[name], { expose: name });
});
}
// Extract other options
+2 -2
View File
@@ -9,8 +9,8 @@ module.exports = function() {
var args = [
"bin/jsx",
"src",
"build/modules"
config.sourceDir,
config.outputDir
];
var rootIDs = expand({
+8
View File
@@ -37,6 +37,14 @@ function run(config, done) {
args.push("--debug");
}
args.push("--tests");
var tests = grunt.file.expand({
nonull: true,
cwd: "src"
}, config.tests || []).forEach(function(file) {
args.push(file.replace(/\.js$/i, ""));
});
var child = spawn({
cmd: phantomjs,
args: args
+48 -9
View File
@@ -2,17 +2,56 @@
// modules in src/test and to specify an ordering on those modules, since
// some still have implicit dependencies on others.
require("./phantom");
require("./console");
var Ap = Array.prototype;
var slice = Ap.slice;
var Fp = Function.prototype;
if (!Fp.bind) {
// PhantomJS doesn't support Function.prototype.bind natively, so
// polyfill it whenever this module is required.
Fp.bind = function(context) {
var func = this;
var args = slice.call(arguments, 1);
var bound;
if (func.prototype) {
if (args.length > 0) {
bound = function() {
return func.apply(
!(this instanceof func) && context || this,
args.concat(slice.call(arguments))
);
};
} else {
bound = function() {
return func.apply(
!(this instanceof func) && context || this,
arguments
);
};
}
bound.prototype = Object.create(func.prototype);
} else if (args.length > 0) {
bound = function() {
return func.apply(
context || this,
args.concat(slice.call(arguments))
);
};
} else {
bound = function() {
return func.apply(context || this, arguments);
};
}
return bound;
};
}
require("ReactTestUtils");
require("reactComponentExpect");
require("./diff");
require("./PrintReporter");
require("./HtmlReporter");
require("./ReporterView");
require("./SpecView");
require("./SuiteView");
require("./jasmine-support");
require("mocks");
require("mock-modules");
require("./mock-timers");
@@ -100,6 +100,7 @@ describe('ImmutableObject', function() {
});
testDev('should prevent shallow field addition when strict', function() {
if (window.callPhantom) return;
expect(function() {
var io = new ImmutableObject({oldField: 'asdf'});
io.newField = 'this will not work';
@@ -107,6 +108,7 @@ describe('ImmutableObject', function() {
});
testDev('should prevent shallow field mutation when strict', function() {
if (window.callPhantom) return;
expect(function() {
var io = new ImmutableObject({oldField: 'asdf'});
io.oldField = 'this will not work!';
@@ -114,6 +116,7 @@ describe('ImmutableObject', function() {
});
testDev('should prevent deep field addition when strict', function() {
if (window.callPhantom) return;
expect(function() {
var io =
new ImmutableObject({shallowField: {deepField: {oldField: null}}});
@@ -122,6 +125,7 @@ describe('ImmutableObject', function() {
});
testDev('should prevent deep field mutation when strict', function() {
if (window.callPhantom) return;
expect(function() {
var io =
new ImmutableObject({shallowField: {deepField: {oldField: null}}});
+18
View File
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<script>
jasmine = parent.jasmine;
jasmine.exposeFrom(window);
console = parent.console;
callPhantom = parent.callPhantom;
</script>
<script src="react-test.js"></script>
</head>
<body>
<script>
require(window.frameElement.getAttribute("test"));
</script>
</body>
</html>
+9 -3
View File
@@ -2,12 +2,18 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="jasmine.css" />
<style type="text/css">
iframe {
visibility: hidden;
position: absolute;
left: -1000px;
top: -1000px;
}
</style>
<script src="jasmine.js"></script>
<script src="jasmine-html.js"></script>
<script src="react-test.js"></script>
<script>
window.onload = function() {
jasmine.getEnv().execute();
require("jasmine").getEnv().execute();
};
</script>
</head>
+33 -11
View File
@@ -19,36 +19,58 @@ fs.changeWorkingDirectory(cwd);
// Hard to believe PhantomJS has no option parsing module.
var port = 8080;
var debug = false;
var lastArg;
var tests = [];
var rest = [];
while (argv.length > 0) {
var arg = argv.pop();
if (arg === "--port") {
port = +lastArg;
port = +rest.pop();
} else if (arg === "--debug") {
debug = true;
} else if (arg === "--tests") {
while (rest.length > 0)
tests.push(rest.pop());
}
lastArg = arg;
rest.push(arg);
}
// Dynamically interpolate the individual test <iframe>s.
var indexHtml = fs.read("index.html").replace(
/<body>([\s\S]*?)<\/body>/im,
function(outer, inner) {
return "<body>" + tests.map(function(test) {
return '\n <iframe src="frame.html" test=' +
JSON.stringify(test) + '></iframe>';
}).join("") + inner + "</body>";
}
);
var server = require("webserver").create();
server.listen(port, function(req, res) {
var file = req.url.replace(/^\/+/, "");
var content;
switch (file) {
case "":
default:
file = "index.html";
break;
case "react-test.js":
file = "../build/" + file;
break;
case "jasmine.css":
case "jasmine.js":
case "jasmine-html.js":
file = "../vendor/jasmine/" + file;
break;
case "jasmine.js":
file = "../build/" + file;
break;
case "frame.html":
break;
case "":
default:
file = "index.html";
content = indexHtml; // Prevents calling fs.read again.
break;
}
if (/\.css$/i.test(file)) {
@@ -60,7 +82,7 @@ server.listen(port, function(req, res) {
}
res.statusCode = 200;
res.write(fs.read(file));
res.write(content || fs.read(file));
res.close();
});
@@ -1,3 +1,5 @@
var jasmine = require("./jasmine");
exports.HtmlReporter =
jasmine.HtmlReporter = function(_doc) {
var self = this;
@@ -1,3 +1,4 @@
var jasmine = require("./jasmine");
var diff = require('./diff');
var red = '\u001b[1;41m';
@@ -54,7 +55,7 @@ PrintReporter.prototype.reportRunnerResults = function(runner) {
this.failCount + " fail"
].join(" "));
require("test/phantom").exit(this.failCount);
require("./phantom").exit(this.failCount);
};
@@ -1,3 +1,5 @@
var jasmine = require("jasmine");
jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
+2
View File
@@ -1,3 +1,5 @@
var jasmine = require("./jasmine");
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
+2
View File
@@ -1,3 +1,5 @@
var jasmine = require("./jasmine");
jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
+38
View File
@@ -0,0 +1,38 @@
require("./phantom");
require("./console");
// TODO Also bundle jasmine.css here.
var jasmine = require("./jasmine");
require("./jasmine-html");
require("./jasmine-support");
require("./HtmlReporter");
require("./PrintReporter");
require("./ReporterView");
require("./SpecView");
require("./SuiteView");
var env = jasmine.getEnv();
env.addReporter(new jasmine.HtmlReporter);
env.addReporter(new jasmine.PrintReporter);
function exposeFrom(obj) {
obj.spyOn = jasmine.spyOn;
obj.it = jasmine.it;
obj.xit = jasmine.xit;
obj.expect = jasmine.expect;
obj.runs = jasmine.runs;
obj.waits = jasmine.waits;
obj.waitsFor = jasmine.waitsFor;
obj.beforeEach = jasmine.beforeEach;
obj.afterEach = jasmine.afterEach;
obj.describe = jasmine.describe;
obj.xdescribe = jasmine.xdescribe;
obj.jasmine = jasmine;
return obj;
}
jasmine.exposeFrom = exposeFrom;
var global = Function("return this")();
exposeFrom(global);
module.exports = jasmine;
View File
View File
+2
View File
@@ -1,3 +1,5 @@
var jasmine = require("./jasmine");
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
@@ -1,5 +1,4 @@
var global = Function("return this")();
var jasmine = global.jasmine;
var jasmine = require("./jasmine");
var spec = false; // TODO
// Add some matcher for mock functions
@@ -38,7 +37,7 @@ var _xit = jasmine.Env.prototype.xit;
jasmine.Env.prototype.it = function(desc, func) {
// If spec is provided, only run matching specs
if (!spec || desc.match(new RegExp(spec, 'i'))) {
return _it.bind(this, desc, func)();
return _it.call(this, desc, func);
} else {
return this.xit(desc, func);
}
@@ -51,7 +50,7 @@ jasmine.Env.prototype.xit = function(desc, func) {
this.reporter.subReporters_[0].totalCount += matches.length;
}
}
return _xit.bind(this, desc, func)();
return _xit.call(this, desc, func);
}
// Mainline Jasmine sets __Jasmine_been_here_before__ on each object to detect
@@ -126,9 +125,3 @@ if (typeof WeakMap !== "undefined") {
return (mismatchKeys.length == 0 && mismatchValues.length == 0);
};
}
var HtmlReporter = require("./HtmlReporter").HtmlReporter;
var PrintReporter = require("./PrintReporter").PrintReporter;
jasmine.getEnv().addReporter(new HtmlReporter);
jasmine.getEnv().addReporter(new PrintReporter);
+12 -14
View File
@@ -1,12 +1,10 @@
var isCommonJS = typeof window == "undefined" && typeof exports == "object";
/**
* Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
*
* @namespace
*/
var jasmine = {};
if (isCommonJS) exports.jasmine = jasmine;
exports = module.exports = jasmine;
/**
* @private
*/
@@ -480,7 +478,7 @@ jasmine.log = function() {
var spyOn = function(obj, methodName) {
return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
};
if (isCommonJS) exports.spyOn = spyOn;
exports.spyOn = spyOn;
/**
* Creates a Jasmine spec that will be added to the current suite.
@@ -498,7 +496,7 @@ if (isCommonJS) exports.spyOn = spyOn;
var it = function(desc, func) {
return jasmine.getEnv().it(desc, func);
};
if (isCommonJS) exports.it = it;
exports.it = it;
/**
* Creates a <em>disabled</em> Jasmine spec.
@@ -511,7 +509,7 @@ if (isCommonJS) exports.it = it;
var xit = function(desc, func) {
return jasmine.getEnv().xit(desc, func);
};
if (isCommonJS) exports.xit = xit;
exports.xit = xit;
/**
* Starts a chain for a Jasmine expectation.
@@ -525,7 +523,7 @@ if (isCommonJS) exports.xit = xit;
var expect = function(actual) {
return jasmine.getEnv().currentSpec.expect(actual);
};
if (isCommonJS) exports.expect = expect;
exports.expect = expect;
/**
* Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
@@ -535,7 +533,7 @@ if (isCommonJS) exports.expect = expect;
var runs = function(func) {
jasmine.getEnv().currentSpec.runs(func);
};
if (isCommonJS) exports.runs = runs;
exports.runs = runs;
/**
* Waits a fixed time period before moving to the next block.
@@ -546,7 +544,7 @@ if (isCommonJS) exports.runs = runs;
var waits = function(timeout) {
jasmine.getEnv().currentSpec.waits(timeout);
};
if (isCommonJS) exports.waits = waits;
exports.waits = waits;
/**
* Waits for the latchFunction to return true before proceeding to the next block.
@@ -558,7 +556,7 @@ if (isCommonJS) exports.waits = waits;
var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
};
if (isCommonJS) exports.waitsFor = waitsFor;
exports.waitsFor = waitsFor;
/**
* A function that is called before each spec in a suite.
@@ -570,7 +568,7 @@ if (isCommonJS) exports.waitsFor = waitsFor;
var beforeEach = function(beforeEachFunction) {
jasmine.getEnv().beforeEach(beforeEachFunction);
};
if (isCommonJS) exports.beforeEach = beforeEach;
exports.beforeEach = beforeEach;
/**
* A function that is called after each spec in a suite.
@@ -582,7 +580,7 @@ if (isCommonJS) exports.beforeEach = beforeEach;
var afterEach = function(afterEachFunction) {
jasmine.getEnv().afterEach(afterEachFunction);
};
if (isCommonJS) exports.afterEach = afterEach;
exports.afterEach = afterEach;
/**
* Defines a suite of specifications.
@@ -602,7 +600,7 @@ if (isCommonJS) exports.afterEach = afterEach;
var describe = function(description, specDefinitions) {
return jasmine.getEnv().describe(description, specDefinitions);
};
if (isCommonJS) exports.describe = describe;
exports.describe = describe;
/**
* Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
@@ -613,7 +611,7 @@ if (isCommonJS) exports.describe = describe;
var xdescribe = function(description, specDefinitions) {
return jasmine.getEnv().xdescribe(description, specDefinitions);
};
if (isCommonJS) exports.xdescribe = xdescribe;
exports.xdescribe = xdescribe;
// Provide the XMLHttpRequest class for IE 5.x-6.x:
-20
View File
@@ -19,26 +19,6 @@
var console = require("./console");
var global = Function("return this")();
var Ap = Array.prototype;
var slice = Ap.slice;
var Fp = Function.prototype;
if (!Fp.bind) {
// PhantomJS doesn't support Function.prototype.bind natively, so
// polyfill it whenever this module is required.
Fp.bind = function(context) {
var func = this;
var args = slice.call(arguments, 1);
return args.length > 0 ? function() {
return func.apply(
context || this,
args.concat(slice.call(arguments))
);
} : function() {
return func.apply(context || this, arguments);
};
};
}
if (global.callPhantom) {
// Phantom's onConsoleMessage support is lacking (only one argument