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.
- checksums.yaml +4 -4
- data/History.md +21 -0
- data/README.md +50 -12
- data/lib/capybara.rb +8 -1
- data/lib/capybara/driver/base.rb +28 -0
- data/lib/capybara/driver/node.rb +3 -2
- data/lib/capybara/helpers.rb +2 -3
- data/lib/capybara/node/actions.rb +5 -2
- data/lib/capybara/node/base.rb +10 -0
- data/lib/capybara/node/document.rb +2 -0
- data/lib/capybara/node/document_matchers.rb +68 -0
- data/lib/capybara/node/element.rb +17 -2
- data/lib/capybara/node/finders.rb +5 -20
- data/lib/capybara/node/matchers.rb +101 -71
- data/lib/capybara/node/simple.rb +9 -15
- data/lib/capybara/queries/base_query.rb +29 -0
- data/lib/capybara/queries/text_query.rb +56 -0
- data/lib/capybara/queries/title_query.rb +40 -0
- data/lib/capybara/query.rb +30 -20
- data/lib/capybara/rack_test/node.rb +11 -3
- data/lib/capybara/result.rb +1 -1
- data/lib/capybara/rspec/features.rb +38 -21
- data/lib/capybara/rspec/matchers.rb +53 -38
- data/lib/capybara/selector.rb +68 -14
- data/lib/capybara/selenium/driver.rb +54 -6
- data/lib/capybara/selenium/node.rb +4 -2
- data/lib/capybara/session.rb +109 -35
- data/lib/capybara/spec/public/test.js +34 -1
- data/lib/capybara/spec/session/accept_alert_spec.rb +57 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +19 -0
- data/lib/capybara/spec/session/accept_prompt_spec.rb +49 -0
- data/lib/capybara/spec/session/assert_text.rb +195 -0
- data/lib/capybara/spec/session/assert_title.rb +69 -0
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +35 -0
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +19 -0
- data/lib/capybara/spec/session/find_field_spec.rb +6 -0
- data/lib/capybara/spec/session/has_text_spec.rb +1 -1
- data/lib/capybara/spec/session/node_spec.rb +16 -1
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +1 -1
- data/lib/capybara/spec/session/visit_spec.rb +5 -0
- data/lib/capybara/spec/views/with_html.erb +4 -0
- data/lib/capybara/spec/views/with_js.erb +17 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/dsl_spec.rb +3 -1
- data/spec/rack_test_spec.rb +12 -1
- data/spec/rspec/features_spec.rb +1 -1
- data/spec/rspec/matchers_spec.rb +113 -20
- data/spec/selenium_spec.rb +10 -1
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ce3ef92ec26ebbca0d14b7fc5b7b619ce8a59dd
|
4
|
+
data.tar.gz: 5c2d6dde6cd9a0e480769b60b0b0afa54c5dea5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 '
|
63
|
+
fill_in 'Email', :with => 'user@example.com'
|
64
64
|
fill_in 'Password', :with => 'password'
|
65
65
|
end
|
66
|
-
|
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 '
|
108
|
+
fill_in 'Email', :with => 'user@example.com'
|
109
109
|
fill_in 'Password', :with => 'password'
|
110
110
|
end
|
111
|
-
|
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 '
|
139
|
+
fill_in 'Email', :with => 'user@example.com'
|
140
140
|
fill_in 'Password', :with => 'caplin'
|
141
141
|
end
|
142
|
-
|
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 '
|
151
|
+
fill_in 'Email', :with => other_user.email
|
152
152
|
fill_in 'Password', :with => other_user.password
|
153
153
|
end
|
154
|
-
|
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 '
|
723
|
+
fill_in 'Email', :with => 'user@example.com'
|
686
724
|
fill_in 'Password', :with => 'password'
|
687
725
|
end
|
688
|
-
|
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 '
|
773
|
+
session.fill_in 'Email', :with => 'user@example.com'
|
736
774
|
session.fill_in 'Password', :with => 'password'
|
737
775
|
end
|
738
|
-
session.
|
776
|
+
session.click_button 'Sign in'
|
739
777
|
```
|
740
778
|
|
741
779
|
## XPath, CSS and selectors
|
data/lib/capybara.rb
CHANGED
@@ -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'
|
data/lib/capybara/driver/base.rb
CHANGED
@@ -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
|
[]
|
data/lib/capybara/driver/node.rb
CHANGED
@@ -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
|
-
|
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
|
46
|
+
raise NotImplementedError
|
46
47
|
end
|
47
48
|
|
48
49
|
def double_click
|
data/lib/capybara/helpers.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
55
|
+
fill_options = options.delete(:fill_options)
|
56
|
+
find(:fillable_field, locator, options).set(with, fill_options)
|
54
57
|
end
|
55
58
|
|
56
59
|
##
|
data/lib/capybara/node/base.rb
CHANGED
@@ -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)
|
@@ -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
|
-
|
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 =
|
33
|
-
result =
|
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 =
|
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 =
|
148
|
-
raise
|
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
|