capybara 0.4.0 → 0.4.1.rc

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/History.txt +35 -0
  2. data/README.rdoc +60 -19
  3. data/lib/capybara.rb +81 -5
  4. data/lib/capybara/driver/base.rb +1 -6
  5. data/lib/capybara/driver/celerity_driver.rb +19 -9
  6. data/lib/capybara/driver/node.rb +8 -0
  7. data/lib/capybara/driver/rack_test_driver.rb +42 -29
  8. data/lib/capybara/driver/selenium_driver.rb +11 -3
  9. data/lib/capybara/dsl.rb +11 -0
  10. data/lib/capybara/node/actions.rb +4 -14
  11. data/lib/capybara/node/base.rb +47 -0
  12. data/lib/capybara/node/document.rb +17 -0
  13. data/lib/capybara/node/element.rb +178 -0
  14. data/lib/capybara/node/finders.rb +78 -27
  15. data/lib/capybara/node/matchers.rb +40 -11
  16. data/lib/capybara/node/simple.rb +116 -0
  17. data/lib/capybara/rspec.rb +18 -0
  18. data/lib/capybara/server.rb +5 -10
  19. data/lib/capybara/session.rb +7 -16
  20. data/lib/capybara/spec/driver.rb +16 -1
  21. data/lib/capybara/spec/session.rb +1 -0
  22. data/lib/capybara/spec/session/all_spec.rb +5 -0
  23. data/lib/capybara/spec/session/attach_file_spec.rb +6 -0
  24. data/lib/capybara/spec/session/click_link_or_button_spec.rb +2 -3
  25. data/lib/capybara/spec/session/find_spec.rb +0 -6
  26. data/lib/capybara/spec/session/first_spec.rb +72 -0
  27. data/lib/capybara/spec/session/has_css_spec.rb +107 -1
  28. data/lib/capybara/spec/session/has_field_spec.rb +60 -0
  29. data/lib/capybara/spec/session/has_select_spec.rb +40 -0
  30. data/lib/capybara/spec/session/javascript.rb +4 -13
  31. data/lib/capybara/spec/session/within_spec.rb +10 -3
  32. data/lib/capybara/spec/test_app.rb +20 -1
  33. data/lib/capybara/spec/views/form.erb +27 -0
  34. data/lib/capybara/spec/views/with_html.erb +21 -5
  35. data/lib/capybara/util/save_and_open_page.rb +8 -5
  36. data/lib/capybara/version.rb +1 -1
  37. data/spec/basic_node_spec.rb +77 -0
  38. data/spec/capybara_spec.rb +18 -0
  39. data/spec/dsl_spec.rb +26 -0
  40. data/spec/rspec_spec.rb +47 -0
  41. data/spec/save_and_open_page_spec.rb +83 -7
  42. data/spec/server_spec.rb +20 -0
  43. data/spec/session/rack_test_session_spec.rb +10 -0
  44. data/spec/string_spec.rb +77 -0
  45. metadata +306 -295
  46. data/lib/capybara/node.rb +0 -221
data/History.txt CHANGED
@@ -1,3 +1,38 @@
1
+ # Version 0.4.1
2
+
3
+ Release date:
4
+
5
+ ### Added
6
+
7
+ * New click_on alias for click_link_or_button, shorter yet unambiguous. [Jonas Nicklas]
8
+ * Finders now accept :visible => false which will find all elements regardless of Capybara.ignore_hidden_elements [Jonas Nicklas]
9
+ * Configure how the server is started via Capybara.server { |app, port| ... }. [John Firebough]
10
+ * Added :between, :maximum and :minimum options to has_selector and friends [James B. Byrne]
11
+ * New Capybara.string util function which allows matchers on arbitrary strings, mostly for helper and view specs [David Chelimsky and Jonas Nicklas]
12
+ * Server boot timeout is now configurable, via Capybara.server_boot_timeout [Adam Cigánek]
13
+ * Built in support for RSpec [Jonas Nicklas]
14
+ * Capybara.using_driver to switch to a different driver temporarily [Jeff Kreeftmeijer]
15
+ * Added Session#first which is somewhat speedier than Session#all, use it internally for speed boost [John Firebaugh]
16
+
17
+ ### Changed
18
+
19
+ * Session#within now accepts the same arguments as other finders, like Session#all and Session#find [Jonas Nicklas]
20
+
21
+ ### Removed
22
+
23
+ * All deprecations from 0.4.0 have been removed. [Jonas Nicklas]
24
+
25
+ ### Fixed
26
+
27
+ * Don't mangle URLs in save_and_open_page when using self-closing tags [Adam Spiers]
28
+ * Catch correct error when server boot times out [Jonas Nicklas]
29
+ * Celerity driver now properly passes through options, making it configurable [Jonas Nicklas]
30
+ * Better implementation of attributes in C[ue]lerity, should fix issues with attributes with strange names [Jonas Nicklas]
31
+ * Session#find no longer swallows errors [Jonas Nicklas]
32
+ * Fix problems with multiple file inputs [Philip Arndt]
33
+ * Submit multipart forms as multipart under rack-test even if they contain no files [Ryan Kinderman]
34
+ * Matchers like has_select? and has_checked_field? now work with dynamically changed values [John Firebaugh]
35
+
1
36
  # Version 0.4.0
2
37
 
3
38
  Release date: 2010-10-22
data/README.rdoc CHANGED
@@ -64,6 +64,57 @@ Now you can use it in your steps:
64
64
  click_link 'Sign in'
65
65
  end
66
66
 
67
+ Capybara sets up some {tags}[http://wiki.github.com/aslakhellesoy/cucumber/tags]
68
+ for you to use in Cucumber. Often you'll want to run only some scenarios with a
69
+ driver that supports JavaScript, Capybara makes this easy: simply tag the
70
+ scenario (or feature) with <tt>@javascript</tt>:
71
+
72
+ @javascript
73
+ Scenario: do something AJAXy
74
+ When I click the AJAX link
75
+ ...
76
+
77
+ You can change which driver Capybara uses for JavaScript:
78
+
79
+ Capybara.javascript_driver = :culerity
80
+
81
+ There are also explicit <tt>@selenium</tt>, <tt>@culerity</tt> and
82
+ <tt>@rack_test</tt> tags set up for you.
83
+
84
+ == Using Capybara with RSpec
85
+
86
+ If you prefer RSpec to using Cucumber, you can use the built in RSpec support:
87
+
88
+ require 'capybara/rspec'
89
+ Capybara.app = MyRackApp
90
+
91
+ You can now use it in your examples:
92
+
93
+ describe "the signup process", :type => :acceptance do
94
+ it "signs me in" do
95
+ within("#session") do
96
+ fill_in 'Login', :with => 'user@example.com'
97
+ fill_in 'Password', :with => 'password'
98
+ end
99
+ click_link 'Sign in'
100
+ end
101
+ end
102
+
103
+ Capybara is only included for examples which have the type
104
+ <tt>:acceptance</tt>.
105
+
106
+ RSpec's metadata feature can be used to switch to a different driver. Use
107
+ <tt>:js => true</tt> to switch to the javascript driver, or provide a
108
+ <tt>:driver</tt> option to switch to one specific driver. For example:
109
+
110
+ describe 'some stuff which requires js', :js => true do
111
+ it 'will use the default js driver'
112
+ it 'will switch to one specific driver', :driver => :celerity
113
+ end
114
+
115
+ Note that Capybara's built in RSpec support only works with RSpec 2.0 or later.
116
+ You'll need to roll your own for earlier versions of RSpec.
117
+
67
118
  == Default and current driver
68
119
 
69
120
  You can set up a default driver for your features. For example if you'd prefer
@@ -82,25 +133,6 @@ You can do this in Before and After blocks to temporarily switch to a different
82
133
  driver. Note that switching driver creates a new session, so you may not be able
83
134
  to switch in the middle of a Scenario.
84
135
 
85
- == Cucumber and Tags
86
-
87
- Capybara sets up some {tags}[http://wiki.github.com/aslakhellesoy/cucumber/tags]
88
- for you to use in Cucumber. Often you'll want to run only some scenarios with a
89
- driver that supports JavaScript, Capybara makes this easy: simply tag the
90
- scenario (or feature) with <tt>@javascript</tt>:
91
-
92
- @javascript
93
- Scenario: do something AJAXy
94
- When I click the AJAX link
95
- ...
96
-
97
- You can change which driver Capybara uses for JavaScript:
98
-
99
- Capybara.javascript_driver = :culerity
100
-
101
- There are also explicit <tt>@selenium</tt>, <tt>@culerity</tt> and
102
- <tt>@rack_test</tt> tags set up for you.
103
-
104
136
  == Selenium
105
137
 
106
138
  At the moment, Capybara supports Webdriver, also called Selenium 2.0, *not*
@@ -456,6 +488,9 @@ Whatever is returned from the block should conform to the API described by
456
488
  Capybara::Driver::Base, it does not however have to inherit from this class.
457
489
  Gems can use this API to add their own drivers to Capybara.
458
490
 
491
+ The {Selenium wiki}[http://code.google.com/p/selenium/wiki/RubyBindings] has
492
+ additional info about how the underlying driver can be configured.
493
+
459
494
  == Gotchas:
460
495
 
461
496
  * Access to session and request is not possible from the test, Access to
@@ -473,6 +508,12 @@ Gems can use this API to add their own drivers to Capybara.
473
508
  use plugins which allow you to travel in time, rather than freeze time.
474
509
  One such plugin is {Timecop}[http://github.com/jtrupiano/timecop].
475
510
 
511
+ * When using Rack::Test, beware if attempting to visit absolute URLs. For
512
+ example, a session might not be shared between visits to <tt>posts_path</tt>
513
+ and <tt>posts_url</tt>. If testing an absolute URL in an Action Mailer email,
514
+ set <tt>default_url_options</tt> to match the Rails default of
515
+ <tt>www.example.com</tt>.
516
+
476
517
  == License:
477
518
 
478
519
  (The MIT License)
data/lib/capybara.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'timeout'
2
2
  require 'nokogiri'
3
3
  require 'xpath'
4
-
5
4
  module Capybara
6
5
  class CapybaraError < StandardError; end
7
6
  class DriverNotFoundError < CapybaraError; end
@@ -14,7 +13,7 @@ module Capybara
14
13
 
15
14
  class << self
16
15
  attr_accessor :asset_root, :app_host, :run_server, :default_host
17
- attr_accessor :server_port
16
+ attr_accessor :server_port, :server_boot_timeout
18
17
  attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements
19
18
  attr_accessor :save_and_open_page_path
20
19
 
@@ -108,6 +107,74 @@ module Capybara
108
107
  @drivers ||= {}
109
108
  end
110
109
 
110
+ ##
111
+ #
112
+ # Register a proc that Capybara will call to run the Rack application.
113
+ #
114
+ # Capybara.server do |app, port|
115
+ # require 'rack/handler/mongrel'
116
+ # Rack::Handler::Mongrel.run(app, :Port => port)
117
+ # end
118
+ #
119
+ # By default, Capybara will try to run thin, falling back to webrick.
120
+ #
121
+ # @yield [app, port] This block recieves a rack app and port and should run a Rack handler
122
+ #
123
+ def server(&block)
124
+ if block_given?
125
+ @server = block
126
+ else
127
+ @server
128
+ end
129
+ end
130
+
131
+ ##
132
+ #
133
+ # Wraps the given string, which should contain an HTML document or fragment
134
+ # in a {Capybara::Node::Simple} which exposes all {Capybara::Node::Matchers} and
135
+ # {Capybara::Node::Finders}. This allows you to query any string containing
136
+ # HTML in the exact same way you would query the current document in a Capybara
137
+ # session. For example:
138
+ #
139
+ # node = Capybara.string <<-HTML
140
+ # <ul>
141
+ # <li id="home">Home</li>
142
+ # <li id="projects">Projects</li>
143
+ # </ul>
144
+ # HTML
145
+ #
146
+ # node.find('#projects').text # => 'Projects'
147
+ # node.has_selector?('li#home', :text => 'Home')
148
+ # node.has_selector?(:projects)
149
+ # node.find('ul').find('li').text # => 'Home'
150
+ #
151
+ # @param [String] html An html fragment or document
152
+ # @return [Capybara::Node::Simple] A node which has Capybara's finders and matchers
153
+ #
154
+ def string(html)
155
+ Capybara::Node::Simple.new(html)
156
+ end
157
+
158
+ ##
159
+ #
160
+ # Runs Capybara's default server for the given application and port
161
+ # under most circumstances you should not have to call this method
162
+ # manually.
163
+ #
164
+ # @param [Rack Application] app The rack application to run
165
+ # @param [Fixnum] port The port to run the application on
166
+ #
167
+ def run_default_server(app, port)
168
+ begin
169
+ require 'rack/handler/thin'
170
+ Thin::Logging.silent = true
171
+ Rack::Handler::Thin.run(app, :Port => port)
172
+ rescue LoadError
173
+ require 'rack/handler/webrick'
174
+ Rack::Handler::WEBrick.run(app, :Port => port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
175
+ end
176
+ end
177
+
111
178
  def deprecate(method, alternate_method)
112
179
  warn "DEPRECATED: ##{method} is deprecated, please use ##{alternate_method} instead"
113
180
  end
@@ -115,12 +182,19 @@ module Capybara
115
182
 
116
183
  autoload :Server, 'capybara/server'
117
184
  autoload :Session, 'capybara/session'
118
- autoload :Node, 'capybara/node'
119
- autoload :Document, 'capybara/node'
120
- autoload :Element, 'capybara/node'
121
185
  autoload :Selector, 'capybara/selector'
122
186
  autoload :VERSION, 'capybara/version'
123
187
 
188
+ module Node
189
+ autoload :Base, 'capybara/node/base'
190
+ autoload :Simple, 'capybara/node/simple'
191
+ autoload :Element, 'capybara/node/element'
192
+ autoload :Document, 'capybara/node/document'
193
+ autoload :Finders, 'capybara/node/finders'
194
+ autoload :Matchers, 'capybara/node/matchers'
195
+ autoload :Actions, 'capybara/node/actions'
196
+ end
197
+
124
198
  module Driver
125
199
  autoload :Base, 'capybara/driver/base'
126
200
  autoload :Node, 'capybara/driver/node'
@@ -133,6 +207,8 @@ end
133
207
 
134
208
  Capybara.configure do |config|
135
209
  config.run_server = true
210
+ config.server {|app, port| Capybara.run_default_server(app, port)}
211
+ config.server_boot_timeout = 10
136
212
  config.default_selector = :css
137
213
  config.default_wait_time = 2
138
214
  config.ignore_hidden_elements = false
@@ -30,7 +30,7 @@ class Capybara::Driver::Base
30
30
  def response_headers
31
31
  raise Capybara::NotSupportedByDriverError
32
32
  end
33
-
33
+
34
34
  def status_code
35
35
  raise Capybara::NotSupportedByDriverError
36
36
  end
@@ -53,11 +53,6 @@ class Capybara::Driver::Base
53
53
  def reset!
54
54
  end
55
55
 
56
- def cleanup!
57
- Capybara.deprecate("cleanup!", "reset!")
58
- reset!
59
- end
60
-
61
56
  def has_shortcircuit_timeout?
62
57
  false
63
58
  end
@@ -5,19 +5,15 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base
5
5
  end
6
6
 
7
7
  def [](name)
8
- value = if name.to_sym == :class
9
- native.class_name
10
- else
11
- native.send(name.to_sym)
12
- end
8
+ value = native.attribute_value(name.to_sym)
13
9
  return value if value and not value.to_s.empty?
14
10
  end
15
11
 
16
12
  def value
17
13
  if tag_name == "select" and native.multiple?
18
- find(".//option[@selected]").map { |n| n.value || n.text }
14
+ find(".//option[@selected]").map { |n| if n.has_value? then n.value else n.text end }
19
15
  else
20
- self[:value]
16
+ native.value
21
17
  end
22
18
  end
23
19
 
@@ -60,6 +56,18 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base
60
56
  native.visible?
61
57
  end
62
58
 
59
+ def checked?
60
+ native.checked?
61
+ rescue # https://github.com/langalex/culerity/issues/issue/33
62
+ false
63
+ end
64
+
65
+ def selected?
66
+ native.selected?
67
+ rescue # https://github.com/langalex/culerity/issues/issue/33
68
+ false
69
+ end
70
+
63
71
  def path
64
72
  native.xpath
65
73
  end
@@ -81,7 +89,9 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base
81
89
  find('./ancestor::select').first
82
90
  end
83
91
 
84
-
92
+ def has_value?
93
+ native.object.hasAttribute('value')
94
+ end
85
95
  end
86
96
 
87
97
  attr_reader :app, :rack_server, :options
@@ -135,7 +145,7 @@ class Capybara::Driver::Celerity < Capybara::Driver::Base
135
145
  def browser
136
146
  unless @_browser
137
147
  require 'celerity'
138
- @_browser = ::Celerity::Browser.new(:browser => :firefox, :log_level => :off)
148
+ @_browser = ::Celerity::Browser.new(options)
139
149
  end
140
150
 
141
151
  @_browser
@@ -48,6 +48,14 @@ module Capybara
48
48
  raise NotImplementedError
49
49
  end
50
50
 
51
+ def checked?
52
+ raise NotImplementedError
53
+ end
54
+
55
+ def selected?
56
+ raise NotImplementedError
57
+ end
58
+
51
59
  def path
52
60
  raise NotSupportedByDriverError
53
61
  end
@@ -11,28 +11,11 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
11
11
  end
12
12
 
13
13
  def [](name)
14
- attr_name = name.to_s
15
- case
16
- when 'select' == tag_name && 'value' == attr_name
17
- if native['multiple'] == 'multiple'
18
- native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content }
19
- else
20
- option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
21
- option[:value] || option.content if option
22
- end
23
- when 'input' == tag_name && 'checkbox' == type && 'checked' == attr_name
24
- native[attr_name] == 'checked' ? true : false
25
- else
26
- native[attr_name]
27
- end
14
+ string_node[name]
28
15
  end
29
16
 
30
17
  def value
31
- if tag_name == 'textarea'
32
- native.content
33
- else
34
- self[:value]
35
- end
18
+ string_node.value
36
19
  end
37
20
 
38
21
  def set(value)
@@ -82,7 +65,15 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
82
65
  end
83
66
 
84
67
  def visible?
85
- native.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none')]").size == 0
68
+ string_node.visible?
69
+ end
70
+
71
+ def checked?
72
+ self[:checked]
73
+ end
74
+
75
+ def selected?
76
+ self[:selected]
86
77
  end
87
78
 
88
79
  def path
@@ -95,6 +86,10 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
95
86
 
96
87
  private
97
88
 
89
+ def string_node
90
+ @string_node ||= Capybara::Node::Simple.new(native)
91
+ end
92
+
98
93
  # a reference to the select node if this is an option node
99
94
  def select_node
100
95
  find('./ancestor::select').first
@@ -110,10 +105,25 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
110
105
  end
111
106
 
112
107
  class Form < Node
108
+ # This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
109
+ # the class specifically when determing whether to consturct the request as multipart.
110
+ # That check should be based solely on the form element's 'enctype' attribute value,
111
+ # which should probably be provided to Rack::Test in its non-GET request methods.
112
+ class NilUploadedFile < Rack::Test::UploadedFile
113
+ def initialize
114
+ @empty_file = Tempfile.new("nil_uploaded_file")
115
+ @empty_file.close
116
+ end
117
+
118
+ def original_filename; ""; end
119
+ def content_type; "application/octet-stream"; end
120
+ def path; @empty_file.path; end
121
+ end
122
+
113
123
  def params(button)
114
124
  params = {}
115
125
 
116
- native.xpath(".//input[not(@disabled) and (not(@type) or (@type!='radio' and @type!='checkbox' and @type!='submit' and @type!='image'))]").map do |input|
126
+ native.xpath(".//input[not(@disabled) and (not(@type) or (@type!='radio' and @type!='file' and @type!='checkbox' and @type!='submit' and @type!='image'))]").map do |input|
117
127
  merge_param!(params, input['name'].to_s, input['value'].to_s)
118
128
  end
119
129
  native.xpath(".//textarea[not(@disabled)]").map do |textarea|
@@ -135,14 +145,17 @@ class Capybara::Driver::RackTest < Capybara::Driver::Base
135
145
  end
136
146
  end
137
147
  native.xpath(".//input[not(@disabled) and @type='file']").map do |input|
138
- unless input['value'].to_s.empty?
139
- if multipart?
140
- content_type = MIME::Types.type_for(input['value'].to_s).first.to_s
141
- file = Rack::Test::UploadedFile.new(input['value'].to_s, content_type)
142
- merge_param!(params, input['name'].to_s, file)
143
- else
144
- merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
145
- end
148
+ if multipart?
149
+ file = \
150
+ if (value = input['value']).to_s.empty?
151
+ NilUploadedFile.new
152
+ else
153
+ content_type = MIME::Types.type_for(value).first.to_s
154
+ Rack::Test::UploadedFile.new(value, content_type)
155
+ end
156
+ merge_param!(params, input['name'].to_s, file)
157
+ else
158
+ merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
146
159
  end
147
160
  end
148
161
  merge_param!(params, button[:name], button[:value] || "") if button[:name]