culerity 0.2.3

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/.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!