jazrb 0.1.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.
Files changed (184) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +134 -0
  3. data/bin/autojaz +4 -0
  4. data/bin/jazrb +230 -0
  5. data/doc/jasmine/files.html +460 -0
  6. data/doc/jasmine/index.html +322 -0
  7. data/doc/jasmine/symbols/_global_.html +1083 -0
  8. data/doc/jasmine/symbols/jasmine.Block.html +417 -0
  9. data/doc/jasmine/symbols/jasmine.Clock.html +678 -0
  10. data/doc/jasmine/symbols/jasmine.Env.html +1135 -0
  11. data/doc/jasmine/symbols/jasmine.EnvjsReporter.html +328 -0
  12. data/doc/jasmine/symbols/jasmine.JsApiReporter.html +822 -0
  13. data/doc/jasmine/symbols/jasmine.Matchers.html +1106 -0
  14. data/doc/jasmine/symbols/jasmine.MultiReporter.html +394 -0
  15. data/doc/jasmine/symbols/jasmine.NestedResults.html +710 -0
  16. data/doc/jasmine/symbols/jasmine.Reporter.html +574 -0
  17. data/doc/jasmine/symbols/jasmine.Runner.html +710 -0
  18. data/doc/jasmine/symbols/jasmine.Spec.html +1372 -0
  19. data/doc/jasmine/symbols/jasmine.Spy.html +855 -0
  20. data/doc/jasmine/symbols/jasmine.Suite.html +705 -0
  21. data/doc/jasmine/symbols/jasmine.XMLReporter.html +328 -0
  22. data/doc/jasmine/symbols/jasmine.html +1359 -0
  23. data/doc/jasmine/symbols/jasmine.util.html +535 -0
  24. data/doc/jasmine/symbols/src/lib_EnvjsReporter.js.html +149 -0
  25. data/doc/jasmine/symbols/src/lib_TrivialReporter.js.html +127 -0
  26. data/doc/jasmine/symbols/src/lib_XMLReporter.js.html +210 -0
  27. data/doc/jasmine/symbols/src/src_Block.js.html +35 -0
  28. data/doc/jasmine/symbols/src/src_Env.js.html +233 -0
  29. data/doc/jasmine/symbols/src/src_JsApiReporter.js.html +110 -0
  30. data/doc/jasmine/symbols/src/src_Matchers.js.html +399 -0
  31. data/doc/jasmine/symbols/src/src_MultiReporter.js.html +36 -0
  32. data/doc/jasmine/symbols/src/src_NestedResults.js.html +88 -0
  33. data/doc/jasmine/symbols/src/src_PrettyPrinter.js.html +128 -0
  34. data/doc/jasmine/symbols/src/src_Queue.js.html +119 -0
  35. data/doc/jasmine/symbols/src/src_Reporter.js.html +35 -0
  36. data/doc/jasmine/symbols/src/src_Reporters.js.html +51 -0
  37. data/doc/jasmine/symbols/src/src_Runner.js.html +75 -0
  38. data/doc/jasmine/symbols/src/src_Spec.js.html +228 -0
  39. data/doc/jasmine/symbols/src/src_Suite.js.html +77 -0
  40. data/doc/jasmine/symbols/src/src_WaitsBlock.js.html +21 -0
  41. data/doc/jasmine/symbols/src/src_WaitsForBlock.js.html +45 -0
  42. data/doc/jasmine/symbols/src/src_base.js.html +566 -0
  43. data/doc/jasmine/symbols/src/src_mock-timeout.js.html +185 -0
  44. data/doc/jasmine/symbols/src/src_util.js.html +75 -0
  45. data/lib/autotest/discover.rb +3 -0
  46. data/lib/autotest/jazrb.rb +55 -0
  47. data/lib/jazrb/intro.js +46 -0
  48. data/lib/jazrb/jasmine/EnvjsReporter.js +141 -0
  49. data/lib/jazrb/jasmine/TrivialReporter.js +155 -0
  50. data/lib/jazrb/jasmine/XMLReporter.js +241 -0
  51. data/lib/jazrb/jasmine/consolex.js +28 -0
  52. data/lib/jazrb/jasmine/jasmine-0.10.0.js +2526 -0
  53. data/lib/jazrb/jasmine/jasmine.js +2526 -0
  54. data/lib/jazrb/jasmine/json2.js +478 -0
  55. data/lib/jazrb/options.rb +26 -0
  56. data/lib/jazrb/outro.js +3 -0
  57. data/lib/jazrb/qintro.js +113 -0
  58. data/lib/jazrb/qoutro.js +3 -0
  59. data/lib/jazrb/qunit/qunit.css +17 -0
  60. data/lib/jazrb/qunit/qunit.js +1027 -0
  61. data/lib/jazrb.rb +0 -0
  62. data/spec/example/hello_world_spec.js +29 -0
  63. data/spec/example/nested/HelloWorldSpec.js +13 -0
  64. data/spec/example/nested/SpecHelper.js +1 -0
  65. data/spec/example/plain_spec.js +8 -0
  66. data/spec/example/spec_helper.js +1 -0
  67. data/spec/spec_helper.js +18 -0
  68. data/spec/spec_runner.js +143 -0
  69. data/test/jazrb_test.rb +7 -0
  70. data/test/test_helper.rb +10 -0
  71. data/vendor/jasmine/MIT.LICENSE +20 -0
  72. data/vendor/jasmine/README.markdown +479 -0
  73. data/vendor/jasmine/Rakefile +155 -0
  74. data/vendor/jasmine/contrib/ruby/jasmine_runner.rb +334 -0
  75. data/vendor/jasmine/contrib/ruby/jasmine_spec_builder.rb +153 -0
  76. data/vendor/jasmine/contrib/ruby/run.html +47 -0
  77. data/vendor/jasmine/contrib/ruby/spec/jasmine_runner_spec.rb +71 -0
  78. data/vendor/jasmine/doc/files.html +460 -0
  79. data/vendor/jasmine/doc/index.html +322 -0
  80. data/vendor/jasmine/doc/symbols/_global_.html +1083 -0
  81. data/vendor/jasmine/doc/symbols/jasmine.Block.html +417 -0
  82. data/vendor/jasmine/doc/symbols/jasmine.Clock.html +678 -0
  83. data/vendor/jasmine/doc/symbols/jasmine.Env.html +1135 -0
  84. data/vendor/jasmine/doc/symbols/jasmine.EnvjsReporter.html +328 -0
  85. data/vendor/jasmine/doc/symbols/jasmine.JsApiReporter.html +822 -0
  86. data/vendor/jasmine/doc/symbols/jasmine.Matchers.html +1106 -0
  87. data/vendor/jasmine/doc/symbols/jasmine.MultiReporter.html +394 -0
  88. data/vendor/jasmine/doc/symbols/jasmine.NestedResults.html +710 -0
  89. data/vendor/jasmine/doc/symbols/jasmine.Reporter.html +574 -0
  90. data/vendor/jasmine/doc/symbols/jasmine.Runner.html +710 -0
  91. data/vendor/jasmine/doc/symbols/jasmine.Spec.html +1372 -0
  92. data/vendor/jasmine/doc/symbols/jasmine.Spy.html +855 -0
  93. data/vendor/jasmine/doc/symbols/jasmine.Suite.html +705 -0
  94. data/vendor/jasmine/doc/symbols/jasmine.XMLReporter.html +328 -0
  95. data/vendor/jasmine/doc/symbols/jasmine.html +1359 -0
  96. data/vendor/jasmine/doc/symbols/jasmine.util.html +535 -0
  97. data/vendor/jasmine/doc/symbols/src/lib_EnvjsReporter.js.html +149 -0
  98. data/vendor/jasmine/doc/symbols/src/lib_TrivialReporter.js.html +127 -0
  99. data/vendor/jasmine/doc/symbols/src/lib_XMLReporter.js.html +210 -0
  100. data/vendor/jasmine/doc/symbols/src/src_Block.js.html +35 -0
  101. data/vendor/jasmine/doc/symbols/src/src_Env.js.html +233 -0
  102. data/vendor/jasmine/doc/symbols/src/src_JsApiReporter.js.html +110 -0
  103. data/vendor/jasmine/doc/symbols/src/src_Matchers.js.html +399 -0
  104. data/vendor/jasmine/doc/symbols/src/src_MultiReporter.js.html +36 -0
  105. data/vendor/jasmine/doc/symbols/src/src_NestedResults.js.html +88 -0
  106. data/vendor/jasmine/doc/symbols/src/src_PrettyPrinter.js.html +128 -0
  107. data/vendor/jasmine/doc/symbols/src/src_Queue.js.html +119 -0
  108. data/vendor/jasmine/doc/symbols/src/src_Reporter.js.html +35 -0
  109. data/vendor/jasmine/doc/symbols/src/src_Reporters.js.html +51 -0
  110. data/vendor/jasmine/doc/symbols/src/src_Runner.js.html +75 -0
  111. data/vendor/jasmine/doc/symbols/src/src_Spec.js.html +228 -0
  112. data/vendor/jasmine/doc/symbols/src/src_Suite.js.html +77 -0
  113. data/vendor/jasmine/doc/symbols/src/src_WaitsBlock.js.html +21 -0
  114. data/vendor/jasmine/doc/symbols/src/src_WaitsForBlock.js.html +45 -0
  115. data/vendor/jasmine/doc/symbols/src/src_base.js.html +566 -0
  116. data/vendor/jasmine/doc/symbols/src/src_mock-timeout.js.html +185 -0
  117. data/vendor/jasmine/doc/symbols/src/src_util.js.html +75 -0
  118. data/vendor/jasmine/examples/html/example_suite.html +27 -0
  119. data/vendor/jasmine/examples/html/spec/example_suite.js +11 -0
  120. data/vendor/jasmine/examples/ruby/Rakefile +33 -0
  121. data/vendor/jasmine/examples/ruby/spec/example/example_spec.js +11 -0
  122. data/vendor/jasmine/examples/ruby/spec/jasmine_helper.rb +41 -0
  123. data/vendor/jasmine/examples/ruby/spec/jasmine_spec.rb +31 -0
  124. data/vendor/jasmine/examples/ruby/spec/saucelabs.yml +24 -0
  125. data/vendor/jasmine/geminstaller.yml +23 -0
  126. data/vendor/jasmine/images/fail-16.png +0 -0
  127. data/vendor/jasmine/images/fail.png +0 -0
  128. data/vendor/jasmine/images/go-16.png +0 -0
  129. data/vendor/jasmine/images/go.png +0 -0
  130. data/vendor/jasmine/images/pending-16.png +0 -0
  131. data/vendor/jasmine/images/pending.png +0 -0
  132. data/vendor/jasmine/images/question-bk.png +0 -0
  133. data/vendor/jasmine/images/questionbk-16.png +0 -0
  134. data/vendor/jasmine/images/spinner.gif +0 -0
  135. data/vendor/jasmine/jasmine.watchr +70 -0
  136. data/vendor/jasmine/lib/EnvjsReporter.js +141 -0
  137. data/vendor/jasmine/lib/TrivialReporter.js +155 -0
  138. data/vendor/jasmine/lib/XMLReporter.js +241 -0
  139. data/vendor/jasmine/lib/consolex.js +28 -0
  140. data/vendor/jasmine/lib/jasmine-0.10.0.js +2526 -0
  141. data/vendor/jasmine/lib/jasmine.css +86 -0
  142. data/vendor/jasmine/lib/json2.js +478 -0
  143. data/vendor/jasmine/spec/jasmine_helper.rb +44 -0
  144. data/vendor/jasmine/spec/jasmine_spec.rb +31 -0
  145. data/vendor/jasmine/spec/runner.html +43 -0
  146. data/vendor/jasmine/spec/runner.js +78 -0
  147. data/vendor/jasmine/spec/saucelabs.yml +24 -0
  148. data/vendor/jasmine/spec/suites/EnvSpec.js +71 -0
  149. data/vendor/jasmine/spec/suites/ExceptionsSpec.js +107 -0
  150. data/vendor/jasmine/spec/suites/JsApiReporterSpec.js +82 -0
  151. data/vendor/jasmine/spec/suites/MatchersSpec.js +589 -0
  152. data/vendor/jasmine/spec/suites/MockClockSpec.js +34 -0
  153. data/vendor/jasmine/spec/suites/MultiReporterSpec.js +30 -0
  154. data/vendor/jasmine/spec/suites/NestedResultsSpec.js +54 -0
  155. data/vendor/jasmine/spec/suites/PrettyPrintSpec.js +93 -0
  156. data/vendor/jasmine/spec/suites/QueueSpec.js +23 -0
  157. data/vendor/jasmine/spec/suites/ReporterSpec.js +60 -0
  158. data/vendor/jasmine/spec/suites/RunnerSpec.js +258 -0
  159. data/vendor/jasmine/spec/suites/SpecRunningSpec.js +1117 -0
  160. data/vendor/jasmine/spec/suites/SpecSpec.js +110 -0
  161. data/vendor/jasmine/spec/suites/SpySpec.js +187 -0
  162. data/vendor/jasmine/spec/suites/SuiteSpec.js +101 -0
  163. data/vendor/jasmine/spec/suites/TrivialReporterSpec.js +140 -0
  164. data/vendor/jasmine/spec/suites/UtilSpec.js +23 -0
  165. data/vendor/jasmine/src/Block.js +34 -0
  166. data/vendor/jasmine/src/Env.js +236 -0
  167. data/vendor/jasmine/src/JsApiReporter.js +103 -0
  168. data/vendor/jasmine/src/Matchers.js +326 -0
  169. data/vendor/jasmine/src/MultiReporter.js +28 -0
  170. data/vendor/jasmine/src/NestedResults.js +99 -0
  171. data/vendor/jasmine/src/PrettyPrinter.js +122 -0
  172. data/vendor/jasmine/src/Queue.js +210 -0
  173. data/vendor/jasmine/src/Reporter.js +27 -0
  174. data/vendor/jasmine/src/Reporters.js +43 -0
  175. data/vendor/jasmine/src/Runner.js +68 -0
  176. data/vendor/jasmine/src/Spec.js +254 -0
  177. data/vendor/jasmine/src/Suite.js +70 -0
  178. data/vendor/jasmine/src/WaitsBlock.js +13 -0
  179. data/vendor/jasmine/src/WaitsForBlock.js +38 -0
  180. data/vendor/jasmine/src/base.js +604 -0
  181. data/vendor/jasmine/src/mock-timeout.js +177 -0
  182. data/vendor/jasmine/src/util.js +67 -0
  183. data/vendor/jasmine/src/version.json +5 -0
  184. metadata +279 -0
@@ -0,0 +1,334 @@
1
+ require 'socket'
2
+ require 'erb'
3
+ require 'json'
4
+
5
+ module Jasmine
6
+ def self.root
7
+ File.expand_path(File.join(File.dirname(__FILE__), '../..'))
8
+ end
9
+
10
+ # this seemingly-over-complex method is necessary to get an open port on at least some of our Macs
11
+ def self.open_socket_on_unused_port
12
+ infos = Socket::getaddrinfo("localhost", nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)
13
+ families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten]
14
+
15
+ return TCPServer.open('0.0.0.0', 0) if families.has_key?('AF_INET')
16
+ return TCPServer.open('::', 0) if families.has_key?('AF_INET6')
17
+ return TCPServer.open(0)
18
+ end
19
+
20
+ def self.find_unused_port
21
+ socket = open_socket_on_unused_port
22
+ port = socket.addr[1]
23
+ socket.close
24
+ port
25
+ end
26
+
27
+ def self.server_is_listening_on(hostname, port)
28
+ require 'socket'
29
+ begin
30
+ socket = TCPSocket.open(hostname, port)
31
+ rescue Errno::ECONNREFUSED
32
+ return false
33
+ end
34
+ socket.close
35
+ true
36
+ end
37
+
38
+ def self.wait_for_listener(port, name = "required process", seconds_to_wait = 10)
39
+ time_out_at = Time.now + seconds_to_wait
40
+ until server_is_listening_on "localhost", port
41
+ sleep 0.1
42
+ puts "Waiting for #{name} on #{port}..."
43
+ raise "#{name} didn't show up on port #{port} after #{seconds_to_wait} seconds." if Time.now > time_out_at
44
+ end
45
+ end
46
+
47
+ def self.kill_process_group(process_group_id, signal="TERM")
48
+ Process.kill signal, -process_group_id # negative pid means kill process group. (see man 2 kill)
49
+ end
50
+
51
+ def self.cachebust(files, root_dir="", replace=nil, replace_with=nil)
52
+ require 'digest/md5'
53
+ files.collect do |file_name|
54
+ real_file_name = replace && replace_with ? file_name.sub(replace, replace_with) : file_name
55
+ begin
56
+ digest = Digest::MD5.hexdigest(File.read("#{root_dir}#{real_file_name}"))
57
+ rescue
58
+ digest = "MISSING-FILE"
59
+ end
60
+ "#{file_name}?cachebust=#{digest}"
61
+ end
62
+ end
63
+
64
+ class RunAdapter
65
+ def initialize(spec_files_or_proc, options = {})
66
+ @spec_files_or_proc = Jasmine.files(spec_files_or_proc) || []
67
+ @jasmine_files = Jasmine.files(options[:jasmine_files]) || [
68
+ "/__JASMINE_ROOT__/lib/" + File.basename(Dir.glob("#{Jasmine.root}/lib/jasmine*.js").first),
69
+ "/__JASMINE_ROOT__/lib/TrivialReporter.js",
70
+ "/__JASMINE_ROOT__/lib/json2.js",
71
+ "/__JASMINE_ROOT__/lib/consolex.js",
72
+ ]
73
+ @stylesheets = ["/__JASMINE_ROOT__/lib/jasmine.css"] + (Jasmine.files(options[:stylesheets]) || [])
74
+ @spec_helpers = Jasmine.files(options[:spec_helpers]) || []
75
+ end
76
+
77
+ def call(env)
78
+ run
79
+ end
80
+
81
+ def run
82
+ stylesheets = @stylesheets
83
+ spec_helpers = @spec_helpers
84
+ spec_files = @spec_files_or_proc
85
+
86
+ jasmine_files = @jasmine_files
87
+ jasmine_files = jasmine_files.call if jasmine_files.respond_to?(:call)
88
+
89
+ css_files = @stylesheets
90
+
91
+
92
+ body = ERB.new(File.read(File.join(File.dirname(__FILE__), "run.html"))).result(binding)
93
+ [
94
+ 200,
95
+ { 'Content-Type' => 'text/html' },
96
+ body
97
+ ]
98
+ end
99
+
100
+
101
+ end
102
+
103
+ class Redirect
104
+ def initialize(url)
105
+ @url = url
106
+ end
107
+
108
+ def call(env)
109
+ [
110
+ 302,
111
+ { 'Location' => @url },
112
+ []
113
+ ]
114
+ end
115
+ end
116
+
117
+ class JsAlert
118
+ def call(env)
119
+ [
120
+ 200,
121
+ { 'Content-Type' => 'application/javascript' },
122
+ "document.write('<p>Couldn\\'t load #{env["PATH_INFO"]}!</p>');"
123
+ ]
124
+ end
125
+ end
126
+
127
+ class FocusedSuite
128
+ def initialize(spec_files_or_proc, options)
129
+ @spec_files_or_proc = Jasmine.files(spec_files_or_proc) || []
130
+ @options = options
131
+ end
132
+
133
+ def call(env)
134
+ spec_files = @spec_files_or_proc
135
+ matching_specs = spec_files.select {|spec_file| spec_file =~ /#{Regexp.escape(env["PATH_INFO"])}/ }.compact
136
+ if !matching_specs.empty?
137
+ run_adapter = Jasmine::RunAdapter.new(matching_specs, @options)
138
+ run_adapter.run
139
+ else
140
+ [
141
+ 200,
142
+ { 'Content-Type' => 'application/javascript' },
143
+ "document.write('<p>Couldn\\'t find any specs matching #{env["PATH_INFO"]}!</p>');"
144
+ ]
145
+ end
146
+ end
147
+
148
+ end
149
+
150
+ class SimpleServer
151
+ def self.start(port, spec_files_or_proc, mappings, options = {})
152
+ require 'thin'
153
+ config = {
154
+ '/__suite__' => Jasmine::FocusedSuite.new(spec_files_or_proc, options),
155
+ '/run.html' => Jasmine::Redirect.new('/'),
156
+ '/' => Jasmine::RunAdapter.new(spec_files_or_proc, options)
157
+ }
158
+ mappings.each do |from, to|
159
+ config[from] = Rack::File.new(to)
160
+ end
161
+
162
+ config["/__JASMINE_ROOT__"] = Rack::File.new(Jasmine.root)
163
+
164
+ app = Rack::Cascade.new([
165
+ Rack::URLMap.new(config),
166
+ JsAlert.new
167
+ ])
168
+
169
+ begin
170
+ Thin::Server.start('0.0.0.0', port, app)
171
+ rescue RuntimeError => e
172
+ raise e unless e.message == 'no acceptor'
173
+ raise RuntimeError.new("A server is already running on port #{port}")
174
+ end
175
+ end
176
+ end
177
+
178
+ class SimpleClient
179
+ def initialize(selenium_host, selenium_port, selenium_browser_start_command, http_address)
180
+ require 'selenium/client'
181
+ @driver = Selenium::Client::Driver.new(
182
+ selenium_host,
183
+ selenium_port,
184
+ selenium_browser_start_command,
185
+ http_address
186
+ )
187
+ @http_address = http_address
188
+ end
189
+
190
+ def tests_have_finished?
191
+ @driver.get_eval("window.jasmine.getEnv().currentRunner.finished") == "true"
192
+ end
193
+
194
+ def connect
195
+ @driver.start
196
+ @driver.open("/")
197
+ end
198
+
199
+ def disconnect
200
+ @driver.stop
201
+ end
202
+
203
+ def run
204
+ until tests_have_finished? do
205
+ sleep 0.1
206
+ end
207
+
208
+ puts @driver.get_eval("window.results()")
209
+ failed_count = @driver.get_eval("window.jasmine.getEnv().currentRunner.results().failedCount").to_i
210
+ failed_count == 0
211
+ end
212
+
213
+ def eval_js(script)
214
+ escaped_script = "'" + script.gsub(/(['\\])/) { '\\' + $1 } + "'"
215
+
216
+ result = @driver.get_eval(" try { eval(#{escaped_script}, window); } catch(err) { window.eval(#{escaped_script}); }")
217
+ JSON.parse("[#{result}]")[0]
218
+ end
219
+ end
220
+
221
+ class Runner
222
+ def initialize(selenium_jar_path, spec_files, dir_mappings, options={})
223
+ @selenium_jar_path = selenium_jar_path
224
+ @spec_files = spec_files
225
+ @dir_mappings = dir_mappings
226
+ @options = options
227
+
228
+ @browser = options[:browser] ? options[:browser].delete(:browser) : 'firefox'
229
+ @selenium_pid = nil
230
+ @jasmine_server_pid = nil
231
+ @selenium_host = 'localhost'
232
+ @jasmine_server_port = Jasmine::find_unused_port
233
+ @selenium_server_port = Jasmine::find_unused_port
234
+ end
235
+
236
+ def start
237
+ start_jasmine_server
238
+ start_selenium_server
239
+ @client = Jasmine::SimpleClient.new(@selenium_host, @selenium_server_port, "*#{@browser}", "http://localhost:#{@jasmine_server_port}/")
240
+ @client.connect
241
+ end
242
+
243
+ def stop
244
+ @client.disconnect
245
+ stop_selenium_server
246
+ stop_jasmine_server
247
+ end
248
+
249
+ def start_jasmine_server
250
+ @jasmine_server_pid = fork do
251
+ Process.setpgrp
252
+ Jasmine::SimpleServer.start(@jasmine_server_port, @spec_files, @dir_mappings, @options)
253
+ exit! 0
254
+ end
255
+ puts "jasmine server started. pid is #{@jasmine_server_pid}"
256
+ Jasmine::wait_for_listener(@jasmine_server_port, "jasmine server")
257
+ end
258
+
259
+ def start_selenium_server
260
+ @selenium_pid = fork do
261
+ Process.setpgrp
262
+ exec "java -jar #{@selenium_jar_path} -port #{@selenium_server_port} > /dev/null 2>&1"
263
+ end
264
+ puts "selenium started. pid is #{@selenium_pid}"
265
+ Jasmine::wait_for_listener(@selenium_server_port, "selenium server")
266
+ end
267
+
268
+ def stop_jasmine_server
269
+ puts "shutting down Jasmine server..."
270
+ Jasmine::kill_process_group(@jasmine_server_pid) if @jasmine_server_pid
271
+ end
272
+
273
+ def stop_selenium_server
274
+ puts "shutting down Selenium server..."
275
+ Jasmine::kill_process_group(@selenium_pid) if @selenium_pid
276
+ end
277
+
278
+ def run
279
+ begin
280
+ start
281
+ puts "servers are listening on their ports -- running the test script..."
282
+ tests_passed = @client.run
283
+ ensure
284
+ stop
285
+ end
286
+ return tests_passed
287
+ end
288
+
289
+ def eval_js(script)
290
+ @client.eval_js(script)
291
+ end
292
+ end
293
+
294
+ class SauceLabsRunner < Runner
295
+ def initialize(spec_files, dir_mappings, options={})
296
+ @spec_files = spec_files
297
+ @dir_mappings = dir_mappings
298
+ @options = options
299
+
300
+ @browser = options[:browser] ? options[:browser].delete(:browser) : 'firefox'
301
+ @jasmine_server_pid = nil
302
+ @jasmine_server_port = Jasmine::find_unused_port
303
+ @saucelabs_config = SeleniumConfig.new(options[:saucelabs_config], options[:saucelabs_config_file], @jasmine_server_port)
304
+ end
305
+
306
+ def start_selenium_server
307
+ @sauce_tunnel = SauceTunnel.new(@saucelabs_config)
308
+ end
309
+
310
+ def start
311
+ start_jasmine_server
312
+ start_selenium_server
313
+ @client = Jasmine::SimpleClient.new(@saucelabs_config['selenium_server_address'],
314
+ 4444,
315
+ @saucelabs_config['selenium_browser_key'],
316
+ "http://#{@saucelabs_config['application_address']}")
317
+ @client.connect
318
+ end
319
+
320
+ def stop
321
+ @client.disconnect
322
+ @sauce_tunnel.shutdown
323
+ stop_jasmine_server
324
+ end
325
+
326
+ end
327
+
328
+ def self.files(f)
329
+ result = f
330
+ result = result.call if result.respond_to?(:call)
331
+ result
332
+ end
333
+
334
+ end
@@ -0,0 +1,153 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "jasmine_runner.rb"))
2
+ require 'enumerator'
3
+ module Jasmine
4
+
5
+ class SpecBuilder
6
+ attr_accessor :suites
7
+
8
+ def initialize(spec_files, runner)
9
+ @spec_files = spec_files
10
+ @runner = runner
11
+ @spec_ids = []
12
+ end
13
+
14
+ def start
15
+ guess_example_locations
16
+
17
+ @runner.start
18
+ load_suite_info
19
+ wait_for_suites_to_finish_running
20
+ end
21
+
22
+ def stop
23
+ @runner.stop
24
+ end
25
+
26
+ def script_path
27
+ File.expand_path(__FILE__)
28
+ end
29
+
30
+ def guess_example_locations
31
+ @example_locations = {}
32
+
33
+ example_name_parts = []
34
+ previous_indent_level = 0
35
+ @spec_files.each do |filename|
36
+ line_number = 1
37
+ File.open(filename, "r") do |file|
38
+ file.readlines.each do |line|
39
+ match = /^(\s*)(describe|it)\s*\(\s*["'](.*)["']\s*,\s*function/.match(line)
40
+ if (match)
41
+ indent_level = match[1].length / 2
42
+ example_name = match[3]
43
+ example_name_parts[indent_level] = example_name
44
+
45
+ full_example_name = example_name_parts.slice(0, indent_level + 1).join(" ")
46
+ @example_locations[full_example_name] = "#{filename}:#{line_number}: in `it'"
47
+ end
48
+ line_number += 1
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def load_suite_info
55
+ started = Time.now
56
+ while !eval_js('jsApiReporter && jsApiReporter.started') do
57
+ raise "couldn't connect to Jasmine after 60 seconds" if (started + 60 < Time.now)
58
+ sleep 0.1
59
+ end
60
+
61
+ @suites = eval_js('JSON.stringify(jsApiReporter.suites())')
62
+ end
63
+
64
+ def results_for(spec_id)
65
+ @spec_results ||= load_results
66
+ @spec_results[spec_id.to_s]
67
+ end
68
+
69
+ def load_results
70
+ @spec_results = {}
71
+ @spec_ids.each_slice(50) do |slice|
72
+ @spec_results.merge!(eval_js("JSON.stringify(jsApiReporter.resultsForSpecs(#{JSON.generate(slice)}))"))
73
+ end
74
+ @spec_results
75
+ end
76
+
77
+ def wait_for_suites_to_finish_running
78
+ puts "Waiting for suite to finish in browser ..."
79
+ while !eval_js('jsApiReporter.finished') do
80
+ sleep 0.1
81
+ end
82
+ end
83
+
84
+ def declare_suites
85
+ me = self
86
+ suites.each do |suite|
87
+ declare_suite(self, suite)
88
+ end
89
+ end
90
+
91
+ def declare_suite(parent, suite)
92
+ me = self
93
+ parent.describe suite["name"] do
94
+ suite["children"].each do |suite_or_spec|
95
+ type = suite_or_spec["type"]
96
+ if type == "suite"
97
+ me.declare_suite(self, suite_or_spec)
98
+ elsif type == "spec"
99
+ me.declare_spec(self, suite_or_spec)
100
+ else
101
+ raise "unknown type #{type} for #{suite_or_spec.inspect}"
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def declare_spec(parent, spec)
108
+ me = self
109
+ example_name = spec["name"]
110
+ @spec_ids << spec["id"]
111
+ backtrace = @example_locations[parent.description + " " + example_name]
112
+ parent.it example_name, {}, backtrace do
113
+ me.report_spec(spec["id"])
114
+ end
115
+ end
116
+
117
+ def report_spec(spec_id)
118
+ spec_results = results_for(spec_id)
119
+
120
+ out = ""
121
+ messages = spec_results['messages'].each do |message|
122
+ case
123
+ when message["type"] == "MessageResult"
124
+ puts message["text"]
125
+ puts "\n"
126
+ else
127
+ unless message["message"] =~ /^Passed.$/
128
+ STDERR << message["message"]
129
+ STDERR << "\n"
130
+
131
+ out << message["message"]
132
+ out << "\n"
133
+ end
134
+
135
+ if !message["passed"] && message["trace"]["stack"]
136
+ stack_trace = message["trace"]["stack"].gsub(/<br \/>/, "\n").gsub(/<\/?b>/, " ")
137
+ STDERR << stack_trace.gsub(/\(.*\)@http:\/\/localhost:[0-9]+\/specs\//, "/spec/")
138
+ STDERR << "\n"
139
+ end
140
+ end
141
+
142
+ end
143
+ fail out unless spec_results['result'] == 'passed'
144
+ puts out unless out.empty?
145
+ end
146
+
147
+ private
148
+
149
+ def eval_js(js)
150
+ @runner.eval_js(js)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2
+ <html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta content="text/html;charset=UTF-8" http-equiv="Content-Type"/>
5
+ <title>Jasmine suite</title>
6
+ <% css_files.each do |css_file| %>
7
+ <link rel="stylesheet" href="<%= css_file %>" type="text/css" media="screen"/>
8
+ <% end %>
9
+
10
+ <% jasmine_files.each do |jasmine_file| %>
11
+ <script src="<%= jasmine_file %>" type="text/javascript"></script>
12
+ <% end %>
13
+
14
+ <% spec_helpers.each do |spec_helper| %>
15
+ <script src="<%= spec_helper %>" type="text/javascript"></script>
16
+ <% end %>
17
+
18
+ <script type="text/javascript">
19
+ var jsApiReporter;
20
+ (function() {
21
+ var jasmineEnv = jasmine.getEnv();
22
+
23
+ jsApiReporter = new jasmine.JsApiReporter();
24
+ var trivialReporter = new jasmine.TrivialReporter();
25
+
26
+ jasmineEnv.addReporter(jsApiReporter);
27
+ jasmineEnv.addReporter(trivialReporter);
28
+
29
+ jasmineEnv.specFilter = function(spec) {
30
+ return trivialReporter.specFilter(spec);
31
+ };
32
+
33
+ window.onload = function() {
34
+ jasmineEnv.execute();
35
+ };
36
+ })();
37
+ </script>
38
+
39
+ <% spec_files.each do |spec_file| %>
40
+ <script src="<%= spec_file %>" type="text/javascript"></script>
41
+ <% end %>
42
+
43
+ </head>
44
+ <body>
45
+ <div id="jasmine_content"></div>
46
+ </body>
47
+ </html>
@@ -0,0 +1,71 @@
1
+ require 'spec'
2
+ require 'open-uri'
3
+ require 'thin'
4
+
5
+ require File.dirname(__FILE__) + '/../jasmine_runner'
6
+
7
+ describe Jasmine::SimpleServer do
8
+ before do
9
+ @port = Jasmine::find_unused_port
10
+ end
11
+
12
+ after do
13
+ Jasmine::kill_process_group(@jasmine_server_pid) if @jasmine_server_pid
14
+ end
15
+
16
+ it "should start and print script tags" do
17
+ @jasmine_server_pid = fork do
18
+ Process.setpgrp
19
+ Jasmine::SimpleServer.start(@port, ["file1", "file2"], {})
20
+ exit! 0
21
+ end
22
+
23
+ Jasmine::wait_for_listener(@port)
24
+
25
+ run_html = open("http://localhost:#{@port}/").read
26
+ run_html.should =~ /<script src="file1"/
27
+ run_html.should =~ /<script src="file2"/
28
+ end
29
+
30
+ describe "RuntimeError" do
31
+
32
+ it "should throw an Already Running error if there is already a server running" do
33
+ Thin::Server.should_receive(:start).and_raise(RuntimeError.new('no acceptor'))
34
+ lambda {
35
+ Jasmine::SimpleServer.start(@port, ["file1", "file2"], {})
36
+ }.should raise_error(RuntimeError, "A server is already running on port #{@port}")
37
+ end
38
+
39
+ it "re-raises other RuntimeErrors" do
40
+ Thin::Server.should_receive(:start).and_raise(RuntimeError.new('some random error'))
41
+ lambda {
42
+ Jasmine::SimpleServer.start(@port, ["file1", "file2"], {})
43
+ }.should raise_error(RuntimeError, "some random error")
44
+ end
45
+
46
+ end
47
+
48
+ it "should take a proc that returns a list of spec files" do
49
+ spec_fileses = [["file1", "file2"], ["file1", "file2", "file3"]]
50
+ spec_files_proc = lambda do
51
+ spec_fileses.shift
52
+ end
53
+
54
+ @jasmine_server_pid = fork do
55
+ Process.setpgrp
56
+ Jasmine::SimpleServer.start(@port, spec_files_proc, {})
57
+ exit! 0
58
+ end
59
+
60
+ Jasmine::wait_for_listener(@port)
61
+
62
+ run_html = open("http://localhost:#{@port}/").read
63
+ run_html.should =~ /<script src="file1"/
64
+ run_html.should =~ /<script src="file2"/
65
+
66
+ run_html = open("http://localhost:#{@port}/").read
67
+ run_html.should =~ /<script src="file1"/
68
+ run_html.should =~ /<script src="file2"/
69
+ run_html.should =~ /<script src="file3"/
70
+ end
71
+ end