Commit 0e69c75e 0e69c75e96483bbabeebe3675dec11e55ff6a868 by Nicolas Perriault

Merged branch '1.1-dev'

2 parents 0628688c a8d6b513
...@@ -10,26 +10,26 @@ oncletom ...@@ -10,26 +10,26 @@ oncletom
10 hannyu 10 hannyu
11 Chris Lorenzo 11 Chris Lorenzo
12 Victor Yap 12 Victor Yap
13 Rob Barreca
14 Tyler Ritchie
13 nrabinowitz 15 nrabinowitz
14 pborreli 16 pborreli
15 Rob Barreca 17 Dave Lee
16 Andrew Childs 18 Andrew Childs
17 Solomon White 19 Solomon White
18 reina.sweet 20 reina.sweet
19 Dave Lee
20 Reina Sweet 21 Reina Sweet
22 Jan Schaumann
21 Elmar Langholz 23 Elmar Langholz
22 Jason Funk 24 Clochix
23 Donovan Hutchinson 25 Donovan Hutchinson
24 Julien Moulin 26 Julien Moulin
25 Michael Geers 27 Michael Geers
26 Jan Schaumann 28 Jason Funk
27 Clochix 29 Vladimir Chizhov
28 Raphaël Benitte
29 Tim Bunce
30 alfetopito
31 jean-philippe serafin 30 jean-philippe serafin
32 snkashis 31 snkashis
32 Rafael
33 Andrew de Andrade 33 Andrew de Andrade
34 Ben Lowery 34 Ben Lowery
35 Chris Winters 35 Chris Winters
...@@ -48,6 +48,8 @@ Mathieu Agopian ...@@ -48,6 +48,8 @@ Mathieu Agopian
48 Mehdi Kabab 48 Mehdi Kabab
49 Mikko Peltonen 49 Mikko Peltonen
50 Pascal Borreli 50 Pascal Borreli
51 Rafael
52 Rafael Garcia 51 Rafael Garcia
52 Raphaël Benitte
53 Tim Bunce
54 alfetopito
53 ``` 55 ```
......
...@@ -1370,6 +1370,19 @@ Casper.prototype.sendKeys = function(selector, keys, options) { ...@@ -1370,6 +1370,19 @@ Casper.prototype.sendKeys = function(selector, keys, options) {
1370 }; 1370 };
1371 1371
1372 /** 1372 /**
1373 * Sets current page content.
1374 *
1375 * @param String content Desired page content
1376 * @return Casper
1377 */
1378 Casper.prototype.setContent = function setContent(content) {
1379 "use strict";
1380 this.checkStarted();
1381 this.page.content = content;
1382 return this;
1383 };
1384
1385 /**
1373 * Sets current WebPage instance the credentials for HTTP authentication. 1386 * Sets current WebPage instance the credentials for HTTP authentication.
1374 * 1387 *
1375 * @param String username 1388 * @param String username
......
...@@ -35,6 +35,13 @@ var events = require('events'); ...@@ -35,6 +35,13 @@ var events = require('events');
35 var utils = require('utils'); 35 var utils = require('utils');
36 var f = utils.format; 36 var f = utils.format;
37 37
38 /**
39 * Creates a tester instance.
40 *
41 * @param Casper casper A Casper instance
42 * @param Object options Tester options
43 * @return Tester
44 */
38 exports.create = function create(casper, options) { 45 exports.create = function create(casper, options) {
39 "use strict"; 46 "use strict";
40 return new Tester(casper, options); 47 return new Tester(casper, options);
...@@ -63,81 +70,51 @@ var Tester = function Tester(casper, options) { ...@@ -63,81 +70,51 @@ var Tester = function Tester(casper, options) {
63 this.aborted = false; 70 this.aborted = false;
64 this.executed = 0; 71 this.executed = 0;
65 this.currentTestFile = null; 72 this.currentTestFile = null;
73 this.currentTestStartTime = null;
74 this.currentSuite = undefined;
66 this.currentSuiteNum = 0; 75 this.currentSuiteNum = 0;
67 this.exporter = require('xunit').create(); 76 this.lastAssertTime = 0;
68 this.loadIncludes = { 77 this.loadIncludes = {
69 includes: [], 78 includes: [],
70 pre: [], 79 pre: [],
71 post: [] 80 post: []
72 }; 81 };
82 this.queue = [];
73 this.running = false; 83 this.running = false;
74 this.suites = []; 84 this.started = false;
85 this.suites = new TestSuite();
75 this.options = utils.mergeObjects({ 86 this.options = utils.mergeObjects({
76 failFast: false, // terminates a suite as soon as a test fails? 87 failFast: false, // terminates a suite as soon as a test fails?
77 failText: "FAIL", // text to use for a successful test 88 failText: "FAIL", // text to use for a succesful test
78 passText: "PASS", // text to use for a failed test 89 passText: "PASS", // text to use for a failed test
79 pad: 80 , // maximum number of chars for a result line 90 pad: 80 , // maximum number of chars for a result line
80 warnText: "WARN" // text to use for a dubious test 91 warnText: "WARN" // text to use for a dubious test
81 }, options); 92 }, options);
82 93
83 // properties
84 this.testResults = {
85 passed: 0,
86 failed: 0,
87 passes: [],
88 failures: [],
89 passesTime: [],
90 failuresTime: []
91 };
92
93 // measuring test duration
94 this.currentTestStartTime = null;
95 this.lastAssertTime = 0;
96
97 this.configure(); 94 this.configure();
98 95
99 this.on('success', function onSuccess(success) { 96 this.on('success', function onSuccess(success) {
100 this.testResults.passes.push(success);
101 var timeElapsed = new Date() - this.currentTestStartTime; 97 var timeElapsed = new Date() - this.currentTestStartTime;
102 this.testResults.passesTime.push(timeElapsed - this.lastAssertTime); 98 this.currentSuite.addSuccess(success, timeElapsed - this.lastAssertTime);
103 this.exporter.addSuccess(fs.absolute(success.file), success.message || success.standard, timeElapsed - this.lastAssertTime);
104 this.lastAssertTime = timeElapsed; 99 this.lastAssertTime = timeElapsed;
105 }); 100 });
106 101
107 this.on('fail', function onFail(failure) { 102 this.on('fail', function onFail(failure) {
108 // export 103 // export
109 var timeElapsed = new Date() - this.currentTestStartTime; 104 var valueKeys = Object.keys(failure.values),
110 this.testResults.failuresTime.push(timeElapsed - this.lastAssertTime); 105 timeElapsed = new Date() - this.currentTestStartTime;
111 this.exporter.addFailure( 106 this.currentSuite.addFailure(failure, timeElapsed - this.lastAssertTime);
112 fs.absolute(failure.file),
113 failure.message || failure.standard,
114 failure.standard || "test failed",
115 failure.type || "unknown",
116 (timeElapsed - this.lastAssertTime)
117 );
118 this.lastAssertTime = timeElapsed; 107 this.lastAssertTime = timeElapsed;
119 this.testResults.failures.push(failure);
120
121 // special printing 108 // special printing
122 if (failure.type) { 109 if (failure.type) {
123 this.comment(' type: ' + failure.type); 110 this.comment(' type: ' + failure.type);
124 } 111 }
125 if (failure.values && Object.keys(failure.values).length > 0) { 112 if (!failure.values || valueKeys.length === 0) {
126 for (var name in failure.values) { 113 return;
127 var comment = ' ' + name + ': ';
128 var value = failure.values[name];
129 try {
130 comment += utils.serialize(failure.values[name]);
131 } catch (e) {
132 try {
133 comment += utils.serialize(failure.values[name].toString());
134 } catch (e2) {
135 comment += '(unserializable value)';
136 }
137 }
138 this.comment(comment);
139 }
140 } 114 }
115 valueKeys.forEach(function(name) {
116 this.comment(f(' %s: %s', name, utils.formatTestValue(failure.values[name], name)));
117 }.bind(this));
141 }); 118 });
142 119
143 // casper events 120 // casper events
...@@ -156,7 +133,7 @@ var Tester = function Tester(casper, options) { ...@@ -156,7 +133,7 @@ var Tester = function Tester(casper, options) {
156 line = backtrace[0].line; 133 line = backtrace[0].line;
157 } catch (e) {} 134 } catch (e) {}
158 } 135 }
159 self.uncaughtError(msg, self.currentTestFile, line); 136 self.uncaughtError(msg, self.currentTestFile, line, backtrace);
160 self.done(); 137 self.done();
161 }); 138 });
162 139
...@@ -186,16 +163,17 @@ exports.Tester = Tester; ...@@ -186,16 +163,17 @@ exports.Tester = Tester;
186 * @param Object|null context Assertion context object (Optional) 163 * @param Object|null context Assertion context object (Optional)
187 * @return Object An assertion result object 164 * @return Object An assertion result object
188 */ 165 */
189 Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject, message, context) { 166 Tester.prototype.assert =
167 Tester.prototype.assertTrue = function assert(subject, message, context) {
190 "use strict"; 168 "use strict";
191 this.executed++; 169 this.executed++;
192 return this.processAssertionResult(utils.mergeObjects({ 170 return this.processAssertionResult(utils.mergeObjects({
193 success: subject === true, 171 success: subject === true,
194 type: "assert", 172 type: "assert",
195 standard: "Subject is strictly true", 173 standard: "Subject is strictly true",
196 message: message, 174 message: message,
197 file: this.currentTestFile, 175 file: this.currentTestFile,
198 values: { 176 values: {
199 subject: utils.getPropertyPath(context, 'values.subject') || subject 177 subject: utils.getPropertyPath(context, 'values.subject') || subject
200 } 178 }
201 }, context || {})); 179 }, context || {}));
...@@ -209,12 +187,13 @@ Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject, ...@@ -209,12 +187,13 @@ Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject,
209 * @param String message Test description (Optional) 187 * @param String message Test description (Optional)
210 * @return Object An assertion result object 188 * @return Object An assertion result object
211 */ 189 */
212 Tester.prototype.assertEquals = Tester.prototype.assertEqual = function assertEquals(subject, expected, message) { 190 Tester.prototype.assertEquals =
191 Tester.prototype.assertEqual = function assertEquals(subject, expected, message) {
213 "use strict"; 192 "use strict";
214 return this.assert(this.testEquals(subject, expected), message, { 193 return this.assert(utils.equals(subject, expected), message, {
215 type: "assertEquals", 194 type: "assertEquals",
216 standard: "Subject equals the expected value", 195 standard: "Subject equals the expected value",
217 values: { 196 values: {
218 subject: subject, 197 subject: subject,
219 expected: expected 198 expected: expected
220 } 199 }
...@@ -232,9 +211,9 @@ Tester.prototype.assertEquals = Tester.prototype.assertEqual = function assertEq ...@@ -232,9 +211,9 @@ Tester.prototype.assertEquals = Tester.prototype.assertEqual = function assertEq
232 Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, message) { 211 Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, message) {
233 "use strict"; 212 "use strict";
234 return this.assert(!this.testEquals(subject, shouldnt), message, { 213 return this.assert(!this.testEquals(subject, shouldnt), message, {
235 type: "assertNotEquals", 214 type: "assertNotEquals",
236 standard: "Subject doesn't equal what it shouldn't be", 215 standard: "Subject doesn't equal what it shouldn't be",
237 values: { 216 values: {
238 subject: subject, 217 subject: subject,
239 shouldnt: shouldnt 218 shouldnt: shouldnt
240 } 219 }
...@@ -246,13 +225,15 @@ Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, m ...@@ -246,13 +225,15 @@ Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, m
246 * 225 *
247 * @param Function fn A function to be evaluated in remote DOM 226 * @param Function fn A function to be evaluated in remote DOM
248 * @param String message Test description 227 * @param String message Test description
249 * @param Object params Object/Array containing the parameters to inject into the function (optional) 228 * @param Object params Object/Array containing the parameters to inject into
229 * the function (optional)
250 * @return Object An assertion result object 230 * @return Object An assertion result object
251 */ 231 */
252 Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertEval(fn, message, params) { 232 Tester.prototype.assertEval =
233 Tester.prototype.assertEvaluate = function assertEval(fn, message, params) {
253 "use strict"; 234 "use strict";
254 return this.assert(this.casper.evaluate(fn, params), message, { 235 return this.assert(this.casper.evaluate(fn, params), message, {
255 type: "assertEval", 236 type: "assertEval",
256 standard: "Evaluated function returns true", 237 standard: "Evaluated function returns true",
257 values: { 238 values: {
258 fn: fn, 239 fn: fn,
...@@ -268,16 +249,18 @@ Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertE ...@@ -268,16 +249,18 @@ Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertE
268 * @param Function fn The function to be evaluated in remote DOM 249 * @param Function fn The function to be evaluated in remote DOM
269 * @param Boolean expected The expected value 250 * @param Boolean expected The expected value
270 * @param String|null message Test description 251 * @param String|null message Test description
271 * @param Object|null params Object containing the parameters to inject into the function (optional) 252 * @param Object|null params Object containing the parameters to inject into the
253 * function (optional)
272 * @return Object An assertion result object 254 * @return Object An assertion result object
273 */ 255 */
274 Tester.prototype.assertEvalEquals = Tester.prototype.assertEvalEqual = function assertEvalEquals(fn, expected, message, params) { 256 Tester.prototype.assertEvalEquals =
257 Tester.prototype.assertEvalEqual = function assertEvalEquals(fn, expected, message, params) {
275 "use strict"; 258 "use strict";
276 var subject = this.casper.evaluate(fn, params); 259 var subject = this.casper.evaluate(fn, params);
277 return this.assert(this.testEquals(subject, expected), message, { 260 return this.assert(utils.equals(subject, expected), message, {
278 type: "assertEvalEquals", 261 type: "assertEvalEquals",
279 standard: "Evaluated function returns the expected value", 262 standard: "Evaluated function returns the expected value",
280 values: { 263 values: {
281 fn: fn, 264 fn: fn,
282 params: params, 265 params: params,
283 subject: subject, 266 subject: subject,
...@@ -299,13 +282,13 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag ...@@ -299,13 +282,13 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag
299 var actual = this.casper.evaluate(function(inputName) { 282 var actual = this.casper.evaluate(function(inputName) {
300 return __utils__.getFieldValue(inputName); 283 return __utils__.getFieldValue(inputName);
301 }, inputName); 284 }, inputName);
302 return this.assert(this.testEquals(actual, expected), message, { 285 return this.assert(utils.equals(actual, expected), message, {
303 type: 'assertField', 286 type: 'assertField',
304 standard: f('"%s" input field has the value "%s"', inputName, expected), 287 standard: f('"%s" input field has the value "%s"', inputName, expected),
305 values: { 288 values: {
306 inputName: inputName, 289 inputName: inputName,
307 actual: actual, 290 actual: actual,
308 expected: expected 291 expected: expected
309 } 292 }
310 }); 293 });
311 }; 294 };
...@@ -318,7 +301,10 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag ...@@ -318,7 +301,10 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag
318 * @param String message Test description 301 * @param String message Test description
319 * @return Object An assertion result object 302 * @return Object An assertion result object
320 */ 303 */
321 Tester.prototype.assertExists = Tester.prototype.assertExist = Tester.prototype.assertSelectorExists = Tester.prototype.assertSelectorExist = function assertExists(selector, message) { 304 Tester.prototype.assertExists =
305 Tester.prototype.assertExist =
306 Tester.prototype.assertSelectorExists =
307 Tester.prototype.assertSelectorExist = function assertExists(selector, message) {
322 "use strict"; 308 "use strict";
323 return this.assert(this.casper.exists(selector), message, { 309 return this.assert(this.casper.exists(selector), message, {
324 type: "assertExists", 310 type: "assertExists",
...@@ -337,7 +323,8 @@ Tester.prototype.assertExists = Tester.prototype.assertExist = Tester.prototype. ...@@ -337,7 +323,8 @@ Tester.prototype.assertExists = Tester.prototype.assertExist = Tester.prototype.
337 * @param String message Test description 323 * @param String message Test description
338 * @return Object An assertion result object 324 * @return Object An assertion result object
339 */ 325 */
340 Tester.prototype.assertDoesntExist = Tester.prototype.assertNotExists = function assertDoesntExist(selector, message) { 326 Tester.prototype.assertDoesntExist =
327 Tester.prototype.assertNotExists = function assertDoesntExist(selector, message) {
341 "use strict"; 328 "use strict";
342 return this.assert(!this.casper.exists(selector), message, { 329 return this.assert(!this.casper.exists(selector), message, {
343 type: "assertDoesntExist", 330 type: "assertDoesntExist",
...@@ -358,7 +345,7 @@ Tester.prototype.assertDoesntExist = Tester.prototype.assertNotExists = function ...@@ -358,7 +345,7 @@ Tester.prototype.assertDoesntExist = Tester.prototype.assertNotExists = function
358 Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) { 345 Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) {
359 "use strict"; 346 "use strict";
360 var currentHTTPStatus = this.casper.currentHTTPStatus; 347 var currentHTTPStatus = this.casper.currentHTTPStatus;
361 return this.assert(this.testEquals(this.casper.currentHTTPStatus, status), message, { 348 return this.assert(utils.equals(this.casper.currentHTTPStatus, status), message, {
362 type: "assertHttpStatus", 349 type: "assertHttpStatus",
363 standard: f("HTTP status code is: %s", status), 350 standard: f("HTTP status code is: %s", status),
364 values: { 351 values: {
...@@ -376,7 +363,8 @@ Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) { ...@@ -376,7 +363,8 @@ Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) {
376 * @param String message Test description 363 * @param String message Test description
377 * @return Object An assertion result object 364 * @return Object An assertion result object
378 */ 365 */
379 Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertMatch(subject, pattern, message) { 366 Tester.prototype.assertMatch =
367 Tester.prototype.assertMatches = function assertMatch(subject, pattern, message) {
380 "use strict"; 368 "use strict";
381 if (utils.betterTypeOf(pattern) !== "regexp") { 369 if (utils.betterTypeOf(pattern) !== "regexp") {
382 throw new CasperError('Invalid regexp.'); 370 throw new CasperError('Invalid regexp.');
...@@ -398,7 +386,8 @@ Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertM ...@@ -398,7 +386,8 @@ Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertM
398 * @param String message Test description 386 * @param String message Test description
399 * @return Object An assertion result object 387 * @return Object An assertion result object
400 */ 388 */
401 Tester.prototype.assertNot = Tester.prototype.assertFalse = function assertNot(condition, message) { 389 Tester.prototype.assertNot =
390 Tester.prototype.assertFalse = function assertNot(condition, message) {
402 "use strict"; 391 "use strict";
403 return this.assert(!condition, message, { 392 return this.assert(!condition, message, {
404 type: "assertNot", 393 type: "assertNot",
...@@ -416,7 +405,8 @@ Tester.prototype.assertNot = Tester.prototype.assertFalse = function assertNot(c ...@@ -416,7 +405,8 @@ Tester.prototype.assertNot = Tester.prototype.assertFalse = function assertNot(c
416 * @param String message Test description 405 * @param String message Test description
417 * @return Object An assertion result object 406 * @return Object An assertion result object
418 */ 407 */
419 Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function assertNotVisible(selector, message) { 408 Tester.prototype.assertNotVisible =
409 Tester.prototype.assertInvisible = function assertNotVisible(selector, message) {
420 "use strict"; 410 "use strict";
421 return this.assert(!this.casper.visible(selector), message, { 411 return this.assert(!this.casper.visible(selector), message, {
422 type: "assertVisible", 412 type: "assertVisible",
...@@ -436,7 +426,9 @@ Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function ...@@ -436,7 +426,9 @@ Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function
436 * @param String message Test description 426 * @param String message Test description
437 * @return Object An assertion result object 427 * @return Object An assertion result object
438 */ 428 */
439 Tester.prototype.assertRaises = Tester.prototype.assertRaise = Tester.prototype.assertThrows = function assertRaises(fn, args, message) { 429 Tester.prototype.assertRaises =
430 Tester.prototype.assertRaise =
431 Tester.prototype.assertThrows = function assertRaises(fn, args, message) {
440 "use strict"; 432 "use strict";
441 var context = { 433 var context = {
442 type: "assertRaises", 434 type: "assertRaises",
...@@ -461,7 +453,8 @@ Tester.prototype.assertRaises = Tester.prototype.assertRaise = Tester.prototype. ...@@ -461,7 +453,8 @@ Tester.prototype.assertRaises = Tester.prototype.assertRaise = Tester.prototype.
461 * @param String message Test description 453 * @param String message Test description
462 * @return Object An assertion result object 454 * @return Object An assertion result object
463 */ 455 */
464 Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = function assertResourceExists(test, message) { 456 Tester.prototype.assertResourceExists =
457 Tester.prototype.assertResourceExist = function assertResourceExists(test, message) {
465 "use strict"; 458 "use strict";
466 return this.assert(this.casper.resourceExists(test), message, { 459 return this.assert(this.casper.resourceExists(test), message, {
467 type: "assertResourceExists", 460 type: "assertResourceExists",
...@@ -479,7 +472,8 @@ Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = f ...@@ -479,7 +472,8 @@ Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = f
479 * @param String message Test description 472 * @param String message Test description
480 * @return Object An assertion result object 473 * @return Object An assertion result object
481 */ 474 */
482 Tester.prototype.assertTextDoesntExist = Tester.prototype.assertTextDoesntExist = function assertTextDoesntExist(text, message) { 475 Tester.prototype.assertTextDoesntExist =
476 Tester.prototype.assertTextDoesntExist = function assertTextDoesntExist(text, message) {
483 "use strict"; 477 "use strict";
484 var textFound = (this.casper.evaluate(function _evaluate() { 478 var textFound = (this.casper.evaluate(function _evaluate() {
485 return document.body.textContent || document.body.innerText; 479 return document.body.textContent || document.body.innerText;
...@@ -500,7 +494,8 @@ Tester.prototype.assertTextDoesntExist = Tester.prototype.assertTextDoesntExist ...@@ -500,7 +494,8 @@ Tester.prototype.assertTextDoesntExist = Tester.prototype.assertTextDoesntExist
500 * @param String message Test description 494 * @param String message Test description
501 * @return Object An assertion result object 495 * @return Object An assertion result object
502 */ 496 */
503 Tester.prototype.assertTextExists = Tester.prototype.assertTextExist = function assertTextExists(text, message) { 497 Tester.prototype.assertTextExists =
498 Tester.prototype.assertTextExist = function assertTextExists(text, message) {
504 "use strict"; 499 "use strict";
505 var textFound = (this.casper.evaluate(function _evaluate() { 500 var textFound = (this.casper.evaluate(function _evaluate() {
506 return document.body.textContent || document.body.innerText; 501 return document.body.textContent || document.body.innerText;
...@@ -525,9 +520,9 @@ Tester.prototype.assertTruthy = function assertTruthy(subject, message) { ...@@ -525,9 +520,9 @@ Tester.prototype.assertTruthy = function assertTruthy(subject, message) {
525 "use strict"; 520 "use strict";
526 /*jshint eqeqeq:false*/ 521 /*jshint eqeqeq:false*/
527 return this.assert(utils.isTruthy(subject), message, { 522 return this.assert(utils.isTruthy(subject), message, {
528 type: "assertTruthy", 523 type: "assertTruthy",
529 standard: "Subject is truthy", 524 standard: "Subject is truthy",
530 values: { 525 values: {
531 subject: subject 526 subject: subject
532 } 527 }
533 }); 528 });
...@@ -544,9 +539,9 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) { ...@@ -544,9 +539,9 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) {
544 "use strict"; 539 "use strict";
545 /*jshint eqeqeq:false*/ 540 /*jshint eqeqeq:false*/
546 return this.assert(utils.isFalsy(subject), message, { 541 return this.assert(utils.isFalsy(subject), message, {
547 type: "assertFalsy", 542 type: "assertFalsy",
548 standard: "Subject is falsy", 543 standard: "Subject is falsy",
549 values: { 544 values: {
550 subject: subject 545 subject: subject
551 } 546 }
552 }); 547 });
...@@ -560,7 +555,8 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) { ...@@ -560,7 +555,8 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) {
560 * @param String message Test description 555 * @param String message Test description
561 * @return Object An assertion result object 556 * @return Object An assertion result object
562 */ 557 */
563 Tester.prototype.assertSelectorHasText = Tester.prototype.assertSelectorContains = function assertSelectorHasText(selector, text, message) { 558 Tester.prototype.assertSelectorHasText =
559 Tester.prototype.assertSelectorContains = function assertSelectorHasText(selector, text, message) {
564 "use strict"; 560 "use strict";
565 var textFound = this.casper.fetchText(selector).indexOf(text) !== -1; 561 var textFound = this.casper.fetchText(selector).indexOf(text) !== -1;
566 return this.assert(textFound, message, { 562 return this.assert(textFound, message, {
...@@ -581,7 +577,8 @@ Tester.prototype.assertSelectorHasText = Tester.prototype.assertSelectorContains ...@@ -581,7 +577,8 @@ Tester.prototype.assertSelectorHasText = Tester.prototype.assertSelectorContains
581 * @param String message Test description 577 * @param String message Test description
582 * @return Object An assertion result object 578 * @return Object An assertion result object
583 */ 579 */
584 Tester.prototype.assertSelectorDoesntHaveText = Tester.prototype.assertSelectorDoesntContain = function assertSelectorDoesntHaveText(selector, text, message) { 580 Tester.prototype.assertSelectorDoesntHaveText =
581 Tester.prototype.assertSelectorDoesntContain = function assertSelectorDoesntHaveText(selector, text, message) {
585 "use strict"; 582 "use strict";
586 var textFound = this.casper.fetchText(selector).indexOf(text) === -1; 583 var textFound = this.casper.fetchText(selector).indexOf(text) === -1;
587 return this.assert(textFound, message, { 584 return this.assert(textFound, message, {
...@@ -604,7 +601,7 @@ Tester.prototype.assertSelectorDoesntHaveText = Tester.prototype.assertSelectorD ...@@ -604,7 +601,7 @@ Tester.prototype.assertSelectorDoesntHaveText = Tester.prototype.assertSelectorD
604 Tester.prototype.assertTitle = function assertTitle(expected, message) { 601 Tester.prototype.assertTitle = function assertTitle(expected, message) {
605 "use strict"; 602 "use strict";
606 var currentTitle = this.casper.getTitle(); 603 var currentTitle = this.casper.getTitle();
607 return this.assert(this.testEquals(currentTitle, expected), message, { 604 return this.assert(utils.equals(currentTitle, expected), message, {
608 type: "assertTitle", 605 type: "assertTitle",
609 standard: f('Page title is: "%s"', expected), 606 standard: f('Page title is: "%s"', expected),
610 values: { 607 values: {
...@@ -621,7 +618,8 @@ Tester.prototype.assertTitle = function assertTitle(expected, message) { ...@@ -621,7 +618,8 @@ Tester.prototype.assertTitle = function assertTitle(expected, message) {
621 * @param String message Test description 618 * @param String message Test description
622 * @return Object An assertion result object 619 * @return Object An assertion result object
623 */ 620 */
624 Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = function assertTitleMatch(pattern, message) { 621 Tester.prototype.assertTitleMatch =
622 Tester.prototype.assertTitleMatches = function assertTitleMatch(pattern, message) {
625 "use strict"; 623 "use strict";
626 if (utils.betterTypeOf(pattern) !== "regexp") { 624 if (utils.betterTypeOf(pattern) !== "regexp") {
627 throw new CasperError('Invalid regexp.'); 625 throw new CasperError('Invalid regexp.');
...@@ -648,7 +646,7 @@ Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = functi ...@@ -648,7 +646,7 @@ Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = functi
648 Tester.prototype.assertType = function assertType(subject, type, message) { 646 Tester.prototype.assertType = function assertType(subject, type, message) {
649 "use strict"; 647 "use strict";
650 var actual = utils.betterTypeOf(subject); 648 var actual = utils.betterTypeOf(subject);
651 return this.assert(this.testEquals(actual, type), message, { 649 return this.assert(utils.equals(actual, type), message, {
652 type: "assertType", 650 type: "assertType",
653 standard: f('Subject type is: "%s"', type), 651 standard: f('Subject type is: "%s"', type),
654 values: { 652 values: {
...@@ -668,7 +666,8 @@ Tester.prototype.assertType = function assertType(subject, type, message) { ...@@ -668,7 +666,8 @@ Tester.prototype.assertType = function assertType(subject, type, message) {
668 * @param String message Test description 666 * @param String message Test description
669 * @return Object An assertion result object 667 * @return Object An assertion result object
670 */ 668 */
671 Tester.prototype.assertUrlMatch = Tester.prototype.assertUrlMatches = function assertUrlMatch(pattern, message) { 669 Tester.prototype.assertUrlMatch =
670 Tester.prototype.assertUrlMatches = function assertUrlMatch(pattern, message) {
672 "use strict"; 671 "use strict";
673 var currentUrl = this.casper.getCurrentUrl(), 672 var currentUrl = this.casper.getCurrentUrl(),
674 patternType = utils.betterTypeOf(pattern), 673 patternType = utils.betterTypeOf(pattern),
...@@ -718,16 +717,30 @@ Tester.prototype.bar = function bar(text, style) { ...@@ -718,16 +717,30 @@ Tester.prototype.bar = function bar(text, style) {
718 }; 717 };
719 718
720 /** 719 /**
721 * Retrieves the sum of all durations of the tests which were 720 * Starts a suite.
722 * executed in the current suite
723 * 721 *
724 * @return Number duration of all tests executed until now (in the current suite) 722 * @param String description Test suite description
723 * @param Function suiteFn Suite function
725 */ 724 */
726 Tester.prototype.calculateSuiteDuration = function calculateSuiteDuration() { 725 Tester.prototype.begin = function begin(description, suiteFn) {
727 "use strict"; 726 "use strict";
728 return this.testResults.passesTime.concat(this.testResults.failuresTime).reduce(function add(a, b) { 727 if (this.started && this.running) {
729 return a + b; 728 return this.queue.push(arguments);
730 }, 0); 729 }
730 description = description || "Untitled suite in " + this.currentTestFile;
731 this.comment(description);
732 this.currentSuite = new TestSuiteResult({
733 name: description,
734 file: this.currentTestFile
735 });
736 this.executed = 0;
737 this.running = this.started = true;
738 try {
739 suiteFn.call(this, this, this.casper);
740 } catch (e) {
741 this.uncaughtError(e, this.currentTestFile, e.line);
742 this.done();
743 }
731 }; 744 };
732 745
733 /** 746 /**
...@@ -784,11 +797,39 @@ Tester.prototype.configure = function configure() { ...@@ -784,11 +797,39 @@ Tester.prototype.configure = function configure() {
784 Tester.prototype.done = function done(planned) { 797 Tester.prototype.done = function done(planned) {
785 "use strict"; 798 "use strict";
786 if (planned > 0 && planned !== this.executed) { 799 if (planned > 0 && planned !== this.executed) {
787 this.fail(f('%s: %d tests planned, %d tests executed', 800 this.dubious(planned, this.executed);
788 this.currentTestFile, planned, this.executed)); 801 }
802 if (this.currentSuite) {
803 this.suites.push(this.currentSuite);
804 this.currentSuite = undefined;
805 this.executed = 0;
789 } 806 }
790 this.emit('test.done'); 807 this.emit('test.done');
791 this.running = false; 808 this.running = this.started = false;
809 var nextTest = this.queue.shift();
810 if (nextTest) {
811 this.begin.apply(this, nextTest);
812 }
813 };
814
815 /**
816 * Marks a test as dubious, when the number of planned tests doesn't match the
817 * number of actually executed one.
818 *
819 * @param String message
820 */
821 Tester.prototype.dubious = function dubious(planned, executed) {
822 "use strict";
823 var message = f('%d tests planned, %d tests executed', planned, executed);
824 return this.assert(false, message, {
825 type: "dubious",
826 standard: message,
827 message: message,
828 values: {
829 planned: planned,
830 executed: executed
831 }
832 });
792 }; 833 };
793 834
794 /** 835 /**
...@@ -874,57 +915,6 @@ Tester.prototype.formatMessage = function formatMessage(message, style) { ...@@ -874,57 +915,6 @@ Tester.prototype.formatMessage = function formatMessage(message, style) {
874 }; 915 };
875 916
876 /** 917 /**
877 * Retrieves current failure data and all failed cases.
878 *
879 * @return Object casedata An object containg information about cases
880 * @return Number casedata.length The number of failed cases
881 * @return Array casedata.cases An array of all the failed case objects
882 */
883 Tester.prototype.getFailures = function getFailures() {
884 "use strict";
885 return {
886 length: this.testResults.failed,
887 cases: this.testResults.failures
888 };
889 };
890
891 /**
892 * Retrieves current passed data and all passed cases.
893 *
894 * @return Object casedata An object containg information about cases
895 * @return Number casedata.length The number of passed cases
896 * @return Array casedata.cases An array of all the passed case objects
897 */
898 Tester.prototype.getPasses = function getPasses() {
899 "use strict";
900 return {
901 length: this.testResults.passed,
902 cases: this.testResults.passes
903 };
904 };
905
906 /**
907 * Retrieves the array where all the durations of failed tests are stored
908 *
909 * @return Array durations of failed tests
910 */
911 Tester.prototype.getFailuresTime = function getFailuresTime() {
912 "use strict";
913 return this.testResults.failuresTime;
914 }
915
916 /**
917 * Retrieves the array where all the durations of passed tests are stored
918 *
919 * @return Array durations of passed tests
920 */
921 Tester.prototype.getPassesTime = function getPassesTime() {
922 "use strict";
923 return this.testResults.passesTime;
924 }
925
926
927 /**
928 * Writes an info-style formatted message to stdout. 918 * Writes an info-style formatted message to stdout.
929 * 919 *
930 * @param String message 920 * @param String message
...@@ -935,7 +925,7 @@ Tester.prototype.info = function info(message) { ...@@ -935,7 +925,7 @@ Tester.prototype.info = function info(message) {
935 }; 925 };
936 926
937 /** 927 /**
938 * Adds a successful test entry to the stack. 928 * Adds a succesful test entry to the stack.
939 * 929 *
940 * @param String message 930 * @param String message
941 */ 931 */
...@@ -956,7 +946,13 @@ Tester.prototype.pass = function pass(message) { ...@@ -956,7 +946,13 @@ Tester.prototype.pass = function pass(message) {
956 */ 946 */
957 Tester.prototype.processAssertionResult = function processAssertionResult(result) { 947 Tester.prototype.processAssertionResult = function processAssertionResult(result) {
958 "use strict"; 948 "use strict";
959 var eventName= 'success', 949 if (!this.currentSuite) {
950 this.currentSuite = new TestSuiteResult({
951 name: "Untitled suite in " + this.currentTestFile,
952 file: this.currentTestFile
953 });
954 }
955 var eventName = 'success',
960 message = result.message || result.standard, 956 message = result.message || result.standard,
961 style = 'INFO', 957 style = 'INFO',
962 status = this.options.passText; 958 status = this.options.passText;
...@@ -964,10 +960,8 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result ...@@ -964,10 +960,8 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result
964 eventName = 'fail'; 960 eventName = 'fail';
965 style = 'RED_BAR'; 961 style = 'RED_BAR';
966 status = this.options.failText; 962 status = this.options.failText;
967 this.testResults.failed++;
968 } else {
969 this.testResults.passed++;
970 } 963 }
964 style = result.type === "dubious" ? "WARN_BAR" : style;
971 this.casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' ')); 965 this.casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' '));
972 this.emit(eventName, result); 966 this.emit(eventName, result);
973 if (this.options.failFast && !result.success) { 967 if (this.options.failFast && !result.success) {
...@@ -979,21 +973,22 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result ...@@ -979,21 +973,22 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result
979 /** 973 /**
980 * Renders a detailed report for each failed test. 974 * Renders a detailed report for each failed test.
981 * 975 *
982 * @param Array failures
983 */ 976 */
984 Tester.prototype.renderFailureDetails = function renderFailureDetails(failures) { 977 Tester.prototype.renderFailureDetails = function renderFailureDetails() {
985 "use strict"; 978 "use strict";
979 var failures = this.suites.getAllFailures();
986 if (failures.length === 0) { 980 if (failures.length === 0) {
987 return; 981 return;
988 } 982 }
989 this.casper.echo(f("\nDetails for the %d failed test%s:\n", failures.length, failures.length > 1 ? "s" : ""), "PARAMETER"); 983 this.casper.echo(f("\nDetails for the %d failed test%s:\n",
984 failures.length, failures.length > 1 ? "s" : ""), "PARAMETER");
990 failures.forEach(function _forEach(failure) { 985 failures.forEach(function _forEach(failure) {
991 var type, message, line; 986 this.casper.echo(f('In %s%s', failure.file, ~~failure.line ? ':' + ~~failure.line : ''));
992 type = failure.type || "unknown"; 987 if (failure.suite) {
993 line = ~~failure.line; 988 this.casper.echo(f(' %s', failure.suite), "PARAMETER");
994 message = failure.message; 989 }
995 this.casper.echo(f('In %s:%s', failure.file, line)); 990 this.casper.echo(f(' %s: %s', failure.type || "unknown",
996 this.casper.echo(f(' %s: %s', type, message || failure.standard || "(no message was entered)"), "COMMENT"); 991 failure.message || failure.standard || "(no message was entered)"), "COMMENT");
997 }.bind(this)); 992 }.bind(this));
998 }; 993 };
999 994
...@@ -1006,14 +1001,19 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { ...@@ -1006,14 +1001,19 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) {
1006 "use strict"; 1001 "use strict";
1007 /*jshint maxstatements:20*/ 1002 /*jshint maxstatements:20*/
1008 save = save || this.options.save; 1003 save = save || this.options.save;
1009 var total = this.testResults.passed + this.testResults.failed, statusText, style, result; 1004 var failed = this.suites.countFailed(),
1010 var exitStatus = ~~(status || (this.testResults.failed > 0 ? 1 : 0)); 1005 passed = this.suites.countPassed(),
1006 total = this.suites.countTotal(),
1007 statusText,
1008 style,
1009 result,
1010 exitStatus = ~~(status || (failed > 0 ? 1 : 0));
1011 if (total === 0) { 1011 if (total === 0) {
1012 statusText = this.options.warnText; 1012 statusText = this.options.warnText;
1013 style = 'WARN_BAR'; 1013 style = 'WARN_BAR';
1014 result = f("%s Looks like you didn't run any test.", statusText); 1014 result = f("%s Looks like you didn't run any test.", statusText);
1015 } else { 1015 } else {
1016 if (this.testResults.failed > 0) { 1016 if (failed > 0) {
1017 statusText = this.options.failText; 1017 statusText = this.options.failText;
1018 style = 'RED_BAR'; 1018 style = 'RED_BAR';
1019 } else { 1019 } else {
...@@ -1021,12 +1021,12 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { ...@@ -1021,12 +1021,12 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) {
1021 style = 'GREEN_BAR'; 1021 style = 'GREEN_BAR';
1022 } 1022 }
1023 result = f('%s %s tests executed in %ss, %d passed, %d failed.', 1023 result = f('%s %s tests executed in %ss, %d passed, %d failed.',
1024 statusText, total, utils.ms2seconds(this.calculateSuiteDuration()), 1024 statusText, total, utils.ms2seconds(this.suites.calculateDuration()),
1025 this.testResults.passed, this.testResults.failed); 1025 passed, failed);
1026 } 1026 }
1027 this.casper.echo(result, style, this.options.pad); 1027 this.casper.echo(result, style, this.options.pad);
1028 if (this.testResults.failed > 0) { 1028 if (failed > 0) {
1029 this.renderFailureDetails(this.testResults.failures); 1029 this.renderFailureDetails();
1030 } 1030 }
1031 if (save) { 1031 if (save) {
1032 this.saveResults(save); 1032 this.saveResults(save);
...@@ -1070,12 +1070,15 @@ Tester.prototype.runSuites = function runSuites() { ...@@ -1070,12 +1070,15 @@ Tester.prototype.runSuites = function runSuites() {
1070 }); 1070 });
1071 1071
1072 if (testFiles.length === 0) { 1072 if (testFiles.length === 0) {
1073 this.bar(f("No test file found in %s, aborting.", Array.prototype.slice.call(arguments)), "RED_BAR"); 1073 this.bar(f("No test file found in %s, aborting.",
1074 Array.prototype.slice.call(arguments)), "RED_BAR");
1074 this.casper.exit(1); 1075 this.casper.exit(1);
1075 } 1076 }
1077
1076 self.currentSuiteNum = 0; 1078 self.currentSuiteNum = 0;
1077 self.currentTestStartTime = new Date(); 1079 self.currentTestStartTime = new Date();
1078 self.lastAssertTime = 0; 1080 self.lastAssertTime = 0;
1081
1079 var interval = setInterval(function _check(self) { 1082 var interval = setInterval(function _check(self) {
1080 if (self.running) { 1083 if (self.running) {
1081 return; 1084 return;
...@@ -1083,14 +1086,10 @@ Tester.prototype.runSuites = function runSuites() { ...@@ -1083,14 +1086,10 @@ Tester.prototype.runSuites = function runSuites() {
1083 if (self.currentSuiteNum === testFiles.length || self.aborted) { 1086 if (self.currentSuiteNum === testFiles.length || self.aborted) {
1084 self.emit('tests.complete'); 1087 self.emit('tests.complete');
1085 clearInterval(interval); 1088 clearInterval(interval);
1086 self.exporter.setSuiteDuration(self.calculateSuiteDuration());
1087 self.aborted = false; 1089 self.aborted = false;
1088 } else { 1090 } else {
1089 self.runTest(testFiles[self.currentSuiteNum]); 1091 self.runTest(testFiles[self.currentSuiteNum]);
1090 self.exporter.setSuiteDuration(self.calculateSuiteDuration());
1091 self.currentSuiteNum++; 1092 self.currentSuiteNum++;
1092 self.passesTime = [];
1093 self.failuresTime = [];
1094 } 1093 }
1095 }, 100, this); 1094 }, 100, this);
1096 }; 1095 };
...@@ -1110,16 +1109,14 @@ Tester.prototype.runTest = function runTest(testFile) { ...@@ -1110,16 +1109,14 @@ Tester.prototype.runTest = function runTest(testFile) {
1110 /** 1109 /**
1111 * Saves results to file. 1110 * Saves results to file.
1112 * 1111 *
1113 * @param String filename Target file path. 1112 * @param String filename Target file path.
1114 */ 1113 */
1115 Tester.prototype.saveResults = function saveResults(filepath) { 1114 Tester.prototype.saveResults = function saveResults(filepath) {
1116 "use strict"; 1115 "use strict";
1117 // FIXME: looks like phantomjs has a pb with fs.isWritable https://groups.google.com/forum/#!topic/casperjs/hcUdwgGZOrU 1116 var exporter = require('xunit').create();
1118 // if (!fs.isWritable(filepath)) { 1117 exporter.setResults(this.suites);
1119 // throw new CasperError(f('Path %s is not writable.', filepath));
1120 // }
1121 try { 1118 try {
1122 fs.write(filepath, this.exporter.getXML(), 'w'); 1119 fs.write(filepath, exporter.getXML(), 'w');
1123 this.casper.echo(f('Result log stored in %s', filepath), 'INFO', 80); 1120 this.casper.echo(f('Result log stored in %s', filepath), 'INFO', 80);
1124 } catch (e) { 1121 } catch (e) {
1125 this.casper.echo(f('Unable to write results to %s: %s', filepath, e), 'ERROR', 80); 1122 this.casper.echo(f('Unable to write results to %s: %s', filepath, e), 'ERROR', 80);
...@@ -1145,17 +1142,185 @@ Tester.prototype.testEquals = Tester.prototype.testEqual = function testEquals(v ...@@ -1145,17 +1142,185 @@ Tester.prototype.testEquals = Tester.prototype.testEqual = function testEquals(v
1145 * @param Error|String error The error 1142 * @param Error|String error The error
1146 * @param String file Test file where the error occurred 1143 * @param String file Test file where the error occurred
1147 * @param Number line Line number (optional) 1144 * @param Number line Line number (optional)
1145 * @param Array backtrace Error stack trace (optional)
1148 */ 1146 */
1149 Tester.prototype.uncaughtError = function uncaughtError(error, file, line) { 1147 Tester.prototype.uncaughtError = function uncaughtError(error, file, line, backtrace) {
1150 "use strict"; 1148 "use strict";
1149 // XXX: this is NOT an assertion scratch that
1151 return this.processAssertionResult({ 1150 return this.processAssertionResult({
1152 success: false, 1151 success: false,
1153 type: "uncaughtError", 1152 type: "uncaughtError",
1154 file: file, 1153 file: file,
1155 line: ~~line || "unknown", 1154 line: ~~line,
1156 message: utils.isObject(error) ? error.message : error, 1155 message: utils.isObject(error) ? error.message : error,
1157 values: { 1156 values: {
1158 error: error 1157 error: error,
1158 stack: backtrace
1159 } 1159 }
1160 }); 1160 });
1161 }; 1161 };
1162
1163 /**
1164 * Test suites array.
1165 *
1166 */
1167 function TestSuite() {}
1168 TestSuite.prototype = [];
1169 exports.TestSuite = TestSuite;
1170
1171 /**
1172 * Returns the number of tests.
1173 *
1174 * @return Number
1175 */
1176 TestSuite.prototype.countTotal = function countTotal() {
1177 "use strict";
1178 return this.countPassed() + this.countFailed();
1179 };
1180
1181 /**
1182 * Returns the number of failed tests.
1183 *
1184 * @return Number
1185 */
1186 TestSuite.prototype.countFailed = function countFailed() {
1187 "use strict";
1188 return this.map(function(result) {
1189 return result.failed;
1190 }).reduce(function(a, b) {
1191 return a + b;
1192 }, 0);
1193 };
1194
1195 /**
1196 * Returns the number of succesful tests.
1197 *
1198 * @return Number
1199 */
1200 TestSuite.prototype.countPassed = function countPassed() {
1201 "use strict";
1202 return this.map(function(result) {
1203 return result.passed;
1204 }).reduce(function(a, b) {
1205 return a + b;
1206 }, 0);
1207 };
1208
1209 /**
1210 * Returns all failures from this suite.
1211 *
1212 * @return Array
1213 */
1214 TestSuite.prototype.getAllFailures = function getAllFailures() {
1215 "use strict";
1216 var failures = [];
1217 this.forEach(function(result) {
1218 failures = failures.concat(result.failures);
1219 });
1220 return failures;
1221 };
1222
1223 /**
1224 * Returns all succesful tests from this suite.
1225 *
1226 * @return Array
1227 */
1228 TestSuite.prototype.getAllPasses = function getAllPasses() {
1229 "use strict";
1230 var passes = [];
1231 this.forEach(function(result) {
1232 passes = passes.concat(result.passes);
1233 });
1234 return passes;
1235 };
1236
1237 /**
1238 * Returns all results from this suite.
1239 *
1240 * @return Array
1241 */
1242 TestSuite.prototype.getAllResults = function getAllResults() {
1243 "use strict";
1244 return this.getAllPasses().concat(this.getAllFailures());
1245 };
1246
1247 /**
1248 * Computes the sum of all durations of the tests which were executed in the
1249 * current suite.
1250 *
1251 * @return Number
1252 */
1253 TestSuite.prototype.calculateDuration = function calculateDuration() {
1254 "use strict";
1255 return this.getAllResults().map(function(result) {
1256 return result.time;
1257 }).reduce(function add(a, b) {
1258 return a + b;
1259 }, 0);
1260 };
1261
1262 /**
1263 * Test suite results object.
1264 *
1265 * @param Object options
1266 */
1267 function TestSuiteResult(options) {
1268 "use strict";
1269 this.name = options && options.name;
1270 this.file = options && options.file;
1271 this.assertions = 0;
1272 this.passed = 0;
1273 this.failed = 0;
1274 this.passes = [];
1275 this.failures = [];
1276 }
1277 exports.TestSuiteResult = TestSuiteResult;
1278
1279 /**
1280 * Adds a success record and its execution time to their associated stacks.
1281 *
1282 * @param Object success
1283 * @param Number time
1284 */
1285 TestSuiteResult.prototype.addSuccess = function addSuccess(success, time) {
1286 "use strict";
1287 success.suite = this.name;
1288 success.time = time;
1289 this.passes.push(success);
1290 this.assertions++;
1291 this.passed++;
1292 };
1293
1294 /**
1295 * Adds a failure record and its execution time to their associated stacks.
1296 *
1297 * @param Object failure
1298 * @param Number time
1299 */
1300 TestSuiteResult.prototype.addFailure = function addFailure(failure, time) {
1301 "use strict";
1302 failure.suite = this.name;
1303 failure.time = time;
1304 this.failures.push(failure);
1305 this.assertions++;
1306 this.failed++;
1307 };
1308
1309 /**
1310 * Computes total duration for this suite.
1311 *
1312 * @return Number
1313 */
1314 TestSuiteResult.prototype.calculateDuration = function calculateDuration() {
1315 "use strict";
1316 function add(a, b) {
1317 return a + b;
1318 }
1319 var passedTimes = this.passes.map(function(success) {
1320 return ~~success.time;
1321 }).reduce(add, 0);
1322 var failedTimes = this.failures.map(function(failure) {
1323 return ~~failure.time;
1324 }).reduce(add, 0);
1325 return passedTimes + failedTimes;
1326 };
......
...@@ -200,6 +200,43 @@ function format(f) { ...@@ -200,6 +200,43 @@ function format(f) {
200 exports.format = format; 200 exports.format = format;
201 201
202 /** 202 /**
203 * Formats a test value.
204 *
205 * @param Mixed value
206 * @return String
207 */
208 function formatTestValue(value, name) {
209 "use strict";
210 var formatted = '';
211 if (value instanceof Error) {
212 formatted += value.message + '\n';
213 if (value.stack) {
214 formatted += indent(value.stack, 12, '#');
215 }
216 } else if (name === 'stack') {
217 if (isArray(value)) {
218 formatted += value.map(function(entry) {
219 return format('in %s() in %s:%d', (entry.function || "anonymous"), entry.file, entry.line);
220 }).join('\n');
221 } else {
222 formatted += 'not provided';
223 }
224 } else {
225 try {
226 formatted += serialize(value);
227 } catch (e) {
228 try {
229 formatted += serialize(value.toString());
230 } catch (e2) {
231 formatted += '(unserializable value)';
232 }
233 }
234 }
235 return formatted;
236 }
237 exports.formatTestValue = formatTestValue;
238
239 /**
203 * Retrieves the value of an Object foreign property using a dot-separated 240 * Retrieves the value of an Object foreign property using a dot-separated
204 * path string. 241 * path string.
205 * 242 *
...@@ -226,6 +263,22 @@ function getPropertyPath(obj, path) { ...@@ -226,6 +263,22 @@ function getPropertyPath(obj, path) {
226 exports.getPropertyPath = getPropertyPath; 263 exports.getPropertyPath = getPropertyPath;
227 264
228 /** 265 /**
266 * Indents a string.
267 *
268 * @param String string
269 * @param Number nchars
270 * @param String prefix
271 * @return String
272 */
273 function indent(string, nchars, prefix) {
274 "use strict";
275 return string.split('\n').map(function(line) {
276 return (prefix || '') + new Array(nchars).join(' ') + line;
277 }).join('\n');
278 }
279 exports.indent = indent;
280
281 /**
229 * Inherit the prototype methods from one constructor into another. 282 * Inherit the prototype methods from one constructor into another.
230 * 283 *
231 * @param {function} ctor Constructor function which needs to inherit the 284 * @param {function} ctor Constructor function which needs to inherit the
......
...@@ -80,76 +80,75 @@ exports.create = function create() { ...@@ -80,76 +80,75 @@ exports.create = function create() {
80 */ 80 */
81 function XUnitExporter() { 81 function XUnitExporter() {
82 "use strict"; 82 "use strict";
83 this._xml = utils.node('testsuite'); 83 this.results = undefined;
84 this._xml = utils.node('testsuites');
84 this._xml.toString = function toString() { 85 this._xml.toString = function toString() {
85 return this.outerHTML; // ouch 86 return '<?xml version="1.0" encoding="UTF-8"?>' + this.outerHTML; // ouch
86 }; 87 };
87 } 88 }
88 exports.XUnitExporter = XUnitExporter; 89 exports.XUnitExporter = XUnitExporter;
89 90
90 /** 91 /**
91 * Adds a successful test result. 92 * Retrieves generated XML object - actually an HTMLElement.
92 *
93 * @param String classname
94 * @param String name
95 * @param Number duration Test duration in milliseconds
96 */
97 XUnitExporter.prototype.addSuccess = function addSuccess(classname, name, duration) {
98 "use strict";
99 var snode = utils.node('testcase', {
100 classname: generateClassName(classname),
101 name: name
102 });
103 if (duration !== undefined) {
104 snode.setAttribute('time', utils.ms2seconds(duration));
105 }
106 this._xml.appendChild(snode);
107 };
108
109 /**
110 * Adds a failed test result.
111 * 93 *
112 * @param String classname 94 * @return HTMLElement
113 * @param String name
114 * @param String message
115 * @param String type
116 * @param Number duration Test duration in milliseconds
117 */ 95 */
118 XUnitExporter.prototype.addFailure = function addFailure(classname, name, message, type, duration) { 96 XUnitExporter.prototype.getXML = function getXML() {
119 "use strict"; 97 "use strict";
120 var fnode = utils.node('testcase', { 98 if (!(this.results instanceof require('tester').TestSuite)) {
121 classname: generateClassName(classname), 99 throw new CasperError('Results not set, cannot get XML.');
122 name: name
123 });
124 if (duration !== undefined) {
125 fnode.setAttribute('time', utils.ms2seconds(duration));
126 } 100 }
127 var failure = utils.node('failure', { 101 this.results.forEach(function(result) {
128 type: type || "unknown" 102 var suiteNode = utils.node('testsuite', {
129 }); 103 name: result.name,
130 failure.appendChild(document.createTextNode(message || "no message left")); 104 tests: result.assertions,
131 fnode.appendChild(failure); 105 failures: result.failed,
132 this._xml.appendChild(fnode); 106 time: utils.ms2seconds(result.calculateDuration()),
107 'package': generateClassName(result.file),
108 });
109 result.passes.forEach(function(success) {
110 var testCase = utils.node('testcase', {
111 name: success.message || success.standard,
112 classname: generateClassName(success.file),
113 time: utils.ms2seconds(~~success.time)
114 });
115 suiteNode.appendChild(testCase);
116 });
117 result.failures.forEach(function(failure) {
118 var testCase = utils.node('testcase', {
119 name: failure.message || failure.standard,
120 classname: generateClassName(failure.file),
121 time: utils.ms2seconds(~~failure.time)
122 });
123 var failureNode = utils.node('failure', {
124 type: failure.type || "failure"
125 });
126 failureNode.appendChild(document.createTextNode(failure.message || "no message left"));
127 if (failure.values && failure.values.error instanceof Error) {
128 var errorNode = utils.node('error', {
129 type: utils.betterTypeOf(failure.values.error)
130 });
131 errorNode.appendChild(document.createTextNode(failure.values.error.stack));
132 testCase.appendChild(errorNode);
133 }
134 testCase.appendChild(failureNode);
135 suiteNode.appendChild(testCase);
136 });
137 this._xml.appendChild(suiteNode);
138 }.bind(this));
139 this._xml.setAttribute('duration', utils.ms2seconds(this.results.calculateDuration()));
140 return this._xml;
133 }; 141 };
134 142
135 /** 143 /**
136 * Adds test suite duration 144 * Sets test results.
137 * 145 *
138 * @param Number duration Test duration in milliseconds 146 * @param TestSuite results
139 */ 147 */
140 XUnitExporter.prototype.setSuiteDuration = function setSuiteDuration(duration) { 148 XUnitExporter.prototype.setResults = function setResults(results) {
141 "use strict"; 149 "use strict";
142 if (!isNaN(duration)) { 150 if (!(results instanceof require('tester').TestSuite)) {
143 this._xml.setAttribute("time", utils.ms2seconds(duration)); 151 throw new CasperError('Invalid results type.');
144 } 152 }
145 }; 153 return this.results = results;
146
147 /**
148 * Retrieves generated XML object - actually an HTMLElement.
149 *
150 * @return HTMLElement
151 */
152 XUnitExporter.prototype.getXML = function getXML() {
153 "use strict";
154 return this._xml;
155 }; 154 };
......
...@@ -200,27 +200,6 @@ casper.reload(function() { ...@@ -200,27 +200,6 @@ casper.reload(function() {
200 t.assertField('checklist[]', [], 'Tester.assertField() works as expected with check lists'); 200 t.assertField('checklist[]', [], 'Tester.assertField() works as expected with check lists');
201 }); 201 });
202 202
203 casper.then(function() {
204 t.comment('Tester.getFailures()');
205 t.assertEquals(typeof t.getFailures().length, "number", "Tester.getFailures() works as expected");
206
207 var passCount = t.getPasses().length;
208 t.comment('Tester.getPasses()');
209 t.assertEquals(1, 1, "Rogue assertEquals pass case");
210 t.assertEquals(t.getPasses().length, passCount + 1, "Tester.getPasses() works as expected");
211 });
212
213 casper.then(function() {
214 t.comment('Tester.calculateSuiteDuration()');
215 function add(a, b) {
216 return a + b;
217 }
218 var passedTime = t.getPassesTime().reduce(add, 0),
219 failedTime = t.getFailuresTime().reduce(add, 0),
220 calculatedSum = t.calculateSuiteDuration();
221 t.assertEquals(calculatedSum, passedTime + failedTime, "Tester.calculateSuiteDuration() works as expected")
222 });
223
224 casper.run(function() { 203 casper.run(function() {
225 t.done(59); 204 t.done(55);
226 }); 205 });
......
...@@ -4,8 +4,7 @@ var utils = require('utils'), ...@@ -4,8 +4,7 @@ var utils = require('utils'),
4 t = casper.test, 4 t = casper.test,
5 x = require('casper').selectXPath; 5 x = require('casper').selectXPath;
6 6
7 t.comment('betterTypeOf()'); 7 casper.test.begin('utils.betterTypeOf() tests', function() {
8 (function() {
9 var testCases = [ 8 var testCases = [
10 {subject: 1, expected: 'number'}, 9 {subject: 1, expected: 'number'},
11 {subject: '1', expected: 'string'}, 10 {subject: '1', expected: 'string'},
...@@ -19,13 +18,13 @@ t.comment('betterTypeOf()'); ...@@ -19,13 +18,13 @@ t.comment('betterTypeOf()');
19 {subject: new RegExp(), expected: 'regexp'} 18 {subject: new RegExp(), expected: 'regexp'}
20 ]; 19 ];
21 testCases.forEach(function(testCase) { 20 testCases.forEach(function(testCase) {
22 t.assertEquals(utils.betterTypeOf(testCase.subject), testCase.expected, 21 this.assertEquals(utils.betterTypeOf(testCase.subject), testCase.expected,
23 require('utils').format('betterTypeOf() detects expected type "%s"', testCase.subject)); 22 utils.format('betterTypeOf() detects expected type "%s"', testCase.expected));
24 }); 23 }.bind(this));
25 })(); 24 this.done(testCases.length);
25 });
26 26
27 t.comment('cleanUrl()'); 27 casper.test.begin('utils.cleanUrl() tests', function() {
28 (function() {
29 var testCases = { 28 var testCases = {
30 'http://google.com/': 'http://google.com/', 29 'http://google.com/': 'http://google.com/',
31 'http://google.com': 'http://google.com/', 30 'http://google.com': 'http://google.com/',
...@@ -39,47 +38,47 @@ t.comment('cleanUrl()'); ...@@ -39,47 +38,47 @@ t.comment('cleanUrl()');
39 '/100': '/100' 38 '/100': '/100'
40 }; 39 };
41 for (var testCase in testCases) { 40 for (var testCase in testCases) {
42 t.assertEquals(utils.cleanUrl(testCase), testCases[testCase], 'cleanUrl() cleans an URL'); 41 this.assertEquals(utils.cleanUrl(testCase), testCases[testCase], 'cleanUrl() cleans an URL');
43 } 42 }
44 })(); 43 this.done(Object.keys(testCases).length);
44 });
45 45
46 t.comment('clone()'); 46 casper.test.begin('utils.clone() tests', function() {
47 (function() {
48 var a = {a: 1, b: 2, c: [1, 2]}; 47 var a = {a: 1, b: 2, c: [1, 2]};
49 t.assertEquals(utils.clone(a), a); 48 this.assertEquals(utils.clone(a), a);
50 var b = [1, 2, 3, a]; 49 var b = [1, 2, 3, a];
51 t.assertEquals(utils.clone(b), b); 50 this.assertEquals(utils.clone(b), b);
52 })(); 51 this.done(2);
52 });
53 53
54 t.comment('equals()'); 54 casper.test.begin('equals() tests', function() {
55 (function() { 55 this.assert(utils.equals(null, null), 'equals() null equality');
56 t.assert(utils.equals(null, null), 'equals() null equality'); 56 this.assertNot(utils.equals(null, undefined), 'equals() null vs. undefined inequality');
57 t.assertNot(utils.equals(null, undefined), 'equals() null vs. undefined inequality'); 57 this.assert(utils.equals("hi", "hi"), 'equals() string equality');
58 t.assert(utils.equals("hi", "hi"), 'equals() string equality'); 58 this.assertNot(utils.equals("hi", "ih"), 'equals() string inequality');
59 t.assertNot(utils.equals("hi", "ih"), 'equals() string inequality'); 59 this.assert(utils.equals(5, 5), 'equals() number equality');
60 t.assert(utils.equals(5, 5), 'equals() number equality'); 60 this.assertNot(utils.equals("5", 5), 'equals() number equality without implicit cast');
61 t.assertNot(utils.equals("5", 5), 'equals() number equality without implicit cast'); 61 this.assert(utils.equals(5, 5.0), 'equals() number equality with cast');
62 t.assert(utils.equals(5, 5.0), 'equals() number equality with cast'); 62 this.assertNot(utils.equals(5, 10), 'equals() number inequality');
63 t.assertNot(utils.equals(5, 10), 'equals() number inequality'); 63 this.assert(utils.equals([], []), 'equals() empty array equality');
64 t.assert(utils.equals([], []), 'equals() empty array equality'); 64 this.assert(utils.equals([1,2], [1,2]), 'equals() array equality');
65 t.assert(utils.equals([1,2], [1,2]), 'equals() array equality'); 65 this.assert(utils.equals([1,2,[1,2,function(){}]], [1,2,[1,2,function(){}]]), 'equals() complex array equality');
66 t.assert(utils.equals([1,2,[1,2,function(){}]], [1,2,[1,2,function(){}]]), 'equals() complex array equality'); 66 this.assertNot(utils.equals([1,2,[1,2,function(a){}]], [1,2,[1,2,function(b){}]]), 'equals() complex array inequality');
67 t.assertNot(utils.equals([1,2,[1,2,function(a){}]], [1,2,[1,2,function(b){}]]), 'equals() complex array inequality'); 67 this.assertNot(utils.equals([1,2], [2,1]), 'equals() shuffled array inequality');
68 t.assertNot(utils.equals([1,2], [2,1]), 'equals() shuffled array inequality'); 68 this.assertNot(utils.equals([1,2], [1,2,3]), 'equals() array length inequality');
69 t.assertNot(utils.equals([1,2], [1,2,3]), 'equals() array length inequality'); 69 this.assert(utils.equals({}, {}), 'equals() empty object equality');
70 t.assert(utils.equals({}, {}), 'equals() empty object equality'); 70 this.assert(utils.equals({a:1,b:2}, {a:1,b:2}), 'equals() object length equality');
71 t.assert(utils.equals({a:1,b:2}, {a:1,b:2}), 'equals() object length equality'); 71 this.assert(utils.equals({a:1,b:2}, {b:2,a:1}), 'equals() shuffled object keys equality');
72 t.assert(utils.equals({a:1,b:2}, {b:2,a:1}), 'equals() shuffled object keys equality'); 72 this.assertNot(utils.equals({a:1,b:2}, {a:1,b:3}), 'equals() object inequality');
73 t.assertNot(utils.equals({a:1,b:2}, {a:1,b:3}), 'equals() object inequality'); 73 this.assert(utils.equals({1:{name:"bob",age:28}, 2:{name:"john",age:26}}, {1:{name:"bob",age:28}, 2:{name:"john",age:26}}), 'equals() complex object equality');
74 t.assert(utils.equals({1:{name:"bob",age:28}, 2:{name:"john",age:26}}, {1:{name:"bob",age:28}, 2:{name:"john",age:26}}), 'equals() complex object equality'); 74 this.assertNot(utils.equals({1:{name:"bob",age:28}, 2:{name:"john",age:26}}, {1:{name:"bob",age:28}, 2:{name:"john",age:27}}), 'equals() complex object inequality');
75 t.assertNot(utils.equals({1:{name:"bob",age:28}, 2:{name:"john",age:26}}, {1:{name:"bob",age:28}, 2:{name:"john",age:27}}), 'equals() complex object inequality'); 75 this.assert(utils.equals(function(x){return x;}, function(x){return x;}), 'equals() function equality');
76 t.assert(utils.equals(function(x){return x;}, function(x){return x;}), 'equals() function equality'); 76 this.assertNot(utils.equals(function(x){return x;}, function(y){return y+2;}), 'equals() function inequality');
77 t.assertNot(utils.equals(function(x){return x;}, function(y){return y+2;}), 'equals() function inequality'); 77 this.assert(utils.equals([{a:1, b:2}, {c:3, d:4}], [{a:1, b:2}, {c:3, d:4}]), 'equals() arrays of objects');
78 t.assert(utils.equals([{a:1, b:2}, {c:3, d:4}], [{a:1, b:2}, {c:3, d:4}]), 'equals() arrays of objects'); 78 this.done(23);
79 })(); 79 });
80 80
81 t.comment('fileExt()'); 81 casper.test.begin('fileExt() tests', function() {
82 (function() {
83 var testCases = { 82 var testCases = {
84 'foo.ext': 'ext', 83 'foo.ext': 'ext',
85 'FOO.EXT': 'ext', 84 'FOO.EXT': 'ext',
...@@ -88,27 +87,25 @@ t.comment('fileExt()'); ...@@ -88,27 +87,25 @@ t.comment('fileExt()');
88 'toto.': '', 87 'toto.': '',
89 ' plop.ext ': 'ext' 88 ' plop.ext ': 'ext'
90 }; 89 };
91
92 for (var testCase in testCases) { 90 for (var testCase in testCases) {
93 t.assertEquals(utils.fileExt(testCase), testCases[testCase], 'fileExt() extract file extension'); 91 this.assertEquals(utils.fileExt(testCase), testCases[testCase], 'fileExt() extract file extension');
94 } 92 }
95 })(); 93 this.done(Object.keys(testCases).length);
94 });
96 95
97 t.comment('fillBlanks()'); 96 casper.test.begin('fillBlanks() tests', function() {
98 (function() {
99 var testCases = { 97 var testCases = {
100 'foo': 'foo ', 98 'foo': 'foo ',
101 ' foo bar ': ' foo bar ', 99 ' foo bar ': ' foo bar ',
102 ' foo bar ': ' foo bar ' 100 ' foo bar ': ' foo bar '
103 }; 101 };
104
105 for (var testCase in testCases) { 102 for (var testCase in testCases) {
106 t.assertEquals(utils.fillBlanks(testCase, 10), testCases[testCase], 'fillBlanks() fills blanks'); 103 this.assertEquals(utils.fillBlanks(testCase, 10), testCases[testCase], 'fillBlanks() fills blanks');
107 } 104 }
108 })(); 105 this.done(Object.keys(testCases).length);
106 });
109 107
110 t.comment('getPropertyPath()'); 108 casper.test.begin('getPropertyPath() tests', function() {
111 (function() {
112 var testCases = [ 109 var testCases = [
113 { 110 {
114 input: utils.getPropertyPath({}, 'a.b.c'), 111 input: utils.getPropertyPath({}, 'a.b.c'),
...@@ -140,19 +137,19 @@ t.comment('getPropertyPath()'); ...@@ -140,19 +137,19 @@ t.comment('getPropertyPath()');
140 } 137 }
141 ]; 138 ];
142 testCases.forEach(function(testCase) { 139 testCases.forEach(function(testCase) {
143 t.assertEquals(testCase.input, testCase.output, 'getPropertyPath() gets a property using a path'); 140 this.assertEquals(testCase.input, testCase.output, 'getPropertyPath() gets a property using a path');
144 }); 141 }.bind(this));
145 })(); 142 this.done(testCases.length);
143 });
146 144
147 t.comment('isArray()'); 145 casper.test.begin('isArray() tests', function() {
148 (function() { 146 this.assertEquals(utils.isArray([]), true, 'isArray() checks for an Array');
149 t.assertEquals(utils.isArray([]), true, 'isArray() checks for an Array'); 147 this.assertEquals(utils.isArray({}), false, 'isArray() checks for an Array');
150 t.assertEquals(utils.isArray({}), false, 'isArray() checks for an Array'); 148 this.assertEquals(utils.isArray("foo"), false, 'isArray() checks for an Array');
151 t.assertEquals(utils.isArray("foo"), false, 'isArray() checks for an Array'); 149 this.done(3);
152 })(); 150 });
153 151
154 t.comment('isClipRect()'); 152 casper.test.begin('isClipRect() tests', function() {
155 (function() {
156 var testCases = [ 153 var testCases = [
157 [{}, false], 154 [{}, false],
158 [{top: 2}, false], 155 [{top: 2}, false],
...@@ -160,14 +157,13 @@ t.comment('isClipRect()'); ...@@ -160,14 +157,13 @@ t.comment('isClipRect()');
160 [{top: 2, left: 2, height: 2, width: 2}, true], 157 [{top: 2, left: 2, height: 2, width: 2}, true],
161 [{top: 2, left: 2, width: 2, height: new Date()}, false] 158 [{top: 2, left: 2, width: 2, height: new Date()}, false]
162 ]; 159 ];
163
164 testCases.forEach(function(testCase) { 160 testCases.forEach(function(testCase) {
165 t.assertEquals(utils.isClipRect(testCase[0]), testCase[1], 'isClipRect() checks for a ClipRect'); 161 this.assertEquals(utils.isClipRect(testCase[0]), testCase[1], 'isClipRect() checks for a ClipRect');
166 }); 162 }.bind(this));
167 })(); 163 this.done(testCases.length);
164 });
168 165
169 t.comment('isHTTPResource()'); 166 casper.test.begin('isHTTPResource() tests', function() {
170 (function() {
171 var testCases = [ 167 var testCases = [
172 [{}, false], 168 [{}, false],
173 [{url: 'file:///var/www/i.html'}, false], 169 [{url: 'file:///var/www/i.html'}, false],
...@@ -176,26 +172,25 @@ t.comment('isHTTPResource()'); ...@@ -176,26 +172,25 @@ t.comment('isHTTPResource()');
176 [{url: 'HTTP://plop.com/'}, true], 172 [{url: 'HTTP://plop.com/'}, true],
177 [{url: 'https://plop.com/'}, true] 173 [{url: 'https://plop.com/'}, true]
178 ]; 174 ];
179
180 testCases.forEach(function(testCase) { 175 testCases.forEach(function(testCase) {
181 t.assertEquals(utils.isHTTPResource(testCase[0]), testCase[1], 'isHTTPResource() checks for an HTTP resource'); 176 this.assertEquals(utils.isHTTPResource(testCase[0]), testCase[1], 'isHTTPResource() checks for an HTTP resource');
182 }); 177 }.bind(this));
183 })(); 178 this.done(Object.keys(testCases).length);
179 });
184 180
185 t.comment('isObject()'); 181 casper.test.begin('isObject() tests', function() {
186 (function() { 182 this.assertEquals(utils.isObject({}), true, 'isObject() checks for an Object');
187 t.assertEquals(utils.isObject({}), true, 'isObject() checks for an Object'); 183 this.assertEquals(utils.isObject([]), true, 'isObject() checks for an Object');
188 t.assertEquals(utils.isObject([]), true, 'isObject() checks for an Object'); 184 this.assertEquals(utils.isObject(1), false, 'isObject() checks for an Object');
189 t.assertEquals(utils.isObject(1), false, 'isObject() checks for an Object'); 185 this.assertEquals(utils.isObject("1"), false, 'isObject() checks for an Object');
190 t.assertEquals(utils.isObject("1"), false, 'isObject() checks for an Object'); 186 this.assertEquals(utils.isObject(function(){}), false, 'isObject() checks for an Object');
191 t.assertEquals(utils.isObject(function(){}), false, 'isObject() checks for an Object'); 187 this.assertEquals(utils.isObject(new Function('return {};')()), true, 'isObject() checks for an Object');
192 t.assertEquals(utils.isObject(new Function('return {};')()), true, 'isObject() checks for an Object'); 188 this.assertEquals(utils.isObject(require('webpage').create()), true, 'isObject() checks for an Object');
193 t.assertEquals(utils.isObject(require('webpage').create()), true, 'isObject() checks for an Object'); 189 this.assertEquals(utils.isObject(null), false, 'isObject() checks for an Object');
194 t.assertEquals(utils.isObject(null), false, 'isObject() checks for an Object'); 190 this.done(8);
195 })(); 191 });
196 192
197 t.comment('isValidSelector()'); 193 casper.test.begin('isValidSelector() tests', function() {
198 (function() {
199 t.assertEquals(utils.isValidSelector({}), false, 'isValidSelector() checks for a valid selector'); 194 t.assertEquals(utils.isValidSelector({}), false, 'isValidSelector() checks for a valid selector');
200 t.assertEquals(utils.isValidSelector(""), false, 'isValidSelector() checks for a valid selector'); 195 t.assertEquals(utils.isValidSelector(""), false, 'isValidSelector() checks for a valid selector');
201 t.assertEquals(utils.isValidSelector("a"), true, 'isValidSelector() checks for a valid selector'); 196 t.assertEquals(utils.isValidSelector("a"), true, 'isValidSelector() checks for a valid selector');
...@@ -219,18 +214,18 @@ t.comment('isValidSelector()'); ...@@ -219,18 +214,18 @@ t.comment('isValidSelector()');
219 type: "css3", 214 type: "css3",
220 path: "a" 215 path: "a"
221 }), false, 'isValidSelector() checks for a valid selector'); 216 }), false, 'isValidSelector() checks for a valid selector');
222 })(); 217 this.done(10);
218 });
223 219
224 t.comment('isWebPage()'); 220 casper.test.begin('isWebPage() tests', function() {
225 (function() {
226 var pageModule = require('webpage'); 221 var pageModule = require('webpage');
227 t.assertEquals(utils.isWebPage(pageModule), false, 'isWebPage() checks for a WebPage instance'); 222 this.assertEquals(utils.isWebPage(pageModule), false, 'isWebPage() checks for a WebPage instance');
228 t.assertEquals(utils.isWebPage(pageModule.create()), true, 'isWebPage() checks for a WebPage instance'); 223 this.assertEquals(utils.isWebPage(pageModule.create()), true, 'isWebPage() checks for a WebPage instance');
229 t.assertEquals(utils.isWebPage(null), false, 'isWebPage() checks for a WebPage instance'); 224 this.assertEquals(utils.isWebPage(null), false, 'isWebPage() checks for a WebPage instance');
230 })(); 225 this.done(3);
226 });
231 227
232 t.comment('isJsFile()'); 228 casper.test.begin('isJsFile() tests', function() {
233 (function() {
234 var testCases = { 229 var testCases = {
235 '': false, 230 '': false,
236 'toto.png': false, 231 'toto.png': false,
...@@ -238,14 +233,13 @@ t.comment('isJsFile()'); ...@@ -238,14 +233,13 @@ t.comment('isJsFile()');
238 'gniii.coffee': true, 233 'gniii.coffee': true,
239 'script.js': true 234 'script.js': true
240 }; 235 };
241
242 for (var testCase in testCases) { 236 for (var testCase in testCases) {
243 t.assertEquals(utils.isJsFile(testCase), testCases[testCase], 'isJsFile() checks for js file'); 237 this.assertEquals(utils.isJsFile(testCase), testCases[testCase], 'isJsFile() checks for js file');
244 } 238 }
245 })(); 239 this.done(Object.keys(testCases).length);
240 });
246 241
247 t.comment('mergeObjects()'); 242 casper.test.begin('mergeObjects() tests', function() {
248 (function() {
249 var testCases = [ 243 var testCases = [
250 { 244 {
251 obj1: {a: 1}, obj2: {b: 2}, merged: {a: 1, b: 2} 245 obj1: {a: 1}, obj2: {b: 2}, merged: {a: 1, b: 2}
...@@ -269,20 +263,19 @@ t.comment('mergeObjects()'); ...@@ -269,20 +263,19 @@ t.comment('mergeObjects()');
269 } 263 }
270 } 264 }
271 ]; 265 ];
272
273 testCases.forEach(function(testCase) { 266 testCases.forEach(function(testCase) {
274 t.assertEquals(utils.mergeObjects(testCase.obj1, testCase.obj2), testCase.merged, 'mergeObjects() can merge objects'); 267 this.assertEquals(utils.mergeObjects(testCase.obj1, testCase.obj2), testCase.merged, 'mergeObjects() can merge objects');
275 }); 268 }.bind(this));
276 })(); 269 this.done(testCases.length);
270 });
277 271
278 t.comment('objectValues()'); 272 casper.test.begin('objectValues() tests', function() {
279 (function() { 273 this.assertEquals(utils.objectValues({}), [], 'objectValues() can extract object values');
280 t.assertEquals(utils.objectValues({}), [], 'objectValues() can extract object values'); 274 this.assertEquals(utils.objectValues({a: 1, b: 2}), [1, 2], 'objectValues() can extract object values');
281 t.assertEquals(utils.objectValues({a: 1, b: 2}), [1, 2], 'objectValues() can extract object values'); 275 this.done(2);
282 })(); 276 });
283 277
284 t.comment('unique()'); 278 casper.test.begin('unique() tests', function() {
285 (function() {
286 var testCases = [ 279 var testCases = [
287 { 280 {
288 input: [1,2,3], 281 input: [1,2,3],
...@@ -302,8 +295,7 @@ t.comment('unique()'); ...@@ -302,8 +295,7 @@ t.comment('unique()');
302 } 295 }
303 ]; 296 ];
304 testCases.forEach(function(testCase) { 297 testCases.forEach(function(testCase) {
305 t.assertEquals(utils.unique(testCase.input), testCase.output, 'unique() computes unique values of an array'); 298 this.assertEquals(utils.unique(testCase.input), testCase.output, 'unique() computes unique values of an array');
306 }); 299 }.bind(this));
307 })(); 300 this.done(testCases.length);
308 301 });
309 t.done(112);
......
1 /*global casper*/ 1 /*global casper*/
2 /*jshint strict:false*/ 2 /*jshint strict:false*/
3 casper.test.comment('phantom.Casper.XUnitExporter'); 3 var tester = require('tester');
4 var testpage = require('webpage').create();
4 5
5 var xunit = require('xunit').create(); 6 casper.test.begin('XUnitReporter() initialization', function suite() {
6 xunit.addSuccess('foo', 'bar'); 7 var xunit = require('xunit').create();
7 casper.test.assertMatch(xunit.getXML(), 8 var results = new tester.TestSuite();
8 /<testcase classname="foo" name="bar"/, 9 xunit.setResults(results);
9 'XUnitExporter.addSuccess() adds a successful testcase'); 10 this.assertTruthy(xunit.getXML());
10 xunit.addFailure('bar', 'baz', 'wrong', 'chucknorriz'); 11 this.done(1);
11 casper.test.assertMatch(xunit.getXML(), 12 });
12 /<testcase classname="bar" name="baz"><failure type="chucknorriz">wrong/,
13 'XUnitExporter.addFailure() adds a failed testcase');
14 13
15 // named classname 14 casper.test.begin('XUnitReporter() can hold test suites', function suite() {
16 xunit = require('xunit').create(); 15 var xunit = require('xunit').create();
17 xunit.addSuccess(require('fs').workingDirectory + '/plop.js', 'It worked'); 16 var results = new tester.TestSuite();
18 casper.test.assertMatch(xunit.getXML(), 17 var suite1 = new tester.TestSuiteResult({
19 /<testcase classname="(.*)plop" name="It worked"/, 18 name: 'foo',
20 'XUnitExporter.addSuccess() handles class name'); 19 file: '/foo'
21 xunit.addSuccess(require('fs').workingDirectory + '/plip.js', 'Failure'); 20 });
22 casper.test.assertMatch(xunit.getXML(), 21 results.push(suite1);
23 /<testcase classname="(.*)plip" name="Failure"/, 22 var suite2 = new tester.TestSuiteResult({
24 'XUnitExporter.addFailure() handles class name'); 23 name: 'bar',
24 file: '/bar'
25 });
26 results.push(suite2);
27 xunit.setResults(results);
28 casper.start().setContent(xunit.getXML());
29 this.assertEvalEquals(function() {
30 return __utils__.findAll('testsuite').length;
31 }, 2);
32 this.assertExists('testsuites[duration]');
33 this.assertExists('testsuite[name="foo"][package="foo"]');
34 this.assertExists('testsuite[name="bar"][package="bar"]');
35 this.done(4);
36 });
25 37
26 // named with time 38 casper.test.begin('XUnitReporter() can hold a suite with a succesful test', function suite() {
27 xunit = require('xunit').create(); 39 var xunit = require('xunit').create();
28 xunit.addSuccess('foo', 'It worked', 1024); 40 var results = new tester.TestSuite();
29 casper.test.assertMatch(xunit.getXML(), 41 var suite1 = new tester.TestSuiteResult({
30 /<testcase classname="foo" name="It worked" time="(.*)"/, 42 name: 'foo',
31 'XUnitExporter.addSuccess() writes duration of test'); 43 file: '/foo'
32 xunit.addFailure('bar', 'baz', 'wrong', 'chucknorriz', 1024); 44 });
33 casper.test.assertMatch(xunit.getXML(), 45 suite1.addSuccess({
34 /<testcase classname="bar" name="baz" time="(.*)"><failure type="chucknorriz">wrong/, 46 success: true,
35 'XUnitExporter.addFailure() adds a failed testcase with duration'); 47 type: "footype",
48 message: "footext",
49 file: "/foo"
50 });
51 results.push(suite1);
52 xunit.setResults(results);
53 casper.start().setContent(xunit.getXML());
54 this.assertExists('testsuite[name="foo"][package="foo"][tests="1"][failures="0"] testcase[name="footext"]');
55 casper.test.done(1);
56 });
36 57
37 // named with time 58 casper.test.begin('XUnitReporter() can handle a failed test', function suite() {
38 xunit = require('xunit').create(); 59 var xunit = require('xunit').create();
39 casper.test.assertMatch(xunit.getXML(), 60 var results = new tester.TestSuite();
40 /<testsuite>/, 61 var suite1 = new tester.TestSuiteResult({
41 'XUnitExporter.create() created <testsuite> without time'); 62 name: 'foo',
42 xunit.setSuiteDuration(1024); 63 file: '/foo'
43 casper.test.assertMatch(xunit.getXML(), 64 });
44 /<testsuite time="1.024">/, 65 suite1.addFailure({
45 'XUnitExporter.setSuiteDuration() sets time in seconds'); 66 success: false,
46 67 type: "footype",
47 casper.test.done(8); 68 message: "footext",
69 file: "/foo"
70 });
71 results.push(suite1);
72 xunit.setResults(results);
73 casper.start().setContent(xunit.getXML());
74 this.assertExists('testsuite[name="foo"][package="foo"][tests="1"][failures="1"] testcase[name="footext"] failure[type="footype"]');
75 this.assertEquals(casper.getElementInfo('failure[type="footype"]').text, 'footext');
76 casper.test.done(2);
77 });
......