whoa the tests are working again

This commit is contained in:
John Bintz 2012-03-24 10:17:33 -04:00
parent cbce214e7d
commit fcb3e2e83f
26 changed files with 381 additions and 170 deletions

View File

@ -1,16 +1,18 @@
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'rspec', :version => 2 do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
group :rspec do
guard 'rspec', :version => 2 do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end
end
if false
guard 'cucumber' do
watch(%r{^features/.+\.feature$})
watch(%r{^features/support/.+$}) { 'features' }
watch(%r{^features/step_definitions/.*$}) { 'features' }
end
group :wip do
guard 'cucumber', :cli => '-p wip' do
watch(%r{^features/.+\.feature$})
watch(%r{^features/support/.+$}) { 'features' }
watch(%r{^features/step_definitions/.*$}) { 'features' }
end
end

View File

@ -25,7 +25,7 @@ class Flowerbox::CLI < Thor
end
desc "test [DIR]", "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
method_options :pwd => :string, [:quiet, '-q'] => false, :env_options => nil, :runners => :string, :runner => :string, :verbose_server => false
def test(dir = "spec/javascripts", *files)
Dir.chdir(pwd) do
result = Flowerbox.run(dir, options.dup.merge(:files => files))

2
config/cucumber.yml Normal file
View File

@ -0,0 +1,2 @@
default: -f pretty
wip: --tags @wip -f pretty

View File

@ -13,10 +13,8 @@ Feature: Basic Run
"""
Given I have the file "lib/cat.js.coffee" with the content:
"""
class Cat
class @Cat
meow: -> "meow"
console.log("made it")
"""
Scenario: Use the Node runner using Jasmine
@ -28,27 +26,25 @@ Feature: Basic Run
c.spec_patterns << "**/*_spec.*"
c.asset_paths << "lib"
c.bare_coffeescript = true
c.test_environment.reporters << "SimpleNodeReporter"
c.reporters << :json
end
"""
When I run Flowerbox with "spec/javascripts"
Then I should have 1 test and 0 failures
@wip
Scenario: Use the Selenium runner using Jasmine
Given I have the file "spec/javascripts/spec_helper.rb" with the content:
"""
Flowerbox.configure do |c|
c.test_with :jasmine
c.run_with :selenium
c.runner_environment.browser = :firefox
c.run_with :chrome
c.spec_patterns << "**/*_spec.*"
c.asset_paths << "lib"
c.bare_coffeescript = true
c.test_environment.reporters << "SimpleSeleniumReporter"
c.reporters << :json
end
"""
When I run Flowerbox with "spec/javascripts"

View File

@ -1,7 +1,9 @@
Then /^I should have (\d+) tests? and (\d+) failures?$/ do |tests, failures|
parts = @output.lines.to_a.last.strip.split('/')
require 'json'
parts[0].should == tests
parts[1].should == failures
Then /^I should have (\d+) tests? and (\d+) failures?$/ do |tests, failures|
results = JSON.parse(@output).last.last
results['total'].should == tests.to_i
results['failures'].should == failures.to_i
end

View File

@ -1,9 +1,7 @@
When /^I run Flowerbox with "([^"]*)"$/ do |arguments|
command = %{bundle exec bin/flowerbox test #{arguments} --pwd #{@root} 2>&1}
puts command
@output = %x{#{command}}
command = %{bundle exec bin/flowerbox test #{arguments} -q --pwd #{@root} 2>&1}
puts @output
@output = %x{#{command}}
raise StandardError.new("Flowerbox failed: #{@output}") if $?.exitstatus != 0
end

View File

@ -33,5 +33,6 @@ Gem::Specification.new do |gem|
gem.add_dependency 'sprockets-vendor_gems'
gem.add_dependency 'thin'
gem.add_dependency 'em-websocket'
gem.add_dependency 'coffee-script'
end

View File

@ -9,11 +9,12 @@ Flowerbox =
contact: (url, data...) ->
Flowerbox.started = true
Flowerbox.done = true if url == 'results'
if !Flowerbox.debug
Flowerbox.socket.send(JSON.stringify([ url, data ]))
Flowerbox.done = true if url == 'results'
started: false
done: false

View File

@ -26,6 +26,8 @@ module Flowerbox
CACHE_DIR = 'tmp/sprockets'
class << self
attr_writer :quiet
def reset!
@configuration = nil
end
@ -51,9 +53,21 @@ module Flowerbox
end
def run(dir, options = {})
Flowerbox.quiet = options[:quiet]
Flowerbox::Run::Test.execute(dir, options)
end
def quiet?
@quiet == true
end
def notify(msg)
if !Flowerbox.quiet?
puts msg
end
end
def browsers
@browsers ||= {}
end

View File

@ -1,3 +1,5 @@
require 'flowerbox/reporter_list'
module Flowerbox
class Configuration
attr_writer :reporters, :backtrace_filter
@ -14,7 +16,7 @@ module Flowerbox
end
def reporters
@reporters ||= []
@reporters ||= Flowerbox::ReporterList.new
end
def additional_files
@ -34,7 +36,8 @@ module Flowerbox
end
def report_with(*whats)
self.reporters = whats.flatten.collect { |what| Flowerbox::Reporter.for(what.to_s) }
self.reporters.clear!
whats.each { |what| self.reporters << what }
end
def configure

View File

@ -4,10 +4,11 @@ module Flowerbox
require 'flowerbox/reporter/file_display'
require 'flowerbox/reporter/base'
require 'flowerbox/reporter/console_base'
require 'flowerbox/reporter/verbose'
require 'flowerbox/reporter/progress'
def self.for(env)
require "flowerbox/reporter/#{env}"
find_constant(env).new
end
end
end

View File

@ -1,44 +1,65 @@
module Flowerbox::Reporter
class Base
def post_report_data
@post_report_data ||= {}
end
require 'forwardable'
def report(gathered_results, data = {})
gathered_results.each do |result|
self.send("report_#{result.type}", result)
module Flowerbox
module Reporter
class Base
extend Forwardable
def_delegators :$stdout, :puts, :print
attr_accessor :options
def post_report_data
@post_report_data ||= {}
end
report_numeric_results(gathered_results, data)
def report(gathered_results, data = {})
gathered_results.each do |result|
self.send("report_#{result.type}", result)
end
post_report_data.each do |type, data|
puts
self.send("post_report_#{type}", data)
report_numeric_results(gathered_results, data)
post_report_data.each do |type, data|
puts
self.send("post_report_#{type}", data)
end
end
end
def post_report_success(data) ; end
def post_report_skipped(data) ; end
def post_report_pending(data) ; end
def report_success(result); end
def report_skipped(result) ; end
def report_failure(result) ; end
def report_pending(result) ; end
def report_undefined(result) ; end
def post_report_undefined(data)
puts "Some steps were not defined. Define them using the following code:".foreground(:yellow)
puts
def report_progress(result); end
data.each do |code|
puts code.foreground(:yellow)
def post_report_success(data) ; end
def post_report_skipped(data) ; end
def post_report_pending(data) ; end
def post_report_undefined(data)
self.puts "Some steps were not defined. Define them using the following code:".foreground(:yellow)
self.puts
data.each do |code|
self.puts code.foreground(:yellow)
end
end
end
def post_report_failed(data) ; end
def post_report_failed(data) ; end
def report_numeric_results(gathered_results, 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
}
def start(message) ; end
def log(message) ; 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
end

View File

@ -2,6 +2,10 @@ module Flowerbox::Reporter
class ConsoleBase < Base
include FileDisplay
def start(message)
self.puts message
end
def report_success(result) ; end
def report_skipped(result) ; end

View File

@ -0,0 +1,21 @@
require 'flowerbox/reporter/base'
require 'json'
module Flowerbox::Reporter
class JSON < Base
def output
@output ||= []
end
def log(message)
@output << [ :log, message ]
end
def report_numeric_results(gathered_results, data = {})
output << [ :results, numeric_results_for(gathered_results) ]
self.puts ::JSON.dump(output)
end
end
end

View File

@ -0,0 +1,37 @@
require 'forwardable'
module Flowerbox
class ReporterList
include Enumerable
extend Forwardable
def_delegators :@reporters, :empty?
def each(&block)
@reporters.each(&block)
end
def initialize
clear!
end
def clear!
@reporters = []
end
def <<(reporter)
@reporters << Flowerbox::Reporter.for(reporter)
end
def add(reporter, options)
reporter_obj = Flowerbox::Reporter.for(reporter)
reporter_obj.options = options
@reporters << reporter_obj
end
def start(message)
@reporters.each { |reporter| reporter.start(message) }
end
end
end

View File

@ -64,7 +64,15 @@ module Flowerbox
def print_progress
@results.each do |result|
reporters.each { |reporter| reporter.report_progress(result) }
reporters.each do |reporter|
begin
reporter.report_progress(result)
rescue => e
puts e.message
puts e.backtrace.join("\n")
raise e
end
end
end
end

View File

@ -24,6 +24,14 @@ module Flowerbox
@started
end
def starting(*args)
@started = true
end
def reporters
Flowerbox.reporters
end
def ensure_alive
while @count < MAX_COUNT && !finished?
@count += 1 if @timer_running
@ -31,8 +39,8 @@ module Flowerbox
end
if !finished?
puts "Something died hard. Here are the tests that did get run before Flowerbox died.".foreground(:red)
puts tests.flatten.join("\n").foreground(:red)
Flowerbox.notify "Something died hard. Here are the tests that did get run before Flowerbox died.".foreground(:red)
Flowerbox.notify tests.flatten.join("\n").foreground(:red)
server.stop
Flowerbox.server = nil
@ -55,7 +63,7 @@ module Flowerbox
@count = 0
@timer_running = true
puts "Flowerbox running your #{Flowerbox.test_environment.name} tests on #{console_name}..."
reporters.start("Flowerbox running your #{Flowerbox.test_environment.name} tests on #{console_name}...")
attempts = 3
@ -90,12 +98,12 @@ module Flowerbox
def configure ; end
def pause_timer
def pause_timer(*args)
@timer_running = false
@count = 0
end
def unpause_timer
def unpause_timer(*args)
@timer_running = true
end
@ -105,7 +113,7 @@ module Flowerbox
def ensure_configured!
if !configured?
puts "#{console_name} is not configured for this project, configuring now..."
Flowerbox.notify "#{console_name} is not configured for this project, configuring now..."
configure
end
end
@ -124,10 +132,6 @@ module Flowerbox
@results.time = time
end
def did_start!
@started = true
end
def server
require 'flowerbox/rack'
@ -147,17 +151,17 @@ module Flowerbox
end
def log(message)
puts message
reporters.log(message)
end
def tests
@tests ||= []
end
def add_tests(new_tests)
def start_test(new_tests)
tests << new_tests
puts new_tests.flatten if options[:verbose_server]
Flowerbox.notify(new_tests.flatten) if options[:verbose_server]
@count = 0
end
@ -166,7 +170,7 @@ module Flowerbox
@failures ||= []
end
def add_results(test_results)
def finish_test(test_results)
results = result_set_from_test_results(test_results)
results.print_progress
@ -188,8 +192,8 @@ module Flowerbox
@time ||= 0
end
def finish!(time)
@time = time
def results(time)
@time = time.first
@finished = true
end
@ -203,7 +207,7 @@ module Flowerbox
end
def handle_coffeescript_compilation_error(exception)
puts exception.message.foreground(:red)
Flowerbox.notify(exception.message.foreground(:red))
@finished = true
raise RunnerDiedError.new(exception.message)

View File

@ -28,7 +28,7 @@ module Flowerbox
def cleanup ; end
def configure
system %{bash -c "mkdir -p node_modules && npm link jsdom && npm link ws"}
Flowerbox.notify %x{bash -c "mkdir -p node_modules && npm link jsdom && npm link ws"}
end
def run(sprockets, spec_files, options)

View File

@ -24,52 +24,46 @@ module Flowerbox
options[:app] || raise(MissingRackApp.new)
end
def rack_app
Thin::Logging.silent = !options[:logging]
rack_app = app
if options[:logging]
rack_app = ::Rack::Builder.new do
use ::Rack::CommonLogger, STDOUT
run app
end
end
rack_app
end
def websocket_app(ws)
ws.onmessage { |message|
command, data = JSON.parse(message)
runner.send(command, data.flatten)
}
end
def start
@server_thread = Thread.new do
EventMachine.run do
server_options = { :Port => port, :Host => interface }
begin
EventMachine.run do
server_options = { :Port => port, :Host => interface }
Thin::Logging.silent = !options[:logging]
EventMachine::WebSocket.start(:host => interface, :port => port + 1, &method(:websocket_app))
rack_app = app
::Rack::Handler::Thin.run(rack_app, server_options) do |server|
Thread.current[:server] = server
if options[:logging]
rack_app = ::Rack::Builder.new do
use ::Rack::CommonLogger, STDOUT
run app
trap('QUIT') { server.stop }
end
end
EventMachine::WebSocket.start(:host => interface, :port => port + 1) do |ws|
ws.onmessage { |message|
command, data = JSON.parse(message)
case command
when 'starting'
runner.did_start!
when 'unpause_timer'
runner.unpause_timer
when 'pause_timer'
runner.pause_timer
when 'ping'
runner.ping
when 'log'
runner.log(data.first)
when 'finish_test'
runner.add_results(data.flatten)
when 'start_test'
runner.add_tests(data.flatten)
when 'results'
runner.finish!(data.flatten.first)
end
}
end
::Rack::Handler::Thin.run(rack_app, server_options) do |server|
Thread.current[:server] = server
trap('QUIT') { server.stop }
end
rescue => e
@server_thread[:exception] = e
raise e
end
end
@ -77,12 +71,16 @@ module Flowerbox
sleep 0.1
end
raise ServerDiedError.new if !@server_thread[:server].running?
if @server_thread[:exception]
raise @server_thread[:exception]
else
raise ServerDiedError.new if !@server_thread.alive?
end
end
def stop
if @server_thread
@server_thread[:server].stop
@server_thread[:server].stop rescue nil
wait_for_server_to_stop
end

View File

@ -8,9 +8,11 @@ module Flowerbox
class SprocketsHandler
extend Forwardable
class LogicalPathNotFoundError < StandardError ; end
attr_reader :files, :options
def_delegators :environment, :append_path, :register_engine, :[], :call
def_delegators :environment, :append_path, :register_engine, :[], :call, :find_asset, :paths
def self.gem_asset_paths
@gem_asset_paths ||= Sprockets.find_gem_vendor_paths
@ -25,43 +27,42 @@ module Flowerbox
end
def add(asset)
assets_for(asset).each { |dependent_asset| @files.add(dependent_asset) }
assets_for(asset).each { |dependent_asset| files.add(dependent_asset) }
end
def assets_for(asset)
environment.find_asset(asset, :bundle => true).to_a
end
def expire_index!
@environment.send(:expire_index!)
find_asset(asset, :bundle => true).to_a
end
def environment
return @environment if @environment
@environment = Sprockets::Environment.new
@environment.cache = Sprockets::Cache::FileStore.new(Flowerbox.cache_dir)
@environment.cache = cache
self.class.gem_asset_paths.each { |path| append_path(path) }
options[:asset_paths].each { |path| append_path(path) }
default_asset_paths.each { |path| @environment.append_path(path) }
@environment
end
def asset_for(*args)
environment.find_asset(*args)
def default_asset_paths
self.class.gem_asset_paths + options[:asset_paths]
end
def cache
Sprockets::Cache::FileStore.new(Flowerbox.cache_dir)
end
def logical_path_for(asset)
asset_path = asset.pathname.to_s
environment.paths.each do |path|
paths.each do |path|
if result = asset_path[%r{^#{path}/(.*)}, 1]
return result
end
end
raise StandardError.new("Could not find logical path for #{asset_path}")
raise LogicalPathNotFoundError.new("Could not find logical path for #{asset_path}")
end
end
end

View File

@ -47,7 +47,7 @@ module Flowerbox
end
def actual_path_for(file)
@sprockets.asset_for(file, :bundle => false).pathname.to_s
@sprockets.find_asset(file, :bundle => false).pathname.to_s
end
end
end

View File

@ -18,6 +18,7 @@ module Flowerbox
def <<(file)
super(file)
@included[file.pathname.to_s] = true
end
@ -25,7 +26,6 @@ module Flowerbox
collect { |file| sprockets.logical_path_for(file) }
end
private
def included?(file)
@included[file.pathname.to_s]
end

View File

@ -0,0 +1,22 @@
require 'spec_helper'
require 'flowerbox/reporter/json'
describe Flowerbox::Reporter::JSON do
let(:json) { described_class.new }
describe '#report_numeric_results' do
let(:results) { 'results' }
let(:numeric_results_for) { 'numeric results for' }
before do
json.expects(:numeric_results_for).with(results).returns(numeric_results_for)
json.expects(:puts)
end
it 'should add numeric results' do
json.report_numeric_results(results)
json.output.should == [ [ :results, numeric_results_for ] ]
end
end
end

View File

@ -5,8 +5,9 @@ require 'thread'
require 'flowerbox/server'
describe Flowerbox::Server do
let(:server) { described_class.new(options) }
let(:server) { described_class.new(runner, options) }
let(:options) { nil }
let(:runner) { nil }
subject { server }
@ -50,6 +51,7 @@ describe Flowerbox::Server do
before do
server.stubs(:port).returns(port)
server.stubs(:interface).returns(interface)
server.stubs(:app).returns(lambda { |env| [ 200, {}, [] ] })
end
it 'should start a Rack server' do
@ -104,18 +106,21 @@ describe Flowerbox::Server do
TCPServer.new(interface, initial)
end
server.stubs(:random_port).returns(initial, initial + 1)
server.stubs(:random_port).returns(initial, initial + 2)
while true
count = 10
while count > 0
begin
TCPSocket.new(interface, initial)
break
rescue Errno::ECONNREFUSED
count -= 1
sleep 0.1
end
end
end
it { should == initial + 1 }
it { should == initial + 2 }
after do
@server.kill

View File

@ -8,46 +8,89 @@ describe Flowerbox::SprocketsHandler do
describe '#add' do
let(:asset) { 'asset' }
let(:path) { 'path' }
let(:paths) { [ path ] }
let(:dependent_asset) { 'dependent' }
let(:pathname_path) { 'pathname path' }
let(:files) { stub }
before do
sprockets_handler.expects(:paths_for).with(asset).returns(paths)
sprockets_handler.expects(:path_for_compiled_asset).with(path).returns(pathname_path)
sprockets_handler.expects(:assets_for).with(asset).returns([ dependent_asset ])
sprockets_handler.stubs(:files).returns(files)
files.expects(:add).with(dependent_asset)
end
it 'should add the asset to the list of ones to work with' do
sprockets_handler.add(asset)
sprockets_handler.files.should == [ pathname_path ]
end
end
describe '#paths_for' do
subject { sprockets_handler.paths_for(asset) }
let(:asset) { 'asset' }
let(:environment) { stub }
let(:bundled_asset) { stub(:to_a => [ processed_asset ]) }
let(:processed_asset) { stub(:pathname => path) }
let(:path) { 'path' }
before do
sprockets_handler.stubs(:environment).returns(environment)
environment.expects(:find_asset).with(asset).returns(bundled_asset)
end
it { should == [ path ] }
end
describe '#environment' do
let(:cache) { stub }
before do
sprockets_handler.stubs(:cache).returns(cache)
sprockets_handler.stubs(:default_asset_paths).returns([])
end
subject { sprockets_handler.environment }
it { should be_a_kind_of(Sprockets::Environment) }
its(:paths) { should == asset_paths }
end
describe '#default_asset_paths' do
let(:gem) { 'gem' }
let(:asset) { 'asset' }
before do
described_class.stubs(:gem_asset_paths).returns([ gem ])
sprockets_handler.stubs(:options).returns(:asset_paths => [ asset ])
end
subject { sprockets_handler.default_asset_paths }
it { should == [ gem, asset ] }
end
describe '#assets_for' do
subject { sprockets_handler.assets_for(asset) }
let(:asset) { 'asset' }
let(:found_asset) { 'found asset' }
let(:other_asset) { 'other asset' }
before do
sprockets_handler.stubs(:find_asset).returns(found_asset)
found_asset.stubs(:to_a).returns(other_asset)
end
it { should == other_asset }
end
describe '#logical_path_for' do
subject { sprockets_handler.logical_path_for(asset) }
let(:path) { 'path' }
let(:result) { 'result' }
let(:asset) { stub(:pathname => Pathname("#{path}/#{result}")) }
before do
sprockets_handler.stubs(:paths).returns(paths)
end
context 'found' do
let(:paths) { [ path ] }
it { should == result }
end
context 'not found' do
let(:paths) { [] }
it 'should raise an exception' do
expect { subject }.to raise_error(Flowerbox::SprocketsHandler::LogicalPathNotFoundError)
end
end
end
end

View File

@ -17,4 +17,31 @@ describe Flowerbox::UniqueAssetList do
unique_asset_list.should == [ first, third ]
end
end
describe '#to_json' do
subject { unique_asset_list.to_json }
let(:file) { 'file' }
let(:path) { 'path' }
before do
unique_asset_list.replace([ file ])
sprockets.expects(:logical_path_for).with(file).returns(path)
end
it { should == [ path ] }
end
describe '#<<' do
let(:asset) { stub(:pathname => Pathname(path)) }
let(:path) { 'path' }
it 'should add the asset and mark it included' do
unique_asset_list << asset
unique_asset_list.should == [ asset ]
unique_asset_list.should be_included(asset)
end
end
end