Repository: jnicklas/evergreen
Branch: master
Commit: ce6d3a99b8d6
Files: 75
Total size: 189.4 KB
Directory structure:
gitextract_us4zmcef/
├── .circleci/
│ └── config.yml
├── .gitignore
├── .gitmodules
├── .tool-versions
├── .travis.yml
├── Gemfile
├── README.rdoc
├── Rakefile
├── bin/
│ └── evergreen
├── config/
│ └── routes.rb
├── evergreen.gemspec
├── example/
│ ├── public/
│ │ └── implementation.js
│ └── spec/
│ └── javascripts/
│ └── foo_spec.js
├── lib/
│ ├── evergreen/
│ │ ├── application.rb
│ │ ├── cli.rb
│ │ ├── helper.rb
│ │ ├── rails.rb
│ │ ├── resources/
│ │ │ ├── evergreen.css
│ │ │ ├── jquery.js
│ │ │ ├── json2.js
│ │ │ ├── list.js
│ │ │ └── run.js
│ │ ├── runner.rb
│ │ ├── server.rb
│ │ ├── spec.rb
│ │ ├── suite.rb
│ │ ├── tasks.rb
│ │ ├── template.rb
│ │ ├── utils/
│ │ │ └── timeout.rb
│ │ ├── version.rb
│ │ └── views/
│ │ ├── _spec_error.erb
│ │ ├── layout.erb
│ │ ├── list.erb
│ │ └── run.erb
│ ├── evergreen.rb
│ └── tasks/
│ └── evergreen.rake
└── spec/
├── evergreen_spec.rb
├── helper_spec.rb
├── meta_spec.rb
├── runner_spec.rb
├── spec_helper.rb
├── spec_spec.rb
├── suite1/
│ ├── public/
│ │ ├── jquery.js
│ │ └── styles.css
│ └── spec/
│ └── javascripts/
│ ├── bar_spec.js
│ ├── coffeescript_spec.coffee
│ ├── failing_spec.js
│ ├── foo_spec.js
│ ├── helpers/
│ │ ├── spec_helper.coffee
│ │ └── spec_helper.js
│ ├── invalid_coffee_spec.coffee
│ ├── libs/
│ │ └── lucid_spec.js
│ ├── models/
│ │ └── game_spec.js
│ ├── slow_spec.coffee
│ ├── templates/
│ │ ├── another_template.html
│ │ ├── escape.html
│ │ ├── one_template.html
│ │ └── script_tags.html
│ ├── templates_spec.js
│ ├── testing_spec.js
│ ├── transactions_spec.js
│ └── with_helper_spec.js
├── suite2/
│ ├── config/
│ │ └── evergreen.rb
│ ├── public_html/
│ │ └── foo.js
│ ├── spec/
│ │ ├── awesome_spec.js
│ │ └── failing_spec.js
│ └── templates/
│ └── foo.html
├── suite3/
│ ├── public/
│ │ └── foo.js
│ ├── spec/
│ │ └── javascripts/
│ │ ├── awesome_spec.js
│ │ ├── failing_spec.js
│ │ ├── helpers/
│ │ │ └── spec_helper.js
│ │ └── templates/
│ │ └── foo.html
│ └── templates/
│ └── foo.html
├── suite_spec.rb
└── template_spec.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .circleci/config.yml
================================================
# Use the latest 2.1 version of CircleCI pipeline process engine.
# See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
# Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
# See: https://circleci.com/docs/2.0/orb-intro/
orbs:
ruby: circleci/ruby@1.4.0
# Define a job to be invoked later in a workflow.
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
jobs:
build:
docker:
- image: cimg/ruby:3.0.6-browsers
executor: ruby/default
steps:
- checkout
- run:
name: setup
command: bundle install
- run:
name: Load submodules
command: git submodule update --init
- run:
name: Run tests
command: bundle exec rspec
# Invoke jobs via workflows
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
workflows:
run_specs: # This is the name of the workflow, feel free to change it to better match your workflow.
# Inside the workflow, you define the jobs you want to run.
jobs:
- build
================================================
FILE: .gitignore
================================================
.rvmrc
.bundle
Gemfile.lock
Gruntfile.js
.rbenv-version
node_modules
coverage/*
================================================
FILE: .gitmodules
================================================
[submodule "lib/jasmine"]
path = lib/jasmine
url = http://github.com/pivotal/jasmine.git
================================================
FILE: .tool-versions
================================================
ruby 3.0.5
================================================
FILE: .travis.yml
================================================
language: ruby
rvm:
- 3.0.5
before_script:
- 'git submodule update --init'
- 'sh -e /etc/init.d/xvfb start'
before_install:
- gem install bundler
env:
- DISPLAY=':99.0'
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
gemspec
group :development, :test do
gem "cuprite"
gem 'puma'
gem 'pry'
end
================================================
FILE: README.rdoc
================================================
= Evergreen
rdoc-image:https://coveralls.io/repos/github/abepetrillo/evergreen/badge.svg?branch=master
rdoc-image:https://api.codeclimate.com/v1/badges/1bc70455454cbc60dd67/maintainability
"Because green is the new Blue(Ridge)"gi
Evergreen is a tool to run javascript unit tests for client side JavaScript. It
combines a server which allows you to serve up and run your specs in a browser,
as well as a runner which uses Capybara and any of its drivers to run your
specs. Evergreen uses the Jasmine unit testing framework for JavaScript.
http://github.com/abepetrillo/evergreen
== Philosophy
Evergreen is a unit testing tool. Its purpose is to test JavaScript in
isolation from your application. If you need a tool that tests how your
JavaScript integrates with your application you should use an integration
testing framework, such as {Capybara}[http://github.com/jnicklas/capybara].
== Maintenance note
Jonas has kindly given me push access so I can help maintain the project and manage pull requests.
If you have any questions or suggestions please feel free to {contact me}[http://github.com/abepetrillo]
== Installation
Install as a Ruby gem:
gem install evergreen
== Usage
Evergreen assumes a file and directory structure, place all your javascript
code inside ./public and all spec files inside ./spec/javascripts. All spec
files should end in _spec.js. For example:
public/javascripts/widget.js
spec/javascripts/widget_spec.js
You can require files from the public directory inside your spec file:
require('/javascripts/widget.js')
describe('a widget', function() {
...
});
You can now look at your spec files inside a browser by starting up the
Evergreen server:
evergreen serve
Alternatively you can run the specs headlessly by running:
evergreen run
== Integrating with Rails 3
Add Evergreen to your Gemfile:
gem 'evergreen', :require => 'evergreen/rails'
Start your rails application and navigate to /evergreen. You should now see a
list of all spec files, click on one to run it.
There's a rake task provided for you that you can use to run your specs:
rake spec:javascripts
== Integrating with Rails 2
Add the following line to your Rakefile:
require 'evergreen/tasks'
This will give you the `spec:javascripts` rake task. Note that mounting is not
possible under Rails 2 and that `require 'evergreen/rails'` will fail.
== Configuration
By default, Evergreen uses Selenium to run your specs and assumes a certain
directory structure. If this standard is fine for you, then you don't need to
do anything else. If you need to configure Evergreen to suit your needs,
Evergreen will automatically look for and load the following files:
config/evergreen.rb
.evergreen
~/.evergreen
The content of these files could look like this:
require 'capybara-webkit'
Evergreen.configure do |config|
config.driver = :webkit
config.public_dir = 'public_html'
config.template_dir = 'templates'
config.spec_dir = 'spec'
end
== Transactions
One problem often faced when writing unit tests for client side code is that
changes to the page are not reverted for the next example, so that successive
examples become dependent on each other. Evergreen adds a special div to your
page with an id of test. This div is automatically emptied before each example.
You should avoid appending markup to the page body and instead append it to
this test div:
describe('transactions', function() {
it("should add stuff in one test...", function() {
$('#test').append('
New Stuff
');
expect($('#test h1#added').length).toEqual(1);
});
it("... should have been removed before the next starts", function() {
expect($('#test h1#added').length).toEqual(0);
});
});
== Templates
Even more powerful than that, Evergreen allows you to create HTML templates to
go along with your specs. Put the templates in their own folder like this:
spec/javascripts/templates/one_template.html
spec/javascripts/templates/another_template.html
You can then load the template into the test div, by calling the template
function in your specs:
describe('transactions', function() {
template('one_template.html')
it("should load the template in this test", function() {
...
});
});
== Spec Helper
If you add a spec_helper file like so:
spec/javascripts/helpers/spec_helper.js
It will automatically be loaded. This is a great place for adding custom
matchers and the like.
== CoffeeScript
Evergreen supports specs written in
{CoffeeScript}[http://github.com/jashkenas/coffee-script]. Just name your spec
file _spec.coffee and it will automatically be translated for you.
Note that since CoffeeScript files are not compiled by Sprockets (as in Rails),
the double-extension .js.coffee is not supported.
You can also add a CoffeeScript spec helper, but remember that CoffeeScript
encloses individual files in a closure, if you need something you define in the
spec helper to be available in your spec files, attach it to the window object:
# spec/javascripts/helpers/spec_helper.coffee
MyThing: "foo" # local to spec helper
window.MyThing: "foo" # global
== Development
If you plan to work on Evergreen, you need to checkout the Jasmine gem, which
is added as a git submodule. Run the following command:
git submodule update --init
If you're using a version of Evergreen from git with bundler, you need to tell
bundler to use submodules, this can be achieved with the following command:
gem 'evergreen', :submodules => true, :git => 'git://github.com/abepetrillo/evergreen.git'
== License:
(The MIT License)
Copyright (c) 2009 Jonas Nicklas
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.
================================================
FILE: Rakefile
================================================
require 'rubygems'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
================================================
FILE: bin/evergreen
================================================
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
require 'evergreen'
begin
# The dup is to keep ARGV intact, so that tools like ruby-debug can respawn.
success = Evergreen::Cli.execute(ARGV.dup)
Kernel.exit(success ? 0 : 1)
rescue SystemExit => e
Kernel.exit(e.status)
rescue Exception => e
STDERR.puts("#{e.message} (#{e.class})")
STDERR.puts(e.backtrace.join("\n"))
Kernel.exit(1)
end
================================================
FILE: config/routes.rb
================================================
Rails.application.routes.draw do
mount Evergreen::Application, :at => '/evergreen'
end
================================================
FILE: evergreen.gemspec
================================================
# -*- encoding: utf-8 -*-
lib = File.expand_path('../lib/', __FILE__)
$:.unshift lib unless $:.include?(lib)
require 'evergreen/version'
Gem::Specification.new do |s|
s.name = "evergreen"
s.rubyforge_project = "evergreen"
s.version = Evergreen::VERSION
s.authors = ["Jonas Nicklas", "Abe Petrillo"]
s.email = ["jonas.nicklas@gmail.com", "abe.petrillo@gmail.com"]
s.description = "Run Jasmine JavaScript unit tests, integrate them into Ruby applications."
s.license = "MIT"
s.files = Dir.glob("{bin,lib,spec,config}/**/*") + %w(README.rdoc)
s.extra_rdoc_files = ["README.rdoc"]
s.executables = ['evergreen']
s.homepage = "http://github.com/abepetrillo/evergreen"
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.rubygems_version = "1.3.6"
s.summary = "Run Jasmine JavaScript unit tests, integrate them into Ruby applications."
s.add_runtime_dependency("capybara", [">= 2.1.0"])
s.add_runtime_dependency("launchy")
s.add_runtime_dependency("sinatra", [">= 1.1"])
s.add_runtime_dependency("json_pure")
s.add_runtime_dependency("coffee-script")
s.add_development_dependency('rspec', ['~>3.2'])
s.add_development_dependency('rake')
s.add_development_dependency('coveralls_reborn')
end
================================================
FILE: example/public/implementation.js
================================================
var foo = 'foo';
================================================
FILE: example/spec/javascripts/foo_spec.js
================================================
require('/implementation.js')
describe('with no tokens', function () {
it("should return an empty string if an empty string is given", function() {
expect(foo).toEqual('foo');
})
it("should return a string unchanged", function() {
expect(foo).toEqual('foo');
})
})
describe('with one token', function () {
it("should replace the token with an empty string if no value is passed in", function() {
expect(foo).toEqual('foo');
})
it("should replace the token with a given value", function() {
})
it("should not replace partial token matches", function() {
})
it("should work when calling replace twice on the same string template", function() {
})
})
describe('with two tokens (OMG!?)', function () {
it("should replace all tokens with their values", function() {
})
it("should not do anything about tokens not present in the string template", function() {
})
it("should replace tokens without value with the empty string", function() {
})
})
================================================
FILE: lib/evergreen/application.rb
================================================
module Evergreen
class Application < Sinatra::Base
set :static, false
set :root, File.expand_path('.', File.dirname(__FILE__))
helpers do
def url(path)
Evergreen.mounted_at.to_s + path.to_s
end
def render_spec(spec)
spec.read if spec
rescue StandardError => error
erb :_spec_error, :locals => { :error => error }
end
end
get '/' do
@suite = Evergreen::Suite.new
erb :list
end
get '/run/all' do
@suite = Evergreen::Suite.new
erb :run
end
get '/run/*' do |name|
@suite = Evergreen::Suite.new
@spec = @suite.get_spec(name)
erb :run
end
get "/jasmine/*" do |path|
send_file File.expand_path(File.join('../jasmine/lib/jasmine-core', path), File.dirname(__FILE__))
end
get "/resources/*" do |path|
send_file File.expand_path(File.join('resources', path), File.dirname(__FILE__))
end
get '/*' do |path|
send_file File.join(Evergreen.root, Evergreen.public_dir, path)
end
end
end
================================================
FILE: lib/evergreen/cli.rb
================================================
module Evergreen
#Translates the arguments passed in from the command line
class Cli
def self.execute(argv)
new.execute(argv)
end
def execute(argv)
command = argv.shift
Evergreen.root = File.expand_path(argv.shift || '.', Dir.pwd)
# detect Rails apps
if File.exist?(File.join(Evergreen.root, 'config/environment.rb'))
require File.join(Evergreen.root, 'config/environment.rb')
require 'evergreen/rails' if defined?(Rails)
end
case command
when "serve"
Evergreen::Server.new.serve
return true
when "run"
return Evergreen::Runner.new.run
else
puts "no such command '#{command}'"
return false
end
end
end
end
================================================
FILE: lib/evergreen/helper.rb
================================================
module Evergreen
class Helper
attr_reader :name, :suite
def initialize(suite, name)
@suite = suite
@name = name
end
def root
suite.root
end
def full_path
File.join(root, Evergreen.helper_dir, name)
end
def read
if full_path =~ /\.coffee$/
require 'coffee-script'
CoffeeScript.compile(File.read(full_path))
else
File.read(full_path)
end
end
alias_method :contents, :read
def exist?
File.exist?(full_path)
end
end
end
================================================
FILE: lib/evergreen/rails.rb
================================================
require 'evergreen'
require 'rails'
module Evergreen
if defined?(Rails::Engine)
class Railtie < Rails::Engine
initializer 'evergreen.config' do
Evergreen.application = Rails.application
Evergreen.root = Rails.root
Evergreen.mounted_at = "/evergreen"
end
end
end
end
================================================
FILE: lib/evergreen/resources/evergreen.css
================================================
body {
font-family: 'Lucida grande', 'sans-serif';
background: #f0f0f0;
}
a {
color: #4DA524;
}
#TrivialReporter {
position: relative !important;
}
#page, #test, .jasmine_reporter {
width: 800px;
background: white;
-moz-border-radius: 3px;
padding: 20px;
margin: 50px auto 30px;
border: 1px solid #ddd;
box-shadow: 0 1px 3px 1px rgba(0,0,0,0.05)
}
#test {
min-height: 50px;
max-height: 300px;
overflow: auto;
margin: 30px auto 30px;
}
.jasmine_reporter {
margin: 30px auto 30px;
}
#page h1 {
font-size: 24px;
margin: 0 0 15px;
border-bottom: 3px solid #AEAEAE;
text-transform: uppercase;
padding-bottom: 4px;
}
#page a.back {
font-size: 12px;
}
#page #all {
border-bottom: 3px solid #ECECEC;
padding: 12px 0;
margin-bottom: 20px;
overflow: hidden;
}
#page ul {
margin: 0;
padding: 0;
}
#page ul li {
list-style: none;
border-bottom: 1px solid #ECECEC;
margin: 1px 0 0;
padding: 8px 0;
position: relative;
overflow: hidden;
}
#page ul li:last-child {
border: none;
}
/*#page ul li:hover, #page #all:hover {
background: #FBFBFB;
}*/
#page ul li a:first-child, #page #all a:first-child {
margin-top: 3px;
float: left;
padding: 4px 2px;
}
#page ul li a:first-child:hover, #page #all a:first-child:hover {
background: #4DA524;
color: #fff;
text-decoration: none;
border-radius: 3px;
}
#page a.run {
display: block;
float: right;
border-radius: 3px;
padding: 4px 10px;
color: #484848;
text-decoration: none;
border: 1px solid #CBCBCB;
border-bottom-color: #BDBDBD;
background: #D8D8D8;
background: -moz-linear-gradient(top, #eaeaea 1%, #d1d1d1 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,#eaeaea), color-stop(100%,#d1d1d1)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #eaeaea 1%,#d1d1d1 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #eaeaea 1%,#d1d1d1 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #eaeaea 1%,#d1d1d1 100%); /* IE10+ */
background: linear-gradient(top, #eaeaea 1%,#d1d1d1 100%); /* W3C */
}
#page a.run:hover {
box-shadow: inset 0 1px 6px 2px rgba(255,255,255,0.8);
text-shadow: 0px 0px 4px rgba(255,255,255,0.7);
}
#page a.run.pass {
border-color: #45991E;
color: #fff;
background: #4DA524;
background: -moz-linear-gradient(top, #4DA524 1%, #408B1D 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,#4DA524), color-stop(100%,#408B1D)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #4DA524 1%,#408B1D 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #4DA524 1%,#408B1D 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #4DA524 1%,#408B1D 100%); /* IE10+ */
background: linear-gradient(top, #4DA524 1%,#408B1D 100%); /* W3C */
box-shadow: inset 0 1px 6px 1px rgba(255,255,255,0.2);
text-shadow: 0px 0px 4px rgba(255,255,255,0.2);
}
#page a.run.fail {
border-color: #CB3737;
color: #fff;
background: #E24040;
background: -moz-linear-gradient(top, #E24040 1%, #C53838 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,#E24040), color-stop(100%,#C53838)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #E24040 1%,#C53838 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #E24040 1%,#C53838 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #E24040 1%,#C53838 100%); /* IE10+ */
background: linear-gradient(top, #E24040 1%,#C53838 100%); /* W3C */
}
#page a.run.pass:hover, #page a.run.fail:hover {
box-shadow: inset 0 1px 6px 2px rgba(255,255,255,0.3);
text-shadow: 0px 0px 4px rgba(255,255,255,0.3)
}
#page a.run.running, #page a.run.running:hover {
border-color: blue;
background-image: url("data:image/gif,GIF89a%10%00%10%00%F2%00%00%FF%FF%FF%00%00%00%C2%C2%C2BBB%00%00%00bbb%82%82%82%92%92%92!%FE%1ACreated%20with%20ajaxload.info%00!%F9%04%00%0A%00%00%00!%FF%0BNETSCAPE2.0%03%01%00%00%00%2C%00%00%00%00%10%00%10%00%00%033%08%BA%DC%FE0%CAIk%13c%08%3A%08%19%9C%07N%98f%09E%B11%C2%BA%14%99%C1%B6.%60%C4%C2q%D0-%5B%189%DD%A6%079%18%0C%07Jk%E7H%00%00!%F9%04%00%0A%00%01%00%2C%00%00%00%00%10%00%10%00%00%034%08%BA%DC%FEN%8C!%20%1B%84%0C%BB%B0%E6%8ADqBQT%601%19%20%60LE%5B%1A%A8%7C%1C%B5u%DF%EDa%18%07%80%20%D7%18%E2%86C%19%B2%25%24*%12%00!%F9%04%00%0A%00%02%00%2C%00%00%00%00%10%00%10%00%00%036%08%BA2%23%2B%CAA%C8%90%CC%94V%2F%06%85c%1C%0E%F4%19N%F1IBa%98%ABp%1C%F0%0A%CC%B3%BD%1C%C6%A8%2B%02Y%ED%17%FC%01%83%C3%0F2%A9d%1A%9F%BF%04%00!%F9%04%00%0A%00%03%00%2C%00%00%00%00%10%00%10%00%00%033%08%BAb%25%2B%CA2%86%91%EC%9CV_%85%8B%A6%09%85!%0C%041D%87a%1C%11%AAF%82%B0%D1%1F%03bR%5D%F3%3D%1F08%2C%1A%8F%C8%A4r9L%00%00!%F9%04%00%0A%00%04%00%2C%00%00%00%00%10%00%10%00%00%032%08%BAr'%2BJ%E7d%14%F0%18%F3L%81%0C%26v%C3%60%5CbT%94%85%84%B9%1EhYB)%CF%CA%40%10%03%1E%E9%3C%1F%C3%26%2C%1A%8F%C8%A4R%92%00%00!%F9%04%00%0A%00%05%00%2C%00%00%00%00%10%00%10%00%00%033%08%BA%20%C2%909%17%E3t%E7%BC%DA%9E0%19%C7%1C%E0!.B%B6%9D%CAW%AC%A21%0C%06%0B%14sa%BB%B05%F7%95%01%810%B0%09%89%BB%9Fm)J%00%00!%F9%04%00%0A%00%06%00%2C%00%00%00%00%10%00%10%00%00%032%08%BA%DC%FE%F0%09%11%D9%9CU%5D%9A%01%EE%DAqp%95%60%88%DDa%9C%DD4%96%85AF%C50%14%90%60%9B%B6%01%0D%04%C2%40%10%9B1%80%C2%D6%CE%91%00%00!%F9%04%00%0A%00%07%00%2C%00%00%00%00%10%00%10%00%00%032%08%BA%DC%FE0%CAI%ABeB%D4%9C)%D7%1E%08%08%C3%20%8E%C7q%0E%0410%A9%CA%B0%AEP%18%C2a%18%07V%DA%A5%02%20ub%18%82%9E%5B%11%90%00%00%3B%00%00%00%00%00%00%00%00%00");
background-repeat: no-repeat;
background-position: center center;
text-indent: -999999px;
padding-right: 14px;
padding-left: 14px;
border-color: #EDEDED;
}
#footer {
margin: 0 auto 30px;
width: 600px;
text-align: center;
font-size: 13px;
color: #aaa;
}
#footer a.elabs {
position: relative;
padding-left: 30px;
}
#footer a.elabs img {
width: 23px;
opacity: 0.6;
position: absolute;
top: -4px;
left: 3px;
}
#footer a.elabs:hover img {
opacity: 1;
}
================================================
FILE: lib/evergreen/resources/jquery.js
================================================
/*!
* jQuery JavaScript Library v1.4.1
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Mon Jan 25 19:43:33 2010 -0500
*/
(function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f,
a.currentTarget);m=0;for(s=i.length;m)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent,
va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]],
[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,
this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this,
a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};
c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
================================================
FILE: lib/evergreen.rb
================================================
require 'rubygems'
require 'sinatra/base'
require 'capybara'
require 'launchy'
require 'evergreen/version'
require 'evergreen/application'
require 'json'
require 'evergreen/utils/timeout'
module Evergreen
autoload :Cli, 'evergreen/cli'
autoload :Server, 'evergreen/server'
autoload :Runner, 'evergreen/runner'
autoload :Suite, 'evergreen/suite'
autoload :Spec, 'evergreen/spec'
autoload :Template, 'evergreen/template'
autoload :Helper, 'evergreen/helper'
class << self
attr_accessor :driver, :root, :application, :public_dir, :spec_dir, :template_dir, :helper_dir, :mounted_at, :spec_timeout
def configure
yield self
end
def use_defaults!
configure do |config|
config.application = Evergreen::Application
config.driver = :selenium
config.public_dir = 'public'
config.spec_dir = 'spec/javascripts'
config.template_dir = 'spec/javascripts/templates'
config.helper_dir = 'spec/javascripts/helpers'
config.mounted_at = ""
config.spec_timeout = 300
end
end
end
end
Evergreen.use_defaults!
================================================
FILE: lib/tasks/evergreen.rake
================================================
# Rails 3.0/3.1 Rake tasks
namespace :spec do
desc "Run JavaScript specs via Evergreen"
task :javascripts => :environment do
result = Evergreen::Runner.new.run
Kernel.exit(1) unless result
end
end
================================================
FILE: spec/evergreen_spec.rb
================================================
require 'spec_helper'
describe Evergreen::Application do
include Capybara::DSL
it "should show a successful test run" do
visit("/")
click_link("testing_spec.js")
expect(page).to have_content "2 specs, 0 failures"
end
it "should show a successful test run for a coffeescript spec" do
visit("/")
click_link("coffeescript_spec.coffee")
expect(page).to have_content("2 specs, 0 failures")
end
it "should show errors for a failing spec" do
visit("/")
click_link("failing_spec.js")
expect(page).to have_content("2 specs, 1 failure")
expect(page).to have_content("Expected 'bar' to equal 'noooooo'.")
end
it "should run all specs" do
visit("/")
click_link("All")
expect(page).to have_content("18 specs, 3 failures")
expect(page).to have_content("Expected 'bar' to equal 'noooooo'.")
end
it "should run a spec inline" do
visit("/")
within('li', :text => 'testing_spec.js') do
click_link("Run")
expect(page).to have_content('Pass')
end
end
it "should run a failing spec inline" do
visit("/")
within('li', :text => 'failing_spec.js') do
click_link("Run")
begin
expect(page).to have_content('Fail')
rescue # why you make me sad, Capybara webkit???
expect(page).to have_content('Fail')
end
end
end
end
================================================
FILE: spec/helper_spec.rb
================================================
require 'spec_helper'
describe Evergreen::Helper do
let(:suite) { Evergreen::Suite.new }
subject { Evergreen::Helper.new(suite, 'spec_helper.js') }
it 'has the corrent details' do
expect(subject.name).to eq 'spec_helper.js'
expect(subject.root).to eq File.expand_path('suite1', File.dirname(__FILE__))
expect(subject.full_path).to eq File.expand_path("spec/javascripts/helpers/spec_helper.js", Evergreen.root)
expect(subject.contents).to eq "var SpecHelper = { spec: 'helper' };\n"
end
context "with coffeescript" do
subject { Evergreen::Helper.new(suite, 'spec_helper.coffee') }
it 'load the coffeeScript helper' do
expect(subject.contents).to include 'window.CoffeeSpecHelper'
end
end
describe '.exists' do
context 'with existing spec file' do
it 'returns true' do
expect(subject.exist?).to eq true
end
end
context "with missing spec file" do
subject { Evergreen::Helper.new(suite, 'does_not_exist.js') }
it 'returns false' do
expect(subject.exist?).to eq false
end
end
end
end
================================================
FILE: spec/meta_spec.rb
================================================
require 'spec_helper'
describe Evergreen::Runner do
let(:suite) { Evergreen::Suite.new }
subject { Evergreen::Spec.new(suite, template) }
context "with standard setup" do
before { Evergreen.root = File.expand_path('suite1', File.dirname(__FILE__)) }
context "with transactions spec" do
let(:template) { 'transactions_spec.js' }
it { is_expected.to pass }
end
context "with spec helper" do
let(:template) { 'with_helper_spec.js' }
it { is_expected.to pass }
end
context "with template spec" do
let(:template) { 'templates_spec.js' }
it { is_expected.to pass }
end
context "invalid coffee" do
let(:template) { 'invalid_coffee_spec.coffee' }
it { is_expected.not_to pass }
end
context "with slow failing spec" do
let(:template) { 'slow_spec.coffee' }
it { is_expected.not_to pass }
end
end
context "with modified setup" do
before { Evergreen.root = File.expand_path('suite2', File.dirname(__FILE__)) }
context "with awesome spec" do
let(:template) { 'awesome_spec.js' }
it { is_expected.to pass }
end
context "with failing spec" do
let(:template) { 'failing_spec.js' }
it { is_expected.not_to pass }
end
end
context 'when noConflict is called via JS' do
before { Evergreen.root = File.expand_path('suite3', File.dirname(__FILE__)) }
let(:template) { 'awesome_spec.js' }
it 'does not over-ride existing methods in window' do
expect(subject).to pass
end
context 'and not using the Evergreen namespace' do
let(:template) { 'failing_spec.js' }
it 'fails' do
expect(subject).to_not pass
end
end
end
end
================================================
FILE: spec/runner_spec.rb
================================================
require 'spec_helper'
describe Evergreen::Runner do
let(:suite) { Evergreen::Suite.new }
let(:runner) { Evergreen::Runner.new(buffer) }
let(:buffer) { StringIO.new }
describe '#run' do
before { runner.run }
describe 'the buffer' do
subject { buffer.rewind; buffer.read }
it { is_expected.to include("Expected 'bar' to equal 'noooooo'") }
it { is_expected.to include("18 examples, 3 failures") }
end
end
describe '#run_spec' do
let(:spec) { suite.get_spec('failing_spec.js') }
before { runner.spec_runner(spec).run }
describe 'the buffer' do
subject { buffer.rewind; buffer.read }
it { is_expected.to include('.F') }
it { is_expected.to include("Expected 'bar' to equal 'noooooo'") }
it { is_expected.to include("2 examples, 1 failures") }
end
end
end
================================================
FILE: spec/spec_helper.rb
================================================
require 'rubygems'
require 'bundler/setup'
require 'evergreen'
require 'rspec'
require 'capybara/dsl'
require "capybara/cuprite"
require 'pry'
require 'coveralls'
Coveralls.wear!
TEST_DRIVER = :cuprite
Evergreen.root = File.expand_path('suite1', File.dirname(__FILE__))
Capybara.app = Evergreen::Application
Capybara.default_driver = TEST_DRIVER
Capybara.javascript_driver = :cuprite
Capybara.register_driver(:cuprite) do |app|
Capybara::Cuprite::Driver.new(app, window_size: [1200, 800])
end
module EvergreenMatchers
class PassSpec # :nodoc:
def description
'Successfull if the runner manages to pass all the JS specs'
end
def matches?(actual)
@actual = actual
@runner = Evergreen::Runner.new(StringIO.new).spec_runner(@actual)
@runner.passed?
end
def failure_message
"expected #{@actual.name} to pass, but it failed with:\n\n#{@runner.failure_messages}"
end
def failure_message_when_negated
"expected #{@actual.name} not to pass, but it did"
end
end
def pass
PassSpec.new
end
end
RSpec.configure do |config|
config.include EvergreenMatchers
config.before do
Capybara.reset_sessions!
Evergreen.use_defaults!
Evergreen.root = File.expand_path('suite1', File.dirname(__FILE__))
Evergreen.driver = TEST_DRIVER
end
end
================================================
FILE: spec/spec_spec.rb
================================================
require 'spec_helper'
describe Evergreen::Spec do
let(:suite) { Evergreen::Suite.new }
subject { Evergreen::Spec.new(suite, 'testing_spec.js') }
it 'has the correct details' do
expect(subject.name).to eq 'testing_spec.js'
expect(subject.root).to eq File.expand_path('suite1', File.dirname(__FILE__))
expect(subject.full_path).to eq File.expand_path("spec/javascripts/testing_spec.js", Evergreen.root)
expect(subject.url).to eq "/run/testing_spec.js"
expect(subject.contents).to include "describe\('testing'"
end
context "with coffeescript" do
subject { Evergreen::Spec.new(suite, 'coffeescript_spec.coffee') }
it 'contains coffeescript' do
expect(subject.contents).to include "describe\('coffeescript', function"
end
end
context "with existing spec file" do
it { is_expected.to exist }
end
context "with missing spec file" do
subject { Evergreen::Spec.new(suite, 'does_not_exist.js') }
it { is_expected.not_to exist }
end
end
================================================
FILE: spec/suite1/public/jquery.js
================================================
/*!
* jQuery JavaScript Library v1.4.1
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Mon Jan 25 19:43:33 2010 -0500
*/
(function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f,
a.currentTarget);m=0;for(s=i.length;m)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent,
va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]],
[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,
this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this,
a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};
c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
================================================
FILE: spec/suite_spec.rb
================================================
require 'spec_helper'
describe Evergreen::Suite do
subject { Evergreen::Suite.new }
describe '#get_spec' do
subject { Evergreen::Suite.new.get_spec('testing_spec.js') }
it 'has the correct name' do
expect(subject.name).to eq 'testing_spec.js'
end
it 'should have the correct root' do
expect(subject.root).to eq File.expand_path('suite1', File.dirname(__FILE__))
end
end
describe '#specs' do
it "should find all specs recursively in the given root directory" do
expect(subject.specs.map(&:name)).to include('testing_spec.js', 'foo_spec.js', 'bar_spec.js', 'libs/lucid_spec.js', 'models/game_spec.js')
end
end
describe '#templates' do
it "should find all specs in the given root directory" do
expect(subject.templates.map(&:name)).to include('one_template.html', 'another_template.html')
end
end
describe '#spec_helpers' do
it "should find all spec helpers in the given helpers directory" do
expect(subject.helpers.map(&:name)).to include('spec_helper.js', 'spec_helper.coffee')
end
end
end
================================================
FILE: spec/template_spec.rb
================================================
require 'spec_helper'
describe Evergreen::Template do
let(:suite) { Evergreen::Suite.new }
subject { Evergreen::Template.new(suite, 'one_template.html') }
it 'has the correct details' do
expect(subject.name).to eq 'one_template.html'
expect(subject.root).to eq File.expand_path('suite1', File.dirname(__FILE__))
expect(subject.full_path).to eq File.expand_path("spec/javascripts/templates/one_template.html", Evergreen.root)
expect(subject.contents).to include '
This is from the template
'
end
describe '.exist?' do
context "with existing spec file" do
it 'returns true' do
expect(subject.exist?).to eq true
end
end
context "with missing spec file" do
subject { Evergreen::Template.new(suite, 'does_not_exist.html') }
it 'returns false' do
expect(subject.exist?).to eq false
end
end
end
end
describe Evergreen::Template, "escaping" do
let(:suite) { Evergreen::Suite.new }
subject { Evergreen::Template.new(suite, 'escape.html') }
it "escapes contents" do
expect(subject.escaped_contents.strip).to eq %{"var foo = 0;\\n"}
end
end