seaweed 0.1.1 → 0.1.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/bin/seaweed +23 -50
- data/lib/Spec/ObjectExtensions.coffee +53 -0
- data/lib/Spec/WindowExtensions.coffee +252 -0
- data/lib/Spec.coffee +127 -239
- data/lib/seaweed/runner.rb +31 -0
- data/lib/seaweed/server.rb +33 -0
- data/lib/seaweed/version.rb +3 -0
- data/lib/seaweed.rb +121 -0
- data/public/ie.js +5 -0
- data/{server/public/jquery-1.5.js → public/jquery-1.5.2.js} +771 -573
- data/public/jsdiff.js +161 -0
- data/public/spec.css +29 -0
- data/views/index.slim +15 -0
- metadata +113 -113
- data/lib/required_files.rb +0 -62
- data/server/public/spec.css +0 -9
- data/server/server.rb +0 -88
- data/server/views/index.slim +0 -11
data/lib/Spec.coffee
CHANGED
@@ -1,17 +1,13 @@
|
|
1
|
+
#= require Spec/ObjectExtensions
|
2
|
+
#= require Spec/WindowExtensions
|
3
|
+
|
1
4
|
# Seaweed Coffeescript spec framework
|
2
|
-
|
5
|
+
|
6
|
+
window.Spec ||= {}
|
7
|
+
$.extend window.Spec, {
|
3
8
|
EnvironmentInitialized: false
|
4
|
-
|
5
|
-
|
6
|
-
Pad: (string, times) ->
|
7
|
-
for i in [1..times]
|
8
|
-
string = ' ' + string
|
9
|
-
string
|
10
|
-
|
11
|
-
# Escapes text for HTML
|
12
|
-
Escape: (string) ->
|
13
|
-
$('<div/>').text(String(string)).html()
|
14
|
-
|
9
|
+
_extended: []
|
10
|
+
|
15
11
|
# Executes a test case
|
16
12
|
describe: (title, definition) ->
|
17
13
|
@initializeEnvironment() unless @EnvironmentInitialized
|
@@ -32,6 +28,50 @@ window.Spec = {
|
|
32
28
|
|
33
29
|
definition()
|
34
30
|
|
31
|
+
# Tries to format definition source code as readable test description
|
32
|
+
descriptionize: (definition) ->
|
33
|
+
# Get function source code
|
34
|
+
definition = String definition
|
35
|
+
|
36
|
+
# Remove function boilerplate from beginning
|
37
|
+
definition = definition.replace(/^\s*function\s*\([^\)]*\)\s*\{\s*(return\s*)?/, '')
|
38
|
+
|
39
|
+
# Remove function boilerplate from end
|
40
|
+
definition = definition.replace(/\s*;\s*\}\s*$/, '')
|
41
|
+
|
42
|
+
# Replace symbols with whitespace
|
43
|
+
definition = definition.replace(/[\s\(\)_\-\.'"]+/g, ' ')
|
44
|
+
|
45
|
+
# Split camelCased terms into seperate words
|
46
|
+
definition = definition.replace(/([a-z])([A-Z])/g, (s, a, b) -> "#{a} #{b.toLowerCase()}")
|
47
|
+
|
48
|
+
definition
|
49
|
+
|
50
|
+
# Escapes text for HTML
|
51
|
+
escape: (string) ->
|
52
|
+
$('<div/>').text(String(string)).html()
|
53
|
+
|
54
|
+
# Extends a one or more classes with test methods
|
55
|
+
extend: () ->
|
56
|
+
for klass in arguments
|
57
|
+
@_extended.push klass
|
58
|
+
$.extend klass, @ObjectExtensions
|
59
|
+
$.extend klass.prototype, @ObjectExtensions if klass.prototype
|
60
|
+
|
61
|
+
# Fails test, with an error message
|
62
|
+
fail: (message, location) ->
|
63
|
+
@passed = false
|
64
|
+
@error = message
|
65
|
+
titles = []
|
66
|
+
for item in @testStack
|
67
|
+
titles.push item.title
|
68
|
+
titles.push @testTitle
|
69
|
+
@errors.push {
|
70
|
+
title: titles.join ' '
|
71
|
+
message: message
|
72
|
+
location: location
|
73
|
+
}
|
74
|
+
|
35
75
|
# Displays a summary of error rate at the end of testing
|
36
76
|
finalize: ->
|
37
77
|
summary = "#{@counts.passed} passed, #{@counts.failed} failed, #{@counts.pending} pending, #{@counts.total} total"
|
@@ -40,9 +80,12 @@ window.Spec = {
|
|
40
80
|
document.title = summary
|
41
81
|
if @errors.length
|
42
82
|
$('<h3>Errors</h3>').appendTo document.body
|
43
|
-
|
83
|
+
|
84
|
+
html = ['<table class="errors"><thead><tr><th>Error</th><th>Location</th><th>Test</th></tr></thead><tbody>']
|
44
85
|
for error in @errors
|
45
|
-
|
86
|
+
html.push '<tr><td>', error.message, '</td><td>', error.location, '</td><td>', error.title, '</td></tr>'
|
87
|
+
html.push '</tbody></table>'
|
88
|
+
$(document.body).append html.join('')
|
46
89
|
when 'terminal'
|
47
90
|
$('.results').append "<br>"
|
48
91
|
for error in @errors
|
@@ -55,10 +98,25 @@ window.Spec = {
|
|
55
98
|
32
|
56
99
|
$('.results').append "[1m[#{color}m#{summary}[0m<br>"
|
57
100
|
|
101
|
+
# Finds a matcher specified by a string, or passes through a matcher
|
102
|
+
# specified directly.
|
103
|
+
findMatcher: (value) ->
|
104
|
+
if typeof value is 'string'
|
105
|
+
if found = value.match(/^be([A-Z]\w*)$/)
|
106
|
+
beAttribute found[1].replace(/^[A-Z]/, (s) -> s.toLowerCase())
|
107
|
+
else if window[value]
|
108
|
+
window[value]
|
109
|
+
else
|
110
|
+
null
|
111
|
+
else
|
112
|
+
value
|
113
|
+
|
58
114
|
# Extends the environment with test methods
|
59
115
|
initializeEnvironment: ->
|
60
116
|
@EnvironmentInitialized = true
|
61
|
-
|
117
|
+
|
118
|
+
$.extend window, @WindowExtensions
|
119
|
+
|
62
120
|
@errors = []
|
63
121
|
@counts = {
|
64
122
|
passed: 0
|
@@ -66,243 +124,73 @@ window.Spec = {
|
|
66
124
|
pending: 0
|
67
125
|
total: 0
|
68
126
|
}
|
69
|
-
|
127
|
+
|
70
128
|
@Format = 'ul'
|
71
129
|
@Format = 'terminal' if location.hash == '#terminal'
|
72
|
-
|
130
|
+
|
73
131
|
# Add results display element to the page
|
74
132
|
switch @Format
|
75
133
|
when 'ul'
|
76
134
|
$('body').append('<ul class="results"></ul>')
|
77
135
|
when 'terminal'
|
78
136
|
$('body').append('<div class="results"></div>')
|
79
|
-
|
80
|
-
# Tests for a positive match
|
81
|
-
Object.prototype.should = (matcher) ->
|
82
|
-
result = matcher(this)
|
83
|
-
Spec.fail "expected #{result[1]}" unless result[0]
|
84
|
-
|
85
|
-
# Tests for a negative match
|
86
|
-
Object.prototype.shouldNot = (matcher) ->
|
87
|
-
result = matcher(this)
|
88
|
-
Spec.fail "expected not #{result[1]}" if result[0]
|
89
|
-
|
90
|
-
# Sets up an expectation
|
91
|
-
window.expectation = (message) ->
|
92
|
-
exp = {
|
93
|
-
message: message
|
94
|
-
meet: -> @met++
|
95
|
-
met: 0
|
96
|
-
desired: 1
|
97
|
-
twice: ->
|
98
|
-
@desired = 2
|
99
|
-
this
|
100
|
-
exactly: (times) ->
|
101
|
-
@desired = times
|
102
|
-
{times: this}
|
103
|
-
timesString: (times) ->
|
104
|
-
switch times
|
105
|
-
when 0
|
106
|
-
'not at all'
|
107
|
-
when 1
|
108
|
-
'once'
|
109
|
-
when 2
|
110
|
-
'twice'
|
111
|
-
else
|
112
|
-
"#{times} times"
|
113
|
-
check: ->
|
114
|
-
if @met != @desired
|
115
|
-
Spec.fail "expected #{message} #{@timesString @desired}, actually received #{@timesString @met}"
|
116
|
-
}
|
117
|
-
Spec.expectations.push exp
|
118
|
-
exp
|
119
|
-
|
120
|
-
# Creates a stub method with an expectation
|
121
|
-
Object.prototype.shouldReceive = (name) ->
|
122
|
-
object = this
|
123
|
-
|
124
|
-
received = expectation "to receive “#{name}”"
|
125
|
-
|
126
|
-
passthrough = object[name]
|
127
|
-
object[name] = -> received.meet()
|
128
|
-
|
129
|
-
received.with = (expectArgs...) ->
|
130
|
-
object[name] = (args...) ->
|
131
|
-
received.meet()
|
132
|
-
correct = true
|
133
|
-
correct = false if expectArgs.length != args.length
|
134
|
-
if correct
|
135
|
-
for i in [0..args.length]
|
136
|
-
correct = false unless String(expectArgs[i]) == String(args[i])
|
137
|
-
unless correct
|
138
|
-
Spec.fail "expected ##{name} to be called with arguments “#{expectArgs.join ', '}”, actual arguments: “#{args.join ', '}”"
|
139
|
-
received
|
140
|
-
|
141
|
-
received.andReturn = (returnValue) ->
|
142
|
-
fn = object[name]
|
143
|
-
object[name] = ->
|
144
|
-
fn.apply this, arguments
|
145
|
-
returnValue
|
146
|
-
received
|
147
|
-
|
148
|
-
received.andPassthrough = ->
|
149
|
-
fn = object[name]
|
150
|
-
object[name] = ->
|
151
|
-
fn.apply this, arguments
|
152
|
-
passthrough.apply this, arguments
|
153
|
-
received
|
154
|
-
|
155
|
-
received
|
156
|
-
|
157
|
-
# Creates a stub method, with an expectation of no calls
|
158
|
-
Object.prototype.shouldNotReceive = (name) ->
|
159
|
-
@shouldReceive(name).exactly(0).times
|
160
|
-
|
161
|
-
# Allows an assertion on a non-object value
|
162
|
-
window.expect = (object) ->
|
163
|
-
{
|
164
|
-
to: (matcher) ->
|
165
|
-
result = matcher(object)
|
166
|
-
Spec.fail "expected #{result[1]}" unless result[0]
|
167
|
-
notTo: (matcher) ->
|
168
|
-
result = matcher(object)
|
169
|
-
Spec.fail "expected not #{result[1]}" if result[0]
|
170
|
-
}
|
171
|
-
|
172
|
-
# Adds a setup step to the current test case
|
173
|
-
window.beforeEach = (action) ->
|
174
|
-
test = Spec.testStack[Spec.testStack.length - 1]
|
175
|
-
test.before.push action
|
176
|
-
|
177
|
-
# Prepares a sub-test of the current test case
|
178
|
-
window.describe = window.context = (title, definition) ->
|
179
|
-
parent = Spec.testStack[Spec.testStack.length - 1]
|
180
|
-
|
181
|
-
ul = $('<ul></ul>')
|
182
|
-
switch Spec.Format
|
183
|
-
when 'ul'
|
184
|
-
parent.ul.append($('<li>' + title + '</li>').append(ul))
|
185
|
-
when 'terminal'
|
186
|
-
$('.results').append(Spec.Pad(title, parent.ul.depth) + "<br>")
|
187
|
-
ul.depth = parent.ul.depth + 2
|
188
|
-
|
189
|
-
Spec.testStack.push {
|
190
|
-
title: title
|
191
|
-
ul: ul
|
192
|
-
before: []
|
193
|
-
}
|
194
|
-
definition()
|
195
|
-
Spec.testStack.pop()
|
196
|
-
|
197
|
-
# Creates a specificaition
|
198
|
-
window.it = (title, definition) ->
|
199
|
-
test = Spec.testStack[Spec.testStack.length - 1]
|
200
|
-
status = if definition?
|
201
|
-
env = {sandbox: $('<div/>').appendTo document.body}
|
202
|
-
for aTest in Spec.testStack
|
203
|
-
for action in aTest.before
|
204
|
-
action.call env
|
205
|
-
|
206
|
-
Spec.expectations = []
|
207
|
-
Spec.testTitle = title
|
208
|
-
|
209
|
-
window.onerror = (message) ->
|
210
|
-
Spec.fail "Error: #{message}"
|
211
|
-
|
212
|
-
Spec.passed = true
|
213
|
-
try
|
214
|
-
definition.call env
|
215
|
-
catch e
|
216
|
-
Spec.fail 'Error: ' + e
|
217
|
-
|
218
|
-
for expectation in Spec.expectations
|
219
|
-
expectation.check()
|
220
|
-
|
221
|
-
delete Spec.expectations
|
222
|
-
delete Spec.testTitle
|
223
|
-
delete window.onerror
|
224
|
-
|
225
|
-
env.sandbox.empty().remove()
|
226
|
-
|
227
|
-
if Spec.passed then "passed"; else "failed"
|
228
|
-
else
|
229
|
-
"pending"
|
230
137
|
|
231
|
-
|
232
|
-
|
233
|
-
li = $('<li>' + title + '</li>')
|
234
|
-
li.addClass status
|
235
|
-
|
236
|
-
test.ul.append li
|
237
|
-
when 'terminal'
|
238
|
-
s = title
|
239
|
-
color = switch status
|
240
|
-
when 'passed' then 32
|
241
|
-
when 'failed' then 31
|
242
|
-
when 'pending' then 33
|
243
|
-
$('.results').append Spec.Pad("[#{color}m#{s}[0m<br>", test.ul.depth)
|
244
|
-
|
245
|
-
Spec.counts[status]++
|
246
|
-
Spec.counts.total++
|
247
|
-
|
248
|
-
# Tests if matched value is a function
|
249
|
-
window.beAFunction = (value) ->
|
250
|
-
[typeof value is 'function', "to have type “function”, actual “#{typeof value}”"]
|
251
|
-
|
252
|
-
# Tests if matched value === expected value
|
253
|
-
window.be = (expected) ->
|
254
|
-
(value) ->
|
255
|
-
[value is expected, "to be “#{Spec.Escape expected}”, actual “#{Spec.Escape value}”"]
|
256
|
-
|
257
|
-
# Tests if matched value is boolean true
|
258
|
-
window.beTrue = (value) ->
|
259
|
-
[String(value) == 'true', "to be true, got “#{Spec.Escape value}”"]
|
260
|
-
|
261
|
-
# Tests if matched value is boolean false
|
262
|
-
window.beFalse = (value) ->
|
263
|
-
[String(value) == 'false', "to be false, got “#{Spec.Escape value}”"]
|
264
|
-
|
265
|
-
# Tests if matched value is an instance of class
|
266
|
-
window.beAnInstanceOf = (klass) ->
|
267
|
-
(value) ->
|
268
|
-
[value instanceof klass, "to be an instance of “#{klass}”"]
|
269
|
-
|
270
|
-
# Tests if matched value == expected value
|
271
|
-
window.equal = (expected) ->
|
272
|
-
(value) ->
|
273
|
-
[String(value) == String(expected), "to equal “#{Spec.Escape expected}”, actual “#{Spec.Escape value}”"]
|
138
|
+
@extend Array, Boolean, Date, Element, Function, jQuery, Number, RegExp,
|
139
|
+
SpecObject, String
|
274
140
|
|
275
|
-
#
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
141
|
+
# Returns an HTML representation of any kind of object
|
142
|
+
inspect: (object) ->
|
143
|
+
if object instanceof Array
|
144
|
+
s = '['
|
145
|
+
first = true
|
146
|
+
for item in object
|
147
|
+
if first
|
148
|
+
first = false
|
149
|
+
else
|
150
|
+
first += ', '
|
151
|
+
s += '“' + @escape(String(item)) + '”'
|
152
|
+
s + ']'
|
153
|
+
else if object is null
|
154
|
+
'null'
|
155
|
+
else if object is undefined
|
156
|
+
'undefined'
|
157
|
+
else if object is true
|
158
|
+
'true'
|
159
|
+
else if object is false
|
160
|
+
'false'
|
161
|
+
else if typeof object == 'object'
|
162
|
+
s = "{"
|
163
|
+
first = true
|
164
|
+
for key of object
|
165
|
+
# Access hasOwnProperty through Object.prototype to work around bug
|
166
|
+
# in IE6/7/8 when calling hasOwnProperty on a DOM element
|
167
|
+
if Object.prototype.hasOwnProperty.call(object, key)
|
168
|
+
if first
|
169
|
+
first = false
|
170
|
+
else
|
171
|
+
s += ", "
|
172
|
+
s += @escape(key) + ': “' + @escape(String(object[key])) + '”'
|
173
|
+
s + "}"
|
174
|
+
else
|
175
|
+
"“#{@escape(object)}”"
|
287
176
|
|
177
|
+
# Adds indentation to a string
|
178
|
+
pad: (string, times) ->
|
179
|
+
for i in [1..times]
|
180
|
+
string = ' ' + string
|
181
|
+
string
|
182
|
+
|
288
183
|
# Cleans test environment initialized with #initializeEnvironment
|
289
184
|
uninitializeEnvironment: ->
|
290
185
|
@EnvironmentInitialized = false
|
291
186
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
delete window.it
|
302
|
-
delete window.beAFunction
|
303
|
-
delete window.be
|
304
|
-
delete window.beTrue
|
305
|
-
delete window.beFalse
|
306
|
-
delete window.beAnInstanceOf
|
307
|
-
delete window.equal
|
187
|
+
for klass in @_extended
|
188
|
+
for key of @ObjectExtensions
|
189
|
+
delete klass[key]
|
190
|
+
delete klass.prototype[key] if klass.prototype
|
191
|
+
|
192
|
+
@_extended.length = 0
|
193
|
+
|
194
|
+
for key of @WindowExtensions
|
195
|
+
delete window[key]
|
308
196
|
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../seaweed')
|
4
|
+
|
5
|
+
module Seaweed
|
6
|
+
class Runner
|
7
|
+
def initialize mode, options={}, parser=nil
|
8
|
+
Seaweed.load_configuration
|
9
|
+
|
10
|
+
Seaweed.port = options[:port] if options[:port]
|
11
|
+
|
12
|
+
if options[:version]
|
13
|
+
puts "Seaweed Version #{Seaweed::VERSION}"
|
14
|
+
else
|
15
|
+
case mode
|
16
|
+
when 's', 'server'
|
17
|
+
Seaweed.start_server
|
18
|
+
when 't', 'terminal'
|
19
|
+
Seaweed.spawn_server
|
20
|
+
Seaweed.run_suite
|
21
|
+
when 'a', 'auto'
|
22
|
+
Seaweed.spawn_server
|
23
|
+
Seaweed.run_suite
|
24
|
+
Seaweed.watch_for_changes
|
25
|
+
else
|
26
|
+
puts parser || "Unknown mode “#{mode}”"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'sinatra'
|
4
|
+
require 'slim'
|
5
|
+
require 'coffee-script'
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/../seaweed')
|
7
|
+
|
8
|
+
module Seaweed
|
9
|
+
class Server < Sinatra::Application
|
10
|
+
# Configure paths
|
11
|
+
set :public_folder, ROOT + '/public'
|
12
|
+
set :views, ROOT + '/views'
|
13
|
+
|
14
|
+
# Configure slim for prettier code formatting
|
15
|
+
Slim::Engine.set_default_options :pretty => true
|
16
|
+
|
17
|
+
# Hide redundant log messages
|
18
|
+
disable :logging
|
19
|
+
|
20
|
+
# Processes request for page index
|
21
|
+
get "/" do
|
22
|
+
# Fetch list of all specification files in specs path
|
23
|
+
@scripts = []
|
24
|
+
Seaweed.specs.each do |path|
|
25
|
+
Dir["#{Seaweed::PROJECT_ROOT}/#{path}/**/*.spec.coffee"].each do |file|
|
26
|
+
@scripts << $1 if file.match Regexp.new("^#{Regexp.escape Seaweed::PROJECT_ROOT}\\/#{Regexp.escape path}\\/(.*).coffee$")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
render :slim, :index
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/seaweed.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'sprockets'
|
5
|
+
require 'net/http'
|
6
|
+
require 'rack'
|
7
|
+
require 'yaml'
|
8
|
+
require 'seaweed/version'
|
9
|
+
|
10
|
+
module Seaweed
|
11
|
+
ROOT = File.expand_path File.join(File.dirname(__FILE__), '..')
|
12
|
+
PROJECT_ROOT = File.expand_path "."
|
13
|
+
|
14
|
+
CONFIG_PATHS = [
|
15
|
+
File.join(PROJECT_ROOT, 'seaweed.yml'),
|
16
|
+
File.join(PROJECT_ROOT, 'config', 'seaweed.yml')
|
17
|
+
]
|
18
|
+
|
19
|
+
@configuration = {}
|
20
|
+
|
21
|
+
def self.load_configuration
|
22
|
+
# Set configuration defaults
|
23
|
+
@configuration['port'] = 4567
|
24
|
+
@configuration['libs'] = ['lib']
|
25
|
+
@configuration['specs'] = ['spec']
|
26
|
+
|
27
|
+
# Load custom configuration file
|
28
|
+
CONFIG_PATHS.each do |path|
|
29
|
+
if File.exists? path
|
30
|
+
@configuration.merge! YAML.load(File.read(path))
|
31
|
+
puts "Loaded configuration from “#{path}”"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.port
|
37
|
+
@configuration['port']
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.port= value
|
41
|
+
@configuration['port'] = value
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.root_url
|
45
|
+
"http://localhost:#{port}/"
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.libs
|
49
|
+
@configuration['libs']
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.specs
|
53
|
+
@configuration['specs']
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.all_paths
|
57
|
+
libs + specs
|
58
|
+
end
|
59
|
+
|
60
|
+
# Prepares a Sprockets::Environment object to serve coffeescript assets
|
61
|
+
def self.sprockets_environment
|
62
|
+
@environment ||= Sprockets::Environment.new.tap do |environment|
|
63
|
+
environment.append_path File.join(Seaweed::ROOT, 'lib')
|
64
|
+
all_paths.each do |path|
|
65
|
+
environment.append_path path
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.start_server
|
71
|
+
app = Rack::Builder.app do
|
72
|
+
map '/assets' do
|
73
|
+
run Seaweed.sprockets_environment
|
74
|
+
end
|
75
|
+
|
76
|
+
map '/' do
|
77
|
+
run Seaweed::Server
|
78
|
+
end
|
79
|
+
end
|
80
|
+
Rack::Handler.default.run app, :Port => port
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.spawn_server
|
84
|
+
# Start server in its own thread
|
85
|
+
Thread.new &start_server
|
86
|
+
|
87
|
+
# Keep trying to connect to server until we succeed
|
88
|
+
begin
|
89
|
+
page = Net::HTTP.get URI.parse(root_url)
|
90
|
+
rescue Errno::ECONNREFUSED
|
91
|
+
sleep 1
|
92
|
+
retry
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.run_suite
|
97
|
+
require 'celerity'
|
98
|
+
|
99
|
+
if @browser
|
100
|
+
@browser.refresh
|
101
|
+
else
|
102
|
+
@browser = Celerity::Browser.new
|
103
|
+
@browser.goto "#{root_url}#terminal"
|
104
|
+
end
|
105
|
+
puts @browser.text
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.watch_for_changes
|
109
|
+
require 'watchr'
|
110
|
+
|
111
|
+
# Build a regexp to match .coffee files in any project paths
|
112
|
+
path_matcher = Regexp.new('^(' + all_paths.map{ |s| Regexp.escape s}.join('|') + ')\/.*\.coffee$')
|
113
|
+
|
114
|
+
script = Watchr::Script.new
|
115
|
+
script.watch(path_matcher) { run_suite }
|
116
|
+
controller = Watchr::Controller.new(script, Watchr.handler.new)
|
117
|
+
controller.run
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
require File.expand_path(File.dirname(__FILE__) + '/seaweed/server')
|