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.
- data/.gitignore +6 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +106 -0
- data/Rakefile +43 -0
- data/VERSION.yml +4 -0
- data/culerity.gemspec +78 -0
- data/features/fixtures/sample_feature +14 -0
- data/features/installing_culerity.feature +46 -0
- data/features/running_cucumber_without_explicitly_running_external_services.feature +24 -0
- data/features/step_definitions/common_steps.rb +175 -0
- data/features/step_definitions/culerity_setup_steps.rb +7 -0
- data/features/step_definitions/jruby_steps.rb +11 -0
- data/features/step_definitions/rails_setup_steps.rb +26 -0
- data/features/support/common.rb +32 -0
- data/features/support/env.rb +24 -0
- data/features/support/matchers.rb +11 -0
- data/init.rb +1 -0
- data/lib/culerity.rb +60 -0
- data/lib/culerity/celerity_server.rb +81 -0
- data/lib/culerity/remote_browser_proxy.rb +58 -0
- data/lib/culerity/remote_object_proxy.rb +76 -0
- data/rails/init.rb +1 -0
- data/rails_generators/culerity/culerity_generator.rb +25 -0
- data/rails_generators/culerity/templates/config/environments/culerity_continuousintegration.rb +28 -0
- data/rails_generators/culerity/templates/config/environments/culerity_development.rb +17 -0
- data/rails_generators/culerity/templates/features/step_definitions/common_celerity_steps.rb +93 -0
- data/rails_generators/culerity/templates/lib/tasks/culerity.rake +38 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/celerity_server_spec.rb +97 -0
- data/spec/remote_browser_proxy_spec.rb +62 -0
- data/spec/remote_object_proxy_spec.rb +63 -0
- data/spec/spec_helper.rb +7 -0
- metadata +110 -0
@@ -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
|
data/rails_generators/culerity/templates/config/environments/culerity_continuousintegration.rb
ADDED
@@ -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!
|