This commit is contained in:
Jarrod Overson 2013-01-08 10:00:13 -08:00
commit 81d45689d0
5 changed files with 253 additions and 17 deletions

View File

@ -62,6 +62,14 @@ Default: `{}`
These options will be passed to your template as an 'options' hash so that you can provide settings to your template. These options will be passed to your template as an 'options' hash so that you can provide settings to your template.
## options.junit
Type: `Object`
Default: `{}`
Set `options.junit.path` to generate JUnit compatible XML from the task (for use in a CI system such as Jenkins).
Set `options.junit.consolidate` to consolidate the generated XML files so that there is one file per top level suite.
# Flags # Flags
Name: `build` Name: `build`

View File

@ -32,7 +32,8 @@ module.exports = function(grunt) {
var runners = { var runners = {
default : __dirname + '/jasmine/templates/DefaultRunner.tmpl', default : __dirname + '/jasmine/templates/DefaultRunner.tmpl',
requirejs : __dirname + '/jasmine/templates/RequireJSRunner.tmpl' requirejs : __dirname + '/jasmine/templates/RequireJSRunner.tmpl',
junit : __dirname + '/jasmine/templates/JUnit.tmpl'
}; };
var runnerOptions = { var runnerOptions = {
@ -53,7 +54,8 @@ module.exports = function(grunt) {
host : '', host : '',
template: 'default', template: 'default',
templateOptions : {}, templateOptions : {},
phantomjs : {} phantomjs : {},
junit: {}
}); });
grunt.util._.defaults(options.templateOptions, runnerOptions[options.template] || {}); grunt.util._.defaults(options.templateOptions, runnerOptions[options.template] || {});
@ -143,13 +145,14 @@ module.exports = function(grunt) {
grunt.event.emit.apply(grunt.event, args); grunt.event.emit.apply(grunt.event, args);
}); });
phantomjs.on('jasmine.writeFile',function(type,filename, xml){ // Not used?
var dir = options[type] && options[type].output; // phantomjs.on('jasmine.writeFile',function(type,filename, xml){
if (dir) { // var dir = options[type] && options[type].output;
grunt.file.mkdir(dir); // if (dir) {
grunt.file.write(path.join(dir, filename), xml); // grunt.file.mkdir(dir);
} // grunt.file.write(path.join(dir, filename), xml);
}); // }
// });
phantomjs.on('jasmine.reportRunnerStarting',function(suites) { phantomjs.on('jasmine.reportRunnerStarting',function(suites) {
@ -203,6 +206,46 @@ module.exports = function(grunt) {
status.skipped += skippedAssertions; status.skipped += skippedAssertions;
}); });
phantomjs.on('jasmine.reportJUnitResults',function(junitData){
if (options.junit && options.junit.path) {
if (options.junit.consolidate) {
grunt.util._(junitData.consolidatedSuites).each(
function(suites)
{
grunt.file.copy(runners.junit, path.join(options.junit.path, 'TEST-' + suites[0].name.replace(/[^\w]/g, '') + '.xml'), {
process: function(src) {
return grunt.util._.template(
src,
{
testsuites: suites
}
);
}
});
}
);
} else {
junitData.suites.forEach(
function(suiteData)
{
grunt.file.copy(runners.junit, path.join(options.junit.path, 'TEST-' + suiteData.name.replace(/[^\w]/g, '') + '.xml'), {
process: function(src) {
return grunt.util._.template(
src,
{
testsuites: [suiteData]
}
);
}
});
}
);
}
}
});
phantomjs.on('jasmine.done',function(elapsed){ phantomjs.on('jasmine.done',function(elapsed){
phantomjs.halt(); phantomjs.halt();
status.duration = elapsed; status.duration = elapsed;
@ -219,6 +262,3 @@ module.exports = function(grunt) {
} }
}; };

View File

@ -0,0 +1,94 @@
/*global jasmine */
(function()
{
'use strict';
function getNestedSuiteName(suite)
{
var names = [];
while (suite) {
names.unshift(suite.description);
suite = suite.parentSuite;
}
return names.join(' ');
}
function getTopLevelSuiteId(suite)
{
var id;
while (suite) {
id = suite.id;
suite = suite.parentSuite;
}
return id;
}
jasmine.JUnitDataReporter = function()
{
};
jasmine.JUnitDataReporter.prototype = {
reportRunnerStarting: function(runner) {
},
reportRunnerResults: function(runner) {
var suitesById = {},
suites = runner.suites().map(
function(suite)
{
var failures = 0,
data = {
topLevelSuiteId: getTopLevelSuiteId(suite),
name: getNestedSuiteName(suite),
time: suite.duration / 1000,
timestamp: suite.timestamp,
tests: suite.specs().length,
errors: 0, // TODO: These exist in the JUnit XML but not sure how they map to jasmine things
testcases: suite.specs().map(
function(spec)
{
var failureMessages = [];
if (spec.results().failedCount) {
failures ++;
spec.results().items_.forEach(
function(expectation)
{
if (!expectation.passed()) {
failureMessages.push(expectation.message);
}
}
);
}
return {
assertions: spec.results().items_.length,
className: getNestedSuiteName(spec.suite),
name: spec.description,
time: spec.duration / 1000,
failureMessages: failureMessages
};
}
)
};
data.failures = failures;
suitesById[suite.id] = data;
return data;
}
);
console.log('Suites:', suites);
},
reportSuiteResults: function(suite) {
suite.timestamp = new Date();
suite.duration = suite.timestamp.getTime() - suite.specs()[0].startTime;
},
reportSpecStarting: function(spec) {
spec.startTime = (new Date()).getTime();
},
reportSpecResults: function(spec) {
spec.duration = (new Date()).getTime() - spec.startTime;
},
log: function(str) {
console.log(str);
}
};
}());

View File

@ -40,6 +40,7 @@
}; };
PhantomReporter.prototype.reportSpecStarting = function(spec) { PhantomReporter.prototype.reportSpecStarting = function(spec) {
spec.startTime = (new Date()).getTime();
var message = { var message = {
suite : { suite : {
description : spec.suite.description description : spec.suite.description
@ -84,14 +85,19 @@
var specIds = runner.specs().map(function(a){return a.id;}); var specIds = runner.specs().map(function(a){return a.id;});
var summary = this.resultsForSpecs(specIds); var summary = this.resultsForSpecs(specIds);
phantom.sendMessage('jasmine.reportRunnerResults',summary); phantom.sendMessage('jasmine.reportRunnerResults',summary);
phantom.sendMessage('jasmine.reportJUnitResults', this.generateJUnitSummary_(runner));
phantom.sendMessage('jasmine.done.PhantomReporter'); phantom.sendMessage('jasmine.done.PhantomReporter');
}; };
PhantomReporter.prototype.reportSuiteResults = function(suite) { PhantomReporter.prototype.reportSuiteResults = function(suite) {
if (suite.specs().length) {
suite.timestamp = new Date();
suite.duration = suite.timestamp.getTime() - suite.specs()[0].startTime;
phantom.sendMessage('jasmine.reportSuiteResults',{ phantom.sendMessage('jasmine.reportSuiteResults',{
description : suite.description, description : suite.description,
results : suite.results() results : suite.results()
}); });
}
}; };
function stringify(obj) { function stringify(obj) {
@ -130,6 +136,7 @@
} }
PhantomReporter.prototype.reportSpecResults = function(spec) { PhantomReporter.prototype.reportSpecResults = function(spec) {
spec.duration = (new Date()).getTime() - spec.startTime;
var _results = spec.results(); var _results = spec.results();
var results = { var results = {
description : _results.description, description : _results.description,
@ -189,5 +196,78 @@
}; };
}; };
function getNestedSuiteName(suite)
{
var names = [];
while (suite) {
names.unshift(suite.description);
suite = suite.parentSuite;
}
return names.join(' ');
}
function getTopLevelSuiteId(suite)
{
var id;
while (suite) {
id = suite.id;
suite = suite.parentSuite;
}
return id;
}
PhantomReporter.prototype.generateJUnitSummary_ = function(runner) {
var consolidatedSuites = {},
suites = runner.suites().map(
function(suite)
{
var failures = 0,
data = {
name: getNestedSuiteName(suite),
time: suite.duration / 1000,
timestamp: suite.timestamp,
tests: suite.specs().length,
errors: 0, // TODO: These exist in the JUnit XML but not sure how they map to jasmine things
testcases: suite.specs().map(
function(spec)
{
var failureMessages = [];
if (spec.results().failedCount) {
failures ++;
spec.results().items_.forEach(
function(expectation)
{
if (!expectation.passed()) {
failureMessages.push(expectation.message);
}
}
);
}
return {
assertions: spec.results().items_.length,
className: getNestedSuiteName(spec.suite),
name: spec.description,
time: spec.duration / 1000,
failureMessages: failureMessages
};
}
)
};
data.failures = failures;
if (suite.parentSuite) {
consolidatedSuites[getTopLevelSuiteId(suite)].push(data);
} else {
consolidatedSuites[suite.id] = [data];
}
return data;
}
);
return {
suites: suites,
consolidatedSuites: consolidatedSuites
};
};
jasmine.getEnv().addReporter( new PhantomReporter() ); jasmine.getEnv().addReporter( new PhantomReporter() );
}()); }());

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuites>
<% testsuites.forEach(function(testsuite) { %>
<testsuite name="<%- testsuite.name %>" errors="<%= testsuite.errors %>" tests="<%= testsuite.tests %>" failures="<%= testsuite.failures %>" time="<%= testsuite.time %>" timestamp="<%= testsuite.timestamp %>">
<% testsuite.testcases.forEach(function(testcase) { %>
<testcase assertions="<%= testcase.assertions %>" classname="<%- testcase.className %>" name="<%- testcase.name %>" time="<%= testcase.time %>">
<% testcase.failureMessages.forEach(function(message) { %>
<failure><%= message %></failure>
<% }) %>
</testcase>
<% }) %>
</testsuite>
<% }) %>
</testsuites>