capybara 2.3.0 → 2.4.0

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