Merge pull request #451 from subtleGradient/subtlegradient/browser-testing

Browser testing.
This commit is contained in:
Ben Newman
2013-11-07 10:20:58 -08:00
22 changed files with 562 additions and 277 deletions
+1 -1
View File
@@ -22,4 +22,4 @@ docs/js/live_editor.js
docs/js/examples
docs/downloads
examples/shared/*.js
test/the-files-to-test.generated.js
+2
View File
@@ -13,3 +13,5 @@ env:
- secure: qPvsJ46XzGrdIuPA70b55xQNGF8jcK7N1LN5CCQYYocXLa+fBrl+fTE77QvehOPhqwJXcj6kOxI+sY0KrVwV7gmq2XY2HZGWUSCxTN0SZlNIzqPA80Y7G/yOjA4PUt8LKgP+8tptyhTAY56qf+hgW8BoLiKOdztYF2p+3zXOLuA=
# SECRET_TOKEN
- secure: dkpPW+VnoqC/okhRdV90m36NcyBFhcwEKL3bNFExAwi0dXnFao8RoFlvnwiPlA23h2faROkMIetXlti6Aju08BgUFV+f9aL6vLyU7gUent4Nd3413zf2fwDtXIWIETg6uLnOpSykGKgCAT/hY3Q2oPLqOoY0OxfgnbqwxkxljrE=
- secure: eHMg4yQWJ+5zFxNcjVwpdpcbZCQMaSByqJy0wwQ2XOOMItdCvSfO/fa+mYoVND/9Orsi5kvXC9tx4sYHyth5hLrvO+bghYjiayrbxYYrxso6G/aU8Pttc7s/qk3I4zadLlua1kGy2TauByxS6nt9nUDe0FDaJKA9k1QXVlpq5Ac=
- secure: v65jUPPNBvJg1xhj4cY7wMKinCESUseHsW1d2gp68SluFHHyjWZOvXYXZdABqB/1uNmQxjbUkwkdSNAOn5k7ppjRcRnlAH2uu1xDqXrGp6cW4MfNPchrAe+FcUMtse/3THGJj8SZ9IQWIl+BHv4XASQ7FwqCR8cwlOfhsqdfch8=
+19 -10
View File
@@ -4,7 +4,8 @@ var exec = require('child_process').exec;
var jsxTask = require('./grunt/tasks/jsx');
var browserifyTask = require('./grunt/tasks/browserify');
var populistTask = require('./grunt/tasks/populist');
var phantomTask = require('./grunt/tasks/phantom');
var webdriverPhantomJSTask = require('./grunt/tasks/webdriver-phantomjs');
var webdriverJasmineTasks = require('./grunt/tasks/webdriver-jasmine');
var npmTask = require('./grunt/tasks/npm');
var releaseTasks = require('./grunt/tasks/release');
@@ -16,7 +17,8 @@ module.exports = function(grunt) {
jsx: require('./grunt/config/jsx/jsx'),
browserify: require('./grunt/config/browserify'),
populist: require('./grunt/config/populist'),
phantom: require('./grunt/config/phantom'),
connect: require('./grunt/config/server')(grunt),
"webdriver-jasmine": require('./grunt/config/webdriver-jasmine.js'),
npm: require('./grunt/config/npm'),
clean: ['./build', './*.gem', './docs/_site', './examples/shared/*.js'],
jshint: require('./grunt/config/jshint'),
@@ -25,11 +27,12 @@ module.exports = function(grunt) {
grunt.config.set('compress', require('./grunt/config/compress'));
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-compare-size');
grunt.loadNpmTasks('grunt-contrib-compress');
Object.keys(grunt.file.readJSON('package.json').devDependencies)
.filter(function(npmTaskName){ return npmTaskName.indexOf('grunt-') === 0;})
.forEach(function(npmTaskName){
grunt.loadNpmTasks(npmTaskName);
})
;
// Alias 'jshint' to 'lint' to better match the workflow we know
grunt.registerTask('lint', ['jshint']);
@@ -42,7 +45,7 @@ module.exports = function(grunt) {
grunt.registerMultiTask('populist', populistTask);
grunt.registerMultiTask('phantom', phantomTask);
grunt.registerMultiTask('webdriver-jasmine', webdriverJasmineTasks);
grunt.registerMultiTask('npm', npmTask);
@@ -70,11 +73,17 @@ module.exports = function(grunt) {
'jsx:jasmine',
'jsx:test',
'version-check',
'populist:jasmine',
'populist:test'
]);
grunt.registerTask('test', ['build:test', 'build:basic', 'phantom:run']);
grunt.registerTask('webdriver-phantomjs', webdriverPhantomJSTask);
grunt.registerTask('test:webdriver:phantomjs', [
'connect',
'webdriver-phantomjs',
'webdriver-jasmine:local'
]);
grunt.registerTask('test', ['build:test', 'build:basic', 'test:webdriver:phantomjs']);
grunt.registerTask('npm:test', ['build', 'npm:pack']);
// Optimized build task that does all of our builds. The subtasks will be run
-11
View File
@@ -1,11 +0,0 @@
var grunt = require("grunt");
exports.run = {
port: 8080,
harness: "test/phantom-harness.js",
// Run `grunt test --debug` to enable in-browser testing.
debug: !!grunt.option("debug"),
tests: [
"**/__tests__/*-test.js"
]
};
+47
View File
@@ -0,0 +1,47 @@
var pxlgif = Buffer('R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==', 'base64');
module.exports = function(grunt){
function printMiddleware(req, res, next) {
if (req._parsedUrl.pathname != '/print') return next();
if (req.query.message.indexOf('ok') === 0){
grunt.log.ok(req.query.message);
} else if (req.query.message.indexOf('not ok') === 0){
grunt.log.error(req.query.message);
} else {
grunt[req.query.type || 'log'].writeln('[%s][%s]', req.headers['user-agent'], Date.now(), req.query.message);
}
res.end(pxlgif);
}
function testResultLoggerMiddleware(req, res, next) {
if (!(req.body && req.body.data)) return next();
grunt.log.writeln('[%s][%s]', req.headers['user-agent'], Date.now(), req.body.data);
res.end('Got it, thanks!');
}
return {
server: {
options: {
base: '.',
hostname: '*',
port: 9999,
middleware: function(connect, options) {
connect.logger.token('user-agent', function(req, res){ return req.headers['user-agent']; });
connect.logger.token('timestamp', function(req, res){ return Date.now(); });
return [
connect.query(),
printMiddleware,
connect.logger({format:'[:user-agent][:timestamp] :method :url', stream:grunt.verbose}),
connect.bodyParser(),
testResultLoggerMiddleware,
connect.static(options.base),
connect.directory(options.base)
];
},
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
var grunt = require('grunt');
exports.local = {
webdriver: {
remote: { protocol: 'http:', hostname: '127.0.0.1', port: 9515, path: '/' }
},
url: "http://127.0.0.1:9999/test/index.html",
onComplete: function(report){
var browser = this;
if (!report.passed){
grunt.fatal("tests failed");
}
},
onError: function(error){
grunt.fatal(error);
}
}
-68
View File
@@ -1,68 +0,0 @@
'use strict';
var assert = require("assert");
var grunt = require("grunt");
var spawn = grunt.util.spawn;
var semver = require("semver");
var MIN_VERSION = "1.9.0";
var phantomjs = require("phantomjs").path;
function checkVersion(error, result, code) {
if (error) {
throw error;
}
assert.strictEqual(code, 0);
var version = result.stdout;
assert.ok(
semver.valid(version),
"Invalid PhantomJS version: " + version
);
assert.ok(
semver.gte(version, MIN_VERSION),
"PhantomJS v" + version + " too old; need to install " +
"v" + MIN_VERSION + " or higher."
);
}
function run(config, done) {
var args = [
config.harness,
"--port", config.port
];
if (config.debug) {
args.push("--debug");
}
args.push("--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
}, done);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
}
module.exports = function() {
var config = this.data;
var done = this.async();
spawn({
cmd: phantomjs,
args: ["--version"]
}, function(error, result, code) {
checkVersion(error, result, code);
run(config, done);
});
};
+12 -1
View File
@@ -1,11 +1,19 @@
'use strict';
var grunt = require('grunt');
var fs = require('fs')
module.exports = function() {
var config = this.data;
var done = this.async();
var theFilesToTestScript = fs.createWriteStream(__dirname + '/../../test/the-files-to-test.generated.js');
theFilesToTestScript.write('// Generated by ');
theFilesToTestScript.write(JSON.stringify(__filename.split(/(?=grunt)/)[1]));
theFilesToTestScript.write(' at ');
theFilesToTestScript.write(JSON.stringify(new Date()));
theFilesToTestScript.write('\n\n');
// create the bundle we'll work with
var args = config.args;
@@ -15,7 +23,9 @@ module.exports = function() {
nonull: true, // Keep IDs that don't expand to anything.
cwd: config.rootDirectory
}, requires).forEach(function(name) {
args.push(name.replace(/\.js$/i, ""));
name = name.replace(/\.js$/i, "");
args.push(name);
theFilesToTestScript.write('harness.enableTest(' + JSON.stringify(name) + ');\n');
});
require("populist").buildP({
@@ -23,6 +33,7 @@ module.exports = function() {
args: args
}).then(function(output) {
grunt.file.write(config.outfile, output);
theFilesToTestScript.end();
done();
});
};
+44
View File
@@ -0,0 +1,44 @@
var grunt = require("grunt");
var wd = require('wd');
module.exports = function(){
var config = this.data;
var taskSucceeded = this.async();
grunt.verbose.write('webdriver remote', JSON.stringify(config.webdriver.remote));
var browser = wd.promiseChainRemote(config.webdriver.remote);
browser.on('status', function(info) {
grunt.verbose.writeln(info);
});
browser.on('command', function(meth, path, data) {
grunt.verbose.writeln(' > ' + meth, path, data || '');
});
browser
.init(config.browser || {})
.get(config.url)
.then(function(){return browser;})
.then(getJSReport)
.then(config.onComplete && config.onComplete.bind(browser), config.onError && config.onError.bind(browser))
.fail(grunt.verbose.writeln.bind(grunt.verbose))
.fin(function(){
if (grunt.option('webdriver-keep-open')) return;
grunt.verbose.writeln('Closing the browser window. To keep it open, pass the --webdriver-keep-open flag to grunt.');
return browser.quit();
})
.done(
taskSucceeded.bind(null,true),
taskSucceeded.bind(null,false)
)
;
}
function getJSReport(browser){
return browser
.waitForCondition("typeof window.jasmine != 'undefined'", 500)
.waitForCondition("typeof window.jasmine.getJSReport != 'undefined'", 10e3)
.waitForCondition("window.testImageURL.running <= 0", 5e3)
.eval("jasmine.getJSReport()")
;
}
+37
View File
@@ -0,0 +1,37 @@
var grunt = require('grunt');
module.exports = function(){
var onReadyCallback = this.async();
var phantomjs = require("phantomjs").path;
var child_process = require('child_process');
var config = this.data || {};
var args = ["--webdriver=" + (config.port || 9515)];
grunt.verbose.writeln('phantomjs START path:%s args:%s', phantomjs, args);
var child = child_process.spawn(phantomjs, args);
process.on('exit', function() {
child.kill();
});
child.on('error', function(error) {
grunt.verbose.writeln('phantomjs ERROR');
grunt.fatal(error);
});
child.on('exit', function(code) {
grunt.verbose.writeln('phantomjs END');
if (code) grunt.fatal('phantomjs FAIL');
});
function verboseWrite(chunk) {
if (onReadyCallback && chunk.toString().indexOf('running on port') != -1) {
grunt.verbose.writeln('phantomjs STARTED');
onReadyCallback();
onReadyCallback = null;
}
grunt.verbose.write(chunk);
}
child.stdout.on('data', verboseWrite);
child.stderr.on('data', verboseWrite);
}
+6 -2
View File
@@ -48,7 +48,7 @@
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-jshint": "~0.6.0",
"optimist": "~0.6.0",
"phantomjs": "~1.9.1-4",
"phantomjs": "~1.9",
"recast": "~0.4.16",
"semver": "~2.1.0",
"uglify-js": "~2.4.0",
@@ -56,7 +56,11 @@
"grunt-compare-size": "~0.4.0",
"gzip-js": "~0.3.2",
"tmp": "~0.0.18",
"grunt-contrib-compress": "~0.5.1"
"grunt-contrib-compress": "~0.5.1",
"jasmine-tapreporter": "~0.2.2",
"grunt-contrib-connect": "~0.5.0",
"es5-shim": "~2.1.0",
"wd": "~0.2.2"
},
"engines": {
"node": ">=0.10.0"
@@ -22,11 +22,11 @@
"use strict";
describe('ReactWebWorker', function() {
it('can run React in a web worker', function() {
;(typeof Worker == 'undefined' ? xit : it)('can run React in a web worker', function() {
var done = false;
var error;
var worker = new Worker('/worker.js');
var worker = new Worker(window.ReactWebWorker_URL || '/src/test/worker.js?_=' + Date.now().toString(36));
worker.addEventListener('message', function(e) {
var data = JSON.parse(e.data);
if (data.type == 'error') {
+1 -1
View File
@@ -6,7 +6,7 @@ var global = {};
importScripts("phantomjs-shims.js");
try {
importScripts("react.js");
importScripts("../../build/react.js");
} catch (e) {
postMessage(JSON.stringify({
type: 'error',
+36 -21
View File
@@ -1,23 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="jasmine.css" />
<script src="jasmine.js"></script>
<script src="react-test.js"></script>
</head>
<body>
<script>
ENABLE_TESTS_HERE
<!doctype html>
<meta charset=utf-8>
<body>
<script>
;(function(){
(function(env) {
// Clean up any nodes the previous test might have added.
env.afterEach(function() {
harness.removeNextSiblings(document.body);
harness.removeNextSiblings(document.getElementById("HTMLReporter"));
});
var urls = [
'../vendor/jasmine/jasmine.js',
'../vendor/jasmine/jasmine-support.js',
'../vendor/jasmine/jasmine-html.js',
'../node_modules/jasmine-tapreporter/src/tapreporter.js',
'../vendor/jasmine-jsreporter/jasmine-jsreporter.js',
env.execute();
})(jasmine.getEnv());
</script>
</body>
</html>
'lib/testImageURL.browser.js',
'lib/reportTestResults.browser.js',
'../build/react.js',
'../build/react-test.js',
'the-files-to-test.generated.js',
'lib/jasmine-execute.js'
];
if (typeof Function.prototype.bind == 'undefined') {
urls.unshift('../node_modules/es5-shim/es5-sham.js');
urls.unshift('../node_modules/es5-shim/es5-shim.js');
}
var cacheBust = '?_=' + Date.now().toString(36);
for (var urls_index = -1, urls_length = urls.length; ++urls_index < urls_length;) {
document.write('<script src="' + urls[urls_index] + cacheBust + '"><\/script>');
}
var base = location.protocol + '//' + location.hostname + ':' + location.port + location.pathname.replace(/^\/?/,'/').split('/').reverse().slice(1).reverse().join('/');
window.ReactWebWorker_URL = base + '/../src/test/worker.js' + cacheBust;
}());
</script>
+14
View File
@@ -0,0 +1,14 @@
document.write('<style> @import "../vendor/jasmine/jasmine.css?_=' + (+new Date).toString(36) + '"; </style>');
;(function(env){
env.addReporter(new jasmine.HtmlReporter);
// Clean up any nodes the previous test might have added.
env.afterEach(function() {
harness.removeNextSiblings(document.body);
harness.removeNextSiblings(document.getElementById("HTMLReporter"));
});
window.onload = function(){
env.execute();
}
})(jasmine.getEnv());
+76
View File
@@ -0,0 +1,76 @@
console._error = console.error;
console._warn = console.warn;
console._log = console.log;
console.log = window.print = function(message){
console._log(message);
testImageURL('/print?type=log&message=' + encodeURIComponent(message) + '&_=' + Date.now().toString(36));
};
console.error = function(message){
console._error(message);
testImageURL('/print?type=error&message=' + encodeURIComponent(message) + '&_=' + Date.now().toString(36));
};
console.warn = function(message){
console._warn(message);
testImageURL('/print?type=warn&message=' + encodeURIComponent(message) + '&_=' + Date.now().toString(36));
};
;(function(env){
env.addReporter(new jasmine.JSReporter());
env.addReporter(new TAPReporter(window.print));
function report(){
if (typeof jasmine.getJSReport != 'function') return setTimeout(report, 100);
postDataToURL(JSON.stringify(jasmine.getJSReport()), '/reportTestResults', function(error, event){
if (error) return console.error(error);
});
}
var oldCallback = env.currentRunner().finishCallback;
env.currentRunner().finishCallback = function(){
if (oldCallback) oldCallback.apply(this, arguments);
report();
};
function postDataToURL(data, url, callback){
var id = '$' + (+new Date()).toString(36);
var postReportingTarget = document.createElement('iframe');
postReportingTarget.id = postReportingTarget.name = 'postReportingTarget' + id;
var postReportingForm = document.createElement('form');
postReportingForm.method = 'POST';
postReportingForm.action = url;
postReportingForm.target = postReportingTarget.name;
var postReportingData = document.createElement('input');
postReportingData.type = 'hidden';
postReportingData.name = 'data';
postReportingData.value = data || '{"error":"unknown error in postReportingData"}';
postReportingForm.appendChild(postReportingData);
postReportingForm.appendChild(postReportingTarget);
postReportingForm.style.cssText = "visibility:hidden; position:absolute; bottom:100%; right:100%";
function done(error, event){
postReportingForm.parentNode.removeChild(postReportingForm);
callback(error, event);
}
postReportingTarget.onerror = function(error){
done(error);
}
postReportingTarget.onload = function(event){
done(null, event);
}
document.body.appendChild(postReportingForm);
setTimeout(function(){
postReportingForm.submit();
},0);
}
}(window.jasmine.getEnv()));
+62
View File
@@ -0,0 +1,62 @@
testImageURL._recycle = function(img){
console.log('_recycle', img);
try {
img.src = '';
img.onload = img.onerror = null;
} catch(e){}
testImageURL._recycleBin.push(img);
}
testImageURL.getImage = function(callback){
// if (!testImageURL._recycleBin) testImageURL._recycleBin = [new Image(),new Image(),new Image(),new Image()];
// function get(){
// if (testImageURL._recycleBin.length === 0) return setTimeout(get, 100);
// callback(testImageURL._recycleBin.shift(), testImageURL._recycle);
// }
// get();
callback(new Image(), function recycle(){});
}
testImageURL.defaultCallback = function(error, event){}
function testImageURL(url, timeout, callback){
if (typeof timeout == 'function'){
callback = timeout;
timeout = testImageURL.timeout;
}
if (typeof callback != 'function') callback = testImageURL.defaultCallback;
testImageURL.getImage(function(img, done){
function callbackWrapper(error, event){
callbackWrapper = testImageURL.noop;
testImageURL.running = (testImageURL.running || 0) - 1;
clearTimeout(timer);
done(img);
img = url = timeout = null;
callback(error, event);
error = event = callback = null;
}
var timer = setTimeout(function(){callbackWrapper(Error('timeout'));}, timeout);
try {
img.onload = function(event){ callbackWrapper(null, event || window.event); };
img.onerror = function(error){ callbackWrapper(error); };
img.src = url;
testImageURL.running = (testImageURL.running || 0) + 1;
if (img.complete === true
|| img.readyState == 4
|| img.width > 0
|| img.height > 0
|| img.readyState == 'complete'
) callbackWrapper(null, null);
}
catch(error){
callbackWrapper(error);
}
});
}
testImageURL.noop = function(){};
testImageURL.timeout = 5000;
-137
View File
@@ -1,137 +0,0 @@
var slice = Array.prototype.slice;
var argv = slice.call(require("system").args);
// Hard to believe PhantomJS has no equivalent of Node's "path" module.
var fs = require("fs");
var splat = [fs.workingDirectory, argv[0]]
.join(fs.separator)
.split(fs.separator);
var harness = splat.pop();
if (harness !== "phantom-harness.js") {
console.error("wrong harness: " + harness);
phantom.exit(-1);
}
var cwd = splat.join(fs.separator);
fs.changeWorkingDirectory(cwd);
// Hard to believe PhantomJS has no option parsing module.
var port = 8080;
var debug = false;
var tests = [];
var rest = [];
while (argv.length > 0) {
var arg = argv.pop();
if (arg === "--port") {
port = +rest.pop();
} else if (arg === "--debug") {
debug = true;
} else if (arg === "--tests") {
while (rest.length > 0)
tests.push(rest.pop());
}
rest.push(arg);
}
// Dynamically enable the individual tests.
var indexHtml = fs.read("index.html").replace(
/^(\s*)ENABLE_TESTS_HERE/m,
function(placeholder, leadingSpace) {
return leadingSpace + tests.map(function(testID) {
return "harness.enableTest(" + JSON.stringify(testID) + ");";
}).join("\n" + leadingSpace);
}
);
var server = require("webserver").create();
server.listen(port, function(req, res) {
var file = req.url.replace(/^\/+/, "");
var content;
switch (file) {
case "jasmine.js":
case "react.js":
case "react-test.js":
file = "../build/" + file;
break;
case "phantomjs-shims.js":
case "worker.js":
file = "../src/test/" + file;
break;
case "jasmine.css":
file = "../vendor/jasmine/" + file;
break;
case "":
default:
file = "index.html";
content = indexHtml; // Prevents calling fs.read again.
break;
}
if (/\.css$/i.test(file)) {
res.setHeader("Content-Type", "text/css");
} else if (/\.js/i.test(file)) {
res.setHeader("Content-Type", "text/javascript");
} else {
res.setHeader("Content-Type", "text/html");
}
res.statusCode = 200;
res.write(content || fs.read(file));
res.close();
});
var url = "http://localhost:" + port;
var green = "\033[32m";
var cyan = "\033[36m";
var reset = "\033[0m";
if (debug) {
console.log(green);
console.log("PhantomJS received the " + cyan + "--debug" + green + " option.");
console.log("Load " + cyan + url + green + " in your browser to execute " +
"the test suite.");
console.log("Type " + cyan + "control-C" + green + " to terminate the " +
"PhantomJS process.");
console.log(reset);
// Leave PhantomJS running until killed with control-C...
} else {
var page = require("webpage").create();
var timeoutSecs = 60;
page.onCallback = function(data) {
switch (data.type) {
case "console":
console[data.method].apply(console, data.args);
break;
case "exit":
// PhantomJS crashes sometimes unless we call phantom.exit in its own
// event loop tick.
setTimeout(function() {
phantom.exit(data.code);
}, 10);
break;
}
};
page.open(url, function(status) {
if (status !== "success") {
console.error("failed to open " + url);
phantom.exit(-1);
}
setTimeout(function() {
console.error(
"PhantomJS tests timed out after " +
timeoutSecs + " seconds.");
phantom.exit(-1);
}, timeoutSecs * 1e3);
});
}
+161
View File
@@ -0,0 +1,161 @@
/*
This file is part of the Jasmine JSReporter project from Ivan De Marino.
Copyright (C) 2011 Ivan De Marino (aka detro, aka detronizator), http://blog.ivandemarino.me, ivan.de.marino@gmail.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL IVAN DE MARINO BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function () {
// Ensure that Jasmine library is loaded first
if (typeof jasmine === "undefined") {
throw new Error("[Jasmine JSReporter] 'Jasmine' library not found");
}
/**
* Calculate elapsed time, in Seconds.
* @param startMs Start time in Milliseconds
* @param finishMs Finish time in Milliseconds
* @return Elapsed time in Seconds */
function elapsedSec (startMs, finishMs) {
return (finishMs - startMs) / 1000;
}
/**
* Round an amount to the given number of Digits.
* If no number of digits is given, than '2' is assumed.
* @param amount Amount to round
* @param numOfDecDigits Number of Digits to round to. Default value is '2'.
* @return Rounded amount */
function round (amount, numOfDecDigits) {
numOfDecDigits = numOfDecDigits || 2;
return Math.round(amount * Math.pow(10, numOfDecDigits)) / Math.pow(10, numOfDecDigits);
}
/**
* Collect information about a Suite, recursively, and return a JSON result.
* @param suite The Jasmine Suite to get data from
*/
function getSuiteData (suite) {
var suiteData = {
description : suite.description,
durationSec : 0,
specs: [],
suites: [],
passed: true
},
specs = suite.specs(),
suites = suite.suites(),
i, ilen;
// Loop over all the Suite's Specs
for (i = 0, ilen = specs.length; i < ilen; ++i) {
suiteData.specs[i] = {
description : specs[i].description,
durationSec : specs[i].durationSec,
passed : specs[i].results().passedCount === specs[i].results().totalCount,
skipped : specs[i].results().skipped,
passedCount : specs[i].results().passedCount,
failedCount : specs[i].results().failedCount,
totalCount : specs[i].results().totalCount
};
suiteData.passed = !suiteData.specs[i].passed ? false : suiteData.passed;
suiteData.durationSec += suiteData.specs[i].durationSec;
}
// Loop over all the Suite's sub-Suites
for (i = 0, ilen = suites.length; i < ilen; ++i) {
suiteData.suites[i] = getSuiteData(suites[i]); //< recursive population
suiteData.passed = !suiteData.suites[i].passed ? false : suiteData.passed;
suiteData.durationSec += suiteData.suites[i].durationSec;
}
// Rounding duration numbers to 3 decimal digits
suiteData.durationSec = round(suiteData.durationSec, 4);
return suiteData;
}
var JSReporter = function () {
};
JSReporter.prototype = {
reportRunnerStarting: function (runner) {
// Nothing to do
},
reportSpecStarting: function (spec) {
// Start timing this spec
spec.startedAt = new Date();
},
reportSpecResults: function (spec) {
// Finish timing this spec and calculate duration/delta (in sec)
spec.finishedAt = new Date();
spec.durationSec = elapsedSec(spec.startedAt.getTime(), spec.finishedAt.getTime());
},
reportSuiteResults: function (suite) {
// Nothing to do
},
reportRunnerResults: function (runner) {
var suites = runner.suites(),
i, ilen;
// Attach results to the "jasmine" object to make those results easy to scrap/find
jasmine.runnerResults = {
suites: [],
durationSec : 0,
passed : true
};
// Loop over all the Suites
for (i = 0, ilen = suites.length; i < ilen; ++i) {
if (suites[i].parentSuite === null) {
jasmine.runnerResults.suites[i] = getSuiteData(suites[i]);
// If 1 suite fails, the whole runner fails
jasmine.runnerResults.passed = !jasmine.runnerResults.suites[i].passed ? false : jasmine.runnerResults.passed;
// Add up all the durations
jasmine.runnerResults.durationSec += jasmine.runnerResults.suites[i].durationSec;
}
}
// Decorate the 'jasmine' object with getters
jasmine.getJSReport = function () {
if (jasmine.runnerResults) {
return jasmine.runnerResults;
}
return null;
};
jasmine.getJSReportAsString = function () {
return JSON.stringify(jasmine.getJSReport());
};
}
};
// export public
jasmine.JSReporter = JSReporter;
})();
-2
View File
@@ -1,5 +1,3 @@
var jasmine = require("./jasmine");
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
+3 -3
View File
@@ -1,4 +1,4 @@
var jasmine = require("./jasmine");
if (typeof jasmine == 'undefined') jasmine = require("./jasmine");
var spec = false; // TODO
// Add some matcher for mock functions
@@ -120,8 +120,8 @@ if (typeof WeakMap !== "undefined") {
mismatchValues.push("arrays were not the same length");
}
_comparedObjects.delete(a);
_comparedObjects.delete(b);
_comparedObjects["delete"](a);
_comparedObjects["delete"](b);
return (mismatchKeys.length == 0 && mismatchValues.length == 0);
};
}
+21 -18
View File
@@ -1,10 +1,12 @@
var isCommonJS = typeof window == "undefined" && typeof exports == "object";
/**
* Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
*
* @namespace
*/
var jasmine = {};
exports = module.exports = jasmine;
if (isCommonJS) exports.jasmine = jasmine;
/**
* @private
*/
@@ -478,7 +480,7 @@ jasmine.log = function() {
var spyOn = function(obj, methodName) {
return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
};
exports.spyOn = spyOn;
if (isCommonJS) exports.spyOn = spyOn;
/**
* Creates a Jasmine spec that will be added to the current suite.
@@ -496,7 +498,7 @@ exports.spyOn = spyOn;
var it = function(desc, func) {
return jasmine.getEnv().it(desc, func);
};
exports.it = it;
if (isCommonJS) exports.it = it;
/**
* Creates a <em>disabled</em> Jasmine spec.
@@ -509,7 +511,7 @@ exports.it = it;
var xit = function(desc, func) {
return jasmine.getEnv().xit(desc, func);
};
exports.xit = xit;
if (isCommonJS) exports.xit = xit;
/**
* Starts a chain for a Jasmine expectation.
@@ -523,7 +525,7 @@ exports.xit = xit;
var expect = function(actual) {
return jasmine.getEnv().currentSpec.expect(actual);
};
exports.expect = expect;
if (isCommonJS) exports.expect = expect;
/**
* Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
@@ -533,7 +535,7 @@ exports.expect = expect;
var runs = function(func) {
jasmine.getEnv().currentSpec.runs(func);
};
exports.runs = runs;
if (isCommonJS) exports.runs = runs;
/**
* Waits a fixed time period before moving to the next block.
@@ -544,7 +546,7 @@ exports.runs = runs;
var waits = function(timeout) {
jasmine.getEnv().currentSpec.waits(timeout);
};
exports.waits = waits;
if (isCommonJS) exports.waits = waits;
/**
* Waits for the latchFunction to return true before proceeding to the next block.
@@ -556,7 +558,7 @@ exports.waits = waits;
var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
};
exports.waitsFor = waitsFor;
if (isCommonJS) exports.waitsFor = waitsFor;
/**
* A function that is called before each spec in a suite.
@@ -568,7 +570,7 @@ exports.waitsFor = waitsFor;
var beforeEach = function(beforeEachFunction) {
jasmine.getEnv().beforeEach(beforeEachFunction);
};
exports.beforeEach = beforeEach;
if (isCommonJS) exports.beforeEach = beforeEach;
/**
* A function that is called after each spec in a suite.
@@ -580,7 +582,7 @@ exports.beforeEach = beforeEach;
var afterEach = function(afterEachFunction) {
jasmine.getEnv().afterEach(afterEachFunction);
};
exports.afterEach = afterEach;
if (isCommonJS) exports.afterEach = afterEach;
/**
* Defines a suite of specifications.
@@ -600,7 +602,7 @@ exports.afterEach = afterEach;
var describe = function(description, specDefinitions) {
return jasmine.getEnv().describe(description, specDefinitions);
};
exports.describe = describe;
if (isCommonJS) exports.describe = describe;
/**
* Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
@@ -611,7 +613,7 @@ exports.describe = describe;
var xdescribe = function(description, specDefinitions) {
return jasmine.getEnv().xdescribe(description, specDefinitions);
};
exports.xdescribe = xdescribe;
if (isCommonJS) exports.xdescribe = xdescribe;
// Provide the XMLHttpRequest class for IE 5.x-6.x:
@@ -871,10 +873,11 @@ jasmine.Env.prototype.it = function(description, func) {
};
jasmine.Env.prototype.xit = function(desc, func) {
var spec = this.it(desc, func);
spec.results_.skipped = true;
spec.runs = function() {};
return spec;
return {
id: this.nextSpecId(),
runs: function() {
}
};
};
jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {
@@ -2592,6 +2595,6 @@ jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
jasmine.version_= {
"major": 1,
"minor": 3,
"build": 1,
"revision": 1354556913
"build": 0,
"revision": 1354052693
};