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
a";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support= {leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null}; b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="";a=r.createDocumentFragment();a.appendChild(d.firstChild); c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props= {"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true, {},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this, a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d); return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]|| a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m= c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value|| {}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d); f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText= ""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j= function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a, d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+ s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a, "events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d, b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b, d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop|| d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b= 0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true}; c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b= a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!== "form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this, "keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"|| d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a= a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this, f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a, b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g|| typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u= l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&& y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&& "2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true); return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"=== g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2=== 0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return hk[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k= 0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="? k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g}; try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id"); return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href", 2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length=== 0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[], l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var i=d;i0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e -1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(), a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")}, nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e): e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!== b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"], col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)}, wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length? d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments, false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&& !c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/'); }; Evergreen.stylesheet = function(file) { document.write(''); }; Evergreen.defineGlobalMethods = function(){ this.potentialConflicts['require'] = window['require'] this.potentialConflicts['template'] = window['template'] this.potentialConflicts['stylesheet'] = window['stylesheet'] window.require = Evergreen.require window.template = Evergreen.template window.stylesheet = Evergreen.stylesheet } //Tells Evergreen to namespace functions instead of potentially over-riding existing ones Evergreen.noConflict = function() { window.require = this.potentialConflicts.require window.template = this.potentialConflicts.template window.stylesheet = this.potentialConflicts.stylesheet } Evergreen.defineGlobalMethods() ================================================ FILE: lib/evergreen/runner.rb ================================================ module Evergreen class Runner class Example def initialize(row) @row = row end def passed? @row['passed'] end def failure_message unless passed? msg = [] msg << " Failed: #{@row['name']}" msg << " #{@row['message']}" msg << " in #{@row['trace']['fileName']}:#{@row['trace']['lineNumber']}" if @row['trace'] msg.join("\n") end end end class SpecRunner attr_reader :runner, :spec def initialize(runner, spec) @runner = runner @spec = spec end def session runner.session end def io runner.io end def run io.puts dots io.puts failure_messages io.puts "\n#{examples.size} examples, #{failed_examples.size} failures" passed? end def examples @results ||= begin session.visit(spec.url) previous_results = "" Evergreen.timeout(Evergreen.spec_timeout, "#{spec.name} timed out!") do dots = session.evaluate_script('Evergreen.dots') io.print dots.sub(/^#{Regexp.escape(previous_results)}/, '') io.flush previous_results = dots session.evaluate_script('Evergreen.done') end dots = session.evaluate_script('Evergreen.dots') io.print dots.sub(/^#{Regexp.escape(previous_results)}/, '') JSON.parse(session.evaluate_script('Evergreen.getResults()')).map do |row| Example.new(row) end end end def failed_examples examples.select { |example| not example.passed? } end def passed? examples.all? { |example| example.passed? } end def dots examples; "" end def failure_messages unless passed? examples.map { |example| example.failure_message }.compact.join("\n\n") end end end attr_reader :suite, :io def initialize(io=STDOUT) @io = io end def spec_runner(spec) SpecRunner.new(self, spec) end def run before = Time.now io.puts "" io.puts dots.to_s io.puts "" if failure_messages io.puts failure_messages io.puts "" end seconds = "%.2f" % (Time.now - before) io.puts "Finished in #{seconds} seconds" io.puts "#{examples.size} examples, #{failed_examples.size} failures" passed? end def examples spec_runners.map { |spec_runner| spec_runner.examples }.flatten end def failed_examples examples.select { |example| not example.passed? } end def passed? spec_runners.all? { |spec_runner| spec_runner.passed? } end def dots spec_runners.map { |spec_runner| spec_runner.dots }.join end def failure_messages unless passed? spec_runners.map { |spec_runner| spec_runner.failure_messages }.compact.join("\n\n") end end def session @session ||= Capybara::Session.new(Evergreen.driver, Evergreen.application) end def suite @suite ||= Evergreen::Suite.new end protected def spec_runners @spec_runners ||= suite.specs.map { |spec| SpecRunner.new(self, spec) } end end end ================================================ FILE: lib/evergreen/server.rb ================================================ module Evergreen class Server attr_reader :suite def serve server.boot Launchy.open("http://#{server.host}:#{server.port}/#{Evergreen.mounted_at.to_s}") trap('SIGINT') { puts 'Shutting down...' ; exit 0 } sleep end protected def server @server ||= Capybara::Server.new(Evergreen.application) end end end ================================================ FILE: lib/evergreen/spec.rb ================================================ require 'open3' module Evergreen class Spec 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.spec_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 url "#{suite.mounted_at}/run/#{name}" end def passed? runner.passed? end def failure_messages runner.failure_messages end def exist? File.exist?(full_path) end protected def runner @runner ||= suite.runner.spec_runner(self) end end end ================================================ FILE: lib/evergreen/suite.rb ================================================ module Evergreen class Suite attr_reader :driver def initialize paths = [ File.expand_path("config/evergreen.rb", root), File.expand_path(".evergreen", root), "#{ENV["HOME"]}/.evergreen" ] paths.each { |path| load(path) if File.exist?(path) } end def root Evergreen.root end def mounted_at Evergreen.mounted_at end def get_spec(name) Spec.new(self, name) end def specs Dir.glob(File.join(root, Evergreen.spec_dir, '**/*_spec.{js,coffee}')).map do |path| Spec.new(self, path.gsub(File.join(root, Evergreen.spec_dir, ''), '')) end end def templates Dir.glob(File.join(root, Evergreen.template_dir, '**/*')).map do |path| Template.new(self, File.basename(path)) end end def helpers Dir.glob(File.join(root, Evergreen.helper_dir, '*')).map do |path| Helper.new(self, File.basename(path)) end end end end ================================================ FILE: lib/evergreen/tasks.rb ================================================ require 'evergreen' # Rails 2.3 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: lib/evergreen/template.rb ================================================ module Evergreen class Template 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.template_dir, name) end def read File.read(full_path) end alias_method :contents, :read def escaped_contents contents.to_json.gsub("", %{}) end def exist? File.exist?(full_path) end end end ================================================ FILE: lib/evergreen/utils/timeout.rb ================================================ module Evergreen class << self ## # Provides timeout similar to standard library Timeout, but avoids threads # def timeout(seconds = 1, error_message = nil, &block) start_time = Time.now result = nil until result return result if result = yield delay = seconds - (Time.now - start_time) if delay <= 0 raise TimeoutError, error_message || "timed out" end sleep(0.05) end end end end ================================================ FILE: lib/evergreen/version.rb ================================================ module Evergreen VERSION = '1.3.0' end ================================================ FILE: lib/evergreen/views/_spec_error.erb ================================================ describe("failure", function() { it("should not fail", function() { throw(<%= "#{error.class}: #{error.message}".to_json %>); }); }); ================================================ FILE: lib/evergreen/views/layout.erb ================================================ Evergreen <%= yield %> ================================================ FILE: lib/evergreen/views/list.erb ================================================

Evergreen

================================================ FILE: lib/evergreen/views/run.erb ================================================

Evergreen

">Back to list
================================================ 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
a";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support= {leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null}; b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="";a=r.createDocumentFragment();a.appendChild(d.firstChild); c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props= {"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true, {},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this, a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d); return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]|| a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m= c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value|| {}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d); f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText= ""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j= function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a, d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+ s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a, "events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d, b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b, d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop|| d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b= 0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true}; c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b= a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!== "form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this, "keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"|| d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a= a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this, f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a, b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g|| typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u= l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&& y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&& "2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true); return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"=== g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2=== 0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return hk[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k= 0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="? k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g}; try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id"); return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href", 2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length=== 0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[], l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var i=d;i0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e -1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(), a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")}, nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e): e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!== b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"], col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)}, wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length? d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments, false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&& !c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/ ================================================ FILE: spec/suite1/spec/javascripts/templates/one_template.html ================================================

This is from the template

================================================ FILE: spec/suite1/spec/javascripts/templates/script_tags.html ================================================

================================================ FILE: spec/suite1/spec/javascripts/templates_spec.js ================================================ require('/jquery.js'); stylesheet('/styles.css'); describe('templates', function() { describe('with template', function() { template('one_template.html'); it("should append the template to the test div", function() { expect($('#test h1#from-template').length).toEqual(1); }); it("should change stuff in one test...", function() { expect($('#test h1#from-template').length).toEqual(1); $('#test h1#from-template').attr('id', 'changed'); expect($('#test h1#changed').length).toEqual(1); expect($('#test h1#from-template').length).toEqual(0); }); it("... should have been removed before the next starts", function() { expect($('#test h1#changed').length).toEqual(0); expect($('#test h1#from-template').length).toEqual(1); }); }); describe('with another template', function() { template('another_template.html'); it("should append the template to the test div", function() { expect($('#test h1#another-template').length).toEqual(1); }); }); describe('with template with script tags', function() { template('script_tags.html'); it("should append the template to the test div", function() { expect($('#test h1#script-tags').length).toEqual(1); }); }); }); describe('stylesheet', function() { template('one_template.html'); it("should style the template", function() { expect($('#from-template').css('width')).toEqual('300px'); }); }); ================================================ FILE: spec/suite1/spec/javascripts/testing_spec.js ================================================ describe('testing', function() { it("should pass", function() { expect('foo').toEqual('foo'); }); it("should also pass", function() { expect('bar').toEqual('bar'); }); }); ================================================ FILE: spec/suite1/spec/javascripts/transactions_spec.js ================================================ require('/jquery.js'); 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); }); }); ================================================ FILE: spec/suite1/spec/javascripts/with_helper_spec.js ================================================ describe('the spec helper', function() { it("should load js file", function() { expect(SpecHelper.spec).toEqual('helper'); }); it("should load coffee file", function() { expect(CoffeeSpecHelper.coffee).toEqual('script'); }); }); ================================================ FILE: spec/suite2/config/evergreen.rb ================================================ Evergreen.configure do |config| config.public_dir = 'public_html' config.template_dir = 'templates' config.spec_dir = 'spec' end ================================================ FILE: spec/suite2/public_html/foo.js ================================================ var something = "The Foo"; ================================================ FILE: spec/suite2/spec/awesome_spec.js ================================================ require('/foo.js'); describe('awesome', function() { template('foo.html'); it('requires public files', function() { expect(something).toEqual('The Foo'); }); it('loads templates', function() { expect(document.getElementById('foo').innerHTML).toEqual('The foo template'); }); }); ================================================ FILE: spec/suite2/spec/failing_spec.js ================================================ describe('failing', function() { it('fails', function() { expect('llama').toEqual('monkey'); }); }); ================================================ FILE: spec/suite2/templates/foo.html ================================================
The foo template
================================================ FILE: spec/suite3/public/foo.js ================================================ var something = "The Foo"; ================================================ FILE: spec/suite3/spec/javascripts/awesome_spec.js ================================================ Evergreen.require('/foo.js'); describe('awesome', function() { Evergreen.template('foo.html'); it('requires public files', function() { expect(something).toEqual('The Foo'); }); it('loads templates', function() { expect(document.getElementById('foo').innerHTML).toEqual('The foo template'); }); }); ================================================ FILE: spec/suite3/spec/javascripts/failing_spec.js ================================================ require('/foo.js'); describe('awesome', function() { template('foo.html'); it('requires public files', function() { expect(something).toEqual('The Foo'); }); it('loads templates', function() { expect(document.getElementById('foo').innerHTML).toEqual('The foo template'); }); }); ================================================ FILE: spec/suite3/spec/javascripts/helpers/spec_helper.js ================================================ Evergreen.noConflict(); require = function(file){ //console.log('custom require code') } template = function(file){ //console.log('custom template code') } ================================================ FILE: spec/suite3/spec/javascripts/templates/foo.html ================================================
The foo template
================================================ FILE: spec/suite3/templates/foo.html ================================================
The foo template
================================================ 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