Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
26
.gitignore
vendored
26
.gitignore
vendored
@ -1,22 +1,6 @@
|
|||||||
*.gem
|
|
||||||
*.rbc
|
|
||||||
.bundle
|
|
||||||
.config
|
|
||||||
.yardoc
|
|
||||||
Gemfile.lock
|
|
||||||
InstalledFiles
|
|
||||||
_yardoc
|
|
||||||
coverage
|
|
||||||
doc/
|
|
||||||
lib/bundler/man
|
|
||||||
pkg
|
|
||||||
rdoc
|
|
||||||
spec/reports
|
|
||||||
test/tmp
|
|
||||||
test/version_tmp
|
|
||||||
tmp
|
|
||||||
.tmp
|
|
||||||
node_modules/
|
|
||||||
.tmp-sprockets/
|
|
||||||
_site/
|
|
||||||
.sass-cache/
|
.sass-cache/
|
||||||
|
Gemfile.lock
|
||||||
|
_site/
|
||||||
|
coverage/
|
||||||
|
tmp/
|
||||||
|
pkg/
|
||||||
|
15
Gemfile
15
Gemfile
@ -1,7 +1,10 @@
|
|||||||
source 'https://rubygems.org'
|
source :rubygems
|
||||||
|
|
||||||
# Specify your gem's dependencies in flowerbox.gemspec
|
|
||||||
gemspec
|
|
||||||
|
|
||||||
gem 'guard-flowerbox', :path => '../guard-flowerbox'
|
|
||||||
|
|
||||||
|
gem 'redcarpet', '~> 1'
|
||||||
|
gem 'jekyll'
|
||||||
|
gem 'compass'
|
||||||
|
gem 'foreman'
|
||||||
|
gem 'guard'
|
||||||
|
gem 'haml'
|
||||||
|
gem 'guard-haml'
|
||||||
|
gem 'guard-compass'
|
||||||
|
17
Guardfile
17
Guardfile
@ -1,18 +1,11 @@
|
|||||||
# A sample Guardfile
|
# A sample Guardfile
|
||||||
# More info at https://github.com/guard/guard#readme
|
# More info at https://github.com/guard/guard#readme
|
||||||
|
|
||||||
group :rspec do
|
|
||||||
guard 'rspec', :version => 2 do
|
guard 'compass', :configuration_file => 'config/compass.rb' do
|
||||||
watch(%r{^spec/.+_spec\.rb$})
|
watch(%r{^sass/(.*)\.s[ac]ss$})
|
||||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
|
||||||
watch('spec/spec_helper.rb') { "spec" }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :wip do
|
guard 'haml', :input => '_layouts/haml', :output => '_layouts' do
|
||||||
guard 'cucumber', :cli => '-p wip' do
|
watch(/^.+(\.html\.haml)/)
|
||||||
watch(%r{^features/.+\.feature$})
|
|
||||||
watch(%r{^features/support/.+$}) { 'features' }
|
|
||||||
watch(%r{^features/step_definitions/.*$}) { 'features' }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
22
LICENSE
22
LICENSE
@ -1,22 +0,0 @@
|
|||||||
Copyright (c) 2012 John Bintz
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
29
README.md
29
README.md
@ -1,29 +0,0 @@
|
|||||||
# Flowerbox
|
|
||||||
|
|
||||||
TODO: Write a gem description
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Add this line to your application's Gemfile:
|
|
||||||
|
|
||||||
gem 'flowerbox'
|
|
||||||
|
|
||||||
And then execute:
|
|
||||||
|
|
||||||
$ bundle
|
|
||||||
|
|
||||||
Or install it yourself as:
|
|
||||||
|
|
||||||
$ gem install flowerbox
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
TODO: Write usage instructions here
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
1. Fork it
|
|
||||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
||||||
3. Commit your changes (`git commit -am 'Added some feature'`)
|
|
||||||
4. Push to the branch (`git push origin my-new-feature`)
|
|
||||||
5. Create new Pull Request
|
|
4
_config.yml
Normal file
4
_config.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
markdown: redcarpet
|
||||||
|
auto: true
|
||||||
|
pygments: true
|
||||||
|
|
14
_layouts/default.html
Normal file
14
_layouts/default.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Flowerbox</title>
|
||||||
|
<link href='http://fonts.googleapis.com/css?family=Pontano+Sans' rel='stylesheet' type='text/css' />
|
||||||
|
<link href='http://fonts.googleapis.com/css?family=Gorditas' rel='stylesheet' type='text/css' />
|
||||||
|
<link href='stylesheets/screen.css' rel='stylesheet' type='text/css' />
|
||||||
|
<link href='stylesheets/syntax.css' rel='stylesheet' type='text/css' />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='container'>
|
||||||
|
{{ content }}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
_layouts/haml/default.html.haml
Normal file
11
_layouts/haml/default.html.haml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
%html
|
||||||
|
%head
|
||||||
|
%title Flowerbox
|
||||||
|
%link{:href => 'http://fonts.googleapis.com/css?family=Pontano+Sans', :rel => :stylesheet, :type => 'text/css'}/
|
||||||
|
%link{:href => 'http://fonts.googleapis.com/css?family=Gorditas', :rel => :stylesheet, :type => 'text/css'}/
|
||||||
|
%link{:rel => :stylesheet, :href => 'stylesheets/screen.css', :type => 'text/css'}/
|
||||||
|
%link{:rel => :stylesheet, :href => 'stylesheets/syntax.css', :type => 'text/css'}/
|
||||||
|
%body
|
||||||
|
#container
|
||||||
|
{{ content }}
|
||||||
|
|
@ -1,95 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
if !ENV['BUNDLE_GEMFILE'] && File.file?('Gemfile')
|
|
||||||
require 'bundler/setup'
|
|
||||||
|
|
||||||
$: << File.expand_path('../../lib', __FILE__)
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'flowerbox'
|
|
||||||
require 'thor'
|
|
||||||
|
|
||||||
class Flowerbox::CLI < Thor
|
|
||||||
include Thor::Actions
|
|
||||||
|
|
||||||
default_task :help
|
|
||||||
|
|
||||||
def help(*args)
|
|
||||||
if !args.first
|
|
||||||
puts "Flowerbox is a multi-environment, multi-runner JavaScript testing tool."
|
|
||||||
puts "It supports Jasmine and Cucumber.js, and it runs tests on Node.js and Selenium-driven browsers."
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "test [DIR]", "Run the specs found in spec dir, loading spec_helper.rb for configuration details"
|
|
||||||
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))
|
|
||||||
Flowerbox.cleanup!
|
|
||||||
exit result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "debug DIR", "Start the Flowerbox server to help debug loading issues."
|
|
||||||
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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "transplant DIR", "Convert an existing JavaScript testing project to Flowerbox"
|
|
||||||
long_desc <<-TXT
|
|
||||||
`flowerbox transplant` converts an existing JavaScript testing project type
|
|
||||||
to Flowerbox. Currently, you can transplant the following types of projects:
|
|
||||||
|
|
||||||
* Pivotal Labs Jasmine gem-style
|
|
||||||
\x5 (also covers jasmine-headless-webkit)
|
|
||||||
|
|
||||||
These types of projects live in `spec/javascripts` (or the specified directory)
|
|
||||||
and have the file `support/jasmine.yml` that defines what files are loaded
|
|
||||||
at what parts in the test load process. `jasmine.yml` is converted to a
|
|
||||||
Flowerbox `spec_helper.rb` file and placed into `spec/javascripts`.
|
|
||||||
|
|
||||||
Flowerbox will ask before overwriting existing files.
|
|
||||||
TXT
|
|
||||||
def transplant(dir)
|
|
||||||
Flowerbox.transplant(dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "clean", "Clean the Sprockets cache"
|
|
||||||
def clean
|
|
||||||
FileUtils.rm_rf(Flowerbox::CACHE_DIR)
|
|
||||||
puts "Sprockets cache cleaned."
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "plant TYPE [DIR]", "Start a new Flowerbox project of TYPE, potentially specifying a different DIR to install"
|
|
||||||
def plant(type, dir = nil)
|
|
||||||
env = Flowerbox::TestEnvironment.for(type)
|
|
||||||
|
|
||||||
self.class.source_root(Flowerbox.path.join(env.plant_source))
|
|
||||||
|
|
||||||
directory('.', dir || env.plant_target)
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing(method, *args)
|
|
||||||
if File.directory?(method.to_s)
|
|
||||||
test(method.to_s, *args)
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
no_tasks do
|
|
||||||
def pwd
|
|
||||||
options[:pwd] || Dir.pwd
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Flowerbox::CLI.start
|
|
||||||
|
|
3
config/compass.rb
Normal file
3
config/compass.rb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
css_dir = 'stylesheets'
|
||||||
|
sass_dir = 'sass'
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
default: -f pretty
|
|
||||||
wip: --tags @wip -f pretty
|
|
@ -1,52 +0,0 @@
|
|||||||
Feature: Basic Run
|
|
||||||
Background:
|
|
||||||
Given I have the file "spec/javascripts/cat_spec.js.coffee" with the content:
|
|
||||||
"""
|
|
||||||
#= require cat
|
|
||||||
|
|
||||||
describe 'Cat', ->
|
|
||||||
describe '#meow', ->
|
|
||||||
it 'should meow', ->
|
|
||||||
cat = new Cat()
|
|
||||||
|
|
||||||
expect(cat.meow()).toEqual('meow')
|
|
||||||
"""
|
|
||||||
Given I have the file "lib/cat.js.coffee" with the content:
|
|
||||||
"""
|
|
||||||
class @Cat
|
|
||||||
meow: -> "meow"
|
|
||||||
"""
|
|
||||||
|
|
||||||
Scenario: Use the Node 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 :node
|
|
||||||
|
|
||||||
c.spec_patterns << "**/*_spec.*"
|
|
||||||
c.asset_paths << "lib"
|
|
||||||
|
|
||||||
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 :chrome
|
|
||||||
|
|
||||||
c.spec_patterns << "**/*_spec.*"
|
|
||||||
c.asset_paths << "lib"
|
|
||||||
|
|
||||||
c.reporters << :json
|
|
||||||
end
|
|
||||||
"""
|
|
||||||
When I run Flowerbox with "spec/javascripts"
|
|
||||||
Then I should have 1 test and 0 failures
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
Given /^I have the file "([^"]*)" with the content:$/ do |file, string|
|
|
||||||
target = File.join(@root, file)
|
|
||||||
|
|
||||||
FileUtils.mkdir_p File.dirname(target)
|
|
||||||
File.open(target, 'wb') { |fh| fh.print string }
|
|
||||||
end
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
require 'json'
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
When /^I run Flowerbox with "([^"]*)"$/ do |arguments|
|
|
||||||
command = %{bundle exec bin/flowerbox test #{arguments} -q --pwd #{@root} 2>&1}
|
|
||||||
|
|
||||||
@output = %x{#{command}}
|
|
||||||
|
|
||||||
raise StandardError.new("Flowerbox failed: #{@output}") if $?.exitstatus != 0
|
|
||||||
end
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
Before do
|
|
||||||
@root = "tmp/features"
|
|
||||||
|
|
||||||
FileUtils.rm_rf @root
|
|
||||||
end
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
|
||||||
require File.expand_path('../lib/flowerbox/version', __FILE__)
|
|
||||||
|
|
||||||
Gem::Specification.new do |gem|
|
|
||||||
gem.authors = ["John Bintz"]
|
|
||||||
gem.email = ["john@coswellproductions.com"]
|
|
||||||
gem.description = %q{No-nonsense JavaScript testing solution.}
|
|
||||||
gem.summary = %q{No-nonsense JavaScript testing solution.}
|
|
||||||
gem.homepage = ""
|
|
||||||
|
|
||||||
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
||||||
gem.files = `git ls-files`.split("\n")
|
|
||||||
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
||||||
gem.name = "flowerbox"
|
|
||||||
gem.require_paths = ["lib"]
|
|
||||||
gem.version = Flowerbox::VERSION
|
|
||||||
|
|
||||||
gem.add_development_dependency 'cucumber'
|
|
||||||
gem.add_development_dependency 'rspec'
|
|
||||||
gem.add_development_dependency 'simplecov'
|
|
||||||
gem.add_development_dependency 'mocha'
|
|
||||||
gem.add_development_dependency 'fakefs'
|
|
||||||
gem.add_development_dependency 'guard'
|
|
||||||
gem.add_development_dependency 'guard-flowerbox'
|
|
||||||
gem.add_development_dependency 'guard-rspec'
|
|
||||||
gem.add_development_dependency 'guard-cucumber'
|
|
||||||
|
|
||||||
gem.add_dependency 'jasmine-core'
|
|
||||||
gem.add_dependency 'thor'
|
|
||||||
gem.add_dependency 'selenium-webdriver'
|
|
||||||
gem.add_dependency 'rainbow'
|
|
||||||
gem.add_dependency 'sprockets'
|
|
||||||
gem.add_dependency 'sprockets-vendor_gems'
|
|
||||||
gem.add_dependency 'thin'
|
|
||||||
gem.add_dependency 'em-websocket'
|
|
||||||
gem.add_dependency 'coffee-script'
|
|
||||||
end
|
|
||||||
|
|
237
index.md
Normal file
237
index.md
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
---
|
||||||
|
title: Flowerbox -- a multi-environment, multi-runner, super-easy JavaScript testing framework framework.
|
||||||
|
layout: default
|
||||||
|
---
|
||||||
|
# Flowerbox
|
||||||
|
## A multi-environment, multi-runner, super-easy JavaScript testing framework framework.
|
||||||
|
---
|
||||||
|
## Super-fast unit testing getting started super guide!
|
||||||
|
### (you need Firefox at the very least)
|
||||||
|
|
||||||
|
{% highlight sh %}
|
||||||
|
gem install flowerbox
|
||||||
|
flowerbox plant jasmine
|
||||||
|
flowerbox spec/javascripts
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## ...and what about integration testing?
|
||||||
|
### (whoa nelly, pure JS Cucumber?)
|
||||||
|
|
||||||
|
{% highlight sh %}
|
||||||
|
flowerbox plant cucumber
|
||||||
|
flowerbox js-features
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## Hey, I'm already using `jasmine-gem` or `jasmine-headless-webkit`
|
||||||
|
### (you're smart!)
|
||||||
|
|
||||||
|
{% highlight sh %}
|
||||||
|
flowerbox transplant spec/javascripts
|
||||||
|
flowerbox spec/javascripts
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## I use Sprockets and need to configure asset paths and templates and stuff!
|
||||||
|
### (...may the force be with you...)
|
||||||
|
|
||||||
|
{% highlight ruby %}
|
||||||
|
# in spec/javascripts/spec_helper.rb
|
||||||
|
|
||||||
|
require 'haml-sprockets'
|
||||||
|
|
||||||
|
Flowerbox.configure do |c|
|
||||||
|
c.asset_paths << "app/assets/javascripts"
|
||||||
|
c.asset_paths << "vendor/assets/javascripts"
|
||||||
|
|
||||||
|
c.additional_files << "applications.js.coffee"
|
||||||
|
|
||||||
|
# ... other stuff ...
|
||||||
|
end
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## Firefox is so 2008. Can I run my tests on Chrome?
|
||||||
|
### (we like Selenium here!)
|
||||||
|
|
||||||
|
{% highlight ruby %}
|
||||||
|
Flowerbox.configure do |c|
|
||||||
|
c.run_with :chrome
|
||||||
|
end
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## Chrome is so 2010. Can I run my tests on node.js?
|
||||||
|
### (evented hipness! jsdom will give you a fake DOM to work with.)
|
||||||
|
|
||||||
|
{% highlight ruby %}
|
||||||
|
# make sure node and npm are in your path
|
||||||
|
# jsdom and ws get installed in your project under node_modules
|
||||||
|
|
||||||
|
Flowerbox.configure do |c|
|
||||||
|
c.run_with :node
|
||||||
|
end
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
{% highlight coffeescript %}
|
||||||
|
# make sure you bind your global things to this
|
||||||
|
|
||||||
|
class @MyCoolClass extends @MyBaseClass
|
||||||
|
cool: -> "yeah man"
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## Where'd headless support go?!
|
||||||
|
|
||||||
|
No, I knew you'd ask. Read the long bit of text below. _tl;dr_: Selenium with Chrome is
|
||||||
|
pretty much the same setup, just as fast, and gives you stack traces and other debugging assistance.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plant some Jasmine
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plant a Cucumber or two
|
||||||
|
|
||||||
|
## Make a feature like in big boy Cucumber
|
||||||
|
### (that's 'cause it *is* big boy Cucumber, just in JS form)
|
||||||
|
|
||||||
|
{% highlight gherkin %}
|
||||||
|
# js-features/features/my_cool_thing.feature
|
||||||
|
|
||||||
|
Feature: My Cool Thing
|
||||||
|
Scenario: Show a cool thing
|
||||||
|
Given I have a cool thing
|
||||||
|
And I have a cool thing viewer
|
||||||
|
When I render the viewer
|
||||||
|
Then I should see the cool thing in the viewer
|
||||||
|
|
||||||
|
@wip # <= tags work, too!
|
||||||
|
Scenario: Maybe do something else
|
||||||
|
Given something
|
||||||
|
When yadda
|
||||||
|
Then BAM
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## Write some steps
|
||||||
|
### (Flowerbox gives you a donut-full of syntactic sugar that wraps around Cucumber.js. Be sure to drink lots of water!)
|
||||||
|
|
||||||
|
{% highlight coffeescript %}
|
||||||
|
# js-features/steps/given/i_have_a_cool_thing.js.coffee
|
||||||
|
|
||||||
|
Flowerbox.Given /^I have a cool thing$/, ->
|
||||||
|
@data =
|
||||||
|
one: 'two'
|
||||||
|
three: 'four'
|
||||||
|
|
||||||
|
@coolThing = new CoolThing(@data)
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
{% highlight coffeescript %}
|
||||||
|
# js-features/steps/given/i_have_a_cool_thing_viewer.js.coffee
|
||||||
|
|
||||||
|
Flowerbox.Given /^I have a cool thing viewer$/, ->
|
||||||
|
@coolThingViewer = new CoolThingViewer(@coolThing)
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
{% highlight coffeescript %}
|
||||||
|
# js-features/steps/when/i_render_the_viewer.js.coffee
|
||||||
|
|
||||||
|
Flowerbox.When /^I render the viewer$/, ->
|
||||||
|
@coolThingViewer.render()
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
{% highlight coffeescript %}
|
||||||
|
# js-features/steps/then/i_should_see_the_cool_thing.js.coffee
|
||||||
|
|
||||||
|
Flowerbox.Then /^I should see the cool thing in the viewer$/, ->
|
||||||
|
@expect(
|
||||||
|
@coolThingViewer.$('input[name="one"]').val()
|
||||||
|
).toEqual(@data.one)
|
||||||
|
|
||||||
|
@expect(
|
||||||
|
@coolThingViewer.$('input[name="three"]').val()
|
||||||
|
).toEqual(@data.three)
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## Get some hooks up in here!
|
||||||
|
### (and other fun environment stuff)
|
||||||
|
|
||||||
|
{% highlight coffeescript %}
|
||||||
|
# js-features/support/env.js.coffee
|
||||||
|
#
|
||||||
|
#= require ../steps
|
||||||
|
|
||||||
|
Flowerbox.World ->
|
||||||
|
@Before (callback) ->
|
||||||
|
@something = 'is available everywhere'
|
||||||
|
callback()
|
||||||
|
|
||||||
|
@After (callback) ->
|
||||||
|
Put = 'some stuff back'
|
||||||
|
callback()
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
## Auto-generate missing steps
|
||||||
|
### (see [cucumber-step_writer](http://github.com/johnbintz/cucumber-step_writer) for my philosophy)
|
||||||
|
|
||||||
|
{% highlight ruby %}
|
||||||
|
# js-features/spec_helper.rb
|
||||||
|
|
||||||
|
Flowerbox.configure do |c|
|
||||||
|
c.report_with :verbose
|
||||||
|
c.reporters.add(
|
||||||
|
:step_writer,
|
||||||
|
:target => 'js-features/step_definitions',
|
||||||
|
:on_finish => lambda { |dir| system %{open #{dir}} }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
{% endhighlight %}
|
||||||
|
---
|
||||||
|
|
||||||
|
# Flowerbox! Yea...wha?
|
||||||
|
|
||||||
|
So I (John) have been in this JavaScript testing game for a while, and, after about
|
||||||
|
three years of doing JS testing for Web apps, decided to finally take all the knowledge I had
|
||||||
|
and make something that was dead-simple to use.
|
||||||
|
|
||||||
|
## You already did once...
|
||||||
|
|
||||||
|
`jasmine-headless-webkit` was my first attempt, but it's a C++/Qt mess. Downloading all of
|
||||||
|
Qt is a total pain. Also, Qt4's JavaScript engine, JavaScriptCore, doesn't have stack trace
|
||||||
|
support, so it got pretty useless for me for bigger, messy projects (looking at you, Backbone).
|
||||||
|
Qt5 has V8, but whatever, I just decided to ditch the whole
|
||||||
|
compiling software thing and replace it with running tests in Selenium and/or node.js.
|
||||||
|
|
||||||
|
## Selenium faster than headless? But CI servers--
|
||||||
|
|
||||||
|
Sure, spinning up that instance of Firefox or Chrome is slower than starting up a small
|
||||||
|
QtWebKit widget, but there's a lot less that can go wrong when using a full-blown
|
||||||
|
browser. This also means your CI server config stays leaner. You may already have Qt installed
|
||||||
|
for capybara-webkit, but one less thing depending on Qt just makes your life (and mine) easier.
|
||||||
|
|
||||||
|
Also, when running Flowerbox with Guard, your browser stays open between test runs, so you only
|
||||||
|
pay the startup penalty once.
|
||||||
|
|
||||||
|
And, if you write everything correctly, you can just run your tests on node.js and not even have
|
||||||
|
to worry about a browser.
|
||||||
|
|
||||||
|
## But you said fake browsers are teh bad--
|
||||||
|
|
||||||
|
I changed my mind. As long as I'm not testing for browser-only things, like CSS interaction
|
||||||
|
or position elements, running tests on node.js with a browser-like DOM provided by jsdom
|
||||||
|
really is good enough. I use full-blown Ruby Cucumber if I need to test more complex browser
|
||||||
|
things.
|
||||||
|
|
||||||
|
## Cucumber.js? Full-stack testing is the only way!
|
||||||
|
|
||||||
|
Yeah, and it's slow. I'll carve out what I can in Cucumber.js while I work on Ruby Cucumber
|
||||||
|
features. Makes the process of getting those complex integration tests written a lot faster.
|
||||||
|
|
||||||
|
Also, I've changed my views on unit testing as a process. Unit testing for me is now about
|
||||||
|
two things:
|
||||||
|
|
||||||
|
* Code design through a liberal use of stubs and mocks.
|
||||||
|
* Really quickly running through blg blobs of inputs/outputs (parsers and such).
|
||||||
|
|
||||||
|
Treating unit testing this way taught me how to better organize and design my code, and I
|
||||||
|
only use it for really testing code when I need to test those big blobs of things.
|
||||||
|
This is my opinion, YMMV, Flowerbox lets you do what you want. Never use Cucumber.js support
|
||||||
|
if you don't want to. Write your integration bits in Jasmine. I don't mind.
|
||||||
|
|
@ -1,65 +0,0 @@
|
|||||||
Flowerbox =
|
|
||||||
debug: false
|
|
||||||
ping: ->
|
|
||||||
Flowerbox.contact('ping')
|
|
||||||
|
|
||||||
messageQueue: []
|
|
||||||
pulling: false
|
|
||||||
|
|
||||||
contact: (url, data...) ->
|
|
||||||
Flowerbox.started = true
|
|
||||||
|
|
||||||
if !Flowerbox.debug
|
|
||||||
message = [ url, data ]
|
|
||||||
Flowerbox.messageQueue.push(message)
|
|
||||||
|
|
||||||
if Flowerbox.socket && !Flowerbox.pulling
|
|
||||||
Flowerbox.queuePuller()
|
|
||||||
|
|
||||||
started: false
|
|
||||||
done: false
|
|
||||||
|
|
||||||
queuePuller: ->
|
|
||||||
Flowerbox.pulling = true
|
|
||||||
|
|
||||||
if Flowerbox.messageQueue.length > 0
|
|
||||||
message = Flowerbox.messageQueue.shift()
|
|
||||||
|
|
||||||
Flowerbox.socket.onmessage = (data) ->
|
|
||||||
if message[0] == 'results'
|
|
||||||
if __$instrument?
|
|
||||||
Flowerbox.socket.send JSON.stringify(['instrument', __$instrument]) {}, ->
|
|
||||||
Flowerbox.done = true
|
|
||||||
else
|
|
||||||
Flowerbox.done = true
|
|
||||||
|
|
||||||
Flowerbox.queuePuller()
|
|
||||||
|
|
||||||
Flowerbox.socket.send JSON.stringify(message)
|
|
||||||
else
|
|
||||||
setTimeout(Flowerbox.queuePuller, 10) if !Flowerbox.done
|
|
||||||
|
|
||||||
fail: ->
|
|
||||||
|
|
||||||
class Flowerbox.Exception
|
|
||||||
constructor: (@source, @name, @stack) ->
|
|
||||||
|
|
||||||
toJSON: ->
|
|
||||||
{ status: Flowerbox.Result.FAILURE, source: @source, name: @name, trace: { stack: @stack } }
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
|||||||
#= require flowerbox/cucumber/reporter
|
|
||||||
|
|
||||||
Flowerbox.Cucumber ||= {}
|
|
||||||
Flowerbox.Cucumber.features = ->
|
|
||||||
Flowerbox.Cucumber.Features ||= []
|
|
||||||
Flowerbox.Cucumber.Features.join("\n")
|
|
||||||
|
|
||||||
Flowerbox.World = (code = null) ->
|
|
||||||
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}"
|
|
||||||
result = null
|
|
||||||
|
|
||||||
if @actual? && expected?
|
|
||||||
switch (typeof @actual)
|
|
||||||
when 'object'
|
|
||||||
result = true
|
|
||||||
for key, value of @actual
|
|
||||||
result = false if expected[key] != value
|
|
||||||
|
|
||||||
for key, value of expected
|
|
||||||
result = false if @actual[key] != value
|
|
||||||
|
|
||||||
if result == null
|
|
||||||
result = (@actual == expected)
|
|
||||||
|
|
||||||
result
|
|
||||||
|
|
||||||
Flowerbox.World ->
|
|
||||||
@assert = (what, message = 'failed') ->
|
|
||||||
throw new Error(message) if !what
|
|
||||||
|
|
||||||
@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
|
|
||||||
@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()
|
|
||||||
|
|
||||||
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()]
|
|
||||||
nextLine = false
|
|
||||||
if stack = (new Error()).stack
|
|
||||||
for line in stack.split('\n')
|
|
||||||
if nextLine
|
|
||||||
result = if line.match(/__F__/)
|
|
||||||
line.replace(/^.*__F__\/([^:]+:\d+).*$/, '$1')
|
|
||||||
else
|
|
||||||
line.replace(/^.*(\(| )([^:]+:\d+).*$/, '$2')
|
|
||||||
|
|
||||||
Flowerbox.Step.files[match.toString()] = [ match, result ]
|
|
||||||
break
|
|
||||||
|
|
||||||
if line.match(/(Given|When|Then)/)
|
|
||||||
nextLine = true
|
|
||||||
|
|
||||||
Flowerbox.Step(type, match, code)
|
|
||||||
|
|
||||||
stepGenerator(type) for type in [ 'Given', 'When', 'Then' ]
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
|||||||
Flowerbox = Flowerbox || {}
|
|
||||||
Flowerbox.Cucumber = 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'
|
|
||||||
@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')
|
|
||||||
|
|
||||||
file = Flowerbox.Step.matchFile(@step.getName()) || "#{Flowerbox.UNKNOWN}:0"
|
|
||||||
|
|
||||||
result = new Flowerbox.Result(step_type: this.type(), source: 'cucumber', original_name: @step.getName(), name: this.nameParts(), file: file)
|
|
||||||
|
|
||||||
if stepResult.isSuccessful()
|
|
||||||
result.status = Flowerbox.Result.SUCCESS
|
|
||||||
else if stepResult.isPending()
|
|
||||||
result.status = Flowerbox.Result.PENDING
|
|
||||||
else if stepResult.isUndefined()
|
|
||||||
result.status = Flowerbox.Result.UNDEFINED
|
|
||||||
result.hasDataTable = @step.hasDataTable()
|
|
||||||
result.hasDocString = @step.hasDocString()
|
|
||||||
else if stepResult.isFailed()
|
|
||||||
result.status = Flowerbox.Result.FAILURE
|
|
||||||
|
|
||||||
error = stepResult.getFailureException()
|
|
||||||
stack = (error.stack || "message\n#{file}:0").split("\n")
|
|
||||||
|
|
||||||
failure = { runner: Flowerbox.environment, message: error.message, stack: stack }
|
|
||||||
|
|
||||||
result.failures.push(failure)
|
|
||||||
|
|
||||||
Flowerbox.contact("finish_test", result.toJSON())
|
|
||||||
|
|
||||||
callback()
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
window.onerror = (message, file, line) ->
|
|
||||||
Flowerbox.contact("log", message)
|
|
||||||
Flowerbox.contact("log", " #{file}:#{line}")
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
|||||||
#= require flowerbox/jasmine/reporter
|
|
||||||
|
|
||||||
# because why not?
|
|
||||||
@context = @describe
|
|
||||||
|
|
||||||
getSplitName = (parts) ->
|
|
||||||
parts.push(String(@description).replace(/[\n\r]/g, ' '))
|
|
||||||
parts
|
|
||||||
|
|
||||||
jasmine.Suite.prototype.getSuiteSplitName = ->
|
|
||||||
this.getSplitName(if @parentSuite then @parentSuite.getSuiteSplitName() else [])
|
|
||||||
|
|
||||||
jasmine.Spec.prototype.getSpecSplitName = ->
|
|
||||||
this.getSplitName(@suite.getSuiteSplitName())
|
|
||||||
|
|
||||||
jasmine.Suite.prototype.getSplitName = getSplitName
|
|
||||||
jasmine.Spec.prototype.getSplitName = getSplitName
|
|
||||||
|
|
||||||
jasmine.Spec.prototype.addMatcherResult = (result) ->
|
|
||||||
for method in jasmine.Spec.beforeAddMatcherResult()
|
|
||||||
method.apply(result, [ this ])
|
|
||||||
|
|
||||||
@results_.addResult(result)
|
|
||||||
|
|
||||||
jasmine.Spec.beforeAddMatcherResult = ->
|
|
||||||
@_beforeAddMatcherResult ||= []
|
|
||||||
|
|
||||||
jasmine.Spec.beforeAddMatcherResult().push (spec) ->
|
|
||||||
@splitName = spec.getSpecSplitName()
|
|
||||||
|
|
||||||
Flowerbox.only = (envs..., code) ->
|
|
||||||
for env in envs
|
|
||||||
if Flowerbox.environment == env
|
|
||||||
describe("only in #{envs.join(', ')}", code)
|
|
||||||
|
|
||||||
jasmine.WaitsBlock.prototype._execute = jasmine.WaitsBlock.prototype.execute
|
|
||||||
jasmine.WaitsForBlock.prototype._execute = jasmine.WaitsForBlock.prototype.execute
|
|
||||||
|
|
||||||
pauseAndRun = (onComplete) ->
|
|
||||||
jasmine.getEnv().reporter.reportSpecWaiting()
|
|
||||||
|
|
||||||
this._execute ->
|
|
||||||
jasmine.getEnv().reporter.reportSpecRunning()
|
|
||||||
onComplete()
|
|
||||||
|
|
||||||
jasmine.WaitsBlock.prototype.execute = pauseAndRun
|
|
||||||
jasmine.WaitsForBlock.prototype.execute = pauseAndRun
|
|
||||||
|
|
||||||
for method in [ "reportSpecWaiting", "reportSpecRunning" ]
|
|
||||||
generator = (method) ->
|
|
||||||
(args...) ->
|
|
||||||
for reporter in @subReporters_
|
|
||||||
if reporter[method]?
|
|
||||||
reporter[method](args...)
|
|
||||||
|
|
||||||
jasmine.MultiReporter.prototype[method] = generator(method)
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
|||||||
jasmine.Spec.beforeAddMatcherResult().push ->
|
|
||||||
if !@passed_
|
|
||||||
@trace = { stack: new Error().stack }
|
|
||||||
|
|
||||||
Flowerbox.fail = -> process.exit(1)
|
|
@ -1,53 +0,0 @@
|
|||||||
class jasmine.FlowerboxReporter
|
|
||||||
buildResult: (spec, overrides = {}) ->
|
|
||||||
data =
|
|
||||||
status: Flowerbox.Result.SUCCESS
|
|
||||||
source: 'jasmine'
|
|
||||||
name: spec.getSpecSplitName()
|
|
||||||
file: "#{Flowerbox.UNKNOWN}:0"
|
|
||||||
|
|
||||||
for key, value of overrides
|
|
||||||
data[key] = value
|
|
||||||
|
|
||||||
new Flowerbox.Result(data)
|
|
||||||
|
|
||||||
reportRunnerStarting: (runner) ->
|
|
||||||
@time = (new Date()).getTime()
|
|
||||||
|
|
||||||
Flowerbox.contact("starting")
|
|
||||||
|
|
||||||
reportSpecStarting: (spec) ->
|
|
||||||
Flowerbox.contact("start_test", spec.getSpecSplitName().join(" "))
|
|
||||||
|
|
||||||
if spec.description == 'encountered a declaration exception'
|
|
||||||
Flowerbox.contact("finish_test", this.buildResult(spec, status: Flowerbox.Result.FAILURE))
|
|
||||||
|
|
||||||
reportSpecWaiting: ->
|
|
||||||
@paused = true
|
|
||||||
Flowerbox.contact('pause_timer')
|
|
||||||
|
|
||||||
reportSpecRunning: ->
|
|
||||||
@paused = false
|
|
||||||
Flowerbox.contact('unpause_timer')
|
|
||||||
|
|
||||||
reportSpecResults: (spec) ->
|
|
||||||
this.reportSpecRunning() if @paused
|
|
||||||
|
|
||||||
result = this.buildResult(spec)
|
|
||||||
|
|
||||||
for item in spec.results().items_
|
|
||||||
if !item.passed_
|
|
||||||
result.status = Flowerbox.Result.FAILURE
|
|
||||||
|
|
||||||
stack = item.trace.stack || []
|
|
||||||
if stack.constructor != Array
|
|
||||||
stack = stack.split("\n")
|
|
||||||
|
|
||||||
failure = { runner: Flowerbox.environment, message: item.message, stack: stack }
|
|
||||||
|
|
||||||
result.failures.push(failure)
|
|
||||||
|
|
||||||
Flowerbox.contact("finish_test", result)
|
|
||||||
reportRunnerResults: (runner) ->
|
|
||||||
Flowerbox.contact("results", (new Date().getTime()) - @time)
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
jasmine.Spec.beforeAddMatcherResult().push ->
|
|
||||||
if !@passed_
|
|
||||||
@trace = { stack: [] }
|
|
||||||
try
|
|
||||||
lol.wut
|
|
||||||
catch e
|
|
||||||
if e.stack
|
|
||||||
file = switch Flowerbox.environment.toLowerCase()
|
|
||||||
when 'firefox'
|
|
||||||
e.stack.split("\n")[3]
|
|
||||||
when 'chrome'
|
|
||||||
e.stack.split("\n")[4]
|
|
||||||
else
|
|
||||||
"#{Flowerbox.UNKNOWN}:0"
|
|
||||||
|
|
||||||
@trace = { stack: [ @message, file ] }
|
|
||||||
|
|
109
lib/flowerbox.rb
109
lib/flowerbox.rb
@ -1,109 +0,0 @@
|
|||||||
require "flowerbox/version"
|
|
||||||
require 'rainbow'
|
|
||||||
require 'forwardable'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
require 'flowerbox/core_ext/module'
|
|
||||||
|
|
||||||
require 'flowerbox/runner'
|
|
||||||
|
|
||||||
require 'flowerbox/run/base'
|
|
||||||
require 'flowerbox/run/test'
|
|
||||||
require 'flowerbox/run/debug'
|
|
||||||
|
|
||||||
require 'flowerbox/test_environment'
|
|
||||||
require 'flowerbox/result'
|
|
||||||
require 'flowerbox/result_set'
|
|
||||||
require 'flowerbox/gathered_result'
|
|
||||||
|
|
||||||
require 'flowerbox/reporter'
|
|
||||||
require 'flowerbox/configuration'
|
|
||||||
|
|
||||||
if defined?(Rails::Engine)
|
|
||||||
require 'flowerbox/rails/engine'
|
|
||||||
end
|
|
||||||
|
|
||||||
CACHE_DIR = 'tmp/sprockets'
|
|
||||||
|
|
||||||
class << self
|
|
||||||
attr_writer :quiet
|
|
||||||
attr_accessor :server
|
|
||||||
|
|
||||||
def reset!
|
|
||||||
@configuration = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def configuration
|
|
||||||
@configuration ||= Flowerbox::Configuration.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def path
|
|
||||||
Pathname(File.expand_path('../..', __FILE__))
|
|
||||||
end
|
|
||||||
|
|
||||||
def configure(&block)
|
|
||||||
configuration.configure(&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def bare_coffeescript
|
|
||||||
@bare_coffeescript ||= true
|
|
||||||
end
|
|
||||||
|
|
||||||
def debug(dir, options = {})
|
|
||||||
Flowerbox::Run::Debug.execute(dir, options)
|
|
||||||
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
|
|
||||||
|
|
||||||
def cleanup!
|
|
||||||
browsers.values.each do |browser|
|
|
||||||
begin
|
|
||||||
browser.close
|
|
||||||
rescue Errno::ECONNREFUSED => e
|
|
||||||
puts "Browser already closed.".foreground(:yellow)
|
|
||||||
rescue ::Selenium::WebDriver::Error::UnknownError => e
|
|
||||||
puts "Unknown browser error, pushing past it to close...".foreground(:yellow)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@browsers = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def transplant(dir)
|
|
||||||
Flowerbox::TestEnvironment.transplantable_environments.each do |env|
|
|
||||||
break if env.transplant(dir)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def cache_dir
|
|
||||||
File.join(CACHE_DIR, Flowerbox.test_environment.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing(method, *args)
|
|
||||||
if configuration.respond_to?(method)
|
|
||||||
configuration.send(method, *args)
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
|||||||
require 'flowerbox/reporter_list'
|
|
||||||
require 'flowerbox/instrumented_files_list'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
class Configuration
|
|
||||||
attr_writer :reporters, :backtrace_filter, :instrument_js
|
|
||||||
attr_accessor :port
|
|
||||||
|
|
||||||
attr_accessor :test_environment, :runner_environment, :bare_coffeescript
|
|
||||||
|
|
||||||
def spec_patterns
|
|
||||||
@spec_patterns ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def asset_paths
|
|
||||||
@asset_paths ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def reporters
|
|
||||||
@reporters ||= Flowerbox::ReporterList.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def additional_files
|
|
||||||
@additional_files ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def backtrace_filter
|
|
||||||
@backtrace_filter ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def instrument_files
|
|
||||||
@instrument_files ||= Flowerbox::InstrumentedFilesList.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def instrument_js?
|
|
||||||
!instrument_files.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_with(what)
|
|
||||||
self.test_environment = Flowerbox::TestEnvironment.for(what)
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_with(*whats)
|
|
||||||
self.runner_environment = whats.flatten.collect { |what| Flowerbox::Runner.for(what.to_s) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_with(*whats)
|
|
||||||
self.reporters.clear!
|
|
||||||
whats.each { |what| self.reporters << what }
|
|
||||||
end
|
|
||||||
|
|
||||||
def configure
|
|
||||||
yield self
|
|
||||||
|
|
||||||
if spec_patterns.empty?
|
|
||||||
spec_patterns << "**/*_spec*"
|
|
||||||
spec_patterns << "*/*_spec*"
|
|
||||||
end
|
|
||||||
|
|
||||||
if reporters.empty?
|
|
||||||
reporters << :progress
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
module CoreExt
|
|
||||||
module Module
|
|
||||||
def find_constant(string)
|
|
||||||
const_get(constants.find { |f| f.to_s.downcase == string.to_s.downcase.gsub('_', '') })
|
|
||||||
end
|
|
||||||
|
|
||||||
def for(env)
|
|
||||||
find_constant(env).new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
|||||||
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
|
|
||||||
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
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
class GatheredResult
|
|
||||||
attr_reader :name
|
|
||||||
|
|
||||||
def initialize(name)
|
|
||||||
@name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(result)
|
|
||||||
results << result
|
|
||||||
end
|
|
||||||
|
|
||||||
def results
|
|
||||||
@results ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def successes
|
|
||||||
results.find_all(&:success?)
|
|
||||||
end
|
|
||||||
|
|
||||||
def failures
|
|
||||||
results.reject(&:success?)
|
|
||||||
end
|
|
||||||
|
|
||||||
def success?
|
|
||||||
@results.all?(&:success?)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
require 'forwardable'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
class InstrumentedFilesList
|
|
||||||
extend Forwardable
|
|
||||||
|
|
||||||
def_delegators :@files_list, :empty?
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@files_list = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(pattern)
|
|
||||||
@files_list << pattern
|
|
||||||
end
|
|
||||||
|
|
||||||
def include?(filename)
|
|
||||||
@files_list.any? { |pattern| filename[pattern] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
class Rack
|
|
||||||
attr_accessor :runner, :sprockets
|
|
||||||
|
|
||||||
def initialize(runner, sprockets)
|
|
||||||
@runner, @sprockets = runner, sprockets
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
|
||||||
dup._call(env)
|
|
||||||
end
|
|
||||||
|
|
||||||
def _call(env)
|
|
||||||
if sprockets_file = env['PATH_INFO'][%r{/__F__(.*)$}, 1]
|
|
||||||
result = sprockets.call(env.merge('QUERY_STRING' => 'body=1', 'PATH_INFO' => sprockets_file))
|
|
||||||
result[1]['Cache-Control'] = 'max-age: 0; must-revalidate; no-store'
|
|
||||||
result[1].delete('ETag')
|
|
||||||
|
|
||||||
result
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
template = runner.template
|
|
||||||
|
|
||||||
[ 200, { 'Content-type' => 'text/html' }, [ template ] ]
|
|
||||||
rescue => e
|
|
||||||
$stderr.puts
|
|
||||||
$stderr.puts e.message
|
|
||||||
$stderr.puts e.backtrace.join("\n")
|
|
||||||
$stderr.puts
|
|
||||||
|
|
||||||
[ 500, {}, [] ]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
require 'rails/engine'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module Rails
|
|
||||||
class Engine < ::Rails::Engine
|
|
||||||
rake_tasks do
|
|
||||||
require 'flowerbox/task'
|
|
||||||
|
|
||||||
Flowerbox::Task.create("flowerbox:spec", :dir => "spec/javascripts")
|
|
||||||
Flowerbox::Task.create("flowerbox:integration", :dir => "js-features")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
module Reporter
|
|
||||||
extend Flowerbox::CoreExt::Module
|
|
||||||
|
|
||||||
require 'flowerbox/reporter/file_display'
|
|
||||||
|
|
||||||
def self.for(env)
|
|
||||||
require "flowerbox/reporter/#{env}"
|
|
||||||
|
|
||||||
find_constant(env).new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
|||||||
require 'forwardable'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module Reporter
|
|
||||||
class Base
|
|
||||||
extend Forwardable
|
|
||||||
|
|
||||||
def_delegators :$stdout, :puts, :print
|
|
||||||
|
|
||||||
attr_accessor :options
|
|
||||||
|
|
||||||
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 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 report_progress(result); end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def post_report_failed(data) ; end
|
|
||||||
|
|
||||||
def report_numeric_results(gathered_results, data = {}) ; end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
require 'flowerbox/reporter/base'
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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)
|
|
||||||
puts failure.filtered_stack.join("\n").foreground(:red) if failure.exception?
|
|
||||||
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
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
|||||||
require 'flowerbox/reporter/console_base'
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
|||||||
require 'flowerbox/reporter/base'
|
|
||||||
|
|
||||||
module Flowerbox::Reporter
|
|
||||||
class StepWriter < Base
|
|
||||||
def report_undefined(result)
|
|
||||||
target = File.join(options[:target], result.step_type.downcase, result.original_name.strip.downcase.gsub(%r{[^\w]+}, '_')) + '.js'
|
|
||||||
|
|
||||||
if result.test_environment.primarily_coffeescript?
|
|
||||||
target << '.coffee'
|
|
||||||
end
|
|
||||||
|
|
||||||
if !File.file?(target)
|
|
||||||
self.puts "Writing #{target}..."
|
|
||||||
|
|
||||||
File.open(target, 'wb') { |fh| fh.print result.test_environment.obtain_test_definition_for(result) }
|
|
||||||
|
|
||||||
post_report_data[:undefined] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def post_report_undefined(data)
|
|
||||||
if post_report_data[:undefined] && options[:on_finish]
|
|
||||||
options[:on_finish].call(options[:target])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
|||||||
require 'flowerbox/reporter/console_base'
|
|
||||||
|
|
||||||
module Flowerbox::Reporter
|
|
||||||
class Verbose < ConsoleBase
|
|
||||||
def initialize
|
|
||||||
@name_stack = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def indent(text, count)
|
|
||||||
" " * count + text
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_progress(result)
|
|
||||||
if result.name
|
|
||||||
result.name.each_with_index do |name, index|
|
|
||||||
if @name_stack[index] != name
|
|
||||||
@name_stack = []
|
|
||||||
|
|
||||||
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
|
|
||||||
else
|
|
||||||
puts result.data
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
|||||||
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)
|
|
||||||
on_each_reporter(:start, message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def log(message)
|
|
||||||
on_each_reporter(:log, message)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def on_each_reporter(method, message)
|
|
||||||
@reporters.each { |reporter| reporter.send(method, message) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
module Result
|
|
||||||
extend Flowerbox::CoreExt::Module
|
|
||||||
|
|
||||||
module Cucumber
|
|
||||||
extend Flowerbox::CoreExt::Module
|
|
||||||
end
|
|
||||||
|
|
||||||
module Jasmine
|
|
||||||
extend Flowerbox::CoreExt::Module
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'flowerbox/result/base'
|
|
||||||
require 'flowerbox/result/exception'
|
|
||||||
require 'flowerbox/result/failure'
|
|
||||||
require 'flowerbox/result/pending'
|
|
||||||
require 'flowerbox/result/failure_message'
|
|
||||||
require '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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
|||||||
require 'forwardable'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module Result
|
|
||||||
class Base
|
|
||||||
require 'flowerbox/result/file_info'
|
|
||||||
include Flowerbox::Result::FileInfo
|
|
||||||
|
|
||||||
extend Forwardable
|
|
||||||
|
|
||||||
attr_reader :data
|
|
||||||
|
|
||||||
def_delegators :data, :[]
|
|
||||||
|
|
||||||
def type
|
|
||||||
@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)
|
|
||||||
self.name.length <=> other.name.length
|
|
||||||
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
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
module Cucumber
|
|
||||||
class Failure < Flowerbox::Result::Failure
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
module Cucumber
|
|
||||||
class Pending < Flowerbox::Result::Pending
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
module Cucumber
|
|
||||||
class Skipped < Flowerbox::Result::Pending
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
module Cucumber
|
|
||||||
class Success < Flowerbox::Result::Base
|
|
||||||
def success?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
class Exception < Base
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
|||||||
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
|
|
||||||
file = first_local_stack
|
|
||||||
|
|
||||||
if file['__F__']
|
|
||||||
file[%r{^.*__F__/([^:]+:\d+)}, 1]
|
|
||||||
else
|
|
||||||
file[%r{\(([^:]+\:\d+)}, 1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def runner
|
|
||||||
@data['runner']
|
|
||||||
end
|
|
||||||
|
|
||||||
def runners
|
|
||||||
@runners ||= [ runner ]
|
|
||||||
end
|
|
||||||
|
|
||||||
def stack
|
|
||||||
@data['stack'] || []
|
|
||||||
end
|
|
||||||
|
|
||||||
def filtered_stack
|
|
||||||
filtered_stack = stack.reject { |line| Flowerbox.backtrace_filter.any? { |filter| line[filter] } }
|
|
||||||
|
|
||||||
filtered_stack.shift if exception?
|
|
||||||
|
|
||||||
filter_coffeescript_lines(filtered_stack)
|
|
||||||
end
|
|
||||||
|
|
||||||
def first_local_stack
|
|
||||||
@first_local_stack ||= (stack[1..-1] || []).find do |line|
|
|
||||||
!system_files.any? { |file| line[%r{\(#{file}}] }
|
|
||||||
end || stack[1] || ''
|
|
||||||
end
|
|
||||||
|
|
||||||
def exception?
|
|
||||||
(stack[0] || '')[%r{^.+Error: }]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
|||||||
module Flowerbox::Result::FileInfo
|
|
||||||
UNKNOWN = '__unknown__'
|
|
||||||
|
|
||||||
def translated_file
|
|
||||||
return @translated_file if @translated_file
|
|
||||||
|
|
||||||
if filename == UNKNOWN
|
|
||||||
@translated_file = UNKNOWN
|
|
||||||
else
|
|
||||||
@translated_file = Flowerbox.test_environment.actual_path_for(filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
@translated_file
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_translated?
|
|
||||||
translated_file[%r{\.coffee$}]
|
|
||||||
end
|
|
||||||
|
|
||||||
def filename
|
|
||||||
file.to_s.split(":").first || UNKNOWN
|
|
||||||
end
|
|
||||||
|
|
||||||
def line_number
|
|
||||||
return @line_number if @line_number
|
|
||||||
|
|
||||||
@line_number = file.to_s.split(":")[1]
|
|
||||||
@line_number = "~#{coffeescript_line_to_js_line(@line_number)}" if file_translated?
|
|
||||||
@line_number ||= "0"
|
|
||||||
end
|
|
||||||
|
|
||||||
alias :line :line_number
|
|
||||||
|
|
||||||
def translated_file_and_line
|
|
||||||
"#{translated_file.gsub(%r{^#{Dir.pwd}/}, '')}:#{line_number}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def system_files
|
|
||||||
Flowerbox.test_environment.system_files
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_coffeescript_lines(lines)
|
|
||||||
[ lines ].flatten.collect { |line|
|
|
||||||
line.gsub(%r{\.coffee:(\d+)}) { |_| ".coffee:~#{coffeescript_line_to_js_line($1)}" }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def coffeescript_line_to_js_line(number)
|
|
||||||
(number.to_i * 0.67 + 1).to_i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
module Jasmine
|
|
||||||
class Failure < Flowerbox::Result::Failure
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
module Jasmine
|
|
||||||
class Success < Flowerbox::Result::Base
|
|
||||||
def success? ; true ; end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
module Flowerbox::Result
|
|
||||||
class Pending < Base
|
|
||||||
def pending?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def failure?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def failures; [] ; end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
class ResultSet
|
|
||||||
attr_reader :results, :options
|
|
||||||
|
|
||||||
attr_accessor :time
|
|
||||||
|
|
||||||
def self.from_results(results, options)
|
|
||||||
results = results.collect do |result|
|
|
||||||
result['runner'] = options[:runner]
|
|
||||||
|
|
||||||
if name = result['name']
|
|
||||||
Flowerbox::Result.for(result['source'], result['status']).new(result)
|
|
||||||
else
|
|
||||||
Flowerbox::Result::Exception.new(result)
|
|
||||||
end
|
|
||||||
end.flatten
|
|
||||||
|
|
||||||
new(results, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.for(runner)
|
|
||||||
new(results, :runner => runner)
|
|
||||||
end
|
|
||||||
|
|
||||||
def reporters
|
|
||||||
Flowerbox.reporters
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(results = [], options = {})
|
|
||||||
@results, @options = results, options
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(other)
|
|
||||||
other.results.each do |other_result|
|
|
||||||
if existing_result = results.find { |result| result == other_result }
|
|
||||||
existing_result << other_result
|
|
||||||
else
|
|
||||||
results << other_result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def exitstatus
|
|
||||||
if results.any?(&:failure?)
|
|
||||||
1
|
|
||||||
elsif results.any?(&:pending?)
|
|
||||||
2
|
|
||||||
else
|
|
||||||
0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def print(data = {})
|
|
||||||
reporters.each { |reporter| reporter.report(flattened_gathered_results, data) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_tests
|
|
||||||
results.length
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_failures
|
|
||||||
results.reject(&:success?).length
|
|
||||||
end
|
|
||||||
|
|
||||||
def print_progress
|
|
||||||
@results.each do |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
|
|
||||||
|
|
||||||
def gathered_results
|
|
||||||
return @gathered_results if @gathered_results
|
|
||||||
|
|
||||||
@gathered_results = []
|
|
||||||
|
|
||||||
results.each do |result|
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
@gathered_results
|
|
||||||
end
|
|
||||||
|
|
||||||
def flattened_gathered_results
|
|
||||||
gathered_results.collect(&:results).flatten
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
|||||||
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.run = self
|
|
||||||
Flowerbox.test_environment.set_additional_options(options[:env_options])
|
|
||||||
end
|
|
||||||
|
|
||||||
def sprockets
|
|
||||||
require 'flowerbox/sprockets_handler'
|
|
||||||
|
|
||||||
Flowerbox::SprocketsHandler.new(:asset_paths => asset_paths)
|
|
||||||
end
|
|
||||||
|
|
||||||
def asset_paths
|
|
||||||
[
|
|
||||||
Flowerbox.path.join("lib/assets/javascripts"),
|
|
||||||
Flowerbox.path.join("vendor/assets/javascripts"),
|
|
||||||
@dir,
|
|
||||||
Flowerbox.asset_paths
|
|
||||||
].flatten
|
|
||||||
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
|
|
||||||
|
|
||||||
def system_files
|
|
||||||
%w{flowerbox json2}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
|||||||
module Flowerbox::Run
|
|
||||||
class Debug < Base
|
|
||||||
def execute
|
|
||||||
prep!
|
|
||||||
|
|
||||||
env = Flowerbox.runner_environment.first
|
|
||||||
env.setup(sprockets, spec_files, options)
|
|
||||||
|
|
||||||
Flowerbox.reporters.clear!
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
|||||||
module Flowerbox::Run
|
|
||||||
class Test < Base
|
|
||||||
require 'flowerbox/runner/base'
|
|
||||||
|
|
||||||
def execute
|
|
||||||
prep!
|
|
||||||
|
|
||||||
result_set = Flowerbox::ResultSet.new
|
|
||||||
|
|
||||||
time = 0
|
|
||||||
realtime = Time.now.to_i
|
|
||||||
failed = false
|
|
||||||
|
|
||||||
runner_envs = Flowerbox.runner_environment.collect do |env|
|
|
||||||
env.ensure_configured!
|
|
||||||
|
|
||||||
result_set << env.run(sprockets, spec_files, options)
|
|
||||||
failed = true if !env.started?
|
|
||||||
|
|
||||||
time += env.time
|
|
||||||
|
|
||||||
env
|
|
||||||
end
|
|
||||||
|
|
||||||
result_set.print(:time => time, :realtime => Time.now.to_i - realtime)
|
|
||||||
|
|
||||||
runner_envs.each(&:cleanup)
|
|
||||||
|
|
||||||
failed ? 1 : result_set.exitstatus
|
|
||||||
rescue Flowerbox::Runner::Base::RunnerDiedError
|
|
||||||
255
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
module Runner
|
|
||||||
extend Flowerbox::CoreExt::Module
|
|
||||||
|
|
||||||
def self.find_constant(string)
|
|
||||||
require "flowerbox/runner/#{string}"
|
|
||||||
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,247 +0,0 @@
|
|||||||
require 'thread'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module Runner
|
|
||||||
class Base
|
|
||||||
attr_reader :sprockets, :spec_files, :options, :time
|
|
||||||
|
|
||||||
attr_accessor :results
|
|
||||||
|
|
||||||
MAX_COUNT = 50
|
|
||||||
|
|
||||||
class RunnerDiedError < StandardError ; end
|
|
||||||
|
|
||||||
def self.mutex
|
|
||||||
@mutex ||= Mutex.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@results = ResultSet.new
|
|
||||||
@started = false
|
|
||||||
end
|
|
||||||
|
|
||||||
def started?
|
|
||||||
@started
|
|
||||||
end
|
|
||||||
|
|
||||||
def starting(*args)
|
|
||||||
@started = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def reporters
|
|
||||||
Flowerbox.reporters
|
|
||||||
end
|
|
||||||
|
|
||||||
def load(file)
|
|
||||||
sprockets.find_asset(file.first, :bundle => false).body
|
|
||||||
end
|
|
||||||
|
|
||||||
def instrument(data)
|
|
||||||
results = []
|
|
||||||
|
|
||||||
data.flatten.first.each do |filename, lines|
|
|
||||||
results += lines.reject { |line| line == nil }
|
|
||||||
end
|
|
||||||
|
|
||||||
visited = results.find_all { |result| result == 1 }.length
|
|
||||||
|
|
||||||
Flowerbox.notify "Coverage: %d/%d lines (%0.2f%% of instrumented code)" % [ visited, results.length, (visited.to_f / results.length.to_f) * 100 ]
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_alive
|
|
||||||
while @count < MAX_COUNT && !finished?
|
|
||||||
@count += 1 if @timer_running
|
|
||||||
sleep 0.1
|
|
||||||
end
|
|
||||||
|
|
||||||
if !finished?
|
|
||||||
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
|
|
||||||
|
|
||||||
raise RunnerDiedError.new
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup(sprockets, spec_files, options)
|
|
||||||
@sprockets, @spec_files, @options = sprockets, spec_files, options
|
|
||||||
|
|
||||||
Flowerbox.test_environment.runner = self
|
|
||||||
Flowerbox.test_environment.inject_into(sprockets)
|
|
||||||
Flowerbox.additional_files.each { |file| sprockets.add(file) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(*args)
|
|
||||||
self.class.mutex.synchronize do
|
|
||||||
setup(*args)
|
|
||||||
|
|
||||||
@count = 0
|
|
||||||
@timer_running = true
|
|
||||||
|
|
||||||
reporters.start("Flowerbox running your #{Flowerbox.test_environment.name} tests on #{console_name}...")
|
|
||||||
|
|
||||||
attempts = 3
|
|
||||||
|
|
||||||
require 'flowerbox/server'
|
|
||||||
|
|
||||||
while true
|
|
||||||
begin
|
|
||||||
server.start
|
|
||||||
|
|
||||||
break
|
|
||||||
rescue Flowerbox::Server::ServerDiedError
|
|
||||||
attempts -= 1
|
|
||||||
raise RunnerDiedError.new if attempts == 0
|
|
||||||
|
|
||||||
Flowerbox.server = nil
|
|
||||||
ensure
|
|
||||||
@sprockets.reset!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
server.stop
|
|
||||||
end
|
|
||||||
|
|
||||||
@results
|
|
||||||
rescue => e
|
|
||||||
case e
|
|
||||||
when ExecJS::RuntimeError, ExecJS::ProgramError, Sprockets::FileNotFound, Sprockets::CircularDependencyError
|
|
||||||
handle_coffeescript_compilation_error(e)
|
|
||||||
else
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def configured?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def configure ; end
|
|
||||||
|
|
||||||
def pause_timer(*args)
|
|
||||||
@timer_running = false
|
|
||||||
@count = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def unpause_timer(*args)
|
|
||||||
@timer_running = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def debug?
|
|
||||||
options[:debug] == true
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_configured!
|
|
||||||
if !configured?
|
|
||||||
Flowerbox.notify "#{console_name} is not configured for this project, configuring now..."
|
|
||||||
configure
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
self.class.name.to_s.split('::').last.downcase.to_sym
|
|
||||||
end
|
|
||||||
|
|
||||||
def start_test_environment
|
|
||||||
Flowerbox.test_environment.start
|
|
||||||
rescue ExecJS::ProgramError => e
|
|
||||||
handle_coffeescript_compilation_error(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
def time=(time)
|
|
||||||
@results.time = time
|
|
||||||
end
|
|
||||||
|
|
||||||
def server
|
|
||||||
require 'flowerbox/rack'
|
|
||||||
|
|
||||||
if Flowerbox.server
|
|
||||||
Flowerbox.server.runner = self
|
|
||||||
Flowerbox.server.app.runner = self
|
|
||||||
Flowerbox.server.app.sprockets = sprockets
|
|
||||||
return Flowerbox.server
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'flowerbox/server'
|
|
||||||
|
|
||||||
server_options = { :app => Flowerbox::Rack.new(self, sprockets) }
|
|
||||||
server_options[:logging] = true if options[:verbose_server]
|
|
||||||
server_options[:port] = Flowerbox.port
|
|
||||||
|
|
||||||
Flowerbox.server = Flowerbox::Server.new(self, server_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def log(message)
|
|
||||||
reporters.log(message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def tests
|
|
||||||
@tests ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def start_test(new_tests)
|
|
||||||
tests << new_tests
|
|
||||||
|
|
||||||
Flowerbox.notify(new_tests.flatten) if options[:verbose_server]
|
|
||||||
|
|
||||||
@count = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def failures
|
|
||||||
@failures ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def finish_test(test_results)
|
|
||||||
results = result_set_from_test_results(test_results)
|
|
||||||
|
|
||||||
results.print_progress
|
|
||||||
|
|
||||||
@count = 0
|
|
||||||
|
|
||||||
@results << results
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_count
|
|
||||||
@tests.length
|
|
||||||
end
|
|
||||||
|
|
||||||
def failure_count
|
|
||||||
@failures.length
|
|
||||||
end
|
|
||||||
|
|
||||||
def time
|
|
||||||
@time ||= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def results(time)
|
|
||||||
@time = time.first
|
|
||||||
|
|
||||||
@finished = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def finished?
|
|
||||||
@finished
|
|
||||||
end
|
|
||||||
|
|
||||||
def ping
|
|
||||||
@count = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_coffeescript_compilation_error(exception)
|
|
||||||
Flowerbox.notify(exception.message.foreground(:red))
|
|
||||||
@finished = true
|
|
||||||
|
|
||||||
raise RunnerDiedError.new(exception.message)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def result_set_from_test_results(test_results)
|
|
||||||
ResultSet.from_results(test_results, options.merge(:runner => name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
require 'flowerbox/runner/selenium'
|
|
||||||
|
|
||||||
class Flowerbox::Runner::Chrome < Flowerbox::Runner::Selenium
|
|
||||||
|
|
||||||
def name
|
|
||||||
"Chrome"
|
|
||||||
end
|
|
||||||
|
|
||||||
def console_name
|
|
||||||
"C".foreground('#4f97d1') +
|
|
||||||
"h".foreground('#ec5244') +
|
|
||||||
"r".foreground('#fdd901') +
|
|
||||||
"o".foreground('#4f97d1') +
|
|
||||||
"m".foreground('#5cb15b') +
|
|
||||||
"e".foreground('#ec5244')
|
|
||||||
end
|
|
||||||
|
|
||||||
def browser
|
|
||||||
Flowerbox.browsers[:chrome] ||= ::Selenium::WebDriver.for(:chrome)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
require 'flowerbox/runner/selenium'
|
|
||||||
|
|
||||||
class Flowerbox::Runner::Firefox < Flowerbox::Runner::Selenium
|
|
||||||
def name
|
|
||||||
"Firefox"
|
|
||||||
end
|
|
||||||
|
|
||||||
def console_name
|
|
||||||
"Firefox".foreground('#d0450b')
|
|
||||||
end
|
|
||||||
|
|
||||||
def browser
|
|
||||||
Flowerbox.browsers[:firefox] ||= ::Selenium::WebDriver.for(:firefox)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
|||||||
require 'tempfile'
|
|
||||||
require 'json'
|
|
||||||
require 'flowerbox/runner/base'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module Runner
|
|
||||||
class Node < Base
|
|
||||||
def name
|
|
||||||
"node.js"
|
|
||||||
end
|
|
||||||
|
|
||||||
def console_name
|
|
||||||
"n".foreground(:white) +
|
|
||||||
"o".foreground('#8cc84b') +
|
|
||||||
"de".foreground(:white) +
|
|
||||||
".js".foreground('#8cc84b')
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
:node
|
|
||||||
end
|
|
||||||
|
|
||||||
def configured?
|
|
||||||
File.directory?(File.join(Dir.pwd, 'node_modules/jsdom')) &&
|
|
||||||
File.directory?(File.join(Dir.pwd, 'node_modules/ws'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def cleanup ; end
|
|
||||||
|
|
||||||
def configure
|
|
||||||
Flowerbox.notify %x{bash -c "mkdir -p node_modules && npm link jsdom && npm link ws"}
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(sprockets, spec_files, options)
|
|
||||||
super do
|
|
||||||
begin
|
|
||||||
file = File.join(Dir.pwd, ".node-tmp.#{Time.now.to_i}.js")
|
|
||||||
File.open(file, 'wb') { |fh| fh.print template }
|
|
||||||
|
|
||||||
system %{node #{file}}
|
|
||||||
|
|
||||||
if $?.exitstatus == 0
|
|
||||||
count = 20
|
|
||||||
while !finished? && count > 0
|
|
||||||
sleep 0.1
|
|
||||||
|
|
||||||
count -= 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
File.unlink(file) if file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def template
|
|
||||||
env = start_test_environment
|
|
||||||
|
|
||||||
<<-JS
|
|
||||||
// whoa node
|
|
||||||
var fs = require('fs'),
|
|
||||||
vm = require('vm'),
|
|
||||||
http = require('http'),
|
|
||||||
jsdom = require('jsdom'),
|
|
||||||
ws = require('ws')
|
|
||||||
|
|
||||||
// expand the sandbox a bit
|
|
||||||
var context = function() {};
|
|
||||||
for (method in global) { context[method] = global[method]; }
|
|
||||||
|
|
||||||
jsdom.env(
|
|
||||||
"<html><head><title></title></head><body></body></html>", [], function(errors, window) {
|
|
||||||
context.window = window;
|
|
||||||
context.WebSocket = ws;
|
|
||||||
|
|
||||||
var gotFlowerbox = false;
|
|
||||||
|
|
||||||
console._log = console.log;
|
|
||||||
|
|
||||||
var socket = new ws('ws://localhost:#{server.port + 1}/');
|
|
||||||
socket.onopen = function() {
|
|
||||||
var files = #{sprockets.files.to_json};
|
|
||||||
|
|
||||||
var fileLoader = function() {
|
|
||||||
if (files.length > 0) {
|
|
||||||
var file = files.shift();
|
|
||||||
|
|
||||||
socket.onmessage = function(data) {
|
|
||||||
vm.runInNewContext(data.data, context, file);
|
|
||||||
|
|
||||||
for (thing in window) {
|
|
||||||
if (!context[thing]) { context[thing] = window[thing] }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gotFlowerbox && context.Flowerbox) {
|
|
||||||
context.Flowerbox.environment = 'node';
|
|
||||||
context.Flowerbox.UNKNOWN = '#{Flowerbox::Result::FileInfo::UNKNOWN}';
|
|
||||||
context.Flowerbox.socket = socket;
|
|
||||||
|
|
||||||
gotFlowerbox = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileLoader();
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.send(JSON.stringify(['load', file]));
|
|
||||||
} else {
|
|
||||||
socket.onmessage = null;
|
|
||||||
|
|
||||||
#{env}
|
|
||||||
|
|
||||||
var waitForFinish;
|
|
||||||
waitForFinish = function() {
|
|
||||||
if (!context.Flowerbox.started || !context.Flowerbox.done) {
|
|
||||||
process.nextTick(waitForFinish);
|
|
||||||
} else {
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
waitForFinish();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fileLoader();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
JS
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
|||||||
require 'selenium-webdriver'
|
|
||||||
require 'flowerbox/runner/base'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module Runner
|
|
||||||
class Selenium < Base
|
|
||||||
def name
|
|
||||||
raise StandardError.new("Override me")
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
:selenium
|
|
||||||
end
|
|
||||||
|
|
||||||
def cleanup
|
|
||||||
end
|
|
||||||
|
|
||||||
def browser
|
|
||||||
raise StandardError.new("Define a browser")
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(sprockets, spec_files, options)
|
|
||||||
super do
|
|
||||||
navigate = Proc.new { browser.navigate.to "http://localhost:#{server.port}/?#{Time.now.to_f}" }
|
|
||||||
|
|
||||||
begin
|
|
||||||
navigate.call
|
|
||||||
rescue ::Selenium::WebDriver::Error::UnknownError => e
|
|
||||||
puts "Browser communication error, reopening all browsers...".foreground(:yellow)
|
|
||||||
Flowerbox.cleanup!
|
|
||||||
|
|
||||||
navigate.call
|
|
||||||
end
|
|
||||||
|
|
||||||
ensure_alive
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def log(msg)
|
|
||||||
puts msg
|
|
||||||
end
|
|
||||||
|
|
||||||
def page_title
|
|
||||||
"Flowerbox - #{Flowerbox.test_environment.name} Runner"
|
|
||||||
end
|
|
||||||
|
|
||||||
def template
|
|
||||||
env = start_test_environment
|
|
||||||
|
|
||||||
<<-HTML
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>#{page_title}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>#{page_title}</h1>
|
|
||||||
<pre id="queue"></pre>
|
|
||||||
<script type="text/javascript">
|
|
||||||
console._log = console.log;
|
|
||||||
|
|
||||||
console.log = function(msg) {
|
|
||||||
console._log(msg);
|
|
||||||
Flowerbox.contact("log", msg);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
#{template_files.join("\n")}
|
|
||||||
<script type="text/javascript">
|
|
||||||
Flowerbox.environment = '#{name}';
|
|
||||||
Flowerbox.UNKNOWN = '#{Flowerbox::Result::FileInfo::UNKNOWN}';
|
|
||||||
|
|
||||||
var context = this;
|
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', function() {
|
|
||||||
Flowerbox.socket = new WebSocket('ws://localhost:#{server.port + 1}/');
|
|
||||||
Flowerbox.socket.onopen = function() {
|
|
||||||
#{env}
|
|
||||||
};
|
|
||||||
}, false);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
HTML
|
|
||||||
end
|
|
||||||
|
|
||||||
def template_files
|
|
||||||
sprockets.files.collect { |file| %{<script type="text/javascript" src="/__F__/#{sprockets.logical_path_for(file)}?#{Time.now.to_f}"></script>} }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
|||||||
require 'rack'
|
|
||||||
require 'net/http'
|
|
||||||
require 'socket'
|
|
||||||
require 'rack/builder'
|
|
||||||
require 'thin'
|
|
||||||
require 'em-websocket'
|
|
||||||
require 'json'
|
|
||||||
require 'forwardable'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
class Server
|
|
||||||
attr_reader :options
|
|
||||||
attr_accessor :runner
|
|
||||||
|
|
||||||
class MissingRackApp < StandardError ; end
|
|
||||||
class ServerDiedError < StandardError ; end
|
|
||||||
|
|
||||||
extend Forwardable
|
|
||||||
|
|
||||||
def_delegators :@server_thread, :alive?
|
|
||||||
|
|
||||||
def initialize(runner, options = {})
|
|
||||||
@runner = runner
|
|
||||||
@options = { :logging => false }.merge(options || {})
|
|
||||||
end
|
|
||||||
|
|
||||||
def app
|
|
||||||
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)
|
|
||||||
|
|
||||||
begin
|
|
||||||
output = runner.send(command, [ data ].flatten)
|
|
||||||
|
|
||||||
if command == 'load'
|
|
||||||
ws.send(output)
|
|
||||||
else
|
|
||||||
ws.send("ok")
|
|
||||||
end
|
|
||||||
rescue => e
|
|
||||||
$stderr.puts e.message
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def start
|
|
||||||
@server_thread = Thread.new do
|
|
||||||
begin
|
|
||||||
server_options = { :Port => port, :Host => interface }
|
|
||||||
|
|
||||||
EventMachine.run do
|
|
||||||
EventMachine::WebSocket.start(:host => interface, :port => port + 1, &method(:websocket_app))
|
|
||||||
|
|
||||||
::Rack::Handler::Thin.run(rack_app, server_options) do |server|
|
|
||||||
Thread.current[:server] = server
|
|
||||||
|
|
||||||
trap('QUIT') { server.stop }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue => e
|
|
||||||
@server_thread[:exception] = e
|
|
||||||
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
while !@server_thread[:server] && alive?
|
|
||||||
sleep 0.1
|
|
||||||
end
|
|
||||||
|
|
||||||
if @server_thread[:exception]
|
|
||||||
raise @server_thread[:exception]
|
|
||||||
else
|
|
||||||
raise ServerDiedError.new if !alive?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def stop
|
|
||||||
if @server_thread
|
|
||||||
@server_thread[:server].stop rescue nil
|
|
||||||
EventMachine.stop_event_loop
|
|
||||||
@server_thread.kill
|
|
||||||
|
|
||||||
wait_for_server_to_stop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def interface
|
|
||||||
options[:interface] || '0.0.0.0'
|
|
||||||
end
|
|
||||||
|
|
||||||
def port
|
|
||||||
return @port if @port
|
|
||||||
|
|
||||||
if options[:port]
|
|
||||||
return @port = options[:port]
|
|
||||||
end
|
|
||||||
|
|
||||||
attempts = 20
|
|
||||||
|
|
||||||
begin
|
|
||||||
try_server_to_something(nil, Proc.new { |port| @port = port }, (random_port / 2).floor * 2)
|
|
||||||
|
|
||||||
attempts -= 1
|
|
||||||
end while !@port and attempts != 0
|
|
||||||
|
|
||||||
raise StandardError.new("can't start server") if attempts == 0
|
|
||||||
|
|
||||||
@port
|
|
||||||
end
|
|
||||||
|
|
||||||
def address
|
|
||||||
@address ||= "http://#{interface}:#{port}/"
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def wait_for_server_to_start
|
|
||||||
started = false
|
|
||||||
|
|
||||||
while !started do
|
|
||||||
try_server_to_something(Proc.new { started = true })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_for_server_to_stop
|
|
||||||
while alive? do
|
|
||||||
try_server_to_something(nil, Proc.new { return })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def try_server_to_something(success, failure = nil, current_port = port)
|
|
||||||
begin
|
|
||||||
connect_interface = '127.0.0.1' if interface == '0.0.0.0'
|
|
||||||
|
|
||||||
socket = TCPSocket.new(connect_interface, current_port)
|
|
||||||
socket.close
|
|
||||||
|
|
||||||
success.call(current_port) if success
|
|
||||||
rescue => e
|
|
||||||
case e
|
|
||||||
when Errno::ECONNREFUSED, Errno::ECONNRESET
|
|
||||||
failure.call(current_port) if failure
|
|
||||||
else
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
sleep 0.1
|
|
||||||
end
|
|
||||||
|
|
||||||
def random_port
|
|
||||||
25000 + Kernel.rand(1000)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
|||||||
require 'sprockets'
|
|
||||||
require 'sprockets/engines'
|
|
||||||
require 'forwardable'
|
|
||||||
require 'sprockets-vendor_gems'
|
|
||||||
require 'fileutils'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
class SprocketsHandler
|
|
||||||
extend Forwardable
|
|
||||||
|
|
||||||
class LogicalPathNotFoundError < StandardError ; end
|
|
||||||
|
|
||||||
attr_reader :files, :options
|
|
||||||
|
|
||||||
def_delegators :environment, :append_path, :register_engine, :[], :call, :find_asset, :paths
|
|
||||||
|
|
||||||
def self.gem_asset_paths
|
|
||||||
@gem_asset_paths ||= Sprockets.find_gem_vendor_paths
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(options)
|
|
||||||
@options = options
|
|
||||||
|
|
||||||
require 'flowerbox/unique_asset_list'
|
|
||||||
|
|
||||||
@files = Flowerbox::UniqueAssetList.new(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def add(asset)
|
|
||||||
assets_for(asset).each { |dependent_asset| files.add(dependent_asset) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def assets_for(asset)
|
|
||||||
find_asset(asset, :bundle => true).to_a
|
|
||||||
end
|
|
||||||
|
|
||||||
def environment
|
|
||||||
return @environment if @environment
|
|
||||||
|
|
||||||
@environment = Sprockets::Environment.new
|
|
||||||
|
|
||||||
if Flowerbox.instrument_js?
|
|
||||||
require 'flowerbox/tilt/instrument_js'
|
|
||||||
|
|
||||||
@environment.register_postprocessor 'application/javascript', Flowerbox::Tilt::InstrumentJS
|
|
||||||
end
|
|
||||||
|
|
||||||
default_asset_paths.each { |path| @environment.append_path(path) }
|
|
||||||
|
|
||||||
@environment
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
paths.each do |path|
|
|
||||||
if result = asset_path[%r{^#{path}/(.*)}, 1]
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
raise LogicalPathNotFoundError.new("Could not find logical path for #{asset_path}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def reset!
|
|
||||||
@environment.send(:expire_index!)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
class Success < BaseResult
|
|
||||||
def print_progress
|
|
||||||
print ".".foreground(:green)
|
|
||||||
end
|
|
||||||
|
|
||||||
def success?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
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
|
|
||||||
Flowerbox.cleanup!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
module TestEnvironment
|
|
||||||
extend Flowerbox::CoreExt::Module
|
|
||||||
|
|
||||||
def self.for(env)
|
|
||||||
require "flowerbox/test_environment/#{env}"
|
|
||||||
|
|
||||||
find_constant(env).new
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.transplantable_environments
|
|
||||||
Dir[File.expand_path('../test_environment/*.rb', __FILE__)].each do |file|
|
|
||||||
require file
|
|
||||||
end
|
|
||||||
|
|
||||||
constants.collect { |k| const_get(k) }.find_all(&:transplantable?)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
|||||||
require 'yaml'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module TestEnvironment
|
|
||||||
class Base
|
|
||||||
attr_accessor :runner, :run
|
|
||||||
|
|
||||||
def name
|
|
||||||
self.class.name.split("::").last
|
|
||||||
end
|
|
||||||
|
|
||||||
def reporters
|
|
||||||
@reporters ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.transplantable?
|
|
||||||
respond_to?(:transplant)
|
|
||||||
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
|
|
||||||
|
|
||||||
def inject_into(sprockets)
|
|
||||||
@sprockets = sprockets
|
|
||||||
|
|
||||||
system_files.each { |file| @sprockets.add(file) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def system_files
|
|
||||||
run.system_files + global_system_files + runner_system_files
|
|
||||||
end
|
|
||||||
|
|
||||||
def start
|
|
||||||
runner.spec_files.each { |file| @sprockets.add(file) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def actual_path_for(file)
|
|
||||||
@sprockets.find_asset(file.gsub(%r{\?.*$}, ''), :bundle => false).pathname.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
|||||||
require 'flowerbox/test_environment/base'
|
|
||||||
|
|
||||||
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)
|
|
||||||
super
|
|
||||||
|
|
||||||
require 'flowerbox/tilt/feature_template'
|
|
||||||
|
|
||||||
@sprockets.register_engine('.feature', Flowerbox::Tilt::FeatureTemplate)
|
|
||||||
end
|
|
||||||
|
|
||||||
def global_system_files
|
|
||||||
%w{cucumber.js flowerbox/cucumber}
|
|
||||||
end
|
|
||||||
|
|
||||||
def runner_system_files
|
|
||||||
[ "flowerbox/cucumber/#{@runner.type}" ]
|
|
||||||
end
|
|
||||||
|
|
||||||
def start
|
|
||||||
super
|
|
||||||
|
|
||||||
<<-JS
|
|
||||||
context.Cucumber = context.require('./cucumber');
|
|
||||||
|
|
||||||
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 = []
|
|
||||||
|
|
||||||
matcher.gsub!(%r{"[^"]+"}) do |_, match|
|
|
||||||
args << "arg#{args.length + 1}"
|
|
||||||
'"([^"]+)"'
|
|
||||||
end
|
|
||||||
|
|
||||||
matcher.gsub!(%r{ \d+ }) do |_, match|
|
|
||||||
args << "arg#{args.length + 1}"
|
|
||||||
" (\d+) "
|
|
||||||
end
|
|
||||||
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if result['hasDataTable']
|
|
||||||
args << "table"
|
|
||||||
messages << "table is a Cucumber AST data table"
|
|
||||||
end
|
|
||||||
|
|
||||||
if result['hasDocString']
|
|
||||||
args << "string"
|
|
||||||
messages << "string is a doc string"
|
|
||||||
end
|
|
||||||
|
|
||||||
args_string = args.join(', ')
|
|
||||||
|
|
||||||
output = []
|
|
||||||
|
|
||||||
if primarily_coffeescript?
|
|
||||||
output << %{Flowerbox.#{result.step_type} /^#{matcher}$/, #{"(#{args_string}) " if !args_string.empty?}->}
|
|
||||||
output += messages.collect { |msg| " # #{msg}" }
|
|
||||||
output << %{ @pending() # add your code here}
|
|
||||||
else
|
|
||||||
output << "Flowerbox.#{result.step_type}(/^#{matcher}$/, function(#{args_string}) {"
|
|
||||||
output += messages.collect { |msg| " // #{msg}" }
|
|
||||||
output << %{ this.pending(); // add your code here}
|
|
||||||
output << "});"
|
|
||||||
end
|
|
||||||
|
|
||||||
output.collect { |line| "#{line}\n" }.join
|
|
||||||
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 }
|
|
||||||
|
|
||||||
coffee_count > js_count
|
|
||||||
end
|
|
||||||
|
|
||||||
def plant_source
|
|
||||||
"skel/cucumber"
|
|
||||||
end
|
|
||||||
|
|
||||||
def plant_target
|
|
||||||
"js-features"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
|||||||
require 'flowerbox/test_environment/base'
|
|
||||||
require 'jasmine-core'
|
|
||||||
require 'yaml'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module TestEnvironment
|
|
||||||
class Jasmine < Base
|
|
||||||
def self.transplant(dir)
|
|
||||||
if File.file?(jasmine_yml = File.join(dir, 'support/jasmine.yml'))
|
|
||||||
puts "Transplanting #{jasmine_yml} into Flowerbox..."
|
|
||||||
|
|
||||||
config = [
|
|
||||||
%{f.test_with :jasmine},
|
|
||||||
%{f.run_with :firefox},
|
|
||||||
%{f.report_with :verbose}
|
|
||||||
]
|
|
||||||
|
|
||||||
YAML.load_file(jasmine_yml).each do |key, value|
|
|
||||||
case key
|
|
||||||
when 'src_dir'
|
|
||||||
[ value ].flatten.each do |dir|
|
|
||||||
config << %{f.asset_paths << "#{dir}"}
|
|
||||||
end
|
|
||||||
when 'src_files'
|
|
||||||
[ value ].flatten.each do |file|
|
|
||||||
config << %{f.additional_files << "#{file}"}
|
|
||||||
end
|
|
||||||
when 'helpers'
|
|
||||||
[ value ].flatten.each do |pattern|
|
|
||||||
Dir[File.join(dir, pattern)].each do |file|
|
|
||||||
config << %{f.additional_files << "#{file.gsub("#{dir}/", '')}"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
when 'spec_files'
|
|
||||||
[ value ].flatten.each do |pattern|
|
|
||||||
config << %{f.spec_patterns << "#{pattern}"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
File.open(target = File.join(dir, 'spec_helper.rb'), 'wb') do |fh|
|
|
||||||
fh.puts "Flowerbox.configure do |f|"
|
|
||||||
config.each do |line|
|
|
||||||
fh.puts " #{line}"
|
|
||||||
end
|
|
||||||
fh.puts "end"
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "#{target} created. Run your tests with:"
|
|
||||||
puts " flowerbox test #{dir}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def inject_into(sprockets)
|
|
||||||
sprockets.append_path(::Jasmine::Core.path)
|
|
||||||
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def global_system_files
|
|
||||||
%w{jasmine.js flowerbox/jasmine.js}
|
|
||||||
end
|
|
||||||
|
|
||||||
def runner_system_files
|
|
||||||
[ "flowerbox/jasmine/#{@runner.type}.js" ]
|
|
||||||
end
|
|
||||||
|
|
||||||
def start
|
|
||||||
super
|
|
||||||
|
|
||||||
<<-JS
|
|
||||||
if (typeof context != 'undefined' && typeof jasmine == 'undefined') {
|
|
||||||
jasmine = context.jasmine;
|
|
||||||
}
|
|
||||||
|
|
||||||
jasmine.getEnv().addReporter(new jasmine.FlowerboxReporter());
|
|
||||||
|
|
||||||
jasmine.getEnv().execute();
|
|
||||||
JS
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
require 'tilt'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module 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
|
|
||||||
end
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
|||||||
require 'tilt'
|
|
||||||
|
|
||||||
module Flowerbox
|
|
||||||
module Tilt
|
|
||||||
class InstrumentJS < ::Tilt::Template
|
|
||||||
def prepare ; end
|
|
||||||
|
|
||||||
def evaluate(scope, locals, &block)
|
|
||||||
if Flowerbox.instrument_files.include?(file)
|
|
||||||
block_comment = false
|
|
||||||
|
|
||||||
lines = data.lines.to_a
|
|
||||||
|
|
||||||
output = [
|
|
||||||
'if (typeof __$instrument == "undefined") { __$instrument = {} }',
|
|
||||||
"__$instrument['#{file}'] = [];",
|
|
||||||
"__$instrument['#{file}'][#{lines.length - 1}] = null;"
|
|
||||||
]
|
|
||||||
|
|
||||||
prior_comma_end = comma_end = false
|
|
||||||
|
|
||||||
lines_instrumented = []
|
|
||||||
|
|
||||||
code_output = []
|
|
||||||
lines.each_with_index do |line, index|
|
|
||||||
line.rstrip!
|
|
||||||
|
|
||||||
instrument = "__$instrument['#{file}'][#{index}] = 1;"
|
|
||||||
|
|
||||||
if line[%r{; *$}]
|
|
||||||
line = line + instrument
|
|
||||||
lines_instrumented << index
|
|
||||||
end
|
|
||||||
|
|
||||||
code_output << line
|
|
||||||
end
|
|
||||||
|
|
||||||
lines_instrumented.each do |line|
|
|
||||||
output << "__$instrument['#{file}'][#{line}] = 0;"
|
|
||||||
end
|
|
||||||
|
|
||||||
(output + code_output).join("\n")
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
class UniqueAssetList < ::Array
|
|
||||||
attr_reader :sprockets
|
|
||||||
|
|
||||||
def initialize(sprockets)
|
|
||||||
super([])
|
|
||||||
|
|
||||||
@sprockets = sprockets
|
|
||||||
|
|
||||||
@included = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def add(files)
|
|
||||||
[ files ].flatten.each do |file|
|
|
||||||
self << file if !included?(file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(file)
|
|
||||||
super(file)
|
|
||||||
|
|
||||||
@included[file.pathname.to_s] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_json
|
|
||||||
collect { |file| sprockets.logical_path_for(file) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def included?(file)
|
|
||||||
@included[file.pathname.to_s]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
module Flowerbox
|
|
||||||
VERSION = "0.0.1"
|
|
||||||
end
|
|
5
sass/ie.scss
Normal file
5
sass/ie.scss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/* Welcome to Compass. Use this file to write IE specific override styles.
|
||||||
|
* Import this file using the following HTML or equivalent:
|
||||||
|
* <!--[if IE]>
|
||||||
|
* <link href="/stylesheets/ie.css" media="screen, projection" rel="stylesheet" type="text/css" />
|
||||||
|
* <![endif]--> */
|
3
sass/print.scss
Normal file
3
sass/print.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/* Welcome to Compass. Use this file to define print styles.
|
||||||
|
* Import this file using the following HTML or equivalent:
|
||||||
|
* <link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css" /> */
|
82
sass/screen.scss
Normal file
82
sass/screen.scss
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/* Welcome to Compass.
|
||||||
|
* In this file you should write your main styles. (or centralize your imports)
|
||||||
|
* Import this file using the following HTML or equivalent:
|
||||||
|
* <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css" /> */
|
||||||
|
|
||||||
|
@import "compass/reset";
|
||||||
|
@import "compass/css3";
|
||||||
|
|
||||||
|
$base_color: #d9b741;
|
||||||
|
$header_color: #000;
|
||||||
|
$header_blend_color: #3a60e5;
|
||||||
|
|
||||||
|
$code_border_color: $header_blend_color;
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: $base_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
width: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.75em;
|
||||||
|
margin: 0.35em 0;
|
||||||
|
color: mix($header_color, $header_blend_color, 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.45em;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
color: mix($header_color, $header_blend_color, 70);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin: 0.2em 0;
|
||||||
|
color: mix($header_color, $header_blend_color, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Gorditas', 'Helvetica Neue', 'Helvetica';
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li {
|
||||||
|
font-family: 'Pontano Sans', 'Helvetica Neue', 'Helvetica';
|
||||||
|
line-height: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
code, pre {
|
||||||
|
font-family: 'Courier New';
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 1em;
|
||||||
|
border: solid $code_border_color 1px;
|
||||||
|
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0.7em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
Feature: My First Feature
|
|
||||||
Scenario: Do Something
|
|
||||||
Given I have a flowerbox
|
|
||||||
When I plant a "cucumber" seed
|
|
||||||
Then I should get the following when I pick:
|
|
||||||
| cucumber |
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
Flowerbox.configure do |f|
|
|
||||||
f.test_with :cucumber
|
|
||||||
f.run_with :firefox
|
|
||||||
|
|
||||||
f.additional_files << "support/env.js.coffee"
|
|
||||||
f.spec_patterns << "features/**/*.feature"
|
|
||||||
|
|
||||||
f.test_environment.prefer_step_language :coffeescript
|
|
||||||
end
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
Flowerbox.Given /^I have a flowerbox$/, ->
|
|
||||||
@flowerbox =
|
|
||||||
plantSeed: (type) ->
|
|
||||||
@types ||= []
|
|
||||||
@types.push(type)
|
|
||||||
pick: ->
|
|
||||||
@types
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
|||||||
Flowerbox.Then /^I should get the following when I pick:$/, (table) ->
|
|
||||||
# table is a Cucumber AST data table
|
|
||||||
data = (row[0] for row in table.raw())
|
|
||||||
@expect(@flowerbox.pick()).toEqual(data)
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
Flowerbox.When /^I plant a "([^"]+)" seed$/, (type) ->
|
|
||||||
@flowerbox.plantSeed(type)
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
#= require_tree ../step_definitions
|
|
@ -1,22 +0,0 @@
|
|||||||
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
|
|
@ -1,131 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
require 'socket'
|
|
||||||
require 'thread'
|
|
||||||
|
|
||||||
require 'flowerbox/server'
|
|
||||||
|
|
||||||
describe Flowerbox::Server do
|
|
||||||
let(:server) { described_class.new(runner, options) }
|
|
||||||
let(:options) { nil }
|
|
||||||
let(:runner) { nil }
|
|
||||||
|
|
||||||
subject { server }
|
|
||||||
|
|
||||||
describe '#initialize' do
|
|
||||||
let(:options) { { :port => port, :interface => interface } }
|
|
||||||
let(:port) { 'port' }
|
|
||||||
let(:interface) { 'interface' }
|
|
||||||
|
|
||||||
its(:port) { should == port }
|
|
||||||
its(:interface) { should == interface }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#app' do
|
|
||||||
subject { server.app }
|
|
||||||
|
|
||||||
context 'no app defined' do
|
|
||||||
before do
|
|
||||||
server.stubs(:options).returns({})
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should raise an error' do
|
|
||||||
expect { server.app }.to raise_error(Flowerbox::Server::MissingRackApp)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'app defined' do
|
|
||||||
let(:app) { 'app' }
|
|
||||||
|
|
||||||
before do
|
|
||||||
server.stubs(:options).returns(:app => app)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { should == app }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#start' do
|
|
||||||
let(:port) { 12345 }
|
|
||||||
let(:interface) { '127.0.0.1' }
|
|
||||||
|
|
||||||
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
|
|
||||||
server.start
|
|
||||||
|
|
||||||
TCPSocket.new(server.interface, server.port)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#interface' do
|
|
||||||
subject { server.interface }
|
|
||||||
|
|
||||||
it { should == '0.0.0.0' }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#port' do
|
|
||||||
let(:interface) { '127.0.0.1' }
|
|
||||||
let(:base) { 25000 }
|
|
||||||
let(:initial) { base + @offset }
|
|
||||||
|
|
||||||
before do
|
|
||||||
server.stubs(:interface).returns(interface)
|
|
||||||
|
|
||||||
@offset = 0
|
|
||||||
ok = true
|
|
||||||
|
|
||||||
begin
|
|
||||||
[ 0, 1 ].each do |index|
|
|
||||||
begin
|
|
||||||
TCPSocket.new(interface, base + @offset + index)
|
|
||||||
@offset += 1
|
|
||||||
ok = false
|
|
||||||
rescue Errno::ECONNREFUSED => e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end while !ok
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { server.port }
|
|
||||||
|
|
||||||
context 'no running service' do
|
|
||||||
before do
|
|
||||||
Kernel.stubs(:rand).returns(@offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { should == initial }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'running service' do
|
|
||||||
before do
|
|
||||||
@server = Thread.new do
|
|
||||||
TCPServer.new(interface, initial)
|
|
||||||
end
|
|
||||||
|
|
||||||
server.stubs(:random_port).returns(initial, initial + 2)
|
|
||||||
|
|
||||||
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 + 2 }
|
|
||||||
|
|
||||||
after do
|
|
||||||
@server.kill
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
require 'flowerbox/sprockets_handler'
|
|
||||||
|
|
||||||
describe Flowerbox::SprocketsHandler do
|
|
||||||
let(:sprockets_handler) { described_class.new(options) }
|
|
||||||
let(:options) { { :asset_paths => asset_paths } }
|
|
||||||
let(:asset_paths) { [ File.expand_path('asset path') ] }
|
|
||||||
|
|
||||||
describe '#add' do
|
|
||||||
let(:asset) { 'asset' }
|
|
||||||
let(:dependent_asset) { 'dependent' }
|
|
||||||
|
|
||||||
let(:pathname_path) { 'pathname path' }
|
|
||||||
let(:files) { stub }
|
|
||||||
|
|
||||||
before do
|
|
||||||
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)
|
|
||||||
end
|
|
||||||
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) }
|
|
||||||
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
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
require 'spec_helper'
|
|
||||||
require 'flowerbox/unique_asset_list'
|
|
||||||
|
|
||||||
describe Flowerbox::UniqueAssetList do
|
|
||||||
let(:unique_asset_list) { described_class.new(sprockets) }
|
|
||||||
let(:sprockets) { stub }
|
|
||||||
|
|
||||||
describe "#add" do
|
|
||||||
let(:first) { stub(:pathname => Pathname.new('one')) }
|
|
||||||
let(:second) { stub(:pathname => Pathname.new('one')) }
|
|
||||||
let(:third) { stub(:pathname => Pathname.new('two')) }
|
|
||||||
|
|
||||||
it 'should not add assets already added' do
|
|
||||||
unique_asset_list.add(first)
|
|
||||||
unique_asset_list.add([ second, third ])
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
Flowerbox.configure do |c|
|
|
||||||
c.test_with :jasmine
|
|
||||||
c.run_with :node
|
|
||||||
|
|
||||||
c.spec_patterns << "*_spec.*"
|
|
||||||
c.spec_patterns << "**/*_spec.*"
|
|
||||||
|
|
||||||
c.test_environment.reporters << "SimpleNodeReporter"
|
|
||||||
end
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
describe("cats", function() {
|
|
||||||
it("should hiss", function() {
|
|
||||||
expect("hiss").toEqual("hiss");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
|||||||
require 'simplecov'
|
|
||||||
SimpleCov.start
|
|
||||||
|
|
||||||
require 'mocha'
|
|
||||||
require 'fakefs/spec_helpers'
|
|
||||||
|
|
||||||
require 'flowerbox'
|
|
||||||
|
|
||||||
RSpec.configure do |c|
|
|
||||||
c.mock_with :mocha
|
|
||||||
end
|
|
5
stylesheets/ie.css
Normal file
5
stylesheets/ie.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/* Welcome to Compass. Use this file to write IE specific override styles.
|
||||||
|
* Import this file using the following HTML or equivalent:
|
||||||
|
* <!--[if IE]>
|
||||||
|
* <link href="/stylesheets/ie.css" media="screen, projection" rel="stylesheet" type="text/css" />
|
||||||
|
* <![endif]--> */
|
3
stylesheets/print.css
Normal file
3
stylesheets/print.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/* Welcome to Compass. Use this file to define print styles.
|
||||||
|
* Import this file using the following HTML or equivalent:
|
||||||
|
* <link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css" /> */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user