diff --git a/bin/flowerbox b/bin/flowerbox index 0915ce0..bd186fc 100755 --- a/bin/flowerbox +++ b/bin/flowerbox @@ -5,9 +5,7 @@ require 'thor' class Flowerbox::CLI < Thor desc "test DIR", "Run the specs found in spec dir, loading spec_helper.rb for configuration details" - method_options :pwd => :string - method_options :runners => :string - method_options :verbose => false + method_options :pwd => :string, :runners => :string, :verbose_server => false def test(dir = "spec/javascripts") Dir.chdir(pwd) do exit Flowerbox.run(dir, options) diff --git a/lib/assets/javascripts/flowerbox.js.coffee b/lib/assets/javascripts/flowerbox.js.coffee index d99f66e..53c8ae2 100644 --- a/lib/assets/javascripts/flowerbox.js.coffee +++ b/lib/assets/javascripts/flowerbox.js.coffee @@ -1,8 +1,35 @@ +#= require_self +#= require flowerbox/result +#= require flowerbox/exception +# Flowerbox = baseUrl: '/' + ping: -> + Flowerbox.contact('ping') + + pause: (time) -> + t = (new Date()).getTime() + + while (t + time) > (new Date().getTime()) + Flowerbox.ping() + contact: (url, data...) -> - xhr = new XMLHttpRequest() - xhr.open("POST", Flowerbox.baseUrl + url, false) - xhr.setRequestHeader("Accept", "application/json") - xhr.send(JSON.stringify(data)) + attempts = 3 + + doContact = -> + attempts -= 1 + + try + xhr = new XMLHttpRequest() + xhr.open("POST", Flowerbox.baseUrl + url, false) + xhr.setRequestHeader("Accept", "application/json") + xhr.send(JSON.stringify(data)) + catch e + if attempts == 0 + throw e + else + doContact() + + doContact() fail: -> + diff --git a/lib/assets/javascripts/flowerbox/cucumber.js.coffee b/lib/assets/javascripts/flowerbox/cucumber.js.coffee index 1e4fcb2..ea66f82 100644 --- a/lib/assets/javascripts/flowerbox/cucumber.js.coffee +++ b/lib/assets/javascripts/flowerbox/cucumber.js.coffee @@ -6,25 +6,68 @@ Flowerbox.Cucumber.features = -> Flowerbox.Cucumber.Features.join("\n") Flowerbox.World = (code = null) -> - -> - for code in (Flowerbox.World.Code || []) - code.apply(this) + if code + Flowerbox.World.Code ||= [] + Flowerbox.World.Code.push(code) + else + -> + for code in (Flowerbox.World.Code || []) + code.apply(this) + +Flowerbox.Matchers = + toEqual: (expected) -> + @message = "Expected #{@actual} #{@notMessage} equal #{expected}" + @actual == expected + +Flowerbox.World -> + @assert = (what, message = 'failed') -> + throw new Error(message) if !what + + @_pending = false + @pending = -> @_pending = true + + @expect = (what) -> new Flowerbox.Matcher(what) + + @addMatchers = (data) -> Flowerbox.Matcher.addMatchers(data) + + Flowerbox.Matcher.matchers = {} + @addMatchers(Flowerbox.Matchers) + +class Flowerbox.Matcher + @addMatchers: (data) -> + for method, code of data + Flowerbox.Matcher.matchers[method] = code + + constructor: (@actual, @_negated = false) -> + @not = this.negated() if !@_negated + @notMessage = if @_negated then "to not" else "to" + + generateMethod = (method) -> + (args...) -> @fail() if !@maybeNegate(Flowerbox.Matcher.matchers[method].apply(this, args)) + + for method, code of Flowerbox.Matcher.matchers + this[method] = generateMethod(method) + + negated: -> + new Flowerbox.Matcher(@actual, true) + + fail: -> + throw new Error(@message) + + maybeNegate: (result) -> + result = !result if @_negated + result Flowerbox.Step = (type, match, code) -> Flowerbox.World.Code ||= [] Flowerbox.World.Code.push (args..., callback) -> - this[type] match, (args..., callback) -> - - pending = false - - this.pending = -> pending = true - - result = code.apply(this) + this[type] match, (args..., callback) => + result = code.apply(this, args) if result? and result.__prototype__ == Error callback.fail(result) else - if pending then callback.pending("pending") else callback() + if @_pending then callback.pending("pending") else callback() null @@ -41,13 +84,14 @@ stepGenerator = (type) -> Flowerbox[type] = (match, code) -> if !Flowerbox.Step.files[match.toString()] count = 2 - for line in (new Error()).stack.split('\n') - if line.match(/__F__/) - count -= 1 + if stack = (new Error()).stack + for line in stack.split('\n') + if line.match(/__F__/) + count -= 1 - if count == 0 - Flowerbox.Step.files[match.toString()] = [ match, line.replace(/^.*__F__/, '') ] - break + if count == 0 + Flowerbox.Step.files[match.toString()] = [ match, line.replace(/^.*__F__/, '') ] + break Flowerbox.Step(type, match, code) diff --git a/lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee b/lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee index 28d9172..52bb46a 100644 --- a/lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee +++ b/lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee @@ -2,6 +2,19 @@ Flowerbox ||= {} Flowerbox.Cucumber ||= {} class Flowerbox.Cucumber.Reporter + nameParts: -> + [ @feature.getName(), @scenario.getName(), "#{this.type()} #{@step.getName()}" ] + + type: -> + type = "Given" + + if @step.isOutcomeStep() + type = "Then" + else if @step.isEventStep() + type = "When" + + type + hear: (event, callback) -> switch event.getName() when 'BeforeFeatures' @@ -26,40 +39,27 @@ class Flowerbox.Cucumber.Reporter when 'StepResult' stepResult = event.getPayloadItem('stepResult') - type = "Given" - - if @step.isOutcomeStep() - type = "Then" - else if @step.isEventStep() - type = "When" - file = Flowerbox.Step.matchFile(@step.getName()) || "unknown:0" - test = { passed_: false, message: 'skipped', splitName: [ @feature.getName(), @scenario.getName(), "#{type} #{@step.getName()}" ], trace: { stack: [ file ] } } + result = new Flowerbox.Result(step_type: this.type(), source: 'cucumber', original_name: @step.getName(), name: this.nameParts(), file: file) if stepResult.isSuccessful() - test.passed_ = true + result.status = Flowerbox.Result.SUCCESS else if stepResult.isPending() - test.message = "pending" + result.status = Flowerbox.Result.PENDING else if stepResult.isUndefined() - regexp = @step.getName() - regexp = regexp.replace(/"[^"]+"/g, '"([^"]+)"') - - test.message = """ - Step not defined. Define it with the following: - - Flowerbox.#{type} /^#{regexp}$/, -> - @pending() - - - """ + result.status = Flowerbox.Result.UNDEFINED else if stepResult.isFailed() + result.status = Flowerbox.Result.FAILURE + error = stepResult.getFailureException() + stack = (error.stack || "message\n#{file}:0").split("\n") - test.message = error.message - test.trace.stack = [ 'file:1' ] + failure = { runner: Flowerbox.environment, message: error.message, stack: stack } - Flowerbox.contact("finish_test", @step.getName(), [ { items_: [ test ] } ]) + result.failures.push(failure) + + Flowerbox.contact("finish_test", result.toJSON()) callback() diff --git a/lib/assets/javascripts/flowerbox/cucumber/selenium.js.coffee b/lib/assets/javascripts/flowerbox/cucumber/selenium.js.coffee new file mode 100644 index 0000000..4332242 --- /dev/null +++ b/lib/assets/javascripts/flowerbox/cucumber/selenium.js.coffee @@ -0,0 +1,4 @@ +window.onerror = (message, file, line) -> + Flowerbox.contact("log", message) + Flowerbox.contact("log", " #{file}:#{line}") + diff --git a/lib/assets/javascripts/flowerbox/exception.js.coffee b/lib/assets/javascripts/flowerbox/exception.js.coffee new file mode 100644 index 0000000..bb22b30 --- /dev/null +++ b/lib/assets/javascripts/flowerbox/exception.js.coffee @@ -0,0 +1,5 @@ +class Flowerbox.Exception + constructor: (@stack) -> + + toJSON: -> + { trace: { stack: @stack } } diff --git a/lib/assets/javascripts/flowerbox/jasmine/reporter.js.coffee b/lib/assets/javascripts/flowerbox/jasmine/reporter.js.coffee index ad92dbd..1a56c1a 100644 --- a/lib/assets/javascripts/flowerbox/jasmine/reporter.js.coffee +++ b/lib/assets/javascripts/flowerbox/jasmine/reporter.js.coffee @@ -7,12 +7,21 @@ class jasmine.FlowerboxReporter Flowerbox.contact("start_test", spec.description) if spec.description == 'encountered a declaration exception' - Flowerbox.contact("finish_test", spec.description, { trace: { stack: [ spec.description ] } }) + Flowerbox.contact("finish_test", new Flowerbox.Exception([ spec.description ])) Flowerbox.contact("results", 0) Flowerbox.fail() if Flowerbox.fail? reportSpecResults: (spec) -> - Flowerbox.contact("finish_test", spec.description, spec.results()) + result = new Flowerbox.Result(status: Flowerbox.Result.SUCCESS, source: 'jasmine', name: spec.getSpecSplitName(), file: 'unknown:0') + + for item in spec.results().items_ + if !item.passed_ + result.status = Flowerbox.Result.FAILURE + failure = { runner: Flowerbox.environment, message: item.message, stack: item.trace.stack } + + result.failures.push(failure) + + Flowerbox.contact("finish_test", result) reportRunnerResults: (runner) -> Flowerbox.contact("results", (new Date().getTime()) - @time) diff --git a/lib/assets/javascripts/flowerbox/jasmine/selenium.js.coffee b/lib/assets/javascripts/flowerbox/jasmine/selenium.js.coffee index a3ee9de..473db0f 100644 --- a/lib/assets/javascripts/flowerbox/jasmine/selenium.js.coffee +++ b/lib/assets/javascripts/flowerbox/jasmine/selenium.js.coffee @@ -7,9 +7,9 @@ jasmine.Spec.beforeAddMatcherResult().push -> if e.stack file = switch Flowerbox.environment when 'firefox' - e.stack.split("\n")[3].replace(/^[^@]*@/, '') + e.stack.split("\n")[3] when 'chrome' - e.stack.split("\n")[4].replace(/^.*\((.*)\)$/, '$1').replace(/:[^:]+$/, '') + e.stack.split("\n")[4] - @trace = { stack: [ file.replace(/^.*__F__/, '') ] } + @trace = { stack: [ file ] } diff --git a/lib/assets/javascripts/flowerbox/result.js.coffee b/lib/assets/javascripts/flowerbox/result.js.coffee new file mode 100644 index 0000000..cf412a4 --- /dev/null +++ b/lib/assets/javascripts/flowerbox/result.js.coffee @@ -0,0 +1,17 @@ +Flowerbox ||= {} + +class Flowerbox.Result + @SUCCESS = 'success' + @PENDING = 'pending' + @UNDEFINED = 'undefined' + @FAILURE = 'failure' + @SKIPPED = 'skipped' + + constructor: (data) -> + for key, value of data + this[key] = value + + this.status ||= Flowerbox.Result.SKIPPED + this.failures ||= [] + + toJSON: => this diff --git a/lib/flowerbox.rb b/lib/flowerbox.rb index 551aade..22a8e67 100644 --- a/lib/flowerbox.rb +++ b/lib/flowerbox.rb @@ -3,6 +3,10 @@ require 'flowerbox-delivery' require 'rainbow' module Flowerbox + module CoreExt + autoload :Module, 'flowerbox/core_ext/module' + end + autoload :Runner, 'flowerbox/runner' module Runner @@ -26,12 +30,19 @@ module Flowerbox autoload :ResultSet, 'flowerbox/result_set' autoload :GatheredResult, 'flowerbox/gathered_result' autoload :Result, 'flowerbox/result' - autoload :BaseResult, 'flowerbox/base_result' - autoload :Success, 'flowerbox/success' - autoload :Failure, 'flowerbox/failure' - autoload :Exception, 'flowerbox/exception' + + autoload :Reporter, 'flowerbox/reporter' class << self + attr_writer :reporters + + def reset! + @spec_patterns = nil + @spec_files = nil + @asset_paths = nil + @reporters = nil + end + def spec_patterns @spec_patterns ||= [] end @@ -40,6 +51,10 @@ module Flowerbox @asset_paths ||= [] end + def reporters + @reporters ||= [] + end + def test_with(what) self.test_environment = Flowerbox::TestEnvironment.for(what) end @@ -48,6 +63,10 @@ module Flowerbox self.runner_environment = whats.flatten.collect { |what| Flowerbox::Runner.for(what.to_s) } end + def report_with(*whats) + self.reporters = whats.flatten.collect { |what| Flowerbox::Reporter.for(what.to_s) } + end + def path Pathname(File.expand_path('../..', __FILE__)) end @@ -61,6 +80,10 @@ module Flowerbox spec_patterns << "**/*_spec*" spec_patterns << "*/*_spec*" end + + if reporters.empty? + reporters << Flowerbox::Reporter.for(:progress) + end end def bare_coffeescript @@ -68,6 +91,8 @@ module Flowerbox end def run(dir, options = {}) + reset! + load File.join(dir, 'spec_helper.rb') if options[:runners] @@ -81,13 +106,18 @@ module Flowerbox result_set = ResultSet.new + time = 0 + realtime = Time.now.to_i + Flowerbox.runner_environment.each do |env| env.ensure_configured! result_set << env.run(build_sprockets_for(dir), spec_files_for(dir), options) + + time += env.time end - result_set.print + result_set.print(:time => time, :realtime => Time.now.to_i - realtime) result_set.exitstatus end @@ -116,7 +146,7 @@ module Flowerbox Flowerbox.spec_patterns.each do |pattern| Dir[File.join(dir, pattern)].each do |file| - @spec_files << file.gsub(dir + '/', '') + @spec_files << File.expand_path(file) end end diff --git a/lib/flowerbox/base_result.rb b/lib/flowerbox/base_result.rb deleted file mode 100644 index e7d5445..0000000 --- a/lib/flowerbox/base_result.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Flowerbox - class BaseResult < Result - attr_reader :name, :message, :file - - def initialize(name, message, file = nil) - @name, @message, @file = name, message, file - end - - def ==(other) - @name == other.name && @message == other.message - end - - def to_s - "#{message} [#{runners.join(',')}] (#{translated_file}:#{line_number})" - end - end -end - diff --git a/lib/flowerbox/core_ext/module.rb b/lib/flowerbox/core_ext/module.rb new file mode 100644 index 0000000..aec667f --- /dev/null +++ b/lib/flowerbox/core_ext/module.rb @@ -0,0 +1,14 @@ +module Flowerbox + module CoreExt + module Module + def find_constant(string) + const_get(constants.find { |f| f.to_s.downcase == string.to_s.downcase }) + end + + def for(env) + find_constant(env).new + end + end + end +end + diff --git a/lib/flowerbox/exception.rb b/lib/flowerbox/exception.rb deleted file mode 100644 index 5ed78f1..0000000 --- a/lib/flowerbox/exception.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Flowerbox - class Exception < Result - attr_reader :name - - def initialize(name) - @name = name - end - - def print - puts "[#{runners.join(',')}] #{name}" - puts - end - end -end - diff --git a/lib/flowerbox/gathered_result.rb b/lib/flowerbox/gathered_result.rb index 2f8d724..14e4f6e 100644 --- a/lib/flowerbox/gathered_result.rb +++ b/lib/flowerbox/gathered_result.rb @@ -25,16 +25,6 @@ module Flowerbox def success? @results.all?(&:success?) end - - def print - puts name.join(' ') - - results.each do |result| - puts " #{result}" - end - - puts - end end end diff --git a/lib/flowerbox/rack.rb b/lib/flowerbox/rack.rb index 39dfe45..e6f7317 100644 --- a/lib/flowerbox/rack.rb +++ b/lib/flowerbox/rack.rb @@ -33,13 +33,17 @@ module Flowerbox end empty_post '/finish_test' do - runner.add_results(data.flatten[1..-1]) + runner.add_results(data.flatten) end empty_post '/log' do runner.log(data.first) end + empty_post '/ping' do + runner.ping + end + get %r{^/__F__(/.*)$} do |file| File.read(file) end diff --git a/lib/flowerbox/reporter.rb b/lib/flowerbox/reporter.rb new file mode 100644 index 0000000..f6a00c8 --- /dev/null +++ b/lib/flowerbox/reporter.rb @@ -0,0 +1,12 @@ +module Flowerbox + module Reporter + extend Flowerbox::CoreExt::Module + + autoload :Base, 'flowerbox/reporter/base' + autoload :ConsoleBase, 'flowerbox/reporter/console_base' + autoload :Verbose, 'flowerbox/reporter/verbose' + autoload :Progress, 'flowerbox/reporter/progress' + autoload :FileDisplay, 'flowerbox/reporter/file_display' + end +end + diff --git a/lib/flowerbox/reporter/base.rb b/lib/flowerbox/reporter/base.rb new file mode 100644 index 0000000..828077b --- /dev/null +++ b/lib/flowerbox/reporter/base.rb @@ -0,0 +1,45 @@ +module Flowerbox::Reporter + class Base + def post_report_data + @post_report_data ||= {} + end + + def report(gathered_results, data = {}) + gathered_results.each do |result| + self.send("report_#{result.type}", result) + end + + report_numeric_results(gathered_results, data) + + post_report_data.each do |type, data| + puts + self.send("post_report_#{type}", data) + end + end + + def post_report_success(data) ; end + def post_report_skipped(data) ; end + def post_report_pending(data) ; end + + def post_report_undefined(data) + puts "Some steps were not defined. Define them using the following code:".foreground(:yellow) + puts + + data.each do |code| + puts code.foreground(:yellow) + end + end + + def post_report_failed(data) ; end + + protected + def numeric_results_for(gathered_results) + { + :total => gathered_results.length, + :failures => gathered_results.find_all(&:failure?).length, + :pending => gathered_results.find_all(&:pending?).length + } + end + end +end + diff --git a/lib/flowerbox/reporter/console_base.rb b/lib/flowerbox/reporter/console_base.rb new file mode 100644 index 0000000..02c4cf2 --- /dev/null +++ b/lib/flowerbox/reporter/console_base.rb @@ -0,0 +1,41 @@ +module Flowerbox::Reporter + class ConsoleBase < Base + include FileDisplay + + def report_success(result) ; end + def report_skipped(result) ; end + + def report_pending(result) + puts + puts result.name.join(" - ").foreground(:yellow) + puts " Pending (finish defining the test)".foreground(:yellow) + ' ' + path_for(result) + end + + def report_undefined(result) + (post_report_data[:undefined] ||= []) << result.test_environment.obtain_test_definition_for(result) + end + + def report_failure(result) + puts + puts result.name.join(" - ").foreground(:red) + result.failures.each do |failure| + puts " " + failure.message.foreground(:red) + " [" + failure.runners.join(',') + "] " + path_for(failure) + end + end + + def report_numeric_results(gathered_results, data = {}) + results = numeric_results_for(gathered_results) + + output = "#{results[:total]} total, #{results[:failures]} failed, #{results[:pending]} pending, #{data[:time].to_f / 1000} (#{data[:realtime]}.0) secs." + color = :green + + color = :yellow if results[:pending] > 0 + color = :red if results[:failures] > 0 + + puts + puts output.foreground(color) + puts + end + end +end + diff --git a/lib/flowerbox/reporter/file_display.rb b/lib/flowerbox/reporter/file_display.rb new file mode 100644 index 0000000..2db1bc9 --- /dev/null +++ b/lib/flowerbox/reporter/file_display.rb @@ -0,0 +1,13 @@ +module Flowerbox::Reporter + module FileDisplay + private + def path_for(result) + if result.line.gsub(%r{[^0-9]}, '').to_i == 0 + '' + else + result.translated_file_and_line.foreground(:cyan) + end + end + end +end + diff --git a/lib/flowerbox/reporter/progress.rb b/lib/flowerbox/reporter/progress.rb new file mode 100644 index 0000000..fcb93c8 --- /dev/null +++ b/lib/flowerbox/reporter/progress.rb @@ -0,0 +1,29 @@ +module Flowerbox::Reporter + class Progress < ConsoleBase + def report_progress(result) + print self.send("progress_#{result.type}") + $stdout.flush + end + + def progress_skipped + "-".foreground(:blue) + end + + def progress_success + ".".foreground(:green) + end + + def progress_failure + "F".foreground(:red) + end + + def progress_pending + "P".foreground(:yellow) + end + + def progress_undefined + "U".foreground(:yellow) + end + end +end + diff --git a/lib/flowerbox/reporter/verbose.rb b/lib/flowerbox/reporter/verbose.rb new file mode 100644 index 0000000..833751c --- /dev/null +++ b/lib/flowerbox/reporter/verbose.rb @@ -0,0 +1,51 @@ +module Flowerbox::Reporter + class Verbose < ConsoleBase + def initialize + @name_stack = [] + end + + def indent(text, count) + " " * count + text + end + + def report_progress(result) + result.name.each_with_index do |name, index| + if @name_stack[index] != name + if name != result.name.last + message = name + else + message = self.send("progress_#{result.type}", result) + if !(file_display = path_for(result)).empty? + message << " # #{file_display}" + end + end + + puts indent(message, index) + end + end + + @name_stack = result.name.dup + end + + def progress_skipped(result) + result.name.last.foreground(:blue) + end + + def progress_success(result) + result.name.last.foreground(:green) + end + + def progress_failure(result) + result.name.last.foreground(:red) + end + + def progress_pending(result) + "#{result.name.last} (Pending)".foreground(:yellow) + end + + def progress_undefined(result) + "#{result.name.last} (Undefined)".foreground(:yellow) + end + end +end + diff --git a/lib/flowerbox/result.rb b/lib/flowerbox/result.rb index 93675b0..e5e99a7 100644 --- a/lib/flowerbox/result.rb +++ b/lib/flowerbox/result.rb @@ -1,45 +1,28 @@ module Flowerbox - class Result - def <=>(other) - result = self.name.length <=> other.name.length + module Result + extend Flowerbox::CoreExt::Module - if result == 0 + module Cucumber + extend Flowerbox::CoreExt::Module + end + module Jasmine + extend Flowerbox::CoreExt::Module + end + + autoload :Base, 'flowerbox/result/base' + autoload :Failure, 'flowerbox/result/failure' + autoload :Pending, 'flowerbox/result/pending' + autoload :FailureMessage, 'flowerbox/result/failure_message' + autoload :FileInfo, 'flowerbox/result/file_info' + + class << self + def for(test_env, type) + require "flowerbox/result/#{test_env}/#{type}" + + test_group = find_constant(test_env) + test_group.find_constant(type) end - - result - end - - def runners - @runners ||= [] - end - - def translated_file - @translated_file ||= if actual_file_base = filename[%r{\.tmp/sprockets(.*)}, 1] - Dir[actual_file_base + "*"].first - else - filename - end - end - - def file_translated? - translated_file != filename - end - - def filename - file.to_s.split(":").first - end - - def line_number - return @line_number if @line_number - - @line_number = file.to_s.split(":").last - @line_number = "~#{@line_number}" if file_translated? - @line_number - end - - def success? - false end end end diff --git a/lib/flowerbox/result/base.rb b/lib/flowerbox/result/base.rb new file mode 100644 index 0000000..1eaf7e9 --- /dev/null +++ b/lib/flowerbox/result/base.rb @@ -0,0 +1,79 @@ +require 'forwardable' + +module Flowerbox + module Result + class Base + include Flowerbox::Result::FileInfo + + extend Forwardable + + attr_reader :data + + def_delegators :data, :[] + + def type + self.class.name.split("::").last.downcase.to_sym + end + + def initialize(data) + @data = data + end + + def name + data['name'] + end + + def message + data['message'] + end + + def runners + data['runners'] + end + + def file + data['file'] + end + + def ==(other) + name == other.name + end + + def <=>(other) + result = self.name.length <=> other.name.length + + if result == 0 + + end + + result + end + + def runners + @runners ||= [] + end + + def success? + false + end + + def failure? + !success? + end + + def pending? + false + end + + def <<(other) + runners + @runners += other.runners + end + + def test_environment + Flowerbox.test_environment + end + end + end +end + diff --git a/lib/flowerbox/result/cucumber/base.rb b/lib/flowerbox/result/cucumber/base.rb new file mode 100644 index 0000000..e69de29 diff --git a/lib/flowerbox/result/cucumber/failure.rb b/lib/flowerbox/result/cucumber/failure.rb new file mode 100644 index 0000000..524df4a --- /dev/null +++ b/lib/flowerbox/result/cucumber/failure.rb @@ -0,0 +1,7 @@ +module Flowerbox::Result + module Cucumber + class Failure < Flowerbox::Result::Failure + end + end +end + diff --git a/lib/flowerbox/result/cucumber/pending.rb b/lib/flowerbox/result/cucumber/pending.rb new file mode 100644 index 0000000..a81f2d3 --- /dev/null +++ b/lib/flowerbox/result/cucumber/pending.rb @@ -0,0 +1,8 @@ +module Flowerbox::Result + module Cucumber + class Pending < Flowerbox::Result::Pending + + end + end +end + diff --git a/lib/flowerbox/result/cucumber/skipped.rb b/lib/flowerbox/result/cucumber/skipped.rb new file mode 100644 index 0000000..5e1b907 --- /dev/null +++ b/lib/flowerbox/result/cucumber/skipped.rb @@ -0,0 +1,7 @@ +module Flowerbox::Result + module Cucumber + class Skipped < Flowerbox::Result::Pending + end + end +end + diff --git a/lib/flowerbox/result/cucumber/success.rb b/lib/flowerbox/result/cucumber/success.rb new file mode 100644 index 0000000..e877fda --- /dev/null +++ b/lib/flowerbox/result/cucumber/success.rb @@ -0,0 +1,10 @@ +module Flowerbox::Result + module Cucumber + class Success < Flowerbox::Result::Base + def success? + true + end + end + end +end + diff --git a/lib/flowerbox/result/cucumber/undefined.rb b/lib/flowerbox/result/cucumber/undefined.rb new file mode 100644 index 0000000..bc08eaf --- /dev/null +++ b/lib/flowerbox/result/cucumber/undefined.rb @@ -0,0 +1,14 @@ +module Flowerbox::Result + module Cucumber + class Undefined < Flowerbox::Result::Pending + def step_type + @data['step_type'] + end + + def original_name + @data['original_name'] + end + end + end +end + diff --git a/lib/flowerbox/result/exception.rb b/lib/flowerbox/result/exception.rb new file mode 100644 index 0000000..956f1d2 --- /dev/null +++ b/lib/flowerbox/result/exception.rb @@ -0,0 +1,6 @@ +module Flowerbox::Result + class Exception < Base + + end +end + diff --git a/lib/flowerbox/result/failure.rb b/lib/flowerbox/result/failure.rb new file mode 100644 index 0000000..521205e --- /dev/null +++ b/lib/flowerbox/result/failure.rb @@ -0,0 +1,20 @@ +module Flowerbox::Result + class Failure < Flowerbox::Result::Base + def failures + @failures ||= @data['failures'].collect { |fail| FailureMessage.new(fail) } + end + + def <<(other) + super + + other.failures.each do |failure| + if existing_failure = failures.find { |f| f.message == failure.message } + existing_failure.runners << failure.runner + else + failures << failure + end + end + end + end +end + diff --git a/lib/flowerbox/result/failure_message.rb b/lib/flowerbox/result/failure_message.rb new file mode 100644 index 0000000..de1ad71 --- /dev/null +++ b/lib/flowerbox/result/failure_message.rb @@ -0,0 +1,32 @@ +module Flowerbox::Result + class FailureMessage + include Flowerbox::Result::FileInfo + + attr_reader :data + + def initialize(data) + @data = data + end + + def message + @data['message'] + end + + def file + first_local_stack[%r{(#{Dir.pwd}.*$)}, 1] + end + + def runner + @data['runner'] + end + + def runners + @runners ||= [ runner ] + end + + def first_local_stack + @data['stack'].find { |line| line[File.join(".tmp/sprockets", Dir.pwd)] } || @data['stack'][1] + end + end +end + diff --git a/lib/flowerbox/result/file_info.rb b/lib/flowerbox/result/file_info.rb new file mode 100644 index 0000000..28e2c54 --- /dev/null +++ b/lib/flowerbox/result/file_info.rb @@ -0,0 +1,32 @@ +module Flowerbox::Result::FileInfo + def translated_file + @translated_file ||= if actual_file_base = filename[%r{\.tmp/sprockets(.*)}, 1] + Dir[actual_file_base + "*"].first + else + filename + end + end + + def file_translated? + translated_file != filename + end + + def filename + file.to_s.split(":").first + end + + def line_number + return @line_number if @line_number + + @line_number = file.to_s.split(":")[1] + @line_number = "~#{@line_number}" if file_translated? + @line_number + end + + alias :line :line_number + + def translated_file_and_line + "#{translated_file.gsub(Dir.pwd + '/', '')}:#{line_number}" + end +end + diff --git a/lib/flowerbox/result/jasmine/failure.rb b/lib/flowerbox/result/jasmine/failure.rb new file mode 100644 index 0000000..b4c5fbb --- /dev/null +++ b/lib/flowerbox/result/jasmine/failure.rb @@ -0,0 +1,7 @@ +module Flowerbox::Result + module Jasmine + class Failure < Flowerbox::Result::Failure + end + end +end + diff --git a/lib/flowerbox/result/jasmine/success.rb b/lib/flowerbox/result/jasmine/success.rb new file mode 100644 index 0000000..146f17d --- /dev/null +++ b/lib/flowerbox/result/jasmine/success.rb @@ -0,0 +1,8 @@ +module Flowerbox::Result + module Jasmine + class Success < Flowerbox::Result::Base + def success? ; true ; end + end + end +end + diff --git a/lib/flowerbox/result/pending.rb b/lib/flowerbox/result/pending.rb new file mode 100644 index 0000000..ab34f68 --- /dev/null +++ b/lib/flowerbox/result/pending.rb @@ -0,0 +1,12 @@ +module Flowerbox::Result + class Pending < Base + def pending? + true + end + + def failure? + false + end + end +end + diff --git a/lib/flowerbox/result_set.rb b/lib/flowerbox/result_set.rb index a68e87a..95b1aa4 100644 --- a/lib/flowerbox/result_set.rb +++ b/lib/flowerbox/result_set.rb @@ -5,21 +5,16 @@ module Flowerbox attr_accessor :time def self.from_results(results, options) - results = results['items_'].collect do |result| - if name = result['splitName'] - case result['passed_'] - when true - Success.new(name, result['message']) - else - Failure.new(name, result['message'], result['trace']['stack'].first) - end + results = results.collect do |result| + result['runner'] = options[:runner] + + if name = result['name'] + Flowerbox::Result.for(result['source'], result['status']).new(result) else - Exception.new(result.first['trace']['stack']) + Flowerbox::Result::Exception.new(result) end end.flatten - results.each { |result| result.runners << options[:runner] } - new(results, options) end @@ -27,6 +22,10 @@ module Flowerbox new(results, :runner => runner) end + def reporters + Flowerbox.reporters + end + def initialize(results = [], options = {}) @results, @options = results, options end @@ -34,7 +33,7 @@ module Flowerbox def <<(other) other.results.each do |other_result| if existing_result = results.find { |result| result == other_result } - existing_result.runners << other_result.runners + existing_result << other_result else results << other_result end @@ -45,10 +44,8 @@ module Flowerbox @results.empty? ? 0 : 1 end - def print - gathered_results.each(&:print) - - puts "#{total_tests} total, #{total_failures} failures, #{time} secs." + def print(data = {}) + reporters.each { |reporter| reporter.report(flattened_gathered_results, data) } end def total_tests @@ -60,7 +57,9 @@ module Flowerbox end def print_progress - @results.each { |result| result.print_progress ; $stdout.flush } + @results.each do |result| + reporters.each { |reporter| reporter.report_progress(result) } + end end def gathered_results @@ -69,22 +68,21 @@ module Flowerbox @gathered_results = [] results.each do |result| - case result - when Flowerbox::Exception - @gathered_results << result - when Flowerbox::Failure - if !(gathered_result = @gathered_results.find { |g| g.name == result.name }) - gathered_result = GatheredResult.new(result.name) + if !(gathered_result = @gathered_results.find { |g| g.name == result.name }) + gathered_result = GatheredResult.new(result.name) - @gathered_results << gathered_result - end - - gathered_result << result + @gathered_results << gathered_result end + + gathered_result << result end @gathered_results end + + def flattened_gathered_results + gathered_results.collect(&:results).flatten + end end end diff --git a/lib/flowerbox/runner.rb b/lib/flowerbox/runner.rb index c764c09..af69eb0 100644 --- a/lib/flowerbox/runner.rb +++ b/lib/flowerbox/runner.rb @@ -1,10 +1,6 @@ module Flowerbox module Runner - class << self - def for(env) - self.const_get(self.constants.find { |c| c.to_s.downcase.to_s == env.to_s }).new - end - end + extend Flowerbox::CoreExt::Module end end diff --git a/lib/flowerbox/runner/base.rb b/lib/flowerbox/runner/base.rb index 6f462d1..7708bfc 100644 --- a/lib/flowerbox/runner/base.rb +++ b/lib/flowerbox/runner/base.rb @@ -1,17 +1,28 @@ module Flowerbox module Runner class Base - attr_reader :sprockets, :spec_files, :options + attr_reader :sprockets, :spec_files, :options, :time attr_accessor :results + MAX_COUNT = 30 + def initialize @results = ResultSet.new end + def ensure_alive + while @count < MAX_COUNT && !finished? + @count += 1 + sleep 0.1 + end + end + def run(sprockets, spec_files, options) @sprockets, @spec_files, @options = sprockets, spec_files, options + @count = 0 + puts "Flowerbox running your #{Flowerbox.test_environment.name} tests on #{console_name}..." server.start @@ -20,9 +31,6 @@ module Flowerbox server.stop - puts - puts - @results end @@ -56,7 +64,7 @@ module Flowerbox return @server if @server server_options = { :app => Flowerbox::Rack } - server_options[:logging] = true if options[:verbose] + server_options[:logging] = true if options[:verbose_server] @server = Flowerbox::Delivery::Server.new(server_options) Flowerbox::Rack.runner = self @@ -74,6 +82,8 @@ module Flowerbox def add_tests(new_tests) tests << new_tests + + @count = 0 end def failures @@ -96,6 +106,10 @@ module Flowerbox @failures.length end + def time + @time ||= 0 + end + def finish!(time) @time = time @@ -106,9 +120,13 @@ module Flowerbox @finished end + def ping + @count = 0 + end + private def result_set_from_test_results(test_results) - ResultSet.from_results(test_results.first, options.merge(:runner => name)) + ResultSet.from_results(test_results, options.merge(:runner => name)) end end end diff --git a/lib/flowerbox/runner/selenium.rb b/lib/flowerbox/runner/selenium.rb index 0ca760c..d9894da 100644 --- a/lib/flowerbox/runner/selenium.rb +++ b/lib/flowerbox/runner/selenium.rb @@ -3,7 +3,6 @@ require 'selenium-webdriver' module Flowerbox module Runner class Selenium < Base - MAX_COUNT = 30 def name raise StandardError.new("Override me") @@ -20,12 +19,7 @@ module Flowerbox selenium.navigate.to "http://localhost:#{server.port}/" - @count = 0 - - while @count < MAX_COUNT && !finished? - @count += 1 - sleep 0.1 - end + ensure_alive ensure selenium.quit if selenium end @@ -60,7 +54,9 @@ console.log = function(msg) { var context = this; - #{env} + window.onload = function() { + #{env} + }; @@ -70,12 +66,6 @@ HTML def template_files sprockets.files.collect { |file| %{} } end - - def add_failures(data) - super - - @count = 0 - end end end end diff --git a/lib/flowerbox/test_environment.rb b/lib/flowerbox/test_environment.rb index 112bac8..1f56e88 100644 --- a/lib/flowerbox/test_environment.rb +++ b/lib/flowerbox/test_environment.rb @@ -1,8 +1,10 @@ module Flowerbox module TestEnvironment + extend Flowerbox::CoreExt::Module + class << self def for(env) - self.const_get(self.constants.find { |c| c.to_s.downcase.to_s == env.to_s }).new + find_constant(env).new end end end diff --git a/lib/flowerbox/test_environment/cucumber.rb b/lib/flowerbox/test_environment/cucumber.rb index 8a4c217..fa1732a 100644 --- a/lib/flowerbox/test_environment/cucumber.rb +++ b/lib/flowerbox/test_environment/cucumber.rb @@ -10,10 +10,12 @@ module Flowerbox end def start_for(runner) + @runner = runner + @sprockets.add("flowerbox/cucumber") @sprockets.add("flowerbox/cucumber/#{runner.type}") - runner.spec_files.each { |file| @sprockets.add(file) } + @runner.spec_files.each { |file| @sprockets.add(file) } <<-JS context.Cucumber = context.require('./cucumber'); @@ -23,6 +25,43 @@ context.cucumber.attachListener(new context.Flowerbox.Cucumber.Reporter()); context.cucumber.start(function() {}); JS end + + def obtain_test_definition_for(result) + matcher = result.original_name + args = [] + + matcher.gsub!(%r{"[^"]+"}) do |_, match| + args << "arg#{args.length + 1}" + '"([^"]+)"' + end + + matcher.gsub!(%r{ \d+ }) do |_, match| + args << "arg#{args.length + 1}" + " (\d+) " + end + + args_string = args.join(', ') + + if primarily_coffeescript? + <<-COFFEE +Flowerbox.#{result.step_type} /^#{matcher}$/, #{"(#{args_string}) " if !args_string.empty?}-> + @pending() # add your code here +COFFEE + else + <<-JS +Flowerbox.#{result.step_type}(/^#{matcher}$/, function(#{args_string}) { + this.pending(); // add your code here +}); +JS + end + end + + def primarily_coffeescript? + coffee_count = @runner.spec_files.inject(0) { |s, n| s += 1 if n[%r{.coffee$}]; s } + js_count = @runner.spec_files.inject(0) { |s, n| s += 1 if n[%r{.js$}]; s } + + coffee_count > js_count + end end end end diff --git a/lib/flowerbox/test_environment/jasmine.rb b/lib/flowerbox/test_environment/jasmine.rb index e4154d6..4c42203 100644 --- a/lib/flowerbox/test_environment/jasmine.rb +++ b/lib/flowerbox/test_environment/jasmine.rb @@ -25,18 +25,9 @@ if (typeof context != 'undefined' && typeof jasmine == 'undefined') { } jasmine.getEnv().addReporter(new jasmine.FlowerboxReporter()); -#{jasmine_reporters.join("\n")} jasmine.getEnv().execute(); JS end - - def jasmine_reporters - reporters.collect { |reporter| %{jasmine.getEnv().addReporter(new jasmine.#{reporter}());} } - end - - def reporters - @reporters ||= [] - end end end end