capybara 0.1
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/History.txt +4 -0
- data/Manifest.txt +42 -0
- data/README.rdoc +208 -0
- data/Rakefile +33 -0
- data/examples/webcat.rb +36 -0
- data/lib/capybara.rb +30 -0
- data/lib/capybara/core_ext/tcp_socket.rb +26 -0
- data/lib/capybara/cucumber.rb +32 -0
- data/lib/capybara/driver/culerity_driver.rb +83 -0
- data/lib/capybara/driver/firewatir_driver.rb +66 -0
- data/lib/capybara/driver/rack_test_driver.rb +151 -0
- data/lib/capybara/driver/safariwatir_driver.rb +67 -0
- data/lib/capybara/driver/selenium_driver.rb +93 -0
- data/lib/capybara/dsl.rb +58 -0
- data/lib/capybara/node.rb +36 -0
- data/lib/capybara/rails.rb +11 -0
- data/lib/capybara/save_and_open_page.rb +25 -0
- data/lib/capybara/server.rb +53 -0
- data/lib/capybara/session.rb +190 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/driver/culerity_driver_spec.rb +10 -0
- data/spec/driver/firewatir_driver_spec.rb +10 -0
- data/spec/driver/rack_test_driver_spec.rb +9 -0
- data/spec/driver/safariwarit_driver_spec.rb +10 -0
- data/spec/driver/selenium_driver_spec.rb +10 -0
- data/spec/drivers_spec.rb +72 -0
- data/spec/dsl_spec.rb +139 -0
- data/spec/fixtures/test_file.txt +1 -0
- data/spec/public/jquery.js +19 -0
- data/spec/session/culerity_session_spec.rb +23 -0
- data/spec/session/rack_test_session_spec.rb +23 -0
- data/spec/session/selenium_session_spec.rb +23 -0
- data/spec/session_spec.rb +611 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/test_app.rb +67 -0
- data/spec/views/form.erb +117 -0
- data/spec/views/with_html.erb +19 -0
- data/spec/views/with_js.erb +20 -0
- data/spec/views/with_scope.erb +36 -0
- data/spec/views/with_simple_html.erb +1 -0
- metadata +164 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Capybara
|
2
|
+
module SaveAndOpenPage
|
3
|
+
extend(self)
|
4
|
+
|
5
|
+
def save_and_open_page(html)
|
6
|
+
require 'tempfile'
|
7
|
+
tempfile = Tempfile.new("capybara#{rand(1000000)}")
|
8
|
+
tempfile.write(rewrite_css_and_image_references(html))
|
9
|
+
tempfile.close
|
10
|
+
open_in_browser(tempfile.path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def open_in_browser(path) # :nodoc
|
14
|
+
require "launchy"
|
15
|
+
Launchy::Browser.run(path)
|
16
|
+
rescue LoadError
|
17
|
+
warn "Sorry, you need to install launchy to open pages: `gem install launchy`"
|
18
|
+
end
|
19
|
+
|
20
|
+
def rewrite_css_and_image_references(response_html) # :nodoc:
|
21
|
+
return response_html unless Capybara.asset_root
|
22
|
+
response_html.gsub(/("|')\/(stylesheets|images)/, '\1' + Capybara.asset_root + '/\2')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
class Capybara::Server
|
5
|
+
attr_reader :app
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def port
|
12
|
+
8080
|
13
|
+
end
|
14
|
+
|
15
|
+
def host
|
16
|
+
'localhost'
|
17
|
+
end
|
18
|
+
|
19
|
+
def url(path)
|
20
|
+
"http://#{host}:#{port}#{path}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def boot
|
24
|
+
Capybara.log "application has already booted" and return if responsive?
|
25
|
+
Capybara.log "booting Rack applicartion on port #{port}"
|
26
|
+
start_time = Time.now
|
27
|
+
Thread.new do
|
28
|
+
Rack::Handler::Mongrel.run @app, :Port => port
|
29
|
+
end
|
30
|
+
Capybara.log "checking if application has booted"
|
31
|
+
loop do
|
32
|
+
Capybara.log("application has booted") and break if responsive?
|
33
|
+
if Time.now - start_time > 10
|
34
|
+
Capybara.log "Rack application timed out during boot"
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
Capybara.log '.'
|
39
|
+
sleep 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def responsive?
|
44
|
+
res = Net::HTTP.start(host, port) { |http| http.get('/') }
|
45
|
+
|
46
|
+
if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
rescue Errno::ECONNREFUSED
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
class Capybara::Session
|
2
|
+
attr_reader :mode, :app
|
3
|
+
|
4
|
+
def initialize(mode, app)
|
5
|
+
@mode = mode
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def driver
|
10
|
+
@driver ||= case mode
|
11
|
+
when :rack_test
|
12
|
+
Capybara::Driver::RackTest.new(app)
|
13
|
+
when :culerity
|
14
|
+
Capybara::Driver::Culerity.new(app)
|
15
|
+
when :selenium
|
16
|
+
Capybara::Driver::Selenium.new(app)
|
17
|
+
else
|
18
|
+
raise Capybara::DriverNotFoundError, "no driver called #{mode} was found"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit(path)
|
23
|
+
driver.visit(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def click_link(locator)
|
27
|
+
find_link(locator).click
|
28
|
+
end
|
29
|
+
|
30
|
+
def click_button(locator)
|
31
|
+
find_button(locator).click
|
32
|
+
end
|
33
|
+
|
34
|
+
def fill_in(locator, options={})
|
35
|
+
find_field(locator, :text_field, :text_area, :password_field).set(options[:with])
|
36
|
+
end
|
37
|
+
|
38
|
+
def choose(locator)
|
39
|
+
find_field(locator, :radio).set(true)
|
40
|
+
end
|
41
|
+
|
42
|
+
def check(locator)
|
43
|
+
find_field(locator, :checkbox).set(true)
|
44
|
+
end
|
45
|
+
|
46
|
+
def uncheck(locator)
|
47
|
+
find_field(locator, :checkbox).set(false)
|
48
|
+
end
|
49
|
+
|
50
|
+
def select(value, options={})
|
51
|
+
find_field(options[:from], :select).select(value)
|
52
|
+
end
|
53
|
+
|
54
|
+
def attach_file(locator, path)
|
55
|
+
find_field(locator, :file_field).set(path)
|
56
|
+
end
|
57
|
+
|
58
|
+
def body
|
59
|
+
driver.body
|
60
|
+
end
|
61
|
+
|
62
|
+
def has_content?(content)
|
63
|
+
has_xpath?("//*[contains(.,'#{content}')]")
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_xpath?(path, options={})
|
67
|
+
results = find(path)
|
68
|
+
if options[:text]
|
69
|
+
results = filter_by_text(results, options[:text])
|
70
|
+
end
|
71
|
+
if options[:count]
|
72
|
+
results.size == options[:count]
|
73
|
+
else
|
74
|
+
results.size > 0
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_css?(path, options={})
|
79
|
+
has_xpath?(css_to_xpath(path), options)
|
80
|
+
end
|
81
|
+
|
82
|
+
def within(kind, scope=nil)
|
83
|
+
kind, scope = :xpath, kind unless scope
|
84
|
+
scope = css_to_xpath(scope) if kind == :css
|
85
|
+
raise Capybara::ElementNotFound, "scope '#{scope}' not found on page" if find(scope).empty?
|
86
|
+
scopes.push(scope)
|
87
|
+
yield
|
88
|
+
scopes.pop
|
89
|
+
end
|
90
|
+
|
91
|
+
def save_and_open_page
|
92
|
+
require 'capybara/save_and_open_page'
|
93
|
+
Capybara::SaveAndOpenPage.save_and_open_page(body)
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_field(locator, *kinds)
|
97
|
+
kinds = FIELDS_PATHS.keys if kinds.empty?
|
98
|
+
field = find_field_by_id(locator, *kinds) || find_field_by_label(locator, *kinds)
|
99
|
+
raise Capybara::ElementNotFound, "no field of kind #{kinds.inspect} with id or label '#{locator}' found" unless field
|
100
|
+
field
|
101
|
+
end
|
102
|
+
alias_method :field_labeled, :find_field
|
103
|
+
|
104
|
+
def find_link(locator)
|
105
|
+
link = find_element("//a[@id='#{locator}']", "//a[contains(.,'#{locator}')]", "//a[@title='#{locator}']")
|
106
|
+
raise Capybara::ElementNotFound, "no link with title, id or text '#{locator}' found" unless link
|
107
|
+
link
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_button(locator)
|
111
|
+
button = find_element(
|
112
|
+
"//input[@type='submit'][@id='#{locator}']",
|
113
|
+
"//input[@type='submit'][@value='#{locator}']",
|
114
|
+
"//input[@type='image'][@id='#{locator}']",
|
115
|
+
"//input[@type='image'][@value='#{locator}']"
|
116
|
+
)
|
117
|
+
raise Capybara::ElementNotFound, "no button with value or id '#{locator}' found" unless button
|
118
|
+
button
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def css_to_xpath(css)
|
124
|
+
Nokogiri::CSS.xpath_for(css).first
|
125
|
+
end
|
126
|
+
|
127
|
+
def filter_by_text(nodes, text)
|
128
|
+
nodes.select do |node|
|
129
|
+
case text
|
130
|
+
when String
|
131
|
+
node.text.include?(text)
|
132
|
+
when Regexp
|
133
|
+
node.text =~ text
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def current_scope
|
139
|
+
scopes.join('')
|
140
|
+
end
|
141
|
+
|
142
|
+
def scopes
|
143
|
+
@scopes ||= []
|
144
|
+
end
|
145
|
+
|
146
|
+
FIELDS_PATHS = {
|
147
|
+
:text_field => proc { |id| "//input[@type='text'][@id='#{id}']" },
|
148
|
+
:text_area => proc { |id| "//textarea[@id='#{id}']" },
|
149
|
+
:password_field => proc { |id| "//input[@type='password'][@id='#{id}']" },
|
150
|
+
:radio => proc { |id| "//input[@type='radio'][@id='#{id}']" },
|
151
|
+
:hidden_field => proc { |id| "//input[@type='hidden'][@id='#{id}']" },
|
152
|
+
:checkbox => proc { |id| "//input[@type='checkbox'][@id='#{id}']" },
|
153
|
+
:select => proc { |id| "//select[@id='#{id}']" },
|
154
|
+
:file_field => proc { |id| "//input[@type='file'][@id='#{id}']" }
|
155
|
+
}
|
156
|
+
|
157
|
+
def find_field_by_id(locator, *kinds)
|
158
|
+
kinds.each do |kind|
|
159
|
+
path = FIELDS_PATHS[kind]
|
160
|
+
element = find(path.call(locator)).first
|
161
|
+
return element if element
|
162
|
+
end
|
163
|
+
return nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def find_field_by_label(locator, *kinds)
|
167
|
+
kinds.each do |kind|
|
168
|
+
label = find("//label[contains(.,'#{locator}')]").first
|
169
|
+
if label
|
170
|
+
element = find_field_by_id(label[:for], kind)
|
171
|
+
return element if element
|
172
|
+
end
|
173
|
+
end
|
174
|
+
return nil
|
175
|
+
end
|
176
|
+
|
177
|
+
def find_element(*locators)
|
178
|
+
locators.each do |locator|
|
179
|
+
element = find(locator).first
|
180
|
+
return element if element
|
181
|
+
end
|
182
|
+
return nil
|
183
|
+
end
|
184
|
+
|
185
|
+
def find(locator)
|
186
|
+
locator = current_scope.to_s + locator
|
187
|
+
driver.find(locator)
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/capybara.rb'}"
|
9
|
+
puts "Loading capybara gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Capybara::Driver::Culerity do
|
4
|
+
before do
|
5
|
+
@driver = Capybara::Driver::Culerity.new(TestApp)
|
6
|
+
end
|
7
|
+
|
8
|
+
it_should_behave_like "driver"
|
9
|
+
it_should_behave_like "driver with javascript support"
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Capybara::Driver::FireWatir do
|
4
|
+
before do
|
5
|
+
@driver = Capybara::Driver::FireWatir.new(TestApp)
|
6
|
+
end
|
7
|
+
|
8
|
+
# it_should_behave_like "driver"
|
9
|
+
# it_should_behave_like "driver with javascript support"
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Capybara::Driver::SafariWatir do
|
4
|
+
before do
|
5
|
+
@driver = Capybara::Driver::SafariWatir.new(TestApp)
|
6
|
+
end
|
7
|
+
|
8
|
+
# it_should_behave_like "driver"
|
9
|
+
# it_should_behave_like "driver with javascript support"
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Capybara::Driver::Selenium do
|
4
|
+
before do
|
5
|
+
@driver = Capybara::Driver::Selenium.new(TestApp)
|
6
|
+
end
|
7
|
+
|
8
|
+
it_should_behave_like "driver"
|
9
|
+
it_should_behave_like "driver with javascript support"
|
10
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
shared_examples_for 'driver' do
|
4
|
+
|
5
|
+
describe '#visit' do
|
6
|
+
it "should move to another page" do
|
7
|
+
@driver.visit('/')
|
8
|
+
@driver.body.should include('Hello world!')
|
9
|
+
@driver.visit('/foo')
|
10
|
+
@driver.body.should include('Another World')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#body' do
|
15
|
+
it "should return text reponses" do
|
16
|
+
@driver.visit('/')
|
17
|
+
@driver.body.should include('Hello world!')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return the full response html" do
|
21
|
+
@driver.visit('/with_simple_html')
|
22
|
+
@driver.body.should include('<h1>Bar</h1>')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#find' do
|
27
|
+
context "with xpath selector" do
|
28
|
+
before do
|
29
|
+
@driver.visit('/with_html')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should find the correct number of elements" do
|
33
|
+
@driver.find('//a').size.should == 3
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should extract node texts" do
|
37
|
+
@driver.find('//a')[0].text.should == 'labore'
|
38
|
+
@driver.find('//a')[1].text.should == 'ullamco'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should extract node attributes" do
|
42
|
+
@driver.find('//a')[0][:href].should == '/with_simple_html'
|
43
|
+
@driver.find('//a')[0][:class].should == 'simple'
|
44
|
+
@driver.find('//a')[1][:href].should == '/foo'
|
45
|
+
@driver.find('//a')[1][:id].should == 'foo'
|
46
|
+
@driver.find('//a')[1][:rel].should be_nil
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should allow assignment of field value" do
|
50
|
+
@driver.find('//input').first.value.should == 'monkey'
|
51
|
+
@driver.find('//input').first.set('gorilla')
|
52
|
+
@driver.find('//input').first.value.should == 'gorilla'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should extract node tag name" do
|
56
|
+
@driver.find('//a')[0].tag_name.should == 'a'
|
57
|
+
@driver.find('//a')[1].tag_name.should == 'a'
|
58
|
+
@driver.find('//p')[1].tag_name.should == 'p'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
shared_examples_for "driver with javascript support" do
|
66
|
+
describe '#find' do
|
67
|
+
it "should find dynamically changed nodes" do
|
68
|
+
@driver.visit('/with_js')
|
69
|
+
@driver.find('//p').first.text.should == 'I changed it'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|