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