diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..3dd5c29 --- /dev/null +++ b/Gemfile @@ -0,0 +1,11 @@ +gem "rake", "0.8.7" +gem "jeweler", "1.4.0" +gem "gemcutter", "0.2.1" + +gem "rspec", ">= 1.1.5", :only => :testing +gem "rack", ">= 1.0.0", :only => :testing +gem "thin", ">= 1.2.4", :only => :testing +gem "selenium-rc", ">=2.1.0", :only => :testing +gem "selenium-client", ">=1.2.17", :only => :testing + +disable_system_gems \ No newline at end of file diff --git a/Rakefile b/Rakefile index 561f4b4..077d638 100644 --- a/Rakefile +++ b/Rakefile @@ -1,27 +1,65 @@ +require "#{File.dirname(__FILE__)}/vendor/gems/environment" +Bundler.require_env :rake + +$LOAD_PATH.unshift File.expand_path("#{File.dirname(__FILE__)}/lib") + +require 'spec' +require 'spec/rake/spectask' + +desc "Run all examples" +Spec::Rake::SpecTask.new('spec') do |t| + t.spec_files = FileList['spec/**/*.rb'] +end + +namespace :jasmine do +# require 'jasmine' + require 'spec/jasmine_self_test_config' + +# desc "Run continuous integration tests" +# require "spec" +# require 'spec/rake/spectask' +# Spec::Rake::SpecTask.new(:ci) do |t| +# t.spec_opts = ["--color", "--format", "specdoc"] +# t.verbose = true +# t.spec_files = [JasmineHelper.meta_spec_path] +# end + + task :server do + puts "your tests are here:" + puts " http://localhost:8888/run.html" + + JasmineSelfTestConfig.new.start_server + end +end + +desc "Run specs via server" +task :jasmine => ['jasmine:server'] + + namespace :jeweler do + unless File.exists?('jasmine/lib') + raise "Jasmine submodule isn't present. Run git submodule init && git submodule update." + end + begin require 'jeweler' require 'rake' Jeweler::Tasks.new do |gemspec| - gemspec.name = "jasmine-ruby" - gemspec.summary = "Jasmine Ruby" - gemspec.description = "Javascript BDD testings" + gemspec.name = "jasmine" + gemspec.summary = "Jasmine Ruby Runner" + gemspec.description = "Javascript BDD test framework" gemspec.email = "ragaskar@gmail.com" - gemspec.homepage = "http://github.com/ragaskar/jasmine-ruby" - gemspec.description = "Jasmine Ruby" - gemspec.authors = ["Rajan Agaskar"] - gemspec.files = FileList.new('bin/*', 'lib/**/**', 'jasmine/lib/**', 'tasks/**', 'templates/**') - + gemspec.homepage = "http://github.com/pivotal/jasmine-ruby" + gemspec.authors = ["Rajan Agaskar", "Christian Williams"] + gemspec.files = FileList.new('bin/jasmine', 'lib/**/**', 'jasmine/lib/**', 'jasmine/contrib/ruby/**', 'tasks/**', 'templates/**') gemspec.add_dependency('rspec', '>= 1.1.5') - gemspec.add_dependency('rack', '>= 1.0.0') gemspec.add_dependency('json', '>= 1.1.9') - gemspec.add_dependency('pivotal-selenium-rc', '>= 1.11.20090610') - gemspec.add_dependency('selenium-client', '>= 1.2.17') - gemspec.add_dependency('thin', '= 1.2.4') + gemspec.add_dependency('rack', '>= 1.0.0') + gemspec.add_dependency('thin', '>= 1.2.4') + gemspec.add_dependency('selenium-rc', '>=2.1.0') + gemspec.add_dependency('selenium-client', '>=1.2.17') end Jeweler::GemcutterTasks.new - rescue LoadError - puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" end end diff --git a/VERSION.yml b/VERSION.yml index 30f3b29..948bf8d 100644 --- a/VERSION.yml +++ b/VERSION.yml @@ -1,5 +1,5 @@ --- -:minor: 2 -:patch: 0 -:build: +:minor: 1 +:build: +:patch: 3 :major: 0 diff --git a/geminstaller.yml b/geminstaller.yml deleted file mode 100644 index f33b27a..0000000 --- a/geminstaller.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -gems: -- name: rake - version: 0.8.7 -- name: jeweler - version: 1.4.0 diff --git a/jasmine b/jasmine index 40ff1cb..701ee71 160000 --- a/jasmine +++ b/jasmine @@ -1 +1 @@ -Subproject commit 40ff1cb4c8339cce5c799549427d6859eb4480a1 +Subproject commit 701ee719e6e1e7d9f2ac84707302cb430ac123e4 diff --git a/jasmine-ruby.gemspec b/jasmine-ruby.gemspec index bf896f0..e8a819f 100644 --- a/jasmine-ruby.gemspec +++ b/jasmine-ruby.gemspec @@ -4,12 +4,12 @@ # -*- encoding: utf-8 -*- Gem::Specification.new do |s| - s.name = %q{jasmine-ruby} + s.name = %q{jasmine} s.version = "0.1.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Rajan Agaskar"] - s.date = %q{2009-12-05} + s.date = %q{2009-12-27} s.default_executable = %q{jasmine} s.description = %q{Jasmine Ruby} s.email = %q{ragaskar@gmail.com} @@ -24,18 +24,18 @@ Gem::Specification.new do |s| "jasmine/lib/jasmine-0.10.0.js", "jasmine/lib/jasmine.css", "jasmine/lib/json2.js", - "lib/jasmine-ruby.rb", - "lib/jasmine-ruby/jasmine_helper.rb", - "lib/jasmine-ruby/jasmine_meta_spec.rb", - "lib/jasmine-ruby/jasmine_runner.rb", - "lib/jasmine-ruby/jasmine_spec_builder.rb", - "lib/jasmine-ruby/run.html", + "lib/jasmine.rb", + "lib/jasmine/jasmine_helper.rb", + "lib/jasmine/jasmine_meta_spec.rb", + "lib/jasmine/jasmine_runner.rb", + "lib/jasmine/spec_builder.rb", + "lib/jasmine/run.html", "templates/Rakefile", - "templates/example_spec.js", + "templates/ExampleSpec.js", "templates/jasmine_helper.rb", - "templates/spec_helper.js" + "templates/SpecHelper.js" ] - s.homepage = %q{http://github.com/ragaskar/jasmine-ruby} + s.homepage = %q{http://github.com/ragaskar/jasmine} s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] s.rubygems_version = %q{1.3.5} @@ -51,25 +51,16 @@ Gem::Specification.new do |s| if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, [">= 1.1.5"]) s.add_runtime_dependency(%q, [">= 1.0.0"]) - s.add_runtime_dependency(%q, [">= 1.1.9"]) - s.add_runtime_dependency(%q, [">= 1.11.20090610"]) - s.add_runtime_dependency(%q, [">= 1.2.17"]) - s.add_runtime_dependency(%q, ["= 1.2.4"]) + s.add_runtime_dependency(%q, [">= 1.2.4"]) else s.add_dependency(%q, [">= 1.1.5"]) s.add_dependency(%q, [">= 1.0.0"]) - s.add_dependency(%q, [">= 1.1.9"]) - s.add_dependency(%q, [">= 1.11.20090610"]) - s.add_dependency(%q, [">= 1.2.17"]) - s.add_dependency(%q, ["= 1.2.4"]) + s.add_dependency(%q, [">= 1.2.4"]) end else s.add_dependency(%q, [">= 1.1.5"]) s.add_dependency(%q, [">= 1.0.0"]) - s.add_dependency(%q, [">= 1.1.9"]) - s.add_dependency(%q, [">= 1.11.20090610"]) - s.add_dependency(%q, [">= 1.2.17"]) - s.add_dependency(%q, ["= 1.2.4"]) + s.add_dependency(%q, [">= 1.2.4"]) end end diff --git a/lib/jasmine-ruby.rb b/lib/jasmine-ruby.rb deleted file mode 100644 index 8f74c56..0000000 --- a/lib/jasmine-ruby.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'jasmine-ruby/jasmine_helper' -require 'jasmine-ruby/jasmine_runner' -require 'jasmine-ruby/jasmine_spec_builder' \ No newline at end of file diff --git a/lib/jasmine-ruby/jasmine_runner.rb b/lib/jasmine-ruby/jasmine_runner.rb deleted file mode 100644 index 40df8c8..0000000 --- a/lib/jasmine-ruby/jasmine_runner.rb +++ /dev/null @@ -1,300 +0,0 @@ -require 'socket' -require 'erb' -require 'json' - -module Jasmine - def self.root - File.expand_path(File.join(File.dirname(__FILE__), '../../jasmine')) - end - - # this seemingly-over-complex method is necessary to get an open port on at least some of our Macs - def self.open_socket_on_unused_port - infos = Socket::getaddrinfo("localhost", nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) - families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten] - - return TCPServer.open('0.0.0.0', 0) if families.has_key?('AF_INET') - return TCPServer.open('::', 0) if families.has_key?('AF_INET6') - return TCPServer.open(0) - end - - def self.find_unused_port - socket = open_socket_on_unused_port - port = socket.addr[1] - socket.close - port - end - - def self.server_is_listening_on(hostname, port) - require 'socket' - begin - socket = TCPSocket.open(hostname, port) - rescue Errno::ECONNREFUSED - return false - end - socket.close - true - end - - def self.wait_for_listener(port, name = "required process", seconds_to_wait = 10) - time_out_at = Time.now + seconds_to_wait - until server_is_listening_on "localhost", port - sleep 0.1 - puts "Waiting for #{name} on #{port}..." - raise "#{name} didn't show up on port #{port} after #{seconds_to_wait} seconds." if Time.now > time_out_at - end - end - - def self.kill_process_group(process_group_id, signal="TERM") - Process.kill signal, -process_group_id # negative pid means kill process group. (see man 2 kill) - end - - def self.cachebust(files, root_dir="", replace=nil, replace_with=nil) - require 'digest/md5' - files.collect do |file_name| - real_file_name = replace && replace_with ? file_name.sub(replace, replace_with) : file_name - begin - digest = Digest::MD5.hexdigest(File.read("#{root_dir}#{real_file_name}")) - rescue - digest = "MISSING-FILE" - end - "#{file_name}?cachebust=#{digest}" - end - end - - class RunAdapter - def initialize(spec_files_or_proc, options = {}) - @spec_files_or_proc = Jasmine.files(spec_files_or_proc) || [] - @jasmine_files = Jasmine.files(options[:jasmine_files]) || [ - "/__JASMINE_ROOT__/lib/" + File.basename(Dir.glob("#{Jasmine.root}/lib/jasmine*.js").first), - "/__JASMINE_ROOT__/lib/TrivialReporter.js", - "/__JASMINE_ROOT__/lib/json2.js", - "/__JASMINE_ROOT__/lib/consolex.js", - ] - @stylesheets = ["/__JASMINE_ROOT__/lib/jasmine.css"] + (Jasmine.files(options[:stylesheets]) || []) - @spec_helpers = Jasmine.files(options[:spec_helpers]) || [] - end - - def call(env) - run - end - - def run - stylesheets = @stylesheets - spec_helpers = @spec_helpers - spec_files = @spec_files_or_proc - - jasmine_files = @jasmine_files - jasmine_files = jasmine_files.call if jasmine_files.respond_to?(:call) - - css_files = @stylesheets - - - body = ERB.new(File.read(File.join(File.dirname(__FILE__), "run.html"))).result(binding) - [ - 200, - { 'Content-Type' => 'text/html' }, - body - ] - end - - - end - - class Redirect - def initialize(url) - @url = url - end - - def call(env) - [ - 302, - { 'Location' => @url }, - [] - ] - end - end - - class JsAlert - def call(env) - [ - 200, - { 'Content-Type' => 'application/javascript' }, - "document.write('

Couldn\\'t load #{env["PATH_INFO"]}!

');" - ] - end - end - - class FocusedSuite - def initialize(spec_files_or_proc, options) - @spec_files_or_proc = Jasmine.files(spec_files_or_proc) || [] - @options = options - end - - def call(env) - spec_files = @spec_files_or_proc - matching_specs = spec_files.select {|spec_file| spec_file =~ /#{Regexp.escape(env["PATH_INFO"])}/ }.compact - if !matching_specs.empty? - run_adapter = Jasmine::RunAdapter.new(matching_specs, @options) - run_adapter.run - else - [ - 200, - { 'Content-Type' => 'application/javascript' }, - "document.write('

Couldn\\'t find any specs matching #{env["PATH_INFO"]}!

');" - ] - end - end - - end - - class SimpleServer - def self.start(port, root_path, spec_files_or_proc, options = {}) - require 'thin' - config = { - '/__suite__' => Jasmine::FocusedSuite.new(spec_files_or_proc, options), - '/run.html' => Jasmine::Redirect.new('/'), - '/' => Jasmine::RunAdapter.new(spec_files_or_proc, options) - } - if (options[:mappings]) - options[:mappings].each do |from, to| - config[from] = Rack::File.new(to) - end - end - - config["/__JASMINE_ROOT__"] = Rack::File.new(Jasmine.root) - - file_serve_config = { - '/' => Rack::File.new(root_path) - } - - app = Rack::Cascade.new([ - Rack::URLMap.new(file_serve_config), - Rack::URLMap.new(config), - JsAlert.new - ]) - - begin - Thin::Server.start('0.0.0.0', port, app) - rescue RuntimeError => e - raise e unless e.message == 'no acceptor' - raise RuntimeError.new("A server is already running on port #{port}") - end - end - end - - class SimpleClient - def initialize(selenium_host, selenium_port, selenium_browser_start_command, http_address) - require 'selenium/client' - @driver = Selenium::Client::Driver.new( - selenium_host, - selenium_port, - selenium_browser_start_command, - http_address - ) - @http_address = http_address - end - - def tests_have_finished? - @driver.get_eval("window.jasmine.getEnv().currentRunner.finished") == "true" - end - - def connect - @driver.start - @driver.open("/") - end - - def disconnect - @driver.stop - end - - def run - until tests_have_finished? do - sleep 0.1 - end - - puts @driver.get_eval("window.results()") - failed_count = @driver.get_eval("window.jasmine.getEnv().currentRunner.results().failedCount").to_i - failed_count == 0 - end - - def eval_js(script) - escaped_script = "'" + script.gsub(/(['\\])/) { '\\' + $1 } + "'" - - result = @driver.get_eval(" try { eval(#{escaped_script}, window); } catch(err) { window.eval(#{escaped_script}); }") - JSON.parse("[#{result}]")[0] - end - end - - class Runner - def initialize(selenium_jar_path, root_path, spec_files, options={}) - @root_path = root_path - @selenium_jar_path = selenium_jar_path - @spec_files = spec_files - @options = options - - @browser = options[:browser] ? options[:browser].delete(:browser) : 'firefox' - @selenium_pid = nil - @jasmine_server_pid = nil - end - - def start - start_servers - @client = Jasmine::SimpleClient.new("localhost", @selenium_server_port, "*#{@browser}", "http://localhost:#{@jasmine_server_port}/") - @client.connect - end - - def stop - @client.disconnect - stop_servers - end - - def start_servers - @jasmine_server_port = Jasmine::find_unused_port - @selenium_server_port = Jasmine::find_unused_port - - @selenium_pid = fork do - Process.setpgrp - exec "java -jar #{@selenium_jar_path} -port #{@selenium_server_port} > /dev/null 2>&1" - end - puts "selenium started. pid is #{@selenium_pid}" - - @jasmine_server_pid = fork do - Process.setpgrp - Jasmine::SimpleServer.start(@jasmine_server_port, @root_path, @spec_files, @options) - exit! 0 - end - puts "jasmine server started. pid is #{@jasmine_server_pid}" - - Jasmine::wait_for_listener(@selenium_server_port, "selenium server") - Jasmine::wait_for_listener(@jasmine_server_port, "jasmine server") - end - - def stop_servers - puts "shutting down the servers..." - Jasmine::kill_process_group(@selenium_pid) if @selenium_pid - Jasmine::kill_process_group(@jasmine_server_pid) if @jasmine_server_pid - end - - def run - begin - start - puts "servers are listening on their ports -- running the test script..." - tests_passed = @client.run - ensure - stop - end - return tests_passed - end - - def eval_js(script) - @client.eval_js(script) - end - end - - def self.files(f) - result = f - result = result.call if result.respond_to?(:call) - result - end - -end diff --git a/lib/jasmine.rb b/lib/jasmine.rb new file mode 100644 index 0000000..a4c01aa --- /dev/null +++ b/lib/jasmine.rb @@ -0,0 +1,7 @@ +require 'jasmine/base' +require 'jasmine/config' +require 'jasmine/server' +require 'jasmine/selenium_driver' + +require 'jasmine/jasmine_helper' +require 'jasmine/spec_builder' \ No newline at end of file diff --git a/lib/jasmine/base.rb b/lib/jasmine/base.rb new file mode 100644 index 0000000..2829910 --- /dev/null +++ b/lib/jasmine/base.rb @@ -0,0 +1,63 @@ +require 'socket' +require 'erb' +require 'json' + +module Jasmine + def self.root + File.expand_path(File.join(File.dirname(__FILE__), '../../jasmine')) + end + + # this seemingly-over-complex method is necessary to get an open port on at least some of our Macs + def self.open_socket_on_unused_port + infos = Socket::getaddrinfo("localhost", nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) + families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten] + + return TCPServer.open('0.0.0.0', 0) if families.has_key?('AF_INET') + return TCPServer.open('::', 0) if families.has_key?('AF_INET6') + return TCPServer.open(0) + end + + def self.find_unused_port + socket = open_socket_on_unused_port + port = socket.addr[1] + socket.close + port + end + + def self.server_is_listening_on(hostname, port) + require 'socket' + begin + socket = TCPSocket.open(hostname, port) + rescue Errno::ECONNREFUSED + return false + end + socket.close + true + end + + def self.wait_for_listener(port, name = "required process", seconds_to_wait = 10) + time_out_at = Time.now + seconds_to_wait + until server_is_listening_on "localhost", port + sleep 0.1 + puts "Waiting for #{name} on #{port}..." + raise "#{name} didn't show up on port #{port} after #{seconds_to_wait} seconds." if Time.now > time_out_at + end + end + + def self.kill_process_group(process_group_id, signal="TERM") + Process.kill signal, -process_group_id # negative pid means kill process group. (see man 2 kill) + end + + def self.cachebust(files, root_dir="", replace=nil, replace_with=nil) + require 'digest/md5' + files.collect do |file_name| + real_file_name = replace && replace_with ? file_name.sub(replace, replace_with) : file_name + begin + digest = Digest::MD5.hexdigest(File.read("#{root_dir}#{real_file_name}")) + rescue + digest = "MISSING-FILE" + end + "#{file_name}?cachebust=#{digest}" + end + end +end \ No newline at end of file diff --git a/lib/jasmine/config.rb b/lib/jasmine/config.rb new file mode 100644 index 0000000..10b48ba --- /dev/null +++ b/lib/jasmine/config.rb @@ -0,0 +1,120 @@ +module Jasmine + class Config + def initialize(options = {}) + require 'selenium_rc' + @selenium_jar_path = SeleniumRC::Server.allocate.jar_path + @options = options + + @browser = options[:browser] ? options.delete(:browser) : 'firefox' + @selenium_pid = nil + @jasmine_server_pid = nil + end + + def start_server(port = 8888) + Jasmine::Server.new(port, self).start + end + + def start + start_servers + @client = Jasmine::SeleniumDriver.new("localhost", @selenium_server_port, "*#{@browser}", "http://localhost:#{@jasmine_server_port}/") + @client.connect + end + + def stop + @client.disconnect + stop_servers + end + + def start_servers + @jasmine_server_port = Jasmine::find_unused_port + @selenium_server_port = Jasmine::find_unused_port + + server = Jasmine::Server.new(@jasmine_server_port, self) + + @selenium_pid = fork do + Process.setpgrp + exec "java -jar #{@selenium_jar_path} -port #{@selenium_server_port} > /dev/null 2>&1" + end + puts "selenium started. pid is #{@selenium_pid}" + + @jasmine_server_pid = fork do + Process.setpgrp + server.start + exit! 0 + end + puts "jasmine server started. pid is #{@jasmine_server_pid}" + + Jasmine::wait_for_listener(@selenium_server_port, "selenium server") + Jasmine::wait_for_listener(@jasmine_server_port, "jasmine server") + end + + def stop_servers + puts "shutting down the servers..." + Jasmine::kill_process_group(@selenium_pid) if @selenium_pid + Jasmine::kill_process_group(@jasmine_server_pid) if @jasmine_server_pid + end + + def run + begin + start + puts "servers are listening on their ports -- running the test script..." + tests_passed = @client.run + ensure + stop + end + return tests_passed + end + + def eval_js(script) + @client.eval_js(script) + end + + def stylesheets + [] + end + + def src_files + [] + end + + def spec_files + raise "You need to declare a spec_files method in #{self.class}!" + end + + def match_files(dir, pattern) + dir = File.expand_path(dir) + Dir.glob(File.join(dir, pattern)).collect {|f| f.sub("#{dir}/", "")}.sort + end + + def src_files + match_files(src_dir, "**/*.js") + end + + def src_path + "src" + end + + def spec_path + "spec" + end + + def spec_files + match_files(spec_dir, "**/*.js") + end + + def mappings + { + "/" + src_path => src_dir, + "/" + spec_path => spec_dir + } + end + + def js_files + src_files.collect {|f| "/" + File.join(src_path, f) } + spec_files.collect {|f| "/" + File.join(spec_path, f) } + end + + def spec_files_full_paths + spec_files.collect {|spec_file| File.join(spec_dir, spec_file) } + end + end +end \ No newline at end of file diff --git a/lib/jasmine-ruby/jasmine_helper.rb b/lib/jasmine/jasmine_helper.rb similarity index 94% rename from lib/jasmine-ruby/jasmine_helper.rb rename to lib/jasmine/jasmine_helper.rb index 54ed746..a2c7b1d 100755 --- a/lib/jasmine-ruby/jasmine_helper.rb +++ b/lib/jasmine/jasmine_helper.rb @@ -34,7 +34,7 @@ class JasmineHelper end def self.meta_spec_path - File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'jasmine-ruby', 'jasmine_meta_spec.rb')) + File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'jasmine', 'jasmine_meta_spec.rb')) end def self.files diff --git a/lib/jasmine-ruby/jasmine_meta_spec.rb b/lib/jasmine/jasmine_meta_spec.rb similarity index 90% rename from lib/jasmine-ruby/jasmine_meta_spec.rb rename to lib/jasmine/jasmine_meta_spec.rb index 34154a2..3dd0e01 100644 --- a/lib/jasmine-ruby/jasmine_meta_spec.rb +++ b/lib/jasmine/jasmine_meta_spec.rb @@ -6,7 +6,7 @@ if File.exist?(helper_overrides) require helper_overrides end require File.expand_path(File.join(File.dirname(__FILE__), "jasmine_runner.rb")) -require File.expand_path(File.join(File.dirname(__FILE__), "jasmine_spec_builder")) +require File.expand_path(File.join(File.dirname(__FILE__), "spec_builder")) jasmine_runner = Jasmine::Runner.new(SeleniumRC::Server.new.jar_path, Dir.pwd, diff --git a/lib/jasmine-ruby/run.html b/lib/jasmine/run.html.erb similarity index 83% rename from lib/jasmine-ruby/run.html rename to lib/jasmine/run.html.erb index 9f0ecd0..80a200c 100644 --- a/lib/jasmine-ruby/run.html +++ b/lib/jasmine/run.html.erb @@ -11,10 +11,6 @@ <% end %> - <% spec_helpers.each do |spec_helper| %> - - <% end %> - - <% spec_files.each do |spec_file| %> - + <% js_files.each do |js_file| %> + <% end %> diff --git a/lib/jasmine/selenium_driver.rb b/lib/jasmine/selenium_driver.rb new file mode 100644 index 0000000..7f9bac8 --- /dev/null +++ b/lib/jasmine/selenium_driver.rb @@ -0,0 +1,44 @@ +module Jasmine + class SeleniumDriver + def initialize(selenium_host, selenium_port, selenium_browser_start_command, http_address) + require 'selenium/client' + @driver = Selenium::Client::Driver.new( + selenium_host, + selenium_port, + selenium_browser_start_command, + http_address + ) + @http_address = http_address + end + + def tests_have_finished? + @driver.get_eval("window.jasmine.getEnv().currentRunner.finished") == "true" + end + + def connect + @driver.start + @driver.open("/") + end + + def disconnect + @driver.stop + end + + def run + until tests_have_finished? do + sleep 0.1 + end + + puts @driver.get_eval("window.results()") + failed_count = @driver.get_eval("window.jasmine.getEnv().currentRunner.results().failedCount").to_i + failed_count == 0 + end + + def eval_js(script) + escaped_script = "'" + script.gsub(/(['\\])/) { '\\' + $1 } + "'" + + result = @driver.get_eval(" try { eval(#{escaped_script}, window); } catch(err) { window.eval(#{escaped_script}); }") + JSON.parse("[#{result}]")[0] + end + end +end \ No newline at end of file diff --git a/lib/jasmine/server.rb b/lib/jasmine/server.rb new file mode 100644 index 0000000..90918fb --- /dev/null +++ b/lib/jasmine/server.rb @@ -0,0 +1,124 @@ +module Jasmine + class RunAdapter + def initialize(config) + @config = config + @jasmine_files = [ + "/__JASMINE_ROOT__/lib/" + File.basename(Dir.glob("#{Jasmine.root}/lib/jasmine*.js").first), + "/__JASMINE_ROOT__/lib/TrivialReporter.js", + "/__JASMINE_ROOT__/lib/json2.js", + "/__JASMINE_ROOT__/lib/consolex.js", + ] + @jasmine_stylesheets = ["/__JASMINE_ROOT__/lib/jasmine.css"] + end + + def call(env) + run + end + + #noinspection RubyUnusedLocalVariable + def run + jasmine_files = @jasmine_files + css_files = @jasmine_stylesheets + (@config.stylesheets || []) + js_files = @config.js_files + + body = ERB.new(File.read(File.join(File.dirname(__FILE__), "run.html.erb"))).result(binding) + [ + 200, + { 'Content-Type' => 'text/html' }, + body + ] + end + + + end + + class Redirect + def initialize(url) + @url = url + end + + def call(env) + [ + 302, + { 'Location' => @url }, + [] + ] + end + end + + class JsAlert + def call(env) + [ + 200, + { 'Content-Type' => 'application/javascript' }, + "document.write('

Couldn\\'t load #{env["PATH_INFO"]}!

');" + ] + end + end + + class FocusedSuite + def initialize(config) + @config = config +# @spec_files_or_proc = spec_files_or_proc || [] +# @options = options + end + + def call(env) + spec_files = @config.spec_files_or_proc + matching_specs = spec_files.select {|spec_file| spec_file =~ /#{Regexp.escape(env["PATH_INFO"])}/ }.compact + if !matching_specs.empty? + run_adapter = Jasmine::RunAdapter.new(matching_specs, @options) + run_adapter.run + else + [ + 200, + { 'Content-Type' => 'application/javascript' }, + "document.write('

Couldn\\'t find any specs matching #{env["PATH_INFO"]}!

');" + ] + end + end + + end + + class Server + attr_reader :thin + + def initialize(port, config) + @port = port + @config = config + + require 'thin' + thin_config = { + '/__suite__' => Jasmine::FocusedSuite.new(@config), + '/run.html' => Jasmine::Redirect.new('/'), + '/' => Jasmine::RunAdapter.new(@config) + } + + @config.mappings.each do |from, to| + thin_config[from] = Rack::File.new(to) + end + + thin_config["/__JASMINE_ROOT__"] = Rack::File.new(Jasmine.root) + + app = Rack::Cascade.new([ + Rack::URLMap.new(thin_config), + JsAlert.new + ]) + + @thin = Thin::Server.new('0.0.0.0', @port, app) + end + + def start + begin + thin.start + rescue RuntimeError => e + raise e unless e.message == 'no acceptor' + raise RuntimeError.new("A server is already running on port #{@port}") + end + end + + def stop + thin.stop + end + end +end \ No newline at end of file diff --git a/lib/jasmine-ruby/jasmine_spec_builder.rb b/lib/jasmine/spec_builder.rb similarity index 95% rename from lib/jasmine-ruby/jasmine_spec_builder.rb rename to lib/jasmine/spec_builder.rb index e4ebfc9..352845e 100644 --- a/lib/jasmine-ruby/jasmine_spec_builder.rb +++ b/lib/jasmine/spec_builder.rb @@ -1,12 +1,13 @@ require 'enumerator' -module Jasmine +module Jasmine class SpecBuilder attr_accessor :suites - def initialize(spec_files, runner) - @spec_files = spec_files - @runner = runner + def initialize(config) + @config = config + @spec_files = config.spec_files + @runner = config @spec_ids = [] end @@ -31,7 +32,7 @@ module Jasmine example_name_parts = [] previous_indent_level = 0 - @spec_files.each do |filename| + @config.spec_files_full_paths.each do |filename| line_number = 1 File.open(filename, "r") do |file| file.readlines.each do |line| diff --git a/spec/config_spec.rb b/spec/config_spec.rb new file mode 100644 index 0000000..3a8b657 --- /dev/null +++ b/spec/config_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) + +describe Jasmine::Config do + before(:each) do + @template_dir = File.expand_path(File.join(File.dirname(__FILE__), "../templates")) + @config = Jasmine::Config.new + @config.stub!(:src_dir).and_return(File.join(@template_dir, "public")) + @config.stub!(:spec_dir).and_return(File.join(@template_dir, "spec")) + end + + it "should provide a list of all src and spec files" do + @config.src_files.should == ['javascripts/Example.js'] + @config.spec_files.should == ['javascript/ExampleSpec.js', 'javascript/SpecHelper.js'] + end + + it "should provide a list of all spec files with full paths" do + @config.spec_files_full_paths.should == [ + File.join(@template_dir, 'spec/javascript/ExampleSpec.js'), + File.join(@template_dir, 'spec/javascript/SpecHelper.js') + ] + end + + it "should provide a list of all js files" do + @config.js_files.should == [ + '/src/javascripts/Example.js', + '/spec/javascript/ExampleSpec.js', + '/spec/javascript/SpecHelper.js', + ] + end + + it "should provide dir mappings" do + @config.mappings.should == { + '/src' => @config.src_dir, + '/spec' => @config.spec_dir + } + end + + it "should allow overriding src and spec paths" do + @config.stub!(:src_path).and_return("public") + @config.stub!(:spec_path).and_return("spekz") + + @config.js_files.should == [ + '/public/javascripts/Example.js', + '/spekz/javascript/ExampleSpec.js', + '/spekz/javascript/SpecHelper.js', + ] + + @config.mappings.should == { + '/public' => @config.src_dir, + '/spekz' => @config.spec_dir + } + end +end \ No newline at end of file diff --git a/spec/jasmine_self_test_config.rb b/spec/jasmine_self_test_config.rb new file mode 100644 index 0000000..2736157 --- /dev/null +++ b/spec/jasmine_self_test_config.rb @@ -0,0 +1,15 @@ +require 'jasmine' + +class JasmineSelfTestConfig < Jasmine::Config + def proj_root + File.expand_path(File.join(File.dirname(__FILE__), "..")) + end + + def src_dir + File.join(proj_root, 'src') + end + + def spec_dir + File.join(proj_root, 'jasmine/spec') + end +end \ No newline at end of file diff --git a/spec/jasmine_self_test_spec.rb b/spec/jasmine_self_test_spec.rb new file mode 100644 index 0000000..e66c32e --- /dev/null +++ b/spec/jasmine_self_test_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) + +require 'jasmine_self_test_config' + +jasmine_config = JasmineSelfTestConfig.new +spec_builder = Jasmine::SpecBuilder.new(jasmine_config) + +should_stop = false + +Spec::Runner.configure do |config| + config.after(:suite) do + spec_builder.stop if should_stop + end +end + +spec_builder.start +should_stop = true +spec_builder.declare_suites diff --git a/spec/jasmine_spec.rb b/spec/jasmine_spec.rb deleted file mode 100644 index 5f652fb..0000000 --- a/spec/jasmine_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'rubygems' -require "selenium_rc" - -JASMINE_SPEC_DIR = File.join(File.dirname(__FILE__), "..", "jasmine", "spec") - -require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "jasmine-ruby", "jasmine_helper.rb")) -require File.expand_path(File.join(JasmineHelper.root, "contrib/ruby/jasmine_spec_builder")) - -jasmine_runner = Jasmine::Runner.new(SeleniumRC::Server.new.jar_path, - JasmineHelper.spec_file_urls, - JasmineHelper.dir_mappings, - :spec_helpers => JasmineHelper.spec_helpers) - -spec_builder = Jasmine::SpecBuilder.new(JasmineHelper.raw_spec_files, jasmine_runner) - -should_stop = false - -Spec::Runner.configure do |config| - config.after(:suite) do - spec_builder.stop if should_stop - end -end - -spec_builder.start -should_stop = true -spec_builder.declare_suites diff --git a/spec/server_spec.rb b/spec/server_spec.rb new file mode 100644 index 0000000..7397716 --- /dev/null +++ b/spec/server_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) + +def read(body) + return body if body.is_a?(String) + out = "" + body.each {|data| out += data } + out +end + +describe Jasmine::Server do + before(:each) do + config = Jasmine::Config.new + config.stub!(:mappings).and_return({ + "/src" => File.join(Jasmine.root, "src"), + "/spec" => File.join(Jasmine.root, "spec") + }) + + config.stub!(:js_files).and_return(["/src/file1.js", "/spec/file2.js"]) + + @server = Jasmine::Server.new(0, config) + @thin_app = @server.thin.app + end + + after(:each) do + @server.thin.stop if @server && @server.thin.running? + end + + it "should serve static files" do + code, headers, body = @thin_app.call("PATH_INFO" => "/spec/suites/EnvSpec.js", "SCRIPT_NAME" => "xxx") + code.should == 200 + headers["Content-Type"].should == "application/javascript" + read(body).should == File.read(File.join(Jasmine.root, "spec/suites/EnvSpec.js")) + end + + it "should serve Jasmine static files under /__JASMINE_ROOT__/" do + code, headers, body = @thin_app.call("PATH_INFO" => "/__JASMINE_ROOT__/lib/jasmine.css", "SCRIPT_NAME" => "xxx") + code.should == 200 + headers["Content-Type"].should == "text/css" + read(body).should == File.read(File.join(Jasmine.root, "lib/jasmine.css")) + end + + it "should redirect /run.html to /" do + code, headers, body = @thin_app.call("PATH_INFO" => "/run.html", "SCRIPT_NAME" => "xxx") + code.should == 302 + headers["Location"].should == "/" + end + + describe "/ page" do + it "should load each js file in order" do + code, headers, body = @thin_app.call("PATH_INFO" => "/", "SCRIPT_NAME" => "xxx") + code.should == 200 + body = read(body) + body.should include("\"/src/file1.js") + body.should include("\"/spec/file2.js") + body.should satisfy {|s| s.index("/src/file1.js") < s.index("/spec/file2.js") } + end + end + + it "should display an error using JS for 404's" do + code, headers, body = @thin_app.call("PATH_INFO" => "/spec/NonExistantFile.js", "SCRIPT_NAME" => "xxx") + code.should == 200 # todo: shouldn't this be 404? will that work with all browsers? + headers["Content-Type"].should == "application/javascript" + read(body).should == "document.write('

Couldn\\'t load /spec/NonExistantFile.js!

');" + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..78c3b71 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,3 @@ +require 'spec' + +require File.expand_path(File.join(File.dirname(__FILE__), "../lib/jasmine")) \ No newline at end of file diff --git a/templates/Rakefile b/templates/Rakefile index 9dae117..ce23bba 100644 --- a/templates/Rakefile +++ b/templates/Rakefile @@ -1,5 +1,5 @@ namespace :jasmine do - require 'jasmine-ruby' + require 'jasmine' helper_overrides = File.expand_path(File.join(File.dirname(__FILE__), "spec/helpers/jasmine_helper.rb")) if File.exist?(helper_overrides) require helper_overrides @@ -17,7 +17,7 @@ namespace :jasmine do puts "your tests are here:" puts " http://localhost:8888/run.html" - Jasmine::SimpleServer.start(8888, + Jasmine::Server.start(8888, File.expand_path(Dir.pwd), lambda { JasmineHelper.specs }, { :spec_helpers => JasmineHelper.files + JasmineHelper.spec_helpers, diff --git a/templates/public/javascripts/example.js b/templates/public/javascripts/example.js new file mode 100644 index 0000000..3fcc4e1 --- /dev/null +++ b/templates/public/javascripts/example.js @@ -0,0 +1,2 @@ +ExampleClass = function() { +}; \ No newline at end of file diff --git a/templates/jasmine_helper.rb b/templates/spec/jasmine_helper.rb similarity index 100% rename from templates/jasmine_helper.rb rename to templates/spec/jasmine_helper.rb diff --git a/templates/example_spec.js b/templates/spec/javascript/ExampleSpec.js similarity index 69% rename from templates/example_spec.js rename to templates/spec/javascript/ExampleSpec.js index 494539e..07ae14f 100644 --- a/templates/example_spec.js +++ b/templates/spec/javascript/ExampleSpec.js @@ -1,9 +1,9 @@ -describe('ExampleSuite', function () { +describe('Example', function () { it('should have a passing test', function() { expect(true).toEqual(true); }); - describe('Nested Describe', function () { + describe('nested describe', function () { it('should also have a passing test', function () { expect(true).toEqual(true); }); diff --git a/templates/spec_helper.js b/templates/spec/javascript/SpecHelper.js similarity index 100% rename from templates/spec_helper.js rename to templates/spec/javascript/SpecHelper.js