Commit 6e35765f 6e35765f79e3ef3c6b2ceced141d2d0b31cdebd3 by Nicolas Perriault

big tester refactor - see details in CHANGELOG

1 parent 1f7acddd
1 CasperJS Changelog 1 CasperJS Changelog
2 ================== 2 ==================
3 3
4 XXXX-XX-XX, v1.1
5 ----------------
6
7 This version is yet to be released.
8
9 ### Important Changes & Caveats
10
11 #### Tester refactor
12
13 Scraping and testing are now betterly separated in CasperJS, and bad code is now a bit less bad. That involves breaking up BC on some points though:
14
15 - The Casper object won't be created with a `test` reference if not invoked using the [`casperjs test` command](http://casperjs.org/testing.html#casper-test-command), therefore the ability to run any test without calling it has been dropped. I know, get over it.
16 - Passing the planned number of tests to `casper.done()` has been dropped as well, because `done()` may be never called at all when big troubles happen; rather use the new `begin()` method and provide the expected number of tests using the second argument:
17
18 ```js
19 casper.test.begin("Planning 4 tests", 4, function(test) {
20 [1, 2, 3, 4].forEach(function() {
21 test.assert(true);
22 });
23 test.done();
24 });
25 ```
26
27 ### Bugfixes & enhancements
28
29 None yet.
30
4 XXXX-XX-XX, v1.0.1 31 XXXX-XX-XX, v1.0.1
5 ------------------ 32 ------------------
6 33
......
...@@ -151,13 +151,18 @@ var Casper = function Casper(options) { ...@@ -151,13 +151,18 @@ var Casper = function Casper(options) {
151 this.started = false; 151 this.started = false;
152 this.step = -1; 152 this.step = -1;
153 this.steps = []; 153 this.steps = [];
154 this.test = tester.create(this); 154 if (phantom.casperTest) {
155 this.test = tester.create(this);
156 }
155 157
156 // init phantomjs error handler 158 // init phantomjs error handler
157 this.initErrorHandler(); 159 this.initErrorHandler();
158 160
159 this.on('error', function(msg, backtrace) { 161 this.on('error', function(msg, backtrace) {
160 if (msg === this.test.SKIP_MESSAGE) { 162 if (msg === this.test.SKIP_MESSAGE) { // FIXME: decouple testing
163 return;
164 }
165 if (msg.indexOf('AssertionError') === 0) { // FIXME: decouple testing
161 return; 166 return;
162 } 167 }
163 var c = this.getColorizer(); 168 var c = this.getColorizer();
...@@ -1303,16 +1308,23 @@ Casper.prototype.run = function run(onComplete, time) { ...@@ -1303,16 +1308,23 @@ Casper.prototype.run = function run(onComplete, time) {
1303 Casper.prototype.runStep = function runStep(step) { 1308 Casper.prototype.runStep = function runStep(step) {
1304 "use strict"; 1309 "use strict";
1305 this.checkStarted(); 1310 this.checkStarted();
1306 var skipLog = utils.isObject(step.options) && step.options.skipLog === true; 1311 var skipLog = utils.isObject(step.options) && step.options.skipLog === true,
1307 var stepInfo = f("Step %d/%d", this.step, this.steps.length); 1312 stepInfo = f("Step %d/%d", this.step, this.steps.length),
1308 var stepResult; 1313 stepResult;
1314 function getCurrentSuiteNum(casper) {
1315 if (casper.test) {
1316 return casper.test.currentSuiteNum + "-" + casper.step;
1317 } else {
1318 return casper.step;
1319 }
1320 }
1309 if (!skipLog && /^http/.test(this.getCurrentUrl())) { 1321 if (!skipLog && /^http/.test(this.getCurrentUrl())) {
1310 this.log(stepInfo + f(' %s (HTTP %d)', this.getCurrentUrl(), this.currentHTTPStatus), "info"); 1322 this.log(stepInfo + f(' %s (HTTP %d)', this.getCurrentUrl(), this.currentHTTPStatus), "info");
1311 } 1323 }
1312 if (utils.isNumber(this.options.stepTimeout) && this.options.stepTimeout > 0) { 1324 if (utils.isNumber(this.options.stepTimeout) && this.options.stepTimeout > 0) {
1313 var stepTimeoutCheckInterval = setInterval(function _check(self, start, stepNum) { 1325 var stepTimeoutCheckInterval = setInterval(function _check(self, start, stepNum) {
1314 if (new Date().getTime() - start > self.options.stepTimeout) { 1326 if (new Date().getTime() - start > self.options.stepTimeout) {
1315 if ((self.test.currentSuiteNum + "-" + self.step) === stepNum) { 1327 if (getCurrentSuiteNum(self) === stepNum) {
1316 self.emit('step.timeout'); 1328 self.emit('step.timeout');
1317 if (utils.isFunction(self.options.onStepTimeout)) { 1329 if (utils.isFunction(self.options.onStepTimeout)) {
1318 self.options.onStepTimeout.call(self, self.options.stepTimeout, stepNum); 1330 self.options.onStepTimeout.call(self, self.options.stepTimeout, stepNum);
...@@ -1320,10 +1332,15 @@ Casper.prototype.runStep = function runStep(step) { ...@@ -1320,10 +1332,15 @@ Casper.prototype.runStep = function runStep(step) {
1320 } 1332 }
1321 clearInterval(stepTimeoutCheckInterval); 1333 clearInterval(stepTimeoutCheckInterval);
1322 } 1334 }
1323 }, this.options.stepTimeout, this, new Date().getTime(), this.test.currentSuiteNum + "-" + this.step); 1335 }, this.options.stepTimeout, this, new Date().getTime(), getCurrentSuiteNum(this));
1324 } 1336 }
1325 this.emit('step.start', step); 1337 this.emit('step.start', step);
1326 stepResult = step.call(this, this.currentResponse); 1338 try {
1339 stepResult = step.call(this, this.currentResponse);
1340 } catch (err) {
1341 this.emit('step.error', err);
1342 throw err;
1343 }
1327 if (utils.isFunction(this.options.onStepComplete)) { 1344 if (utils.isFunction(this.options.onStepComplete)) {
1328 this.options.onStepComplete.call(this, this, stepResult); 1345 this.options.onStepComplete.call(this, this, stepResult);
1329 } 1346 }
...@@ -1643,7 +1660,8 @@ Casper.prototype.visible = function visible(selector) { ...@@ -1643,7 +1660,8 @@ Casper.prototype.visible = function visible(selector) {
1643 }; 1660 };
1644 1661
1645 /** 1662 /**
1646 * Displays a warning message onto the console and logs the event. 1663 * Displays a warning message onto the console and logs the event. Also emits a
1664 * `warn` event with the message passed.
1647 * 1665 *
1648 * @param String message 1666 * @param String message
1649 * @return Casper 1667 * @return Casper
...@@ -1652,6 +1670,7 @@ Casper.prototype.warn = function warn(message) { ...@@ -1652,6 +1670,7 @@ Casper.prototype.warn = function warn(message) {
1652 "use strict"; 1670 "use strict";
1653 this.log(message, "warning", "phantom"); 1671 this.log(message, "warning", "phantom");
1654 var formatted = f.apply(null, ["⚠  " + message].concat([].slice.call(arguments, 1))); 1672 var formatted = f.apply(null, ["⚠  " + message].concat([].slice.call(arguments, 1)));
1673 this.emit('warn', message);
1655 return this.echo(formatted, 'COMMENT'); 1674 return this.echo(formatted, 'COMMENT');
1656 }; 1675 };
1657 1676
......
...@@ -35,6 +35,15 @@ var events = require('events'); ...@@ -35,6 +35,15 @@ 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 function AssertionError(msg, result) {
39 Error.call(this);
40 this.message = msg;
41 this.name = 'AssertionError';
42 this.result = result;
43 }
44 AssertionError.prototype = new Error();
45 exports.AssertionError = AssertionError;
46
38 /** 47 /**
39 * Creates a tester instance. 48 * Creates a tester instance.
40 * 49 *
...@@ -79,10 +88,6 @@ var Tester = function Tester(casper, options) { ...@@ -79,10 +88,6 @@ var Tester = function Tester(casper, options) {
79 pre: [], 88 pre: [],
80 post: [] 89 post: []
81 }; 90 };
82 this.queue = [];
83 this.running = false;
84 this.started = false;
85 this.suiteResults = new TestSuiteResult();
86 this.options = utils.mergeObjects({ 91 this.options = utils.mergeObjects({
87 failFast: false, // terminates a suite as soon as a test fails? 92 failFast: false, // terminates a suite as soon as a test fails?
88 failText: "FAIL", // text to use for a succesful test 93 failText: "FAIL", // text to use for a succesful test
...@@ -90,6 +95,10 @@ var Tester = function Tester(casper, options) { ...@@ -90,6 +95,10 @@ var Tester = function Tester(casper, options) {
90 pad: 80 , // maximum number of chars for a result line 95 pad: 80 , // maximum number of chars for a result line
91 warnText: "WARN" // text to use for a dubious test 96 warnText: "WARN" // text to use for a dubious test
92 }, options); 97 }, options);
98 this.queue = [];
99 this.running = false;
100 this.started = false;
101 this.suiteResults = new TestSuiteResult();
93 102
94 this.configure(); 103 this.configure();
95 104
...@@ -109,6 +118,12 @@ var Tester = function Tester(casper, options) { ...@@ -109,6 +118,12 @@ var Tester = function Tester(casper, options) {
109 if (failure.type) { 118 if (failure.type) {
110 this.comment(' type: ' + failure.type); 119 this.comment(' type: ' + failure.type);
111 } 120 }
121 if (failure.file) {
122 this.comment(' file: ' + failure.file + (failure.line ? ':' + failure.line : ''));
123 }
124 if (failure.lineContents) {
125 this.comment(' code: ' + failure.lineContents);
126 }
112 if (!failure.values || valueKeys.length === 0) { 127 if (!failure.values || valueKeys.length === 0) {
113 return; 128 return;
114 } 129 }
...@@ -119,7 +134,8 @@ var Tester = function Tester(casper, options) { ...@@ -119,7 +134,8 @@ var Tester = function Tester(casper, options) {
119 134
120 // casper events 135 // casper events
121 this.casper.on('error', function onCasperError(msg, backtrace) { 136 this.casper.on('error', function onCasperError(msg, backtrace) {
122 if (!phantom.casperTest) { 137 var line = 0;
138 if (msg.indexOf('AssertionError') === 0) {
123 return; 139 return;
124 } 140 }
125 if (msg === self.SKIP_MESSAGE) { 141 if (msg === self.SKIP_MESSAGE) {
...@@ -127,19 +143,20 @@ var Tester = function Tester(casper, options) { ...@@ -127,19 +143,20 @@ var Tester = function Tester(casper, options) {
127 self.aborted = true; 143 self.aborted = true;
128 return self.done(); 144 return self.done();
129 } 145 }
130 var line = 0; 146 try {
131 if (!utils.isString(msg)) { 147 line = backtrace.filter(function(entry) {
132 try { 148 return self.currentTestFile === entry.file;
133 line = backtrace[0].line; 149 })[0].line;
134 } catch (e) {} 150 } catch (e) {}
135 }
136 self.uncaughtError(msg, self.currentTestFile, line, backtrace); 151 self.uncaughtError(msg, self.currentTestFile, line, backtrace);
137 self.done(); 152 self.done();
138 }); 153 });
139 154
140 this.casper.on('step.error', function onStepError(e) { 155 this.casper.on('step.error', function onStepError(error) {
141 if (e.message !== self.SKIP_MESSAGE) { 156 if (error instanceof AssertionError) {
142 self.uncaughtError(e, self.currentTestFile); 157 self.processAssertionError(error);
158 } else if (error.message !== self.SKIP_MESSAGE) {
159 self.uncaughtError(error, self.currentTestFile);
143 } 160 }
144 self.done(); 161 self.done();
145 }); 162 });
...@@ -158,16 +175,19 @@ exports.Tester = Tester; ...@@ -158,16 +175,19 @@ exports.Tester = Tester;
158 * family methods; supplementary informations are then passed using the 175 * family methods; supplementary informations are then passed using the
159 * `context` argument. 176 * `context` argument.
160 * 177 *
178 * Note: an AssertionError is thrown if the assertion fails.
179 *
161 * @param Boolean subject The condition to test 180 * @param Boolean subject The condition to test
162 * @param String message Test description 181 * @param String message Test description
163 * @param Object|null context Assertion context object (Optional) 182 * @param Object|null context Assertion context object (Optional)
164 * @return Object An assertion result object 183 * @return Object An assertion result object if test passed
184 * @throws AssertionError in case the test failed
165 */ 185 */
166 Tester.prototype.assert = 186 Tester.prototype.assert =
167 Tester.prototype.assertTrue = function assert(subject, message, context) { 187 Tester.prototype.assertTrue = function assert(subject, message, context) {
168 "use strict"; 188 "use strict";
169 this.executed++; 189 this.executed++;
170 return this.processAssertionResult(utils.mergeObjects({ 190 var result = utils.mergeObjects({
171 success: subject === true, 191 success: subject === true,
172 type: "assert", 192 type: "assert",
173 standard: "Subject is strictly true", 193 standard: "Subject is strictly true",
...@@ -176,7 +196,11 @@ Tester.prototype.assertTrue = function assert(subject, message, context) { ...@@ -176,7 +196,11 @@ Tester.prototype.assertTrue = function assert(subject, message, context) {
176 values: { 196 values: {
177 subject: utils.getPropertyPath(context, 'values.subject') || subject 197 subject: utils.getPropertyPath(context, 'values.subject') || subject
178 } 198 }
179 }, context || {})); 199 }, context || {});
200 if (!result.success) {
201 throw new AssertionError(message, result);
202 }
203 return this.processAssertionResult(result);
180 }; 204 };
181 205
182 /** 206 /**
...@@ -720,9 +744,10 @@ Tester.prototype.bar = function bar(text, style) { ...@@ -720,9 +744,10 @@ Tester.prototype.bar = function bar(text, style) {
720 * Starts a suite. 744 * Starts a suite.
721 * 745 *
722 * @param String description Test suite description 746 * @param String description Test suite description
747 * @param Number planned Number of planned tests in this suite
723 * @param Function suiteFn Suite function 748 * @param Function suiteFn Suite function
724 */ 749 */
725 Tester.prototype.begin = function begin(description, suiteFn) { 750 Tester.prototype.begin = function begin(description, planned, suiteFn) {
726 "use strict"; 751 "use strict";
727 if (this.started && this.running) { 752 if (this.started && this.running) {
728 return this.queue.push(arguments); 753 return this.queue.push(arguments);
...@@ -731,14 +756,19 @@ Tester.prototype.begin = function begin(description, suiteFn) { ...@@ -731,14 +756,19 @@ Tester.prototype.begin = function begin(description, suiteFn) {
731 this.comment(description); 756 this.comment(description);
732 this.currentSuite = new TestCaseResult({ 757 this.currentSuite = new TestCaseResult({
733 name: description, 758 name: description,
734 file: this.currentTestFile 759 file: this.currentTestFile,
760 planned: Math.abs(~~planned) || undefined
735 }); 761 });
736 this.executed = 0; 762 this.executed = 0;
737 this.running = this.started = true; 763 this.running = this.started = true;
738 try { 764 try {
739 suiteFn.call(this, this, this.casper); 765 suiteFn.call(this, this, this.casper);
740 } catch (e) { 766 } catch (err) {
741 this.uncaughtError(e, this.currentTestFile, e.line); 767 if (err instanceof AssertionError) {
768 this.processAssertionError(err);
769 } else {
770 this.uncaughtError(err, this.currentTestFile, err.line);
771 }
742 this.done(); 772 this.done();
743 } 773 }
744 }; 774 };
...@@ -796,7 +826,12 @@ Tester.prototype.configure = function configure() { ...@@ -796,7 +826,12 @@ Tester.prototype.configure = function configure() {
796 */ 826 */
797 Tester.prototype.done = function done(planned) { 827 Tester.prototype.done = function done(planned) {
798 "use strict"; 828 "use strict";
799 if (planned > 0 && planned !== this.executed) { 829 if (arguments.length > 0) {
830 this.casper.warn('done() `planned` arg is deprecated as of 1.1');
831 }
832 if (this.currentSuite.planned && this.currentSuite.planned !== this.executed) {
833 this.dubious(this.currentSuite.planned, this.executed);
834 } else if (planned && planned !== this.executed) {
800 this.dubious(planned, this.executed); 835 this.dubious(planned, this.executed);
801 } 836 }
802 if (this.currentSuite) { 837 if (this.currentSuite) {
...@@ -821,15 +856,15 @@ Tester.prototype.done = function done(planned) { ...@@ -821,15 +856,15 @@ Tester.prototype.done = function done(planned) {
821 Tester.prototype.dubious = function dubious(planned, executed) { 856 Tester.prototype.dubious = function dubious(planned, executed) {
822 "use strict"; 857 "use strict";
823 var message = f('%d tests planned, %d tests executed', planned, executed); 858 var message = f('%d tests planned, %d tests executed', planned, executed);
824 return this.assert(false, message, { 859 this.currentSuite.addWarning({
825 type: "dubious", 860 message: message,
826 standard: message, 861 file: this.currentTestFile,
827 message: message,
828 values: { 862 values: {
829 planned: planned, 863 planned: planned,
830 executed: executed 864 executed: executed
831 } 865 }
832 }); 866 });
867 this.casper.warn(message);
833 }; 868 };
834 869
835 /** 870 /**
...@@ -938,6 +973,31 @@ Tester.prototype.pass = function pass(message) { ...@@ -938,6 +973,31 @@ Tester.prototype.pass = function pass(message) {
938 }; 973 };
939 974
940 /** 975 /**
976 * Processes an assertion error.
977 *
978 * @param AssertionError error
979 */
980 Tester.prototype.processAssertionError = function(error) {
981 "use strict";
982 var result = error && error.result,
983 testFile = this.currentTestFile,
984 stackEntry;
985 try {
986 stackEntry = error.stackArray.filter(function(entry) {
987 return testFile === entry.sourceURL;
988 })[0];
989 } catch (e) {}
990 if (stackEntry) {
991 result.line = stackEntry.line;
992 try {
993 result.lineContents = fs.read(this.currentTestFile).split('\n')[result.line - 1].trim();
994 } catch (e) {
995 }
996 }
997 return this.processAssertionResult(result);
998 };
999
1000 /**
941 * Processes an assertion result by emitting the appropriate event and 1001 * Processes an assertion result by emitting the appropriate event and
942 * printing result onto the console. 1002 * printing result onto the console.
943 * 1003 *
...@@ -947,9 +1007,11 @@ Tester.prototype.pass = function pass(message) { ...@@ -947,9 +1007,11 @@ Tester.prototype.pass = function pass(message) {
947 Tester.prototype.processAssertionResult = function processAssertionResult(result) { 1007 Tester.prototype.processAssertionResult = function processAssertionResult(result) {
948 "use strict"; 1008 "use strict";
949 if (!this.currentSuite) { 1009 if (!this.currentSuite) {
1010 // this is for BC when begin() didn't exist
950 this.currentSuite = new TestCaseResult({ 1011 this.currentSuite = new TestCaseResult({
951 name: "Untitled suite in " + this.currentTestFile, 1012 name: "Untitled suite in " + this.currentTestFile,
952 file: this.currentTestFile 1013 file: this.currentTestFile,
1014 planned: undefined
953 }); 1015 });
954 } 1016 }
955 var eventName = 'success', 1017 var eventName = 'success',
...@@ -1001,7 +1063,6 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { ...@@ -1001,7 +1063,6 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) {
1001 "use strict"; 1063 "use strict";
1002 /*jshint maxstatements:20*/ 1064 /*jshint maxstatements:20*/
1003 save = save || this.options.save; 1065 save = save || this.options.save;
1004 this.done(); // never too sure
1005 var failed = this.suiteResults.countFailed(), 1066 var failed = this.suiteResults.countFailed(),
1006 passed = this.suiteResults.countPassed(), 1067 passed = this.suiteResults.countPassed(),
1007 total = this.suiteResults.countTotal(), 1068 total = this.suiteResults.countTotal(),
...@@ -1269,16 +1330,52 @@ function TestCaseResult(options) { ...@@ -1269,16 +1330,52 @@ function TestCaseResult(options) {
1269 "use strict"; 1330 "use strict";
1270 this.name = options && options.name; 1331 this.name = options && options.name;
1271 this.file = options && options.file; 1332 this.file = options && options.file;
1272 this.assertions = 0; 1333 this.planned = ~~(options && options.planned) || undefined;
1273 this.passed = 0; 1334 this.errors = [];
1274 this.failed = 0;
1275 this.passes = [];
1276 this.failures = []; 1335 this.failures = [];
1336 this.passes = [];
1337 this.warnings = [];
1338 this.__defineGetter__("assertions", function() {
1339 return this.passed + this.failed;
1340 });
1341 this.__defineGetter__("crashed", function() {
1342 return this.errors.length;
1343 });
1344 this.__defineGetter__("failed", function() {
1345 return this.failures.length;
1346 });
1347 this.__defineGetter__("passed", function() {
1348 return this.passes.length;
1349 });
1277 } 1350 }
1278 exports.TestCaseResult = TestCaseResult; 1351 exports.TestCaseResult = TestCaseResult;
1279 1352
1280 /** 1353 /**
1281 * Adds a success record and its execution time to their associated stacks. 1354 * Adds a failure record and its execution time.
1355 *
1356 * @param Object failure
1357 * @param Number time
1358 */
1359 TestCaseResult.prototype.addFailure = function addFailure(failure, time) {
1360 "use strict";
1361 failure.suite = this.name;
1362 failure.time = time;
1363 this.failures.push(failure);
1364 };
1365
1366 /**
1367 * Adds an error record.
1368 *
1369 * @param Object failure
1370 */
1371 TestCaseResult.prototype.addError = function addFailure(error) {
1372 "use strict";
1373 error.suite = this.name;
1374 this.errors.push(error);
1375 };
1376
1377 /**
1378 * Adds a success record and its execution time.
1282 * 1379 *
1283 * @param Object success 1380 * @param Object success
1284 * @param Number time 1381 * @param Number time
...@@ -1288,23 +1385,16 @@ TestCaseResult.prototype.addSuccess = function addSuccess(success, time) { ...@@ -1288,23 +1385,16 @@ TestCaseResult.prototype.addSuccess = function addSuccess(success, time) {
1288 success.suite = this.name; 1385 success.suite = this.name;
1289 success.time = time; 1386 success.time = time;
1290 this.passes.push(success); 1387 this.passes.push(success);
1291 this.assertions++;
1292 this.passed++;
1293 }; 1388 };
1294 1389
1295 /** 1390 /**
1296 * Adds a failure record and its execution time to their associated stacks. 1391 * Adds a warning record.
1297 * 1392 *
1298 * @param Object failure 1393 * @param Object warning
1299 * @param Number time
1300 */ 1394 */
1301 TestCaseResult.prototype.addFailure = function addFailure(failure, time) { 1395 TestCaseResult.prototype.addWarning = function addWarning(warning) {
1302 "use strict"; 1396 warning.suite = this.name;
1303 failure.suite = this.name; 1397 this.warnings.push(warning);
1304 failure.time = time;
1305 this.failures.push(failure);
1306 this.assertions++;
1307 this.failed++;
1308 }; 1398 };
1309 1399
1310 /** 1400 /**
......
...@@ -45,8 +45,7 @@ var TestSuiteResult = require('tester').TestSuiteResult; ...@@ -45,8 +45,7 @@ var TestSuiteResult = require('tester').TestSuiteResult;
45 */ 45 */
46 function generateClassName(classname) { 46 function generateClassName(classname) {
47 "use strict"; 47 "use strict";
48 classname = classname.replace(phantom.casperPath, "").trim(); 48 var script = classname.replace(phantom.casperPath, "").trim() || phantom.casperScript;
49 var script = classname || phantom.casperScript;
50 if (script.indexOf(fs.workingDirectory) === 0) { 49 if (script.indexOf(fs.workingDirectory) === 0) {
51 script = script.substring(fs.workingDirectory.length + 1); 50 script = script.substring(fs.workingDirectory.length + 1);
52 } 51 }
...@@ -56,12 +55,10 @@ function generateClassName(classname) { ...@@ -56,12 +55,10 @@ function generateClassName(classname) {
56 if (~script.indexOf('.')) { 55 if (~script.indexOf('.')) {
57 script = script.substring(0, script.lastIndexOf('.')); 56 script = script.substring(0, script.lastIndexOf('.'));
58 } 57 }
59
60 // If we have trimmed our string down to nothing, default to script name 58 // If we have trimmed our string down to nothing, default to script name
61 if (!script && phantom.casperScript) { 59 if (!script && phantom.casperScript) {
62 script = phantom.casperScript; 60 script = phantom.casperScript;
63 } 61 }
64
65 return script || "unknown"; 62 return script || "unknown";
66 } 63 }
67 64
...@@ -103,7 +100,7 @@ XUnitExporter.prototype.getXML = function getXML() { ...@@ -103,7 +100,7 @@ XUnitExporter.prototype.getXML = function getXML() {
103 var suiteNode = utils.node('testsuite', { 100 var suiteNode = utils.node('testsuite', {
104 name: result.name, 101 name: result.name,
105 tests: result.assertions, 102 tests: result.assertions,
106 failures: result.failed, 103 failures: result.failures.length,
107 time: utils.ms2seconds(result.calculateDuration()), 104 time: utils.ms2seconds(result.calculateDuration()),
108 'package': generateClassName(result.file), 105 'package': generateClassName(result.file),
109 }); 106 });
......
1 { 1 {
2 "name": "casperjs", 2 "name": "casperjs",
3 "description": "Navigation scripting & testing utility for PhantomJS", 3 "description": "Navigation scripting & testing utility for PhantomJS",
4 "version": "1.0.0", 4 "version": "1.1.0-DEV",
5 "keywords": [ 5 "keywords": [
6 "phantomjs", 6 "phantomjs",
7 "javascript" 7 "javascript"
......
1 /*jshint strict:false*/ 1 /*jshint strict:false*/
2 /*global CasperError console phantom require*/ 2 /*global CasperError casper console phantom require*/
3 /**
4 * Google sample testing.
5 *
6 * Usage:
7 *
8 * $ casperjs test googletesting.js
9 */
10 casper.test.begin('Google search retrieves 10 or more results', 5, function suite(test) {
11 casper.start("http://www.google.fr/", function() {
12 test.assertTitle("Google", "google homepage title is the one expected");
13 test.assertExists('form[action="/search"]', "main form is found");
14 this.fill('form[action="/search"]', {
15 q: "foo"
16 }, true);
17 });
3 18
4 var casper = require("casper").create({ 19 casper.then(function() {
5 logLevel: "debug" 20 test.assertTitle("!!foo - Recherche Google", "google title is ok");
6 }); 21 test.assertUrlMatch(/q=foo/, "search term has been submitted");
7 22 test.assertEval(function() {
8 casper.start("http://www.google.fr/", function() { 23 return __utils__.findAll("h3.r").length >= 10;
9 this.test.assertTitle("Google", "google homepage title is the one expected"); 24 }, "google search for \"foo\" retrieves 10 or more results");
10 this.test.assertExists('form[action="/search"]', "main form is found"); 25 });
11 this.fill('form[action="/search"]', {
12 q: "foo"
13 }, true);
14 });
15
16 casper.then(function() {
17 this.test.assertTitle("foo - Recherche Google", "google title is ok");
18 this.test.assertUrlMatch(/q=foo/, "search term has been submitted");
19 this.test.assertEval((function() {
20 return __utils__.findAll("h3.r").length >= 10;
21 }), "google search for \"foo\" retrieves 10 or more results");
22 });
23 26
24 casper.run(function() { 27 casper.run(function() {
25 this.test.renderResults(true); 28 test.done();
29 });
26 }); 30 });
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 /*global CasperError casper console phantom require*/ 2 /*global CasperError casper console phantom require*/
3 var fs = require('fs'); 3 var fs = require('fs');
4 4
5 casper.test.begin('Tester.sortFiles()', function suite(test) { 5 casper.test.begin('Tester.sortFiles()', 1, function suite(test) {
6 var testDirRoot = fs.pathJoin(phantom.casperPath, 'tests', 'testdir'); 6 var testDirRoot = fs.pathJoin(phantom.casperPath, 'tests', 'testdir');
7 var files = test.findTestFiles(testDirRoot); 7 var files = test.findTestFiles(testDirRoot);
8 var expected = [ 8 var expected = [
...@@ -17,5 +17,5 @@ casper.test.begin('Tester.sortFiles()', function suite(test) { ...@@ -17,5 +17,5 @@ casper.test.begin('Tester.sortFiles()', function suite(test) {
17 return fs.pathJoin.apply(fs, [testDirRoot].concat(entry.split('/'))); 17 return fs.pathJoin.apply(fs, [testDirRoot].concat(entry.split('/')));
18 }); 18 });
19 test.assertEquals(files, expected, 'findTestFiles() find test files and sort them'); 19 test.assertEquals(files, expected, 'findTestFiles() find test files and sort them');
20 test.done(1); 20 test.done();
21 }); 21 });
......
...@@ -3,17 +3,17 @@ ...@@ -3,17 +3,17 @@
3 3
4 var TestCaseResult = require('tester').TestCaseResult; 4 var TestCaseResult = require('tester').TestCaseResult;
5 5
6 casper.test.begin('TestCaseResult.constructor() tests', function(test) { 6 casper.test.begin('TestCaseResult.constructor() tests', 4, function(test) {
7 var caseResult1 = new TestCaseResult(); 7 var caseResult1 = new TestCaseResult();
8 test.assertType(caseResult1.name, "undefined", 'TestCaseResult.constructor() name is undefined by default'); 8 test.assertType(caseResult1.name, "undefined", 'TestCaseResult.constructor() name is undefined by default');
9 test.assertType(caseResult1.file, "undefined", 'TestCaseResult.constructor() file is undefined by default'); 9 test.assertType(caseResult1.file, "undefined", 'TestCaseResult.constructor() file is undefined by default');
10 var caseResult2 = new TestCaseResult({name: 'foo', file: '/tmp/foo'}); 10 var caseResult2 = new TestCaseResult({name: 'foo', file: '/tmp/foo'});
11 test.assertEquals(caseResult2.name, "foo", 'TestCaseResult.constructor() can set name'); 11 test.assertEquals(caseResult2.name, "foo", 'TestCaseResult.constructor() can set name');
12 test.assertEquals(caseResult2.file, "/tmp/foo", 'TestCaseResult.constructor() can set file'); 12 test.assertEquals(caseResult2.file, "/tmp/foo", 'TestCaseResult.constructor() can set file');
13 test.done(4); 13 test.done();
14 }); 14 });
15 15
16 casper.test.begin('TestCaseResult.addSuccess() and TestCaseResult.addFailure() tests', function(test) { 16 casper.test.begin('TestCaseResult.addSuccess() and TestCaseResult.addFailure() tests', 22, function(test) {
17 var caseResult = new TestCaseResult({name: 'foo', file: '/tmp/foo'}); 17 var caseResult = new TestCaseResult({name: 'foo', file: '/tmp/foo'});
18 test.assertEquals(caseResult.assertions, 0, 'test case result counts no assertion by default'); 18 test.assertEquals(caseResult.assertions, 0, 'test case result counts no assertion by default');
19 test.assertEquals(caseResult.passed, 0, 'test case result counts no success by default'); 19 test.assertEquals(caseResult.passed, 0, 'test case result counts no success by default');
...@@ -43,8 +43,8 @@ casper.test.begin('TestCaseResult.addSuccess() and TestCaseResult.addFailure() t ...@@ -43,8 +43,8 @@ casper.test.begin('TestCaseResult.addSuccess() and TestCaseResult.addFailure() t
43 caseResult.addSuccess({}, 1000); 43 caseResult.addSuccess({}, 1000);
44 test.assertEquals(caseResult.assertions, 3, 'test case result counts three assertions'); 44 test.assertEquals(caseResult.assertions, 3, 'test case result counts three assertions');
45 test.assertEquals(caseResult.passed, 2, 'test case result counts two successes'); 45 test.assertEquals(caseResult.passed, 2, 'test case result counts two successes');
46 test.assertEquals(caseResult.failed, 1, 'test case result counts no failure'); 46 test.assertEquals(caseResult.failed, 1, 'test case result counts one failure');
47 test.assertEquals(caseResult.calculateDuration(), 1337 + 42 + 1000, 47 test.assertEquals(caseResult.calculateDuration(), 1337 + 42 + 1000,
48 'TestCaseResult.calculateDuration() computes new tests duration'); 48 'TestCaseResult.calculateDuration() computes new tests duration');
49 test.done(22); 49 test.done();
50 }); 50 });
......
...@@ -18,7 +18,7 @@ function generateCaseResult(options) { ...@@ -18,7 +18,7 @@ function generateCaseResult(options) {
18 return caseResult; 18 return caseResult;
19 } 19 }
20 20
21 casper.test.begin('TestSuiteResult() basic tests', function(test) { 21 casper.test.begin('TestSuiteResult() basic tests', 8, function(test) {
22 var suiteResult = new TestSuiteResult(); 22 var suiteResult = new TestSuiteResult();
23 test.assertEquals(suiteResult.constructor.name, 'Array', 'TestSuiteResult() is derived from Array'); 23 test.assertEquals(suiteResult.constructor.name, 'Array', 'TestSuiteResult() is derived from Array');
24 test.assertEquals(suiteResult.countTotal(), 0); 24 test.assertEquals(suiteResult.countTotal(), 0);
...@@ -31,7 +31,7 @@ casper.test.begin('TestSuiteResult() basic tests', function(test) { ...@@ -31,7 +31,7 @@ casper.test.begin('TestSuiteResult() basic tests', function(test) {
31 test.done(); 31 test.done();
32 }); 32 });
33 33
34 casper.test.begin('TestSuiteResult() accumulation tests', function(test) { 34 casper.test.begin('TestSuiteResult() accumulation tests', 7, function(test) {
35 var suiteResult = new TestSuiteResult(); 35 var suiteResult = new TestSuiteResult();
36 suiteResult.push(generateCaseResult({ 36 suiteResult.push(generateCaseResult({
37 name: 'foo', 37 name: 'foo',
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
3 var tester = require('tester'); 3 var tester = require('tester');
4 var testpage = require('webpage').create(); 4 var testpage = require('webpage').create();
5 5
6 casper.test.begin('XUnitReporter() initialization', function suite() { 6 casper.test.begin('XUnitReporter() initialization', 1, function suite() {
7 var xunit = require('xunit').create(); 7 var xunit = require('xunit').create();
8 var results = new tester.TestSuiteResult(); 8 var results = new tester.TestSuiteResult();
9 xunit.setResults(results); 9 xunit.setResults(results);
10 this.assertTruthy(xunit.getXML()); 10 this.assertTruthy(xunit.getXML());
11 this.done(1); 11 this.done();
12 }); 12 });
13 13
14 casper.test.begin('XUnitReporter() can hold test suites', function suite() { 14 casper.test.begin('XUnitReporter() can hold test suites', 4, function suite() {
15 var xunit = require('xunit').create(); 15 var xunit = require('xunit').create();
16 var results = new tester.TestSuiteResult(); 16 var results = new tester.TestSuiteResult();
17 var suite1 = new tester.TestCaseResult({ 17 var suite1 = new tester.TestCaseResult({
...@@ -32,10 +32,10 @@ casper.test.begin('XUnitReporter() can hold test suites', function suite() { ...@@ -32,10 +32,10 @@ casper.test.begin('XUnitReporter() can hold test suites', function suite() {
32 this.assertExists('testsuites[duration]'); 32 this.assertExists('testsuites[duration]');
33 this.assertExists('testsuite[name="foo"][package="foo"]'); 33 this.assertExists('testsuite[name="foo"][package="foo"]');
34 this.assertExists('testsuite[name="bar"][package="bar"]'); 34 this.assertExists('testsuite[name="bar"][package="bar"]');
35 this.done(4); 35 this.done();
36 }); 36 });
37 37
38 casper.test.begin('XUnitReporter() can hold a suite with a succesful test', function suite() { 38 casper.test.begin('XUnitReporter() can hold a suite with a succesful test', 1, function suite() {
39 var xunit = require('xunit').create(); 39 var xunit = require('xunit').create();
40 var results = new tester.TestSuiteResult(); 40 var results = new tester.TestSuiteResult();
41 var suite1 = new tester.TestCaseResult({ 41 var suite1 = new tester.TestCaseResult({
...@@ -52,10 +52,10 @@ casper.test.begin('XUnitReporter() can hold a suite with a succesful test', func ...@@ -52,10 +52,10 @@ casper.test.begin('XUnitReporter() can hold a suite with a succesful test', func
52 xunit.setResults(results); 52 xunit.setResults(results);
53 casper.start().setContent(xunit.getXML()); 53 casper.start().setContent(xunit.getXML());
54 this.assertExists('testsuite[name="foo"][package="foo"][tests="1"][failures="0"] testcase[name="footext"]'); 54 this.assertExists('testsuite[name="foo"][package="foo"][tests="1"][failures="0"] testcase[name="footext"]');
55 casper.test.done(1); 55 casper.test.done();
56 }); 56 });
57 57
58 casper.test.begin('XUnitReporter() can handle a failed test', function suite() { 58 casper.test.begin('XUnitReporter() can handle a failed test', 2, function suite() {
59 var xunit = require('xunit').create(); 59 var xunit = require('xunit').create();
60 var results = new tester.TestSuiteResult(); 60 var results = new tester.TestSuiteResult();
61 var suite1 = new tester.TestCaseResult({ 61 var suite1 = new tester.TestCaseResult({
...@@ -73,5 +73,5 @@ casper.test.begin('XUnitReporter() can handle a failed test', function suite() { ...@@ -73,5 +73,5 @@ casper.test.begin('XUnitReporter() can handle a failed test', function suite() {
73 casper.start().setContent(xunit.getXML()); 73 casper.start().setContent(xunit.getXML());
74 this.assertExists('testsuite[name="foo"][package="foo"][tests="1"][failures="1"] testcase[name="footext"] failure[type="footype"]'); 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'); 75 this.assertEquals(casper.getElementInfo('failure[type="footype"]').text, 'footext');
76 casper.test.done(2); 76 casper.test.done();
77 }); 77 });
......