culerity 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/.gitignore +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +106 -0
  4. data/Rakefile +43 -0
  5. data/VERSION.yml +4 -0
  6. data/culerity.gemspec +78 -0
  7. data/features/fixtures/sample_feature +14 -0
  8. data/features/installing_culerity.feature +46 -0
  9. data/features/running_cucumber_without_explicitly_running_external_services.feature +24 -0
  10. data/features/step_definitions/common_steps.rb +175 -0
  11. data/features/step_definitions/culerity_setup_steps.rb +7 -0
  12. data/features/step_definitions/jruby_steps.rb +11 -0
  13. data/features/step_definitions/rails_setup_steps.rb +26 -0
  14. data/features/support/common.rb +32 -0
  15. data/features/support/env.rb +24 -0
  16. data/features/support/matchers.rb +11 -0
  17. data/init.rb +1 -0
  18. data/lib/culerity.rb +60 -0
  19. data/lib/culerity/celerity_server.rb +81 -0
  20. data/lib/culerity/remote_browser_proxy.rb +58 -0
  21. data/lib/culerity/remote_object_proxy.rb +76 -0
  22. data/rails/init.rb +1 -0
  23. data/rails_generators/culerity/culerity_generator.rb +25 -0
  24. data/rails_generators/culerity/templates/config/environments/culerity_continuousintegration.rb +28 -0
  25. data/rails_generators/culerity/templates/config/environments/culerity_development.rb +17 -0
  26. data/rails_generators/culerity/templates/features/step_definitions/common_celerity_steps.rb +93 -0
  27. data/rails_generators/culerity/templates/lib/tasks/culerity.rake +38 -0
  28. data/script/console +10 -0
  29. data/script/destroy +14 -0
  30. data/script/generate +14 -0
  31. data/spec/celerity_server_spec.rb +97 -0
  32. data/spec/remote_browser_proxy_spec.rb +62 -0
  33. data/spec/remote_object_proxy_spec.rb +63 -0
  34. data/spec/spec_helper.rb +7 -0
  35. metadata +110 -0
@@ -0,0 +1,7 @@
1
+ When /^I setup load path to local code$/ do
2
+ project_lib_path = File.expand_path(File.dirname(__FILE__) + "/../../lib")
3
+ in_project_folder do
4
+ force_local_lib_override(:target => 'features/step_definitions/common_celerity_steps.rb')
5
+ end
6
+ end
7
+
@@ -0,0 +1,11 @@
1
+ Given /^I have jruby installed$/ do
2
+ @jruby_cmd = `which jruby`.strip
3
+ raise "Need to setup @jruby_cmd to test jruby environment" if @jruby_cmd.blank?
4
+ end
5
+
6
+ Then /^the gem "([^\"]*)" is installed into jruby environment$/ do |gem_name|
7
+ raise "Need to setup @jruby_cmd to test jruby environment" if @jruby_cmd.blank?
8
+ gem_list = `#{@jruby_cmd} -S gem list #{gem_name}`
9
+ gem_list.should =~ /#{gem_name}/
10
+ end
11
+
@@ -0,0 +1,26 @@
1
+ Given /^a Rails app$/ do
2
+ FileUtils.chdir(@tmp_root) do
3
+ `rails my_project`
4
+ end
5
+ @active_project_folder = File.expand_path(File.join(@tmp_root, "my_project"))
6
+ end
7
+
8
+ Given /^I copy the project generators into "([^\"]*)"$/ do |target_folder|
9
+ in_project_folder do
10
+ FileUtils.mkdir_p(target_folder)
11
+ end
12
+ `cp -rf #{File.dirname(__FILE__) + "/../../rails_generators/*"} #{File.join(@active_project_folder, target_folder)}`
13
+ end
14
+
15
+ When /^I add a feature file to test Rails index.html default file$/ do
16
+ sample_feature = File.expand_path(File.dirname(__FILE__) + "/../fixtures/sample_feature")
17
+ in_project_folder do
18
+ `cp -rf #{sample_feature} features/sample.feature`
19
+ end
20
+ end
21
+
22
+ After do
23
+ in_project_folder do
24
+ Given 'I invoke task "rake culerity:rails:stop"'
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ module CommonHelpers
2
+ def in_tmp_folder(&block)
3
+ FileUtils.chdir(@tmp_root, &block)
4
+ end
5
+
6
+ def in_project_folder(&block)
7
+ project_folder = @active_project_folder || @tmp_root
8
+ FileUtils.chdir(project_folder, &block)
9
+ end
10
+
11
+ def in_home_folder(&block)
12
+ FileUtils.chdir(@home_path, &block)
13
+ end
14
+
15
+ def force_local_lib_override(options = {})
16
+ target_path = options[:target_path] || options[:target_file] || options[:target] || 'Rakefile'
17
+ in_project_folder do
18
+ contents = File.read(target_path)
19
+ File.open(target_path, "w+") do |f|
20
+ f << "$:.unshift('#{@lib_path}')\n"
21
+ f << contents
22
+ end
23
+ end
24
+ end
25
+
26
+ def setup_active_project_folder project_name
27
+ @active_project_folder = File.join(@tmp_root, project_name)
28
+ @project_name = project_name
29
+ end
30
+ end
31
+
32
+ World(CommonHelpers)
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + "/../../lib/culerity"
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
7
+
8
+ Before do
9
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
10
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
11
+ @lib_path = File.expand_path(File.dirname(__FILE__) + "/../../lib")
12
+ FileUtils.rm_rf @tmp_root
13
+ FileUtils.mkdir_p @home_path
14
+ ENV['HOME'] = @home_path
15
+ end
16
+
17
+ require 'rubigen'
18
+ require 'rubigen/helpers/generator_test_helper'
19
+ include RubiGen::GeneratorTestHelper
20
+ require 'rails_generator'
21
+
22
+ SOURCES = Dir[File.dirname(__FILE__) + "/../../generators"].map do |f|
23
+ RubiGen::PathSource.new(:test, File.expand_path(f))
24
+ end
@@ -0,0 +1,11 @@
1
+ module Matchers
2
+ def contain(expected)
3
+ simple_matcher("contain #{expected.inspect}") do |given, matcher|
4
+ matcher.failure_message = "expected #{given.inspect} to contain #{expected.inspect}"
5
+ matcher.negative_failure_message = "expected #{given.inspect} not to contain #{expected.inspect}"
6
+ given.index expected
7
+ end
8
+ end
9
+ end
10
+
11
+ World(Matchers)
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'rails/init'
data/lib/culerity.rb ADDED
@@ -0,0 +1,60 @@
1
+ require File.dirname(__FILE__) + '/culerity/remote_object_proxy'
2
+ require File.dirname(__FILE__) + '/culerity/remote_browser_proxy'
3
+
4
+ module Culerity
5
+
6
+ module ServerCommands
7
+ def exit_server
8
+ self << '["_exit_"]'
9
+ Process.kill(6, self.pid.to_i)
10
+ end
11
+
12
+ def close_browsers
13
+ self.puts '["_close_browsers_"]'
14
+ end
15
+ end
16
+
17
+ def self.run_server
18
+ IO.popen("jruby #{__FILE__}", 'r+').extend(ServerCommands)
19
+
20
+ # open the two pipes that were created below
21
+ # while(!File.exists?("tmp/culerity_in.pipe"))
22
+ # sleep(1)
23
+ # end
24
+ # pipe_in = open("tmp/culerity_in.pipe", "w+")
25
+ # pipe_out = open("tmp/culerity_out.pipe", "r+")
26
+
27
+
28
+
29
+ # store celerity pid in tmp/culerity_celerity.pid
30
+ # store server pid in tmp/culerity_rails_server.pid
31
+
32
+ # open named pipes to communicate with celerity_server + return them
33
+ end
34
+
35
+ def self.run_rails
36
+ unless File.exists?("tmp/culerity_rails_server.pid")
37
+ puts "WARNING: Speed up execution by running 'rake culerity:rails:start'"
38
+ port = 3001
39
+ environment = 'culerity_development'
40
+ puts "Launched rails on :#{port}..."
41
+ return IO.popen("script/server -e #{environment} -p #{port}", 'r+')
42
+ end
43
+ end
44
+ end
45
+
46
+ if __FILE__ == $0
47
+ # `rm tmp/culerity_in.pipe`
48
+ # `mkfifo tmp/culerity_in.pipe`
49
+ # `rm tmp/culerity_out.pipe`
50
+ # `mkfifo tmp/culerity_out.pipe`
51
+ #
52
+ # pipe_in = open("tmp/culerity_in.pipe", "r+")
53
+ # p pipe_in
54
+ # p STDIN
55
+ # pipe_out = open("tmp/culerity_out.pipe", "w+")
56
+ #
57
+ require File.dirname(__FILE__) + '/culerity/celerity_server'
58
+ Culerity::CelerityServer.new STDIN, STDOUT
59
+ end
60
+
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'celerity'
3
+
4
+
5
+ module Culerity
6
+ class CelerityServer
7
+
8
+ def initialize(_in, _out)
9
+ @proxies = {}
10
+ @browsers = []
11
+
12
+ while(true)
13
+ call = eval _in.gets.to_s.strip
14
+ return if call == ["_exit_"]
15
+ next(close_browsers) if call == ["_close_browsers_"]
16
+ unless call.nil?
17
+ begin
18
+ # check if last arg is a block
19
+ if call.last.is_a?(Proc)
20
+ # pass as &call[-1]
21
+ result = target(call.first).send call[1], *call[2..-2], &call[-1]
22
+ else
23
+ # just call with args as normal
24
+ result = target(call.first).send call[1], *call[2..-1]
25
+ end
26
+ _out << "[:return, #{proxify result}]\n"
27
+ rescue => e
28
+ _out << "[:exception, \"#{e.class.name}\", #{e.message.inspect}, #{e.backtrace.inspect}]\n"
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ private
37
+
38
+ def configure_browser(options)
39
+ @browser_options = options
40
+ end
41
+
42
+ def new_browser(options, number = nil)
43
+ number ||= @browsers.size
44
+ @browsers[number] = Celerity::Browser.new(options || @browser_options || {})
45
+ "browser#{number}"
46
+ end
47
+
48
+ def close_browsers
49
+ @browsers.each { |browser| browser.close }
50
+ @browsers = []
51
+ end
52
+
53
+ def browser(number)
54
+ unless @browsers[number]
55
+ new_browser(nil, number)
56
+ end
57
+ @browsers[number]
58
+ end
59
+
60
+ def target(object_id)
61
+ if object_id =~ /browser(\d+)/
62
+ browser($1.to_i)
63
+ elsif object_id == 'celerity'
64
+ self
65
+ else
66
+ @proxies[object_id]
67
+ end
68
+ end
69
+
70
+ def proxify(result, in_array = false)
71
+ if result.is_a?(Array)
72
+ result.map {|x| proxify(x, true) }.inspect
73
+ elsif [String, TrueClass, FalseClass, Fixnum, Float, NilClass].include?(result.class)
74
+ in_array ? result : result.inspect
75
+ else
76
+ @proxies[result.object_id] = result
77
+ "Culerity::RemoteObjectProxy.new(#{result.object_id}, @io)"
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,58 @@
1
+ module Culerity
2
+
3
+ class RemoteBrowserProxy < RemoteObjectProxy
4
+ def initialize(io, browser_options = {})
5
+ @io = io
6
+ @remote_object_id = "celerity".inspect
7
+ @remote_object_id = new_browser(browser_options).inspect
8
+ end
9
+
10
+ #
11
+ # Calls the block until it returns true or +time_to_wait+ is reached.
12
+ # +time_to_wait+ is 30 seconds by default
13
+ #
14
+ # Returns true upon success
15
+ # Raises Timeout::Error when +time_to_wait+ is reached.
16
+ #
17
+ def wait_until time_to_wait=30, &block
18
+ Timeout.timeout(time_to_wait) do
19
+ until block.call
20
+ sleep 0.1
21
+ end
22
+ end
23
+ true
24
+ end
25
+
26
+ #
27
+ # Calls the block until it doesn't return true or +time_to_wait+ is reached.
28
+ # +time_to_wait+ is 30 seconds by default
29
+ #
30
+ # Returns true upon success
31
+ # Raises Timeout::Error when +time_to_wait+ is reached.
32
+ #
33
+ def wait_while time_to_wait=30, &block
34
+ Timeout.timeout(time_to_wait) do
35
+ while block.call
36
+ sleep 0.1
37
+ end
38
+ end
39
+ true
40
+ end
41
+
42
+
43
+ #
44
+ # Specify whether to accept or reject all confirm js dialogs
45
+ # for the code in the block that's run.
46
+ #
47
+ def confirm(bool, &block)
48
+ blk = "lambda { #{bool} }"
49
+
50
+ self.send_remote(:add_listener, :confirm) { blk }
51
+ block.call
52
+ self.send_remote(:remove_listener, :confirm) { blk }
53
+ end
54
+
55
+ end
56
+
57
+
58
+ end
@@ -0,0 +1,76 @@
1
+ module Culerity
2
+
3
+ class CulerityException < StandardError
4
+ def initialize(message, backtrace)
5
+ super message
6
+ #self.backtrace = backtrace
7
+ end
8
+ end
9
+
10
+ class RemoteObjectProxy
11
+ def initialize(remote_object_id, io)
12
+ @remote_object_id = remote_object_id
13
+ @io = io
14
+ end
15
+
16
+ #
17
+ # Commonly used to get the HTML id attribute
18
+ # Use `object_id` to get the local objects' id.
19
+ #
20
+ def id
21
+ send_remote(:id)
22
+ end
23
+
24
+ def method_missing(name, *args)
25
+ send_remote(name, *args)
26
+ end
27
+
28
+ #
29
+ # Calls the passed method on the remote object with any arguments specified.
30
+ # Behaves the same as <code>Object#send</code>.
31
+ #
32
+ # If you pass it a block then it will append the block as a "lambda { … }".
33
+ # If your block returns a lambda string ("lambda { … }") then it will be passed
34
+ # straight through, otherwise it will be wrapped in a lambda string before sending.
35
+ #
36
+ def send_remote(name, *args, &blk)
37
+ input = [remote_object_id, %Q{"#{name}"}, *args.map{|a| a.inspect}]
38
+ input << block_to_string(&blk) if block_given?
39
+ @io << "[#{input.join(", ")}]\n"
40
+ process_result @io.gets.to_s.strip
41
+ end
42
+
43
+ def exit
44
+ @io << '["_exit_"]'
45
+ end
46
+
47
+ private
48
+
49
+ def process_result(result)
50
+ res = eval result
51
+ if res.first == :return
52
+ res[1]
53
+ elsif res.first == :exception
54
+ raise CulerityException.new("#{res[1]}: #{res[2]}", res[3])
55
+ end
56
+ end
57
+
58
+ #
59
+ # Takes a block and either returns the result (if it returns "lambda { … }")
60
+ # or builds the lambda string with the result of the block in it.
61
+ #
62
+ # Returns a string in the format "lambda { … }"
63
+ #
64
+ def block_to_string &block
65
+ result = block.call.to_s
66
+ unless result.is_a?(String) && result[/^lambda \s* \{ .*? \}/x]
67
+ result = "lambda { #{result} }"
68
+ end
69
+ result
70
+ end
71
+
72
+ def remote_object_id
73
+ @remote_object_id
74
+ end
75
+ end
76
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'culerity'
@@ -0,0 +1,25 @@
1
+ class CulerityGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+ m.directory 'features/step_definitions'
6
+ m.file 'features/step_definitions/common_celerity_steps.rb', 'features/step_definitions/common_celerity_steps.rb'
7
+ m.file 'config/environments/culerity_continuousintegration.rb', 'config/environments/culerity_continuousintegration.rb'
8
+ m.file 'config/environments/culerity_development.rb', 'config/environments/culerity_development.rb'
9
+
10
+ m.gsub_file 'config/database.yml', /cucumber:.*\n/, "cucumber: &CUCUMBER\n"
11
+
12
+ m.gsub_file 'config/database.yml', /\z/, "\nculerity_development:\n <<: *CUCUMBER"
13
+ m.gsub_file 'config/database.yml', /\z/, "\nculerity_continuousintegration:\n <<: *CUCUMBER"
14
+
15
+ m.file "lib/tasks/culerity.rake", "lib/tasks/culerity.rake"
16
+ end
17
+ end
18
+
19
+ protected
20
+
21
+ def banner
22
+ "Usage: #{$0} culerity"
23
+ end
24
+
25
+ end
@@ -0,0 +1,28 @@
1
+ # Settings specified here will take precedence over those in config/environment.rb
2
+
3
+ # The production environment is meant for finished, "live" apps.
4
+ # Code is not reloaded between requests
5
+ config.cache_classes = true
6
+
7
+ # Full error reports are disabled and caching is turned on
8
+ config.action_controller.consider_all_requests_local = false
9
+ config.action_controller.perform_caching = true
10
+ config.action_view.cache_template_loading = true
11
+
12
+ # See everything in the log (default is :info)
13
+ # config.log_level = :debug
14
+
15
+ # Use a different logger for distributed setups
16
+ # config.logger = SyslogLogger.new
17
+
18
+ # Use a different cache store in production
19
+ # config.cache_store = :mem_cache_store
20
+
21
+ # Enable serving of images, stylesheets, and javascripts from an asset server
22
+ # config.action_controller.asset_host = "http://assets.example.com"
23
+
24
+ # Disable delivery errors, bad email addresses will be ignored
25
+ # config.action_mailer.raise_delivery_errors = false
26
+
27
+ # Enable threaded mode
28
+ # config.threadsafe!