capybara 2.0.3 → 2.1.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.
Files changed (82) hide show
  1. data/History.md +82 -0
  2. data/README.md +57 -52
  3. data/lib/capybara/cucumber.rb +4 -1
  4. data/lib/capybara/driver/base.rb +13 -9
  5. data/lib/capybara/driver/node.rb +18 -6
  6. data/lib/capybara/helpers.rb +111 -22
  7. data/lib/capybara/node/actions.rb +30 -25
  8. data/lib/capybara/node/base.rb +25 -32
  9. data/lib/capybara/node/document.rb +6 -2
  10. data/lib/capybara/node/element.rb +44 -7
  11. data/lib/capybara/node/finders.rb +49 -22
  12. data/lib/capybara/node/matchers.rb +38 -21
  13. data/lib/capybara/node/simple.rb +31 -9
  14. data/lib/capybara/query.rb +72 -26
  15. data/lib/capybara/rack_test/browser.rb +11 -3
  16. data/lib/capybara/rack_test/css_handlers.rb +8 -0
  17. data/lib/capybara/rack_test/driver.rb +11 -3
  18. data/lib/capybara/rack_test/form.rb +8 -1
  19. data/lib/capybara/rack_test/node.rb +91 -35
  20. data/lib/capybara/rails.rb +0 -1
  21. data/lib/capybara/result.rb +21 -37
  22. data/lib/capybara/rspec/matchers.rb +60 -31
  23. data/lib/capybara/selector.rb +46 -13
  24. data/lib/capybara/selenium/driver.rb +42 -7
  25. data/lib/capybara/selenium/node.rb +23 -5
  26. data/lib/capybara/server.rb +1 -1
  27. data/lib/capybara/session.rb +47 -12
  28. data/lib/capybara/spec/public/test.js +12 -0
  29. data/lib/capybara/spec/session/all_spec.rb +9 -8
  30. data/lib/capybara/spec/session/attach_file_spec.rb +14 -0
  31. data/lib/capybara/spec/session/check_spec.rb +14 -0
  32. data/lib/capybara/spec/session/choose_spec.rb +14 -0
  33. data/lib/capybara/spec/session/click_button_spec.rb +77 -1
  34. data/lib/capybara/spec/session/click_link_or_button_spec.rb +65 -0
  35. data/lib/capybara/spec/session/click_link_spec.rb +24 -0
  36. data/lib/capybara/spec/session/current_scope_spec.rb +29 -0
  37. data/lib/capybara/spec/session/fill_in_spec.rb +26 -0
  38. data/lib/capybara/spec/session/find_button_spec.rb +12 -0
  39. data/lib/capybara/spec/session/find_by_id_spec.rb +12 -1
  40. data/lib/capybara/spec/session/find_field_spec.rb +30 -0
  41. data/lib/capybara/spec/session/find_link_spec.rb +12 -0
  42. data/lib/capybara/spec/session/find_spec.rb +264 -16
  43. data/lib/capybara/spec/session/first_spec.rb +25 -8
  44. data/lib/capybara/spec/session/has_css_spec.rb +2 -2
  45. data/lib/capybara/spec/session/has_field_spec.rb +10 -2
  46. data/lib/capybara/spec/session/has_selector_spec.rb +9 -0
  47. data/lib/capybara/spec/session/has_text_spec.rb +96 -0
  48. data/lib/capybara/spec/session/has_title_spec.rb +47 -0
  49. data/lib/capybara/spec/session/node_spec.rb +48 -0
  50. data/lib/capybara/spec/session/reset_session_spec.rb +10 -2
  51. data/lib/capybara/spec/session/save_page_spec.rb +30 -0
  52. data/lib/capybara/spec/session/select_spec.rb +65 -0
  53. data/lib/capybara/spec/session/text_spec.rb +31 -0
  54. data/lib/capybara/spec/session/title_spec.rb +16 -0
  55. data/lib/capybara/spec/session/uncheck_spec.rb +14 -0
  56. data/lib/capybara/spec/session/unselect_spec.rb +42 -0
  57. data/lib/capybara/spec/session/visit_spec.rb +3 -3
  58. data/lib/capybara/spec/session/within_frame_spec.rb +14 -0
  59. data/lib/capybara/spec/session/within_spec.rb +3 -3
  60. data/lib/capybara/spec/spec_helper.rb +7 -2
  61. data/lib/capybara/spec/views/form.erb +39 -2
  62. data/lib/capybara/spec/views/frame_child.erb +9 -0
  63. data/lib/capybara/spec/views/frame_parent.erb +8 -0
  64. data/lib/capybara/spec/views/with_base_tag.erb +10 -0
  65. data/lib/capybara/spec/views/with_count.erb +7 -0
  66. data/lib/capybara/spec/views/with_hover.erb +17 -0
  67. data/lib/capybara/spec/views/with_html.erb +23 -2
  68. data/lib/capybara/spec/views/with_js.erb +5 -0
  69. data/lib/capybara/spec/views/with_scope.erb +6 -1
  70. data/lib/capybara/spec/views/with_title.erb +1 -0
  71. data/lib/capybara/spec/views/within_frames.erb +1 -0
  72. data/lib/capybara/version.rb +1 -1
  73. data/lib/capybara.rb +43 -62
  74. data/spec/basic_node_spec.rb +75 -24
  75. data/spec/dsl_spec.rb +2 -1
  76. data/spec/rack_test_spec.rb +4 -3
  77. data/spec/rspec/matchers_spec.rb +105 -17
  78. data/spec/server_spec.rb +15 -8
  79. data/spec/spec_helper.rb +2 -1
  80. data.tar.gz.sig +0 -0
  81. metadata +82 -22
  82. metadata.gz.sig +0 -0
@@ -1,7 +1,26 @@
1
1
  module Capybara
2
2
  class Selector
3
- attr_reader :name, :custom_filters
3
+ class Filter
4
+ def initialize(name, block, options={})
5
+ @name = name
6
+ @block = block
7
+ @options = options
8
+ end
9
+
10
+ def default?
11
+ @options.has_key?(:default)
12
+ end
13
+
14
+ def default
15
+ @options[:default]
16
+ end
17
+
18
+ def matches?(node, value)
19
+ @block.call(node, value)
20
+ end
21
+ end
4
22
 
23
+ attr_reader :name, :custom_filters, :format
5
24
 
6
25
  class << self
7
26
  def all
@@ -27,16 +46,16 @@ module Capybara
27
46
  end
28
47
 
29
48
  def xpath(&block)
49
+ @format = :xpath
30
50
  @xpath = block if block
31
51
  @xpath
32
52
  end
33
53
 
34
54
  # Same as xpath, but wrap in XPath.css().
35
55
  def css(&block)
36
- if block
37
- @xpath = xpath { |*args| XPath.css(block.call(*args)) }
38
- end
39
- @xpath
56
+ @format = :css
57
+ @css = block if block
58
+ @css
40
59
  end
41
60
 
42
61
  def match(&block)
@@ -50,15 +69,19 @@ module Capybara
50
69
  end
51
70
 
52
71
  def call(locator)
53
- @xpath.call(locator)
72
+ if @format==:css
73
+ @css.call(locator)
74
+ else
75
+ @xpath.call(locator)
76
+ end
54
77
  end
55
78
 
56
79
  def match?(locator)
57
80
  @match and @match.call(locator)
58
81
  end
59
82
 
60
- def filter(name, &block)
61
- @custom_filters[name] = block
83
+ def filter(name, options={}, &block)
84
+ @custom_filters[name] = Filter.new(name, block, options)
62
85
  end
63
86
  end
64
87
  end
@@ -79,8 +102,15 @@ Capybara.add_selector(:field) do
79
102
  xpath { |locator| XPath::HTML.field(locator) }
80
103
  filter(:checked) { |node, value| not(value ^ node.checked?) }
81
104
  filter(:unchecked) { |node, value| (value ^ node.checked?) }
105
+ filter(:disabled, :default => false) { |node, value| not(value ^ node.disabled?) }
82
106
  filter(:with) { |node, with| node.value == with }
83
- filter(:type) { |node, type| node[:type] == type }
107
+ filter(:type) do |node, type|
108
+ if ['textarea', 'select'].include?(type)
109
+ node.tag_name == type
110
+ else
111
+ node[:type] == type
112
+ end
113
+ end
84
114
  end
85
115
 
86
116
  Capybara.add_selector(:fieldset) do
@@ -90,6 +120,7 @@ end
90
120
  Capybara.add_selector(:link_or_button) do
91
121
  label "link or button"
92
122
  xpath { |locator| XPath::HTML.link_or_button(locator) }
123
+ filter(:disabled, :default => false) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
93
124
  end
94
125
 
95
126
  Capybara.add_selector(:link) do
@@ -101,11 +132,13 @@ end
101
132
 
102
133
  Capybara.add_selector(:button) do
103
134
  xpath { |locator| XPath::HTML.button(locator) }
135
+ filter(:disabled, :default => false) { |node, value| not(value ^ node.disabled?) }
104
136
  end
105
137
 
106
138
  Capybara.add_selector(:fillable_field) do
107
139
  label "field"
108
140
  xpath { |locator| XPath::HTML.fillable_field(locator) }
141
+ filter(:disabled, :default => false) { |node, value| not(value ^ node.disabled?) }
109
142
  end
110
143
 
111
144
  Capybara.add_selector(:radio_button) do
@@ -113,12 +146,14 @@ Capybara.add_selector(:radio_button) do
113
146
  xpath { |locator| XPath::HTML.radio_button(locator) }
114
147
  filter(:checked) { |node, value| not(value ^ node.checked?) }
115
148
  filter(:unchecked) { |node, value| (value ^ node.checked?) }
149
+ filter(:disabled, :default => false) { |node, value| not(value ^ node.disabled?) }
116
150
  end
117
151
 
118
152
  Capybara.add_selector(:checkbox) do
119
153
  xpath { |locator| XPath::HTML.checkbox(locator) }
120
154
  filter(:checked) { |node, value| not(value ^ node.checked?) }
121
155
  filter(:unchecked) { |node, value| (value ^ node.checked?) }
156
+ filter(:disabled, :default => false) { |node, value| not(value ^ node.disabled?) }
122
157
  end
123
158
 
124
159
  Capybara.add_selector(:select) do
@@ -133,6 +168,7 @@ Capybara.add_selector(:select) do
133
168
  actual = node.all(:xpath, './/option').select { |option| option.selected? }.map { |option| option.text }
134
169
  [selected].flatten.sort == actual.sort
135
170
  end
171
+ filter(:disabled, :default => false) { |node, value| not(value ^ node.disabled?) }
136
172
  end
137
173
 
138
174
  Capybara.add_selector(:option) do
@@ -142,10 +178,7 @@ end
142
178
  Capybara.add_selector(:file_field) do
143
179
  label "file field"
144
180
  xpath { |locator| XPath::HTML.file_field(locator) }
145
- end
146
-
147
- Capybara.add_selector(:content) do
148
- xpath { |content| XPath::HTML.content(content) }
181
+ filter(:disabled, :default => false) { |node, value| not(value ^ node.disabled?) }
149
182
  end
150
183
 
151
184
  Capybara.add_selector(:table) do
@@ -1,5 +1,3 @@
1
- require 'selenium-webdriver'
2
-
3
1
  class Capybara::Selenium::Driver < Capybara::Driver::Base
4
2
  DEFAULT_OPTIONS = {
5
3
  :browser => :firefox
@@ -24,9 +22,20 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
24
22
  end
25
23
 
26
24
  def initialize(app, options={})
25
+ begin
26
+ require 'selenium-webdriver'
27
+ rescue LoadError => e
28
+ if e.message =~ /selenium-webdriver/
29
+ raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
30
+ else
31
+ raise e
32
+ end
33
+ end
34
+
27
35
  @app = app
28
36
  @browser = nil
29
37
  @exit_status = nil
38
+ @frame_handles = {}
30
39
  @options = DEFAULT_OPTIONS.merge(options)
31
40
  end
32
41
 
@@ -38,14 +47,22 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
38
47
  browser.page_source
39
48
  end
40
49
 
50
+ def title
51
+ browser.title
52
+ end
53
+
41
54
  def current_url
42
55
  browser.current_url
43
56
  end
44
57
 
45
- def find(selector)
58
+ def find_xpath(selector)
46
59
  browser.find_elements(:xpath, selector).map { |node| Capybara::Selenium::Node.new(self, node) }
47
60
  end
48
61
 
62
+ def find_css(selector)
63
+ browser.find_elements(:css, selector).map { |node| Capybara::Selenium::Node.new(self, node) }
64
+ end
65
+
49
66
  def wait?; true; end
50
67
  def needs_server?; true; end
51
68
 
@@ -74,12 +91,29 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
74
91
  end
75
92
  end
76
93
 
77
- def within_frame(frame_id)
78
- old_window = browser.window_handle
79
- browser.switch_to.frame(frame_id)
94
+ ##
95
+ #
96
+ # Webdriver supports frame name, id, index(zero-based) or {Capybara::Element} to find iframe
97
+ #
98
+ # @overload within_frame(index)
99
+ # @param [Integer] index index of a frame
100
+ # @overload within_frame(name_or_id)
101
+ # @param [String] name_or_id name or id of a frame
102
+ # @overload within_frame(element)
103
+ # @param [Capybara::Node::Base] a_node frame element
104
+ #
105
+ def within_frame(frame_handle)
106
+ @frame_handles[browser.window_handle] ||= []
107
+ frame_handle = frame_handle.native if frame_handle.is_a?(Capybara::Node::Base)
108
+ @frame_handles[browser.window_handle] << frame_handle
109
+ a=browser.switch_to.frame(frame_handle)
80
110
  yield
81
111
  ensure
82
- browser.switch_to.window old_window
112
+ # There doesnt appear to be any way in Webdriver to move back to a parent frame
113
+ # other than going back to the root and then reiterating down
114
+ @frame_handles[browser.window_handle].pop
115
+ browser.switch_to.default_content
116
+ @frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
83
117
  end
84
118
 
85
119
  def find_window( selector )
@@ -111,4 +145,5 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
111
145
  def invalid_element_errors
112
146
  [Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::ElementNotVisibleError]
113
147
  end
148
+
114
149
  end
@@ -1,9 +1,14 @@
1
1
  class Capybara::Selenium::Node < Capybara::Driver::Node
2
- def text
2
+ def visible_text
3
3
  # Selenium doesn't normalize Unicode whitespace.
4
4
  Capybara::Helpers.normalize_whitespace(native.text)
5
5
  end
6
6
 
7
+ def all_text
8
+ text = driver.browser.execute_script("return arguments[0].textContent", native)
9
+ Capybara::Helpers.normalize_whitespace(text)
10
+ end
11
+
7
12
  def [](name)
8
13
  native.attribute(name.to_s)
9
14
  rescue Selenium::WebDriver::Error::WebDriverError
@@ -32,7 +37,8 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
32
37
  path_names = value.to_s.empty? ? [] : value
33
38
  native.send_keys(*path_names)
34
39
  elsif tag_name == 'textarea' or tag_name == 'input'
35
- driver.browser.execute_script "arguments[0].value = ''", native
40
+ #script can change a readonly element which user input cannot, so dont execute if readonly
41
+ driver.browser.execute_script "arguments[0].value = ''", native unless self[:readonly]
36
42
  native.send_keys(value.to_s)
37
43
  end
38
44
  end
@@ -52,6 +58,10 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
52
58
  native.click
53
59
  end
54
60
 
61
+ def hover
62
+ driver.browser.action.move_to(native).perform
63
+ end
64
+
55
65
  def drag_to(element)
56
66
  driver.browser.action.drag_and_drop(native, element.native).perform
57
67
  end
@@ -70,12 +80,20 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
70
80
  selected and selected != "false"
71
81
  end
72
82
 
83
+ def disabled?
84
+ !native.enabled?
85
+ end
86
+
73
87
  alias :checked? :selected?
74
88
 
75
- def find(locator)
89
+ def find_xpath(locator)
76
90
  native.find_elements(:xpath, locator).map { |n| self.class.new(driver, n) }
77
91
  end
78
-
92
+
93
+ def find_css(locator)
94
+ native.find_elements(:css, locator).map { |n| self.class.new(driver, n) }
95
+ end
96
+
79
97
  def ==(other)
80
98
  native == other.native
81
99
  end
@@ -84,6 +102,6 @@ private
84
102
 
85
103
  # a reference to the select node if this is an option node
86
104
  def select_node
87
- find('./ancestor::select').first
105
+ find_xpath('./ancestor::select').first
88
106
  end
89
107
  end
@@ -62,7 +62,7 @@ module Capybara
62
62
  if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
63
63
  return res.body == @app.object_id.to_s
64
64
  end
65
- rescue Errno::ECONNREFUSED, Errno::EBADF
65
+ rescue SystemCallError
66
66
  return false
67
67
  end
68
68
 
@@ -40,11 +40,13 @@ module Capybara
40
40
  :body, :html, :current_url, :current_host, :evaluate_script, :source,
41
41
  :visit, :within, :within_fieldset, :within_table, :within_frame,
42
42
  :within_window, :current_path, :save_page, :save_and_open_page,
43
- :save_screenshot, :reset_session!, :response_headers, :status_code
43
+ :save_screenshot, :reset_session!, :response_headers, :status_code,
44
+ :title, :has_title?, :has_no_title?, :current_scope
44
45
  ]
45
46
  DSL_METHODS = NODE_METHODS + SESSION_METHODS
46
47
 
47
48
  attr_reader :mode, :app, :server
49
+ attr_accessor :synchronized
48
50
 
49
51
  def initialize(mode, app=nil)
50
52
  @mode = mode
@@ -74,7 +76,7 @@ module Capybara
74
76
  def reset!
75
77
  driver.reset! if @touched
76
78
  @touched = false
77
- raise @server.error if @server and @server.error
79
+ raise @server.error if Capybara.raise_server_errors and @server and @server.error
78
80
  ensure
79
81
  @server.reset_error! if @server
80
82
  end
@@ -137,6 +139,14 @@ module Capybara
137
139
  driver.current_url
138
140
  end
139
141
 
142
+ ##
143
+ #
144
+ # @return [String] Title of the current page
145
+ #
146
+ def title
147
+ driver.title
148
+ end
149
+
140
150
  ##
141
151
  #
142
152
  # Navigate to the given URL. The URL can either be a relative URL or an absolute URL
@@ -243,13 +253,16 @@ module Capybara
243
253
 
244
254
  ##
245
255
  #
246
- # Execute the given block within the given iframe given the id of that iframe. Only works on
247
- # some drivers (e.g. Selenium)
256
+ # Execute the given block within the given iframe using given frame name or index.
257
+ # May be supported by not all drivers. Drivers that support it, may provide additional options.
248
258
  #
249
- # @param [String] frame_id Id of the frame
259
+ # @overload within_frame(index)
260
+ # @param [Integer] index index of a frame
261
+ # @overload within_frame(name)
262
+ # @param [String] name name of a frame
250
263
  #
251
- def within_frame(frame_id)
252
- driver.within_frame(frame_id) do
264
+ def within_frame(frame_handle)
265
+ driver.within_frame(frame_handle) do
253
266
  yield
254
267
  end
255
268
  end
@@ -304,7 +317,7 @@ module Capybara
304
317
 
305
318
  FileUtils.mkdir_p(File.dirname(path))
306
319
 
307
- File.open(path,'w') { |f| f.write(body) }
320
+ File.open(path,'w') { |f| f.write(Capybara::Helpers.inject_asset_host(body)) }
308
321
  path
309
322
  end
310
323
 
@@ -312,7 +325,7 @@ module Capybara
312
325
  #
313
326
  # Save a snapshot of the page and open it in a browser for inspection
314
327
  #
315
- # @param [String] path The path to where it should be saved [optional]
328
+ # @param [String] file_name The path to where it should be saved [optional]
316
329
  #
317
330
  def save_and_open_page(file_name=nil)
318
331
  require "launchy"
@@ -338,7 +351,7 @@ module Capybara
338
351
  NODE_METHODS.each do |method|
339
352
  define_method method do |*args, &block|
340
353
  @touched = true
341
- current_node.send(method, *args, &block)
354
+ current_scope.send(method, *args, &block)
342
355
  end
343
356
  end
344
357
 
@@ -346,12 +359,34 @@ module Capybara
346
359
  %(#<Capybara::Session>)
347
360
  end
348
361
 
349
- private
362
+ def has_title?(content)
363
+ document.synchronize do
364
+ unless title.match(Capybara::Helpers.to_regexp(content))
365
+ raise ExpectationNotMet
366
+ end
367
+ end
368
+ return true
369
+ rescue Capybara::ExpectationNotMet
370
+ return false
371
+ end
372
+
373
+ def has_no_title?(content)
374
+ document.synchronize do
375
+ if title.match(Capybara::Helpers.to_regexp(content))
376
+ raise ExpectationNotMet
377
+ end
378
+ end
379
+ return true
380
+ rescue Capybara::ExpectationNotMet
381
+ return false
382
+ end
350
383
 
351
- def current_node
384
+ def current_scope
352
385
  scopes.last
353
386
  end
354
387
 
388
+ private
389
+
355
390
  def scopes
356
391
  @scopes ||= [document]
357
392
  end
@@ -18,6 +18,13 @@ $(function() {
18
18
  }, 500);
19
19
  return false;
20
20
  });
21
+ $('#slow-click').click(function() {
22
+ var link = $(this);
23
+ setTimeout(function() {
24
+ $(link).after('<a id="slow-clicked" href="#">Slow link clicked</a>');
25
+ }, 4000);
26
+ return false;
27
+ });
21
28
  $('#waiter').change(function() {
22
29
  activeRequests = 1;
23
30
  setTimeout(function() {
@@ -48,4 +55,9 @@ $(function() {
48
55
  $('#the-list').html('<li>Foo</li><li>Bar</li>');
49
56
  }, 250)
50
57
  });
58
+ $('#change-title').click(function() {
59
+ setTimeout(function() {
60
+ $('title').text('changed title')
61
+ }, 250)
62
+ });
51
63
  });
@@ -48,22 +48,23 @@ Capybara::SpecHelper.spec "#all" do
48
48
  @session.all('h1').first.text.should == 'This is a test'
49
49
  @session.all("input[id='test_field']").first[:value].should == 'monkey'
50
50
  end
51
- after { Capybara.default_selector = :xpath }
52
51
  end
53
52
 
54
53
  context "with visible filter" do
55
- after { Capybara.ignore_hidden_elements = false }
56
- it "should only find visible nodes" do
57
- @session.all(:css, "a.simple").should have(2).elements
54
+ it "should only find visible nodes when true" do
58
55
  @session.all(:css, "a.simple", :visible => true).should have(1).elements
59
- Capybara.ignore_hidden_elements = true
60
- @session.all(:css, "a.simple").should have(1).elements
61
56
  end
62
57
 
63
- it "should only find invisible nodes" do
64
- Capybara.ignore_hidden_elements = true
58
+ it "should find nodes regardless of whether they are invisible when false" do
65
59
  @session.all(:css, "a.simple", :visible => false).should have(2).elements
66
60
  end
61
+
62
+ it "should default to Capybara.ignore_hidden_elements" do
63
+ Capybara.ignore_hidden_elements = true
64
+ @session.all(:css, "a.simple").should have(1).elements
65
+ Capybara.ignore_hidden_elements = false
66
+ @session.all(:css, "a.simple").should have(2).elements
67
+ end
67
68
  end
68
69
 
69
70
  context "within a scope" do
@@ -87,4 +87,18 @@ Capybara::SpecHelper.spec "#attach_file" do
87
87
  expect { @session.attach_file('Image', '/no_such_file.png') }.to raise_error(Capybara::FileNotFound)
88
88
  end
89
89
  end
90
+
91
+ context "with :exact option" do
92
+ it "should set a file path by partial label when false" do
93
+ @session.attach_file "Imag", __FILE__, :exact => false
94
+ @session.click_button('awesome')
95
+ extract_results(@session)['image'].should == File.basename(__FILE__)
96
+ end
97
+
98
+ it "not allow partial matches when true" do
99
+ expect do
100
+ @session.attach_file "Imag", __FILE__, :exact => true
101
+ end.to raise_error(Capybara::ElementNotFound)
102
+ end
103
+ end
90
104
  end
@@ -82,4 +82,18 @@ Capybara::SpecHelper.spec "#check" do
82
82
  end.to raise_error(Capybara::ElementNotFound)
83
83
  end
84
84
  end
85
+
86
+ context "with :exact option" do
87
+ it "should accept partial matches when false" do
88
+ @session.check('Ham', :exact => false)
89
+ @session.click_button('awesome')
90
+ extract_results(@session)['pets'].should include('hamster')
91
+ end
92
+
93
+ it "should not accept partial matches when true" do
94
+ expect do
95
+ @session.check('Ham', :exact => true)
96
+ end.to raise_error(Capybara::ElementNotFound)
97
+ end
98
+ end
85
99
  end
@@ -37,4 +37,18 @@ Capybara::SpecHelper.spec "#choose" do
37
37
  end.to raise_error(Capybara::ElementNotFound)
38
38
  end
39
39
  end
40
+
41
+ context "with :exact option" do
42
+ it "should accept partial matches when false" do
43
+ @session.choose("Mal", :exact => false)
44
+ @session.click_button('awesome')
45
+ extract_results(@session)['gender'].should == 'male'
46
+ end
47
+
48
+ it "should not accept partial matches when true" do
49
+ expect do
50
+ @session.choose("Mal", :exact => true)
51
+ end.to raise_error(Capybara::ElementNotFound)
52
+ end
53
+ end
40
54
  end
@@ -63,7 +63,7 @@ Capybara::SpecHelper.spec '#click_button' do
63
63
  end
64
64
 
65
65
  it "should serialise and submit color fields" do
66
- @results['html5_color'].should == '#FFFFFF'
66
+ @results['html5_color'].upcase.should == '#FFFFFF'
67
67
  end
68
68
  end
69
69
 
@@ -162,6 +162,69 @@ Capybara::SpecHelper.spec '#click_button' do
162
162
  end
163
163
  end
164
164
 
165
+ context "with fields associated with the form using the form attribute" do
166
+ before do
167
+ @session.click_button('submit_form1')
168
+ @results = extract_results(@session)
169
+ end
170
+
171
+ it "should serialize and submit text fields" do
172
+ @results['outside_input'].should == 'outside_input'
173
+ end
174
+
175
+ it "should serialize text areas" do
176
+ @results['outside_textarea'].should == 'Some text here'
177
+ end
178
+
179
+ it "should serialize select tags" do
180
+ @results['outside_select'].should == 'Ruby'
181
+ end
182
+
183
+ it "should not serliaze fields associated with a different form" do
184
+ @results['for_form2'].should be_nil
185
+ end
186
+ end
187
+
188
+
189
+ context "with submit button outside the form defined by <button> tag" do
190
+ before do
191
+ @session.click_button('outside_button')
192
+ @results = extract_results(@session)
193
+ end
194
+
195
+ it "should submit the associated form" do
196
+ @results['which_form'].should == 'form2'
197
+ end
198
+
199
+ it "should submit the button that was clicked, but not other buttons" do
200
+ @results['outside_button'].should == 'outside_button'
201
+ @results['unused'].should be_nil
202
+ end
203
+ end
204
+
205
+ context "with submit button outside the form defined by <input type='submit'> tag" do
206
+ before do
207
+ @session.click_button('outside_submit')
208
+ @results = extract_results(@session)
209
+ end
210
+
211
+ it "should submit the associated form" do
212
+ @results['which_form'].should == 'form1'
213
+ end
214
+
215
+ it "should submit the button that was clicked, but not other buttons" do
216
+ @results['outside_submit'].should == 'outside_submit'
217
+ @results['submit_form1'].should be_nil
218
+ end
219
+ end
220
+
221
+ context "with submit button for form1 located within form2" do
222
+ it "should submit the form associated with the button" do
223
+ @session.click_button('other_form_button')
224
+ extract_results(@session)['which_form'].should == "form1"
225
+ end
226
+ end
227
+
165
228
  context "with alt given on an image button" do
166
229
  it "should submit the associated form" do
167
230
  @session.click_button('oh hai thar')
@@ -332,4 +395,17 @@ Capybara::SpecHelper.spec '#click_button' do
332
395
  addresses[1]["city"].should == 'Mikolaiv'
333
396
  addresses[1]["country"].should == 'Ukraine'
334
397
  end
398
+
399
+ context "with :exact option" do
400
+ it "should accept partial matches when false" do
401
+ @session.click_button('What an Awesome', :exact => false)
402
+ extract_results(@session)['first_name'].should == 'John'
403
+ end
404
+
405
+ it "should not accept partial matches when true" do
406
+ expect do
407
+ @session.click_button('What an Awesome', :exact => true)
408
+ end.to raise_error(Capybara::ElementNotFound)
409
+ end
410
+ end
335
411
  end