27 |
28 |
29 |
30 |
52 |
--------------------------------------------------------------------------------
/spec/meta_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Evergreen::Runner do
4 | let(:suite) { Evergreen::Suite.new }
5 | subject { Evergreen::Spec.new(suite, template) }
6 |
7 | context "with standard setup" do
8 | before { Evergreen.root = File.expand_path('suite1', File.dirname(__FILE__)) }
9 |
10 | context "with transactions spec" do
11 | let(:template) { 'transactions_spec.js' }
12 | it { is_expected.to pass }
13 | end
14 |
15 | context "with spec helper" do
16 | let(:template) { 'with_helper_spec.js' }
17 | it { is_expected.to pass }
18 | end
19 |
20 | context "with template spec" do
21 | let(:template) { 'templates_spec.js' }
22 | it { is_expected.to pass }
23 | end
24 |
25 | context "invalid coffee" do
26 | let(:template) { 'invalid_coffee_spec.coffee' }
27 | it { is_expected.not_to pass }
28 | end
29 |
30 | context "with slow failing spec" do
31 | let(:template) { 'slow_spec.coffee' }
32 | it { is_expected.not_to pass }
33 | end
34 | end
35 |
36 | context "with modified setup" do
37 | before { Evergreen.root = File.expand_path('suite2', File.dirname(__FILE__)) }
38 |
39 | context "with awesome spec" do
40 | let(:template) { 'awesome_spec.js' }
41 | it { is_expected.to pass }
42 | end
43 |
44 | context "with failing spec" do
45 | let(:template) { 'failing_spec.js' }
46 | it { is_expected.not_to pass }
47 | end
48 | end
49 |
50 | context 'when noConflict is called via JS' do
51 | before { Evergreen.root = File.expand_path('suite3', File.dirname(__FILE__)) }
52 | let(:template) { 'awesome_spec.js' }
53 | it 'does not over-ride existing methods in window' do
54 | expect(subject).to pass
55 | end
56 |
57 | context 'and not using the Evergreen namespace' do
58 | let(:template) { 'failing_spec.js' }
59 | it 'fails' do
60 | expect(subject).to_not pass
61 | end
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/lib/evergreen/resources/run.js:
--------------------------------------------------------------------------------
1 | if(!this.JSON){this.JSON={};}
2 |
3 | var Evergreen = {
4 | potentialConflicts: {}
5 | };
6 |
7 | Evergreen.dots = ""
8 |
9 | Evergreen.ReflectiveReporter = function() {
10 | this.reportRunnerStarting = function(runner) {
11 | Evergreen.results = [];
12 | };
13 | this.reportSpecResults = function(spec) {
14 | var results = spec.results();
15 | var item = results.getItems()[0] || {};
16 | Evergreen.results.push({
17 | name: spec.getFullName(),
18 | passed: results.failedCount === 0,
19 | message: item.message,
20 | trace: item.trace
21 | });
22 | Evergreen.dots += (results.failedCount === 0) ? "." : "F";
23 | };
24 | this.reportRunnerResults = function(runner) {
25 | Evergreen.done = true;
26 | if(Evergreen.onDone) { Evergreen.onDone() }
27 | };
28 | };
29 |
30 | Evergreen.templates = {};
31 |
32 | Evergreen.getResults = function() {
33 | return JSON.stringify(Evergreen.results);
34 | };
35 |
36 | beforeEach(function() {
37 | document.getElementById('test').innerHTML = "";
38 | });
39 |
40 | Evergreen.template = function(name) {
41 | beforeEach(function() {
42 | document.getElementById('test').innerHTML = Evergreen.templates[name]
43 | });
44 | };
45 |
46 | Evergreen.require = function(file) {
47 | document.write('');
48 | };
49 |
50 | Evergreen.stylesheet = function(file) {
51 | document.write('');
52 | };
53 |
54 | Evergreen.defineGlobalMethods = function(){
55 | this.potentialConflicts['require'] = window['require']
56 | this.potentialConflicts['template'] = window['template']
57 | this.potentialConflicts['stylesheet'] = window['stylesheet']
58 |
59 | window.require = Evergreen.require
60 | window.template = Evergreen.template
61 | window.stylesheet = Evergreen.stylesheet
62 | }
63 |
64 |
65 |
66 | //Tells Evergreen to namespace functions instead of potentially over-riding existing ones
67 | Evergreen.noConflict = function() {
68 | window.require = this.potentialConflicts.require
69 | window.template = this.potentialConflicts.template
70 | window.stylesheet = this.potentialConflicts.stylesheet
71 | }
72 |
73 | Evergreen.defineGlobalMethods()
74 |
--------------------------------------------------------------------------------
/lib/evergreen/resources/json2.js:
--------------------------------------------------------------------------------
1 | var JSON;if(!JSON){JSON={};}
2 | (function(){"use strict";function f(n){return n<10?'0'+n:n;}
3 | if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
4 | f(this.getUTCMonth()+1)+'-'+
5 | f(this.getUTCDate())+'T'+
6 | f(this.getUTCHours())+':'+
7 | f(this.getUTCMinutes())+':'+
8 | f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
9 | var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
10 | function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
11 | if(typeof rep==='function'){value=rep.call(holder,key,value);}
12 | switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
13 | gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i 'evergreen/rails'
67 |
68 | Start your rails application and navigate to /evergreen. You should now see a
69 | list of all spec files, click on one to run it.
70 |
71 | There's a rake task provided for you that you can use to run your specs:
72 |
73 | rake spec:javascripts
74 |
75 | == Integrating with Rails 2
76 |
77 | Add the following line to your Rakefile:
78 |
79 | require 'evergreen/tasks'
80 |
81 | This will give you the `spec:javascripts` rake task. Note that mounting is not
82 | possible under Rails 2 and that `require 'evergreen/rails'` will fail.
83 |
84 | == Configuration
85 |
86 | By default, Evergreen uses Selenium to run your specs and assumes a certain
87 | directory structure. If this standard is fine for you, then you don't need to
88 | do anything else. If you need to configure Evergreen to suit your needs,
89 | Evergreen will automatically look for and load the following files:
90 |
91 | config/evergreen.rb
92 | .evergreen
93 | ~/.evergreen
94 |
95 | The content of these files could look like this:
96 |
97 | require 'capybara-webkit'
98 |
99 | Evergreen.configure do |config|
100 | config.driver = :webkit
101 | config.public_dir = 'public_html'
102 | config.template_dir = 'templates'
103 | config.spec_dir = 'spec'
104 | end
105 |
106 | == Transactions
107 |
108 | One problem often faced when writing unit tests for client side code is that
109 | changes to the page are not reverted for the next example, so that successive
110 | examples become dependent on each other. Evergreen adds a special div to your
111 | page with an id of test. This div is automatically emptied before each example.
112 | You should avoid appending markup to the page body and instead append it to
113 | this test div:
114 |
115 | describe('transactions', function() {
116 | it("should add stuff in one test...", function() {
117 | $('#test').append('
New Stuff
');
118 | expect($('#test h1#added').length).toEqual(1);
119 | });
120 |
121 | it("... should have been removed before the next starts", function() {
122 | expect($('#test h1#added').length).toEqual(0);
123 | });
124 | });
125 |
126 | == Templates
127 |
128 | Even more powerful than that, Evergreen allows you to create HTML templates to
129 | go along with your specs. Put the templates in their own folder like this:
130 |
131 | spec/javascripts/templates/one_template.html
132 | spec/javascripts/templates/another_template.html
133 |
134 | You can then load the template into the test div, by calling the template
135 | function in your specs:
136 |
137 | describe('transactions', function() {
138 | template('one_template.html')
139 |
140 | it("should load the template in this test", function() {
141 | ...
142 | });
143 | });
144 |
145 | == Spec Helper
146 |
147 | If you add a spec_helper file like so:
148 |
149 | spec/javascripts/helpers/spec_helper.js
150 |
151 | It will automatically be loaded. This is a great place for adding custom
152 | matchers and the like.
153 |
154 | == CoffeeScript
155 |
156 | Evergreen supports specs written in
157 | {CoffeeScript}[http://github.com/jashkenas/coffee-script]. Just name your spec
158 | file _spec.coffee and it will automatically be translated for you.
159 |
160 | Note that since CoffeeScript files are not compiled by Sprockets (as in Rails),
161 | the double-extension .js.coffee is not supported.
162 |
163 | You can also add a CoffeeScript spec helper, but remember that CoffeeScript
164 | encloses individual files in a closure, if you need something you define in the
165 | spec helper to be available in your spec files, attach it to the window object:
166 |
167 | # spec/javascripts/helpers/spec_helper.coffee
168 |
169 | MyThing: "foo" # local to spec helper
170 | window.MyThing: "foo" # global
171 |
172 | == Development
173 |
174 | If you plan to work on Evergreen, you need to checkout the Jasmine gem, which
175 | is added as a git submodule. Run the following command:
176 |
177 | git submodule update --init
178 |
179 | If you're using a version of Evergreen from git with bundler, you need to tell
180 | bundler to use submodules, this can be achieved with the following command:
181 |
182 | gem 'evergreen', :submodules => true, :git => 'git://github.com/abepetrillo/evergreen.git'
183 |
184 | == License:
185 |
186 | (The MIT License)
187 |
188 | Copyright (c) 2009 Jonas Nicklas
189 |
190 | Permission is hereby granted, free of charge, to any person obtaining
191 | a copy of this software and associated documentation files (the
192 | 'Software'), to deal in the Software without restriction, including
193 | without limitation the rights to use, copy, modify, merge, publish,
194 | distribute, sublicense, and/or sell copies of the Software, and to
195 | permit persons to whom the Software is furnished to do so, subject to
196 | the following conditions:
197 |
198 | The above copyright notice and this permission notice shall be
199 | included in all copies or substantial portions of the Software.
200 |
201 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
202 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
203 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
204 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
205 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
206 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
207 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
208 |
--------------------------------------------------------------------------------
/spec/suite1/public/jquery.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery JavaScript Library v1.4.1
3 | * http://jquery.com/
4 | *
5 | * Copyright 2010, John Resig
6 | * Dual licensed under the MIT or GPL Version 2 licenses.
7 | * http://jquery.org/license
8 | *
9 | * Includes Sizzle.js
10 | * http://sizzlejs.com/
11 | * Copyright 2010, The Dojo Foundation
12 | * Released under the MIT, BSD, and GPL Licenses.
13 | *
14 | * Date: Mon Jan 25 19:43:33 2010 -0500
15 | */
16 | (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,
18 | 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,
21 | 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]],
22 | [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,
23 | 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,
24 | 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};
25 | 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