big tester refactor - see details in CHANGELOG
Showing
11 changed files
with
227 additions
and
90 deletions
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 | if (phantom.casperTest) { | ||
154 | this.test = tester.create(this); | 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); |
1338 | try { | ||
1326 | stepResult = step.call(this, this.currentResponse); | 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; | ||
131 | if (!utils.isString(msg)) { | ||
132 | try { | 146 | try { |
133 | line = backtrace[0].line; | 147 | line = backtrace.filter(function(entry) { |
148 | return self.currentTestFile === entry.file; | ||
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", | ||
826 | standard: message, | ||
827 | message: message, | 860 | message: message, |
861 | file: this.currentTestFile, | ||
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 | 3 | /** | |
4 | var casper = require("casper").create({ | 4 | * Google sample testing. |
5 | logLevel: "debug" | 5 | * |
6 | }); | 6 | * Usage: |
7 | 7 | * | |
8 | casper.start("http://www.google.fr/", function() { | 8 | * $ casperjs test googletesting.js |
9 | this.test.assertTitle("Google", "google homepage title is the one expected"); | 9 | */ |
10 | this.test.assertExists('form[action="/search"]', "main form is found"); | 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"); | ||
11 | this.fill('form[action="/search"]', { | 14 | this.fill('form[action="/search"]', { |
12 | q: "foo" | 15 | q: "foo" |
13 | }, true); | 16 | }, true); |
14 | }); | 17 | }); |
15 | 18 | ||
16 | casper.then(function() { | 19 | casper.then(function() { |
17 | this.test.assertTitle("foo - Recherche Google", "google title is ok"); | 20 | test.assertTitle("!!foo - Recherche Google", "google title is ok"); |
18 | this.test.assertUrlMatch(/q=foo/, "search term has been submitted"); | 21 | test.assertUrlMatch(/q=foo/, "search term has been submitted"); |
19 | this.test.assertEval((function() { | 22 | test.assertEval(function() { |
20 | return __utils__.findAll("h3.r").length >= 10; | 23 | return __utils__.findAll("h3.r").length >= 10; |
21 | }), "google search for \"foo\" retrieves 10 or more results"); | 24 | }, "google search for \"foo\" retrieves 10 or more results"); |
22 | }); | 25 | }); |
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', | ... | ... |
This diff is collapsed.
Click to expand it.
... | @@ -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 | }); | ... | ... |
-
Please register or sign in to post a comment