diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..164ecb478 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +script: cd test && phantomjs lib/run_jasmine_test.coffee index.html diff --git a/test/index.html b/test/index.html index 9d7da2254..20fe70185 100644 --- a/test/index.html +++ b/test/index.html @@ -7,6 +7,8 @@ + + @@ -18,31 +20,31 @@ - - - - + + + + - - - - - + + + + + - - + + - + - - - + + + - - - - - + + + + + @@ -66,9 +68,11 @@ jasmineEnv.updateInterval = 1000; var htmlReporter = new jasmine.HtmlReporter(); - jasmineEnv.addReporter(htmlReporter); + window.console_reporter = new jasmine.ConsoleReporter(); + jasmineEnv.addReporter(console_reporter); + jasmineEnv.specFilter = function (spec) { return htmlReporter.specFilter(spec); }; diff --git a/test/lib/bind-shim.js b/test/lib/bind-shim.js new file mode 100644 index 000000000..14d90b768 --- /dev/null +++ b/test/lib/bind-shim.js @@ -0,0 +1,26 @@ +// PhantomJS is missing Function.prototype.bind: +// http://code.google.com/p/phantomjs/issues/detail?id=522 + +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} diff --git a/test/lib/console-runner.js b/test/lib/console-runner.js new file mode 100644 index 000000000..fcd4a6d47 --- /dev/null +++ b/test/lib/console-runner.js @@ -0,0 +1,104 @@ +/** + Jasmine Reporter that outputs test results to the browser console. + Useful for running in a headless environment such as PhantomJs, ZombieJs etc. + + Usage: + // From your html file that loads jasmine: + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter()); + jasmine.getEnv().execute(); +*/ + +(function(jasmine, console) { + if (!jasmine) { + throw "jasmine library isn't loaded!"; + } + + var ANSI = {} + ANSI.color_map = { + "green" : 32, + "red" : 31 + } + + ANSI.colorize_text = function(text, color) { + var color_code = this.color_map[color]; + return "\033[" + color_code + "m" + text + "\033[0m"; + } + + var ConsoleReporter = function() { + if (!console || !console.log) { throw "console isn't present!"; } + this.status = this.statuses.stopped; + }; + + var proto = ConsoleReporter.prototype; + proto.statuses = { + stopped : "stopped", + running : "running", + fail : "fail", + success : "success" + }; + + proto.reportRunnerStarting = function(runner) { + this.status = this.statuses.running; + this.start_time = (new Date()).getTime(); + this.executed_specs = 0; + this.passed_specs = 0; + this.log("Starting..."); + }; + + proto.reportRunnerResults = function(runner) { + var failed = this.executed_specs - this.passed_specs; + var spec_str = this.executed_specs + (this.executed_specs === 1 ? " spec, " : " specs, "); + var fail_str = failed + (failed === 1 ? " failure in " : " failures in "); + var color = (failed > 0)? "red" : "green"; + var dur = (new Date()).getTime() - this.start_time; + + this.log(""); + this.log("Finished"); + this.log("-----------------"); + this.log(spec_str + fail_str + (dur/1000) + "s.", color); + + this.status = (failed > 0)? this.statuses.fail : this.statuses.success; + + /* Print something that signals that testing is over so that headless browsers + like PhantomJs know when to terminate. */ + this.log(""); + this.log("ConsoleReporter finished"); + }; + + + proto.reportSpecStarting = function(spec) { + this.executed_specs++; + }; + + proto.reportSpecResults = function(spec) { + if (spec.results().passed()) { + this.passed_specs++; + return; + } + + var resultText = spec.suite.description + " : " + spec.description; + this.log(resultText, "red"); + + var items = spec.results().getItems() + for (var i = 0; i < items.length; i++) { + var trace = items[i].trace.stack || items[i].trace; + this.log(trace, "red"); + } + }; + + proto.reportSuiteResults = function(suite) { + if (!suite.parentSuite) { return; } + var results = suite.results(); + var failed = results.totalCount - results.passedCount; + var color = (failed > 0)? "red" : "green"; + this.log(suite.description + ": " + results.passedCount + " of " + results.totalCount + " passed.", color); + }; + + proto.log = function(str, color) { + var text = (color != undefined)? ANSI.colorize_text(str, color) : str; + console.log(text) + }; + + jasmine.ConsoleReporter = ConsoleReporter; +})(jasmine, console); + diff --git a/test/lib/run_jasmine_test.coffee b/test/lib/run_jasmine_test.coffee new file mode 100644 index 000000000..3c276aeab --- /dev/null +++ b/test/lib/run_jasmine_test.coffee @@ -0,0 +1,46 @@ +#!/usr/local/bin/phantomjs + +# Runs a Jasmine Suite from an html page +# @page is a PhantomJs page object +# @exit_func is the function to call in order to exit the script + +class PhantomJasmineRunner + constructor: (@page, @exit_func = phantom.exit) -> + @tries = 0 + @max_tries = 10 + + get_status: -> @page.evaluate(-> console_reporter.status) + + terminate: -> + switch @get_status() + when "success" then @exit_func 0 + when "fail" then @exit_func 1 + else @exit_func 2 + +# Script Begin +if phantom.args.length == 0 + console.log "Need a url as the argument" + phantom.exit 1 + +page = new WebPage() + +runner = new PhantomJasmineRunner(page) + +# Don't supress console output +page.onConsoleMessage = (msg) -> + console.log msg + + # Terminate when the reporter singals that testing is over. + # We cannot use a callback function for this (because page.evaluate is sandboxed), + # so we have to *observe* the website. + if msg == "ConsoleReporter finished" + runner.terminate() + +address = phantom.args[0] + +page.open address, (status) -> + if status != "success" + console.log "can't load the address!" + phantom.exit 1 + + # Now we wait until onConsoleMessage reads the termination signal from the log.