edgecase-jasmine 1.0.1.1

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 (35) hide show
  1. data/README.markdown +45 -0
  2. data/bin/jasmine +6 -0
  3. data/generators/jasmine/jasmine_generator.rb +30 -0
  4. data/generators/jasmine/templates/INSTALL +9 -0
  5. data/generators/jasmine/templates/jasmine-example/SpecRunner.html +27 -0
  6. data/generators/jasmine/templates/jasmine-example/spec/PlayerSpec.js +58 -0
  7. data/generators/jasmine/templates/jasmine-example/spec/SpecHelper.js +9 -0
  8. data/generators/jasmine/templates/jasmine-example/src/Player.js +22 -0
  9. data/generators/jasmine/templates/jasmine-example/src/Song.js +7 -0
  10. data/generators/jasmine/templates/lib/tasks/jasmine.rake +2 -0
  11. data/generators/jasmine/templates/spec/javascripts/support/jasmine-rails.yml +81 -0
  12. data/generators/jasmine/templates/spec/javascripts/support/jasmine.yml +73 -0
  13. data/generators/jasmine/templates/spec/javascripts/support/jasmine_runner.rb +21 -0
  14. data/jasmine/example/SpecRunner.html +27 -0
  15. data/jasmine/lib/jasmine-html.js +188 -0
  16. data/jasmine/lib/jasmine.css +166 -0
  17. data/jasmine/lib/jasmine.js +2421 -0
  18. data/jasmine/lib/json2.js +478 -0
  19. data/lib/jasmine.rb +8 -0
  20. data/lib/jasmine/base.rb +59 -0
  21. data/lib/jasmine/command_line_tool.rb +68 -0
  22. data/lib/jasmine/config.rb +186 -0
  23. data/lib/jasmine/run.html.erb +43 -0
  24. data/lib/jasmine/selenium_driver.rb +44 -0
  25. data/lib/jasmine/server.rb +120 -0
  26. data/lib/jasmine/spec_builder.rb +152 -0
  27. data/lib/jasmine/tasks/jasmine.rake +31 -0
  28. data/spec/config_spec.rb +259 -0
  29. data/spec/jasmine_command_line_tool_spec.rb +26 -0
  30. data/spec/jasmine_self_test_config.rb +15 -0
  31. data/spec/jasmine_self_test_spec.rb +18 -0
  32. data/spec/rails_generator_spec.rb +27 -0
  33. data/spec/server_spec.rb +82 -0
  34. data/spec/spec_helper.rb +40 -0
  35. metadata +204 -0
data/lib/jasmine.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'jasmine/base'
2
+ require 'jasmine/config'
3
+ require 'jasmine/server'
4
+ require 'jasmine/selenium_driver'
5
+
6
+ require 'jasmine/spec_builder'
7
+
8
+ require 'jasmine/command_line_tool'
@@ -0,0 +1,59 @@
1
+ require 'socket'
2
+ require 'erb'
3
+ require 'multi_json'
4
+
5
+ module Jasmine
6
+ def self.root
7
+ ENV["JASMINE_ROOT"] || File.expand_path(File.join(File.dirname(__FILE__), '../../jasmine'))
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.cachebust(files, root_dir="", replace=nil, replace_with=nil)
48
+ require 'digest/md5'
49
+ files.collect do |file_name|
50
+ real_file_name = replace && replace_with ? file_name.sub(replace, replace_with) : file_name
51
+ begin
52
+ digest = Digest::MD5.hexdigest(File.read("#{root_dir}#{real_file_name}"))
53
+ rescue
54
+ digest = "MISSING-FILE"
55
+ end
56
+ "#{file_name}?cachebust=#{digest}"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,68 @@
1
+ module Jasmine
2
+ class CommandLineTool
3
+ def cwd
4
+ File.expand_path(File.join(File.dirname(__FILE__), '../..'))
5
+ end
6
+
7
+ def expand(*paths)
8
+ File.expand_path(File.join(*paths))
9
+ end
10
+
11
+ def template_path(filepath)
12
+ expand(cwd, File.join("generators/jasmine/templates", filepath))
13
+ end
14
+
15
+ def dest_path(filepath)
16
+ expand(Dir.pwd, filepath)
17
+ end
18
+
19
+ def copy_unless_exists(relative_path, dest_path = nil)
20
+ unless File.exist?(dest_path(relative_path))
21
+ FileUtils.copy(template_path(relative_path), dest_path(dest_path || relative_path))
22
+ end
23
+ end
24
+
25
+ def process(argv)
26
+ if argv[0] == 'init'
27
+ require 'fileutils'
28
+
29
+ FileUtils.makedirs('public/javascripts')
30
+ FileUtils.makedirs('spec/javascripts')
31
+ FileUtils.makedirs('spec/javascripts/support')
32
+ FileUtils.makedirs('spec/javascripts/helpers')
33
+
34
+ copy_unless_exists('jasmine-example/src/Player.js', 'public/javascripts/Player.js')
35
+ copy_unless_exists('jasmine-example/src/Song.js', 'public/javascripts/Song.js')
36
+ copy_unless_exists('jasmine-example/spec/PlayerSpec.js', 'spec/javascripts/PlayerSpec.js')
37
+ copy_unless_exists('jasmine-example/spec/SpecHelper.js', 'spec/javascripts/helpers/SpecHelper.js')
38
+ copy_unless_exists('spec/javascripts/support/jasmine_runner.rb')
39
+
40
+ rails_tasks_dir = dest_path('lib/tasks')
41
+ if File.exist?(rails_tasks_dir)
42
+ copy_unless_exists('lib/tasks/jasmine.rake')
43
+ copy_unless_exists('spec/javascripts/support/jasmine-rails.yml', 'spec/javascripts/support/jasmine.yml')
44
+ else
45
+ copy_unless_exists('spec/javascripts/support/jasmine.yml')
46
+ write_mode = 'w'
47
+ if File.exist?(dest_path('Rakefile'))
48
+ load dest_path('Rakefile')
49
+ write_mode = 'a'
50
+ end
51
+
52
+ require 'rake'
53
+ unless Rake::Task.task_defined?('jasmine')
54
+ File.open(dest_path('Rakefile'), write_mode) do |f|
55
+ f.write("\n" + File.read(template_path('lib/tasks/jasmine.rake')))
56
+ end
57
+ end
58
+ end
59
+ File.open(template_path('INSTALL'), 'r').each_line do |line|
60
+ puts line
61
+ end
62
+ else
63
+ puts "unknown command #{argv}"
64
+ puts "Usage: jasmine init"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,186 @@
1
+ module Jasmine
2
+ class Config
3
+ require 'yaml'
4
+ require 'erb'
5
+
6
+ def browser
7
+ ENV["JASMINE_BROWSER"] || 'firefox'
8
+ end
9
+
10
+ def jasmine_host
11
+ ENV["JASMINE_HOST"] || 'http://localhost'
12
+ end
13
+
14
+ def external_selenium_server_port
15
+ ENV['SELENIUM_SERVER_PORT'] && ENV['SELENIUM_SERVER_PORT'].to_i > 0 ? ENV['SELENIUM_SERVER_PORT'].to_i : nil
16
+ end
17
+
18
+ def start_server(port = 8888)
19
+ handler = Rack::Handler.default
20
+ handler.run Jasmine.app(self), :Port => port, :AccessLog => []
21
+ end
22
+
23
+ def start
24
+ start_servers
25
+ @client = Jasmine::SeleniumDriver.new("localhost", @selenium_server_port, "*#{browser}", "#{jasmine_host}:#{@jasmine_server_port}/")
26
+ @client.connect
27
+ end
28
+
29
+ def stop
30
+ @client.disconnect
31
+ end
32
+
33
+ def start_jasmine_server
34
+ @jasmine_server_port = Jasmine::find_unused_port
35
+ Thread.new do
36
+ start_server(@jasmine_server_port)
37
+ end
38
+ Jasmine::wait_for_listener(@jasmine_server_port, "jasmine server")
39
+ puts "jasmine server started."
40
+ end
41
+
42
+ def windows?
43
+ require 'rbconfig'
44
+ ::RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
45
+ end
46
+
47
+ def start_selenium_server
48
+ @selenium_server_port = external_selenium_server_port
49
+ if @selenium_server_port.nil?
50
+ @selenium_server_port = Jasmine::find_unused_port
51
+ require 'selenium_rc'
52
+ SeleniumRC::Server.send(:include, SeleniumServerForkHackForRSpec)
53
+ SeleniumRC::Server.boot("localhost", @selenium_server_port, :args => [windows? ? ">NUL" : "> /dev/null"])
54
+ else
55
+ Jasmine::wait_for_listener(@selenium_server_port, "selenium server")
56
+ end
57
+ end
58
+
59
+ def start_servers
60
+ start_jasmine_server
61
+ start_selenium_server
62
+ end
63
+
64
+ def run
65
+ begin
66
+ start
67
+ puts "servers are listening on their ports -- running the test script..."
68
+ tests_passed = @client.run
69
+ ensure
70
+ stop
71
+ end
72
+ return tests_passed
73
+ end
74
+
75
+ def eval_js(script)
76
+ @client.eval_js(script)
77
+ end
78
+
79
+ def match_files(dir, patterns)
80
+ dir = File.expand_path(dir)
81
+ patterns.collect do |pattern|
82
+ matches = Dir.glob(File.join(dir, pattern))
83
+ matches.collect {|f| f.sub("#{dir}/", "")}.sort
84
+ end.flatten.uniq
85
+ end
86
+
87
+ def simple_config
88
+ config = File.exist?(simple_config_file) ? YAML::load(ERB.new(File.read(simple_config_file)).result(binding)) : false
89
+ config || {}
90
+ end
91
+
92
+
93
+ def spec_path
94
+ "/__spec__"
95
+ end
96
+
97
+ def root_path
98
+ "/__root__"
99
+ end
100
+
101
+ def js_files(spec_filter = nil)
102
+ spec_files_to_include = spec_filter.nil? ? spec_files : match_files(spec_dir, [spec_filter])
103
+ src_files.collect {|f| "/" + f } + helpers.collect {|f| File.join(spec_path, f) } + spec_files_to_include.collect {|f| File.join(spec_path, f) }
104
+ end
105
+
106
+ def css_files
107
+ stylesheets.collect {|f| "/" + f }
108
+ end
109
+
110
+ def spec_files_full_paths
111
+ spec_files.collect {|spec_file| File.join(spec_dir, spec_file) }
112
+ end
113
+
114
+ def project_root
115
+ Dir.pwd
116
+ end
117
+
118
+ def simple_config_file
119
+ File.join(project_root, 'spec/javascripts/support/jasmine.yml')
120
+ end
121
+
122
+ def src_dir
123
+ if simple_config['src_dir']
124
+ File.join(project_root, simple_config['src_dir'])
125
+ else
126
+ project_root
127
+ end
128
+ end
129
+
130
+ def spec_dir
131
+ if simple_config['spec_dir']
132
+ File.join(project_root, simple_config['spec_dir'])
133
+ else
134
+ File.join(project_root, 'spec/javascripts')
135
+ end
136
+ end
137
+
138
+ def helpers
139
+ if simple_config['helpers']
140
+ match_files(spec_dir, simple_config['helpers'])
141
+ else
142
+ match_files(spec_dir, ["helpers/**/*.js"])
143
+ end
144
+ end
145
+
146
+ def src_files
147
+ if simple_config['src_files']
148
+ match_files(src_dir, simple_config['src_files'])
149
+ else
150
+ []
151
+ end
152
+ end
153
+
154
+ def spec_files
155
+ if simple_config['spec_files']
156
+ match_files(spec_dir, simple_config['spec_files'])
157
+ else
158
+ match_files(spec_dir, ["**/*[sS]pec.js"])
159
+ end
160
+ end
161
+
162
+ def stylesheets
163
+ if simple_config['stylesheets']
164
+ match_files(src_dir, simple_config['stylesheets'])
165
+ else
166
+ []
167
+ end
168
+ end
169
+
170
+ module SeleniumServerForkHackForRSpec
171
+ # without this, Selenium's forked process will attempt to run specs a second time at exit;
172
+ # see http://www.ruby-forum.com/topic/212722
173
+ def self.included(base)
174
+ alias_method :fork_without_fix_for_rspec, :fork
175
+ alias_method :fork, :fork_with_fix_for_rspec
176
+ end
177
+
178
+ def fork_with_fix_for_rspec
179
+ fork_without_fix_for_rspec do
180
+ yield
181
+ at_exit { exit! }
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,43 @@
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
+ <script type="text/javascript">
15
+ var jsApiReporter;
16
+ (function() {
17
+ var jasmineEnv = jasmine.getEnv();
18
+
19
+ jsApiReporter = new jasmine.JsApiReporter();
20
+ var trivialReporter = new jasmine.TrivialReporter();
21
+
22
+ jasmineEnv.addReporter(jsApiReporter);
23
+ jasmineEnv.addReporter(trivialReporter);
24
+
25
+ jasmineEnv.specFilter = function(spec) {
26
+ return trivialReporter.specFilter(spec);
27
+ };
28
+
29
+ window.onload = function() {
30
+ jasmineEnv.execute();
31
+ };
32
+ })();
33
+ </script>
34
+
35
+ <% js_files.each do |js_file| %>
36
+ <script src="<%= js_file %>" type="text/javascript"></script>
37
+ <% end %>
38
+
39
+ </head>
40
+ <body>
41
+ <div id="jasmine_content"></div>
42
+ </body>
43
+ </html>
@@ -0,0 +1,44 @@
1
+ module Jasmine
2
+ class SeleniumDriver
3
+ def initialize(selenium_host, selenium_port, selenium_browser_start_command, http_address)
4
+ require 'selenium/client'
5
+ @driver = Selenium::Client::Driver.new(
6
+ selenium_host,
7
+ selenium_port,
8
+ selenium_browser_start_command,
9
+ http_address
10
+ )
11
+ @http_address = http_address
12
+ end
13
+
14
+ def tests_have_finished?
15
+ @driver.get_eval("window.jasmine.getEnv().currentRunner.finished") == "true"
16
+ end
17
+
18
+ def connect
19
+ @driver.start
20
+ @driver.open("/")
21
+ end
22
+
23
+ def disconnect
24
+ @driver.stop
25
+ end
26
+
27
+ def run
28
+ until tests_have_finished? do
29
+ sleep 0.1
30
+ end
31
+
32
+ puts @driver.get_eval("window.results()")
33
+ failed_count = @driver.get_eval("window.jasmine.getEnv().currentRunner.results().failedCount").to_i
34
+ failed_count == 0
35
+ end
36
+
37
+ def eval_js(script)
38
+ escaped_script = "'" + script.gsub(/(['\\])/) { '\\' + $1 } + "'"
39
+
40
+ result = @driver.get_eval("try { eval(#{escaped_script}, window); } catch(err) { window.eval(#{escaped_script}); }")
41
+ MultiJson.decode("{\"result\":#{result}}")["result"]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,120 @@
1
+ require 'rack'
2
+
3
+ # Backport Rack::Handler.default from Rack 1.1.0 for Rails 2.3.x compatibility.
4
+ unless Rack::Handler.respond_to?(:default)
5
+ module Rack::Handler
6
+ def self.default(options = {})
7
+ # Guess.
8
+ if ENV.include?("PHP_FCGI_CHILDREN")
9
+ # We already speak FastCGI
10
+ options.delete :File
11
+ options.delete :Port
12
+
13
+ Rack::Handler::FastCGI
14
+ elsif ENV.include?("REQUEST_METHOD")
15
+ Rack::Handler::CGI
16
+ else
17
+ begin
18
+ Rack::Handler::Mongrel
19
+ rescue LoadError => e
20
+ Rack::Handler::WEBrick
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ module Jasmine
28
+ class RunAdapter
29
+ def initialize(config)
30
+ @config = config
31
+ @jasmine_files = [
32
+ "/__JASMINE_ROOT__/lib/jasmine.js",
33
+ "/__JASMINE_ROOT__/lib/jasmine-html.js",
34
+ "/__JASMINE_ROOT__/lib/json2.js",
35
+ ]
36
+ @jasmine_stylesheets = ["/__JASMINE_ROOT__/lib/jasmine.css"]
37
+ end
38
+
39
+ def call(env)
40
+ return not_found if env["PATH_INFO"] != "/"
41
+ run
42
+ end
43
+
44
+ def not_found
45
+ body = "File not found: #{@path_info}\n"
46
+ [404, {"Content-Type" => "text/plain",
47
+ "Content-Length" => body.size.to_s,
48
+ "X-Cascade" => "pass"},
49
+ [body]]
50
+ end
51
+
52
+ #noinspection RubyUnusedLocalVariable
53
+ def run(focused_suite = nil)
54
+ jasmine_files = @jasmine_files
55
+ css_files = @jasmine_stylesheets + (@config.css_files || [])
56
+ js_files = @config.js_files(focused_suite)
57
+ body = ERB.new(File.read(File.join(File.dirname(__FILE__), "run.html.erb"))).result(binding)
58
+ [
59
+ 200,
60
+ { 'Content-Type' => 'text/html' },
61
+ body
62
+ ]
63
+ end
64
+ end
65
+
66
+ class Redirect
67
+ def initialize(url)
68
+ @url = url
69
+ end
70
+
71
+ def call(env)
72
+ [
73
+ 302,
74
+ { 'Location' => @url },
75
+ []
76
+ ]
77
+ end
78
+ end
79
+
80
+ class JsAlert
81
+ def call(env)
82
+ [
83
+ 200,
84
+ { 'Content-Type' => 'application/javascript' },
85
+ "document.write('<p>Couldn\\'t load #{env["PATH_INFO"]}!</p>');"
86
+ ]
87
+ end
88
+ end
89
+
90
+ class FocusedSuite
91
+ def initialize(config)
92
+ @config = config
93
+ end
94
+
95
+ def call(env)
96
+ run_adapter = Jasmine::RunAdapter.new(@config)
97
+ run_adapter.run(env["PATH_INFO"])
98
+ end
99
+ end
100
+
101
+ def self.app(config)
102
+ Rack::Builder.app do
103
+ use Rack::Head
104
+
105
+ map('/run.html') { run Jasmine::Redirect.new('/') }
106
+ map('/__suite__') { run Jasmine::FocusedSuite.new(config) }
107
+
108
+ map('/__JASMINE_ROOT__') { run Rack::File.new(Jasmine.root) }
109
+ map(config.spec_path) { run Rack::File.new(config.spec_dir) }
110
+ map(config.root_path) { run Rack::File.new(config.project_root) }
111
+
112
+ map('/') do
113
+ run Rack::Cascade.new([
114
+ Rack::URLMap.new('/' => Rack::File.new(config.src_dir)),
115
+ Jasmine::RunAdapter.new(config)
116
+ ])
117
+ end
118
+ end
119
+ end
120
+ end