capybara 2.7.1 → 2.8.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +22 -0
  3. data/README.md +27 -3
  4. data/lib/capybara.rb +19 -4
  5. data/lib/capybara/driver/base.rb +6 -2
  6. data/lib/capybara/driver/node.rb +13 -5
  7. data/lib/capybara/helpers.rb +2 -2
  8. data/lib/capybara/node/actions.rb +116 -17
  9. data/lib/capybara/node/base.rb +7 -1
  10. data/lib/capybara/node/element.rb +23 -3
  11. data/lib/capybara/node/finders.rb +45 -29
  12. data/lib/capybara/node/matchers.rb +13 -15
  13. data/lib/capybara/queries/selector_query.rb +22 -5
  14. data/lib/capybara/queries/text_query.rb +42 -6
  15. data/lib/capybara/rack_test/node.rb +13 -1
  16. data/lib/capybara/result.rb +80 -8
  17. data/lib/capybara/rspec/features.rb +13 -6
  18. data/lib/capybara/selector.rb +98 -71
  19. data/lib/capybara/selector/filter_set.rb +46 -0
  20. data/lib/capybara/selenium/driver.rb +22 -23
  21. data/lib/capybara/selenium/node.rb +14 -6
  22. data/lib/capybara/server.rb +20 -10
  23. data/lib/capybara/session.rb +44 -8
  24. data/lib/capybara/spec/session/all_spec.rb +4 -4
  25. data/lib/capybara/spec/session/assert_text.rb +23 -0
  26. data/lib/capybara/spec/session/check_spec.rb +66 -8
  27. data/lib/capybara/spec/session/choose_spec.rb +20 -0
  28. data/lib/capybara/spec/session/click_button_spec.rb +0 -3
  29. data/lib/capybara/spec/session/click_link_spec.rb +7 -0
  30. data/lib/capybara/spec/session/execute_script_spec.rb +6 -1
  31. data/lib/capybara/spec/session/find_button_spec.rb +19 -1
  32. data/lib/capybara/spec/session/find_field_spec.rb +21 -1
  33. data/lib/capybara/spec/session/find_link_spec.rb +19 -1
  34. data/lib/capybara/spec/session/find_spec.rb +32 -4
  35. data/lib/capybara/spec/session/first_spec.rb +4 -4
  36. data/lib/capybara/spec/session/has_field_spec.rb +4 -0
  37. data/lib/capybara/spec/session/has_text_spec.rb +2 -2
  38. data/lib/capybara/spec/session/node_spec.rb +24 -3
  39. data/lib/capybara/spec/session/reset_session_spec.rb +7 -0
  40. data/lib/capybara/spec/session/selectors_spec.rb +14 -0
  41. data/lib/capybara/spec/session/uncheck_spec.rb +39 -0
  42. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +4 -2
  43. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
  44. data/lib/capybara/spec/session/window/window_spec.rb +36 -22
  45. data/lib/capybara/spec/session/within_frame_spec.rb +19 -0
  46. data/lib/capybara/spec/spec_helper.rb +3 -0
  47. data/lib/capybara/spec/views/form.erb +34 -6
  48. data/lib/capybara/spec/views/with_html.erb +5 -1
  49. data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
  50. data/lib/capybara/spec/views/with_windows.erb +2 -0
  51. data/lib/capybara/version.rb +1 -1
  52. data/spec/capybara_spec.rb +34 -0
  53. data/spec/rack_test_spec.rb +24 -0
  54. data/spec/result_spec.rb +25 -0
  55. data/spec/rspec/features_spec.rb +3 -3
  56. data/spec/selenium_spec.rb +6 -3
  57. data/spec/server_spec.rb +2 -2
  58. metadata +18 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85d6a1e7f742859bc5336d455e804e941efbaffe
4
- data.tar.gz: 9235385a6473080bb3ac0cae536857a054d323fc
3
+ metadata.gz: 0cbea94168c582ba31d25ccf5f24312005b47616
4
+ data.tar.gz: 7b779b6363ffb09be883cfe71ac2fde7cd616851
5
5
  SHA512:
6
- metadata.gz: 301575ccd96eb6a4239441886571377806d911f0de5868fc3891f052518199fef15424eba03429e8bf7e7ba5d1abba62e6953ba7a171a66ba2e3a325824806a8
7
- data.tar.gz: 3339a52f863d7417eaedae85e8e556d5359f40a04f4c628dc883ddadd517a04d62c0f40b3a0d2d6cf2600842a6f3f1bb011d1df5fe88a977278273ae7055a417
6
+ metadata.gz: 132688050a792bda11f9d4054aa5bac76e6f4070668f72c10bce230060d622e306e9eeb1b2bb079de66d428ca24f2814d07102083619db4e8f53075c473727a6
7
+ data.tar.gz: dbd2d43752dad9b40b2281ca1281cd3751e774ba6b785024f98c3c7c845637cdb76c36521343d16f338af34c21439cab00a76f837b32c4a1a88115f52a99e674
data/History.md CHANGED
@@ -1,3 +1,25 @@
1
+ # Version 2.8.0
2
+ Release date: 2016-08-16
3
+
4
+ ### Fixed
5
+ * Issue with modals present when closing the page using selenium - Issue #1696 [Jonas Nicklas, Thomas Walpole]
6
+ * Server errors raised in test code have the cause set to an explanatory exception
7
+ in rubies that support Exception#cause rather than a confusing ExpectationNotMet - Issue #1719 [Thomas Walpole]
8
+ * background/given/given! RSoec aliases will work if RSpec config.shared_context_metadata_behavior == :apply_to_host_groups [Thomas Walpole]
9
+ * Fixed setting of unexpectedAlertError now that Selenium will be freezing the Capabilities::DEFAULTS [Thomas Walpole]
10
+
11
+ ### Added
12
+ * 'check', 'uncheck', and 'choose' can now optionally click the associated label if the checkbox/radio button is not visible [Thomas Walpole]
13
+ * Raise error if Capybara.app_host/default_host are specified incorrectly [Thomas Walpole]
14
+ * Capybara::Selector::FilterSet allows for sharing filter definitions between selectors [Thomas Walpole]
15
+ * Remove need to pass nil locator in most node actions when locator is not needed [Thomas Walpole]
16
+ * New frames API for drivers - Issue #1365 [Thomas Walpole]
17
+ * Deprecated Element#parent in favor of Element#query_scope to better indicate what it is [Thomas Walpole]
18
+ * Improved error messages for have_text matcher [Alex Chaffee, Thomas Walpole]
19
+ * The `:with` option for the field selector now accepts a regular expression for matching the field value [Uwe Kubosch]
20
+ * Support matching on aria-label attribute when finding fields/links/buttons - Issue #1528 [Thomas Walpole]
21
+ * Optimize Capybara::Result to only apply fields as necessary in common use-case of `.all[idx]` [Thomas Walpole]
22
+
1
23
  #Version 2.7.1
2
24
  Release date: 2016-05-01
3
25
 
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Build Status](https://secure.travis-ci.org/jnicklas/capybara.svg)](https://travis-ci.org/jnicklas/capybara)
4
4
  [![Dependency Status](https://gemnasium.com/jnicklas/capybara.svg)](https://gemnasium.com/jnicklas/capybara)
5
5
  [![Code Climate](https://codeclimate.com/github/jnicklas/capybara.svg)](https://codeclimate.com/github/jnicklas/capybara)
6
+ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6
7
 
7
8
  Capybara helps you test web applications by simulating how a real user would
8
9
  interact with your app. It is agnostic about the driver running your tests and
@@ -12,6 +13,8 @@ through an external gem.
12
13
  **Need help?** Ask on the mailing list (please do not open an issue on
13
14
  GitHub): http://groups.google.com/group/ruby-capybara
14
15
 
16
+ **Note: Firefox 48** If you're using Firefox with selenium-webdrvier, stay on Firefox 47.0.1 and selenium-webdriver 2.53.4. Firefox 48 requires geckodriver and is not supported without selenium-webdriver 3 which currently has multiple issues and is feature incomplete
17
+
15
18
  ## Table of contents
16
19
 
17
20
  - [Key benefits](#key-benefits)
@@ -44,7 +47,7 @@ GitHub): http://groups.google.com/group/ruby-capybara
44
47
  - [Asynchronous JavaScript (Ajax and friends)](#asynchronous-javascript-ajax-and-friends)
45
48
  - [Using the DSL elsewhere](#using-the-dsl-elsewhere)
46
49
  - [Calling remote servers](#calling-remote-servers)
47
- - [Using the sessions manually](#using-the-sessions-manually)
50
+ - [Using sessions](#using-sessions)
48
51
  - [XPath, CSS and selectors](#xpath-css-and-selectors)
49
52
  - [Beware the XPath // trap](#beware-the-xpath--trap)
50
53
  - [Configuring and adding drivers](#configuring-and-adding-drivers)
@@ -69,7 +72,7 @@ Capybara requires Ruby 1.9.3 or later. To install, add this line to your
69
72
  gem 'capybara'
70
73
  ```
71
74
 
72
- **Note:** If using Ruby < 2.0 you will also need to limit the version of mime-types to < 3.0
75
+ **Note:** If using Ruby < 2.0 you will also need to limit the version of mime-types to < 3.0 and the version of rack to < 2.0
73
76
 
74
77
  If the application that you are testing is a Rails app, add this line to your test helper file:
75
78
 
@@ -818,7 +821,28 @@ remote application:
818
821
  Capybara.run_server = false
819
822
  ```
820
823
 
821
- ## <a name="using-the-sessions-manually"></a>Using the sessions manually
824
+ ## <a name="using-sessions"></a>Using sessions
825
+
826
+ Capybara manages named sessions (:default if not specified) allowing multiple sessions using the same driver and test app instance to be interacted with.
827
+ A new session will be created using the current driver if a session with the given name using the current driver and test app instance is not found.
828
+
829
+ ### Named sessions
830
+ To perform operations in a different session and then revert to the previous session
831
+
832
+ ```ruby
833
+ Capybara.using_session("Bob's session") do
834
+ #do something in Bob's browser session
835
+ end
836
+ #reverts to previous session
837
+ ```
838
+
839
+ To permanently switch the current session to a different session
840
+
841
+ ```ruby
842
+ Capybara.session_name = "some other session"
843
+ ````
844
+
845
+ ### <a name="using-sessions-manually"></a>Using sessions manually
822
846
 
823
847
  For ultimate control, you can instantiate and use a
824
848
  [Session](http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Session)
@@ -20,10 +20,11 @@ module Capybara
20
20
  class ReadOnlyElementError < CapybaraError; end
21
21
 
22
22
  class << self
23
- attr_accessor :asset_host, :app_host, :run_server, :default_host, :always_include_port
24
- attr_accessor :server_port, :exact, :match, :exact_options, :visible_text_only
23
+ attr_reader :app_host, :default_host
24
+ attr_accessor :asset_host, :run_server, :always_include_port
25
+ attr_accessor :server_port, :exact, :match, :exact_options, :visible_text_only, :enable_aria_label
25
26
  attr_accessor :default_selector, :default_max_wait_time, :ignore_hidden_elements
26
- attr_accessor :save_path, :wait_on_first_by_default, :automatic_reload
27
+ attr_accessor :save_path, :wait_on_first_by_default, :automatic_label_click, :automatic_reload
27
28
  attr_accessor :reuse_server, :raise_server_errors, :server_errors
28
29
  attr_writer :default_driver, :current_driver, :javascript_driver, :session_name, :server_host
29
30
  attr_reader :save_and_open_page_path
@@ -40,7 +41,7 @@ module Capybara
40
41
  #
41
42
  # === Configurable options
42
43
  #
43
- # [app_host = String] The default host to use when giving a relative URL to visit
44
+ # [app_host = String/nil] The default host to use when giving a relative URL to visit, must be a valid URL e.g. http://www.example.com
44
45
  # [always_include_port = Boolean] Whether the Rack server's port should automatically be inserted into every visited URL (Default: false)
45
46
  # [asset_host = String] Where dynamic assets are hosted - will be prepended to relative asset locations if present (Default: nil)
46
47
  # [run_server = Boolean] Whether to start a Rack server for the given Rack app (Default: true)
@@ -52,6 +53,8 @@ module Capybara
52
53
  # [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true)
53
54
  # [save_path = String] Where to put pages saved through save_(page|screenshot), save_and_open_(page|screenshot) (Default: Dir.pwd)
54
55
  # [wait_on_first_by_default = Boolean] Whether Node#first defaults to Capybara waiting behavior for at least 1 element to match (Default: false)
56
+ # [automatic_label_click = Boolean] Whether Node#choose, Node#check, Node#uncheck will attempt to click the associated label element if the checkbox/radio button are non-visible (Default: false)
57
+ # [enable_aria_label = Boolean] Whether fields, links, and buttons will match against aria-label attribute (Default: false)
55
58
  # [reuse_server = Boolean] Reuse the server thread between multiple sessions using the same app object (Default: true)
56
59
  # === DSL Options
57
60
  #
@@ -387,6 +390,16 @@ module Capybara
387
390
  @save_and_open_page_path = path
388
391
  end
389
392
 
393
+ def app_host=(url)
394
+ raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::regexp)
395
+ @app_host = url
396
+ end
397
+
398
+ def default_host=(url)
399
+ raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::regexp)
400
+ @default_host = url
401
+ end
402
+
390
403
  def included(base)
391
404
  base.send(:include, Capybara::DSL)
392
405
  warn "`include Capybara` is deprecated. Please use `include Capybara::DSL` instead."
@@ -487,6 +500,8 @@ Capybara.configure do |config|
487
500
  config.server_errors = [StandardError]
488
501
  config.visible_text_only = false
489
502
  config.wait_on_first_by_default = false
503
+ config.automatic_label_click = false
504
+ config.enable_aria_label = false
490
505
  config.reuse_server = true
491
506
  end
492
507
 
@@ -48,8 +48,12 @@ class Capybara::Driver::Base
48
48
  raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#status_code'
49
49
  end
50
50
 
51
- def within_frame(frame_handle)
52
- raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#within_frame'
51
+ ##
52
+ #
53
+ # @param frame [Capybara::Node::Element, :parent, :top] The iframe element to switch to
54
+ #
55
+ def switch_to_frame(frame)
56
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#switch_to_frame'
53
57
  end
54
58
 
55
59
  def current_window_handle
@@ -42,23 +42,23 @@ module Capybara
42
42
  def click
43
43
  raise NotImplementedError
44
44
  end
45
-
45
+
46
46
  def right_click
47
47
  raise NotImplementedError
48
48
  end
49
-
49
+
50
50
  def double_click
51
51
  raise NotImplementedError
52
52
  end
53
-
53
+
54
54
  def send_keys(*args)
55
55
  raise NotImplementedError
56
56
  end
57
-
57
+
58
58
  def hover
59
59
  raise NotImplementedError
60
60
  end
61
-
61
+
62
62
  def drag_to(element)
63
63
  raise NotImplementedError
64
64
  end
@@ -83,6 +83,14 @@ module Capybara
83
83
  raise NotImplementedError
84
84
  end
85
85
 
86
+ def readonly?
87
+ !!self[:readonly]
88
+ end
89
+
90
+ def multiple?
91
+ !!self[:multiple]
92
+ end
93
+
86
94
  def path
87
95
  raise NotSupportedByDriverError, 'Capybara::Driver::Node#path'
88
96
  end
@@ -28,8 +28,8 @@ module Capybara
28
28
  # @param [String] text Text to escape
29
29
  # @return [String] Escaped text
30
30
  #
31
- def to_regexp(text)
32
- text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(normalize_whitespace(text)))
31
+ def to_regexp(text, options=nil)
32
+ text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(normalize_whitespace(text)), options)
33
33
  end
34
34
 
35
35
  ##
@@ -7,10 +7,19 @@ module Capybara
7
7
  #
8
8
  # Finds a button or link by id, text or value and clicks it. Also looks at image
9
9
  # alt text inside the link.
10
+ # @!macro waiting_behavior
11
+ # If the driver is capable of executing JavaScript, +$0+ will wait for a set amount of time
12
+ # and continuously retry finding the element until either the element is found or the time
13
+ # expires. The length of time +find+ will wait is controlled through {Capybara.default_max_wait_time}
10
14
  #
11
- # @param [String] locator Text, id or value of link or button
15
+ # @option options [false, Numeric] wait (Capybara.default_max_wait_time) Maximum time to wait for matching element to appear.
12
16
  #
13
- def click_link_or_button(locator, options={})
17
+ # @overload click_link_or_button([locator], options)
18
+ #
19
+ # @param [String] locator Text, id or value of link or button
20
+ #
21
+ def click_link_or_button(locator=nil, options={})
22
+ locator, options = nil, locator if locator.is_a? Hash
14
23
  find(:link_or_button, locator, options).click
15
24
  end
16
25
  alias_method :click_on, :click_link_or_button
@@ -20,10 +29,14 @@ module Capybara
20
29
  # Finds a link by id, text or title and clicks it. Also looks at image
21
30
  # alt text inside the link.
22
31
  #
23
- # @param [String] locator text, id, title or nested image's alt attribute
24
- # @param options See {Capybara::Node::Finders#find_link}
32
+ # @macro waiting_behavior
33
+ #
34
+ # @overload click_link([locator], options)
35
+ # @param [String] locator text, id, title or nested image's alt attribute
36
+ # @param options See {Capybara::Node::Finders#find_link}
25
37
  #
26
- def click_link(locator, options={})
38
+ def click_link(locator=nil, options={})
39
+ locator, options = nil, locator if locator.is_a? Hash
27
40
  find(:link, locator, options).click
28
41
  end
29
42
 
@@ -34,9 +47,13 @@ module Capybara
34
47
  # \<button> element. All buttons can be found by their id, value, or title. \<button> elements can also be found
35
48
  # by their text content, and image \<input> elements by their alt attribute
36
49
  #
37
- # @param [String] locator Which button to find
38
- # @param options See {Capybara::Node::Finders#find_button}
39
- def click_button(locator, options={})
50
+ # @macro waiting_behavior
51
+ #
52
+ # @overload click_button([locator], options)
53
+ # @param [String] locator Which button to find
54
+ # @param options See {Capybara::Node::Finders#find_button}
55
+ def click_button(locator=nil, options={})
56
+ locator, options = nil, locator if locator.is_a? Hash
40
57
  find(:button, locator, options).click
41
58
  end
42
59
 
@@ -47,18 +64,28 @@ module Capybara
47
64
  #
48
65
  # page.fill_in 'Name', :with => 'Bob'
49
66
  #
67
+ # @macro waiting_behavior
68
+ #
50
69
  # @param [String] locator Which field to fill in
51
70
  # @param [Hash] options
52
- # @option options [String] :with The value to fill in - required
53
- # @option options [Hash] :fill_options Driver specific options regarding how to fill fields
71
+ # @option options [String] :with The value to fill in - required
72
+ # @option options [Hash] :fill_options Driver specific options regarding how to fill fields
73
+ # @option options [Boolean] :multiple Match fields that can have multiple values?
74
+ # @option options [String] id Match fields that match the id attribute
75
+ # @option options [String] name Match fields that match the name attribute
76
+ # @option options [String] placeholder Match fields that match the placeholder attribute
54
77
  #
55
78
  def fill_in(locator, options={})
79
+ locator, options = nil, locator if locator.is_a? Hash
56
80
  raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
57
81
  with = options.delete(:with)
58
82
  fill_options = options.delete(:fill_options)
59
83
  find(:fillable_field, locator, options).set(with, fill_options)
60
84
  end
61
85
 
86
+ # @!macro label_click
87
+ # @option options [Boolean] :allow_label_click (Capybara.automatic_label_click) Attempt to click the label to toggle state if element is non-visible.
88
+
62
89
  ##
63
90
  #
64
91
  # Find a radio button and mark it as checked. The radio button can be found
@@ -66,10 +93,30 @@ module Capybara
66
93
  #
67
94
  # page.choose('Male')
68
95
  #
69
- # @param [String] locator Which radio button to choose
96
+ # @overload choose([locator], options)
97
+ # @param [String] locator Which radio button to choose
70
98
  #
99
+ # @option options [String] :option Value of the radio_button to choose
100
+ # @option options [String] id Match fields that match the id attribute
101
+ # @option options [String] name Match fields that match the name attribute
102
+ # @macro waiting_behavior
103
+ # @macro label_click
71
104
  def choose(locator, options={})
72
- find(:radio_button, locator, options).set(true)
105
+ locator, options = nil, locator if locator.is_a? Hash
106
+ allow_label_click = options.delete(:allow_label_click) { Capybara.automatic_label_click }
107
+
108
+ begin
109
+ find(:radio_button, locator, options).set(true)
110
+ rescue Capybara::ElementNotFound => e
111
+ raise unless allow_label_click
112
+ begin
113
+ radio = find(:radio_button, locator, options.merge({wait: 0, visible: :hidden}))
114
+ label = find(:label, for: radio, wait: 0, visible: true)
115
+ label.click unless radio.checked?
116
+ rescue
117
+ raise e
118
+ end
119
+ end
73
120
  end
74
121
 
75
122
  ##
@@ -79,10 +126,32 @@ module Capybara
79
126
  #
80
127
  # page.check('German')
81
128
  #
82
- # @param [String] locator Which check box to check
129
+ #
130
+ # @overload check([locator], options)
131
+ # @param [String] locator Which check box to check
132
+ #
133
+ # @option options [String] :option Value of the checkbox to select
134
+ # @option options [String] id Match fields that match the id attribute
135
+ # @option options [String] name Match fields that match the name attribute
136
+ # @macro label_click
137
+ # @macro waiting_behavior
83
138
  #
84
139
  def check(locator, options={})
85
- find(:checkbox, locator, options).set(true)
140
+ locator, options = nil, locator if locator.is_a? Hash
141
+ allow_label_click = options.delete(:allow_label_click) { Capybara.automatic_label_click }
142
+
143
+ begin
144
+ find(:checkbox, locator, options).set(true)
145
+ rescue Capybara::ElementNotFound => e
146
+ raise unless allow_label_click
147
+ begin
148
+ cbox = find(:checkbox, locator, options.merge({wait: 0, visible: :hidden}))
149
+ label = find(:label, for: cbox, wait: 0, visible: true)
150
+ label.click unless cbox.checked?
151
+ rescue
152
+ raise e
153
+ end
154
+ end
86
155
  end
87
156
 
88
157
  ##
@@ -92,10 +161,32 @@ module Capybara
92
161
  #
93
162
  # page.uncheck('German')
94
163
  #
95
- # @param [String] locator Which check box to uncheck
164
+ #
165
+ # @overload uncheck([locator], options)
166
+ # @param [String] locator Which check box to uncheck
167
+ #
168
+ # @option options [String] :option Value of the checkbox to deselect
169
+ # @option options [String] id Match fields that match the id attribute
170
+ # @option options [String] name Match fields that match the name attribute
171
+ # @macro label_click
172
+ # @macro waiting_behavior
96
173
  #
97
174
  def uncheck(locator, options={})
98
- find(:checkbox, locator, options).set(false)
175
+ locator, options = nil, locator if locator.is_a? Hash
176
+ allow_label_click = options.delete(:allow_label_click) { Capybara.automatic_label_click }
177
+
178
+ begin
179
+ find(:checkbox, locator, options).set(false)
180
+ rescue Capybara::ElementNotFound => e
181
+ raise unless allow_label_click
182
+ begin
183
+ cbox = find(:checkbox, locator, options.merge({wait: 0, visible: :hidden}))
184
+ label = find(:label, for: cbox, wait: 0, visible: true)
185
+ label.click if cbox.checked?
186
+ rescue
187
+ raise e
188
+ end
189
+ end
99
190
  end
100
191
 
101
192
  ##
@@ -109,6 +200,8 @@ module Capybara
109
200
  #
110
201
  # page.select 'March', :from => 'Month'
111
202
  #
203
+ # @macro waiting_behavior
204
+ #
112
205
  # @param [String] value Which option to select
113
206
  # @option options [String] :from The id, name or label of the select box
114
207
  #
@@ -129,6 +222,8 @@ module Capybara
129
222
  #
130
223
  # page.unselect 'March', :from => 'Month'
131
224
  #
225
+ # @macro waiting_behavior
226
+ #
132
227
  # @param [String] value Which option to unselect
133
228
  # @param [Hash{:from => String}] options The id, name or label of the select box
134
229
  #
@@ -148,15 +243,19 @@ module Capybara
148
243
  #
149
244
  # page.attach_file(locator, '/path/to/file.png')
150
245
  #
246
+ # @macro waiting_behavior
247
+ #
151
248
  # @param [String] locator Which field to attach the file to
152
249
  # @param [String] path The path of the file that will be attached, or an array of paths
153
250
  #
154
251
  # @option options [Symbol] match (Capybara.match) The matching strategy to use (:one, :first, :prefer_exact, :smart).
155
252
  # @option options [Boolean] exact (Capybara.exact) Match the exact label name/contents or accept a partial match.
156
- # @option options [Fixnum] wait (Capybara.default_max_wait_time) If using a Javascript driver, maximum number of seconds during which the element will be searched for.
157
253
  # @option options [Boolean] multiple Match field which allows multiple file selection
254
+ # @option options [String] id Match fields that match the id attribute
255
+ # @option options [String] name Match fields that match the name attribute
158
256
  #
159
257
  def attach_file(locator, path, options={})
258
+ locator, path, options = nil, locator, path if path.is_a? Hash
160
259
  Array(path).each do |p|
161
260
  raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
162
261
  end