jspec 2.11.2
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 +522 -0
- data/Manifest +57 -0
- data/README.rdoc +825 -0
- data/Rakefile +75 -0
- data/bin/jspec +305 -0
- data/jspec.gemspec +44 -0
- data/lib/images/bg.png +0 -0
- data/lib/images/hr.png +0 -0
- data/lib/images/loading.gif +0 -0
- data/lib/images/sprites.bg.png +0 -0
- data/lib/images/sprites.png +0 -0
- data/lib/images/vr.png +0 -0
- data/lib/jspec.css +145 -0
- data/lib/jspec.jquery.js +71 -0
- data/lib/jspec.js +1771 -0
- data/lib/jspec.shell.js +36 -0
- data/lib/jspec.timers.js +90 -0
- data/lib/jspec.xhr.js +183 -0
- data/server/browsers.rb +228 -0
- data/server/helpers.rb +82 -0
- data/server/routes.rb +57 -0
- data/server/server.rb +88 -0
- data/spec/async +1 -0
- data/spec/env.js +695 -0
- data/spec/fixtures/test.html +1 -0
- data/spec/fixtures/test.json +1 -0
- data/spec/fixtures/test.xml +5 -0
- data/spec/helpers.js +66 -0
- data/spec/server.rb +2 -0
- data/spec/spec.dom.html +34 -0
- data/spec/spec.fixtures.js +18 -0
- data/spec/spec.grammar-less.js +34 -0
- data/spec/spec.grammar.js +226 -0
- data/spec/spec.jquery.js +176 -0
- data/spec/spec.jquery.xhr.js +65 -0
- data/spec/spec.js +166 -0
- data/spec/spec.matchers.js +493 -0
- data/spec/spec.modules.js +67 -0
- data/spec/spec.node.js +46 -0
- data/spec/spec.rhino.js +17 -0
- data/spec/spec.server.html +29 -0
- data/spec/spec.shared-behaviors.js +80 -0
- data/spec/spec.utils.js +279 -0
- data/spec/spec.xhr.js +156 -0
- data/templates/default/History.rdoc +4 -0
- data/templates/default/README.rdoc +29 -0
- data/templates/default/lib/yourlib.core.js +2 -0
- data/templates/default/spec/server.rb +4 -0
- data/templates/default/spec/spec.core.js +8 -0
- data/templates/default/spec/spec.dom.html +20 -0
- data/templates/default/spec/spec.rhino.js +8 -0
- data/templates/default/spec/spec.server.html +16 -0
- data/templates/rails/server.rb +4 -0
- data/templates/rails/spec.application.js +8 -0
- data/templates/rails/spec.dom.html +20 -0
- data/templates/rails/spec.rhino.js +8 -0
- data/templates/rails/spec.server.html +16 -0
- metadata +168 -0
data/lib/jspec.shell.js
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
// JSpec - Shell - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
3
|
+
|
4
|
+
;(function(){
|
5
|
+
|
6
|
+
var _quit = quit
|
7
|
+
|
8
|
+
Shell = {
|
9
|
+
|
10
|
+
// --- Global
|
11
|
+
|
12
|
+
main: this,
|
13
|
+
|
14
|
+
// --- Commands
|
15
|
+
|
16
|
+
commands: {
|
17
|
+
quit: ['Terminate the shell', function(){ _quit() }],
|
18
|
+
exit: ['Terminate the shell', function(){ _quit() }]
|
19
|
+
},
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Start the interactive shell.
|
23
|
+
*
|
24
|
+
* @api public
|
25
|
+
*/
|
26
|
+
|
27
|
+
start : function() {
|
28
|
+
for (var name in this.commands)
|
29
|
+
if (this.commands.hasOwnProperty(name))
|
30
|
+
this.main.__defineGetter__(name, this.commands[name][1])
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
Shell.start()
|
35
|
+
|
36
|
+
})()
|
data/lib/jspec.timers.js
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
// JSpec - Mock Timers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
3
|
+
|
4
|
+
;(function(){
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Version.
|
8
|
+
*/
|
9
|
+
|
10
|
+
mockTimersVersion = '1.0.1'
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Localized timer stack.
|
14
|
+
*/
|
15
|
+
|
16
|
+
var timers = []
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Set mock timeout with _callback_ and timeout of _ms_.
|
20
|
+
*
|
21
|
+
* @param {function} callback
|
22
|
+
* @param {int} ms
|
23
|
+
* @return {int}
|
24
|
+
* @api public
|
25
|
+
*/
|
26
|
+
|
27
|
+
setTimeout = function(callback, ms) {
|
28
|
+
var id
|
29
|
+
return id = setInterval(function(){
|
30
|
+
callback()
|
31
|
+
clearInterval(id)
|
32
|
+
}, ms)
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Set mock interval with _callback_ and interval of _ms_.
|
37
|
+
*
|
38
|
+
* @param {function} callback
|
39
|
+
* @param {int} ms
|
40
|
+
* @return {int}
|
41
|
+
* @api public
|
42
|
+
*/
|
43
|
+
|
44
|
+
setInterval = function(callback, ms) {
|
45
|
+
callback.step = ms, callback.current = callback.last = 0
|
46
|
+
return timers[timers.length] = callback, timers.length
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Destroy timer with _id_.
|
51
|
+
*
|
52
|
+
* @param {int} id
|
53
|
+
* @return {bool}
|
54
|
+
* @api public
|
55
|
+
*/
|
56
|
+
|
57
|
+
clearInterval = function(id) {
|
58
|
+
return delete timers[--id]
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Reset timers.
|
63
|
+
*
|
64
|
+
* @return {array}
|
65
|
+
* @api public
|
66
|
+
*/
|
67
|
+
|
68
|
+
resetTimers = function() {
|
69
|
+
return timers = []
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Increment each timers internal clock by _ms_.
|
74
|
+
*
|
75
|
+
* @param {int} ms
|
76
|
+
* @api public
|
77
|
+
*/
|
78
|
+
|
79
|
+
tick = function(ms) {
|
80
|
+
for (var i = 0, len = timers.length; i < len; ++i)
|
81
|
+
if (timers[i] && (timers[i].current += ms))
|
82
|
+
if (timers[i].current - timers[i].last >= timers[i].step) {
|
83
|
+
var times = Math.floor((timers[i].current - timers[i].last) / timers[i].step)
|
84
|
+
var remainder = (timers[i].current - timers[i].last) % timers[i].step
|
85
|
+
timers[i].last = timers[i].current - remainder
|
86
|
+
while (times-- && timers[i]) timers[i]()
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
})()
|
data/lib/jspec.xhr.js
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
|
2
|
+
// JSpec - XHR - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
3
|
+
|
4
|
+
(function(){
|
5
|
+
|
6
|
+
// --- Original XMLHttpRequest
|
7
|
+
|
8
|
+
var OriginalXMLHttpRequest = 'XMLHttpRequest' in this ?
|
9
|
+
XMLHttpRequest :
|
10
|
+
function(){}
|
11
|
+
|
12
|
+
// --- MockXMLHttpRequest
|
13
|
+
|
14
|
+
var MockXMLHttpRequest = function() {
|
15
|
+
this.requestHeaders = {}
|
16
|
+
}
|
17
|
+
|
18
|
+
MockXMLHttpRequest.prototype = {
|
19
|
+
status: 0,
|
20
|
+
async: true,
|
21
|
+
readyState: 0,
|
22
|
+
responseText: '',
|
23
|
+
abort: function(){},
|
24
|
+
onreadystatechange: function(){},
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Return response headers hash.
|
28
|
+
*/
|
29
|
+
|
30
|
+
getAllResponseHeaders : function(){
|
31
|
+
return this.responseHeaders
|
32
|
+
},
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Return case-insensitive value for header _name_.
|
36
|
+
*/
|
37
|
+
|
38
|
+
getResponseHeader : function(name) {
|
39
|
+
return this.responseHeaders[name.toLowerCase()]
|
40
|
+
},
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Set case-insensitive _value_ for header _name_.
|
44
|
+
*/
|
45
|
+
|
46
|
+
setRequestHeader : function(name, value) {
|
47
|
+
this.requestHeaders[name.toLowerCase()] = value
|
48
|
+
},
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Open mock request.
|
52
|
+
*/
|
53
|
+
|
54
|
+
open : function(method, url, async, user, password) {
|
55
|
+
this.user = user
|
56
|
+
this.password = password
|
57
|
+
this.url = url
|
58
|
+
this.readyState = 1
|
59
|
+
this.method = method.toUpperCase()
|
60
|
+
if (async != undefined) this.async = async
|
61
|
+
if (this.async) this.onreadystatechange()
|
62
|
+
},
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Send request _data_.
|
66
|
+
*/
|
67
|
+
|
68
|
+
send : function(data) {
|
69
|
+
this.data = data
|
70
|
+
this.readyState = 4
|
71
|
+
if (this.method == 'HEAD') this.responseText = null
|
72
|
+
this.responseHeaders['content-length'] = (this.responseText || '').length
|
73
|
+
if(this.async) this.onreadystatechange()
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
// --- Response status codes
|
78
|
+
|
79
|
+
JSpec.statusCodes = {
|
80
|
+
100: 'Continue',
|
81
|
+
101: 'Switching Protocols',
|
82
|
+
200: 'OK',
|
83
|
+
201: 'Created',
|
84
|
+
202: 'Accepted',
|
85
|
+
203: 'Non-Authoritative Information',
|
86
|
+
204: 'No Content',
|
87
|
+
205: 'Reset Content',
|
88
|
+
206: 'Partial Content',
|
89
|
+
300: 'Multiple Choice',
|
90
|
+
301: 'Moved Permanently',
|
91
|
+
302: 'Found',
|
92
|
+
303: 'See Other',
|
93
|
+
304: 'Not Modified',
|
94
|
+
305: 'Use Proxy',
|
95
|
+
307: 'Temporary Redirect',
|
96
|
+
400: 'Bad Request',
|
97
|
+
401: 'Unauthorized',
|
98
|
+
402: 'Payment Required',
|
99
|
+
403: 'Forbidden',
|
100
|
+
404: 'Not Found',
|
101
|
+
405: 'Method Not Allowed',
|
102
|
+
406: 'Not Acceptable',
|
103
|
+
407: 'Proxy Authentication Required',
|
104
|
+
408: 'Request Timeout',
|
105
|
+
409: 'Conflict',
|
106
|
+
410: 'Gone',
|
107
|
+
411: 'Length Required',
|
108
|
+
412: 'Precondition Failed',
|
109
|
+
413: 'Request Entity Too Large',
|
110
|
+
414: 'Request-URI Too Long',
|
111
|
+
415: 'Unsupported Media Type',
|
112
|
+
416: 'Requested Range Not Satisfiable',
|
113
|
+
417: 'Expectation Failed',
|
114
|
+
422: 'Unprocessable Entity',
|
115
|
+
500: 'Internal Server Error',
|
116
|
+
501: 'Not Implemented',
|
117
|
+
502: 'Bad Gateway',
|
118
|
+
503: 'Service Unavailable',
|
119
|
+
504: 'Gateway Timeout',
|
120
|
+
505: 'HTTP Version Not Supported'
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Mock XMLHttpRequest requests.
|
125
|
+
*
|
126
|
+
* mockRequest().and_return('some data', 'text/plain', 200, { 'X-SomeHeader' : 'somevalue' })
|
127
|
+
*
|
128
|
+
* @return {hash}
|
129
|
+
* @api public
|
130
|
+
*/
|
131
|
+
|
132
|
+
function mockRequest() {
|
133
|
+
return { and_return : function(body, type, status, headers) {
|
134
|
+
XMLHttpRequest = MockXMLHttpRequest
|
135
|
+
status = status || 200
|
136
|
+
headers = headers || {}
|
137
|
+
headers['content-type'] = type
|
138
|
+
JSpec.extend(XMLHttpRequest.prototype, {
|
139
|
+
responseText: body,
|
140
|
+
responseHeaders: headers,
|
141
|
+
status: status,
|
142
|
+
statusText: JSpec.statusCodes[status]
|
143
|
+
})
|
144
|
+
}}
|
145
|
+
}
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Unmock XMLHttpRequest requests.
|
149
|
+
*
|
150
|
+
* @api public
|
151
|
+
*/
|
152
|
+
|
153
|
+
function unmockRequest() {
|
154
|
+
XMLHttpRequest = OriginalXMLHttpRequest
|
155
|
+
}
|
156
|
+
|
157
|
+
JSpec.include({
|
158
|
+
name: 'Mock XHR',
|
159
|
+
|
160
|
+
// --- Utilities
|
161
|
+
|
162
|
+
utilities : {
|
163
|
+
mockRequest: mockRequest,
|
164
|
+
unmockRequest: unmockRequest
|
165
|
+
},
|
166
|
+
|
167
|
+
// --- Hooks
|
168
|
+
|
169
|
+
afterSpec : function() {
|
170
|
+
this.utilities.unmockRequest()
|
171
|
+
},
|
172
|
+
|
173
|
+
// --- DSLs
|
174
|
+
|
175
|
+
DSLs : {
|
176
|
+
snake : {
|
177
|
+
mock_request: mockRequest,
|
178
|
+
unmock_request: unmockRequest
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
})
|
183
|
+
})()
|
data/server/browsers.rb
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
|
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
|
180
|
+
end
|
181
|
+
|
182
|
+
def supported?
|
183
|
+
windows?
|
184
|
+
end
|
185
|
+
|
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
|
226
|
+
end
|
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
|