capybara 0.1

Sign up to get free protection for your applications and to get access to all the features.
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