visionmedia-jspec 2.8.4 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.rdoc +21 -0
- data/Manifest +5 -0
- data/README.rdoc +36 -10
- data/Rakefile +2 -0
- data/bin/jspec +84 -24
- data/jspec.gemspec +9 -3
- data/lib/jspec.jquery.js +3 -3
- data/lib/jspec.js +188 -134
- data/lib/jspec.xhr.js +62 -53
- data/server/browsers.rb +222 -10
- data/server/helpers.rb +82 -0
- data/server/routes.rb +57 -0
- data/server/server.rb +70 -82
- data/spec/server.rb +2 -0
- data/spec/spec.jquery.js +3 -3
- data/spec/spec.matchers.js +1 -0
- data/spec/spec.server.html +5 -5
- data/spec/spec.xhr.js +6 -1
- data/templates/default/spec/server.rb +4 -0
- data/templates/default/spec/spec.server.html +4 -4
- data/templates/rails/server.rb +4 -0
- data/templates/rails/spec.server.html +4 -4
- metadata +27 -2
data/server/browsers.rb
CHANGED
@@ -1,16 +1,228 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
#--
|
5
|
+
# Browser
|
6
|
+
#++
|
7
|
+
|
8
|
+
class Browser
|
9
|
+
|
10
|
+
##
|
11
|
+
# Check if the user agent _string_ matches this browser.
|
12
|
+
|
13
|
+
def self.matches_agent? string; end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Check if the browser matches the name _string_.
|
17
|
+
|
18
|
+
def self.matches_name? string; end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Subclasses.
|
22
|
+
|
23
|
+
def self.subclasses
|
24
|
+
@subclasses ||= []
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Stack subclasses.
|
29
|
+
|
30
|
+
def self.inherited subclass
|
31
|
+
subclasses << subclass
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Weither or not the browser is supported.
|
36
|
+
|
37
|
+
def supported?; true end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Server setup.
|
41
|
+
|
42
|
+
def setup; end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Server teardown.
|
46
|
+
|
47
|
+
def teardown; end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Host environment.
|
51
|
+
|
52
|
+
def host
|
53
|
+
Config::CONFIG['host']
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Check if we are using macos.
|
58
|
+
|
59
|
+
def macos?
|
60
|
+
host.include? 'darwin'
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Check if we are using windows.
|
65
|
+
|
66
|
+
def windows?
|
67
|
+
host.include? 'mswin'
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Check if we are using linux.
|
72
|
+
|
73
|
+
def linux?
|
74
|
+
host.include? 'linux'
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Run applescript _code_.
|
79
|
+
|
80
|
+
def applescript code
|
81
|
+
raise "Can't run AppleScript on #{host}" unless macos?
|
82
|
+
system "osascript -e '#{code}' 2>&1 >/dev/null"
|
83
|
+
end
|
84
|
+
|
85
|
+
#--
|
86
|
+
# Firefox
|
87
|
+
#++
|
88
|
+
|
89
|
+
class Firefox < self
|
90
|
+
def self.matches_agent? string
|
91
|
+
string =~ /firefox/i
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.matches_name? string
|
95
|
+
string =~ /ff|firefox|mozilla/i
|
96
|
+
end
|
97
|
+
|
98
|
+
def visit uri
|
99
|
+
system "open -g -a Firefox '#{uri}'" if macos?
|
100
|
+
system "firefox #{uri}" if linux?
|
101
|
+
system "#{File.join(ENV['ProgramFiles'] || 'c:\Program Files', '\Mozilla Firefox\firefox.exe')} #{uri}" if windows?
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
'Firefox'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#--
|
110
|
+
# Safari
|
111
|
+
#++
|
112
|
+
|
113
|
+
class Safari < self
|
114
|
+
def self.matches_agent? string
|
115
|
+
string =~ /safari/i && string !~ /chrome/i
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.matches_name? string
|
119
|
+
string =~ /safari/i
|
120
|
+
end
|
121
|
+
|
122
|
+
def supported?
|
123
|
+
macos?
|
124
|
+
end
|
125
|
+
|
126
|
+
def setup
|
127
|
+
applescript 'tell application "Safari" to make new document'
|
128
|
+
end
|
129
|
+
|
130
|
+
def visit uri
|
131
|
+
applescript 'tell application "Safari" to set URL of front document to "' + uri + '"'
|
132
|
+
end
|
133
|
+
|
134
|
+
def matches_agent? string
|
135
|
+
string =~ /safari/i
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_s
|
139
|
+
'Safari'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
#--
|
144
|
+
# Chrome
|
145
|
+
#++
|
146
|
+
|
147
|
+
class Chrome < self
|
148
|
+
def self.matches_agent? string
|
149
|
+
string =~ /chrome/i
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.matches_name? string
|
153
|
+
string =~ /google|chrome/i
|
154
|
+
end
|
155
|
+
|
156
|
+
def supported?
|
157
|
+
macos?
|
158
|
+
end
|
159
|
+
|
160
|
+
def visit uri
|
161
|
+
system "open -g -a Chromium #{uri}" if macos?
|
162
|
+
end
|
163
|
+
|
164
|
+
def to_s
|
165
|
+
'Chrome'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
#--
|
170
|
+
# Internet Explorer
|
171
|
+
#++
|
172
|
+
|
173
|
+
class IE < self
|
174
|
+
def self.matches_agent? string
|
175
|
+
string =~ /microsoft/i
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.matches_name? string
|
179
|
+
string =~ /ie|explorer/i
|
6
180
|
end
|
7
181
|
|
8
|
-
def
|
9
|
-
|
182
|
+
def supported?
|
183
|
+
windows?
|
10
184
|
end
|
11
185
|
|
12
|
-
|
13
|
-
|
14
|
-
|
186
|
+
def setup
|
187
|
+
require 'win32ole'
|
188
|
+
end
|
189
|
+
|
190
|
+
def visit uri
|
191
|
+
ie = WIN32OLE.new 'InternetExplorer.Application'
|
192
|
+
ie.visible = true
|
193
|
+
ie.Navigate uri
|
194
|
+
while ie.ReadyState != 4 do
|
195
|
+
sleep 1
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def to_s
|
200
|
+
'Internet Explorer'
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
#--
|
205
|
+
# Opera
|
206
|
+
#++
|
207
|
+
|
208
|
+
class Opera < self
|
209
|
+
def self.matches_agent? string
|
210
|
+
string =~ /opera/i
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.matches_name? string
|
214
|
+
string =~ /opera/i
|
215
|
+
end
|
216
|
+
|
217
|
+
def visit uri
|
218
|
+
system "open -g -a Opera #{uri}" if macos?
|
219
|
+
system "c:\Program Files\Opera\Opera.exe #{uri}" if windows?
|
220
|
+
system "opera #{uri}" if linux?
|
221
|
+
end
|
222
|
+
|
223
|
+
def to_s
|
224
|
+
'Opera'
|
225
|
+
end
|
15
226
|
end
|
16
|
-
|
227
|
+
|
228
|
+
end
|
data/server/helpers.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
helpers do
|
3
|
+
|
4
|
+
##
|
5
|
+
# Return dotted assertion graph for _assertions_.
|
6
|
+
|
7
|
+
def assertion_graph_for assertions
|
8
|
+
return if assertions.empty?
|
9
|
+
assertions.map do |assertion|
|
10
|
+
assertion['passed'] ? green('.') : red('.')
|
11
|
+
end.join
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Override Sinatra's #send_file to prevent caching.
|
16
|
+
|
17
|
+
def send_file path, opts = {}
|
18
|
+
stat = File.stat(path)
|
19
|
+
response['Cache-Control'] = 'no-cache'
|
20
|
+
content_type media_type(opts[:type]) ||
|
21
|
+
media_type(File.extname(path)) ||
|
22
|
+
response['Content-Type'] ||
|
23
|
+
'application/octet-stream'
|
24
|
+
response['Content-Length'] ||= (opts[:length] || stat.size).to_s
|
25
|
+
|
26
|
+
if opts[:disposition] == 'attachment' || opts[:filename]
|
27
|
+
attachment opts[:filename] || path
|
28
|
+
elsif opts[:disposition] == 'inline'
|
29
|
+
response['Content-Disposition'] = 'inline'
|
30
|
+
end
|
31
|
+
|
32
|
+
halt ::Sinatra::Application::StaticFile.open(path, 'rb')
|
33
|
+
rescue Errno::ENOENT
|
34
|
+
not_found
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Find the browser name for the current user agent.
|
39
|
+
|
40
|
+
def browser_name
|
41
|
+
Browser.subclasses.find do |browser|
|
42
|
+
browser.matches_agent? env['HTTP_USER_AGENT']
|
43
|
+
end.new
|
44
|
+
rescue
|
45
|
+
'Unknown'
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Wrap _string_ with ansi escape sequence using _code_.
|
50
|
+
|
51
|
+
def color string, code
|
52
|
+
"\e[#{code}m#{string}\e[0m"
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Bold _string_.
|
57
|
+
|
58
|
+
def bold string
|
59
|
+
color string, 1
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Color _string_ red.
|
64
|
+
|
65
|
+
def red string
|
66
|
+
color string, 31
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Color _string_ green.
|
71
|
+
|
72
|
+
def green string
|
73
|
+
color string, 32
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Color _string_ blue.
|
78
|
+
|
79
|
+
def blue string
|
80
|
+
color string, 34
|
81
|
+
end
|
82
|
+
end
|
data/server/routes.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
get '/jspec/*' do |path|
|
3
|
+
send_file JSPEC_ROOT + '/lib/' + path
|
4
|
+
end
|
5
|
+
|
6
|
+
post '/results' do
|
7
|
+
require 'json'
|
8
|
+
data = JSON.parse request.body.read
|
9
|
+
if data['options'].include?('verbose') && data['options']['verbose'] ||
|
10
|
+
data['options'].include?('failuresOnly') && data['options']['failuresOnly']
|
11
|
+
puts "\n\n %s Passes: %s Failures: %s\n\n" % [
|
12
|
+
bold(browser_name),
|
13
|
+
green(data['stats']['passes']),
|
14
|
+
red(data['stats']['failures'])]
|
15
|
+
data['results'].compact.each do |suite|
|
16
|
+
specs = suite['specs'].compact.map do |spec|
|
17
|
+
case spec['status'].to_sym
|
18
|
+
when :pass
|
19
|
+
next if data['options'].include?('failuresOnly') && data['options']['failuresOnly']
|
20
|
+
' ' + green(spec['description']) + assertion_graph_for(spec['assertions']).to_s + "\n"
|
21
|
+
when :fail
|
22
|
+
" #{red(spec['description'])}\n #{spec['message']}\n\n"
|
23
|
+
else
|
24
|
+
" #{blue(spec['description'])}\n"
|
25
|
+
end
|
26
|
+
end.join
|
27
|
+
unless specs.strip.empty?
|
28
|
+
puts "\n " + bold(suite['description'])
|
29
|
+
puts specs
|
30
|
+
end
|
31
|
+
end
|
32
|
+
else
|
33
|
+
puts "%20s Passes: %s Failures: %s" % [
|
34
|
+
bold(browser_name),
|
35
|
+
green(data['stats']['passes']),
|
36
|
+
red(data['stats']['failures'])]
|
37
|
+
end
|
38
|
+
halt 200
|
39
|
+
end
|
40
|
+
|
41
|
+
get '/*' do |path|
|
42
|
+
pass unless File.exists?(path)
|
43
|
+
send_file path
|
44
|
+
end
|
45
|
+
|
46
|
+
#--
|
47
|
+
# Simulation Routes
|
48
|
+
#++
|
49
|
+
|
50
|
+
get '/slow/*' do |seconds|
|
51
|
+
sleep seconds.to_i
|
52
|
+
halt 200
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/status/*' do |code|
|
56
|
+
halt code.to_i
|
57
|
+
end
|
data/server/server.rb
CHANGED
@@ -1,100 +1,88 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
require '
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'sinatra'
|
5
|
+
require 'thread'
|
6
|
+
require 'browsers'
|
7
|
+
require 'helpers'
|
8
|
+
require 'routes'
|
5
9
|
|
6
10
|
module JSpec
|
7
11
|
class Server
|
8
|
-
attr_reader :responses, :browsers, :root
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
@browsers = options.delete :browsers
|
13
|
-
@root = options.delete :root
|
14
|
-
end
|
13
|
+
##
|
14
|
+
# Suite HTML.
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
def browser string
|
41
|
-
case string
|
42
|
-
when /Safari/ ; :Safari
|
43
|
-
when /Firefox/ ; :Firefox
|
44
|
-
when /MSIE/ ; :MSIE
|
45
|
-
when /Opera/ ; :Opera
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def bold string
|
50
|
-
color string, 1
|
51
|
-
end
|
52
|
-
|
53
|
-
def red string
|
54
|
-
color string, 31
|
55
|
-
end
|
56
|
-
|
57
|
-
def green string
|
58
|
-
color string, 32
|
59
|
-
end
|
60
|
-
|
61
|
-
def color string, code
|
62
|
-
"\e[#{code}m#{string}\e[m"
|
16
|
+
attr_accessor :suite
|
17
|
+
|
18
|
+
##
|
19
|
+
# Host string.
|
20
|
+
|
21
|
+
attr_reader :host
|
22
|
+
|
23
|
+
##
|
24
|
+
# Port number.
|
25
|
+
|
26
|
+
attr_reader :port
|
27
|
+
|
28
|
+
##
|
29
|
+
# Server instance.
|
30
|
+
|
31
|
+
attr_reader :server
|
32
|
+
|
33
|
+
##
|
34
|
+
# Initialize.
|
35
|
+
|
36
|
+
def initialize suite, port
|
37
|
+
@suite, @port, @host = suite, port, :localhost
|
63
38
|
end
|
64
39
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
40
|
+
##
|
41
|
+
# URI formed by the given host and port.
|
42
|
+
|
43
|
+
def uri
|
44
|
+
'http://%s:%d' % [host, port]
|
70
45
|
end
|
71
46
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
47
|
+
##
|
48
|
+
# Start the server with _browsers_ which defaults to all supported browsers.
|
49
|
+
|
50
|
+
def start browsers = nil
|
51
|
+
browsers ||= Browser.subclasses.map { |browser| browser.new }
|
52
|
+
browsers.map do |browser|
|
53
|
+
Thread.new {
|
54
|
+
sleep 1
|
55
|
+
if browser.supported?
|
56
|
+
browser.setup
|
57
|
+
browser.visit uri + '/' + suite
|
58
|
+
browser.teardown
|
59
|
+
end
|
83
60
|
}
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
self
|
61
|
+
end.push(Thread.new {
|
62
|
+
start!
|
63
|
+
}).reverse.each { |thread| thread.join }
|
88
64
|
end
|
89
65
|
|
90
|
-
|
91
|
-
|
92
|
-
|
66
|
+
private
|
67
|
+
|
68
|
+
#:nodoc:
|
69
|
+
|
70
|
+
def start!
|
71
|
+
Sinatra::Application.class_eval do
|
72
|
+
begin
|
73
|
+
$stderr.puts 'Started JSpec server at http://%s:%d' % [host, port.to_i]
|
74
|
+
detect_rack_handler.run self, :Host => host, :Port => port do |server|
|
75
|
+
trap 'INT' do
|
76
|
+
server.respond_to?(:stop!) ? server.stop! : server.stop
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rescue Errno::EADDRINUSE
|
80
|
+
raise "Port #{port} already in use"
|
81
|
+
rescue Errno::EACCES
|
82
|
+
raise "Permission Denied on port #{port}"
|
83
|
+
end
|
93
84
|
end
|
94
85
|
end
|
95
86
|
|
96
|
-
def self.browser name
|
97
|
-
eval("JSpec::Browser::#{name}").new
|
98
|
-
end
|
99
87
|
end
|
100
88
|
end
|
data/spec/server.rb
ADDED
data/spec/spec.jquery.js
CHANGED
@@ -20,16 +20,16 @@ describe 'jQuery'
|
|
20
20
|
|
21
21
|
describe 'async'
|
22
22
|
it 'should load mah cookies (textfile)'
|
23
|
-
$.
|
23
|
+
$.get('async', function(text){
|
24
24
|
text.should_eql 'cookies!'
|
25
25
|
})
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'should load mah cookies twice (ensure multiple async requests work)'
|
29
|
-
$.
|
29
|
+
$.get('async', function(text){
|
30
30
|
text.should.eql 'cookies!'
|
31
31
|
})
|
32
|
-
$.
|
32
|
+
$.get('async', function(text){
|
33
33
|
text.should.not.eql 'rawr'
|
34
34
|
})
|
35
35
|
end
|
data/spec/spec.matchers.js
CHANGED
data/spec/spec.server.html
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
3
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>
|
4
|
-
<script src="jspec.js"></script>
|
5
|
-
<script src="jspec.jquery.js"></script>
|
6
|
-
<script src="jspec.xhr.js"></script>
|
4
|
+
<script src="/jspec/jspec.js"></script>
|
5
|
+
<script src="/jspec/jspec.jquery.js"></script>
|
6
|
+
<script src="/jspec/jspec.xhr.js"></script>
|
7
7
|
<script src="modules.js"></script>
|
8
8
|
<script src="spec.grammar-less.js"></script>
|
9
9
|
<script>
|
@@ -19,8 +19,8 @@
|
|
19
19
|
.exec('spec.modules.js')
|
20
20
|
.exec('spec.xhr.js')
|
21
21
|
.exec('spec.jquery.xhr.js')
|
22
|
-
.run()
|
23
|
-
.
|
22
|
+
.run({ formatter : JSpec.formatters.Server })
|
23
|
+
.report()
|
24
24
|
}
|
25
25
|
</script>
|
26
26
|
</head>
|
data/spec/spec.xhr.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
2
|
describe 'JSpec'
|
3
|
-
describe '
|
3
|
+
describe 'Mock XHR'
|
4
4
|
before
|
5
5
|
responseFrom = function(path) {
|
6
6
|
request = new XMLHttpRequest
|
@@ -10,6 +10,11 @@ describe 'JSpec'
|
|
10
10
|
}
|
11
11
|
end
|
12
12
|
|
13
|
+
it 'should provide snake DSL methods'
|
14
|
+
mock_request.should.equal mockRequest
|
15
|
+
unmock_request.should.equal unmockRequest
|
16
|
+
end
|
17
|
+
|
13
18
|
it 'should mock XMLHttpRequests if unmockRequest() is called or the spec block has finished'
|
14
19
|
original = XMLHttpRequest
|
15
20
|
mockRequest().and_return('test')
|
@@ -1,13 +1,13 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
|
-
<script src="jspec.js"></script>
|
4
|
-
<script src="
|
3
|
+
<script src="/jspec/jspec.js"></script>
|
4
|
+
<script src="/lib/yourlib.core.js"></script>
|
5
5
|
<script>
|
6
6
|
function runSuites() {
|
7
7
|
JSpec
|
8
8
|
.exec('spec.core.js')
|
9
|
-
.run()
|
10
|
-
.
|
9
|
+
.run({ formatter : JSpec.formatters.Server, verbose: true, failuresOnly: true })
|
10
|
+
.report()
|
11
11
|
}
|
12
12
|
</script>
|
13
13
|
</head>
|
@@ -1,13 +1,13 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
|
-
<script src="jspec.js"></script>
|
4
|
-
<script src="
|
3
|
+
<script src="/jspec/jspec.js"></script>
|
4
|
+
<script src="/public/javascripts/application.js"></script>
|
5
5
|
<script>
|
6
6
|
function runSuites() {
|
7
7
|
JSpec
|
8
8
|
.exec('spec.application.js')
|
9
|
-
.run()
|
10
|
-
.
|
9
|
+
.run({ formatter : JSpec.formatters.Server, verbose: true, failuresOnly: true })
|
10
|
+
.report()
|
11
11
|
}
|
12
12
|
</script>
|
13
13
|
</head>
|