Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jUnit Support #27

Merged
merged 2 commits into from Jul 25, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 95 additions & 0 deletions lib/formatters/junit.js
@@ -0,0 +1,95 @@
/**
* @fileoverview jUnit Reporter
* @author Jamund Ferguson
*/

/*jshint node:true*/

//------------------------------------------------------------------------------
// Helper Functions
//------------------------------------------------------------------------------

function getMessageType(message, rules) {

if (message.fatal || rules[message.ruleId] === 2) {
return "Error";
} else {
return "Warning";
}

}

/**
* Replace special characters before write to output.
*
* Rules:
* - single quotes is the escape sequence for double-quotes
* - &lt; is the escape sequence for <
* - &gt; is the escape sequence for >
* - &quot; is the escape sequence for "
* - &apos; is the escape sequence for '
* - &amp; is the escape sequence for &
*
* @param {String} message to escape
* @return escaped message as {String}
*/
function escapeSpecialCharacters(str) {

str = str || "";
var pairs = {
"&": "&amp;",
"\"": "&quot;",
"'": "&apos;",
"<": "&lt;",
">": "&gt;"
};
for (var r in pairs) {
str = str.replace(new RegExp(r, "g"), pairs[r]);
}
return str || "";

}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

module.exports = function(results, config) {

var output = "",
rules = config.rules || {};

output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
output += "<testsuites>\n";

results.forEach(function(result) {

var messages = result.messages;

if (messages.length) {
output += "<testsuite package=\"org.eslint\" time=\"0\" tests=\"" + messages.length + "\" errors=\"" + messages.length + "\" name=\"" + result.filePath + "\">\n";
}

messages.forEach(function(message) {
var type = message.fatal ? "error" : "failure";
output += "<testcase time=\"0\" name=\"org.eslint." + (message.ruleId || "unknown") + "\">";
output += "<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">";
output += "<![CDATA[";
output += "line " + message.line + ", col ";
output += message.column + ", " + getMessageType(message, rules);
output += " - " + escapeSpecialCharacters(message.message);
output += "]]>";
output += "</" + type + ">";
output += "</testcase>\n";
});

if (messages.length) {
output += "</testsuite>\n";
}

});

output += "</testsuites>\n";

return output;
};
193 changes: 193 additions & 0 deletions tests/lib/formatters/junit.js
@@ -0,0 +1,193 @@
/**
* @fileoverview Tests for jUnit Formatter.
* @author Jamund Ferguson
*/

/*jshint node:true*/

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var vows = require("vows"),
assert = require("assert"),
sinon = require("sinon"),
formatter = require("../../../lib/formatters/junit");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

vows.describe("formatter:junit").addBatch({

"when there are no problems": {

topic: [],

"should not complain about anything": function(topic) {
var config = {}; // not needed for this test
var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites></testsuites>', result.replace(/\n/g, ""));
}
},

"when passed a single message": {

topic: [{
filePath: "foo.js",
messages: [{
message: "Unexpected foo.",
line: 5,
column: 10,
ruleId: "foo"
}]
}],

"should return a single <testcase> with a message and the line and col number in the body (error)": function(topic) {
var config = {
rules: { foo: 2 }
};

var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite package="org.eslint" time="0" tests="1" errors="1" name="foo.js"><testcase time="0" name="org.eslint.foo"><failure message="Unexpected foo."><![CDATA[line 5, col 10, Error - Unexpected foo.]]></failure></testcase></testsuite></testsuites>', result.replace(/\n/g, ""));
},

"should return a single <testcase> with a message and the line and col number in the body (warning)": function(topic) {
var config = {
rules: { foo: 1 }
};

var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite package="org.eslint" time="0" tests="1" errors="1" name="foo.js"><testcase time="0" name="org.eslint.foo"><failure message="Unexpected foo."><![CDATA[line 5, col 10, Warning - Unexpected foo.]]></failure></testcase></testsuite></testsuites>', result.replace(/\n/g, ""));
}

},

"when passed a fatal error message": {

topic: [{
filePath: "foo.js",
messages: [{
fatal: true,
message: "Unexpected foo.",
line: 5,
column: 10,
ruleId: "foo"
}]
}],

"should return a single <testcase> and an <error>": function(topic) {
var config = {};
var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite package="org.eslint" time="0" tests="1" errors="1" name="foo.js"><testcase time="0" name="org.eslint.foo"><error message="Unexpected foo."><![CDATA[line 5, col 10, Error - Unexpected foo.]]></error></testcase></testsuite></testsuites>', result.replace(/\n/g, ""));
}
},

"when passed multiple messages": {

topic: [{
filePath: "foo.js",
messages: [{
message: "Unexpected foo.",
line: 5,
column: 10,
ruleId: "foo"
}, {
message: "Unexpected bar.",
line: 6,
column: 11,
ruleId: "bar"
}]
}],

"should return a multiple <testcase>'s": function(topic) {
var config = {
rules: { foo: 2, bar: 1 }
};

var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite package="org.eslint" time="0" tests="2" errors="2" name="foo.js"><testcase time="0" name="org.eslint.foo"><failure message="Unexpected foo."><![CDATA[line 5, col 10, Error - Unexpected foo.]]></failure></testcase><testcase time="0" name="org.eslint.bar"><failure message="Unexpected bar."><![CDATA[line 6, col 11, Warning - Unexpected bar.]]></failure></testcase></testsuite></testsuites>', result.replace(/\n/g, ""));
}

},

"when passed special characters": {

topic: [{
filePath: "foo.js",
messages: [{
message: "Unexpected <foo></foo>.",
line: 5,
column: 10,
ruleId: "foo"
}]
}],

"should make them go away": function(topic) {
var config = {
rules: { foo: 1 }
};

var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite package="org.eslint" time="0" tests="1" errors="1" name="foo.js"><testcase time="0" name="org.eslint.foo"><failure message="Unexpected &lt;foo&gt;&lt;/foo&gt;."><![CDATA[line 5, col 10, Warning - Unexpected &lt;foo&gt;&lt;/foo&gt;.]]></failure></testcase></testsuite></testsuites>', result.replace(/\n/g, ""));

}
},

"when passed multiple files with 1 message each": {
topic: [{
filePath: "foo.js",
messages: [{
message: "Unexpected foo.",
line: 5,
column: 10,
ruleId: "foo"
}]
}, {
filePath: "bar.js",
messages: [{
message: "Unexpected bar.",
line: 6,
column: 11,
ruleId: "bar"
}]
}],

"should return 2 <testsuite>'s": function(topic) {
var config = {
rules: { foo: 1, bar: 2 }
};

var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite package="org.eslint" time="0" tests="1" errors="1" name="foo.js"><testcase time="0" name="org.eslint.foo"><failure message="Unexpected foo."><![CDATA[line 5, col 10, Warning - Unexpected foo.]]></failure></testcase></testsuite><testsuite package="org.eslint" time="0" tests="1" errors="1" name="bar.js"><testcase time="0" name="org.eslint.bar"><failure message="Unexpected bar."><![CDATA[line 6, col 11, Error - Unexpected bar.]]></failure></testcase></testsuite></testsuites>', result.replace(/\n/g, ""));

}
},

"when passed multiple files with total 1 failure": {
topic: [{
filePath: "foo.js",
messages: [{
message: "Unexpected foo.",
line: 5,
column: 10,
ruleId: "foo"
}]
}, {
filePath: "bar.js",
messages: []
}],

"should return 1 <testsuite>'": function(topic) {
var config = {
rules: { foo: 1, bar: 2 }
};

var result = formatter(topic, config);
assert.equal('<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite package="org.eslint" time="0" tests="1" errors="1" name="foo.js"><testcase time="0" name="org.eslint.foo"><failure message="Unexpected foo."><![CDATA[line 5, col 10, Warning - Unexpected foo.]]></failure></testcase></testsuite></testsuites>', result.replace(/\n/g, ""));

}
}


}).export(module);