From f6ba8be382b755ecd624a608c7c75bfdb1b04396 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Mon, 7 Jan 2013 17:12:37 +0000 Subject: [PATCH 01/13] Commenting out unused code. The PhantomReporter never fires a writeFile event so I don't think this code could ever be triggered? --- tasks/jasmine.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tasks/jasmine.js b/tasks/jasmine.js index 20f3205..ab7cc49 100644 --- a/tasks/jasmine.js +++ b/tasks/jasmine.js @@ -143,13 +143,14 @@ module.exports = function(grunt) { grunt.event.emit.apply(grunt.event, args); }); - phantomjs.on('jasmine.writeFile',function(type,filename, xml){ - var dir = options[type] && options[type].output; - if (dir) { - grunt.file.mkdir(dir); - grunt.file.write(path.join(dir, filename), xml); - } - }); + // Not used? +// phantomjs.on('jasmine.writeFile',function(type,filename, xml){ +// var dir = options[type] && options[type].output; +// if (dir) { +// grunt.file.mkdir(dir); +// grunt.file.write(path.join(dir, filename), xml); +// } +// }); phantomjs.on('jasmine.reportRunnerStarting',function(suites) { From 0313861501eb9872a7e63118edcb46469db723f2 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Mon, 7 Jan 2013 21:34:04 +0000 Subject: [PATCH 02/13] Built a JUnitDataReporter which outputs the data while is required to create a junit xml file. It's easier to test in a browser so wrote the logic here first - will now copy across to the PhantomReporter for reuse by the plugin... --- tasks/jasmine/reporters/JUnitDataReporter.js | 80 ++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tasks/jasmine/reporters/JUnitDataReporter.js diff --git a/tasks/jasmine/reporters/JUnitDataReporter.js b/tasks/jasmine/reporters/JUnitDataReporter.js new file mode 100644 index 0000000..d524ce3 --- /dev/null +++ b/tasks/jasmine/reporters/JUnitDataReporter.js @@ -0,0 +1,80 @@ +/*global jasmine */ +(function() +{ + 'use strict'; + + function getNestedSuiteName(suite) + { + var names = []; + while (suite) { + names.unshift(suite.description); + suite = suite.parentSuite; + } + return names.join(' '); + } + + jasmine.JUnitDataReporter = function() + { + + }; + + jasmine.JUnitDataReporter.prototype = { + reportRunnerStarting: function(runner) { + }, + reportRunnerResults: function(runner) { + var suites = runner.suites().map( + function(suite) + { + var failures = 0, + data = { + name: getNestedSuiteName(suite), + time: suite.duration, + 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((failureMessages.length + 1) + ': ' + expectation.message); + } + } + ); + } + return { + className: getNestedSuiteName(spec.suite), + name: spec.description, + time: spec.duration, + failureMessage: failureMessages.join(' ') + }; + } + ) + }; + data.failures = failures; + 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); + } + }; + +}()); \ No newline at end of file From a6fd38da1be774dc394c9b4dd08d7598081789d3 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Mon, 7 Jan 2013 21:39:40 +0000 Subject: [PATCH 03/13] Added the relevant code from the JUnitDataReporter into the PhantomReporter for use from within the Phantom environment --- tasks/jasmine/reporters/PhantomReporter.js | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tasks/jasmine/reporters/PhantomReporter.js b/tasks/jasmine/reporters/PhantomReporter.js index 1657aa2..dded139 100644 --- a/tasks/jasmine/reporters/PhantomReporter.js +++ b/tasks/jasmine/reporters/PhantomReporter.js @@ -40,6 +40,7 @@ }; PhantomReporter.prototype.reportSpecStarting = function(spec) { + spec.startTime = (new Date()).getTime(); var message = { suite : { description : spec.suite.description @@ -84,10 +85,13 @@ var specIds = runner.specs().map(function(a){return a.id;}); var summary = this.resultsForSpecs(specIds); phantom.sendMessage('jasmine.reportRunnerResults',summary); + phantom.sendMessage('jasmine.reportJUnitResults', this.generateJUnitSummary_(runner)); phantom.sendMessage('jasmine.done.PhantomReporter'); }; PhantomReporter.prototype.reportSuiteResults = function(suite) { + suite.timestamp = new Date(); + suite.duration = suite.timestamp.getTime() - suite.specs()[0].startTime; phantom.sendMessage('jasmine.reportSuiteResults',{ description : suite.description, results : suite.results() @@ -130,6 +134,7 @@ } PhantomReporter.prototype.reportSpecResults = function(spec) { + spec.duration = (new Date()).getTime() - spec.startTime; var _results = spec.results(); var results = { description : _results.description, @@ -189,5 +194,56 @@ }; }; + function getNestedSuiteName(suite) + { + var names = []; + while (suite) { + names.unshift(suite.description); + suite = suite.parentSuite; + } + return names.join(' '); + } + + PhantomReporter.prototype.generateJUnitSummary_ = function(runner) { + return runner.suites().map( + function(suite) + { + var failures = 0, + data = { + name: getNestedSuiteName(suite), + time: suite.duration, + 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((failureMessages.length + 1) + ': ' + expectation.message); + } + } + ); + } + return { + className: getNestedSuiteName(spec.suite), + name: spec.description, + time: spec.duration, + failureMessage: failureMessages.join(' ') + }; + } + ) + }; + data.failures = failures; + return data; + } + ); + }; + jasmine.getEnv().addReporter( new PhantomReporter() ); }()); From 28189d7f05933c9b4b0dba9b74a736e8365503e5 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Mon, 7 Jan 2013 23:04:44 +0000 Subject: [PATCH 04/13] Generating an XML file from a template based on the run specs... --- tasks/jasmine.js | 26 ++++++++++++++++++++------ tasks/jasmine/templates/JUnit.tmpl | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 tasks/jasmine/templates/JUnit.tmpl diff --git a/tasks/jasmine.js b/tasks/jasmine.js index ab7cc49..5991738 100644 --- a/tasks/jasmine.js +++ b/tasks/jasmine.js @@ -32,7 +32,8 @@ module.exports = function(grunt) { var runners = { 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 = { @@ -53,7 +54,8 @@ module.exports = function(grunt) { host : '', template: 'default', templateOptions : {}, - phantomjs : {} + phantomjs : {}, + junit: {} }); grunt.util._.defaults(options.templateOptions, runnerOptions[options.template] || {}); @@ -204,6 +206,21 @@ module.exports = function(grunt) { status.skipped += skippedAssertions; }); + phantomjs.on('jasmine.reportJUnitResults',function(junitData){ + if (options.junit && options.junit.path) { + grunt.file.copy(runners.junit, path.join(options.junit.path,'out-TEST.xml'), { + process : function(src) { + return grunt.util._.template( + src, + { + testsuites: junitData + } + ); + } + }) + } + }); + phantomjs.on('jasmine.done',function(elapsed){ phantomjs.halt(); status.duration = elapsed; @@ -219,7 +236,4 @@ module.exports = function(grunt) { }); } -}; - - - +}; \ No newline at end of file diff --git a/tasks/jasmine/templates/JUnit.tmpl b/tasks/jasmine/templates/JUnit.tmpl new file mode 100644 index 0000000..7048712 --- /dev/null +++ b/tasks/jasmine/templates/JUnit.tmpl @@ -0,0 +1,14 @@ + + +<% testsuites.forEach(function(testsuite) { %> + + <% testsuite.testcases.forEach(function(testcase) { %> + + <% if (testcase.failureMessage) { %> + <%= testsuite.failureMessage %> + <% } %> + + <% }) %> + +<% }) %> + \ No newline at end of file From e8e95dbbcabdf45f217cb3bdd464e6457988ef2e Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 10:09:55 +0000 Subject: [PATCH 05/13] Test duration in JUnit is reported in seconds rather than miliseconds... --- tasks/jasmine/reporters/JUnitDataReporter.js | 4 ++-- tasks/jasmine/reporters/PhantomReporter.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/jasmine/reporters/JUnitDataReporter.js b/tasks/jasmine/reporters/JUnitDataReporter.js index d524ce3..de58256 100644 --- a/tasks/jasmine/reporters/JUnitDataReporter.js +++ b/tasks/jasmine/reporters/JUnitDataReporter.js @@ -28,7 +28,7 @@ var failures = 0, data = { name: getNestedSuiteName(suite), - time: suite.duration, + 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 @@ -50,7 +50,7 @@ return { className: getNestedSuiteName(spec.suite), name: spec.description, - time: spec.duration, + time: spec.duration / 1000, failureMessage: failureMessages.join(' ') }; } diff --git a/tasks/jasmine/reporters/PhantomReporter.js b/tasks/jasmine/reporters/PhantomReporter.js index dded139..53ab6c4 100644 --- a/tasks/jasmine/reporters/PhantomReporter.js +++ b/tasks/jasmine/reporters/PhantomReporter.js @@ -211,7 +211,7 @@ var failures = 0, data = { name: getNestedSuiteName(suite), - time: suite.duration, + 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 @@ -233,7 +233,7 @@ return { className: getNestedSuiteName(spec.suite), name: spec.description, - time: spec.duration, + time: spec.duration / 1000, failureMessage: failureMessages.join(' ') }; } From 8e85e593bdf204549d0df937f226e7820765fd67 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 10:13:55 +0000 Subject: [PATCH 06/13] Fixed a typo which prevented failure messages from appearing in output --- tasks/jasmine/templates/JUnit.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/jasmine/templates/JUnit.tmpl b/tasks/jasmine/templates/JUnit.tmpl index 7048712..a4cb5d7 100644 --- a/tasks/jasmine/templates/JUnit.tmpl +++ b/tasks/jasmine/templates/JUnit.tmpl @@ -5,7 +5,7 @@ <% testsuite.testcases.forEach(function(testcase) { %> <% if (testcase.failureMessage) { %> - <%= testsuite.failureMessage %> + <%= testcase.failureMessage %> <% } %> <% }) %> From ef8f191c46068649a7e6b626be394a8d1e37fe18 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 10:38:29 +0000 Subject: [PATCH 07/13] Outputting additional information into the JUnit XML Since it is available and seems to be desired by the JUnit spec [ http://stackoverflow.com/questions/4922867/junit-xml-format-specification-that-hudson-supports ] --- tasks/jasmine/reporters/JUnitDataReporter.js | 5 +- tasks/jasmine/reporters/PhantomReporter.js | 59 ++++++++++---------- tasks/jasmine/templates/JUnit.tmpl | 8 +-- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/tasks/jasmine/reporters/JUnitDataReporter.js b/tasks/jasmine/reporters/JUnitDataReporter.js index de58256..d65b3dd 100644 --- a/tasks/jasmine/reporters/JUnitDataReporter.js +++ b/tasks/jasmine/reporters/JUnitDataReporter.js @@ -42,16 +42,17 @@ function(expectation) { if (!expectation.passed()) { - failureMessages.push((failureMessages.length + 1) + ': ' + expectation.message); + failureMessages.push(expectation.message); } } ); } return { + assertions: spec.results().items_.length, className: getNestedSuiteName(spec.suite), name: spec.description, time: spec.duration / 1000, - failureMessage: failureMessages.join(' ') + failureMessages: failureMessages }; } ) diff --git a/tasks/jasmine/reporters/PhantomReporter.js b/tasks/jasmine/reporters/PhantomReporter.js index 53ab6c4..266ff9e 100644 --- a/tasks/jasmine/reporters/PhantomReporter.js +++ b/tasks/jasmine/reporters/PhantomReporter.js @@ -209,36 +209,37 @@ 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((failureMessages.length + 1) + ': ' + expectation.message); - } + 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 { - className: getNestedSuiteName(spec.suite), - name: spec.description, - time: spec.duration / 1000, - failureMessage: failureMessages.join(' ') - }; - } - ) - }; + return { + assertions: spec.results().items_.length, + className: getNestedSuiteName(spec.suite), + name: spec.description, + time: spec.duration / 1000, + failureMessages: failureMessages + }; + } + ) + }; data.failures = failures; return data; } diff --git a/tasks/jasmine/templates/JUnit.tmpl b/tasks/jasmine/templates/JUnit.tmpl index a4cb5d7..eea8f46 100644 --- a/tasks/jasmine/templates/JUnit.tmpl +++ b/tasks/jasmine/templates/JUnit.tmpl @@ -3,10 +3,10 @@ <% testsuites.forEach(function(testsuite) { %> <% testsuite.testcases.forEach(function(testcase) { %> - - <% if (testcase.failureMessage) { %> - <%= testcase.failureMessage %> - <% } %> + + <% testcase.failureMessages.forEach(function(message) { %> + <%= message %> + <% }) %> <% }) %> From f19d07a209dde05bd38b41d5089405f8c24a571c Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 13:09:05 +0000 Subject: [PATCH 08/13] Writing the suites out to separate xml files with the option to consolidate into one file per top level suite... --- tasks/jasmine.js | 41 +++++++-- tasks/jasmine/reporters/JUnitDataReporter.js | 89 ++++++++++-------- tasks/jasmine/reporters/PhantomReporter.js | 97 ++++++++++++-------- 3 files changed, 143 insertions(+), 84 deletions(-) diff --git a/tasks/jasmine.js b/tasks/jasmine.js index 5991738..e8f8764 100644 --- a/tasks/jasmine.js +++ b/tasks/jasmine.js @@ -208,16 +208,41 @@ module.exports = function(grunt) { phantomjs.on('jasmine.reportJUnitResults',function(junitData){ if (options.junit && options.junit.path) { - grunt.file.copy(runners.junit, path.join(options.junit.path,'out-TEST.xml'), { - process : function(src) { - return grunt.util._.template( - src, + + if (options.junit.consolidate) { + + grunt.util._(junitData.consolidatedSuites).each( + function(suites) { - testsuites: junitData + grunt.file.copy(runners.junit, path.join(options.junit.path, suites[0].name.replace(/[^\w]/g, '') + '-TEST.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, suiteData.name.replace(/[^\w]/g, '') + '-TEST.xml'), { + process: function(src) { + return grunt.util._.template( + src, + { + testsuites: [suiteData] + } + ); + } + }) + } + ); + } } }); diff --git a/tasks/jasmine/reporters/JUnitDataReporter.js b/tasks/jasmine/reporters/JUnitDataReporter.js index d65b3dd..8ca899a 100644 --- a/tasks/jasmine/reporters/JUnitDataReporter.js +++ b/tasks/jasmine/reporters/JUnitDataReporter.js @@ -13,6 +13,16 @@ return names.join(' '); } + function getTopLevelSuiteId(suite) + { + var id; + while (suite) { + id = suite.id; + suite = suite.parentSuite; + } + return id; + } + jasmine.JUnitDataReporter = function() { @@ -22,45 +32,48 @@ reportRunnerStarting: function(runner) { }, reportRunnerResults: function(runner) { - var 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); - } + 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; - return data; - } - ); + 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) { diff --git a/tasks/jasmine/reporters/PhantomReporter.js b/tasks/jasmine/reporters/PhantomReporter.js index 266ff9e..d17a6f6 100644 --- a/tasks/jasmine/reporters/PhantomReporter.js +++ b/tasks/jasmine/reporters/PhantomReporter.js @@ -204,46 +204,67 @@ return names.join(' '); } + function getTopLevelSuiteId(suite) + { + var id; + while (suite) { + id = suite.id; + suite = suite.parentSuite; + } + return id; + } + PhantomReporter.prototype.generateJUnitSummary_ = function(runner) { - return 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); - } + 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; - return data; - } - ); + 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() ); From 0e5d54587da2f4ed56a860d9450b0d5450518181 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 13:16:32 +0000 Subject: [PATCH 09/13] Fixing lint errors --- tasks/jasmine.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/jasmine.js b/tasks/jasmine.js index e8f8764..6d3d966 100644 --- a/tasks/jasmine.js +++ b/tasks/jasmine.js @@ -223,7 +223,7 @@ module.exports = function(grunt) { } ); } - }) + }); } ); } else { @@ -239,7 +239,7 @@ module.exports = function(grunt) { } ); } - }) + }); } ); } From d6796a57a47bee44a4a793b0d26d8577287346d7 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 13:19:02 +0000 Subject: [PATCH 10/13] Not dying when there is a suite with no specs - this seems to happen in the bundled requirejs example. --- tasks/jasmine/reporters/PhantomReporter.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tasks/jasmine/reporters/PhantomReporter.js b/tasks/jasmine/reporters/PhantomReporter.js index d17a6f6..a953f61 100644 --- a/tasks/jasmine/reporters/PhantomReporter.js +++ b/tasks/jasmine/reporters/PhantomReporter.js @@ -90,12 +90,14 @@ }; PhantomReporter.prototype.reportSuiteResults = function(suite) { - suite.timestamp = new Date(); - suite.duration = suite.timestamp.getTime() - suite.specs()[0].startTime; - phantom.sendMessage('jasmine.reportSuiteResults',{ - description : suite.description, - results : suite.results() - }); + if (suite.specs().length) { + suite.timestamp = new Date(); + suite.duration = suite.timestamp.getTime() - suite.specs()[0].startTime; + phantom.sendMessage('jasmine.reportSuiteResults',{ + description : suite.description, + results : suite.results() + }); + } }; function stringify(obj) { From cacf4a82be4bec83463352ac9ff4d630caa72b07 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 13:19:36 +0000 Subject: [PATCH 11/13] Added some basic documentation for the new JUnit options --- docs/jasmine-options.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/jasmine-options.md b/docs/jasmine-options.md index e0387e8..f7fc016 100644 --- a/docs/jasmine-options.md +++ b/docs/jasmine-options.md @@ -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. +## 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 Name: `build` From d224f2c687c00125230fbb77c307fb1bfa261867 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 14:29:10 +0000 Subject: [PATCH 12/13] Naming the generated JUnit files TEST-[name].xml as they were in the old version of the plugin for easier migration where Jenkins is searching for files named in that way. --- tasks/jasmine.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/jasmine.js b/tasks/jasmine.js index 6d3d966..5dfd034 100644 --- a/tasks/jasmine.js +++ b/tasks/jasmine.js @@ -214,7 +214,7 @@ module.exports = function(grunt) { grunt.util._(junitData.consolidatedSuites).each( function(suites) { - grunt.file.copy(runners.junit, path.join(options.junit.path, suites[0].name.replace(/[^\w]/g, '') + '-TEST.xml'), { + 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, @@ -230,7 +230,7 @@ module.exports = function(grunt) { junitData.suites.forEach( function(suiteData) { - grunt.file.copy(runners.junit, path.join(options.junit.path, suiteData.name.replace(/[^\w]/g, '') + '-TEST.xml'), { + 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, From 679bb383aad43d16ab5771936e567774d2c88f19 Mon Sep 17 00:00:00 2001 From: Kelvin Luck Date: Tue, 8 Jan 2013 15:02:26 +0000 Subject: [PATCH 13/13] Fix for the classname not appearing in the generated XML file... --- tasks/jasmine/templates/JUnit.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/jasmine/templates/JUnit.tmpl b/tasks/jasmine/templates/JUnit.tmpl index eea8f46..5f65516 100644 --- a/tasks/jasmine/templates/JUnit.tmpl +++ b/tasks/jasmine/templates/JUnit.tmpl @@ -3,7 +3,7 @@ <% testsuites.forEach(function(testsuite) { %> <% testsuite.testcases.forEach(function(testcase) { %> - + <% testcase.failureMessages.forEach(function(message) { %> <%= message %> <% }) %>