capybara 2.3.0 → 2.4.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +21 -0
  3. data/README.md +50 -12
  4. data/lib/capybara.rb +8 -1
  5. data/lib/capybara/driver/base.rb +28 -0
  6. data/lib/capybara/driver/node.rb +3 -2
  7. data/lib/capybara/helpers.rb +2 -3
  8. data/lib/capybara/node/actions.rb +5 -2
  9. data/lib/capybara/node/base.rb +10 -0
  10. data/lib/capybara/node/document.rb +2 -0
  11. data/lib/capybara/node/document_matchers.rb +68 -0
  12. data/lib/capybara/node/element.rb +17 -2
  13. data/lib/capybara/node/finders.rb +5 -20
  14. data/lib/capybara/node/matchers.rb +101 -71
  15. data/lib/capybara/node/simple.rb +9 -15
  16. data/lib/capybara/queries/base_query.rb +29 -0
  17. data/lib/capybara/queries/text_query.rb +56 -0
  18. data/lib/capybara/queries/title_query.rb +40 -0
  19. data/lib/capybara/query.rb +30 -20
  20. data/lib/capybara/rack_test/node.rb +11 -3
  21. data/lib/capybara/result.rb +1 -1
  22. data/lib/capybara/rspec/features.rb +38 -21
  23. data/lib/capybara/rspec/matchers.rb +53 -38
  24. data/lib/capybara/selector.rb +68 -14
  25. data/lib/capybara/selenium/driver.rb +54 -6
  26. data/lib/capybara/selenium/node.rb +4 -2
  27. data/lib/capybara/session.rb +109 -35
  28. data/lib/capybara/spec/public/test.js +34 -1
  29. data/lib/capybara/spec/session/accept_alert_spec.rb +57 -0
  30. data/lib/capybara/spec/session/accept_confirm_spec.rb +19 -0
  31. data/lib/capybara/spec/session/accept_prompt_spec.rb +49 -0
  32. data/lib/capybara/spec/session/assert_text.rb +195 -0
  33. data/lib/capybara/spec/session/assert_title.rb +69 -0
  34. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +35 -0
  35. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +19 -0
  36. data/lib/capybara/spec/session/find_field_spec.rb +6 -0
  37. data/lib/capybara/spec/session/has_text_spec.rb +1 -1
  38. data/lib/capybara/spec/session/node_spec.rb +16 -1
  39. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +1 -1
  40. data/lib/capybara/spec/session/visit_spec.rb +5 -0
  41. data/lib/capybara/spec/views/with_html.erb +4 -0
  42. data/lib/capybara/spec/views/with_js.erb +17 -0
  43. data/lib/capybara/version.rb +1 -1
  44. data/spec/dsl_spec.rb +3 -1
  45. data/spec/rack_test_spec.rb +12 -1
  46. data/spec/rspec/features_spec.rb +1 -1
  47. data/spec/rspec/matchers_spec.rb +113 -20
  48. data/spec/selenium_spec.rb +10 -1
  49. metadata +13 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9aa9a8cc20ae71dd404a731f98867ba19be9d4f2
4
- data.tar.gz: 58b4097a52c34c878ceca8b9bf8477bc20a6e32a
3
+ metadata.gz: 4ce3ef92ec26ebbca0d14b7fc5b7b619ce8a59dd
4
+ data.tar.gz: 5c2d6dde6cd9a0e480769b60b0b0afa54c5dea5a
5
5
  SHA512:
6
- metadata.gz: 4f49792d051b3a87893c10becbb4866eff4cee8a6e9d146a3adfec388df1373c1fe382c390ac9f982de24845c8ea4297a0f168ade5318c628611d1043752cafb
7
- data.tar.gz: c707670017ac8f9b9cd335c1cd006724f84d56747c79f7e4ebf4203b4cdac39fa3e211b11c7c8e2819dbd8556bb2c49876e945c37328c47e84bc465e679aa034
6
+ metadata.gz: 5afcbe6b66303842f126182db09eacc9bbde7ac3a77db257ed18015a5dda24b6ba47c2b4226a6310f7208740b1d35bfeb5a4460d16e13683796f3cbd4cebfb66
7
+ data.tar.gz: c39745bcee5996ed04722545461380da5e2f85372c4054bc8c9a739e0a91400be53c9870cb9b6cc488188859df707145936275b992bc35326ad7f773fb0d520a
data/History.md CHANGED
@@ -1,3 +1,24 @@
1
+ # Unreleased
2
+
3
+ ### Added
4
+
5
+ * 'assert_text', 'assert_no_text', 'assert_title', 'assert_no_title' methods added [Andrey Botalov]
6
+ * have_title matcher now supports :wait option [Andrey Botalov]
7
+ * More descriptive have_text error messages [Andrey Botalov]
8
+ * New modal API ('accept_alert', 'accept_confirm', 'dismiss_confirm', 'accept_prompt', 'dismiss_prompt') - [Mike Pack, Thomas Walpole]
9
+ * Warning when attempting to set contents of a readonly element
10
+ * Suport for and/or compounding of Capybara's RSpec matchers for RSpec 3 [Thomas Walpole]
11
+ * :fill_options option for 'fill_in' method that propagates to 'set' to allow for driver specific modification of how fields are filled in [Gabriel Sobrinho, Thomas Walpole]
12
+ * Improved selector/filter description in failure messages [Thomas Walpole]
13
+
14
+ ### Fixed
15
+
16
+ * HaveText error message now shows the text checked all the time
17
+ * RackTest driver no longer attempts to follow an anchor tag without an href attribute
18
+ * Warnings under RSpec 3
19
+ * Handle URI schemes like about: correctly [Andrey Botalov]
20
+ * RSpecs expose_dsl_globally option is now followed [Myron Marston, Thomas Walpole]
21
+
1
22
  # Version 2.3.0
2
23
 
3
24
  Release date: 2014-06-02
data/README.md CHANGED
@@ -60,10 +60,10 @@ You can use the Capybara DSL in your steps, like so:
60
60
  ```ruby
61
61
  When /I sign in/ do
62
62
  within("#session") do
63
- fill_in 'Login', :with => 'user@example.com'
63
+ fill_in 'Email', :with => 'user@example.com'
64
64
  fill_in 'Password', :with => 'password'
65
65
  end
66
- click_link 'Sign in'
66
+ click_button 'Sign in'
67
67
  end
68
68
  ```
69
69
 
@@ -105,10 +105,10 @@ describe "the signin process", :type => :feature do
105
105
  it "signs me in" do
106
106
  visit '/sessions/new'
107
107
  within("#session") do
108
- fill_in 'Login', :with => 'user@example.com'
108
+ fill_in 'Email', :with => 'user@example.com'
109
109
  fill_in 'Password', :with => 'password'
110
110
  end
111
- click_link 'Sign in'
111
+ click_button 'Sign in'
112
112
  expect(page).to have_content 'Success'
113
113
  end
114
114
  end
@@ -136,10 +136,10 @@ feature "Signing in" do
136
136
  scenario "Signing in with correct credentials" do
137
137
  visit '/sessions/new'
138
138
  within("#session") do
139
- fill_in 'Login', :with => 'user@example.com'
139
+ fill_in 'Email', :with => 'user@example.com'
140
140
  fill_in 'Password', :with => 'caplin'
141
141
  end
142
- click_link 'Sign in'
142
+ click_button 'Sign in'
143
143
  expect(page).to have_content 'Success'
144
144
  end
145
145
 
@@ -148,10 +148,10 @@ feature "Signing in" do
148
148
  scenario "Signing in as another user" do
149
149
  visit '/sessions/new'
150
150
  within("#session") do
151
- fill_in 'Login', :with => other_user.email
151
+ fill_in 'Email', :with => other_user.email
152
152
  fill_in 'Password', :with => other_user.password
153
153
  end
154
- click_link 'Sign in'
154
+ click_button 'Sign in'
155
155
  expect(page).to have_content 'Invalid email or password'
156
156
  end
157
157
  end
@@ -497,6 +497,44 @@ that this may break with more complicated expressions:
497
497
  result = page.evaluate_script('4 + 4');
498
498
  ```
499
499
 
500
+ ### Modals
501
+
502
+ In drivers which support it, you can accept, dismiss and respond to alerts, confirms and prompts.
503
+
504
+ You can accept or dismiss alert messages by wrapping the code that produces an alert in a block:
505
+
506
+ ```ruby
507
+ accept_alert do
508
+ click_link('Show Alert')
509
+ end
510
+ ```
511
+
512
+ You can accept or dismiss a confirmation by wrapping it in a block, as well:
513
+
514
+ ```ruby
515
+ dismiss_confirm do
516
+ click_link('Show Confirm')
517
+ end
518
+ ```
519
+
520
+ You can accept or dismiss prompts as well, and also provide text to fill in for the response:
521
+
522
+ ```ruby
523
+ accept_prompt(with: 'Linus Torvalds') do
524
+ click_link('Show Prompt About Linux')
525
+ end
526
+ ```
527
+
528
+ All modal methods return the message that was presented. So, you can access the prompt message
529
+ by assigning the return to a variable:
530
+
531
+ ```ruby
532
+ message = accept_prompt(with: 'Linus Torvalds') do
533
+ click_link('Show Prompt About Linux')
534
+ end
535
+ expect(message).to eq('Who is the chief architect of Linux?')
536
+ ```
537
+
500
538
  ### Debugging
501
539
 
502
540
  It can be useful to take a snapshot of the page as it currently is and take a
@@ -682,10 +720,10 @@ module MyModule
682
720
 
683
721
  def login!
684
722
  within("//form[@id='session']") do
685
- fill_in 'Login', :with => 'user@example.com'
723
+ fill_in 'Email', :with => 'user@example.com'
686
724
  fill_in 'Password', :with => 'password'
687
725
  end
688
- click_link 'Sign in'
726
+ click_button 'Sign in'
689
727
  end
690
728
  end
691
729
  ```
@@ -732,10 +770,10 @@ require 'capybara'
732
770
 
733
771
  session = Capybara::Session.new(:webkit, my_rack_app)
734
772
  session.within("//form[@id='session']") do
735
- session.fill_in 'Login', :with => 'user@example.com'
773
+ session.fill_in 'Email', :with => 'user@example.com'
736
774
  session.fill_in 'Password', :with => 'password'
737
775
  end
738
- session.click_link 'Sign in'
776
+ session.click_button 'Sign in'
739
777
  ```
740
778
 
741
779
  ## XPath, CSS and selectors
@@ -7,6 +7,7 @@ module Capybara
7
7
  class DriverNotFoundError < CapybaraError; end
8
8
  class FrozenInTime < CapybaraError; end
9
9
  class ElementNotFound < CapybaraError; end
10
+ class ModalNotFound < CapybaraError; end
10
11
  class Ambiguous < ElementNotFound; end
11
12
  class ExpectationNotMet < ElementNotFound; end
12
13
  class FileNotFound < CapybaraError; end
@@ -15,6 +16,7 @@ module Capybara
15
16
  class InfiniteRedirectError < CapybaraError; end
16
17
  class ScopeError < CapybaraError; end
17
18
  class WindowError < CapybaraError; end
19
+ class ReadOnlyElementError < CapybaraError; end
18
20
 
19
21
  class << self
20
22
  attr_accessor :asset_host, :app_host, :run_server, :default_host, :always_include_port
@@ -321,13 +323,18 @@ module Capybara
321
323
  require 'capybara/window'
322
324
  require 'capybara/server'
323
325
  require 'capybara/selector'
324
- require 'capybara/query'
325
326
  require 'capybara/result'
326
327
  require 'capybara/version'
327
328
 
329
+ require 'capybara/queries/base_query'
330
+ require 'capybara/query'
331
+ require 'capybara/queries/text_query'
332
+ require 'capybara/queries/title_query'
333
+
328
334
  require 'capybara/node/finders'
329
335
  require 'capybara/node/matchers'
330
336
  require 'capybara/node/actions'
337
+ require 'capybara/node/document_matchers'
331
338
  require 'capybara/node/simple'
332
339
  require 'capybara/node/base'
333
340
  require 'capybara/node/element'
@@ -90,6 +90,34 @@ class Capybara::Driver::Base
90
90
  def no_such_window_error
91
91
  raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#no_such_window_error'
92
92
  end
93
+
94
+
95
+ ##
96
+ #
97
+ # Execute the block, and then accept the modal opened.
98
+ # @param type [:alert, :confirm, :prompt]
99
+ # @option options [Numeric] :wait How long to wait for the modal to appear after executing the block.
100
+ # @option options [String, Regexp] :text Text to verify is in the message shown in the modal
101
+ # @option options [String] :with Text to fill in in the case of a prompt
102
+ # @return [String] the message shown in the modal
103
+ # @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
104
+ #
105
+ def accept_modal(type, options={}, &blk)
106
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#accept_modal'
107
+ end
108
+
109
+ ##
110
+ #
111
+ # Execute the block, and then dismiss the modal opened.
112
+ # @param type [:alert, :confirm, :prompt]
113
+ # @option options [Numeric] :wait How long to wait for the modal to appear after executing the block.
114
+ # @option options [String, Regexp] :text Text to verify is in the message shown in the modal
115
+ # @return [String] the message shown in the modal
116
+ # @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
117
+ #
118
+ def dismiss_modal(type, options={}, &blk)
119
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#dismiss_modal'
120
+ end
93
121
 
94
122
  def invalid_element_errors
95
123
  []
@@ -25,7 +25,8 @@ module Capybara
25
25
  end
26
26
 
27
27
  # @param value String or Array. Array is only allowed if node has 'multiple' attribute
28
- def set(value)
28
+ # @param options [Hash{}] Driver specific options for how to set a value on a node
29
+ def set(value, options={})
29
30
  raise NotImplementedError
30
31
  end
31
32
 
@@ -42,7 +43,7 @@ module Capybara
42
43
  end
43
44
 
44
45
  def right_click
45
- raise NotImplmentedError
46
+ raise NotImplementedError
46
47
  end
47
48
 
48
49
  def double_click
@@ -40,10 +40,9 @@ module Capybara
40
40
  # @return [String] The modified HTML code
41
41
  #
42
42
  def inject_asset_host(html)
43
- if Capybara.asset_host
44
- if Nokogiri::HTML(html).css("base").empty? and match = html.match(/<head[^<]*?>/)
43
+ if Capybara.asset_host && Nokogiri::HTML(html).css("base").empty?
44
+ match = html.match(/<head[^<]*?>/)
45
45
  html.clone.insert match.end(0), "<base href='#{Capybara.asset_host}' />"
46
- end
47
46
  else
48
47
  html
49
48
  end
@@ -45,12 +45,15 @@ module Capybara
45
45
  # page.fill_in 'Name', :with => 'Bob'
46
46
  #
47
47
  # @param [String] locator Which field to fill in
48
- # @param [Hash{:with => String}] options The value to fill in
48
+ # @param [Hash] options
49
+ # @option options [String] :with The value to fill in - required
50
+ # @option options [Hash] :fill_options Driver specific options regarding how to fill fields
49
51
  #
50
52
  def fill_in(locator, options={})
51
53
  raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
52
54
  with = options.delete(:with)
53
- find(:fillable_field, locator, options).set(with)
55
+ fill_options = options.delete(:fill_options)
56
+ find(:fillable_field, locator, options).set(with, fill_options)
54
57
  end
55
58
 
56
59
  ##
@@ -97,6 +97,16 @@ module Capybara
97
97
  end
98
98
  end
99
99
 
100
+ # @api private
101
+ def find_css(css)
102
+ base.find_css(css)
103
+ end
104
+
105
+ # @api private
106
+ def find_xpath(xpath)
107
+ base.find_xpath(xpath)
108
+ end
109
+
100
110
  protected
101
111
 
102
112
  def catch_error?(error, errors = nil)
@@ -9,6 +9,8 @@ module Capybara
9
9
  # @see Capybara::Node
10
10
  #
11
11
  class Document < Base
12
+ include Capybara::Node::DocumentMatchers
13
+
12
14
  def inspect
13
15
  %(#<Capybara::Document>)
14
16
  end
@@ -0,0 +1,68 @@
1
+ module Capybara
2
+ module Node
3
+ module DocumentMatchers
4
+ ##
5
+ # Asserts that the page has the given title.
6
+ #
7
+ # @!macro title_query_params
8
+ # @overload $0(string, options = {})
9
+ # @param string [String] The string that title should include
10
+ # @overload $0(regexp, options = {})
11
+ # @param regexp [Regexp] The regexp that title should match to
12
+ # @option options [Numeric] :wait (Capybara.default_wait_time) Time that Capybara will wait for title to eq/match given string/regexp argument
13
+ # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
14
+ # @return [true]
15
+ #
16
+ def assert_title(title, options = {})
17
+ query = Capybara::Queries::TitleQuery.new(title, options)
18
+ synchronize(query.wait) do
19
+ unless query.resolves_for?(self)
20
+ raise Capybara::ExpectationNotMet, query.failure_message
21
+ end
22
+ end
23
+ return true
24
+ end
25
+
26
+ ##
27
+ # Asserts that the page doesn't have the given title.
28
+ #
29
+ # @macro title_query_params
30
+ # @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
31
+ # @return [true]
32
+ #
33
+ def assert_no_title(title, options = {})
34
+ query = Capybara::Queries::TitleQuery.new(title, options)
35
+ synchronize(query.wait) do
36
+ if query.resolves_for?(self)
37
+ raise Capybara::ExpectationNotMet, query.negative_failure_message
38
+ end
39
+ end
40
+ return true
41
+ end
42
+
43
+ ##
44
+ # Checks if the page has the given title.
45
+ #
46
+ # @macro title_query_params
47
+ # @return [Boolean]
48
+ #
49
+ def has_title?(title, options = {})
50
+ assert_title(title, options)
51
+ rescue Capybara::ExpectationNotMet
52
+ return false
53
+ end
54
+
55
+ ##
56
+ # Checks if the page doesn't have the given title.
57
+ #
58
+ # @macro title_query_params
59
+ # @return [Boolean]
60
+ #
61
+ def has_no_title?(title, options = {})
62
+ assert_no_title(title, options)
63
+ rescue Capybara::ExpectationNotMet
64
+ return false
65
+ end
66
+ end
67
+ end
68
+ end
@@ -89,9 +89,24 @@ module Capybara
89
89
  # Set the value of the form element to the given value.
90
90
  #
91
91
  # @param [String] value The new value
92
+ # @param [Hash{}] options Driver specific options for how to set the value
92
93
  #
93
- def set(value)
94
- synchronize { base.set(value) }
94
+ def set(value, options={})
95
+ options ||= {}
96
+
97
+ driver_supports_options = (base.method(:set).arity != 1)
98
+
99
+ unless options.empty? || driver_supports_options
100
+ warn "Options passed to Capybara::Node#set but the driver doesn't support them"
101
+ end
102
+
103
+ synchronize do
104
+ if driver_supports_options
105
+ base.set(value, options)
106
+ else
107
+ base.set(value)
108
+ end
109
+ end
95
110
  end
96
111
 
97
112
  ##
@@ -29,10 +29,10 @@ module Capybara
29
29
  query = Capybara::Query.new(*args)
30
30
  synchronize(query.wait) do
31
31
  if query.match == :smart or query.match == :prefer_exact
32
- result = resolve_query(query, true)
33
- result = resolve_query(query, false) if result.size == 0 and not query.exact?
32
+ result = query.resolve_for(self, true)
33
+ result = query.resolve_for(self, false) if result.size == 0 && !query.exact?
34
34
  else
35
- result = resolve_query(query)
35
+ result = query.resolve_for(self)
36
36
  end
37
37
  if query.match == :one or query.match == :smart and result.size > 1
38
38
  raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
@@ -144,8 +144,8 @@ module Capybara
144
144
  def all(*args)
145
145
  query = Capybara::Query.new(*args)
146
146
  synchronize(query.wait) do
147
- result = resolve_query(query)
148
- raise(Capybara::ExpectationNotMet, result.failure_message) unless result.matches_count?
147
+ result = query.resolve_for(self)
148
+ raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
149
149
  result
150
150
  end
151
151
  end
@@ -164,21 +164,6 @@ module Capybara
164
164
  def first(*args)
165
165
  all(*args).first
166
166
  end
167
-
168
- private
169
-
170
- def resolve_query(query, exact=nil)
171
- synchronize do
172
- elements = if query.selector.format==:css
173
- base.find_css(query.css)
174
- else
175
- base.find_xpath(query.xpath(exact))
176
- end.map do |node|
177
- Capybara::Node::Element.new(session, node, self, query)
178
- end
179
- Capybara::Result.new(elements, query)
180
- end
181
- end
182
167
  end
183
168
  end
184
169
  end