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.
Files changed (43) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +42 -0
  3. data/README.rdoc +208 -0
  4. data/Rakefile +33 -0
  5. data/examples/webcat.rb +36 -0
  6. data/lib/capybara.rb +30 -0
  7. data/lib/capybara/core_ext/tcp_socket.rb +26 -0
  8. data/lib/capybara/cucumber.rb +32 -0
  9. data/lib/capybara/driver/culerity_driver.rb +83 -0
  10. data/lib/capybara/driver/firewatir_driver.rb +66 -0
  11. data/lib/capybara/driver/rack_test_driver.rb +151 -0
  12. data/lib/capybara/driver/safariwatir_driver.rb +67 -0
  13. data/lib/capybara/driver/selenium_driver.rb +93 -0
  14. data/lib/capybara/dsl.rb +58 -0
  15. data/lib/capybara/node.rb +36 -0
  16. data/lib/capybara/rails.rb +11 -0
  17. data/lib/capybara/save_and_open_page.rb +25 -0
  18. data/lib/capybara/server.rb +53 -0
  19. data/lib/capybara/session.rb +190 -0
  20. data/script/console +10 -0
  21. data/script/destroy +14 -0
  22. data/script/generate +14 -0
  23. data/spec/driver/culerity_driver_spec.rb +10 -0
  24. data/spec/driver/firewatir_driver_spec.rb +10 -0
  25. data/spec/driver/rack_test_driver_spec.rb +9 -0
  26. data/spec/driver/safariwarit_driver_spec.rb +10 -0
  27. data/spec/driver/selenium_driver_spec.rb +10 -0
  28. data/spec/drivers_spec.rb +72 -0
  29. data/spec/dsl_spec.rb +139 -0
  30. data/spec/fixtures/test_file.txt +1 -0
  31. data/spec/public/jquery.js +19 -0
  32. data/spec/session/culerity_session_spec.rb +23 -0
  33. data/spec/session/rack_test_session_spec.rb +23 -0
  34. data/spec/session/selenium_session_spec.rb +23 -0
  35. data/spec/session_spec.rb +611 -0
  36. data/spec/spec_helper.rb +10 -0
  37. data/spec/test_app.rb +67 -0
  38. data/spec/views/form.erb +117 -0
  39. data/spec/views/with_html.erb +19 -0
  40. data/spec/views/with_js.erb +20 -0
  41. data/spec/views/with_scope.erb +36 -0
  42. data/spec/views/with_simple_html.erb +1 -0
  43. metadata +164 -0
@@ -0,0 +1,11 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+
4
+ Capybara.app = Rack::Builder.new do
5
+ map "/" do
6
+ use Rails::Rack::Static
7
+ run ActionController::Dispatcher.new
8
+ end
9
+ end.to_app
10
+
11
+ Capybara.asset_root = Rails.root.join('public')
@@ -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
@@ -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"
@@ -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)
@@ -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,9 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe Capybara::Driver::RackTest do
4
+ before do
5
+ @driver = Capybara::Driver::RackTest.new(TestApp)
6
+ end
7
+
8
+ it_should_behave_like "driver"
9
+ 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