From bbc95564f17731aad0b0ba5e501776e51f2da56d Mon Sep 17 00:00:00 2001 From: John Bintz Date: Fri, 9 Mar 2012 10:05:54 -0500 Subject: [PATCH] reeeeealy basic cucumber support --- lib/assets/javascripts/flowerbox.js.coffee | 1 + .../javascripts/flowerbox/cucumber.js.coffee | 55 ++++++++++++++++ .../flowerbox/cucumber/reporter.js.coffee | 65 +++++++++++++++++++ .../delivery/tilt/feature_template.rb | 22 +++++++ lib/flowerbox/failure.rb | 25 ++++++- lib/flowerbox/rack.rb | 1 + lib/flowerbox/result.rb | 4 +- lib/flowerbox/runner/base.rb | 1 - lib/flowerbox/runner/node.rb | 5 +- lib/flowerbox/runner/selenium.rb | 8 +-- lib/flowerbox/test_environment/cucumber.rb | 21 +++--- 11 files changed, 187 insertions(+), 21 deletions(-) create mode 100644 lib/assets/javascripts/flowerbox/cucumber.js.coffee create mode 100644 lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee create mode 100644 lib/flowerbox/delivery/tilt/feature_template.rb diff --git a/lib/assets/javascripts/flowerbox.js.coffee b/lib/assets/javascripts/flowerbox.js.coffee index 4b90d32..d99f66e 100644 --- a/lib/assets/javascripts/flowerbox.js.coffee +++ b/lib/assets/javascripts/flowerbox.js.coffee @@ -3,5 +3,6 @@ Flowerbox = contact: (url, data...) -> xhr = new XMLHttpRequest() xhr.open("POST", Flowerbox.baseUrl + url, false) + xhr.setRequestHeader("Accept", "application/json") xhr.send(JSON.stringify(data)) fail: -> diff --git a/lib/assets/javascripts/flowerbox/cucumber.js.coffee b/lib/assets/javascripts/flowerbox/cucumber.js.coffee new file mode 100644 index 0000000..1e4fcb2 --- /dev/null +++ b/lib/assets/javascripts/flowerbox/cucumber.js.coffee @@ -0,0 +1,55 @@ +#= require flowerbox/cucumber/reporter + +Flowerbox.Cucumber ||= {} +Flowerbox.Cucumber.features = -> + Flowerbox.Cucumber.Features ||= [] + Flowerbox.Cucumber.Features.join("\n") + +Flowerbox.World = (code = null) -> + -> + for code in (Flowerbox.World.Code || []) + code.apply(this) + +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) + + if result? and result.__prototype__ == Error + callback.fail(result) + else + if pending then callback.pending("pending") else callback() + + null + +Flowerbox.Step.files ||= {} +Flowerbox.Step.matchFile = (name) -> + for key, data of Flowerbox.Step.files + [ regexp, filename ] = data + if name.match(regexp) + return filename + + null + +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 count == 0 + Flowerbox.Step.files[match.toString()] = [ match, line.replace(/^.*__F__/, '') ] + break + + Flowerbox.Step(type, match, code) + +stepGenerator(type) for type in [ 'Given', 'When', 'Then' ] + diff --git a/lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee b/lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee new file mode 100644 index 0000000..28d9172 --- /dev/null +++ b/lib/assets/javascripts/flowerbox/cucumber/reporter.js.coffee @@ -0,0 +1,65 @@ +Flowerbox ||= {} +Flowerbox.Cucumber ||= {} + +class Flowerbox.Cucumber.Reporter + hear: (event, callback) -> + switch event.getName() + when 'BeforeFeatures' + @time = (new Date()).getTime() + + Flowerbox.contact("starting") + + when 'AfterFeatures' + Flowerbox.contact("results", (new Date()).getTime() - @time) + + when 'BeforeFeature' + @feature = event.getPayloadItem('feature') + + when 'BeforeScenario' + @scenario = event.getPayloadItem('scenario') + + when 'BeforeStep' + @step = event.getPayloadItem('step') + + Flowerbox.contact("start_test", @step.getName()) + + 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 ] } } + + if stepResult.isSuccessful() + test.passed_ = true + else if stepResult.isPending() + test.message = "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() + + + """ + else if stepResult.isFailed() + error = stepResult.getFailureException() + + test.message = error.message + test.trace.stack = [ 'file:1' ] + + Flowerbox.contact("finish_test", @step.getName(), [ { items_: [ test ] } ]) + + callback() + diff --git a/lib/flowerbox/delivery/tilt/feature_template.rb b/lib/flowerbox/delivery/tilt/feature_template.rb new file mode 100644 index 0000000..12cf32f --- /dev/null +++ b/lib/flowerbox/delivery/tilt/feature_template.rb @@ -0,0 +1,22 @@ +require 'tilt' + +module Flowerbox::Delivery::Tilt + class FeatureTemplate < Tilt::Template + self.default_mime_type = 'application/javascript' + + def prepare; end + + def evaluate(scope, locals, &block) + <<-JS +Flowerbox.Cucumber.Features = Flowerbox.Cucumber.Features || []; + +Flowerbox.Cucumber.Features.push("#{escaped_data}"); +JS + end + + def escaped_data + data.gsub("\n", "\\n").gsub('"', '\\"') + end + end +end + diff --git a/lib/flowerbox/failure.rb b/lib/flowerbox/failure.rb index 85e2473..cb9dfab 100644 --- a/lib/flowerbox/failure.rb +++ b/lib/flowerbox/failure.rb @@ -1,7 +1,30 @@ module Flowerbox class Failure < BaseResult + def pending? + message == "pending" + end + + def skipped? + message == "skipped" + end + + def undefined? + message[%r{^Step not defined}] + end + def print_progress - print "F".foreground(:red) + case message + when "pending" + print "P".foreground(:yellow) + when "skipped" + print "-".foreground(:cyan) + else + if undefined? + print "U".foreground(:yellow) + else + print "F".foreground(:red) + end + end end end end diff --git a/lib/flowerbox/rack.rb b/lib/flowerbox/rack.rb index 0da2f82..39dfe45 100644 --- a/lib/flowerbox/rack.rb +++ b/lib/flowerbox/rack.rb @@ -1,5 +1,6 @@ require 'sinatra' require 'json' +require 'cgi' module Flowerbox class Rack < Sinatra::Base diff --git a/lib/flowerbox/result.rb b/lib/flowerbox/result.rb index ff0fc27..93675b0 100644 --- a/lib/flowerbox/result.rb +++ b/lib/flowerbox/result.rb @@ -27,13 +27,13 @@ module Flowerbox end def filename - file.split(":").first + file.to_s.split(":").first end def line_number return @line_number if @line_number - @line_number = file.split(":").last + @line_number = file.to_s.split(":").last @line_number = "~#{@line_number}" if file_translated? @line_number end diff --git a/lib/flowerbox/runner/base.rb b/lib/flowerbox/runner/base.rb index 271b0b9..6f462d1 100644 --- a/lib/flowerbox/runner/base.rb +++ b/lib/flowerbox/runner/base.rb @@ -49,7 +49,6 @@ module Flowerbox end def time=(time) - p time @results.time = time end diff --git a/lib/flowerbox/runner/node.rb b/lib/flowerbox/runner/node.rb index 2936722..1aae5bc 100644 --- a/lib/flowerbox/runner/node.rb +++ b/lib/flowerbox/runner/node.rb @@ -22,11 +22,12 @@ module Flowerbox def configured? File.directory?(File.join(Dir.pwd, 'node_modules/jsdom')) && - File.directory?(File.join(Dir.pwd, 'node_modules/XMLHttpRequest')) + File.directory?(File.join(Dir.pwd, 'node_modules/XMLHttpRequest')) && + File.directory?(File.join(Dir.pwd, 'node_modules/cucumber')) end def configure - system %{bash -c "mkdir -p node_modules && npm link jsdom && npm link xmlhttprequest"} + system %{bash -c "mkdir -p node_modules && npm link jsdom && npm link xmlhttprequest && npm link cucumber"} end def run(sprockets, spec_files, options) diff --git a/lib/flowerbox/runner/selenium.rb b/lib/flowerbox/runner/selenium.rb index 1e428a1..0ca760c 100644 --- a/lib/flowerbox/runner/selenium.rb +++ b/lib/flowerbox/runner/selenium.rb @@ -20,8 +20,6 @@ module Flowerbox selenium.navigate.to "http://localhost:#{server.port}/" - sleep 10 - @count = 0 while @count < MAX_COUNT && !finished? @@ -44,7 +42,7 @@ module Flowerbox <<-HTML - Flowerbox - Selenium Runner + Flowerbox - #{Flowerbox.test_environment.name} Runner diff --git a/lib/flowerbox/test_environment/cucumber.rb b/lib/flowerbox/test_environment/cucumber.rb index 51a25f0..8a4c217 100644 --- a/lib/flowerbox/test_environment/cucumber.rb +++ b/lib/flowerbox/test_environment/cucumber.rb @@ -1,9 +1,11 @@ module Flowerbox module TestEnvironment - class Cucumber + class Cucumber < Base def inject_into(sprockets) @sprockets = sprockets + @sprockets.register_engine('.feature', Flowerbox::Delivery::Tilt::FeatureTemplate) + @sprockets.add('cucumber.js') end @@ -14,21 +16,18 @@ module Flowerbox runner.spec_files.each { |file| @sprockets.add(file) } <<-JS -if (typeof context != 'undefined' && typeof jasmine == 'undefined') { - jasmine = context.jasmine; -} +context.Cucumber = context.require('./cucumber'); -jasmine.getEnv().addReporter(new jasmine.FlowerboxReporter()); -#{jasmine_reporters.join("\n")} -jasmine.getEnv().execute(); +context.cucumber = context.Cucumber(context.Flowerbox.Cucumber.features(), context.Flowerbox.World()); +context.cucumber.attachListener(new context.Flowerbox.Cucumber.Reporter()); +context.cucumber.start(function() {}); JS end - - def jasmine_reporters - reporters.collect { |reporter| %{jasmine.getEnv().addReporter(new jasmine.#{reporter}());} } - end end end end +module Flowerbox::Delivery::Tilt + autoload :FeatureTemplate, 'flowerbox/delivery/tilt/feature_template' +end