guard-jasmine 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +247 -0
- data/lib/guard/jasmine.rb +31 -0
- data/lib/guard/jasmine/formatter.rb +35 -0
- data/lib/guard/jasmine/inspector.rb +31 -0
- data/lib/guard/jasmine/phantomjs/run-jasmine.coffee +84 -0
- data/lib/guard/jasmine/runner.rb +108 -0
- data/lib/guard/jasmine/templates/Guardfile +5 -0
- data/lib/guard/jasmine/version.rb +5 -0
- metadata +151 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Michael Kessler
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
# Guard::Jasmine [![Build Status](https://secure.travis-ci.org/netzpirat/guard-jasmine.png)](http://travis-ci.org/netzpirat/guard-jasmine)
|
2
|
+
|
3
|
+
Guard::Jasmine automatically tests your Jasmine specs when files are modified.
|
4
|
+
|
5
|
+
Tested on MRI Ruby 1.8.7, 1.9.2, REE and the latest versions of JRuby & Rubinius.
|
6
|
+
|
7
|
+
If you have any questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard`
|
8
|
+
(irc.freenode.net).
|
9
|
+
|
10
|
+
## Highlights
|
11
|
+
|
12
|
+
* Continuous testing based on file modifications by [Guard][], manifold configurable rules
|
13
|
+
with RegEx and Ruby.
|
14
|
+
|
15
|
+
* Fast headless testing in [PhantomJS][], a full featured WebKit browser with native support for
|
16
|
+
various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.
|
17
|
+
|
18
|
+
* With Rails 3.1 you can write your [Jasmine][] specs in [CoffeeScript][] also, fully integrated into the
|
19
|
+
[Rails 3.1 asset pipeline][] with [Jasminerice][].
|
20
|
+
|
21
|
+
* Runs on Mac OS X, Linux and Windows.
|
22
|
+
|
23
|
+
## How it works
|
24
|
+
|
25
|
+
1. Configure your Jasmine based JavaScript/CoffeeScript specs with Jasminerice in the asset pipeline, serve it over
|
26
|
+
the normal Jasmine Spec Runner. (With Rails 2 & 3 you configure your plain JavaScript Jasmine specs with [the Jasmine Gem][].)
|
27
|
+
|
28
|
+
2. You configure Guard to trigger certain specs based on file modifications.
|
29
|
+
|
30
|
+
3. Guard uses PhantomJS to request the Jasmine Spec Runner headless and notifies you of the result in the terminal and
|
31
|
+
optionally over system notifications like Growl, Libnotify or Notifu.
|
32
|
+
|
33
|
+
## Install
|
34
|
+
|
35
|
+
### Guard and Guard::Jasmine
|
36
|
+
|
37
|
+
Please be sure to have [Guard][] installed.
|
38
|
+
|
39
|
+
Install the gem:
|
40
|
+
|
41
|
+
```bash
|
42
|
+
$ gem install guard-jasmine
|
43
|
+
```
|
44
|
+
|
45
|
+
Add it to your `Gemfile`, preferably inside the development group:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
gem 'guard-jasmine'
|
49
|
+
```
|
50
|
+
|
51
|
+
Add guard definition to your `Guardfile` by running this command:
|
52
|
+
|
53
|
+
```bash
|
54
|
+
$ guard init jasmine
|
55
|
+
```
|
56
|
+
|
57
|
+
### Rails 3.1
|
58
|
+
|
59
|
+
With Rails 3.1 you can write your Jasmine specs in addition to JavaScript with CoffeeScript, fully integrated into the
|
60
|
+
Rails 3.1 asset pipeline with Jasminerice.
|
61
|
+
|
62
|
+
Please read the detailed installation and configuration instructions at [Jasminerice][].
|
63
|
+
|
64
|
+
In short, you add it to your `Gemfile`:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
group :development, :test do
|
68
|
+
gem 'jasmine'
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
and add a route for the Jasmine Test Runner to `config/routes.rb`:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
if ["development", "test"].include? Rails.env
|
76
|
+
mount Jasminerice::Engine => "/jasmine"
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
Next you create the directory `spec/javascripts` where your CoffeeScript tests go into. You define the Rails 3.1
|
81
|
+
asset pipeline manifest in `spec/javascripts/spec.js.coffee`:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
#=require_tree ./
|
85
|
+
```
|
86
|
+
|
87
|
+
### Rails 2 & 3
|
88
|
+
|
89
|
+
With Rails 3 you write your Jasmine specs in JavaScript, configured and server with the Jasmine gem. Please read the
|
90
|
+
detailed installation and configuration instructions at [the Jasmine Gem][].
|
91
|
+
|
92
|
+
### PhantomJS
|
93
|
+
|
94
|
+
You need the PhantomJS browser installed on your system. You can download binaries for Mac OS X and Windows from
|
95
|
+
[the PhantomJS download section][].
|
96
|
+
|
97
|
+
Alternatively you can install [Homebrew][] on Mac OS X and install it with:
|
98
|
+
|
99
|
+
```bash
|
100
|
+
$ brew install phantomjs
|
101
|
+
```
|
102
|
+
|
103
|
+
If you are using Ubuntu 10.10, you can install it with apt:
|
104
|
+
|
105
|
+
```bash
|
106
|
+
$ sudo add-apt-repository ppa:jerome-etienne/neoip
|
107
|
+
$ sudo apt-get update
|
108
|
+
$ sudo apt-get install phantomjs
|
109
|
+
```
|
110
|
+
|
111
|
+
You can also build it from source for several other operating systems, please consult the
|
112
|
+
[PhantomJS build instructions][].
|
113
|
+
|
114
|
+
## Usage
|
115
|
+
|
116
|
+
Please read the [Guard usage documentation](https://github.com/guard/guard#readme).
|
117
|
+
|
118
|
+
## Guardfile
|
119
|
+
|
120
|
+
Guard::Jasmine can be adapted to all kind of projects. Please read the
|
121
|
+
[Guard documentation](https://github.com/guard/guard#readme) for more information about the Guardfile DSL.
|
122
|
+
|
123
|
+
### Rails 3.1 with Jasminerice
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
guard 'jasmine' do
|
127
|
+
watch(%r{app/assets/javascripts/(.+)\.(js\.coffee|js)}) { |m| "spec/javascripts/#{m[1]}_spec.#{m[2]}" }
|
128
|
+
watch(%r{spec/javascripts/(.+)_spec\.(js\.coffee|js)}) { |m| "spec/javascripts/#{m[1]}_spec.#{m[2]}" }
|
129
|
+
watch(%r{spec/javascripts/spec\.(js\.coffee|js)}) { "spec/javascripts" }
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
### Rails 2 & 3 with the Jasmine gem
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
guard 'jasmine', :url => 'http://127.0.0.1:8888' do
|
137
|
+
watch(%r{public/javascripts/(.+)\.js}) { |m| "spec/javascripts/#{m[1]}_spec.js" }
|
138
|
+
watch(%r{spec/javascripts/(.+)_spec\.js}) { |m| "spec/javascripts/#{m[1]}_spec.js" }
|
139
|
+
watch(%r{spec/javascripts/support/jasmine\.yml}) { "spec/javascripts" }
|
140
|
+
watch(%r{spec/javascripts/support/jasmine_config\.rb}) { "spec/javascripts" }
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
## Options
|
145
|
+
|
146
|
+
There following options can be passed to Guard::Jasmine:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
:jasmine_url => 'http://192.168.1.5/jasmine' # URL where Jasmine is served.
|
150
|
+
# default: http://127.0.0.1/jasmine
|
151
|
+
|
152
|
+
:phantomjs_bin => '~/bin/phantomjs' # Path to phantomjs.
|
153
|
+
# default: '/usr/local/bin/phantomjs'
|
154
|
+
|
155
|
+
:notifications => false # Show success and error messages.
|
156
|
+
# default: true
|
157
|
+
|
158
|
+
:hide_success => true # Disable successful compilation messages.
|
159
|
+
# default: false
|
160
|
+
```
|
161
|
+
|
162
|
+
## Alternatives
|
163
|
+
|
164
|
+
* [guard-jasmine-headless-webkit][], a Guard for [jasmine-headless-webkit][], but doesn't run in JRuby.
|
165
|
+
* [Evergreen][], runs CoffeeScript specs headless, but has no
|
166
|
+
continuous testing support.
|
167
|
+
* [Jezebel][] a Node.js REPL and continuous test runner for [Jessie][], a Node runner for Jasmine, but has no full
|
168
|
+
featured browser environment.
|
169
|
+
|
170
|
+
## Development
|
171
|
+
|
172
|
+
- Source hosted at [GitHub](https://github.com/netzpirat/guard-Jasmine)
|
173
|
+
- Report issues and feature requests to [GitHub Issues](https://github.com/netzpirat/guard-Jasmine/issues)
|
174
|
+
|
175
|
+
Pull requests are very welcome! Make sure your patches are well tested.
|
176
|
+
|
177
|
+
For questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard`
|
178
|
+
(irc.freenode.net).
|
179
|
+
|
180
|
+
## Acknowledgment
|
181
|
+
|
182
|
+
[Ariya Hidayat][] for [PhantomJS][], a powerfull headless WebKit browser.
|
183
|
+
|
184
|
+
[Brad Phelan][] for [Jasminerice][], an elegant solution for [Jasmine][] in the Rails 3.1 asset pipeline.
|
185
|
+
|
186
|
+
[Pivotal Labs][] for their beautiful [Jasmine][] BDD testing framework that makes JavaScript testing fun.
|
187
|
+
|
188
|
+
[Jeremy Ashkenas][] for [CoffeeScript][], that little language that compiles into JavaScript and makes me enjoy the
|
189
|
+
frontend.
|
190
|
+
|
191
|
+
The [Guard Team][] for giving us such a nice piece of software that is so easy to extend, one *has* to make a plugin
|
192
|
+
for it!
|
193
|
+
|
194
|
+
All the authors of the numerous [Guards][] available for making the Guard ecosystem so much growing and comprehensive.
|
195
|
+
|
196
|
+
## License
|
197
|
+
|
198
|
+
The Jasmine PhantomJS runner file [run-jasmine.coffee][] from [Roejames12][] is licensed under the BSD license.
|
199
|
+
|
200
|
+
The Guard::Jasmine itself is released under:
|
201
|
+
|
202
|
+
(The MIT License)
|
203
|
+
|
204
|
+
Copyright (c) 2011 Michael Kessler
|
205
|
+
|
206
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
207
|
+
a copy of this software and associated documentation files (the
|
208
|
+
'Software'), to deal in the Software without restriction, including
|
209
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
210
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
211
|
+
permit persons to whom the Software is furnished to do so, subject to
|
212
|
+
the following conditions:
|
213
|
+
|
214
|
+
The above copyright notice and this permission notice shall be
|
215
|
+
included in all copies or substantial portions of the Software.
|
216
|
+
|
217
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
218
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
219
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
220
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
221
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
222
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
223
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
224
|
+
|
225
|
+
[Guard]: https://github.com/guard/guard
|
226
|
+
[Guards]: https://github.com/guard
|
227
|
+
[Guard Team]: https://github.com/guard/guard/contributors
|
228
|
+
[Ariya Hidayat]: http://twitter.com/#!/AriyaHidayat
|
229
|
+
[PhantomJS]: http://www.phantomjs.org/
|
230
|
+
[the PhantomJS download section]: http://code.google.com/p/phantomjs/downloads/list
|
231
|
+
[PhantomJS build instructions]: http://code.google.com/p/phantomjs/wiki/BuildInstructions
|
232
|
+
[Roejames12]: https://github.com/Roejames12
|
233
|
+
[run-jasmine.coffee]: https://github.com/ariya/phantomjs/blob/master/examples/run-jasmine.coffee
|
234
|
+
[Brad Phelan]: http://twitter.com/#!/bradgonesurfing
|
235
|
+
[Jasminerice]: https://github.com/bradphelan/jasminerice
|
236
|
+
[Pivotal Labs]: http://pivotallabs.com/
|
237
|
+
[Jasmine]: http://pivotal.github.com/jasmine/
|
238
|
+
[the Jasmine Gem]: https://github.com/pivotal/jasmine-gem
|
239
|
+
[Jeremy Ashkenas]: http://twitter.com/#!/jashkenas
|
240
|
+
[CoffeeScript]: http://jashkenas.github.com/coffee-script/
|
241
|
+
[Rails 3.1 asset pipeline]: http://guides.rubyonrails.org/asset_pipeline.html
|
242
|
+
[Homebrew]: http://mxcl.github.com/homebrew/
|
243
|
+
[Jezebel]: https://github.com/benrady/jezebel
|
244
|
+
[Jessie]: https://github.com/futuresimple/jessie
|
245
|
+
[guard-jasmine-headless-webkit]: https://github.com/johnbintz/guard-jasmine-headless-webkit
|
246
|
+
[jasmine-headless-webkit]: https://github.com/johnbintz/jasmine-headless-webkit/
|
247
|
+
[Evergreen]: https://github.com/jnicklas/evergreen
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'guard'
|
2
|
+
require 'guard/guard'
|
3
|
+
require 'guard/watcher'
|
4
|
+
|
5
|
+
module Guard
|
6
|
+
class Jasmine < Guard
|
7
|
+
|
8
|
+
autoload :Formatter, 'guard/jasmine/formatter'
|
9
|
+
autoload :Inspector, 'guard/jasmine/inspector'
|
10
|
+
autoload :Runner, 'guard/jasmine/runner'
|
11
|
+
|
12
|
+
def initialize(watchers = [], options = { })
|
13
|
+
defaults = {
|
14
|
+
:jasmine_url => 'http://localhost:3000/jasmine',
|
15
|
+
:phantomjs_bin => '/usr/local/bin/phantomjs',
|
16
|
+
:notification => true,
|
17
|
+
:hide_success => false
|
18
|
+
}
|
19
|
+
super(watchers, defaults.merge(options))
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_all
|
23
|
+
run_on_change(Watcher.match_files(self, Dir.glob(File.join('spec', '**', '*.js(.coffee)?'))))
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_on_change(paths)
|
27
|
+
Runner.run(Inspector.clean(paths), options)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Guard
|
2
|
+
class Jasmine
|
3
|
+
module Formatter
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def info(message, options = { })
|
7
|
+
::Guard::UI.info(message, options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def debug(message, options = { })
|
11
|
+
::Guard::UI.debug(message, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def error(message, options = { })
|
15
|
+
::Guard::UI.error(color(message, ';31'), options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def success(message, options = { })
|
19
|
+
::Guard::UI.info(color(message, ';32'), options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def notify(message, options = { })
|
23
|
+
::Guard::Notifier.notify(message, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def color(text, color_code)
|
29
|
+
::Guard::UI.send(:color_enabled?) ? "\e[0#{color_code}m#{text}\e[0m" : text
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Guard
|
2
|
+
class Jasmine
|
3
|
+
module Inspector
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def clean(paths)
|
7
|
+
paths.uniq!
|
8
|
+
paths.compact!
|
9
|
+
paths = paths.select { |p| jasmine_spec?(p) }
|
10
|
+
clear_jasmine_specs
|
11
|
+
paths
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def jasmine_spec?(path)
|
17
|
+
jasmine_specs.include?(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def jasmine_specs
|
21
|
+
@jasmine_specs ||= Dir.glob('spec/**/*_spec.{js,js.coffee}')
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear_jasmine_specs
|
25
|
+
@jasmine_specs = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
##
|
2
|
+
# Wait until the test condition is true or a timeout occurs. Useful for waiting
|
3
|
+
# on a server response or for a ui change (fadeIn, etc.) to occur.
|
4
|
+
#
|
5
|
+
# @param testFx javascript condition that evaluates to a boolean,
|
6
|
+
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
|
7
|
+
# as a callback function.
|
8
|
+
# @param onReady what to do when testFx condition is fulfilled,
|
9
|
+
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
|
10
|
+
# as a callback function.
|
11
|
+
# @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
|
12
|
+
##
|
13
|
+
waitFor = (testFx, onReady, timeOutMillis=3000) ->
|
14
|
+
start = new Date().getTime()
|
15
|
+
condition = false
|
16
|
+
f = ->
|
17
|
+
if (new Date().getTime() - start < timeOutMillis) and not condition
|
18
|
+
# If not time-out yet and condition not yet fulfilled
|
19
|
+
condition = (if typeof testFx is 'string' then eval testFx else testFx()) #< defensive code
|
20
|
+
else
|
21
|
+
if not condition
|
22
|
+
# If condition still not fulfilled (timeout but condition is 'false')
|
23
|
+
console.log JSON.stringify { error: "Timeout requesting Jasmine test runner!" }
|
24
|
+
phantom.exit(1)
|
25
|
+
else
|
26
|
+
# Condition fulfilled (timeout and/or condition is 'true')
|
27
|
+
if typeof onReady is 'string' then eval onReady else onReady() #< Do what it's supposed to do once the condition is fulfilled
|
28
|
+
clearInterval interval #< Stop this interval
|
29
|
+
|
30
|
+
interval = setInterval f, 100 #< repeat check every 100ms
|
31
|
+
|
32
|
+
if phantom.args.length isnt 1
|
33
|
+
console.log JSON.stringify { error: "Wrong usage of PhantomJS script!" }
|
34
|
+
phantom.exit()
|
35
|
+
|
36
|
+
page = new WebPage()
|
37
|
+
page.onConsoleMessage = (msg) -> console.log msg
|
38
|
+
|
39
|
+
url = phantom.args[0]
|
40
|
+
|
41
|
+
page.open url, (status) ->
|
42
|
+
if status isnt 'success'
|
43
|
+
console.log JSON.stringify { error: "Unable to access Jasmine specs at #{ url }" }
|
44
|
+
phantom.exit()
|
45
|
+
|
46
|
+
else
|
47
|
+
waitFor ->
|
48
|
+
page.evaluate ->
|
49
|
+
if document.body.querySelector '.finished-at' then true else false
|
50
|
+
, ->
|
51
|
+
page.evaluate ->
|
52
|
+
result = {
|
53
|
+
suites: []
|
54
|
+
}
|
55
|
+
|
56
|
+
# Extract runner stats from the HTML
|
57
|
+
stats = /(\d+) specs, (\d+) failures? in (\d+.\d+)s/.exec document.body.querySelector('.description').innerText
|
58
|
+
result['stats'] = {
|
59
|
+
specs: parseInt stats[1]
|
60
|
+
failures: parseInt stats[2]
|
61
|
+
time: parseFloat stats[3]
|
62
|
+
}
|
63
|
+
|
64
|
+
# Extract failed suites
|
65
|
+
for failedSuite in document.body.querySelectorAll 'div.jasmine_reporter > div.suite.failed'
|
66
|
+
description = failedSuite.querySelector('a.description')
|
67
|
+
suite = {
|
68
|
+
description: description.innerText
|
69
|
+
filter: description.getAttribute('href')
|
70
|
+
specs: []
|
71
|
+
}
|
72
|
+
|
73
|
+
for failedSpec in failedSuite.querySelectorAll 'div.spec.failed'
|
74
|
+
spec = {
|
75
|
+
description: failedSpec.querySelector('a.description').getAttribute 'title'
|
76
|
+
error_message: failedSpec.querySelector('div.messages div.resultMessage').innerText
|
77
|
+
}
|
78
|
+
suite['specs'].push spec
|
79
|
+
|
80
|
+
result['suites'].push suite
|
81
|
+
|
82
|
+
console.log JSON.stringify result, undefined, 2
|
83
|
+
|
84
|
+
phantom.exit()
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class Jasmine
|
5
|
+
module Runner
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def run(files, options = { })
|
9
|
+
return false if files.empty?
|
10
|
+
|
11
|
+
message = options[:message] || (files == ['spec/javascripts'] ? 'Run all specs' : "Run specs #{ files.join(' ') }")
|
12
|
+
UI.info message, :reset => true
|
13
|
+
|
14
|
+
files.inject([]) do |results, file|
|
15
|
+
results << notify_result(run_jasmine(file, options), options)
|
16
|
+
|
17
|
+
results
|
18
|
+
end.compact
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def run_jasmine(file, options)
|
24
|
+
suite = jasmine_suite(file, options)
|
25
|
+
Formatter.info("Run Jasmine tests: #{ suite }")
|
26
|
+
IO.popen(phantomjs_command(options) + ' ' + suite)
|
27
|
+
end
|
28
|
+
|
29
|
+
def phantomjs_command(options)
|
30
|
+
options[:phantomjs_bin] + ' ' + phantomjs_script
|
31
|
+
end
|
32
|
+
|
33
|
+
def jasmine_suite(file, options)
|
34
|
+
options[:jasmine_url] + suite_query_for(file)
|
35
|
+
end
|
36
|
+
|
37
|
+
def phantomjs_script
|
38
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'phantomjs', 'run-jasmine.coffee'))
|
39
|
+
end
|
40
|
+
|
41
|
+
def suite_query_for(file)
|
42
|
+
return '' if file == 'spec/javascripts'
|
43
|
+
query_string = ''
|
44
|
+
|
45
|
+
File.foreach(file) do |line|
|
46
|
+
if line =~ /describe\s*[("']+(.*?)["')]+/
|
47
|
+
query_string = "?spec=#{ $1 }"
|
48
|
+
break
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
URI.encode(query_string)
|
53
|
+
end
|
54
|
+
|
55
|
+
def notify_result(output, options)
|
56
|
+
result = MultiJson.decode(output.read)
|
57
|
+
output.close
|
58
|
+
|
59
|
+
if result['error']
|
60
|
+
notify_runtime_error(result, options)
|
61
|
+
else
|
62
|
+
notify_spec_result(result, options)
|
63
|
+
end
|
64
|
+
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
def notify_runtime_error(result, options)
|
69
|
+
message = "An error occurred: #{ result['error'] }"
|
70
|
+
Formatter.error(message)
|
71
|
+
Formatter.notify(message, :title => 'Jasmine error', :image => :failed, :priority => 2) if options[:notification]
|
72
|
+
end
|
73
|
+
|
74
|
+
def notify_spec_result(result, options)
|
75
|
+
specs = result['stats']['specs']
|
76
|
+
failures = result['stats']['failures']
|
77
|
+
time = result['stats']['time']
|
78
|
+
plural = failures == 1 ? '' : 's'
|
79
|
+
|
80
|
+
message = "Jasmine ran #{ specs } specs, #{ failures } failure#{ plural } in #{ time }s."
|
81
|
+
|
82
|
+
if failures != 0
|
83
|
+
notify_spec_failures(result, message, options)
|
84
|
+
else
|
85
|
+
Formatter.success(message)
|
86
|
+
Formatter.notify(message, :title => 'Jasmine results') if options[:notification] && !options[:hide_success]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def notify_spec_failures(result, stats, options)
|
91
|
+
messages = result['suites'].inject('') do |messages, suite|
|
92
|
+
suite['specs'].each do |spec|
|
93
|
+
messages << "Spec '#{ spec['description'] }' failed with '#{ spec['error_message'] }'!\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
messages
|
97
|
+
end
|
98
|
+
|
99
|
+
messages << stats
|
100
|
+
|
101
|
+
Formatter.error(messages)
|
102
|
+
Formatter.notify(messages, :title => 'Jasmine results', :image => :failed, :priority => 2) if options[:notification]
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
guard 'jasmine' do
|
2
|
+
watch(%r{app/assets/javascripts/(.+)\.(js\.coffee|js)}) { |m| "spec/javascripts/#{m[1]}_spec.#{m[2]}" }
|
3
|
+
watch(%r{spec/javascripts/(.+)_spec\.(js\.coffee|js)}) { |m| "spec/javascripts/#{m[1]}_spec.#{m[2]}" }
|
4
|
+
watch(%r{spec/javascripts/spec\.(js\.coffee|js)}) { "spec/javascripts" }
|
5
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: guard-jasmine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Michael Kessler
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-07 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: guard
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 4
|
32
|
+
version: "0.4"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: multi_json
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 17
|
44
|
+
segments:
|
45
|
+
- 1
|
46
|
+
- 0
|
47
|
+
- 3
|
48
|
+
version: 1.0.3
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: bundler
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 15
|
60
|
+
segments:
|
61
|
+
- 1
|
62
|
+
- 0
|
63
|
+
version: "1.0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: guard-rspec
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
- 4
|
78
|
+
version: "0.4"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rspec
|
83
|
+
prerelease: false
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 15
|
90
|
+
segments:
|
91
|
+
- 2
|
92
|
+
- 6
|
93
|
+
version: "2.6"
|
94
|
+
type: :development
|
95
|
+
version_requirements: *id005
|
96
|
+
description: Guard::Jasmine automatically tests your Jasmine specs on PhantomJS
|
97
|
+
email:
|
98
|
+
- michi@netzpiraten.ch
|
99
|
+
executables: []
|
100
|
+
|
101
|
+
extensions: []
|
102
|
+
|
103
|
+
extra_rdoc_files: []
|
104
|
+
|
105
|
+
files:
|
106
|
+
- lib/guard/jasmine/formatter.rb
|
107
|
+
- lib/guard/jasmine/inspector.rb
|
108
|
+
- lib/guard/jasmine/phantomjs/run-jasmine.coffee
|
109
|
+
- lib/guard/jasmine/runner.rb
|
110
|
+
- lib/guard/jasmine/templates/Guardfile
|
111
|
+
- lib/guard/jasmine/version.rb
|
112
|
+
- lib/guard/jasmine.rb
|
113
|
+
- LICENSE
|
114
|
+
- README.md
|
115
|
+
homepage: http://github.com/netzpirat/guard-jasmine
|
116
|
+
licenses: []
|
117
|
+
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
|
121
|
+
require_paths:
|
122
|
+
- lib
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
hash: 23
|
138
|
+
segments:
|
139
|
+
- 1
|
140
|
+
- 3
|
141
|
+
- 6
|
142
|
+
version: 1.3.6
|
143
|
+
requirements: []
|
144
|
+
|
145
|
+
rubyforge_project: guard-jasmine
|
146
|
+
rubygems_version: 1.8.6
|
147
|
+
signing_key:
|
148
|
+
specification_version: 3
|
149
|
+
summary: Guard gem for headless testing with Jasmine
|
150
|
+
test_files: []
|
151
|
+
|