Commit 484639be 484639be1e1e8f45c136d82a53b80e9adf2b97b9 by Nicolas Perriault

Initial attempt at bringing support for test cases - refs #56

1 parent a2ebe03a
...@@ -4,6 +4,7 @@ CasperJS Changelog ...@@ -4,6 +4,7 @@ CasperJS Changelog
4 XXXX-XX-XX, v1.0.0 4 XXXX-XX-XX, v1.0.0
5 ------------------ 5 ------------------
6 6
7 - merged PR [#322](https://github.com/n1k0/casperjs/pull/322) - Support number in withFrame()
7 - fixed [#323](https://github.com/n1k0/casperjs/issues/323) - `thenEvaluate()` should be updated to take the same parameters as `evaluate()`, while maintaining backwards compatibility. 8 - fixed [#323](https://github.com/n1k0/casperjs/issues/323) - `thenEvaluate()` should be updated to take the same parameters as `evaluate()`, while maintaining backwards compatibility.
8 - merged PR [#319](https://github.com/n1k0/casperjs/pull/319), fixed [#209](https://github.com/n1k0/casperjs/issues/209) - test duration has been added to XUnit XML result file. 9 - merged PR [#319](https://github.com/n1k0/casperjs/pull/319), fixed [#209](https://github.com/n1k0/casperjs/issues/209) - test duration has been added to XUnit XML result file.
9 - `Casper.userAgent()` does not require the instance to be started anymore 10 - `Casper.userAgent()` does not require the instance to be started anymore
......
...@@ -5,17 +5,18 @@ You can check out the [contribution graphs on github](https://github.com/n1k0/ca ...@@ -5,17 +5,18 @@ You can check out the [contribution graphs on github](https://github.com/n1k0/ca
5 ``` 5 ```
6 $ git shortlog -s -n | cut -c8- 6 $ git shortlog -s -n | cut -c8-
7 Nicolas Perriault 7 Nicolas Perriault
8 oncletom
9 Brikou CARRE 8 Brikou CARRE
9 oncletom
10 hannyu 10 hannyu
11 Chris Lorenzo 11 Chris Lorenzo
12 Victor Yap 12 Victor Yap
13 nrabinowitz 13 nrabinowitz
14 pborreli 14 pborreli
15 Dave Lee 15 Rob Barreca
16 Andrew Childs
16 Solomon White 17 Solomon White
17 reina.sweet 18 reina.sweet
18 Andrew Childs 19 Dave Lee
19 Reina Sweet 20 Reina Sweet
20 Elmar Langholz 21 Elmar Langholz
21 Jason Funk 22 Jason Funk
...@@ -24,7 +25,6 @@ Julien Moulin ...@@ -24,7 +25,6 @@ Julien Moulin
24 Michael Geers 25 Michael Geers
25 Jan Schaumann 26 Jan Schaumann
26 Clochix 27 Clochix
27 Rafael Garcia
28 Raphaël Benitte 28 Raphaël Benitte
29 Tim Bunce 29 Tim Bunce
30 alfetopito 30 alfetopito
...@@ -49,4 +49,5 @@ Mehdi Kabab ...@@ -49,4 +49,5 @@ Mehdi Kabab
49 Mikko Peltonen 49 Mikko Peltonen
50 Pascal Borreli 50 Pascal Borreli
51 Rafael 51 Rafael
52 Rafael Garcia
52 ``` 53 ```
......
1 Subproject commit b0f51d643a839fb66c174086c426b9c7b49e2a80 1 Subproject commit 0f6c923dbfe9bb605adfa5c9d6c8cd3ac1d56586
......
...@@ -1372,6 +1372,19 @@ Casper.prototype.sendKeys = function(selector, keys, options) { ...@@ -1372,6 +1372,19 @@ Casper.prototype.sendKeys = function(selector, keys, options) {
1372 }; 1372 };
1373 1373
1374 /** 1374 /**
1375 * Sets current page content.
1376 *
1377 * @param String content Desired page content
1378 * @return Casper
1379 */
1380 Casper.prototype.setContent = function setContent(content) {
1381 "use strict";
1382 this.checkStarted();
1383 this.page.content = content;
1384 return this;
1385 };
1386
1387 /**
1375 * Sets current WebPage instance the credentials for HTTP authentication. 1388 * Sets current WebPage instance the credentials for HTTP authentication.
1376 * 1389 *
1377 * @param String username 1390 * @param String username
...@@ -1873,18 +1886,22 @@ Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, on ...@@ -1873,18 +1886,22 @@ Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, on
1873 * Makes the provided frame page as the currently active one. Note that the 1886 * Makes the provided frame page as the currently active one. Note that the
1874 * active page will be reverted when finished. 1887 * active page will be reverted when finished.
1875 * 1888 *
1876 * @param String frameName Target frame name 1889 * @param String|Number frameInfo Target frame name or number
1877 * @param Function then Next step function 1890 * @param Function then Next step function
1878 * @return Casper 1891 * @return Casper
1879 */ 1892 */
1880 Casper.prototype.withFrame = function withFrame(frameName, then) { 1893 Casper.prototype.withFrame = function withFrame(frameInfo, then) {
1881 "use strict"; 1894 "use strict";
1882 this.then(function _step() { 1895 this.then(function _step() {
1883 if (this.page.childFramesName().indexOf(frameName) === -1) { 1896 if (utils.isNumber(frameInfo)) {
1884 throw new CasperError(f('No frame named "%s" was found.', frameName)); 1897 if (frameInfo > this.page.childFramesCount() - 1) {
1898 throw new CasperError(f('Frame number "%d" is out of bounds.', frameInfo));
1899 }
1900 } else if (this.page.childFramesName().indexOf(frameInfo) === -1) {
1901 throw new CasperError(f('No frame named "%s" was found.', frameInfo));
1885 } 1902 }
1886 // make the frame page the currently active one 1903 // make the frame page the currently active one
1887 this.page.switchToChildFrame(frameName); 1904 this.page.switchToChildFrame(frameInfo);
1888 }); 1905 });
1889 try { 1906 try {
1890 this.then(then); 1907 this.then(then);
......
...@@ -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);
...@@ -48,7 +55,7 @@ exports.create = function create(casper, options) { ...@@ -48,7 +55,7 @@ exports.create = function create(casper, options) {
48 */ 55 */
49 var Tester = function Tester(casper, options) { 56 var Tester = function Tester(casper, options) {
50 "use strict"; 57 "use strict";
51 /*jshint maxstatements:20*/ 58 /*jshint maxstatements:30*/
52 59
53 if (!utils.isCasperObject(casper)) { 60 if (!utils.isCasperObject(casper)) {
54 throw new CasperError("Tester needs a Casper instance"); 61 throw new CasperError("Tester needs a Casper instance");
...@@ -60,32 +67,22 @@ var Tester = function Tester(casper, options) { ...@@ -60,32 +67,22 @@ var Tester = function Tester(casper, options) {
60 67
61 this.executed = 0; 68 this.executed = 0;
62 this.currentTestFile = null; 69 this.currentTestFile = null;
70 this.currentSuite = undefined;
63 this.currentSuiteNum = 0; 71 this.currentSuiteNum = 0;
64 this.exporter = require('xunit').create();
65 this.loadIncludes = { 72 this.loadIncludes = {
66 includes: [], 73 includes: [],
67 pre: [], 74 pre: [],
68 post: [] 75 post: []
69 }; 76 };
70 this.running = false; 77 this.running = false;
71 this.suites = []; 78 this.suites = new TestSuite();
72 this.options = utils.mergeObjects({ 79 this.options = utils.mergeObjects({
73 failFast: false, // terminates a suite as soon as a test fails? 80 failFast: false, // terminates a suite as soon as a test fails?
74 failText: "FAIL", // text to use for a successful test 81 failText: "FAIL", // text to use for a succesful test
75 passText: "PASS", // text to use for a failed test 82 passText: "PASS", // text to use for a failed test
76 pad: 80 // maximum number of chars for a result line 83 pad: 80 // maximum number of chars for a result line
77 }, options); 84 }, options);
78 85
79 // properties
80 this.testResults = {
81 passed: 0,
82 failed: 0,
83 passes: [],
84 failures: [],
85 passesTime: [],
86 failuresTime: []
87 };
88
89 // measuring test duration 86 // measuring test duration
90 this.currentTestStartTime = null; 87 this.currentTestStartTime = null;
91 this.lastAssertTime = 0; 88 this.lastAssertTime = 0;
...@@ -93,47 +90,27 @@ var Tester = function Tester(casper, options) { ...@@ -93,47 +90,27 @@ var Tester = function Tester(casper, options) {
93 this.configure(); 90 this.configure();
94 91
95 this.on('success', function onSuccess(success) { 92 this.on('success', function onSuccess(success) {
96 this.testResults.passes.push(success);
97 var timeElapsed = new Date() - this.currentTestStartTime; 93 var timeElapsed = new Date() - this.currentTestStartTime;
98 this.testResults.passesTime.push(timeElapsed - this.lastAssertTime); 94 this.currentSuite.addSuccess(success, timeElapsed - this.lastAssertTime);
99 this.exporter.addSuccess(fs.absolute(success.file), success.message || success.standard, timeElapsed - this.lastAssertTime);
100 this.lastAssertTime = timeElapsed; 95 this.lastAssertTime = timeElapsed;
101 }); 96 });
102 97
103 this.on('fail', function onFail(failure) { 98 this.on('fail', function onFail(failure) {
104 // export 99 // export
105 var timeElapsed = new Date() - this.currentTestStartTime; 100 var valueKeys = Object.keys(failure.values),
106 this.testResults.failuresTime.push(timeElapsed - this.lastAssertTime); 101 timeElapsed = new Date() - this.currentTestStartTime;
107 this.exporter.addFailure( 102 this.currentSuite.addFailure(failure, timeElapsed - this.lastAssertTime);
108 fs.absolute(failure.file),
109 failure.message || failure.standard,
110 failure.standard || "test failed",
111 failure.type || "unknown",
112 (timeElapsed - this.lastAssertTime)
113 );
114 this.lastAssertTime = timeElapsed; 103 this.lastAssertTime = timeElapsed;
115 this.testResults.failures.push(failure);
116
117 // special printing 104 // special printing
118 if (failure.type) { 105 if (failure.type) {
119 this.comment(' type: ' + failure.type); 106 this.comment(' type: ' + failure.type);
120 } 107 }
121 if (failure.values && Object.keys(failure.values).length > 0) { 108 if (!failure.values || valueKeys.length === 0) {
122 for (var name in failure.values) { 109 return;
123 var comment = ' ' + name + ': ';
124 var value = failure.values[name];
125 try {
126 comment += utils.serialize(failure.values[name]);
127 } catch (e) {
128 try {
129 comment += utils.serialize(failure.values[name].toString());
130 } catch (e2) {
131 comment += '(unserializable value)';
132 }
133 }
134 this.comment(comment);
135 }
136 } 110 }
111 valueKeys.forEach(function(name) {
112 this.comment(f(' %s: %s', name, utils.formatTestValue(failure.values[name], name)));
113 }.bind(this));
137 }); 114 });
138 115
139 // casper events 116 // casper events
...@@ -147,7 +124,7 @@ var Tester = function Tester(casper, options) { ...@@ -147,7 +124,7 @@ var Tester = function Tester(casper, options) {
147 line = backtrace[0].line; 124 line = backtrace[0].line;
148 } catch (e) {} 125 } catch (e) {}
149 } 126 }
150 this.test.uncaughtError(msg, this.test.currentTestFile, line); 127 this.test.uncaughtError(msg, this.test.currentTestFile, line, backtrace);
151 this.test.done(); 128 this.test.done();
152 }); 129 });
153 130
...@@ -175,16 +152,17 @@ exports.Tester = Tester; ...@@ -175,16 +152,17 @@ exports.Tester = Tester;
175 * @param Object|null context Assertion context object (Optional) 152 * @param Object|null context Assertion context object (Optional)
176 * @return Object An assertion result object 153 * @return Object An assertion result object
177 */ 154 */
178 Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject, message, context) { 155 Tester.prototype.assert =
156 Tester.prototype.assertTrue = function assert(subject, message, context) {
179 "use strict"; 157 "use strict";
180 this.executed++; 158 this.executed++;
181 return this.processAssertionResult(utils.mergeObjects({ 159 return this.processAssertionResult(utils.mergeObjects({
182 success: subject === true, 160 success: subject === true,
183 type: "assert", 161 type: "assert",
184 standard: "Subject is strictly true", 162 standard: "Subject is strictly true",
185 message: message, 163 message: message,
186 file: this.currentTestFile, 164 file: this.currentTestFile,
187 values: { 165 values: {
188 subject: utils.getPropertyPath(context, 'values.subject') || subject 166 subject: utils.getPropertyPath(context, 'values.subject') || subject
189 } 167 }
190 }, context || {})); 168 }, context || {}));
...@@ -198,12 +176,13 @@ Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject, ...@@ -198,12 +176,13 @@ Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject,
198 * @param String message Test description (Optional) 176 * @param String message Test description (Optional)
199 * @return Object An assertion result object 177 * @return Object An assertion result object
200 */ 178 */
201 Tester.prototype.assertEquals = Tester.prototype.assertEqual = function assertEquals(subject, expected, message) { 179 Tester.prototype.assertEquals =
180 Tester.prototype.assertEqual = function assertEquals(subject, expected, message) {
202 "use strict"; 181 "use strict";
203 return this.assert(this.testEquals(subject, expected), message, { 182 return this.assert(utils.equals(subject, expected), message, {
204 type: "assertEquals", 183 type: "assertEquals",
205 standard: "Subject equals the expected value", 184 standard: "Subject equals the expected value",
206 values: { 185 values: {
207 subject: subject, 186 subject: subject,
208 expected: expected 187 expected: expected
209 } 188 }
...@@ -221,9 +200,9 @@ Tester.prototype.assertEquals = Tester.prototype.assertEqual = function assertEq ...@@ -221,9 +200,9 @@ Tester.prototype.assertEquals = Tester.prototype.assertEqual = function assertEq
221 Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, message) { 200 Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, message) {
222 "use strict"; 201 "use strict";
223 return this.assert(!this.testEquals(subject, shouldnt), message, { 202 return this.assert(!this.testEquals(subject, shouldnt), message, {
224 type: "assertNotEquals", 203 type: "assertNotEquals",
225 standard: "Subject doesn't equal what it shouldn't be", 204 standard: "Subject doesn't equal what it shouldn't be",
226 values: { 205 values: {
227 subject: subject, 206 subject: subject,
228 shouldnt: shouldnt 207 shouldnt: shouldnt
229 } 208 }
...@@ -235,13 +214,15 @@ Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, m ...@@ -235,13 +214,15 @@ Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, m
235 * 214 *
236 * @param Function fn A function to be evaluated in remote DOM 215 * @param Function fn A function to be evaluated in remote DOM
237 * @param String message Test description 216 * @param String message Test description
238 * @param Object params Object/Array containing the parameters to inject into the function (optional) 217 * @param Object params Object/Array containing the parameters to inject into
218 * the function (optional)
239 * @return Object An assertion result object 219 * @return Object An assertion result object
240 */ 220 */
241 Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertEval(fn, message, params) { 221 Tester.prototype.assertEval =
222 Tester.prototype.assertEvaluate = function assertEval(fn, message, params) {
242 "use strict"; 223 "use strict";
243 return this.assert(this.casper.evaluate(fn, params), message, { 224 return this.assert(this.casper.evaluate(fn, params), message, {
244 type: "assertEval", 225 type: "assertEval",
245 standard: "Evaluated function returns true", 226 standard: "Evaluated function returns true",
246 values: { 227 values: {
247 fn: fn, 228 fn: fn,
...@@ -257,16 +238,18 @@ Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertE ...@@ -257,16 +238,18 @@ Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertE
257 * @param Function fn The function to be evaluated in remote DOM 238 * @param Function fn The function to be evaluated in remote DOM
258 * @param Boolean expected The expected value 239 * @param Boolean expected The expected value
259 * @param String|null message Test description 240 * @param String|null message Test description
260 * @param Object|null params Object containing the parameters to inject into the function (optional) 241 * @param Object|null params Object containing the parameters to inject into the
242 * function (optional)
261 * @return Object An assertion result object 243 * @return Object An assertion result object
262 */ 244 */
263 Tester.prototype.assertEvalEquals = Tester.prototype.assertEvalEqual = function assertEvalEquals(fn, expected, message, params) { 245 Tester.prototype.assertEvalEquals =
246 Tester.prototype.assertEvalEqual = function assertEvalEquals(fn, expected, message, params) {
264 "use strict"; 247 "use strict";
265 var subject = this.casper.evaluate(fn, params); 248 var subject = this.casper.evaluate(fn, params);
266 return this.assert(this.testEquals(subject, expected), message, { 249 return this.assert(utils.equals(subject, expected), message, {
267 type: "assertEvalEquals", 250 type: "assertEvalEquals",
268 standard: "Evaluated function returns the expected value", 251 standard: "Evaluated function returns the expected value",
269 values: { 252 values: {
270 fn: fn, 253 fn: fn,
271 params: params, 254 params: params,
272 subject: subject, 255 subject: subject,
...@@ -288,13 +271,13 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag ...@@ -288,13 +271,13 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag
288 var actual = this.casper.evaluate(function(inputName) { 271 var actual = this.casper.evaluate(function(inputName) {
289 return __utils__.getFieldValue(inputName); 272 return __utils__.getFieldValue(inputName);
290 }, inputName); 273 }, inputName);
291 return this.assert(this.testEquals(actual, expected), message, { 274 return this.assert(utils.equals(actual, expected), message, {
292 type: 'assertField', 275 type: 'assertField',
293 standard: f('"%s" input field has the value "%s"', inputName, expected), 276 standard: f('"%s" input field has the value "%s"', inputName, expected),
294 values: { 277 values: {
295 inputName: inputName, 278 inputName: inputName,
296 actual: actual, 279 actual: actual,
297 expected: expected 280 expected: expected
298 } 281 }
299 }); 282 });
300 }; 283 };
...@@ -307,7 +290,10 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag ...@@ -307,7 +290,10 @@ Tester.prototype.assertField = function assertField(inputName, expected, messag
307 * @param String message Test description 290 * @param String message Test description
308 * @return Object An assertion result object 291 * @return Object An assertion result object
309 */ 292 */
310 Tester.prototype.assertExists = Tester.prototype.assertExist = Tester.prototype.assertSelectorExists = Tester.prototype.assertSelectorExist = function assertExists(selector, message) { 293 Tester.prototype.assertExists =
294 Tester.prototype.assertExist =
295 Tester.prototype.assertSelectorExists =
296 Tester.prototype.assertSelectorExist = function assertExists(selector, message) {
311 "use strict"; 297 "use strict";
312 return this.assert(this.casper.exists(selector), message, { 298 return this.assert(this.casper.exists(selector), message, {
313 type: "assertExists", 299 type: "assertExists",
...@@ -326,7 +312,8 @@ Tester.prototype.assertExists = Tester.prototype.assertExist = Tester.prototype. ...@@ -326,7 +312,8 @@ Tester.prototype.assertExists = Tester.prototype.assertExist = Tester.prototype.
326 * @param String message Test description 312 * @param String message Test description
327 * @return Object An assertion result object 313 * @return Object An assertion result object
328 */ 314 */
329 Tester.prototype.assertDoesntExist = Tester.prototype.assertNotExists = function assertDoesntExist(selector, message) { 315 Tester.prototype.assertDoesntExist =
316 Tester.prototype.assertNotExists = function assertDoesntExist(selector, message) {
330 "use strict"; 317 "use strict";
331 return this.assert(!this.casper.exists(selector), message, { 318 return this.assert(!this.casper.exists(selector), message, {
332 type: "assertDoesntExist", 319 type: "assertDoesntExist",
...@@ -347,7 +334,7 @@ Tester.prototype.assertDoesntExist = Tester.prototype.assertNotExists = function ...@@ -347,7 +334,7 @@ Tester.prototype.assertDoesntExist = Tester.prototype.assertNotExists = function
347 Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) { 334 Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) {
348 "use strict"; 335 "use strict";
349 var currentHTTPStatus = this.casper.currentHTTPStatus; 336 var currentHTTPStatus = this.casper.currentHTTPStatus;
350 return this.assert(this.testEquals(this.casper.currentHTTPStatus, status), message, { 337 return this.assert(utils.equals(this.casper.currentHTTPStatus, status), message, {
351 type: "assertHttpStatus", 338 type: "assertHttpStatus",
352 standard: f("HTTP status code is: %s", status), 339 standard: f("HTTP status code is: %s", status),
353 values: { 340 values: {
...@@ -365,7 +352,8 @@ Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) { ...@@ -365,7 +352,8 @@ Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) {
365 * @param String message Test description 352 * @param String message Test description
366 * @return Object An assertion result object 353 * @return Object An assertion result object
367 */ 354 */
368 Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertMatch(subject, pattern, message) { 355 Tester.prototype.assertMatch =
356 Tester.prototype.assertMatches = function assertMatch(subject, pattern, message) {
369 "use strict"; 357 "use strict";
370 return this.assert(pattern.test(subject), message, { 358 return this.assert(pattern.test(subject), message, {
371 type: "assertMatch", 359 type: "assertMatch",
...@@ -384,7 +372,8 @@ Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertM ...@@ -384,7 +372,8 @@ Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertM
384 * @param String message Test description 372 * @param String message Test description
385 * @return Object An assertion result object 373 * @return Object An assertion result object
386 */ 374 */
387 Tester.prototype.assertNot = Tester.prototype.assertFalse = function assertNot(condition, message) { 375 Tester.prototype.assertNot =
376 Tester.prototype.assertFalse = function assertNot(condition, message) {
388 "use strict"; 377 "use strict";
389 return this.assert(!condition, message, { 378 return this.assert(!condition, message, {
390 type: "assertNot", 379 type: "assertNot",
...@@ -402,7 +391,8 @@ Tester.prototype.assertNot = Tester.prototype.assertFalse = function assertNot(c ...@@ -402,7 +391,8 @@ Tester.prototype.assertNot = Tester.prototype.assertFalse = function assertNot(c
402 * @param String message Test description 391 * @param String message Test description
403 * @return Object An assertion result object 392 * @return Object An assertion result object
404 */ 393 */
405 Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function assertNotVisible(selector, message) { 394 Tester.prototype.assertNotVisible =
395 Tester.prototype.assertInvisible = function assertNotVisible(selector, message) {
406 "use strict"; 396 "use strict";
407 return this.assert(!this.casper.visible(selector), message, { 397 return this.assert(!this.casper.visible(selector), message, {
408 type: "assertVisible", 398 type: "assertVisible",
...@@ -422,7 +412,9 @@ Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function ...@@ -422,7 +412,9 @@ Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function
422 * @param String message Test description 412 * @param String message Test description
423 * @return Object An assertion result object 413 * @return Object An assertion result object
424 */ 414 */
425 Tester.prototype.assertRaises = Tester.prototype.assertRaise = Tester.prototype.assertThrows = function assertRaises(fn, args, message) { 415 Tester.prototype.assertRaises =
416 Tester.prototype.assertRaise =
417 Tester.prototype.assertThrows = function assertRaises(fn, args, message) {
426 "use strict"; 418 "use strict";
427 var context = { 419 var context = {
428 type: "assertRaises", 420 type: "assertRaises",
...@@ -447,7 +439,8 @@ Tester.prototype.assertRaises = Tester.prototype.assertRaise = Tester.prototype. ...@@ -447,7 +439,8 @@ Tester.prototype.assertRaises = Tester.prototype.assertRaise = Tester.prototype.
447 * @param String message Test description 439 * @param String message Test description
448 * @return Object An assertion result object 440 * @return Object An assertion result object
449 */ 441 */
450 Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = function assertResourceExists(test, message) { 442 Tester.prototype.assertResourceExists =
443 Tester.prototype.assertResourceExist = function assertResourceExists(test, message) {
451 "use strict"; 444 "use strict";
452 return this.assert(this.casper.resourceExists(test), message, { 445 return this.assert(this.casper.resourceExists(test), message, {
453 type: "assertResourceExists", 446 type: "assertResourceExists",
...@@ -465,7 +458,8 @@ Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = f ...@@ -465,7 +458,8 @@ Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = f
465 * @param String message Test description 458 * @param String message Test description
466 * @return Object An assertion result object 459 * @return Object An assertion result object
467 */ 460 */
468 Tester.prototype.assertTextDoesntExist = Tester.prototype.assertTextDoesntExist = function assertTextDoesntExist(text, message) { 461 Tester.prototype.assertTextDoesntExist =
462 Tester.prototype.assertTextDoesntExist = function assertTextDoesntExist(text, message) {
469 "use strict"; 463 "use strict";
470 var textFound = (this.casper.evaluate(function _evaluate() { 464 var textFound = (this.casper.evaluate(function _evaluate() {
471 return document.body.textContent || document.body.innerText; 465 return document.body.textContent || document.body.innerText;
...@@ -486,7 +480,8 @@ Tester.prototype.assertTextDoesntExist = Tester.prototype.assertTextDoesntExist ...@@ -486,7 +480,8 @@ Tester.prototype.assertTextDoesntExist = Tester.prototype.assertTextDoesntExist
486 * @param String message Test description 480 * @param String message Test description
487 * @return Object An assertion result object 481 * @return Object An assertion result object
488 */ 482 */
489 Tester.prototype.assertTextExists = Tester.prototype.assertTextExist = function assertTextExists(text, message) { 483 Tester.prototype.assertTextExists =
484 Tester.prototype.assertTextExist = function assertTextExists(text, message) {
490 "use strict"; 485 "use strict";
491 var textFound = (this.casper.evaluate(function _evaluate() { 486 var textFound = (this.casper.evaluate(function _evaluate() {
492 return document.body.textContent || document.body.innerText; 487 return document.body.textContent || document.body.innerText;
...@@ -511,9 +506,9 @@ Tester.prototype.assertTruthy = function assertTruthy(subject, message) { ...@@ -511,9 +506,9 @@ Tester.prototype.assertTruthy = function assertTruthy(subject, message) {
511 "use strict"; 506 "use strict";
512 /*jshint eqeqeq:false*/ 507 /*jshint eqeqeq:false*/
513 return this.assert(utils.isTruthy(subject), message, { 508 return this.assert(utils.isTruthy(subject), message, {
514 type: "assertTruthy", 509 type: "assertTruthy",
515 standard: "Subject is truthy", 510 standard: "Subject is truthy",
516 values: { 511 values: {
517 subject: subject 512 subject: subject
518 } 513 }
519 }); 514 });
...@@ -530,9 +525,9 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) { ...@@ -530,9 +525,9 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) {
530 "use strict"; 525 "use strict";
531 /*jshint eqeqeq:false*/ 526 /*jshint eqeqeq:false*/
532 return this.assert(utils.isFalsy(subject), message, { 527 return this.assert(utils.isFalsy(subject), message, {
533 type: "assertFalsy", 528 type: "assertFalsy",
534 standard: "Subject is falsy", 529 standard: "Subject is falsy",
535 values: { 530 values: {
536 subject: subject 531 subject: subject
537 } 532 }
538 }); 533 });
...@@ -546,7 +541,8 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) { ...@@ -546,7 +541,8 @@ Tester.prototype.assertFalsy = function assertFalsy(subject, message) {
546 * @param String message Test description 541 * @param String message Test description
547 * @return Object An assertion result object 542 * @return Object An assertion result object
548 */ 543 */
549 Tester.prototype.assertSelectorHasText = Tester.prototype.assertSelectorContains = function assertSelectorHasText(selector, text, message) { 544 Tester.prototype.assertSelectorHasText =
545 Tester.prototype.assertSelectorContains = function assertSelectorHasText(selector, text, message) {
550 "use strict"; 546 "use strict";
551 var textFound = this.casper.fetchText(selector).indexOf(text) !== -1; 547 var textFound = this.casper.fetchText(selector).indexOf(text) !== -1;
552 return this.assert(textFound, message, { 548 return this.assert(textFound, message, {
...@@ -567,7 +563,8 @@ Tester.prototype.assertSelectorHasText = Tester.prototype.assertSelectorContains ...@@ -567,7 +563,8 @@ Tester.prototype.assertSelectorHasText = Tester.prototype.assertSelectorContains
567 * @param String message Test description 563 * @param String message Test description
568 * @return Object An assertion result object 564 * @return Object An assertion result object
569 */ 565 */
570 Tester.prototype.assertSelectorDoesntHaveText = Tester.prototype.assertSelectorDoesntContain = function assertSelectorDoesntHaveText(selector, text, message) { 566 Tester.prototype.assertSelectorDoesntHaveText =
567 Tester.prototype.assertSelectorDoesntContain = function assertSelectorDoesntHaveText(selector, text, message) {
571 "use strict"; 568 "use strict";
572 var textFound = this.casper.fetchText(selector).indexOf(text) === -1; 569 var textFound = this.casper.fetchText(selector).indexOf(text) === -1;
573 return this.assert(textFound, message, { 570 return this.assert(textFound, message, {
...@@ -590,7 +587,7 @@ Tester.prototype.assertSelectorDoesntHaveText = Tester.prototype.assertSelectorD ...@@ -590,7 +587,7 @@ Tester.prototype.assertSelectorDoesntHaveText = Tester.prototype.assertSelectorD
590 Tester.prototype.assertTitle = function assertTitle(expected, message) { 587 Tester.prototype.assertTitle = function assertTitle(expected, message) {
591 "use strict"; 588 "use strict";
592 var currentTitle = this.casper.getTitle(); 589 var currentTitle = this.casper.getTitle();
593 return this.assert(this.testEquals(currentTitle, expected), message, { 590 return this.assert(utils.equals(currentTitle, expected), message, {
594 type: "assertTitle", 591 type: "assertTitle",
595 standard: f('Page title is: "%s"', expected), 592 standard: f('Page title is: "%s"', expected),
596 values: { 593 values: {
...@@ -607,7 +604,8 @@ Tester.prototype.assertTitle = function assertTitle(expected, message) { ...@@ -607,7 +604,8 @@ Tester.prototype.assertTitle = function assertTitle(expected, message) {
607 * @param String message Test description 604 * @param String message Test description
608 * @return Object An assertion result object 605 * @return Object An assertion result object
609 */ 606 */
610 Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = function assertTitleMatch(pattern, message) { 607 Tester.prototype.assertTitleMatch =
608 Tester.prototype.assertTitleMatches = function assertTitleMatch(pattern, message) {
611 "use strict"; 609 "use strict";
612 var currentTitle = this.casper.getTitle(); 610 var currentTitle = this.casper.getTitle();
613 return this.assert(pattern.test(currentTitle), message, { 611 return this.assert(pattern.test(currentTitle), message, {
...@@ -631,7 +629,7 @@ Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = functi ...@@ -631,7 +629,7 @@ Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = functi
631 Tester.prototype.assertType = function assertType(subject, type, message) { 629 Tester.prototype.assertType = function assertType(subject, type, message) {
632 "use strict"; 630 "use strict";
633 var actual = utils.betterTypeOf(subject); 631 var actual = utils.betterTypeOf(subject);
634 return this.assert(this.testEquals(actual, type), message, { 632 return this.assert(utils.equals(actual, type), message, {
635 type: "assertType", 633 type: "assertType",
636 standard: f('Subject type is: "%s"', type), 634 standard: f('Subject type is: "%s"', type),
637 values: { 635 values: {
...@@ -651,7 +649,8 @@ Tester.prototype.assertType = function assertType(subject, type, message) { ...@@ -651,7 +649,8 @@ Tester.prototype.assertType = function assertType(subject, type, message) {
651 * @param String message Test description 649 * @param String message Test description
652 * @return Object An assertion result object 650 * @return Object An assertion result object
653 */ 651 */
654 Tester.prototype.assertUrlMatch = Tester.prototype.assertUrlMatches = function assertUrlMatch(pattern, message) { 652 Tester.prototype.assertUrlMatch =
653 Tester.prototype.assertUrlMatches = function assertUrlMatch(pattern, message) {
655 "use strict"; 654 "use strict";
656 var currentUrl = this.casper.getCurrentUrl(), 655 var currentUrl = this.casper.getCurrentUrl(),
657 patternType = utils.betterTypeOf(pattern), 656 patternType = utils.betterTypeOf(pattern),
...@@ -701,16 +700,26 @@ Tester.prototype.bar = function bar(text, style) { ...@@ -701,16 +700,26 @@ Tester.prototype.bar = function bar(text, style) {
701 }; 700 };
702 701
703 /** 702 /**
704 * Retrieves the sum of all durations of the tests which were 703 * Starts a suite.
705 * executed in the current suite
706 * 704 *
707 * @return Number duration of all tests executed until now (in the current suite) 705 * @param String description Test suite description
706 * @param Function suiteFn Suite function
708 */ 707 */
709 Tester.prototype.calculateSuiteDuration = function calculateSuiteDuration() { 708 Tester.prototype.begin = function begin(description, suiteFn) {
710 "use strict"; 709 "use strict";
711 return this.testResults.passesTime.concat(this.testResults.failuresTime).reduce(function add(a, b) { 710 description = description || "Untitled suite in " + this.currentTestFile;
712 return a + b; 711 this.comment(description);
713 }, 0); 712 this.executed = 0;
713 this.currentSuite = new TestSuiteResult({
714 name: description,
715 file: this.currentTestFile
716 });
717 try {
718 suiteFn.call(this, this.casper);
719 } catch (e) {
720 this.uncaughtError(e, this.currentTestFile, e.line);
721 this.done();
722 }
714 }; 723 };
715 724
716 /** 725 /**
...@@ -767,14 +776,38 @@ Tester.prototype.configure = function configure() { ...@@ -767,14 +776,38 @@ Tester.prototype.configure = function configure() {
767 Tester.prototype.done = function done(planned) { 776 Tester.prototype.done = function done(planned) {
768 "use strict"; 777 "use strict";
769 if (planned > 0 && planned !== this.executed) { 778 if (planned > 0 && planned !== this.executed) {
770 this.fail(f('%s: %d tests planned, %d tests executed', 779 this.dubious(planned, this.executed);
771 this.currentTestFile, planned, this.executed)); 780 }
781 if (this.currentSuite) {
782 this.suites.push(this.currentSuite);
783 this.currentSuite = undefined;
784 this.executed = 0;
772 } 785 }
773 this.emit('test.done'); 786 this.emit('test.done');
774 this.running = false; 787 this.running = false;
775 }; 788 };
776 789
777 /** 790 /**
791 * Marks a test as dubious, when the number of planned tests doesn't match the
792 * number of actually executed one.
793 *
794 * @param String message
795 */
796 Tester.prototype.dubious = function dubious(planned, executed) {
797 "use strict";
798 var message = f('%d tests planned, %d tests executed', planned, executed);
799 return this.assert(false, message, {
800 type: "dubious",
801 standard: message,
802 message: message,
803 values: {
804 planned: planned,
805 executed: executed
806 }
807 });
808 };
809
810 /**
778 * Writes an error-style formatted message to stdout. 811 * Writes an error-style formatted message to stdout.
779 * 812 *
780 * @param String message 813 * @param String message
...@@ -857,57 +890,6 @@ Tester.prototype.formatMessage = function formatMessage(message, style) { ...@@ -857,57 +890,6 @@ Tester.prototype.formatMessage = function formatMessage(message, style) {
857 }; 890 };
858 891
859 /** 892 /**
860 * Retrieves current failure data and all failed cases.
861 *
862 * @return Object casedata An object containg information about cases
863 * @return Number casedata.length The number of failed cases
864 * @return Array casedata.cases An array of all the failed case objects
865 */
866 Tester.prototype.getFailures = function getFailures() {
867 "use strict";
868 return {
869 length: this.testResults.failed,
870 cases: this.testResults.failures
871 };
872 };
873
874 /**
875 * Retrieves current passed data and all passed cases.
876 *
877 * @return Object casedata An object containg information about cases
878 * @return Number casedata.length The number of passed cases
879 * @return Array casedata.cases An array of all the passed case objects
880 */
881 Tester.prototype.getPasses = function getPasses() {
882 "use strict";
883 return {
884 length: this.testResults.passed,
885 cases: this.testResults.passes
886 };
887 };
888
889 /**
890 * Retrieves the array where all the durations of failed tests are stored
891 *
892 * @return Array durations of failed tests
893 */
894 Tester.prototype.getFailuresTime = function getFailuresTime() {
895 "use strict";
896 return this.testResults.failuresTime;
897 }
898
899 /**
900 * Retrieves the array where all the durations of passed tests are stored
901 *
902 * @return Array durations of passed tests
903 */
904 Tester.prototype.getPassesTime = function getPassesTime() {
905 "use strict";
906 return this.testResults.passesTime;
907 }
908
909
910 /**
911 * Writes an info-style formatted message to stdout. 893 * Writes an info-style formatted message to stdout.
912 * 894 *
913 * @param String message 895 * @param String message
...@@ -918,7 +900,7 @@ Tester.prototype.info = function info(message) { ...@@ -918,7 +900,7 @@ Tester.prototype.info = function info(message) {
918 }; 900 };
919 901
920 /** 902 /**
921 * Adds a successful test entry to the stack. 903 * Adds a succesful test entry to the stack.
922 * 904 *
923 * @param String message 905 * @param String message
924 */ 906 */
...@@ -939,6 +921,12 @@ Tester.prototype.pass = function pass(message) { ...@@ -939,6 +921,12 @@ Tester.prototype.pass = function pass(message) {
939 */ 921 */
940 Tester.prototype.processAssertionResult = function processAssertionResult(result) { 922 Tester.prototype.processAssertionResult = function processAssertionResult(result) {
941 "use strict"; 923 "use strict";
924 if (!this.currentSuite) {
925 this.currentSuite = new TestSuiteResult({
926 name: "Untitled suite in " + this.currentTestFile,
927 file: this.currentTestFile
928 });
929 }
942 var eventName= 'success', 930 var eventName= 'success',
943 message = result.message || result.standard, 931 message = result.message || result.standard,
944 style = 'INFO', 932 style = 'INFO',
...@@ -947,9 +935,6 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result ...@@ -947,9 +935,6 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result
947 eventName = 'fail'; 935 eventName = 'fail';
948 style = 'RED_BAR'; 936 style = 'RED_BAR';
949 status = this.options.failText; 937 status = this.options.failText;
950 this.testResults.failed++;
951 } else {
952 this.testResults.passed++;
953 } 938 }
954 this.casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' ')); 939 this.casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' '));
955 this.emit(eventName, result); 940 this.emit(eventName, result);
...@@ -962,21 +947,22 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result ...@@ -962,21 +947,22 @@ Tester.prototype.processAssertionResult = function processAssertionResult(result
962 /** 947 /**
963 * Renders a detailed report for each failed test. 948 * Renders a detailed report for each failed test.
964 * 949 *
965 * @param Array failures
966 */ 950 */
967 Tester.prototype.renderFailureDetails = function renderFailureDetails(failures) { 951 Tester.prototype.renderFailureDetails = function renderFailureDetails() {
968 "use strict"; 952 "use strict";
953 var failures = this.suites.getAllFailures();
969 if (failures.length === 0) { 954 if (failures.length === 0) {
970 return; 955 return;
971 } 956 }
972 this.casper.echo(f("\nDetails for the %d failed test%s:\n", failures.length, failures.length > 1 ? "s" : ""), "PARAMETER"); 957 this.casper.echo(f("\nDetails for the %d failed test%s:\n",
958 failures.length, failures.length > 1 ? "s" : ""), "PARAMETER");
973 failures.forEach(function _forEach(failure) { 959 failures.forEach(function _forEach(failure) {
974 var type, message, line; 960 this.casper.echo(f('In %s%s', failure.file, ~~failure.line ? ':' + ~~failure.line : ''));
975 type = failure.type || "unknown"; 961 if (failure.suite) {
976 line = ~~failure.line; 962 this.casper.echo(f(' %s', failure.suite), "PARAMETER");
977 message = failure.message; 963 }
978 this.casper.echo(f('In %s:%s', failure.file, line)); 964 this.casper.echo(f(' %s: %s', failure.type || "unknown",
979 this.casper.echo(f(' %s: %s', type, message || failure.standard || "(no message was entered)"), "COMMENT"); 965 failure.message || failure.standard || "(no message was entered)"), "COMMENT");
980 }); 966 });
981 }; 967 };
982 968
...@@ -989,14 +975,19 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { ...@@ -989,14 +975,19 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) {
989 "use strict"; 975 "use strict";
990 /*jshint maxstatements:20*/ 976 /*jshint maxstatements:20*/
991 save = save || this.options.save; 977 save = save || this.options.save;
992 var total = this.testResults.passed + this.testResults.failed, statusText, style, result; 978 var failed = this.suites.countFailed(),
993 var exitStatus = ~~(status || (this.testResults.failed > 0 ? 1 : 0)); 979 passed = this.suites.countPassed(),
980 total = this.suites.countTotal(),
981 statusText,
982 style,
983 result,
984 exitStatus = ~~(status || (failed > 0 ? 1 : 0));
994 if (total === 0) { 985 if (total === 0) {
995 statusText = this.options.failText; 986 statusText = this.options.failText;
996 style = 'RED_BAR'; 987 style = 'RED_BAR';
997 result = f("%s Looks like you didn't run any test.", statusText); 988 result = f("%s Looks like you didn't run any test.", statusText);
998 } else { 989 } else {
999 if (this.testResults.failed > 0) { 990 if (failed > 0) {
1000 statusText = this.options.failText; 991 statusText = this.options.failText;
1001 style = 'RED_BAR'; 992 style = 'RED_BAR';
1002 } else { 993 } else {
...@@ -1004,12 +995,12 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { ...@@ -1004,12 +995,12 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) {
1004 style = 'GREEN_BAR'; 995 style = 'GREEN_BAR';
1005 } 996 }
1006 result = f('%s %s tests executed in %ss, %d passed, %d failed.', 997 result = f('%s %s tests executed in %ss, %d passed, %d failed.',
1007 statusText, total, utils.ms2seconds(this.calculateSuiteDuration()), 998 statusText, total, utils.ms2seconds(this.suites.calculateDuration()),
1008 this.testResults.passed, this.testResults.failed); 999 passed, failed);
1009 } 1000 }
1010 this.casper.echo(result, style, this.options.pad); 1001 this.casper.echo(result, style, this.options.pad);
1011 if (this.testResults.failed > 0) { 1002 if (failed > 0) {
1012 this.renderFailureDetails(this.testResults.failures); 1003 this.renderFailureDetails();
1013 } 1004 }
1014 if (save) { 1005 if (save) {
1015 this.saveResults(save); 1006 this.saveResults(save);
...@@ -1053,12 +1044,15 @@ Tester.prototype.runSuites = function runSuites() { ...@@ -1053,12 +1044,15 @@ Tester.prototype.runSuites = function runSuites() {
1053 }); 1044 });
1054 1045
1055 if (testFiles.length === 0) { 1046 if (testFiles.length === 0) {
1056 this.bar(f("No test file found in %s, aborting.", Array.prototype.slice.call(arguments)), "RED_BAR"); 1047 this.bar(f("No test file found in %s, aborting.",
1048 Array.prototype.slice.call(arguments)), "RED_BAR");
1057 this.casper.exit(1); 1049 this.casper.exit(1);
1058 } 1050 }
1051
1059 self.currentSuiteNum = 0; 1052 self.currentSuiteNum = 0;
1060 self.currentTestStartTime = new Date(); 1053 self.currentTestStartTime = new Date();
1061 self.lastAssertTime = 0; 1054 self.lastAssertTime = 0;
1055
1062 var interval = setInterval(function _check(self) { 1056 var interval = setInterval(function _check(self) {
1063 if (self.running) { 1057 if (self.running) {
1064 return; 1058 return;
...@@ -1066,13 +1060,9 @@ Tester.prototype.runSuites = function runSuites() { ...@@ -1066,13 +1060,9 @@ Tester.prototype.runSuites = function runSuites() {
1066 if (self.currentSuiteNum === testFiles.length) { 1060 if (self.currentSuiteNum === testFiles.length) {
1067 self.emit('tests.complete'); 1061 self.emit('tests.complete');
1068 clearInterval(interval); 1062 clearInterval(interval);
1069 self.exporter.setSuiteDuration(self.calculateSuiteDuration());
1070 } else { 1063 } else {
1071 self.runTest(testFiles[self.currentSuiteNum]); 1064 self.runTest(testFiles[self.currentSuiteNum]);
1072 self.exporter.setSuiteDuration(self.calculateSuiteDuration());
1073 self.currentSuiteNum++; 1065 self.currentSuiteNum++;
1074 self.passesTime = [];
1075 self.failuresTime = [];
1076 } 1066 }
1077 }, 100, this); 1067 }, 100, this);
1078 }; 1068 };
...@@ -1092,16 +1082,14 @@ Tester.prototype.runTest = function runTest(testFile) { ...@@ -1092,16 +1082,14 @@ Tester.prototype.runTest = function runTest(testFile) {
1092 /** 1082 /**
1093 * Saves results to file. 1083 * Saves results to file.
1094 * 1084 *
1095 * @param String filename Target file path. 1085 * @param String filename Target file path.
1096 */ 1086 */
1097 Tester.prototype.saveResults = function saveResults(filepath) { 1087 Tester.prototype.saveResults = function saveResults(filepath) {
1098 "use strict"; 1088 "use strict";
1099 // FIXME: looks like phantomjs has a pb with fs.isWritable https://groups.google.com/forum/#!topic/casperjs/hcUdwgGZOrU 1089 var exporter = require('xunit').create();
1100 // if (!fs.isWritable(filepath)) { 1090 exporter.setResults(this.suites);
1101 // throw new CasperError(f('Path %s is not writable.', filepath));
1102 // }
1103 try { 1091 try {
1104 fs.write(filepath, this.exporter.getXML(), 'w'); 1092 fs.write(filepath, exporter.getXML(), 'w');
1105 this.casper.echo(f('Result log stored in %s', filepath), 'INFO', 80); 1093 this.casper.echo(f('Result log stored in %s', filepath), 'INFO', 80);
1106 } catch (e) { 1094 } catch (e) {
1107 this.casper.echo(f('Unable to write results to %s: %s', filepath, e), 'ERROR', 80); 1095 this.casper.echo(f('Unable to write results to %s: %s', filepath, e), 'ERROR', 80);
...@@ -1127,17 +1115,184 @@ Tester.prototype.testEquals = Tester.prototype.testEqual = function testEquals(v ...@@ -1127,17 +1115,184 @@ Tester.prototype.testEquals = Tester.prototype.testEqual = function testEquals(v
1127 * @param Error|String error The error 1115 * @param Error|String error The error
1128 * @param String file Test file where the error occurred 1116 * @param String file Test file where the error occurred
1129 * @param Number line Line number (optional) 1117 * @param Number line Line number (optional)
1118 * @param Array backtrace Error stack trace (optional)
1130 */ 1119 */
1131 Tester.prototype.uncaughtError = function uncaughtError(error, file, line) { 1120 Tester.prototype.uncaughtError = function uncaughtError(error, file, line, backtrace) {
1132 "use strict"; 1121 "use strict";
1133 return this.processAssertionResult({ 1122 return this.processAssertionResult({
1134 success: false, 1123 success: false,
1135 type: "uncaughtError", 1124 type: "uncaughtError",
1136 file: file, 1125 file: file,
1137 line: ~~line || "unknown", 1126 line: ~~line,
1138 message: utils.isObject(error) ? error.message : error, 1127 message: utils.isObject(error) ? error.message : error,
1139 values: { 1128 values: {
1140 error: error 1129 error: error,
1130 stack: backtrace
1141 } 1131 }
1142 }); 1132 });
1143 }; 1133 };
1134
1135 /**
1136 * Test suites array.
1137 *
1138 */
1139 function TestSuite() {}
1140 TestSuite.prototype = [];
1141 exports.TestSuite = TestSuite;
1142
1143 /**
1144 * Returns the number of tests.
1145 *
1146 * @return Number
1147 */
1148 TestSuite.prototype.countTotal = function countTotal() {
1149 "use strict";
1150 return this.countPassed() + this.countFailed();
1151 };
1152
1153 /**
1154 * Returns the number of failed tests.
1155 *
1156 * @return Number
1157 */
1158 TestSuite.prototype.countFailed = function countFailed() {
1159 "use strict";
1160 return this.map(function(result) {
1161 return result.failed;
1162 }).reduce(function(a, b) {
1163 return a + b;
1164 });
1165 };
1166
1167 /**
1168 * Returns the number of succesful tests.
1169 *
1170 * @return Number
1171 */
1172 TestSuite.prototype.countPassed = function countPassed() {
1173 "use strict";
1174 return this.map(function(result) {
1175 return result.passed;
1176 }).reduce(function(a, b) {
1177 return a + b;
1178 });
1179 };
1180
1181 /**
1182 * Returns all failures from this suite.
1183 *
1184 * @return Array
1185 */
1186 TestSuite.prototype.getAllFailures = function getAllFailures() {
1187 "use strict";
1188 var failures = [];
1189 this.forEach(function(result) {
1190 failures = failures.concat(result.failures);
1191 });
1192 return failures;
1193 };
1194
1195 /**
1196 * Returns all succesful tests from this suite.
1197 *
1198 * @return Array
1199 */
1200 TestSuite.prototype.getAllPasses = function getAllPasses() {
1201 "use strict";
1202 var passes = [];
1203 this.forEach(function(result) {
1204 passes = passes.concat(result.passes);
1205 });
1206 return passes;
1207 };
1208
1209 /**
1210 * Returns all results from this suite.
1211 *
1212 * @return Array
1213 */
1214 TestSuite.prototype.getAllResults = function getAllResults() {
1215 "use strict";
1216 return this.getAllPasses().concat(this.getAllFailures());
1217 };
1218
1219 /**
1220 * Computes the sum of all durations of the tests which were executed in the
1221 * current suite.
1222 *
1223 * @return Number
1224 */
1225 TestSuite.prototype.calculateDuration = function calculateDuration() {
1226 "use strict";
1227 return this.getAllResults().map(function(result) {
1228 return result.time;
1229 }).reduce(function add(a, b) {
1230 return a + b;
1231 }, 0);
1232 };
1233
1234 /**
1235 * Test suite results object.
1236 *
1237 * @param Object options
1238 */
1239 function TestSuiteResult(options) {
1240 "use strict";
1241 this.name = options && options.name;
1242 this.file = options && options.file;
1243 this.assertions = 0;
1244 this.passed = 0;
1245 this.failed = 0;
1246 this.passes = [];
1247 this.failures = [];
1248 }
1249 exports.TestSuiteResult = TestSuiteResult;
1250
1251 /**
1252 * Adds a success record and its execution time to their associated stacks.
1253 *
1254 * @param Object success
1255 * @param Number time
1256 */
1257 TestSuiteResult.prototype.addSuccess = function addSuccess(success, time) {
1258 "use strict";
1259 success.suite = this.name;
1260 success.time = time;
1261 this.passes.push(success);
1262 this.assertions++;
1263 this.passed++;
1264 };
1265
1266 /**
1267 * Adds a failure record and its execution time to their associated stacks.
1268 *
1269 * @param Object failure
1270 * @param Number time
1271 */
1272 TestSuiteResult.prototype.addFailure = function addFailure(failure, time) {
1273 "use strict";
1274 failure.suite = this.name;
1275 failure.time = time;
1276 this.failures.push(failure);
1277 this.assertions++;
1278 this.failed++;
1279 };
1280
1281 /**
1282 * Computes total duration for this suite.
1283 *
1284 * @return Number
1285 */
1286 TestSuiteResult.prototype.calculateDuration = function calculateDuration() {
1287 "use strict";
1288 function add(a, b) {
1289 return a + b;
1290 }
1291 var passedTimes = this.passes.map(function(success) {
1292 return ~~success.time;
1293 }).reduce(add, 0);
1294 var failedTimes = this.failures.map(function(failure) {
1295 return ~~failure.time;
1296 }).reduce(add, 0);
1297 return passedTimes + failedTimes;
1298 };
......
...@@ -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
...@@ -295,7 +348,7 @@ exports.isClipRect = isClipRect; ...@@ -295,7 +348,7 @@ exports.isClipRect = isClipRect;
295 function isFalsy(subject) { 348 function isFalsy(subject) {
296 "use strict"; 349 "use strict";
297 /*jshint eqeqeq:false*/ 350 /*jshint eqeqeq:false*/
298 return subject == new Function('return false;')(); 351 return !subject;
299 } 352 }
300 exports.isFalsy = isFalsy; 353 exports.isFalsy = isFalsy;
301 /** 354 /**
...@@ -392,7 +445,7 @@ exports.isString = isString; ...@@ -392,7 +445,7 @@ exports.isString = isString;
392 function isTruthy(subject) { 445 function isTruthy(subject) {
393 "use strict"; 446 "use strict";
394 /*jshint eqeqeq:false*/ 447 /*jshint eqeqeq:false*/
395 return subject == new Function('return true;')(); 448 return !!subject;
396 } 449 }
397 exports.isTruthy = isTruthy; 450 exports.isTruthy = isTruthy;
398 451
......
...@@ -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 };
......
...@@ -25,7 +25,17 @@ casper.withFrame('frame2', function() { ...@@ -25,7 +25,17 @@ casper.withFrame('frame2', function() {
25 this.test.assertTitle('CasperJS frame 3'); 25 this.test.assertTitle('CasperJS frame 3');
26 }); 26 });
27 27
28 casper.withFrame(0, function() {
29 this.test.assertTitle('CasperJS frame 1');
30 this.test.assertExists("#f1");
31 this.test.assertDoesntExist("#f2");
32 });
33
34 casper.withFrame(1, function() {
35 this.test.assertTitle('CasperJS frame 3');
36 });
37
28 casper.run(function() { 38 casper.run(function() {
29 this.test.assertTitle('CasperJS test frames'); 39 this.test.assertTitle('CasperJS test frames');
30 this.test.done(10); 40 this.test.done(14);
31 }); 41 });
......
...@@ -47,7 +47,7 @@ casper.thenOpen('tests/site/index.html', function() { ...@@ -47,7 +47,7 @@ casper.thenOpen('tests/site/index.html', function() {
47 t.assertTruthy('1', 'Tester.assertTruthy() works as expected'); 47 t.assertTruthy('1', 'Tester.assertTruthy() works as expected');
48 48
49 t.comment('Tester.assertFalsy()'); 49 t.comment('Tester.assertFalsy()');
50 t.assertFalsy('0', 'Tester.assertFalsy() works as expected'); 50 t.assertFalsy('', 'Tester.assertFalsy() works as expected');
51 51
52 t.comment('Tester.assertNot()'); 52 t.comment('Tester.assertNot()');
53 t.assertNot(false, 'Tester.assertNot() works as expected'); 53 t.assertNot(false, 'Tester.assertNot() works as expected');
...@@ -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(casper) {
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(casper) {
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(casper) {
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(casper) {
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,18 @@ t.comment('mergeObjects()'); ...@@ -269,20 +263,18 @@ 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 });
282 })();
283 276
284 t.comment('unique()'); 277 casper.test.begin('unique() tests', function() {
285 (function() {
286 var testCases = [ 278 var testCases = [
287 { 279 {
288 input: [1,2,3], 280 input: [1,2,3],
...@@ -302,8 +294,7 @@ t.comment('unique()'); ...@@ -302,8 +294,7 @@ t.comment('unique()');
302 } 294 }
303 ]; 295 ];
304 testCases.forEach(function(testCase) { 296 testCases.forEach(function(testCase) {
305 t.assertEquals(utils.unique(testCase.input), testCase.output, 'unique() computes unique values of an array'); 297 this.assertEquals(utils.unique(testCase.input), testCase.output, 'unique() computes unique values of an array');
306 }); 298 }.bind(this));
307 })(); 299 this.done(testCases.length);
308 300 });
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,
67 type: "footype",
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 casper.test.done(1);
76 });
46 77
47 casper.test.done(8);
......