jshint configuration, code cleaning
Showing
11 changed files
with
1458 additions
and
1293 deletions
.jshintconfig
0 → 100644
1 | { | ||
2 | "asi": true, | ||
3 | "browser": true, | ||
4 | "debug": true, | ||
5 | "devel": true, | ||
6 | "eqeqeq": true, | ||
7 | "evil": true, | ||
8 | "maxparams": 5, | ||
9 | "maxdepth": 3, | ||
10 | "maxstatements": 15, | ||
11 | "maxcomplexity": 7, | ||
12 | "regexdash": true, | ||
13 | "strict": true, | ||
14 | "sub": true, | ||
15 | "trailing": true, | ||
16 | "undef": true, | ||
17 | |||
18 | "predef" : [ | ||
19 | "exports", | ||
20 | "phantom", | ||
21 | "require", | ||
22 | "window" | ||
23 | ] | ||
24 | } |
.jshintignore
0 → 100644
... | @@ -208,12 +208,6 @@ function bootstrap(global) { | ... | @@ -208,12 +208,6 @@ function bootstrap(global) { |
208 | // custom global CasperError | 208 | // custom global CasperError |
209 | global.CasperError = function CasperError(msg) { | 209 | global.CasperError = function CasperError(msg) { |
210 | Error.call(this); | 210 | Error.call(this); |
211 | try { | ||
212 | // let's get where this error has been thrown from, if we can | ||
213 | this._from = arguments.callee.caller.name; | ||
214 | } catch (e) { | ||
215 | this._from = "anonymous"; | ||
216 | } | ||
217 | this.message = msg; | 211 | this.message = msg; |
218 | this.name = 'CasperError'; | 212 | this.name = 'CasperError'; |
219 | }; | 213 | }; | ... | ... |
... | @@ -33,6 +33,7 @@ | ... | @@ -33,6 +33,7 @@ |
33 | var colorizer = require('colorizer'); | 33 | var colorizer = require('colorizer'); |
34 | var events = require('events'); | 34 | var events = require('events'); |
35 | var fs = require('fs'); | 35 | var fs = require('fs'); |
36 | var http = require('http'); | ||
36 | var mouse = require('mouse'); | 37 | var mouse = require('mouse'); |
37 | var qs = require('querystring'); | 38 | var qs = require('querystring'); |
38 | var tester = require('tester'); | 39 | var tester = require('tester'); |
... | @@ -192,6 +193,7 @@ utils.inherits(Casper, events.EventEmitter); | ... | @@ -192,6 +193,7 @@ utils.inherits(Casper, events.EventEmitter); |
192 | */ | 193 | */ |
193 | Casper.prototype.back = function back() { | 194 | Casper.prototype.back = function back() { |
194 | "use strict"; | 195 | "use strict"; |
196 | this.checkStarted(); | ||
195 | return this.then(function _step() { | 197 | return this.then(function _step() { |
196 | this.emit('back'); | 198 | this.emit('back'); |
197 | this.evaluate(function _evaluate() { | 199 | this.evaluate(function _evaluate() { |
... | @@ -230,9 +232,8 @@ Casper.prototype.base64encode = function base64encode(url, method, data) { | ... | @@ -230,9 +232,8 @@ Casper.prototype.base64encode = function base64encode(url, method, data) { |
230 | */ | 232 | */ |
231 | Casper.prototype.capture = function capture(targetFile, clipRect) { | 233 | Casper.prototype.capture = function capture(targetFile, clipRect) { |
232 | "use strict"; | 234 | "use strict"; |
233 | if (!this.started) { | 235 | /*jshint maxstatements:20*/ |
234 | throw new CasperError("Casper not started, can't capture()"); | 236 | this.checkStarted(); |
235 | } | ||
236 | var previousClipRect; | 237 | var previousClipRect; |
237 | targetFile = fs.absolute(targetFile); | 238 | targetFile = fs.absolute(targetFile); |
238 | if (clipRect) { | 239 | if (clipRect) { |
... | @@ -270,16 +271,9 @@ Casper.prototype.capture = function capture(targetFile, clipRect) { | ... | @@ -270,16 +271,9 @@ Casper.prototype.capture = function capture(targetFile, clipRect) { |
270 | */ | 271 | */ |
271 | Casper.prototype.captureBase64 = function captureBase64(format, area) { | 272 | Casper.prototype.captureBase64 = function captureBase64(format, area) { |
272 | "use strict"; | 273 | "use strict"; |
273 | if (!this.started) { | 274 | /*jshint maxstatements:20*/ |
274 | throw new CasperError("Casper not started, can't captureBase64()"); | 275 | this.checkStarted(); |
275 | } | 276 | var base64, previousClipRect, formats = ['bmp', 'jpg', 'jpeg', 'png', 'ppm', 'tiff', 'xbm', 'xpm']; |
276 | if (!('renderBase64' in this.page)) { | ||
277 | this.warn('captureBase64() requires PhantomJS >= 1.6'); | ||
278 | return; | ||
279 | } | ||
280 | var base64; | ||
281 | var previousClipRect; | ||
282 | var formats = ['bmp', 'jpg', 'jpeg', 'png', 'ppm', 'tiff', 'xbm', 'xpm']; | ||
283 | if (formats.indexOf(format.toLowerCase()) === -1) { | 277 | if (formats.indexOf(format.toLowerCase()) === -1) { |
284 | throw new CasperError(f('Unsupported format "%s"', format)); | 278 | throw new CasperError(f('Unsupported format "%s"', format)); |
285 | } | 279 | } |
... | @@ -346,6 +340,20 @@ Casper.prototype.checkStep = function checkStep(self, onComplete) { | ... | @@ -346,6 +340,20 @@ Casper.prototype.checkStep = function checkStep(self, onComplete) { |
346 | }; | 340 | }; |
347 | 341 | ||
348 | /** | 342 | /** |
343 | * Checks if this instance is started. | ||
344 | * | ||
345 | * @return Boolean | ||
346 | * @throws CasperError | ||
347 | */ | ||
348 | Casper.prototype.checkStarted = function checkStarted() { | ||
349 | "use strict"; | ||
350 | if (!this.started) { | ||
351 | throw new CasperError(f("Casper is not started, can't execute `%s()`", | ||
352 | checkStarted.caller.name)); | ||
353 | } | ||
354 | }; | ||
355 | |||
356 | /** | ||
349 | * Clears the current page execution environment context. Useful to avoid | 357 | * Clears the current page execution environment context. Useful to avoid |
350 | * having previously loaded DOM contents being still active (refs #34). | 358 | * having previously loaded DOM contents being still active (refs #34). |
351 | * | 359 | * |
... | @@ -356,6 +364,7 @@ Casper.prototype.checkStep = function checkStep(self, onComplete) { | ... | @@ -356,6 +364,7 @@ Casper.prototype.checkStep = function checkStep(self, onComplete) { |
356 | */ | 364 | */ |
357 | Casper.prototype.clear = function clear() { | 365 | Casper.prototype.clear = function clear() { |
358 | "use strict"; | 366 | "use strict"; |
367 | this.checkStarted(); | ||
359 | this.page.content = ''; | 368 | this.page.content = ''; |
360 | return this; | 369 | return this; |
361 | }; | 370 | }; |
... | @@ -371,6 +380,7 @@ Casper.prototype.clear = function clear() { | ... | @@ -371,6 +380,7 @@ Casper.prototype.clear = function clear() { |
371 | */ | 380 | */ |
372 | Casper.prototype.click = function click(selector) { | 381 | Casper.prototype.click = function click(selector) { |
373 | "use strict"; | 382 | "use strict"; |
383 | this.checkStarted(); | ||
374 | return this.mouseEvent('click', selector); | 384 | return this.mouseEvent('click', selector); |
375 | }; | 385 | }; |
376 | 386 | ||
... | @@ -384,6 +394,7 @@ Casper.prototype.click = function click(selector) { | ... | @@ -384,6 +394,7 @@ Casper.prototype.click = function click(selector) { |
384 | */ | 394 | */ |
385 | Casper.prototype.clickLabel = function clickLabel(label, tag) { | 395 | Casper.prototype.clickLabel = function clickLabel(label, tag) { |
386 | "use strict"; | 396 | "use strict"; |
397 | this.checkStarted(); | ||
387 | tag = tag || "*"; | 398 | tag = tag || "*"; |
388 | var escapedLabel = label.toString().replace(/"/g, '\\"'); | 399 | var escapedLabel = label.toString().replace(/"/g, '\\"'); |
389 | var selector = selectXPath(f('//%s[text()="%s"]', tag, escapedLabel)); | 400 | var selector = selectXPath(f('//%s[text()="%s"]', tag, escapedLabel)); |
... | @@ -416,6 +427,7 @@ Casper.prototype.createStep = function createStep(fn, options) { | ... | @@ -416,6 +427,7 @@ Casper.prototype.createStep = function createStep(fn, options) { |
416 | */ | 427 | */ |
417 | Casper.prototype.debugHTML = function debugHTML(selector, outer) { | 428 | Casper.prototype.debugHTML = function debugHTML(selector, outer) { |
418 | "use strict"; | 429 | "use strict"; |
430 | this.checkStarted(); | ||
419 | return this.echo(this.getHTML(selector, outer)); | 431 | return this.echo(this.getHTML(selector, outer)); |
420 | }; | 432 | }; |
421 | 433 | ||
... | @@ -426,6 +438,7 @@ Casper.prototype.debugHTML = function debugHTML(selector, outer) { | ... | @@ -426,6 +438,7 @@ Casper.prototype.debugHTML = function debugHTML(selector, outer) { |
426 | */ | 438 | */ |
427 | Casper.prototype.debugPage = function debugPage() { | 439 | Casper.prototype.debugPage = function debugPage() { |
428 | "use strict"; | 440 | "use strict"; |
441 | this.checkStarted(); | ||
429 | this.echo(this.evaluate(function _evaluate() { | 442 | this.echo(this.evaluate(function _evaluate() { |
430 | return document.body.textContent || document.body.innerText; | 443 | return document.body.textContent || document.body.innerText; |
431 | })); | 444 | })); |
... | @@ -441,6 +454,7 @@ Casper.prototype.debugPage = function debugPage() { | ... | @@ -441,6 +454,7 @@ Casper.prototype.debugPage = function debugPage() { |
441 | */ | 454 | */ |
442 | Casper.prototype.die = function die(message, status) { | 455 | Casper.prototype.die = function die(message, status) { |
443 | "use strict"; | 456 | "use strict"; |
457 | this.checkStarted(); | ||
444 | this.result.status = "error"; | 458 | this.result.status = "error"; |
445 | this.result.time = new Date().getTime() - this.startTime; | 459 | this.result.time = new Date().getTime() - this.startTime; |
446 | if (!utils.isString(message) || !message.length) { | 460 | if (!utils.isString(message) || !message.length) { |
... | @@ -465,6 +479,7 @@ Casper.prototype.die = function die(message, status) { | ... | @@ -465,6 +479,7 @@ Casper.prototype.die = function die(message, status) { |
465 | */ | 479 | */ |
466 | Casper.prototype.download = function download(url, targetPath, method, data) { | 480 | Casper.prototype.download = function download(url, targetPath, method, data) { |
467 | "use strict"; | 481 | "use strict"; |
482 | this.checkStarted(); | ||
468 | var cu = require('clientutils').create(utils.mergeObjects({}, this.options)); | 483 | var cu = require('clientutils').create(utils.mergeObjects({}, this.options)); |
469 | try { | 484 | try { |
470 | fs.write(targetPath, cu.decode(this.base64encode(url, method, data)), 'wb'); | 485 | fs.write(targetPath, cu.decode(this.base64encode(url, method, data)), 'wb'); |
... | @@ -548,6 +563,7 @@ Casper.prototype.echo = function echo(text, style, pad) { | ... | @@ -548,6 +563,7 @@ Casper.prototype.echo = function echo(text, style, pad) { |
548 | */ | 563 | */ |
549 | Casper.prototype.evaluate = function evaluate(fn, context) { | 564 | Casper.prototype.evaluate = function evaluate(fn, context) { |
550 | "use strict"; | 565 | "use strict"; |
566 | this.checkStarted(); | ||
551 | // ensure client utils are always injected | 567 | // ensure client utils are always injected |
552 | this.injectClientUtils(); | 568 | this.injectClientUtils(); |
553 | // function context | 569 | // function context |
... | @@ -571,6 +587,7 @@ Casper.prototype.evaluate = function evaluate(fn, context) { | ... | @@ -571,6 +587,7 @@ Casper.prototype.evaluate = function evaluate(fn, context) { |
571 | */ | 587 | */ |
572 | Casper.prototype.evaluateOrDie = function evaluateOrDie(fn, message, status) { | 588 | Casper.prototype.evaluateOrDie = function evaluateOrDie(fn, message, status) { |
573 | "use strict"; | 589 | "use strict"; |
590 | this.checkStarted(); | ||
574 | if (!this.evaluate(fn)) { | 591 | if (!this.evaluate(fn)) { |
575 | return this.die(message, status); | 592 | return this.die(message, status); |
576 | } | 593 | } |
... | @@ -586,6 +603,7 @@ Casper.prototype.evaluateOrDie = function evaluateOrDie(fn, message, status) { | ... | @@ -586,6 +603,7 @@ Casper.prototype.evaluateOrDie = function evaluateOrDie(fn, message, status) { |
586 | */ | 603 | */ |
587 | Casper.prototype.exists = function exists(selector) { | 604 | Casper.prototype.exists = function exists(selector) { |
588 | "use strict"; | 605 | "use strict"; |
606 | this.checkStarted(); | ||
589 | return this.evaluate(function _evaluate(selector) { | 607 | return this.evaluate(function _evaluate(selector) { |
590 | return window.__utils__.exists(selector); | 608 | return window.__utils__.exists(selector); |
591 | }, { selector: selector }); | 609 | }, { selector: selector }); |
... | @@ -612,9 +630,7 @@ Casper.prototype.exit = function exit(status) { | ... | @@ -612,9 +630,7 @@ Casper.prototype.exit = function exit(status) { |
612 | */ | 630 | */ |
613 | Casper.prototype.fetchText = function fetchText(selector) { | 631 | Casper.prototype.fetchText = function fetchText(selector) { |
614 | "use strict"; | 632 | "use strict"; |
615 | if (!this.started) { | 633 | this.checkStarted(); |
616 | throw new CasperError("Casper not started, can't fetchText()"); | ||
617 | } | ||
618 | return this.evaluate(function _evaluate(selector) { | 634 | return this.evaluate(function _evaluate(selector) { |
619 | return window.__utils__.fetchText(selector); | 635 | return window.__utils__.fetchText(selector); |
620 | }, { selector: selector }); | 636 | }, { selector: selector }); |
... | @@ -629,6 +645,7 @@ Casper.prototype.fetchText = function fetchText(selector) { | ... | @@ -629,6 +645,7 @@ Casper.prototype.fetchText = function fetchText(selector) { |
629 | */ | 645 | */ |
630 | Casper.prototype.fill = function fill(selector, vals, submit) { | 646 | Casper.prototype.fill = function fill(selector, vals, submit) { |
631 | "use strict"; | 647 | "use strict"; |
648 | this.checkStarted(); | ||
632 | submit = submit === true ? submit : false; | 649 | submit = submit === true ? submit : false; |
633 | if (!utils.isObject(vals)) { | 650 | if (!utils.isObject(vals)) { |
634 | throw new CasperError("Form values must be provided as an object"); | 651 | throw new CasperError("Form values must be provided as an object"); |
... | @@ -643,15 +660,8 @@ Casper.prototype.fill = function fill(selector, vals, submit) { | ... | @@ -643,15 +660,8 @@ Casper.prototype.fill = function fill(selector, vals, submit) { |
643 | if (!fillResults) { | 660 | if (!fillResults) { |
644 | throw new CasperError("Unable to fill form"); | 661 | throw new CasperError("Unable to fill form"); |
645 | } else if (fillResults.errors.length > 0) { | 662 | } else if (fillResults.errors.length > 0) { |
646 | (function _each(self){ | 663 | throw new CasperError(f('Errors encountered while filling form: %s', |
647 | fillResults.errors.forEach(function _forEach(error) { | 664 | fillResults.errors.join('; '))); |
648 | throw new CasperError(error); | ||
649 | }); | ||
650 | })(this); | ||
651 | if (submit) { | ||
652 | this.warn("Errors encountered while filling form; submission aborted"); | ||
653 | submit = false; | ||
654 | } | ||
655 | } | 665 | } |
656 | // File uploads | 666 | // File uploads |
657 | if (fillResults.files && fillResults.files.length > 0) { | 667 | if (fillResults.files && fillResults.files.length > 0) { |
... | @@ -691,6 +701,7 @@ Casper.prototype.fill = function fill(selector, vals, submit) { | ... | @@ -691,6 +701,7 @@ Casper.prototype.fill = function fill(selector, vals, submit) { |
691 | */ | 701 | */ |
692 | Casper.prototype.forward = function forward(then) { | 702 | Casper.prototype.forward = function forward(then) { |
693 | "use strict"; | 703 | "use strict"; |
704 | this.checkStarted(); | ||
694 | return this.then(function _step() { | 705 | return this.then(function _step() { |
695 | this.emit('forward'); | 706 | this.emit('forward'); |
696 | this.evaluate(function _evaluate() { | 707 | this.evaluate(function _evaluate() { |
... | @@ -717,9 +728,7 @@ Casper.prototype.getColorizer = function getColorizer() { | ... | @@ -717,9 +728,7 @@ Casper.prototype.getColorizer = function getColorizer() { |
717 | */ | 728 | */ |
718 | Casper.prototype.getPageContent = function getPageContent() { | 729 | Casper.prototype.getPageContent = function getPageContent() { |
719 | "use strict"; | 730 | "use strict"; |
720 | if (!this.started) { | 731 | this.checkStarted(); |
721 | throw new CasperError("Casper not started, can't getPageContent()"); | ||
722 | } | ||
723 | var contentType = utils.getPropertyPath(this, 'currentResponse.contentType'); | 732 | var contentType = utils.getPropertyPath(this, 'currentResponse.contentType'); |
724 | if (!utils.isString(contentType)) { | 733 | if (!utils.isString(contentType)) { |
725 | return this.page.content; | 734 | return this.page.content; |
... | @@ -742,6 +751,7 @@ Casper.prototype.getPageContent = function getPageContent() { | ... | @@ -742,6 +751,7 @@ Casper.prototype.getPageContent = function getPageContent() { |
742 | */ | 751 | */ |
743 | Casper.prototype.getCurrentUrl = function getCurrentUrl() { | 752 | Casper.prototype.getCurrentUrl = function getCurrentUrl() { |
744 | "use strict"; | 753 | "use strict"; |
754 | this.checkStarted(); | ||
745 | var url = this.evaluate(function _evaluate() { | 755 | var url = this.evaluate(function _evaluate() { |
746 | return document.location.href; | 756 | return document.location.href; |
747 | }); | 757 | }); |
... | @@ -762,6 +772,7 @@ Casper.prototype.getCurrentUrl = function getCurrentUrl() { | ... | @@ -762,6 +772,7 @@ Casper.prototype.getCurrentUrl = function getCurrentUrl() { |
762 | */ | 772 | */ |
763 | Casper.prototype.getElementAttribute = Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) { | 773 | Casper.prototype.getElementAttribute = Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) { |
764 | "use strict"; | 774 | "use strict"; |
775 | this.checkStarted(); | ||
765 | return this.evaluate(function _evaluate(selector, attribute) { | 776 | return this.evaluate(function _evaluate(selector, attribute) { |
766 | return document.querySelector(selector).getAttribute(attribute); | 777 | return document.querySelector(selector).getAttribute(attribute); |
767 | }, { selector: selector, attribute: attribute }); | 778 | }, { selector: selector, attribute: attribute }); |
... | @@ -775,6 +786,7 @@ Casper.prototype.getElementAttribute = Casper.prototype.getElementAttr = functio | ... | @@ -775,6 +786,7 @@ Casper.prototype.getElementAttribute = Casper.prototype.getElementAttr = functio |
775 | */ | 786 | */ |
776 | Casper.prototype.getElementBounds = function getElementBounds(selector) { | 787 | Casper.prototype.getElementBounds = function getElementBounds(selector) { |
777 | "use strict"; | 788 | "use strict"; |
789 | this.checkStarted(); | ||
778 | if (!this.exists(selector)) { | 790 | if (!this.exists(selector)) { |
779 | throw new CasperError("No element matching selector found: " + selector); | 791 | throw new CasperError("No element matching selector found: " + selector); |
780 | } | 792 | } |
... | @@ -795,6 +807,7 @@ Casper.prototype.getElementBounds = function getElementBounds(selector) { | ... | @@ -795,6 +807,7 @@ Casper.prototype.getElementBounds = function getElementBounds(selector) { |
795 | */ | 807 | */ |
796 | Casper.prototype.getElementsBounds = function getElementBounds(selector) { | 808 | Casper.prototype.getElementsBounds = function getElementBounds(selector) { |
797 | "use strict"; | 809 | "use strict"; |
810 | this.checkStarted(); | ||
798 | if (!this.exists(selector)) { | 811 | if (!this.exists(selector)) { |
799 | throw new CasperError("No element matching selector found: " + selector); | 812 | throw new CasperError("No element matching selector found: " + selector); |
800 | } | 813 | } |
... | @@ -811,6 +824,7 @@ Casper.prototype.getElementsBounds = function getElementBounds(selector) { | ... | @@ -811,6 +824,7 @@ Casper.prototype.getElementsBounds = function getElementBounds(selector) { |
811 | */ | 824 | */ |
812 | Casper.prototype.getGlobal = function getGlobal(name) { | 825 | Casper.prototype.getGlobal = function getGlobal(name) { |
813 | "use strict"; | 826 | "use strict"; |
827 | this.checkStarted(); | ||
814 | var result = this.evaluate(function _evaluate(name) { | 828 | var result = this.evaluate(function _evaluate(name) { |
815 | var result = {}; | 829 | var result = {}; |
816 | try { | 830 | try { |
... | @@ -843,9 +857,7 @@ Casper.prototype.getGlobal = function getGlobal(name) { | ... | @@ -843,9 +857,7 @@ Casper.prototype.getGlobal = function getGlobal(name) { |
843 | */ | 857 | */ |
844 | Casper.prototype.getHTML = function getHTML(selector, outer) { | 858 | Casper.prototype.getHTML = function getHTML(selector, outer) { |
845 | "use strict"; | 859 | "use strict"; |
846 | if (!this.started) { | 860 | this.checkStarted(); |
847 | throw new CasperError("Casper not started, can't getHTML()"); | ||
848 | } | ||
849 | if (!selector) { | 861 | if (!selector) { |
850 | return this.page.content; | 862 | return this.page.content; |
851 | } | 863 | } |
... | @@ -865,12 +877,43 @@ Casper.prototype.getHTML = function getHTML(selector, outer) { | ... | @@ -865,12 +877,43 @@ Casper.prototype.getHTML = function getHTML(selector, outer) { |
865 | */ | 877 | */ |
866 | Casper.prototype.getTitle = function getTitle() { | 878 | Casper.prototype.getTitle = function getTitle() { |
867 | "use strict"; | 879 | "use strict"; |
880 | this.checkStarted(); | ||
868 | return this.evaluate(function _evaluate() { | 881 | return this.evaluate(function _evaluate() { |
869 | return document.title; | 882 | return document.title; |
870 | }); | 883 | }); |
871 | }; | 884 | }; |
872 | 885 | ||
873 | /** | 886 | /** |
887 | * Handles received HTTP resource. | ||
888 | * | ||
889 | * @param Object resource PhantomJS HTTP resource | ||
890 | */ | ||
891 | Casper.prototype.handleReceivedResource = function(resource) { | ||
892 | "use strict"; | ||
893 | if (resource.stage !== "end") { | ||
894 | return; | ||
895 | } | ||
896 | this.resources.push(resource); | ||
897 | if (resource.url !== this.requestUrl) { | ||
898 | return; | ||
899 | } | ||
900 | this.currentHTTPStatus = null; | ||
901 | this.currentResponse = undefined; | ||
902 | if (utils.isHTTPResource(resource)) { | ||
903 | this.currentResponse = resource; | ||
904 | this.currentHTTPStatus = resource.status; | ||
905 | this.emit('http.status.' + resource.status, resource); | ||
906 | if (utils.isObject(this.options.httpStatusHandlers) && | ||
907 | resource.status in this.options.httpStatusHandlers && | ||
908 | utils.isFunction(this.options.httpStatusHandlers[resource.status])) { | ||
909 | this.options.httpStatusHandlers[resource.status].call(this, this, resource); | ||
910 | } | ||
911 | } | ||
912 | this.currentUrl = resource.url; | ||
913 | this.emit('location.changed', resource.url); | ||
914 | }; | ||
915 | |||
916 | /** | ||
874 | * Initializes PhantomJS error handler. | 917 | * Initializes PhantomJS error handler. |
875 | * | 918 | * |
876 | */ | 919 | */ |
... | @@ -886,11 +929,39 @@ Casper.prototype.initErrorHandler = function initErrorHandler() { | ... | @@ -886,11 +929,39 @@ Casper.prototype.initErrorHandler = function initErrorHandler() { |
886 | }; | 929 | }; |
887 | 930 | ||
888 | /** | 931 | /** |
932 | * Injects configured client scripts. | ||
933 | * | ||
934 | * @return Casper | ||
935 | */ | ||
936 | Casper.prototype.injectClientScripts = function injectClientScripts() { | ||
937 | "use strict"; | ||
938 | this.checkStarted(); | ||
939 | if (!this.options.clientScripts) { | ||
940 | return; | ||
941 | } | ||
942 | if (utils.isString(this.options.clientScripts)) { | ||
943 | this.options.clientScripts = [this.options.clientScripts]; | ||
944 | } | ||
945 | if (!utils.isArray(this.options.clientScripts)) { | ||
946 | throw new CasperError("The clientScripts option must be an array"); | ||
947 | } | ||
948 | this.options.clientScripts.forEach(function _forEach(script) { | ||
949 | if (this.page.injectJs(script)) { | ||
950 | this.log(f('Automatically injected %s client side', script), "debug"); | ||
951 | } else { | ||
952 | this.warn('Failed injecting %s client side', script); | ||
953 | } | ||
954 | }); | ||
955 | return this; | ||
956 | }; | ||
957 | |||
958 | /** | ||
889 | * Injects Client-side utilities in current page context. | 959 | * Injects Client-side utilities in current page context. |
890 | * | 960 | * |
891 | */ | 961 | */ |
892 | Casper.prototype.injectClientUtils = function injectClientUtils() { | 962 | Casper.prototype.injectClientUtils = function injectClientUtils() { |
893 | "use strict"; | 963 | "use strict"; |
964 | this.checkStarted(); | ||
894 | var clientUtilsInjected = this.page.evaluate(function() { | 965 | var clientUtilsInjected = this.page.evaluate(function() { |
895 | return typeof window.__utils__ === "object"; | 966 | return typeof window.__utils__ === "object"; |
896 | }); | 967 | }); |
... | @@ -938,8 +1009,10 @@ Casper.prototype.log = function log(message, level, space) { | ... | @@ -938,8 +1009,10 @@ Casper.prototype.log = function log(message, level, space) { |
938 | if (level in this.logFormats && utils.isFunction(this.logFormats[level])) { | 1009 | if (level in this.logFormats && utils.isFunction(this.logFormats[level])) { |
939 | message = this.logFormats[level](message, level, space); | 1010 | message = this.logFormats[level](message, level, space); |
940 | } else { | 1011 | } else { |
941 | var levelStr = this.colorizer.colorize(f('[%s]', level), this.logStyles[level]); | 1012 | message = f('%s [%s] %s', |
942 | message = f('%s [%s] %s', levelStr, space, message); | 1013 | this.colorizer.colorize(f('[%s]', level), this.logStyles[level]), |
1014 | space, | ||
1015 | message); | ||
943 | } | 1016 | } |
944 | if (this.options.verbose) { | 1017 | if (this.options.verbose) { |
945 | this.echo(this.filter('log.message', message) || message); // direct output | 1018 | this.echo(this.filter('log.message', message) || message); // direct output |
... | @@ -961,6 +1034,7 @@ Casper.prototype.log = function log(message, level, space) { | ... | @@ -961,6 +1034,7 @@ Casper.prototype.log = function log(message, level, space) { |
961 | */ | 1034 | */ |
962 | Casper.prototype.mouseEvent = function mouseEvent(type, selector) { | 1035 | Casper.prototype.mouseEvent = function mouseEvent(type, selector) { |
963 | "use strict"; | 1036 | "use strict"; |
1037 | this.checkStarted(); | ||
964 | this.log("Mouse event '" + type + "' on selector: " + selector, "debug"); | 1038 | this.log("Mouse event '" + type + "' on selector: " + selector, "debug"); |
965 | if (!this.exists(selector)) { | 1039 | if (!this.exists(selector)) { |
966 | throw new CasperError(f("Cannot dispatch %s event on nonexistent selector: %s", type, selector)); | 1040 | throw new CasperError(f("Cannot dispatch %s event on nonexistent selector: %s", type, selector)); |
... | @@ -998,6 +1072,7 @@ Casper.prototype.mouseEvent = function mouseEvent(type, selector) { | ... | @@ -998,6 +1072,7 @@ Casper.prototype.mouseEvent = function mouseEvent(type, selector) { |
998 | */ | 1072 | */ |
999 | Casper.prototype.open = function open(location, settings) { | 1073 | Casper.prototype.open = function open(location, settings) { |
1000 | "use strict"; | 1074 | "use strict"; |
1075 | this.checkStarted(); | ||
1001 | // settings validation | 1076 | // settings validation |
1002 | if (!settings) { | 1077 | if (!settings) { |
1003 | settings = { | 1078 | settings = { |
... | @@ -1040,9 +1115,6 @@ Casper.prototype.open = function open(location, settings) { | ... | @@ -1040,9 +1115,6 @@ Casper.prototype.open = function open(location, settings) { |
1040 | } | 1115 | } |
1041 | this.emit('open', this.requestUrl, settings); | 1116 | this.emit('open', this.requestUrl, settings); |
1042 | this.log(f('opening url: %s, HTTP %s', this.requestUrl, settings.method.toUpperCase()), "debug"); | 1117 | this.log(f('opening url: %s, HTTP %s', this.requestUrl, settings.method.toUpperCase()), "debug"); |
1043 | if ('headers' in settings && phantom.version.minor < 6) { | ||
1044 | this.warn('Custom headers in outgoing requests are supported in PhantomJS >= 1.6'); | ||
1045 | } | ||
1046 | this.page.openUrl(this.requestUrl, { | 1118 | this.page.openUrl(this.requestUrl, { |
1047 | operation: settings.method, | 1119 | operation: settings.method, |
1048 | data: settings.data, | 1120 | data: settings.data, |
... | @@ -1060,9 +1132,7 @@ Casper.prototype.open = function open(location, settings) { | ... | @@ -1060,9 +1132,7 @@ Casper.prototype.open = function open(location, settings) { |
1060 | */ | 1132 | */ |
1061 | Casper.prototype.reload = function reload(then) { | 1133 | Casper.prototype.reload = function reload(then) { |
1062 | "use strict"; | 1134 | "use strict"; |
1063 | if (!this.started) { | 1135 | this.checkStarted(); |
1064 | throw new CasperError("Casper not started, can't reload()"); | ||
1065 | } | ||
1066 | this.evaluate(function() { | 1136 | this.evaluate(function() { |
1067 | window.location.reload(); | 1137 | window.location.reload(); |
1068 | }); | 1138 | }); |
... | @@ -1097,6 +1167,7 @@ Casper.prototype.repeat = function repeat(times, then) { | ... | @@ -1097,6 +1167,7 @@ Casper.prototype.repeat = function repeat(times, then) { |
1097 | */ | 1167 | */ |
1098 | Casper.prototype.resourceExists = function resourceExists(test) { | 1168 | Casper.prototype.resourceExists = function resourceExists(test) { |
1099 | "use strict"; | 1169 | "use strict"; |
1170 | this.checkStarted(); | ||
1100 | var testFn; | 1171 | var testFn; |
1101 | switch (utils.betterTypeOf(test)) { | 1172 | switch (utils.betterTypeOf(test)) { |
1102 | case "string": | 1173 | case "string": |
... | @@ -1128,6 +1199,7 @@ Casper.prototype.resourceExists = function resourceExists(test) { | ... | @@ -1128,6 +1199,7 @@ Casper.prototype.resourceExists = function resourceExists(test) { |
1128 | */ | 1199 | */ |
1129 | Casper.prototype.run = function run(onComplete, time) { | 1200 | Casper.prototype.run = function run(onComplete, time) { |
1130 | "use strict"; | 1201 | "use strict"; |
1202 | this.checkStarted(); | ||
1131 | if (!this.steps || this.steps.length < 1) { | 1203 | if (!this.steps || this.steps.length < 1) { |
1132 | this.log("No steps defined, aborting", "error"); | 1204 | this.log("No steps defined, aborting", "error"); |
1133 | return this; | 1205 | return this; |
... | @@ -1145,6 +1217,7 @@ Casper.prototype.run = function run(onComplete, time) { | ... | @@ -1145,6 +1217,7 @@ Casper.prototype.run = function run(onComplete, time) { |
1145 | */ | 1217 | */ |
1146 | Casper.prototype.runStep = function runStep(step) { | 1218 | Casper.prototype.runStep = function runStep(step) { |
1147 | "use strict"; | 1219 | "use strict"; |
1220 | this.checkStarted(); | ||
1148 | var skipLog = utils.isObject(step.options) && step.options.skipLog === true; | 1221 | var skipLog = utils.isObject(step.options) && step.options.skipLog === true; |
1149 | var stepInfo = f("Step %d/%d", this.step, this.steps.length); | 1222 | var stepInfo = f("Step %d/%d", this.step, this.steps.length); |
1150 | var stepResult; | 1223 | var stepResult; |
... | @@ -1184,9 +1257,7 @@ Casper.prototype.runStep = function runStep(step) { | ... | @@ -1184,9 +1257,7 @@ Casper.prototype.runStep = function runStep(step) { |
1184 | */ | 1257 | */ |
1185 | Casper.prototype.setHttpAuth = function setHttpAuth(username, password) { | 1258 | Casper.prototype.setHttpAuth = function setHttpAuth(username, password) { |
1186 | "use strict"; | 1259 | "use strict"; |
1187 | if (!this.started) { | 1260 | this.checkStarted(); |
1188 | throw new CasperError("Casper must be started in order to use the setHttpAuth() method"); | ||
1189 | } | ||
1190 | if (!utils.isString(username) || !utils.isString(password)) { | 1261 | if (!utils.isString(username) || !utils.isString(password)) { |
1191 | throw new CasperError("Both username and password must be strings"); | 1262 | throw new CasperError("Both username and password must be strings"); |
1192 | } | 1263 | } |
... | @@ -1258,6 +1329,7 @@ Casper.prototype.start = function start(location, then) { | ... | @@ -1258,6 +1329,7 @@ Casper.prototype.start = function start(location, then) { |
1258 | * @return Object | 1329 | * @return Object |
1259 | */ | 1330 | */ |
1260 | Casper.prototype.status = function status(asString) { | 1331 | Casper.prototype.status = function status(asString) { |
1332 | "use strict"; | ||
1261 | var properties = ['currentHTTPStatus', 'loadInProgress', 'navigationRequested', | 1333 | var properties = ['currentHTTPStatus', 'loadInProgress', 'navigationRequested', |
1262 | 'options', 'pendingWait', 'requestUrl', 'started', 'step', 'url']; | 1334 | 'options', 'pendingWait', 'requestUrl', 'started', 'step', 'url']; |
1263 | var currentStatus = {}; | 1335 | var currentStatus = {}; |
... | @@ -1275,9 +1347,7 @@ Casper.prototype.status = function status(asString) { | ... | @@ -1275,9 +1347,7 @@ Casper.prototype.status = function status(asString) { |
1275 | */ | 1347 | */ |
1276 | Casper.prototype.then = function then(step) { | 1348 | Casper.prototype.then = function then(step) { |
1277 | "use strict"; | 1349 | "use strict"; |
1278 | if (!this.started) { | 1350 | this.checkStarted(); |
1279 | throw new CasperError("Casper not started; please use Casper#start"); | ||
1280 | } | ||
1281 | if (!utils.isFunction(step)) { | 1351 | if (!utils.isFunction(step)) { |
1282 | throw new CasperError("You can only define a step as a function"); | 1352 | throw new CasperError("You can only define a step as a function"); |
1283 | } | 1353 | } |
... | @@ -1315,6 +1385,7 @@ Casper.prototype.then = function then(step) { | ... | @@ -1315,6 +1385,7 @@ Casper.prototype.then = function then(step) { |
1315 | */ | 1385 | */ |
1316 | Casper.prototype.thenClick = function thenClick(selector, then, fallbackToHref) { | 1386 | Casper.prototype.thenClick = function thenClick(selector, then, fallbackToHref) { |
1317 | "use strict"; | 1387 | "use strict"; |
1388 | this.checkStarted(); | ||
1318 | if (arguments.length > 2) { | 1389 | if (arguments.length > 2) { |
1319 | this.emit("deprecated", "The thenClick() method does not process the fallbackToHref argument since 0.6"); | 1390 | this.emit("deprecated", "The thenClick() method does not process the fallbackToHref argument since 0.6"); |
1320 | } | 1391 | } |
... | @@ -1335,6 +1406,7 @@ Casper.prototype.thenClick = function thenClick(selector, then, fallbackToHref) | ... | @@ -1335,6 +1406,7 @@ Casper.prototype.thenClick = function thenClick(selector, then, fallbackToHref) |
1335 | */ | 1406 | */ |
1336 | Casper.prototype.thenEvaluate = function thenEvaluate(fn, context) { | 1407 | Casper.prototype.thenEvaluate = function thenEvaluate(fn, context) { |
1337 | "use strict"; | 1408 | "use strict"; |
1409 | this.checkStarted(); | ||
1338 | return this.then(function _step() { | 1410 | return this.then(function _step() { |
1339 | this.evaluate(fn, context); | 1411 | this.evaluate(fn, context); |
1340 | }); | 1412 | }); |
... | @@ -1350,6 +1422,7 @@ Casper.prototype.thenEvaluate = function thenEvaluate(fn, context) { | ... | @@ -1350,6 +1422,7 @@ Casper.prototype.thenEvaluate = function thenEvaluate(fn, context) { |
1350 | */ | 1422 | */ |
1351 | Casper.prototype.thenOpen = function thenOpen(location, settings, then) { | 1423 | Casper.prototype.thenOpen = function thenOpen(location, settings, then) { |
1352 | "use strict"; | 1424 | "use strict"; |
1425 | this.checkStarted(); | ||
1353 | if (!(settings && !utils.isFunction(settings))) { | 1426 | if (!(settings && !utils.isFunction(settings))) { |
1354 | then = settings; | 1427 | then = settings; |
1355 | settings = null; | 1428 | settings = null; |
... | @@ -1375,6 +1448,7 @@ Casper.prototype.thenOpen = function thenOpen(location, settings, then) { | ... | @@ -1375,6 +1448,7 @@ Casper.prototype.thenOpen = function thenOpen(location, settings, then) { |
1375 | */ | 1448 | */ |
1376 | Casper.prototype.thenOpenAndEvaluate = function thenOpenAndEvaluate(location, fn, context) { | 1449 | Casper.prototype.thenOpenAndEvaluate = function thenOpenAndEvaluate(location, fn, context) { |
1377 | "use strict"; | 1450 | "use strict"; |
1451 | this.checkStarted(); | ||
1378 | return this.thenOpen(location).thenEvaluate(fn, context); | 1452 | return this.thenOpen(location).thenEvaluate(fn, context); |
1379 | }; | 1453 | }; |
1380 | 1454 | ||
... | @@ -1384,6 +1458,7 @@ Casper.prototype.thenOpenAndEvaluate = function thenOpenAndEvaluate(location, fn | ... | @@ -1384,6 +1458,7 @@ Casper.prototype.thenOpenAndEvaluate = function thenOpenAndEvaluate(location, fn |
1384 | * @return String | 1458 | * @return String |
1385 | */ | 1459 | */ |
1386 | Casper.prototype.toString = function toString() { | 1460 | Casper.prototype.toString = function toString() { |
1461 | "use strict"; | ||
1387 | return '[object Casper], currently at ' + this.getCurrentUrl(); | 1462 | return '[object Casper], currently at ' + this.getCurrentUrl(); |
1388 | }; | 1463 | }; |
1389 | 1464 | ||
... | @@ -1395,9 +1470,7 @@ Casper.prototype.toString = function toString() { | ... | @@ -1395,9 +1470,7 @@ Casper.prototype.toString = function toString() { |
1395 | */ | 1470 | */ |
1396 | Casper.prototype.userAgent = function userAgent(agent) { | 1471 | Casper.prototype.userAgent = function userAgent(agent) { |
1397 | "use strict"; | 1472 | "use strict"; |
1398 | if (!this.started) { | 1473 | this.checkStarted(); |
1399 | throw new CasperError("Casper not started, can't set userAgent"); | ||
1400 | } | ||
1401 | this.options.pageSettings.userAgent = this.page.settings.userAgent = agent; | 1474 | this.options.pageSettings.userAgent = this.page.settings.userAgent = agent; |
1402 | return this; | 1475 | return this; |
1403 | }; | 1476 | }; |
... | @@ -1411,9 +1484,7 @@ Casper.prototype.userAgent = function userAgent(agent) { | ... | @@ -1411,9 +1484,7 @@ Casper.prototype.userAgent = function userAgent(agent) { |
1411 | */ | 1484 | */ |
1412 | Casper.prototype.viewport = function viewport(width, height) { | 1485 | Casper.prototype.viewport = function viewport(width, height) { |
1413 | "use strict"; | 1486 | "use strict"; |
1414 | if (!this.started) { | 1487 | this.checkStarted(); |
1415 | throw new CasperError("Casper must be started in order to set viewport at runtime"); | ||
1416 | } | ||
1417 | if (!utils.isNumber(width) || !utils.isNumber(height) || width <= 0 || height <= 0) { | 1488 | if (!utils.isNumber(width) || !utils.isNumber(height) || width <= 0 || height <= 0) { |
1418 | throw new CasperError(f("Invalid viewport: %dx%d", width, height)); | 1489 | throw new CasperError(f("Invalid viewport: %dx%d", width, height)); |
1419 | } | 1490 | } |
... | @@ -1435,6 +1506,7 @@ Casper.prototype.viewport = function viewport(width, height) { | ... | @@ -1435,6 +1506,7 @@ Casper.prototype.viewport = function viewport(width, height) { |
1435 | */ | 1506 | */ |
1436 | Casper.prototype.visible = function visible(selector) { | 1507 | Casper.prototype.visible = function visible(selector) { |
1437 | "use strict"; | 1508 | "use strict"; |
1509 | this.checkStarted(); | ||
1438 | return this.evaluate(function _evaluate(selector) { | 1510 | return this.evaluate(function _evaluate(selector) { |
1439 | return window.__utils__.visible(selector); | 1511 | return window.__utils__.visible(selector); |
1440 | }, { selector: selector }); | 1512 | }, { selector: selector }); |
... | @@ -1463,6 +1535,7 @@ Casper.prototype.warn = function warn(message) { | ... | @@ -1463,6 +1535,7 @@ Casper.prototype.warn = function warn(message) { |
1463 | */ | 1535 | */ |
1464 | Casper.prototype.wait = function wait(timeout, then) { | 1536 | Casper.prototype.wait = function wait(timeout, then) { |
1465 | "use strict"; | 1537 | "use strict"; |
1538 | this.checkStarted(); | ||
1466 | timeout = ~~timeout; | 1539 | timeout = ~~timeout; |
1467 | if (timeout < 1) { | 1540 | if (timeout < 1) { |
1468 | this.die("wait() only accepts a positive integer > 0 as a timeout value"); | 1541 | this.die("wait() only accepts a positive integer > 0 as a timeout value"); |
... | @@ -1505,6 +1578,7 @@ Casper.prototype.waitDone = function waitDone() { | ... | @@ -1505,6 +1578,7 @@ Casper.prototype.waitDone = function waitDone() { |
1505 | */ | 1578 | */ |
1506 | Casper.prototype.waitFor = function waitFor(testFx, then, onTimeout, timeout) { | 1579 | Casper.prototype.waitFor = function waitFor(testFx, then, onTimeout, timeout) { |
1507 | "use strict"; | 1580 | "use strict"; |
1581 | this.checkStarted(); | ||
1508 | timeout = timeout ? timeout : this.options.waitTimeout; | 1582 | timeout = timeout ? timeout : this.options.waitTimeout; |
1509 | if (!utils.isFunction(testFx)) { | 1583 | if (!utils.isFunction(testFx)) { |
1510 | this.die("waitFor() needs a test function"); | 1584 | this.die("waitFor() needs a test function"); |
... | @@ -1553,6 +1627,7 @@ Casper.prototype.waitFor = function waitFor(testFx, then, onTimeout, timeout) { | ... | @@ -1553,6 +1627,7 @@ Casper.prototype.waitFor = function waitFor(testFx, then, onTimeout, timeout) { |
1553 | */ | 1627 | */ |
1554 | Casper.prototype.waitForResource = function waitForResource(test, then, onTimeout, timeout) { | 1628 | Casper.prototype.waitForResource = function waitForResource(test, then, onTimeout, timeout) { |
1555 | "use strict"; | 1629 | "use strict"; |
1630 | this.checkStarted(); | ||
1556 | timeout = timeout ? timeout : this.options.waitTimeout; | 1631 | timeout = timeout ? timeout : this.options.waitTimeout; |
1557 | return this.waitFor(function _check() { | 1632 | return this.waitFor(function _check() { |
1558 | return this.resourceExists(test); | 1633 | return this.resourceExists(test); |
... | @@ -1571,6 +1646,7 @@ Casper.prototype.waitForResource = function waitForResource(test, then, onTimeou | ... | @@ -1571,6 +1646,7 @@ Casper.prototype.waitForResource = function waitForResource(test, then, onTimeou |
1571 | */ | 1646 | */ |
1572 | Casper.prototype.waitForSelector = function waitForSelector(selector, then, onTimeout, timeout) { | 1647 | Casper.prototype.waitForSelector = function waitForSelector(selector, then, onTimeout, timeout) { |
1573 | "use strict"; | 1648 | "use strict"; |
1649 | this.checkStarted(); | ||
1574 | timeout = timeout ? timeout : this.options.waitTimeout; | 1650 | timeout = timeout ? timeout : this.options.waitTimeout; |
1575 | return this.waitFor(function _check() { | 1651 | return this.waitFor(function _check() { |
1576 | return this.exists(selector); | 1652 | return this.exists(selector); |
... | @@ -1589,6 +1665,7 @@ Casper.prototype.waitForSelector = function waitForSelector(selector, then, onTi | ... | @@ -1589,6 +1665,7 @@ Casper.prototype.waitForSelector = function waitForSelector(selector, then, onTi |
1589 | */ | 1665 | */ |
1590 | Casper.prototype.waitWhileSelector = function waitWhileSelector(selector, then, onTimeout, timeout) { | 1666 | Casper.prototype.waitWhileSelector = function waitWhileSelector(selector, then, onTimeout, timeout) { |
1591 | "use strict"; | 1667 | "use strict"; |
1668 | this.checkStarted(); | ||
1592 | timeout = timeout ? timeout : this.options.waitTimeout; | 1669 | timeout = timeout ? timeout : this.options.waitTimeout; |
1593 | return this.waitFor(function _check() { | 1670 | return this.waitFor(function _check() { |
1594 | return !this.exists(selector); | 1671 | return !this.exists(selector); |
... | @@ -1607,6 +1684,7 @@ Casper.prototype.waitWhileSelector = function waitWhileSelector(selector, then, | ... | @@ -1607,6 +1684,7 @@ Casper.prototype.waitWhileSelector = function waitWhileSelector(selector, then, |
1607 | */ | 1684 | */ |
1608 | Casper.prototype.waitUntilVisible = function waitUntilVisible(selector, then, onTimeout, timeout) { | 1685 | Casper.prototype.waitUntilVisible = function waitUntilVisible(selector, then, onTimeout, timeout) { |
1609 | "use strict"; | 1686 | "use strict"; |
1687 | this.checkStarted(); | ||
1610 | timeout = timeout ? timeout : this.options.waitTimeout; | 1688 | timeout = timeout ? timeout : this.options.waitTimeout; |
1611 | return this.waitFor(function _check() { | 1689 | return this.waitFor(function _check() { |
1612 | return this.visible(selector); | 1690 | return this.visible(selector); |
... | @@ -1625,6 +1703,7 @@ Casper.prototype.waitUntilVisible = function waitUntilVisible(selector, then, on | ... | @@ -1625,6 +1703,7 @@ Casper.prototype.waitUntilVisible = function waitUntilVisible(selector, then, on |
1625 | */ | 1703 | */ |
1626 | Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, onTimeout, timeout) { | 1704 | Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, onTimeout, timeout) { |
1627 | "use strict"; | 1705 | "use strict"; |
1706 | this.checkStarted(); | ||
1628 | timeout = timeout ? timeout : this.options.waitTimeout; | 1707 | timeout = timeout ? timeout : this.options.waitTimeout; |
1629 | return this.waitFor(function _check() { | 1708 | return this.waitFor(function _check() { |
1630 | return !this.visible(selector); | 1709 | return !this.visible(selector); |
... | @@ -1639,17 +1718,11 @@ Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, on | ... | @@ -1639,17 +1718,11 @@ Casper.prototype.waitWhileVisible = function waitWhileVisible(selector, then, on |
1639 | */ | 1718 | */ |
1640 | Casper.prototype.zoom = function zoom(factor) { | 1719 | Casper.prototype.zoom = function zoom(factor) { |
1641 | "use strict"; | 1720 | "use strict"; |
1642 | if (!this.started) { | 1721 | this.checkStarted(); |
1643 | throw new CasperError("Casper has not been started, can't set zoom factor"); | ||
1644 | } | ||
1645 | if (!utils.isNumber(factor) || factor <= 0) { | 1722 | if (!utils.isNumber(factor) || factor <= 0) { |
1646 | throw new CasperError("Invalid zoom factor: " + factor); | 1723 | throw new CasperError("Invalid zoom factor: " + factor); |
1647 | } | 1724 | } |
1648 | if ('zoomFactor' in this.page) { | 1725 | this.page.zoomFactor = factor; |
1649 | this.page.zoomFactor = factor; | ||
1650 | } else { | ||
1651 | this.warn("zoom() requires PhantomJS >= 1.6"); | ||
1652 | } | ||
1653 | return this; | 1726 | return this; |
1654 | }; | 1727 | }; |
1655 | 1728 | ||
... | @@ -1662,7 +1735,7 @@ Casper.prototype.zoom = function zoom(factor) { | ... | @@ -1662,7 +1735,7 @@ Casper.prototype.zoom = function zoom(factor) { |
1662 | */ | 1735 | */ |
1663 | Casper.extend = function(proto) { | 1736 | Casper.extend = function(proto) { |
1664 | "use strict"; | 1737 | "use strict"; |
1665 | this.warn('Casper.extend() has been deprecated since 0.6; check the docs'); | 1738 | this.emit("deprecated", "Casper.extend() has been deprecated since 0.6; check the docs") |
1666 | if (!utils.isObject(proto)) { | 1739 | if (!utils.isObject(proto)) { |
1667 | throw new CasperError("extends() only accept objects as prototypes"); | 1740 | throw new CasperError("extends() only accept objects as prototypes"); |
1668 | } | 1741 | } |
... | @@ -1731,21 +1804,7 @@ function createPage(casper) { | ... | @@ -1731,21 +1804,7 @@ function createPage(casper) { |
1731 | casper.options.onLoadError.call(casper, casper, casper.requestUrl, status); | 1804 | casper.options.onLoadError.call(casper, casper, casper.requestUrl, status); |
1732 | } | 1805 | } |
1733 | } | 1806 | } |
1734 | if (casper.options.clientScripts) { | 1807 | casper.injectClientScripts(); |
1735 | if (utils.isString(casper.options.clientScripts)) { | ||
1736 | casper.options.clientScripts = [casper.options.clientScripts]; | ||
1737 | } | ||
1738 | if (!utils.isArray(casper.options.clientScripts)) { | ||
1739 | throw new CasperError("The clientScripts option must be an array"); | ||
1740 | } | ||
1741 | casper.options.clientScripts.forEach(function _forEach(script) { | ||
1742 | if (casper.page.injectJs(script)) { | ||
1743 | casper.log(f('Automatically injected %s client side', script), "debug"); | ||
1744 | } else { | ||
1745 | casper.warn('Failed injecting %s client side', script); | ||
1746 | } | ||
1747 | }); | ||
1748 | } | ||
1749 | // Client-side utils injection | 1808 | // Client-side utils injection |
1750 | casper.injectClientUtils(); | 1809 | casper.injectClientUtils(); |
1751 | // history | 1810 | // history |
... | @@ -1765,34 +1824,12 @@ function createPage(casper) { | ... | @@ -1765,34 +1824,12 @@ function createPage(casper) { |
1765 | return casper.filter('page.prompt', message, value); | 1824 | return casper.filter('page.prompt', message, value); |
1766 | }; | 1825 | }; |
1767 | page.onResourceReceived = function onResourceReceived(resource) { | 1826 | page.onResourceReceived = function onResourceReceived(resource) { |
1768 | if (utils.isHTTPResource(resource)) { | 1827 | http.augmentResponse(resource); |
1769 | require('http').augmentResponse(resource); | ||
1770 | } else { | ||
1771 | casper.log(f('Non-HTTP resource received from %s', resource.url), "debug"); | ||
1772 | } | ||
1773 | casper.emit('resource.received', resource); | 1828 | casper.emit('resource.received', resource); |
1774 | if (utils.isFunction(casper.options.onResourceReceived)) { | 1829 | if (utils.isFunction(casper.options.onResourceReceived)) { |
1775 | casper.options.onResourceReceived.call(casper, casper, resource); | 1830 | casper.options.onResourceReceived.call(casper, casper, resource); |
1776 | } | 1831 | } |
1777 | if (resource.stage === "end") { | 1832 | casper.handleReceivedResource(resource); |
1778 | casper.resources.push(resource); | ||
1779 | } | ||
1780 | if (resource.url === casper.requestUrl && resource.stage === "end") { | ||
1781 | casper.currentHTTPStatus = null; | ||
1782 | casper.currentResponse = undefined; | ||
1783 | if (utils.isHTTPResource(resource)) { | ||
1784 | casper.currentResponse = resource; | ||
1785 | casper.currentHTTPStatus = resource.status; | ||
1786 | casper.emit('http.status.' + resource.status, resource); | ||
1787 | if (utils.isObject(casper.options.httpStatusHandlers) && | ||
1788 | resource.status in casper.options.httpStatusHandlers && | ||
1789 | utils.isFunction(casper.options.httpStatusHandlers[resource.status])) { | ||
1790 | casper.options.httpStatusHandlers[resource.status].call(casper, casper, resource); | ||
1791 | } | ||
1792 | } | ||
1793 | casper.currentUrl = resource.url; | ||
1794 | casper.emit('location.changed', resource.url); | ||
1795 | } | ||
1796 | }; | 1833 | }; |
1797 | page.onResourceRequested = function onResourceRequested(request) { | 1834 | page.onResourceRequested = function onResourceRequested(request) { |
1798 | casper.emit('resource.requested', request); | 1835 | casper.emit('resource.requested', request); | ... | ... |
... | @@ -75,6 +75,7 @@ | ... | @@ -75,6 +75,7 @@ |
75 | * @return string | 75 | * @return string |
76 | */ | 76 | */ |
77 | this.decode = function decode(str) { | 77 | this.decode = function decode(str) { |
78 | /*jshint maxstatements:30 maxcomplexity:30 */ | ||
78 | var c1, c2, c3, c4, i = 0, len = str.length, out = ""; | 79 | var c1, c2, c3, c4, i = 0, len = str.length, out = ""; |
79 | while (i < len) { | 80 | while (i < len) { |
80 | do { | 81 | do { |
... | @@ -123,6 +124,7 @@ | ... | @@ -123,6 +124,7 @@ |
123 | * @return string | 124 | * @return string |
124 | */ | 125 | */ |
125 | this.encode = function encode(str) { | 126 | this.encode = function encode(str) { |
127 | /*jshint maxstatements:30 */ | ||
126 | var out = "", i = 0, len = str.length, c1, c2, c3; | 128 | var out = "", i = 0, len = str.length, c1, c2, c3; |
127 | while (i < len) { | 129 | while (i < len) { |
128 | c1 = str.charCodeAt(i++) & 0xff; | 130 | c1 = str.charCodeAt(i++) & 0xff; |
... | @@ -183,9 +185,9 @@ | ... | @@ -183,9 +185,9 @@ |
183 | /** | 185 | /** |
184 | * Fills a form with provided field values, and optionnaly submits it. | 186 | * Fills a form with provided field values, and optionnaly submits it. |
185 | * | 187 | * |
186 | * @param HTMLElement|String form A form element, or a CSS3 selector to a form element | 188 | * @param HTMLElement|String form A form element, or a CSS3 selector to a form element |
187 | * @param Object vals Field values | 189 | * @param Object vals Field values |
188 | * @return Object An object containing setting result for each field, including file uploads | 190 | * @return Object An object containing setting result for each field, including file uploads |
189 | */ | 191 | */ |
190 | this.fill = function fill(form, vals) { | 192 | this.fill = function fill(form, vals) { |
191 | var out = { | 193 | var out = { |
... | @@ -295,37 +297,14 @@ | ... | @@ -295,37 +297,14 @@ |
295 | * Retrieves string contents from a binary file behind an url. Silently | 297 | * Retrieves string contents from a binary file behind an url. Silently |
296 | * fails but log errors. | 298 | * fails but log errors. |
297 | * | 299 | * |
298 | * @param String url | 300 | * @param String url Url. |
299 | * @param String method | 301 | * @param String method HTTP method. |
300 | * @param Object data | 302 | * @param Object data Request parameters. |
301 | * @return string | 303 | * @return String |
302 | */ | 304 | */ |
303 | this.getBinary = function getBinary(url, method, data) { | 305 | this.getBinary = function getBinary(url, method, data) { |
304 | try { | 306 | try { |
305 | var xhr = new XMLHttpRequest(), dataString = ""; | 307 | return this.sendAJAX(url, method, data, false); |
306 | if (typeof method !== "string" || ["GET", "POST"].indexOf(method.toUpperCase()) === -1) { | ||
307 | method = "GET"; | ||
308 | } else { | ||
309 | method = method.toUpperCase(); | ||
310 | } | ||
311 | xhr.open(method, url, false); | ||
312 | this.log("getBinary(): Using HTTP method: '" + method + "'", "debug"); | ||
313 | xhr.overrideMimeType("text/plain; charset=x-user-defined"); | ||
314 | if (method === "POST") { | ||
315 | if (typeof data === "object") { | ||
316 | var dataList = []; | ||
317 | for (var k in data) { | ||
318 | dataList.push(encodeURIComponent(k) + "=" + encodeURIComponent(data[k].toString())); | ||
319 | } | ||
320 | dataString = dataList.join('&'); | ||
321 | this.log("getBinary(): Using request data: '" + dataString + "'", "debug"); | ||
322 | } else if (typeof data === "string") { | ||
323 | dataString = data; | ||
324 | } | ||
325 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | ||
326 | } | ||
327 | xhr.send(method === "POST" ? dataString : null); | ||
328 | return xhr.responseText; | ||
329 | } catch (e) { | 308 | } catch (e) { |
330 | if (e.name === "NETWORK_ERR" && e.code === 101) { | 309 | if (e.name === "NETWORK_ERR" && e.code === 101) { |
331 | this.log("getBinary(): Unfortunately, casperjs cannot make cross domain ajax requests", "warning"); | 310 | this.log("getBinary(): Unfortunately, casperjs cannot make cross domain ajax requests", "warning"); |
... | @@ -529,6 +508,42 @@ | ... | @@ -529,6 +508,42 @@ |
529 | }; | 508 | }; |
530 | 509 | ||
531 | /** | 510 | /** |
511 | * Performs an AJAX request. | ||
512 | * | ||
513 | * @param String url Url. | ||
514 | * @param String method HTTP method. | ||
515 | * @param Object data Request parameters. | ||
516 | * @param Boolean async Asynchroneous request? (default: false) | ||
517 | * @return String Response text. | ||
518 | */ | ||
519 | this.sendAJAX = function sendAJAX(url, method, data, async) { | ||
520 | var xhr = new XMLHttpRequest(), dataString = ""; | ||
521 | if (typeof method !== "string" || ["GET", "POST"].indexOf(method.toUpperCase()) === -1) { | ||
522 | method = "GET"; | ||
523 | } else { | ||
524 | method = method.toUpperCase(); | ||
525 | } | ||
526 | xhr.open(method, url, !!async); | ||
527 | this.log("getBinary(): Using HTTP method: '" + method + "'", "debug"); | ||
528 | xhr.overrideMimeType("text/plain; charset=x-user-defined"); | ||
529 | if (method === "POST") { | ||
530 | if (typeof data === "object") { | ||
531 | var dataList = []; | ||
532 | for (var k in data) { | ||
533 | dataList.push(encodeURIComponent(k) + "=" + encodeURIComponent(data[k].toString())); | ||
534 | } | ||
535 | dataString = dataList.join('&'); | ||
536 | this.log("sendAJAX(): Using request data: '" + dataString + "'", "debug"); | ||
537 | } else if (typeof data === "string") { | ||
538 | dataString = data; | ||
539 | } | ||
540 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | ||
541 | } | ||
542 | xhr.send(method === "POST" ? dataString : null); | ||
543 | return xhr.responseText; | ||
544 | }; | ||
545 | |||
546 | /** | ||
532 | * Sets a field (or a set of fields) value. Fails silently, but log | 547 | * Sets a field (or a set of fields) value. Fails silently, but log |
533 | * error messages. | 548 | * error messages. |
534 | * | 549 | * | ... | ... |
... | @@ -28,6 +28,8 @@ | ... | @@ -28,6 +28,8 @@ |
28 | * | 28 | * |
29 | */ | 29 | */ |
30 | 30 | ||
31 | var utils = require('utils'); | ||
32 | |||
31 | /* | 33 | /* |
32 | * Building an Array subclass | 34 | * Building an Array subclass |
33 | */ | 35 | */ |
... | @@ -59,7 +61,8 @@ responseHeaders.prototype.get = function get(name){ | ... | @@ -59,7 +61,8 @@ responseHeaders.prototype.get = function get(name){ |
59 | * @param mixed response Phantom response or undefined (generally with local files) | 61 | * @param mixed response Phantom response or undefined (generally with local files) |
60 | */ | 62 | */ |
61 | exports.augmentResponse = function(response) { | 63 | exports.augmentResponse = function(response) { |
62 | if (response === undefined) { | 64 | "use strict"; |
65 | if (!utils.isHTTPResource(response)) { | ||
63 | return; | 66 | return; |
64 | } | 67 | } |
65 | response.headers.__proto__ = responseHeaders.prototype; | 68 | response.headers.__proto__ = responseHeaders.prototype; | ... | ... |
... | @@ -53,6 +53,8 @@ var Tester = function Tester(casper, options) { | ... | @@ -53,6 +53,8 @@ var Tester = function Tester(casper, options) { |
53 | throw new CasperError("Tester needs a Casper instance"); | 53 | throw new CasperError("Tester needs a Casper instance"); |
54 | } | 54 | } |
55 | 55 | ||
56 | this.casper = casper; | ||
57 | |||
56 | this.currentTestFile = null; | 58 | this.currentTestFile = null; |
57 | this.currentSuiteNum = 0; | 59 | this.currentSuiteNum = 0; |
58 | this.exporter = require('xunit').create(); | 60 | this.exporter = require('xunit').create(); |
... | @@ -132,7 +134,7 @@ var Tester = function Tester(casper, options) { | ... | @@ -132,7 +134,7 @@ var Tester = function Tester(casper, options) { |
132 | } catch (e) { | 134 | } catch (e) { |
133 | try { | 135 | try { |
134 | comment += utils.serialize(failure.values[name].toString()); | 136 | comment += utils.serialize(failure.values[name].toString()); |
135 | } catch (e) { | 137 | } catch (e2) { |
136 | comment += '(unserializable value)'; | 138 | comment += '(unserializable value)'; |
137 | } | 139 | } |
138 | } | 140 | } |
... | @@ -140,789 +142,829 @@ var Tester = function Tester(casper, options) { | ... | @@ -140,789 +142,829 @@ var Tester = function Tester(casper, options) { |
140 | } | 142 | } |
141 | } | 143 | } |
142 | }); | 144 | }); |
145 | }; | ||
143 | 146 | ||
144 | // methods | 147 | // Tester class is an EventEmitter |
145 | /** | 148 | utils.inherits(Tester, events.EventEmitter); |
146 | * Asserts that a condition strictly resolves to true. Also returns an | 149 | exports.Tester = Tester; |
147 | * "assertion object" containing useful informations about the test case | ||
148 | * results. | ||
149 | * | ||
150 | * This method is also used as the base one used for all other `assert*` | ||
151 | * family methods; supplementary informations are then passed using the | ||
152 | * `context` argument. | ||
153 | * | ||
154 | * @param Boolean subject The condition to test | ||
155 | * @param String message Test description | ||
156 | * @param Object|null context Assertion context object (Optional) | ||
157 | * @return Object An assertion result object | ||
158 | */ | ||
159 | this.assert = this.assertTrue = function assert(subject, message, context) { | ||
160 | return this.processAssertionResult(utils.mergeObjects({ | ||
161 | success: subject === true, | ||
162 | type: "assert", | ||
163 | standard: "Subject is strictly true", | ||
164 | message: message, | ||
165 | file: this.currentTestFile, | ||
166 | values: { | ||
167 | subject: utils.getPropertyPath(context, 'values.subject') || subject | ||
168 | } | ||
169 | }, context || {})); | ||
170 | }; | ||
171 | 150 | ||
172 | /** | 151 | /** |
173 | * Asserts that two values are strictly equals. | 152 | * Asserts that a condition strictly resolves to true. Also returns an |
174 | * | 153 | * "assertion object" containing useful informations about the test case |
175 | * @param Mixed subject The value to test | 154 | * results. |
176 | * @param Mixed expected The expected value | 155 | * |
177 | * @param String message Test description (Optional) | 156 | * This method is also used as the base one used for all other `assert*` |
178 | * @return Object An assertion result object | 157 | * family methods; supplementary informations are then passed using the |
179 | */ | 158 | * `context` argument. |
180 | this.assertEquals = this.assertEqual = function assertEquals(subject, expected, message) { | 159 | * |
181 | return this.assert(this.testEquals(subject, expected), message, { | 160 | * @param Boolean subject The condition to test |
182 | type: "assertEquals", | 161 | * @param String message Test description |
183 | standard: "Subject equals the expected value", | 162 | * @param Object|null context Assertion context object (Optional) |
184 | values: { | 163 | * @return Object An assertion result object |
185 | subject: subject, | 164 | */ |
186 | expected: expected | 165 | Tester.prototype.assert = Tester.prototype.assertTrue = function assert(subject, message, context) { |
187 | } | 166 | "use strict"; |
188 | }); | 167 | return this.processAssertionResult(utils.mergeObjects({ |
189 | }; | 168 | success: subject === true, |
169 | type: "assert", | ||
170 | standard: "Subject is strictly true", | ||
171 | message: message, | ||
172 | file: this.currentTestFile, | ||
173 | values: { | ||
174 | subject: utils.getPropertyPath(context, 'values.subject') || subject | ||
175 | } | ||
176 | }, context || {})); | ||
177 | }; | ||
190 | 178 | ||
191 | /** | 179 | /** |
192 | * Asserts that two values are strictly not equals. | 180 | * Asserts that two values are strictly equals. |
193 | * | 181 | * |
194 | * @param Mixed subject The value to test | 182 | * @param Mixed subject The value to test |
195 | * @param Mixed expected The unwanted value | 183 | * @param Mixed expected The expected value |
196 | * @param String|null message Test description (Optional) | 184 | * @param String message Test description (Optional) |
197 | * @return Object An assertion result object | 185 | * @return Object An assertion result object |
198 | */ | 186 | */ |
199 | this.assertNotEquals = function assertNotEquals(subject, shouldnt, message) { | 187 | Tester.prototype.assertEquals = Tester.prototype.assertEqual = function assertEquals(subject, expected, message) { |
200 | return this.assert(!this.testEquals(subject, shouldnt), message, { | 188 | "use strict"; |
201 | type: "assertNotEquals", | 189 | return this.assert(this.testEquals(subject, expected), message, { |
202 | standard: "Subject doesn't equal what it shouldn't be", | 190 | type: "assertEquals", |
203 | values: { | 191 | standard: "Subject equals the expected value", |
204 | subject: subject, | 192 | values: { |
205 | shouldnt: shouldnt | 193 | subject: subject, |
206 | } | 194 | expected: expected |
207 | }); | 195 | } |
208 | }; | 196 | }); |
197 | }; | ||
209 | 198 | ||
210 | /** | 199 | /** |
211 | * Asserts that a code evaluation in remote DOM resolves to true. | 200 | * Asserts that two values are strictly not equals. |
212 | * | 201 | * |
213 | * @param Function fn A function to be evaluated in remote DOM | 202 | * @param Mixed subject The value to test |
214 | * @param String message Test description | 203 | * @param Mixed expected The unwanted value |
215 | * @param Object params Object containing the parameters to inject into the function (optional) | 204 | * @param String|null message Test description (Optional) |
216 | * @return Object An assertion result object | 205 | * @return Object An assertion result object |
217 | */ | 206 | */ |
218 | this.assertEval = this.assertEvaluate = function assertEval(fn, message, params) { | 207 | Tester.prototype.assertNotEquals = function assertNotEquals(subject, shouldnt, message) { |
219 | return this.assert(casper.evaluate(fn, params), message, { | 208 | "use strict"; |
220 | type: "assertEval", | 209 | return this.assert(!this.testEquals(subject, shouldnt), message, { |
221 | standard: "Evaluated function returns true", | 210 | type: "assertNotEquals", |
222 | values: { | 211 | standard: "Subject doesn't equal what it shouldn't be", |
223 | fn: fn, | 212 | values: { |
224 | params: params | 213 | subject: subject, |
225 | } | 214 | shouldnt: shouldnt |
226 | }); | 215 | } |
227 | }; | 216 | }); |
217 | }; | ||
228 | 218 | ||
229 | /** | 219 | /** |
230 | * Asserts that the result of a code evaluation in remote DOM equals | 220 | * Asserts that a code evaluation in remote DOM resolves to true. |
231 | * an expected value. | 221 | * |
232 | * | 222 | * @param Function fn A function to be evaluated in remote DOM |
233 | * @param Function fn The function to be evaluated in remote DOM | 223 | * @param String message Test description |
234 | * @param Boolean expected The expected value | 224 | * @param Object params Object containing the parameters to inject into the function (optional) |
235 | * @param String|null message Test description | 225 | * @return Object An assertion result object |
236 | * @param Object|null params Object containing the parameters to inject into the function (optional) | 226 | */ |
237 | * @return Object An assertion result object | 227 | Tester.prototype.assertEval = Tester.prototype.assertEvaluate = function assertEval(fn, message, params) { |
238 | */ | 228 | "use strict"; |
239 | this.assertEvalEquals = this.assertEvalEqual = function assertEvalEquals(fn, expected, message, params) { | 229 | return this.assert(this.casper.evaluate(fn, params), message, { |
240 | var subject = casper.evaluate(fn, params); | 230 | type: "assertEval", |
241 | return this.assert(this.testEquals(subject, expected), message, { | 231 | standard: "Evaluated function returns true", |
242 | type: "assertEvalEquals", | 232 | values: { |
243 | standard: "Evaluated function returns the expected value", | 233 | fn: fn, |
244 | values: { | 234 | params: params |
245 | fn: fn, | 235 | } |
246 | params: params, | 236 | }); |
247 | subject: subject, | 237 | }; |
248 | expected: expected | ||
249 | } | ||
250 | }); | ||
251 | }; | ||
252 | 238 | ||
253 | /** | 239 | /** |
254 | * Asserts that a given input field has the provided value. | 240 | * Asserts that the result of a code evaluation in remote DOM equals |
255 | * | 241 | * an expected value. |
256 | * @param String input_name The name attribute of the input element | 242 | * |
257 | * @param String expected_value The expected value of the input element | 243 | * @param Function fn The function to be evaluated in remote DOM |
258 | * @param String message Test description | 244 | * @param Boolean expected The expected value |
259 | * @return Object An assertion result object | 245 | * @param String|null message Test description |
260 | */ | 246 | * @param Object|null params Object containing the parameters to inject into the function (optional) |
261 | this.assertField = function assertField(input_name, expected_value, message) { | 247 | * @return Object An assertion result object |
262 | var actual_value = casper.evaluate(function(input_name) { | 248 | */ |
263 | var input = document.querySelector('input[name="' + input_name + '"]'); | 249 | Tester.prototype.assertEvalEquals = Tester.prototype.assertEvalEqual = function assertEvalEquals(fn, expected, message, params) { |
264 | return input ? input.value : null; | 250 | "use strict"; |
265 | }, { input_name: input_name }); | 251 | var subject = this.casper.evaluate(fn, params); |
266 | return this.assert(this.testEquals(actual_value, expected_value), message, { | 252 | return this.assert(this.testEquals(subject, expected), message, { |
267 | type: 'assertField', | 253 | type: "assertEvalEquals", |
268 | standard: f('"%s" input field has the value "%s"', input_name, expected_value), | 254 | standard: "Evaluated function returns the expected value", |
269 | values: { | 255 | values: { |
270 | input_name: input_name, | 256 | fn: fn, |
271 | actual_value: actual_value, | 257 | params: params, |
272 | expected_value: expected_value | 258 | subject: subject, |
273 | } | 259 | expected: expected |
274 | }); | 260 | } |
275 | }; | 261 | }); |
262 | }; | ||
276 | 263 | ||
277 | /** | 264 | /** |
278 | * Asserts that an element matching the provided selector expression exists in | 265 | * Asserts that a given input field has the provided value. |
279 | * remote DOM. | 266 | * |
280 | * | 267 | * @param String input_name The name attribute of the input element |
281 | * @param String selector Selector expression | 268 | * @param String expected_value The expected value of the input element |
282 | * @param String message Test description | 269 | * @param String message Test description |
283 | * @return Object An assertion result object | 270 | * @return Object An assertion result object |
284 | */ | 271 | */ |
285 | this.assertExists = this.assertExist = this.assertSelectorExists = this.assertSelectorExist = function assertExists(selector, message) { | 272 | Tester.prototype.assertField = function assertField(input_name, expected_value, message) { |
286 | return this.assert(casper.exists(selector), message, { | 273 | "use strict"; |
287 | type: "assertExists", | 274 | var actual_value = this.casper.evaluate(function(input_name) { |
288 | standard: f("Found an element matching: %s", selector), | 275 | var input = document.querySelector('input[name="' + input_name + '"]'); |
289 | values: { | 276 | return input ? input.value : null; |
290 | selector: selector | 277 | }, { input_name: input_name }); |
291 | } | 278 | return this.assert(this.testEquals(actual_value, expected_value), message, { |
292 | }); | 279 | type: 'assertField', |
293 | }; | 280 | standard: f('"%s" input field has the value "%s"', input_name, expected_value), |
281 | values: { | ||
282 | input_name: input_name, | ||
283 | actual_value: actual_value, | ||
284 | expected_value: expected_value | ||
285 | } | ||
286 | }); | ||
287 | }; | ||
294 | 288 | ||
295 | /** | 289 | /** |
296 | * Asserts that an element matching the provided selector expression does not | 290 | * Asserts that an element matching the provided selector expression exists in |
297 | * exists in remote DOM. | 291 | * remote DOM. |
298 | * | 292 | * |
299 | * @param String selector Selector expression | 293 | * @param String selector Selector expression |
300 | * @param String message Test description | 294 | * @param String message Test description |
301 | * @return Object An assertion result object | 295 | * @return Object An assertion result object |
302 | */ | 296 | */ |
303 | this.assertDoesntExist = this.assertNotExists = function assertDoesntExist(selector, message) { | 297 | Tester.prototype.assertExists = Tester.prototype.assertExist = this.assertSelectorExists = Tester.prototype.assertSelectorExist = function assertExists(selector, message) { |
304 | return this.assert(!casper.exists(selector), message, { | 298 | "use strict"; |
305 | type: "assertDoesntExist", | 299 | return this.assert(this.casper.exists(selector), message, { |
306 | standard: f("No element found matching selector: %s", selector), | 300 | type: "assertExists", |
307 | values: { | 301 | standard: f("Found an element matching: %s", selector), |
308 | selector: selector | 302 | values: { |
309 | } | 303 | selector: selector |
310 | }); | 304 | } |
311 | }; | 305 | }); |
306 | }; | ||
312 | 307 | ||
313 | /** | 308 | /** |
314 | * Asserts that current HTTP status is the one passed as argument. | 309 | * Asserts that an element matching the provided selector expression does not |
315 | * | 310 | * exists in remote DOM. |
316 | * @param Number status HTTP status code | 311 | * |
317 | * @param String message Test description | 312 | * @param String selector Selector expression |
318 | * @return Object An assertion result object | 313 | * @param String message Test description |
319 | */ | 314 | * @return Object An assertion result object |
320 | this.assertHttpStatus = function assertHttpStatus(status, message) { | 315 | */ |
321 | var currentHTTPStatus = casper.currentHTTPStatus; | 316 | Tester.prototype.assertDoesntExist = Tester.prototype.assertNotExists = function assertDoesntExist(selector, message) { |
322 | return this.assert(this.testEquals(casper.currentHTTPStatus, status), message, { | 317 | "use strict"; |
323 | type: "assertHttpStatus", | 318 | return this.assert(!this.casper.exists(selector), message, { |
324 | standard: f("HTTP status code is: %s", status), | 319 | type: "assertDoesntExist", |
325 | values: { | 320 | standard: f("No element found matching selector: %s", selector), |
326 | current: currentHTTPStatus, | 321 | values: { |
327 | expected: status | 322 | selector: selector |
328 | } | 323 | } |
329 | }); | 324 | }); |
330 | }; | 325 | }; |
331 | 326 | ||
332 | /** | 327 | /** |
333 | * Asserts that a provided string matches a provided RegExp pattern. | 328 | * Asserts that current HTTP status is the one passed as argument. |
334 | * | 329 | * |
335 | * @param String subject The string to test | 330 | * @param Number status HTTP status code |
336 | * @param RegExp pattern A RegExp object instance | 331 | * @param String message Test description |
337 | * @param String message Test description | 332 | * @return Object An assertion result object |
338 | * @return Object An assertion result object | 333 | */ |
339 | */ | 334 | Tester.prototype.assertHttpStatus = function assertHttpStatus(status, message) { |
340 | this.assertMatch = this.assertMatches = function assertMatch(subject, pattern, message) { | 335 | "use strict"; |
341 | return this.assert(pattern.test(subject), message, { | 336 | var currentHTTPStatus = this.casper.currentHTTPStatus; |
342 | type: "assertMatch", | 337 | return this.assert(this.testEquals(this.casper.currentHTTPStatus, status), message, { |
343 | standard: "Subject matches the provided pattern", | 338 | type: "assertHttpStatus", |
344 | values: { | 339 | standard: f("HTTP status code is: %s", status), |
345 | subject: subject, | 340 | values: { |
346 | pattern: pattern.toString() | 341 | current: currentHTTPStatus, |
347 | } | 342 | expected: status |
348 | }); | 343 | } |
349 | }; | 344 | }); |
345 | }; | ||
350 | 346 | ||
351 | /** | 347 | /** |
352 | * Asserts a condition resolves to false. | 348 | * Asserts that a provided string matches a provided RegExp pattern. |
353 | * | 349 | * |
354 | * @param Boolean condition The condition to test | 350 | * @param String subject The string to test |
355 | * @param String message Test description | 351 | * @param RegExp pattern A RegExp object instance |
356 | * @return Object An assertion result object | 352 | * @param String message Test description |
357 | */ | 353 | * @return Object An assertion result object |
358 | this.assertNot = function assertNot(condition, message) { | 354 | */ |
359 | return this.assert(!condition, message, { | 355 | Tester.prototype.assertMatch = Tester.prototype.assertMatches = function assertMatch(subject, pattern, message) { |
360 | type: "assertNot", | 356 | "use strict"; |
361 | standard: "Subject is falsy", | 357 | return this.assert(pattern.test(subject), message, { |
362 | values: { | 358 | type: "assertMatch", |
363 | condition: condition | 359 | standard: "Subject matches the provided pattern", |
364 | } | 360 | values: { |
365 | }); | 361 | subject: subject, |
366 | }; | 362 | pattern: pattern.toString() |
363 | } | ||
364 | }); | ||
365 | }; | ||
367 | 366 | ||
368 | /** | 367 | /** |
369 | * Asserts that a selector expression is not currently visible. | 368 | * Asserts a condition resolves to false. |
370 | * | 369 | * |
371 | * @param String expected selector expression | 370 | * @param Boolean condition The condition to test |
372 | * @param String message Test description | 371 | * @param String message Test description |
373 | * @return Object An assertion result object | 372 | * @return Object An assertion result object |
374 | */ | 373 | */ |
375 | this.assertNotVisible = this.assertInvisible = function assertNotVisible(selector, message) { | 374 | Tester.prototype.assertNot = function assertNot(condition, message) { |
376 | return this.assert(!casper.visible(selector), message, { | 375 | "use strict"; |
377 | type: "assertVisible", | 376 | return this.assert(!condition, message, { |
378 | standard: "Selector is not visible", | 377 | type: "assertNot", |
379 | values: { | 378 | standard: "Subject is falsy", |
380 | selector: selector | 379 | values: { |
381 | } | 380 | condition: condition |
382 | }); | 381 | } |
383 | }; | 382 | }); |
383 | }; | ||
384 | 384 | ||
385 | /** | 385 | /** |
386 | * Asserts that the provided function called with the given parameters | 386 | * Asserts that a selector expression is not currently visible. |
387 | * will raise an exception. | 387 | * |
388 | * | 388 | * @param String expected selector expression |
389 | * @param Function fn The function to test | 389 | * @param String message Test description |
390 | * @param Array args The arguments to pass to the function | 390 | * @return Object An assertion result object |
391 | * @param String message Test description | 391 | */ |
392 | * @return Object An assertion result object | 392 | Tester.prototype.assertNotVisible = Tester.prototype.assertInvisible = function assertNotVisible(selector, message) { |
393 | */ | 393 | "use strict"; |
394 | this.assertRaises = this.assertRaise = this.assertThrows = function assertRaises(fn, args, message) { | 394 | return this.assert(!this.casper.visible(selector), message, { |
395 | var context = { | 395 | type: "assertVisible", |
396 | type: "assertRaises", | 396 | standard: "Selector is not visible", |
397 | standard: "Function raises an error" | 397 | values: { |
398 | }; | 398 | selector: selector |
399 | try { | ||
400 | fn.apply(null, args); | ||
401 | this.assert(false, message, context); | ||
402 | } catch (error) { | ||
403 | this.assert(true, message, utils.mergeObjects(context, { | ||
404 | values: { | ||
405 | error: error | ||
406 | } | ||
407 | })); | ||
408 | } | 399 | } |
409 | }; | 400 | }); |
401 | }; | ||
410 | 402 | ||
411 | /** | 403 | /** |
412 | * Asserts that the current page has a resource that matches the provided test | 404 | * Asserts that the provided function called with the given parameters |
413 | * | 405 | * will raise an exception. |
414 | * @param Function/String test A test function that is called with every response | 406 | * |
415 | * @param String message Test description | 407 | * @param Function fn The function to test |
416 | * @return Object An assertion result object | 408 | * @param Array args The arguments to pass to the function |
417 | */ | 409 | * @param String message Test description |
418 | this.assertResourceExists = this.assertResourceExist = function assertResourceExists(test, message) { | 410 | * @return Object An assertion result object |
419 | return this.assert(casper.resourceExists(test), message, { | 411 | */ |
420 | type: "assertResourceExists", | 412 | Tester.prototype.assertRaises = Tester.prototype.assertRaise = this.assertThrows = function assertRaises(fn, args, message) { |
421 | standard: "Expected resource has been found", | 413 | "use strict"; |
414 | var context = { | ||
415 | type: "assertRaises", | ||
416 | standard: "Function raises an error" | ||
417 | }; | ||
418 | try { | ||
419 | fn.apply(null, args); | ||
420 | this.assert(false, message, context); | ||
421 | } catch (error) { | ||
422 | this.assert(true, message, utils.mergeObjects(context, { | ||
422 | values: { | 423 | values: { |
423 | test: test | 424 | error: error |
424 | } | 425 | } |
425 | }); | 426 | })); |
426 | }; | 427 | } |
428 | }; | ||
427 | 429 | ||
428 | /** | 430 | /** |
429 | * Asserts that given text exists in the document body. | 431 | * Asserts that the current page has a resource that matches the provided test |
430 | * | 432 | * |
431 | * @param String text Text to be found | 433 | * @param Function/String test A test function that is called with every response |
432 | * @param String message Test description | 434 | * @param String message Test description |
433 | * @return Object An assertion result object | 435 | * @return Object An assertion result object |
434 | */ | 436 | */ |
435 | this.assertTextExists = this.assertTextExist = function assertTextExists(text, message) { | 437 | Tester.prototype.assertResourceExists = Tester.prototype.assertResourceExist = function assertResourceExists(test, message) { |
436 | var textFound = (casper.evaluate(function _evaluate() { | 438 | "use strict"; |
437 | return document.body.textContent || document.body.innerText; | 439 | return this.assert(this.casper.resourceExists(test), message, { |
438 | }).indexOf(text) !== -1); | 440 | type: "assertResourceExists", |
439 | return this.assert(textFound, message, { | 441 | standard: "Expected resource has been found", |
440 | type: "assertTextExists", | 442 | values: { |
441 | standard: "Found expected text within the document body", | 443 | test: test |
442 | values: { | 444 | } |
443 | text: text | 445 | }); |
444 | } | 446 | }; |
445 | }); | ||
446 | }; | ||
447 | 447 | ||
448 | /** | 448 | /** |
449 | * Asserts that given text exists in the provided selector. | 449 | * Asserts that given text exists in the document body. |
450 | * | 450 | * |
451 | * @param String selector Selector expression | 451 | * @param String text Text to be found |
452 | * @param String text Text to be found | 452 | * @param String message Test description |
453 | * @param String message Test description | 453 | * @return Object An assertion result object |
454 | * @return Object An assertion result object | 454 | */ |
455 | */ | 455 | Tester.prototype.assertTextExists = Tester.prototype.assertTextExist = function assertTextExists(text, message) { |
456 | this.assertSelectorHasText = function assertSelectorHasText(selector, text, message) { | 456 | "use strict"; |
457 | var textFound = casper.fetchText(selector).indexOf(text) !== -1; | 457 | var textFound = (this.casper.evaluate(function _evaluate() { |
458 | return this.assert(textFound, message, { | 458 | return document.body.textContent || document.body.innerText; |
459 | type: "assertTextInSelector", | 459 | }).indexOf(text) !== -1); |
460 | standard: f('Found "%s" within the selector "%s"', text, selector), | 460 | return this.assert(textFound, message, { |
461 | values: { | 461 | type: "assertTextExists", |
462 | selector: selector, | 462 | standard: "Found expected text within the document body", |
463 | text: text | 463 | values: { |
464 | } | 464 | text: text |
465 | }); | 465 | } |
466 | }; | 466 | }); |
467 | }; | ||
467 | 468 | ||
468 | /** | 469 | /** |
469 | * Asserts that given text does not exist in the provided selector. | 470 | * Asserts that given text exists in the provided selector. |
470 | * | 471 | * |
471 | * @param String selector Selector expression | 472 | * @param String selector Selector expression |
472 | * @param String text Text not to be found | 473 | * @param String text Text to be found |
473 | * @param String message Test description | 474 | * @param String message Test description |
474 | * @return Object An assertion result object | 475 | * @return Object An assertion result object |
475 | */ | 476 | */ |
476 | this.assertSelectorDoesntHaveText = function assertSelectorDoesntHaveText(selector, text, message) { | 477 | Tester.prototype.assertSelectorHasText = function assertSelectorHasText(selector, text, message) { |
477 | var textFound = casper.fetchText(selector).indexOf(text) === -1; | 478 | "use strict"; |
478 | return this.assert(textFound, message, { | 479 | var textFound = this.casper.fetchText(selector).indexOf(text) !== -1; |
479 | type: "assertNoTextInSelector", | 480 | return this.assert(textFound, message, { |
480 | standard: f('Did not find "%s" within the selector "%s"', text, selector), | 481 | type: "assertTextInSelector", |
481 | values: { | 482 | standard: f('Found "%s" within the selector "%s"', text, selector), |
482 | selector: selector, | 483 | values: { |
483 | text: text | 484 | selector: selector, |
484 | } | 485 | text: text |
485 | }); | 486 | } |
486 | }; | 487 | }); |
488 | }; | ||
487 | 489 | ||
488 | /** | 490 | /** |
489 | * Asserts that title of the remote page equals to the expected one. | 491 | * Asserts that given text does not exist in the provided selector. |
490 | * | 492 | * |
491 | * @param String expected The expected title string | 493 | * @param String selector Selector expression |
492 | * @param String message Test description | 494 | * @param String text Text not to be found |
493 | * @return Object An assertion result object | 495 | * @param String message Test description |
494 | */ | 496 | * @return Object An assertion result object |
495 | this.assertTitle = function assertTitle(expected, message) { | 497 | */ |
496 | var currentTitle = casper.getTitle(); | 498 | Tester.prototype.assertSelectorDoesntHaveText = function assertSelectorDoesntHaveText(selector, text, message) { |
497 | return this.assert(this.testEquals(currentTitle, expected), message, { | 499 | "use strict"; |
498 | type: "assertTitle", | 500 | var textFound = this.casper.fetchText(selector).indexOf(text) === -1; |
499 | standard: f('Page title is: "%s"', expected), | 501 | return this.assert(textFound, message, { |
500 | values: { | 502 | type: "assertNoTextInSelector", |
501 | subject: currentTitle, | 503 | standard: f('Did not find "%s" within the selector "%s"', text, selector), |
502 | expected: expected | 504 | values: { |
503 | } | 505 | selector: selector, |
504 | }); | 506 | text: text |
505 | }; | 507 | } |
508 | }); | ||
509 | }; | ||
506 | 510 | ||
507 | /** | 511 | /** |
508 | * Asserts that title of the remote page matched the provided pattern. | 512 | * Asserts that title of the remote page equals to the expected one. |
509 | * | 513 | * |
510 | * @param RegExp pattern The pattern to test the title against | 514 | * @param String expected The expected title string |
511 | * @param String message Test description | 515 | * @param String message Test description |
512 | * @return Object An assertion result object | 516 | * @return Object An assertion result object |
513 | */ | 517 | */ |
514 | this.assertTitleMatch = this.assertTitleMatches = function assertTitleMatch(pattern, message) { | 518 | Tester.prototype.assertTitle = function assertTitle(expected, message) { |
515 | var currentTitle = casper.getTitle(); | 519 | "use strict"; |
516 | return this.assert(pattern.test(currentTitle), message, { | 520 | var currentTitle = this.casper.getTitle(); |
517 | type: "assertTitle", | 521 | return this.assert(this.testEquals(currentTitle, expected), message, { |
518 | details: "Page title does not match the provided pattern", | 522 | type: "assertTitle", |
519 | values: { | 523 | standard: f('Page title is: "%s"', expected), |
520 | subject: currentTitle, | 524 | values: { |
521 | pattern: pattern.toString() | 525 | subject: currentTitle, |
522 | } | 526 | expected: expected |
523 | }); | 527 | } |
524 | }; | 528 | }); |
529 | }; | ||
525 | 530 | ||
526 | /** | 531 | /** |
527 | * Asserts that the provided subject is of the given type. | 532 | * Asserts that title of the remote page matched the provided pattern. |
528 | * | 533 | * |
529 | * @param mixed subject The value to test | 534 | * @param RegExp pattern The pattern to test the title against |
530 | * @param String type The javascript type name | 535 | * @param String message Test description |
531 | * @param String message Test description | 536 | * @return Object An assertion result object |
532 | * @return Object An assertion result object | 537 | */ |
533 | */ | 538 | Tester.prototype.assertTitleMatch = Tester.prototype.assertTitleMatches = function assertTitleMatch(pattern, message) { |
534 | this.assertType = function assertType(subject, type, message) { | 539 | "use strict"; |
535 | var actual = utils.betterTypeOf(subject); | 540 | var currentTitle = this.casper.getTitle(); |
536 | return this.assert(this.testEquals(actual, type), message, { | 541 | return this.assert(pattern.test(currentTitle), message, { |
537 | type: "assertType", | 542 | type: "assertTitle", |
538 | standard: f('Subject type is: "%s"', type), | 543 | details: "Page title does not match the provided pattern", |
539 | values: { | 544 | values: { |
540 | subject: subject, | 545 | subject: currentTitle, |
541 | type: type, | 546 | pattern: pattern.toString() |
542 | actual: actual | 547 | } |
543 | } | 548 | }); |
544 | }); | 549 | }; |
545 | }; | ||
546 | 550 | ||
547 | /** | 551 | /** |
548 | * Asserts that a the current page url matches the provided RegExp | 552 | * Asserts that the provided subject is of the given type. |
549 | * pattern. | 553 | * |
550 | * | 554 | * @param mixed subject The value to test |
551 | * @param RegExp pattern A RegExp object instance | 555 | * @param String type The javascript type name |
552 | * @param String message Test description | 556 | * @param String message Test description |
553 | * @return Object An assertion result object | 557 | * @return Object An assertion result object |
554 | */ | 558 | */ |
555 | this.assertUrlMatch = this.assertUrlMatches = function assertUrlMatch(pattern, message) { | 559 | Tester.prototype.assertType = function assertType(subject, type, message) { |
556 | var currentUrl = casper.getCurrentUrl(); | 560 | "use strict"; |
557 | return this.assert(pattern.test(currentUrl), message, { | 561 | var actual = utils.betterTypeOf(subject); |
558 | type: "assertUrlMatch", | 562 | return this.assert(this.testEquals(actual, type), message, { |
559 | standard: "Current url matches the provided pattern", | 563 | type: "assertType", |
560 | values: { | 564 | standard: f('Subject type is: "%s"', type), |
561 | currentUrl: currentUrl, | 565 | values: { |
562 | pattern: pattern.toString() | 566 | subject: subject, |
563 | } | 567 | type: type, |
564 | }); | 568 | actual: actual |
565 | }; | 569 | } |
570 | }); | ||
571 | }; | ||
566 | 572 | ||
567 | /** | 573 | /** |
568 | * Asserts that a selector expression is currently visible. | 574 | * Asserts that a the current page url matches the provided RegExp |
569 | * | 575 | * pattern. |
570 | * @param String expected selector expression | 576 | * |
571 | * @param String message Test description | 577 | * @param RegExp pattern A RegExp object instance |
572 | * @return Object An assertion result object | 578 | * @param String message Test description |
573 | */ | 579 | * @return Object An assertion result object |
574 | this.assertVisible = function assertVisible(selector, message) { | 580 | */ |
575 | return this.assert(casper.visible(selector), message, { | 581 | Tester.prototype.assertUrlMatch = Tester.prototype.assertUrlMatches = function assertUrlMatch(pattern, message) { |
576 | type: "assertVisible", | 582 | "use strict"; |
577 | standard: "Selector is visible", | 583 | var currentUrl = this.casper.getCurrentUrl(); |
578 | values: { | 584 | return this.assert(pattern.test(currentUrl), message, { |
579 | selector: selector | 585 | type: "assertUrlMatch", |
580 | } | 586 | standard: "Current url matches the provided pattern", |
581 | }); | 587 | values: { |
582 | }; | 588 | currentUrl: currentUrl, |
589 | pattern: pattern.toString() | ||
590 | } | ||
591 | }); | ||
592 | }; | ||
583 | 593 | ||
584 | /** | 594 | /** |
585 | * Prints out a colored bar onto the console. | 595 | * Asserts that a selector expression is currently visible. |
586 | * | 596 | * |
587 | */ | 597 | * @param String expected selector expression |
588 | this.bar = function bar(text, style) { | 598 | * @param String message Test description |
589 | casper.echo(text, style, this.options.pad); | 599 | * @return Object An assertion result object |
590 | }; | 600 | */ |
601 | Tester.prototype.assertVisible = function assertVisible(selector, message) { | ||
602 | "use strict"; | ||
603 | return this.assert(this.casper.visible(selector), message, { | ||
604 | type: "assertVisible", | ||
605 | standard: "Selector is visible", | ||
606 | values: { | ||
607 | selector: selector | ||
608 | } | ||
609 | }); | ||
610 | }; | ||
591 | 611 | ||
592 | /** | 612 | /** |
593 | * Render a colorized output. Basically a proxy method for | 613 | * Prints out a colored bar onto the console. |
594 | * Casper.Colorizer#colorize() | 614 | * |
595 | */ | 615 | */ |
596 | this.colorize = function colorize(message, style) { | 616 | Tester.prototype.bar = function bar(text, style) { |
597 | return casper.getColorizer().colorize(message, style); | 617 | "use strict"; |
598 | }; | 618 | this.casper.echo(text, style, this.options.pad); |
619 | }; | ||
599 | 620 | ||
600 | /** | 621 | /** |
601 | * Writes a comment-style formatted message to stdout. | 622 | * Render a colorized output. Basically a proxy method for |
602 | * | 623 | * Casper.Colorizer#colorize() |
603 | * @param String message | 624 | */ |
604 | */ | 625 | Tester.prototype.colorize = function colorize(message, style) { |
605 | this.comment = function comment(message) { | 626 | "use strict"; |
606 | casper.echo('# ' + message, 'COMMENT'); | 627 | return this.casper.getColorizer().colorize(message, style); |
607 | }; | 628 | }; |
608 | 629 | ||
609 | /** | 630 | /** |
610 | * Declares the current test suite done. | 631 | * Writes a comment-style formatted message to stdout. |
611 | * | 632 | * |
612 | */ | 633 | * @param String message |
613 | this.done = function done() { | 634 | */ |
614 | this.emit('test.done'); | 635 | Tester.prototype.comment = function comment(message) { |
615 | this.running = false; | 636 | "use strict"; |
616 | }; | 637 | this.casper.echo('# ' + message, 'COMMENT'); |
638 | }; | ||
617 | 639 | ||
618 | /** | 640 | /** |
619 | * Writes an error-style formatted message to stdout. | 641 | * Declares the current test suite done. |
620 | * | 642 | * |
621 | * @param String message | 643 | */ |
622 | */ | 644 | Tester.prototype.done = function done() { |
623 | this.error = function error(message) { | 645 | "use strict"; |
624 | casper.echo(message, 'ERROR'); | 646 | this.emit('test.done'); |
625 | }; | 647 | this.running = false; |
648 | }; | ||
626 | 649 | ||
627 | /** | 650 | /** |
628 | * Executes a file, wraping and evaluating its code in an isolated | 651 | * Writes an error-style formatted message to stdout. |
629 | * environment where only the current `casper` instance is passed. | 652 | * |
630 | * | 653 | * @param String message |
631 | * @param String file Absolute path to some js/coffee file | 654 | */ |
632 | */ | 655 | Tester.prototype.error = function error(message) { |
633 | this.exec = function exec(file) { | 656 | "use strict"; |
634 | file = this.filter('exec.file', file) || file; | 657 | this.casper.echo(message, 'ERROR'); |
635 | if (!fs.isFile(file) || !utils.isJsFile(file)) { | 658 | }; |
636 | var e = new CasperError(f("Cannot exec %s: can only exec() files with .js or .coffee extensions", file)); | ||
637 | e.fileName = file; | ||
638 | throw e; | ||
639 | } | ||
640 | this.currentTestFile = file; | ||
641 | phantom.injectJs(file); | ||
642 | }; | ||
643 | 659 | ||
644 | /** | 660 | /** |
645 | * Adds a failed test entry to the stack. | 661 | * Executes a file, wraping and evaluating its code in an isolated |
646 | * | 662 | * environment where only the current `casper` instance is passed. |
647 | * @param String message | 663 | * |
648 | */ | 664 | * @param String file Absolute path to some js/coffee file |
649 | this.fail = function fail(message) { | 665 | */ |
650 | return this.assert(false, message, { | 666 | Tester.prototype.exec = function exec(file) { |
651 | type: "fail", | 667 | "use strict"; |
652 | standard: "explicit call to fail()" | 668 | file = this.filter('exec.file', file) || file; |
653 | }); | 669 | if (!fs.isFile(file) || !utils.isJsFile(file)) { |
654 | }; | 670 | var e = new CasperError(f("Cannot exec %s: can only exec() files with .js or .coffee extensions", file)); |
671 | e.fileName = file; | ||
672 | throw e; | ||
673 | } | ||
674 | this.currentTestFile = file; | ||
675 | phantom.injectJs(file); | ||
676 | }; | ||
655 | 677 | ||
656 | /** | 678 | /** |
657 | * Recursively finds all test files contained in a given directory. | 679 | * Adds a failed test entry to the stack. |
658 | * | 680 | * |
659 | * @param String dir Path to some directory to scan | 681 | * @param String message |
660 | */ | 682 | */ |
661 | this.findTestFiles = function findTestFiles(dir) { | 683 | Tester.prototype.fail = function fail(message) { |
662 | var self = this; | 684 | "use strict"; |
663 | if (!fs.isDirectory(dir)) { | 685 | return this.assert(false, message, { |
664 | return []; | 686 | type: "fail", |
665 | } | 687 | standard: "explicit call to fail()" |
666 | var entries = fs.list(dir).filter(function _filter(entry) { | 688 | }); |
667 | return entry !== '.' && entry !== '..'; | 689 | }; |
668 | }).map(function _map(entry) { | ||
669 | return fs.absolute(fs.pathJoin(dir, entry)); | ||
670 | }); | ||
671 | entries.forEach(function _forEach(entry) { | ||
672 | if (fs.isDirectory(entry)) { | ||
673 | entries = entries.concat(self.findTestFiles(entry)); | ||
674 | } | ||
675 | }); | ||
676 | return entries.filter(function _filter(entry) { | ||
677 | return utils.isJsFile(fs.absolute(fs.pathJoin(dir, entry))); | ||
678 | }).sort(); | ||
679 | }; | ||
680 | 690 | ||
681 | /** | 691 | /** |
682 | * Formats a message to highlight some parts of it. | 692 | * Recursively finds all test files contained in a given directory. |
683 | * | 693 | * |
684 | * @param String message | 694 | * @param String dir Path to some directory to scan |
685 | * @param String style | 695 | */ |
686 | */ | 696 | Tester.prototype.findTestFiles = function findTestFiles(dir) { |
687 | this.formatMessage = function formatMessage(message, style) { | 697 | "use strict"; |
688 | var parts = /^([a-z0-9_\.]+\(\))(.*)/i.exec(message); | 698 | var self = this; |
689 | if (!parts) { | 699 | if (!fs.isDirectory(dir)) { |
690 | return message; | 700 | return []; |
701 | } | ||
702 | var entries = fs.list(dir).filter(function _filter(entry) { | ||
703 | return entry !== '.' && entry !== '..'; | ||
704 | }).map(function _map(entry) { | ||
705 | return fs.absolute(fs.pathJoin(dir, entry)); | ||
706 | }); | ||
707 | entries.forEach(function _forEach(entry) { | ||
708 | if (fs.isDirectory(entry)) { | ||
709 | entries = entries.concat(self.findTestFiles(entry)); | ||
691 | } | 710 | } |
692 | return this.colorize(parts[1], 'PARAMETER') + this.colorize(parts[2], style); | 711 | }); |
693 | }; | 712 | return entries.filter(function _filter(entry) { |
713 | return utils.isJsFile(fs.absolute(fs.pathJoin(dir, entry))); | ||
714 | }).sort(); | ||
715 | }; | ||
694 | 716 | ||
695 | /** | 717 | /** |
696 | * Retrieves current failure data and all failed cases. | 718 | * Formats a message to highlight some parts of it. |
697 | * | 719 | * |
698 | * @return Object casedata An object containg information about cases | 720 | * @param String message |
699 | * @return Number casedata.length The number of failed cases | 721 | * @param String style |
700 | * @return Array casedata.cases An array of all the failed case objects | 722 | */ |
701 | */ | 723 | Tester.prototype.formatMessage = function formatMessage(message, style) { |
702 | this.getFailures = function getFailures() { | 724 | "use strict"; |
703 | return { | 725 | var parts = /^([a-z0-9_\.]+\(\))(.*)/i.exec(message); |
704 | length: this.testResults.failed, | 726 | if (!parts) { |
705 | cases: this.testResults.failures | 727 | return message; |
706 | }; | 728 | } |
707 | }; | 729 | return this.colorize(parts[1], 'PARAMETER') + this.colorize(parts[2], style); |
730 | }; | ||
708 | 731 | ||
709 | /** | 732 | /** |
710 | * Retrieves current passed data and all passed cases. | 733 | * Retrieves current failure data and all failed cases. |
711 | * | 734 | * |
712 | * @return Object casedata An object containg information about cases | 735 | * @return Object casedata An object containg information about cases |
713 | * @return Number casedata.length The number of passed cases | 736 | * @return Number casedata.length The number of failed cases |
714 | * @return Array casedata.cases An array of all the passed case objects | 737 | * @return Array casedata.cases An array of all the failed case objects |
715 | */ | 738 | */ |
716 | this.getPasses = function getPasses() { | 739 | Tester.prototype.getFailures = function getFailures() { |
717 | return { | 740 | "use strict"; |
718 | length: this.testResults.passed, | 741 | return { |
719 | cases: this.testResults.passes | 742 | length: this.testResults.failed, |
720 | }; | 743 | cases: this.testResults.failures |
721 | }; | 744 | }; |
745 | }; | ||
722 | 746 | ||
723 | /** | 747 | /** |
724 | * Writes an info-style formatted message to stdout. | 748 | * Retrieves current passed data and all passed cases. |
725 | * | 749 | * |
726 | * @param String message | 750 | * @return Object casedata An object containg information about cases |
727 | */ | 751 | * @return Number casedata.length The number of passed cases |
728 | this.info = function info(message) { | 752 | * @return Array casedata.cases An array of all the passed case objects |
729 | casper.echo(message, 'PARAMETER'); | 753 | */ |
754 | Tester.prototype.getPasses = function getPasses() { | ||
755 | "use strict"; | ||
756 | return { | ||
757 | length: this.testResults.passed, | ||
758 | cases: this.testResults.passes | ||
730 | }; | 759 | }; |
760 | }; | ||
731 | 761 | ||
732 | /** | 762 | /** |
733 | * Adds a successful test entry to the stack. | 763 | * Writes an info-style formatted message to stdout. |
734 | * | 764 | * |
735 | * @param String message | 765 | * @param String message |
736 | */ | 766 | */ |
737 | this.pass = function pass(message) { | 767 | Tester.prototype.info = function info(message) { |
738 | return this.assert(true, message, { | 768 | "use strict"; |
739 | type: "pass", | 769 | this.casper.echo(message, 'PARAMETER'); |
740 | standard: "explicit call to pass()" | 770 | }; |
741 | }); | ||
742 | }; | ||
743 | 771 | ||
744 | /** | 772 | /** |
745 | * Processes an assertion result by emitting the appropriate event and | 773 | * Adds a successful test entry to the stack. |
746 | * printing result onto the console. | 774 | * |
747 | * | 775 | * @param String message |
748 | * @param Object result An assertion result object | 776 | */ |
749 | * @return Object The passed assertion result Object | 777 | Tester.prototype.pass = function pass(message) { |
750 | */ | 778 | "use strict"; |
751 | this.processAssertionResult = function processAssertionResult(result) { | 779 | return this.assert(true, message, { |
752 | var eventName, style, status; | 780 | type: "pass", |
753 | if (result.success === true) { | 781 | standard: "explicit call to pass()" |
754 | eventName = 'success'; | 782 | }); |
755 | style = 'INFO'; | 783 | }; |
756 | status = this.options.passText; | ||
757 | this.testResults.passed++; | ||
758 | } else { | ||
759 | eventName = 'fail'; | ||
760 | style = 'RED_BAR'; | ||
761 | status = this.options.failText; | ||
762 | this.testResults.failed++; | ||
763 | } | ||
764 | var message = result.message || result.standard; | ||
765 | casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' ')); | ||
766 | this.emit(eventName, result); | ||
767 | return result; | ||
768 | }; | ||
769 | 784 | ||
770 | /** | 785 | /** |
771 | * Renders a detailed report for each failed test. | 786 | * Processes an assertion result by emitting the appropriate event and |
772 | * | 787 | * printing result onto the console. |
773 | * @param Array failures | 788 | * |
774 | */ | 789 | * @param Object result An assertion result object |
775 | this.renderFailureDetails = function renderFailureDetails(failures) { | 790 | * @return Object The passed assertion result Object |
776 | if (failures.length === 0) { | 791 | */ |
777 | return; | 792 | Tester.prototype.processAssertionResult = function processAssertionResult(result) { |
778 | } | 793 | "use strict"; |
779 | casper.echo(f("\nDetails for the %d failed test%s:\n", failures.length, failures.length > 1 ? "s" : ""), "PARAMETER"); | 794 | var eventName, style, status; |
780 | failures.forEach(function _forEach(failure) { | 795 | if (result.success === true) { |
781 | var type, message, line; | 796 | eventName = 'success'; |
782 | type = failure.type || "unknown"; | 797 | style = 'INFO'; |
783 | line = ~~failure.line; | 798 | status = this.options.passText; |
784 | message = failure.message; | 799 | this.testResults.passed++; |
785 | casper.echo(f('In %s:%s', failure.file, line)); | 800 | } else { |
786 | casper.echo(f(' %s: %s', type, message || failure.standard || "(no message was entered)"), "COMMENT"); | 801 | eventName = 'fail'; |
787 | }); | 802 | style = 'RED_BAR'; |
788 | }; | 803 | status = this.options.failText; |
804 | this.testResults.failed++; | ||
805 | } | ||
806 | var message = result.message || result.standard; | ||
807 | this.casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' ')); | ||
808 | this.emit(eventName, result); | ||
809 | return result; | ||
810 | }; | ||
789 | 811 | ||
790 | /** | 812 | /** |
791 | * Render tests results, an optionally exit phantomjs. | 813 | * Renders a detailed report for each failed test. |
792 | * | 814 | * |
793 | * @param Boolean exit | 815 | * @param Array failures |
794 | */ | 816 | */ |
795 | this.renderResults = function renderResults(exit, status, save) { | 817 | Tester.prototype.renderFailureDetails = function renderFailureDetails(failures) { |
796 | save = utils.isString(save) ? save : this.options.save; | 818 | "use strict"; |
797 | var total = this.testResults.passed + this.testResults.failed, statusText, style, result; | 819 | if (failures.length === 0) { |
798 | var exitStatus = ~~(status || (this.testResults.failed > 0 ? 1 : 0)); | 820 | return; |
799 | if (total === 0) { | 821 | } |
822 | this.casper.echo(f("\nDetails for the %d failed test%s:\n", failures.length, failures.length > 1 ? "s" : ""), "PARAMETER"); | ||
823 | failures.forEach(function _forEach(failure) { | ||
824 | var type, message, line; | ||
825 | type = failure.type || "unknown"; | ||
826 | line = ~~failure.line; | ||
827 | message = failure.message; | ||
828 | this.casper.echo(f('In %s:%s', failure.file, line)); | ||
829 | this.casper.echo(f(' %s: %s', type, message || failure.standard || "(no message was entered)"), "COMMENT"); | ||
830 | }); | ||
831 | }; | ||
832 | |||
833 | /** | ||
834 | * Render tests results, an optionally exit phantomjs. | ||
835 | * | ||
836 | * @param Boolean exit | ||
837 | */ | ||
838 | Tester.prototype.renderResults = function renderResults(exit, status, save) { | ||
839 | "use strict"; | ||
840 | save = utils.isString(save) ? save : this.options.save; | ||
841 | var total = this.testResults.passed + this.testResults.failed, statusText, style, result; | ||
842 | var exitStatus = ~~(status || (this.testResults.failed > 0 ? 1 : 0)); | ||
843 | if (total === 0) { | ||
844 | statusText = this.options.failText; | ||
845 | style = 'RED_BAR'; | ||
846 | result = f("%s Looks like you didn't run any test.", statusText); | ||
847 | } else { | ||
848 | if (this.testResults.failed > 0) { | ||
800 | statusText = this.options.failText; | 849 | statusText = this.options.failText; |
801 | style = 'RED_BAR'; | 850 | style = 'RED_BAR'; |
802 | result = f("%s Looks like you didn't run any test.", statusText); | ||
803 | } else { | 851 | } else { |
804 | if (this.testResults.failed > 0) { | 852 | statusText = this.options.passText; |
805 | statusText = this.options.failText; | 853 | style = 'GREEN_BAR'; |
806 | style = 'RED_BAR'; | ||
807 | } else { | ||
808 | statusText = this.options.passText; | ||
809 | style = 'GREEN_BAR'; | ||
810 | } | ||
811 | result = f('%s %s tests executed, %d passed, %d failed.', | ||
812 | statusText, total, this.testResults.passed, this.testResults.failed); | ||
813 | } | 854 | } |
814 | casper.echo(result, style, this.options.pad); | 855 | result = f('%s %s tests executed, %d passed, %d failed.', |
815 | if (this.testResults.failed > 0) { | 856 | statusText, total, this.testResults.passed, this.testResults.failed); |
816 | this.renderFailureDetails(this.testResults.failures); | 857 | } |
817 | } | 858 | this.casper.echo(result, style, this.options.pad); |
818 | if (save && utils.isFunction(require)) { | 859 | if (this.testResults.failed > 0) { |
819 | try { | 860 | this.renderFailureDetails(this.testResults.failures); |
820 | fs.write(save, this.exporter.getXML(), 'w'); | 861 | } |
821 | casper.echo(f('Result log stored in %s', save), 'INFO', 80); | 862 | if (save && utils.isFunction(require)) { |
822 | } catch (e) { | 863 | try { |
823 | casper.echo(f('Unable to write results to %s: %s', save, e), 'ERROR', 80); | 864 | fs.write(save, this.exporter.getXML(), 'w'); |
824 | } | 865 | this.casper.echo(f('Result log stored in %s', save), 'INFO', 80); |
825 | } | 866 | } catch (e) { |
826 | if (exit === true) { | 867 | this.casper.echo(f('Unable to write results to %s: %s', save, e), 'ERROR', 80); |
827 | casper.exit(exitStatus); | ||
828 | } | ||
829 | }; | ||
830 | |||
831 | /** | ||
832 | * Runs al suites contained in the paths passed as arguments. | ||
833 | * | ||
834 | */ | ||
835 | this.runSuites = function runSuites() { | ||
836 | var testFiles = [], self = this; | ||
837 | if (arguments.length === 0) { | ||
838 | throw new CasperError("runSuites() needs at least one path argument"); | ||
839 | } | 868 | } |
840 | this.loadIncludes.includes.forEach(function _forEachInclude(include) { | 869 | } |
841 | phantom.injectJs(include); | 870 | if (exit === true) { |
842 | }); | 871 | this.casper.exit(exitStatus); |
843 | 872 | } | |
844 | this.loadIncludes.pre.forEach(function _forEachPreTest(preTestFile) { | 873 | }; |
845 | testFiles = testFiles.concat(preTestFile); | ||
846 | }); | ||
847 | 874 | ||
848 | Array.prototype.forEach.call(arguments, function _forEachArgument(path) { | 875 | /** |
849 | if (!fs.exists(path)) { | 876 | * Runs al suites contained in the paths passed as arguments. |
850 | self.bar(f("Path %s doesn't exist", path), "RED_BAR"); | 877 | * |
851 | } | 878 | */ |
852 | if (fs.isDirectory(path)) { | 879 | Tester.prototype.runSuites = function runSuites() { |
853 | testFiles = testFiles.concat(self.findTestFiles(path)); | 880 | "use strict"; |
854 | } else if (fs.isFile(path)) { | 881 | var testFiles = [], self = this; |
855 | testFiles.push(path); | 882 | if (arguments.length === 0) { |
856 | } | 883 | throw new CasperError("runSuites() needs at least one path argument"); |
857 | }); | 884 | } |
885 | this.loadIncludes.includes.forEach(function _forEachInclude(include) { | ||
886 | phantom.injectJs(include); | ||
887 | }); | ||
858 | 888 | ||
859 | this.loadIncludes.post.forEach(function _forEachPostTest(postTestFile) { | 889 | this.loadIncludes.pre.forEach(function _forEachPreTest(preTestFile) { |
860 | testFiles = testFiles.concat(postTestFile); | 890 | testFiles = testFiles.concat(preTestFile); |
861 | }); | 891 | }); |
862 | 892 | ||
863 | if (testFiles.length === 0) { | 893 | Array.prototype.forEach.call(arguments, function _forEachArgument(path) { |
864 | this.bar(f("No test file found in %s, aborting.", Array.prototype.slice.call(arguments)), "RED_BAR"); | 894 | if (!fs.exists(path)) { |
865 | casper.exit(1); | 895 | self.bar(f("Path %s doesn't exist", path), "RED_BAR"); |
866 | } | 896 | } |
867 | self.currentSuiteNum = 0; | 897 | if (fs.isDirectory(path)) { |
868 | var interval = setInterval(function _check(self) { | 898 | testFiles = testFiles.concat(self.findTestFiles(path)); |
869 | if (self.running) { | 899 | } else if (fs.isFile(path)) { |
870 | return; | 900 | testFiles.push(path); |
871 | } | 901 | } |
872 | if (self.currentSuiteNum === testFiles.length) { | 902 | }); |
873 | self.emit('tests.complete'); | ||
874 | clearInterval(interval); | ||
875 | } else { | ||
876 | self.runTest(testFiles[self.currentSuiteNum]); | ||
877 | self.currentSuiteNum++; | ||
878 | } | ||
879 | }, 100, this); | ||
880 | }; | ||
881 | 903 | ||
882 | /** | 904 | this.loadIncludes.post.forEach(function _forEachPostTest(postTestFile) { |
883 | * Runs a test file | 905 | testFiles = testFiles.concat(postTestFile); |
884 | * | 906 | }); |
885 | */ | ||
886 | this.runTest = function runTest(testFile) { | ||
887 | this.bar(f('Test file: %s', testFile), 'INFO_BAR'); | ||
888 | this.running = true; // this.running is set back to false with done() | ||
889 | this.exec(testFile); | ||
890 | }; | ||
891 | 907 | ||
892 | /** | 908 | if (testFiles.length === 0) { |
893 | * Tests equality between the two passed arguments. | 909 | this.bar(f("No test file found in %s, aborting.", Array.prototype.slice.call(arguments)), "RED_BAR"); |
894 | * | 910 | this.casper.exit(1); |
895 | * @param Mixed v1 | 911 | } |
896 | * @param Mixed v2 | 912 | self.currentSuiteNum = 0; |
897 | * @param Boolean | 913 | var interval = setInterval(function _check(self) { |
898 | */ | 914 | if (self.running) { |
899 | this.testEquals = this.testEqual = function testEquals(v1, v2) { | 915 | return; |
900 | return utils.equals(v1, v2); | 916 | } |
901 | }; | 917 | if (self.currentSuiteNum === testFiles.length) { |
918 | self.emit('tests.complete'); | ||
919 | clearInterval(interval); | ||
920 | } else { | ||
921 | self.runTest(testFiles[self.currentSuiteNum]); | ||
922 | self.currentSuiteNum++; | ||
923 | } | ||
924 | }, 100, this); | ||
925 | }; | ||
902 | 926 | ||
903 | /** | 927 | /** |
904 | * Processes an error caught while running tests contained in a given test | 928 | * Runs a test file |
905 | * file. | 929 | * |
906 | * | 930 | */ |
907 | * @param Error|String error The error | 931 | Tester.prototype.runTest = function runTest(testFile) { |
908 | * @param String file Test file where the error occurred | 932 | "use strict"; |
909 | * @param Number line Line number (optional) | 933 | this.bar(f('Test file: %s', testFile), 'INFO_BAR'); |
910 | */ | 934 | this.running = true; // this.running is set back to false with done() |
911 | this.uncaughtError = function uncaughtError(error, file, line) { | 935 | this.exec(testFile); |
912 | return this.processAssertionResult({ | ||
913 | success: false, | ||
914 | type: "uncaughtError", | ||
915 | file: file, | ||
916 | line: ~~line || "unknown", | ||
917 | message: utils.isObject(error) ? error.message : error, | ||
918 | values: { | ||
919 | error: error | ||
920 | } | ||
921 | }); | ||
922 | }; | ||
923 | }; | 936 | }; |
924 | 937 | ||
925 | // Tester class is an EventEmitter | 938 | /** |
926 | utils.inherits(Tester, events.EventEmitter); | 939 | * Tests equality between the two passed arguments. |
940 | * | ||
941 | * @param Mixed v1 | ||
942 | * @param Mixed v2 | ||
943 | * @param Boolean | ||
944 | */ | ||
945 | Tester.prototype.testEquals = Tester.prototype.testEqual = function testEquals(v1, v2) { | ||
946 | "use strict"; | ||
947 | return utils.equals(v1, v2); | ||
948 | }; | ||
927 | 949 | ||
928 | exports.Tester = Tester; | 950 | /** |
951 | * Processes an error caught while running tests contained in a given test | ||
952 | * file. | ||
953 | * | ||
954 | * @param Error|String error The error | ||
955 | * @param String file Test file where the error occurred | ||
956 | * @param Number line Line number (optional) | ||
957 | */ | ||
958 | Tester.prototype.uncaughtError = function uncaughtError(error, file, line) { | ||
959 | "use strict"; | ||
960 | return this.processAssertionResult({ | ||
961 | success: false, | ||
962 | type: "uncaughtError", | ||
963 | file: file, | ||
964 | line: ~~line || "unknown", | ||
965 | message: utils.isObject(error) ? error.message : error, | ||
966 | values: { | ||
967 | error: error | ||
968 | } | ||
969 | }); | ||
970 | }; | ... | ... |
... | @@ -30,459 +30,482 @@ | ... | @@ -30,459 +30,482 @@ |
30 | 30 | ||
31 | /*global CasperError console exports phantom require*/ | 31 | /*global CasperError console exports phantom require*/ |
32 | 32 | ||
33 | (function(exports) { | 33 | /** |
34 | * Provides a better typeof operator equivalent, able to retrieve the array | ||
35 | * type. | ||
36 | * | ||
37 | * @param mixed input | ||
38 | * @return String | ||
39 | * @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ | ||
40 | */ | ||
41 | function betterTypeOf(input) { | ||
34 | "use strict"; | 42 | "use strict"; |
35 | 43 | try { | |
36 | /** | 44 | return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase(); |
37 | * Provides a better typeof operator equivalent, able to retrieve the array | 45 | } catch (e) { |
38 | * type. | 46 | return typeof input; |
39 | * | ||
40 | * @param mixed input | ||
41 | * @return String | ||
42 | * @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ | ||
43 | */ | ||
44 | function betterTypeOf(input) { | ||
45 | try { | ||
46 | return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase(); | ||
47 | } catch (e) { | ||
48 | return typeof input; | ||
49 | } | ||
50 | } | 47 | } |
51 | exports.betterTypeOf = betterTypeOf; | 48 | } |
49 | exports.betterTypeOf = betterTypeOf; | ||
52 | 50 | ||
53 | /** | 51 | /** |
54 | * Cleans a passed URL if it lacks a slash at the end when a sole domain is used. | 52 | * Cleans a passed URL if it lacks a slash at the end when a sole domain is used. |
55 | * | 53 | * |
56 | * @param String url An HTTP URL | 54 | * @param String url An HTTP URL |
57 | * @return String | 55 | * @return String |
58 | */ | 56 | */ |
59 | function cleanUrl(url) { | 57 | function cleanUrl(url) { |
60 | var parts = /(https?):\/\/(.*)/i.exec(url); | 58 | "use strict"; |
61 | if (!parts) { | 59 | var parts = /(https?):\/\/(.*)/i.exec(url); |
62 | return url; | 60 | if (!parts) { |
63 | } | ||
64 | var protocol = parts[1]; | ||
65 | var subparts = parts[2].split('/'); | ||
66 | if (subparts.length === 1) { | ||
67 | return format("%s://%s/", protocol, subparts[0]); | ||
68 | } | ||
69 | return url; | 61 | return url; |
70 | } | 62 | } |
71 | exports.cleanUrl = cleanUrl; | 63 | var protocol = parts[1]; |
72 | 64 | var subparts = parts[2].split('/'); | |
73 | /** | 65 | if (subparts.length === 1) { |
74 | * Dumps a JSON representation of passed value to the console. Used for | 66 | return format("%s://%s/", protocol, subparts[0]); |
75 | * debugging purpose only. | ||
76 | * | ||
77 | * @param Mixed value | ||
78 | */ | ||
79 | function dump(value) { | ||
80 | console.log(serialize(value, 4)); | ||
81 | } | 67 | } |
82 | exports.dump = dump; | 68 | return url; |
69 | } | ||
70 | exports.cleanUrl = cleanUrl; | ||
71 | |||
72 | /** | ||
73 | * Dumps a JSON representation of passed value to the console. Used for | ||
74 | * debugging purpose only. | ||
75 | * | ||
76 | * @param Mixed value | ||
77 | */ | ||
78 | function dump(value) { | ||
79 | "use strict"; | ||
80 | console.log(serialize(value, 4)); | ||
81 | } | ||
82 | exports.dump = dump; | ||
83 | 83 | ||
84 | /** | 84 | /** |
85 | * Tests equality between the two passed arguments. | 85 | * Tests equality between the two passed arguments. |
86 | * | 86 | * |
87 | * @param Mixed v1 | 87 | * @param Mixed v1 |
88 | * @param Mixed v2 | 88 | * @param Mixed v2 |
89 | * @param Boolean | 89 | * @param Boolean |
90 | */ | 90 | */ |
91 | function equals(v1, v2) { | 91 | function equals(v1, v2) { |
92 | if (betterTypeOf(v1) !== betterTypeOf(v2)) { | 92 | "use strict"; |
93 | if (betterTypeOf(v1) !== betterTypeOf(v2)) { | ||
94 | return false; | ||
95 | } | ||
96 | if (isFunction(v1)) { | ||
97 | return v1.toString() === v2.toString(); | ||
98 | } | ||
99 | if (v1 instanceof Object) { | ||
100 | if (Object.keys(v1).length !== Object.keys(v2).length) { | ||
93 | return false; | 101 | return false; |
94 | } | 102 | } |
95 | if (isFunction(v1)) { | 103 | for (var k in v1) { |
96 | return v1.toString() === v2.toString(); | 104 | if (!equals(v1[k], v2[k])) { |
97 | } | ||
98 | if (v1 instanceof Object) { | ||
99 | if (Object.keys(v1).length !== Object.keys(v2).length) { | ||
100 | return false; | 105 | return false; |
101 | } | 106 | } |
102 | for (var k in v1) { | ||
103 | if (!equals(v1[k], v2[k])) { | ||
104 | return false; | ||
105 | } | ||
106 | } | ||
107 | return true; | ||
108 | } | 107 | } |
109 | return v1 === v2; | 108 | return true; |
110 | } | 109 | } |
111 | exports.equals = equals; | 110 | return v1 === v2; |
111 | } | ||
112 | exports.equals = equals; | ||
112 | 113 | ||
113 | /** | 114 | /** |
114 | * Returns the file extension in lower case. | 115 | * Returns the file extension in lower case. |
115 | * | 116 | * |
116 | * @param String file File path | 117 | * @param String file File path |
117 | * @return string | 118 | * @return string |
118 | */ | 119 | */ |
119 | function fileExt(file) { | 120 | function fileExt(file) { |
120 | try { | 121 | "use strict"; |
121 | return file.split('.').pop().toLowerCase().trim(); | 122 | try { |
122 | } catch(e) { | 123 | return file.split('.').pop().toLowerCase().trim(); |
123 | return ''; | 124 | } catch(e) { |
124 | } | 125 | return ''; |
125 | } | 126 | } |
126 | exports.fileExt = fileExt; | 127 | } |
128 | exports.fileExt = fileExt; | ||
127 | 129 | ||
128 | /** | 130 | /** |
129 | * Takes a string and append blanks until the pad value is reached. | 131 | * Takes a string and append blanks until the pad value is reached. |
130 | * | 132 | * |
131 | * @param String text | 133 | * @param String text |
132 | * @param Number pad Pad value (optional; default: 80) | 134 | * @param Number pad Pad value (optional; default: 80) |
133 | * @return String | 135 | * @return String |
134 | */ | 136 | */ |
135 | function fillBlanks(text, pad) { | 137 | function fillBlanks(text, pad) { |
136 | pad = pad || 80; | 138 | "use strict"; |
137 | if (text.length < pad) { | 139 | pad = pad || 80; |
138 | text += new Array(pad - text.length + 1).join(' '); | 140 | if (text.length < pad) { |
139 | } | 141 | text += new Array(pad - text.length + 1).join(' '); |
140 | return text; | ||
141 | } | 142 | } |
142 | exports.fillBlanks = fillBlanks; | 143 | return text; |
144 | } | ||
145 | exports.fillBlanks = fillBlanks; | ||
143 | 146 | ||
144 | /** | 147 | /** |
145 | * Formats a string with passed parameters. Ported from nodejs `util.format()`. | 148 | * Formats a string with passed parameters. Ported from nodejs `util.format()`. |
146 | * | 149 | * |
147 | * @return String | 150 | * @return String |
148 | */ | 151 | */ |
149 | function format(f) { | 152 | function format(f) { |
150 | var i = 1; | 153 | "use strict"; |
151 | var args = arguments; | 154 | var i = 1; |
152 | var len = args.length; | 155 | var args = arguments; |
153 | var str = String(f).replace(/%[sdj%]/g, function _replace(x) { | 156 | var len = args.length; |
154 | if (i >= len) return x; | 157 | var str = String(f).replace(/%[sdj%]/g, function _replace(x) { |
155 | switch (x) { | 158 | if (i >= len) return x; |
156 | case '%s': | 159 | switch (x) { |
157 | return String(args[i++]); | 160 | case '%s': |
158 | case '%d': | 161 | return String(args[i++]); |
159 | return Number(args[i++]); | 162 | case '%d': |
160 | case '%j': | 163 | return Number(args[i++]); |
161 | return JSON.stringify(args[i++]); | 164 | case '%j': |
162 | case '%%': | 165 | return JSON.stringify(args[i++]); |
163 | return '%'; | 166 | case '%%': |
164 | default: | 167 | return '%'; |
165 | return x; | 168 | default: |
166 | } | 169 | return x; |
167 | }); | ||
168 | for (var x = args[i]; i < len; x = args[++i]) { | ||
169 | if (x === null || typeof x !== 'object') { | ||
170 | str += ' ' + x; | ||
171 | } else { | ||
172 | str += '[obj]'; | ||
173 | } | ||
174 | } | 170 | } |
175 | return str; | 171 | }); |
176 | } | 172 | for (var x = args[i]; i < len; x = args[++i]) { |
177 | exports.format = format; | 173 | if (x === null || typeof x !== 'object') { |
178 | 174 | str += ' ' + x; | |
179 | /** | 175 | } else { |
180 | * Retrieves the value of an Object foreign property using a dot-separated | 176 | str += '[obj]'; |
181 | * path string. | ||
182 | * | ||
183 | * Beware, this function doesn't handle object key names containing a dot. | ||
184 | * | ||
185 | * @param Object obj The source object | ||
186 | * @param String path Dot separated path, eg. "x.y.z" | ||
187 | */ | ||
188 | function getPropertyPath(obj, path) { | ||
189 | if (!isObject(obj) || !isString(path)) { | ||
190 | return undefined; | ||
191 | } | 177 | } |
192 | var value = obj; | ||
193 | path.split('.').forEach(function(property) { | ||
194 | if (typeof value === "object" && property in value) { | ||
195 | value = value[property]; | ||
196 | } else { | ||
197 | value = undefined; | ||
198 | } | ||
199 | }); | ||
200 | return value; | ||
201 | } | 178 | } |
202 | exports.getPropertyPath = getPropertyPath; | 179 | return str; |
180 | } | ||
181 | exports.format = format; | ||
203 | 182 | ||
204 | /** | 183 | /** |
205 | * Inherit the prototype methods from one constructor into another. | 184 | * Retrieves the value of an Object foreign property using a dot-separated |
206 | * | 185 | * path string. |
207 | * @param {function} ctor Constructor function which needs to inherit the | 186 | * |
208 | * prototype. | 187 | * Beware, this function doesn't handle object key names containing a dot. |
209 | * @param {function} superCtor Constructor function to inherit prototype from. | 188 | * |
210 | */ | 189 | * @param Object obj The source object |
211 | function inherits(ctor, superCtor) { | 190 | * @param String path Dot separated path, eg. "x.y.z" |
212 | ctor.super_ = ctor.__super__ = superCtor; | 191 | */ |
213 | ctor.prototype = Object.create(superCtor.prototype, { | 192 | function getPropertyPath(obj, path) { |
214 | constructor: { | 193 | "use strict"; |
215 | value: ctor, | 194 | if (!isObject(obj) || !isString(path)) { |
216 | enumerable: false, | 195 | return undefined; |
217 | writable: true, | ||
218 | configurable: true | ||
219 | } | ||
220 | }); | ||
221 | } | 196 | } |
222 | exports.inherits = inherits; | 197 | var value = obj; |
198 | path.split('.').forEach(function(property) { | ||
199 | if (typeof value === "object" && property in value) { | ||
200 | value = value[property]; | ||
201 | } else { | ||
202 | value = undefined; | ||
203 | } | ||
204 | }); | ||
205 | return value; | ||
206 | } | ||
207 | exports.getPropertyPath = getPropertyPath; | ||
223 | 208 | ||
224 | /** | 209 | /** |
225 | * Checks if value is a javascript Array | 210 | * Inherit the prototype methods from one constructor into another. |
226 | * | 211 | * |
227 | * @param mixed value | 212 | * @param {function} ctor Constructor function which needs to inherit the |
228 | * @return Boolean | 213 | * prototype. |
229 | */ | 214 | * @param {function} superCtor Constructor function to inherit prototype from. |
230 | function isArray(value) { | 215 | */ |
231 | return Array.isArray(value) || isType(value, "array"); | 216 | function inherits(ctor, superCtor) { |
232 | } | 217 | "use strict"; |
233 | exports.isArray = isArray; | 218 | ctor.super_ = ctor.__super__ = superCtor; |
219 | ctor.prototype = Object.create(superCtor.prototype, { | ||
220 | constructor: { | ||
221 | value: ctor, | ||
222 | enumerable: false, | ||
223 | writable: true, | ||
224 | configurable: true | ||
225 | } | ||
226 | }); | ||
227 | } | ||
228 | exports.inherits = inherits; | ||
234 | 229 | ||
235 | /** | 230 | /** |
236 | * Checks if passed argument is an instance of Capser object. | 231 | * Checks if value is a javascript Array |
237 | * | 232 | * |
238 | * @param mixed value | 233 | * @param mixed value |
239 | * @return Boolean | 234 | * @return Boolean |
240 | */ | 235 | */ |
241 | function isCasperObject(value) { | 236 | function isArray(value) { |
242 | return value instanceof require('casper').Casper; | 237 | "use strict"; |
243 | } | 238 | return Array.isArray(value) || isType(value, "array"); |
244 | exports.isCasperObject = isCasperObject; | 239 | } |
240 | exports.isArray = isArray; | ||
245 | 241 | ||
246 | /** | 242 | /** |
247 | * Checks if value is a phantomjs clipRect-compatible object | 243 | * Checks if passed argument is an instance of Capser object. |
248 | * | 244 | * |
249 | * @param mixed value | 245 | * @param mixed value |
250 | * @return Boolean | 246 | * @return Boolean |
251 | */ | 247 | */ |
252 | function isClipRect(value) { | 248 | function isCasperObject(value) { |
253 | return isType(value, "cliprect") || ( | 249 | "use strict"; |
254 | isObject(value) && | 250 | return value instanceof require('casper').Casper; |
255 | isNumber(value.top) && isNumber(value.left) && | 251 | } |
256 | isNumber(value.width) && isNumber(value.height) | 252 | exports.isCasperObject = isCasperObject; |
257 | ); | ||
258 | } | ||
259 | exports.isClipRect = isClipRect; | ||
260 | 253 | ||
261 | /** | 254 | /** |
262 | * Checks if value is a javascript Function | 255 | * Checks if value is a phantomjs clipRect-compatible object |
263 | * | 256 | * |
264 | * @param mixed value | 257 | * @param mixed value |
265 | * @return Boolean | 258 | * @return Boolean |
266 | */ | 259 | */ |
267 | function isFunction(value) { | 260 | function isClipRect(value) { |
268 | return isType(value, "function"); | 261 | "use strict"; |
269 | } | 262 | return isType(value, "cliprect") || ( |
270 | exports.isFunction = isFunction; | 263 | isObject(value) && |
264 | isNumber(value.top) && isNumber(value.left) && | ||
265 | isNumber(value.width) && isNumber(value.height) | ||
266 | ); | ||
267 | } | ||
268 | exports.isClipRect = isClipRect; | ||
271 | 269 | ||
272 | /** | 270 | /** |
273 | * Checks if passed resource involves an HTTP url. | 271 | * Checks if value is a javascript Function |
274 | * | 272 | * |
275 | * @param Object resource The PhantomJS HTTP resource object | 273 | * @param mixed value |
276 | * @return Boolean | 274 | * @return Boolean |
277 | */ | 275 | */ |
278 | function isHTTPResource(resource) { | 276 | function isFunction(value) { |
279 | return isObject(resource) && /^http/i.test(resource.url); | 277 | "use strict"; |
280 | } | 278 | return isType(value, "function"); |
281 | exports.isHTTPResource = isHTTPResource; | 279 | } |
280 | exports.isFunction = isFunction; | ||
282 | 281 | ||
283 | /** | 282 | /** |
284 | * Checks if a file is apparently javascript compatible (.js or .coffee). | 283 | * Checks if passed resource involves an HTTP url. |
285 | * | 284 | * |
286 | * @param String file Path to the file to test | 285 | * @param Object resource The PhantomJS HTTP resource object |
287 | * @return Boolean | 286 | * @return Boolean |
288 | */ | 287 | */ |
289 | function isJsFile(file) { | 288 | function isHTTPResource(resource) { |
290 | var ext = fileExt(file); | 289 | "use strict"; |
291 | return isString(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1; | 290 | return isObject(resource) && /^http/i.test(resource.url); |
292 | } | 291 | } |
293 | exports.isJsFile = isJsFile; | 292 | exports.isHTTPResource = isHTTPResource; |
294 | 293 | ||
295 | /** | 294 | /** |
296 | * Checks if the provided value is null | 295 | * Checks if a file is apparently javascript compatible (.js or .coffee). |
297 | * | 296 | * |
298 | * @return Boolean | 297 | * @param String file Path to the file to test |
299 | */ | 298 | * @return Boolean |
300 | function isNull(value) { | 299 | */ |
301 | return isType(value, "null"); | 300 | function isJsFile(file) { |
302 | } | 301 | "use strict"; |
303 | exports.isNull = isNull; | 302 | var ext = fileExt(file); |
303 | return isString(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1; | ||
304 | } | ||
305 | exports.isJsFile = isJsFile; | ||
304 | 306 | ||
305 | /** | 307 | /** |
306 | * Checks if value is a javascript Number | 308 | * Checks if the provided value is null |
307 | * | 309 | * |
308 | * @param mixed value | 310 | * @return Boolean |
309 | * @return Boolean | 311 | */ |
310 | */ | 312 | function isNull(value) { |
311 | function isNumber(value) { | 313 | "use strict"; |
312 | return isType(value, "number"); | 314 | return isType(value, "null"); |
313 | } | 315 | } |
314 | exports.isNumber = isNumber; | 316 | exports.isNull = isNull; |
315 | 317 | ||
316 | /** | 318 | /** |
317 | * Checks if value is a javascript Object | 319 | * Checks if value is a javascript Number |
318 | * | 320 | * |
319 | * @param mixed value | 321 | * @param mixed value |
320 | * @return Boolean | 322 | * @return Boolean |
321 | */ | 323 | */ |
322 | function isObject(value) { | 324 | function isNumber(value) { |
323 | var objectTypes = ["array", "object", "qtruntimeobject"]; | 325 | "use strict"; |
324 | return objectTypes.indexOf(betterTypeOf(value)) >= 0; | 326 | return isType(value, "number"); |
325 | } | 327 | } |
326 | exports.isObject = isObject; | 328 | exports.isNumber = isNumber; |
327 | 329 | ||
328 | /** | 330 | /** |
329 | * Checks if value is a javascript String | 331 | * Checks if value is a javascript Object |
330 | * | 332 | * |
331 | * @param mixed value | 333 | * @param mixed value |
332 | * @return Boolean | 334 | * @return Boolean |
333 | */ | 335 | */ |
334 | function isString(value) { | 336 | function isObject(value) { |
335 | return isType(value, "string"); | 337 | "use strict"; |
336 | } | 338 | var objectTypes = ["array", "object", "qtruntimeobject"]; |
337 | exports.isString = isString; | 339 | return objectTypes.indexOf(betterTypeOf(value)) >= 0; |
340 | } | ||
341 | exports.isObject = isObject; | ||
338 | 342 | ||
339 | /** | 343 | /** |
340 | * Shorthands for checking if a value is of the given type. Can check for | 344 | * Checks if value is a javascript String |
341 | * arrays. | 345 | * |
342 | * | 346 | * @param mixed value |
343 | * @param mixed what The value to check | 347 | * @return Boolean |
344 | * @param String typeName The type name ("string", "number", "function", etc.) | 348 | */ |
345 | * @return Boolean | 349 | function isString(value) { |
346 | */ | 350 | "use strict"; |
347 | function isType(what, typeName) { | 351 | return isType(value, "string"); |
348 | if (typeof typeName !== "string" || !typeName) { | 352 | } |
349 | throw new CasperError("You must pass isType() a typeName string"); | 353 | exports.isString = isString; |
350 | } | ||
351 | return betterTypeOf(what).toLowerCase() === typeName.toLowerCase(); | ||
352 | } | ||
353 | exports.isType = isType; | ||
354 | 354 | ||
355 | /** | 355 | /** |
356 | * Checks if the provided value is undefined | 356 | * Shorthands for checking if a value is of the given type. Can check for |
357 | * | 357 | * arrays. |
358 | * @return Boolean | 358 | * |
359 | */ | 359 | * @param mixed what The value to check |
360 | function isUndefined(value) { | 360 | * @param String typeName The type name ("string", "number", "function", etc.) |
361 | return isType(value, "undefined"); | 361 | * @return Boolean |
362 | */ | ||
363 | function isType(what, typeName) { | ||
364 | "use strict"; | ||
365 | if (typeof typeName !== "string" || !typeName) { | ||
366 | throw new CasperError("You must pass isType() a typeName string"); | ||
362 | } | 367 | } |
363 | exports.isUndefined = isUndefined; | 368 | return betterTypeOf(what).toLowerCase() === typeName.toLowerCase(); |
369 | } | ||
370 | exports.isType = isType; | ||
364 | 371 | ||
365 | /** | 372 | /** |
366 | * Checks if value is a valid selector Object. | 373 | * Checks if the provided value is undefined |
367 | * | 374 | * |
368 | * @param mixed value | 375 | * @return Boolean |
369 | * @return Boolean | 376 | */ |
370 | */ | 377 | function isUndefined(value) { |
371 | function isValidSelector(value) { | 378 | "use strict"; |
372 | if (isString(value)) { | 379 | return isType(value, "undefined"); |
373 | try { | 380 | } |
374 | // phantomjs env has a working document object, let's use it | 381 | exports.isUndefined = isUndefined; |
375 | document.querySelector(value); | 382 | |
376 | } catch(e) { | 383 | /** |
377 | if ('name' in e && e.name === 'SYNTAX_ERR') { | 384 | * Checks if value is a valid selector Object. |
378 | return false; | 385 | * |
379 | } | 386 | * @param mixed value |
380 | } | 387 | * @return Boolean |
381 | return true; | 388 | */ |
382 | } else if (isObject(value)) { | 389 | function isValidSelector(value) { |
383 | if (!value.hasOwnProperty('type')) { | 390 | "use strict"; |
384 | return false; | 391 | if (isString(value)) { |
385 | } | 392 | try { |
386 | if (!value.hasOwnProperty('path')) { | 393 | // phantomjs env has a working document object, let's use it |
387 | return false; | 394 | document.querySelector(value); |
388 | } | 395 | } catch(e) { |
389 | if (['css', 'xpath'].indexOf(value.type) === -1) { | 396 | if ('name' in e && e.name === 'SYNTAX_ERR') { |
390 | return false; | 397 | return false; |
391 | } | 398 | } |
392 | return true; | ||
393 | } | 399 | } |
394 | return false; | 400 | return true; |
401 | } else if (isObject(value)) { | ||
402 | if (!value.hasOwnProperty('type')) { | ||
403 | return false; | ||
404 | } | ||
405 | if (!value.hasOwnProperty('path')) { | ||
406 | return false; | ||
407 | } | ||
408 | if (['css', 'xpath'].indexOf(value.type) === -1) { | ||
409 | return false; | ||
410 | } | ||
411 | return true; | ||
395 | } | 412 | } |
396 | exports.isValidSelector = isValidSelector; | 413 | return false; |
414 | } | ||
415 | exports.isValidSelector = isValidSelector; | ||
397 | 416 | ||
398 | /** | 417 | /** |
399 | * Checks if the provided var is a WebPage instance | 418 | * Checks if the provided var is a WebPage instance |
400 | * | 419 | * |
401 | * @param mixed what | 420 | * @param mixed what |
402 | * @return Boolean | 421 | * @return Boolean |
403 | */ | 422 | */ |
404 | function isWebPage(what) { | 423 | function isWebPage(what) { |
405 | return betterTypeOf(what) === "qtruntimeobject" && what.objectName === 'WebPage'; | 424 | "use strict"; |
406 | } | 425 | return betterTypeOf(what) === "qtruntimeobject" && what.objectName === 'WebPage'; |
407 | exports.isWebPage = isWebPage; | 426 | } |
427 | exports.isWebPage = isWebPage; | ||
408 | 428 | ||
409 | /** | 429 | /** |
410 | * Object recursive merging utility. | 430 | * Object recursive merging utility. |
411 | * | 431 | * |
412 | * @param Object origin the origin object | 432 | * @param Object origin the origin object |
413 | * @param Object add the object to merge data into origin | 433 | * @param Object add the object to merge data into origin |
414 | * @return Object | 434 | * @return Object |
415 | */ | 435 | */ |
416 | function mergeObjects(origin, add) { | 436 | function mergeObjects(origin, add) { |
417 | for (var p in add) { | 437 | "use strict"; |
418 | try { | 438 | for (var p in add) { |
419 | if (add[p].constructor === Object) { | 439 | try { |
420 | origin[p] = mergeObjects(origin[p], add[p]); | 440 | if (add[p].constructor === Object) { |
421 | } else { | 441 | origin[p] = mergeObjects(origin[p], add[p]); |
422 | origin[p] = add[p]; | 442 | } else { |
423 | } | 443 | origin[p] = add[p]; |
424 | } catch(e) { | ||
425 | origin[p] = add[p]; | ||
426 | } | 444 | } |
445 | } catch(e) { | ||
446 | origin[p] = add[p]; | ||
427 | } | 447 | } |
428 | return origin; | ||
429 | } | 448 | } |
430 | exports.mergeObjects = mergeObjects; | 449 | return origin; |
450 | } | ||
451 | exports.mergeObjects = mergeObjects; | ||
431 | 452 | ||
432 | /** | 453 | /** |
433 | * Creates an (SG|X)ML node element. | 454 | * Creates an (SG|X)ML node element. |
434 | * | 455 | * |
435 | * @param String name The node name | 456 | * @param String name The node name |
436 | * @param Object attributes Optional attributes | 457 | * @param Object attributes Optional attributes |
437 | * @return HTMLElement | 458 | * @return HTMLElement |
438 | */ | 459 | */ |
439 | function node(name, attributes) { | 460 | function node(name, attributes) { |
440 | var _node = document.createElement(name); | 461 | "use strict"; |
441 | for (var attrName in attributes) { | 462 | var _node = document.createElement(name); |
442 | var value = attributes[attrName]; | 463 | for (var attrName in attributes) { |
443 | if (attributes.hasOwnProperty(attrName) && isString(attrName)) { | 464 | var value = attributes[attrName]; |
444 | _node.setAttribute(attrName, value); | 465 | if (attributes.hasOwnProperty(attrName) && isString(attrName)) { |
445 | } | 466 | _node.setAttribute(attrName, value); |
446 | } | 467 | } |
447 | return _node; | ||
448 | } | 468 | } |
449 | exports.node = node; | 469 | return _node; |
470 | } | ||
471 | exports.node = node; | ||
450 | 472 | ||
451 | /** | 473 | /** |
452 | * Serializes a value using JSON. | 474 | * Serializes a value using JSON. |
453 | * | 475 | * |
454 | * @param Mixed value | 476 | * @param Mixed value |
455 | * @return String | 477 | * @return String |
456 | */ | 478 | */ |
457 | function serialize(value, indent) { | 479 | function serialize(value, indent) { |
458 | if (isArray(value)) { | 480 | "use strict"; |
459 | value = value.map(function _map(prop) { | 481 | if (isArray(value)) { |
460 | return isFunction(prop) ? prop.toString().replace(/\s{2,}/, '') : prop; | 482 | value = value.map(function _map(prop) { |
461 | }); | 483 | return isFunction(prop) ? prop.toString().replace(/\s{2,}/, '') : prop; |
462 | } | 484 | }); |
463 | return JSON.stringify(value, null, indent); | ||
464 | } | 485 | } |
465 | exports.serialize = serialize; | 486 | return JSON.stringify(value, null, indent); |
487 | } | ||
488 | exports.serialize = serialize; | ||
466 | 489 | ||
467 | /** | 490 | /** |
468 | * Returns unique values from an array. | 491 | * Returns unique values from an array. |
469 | * | 492 | * |
470 | * Note: ugly code is ugly, but efficient: http://jsperf.com/array-unique2/8 | 493 | * Note: ugly code is ugly, but efficient: http://jsperf.com/array-unique2/8 |
471 | * | 494 | * |
472 | * @param Array array | 495 | * @param Array array |
473 | * @return Array | 496 | * @return Array |
474 | */ | 497 | */ |
475 | function unique(array) { | 498 | function unique(array) { |
476 | var o = {}, | 499 | "use strict"; |
477 | r = []; | 500 | var o = {}, |
478 | for (var i = 0, len = array.length; i !== len; i++) { | 501 | r = []; |
479 | var d = array[i]; | 502 | for (var i = 0, len = array.length; i !== len; i++) { |
480 | if (o[d] !== 1) { | 503 | var d = array[i]; |
481 | o[d] = 1; | 504 | if (o[d] !== 1) { |
482 | r[r.length] = d; | 505 | o[d] = 1; |
483 | } | 506 | r[r.length] = d; |
484 | } | 507 | } |
485 | return r; | ||
486 | } | 508 | } |
487 | exports.unique = unique; | 509 | return r; |
488 | })(exports); | 510 | } |
511 | exports.unique = unique; | ... | ... |
... | @@ -33,6 +33,36 @@ | ... | @@ -33,6 +33,36 @@ |
33 | var utils = require('utils'); | 33 | var utils = require('utils'); |
34 | var fs = require('fs'); | 34 | var fs = require('fs'); |
35 | 35 | ||
36 | /** | ||
37 | * Generates a value for 'classname' attribute of the JUnit XML report. | ||
38 | * | ||
39 | * Uses the (relative) file name of the current casper script without file | ||
40 | * extension as classname. | ||
41 | * | ||
42 | * @param String classname | ||
43 | * @return String | ||
44 | */ | ||
45 | function generateClassName(classname) { | ||
46 | "use strict"; | ||
47 | classname = classname.replace(phantom.casperPath, "").trim(); | ||
48 | var script = classname || phantom.casperScript; | ||
49 | if (script.indexOf(fs.workingDirectory) === 0) { | ||
50 | script = script.substring(fs.workingDirectory.length + 1); | ||
51 | } | ||
52 | if (script.indexOf('/') === 0) { | ||
53 | script = script.substring(1, script.length); | ||
54 | } | ||
55 | if (~script.indexOf('.')) { | ||
56 | script = script.substring(0, script.lastIndexOf('.')); | ||
57 | } | ||
58 | return script || "unknown"; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Creates a XUnit instance | ||
63 | * | ||
64 | * @return XUnit | ||
65 | */ | ||
36 | exports.create = function create() { | 66 | exports.create = function create() { |
37 | "use strict"; | 67 | "use strict"; |
38 | return new XUnitExporter(); | 68 | return new XUnitExporter(); |
... | @@ -88,31 +118,6 @@ XUnitExporter.prototype.addFailure = function addFailure(classname, name, messag | ... | @@ -88,31 +118,6 @@ XUnitExporter.prototype.addFailure = function addFailure(classname, name, messag |
88 | }; | 118 | }; |
89 | 119 | ||
90 | /** | 120 | /** |
91 | * Generates a value for 'classname' attribute of the JUnit XML report. | ||
92 | * | ||
93 | * Uses the (relative) file name of the current casper script without file | ||
94 | * extension as classname. | ||
95 | * | ||
96 | * @param String classname | ||
97 | * @return String | ||
98 | */ | ||
99 | function generateClassName(classname) { | ||
100 | "use strict"; | ||
101 | classname = classname.replace(phantom.casperPath, "").trim(); | ||
102 | var script = classname || phantom.casperScript; | ||
103 | if (script.indexOf(fs.workingDirectory) === 0) { | ||
104 | script = script.substring(fs.workingDirectory.length + 1); | ||
105 | } | ||
106 | if (script.indexOf('/') === 0) { | ||
107 | script = script.substring(1, script.length); | ||
108 | } | ||
109 | if (~script.indexOf('.')) { | ||
110 | script = script.substring(0, script.lastIndexOf('.')); | ||
111 | } | ||
112 | return script || "unknown"; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * Retrieves generated XML object - actually an HTMLElement. | 121 | * Retrieves generated XML object - actually an HTMLElement. |
117 | * | 122 | * |
118 | * @return HTMLElement | 123 | * @return HTMLElement | ... | ... |
1 | /*global phantom*/ | ||
2 | |||
1 | if (!phantom.casperLoaded) { | 3 | if (!phantom.casperLoaded) { |
2 | console.log('This script must be invoked using the casperjs executable'); | 4 | console.log('This script must be invoked using the casperjs executable'); |
3 | phantom.exit(1); | 5 | phantom.exit(1); |
... | @@ -15,6 +17,7 @@ var casper = require('casper').create({ | ... | @@ -15,6 +17,7 @@ var casper = require('casper').create({ |
15 | 17 | ||
16 | // local utils | 18 | // local utils |
17 | function checkSelfTest(tests) { | 19 | function checkSelfTest(tests) { |
20 | "use strict"; | ||
18 | var isCasperTest = false; | 21 | var isCasperTest = false; |
19 | tests.forEach(function(test) { | 22 | tests.forEach(function(test) { |
20 | var testDir = fs.absolute(fs.dirname(test)); | 23 | var testDir = fs.absolute(fs.dirname(test)); |
... | @@ -28,6 +31,7 @@ function checkSelfTest(tests) { | ... | @@ -28,6 +31,7 @@ function checkSelfTest(tests) { |
28 | } | 31 | } |
29 | 32 | ||
30 | function checkIncludeFile(include) { | 33 | function checkIncludeFile(include) { |
34 | "use strict"; | ||
31 | var absInclude = fs.absolute(include.trim()); | 35 | var absInclude = fs.absolute(include.trim()); |
32 | if (!fs.exists(absInclude)) { | 36 | if (!fs.exists(absInclude)) { |
33 | casper.warn("%s file not found, can't be included", absInclude); | 37 | casper.warn("%s file not found, can't be included", absInclude); |
... | @@ -60,6 +64,7 @@ if (casper.cli.get('no-colors') === true) { | ... | @@ -60,6 +64,7 @@ if (casper.cli.get('no-colors') === true) { |
60 | // test paths are passed as args | 64 | // test paths are passed as args |
61 | if (casper.cli.args.length) { | 65 | if (casper.cli.args.length) { |
62 | tests = casper.cli.args.filter(function(path) { | 66 | tests = casper.cli.args.filter(function(path) { |
67 | "use strict"; | ||
63 | return fs.isFile(path) || fs.isDirectory(path); | 68 | return fs.isFile(path) || fs.isDirectory(path); |
64 | }); | 69 | }); |
65 | } else { | 70 | } else { |
... | @@ -75,6 +80,7 @@ if (!phantom.casperSelfTest && checkSelfTest(tests)) { | ... | @@ -75,6 +80,7 @@ if (!phantom.casperSelfTest && checkSelfTest(tests)) { |
75 | 80 | ||
76 | // includes handling | 81 | // includes handling |
77 | this.loadIncludes.forEach(function(include){ | 82 | this.loadIncludes.forEach(function(include){ |
83 | "use strict"; | ||
78 | var container; | 84 | var container; |
79 | if (casper.cli.has(include)) { | 85 | if (casper.cli.has(include)) { |
80 | container = casper.cli.get(include).split(',').map(function(file) { | 86 | container = casper.cli.get(include).split(',').map(function(file) { |
... | @@ -89,6 +95,7 @@ this.loadIncludes.forEach(function(include){ | ... | @@ -89,6 +95,7 @@ this.loadIncludes.forEach(function(include){ |
89 | 95 | ||
90 | // test suites completion listener | 96 | // test suites completion listener |
91 | casper.test.on('tests.complete', function() { | 97 | casper.test.on('tests.complete', function() { |
98 | "use strict"; | ||
92 | this.renderResults(true, undefined, casper.cli.get('xunit') || undefined); | 99 | this.renderResults(true, undefined, casper.cli.get('xunit') || undefined); |
93 | }); | 100 | }); |
94 | 101 | ... | ... |
1 | /** | 1 | /** |
2 | * CasperJS local HTTP test server | 2 | * CasperJS local HTTP test server |
3 | */ | 3 | */ |
4 | |||
5 | /*global phantom casper require*/ | ||
6 | |||
4 | var colorizer = require('colorizer').create('Colorizer'); | 7 | var colorizer = require('colorizer').create('Colorizer'); |
5 | var fs = require('fs'); | 8 | var fs = require('fs'); |
6 | var utils = require('utils'); | 9 | var utils = require('utils'); |
... | @@ -9,10 +12,12 @@ var service; | ... | @@ -9,10 +12,12 @@ var service; |
9 | var testServerPort = 54321; | 12 | var testServerPort = 54321; |
10 | 13 | ||
11 | function info(message) { | 14 | function info(message) { |
15 | "use strict"; | ||
12 | console.log(colorizer.colorize('INFO', 'INFO_BAR') + ' ' + message); | 16 | console.log(colorizer.colorize('INFO', 'INFO_BAR') + ' ' + message); |
13 | } | 17 | } |
14 | 18 | ||
15 | service = server.listen(testServerPort, function(request, response) { | 19 | service = server.listen(testServerPort, function(request, response) { |
20 | "use strict"; | ||
16 | var pageFile = fs.pathJoin(phantom.casperPath, request.url); | 21 | var pageFile = fs.pathJoin(phantom.casperPath, request.url); |
17 | if (!fs.exists(pageFile) || !fs.isFile(pageFile)) { | 22 | if (!fs.exists(pageFile) || !fs.isFile(pageFile)) { |
18 | response.statusCode = 404; | 23 | response.statusCode = 404; |
... | @@ -26,16 +31,18 @@ service = server.listen(testServerPort, function(request, response) { | ... | @@ -26,16 +31,18 @@ service = server.listen(testServerPort, function(request, response) { |
26 | 31 | ||
27 | // overriding Casper.open to prefix all test urls | 32 | // overriding Casper.open to prefix all test urls |
28 | casper.setFilter('open.location', function(location) { | 33 | casper.setFilter('open.location', function(location) { |
34 | "use strict"; | ||
29 | if (/^file/.test(location)) { | 35 | if (/^file/.test(location)) { |
30 | return location; | 36 | return location; |
31 | } | 37 | } |
32 | if (!/^http/.test(location)) { | 38 | if (!/^http/.test(location)) { |
33 | return f('http://localhost:%d/%s', testServerPort, location); | 39 | return utils.format('http://localhost:%d/%s', testServerPort, location); |
34 | } | 40 | } |
35 | return location; | 41 | return location; |
36 | }); | 42 | }); |
37 | 43 | ||
38 | // test suites completion listener | 44 | // test suites completion listener |
39 | casper.test.on('tests.complete', function() { | 45 | casper.test.on('tests.complete', function() { |
46 | "use strict"; | ||
40 | server.close(); | 47 | server.close(); |
41 | }); | 48 | }); | ... | ... |
-
Please register or sign in to post a comment