Commit 12272eae 12272eae4edbfd3e5519b67e9883fcd32e3b7046 by Jan-Martin Fruehwacht Committed by Nicolas Perriault

fixes #209, merged #319 - time attribute added to xunit xml

1 parent 3d17e643
CasperJS Changelog
==================
XXXX-XX-XX, v1.0.0
------------------
- merged PR [#319](https://github.com/n1k0/casperjs/pull/319), fixed [#209](https://github.com/n1k0/casperjs/issues/209) - test duration has been added to XUnit XML result file.
2012-12-14, v1.0.0-RC6
----------------------
......
......@@ -81,25 +81,39 @@ var Tester = function Tester(casper, options) {
passed: 0,
failed: 0,
passes: [],
failures: []
failures: [],
passesTime: [],
failuresTime: []
};
// measuring test duration
this.currentTestStartTime = null;
this.lastAssertTime = 0;
this.configure();
this.on('success', function onSuccess(success) {
this.testResults.passes.push(success);
this.exporter.addSuccess(fs.absolute(success.file), success.message || success.standard);
var timeElapsed = new Date() - this.currentTestStartTime;
this.testResults.passesTime.push(timeElapsed - this.lastAssertTime);
this.exporter.addSuccess(fs.absolute(success.file), success.message || success.standard, timeElapsed - this.lastAssertTime);
this.lastAssertTime = timeElapsed;
});
this.on('fail', function onFail(failure) {
// export
var timeElapsed = new Date() - this.currentTestStartTime;
this.testResults.failuresTime.push(timeElapsed - this.lastAssertTime);
this.exporter.addFailure(
fs.absolute(failure.file),
failure.message || failure.standard,
failure.standard || "test failed",
failure.type || "unknown"
);
fs.absolute(failure.file),
failure.message || failure.standard,
failure.standard || "test failed",
failure.type || "unknown",
(timeElapsed - this.lastAssertTime)
);
this.lastAssertTime = timeElapsed;
this.testResults.failures.push(failure);
// special printing
if (failure.type) {
this.comment(' type: ' + failure.type);
......@@ -687,6 +701,19 @@ Tester.prototype.bar = function bar(text, style) {
};
/**
* Retrieves the sum of all durations of the tests which were
* executed in the current suite
*
* @return Number duration of all tests executed until now (in the current suite)
*/
Tester.prototype.calculateSuiteDuration = function calculateSuiteDuration() {
"use strict";
return this.testResults.passesTime.concat(this.testResults.failuresTime).reduce(function add(a, b) {
return a + b;
}, 0);
};
/**
* Render a colorized output. Basically a proxy method for
* Casper.Colorizer#colorize()
*/
......@@ -860,6 +887,27 @@ Tester.prototype.getPasses = function getPasses() {
};
/**
* Retrieves the array where all the durations of failed tests are stored
*
* @return Array durations of failed tests
*/
Tester.prototype.getFailuresTime = function getFailuresTime() {
"use strict";
return this.testResults.failuresTime;
}
/**
* Retrieves the array where all the durations of passed tests are stored
*
* @return Array durations of passed tests
*/
Tester.prototype.getPassesTime = function getPassesTime() {
"use strict";
return this.testResults.passesTime;
}
/**
* Writes an info-style formatted message to stdout.
*
* @param String message
......@@ -1008,6 +1056,8 @@ Tester.prototype.runSuites = function runSuites() {
this.casper.exit(1);
}
self.currentSuiteNum = 0;
self.currentTestStartTime = new Date();
self.lastAssertTime = 0;
var interval = setInterval(function _check(self) {
if (self.running) {
return;
......@@ -1015,9 +1065,13 @@ Tester.prototype.runSuites = function runSuites() {
if (self.currentSuiteNum === testFiles.length) {
self.emit('tests.complete');
clearInterval(interval);
self.exporter.setSuiteDuration(self.calculateSuiteDuration());
} else {
self.runTest(testFiles[self.currentSuiteNum]);
self.exporter.setSuiteDuration(self.calculateSuiteDuration());
self.currentSuiteNum++;
self.passesTime = [];
self.failuresTime = [];
}
}, 100, this);
};
......
......@@ -34,6 +34,17 @@ var utils = require('utils');
var fs = require('fs');
/**
* Returns duration in seconds, matching XUnit "standard".
*
* @param Number duration Duration in milliseconds
* @return String
*/
function format_duration(duration) {
"use strict";
return (duration / 1000).toString();
}
/**
* Generates a value for 'classname' attribute of the JUnit XML report.
*
* Uses the (relative) file name of the current casper script without file
......@@ -92,13 +103,18 @@ exports.XUnitExporter = XUnitExporter;
*
* @param String classname
* @param String name
* @param Number duration Test duration in milliseconds
*/
XUnitExporter.prototype.addSuccess = function addSuccess(classname, name) {
XUnitExporter.prototype.addSuccess = function addSuccess(classname, name, duration) {
"use strict";
this._xml.appendChild(utils.node('testcase', {
var snode = utils.node('testcase', {
classname: generateClassName(classname),
name: name
}));
name: name
});
if (duration !== undefined) {
snode.setAttribute('time', format_duration(duration));
}
this._xml.appendChild(snode);
};
/**
......@@ -108,13 +124,17 @@ XUnitExporter.prototype.addSuccess = function addSuccess(classname, name) {
* @param String name
* @param String message
* @param String type
* @param Number duration Test duration in milliseconds
*/
XUnitExporter.prototype.addFailure = function addFailure(classname, name, message, type) {
XUnitExporter.prototype.addFailure = function addFailure(classname, name, message, type, duration) {
"use strict";
var fnode = utils.node('testcase', {
classname: generateClassName(classname),
name: name
});
if (duration !== undefined) {
fnode.setAttribute('time', format_duration(duration));
}
var failure = utils.node('failure', {
type: type || "unknown"
});
......@@ -124,6 +144,18 @@ XUnitExporter.prototype.addFailure = function addFailure(classname, name, messag
};
/**
* Adds test suite duration
*
* @param Number duration Test duration in milliseconds
*/
XUnitExporter.prototype.setSuiteDuration = function setSuiteDuration(duration) {
"use strict";
if (!isNaN(duration)) {
this._xml.setAttribute("time", format_duration(duration));
}
};
/**
* Retrieves generated XML object - actually an HTMLElement.
*
* @return HTMLElement
......
......@@ -210,6 +210,17 @@ casper.then(function() {
t.assertEquals(t.getPasses().length, passCount + 1, "Tester.getPasses() works as expected");
});
casper.then(function() {
t.comment('Tester.calculateSuiteDuration()');
function add(a, b) {
return a + b;
}
var passedTime = t.getPassesTime().reduce(add, 0),
failedTime = t.getFailuresTime().reduce(add, 0),
calculatedSum = t.calculateSuiteDuration();
t.assertEquals(calculatedSum, passedTime + failedTime, "Tester.calculateSuiteDuration() works as expected")
});
casper.run(function() {
t.done(58);
t.done(59);
});
......
......@@ -4,15 +4,44 @@ casper.test.comment('phantom.Casper.XUnitExporter');
var xunit = require('xunit').create();
xunit.addSuccess('foo', 'bar');
casper.test.assertMatch(xunit.getXML(), /<testcase classname="foo" name="bar"/, 'XUnitExporter.addSuccess() adds a successful testcase');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="foo" name="bar"/,
'XUnitExporter.addSuccess() adds a successful testcase');
xunit.addFailure('bar', 'baz', 'wrong', 'chucknorriz');
casper.test.assertMatch(xunit.getXML(), /<testcase classname="bar" name="baz"><failure type="chucknorriz">wrong/, 'XUnitExporter.addFailure() adds a failed testcase');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="bar" name="baz"><failure type="chucknorriz">wrong/,
'XUnitExporter.addFailure() adds a failed testcase');
// named classname
xunit = require('xunit').create();
xunit.addSuccess(require('fs').workingDirectory + '/plop.js', 'It worked');
casper.test.assertMatch(xunit.getXML(), /<testcase classname="(.*)plop" name="It worked"/, 'XUnitExporter.addSuccess() handles class name');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="(.*)plop" name="It worked"/,
'XUnitExporter.addSuccess() handles class name');
xunit.addSuccess(require('fs').workingDirectory + '/plip.js', 'Failure');
casper.test.assertMatch(xunit.getXML(), /<testcase classname="(.*)plip" name="Failure"/, 'XUnitExporter.addFailure() handles class name');
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="(.*)plip" name="Failure"/,
'XUnitExporter.addFailure() handles class name');
casper.test.done(4);
// named with time
xunit = require('xunit').create();
xunit.addSuccess('foo', 'It worked', 1024);
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="foo" name="It worked" time="(.*)"/,
'XUnitExporter.addSuccess() writes duration of test');
xunit.addFailure('bar', 'baz', 'wrong', 'chucknorriz', 1024);
casper.test.assertMatch(xunit.getXML(),
/<testcase classname="bar" name="baz" time="(.*)"><failure type="chucknorriz">wrong/,
'XUnitExporter.addFailure() adds a failed testcase with duration');
// named with time
xunit = require('xunit').create();
casper.test.assertMatch(xunit.getXML(),
/<testsuite>/,
'XUnitExporter.create() created <testsuite> without time');
xunit.setSuiteDuration(1024);
casper.test.assertMatch(xunit.getXML(),
/<testsuite time="1.024">/,
'XUnitExporter.setSuiteDuration() sets time in seconds');
casper.test.done(8);
......