diff --git a/bin/flowerbox b/bin/flowerbox index b1c436f..50fb3c7 100755 --- a/bin/flowerbox +++ b/bin/flowerbox @@ -4,16 +4,16 @@ require 'flowerbox' 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, :runners => :string, :runner => :string, :verbose_server => false - def test(dir = "spec/javascripts") + desc "test DIR FILES...", "Run the specs found in spec dir, loading spec_helper.rb for configuration details" + method_options :pwd => :string, :env_options => nil, :runners => :string, :runner => :string, :verbose_server => false + def test(dir = "spec/javascripts", *files) Dir.chdir(pwd) do - exit Flowerbox.run(dir, options.dup) + exit Flowerbox.run(dir, options.dup.merge(:files => files)) end end desc "debug DIR", "Start the Flowerbox server to help debug loading issues." - method_options :pwd => :string, :runners => :string, :runner => :string, :verbose_server => false + method_options :pwd => :string, :env_options => nil, :runners => :string, :runner => :string, :verbose_server => false def debug(dir = "spec/javascripts") Dir.chdir(pwd) do Flowerbox.debug(dir, options.dup) diff --git a/lib/assets/javascripts/flowerbox/cucumber.js.coffee b/lib/assets/javascripts/flowerbox/cucumber.js.coffee index ea66f82..5c833f8 100644 --- a/lib/assets/javascripts/flowerbox/cucumber.js.coffee +++ b/lib/assets/javascripts/flowerbox/cucumber.js.coffee @@ -17,15 +17,21 @@ Flowerbox.World = (code = null) -> Flowerbox.Matchers = toEqual: (expected) -> @message = "Expected #{@actual} #{@notMessage} equal #{expected}" - @actual == expected + if typeof @actual == 'object' + for key, value of @actual + return false if expected[key] != value + + for key, value of expected + return false if @actual[key] != value + + true + else + @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) @@ -33,6 +39,20 @@ Flowerbox.World -> Flowerbox.Matcher.matchers = {} @addMatchers(Flowerbox.Matchers) + if Flowerbox.Cucumber.tags + negatedTags = [] + for tagSet in Flowerbox.Cucumber.tags + tags = for tag in tagSet.split(',') + if tag.substr(0, 1) == '@' + "~" + tag + else + tag.substr(1) + + negatedTags.push(tags) + + @around (negatedTags..., runScenario) -> + + class Flowerbox.Matcher @addMatchers: (data) -> for method, code of data @@ -62,12 +82,15 @@ Flowerbox.Step = (type, match, code) -> Flowerbox.World.Code ||= [] Flowerbox.World.Code.push (args..., callback) -> this[type] match, (args..., callback) => + _pending = false + @pending = -> _pending = true + 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 diff --git a/lib/assets/javascripts/flowerbox/jasmine/node.js.coffee b/lib/assets/javascripts/flowerbox/jasmine/node.js.coffee index ca05f49..79e74db 100644 --- a/lib/assets/javascripts/flowerbox/jasmine/node.js.coffee +++ b/lib/assets/javascripts/flowerbox/jasmine/node.js.coffee @@ -5,7 +5,7 @@ jasmine.Spec.beforeAddMatcherResult().push -> errorInfo = new Error().stack[3] - @trace = { stack: [ "#{errorInfo.getFileName()}:#{errorInfo.getLineNumber()}" ] } + @trace = { stack: "#{errorInfo.getFileName()}:#{errorInfo.getLineNumber()}" } Error.prepareStackTrace = Error.prepareStackTrace_ diff --git a/lib/flowerbox.rb b/lib/flowerbox.rb index 0752817..071139c 100644 --- a/lib/flowerbox.rb +++ b/lib/flowerbox.rb @@ -2,16 +2,19 @@ require "flowerbox/version" require 'flowerbox-delivery' require 'rainbow' -module Guard - autoload :Flowerbox, 'guard/flowerbox' -end - module Flowerbox module CoreExt autoload :Module, 'flowerbox/core_ext/module' end autoload :Runner, 'flowerbox/runner' + autoload :Task, 'flowerbox/task' + + module Run + autoload :Base, 'flowerbox/run/base' + autoload :Test, 'flowerbox/run/test' + autoload :Debug, 'flowerbox/run/debug' + end module Runner autoload :Node, 'flowerbox/runner/node' @@ -100,115 +103,12 @@ module Flowerbox @bare_coffeescript ||= true end - def prep(dir, options = {}) - reset! - - load File.join(dir, 'spec_helper.rb') - - require 'coffee_script' - require 'tilt/coffee' - - Tilt::CoffeeScriptTemplate.default_bare = Flowerbox.bare_coffeescript - - if runners = options[:runners] || options[:runner] - Flowerbox.run_with(runners.split(',')) - end - end - def debug(dir, options = {}) - options[:debug] = true - - prep(dir, options) - - env = Flowerbox.runner_environment.first - env.setup(build_sprockets_for(dir), spec_files_for(dir), options) - - Flowerbox.reporters.replace([]) - - puts "Flowerbox debug server running test prepared for #{env.console_name} on #{env.server.address}" - - env.server.start - - trap('INT') do - env.server.stop - end - - @restart = false - - trap('QUIT') do - puts "Restarting Flowerbox server..." - @restart = true - env.server.stop - end - - while env.server.alive? - sleep 0.25 - end - - if @restart - debug(dir, options) - else - puts "Flowerbox finished." - end + Flowerbox::Run::Debug.execute(dir, options) end def run(dir, options = {}) - prep(dir, options) - - result_set = ResultSet.new - - time = 0 - realtime = Time.now.to_i - - runner_envs = Flowerbox.runner_environment.collect do |env| - env.ensure_configured! - - result_set << env.run(build_sprockets_for(dir), spec_files_for(dir), options) - - time += env.time - - env - end - - result_set.print(:time => time, :realtime => Time.now.to_i - realtime) - - runner_envs.each(&:cleanup) - - result_set.exitstatus - end - - def build_sprockets_for(dir) - sprockets = Flowerbox::Delivery::SprocketsHandler.new( - :asset_paths => [ - Flowerbox.path.join("lib/assets/javascripts"), - Flowerbox.path.join("vendor/assets/javascripts"), - dir, - Flowerbox.asset_paths - ].flatten - ) - - sprockets.add('flowerbox') - sprockets.add('json2') - - Flowerbox.test_environment.inject_into(sprockets) - - Flowerbox.additional_files.each { |file| sprockets.add(file) } - - sprockets - end - - def spec_files_for(dir) - return @spec_files if @spec_files - - @spec_files = [] - - Flowerbox.spec_patterns.each do |pattern| - Dir[File.join(dir, pattern)].each do |file| - @spec_files << File.expand_path(file) - end - end - - @spec_files + Flowerbox::Run::Test.execute(dir, options) end end end diff --git a/lib/flowerbox/rack.rb b/lib/flowerbox/rack.rb index 9b0bdc0..04475fd 100644 --- a/lib/flowerbox/rack.rb +++ b/lib/flowerbox/rack.rb @@ -60,6 +60,12 @@ module Flowerbox runner.template end + + class << self + private + def setup_protetion(builder) + end + end end end diff --git a/lib/flowerbox/result/failure_message.rb b/lib/flowerbox/result/failure_message.rb index de1ad71..67b2548 100644 --- a/lib/flowerbox/result/failure_message.rb +++ b/lib/flowerbox/result/failure_message.rb @@ -13,7 +13,7 @@ module Flowerbox::Result end def file - first_local_stack[%r{(#{Dir.pwd}.*$)}, 1] + first_local_stack[%r{__F__/(.*)$}, 1] end def runner @@ -25,7 +25,7 @@ module Flowerbox::Result end def first_local_stack - @data['stack'].find { |line| line[File.join(".tmp/sprockets", Dir.pwd)] } || @data['stack'][1] + @data['stack'].find { |line| line['__F__'] } || @data['stack'][1] end end end diff --git a/lib/flowerbox/result/file_info.rb b/lib/flowerbox/result/file_info.rb index 28e2c54..287628b 100644 --- a/lib/flowerbox/result/file_info.rb +++ b/lib/flowerbox/result/file_info.rb @@ -26,7 +26,7 @@ module Flowerbox::Result::FileInfo alias :line :line_number def translated_file_and_line - "#{translated_file.gsub(Dir.pwd + '/', '')}:#{line_number}" + "#{translated_file.gsub(%r{^/}, '')}:#{line_number}" end end diff --git a/lib/flowerbox/result_set.rb b/lib/flowerbox/result_set.rb index 95b1aa4..f403754 100644 --- a/lib/flowerbox/result_set.rb +++ b/lib/flowerbox/result_set.rb @@ -41,7 +41,7 @@ module Flowerbox end def exitstatus - @results.empty? ? 0 : 1 + results.any?(&:failure?) ? 1 : 0 end def print(data = {}) diff --git a/lib/flowerbox/run/base.rb b/lib/flowerbox/run/base.rb new file mode 100644 index 0000000..90d59ec --- /dev/null +++ b/lib/flowerbox/run/base.rb @@ -0,0 +1,79 @@ +module Flowerbox::Run + class Base + attr_reader :dir, :options + + def self.execute(dir, options) + new(dir, options).execute + end + + def initialize(dir, options) + @dir, @options = dir, options + end + + def execute + raise StandardError.new("override in subclass") + end + + def prep! + Flowerbox.reset! + + load File.join(dir, 'spec_helper.rb') + + require 'coffee_script' + require 'tilt/coffee' + + Tilt::CoffeeScriptTemplate.default_bare = Flowerbox.bare_coffeescript + + if runners = options[:runners] || options[:runner] + Flowerbox.run_with(runners.split(',')) + end + + Flowerbox.test_environment.set_additional_options(options[:env_options]) + end + + def sprockets + sprockets = Flowerbox::Delivery::SprocketsHandler.new( + :asset_paths => [ + Flowerbox.path.join("lib/assets/javascripts"), + Flowerbox.path.join("vendor/assets/javascripts"), + @dir, + Flowerbox.asset_paths + ].flatten + ) + + sprockets.add('flowerbox') + sprockets.add('json2') + + Flowerbox.test_environment.inject_into(sprockets) + + Flowerbox.additional_files.each { |file| sprockets.add(file) } + + sprockets + end + + def spec_files + return @spec_files if @spec_files + + @spec_files = [] + + Flowerbox.spec_patterns.each do |pattern| + Dir[File.join(dir, pattern)].each do |file| + if !only || only.find { |match| file[%r{^#{match}}] } + @spec_files << File.expand_path(file) + end + end + end + + @spec_files + end + + def only + return @only if @only + + @only = options[:files] || [] + @only = nil if only.empty? + @only + end + end +end + diff --git a/lib/flowerbox/run/debug.rb b/lib/flowerbox/run/debug.rb new file mode 100644 index 0000000..db9ca01 --- /dev/null +++ b/lib/flowerbox/run/debug.rb @@ -0,0 +1,43 @@ +module Flowerbox::Run + class Debug < Base + def execute + prep! + + env = Flowerbox.runner_environment.first + env.setup(sprockets, spec_files, options) + + Flowerbox.reporters.replace([]) + + puts "Flowerbox debug server running test prepared for #{env.console_name} on #{env.server.address}" + + env.server.start + + trap('INT') do + env.server.stop + end + + @restart = false + + trap('QUIT') do + puts "Restarting Flowerbox server..." + @restart = true + env.server.stop + end + + while env.server.alive? + sleep 0.25 + end + + if @restart + debug(dir, options) + else + puts "Flowerbox finished." + end + end + + def options + @options.dup.merge(:debug => true) + end + end +end + diff --git a/lib/flowerbox/run/test.rb b/lib/flowerbox/run/test.rb new file mode 100644 index 0000000..12c9c43 --- /dev/null +++ b/lib/flowerbox/run/test.rb @@ -0,0 +1,29 @@ +module Flowerbox::Run + class Test < Base + def execute + prep! + + result_set = Flowerbox::ResultSet.new + + time = 0 + realtime = Time.now.to_i + + runner_envs = Flowerbox.runner_environment.collect do |env| + env.ensure_configured! + + result_set << env.run(sprockets, spec_files, options) + + time += env.time + + env + end + + result_set.print(:time => time, :realtime => Time.now.to_i - realtime) + + runner_envs.each(&:cleanup) + + result_set.exitstatus + end + end +end + diff --git a/lib/flowerbox/task.rb b/lib/flowerbox/task.rb new file mode 100644 index 0000000..69ebbad --- /dev/null +++ b/lib/flowerbox/task.rb @@ -0,0 +1,26 @@ +module Flowerbox + class Task + include Rake::DSL if defined?(Rake::DSL) + + def self.create(*args) + new(*args).add + end + + attr_reader :name, :options + + def initialize(name = "flowerbox", options = nil) + @name = name + @options = options || {} + + @options = { :dir => 'spec/javascripts' }.merge(@options) + end + + def add + desc "Run Flowerbox for the tests in #{options[:dir]}" + task(name) do + raise StandardError.new("Flowerbox tests failed") if Flowerbox.run(@options[:dir], @options) != 0 + end + end + end +end + diff --git a/lib/flowerbox/test_environment/base.rb b/lib/flowerbox/test_environment/base.rb index 3004999..d371db5 100644 --- a/lib/flowerbox/test_environment/base.rb +++ b/lib/flowerbox/test_environment/base.rb @@ -1,3 +1,5 @@ +require 'yaml' + module Flowerbox module TestEnvironment class Base @@ -8,6 +10,21 @@ module Flowerbox def reporters @reporters ||= [] end + + def set_additional_options(opts = nil) + @options = {} + + if opts + case opts + when String + @options = Hash[YAML.load(opts).collect { |k, v| [ k.to_sym, v ] }] + when Hash + @options = opts + end + + @options[:tags] = [ @options[:tags] ].flatten(1) if @options[:tags] + end + end end end end diff --git a/lib/flowerbox/test_environment/cucumber.rb b/lib/flowerbox/test_environment/cucumber.rb index fa1732a..af6b5db 100644 --- a/lib/flowerbox/test_environment/cucumber.rb +++ b/lib/flowerbox/test_environment/cucumber.rb @@ -1,6 +1,14 @@ module Flowerbox module TestEnvironment class Cucumber < Base + def initialize + @step_language = nil + end + + def prefer_step_language(language) + @step_language = language + end + def inject_into(sprockets) @sprockets = sprockets @@ -20,12 +28,19 @@ module Flowerbox <<-JS context.Cucumber = context.require('./cucumber'); -context.cucumber = context.Cucumber(context.Flowerbox.Cucumber.features(), context.Flowerbox.World()); +options = {} +#{maybe_tags} + +context.cucumber = context.Cucumber(context.Flowerbox.Cucumber.features(), context.Flowerbox.World(), options); context.cucumber.attachListener(new context.Flowerbox.Cucumber.Reporter()); context.cucumber.start(function() {}); JS end + def maybe_tags + "options.tags = #{@options[:tags].to_json};" if @options[:tags] + end + def obtain_test_definition_for(result) matcher = result.original_name args = [] @@ -57,6 +72,8 @@ JS end def primarily_coffeescript? + return true if @step_language == :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 } diff --git a/lib/guard/flowerbox.rb b/lib/guard/flowerbox.rb deleted file mode 100644 index 0b8068d..0000000 --- a/lib/guard/flowerbox.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'guard' -require 'guard/guard' - -module Guard - class Flowerbox < ::Guard::Guard - def initialize(watchers = [], options = {}) - @options = options - end - - def start - puts "Starting Guard::Flowerbox..." - end - - def run_all - ::Flowerbox.run(@options[:dir], @options) - end - - def run_on_change(files = []) - ::Flowerbox.run(@options[:dir], @options.merge(:files => files)) - end - end -end -