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/
|
||||
Gemfile.lock
|
||||
_site/
|
||||
coverage/
|
||||
tmp/
|
||||
pkg/
|
||||
|
15
Gemfile
15
Gemfile
@ -1,7 +1,10 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in flowerbox.gemspec
|
||||
gemspec
|
||||
|
||||
gem 'guard-flowerbox', :path => '../guard-flowerbox'
|
||||
source :rubygems
|
||||
|
||||
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
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
group :rspec do
|
||||
guard 'rspec', :version => 2 do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch('spec/spec_helper.rb') { "spec" }
|
||||
end
|
||||
|
||||
guard 'compass', :configuration_file => 'config/compass.rb' do
|
||||
watch(%r{^sass/(.*)\.s[ac]ss$})
|
||||
end
|
||||
|
||||
group :wip do
|
||||
guard 'cucumber', :cli => '-p wip' do
|
||||
watch(%r{^features/.+\.feature$})
|
||||
watch(%r{^features/support/.+$}) { 'features' }
|
||||
watch(%r{^features/step_definitions/.*$}) { 'features' }
|
||||
end
|
||||
guard 'haml', :input => '_layouts/haml', :output => '_layouts' do
|
||||
watch(/^.+(\.html\.haml)/)
|
||||
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