1 |
|
2 | (function(global) {
|
3 | var UNDEFINED,
|
4 | exportObject;
|
5 |
|
6 | if (typeof module !== "undefined" && module.exports) {
|
7 | exportObject = exports;
|
8 | } else {
|
9 | exportObject = global.jasmineReporters = global.jasmineReporters || {};
|
10 | }
|
11 |
|
12 | function elapsed(start, end) { return (end - start)/1000; }
|
13 | function isFailed(obj) { return obj.status === "failed"; }
|
14 | function isSkipped(obj) { return obj.status === "pending"; }
|
15 | function isDisabled(obj) { return obj.status === "disabled"; }
|
16 | function pad(n) { return n < 10 ? "0"+n : n; }
|
17 | function extend(dupe, obj) {
|
18 | for (var prop in obj) {
|
19 | if (obj.hasOwnProperty(prop)) {
|
20 | dupe[prop] = obj[prop];
|
21 | }
|
22 | }
|
23 | return dupe;
|
24 | }
|
25 | function escapeInvalidXmlChars(str) {
|
26 | return str.replace(/&/g, "&")
|
27 | .replace(/</g, "<")
|
28 | .replace(/>/g, ">")
|
29 | .replace(/"/g, """)
|
30 | .replace(/'/g, "'");
|
31 | }
|
32 | function dateString(date) {
|
33 | var year = date.getFullYear();
|
34 | var month = date.getMonth()+1;
|
35 | var day = date.getDate();
|
36 | return year + "-" + pad(month) + "-" + pad(day);
|
37 | }
|
38 | function timeString(date) {
|
39 | var hours = date.getHours();
|
40 | var minutes = date.getMinutes();
|
41 | var seconds = date.getSeconds();
|
42 | return pad(hours) + ":" + pad(minutes) + ":" + pad(seconds);
|
43 | }
|
44 | function getQualifiedFilename(path, filename, separator) {
|
45 | if (path && path.substr(-1) !== separator && filename.substr(0) !== separator) {
|
46 | path += separator;
|
47 | }
|
48 | return path + filename;
|
49 | }
|
50 | function log(str) {
|
51 | var con = global.console || console;
|
52 | if (con && con.log) {
|
53 | con.log(str);
|
54 | }
|
55 | }
|
56 |
|
57 |
|
58 | |
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 | exportObject.NUnitXmlReporter = function(options) {
|
78 | var self = this;
|
79 | self.started = false;
|
80 | self.finished = false;
|
81 |
|
82 | options = options || {};
|
83 | self.savePath = options.savePath || "";
|
84 | self.filename = options.filename || "nunitresults.xml";
|
85 | self.reportName = options.reportName || "Jasmine Results";
|
86 |
|
87 | var suites = [],
|
88 | currentSuite = null,
|
89 | totalSpecsExecuted = 0,
|
90 | totalSpecsSkipped = 0,
|
91 | totalSpecsDisabled = 0,
|
92 | totalSpecsFailed = 0,
|
93 | totalSpecsDefined,
|
94 |
|
95 | fakeFocusedSuite = {
|
96 | id: "focused",
|
97 | description: "focused specs",
|
98 | fullName: "focused specs"
|
99 | };
|
100 |
|
101 | var __suites = {}, __specs = {};
|
102 | function getSuite(suite) {
|
103 | __suites[suite.id] = extend(__suites[suite.id] || {}, suite);
|
104 | return __suites[suite.id];
|
105 | }
|
106 | function getSpec(spec, suite) {
|
107 | __specs[spec.id] = extend(__specs[spec.id] || {}, spec);
|
108 | var ret = __specs[spec.id];
|
109 | if (suite && !ret._suite) {
|
110 | ret._suite = suite;
|
111 | suite._specs.push(ret);
|
112 | }
|
113 | return ret;
|
114 | }
|
115 |
|
116 | self.jasmineStarted = function(summary) {
|
117 | totalSpecsDefined = summary && summary.totalSpecsDefined || NaN;
|
118 | exportObject.startTime = new Date();
|
119 | self.started = true;
|
120 | };
|
121 | self.suiteStarted = function(suite) {
|
122 | suite = getSuite(suite);
|
123 | suite._startTime = new Date();
|
124 | suite._specs = [];
|
125 | suite._suites = [];
|
126 | suite._failures = 0;
|
127 | suite._nestedFailures = 0;
|
128 | suite._skipped = 0;
|
129 | suite._disabled = 0;
|
130 | suite._parent = currentSuite;
|
131 | if (!currentSuite) {
|
132 | suites.push(suite);
|
133 | } else {
|
134 | currentSuite._suites.push(suite);
|
135 | }
|
136 | currentSuite = suite;
|
137 | };
|
138 | self.specStarted = function(spec) {
|
139 | if (!currentSuite) {
|
140 |
|
141 | self.suiteStarted(fakeFocusedSuite);
|
142 | }
|
143 | spec = getSpec(spec, currentSuite);
|
144 | spec._startTime = new Date();
|
145 | };
|
146 | self.specDone = function(spec) {
|
147 | spec = getSpec(spec, currentSuite);
|
148 | spec._endTime = new Date();
|
149 | if (isSkipped(spec)) {
|
150 | spec._suite._skipped++;
|
151 | totalSpecsSkipped++;
|
152 | }
|
153 | if (isDisabled(spec)) {
|
154 | spec._suite._disabled++;
|
155 | totalSpecsDisabled++;
|
156 | }
|
157 | if (isFailed(spec)) {
|
158 | spec._suite._failures++;
|
159 |
|
160 | for (var parent=spec._suite._parent; parent; parent=parent._parent) {
|
161 | parent._nestedFailures++;
|
162 | }
|
163 | totalSpecsFailed++;
|
164 | }
|
165 | totalSpecsExecuted++;
|
166 | };
|
167 | self.suiteDone = function(suite) {
|
168 | suite = getSuite(suite);
|
169 | if (suite._parent === UNDEFINED) {
|
170 |
|
171 | self.suiteStarted(suite);
|
172 | suite._disabled = true;
|
173 | }
|
174 | suite._endTime = new Date();
|
175 | currentSuite = suite._parent;
|
176 | };
|
177 | self.jasmineDone = function() {
|
178 | if (currentSuite) {
|
179 |
|
180 | self.suiteDone(fakeFocusedSuite);
|
181 | }
|
182 | self.writeFile(resultsAsXml());
|
183 |
|
184 |
|
185 | self.finished = true;
|
186 |
|
187 | exportObject.endTime = new Date();
|
188 | };
|
189 |
|
190 | self.writeFile = function(text) {
|
191 | var errors = [];
|
192 | var path = self.savePath;
|
193 | var filename = self.filename;
|
194 |
|
195 | function phantomWrite(path, filename, text) {
|
196 |
|
197 | filename = getQualifiedFilename(path, filename, window.fs_path_separator);
|
198 |
|
199 | __phantom_writeFile(filename, text);
|
200 | }
|
201 |
|
202 | function nodeWrite(path, filename, text) {
|
203 | var fs = require("fs");
|
204 | var nodejs_path = require("path");
|
205 | require("mkdirp").sync(path);
|
206 | var filepath = nodejs_path.join(path, filename);
|
207 | var xmlfile = fs.openSync(filepath, "w");
|
208 | fs.writeSync(xmlfile, text, 0);
|
209 | fs.closeSync(xmlfile);
|
210 | return;
|
211 | }
|
212 |
|
213 |
|
214 | try {
|
215 | phantomWrite(path, filename, text);
|
216 | return;
|
217 | } catch (e) { errors.push(" PhantomJs attempt: " + e.message); }
|
218 | try {
|
219 | nodeWrite(path, filename, text);
|
220 | return;
|
221 | } catch (f) { errors.push(" NodeJS attempt: " + f.message); }
|
222 |
|
223 |
|
224 | log("Warning: writing nunit report failed for '" + path + "', '" +
|
225 | filename + "'. Reasons:\n" +
|
226 | errors.join("\n")
|
227 | );
|
228 | };
|
229 |
|
230 |
|
231 | function resultsAsXml() {
|
232 | var date = new Date(),
|
233 | totalSpecs = totalSpecsDefined || totalSpecsExecuted,
|
234 | disabledSpecs = totalSpecs - totalSpecsExecuted + totalSpecsDisabled,
|
235 | skippedSpecs = totalSpecsSkipped + disabledSpecs;
|
236 |
|
237 | var xml = '<?xml version="1.0" encoding="utf-8" ?>';
|
238 | xml += '\n<test-results name="' + escapeInvalidXmlChars(self.reportName) + '"';
|
239 | xml += ' total="' + totalSpecs + '"';
|
240 | xml += ' failures="' + totalSpecsFailed + '"';
|
241 | xml += ' not-run="' + skippedSpecs + '"';
|
242 | xml += ' date="' + dateString(date) + '"';
|
243 | xml += ' time="' + timeString(date) + '"';
|
244 | xml += ">";
|
245 |
|
246 | for (var i=0; i<suites.length; i++) {
|
247 | xml += suiteAsXml(suites[i], " ");
|
248 | }
|
249 | xml += "\n</test-results>";
|
250 | return xml;
|
251 | }
|
252 | };
|
253 |
|
254 | function suiteAsXml(suite, indent) {
|
255 | indent = indent || "";
|
256 | var i, xml = "\n" + indent + "<test-suite";
|
257 | xml += ' name="' + escapeInvalidXmlChars(suite.description) + '"';
|
258 | xml += ' executed="' + !suite._disabled + '"';
|
259 | xml += ' success="' + !(suite._failures || suite._nestedFailures) + '"';
|
260 | xml += ' time="' + elapsed(suite._startTime, suite._endTime) + '"';
|
261 | xml += ">";
|
262 | xml += "\n" + indent + " <results>";
|
263 |
|
264 | for (i=0; i<suite._suites.length; i++) {
|
265 | xml += suiteAsXml(suite._suites[i], indent+" ");
|
266 | }
|
267 | for (i=0; i<suite._specs.length; i++) {
|
268 | xml += specAsXml(suite._specs[i], indent+" ");
|
269 | }
|
270 | xml += "\n" + indent + " </results>";
|
271 | xml += "\n" + indent + "</test-suite>";
|
272 | return xml;
|
273 | }
|
274 | function specAsXml(spec, indent) {
|
275 | indent = indent || "";
|
276 | var xml = "\n" + indent + "<test-case";
|
277 | xml += ' name="' + escapeInvalidXmlChars(spec.description) + '"';
|
278 | xml += ' executed="' + !(isSkipped(spec) || isDisabled(spec)) + '"';
|
279 | xml += ' success="' + !isFailed(spec) + '"';
|
280 | xml += ' time="' + elapsed(spec._startTime, spec._endTime) + '"';
|
281 | xml += ">";
|
282 | for (var i = 0, failure; i < spec.failedExpectations.length; i++) {
|
283 | failure = spec.failedExpectations[i];
|
284 | xml += "\n" + indent + " <failure>";
|
285 | xml += "\n" + indent + " <message><![CDATA[" + failure.message + "]]></message>";
|
286 | xml += "\n" + indent + " <stack-trace><![CDATA[" + failure.stack + "]]></stack-trace>";
|
287 | xml += "\n" + indent + " </failure>";
|
288 | }
|
289 | xml += "\n" + indent + "</test-case>";
|
290 | return xml;
|
291 | }
|
292 | })(this);
|