rails-carrot 1.0.0
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/Manifest +9 -0
- data/README +81 -0
- data/Rakefile +12 -0
- data/carrot.gemspec +30 -0
- data/lib/carrot/extend_server.rb +59 -0
- data/lib/carrot/rails.rb +15 -0
- data/lib/carrot/server.rb +96 -0
- data/lib/carrot/util/timeout.rb +27 -0
- data/lib/carrot.rb +75 -0
- data/rails-carrot.gemspec +30 -0
- metadata +84 -0
data/Manifest
ADDED
data/README
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
You can use carrot with celerity, or another driver.
|
2
|
+
|
3
|
+
1. gem 'carrot'
|
4
|
+
|
5
|
+
2. add a celerity_helper
|
6
|
+
|
7
|
+
3. Add some code in celerity_helper
|
8
|
+
require 'database_cleaner'
|
9
|
+
require 'celerity'
|
10
|
+
require 'carrot'
|
11
|
+
|
12
|
+
ENV["RAILS_ENV"] = 'celerity' # You can change it with your enviroment
|
13
|
+
require File.expand_path("../../config/environment", __FILE__)
|
14
|
+
require 'rspec/rails'
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
4. Three test styles
|
19
|
+
a) Remote
|
20
|
+
Carrot.configure do |config|
|
21
|
+
config.run_server = false
|
22
|
+
config.app_host = "www.google.com"
|
23
|
+
end
|
24
|
+
Carrot.register_driver(Celerity::Browser.new)
|
25
|
+
|
26
|
+
b) Local
|
27
|
+
Carrot.configure do |config|
|
28
|
+
config.run_server = true
|
29
|
+
end
|
30
|
+
Carrot.register_driver(Celerity::Browser.new)
|
31
|
+
Carrot.boot
|
32
|
+
|
33
|
+
c) Local, server with external ruby
|
34
|
+
|
35
|
+
Carrot.configure do |config|
|
36
|
+
config.run_server = true
|
37
|
+
config.external_ruby = true
|
38
|
+
config.rails_command = "~/.rvm/gems/ruby-1.9.2-p0/bin/rails s -e celerity -p 3001"
|
39
|
+
config.project_path = "#{Rails.root}"
|
40
|
+
config.server_port = 3001
|
41
|
+
# config.server_debug = true
|
42
|
+
end
|
43
|
+
Carrot.register_driver(Celerity::Browser.new)
|
44
|
+
Carrot.boot
|
45
|
+
|
46
|
+
RSpec.configure do |config|
|
47
|
+
DatabaseCleaner.strategy = :truncation
|
48
|
+
|
49
|
+
config.before :each do
|
50
|
+
@browser = Carrot.driver # get native driver
|
51
|
+
DatabaseCleaner.clean
|
52
|
+
end
|
53
|
+
|
54
|
+
config.after :all do
|
55
|
+
Carrot.shutdown # shutdown server
|
56
|
+
DatabaseCleaner.clean
|
57
|
+
end
|
58
|
+
|
59
|
+
config.use_transactional_fixtures = false
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
5 Test
|
64
|
+
a) get url
|
65
|
+
Carrot.url(path)
|
66
|
+
eg:
|
67
|
+
Carrot.url('/') => http://host/
|
68
|
+
Carrot.url('/hello') => http://host/hello
|
69
|
+
|
70
|
+
b) get native browser driver
|
71
|
+
Carrot.driver
|
72
|
+
|
73
|
+
6 FAQ
|
74
|
+
a) Does Carrot support server with external ruby?
|
75
|
+
Yes, but it just supports jruby run rspec and run server using external ruby. I use jruby to create native java process to run server using external ruby.
|
76
|
+
|
77
|
+
If you want to use ruby to run rspec and run server using external ruby, you can create a new server class to support it or wait for my upgrade.
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('rails-carrot', '1.0.0') do |p|
|
6
|
+
p.description = "Carrot can make QA test easy with any integration test gem."
|
7
|
+
p.url = "http://github.com/sloanwu/carrot.git"
|
8
|
+
p.author = "Sloan Wu"
|
9
|
+
p.email = "sloanwu@gmail.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
data/carrot.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{carrot}
|
5
|
+
s.version = "1.0.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Sloan Wu"]
|
9
|
+
s.date = %q{2010-11-02}
|
10
|
+
s.description = %q{Carrot can make QA test easy with any integration test gem.}
|
11
|
+
s.email = %q{sloanwu@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["lib/carrot.rb", "lib/carrot/extend_server.rb", "lib/carrot/rails.rb", "lib/carrot/server.rb", "lib/carrot/util/timeout.rb"]
|
13
|
+
s.files = ["Manifest", "Rakefile", "carrot.gemspec", "lib/carrot.rb", "lib/carrot/extend_server.rb", "lib/carrot/rails.rb", "lib/carrot/server.rb", "lib/carrot/util/timeout.rb"]
|
14
|
+
s.homepage = %q{http://github.com/sloanwu/carrot.git}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Carrot", "--main", "README"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{carrot}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Carrot can make QA test easy with any integration test gem.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "java"
|
2
|
+
require 'carrot/util/timeout'
|
3
|
+
require 'uri'
|
4
|
+
require 'net/http'
|
5
|
+
require 'rack'
|
6
|
+
import java.lang.Runtime
|
7
|
+
import java.io.InputStreamReader
|
8
|
+
import java.io.BufferedReader
|
9
|
+
|
10
|
+
module Carrot
|
11
|
+
class ExtendServer
|
12
|
+
attr_accessor :server_process, :shutdown_process
|
13
|
+
|
14
|
+
def run()
|
15
|
+
Thread.new do
|
16
|
+
command = ["/bin/bash","-c", "cd #{Carrot.project_path} ; #{Carrot.rails_command}"].to_java(java.lang.String)
|
17
|
+
begin
|
18
|
+
@server_process = Runtime.getRuntime().exec(command);
|
19
|
+
if Carrot.server_debug
|
20
|
+
buf = BufferedReader.new(InputStreamReader.new(@server_process.get_input_stream))
|
21
|
+
while line = buf.readLine()
|
22
|
+
puts line
|
23
|
+
end
|
24
|
+
end
|
25
|
+
@server_process.wait_for
|
26
|
+
rescue Exception => e
|
27
|
+
puts e
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
Carrot.timeout(60) { if responsive? then true else sleep(0.5) and false end }
|
32
|
+
end
|
33
|
+
|
34
|
+
def shutdown
|
35
|
+
Thread.new do
|
36
|
+
command = ["/bin/bash","-c", "cd #{Carrot.project_path} ; kill `cat #{Carrot.project_path}/tmp/pids/server.pid`"].to_java(java.lang.String)
|
37
|
+
begin
|
38
|
+
@shutdown_process = Runtime.getRuntime().exec(command);
|
39
|
+
@shutdown_process.wait_for
|
40
|
+
@shutdown_process.destroy
|
41
|
+
rescue Exception => e
|
42
|
+
puts e
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def responsive?
|
48
|
+
host = "127.0.0.1"
|
49
|
+
res = Net::HTTP.start(host, Carrot.server_port) { |http| http.get('/__identify__') }
|
50
|
+
|
51
|
+
unless res.nil?
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
rescue Errno::ECONNREFUSED, Errno::EBADF
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/lib/carrot/rails.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'carrot'
|
2
|
+
|
3
|
+
Carrot.app = Rack::Builder.new do
|
4
|
+
map "/" do
|
5
|
+
if Rails.version.to_f >= 3.0
|
6
|
+
run Rails.application
|
7
|
+
else # Rails 2
|
8
|
+
use Rails::Rack::Static
|
9
|
+
run ActionController::Dispatcher.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end.to_app
|
13
|
+
|
14
|
+
Carrot.asset_root = Rails.root.join('public')
|
15
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'rack'
|
4
|
+
require 'carrot/util/timeout'
|
5
|
+
require 'carrot/rails'
|
6
|
+
|
7
|
+
module Carrot
|
8
|
+
class Server
|
9
|
+
class Identify
|
10
|
+
def initialize(app)
|
11
|
+
@app = app
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
if env["PATH_INFO"] == "/__identify__"
|
16
|
+
[200, {}, @app.object_id.to_s]
|
17
|
+
else
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def ports
|
25
|
+
@ports ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :app, :port
|
30
|
+
|
31
|
+
def initialize(app)
|
32
|
+
@app = app
|
33
|
+
end
|
34
|
+
|
35
|
+
def host
|
36
|
+
"127.0.0.1"
|
37
|
+
end
|
38
|
+
|
39
|
+
def url(path)
|
40
|
+
if path =~ /^http/
|
41
|
+
path
|
42
|
+
else
|
43
|
+
(Carrot.app_host || "http://#{host}:#{Carrot.server_port || port}") + path.to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def responsive?
|
48
|
+
res = Net::HTTP.start(host, @port) { |http| http.get('/__identify__') }
|
49
|
+
|
50
|
+
if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
|
51
|
+
return res.body == @app.object_id.to_s
|
52
|
+
end
|
53
|
+
rescue Errno::ECONNREFUSED, Errno::EBADF
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
|
57
|
+
def boot
|
58
|
+
if Carrot.external_ruby
|
59
|
+
require 'carrot/extend_server'
|
60
|
+
Carrot.external_server = ExtendServer.new
|
61
|
+
Carrot.external_server.run
|
62
|
+
elsif @app
|
63
|
+
@port = Carrot::Server.ports[@app.object_id]
|
64
|
+
|
65
|
+
if not @port or not responsive?
|
66
|
+
@port = Carrot.server_port || find_available_port
|
67
|
+
Carrot::Server.ports[@app.object_id] = @port
|
68
|
+
|
69
|
+
Thread.new do
|
70
|
+
begin
|
71
|
+
require 'rack/handler/thin'
|
72
|
+
Thin::Logging.silent = true
|
73
|
+
Rack::Handler::Thin.run(Identify.new(@app), :Port => @port)
|
74
|
+
rescue LoadError
|
75
|
+
require 'rack/handler/webrick'
|
76
|
+
Rack::Handler::WEBrick.run(Identify.new(@app), :Port => @port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Carrot.timeout(10) { if responsive? then true else sleep(0.5) and false end }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def find_available_port
|
89
|
+
server = TCPServer.new('127.0.0.1', 0)
|
90
|
+
server.addr[1]
|
91
|
+
ensure
|
92
|
+
server.close if server
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Carrot
|
2
|
+
class << self
|
3
|
+
|
4
|
+
##
|
5
|
+
# Provides timeout similar to standard library Timeout, but avoids threads
|
6
|
+
#
|
7
|
+
def timeout(seconds = 1, driver = nil, &block)
|
8
|
+
start_time = Time.now
|
9
|
+
|
10
|
+
result = nil
|
11
|
+
|
12
|
+
until result
|
13
|
+
return result if result = yield
|
14
|
+
|
15
|
+
delay = seconds - (Time.now - start_time)
|
16
|
+
if delay <= 0
|
17
|
+
raise TimeoutError
|
18
|
+
end
|
19
|
+
|
20
|
+
driver && driver.wait_until(delay)
|
21
|
+
|
22
|
+
sleep(0.05)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/carrot.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'xpath'
|
4
|
+
|
5
|
+
module Carrot
|
6
|
+
class CarrotError < StandardError; end
|
7
|
+
class DriverNotFoundError < CarrotError; end
|
8
|
+
class ElementNotFound < CarrotError; end
|
9
|
+
class UnselectNotAllowed < CarrotError; end
|
10
|
+
class NotSupportedByDriverError < CarrotError; end
|
11
|
+
class TimeoutError < CarrotError; end
|
12
|
+
class LocateHiddenElementError < CarrotError; end
|
13
|
+
class InfiniteRedirectError < TimeoutError; end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :asset_root, :app_host, :run_server, :default_host
|
17
|
+
attr_accessor :server_port
|
18
|
+
attr_accessor :driver
|
19
|
+
attr_accessor :app
|
20
|
+
attr_accessor :external_ruby, :rails_command, :project_path
|
21
|
+
attr_accessor :external_server, :server_debug
|
22
|
+
|
23
|
+
##
|
24
|
+
#
|
25
|
+
# Configure Carrot to suit your needs.
|
26
|
+
#
|
27
|
+
# Carrot.configure do |config|
|
28
|
+
# config.run_server = false
|
29
|
+
# config.app_host = 'http://www.google.com'
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# === Configurable options
|
33
|
+
#
|
34
|
+
# [asset_root = String] Where static assets are located, used by save_and_open_page
|
35
|
+
# [app_host = String] The default host to use when giving a relative URL to visit
|
36
|
+
# [run_server = Boolean] Whether to start a Rack server for the given Rack app (Default: true)
|
37
|
+
# [default_selector = :css/:xpath] Methods which take a selector use the given type by default (Default: CSS)
|
38
|
+
# [default_wait_time = Integer] The number of seconds to wait for asynchronous processes to finish (Default: 2)
|
39
|
+
# [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: false)
|
40
|
+
#
|
41
|
+
|
42
|
+
def configure
|
43
|
+
yield self
|
44
|
+
end
|
45
|
+
|
46
|
+
def url(path)
|
47
|
+
@rack_server.url(path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def register_driver(obj)
|
51
|
+
@driver = obj
|
52
|
+
end
|
53
|
+
|
54
|
+
def boot
|
55
|
+
@rack_server = Carrot::Server.new(@app)
|
56
|
+
@rack_server.boot if Carrot.run_server
|
57
|
+
end
|
58
|
+
|
59
|
+
def shutdown
|
60
|
+
@external_server.shutdown if @external_server
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
autoload :Server, 'carrot/server'
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
Carrot.configure do |config|
|
70
|
+
config.run_server = true
|
71
|
+
config.external_ruby = false
|
72
|
+
config.server_debug = false
|
73
|
+
config.server_port = 3000
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{rails-carrot}
|
5
|
+
s.version = "1.0.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Sloan Wu"]
|
9
|
+
s.date = %q{2010-11-02}
|
10
|
+
s.description = %q{Carrot can make QA test easy with any integration test gem.}
|
11
|
+
s.email = %q{sloanwu@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["README", "lib/carrot.rb", "lib/carrot/extend_server.rb", "lib/carrot/rails.rb", "lib/carrot/server.rb", "lib/carrot/util/timeout.rb"]
|
13
|
+
s.files = ["Manifest", "README", "Rakefile", "carrot.gemspec", "lib/carrot.rb", "lib/carrot/extend_server.rb", "lib/carrot/rails.rb", "lib/carrot/server.rb", "lib/carrot/util/timeout.rb", "rails-carrot.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/sloanwu/carrot.git}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rails-carrot", "--main", "README"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{rails-carrot}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Carrot can make QA test easy with any integration test gem.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails-carrot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Sloan Wu
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-02 00:00:00 +08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Carrot can make QA test easy with any integration test gem.
|
22
|
+
email: sloanwu@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README
|
29
|
+
- lib/carrot.rb
|
30
|
+
- lib/carrot/extend_server.rb
|
31
|
+
- lib/carrot/rails.rb
|
32
|
+
- lib/carrot/server.rb
|
33
|
+
- lib/carrot/util/timeout.rb
|
34
|
+
files:
|
35
|
+
- Manifest
|
36
|
+
- README
|
37
|
+
- Rakefile
|
38
|
+
- carrot.gemspec
|
39
|
+
- lib/carrot.rb
|
40
|
+
- lib/carrot/extend_server.rb
|
41
|
+
- lib/carrot/rails.rb
|
42
|
+
- lib/carrot/server.rb
|
43
|
+
- lib/carrot/util/timeout.rb
|
44
|
+
- rails-carrot.gemspec
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/sloanwu/carrot.git
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --line-numbers
|
52
|
+
- --inline-source
|
53
|
+
- --title
|
54
|
+
- Rails-carrot
|
55
|
+
- --main
|
56
|
+
- README
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 1
|
74
|
+
- 2
|
75
|
+
version: "1.2"
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project: rails-carrot
|
79
|
+
rubygems_version: 1.3.7
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Carrot can make QA test easy with any integration test gem.
|
83
|
+
test_files: []
|
84
|
+
|