capybara 1.1.4 → 2.0.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.
- data/{History.txt → History.md} +138 -0
- data/License.txt +22 -0
- data/README.md +850 -0
- data/lib/capybara/cucumber.rb +2 -5
- data/lib/capybara/driver/base.rb +6 -6
- data/lib/capybara/driver/node.rb +3 -2
- data/lib/capybara/dsl.rb +13 -124
- data/lib/capybara/helpers.rb +33 -0
- data/lib/capybara/node/actions.rb +16 -30
- data/lib/capybara/node/base.rb +56 -13
- data/lib/capybara/node/element.rb +18 -30
- data/lib/capybara/node/finders.rb +28 -90
- data/lib/capybara/node/matchers.rb +121 -73
- data/lib/capybara/node/simple.rb +13 -11
- data/lib/capybara/query.rb +78 -0
- data/lib/capybara/rack_test/browser.rb +27 -39
- data/lib/capybara/rack_test/driver.rb +13 -3
- data/lib/capybara/rack_test/node.rb +31 -2
- data/lib/capybara/result.rb +72 -0
- data/lib/capybara/rspec/features.rb +4 -1
- data/lib/capybara/rspec/matchers.rb +33 -63
- data/lib/capybara/rspec.rb +7 -4
- data/lib/capybara/selector.rb +97 -34
- data/lib/capybara/selenium/driver.rb +15 -62
- data/lib/capybara/selenium/node.rb +14 -21
- data/lib/capybara/server.rb +32 -27
- data/lib/capybara/session.rb +90 -50
- data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
- data/lib/capybara/spec/public/jquery-ui.js +791 -0
- data/lib/capybara/spec/public/jquery.js +9046 -0
- data/lib/capybara/spec/public/test.js +3 -0
- data/lib/capybara/spec/session/all_spec.rb +61 -59
- data/lib/capybara/spec/session/assert_selector.rb +123 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +72 -55
- data/lib/capybara/spec/session/body_spec.rb +21 -0
- data/lib/capybara/spec/session/check_spec.rb +68 -48
- data/lib/capybara/spec/session/choose_spec.rb +32 -18
- data/lib/capybara/spec/session/click_button_spec.rb +263 -232
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +40 -29
- data/lib/capybara/spec/session/click_link_spec.rb +96 -96
- data/lib/capybara/spec/session/current_url_spec.rb +88 -10
- data/lib/capybara/spec/session/evaluate_script_spec.rb +6 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +7 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +119 -103
- data/lib/capybara/spec/session/find_button_spec.rb +16 -14
- data/lib/capybara/spec/session/find_by_id_spec.rb +16 -14
- data/lib/capybara/spec/session/find_field_spec.rb +23 -21
- data/lib/capybara/spec/session/find_link_spec.rb +15 -14
- data/lib/capybara/spec/session/find_spec.rb +93 -115
- data/lib/capybara/spec/session/first_spec.rb +51 -85
- data/lib/capybara/spec/session/has_button_spec.rb +22 -24
- data/lib/capybara/spec/session/has_css_spec.rb +190 -205
- data/lib/capybara/spec/session/has_field_spec.rb +170 -144
- data/lib/capybara/spec/session/has_link_spec.rb +26 -29
- data/lib/capybara/spec/session/has_select_spec.rb +161 -109
- data/lib/capybara/spec/session/has_selector_spec.rb +94 -100
- data/lib/capybara/spec/session/has_table_spec.rb +22 -88
- data/lib/capybara/spec/session/has_text_spec.rb +195 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +100 -96
- data/lib/capybara/spec/session/headers.rb +4 -17
- data/lib/capybara/spec/session/html_spec.rb +15 -0
- data/lib/capybara/spec/session/node_spec.rb +205 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +42 -0
- data/lib/capybara/spec/session/response_code.rb +4 -17
- data/lib/capybara/spec/session/save_page_spec.rb +46 -0
- data/lib/capybara/spec/session/screenshot.rb +13 -0
- data/lib/capybara/spec/session/select_spec.rb +99 -88
- data/lib/capybara/spec/session/source_spec.rb +12 -0
- data/lib/capybara/spec/session/text_spec.rb +15 -12
- data/lib/capybara/spec/session/uncheck_spec.rb +22 -17
- data/lib/capybara/spec/session/unselect_spec.rb +69 -58
- data/lib/capybara/spec/session/visit_spec.rb +74 -0
- data/lib/capybara/spec/session/within_frame_spec.rb +31 -0
- data/lib/capybara/spec/session/within_spec.rb +118 -131
- data/lib/capybara/spec/session/within_window_spec.rb +38 -0
- data/lib/capybara/spec/spec_helper.rb +84 -0
- data/lib/capybara/spec/test_app.rb +32 -6
- data/lib/capybara/spec/views/form.erb +12 -10
- data/lib/capybara/spec/views/host_links.erb +2 -2
- data/lib/capybara/spec/views/tables.erb +6 -66
- data/lib/capybara/spec/views/with_html.erb +9 -4
- data/lib/capybara/spec/views/with_js.erb +11 -7
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara.rb +125 -6
- data/spec/basic_node_spec.rb +17 -5
- data/spec/capybara_spec.rb +9 -0
- data/spec/dsl_spec.rb +31 -17
- data/spec/rack_test_spec.rb +157 -0
- data/spec/result_spec.rb +51 -0
- data/spec/rspec/features_spec.rb +19 -2
- data/spec/rspec/matchers_spec.rb +170 -89
- data/spec/rspec_spec.rb +1 -3
- data/spec/selenium_spec.rb +53 -0
- data/spec/server_spec.rb +37 -25
- data/spec/spec_helper.rb +1 -30
- metadata +39 -31
- data/README.rdoc +0 -722
- data/lib/capybara/spec/driver.rb +0 -301
- data/lib/capybara/spec/session/current_host_spec.rb +0 -68
- data/lib/capybara/spec/session/has_content_spec.rb +0 -106
- data/lib/capybara/spec/session/javascript.rb +0 -306
- data/lib/capybara/spec/session.rb +0 -154
- data/lib/capybara/util/save_and_open_page.rb +0 -44
- data/lib/capybara/util/timeout.rb +0 -27
- data/spec/driver/rack_test_driver_spec.rb +0 -89
- data/spec/driver/selenium_driver_spec.rb +0 -37
- data/spec/save_and_open_page_spec.rb +0 -155
- data/spec/session/rack_test_session_spec.rb +0 -55
- data/spec/session/selenium_session_spec.rb +0 -26
- data/spec/string_spec.rb +0 -77
- data/spec/timeout_spec.rb +0 -28
data/lib/capybara/cucumber.rb
CHANGED
|
@@ -6,8 +6,9 @@ require 'capybara/rspec/matchers'
|
|
|
6
6
|
World(Capybara::DSL)
|
|
7
7
|
World(Capybara::RSpecMatchers)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Before do
|
|
10
10
|
Capybara.reset_sessions!
|
|
11
|
+
Capybara.use_default_driver
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
Before '@javascript' do
|
|
@@ -22,7 +23,3 @@ Before do |scenario|
|
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
|
-
|
|
26
|
-
After do
|
|
27
|
-
Capybara.use_default_driver
|
|
28
|
-
end
|
data/lib/capybara/driver/base.rb
CHANGED
|
@@ -15,7 +15,7 @@ class Capybara::Driver::Base
|
|
|
15
15
|
raise NotImplementedError
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def
|
|
18
|
+
def html
|
|
19
19
|
raise NotImplementedError
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -27,6 +27,10 @@ class Capybara::Driver::Base
|
|
|
27
27
|
raise Capybara::NotSupportedByDriverError
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def save_screenshot(path, options={})
|
|
31
|
+
raise Capybara::NotSupportedByDriverError
|
|
32
|
+
end
|
|
33
|
+
|
|
30
34
|
def response_headers
|
|
31
35
|
raise Capybara::NotSupportedByDriverError
|
|
32
36
|
end
|
|
@@ -51,14 +55,10 @@ class Capybara::Driver::Base
|
|
|
51
55
|
false
|
|
52
56
|
end
|
|
53
57
|
|
|
54
|
-
def wait_until(*args)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
58
|
def reset!
|
|
58
59
|
end
|
|
59
60
|
|
|
60
|
-
def
|
|
61
|
+
def needs_server?
|
|
61
62
|
false
|
|
62
63
|
end
|
|
63
|
-
|
|
64
64
|
end
|
data/lib/capybara/driver/node.rb
CHANGED
|
@@ -20,6 +20,7 @@ module Capybara
|
|
|
20
20
|
raise NotImplementedError
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# @param value String or Array. Array is only allowed if node has 'multiple' attribute
|
|
23
24
|
def set(value)
|
|
24
25
|
raise NotImplementedError
|
|
25
26
|
end
|
|
@@ -65,9 +66,9 @@ module Capybara
|
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
def inspect
|
|
68
|
-
%(
|
|
69
|
+
%(#<#{self.class} tag="#{tag_name}" path="#{path}">)
|
|
69
70
|
rescue NotSupportedByDriverError
|
|
70
|
-
%(
|
|
71
|
+
%(#<#{self.class} tag="#{tag_name}">)
|
|
71
72
|
end
|
|
72
73
|
end
|
|
73
74
|
end
|
data/lib/capybara/dsl.rb
CHANGED
|
@@ -1,127 +1,20 @@
|
|
|
1
1
|
require 'capybara'
|
|
2
2
|
|
|
3
3
|
module Capybara
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class << self
|
|
10
|
-
attr_writer :default_driver, :current_driver, :javascript_driver, :session_name
|
|
11
|
-
|
|
12
|
-
attr_accessor :app
|
|
13
|
-
|
|
14
|
-
##
|
|
15
|
-
#
|
|
16
|
-
# @return [Symbol] The name of the driver to use by default
|
|
17
|
-
#
|
|
18
|
-
def default_driver
|
|
19
|
-
@default_driver || :rack_test
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
##
|
|
23
|
-
#
|
|
24
|
-
# @return [Symbol] The name of the driver currently in use
|
|
25
|
-
#
|
|
26
|
-
def current_driver
|
|
27
|
-
@current_driver || default_driver
|
|
28
|
-
end
|
|
29
|
-
alias_method :mode, :current_driver
|
|
30
|
-
|
|
31
|
-
##
|
|
32
|
-
#
|
|
33
|
-
# @return [Symbol] The name of the driver used when JavaScript is needed
|
|
34
|
-
#
|
|
35
|
-
def javascript_driver
|
|
36
|
-
@javascript_driver || :selenium
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
##
|
|
40
|
-
#
|
|
41
|
-
# Use the default driver as the current driver
|
|
42
|
-
#
|
|
43
|
-
def use_default_driver
|
|
44
|
-
@current_driver = nil
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
##
|
|
48
|
-
#
|
|
49
|
-
# Yield a block using a specific driver
|
|
50
|
-
#
|
|
51
|
-
def using_driver(driver)
|
|
52
|
-
previous_driver = Capybara.current_driver
|
|
53
|
-
Capybara.current_driver = driver
|
|
54
|
-
yield
|
|
55
|
-
ensure
|
|
56
|
-
@current_driver = previous_driver
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
##
|
|
60
|
-
#
|
|
61
|
-
# Yield a block using a specific wait time
|
|
62
|
-
#
|
|
63
|
-
def using_wait_time(seconds)
|
|
64
|
-
previous_wait_time = Capybara.default_wait_time
|
|
65
|
-
Capybara.default_wait_time = seconds
|
|
66
|
-
yield
|
|
67
|
-
ensure
|
|
68
|
-
Capybara.default_wait_time = previous_wait_time
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
##
|
|
72
|
-
#
|
|
73
|
-
# The current Capybara::Session base on what is set as Capybara.app and Capybara.current_driver
|
|
74
|
-
#
|
|
75
|
-
# @return [Capybara::Session] The currently used session
|
|
76
|
-
#
|
|
77
|
-
def current_session
|
|
78
|
-
session_pool["#{current_driver}:#{session_name}:#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
##
|
|
82
|
-
#
|
|
83
|
-
# Reset sessions, cleaning out the pool of sessions. This will remove any session information such
|
|
84
|
-
# as cookies.
|
|
85
|
-
#
|
|
86
|
-
def reset_sessions!
|
|
87
|
-
session_pool.each { |mode, session| session.reset! }
|
|
88
|
-
end
|
|
89
|
-
alias_method :reset!, :reset_sessions!
|
|
90
|
-
|
|
91
|
-
##
|
|
92
|
-
#
|
|
93
|
-
# The current session name.
|
|
94
|
-
#
|
|
95
|
-
# @return [Symbol] The name of the currently used session.
|
|
96
|
-
#
|
|
97
|
-
def session_name
|
|
98
|
-
@session_name ||= :default
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
##
|
|
102
|
-
#
|
|
103
|
-
# Yield a block using a specific session name.
|
|
104
|
-
#
|
|
105
|
-
def using_session(name)
|
|
106
|
-
self.session_name = name
|
|
107
|
-
yield
|
|
108
|
-
ensure
|
|
109
|
-
self.session_name = :default
|
|
4
|
+
module DSL
|
|
5
|
+
def self.included(base)
|
|
6
|
+
warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
|
|
7
|
+
super
|
|
110
8
|
end
|
|
111
9
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
@session_pool ||= {}
|
|
10
|
+
def self.extended(base)
|
|
11
|
+
warn "extending the main object with Capybara::DSL is not recommended!" if base == TOPLEVEL_BINDING.eval("self")
|
|
12
|
+
super
|
|
116
13
|
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
module DSL
|
|
120
14
|
|
|
121
15
|
##
|
|
122
16
|
#
|
|
123
|
-
# Shortcut to working in a different session.
|
|
124
|
-
# in a class or module.
|
|
17
|
+
# Shortcut to working in a different session.
|
|
125
18
|
#
|
|
126
19
|
def using_session(name, &block)
|
|
127
20
|
Capybara.using_session(name, &block)
|
|
@@ -129,8 +22,7 @@ module Capybara
|
|
|
129
22
|
|
|
130
23
|
##
|
|
131
24
|
#
|
|
132
|
-
# Shortcut to
|
|
133
|
-
# in a class or module.
|
|
25
|
+
# Shortcut to using a different wait time.
|
|
134
26
|
#
|
|
135
27
|
def using_wait_time(seconds, &block)
|
|
136
28
|
Capybara.using_wait_time(seconds, &block)
|
|
@@ -138,8 +30,7 @@ module Capybara
|
|
|
138
30
|
|
|
139
31
|
##
|
|
140
32
|
#
|
|
141
|
-
# Shortcut to accessing the current session.
|
|
142
|
-
# class or module.
|
|
33
|
+
# Shortcut to accessing the current session.
|
|
143
34
|
#
|
|
144
35
|
# class MyClass
|
|
145
36
|
# include Capybara::DSL
|
|
@@ -156,11 +47,9 @@ module Capybara
|
|
|
156
47
|
end
|
|
157
48
|
|
|
158
49
|
Session::DSL_METHODS.each do |method|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
end
|
|
163
|
-
RUBY
|
|
50
|
+
define_method method do |*args, &block|
|
|
51
|
+
page.send method, *args, &block
|
|
52
|
+
end
|
|
164
53
|
end
|
|
165
54
|
end
|
|
166
55
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Capybara
|
|
2
|
+
module Helpers
|
|
3
|
+
class << self
|
|
4
|
+
##
|
|
5
|
+
#
|
|
6
|
+
# Normalizes whitespace space by stripping leading and trailing
|
|
7
|
+
# whitespace and replacing sequences of whitespace characters
|
|
8
|
+
# with a single space.
|
|
9
|
+
#
|
|
10
|
+
# @param [String] text Text to normalize
|
|
11
|
+
# @return [String] Normalized text
|
|
12
|
+
#
|
|
13
|
+
def normalize_whitespace(text)
|
|
14
|
+
# http://en.wikipedia.org/wiki/Whitespace_character#Unicode
|
|
15
|
+
# We should have a better reference.
|
|
16
|
+
# See also http://stackoverflow.com/a/11758133/525872
|
|
17
|
+
text.to_s.gsub(/[\s\u0085\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+/, ' ').strip
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
#
|
|
22
|
+
# Escapes any characters that would have special meaning in a regexp
|
|
23
|
+
# if text is not a regexp
|
|
24
|
+
#
|
|
25
|
+
# @param [String] text Text to escape
|
|
26
|
+
# @return [String] Escaped text
|
|
27
|
+
#
|
|
28
|
+
def to_regexp(text)
|
|
29
|
+
text.is_a?(Regexp) ? text : Regexp.escape(normalize_whitespace(text))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -10,8 +10,7 @@ module Capybara
|
|
|
10
10
|
# @param [String] locator Text, id or value of link or button
|
|
11
11
|
#
|
|
12
12
|
def click_link_or_button(locator)
|
|
13
|
-
|
|
14
|
-
find(:xpath, XPath::HTML.link_or_button(locator), :message => msg).click
|
|
13
|
+
find(:link_or_button, locator).click
|
|
15
14
|
end
|
|
16
15
|
alias_method :click_on, :click_link_or_button
|
|
17
16
|
|
|
@@ -23,8 +22,7 @@ module Capybara
|
|
|
23
22
|
# @param [String] locator Text, id or text of link
|
|
24
23
|
#
|
|
25
24
|
def click_link(locator)
|
|
26
|
-
|
|
27
|
-
find(:xpath, XPath::HTML.link(locator), :message => msg).click
|
|
25
|
+
find(:link, locator).click
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
##
|
|
@@ -34,8 +32,7 @@ module Capybara
|
|
|
34
32
|
# @param [String] locator Text, id or value of button
|
|
35
33
|
#
|
|
36
34
|
def click_button(locator)
|
|
37
|
-
|
|
38
|
-
find(:xpath, XPath::HTML.button(locator), :message => msg).click
|
|
35
|
+
find(:button, locator).click
|
|
39
36
|
end
|
|
40
37
|
|
|
41
38
|
##
|
|
@@ -49,9 +46,8 @@ module Capybara
|
|
|
49
46
|
# @param [Hash{:with => String}] The value to fill in
|
|
50
47
|
#
|
|
51
48
|
def fill_in(locator, options={})
|
|
52
|
-
msg = "cannot fill in, no text field, text area or password field with id, name, or label '#{locator}' found"
|
|
53
49
|
raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
|
|
54
|
-
find(:
|
|
50
|
+
find(:fillable_field, locator).set(options[:with])
|
|
55
51
|
end
|
|
56
52
|
|
|
57
53
|
##
|
|
@@ -64,8 +60,7 @@ module Capybara
|
|
|
64
60
|
# @param [String] locator Which radio button to choose
|
|
65
61
|
#
|
|
66
62
|
def choose(locator)
|
|
67
|
-
|
|
68
|
-
find(:xpath, XPath::HTML.radio_button(locator), :message => msg).set(true)
|
|
63
|
+
find(:radio_button, locator).set(true)
|
|
69
64
|
end
|
|
70
65
|
|
|
71
66
|
##
|
|
@@ -78,8 +73,7 @@ module Capybara
|
|
|
78
73
|
# @param [String] locator Which check box to check
|
|
79
74
|
#
|
|
80
75
|
def check(locator)
|
|
81
|
-
|
|
82
|
-
find(:xpath, XPath::HTML.checkbox(locator), :message => msg).set(true)
|
|
76
|
+
find(:checkbox, locator).set(true)
|
|
83
77
|
end
|
|
84
78
|
|
|
85
79
|
##
|
|
@@ -92,8 +86,7 @@ module Capybara
|
|
|
92
86
|
# @param [String] locator Which check box to uncheck
|
|
93
87
|
#
|
|
94
88
|
def uncheck(locator)
|
|
95
|
-
|
|
96
|
-
find(:xpath, XPath::HTML.checkbox(locator), :message => msg).set(false)
|
|
89
|
+
find(:checkbox, locator).set(false)
|
|
97
90
|
end
|
|
98
91
|
|
|
99
92
|
##
|
|
@@ -109,13 +102,9 @@ module Capybara
|
|
|
109
102
|
#
|
|
110
103
|
def select(value, options={})
|
|
111
104
|
if options.has_key?(:from)
|
|
112
|
-
|
|
113
|
-
no_option_msg = "cannot select option, no option with text '#{value}' in select box '#{options[:from]}'"
|
|
114
|
-
select = find(:xpath, XPath::HTML.select(options[:from]), :message => no_select_msg)
|
|
115
|
-
select.find(:xpath, XPath::HTML.option(value), :message => no_option_msg).select_option
|
|
105
|
+
find(:select, options[:from]).find(:option, value).select_option
|
|
116
106
|
else
|
|
117
|
-
|
|
118
|
-
find(:xpath, XPath::HTML.option(value), :message => no_option_msg).select_option
|
|
107
|
+
find(:option, value).select_option
|
|
119
108
|
end
|
|
120
109
|
end
|
|
121
110
|
|
|
@@ -132,13 +121,9 @@ module Capybara
|
|
|
132
121
|
#
|
|
133
122
|
def unselect(value, options={})
|
|
134
123
|
if options.has_key?(:from)
|
|
135
|
-
|
|
136
|
-
no_option_msg = "cannot unselect option, no option with text '#{value}' in select box '#{options[:from]}'"
|
|
137
|
-
select = find(:xpath, XPath::HTML.select(options[:from]), :message => no_select_msg)
|
|
138
|
-
select.find(:xpath, XPath::HTML.option(value), :message => no_option_msg).unselect_option
|
|
124
|
+
find(:select, options[:from]).find(:option, value).unselect_option
|
|
139
125
|
else
|
|
140
|
-
|
|
141
|
-
find(:xpath, XPath::HTML.option(value), :message => no_option_msg).unselect_option
|
|
126
|
+
find(:option, value).unselect_option
|
|
142
127
|
end
|
|
143
128
|
end
|
|
144
129
|
|
|
@@ -150,12 +135,13 @@ module Capybara
|
|
|
150
135
|
# page.attach_file(locator, '/path/to/file.png')
|
|
151
136
|
#
|
|
152
137
|
# @param [String] locator Which field to attach the file to
|
|
153
|
-
# @param [String] path The path of the file that will be attached
|
|
138
|
+
# @param [String] path The path of the file that will be attached, or an array of paths
|
|
154
139
|
#
|
|
155
140
|
def attach_file(locator, path)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
141
|
+
(String === path ? [path] : path).each do |p|
|
|
142
|
+
raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
|
|
143
|
+
end
|
|
144
|
+
find(:file_field, locator).set(path)
|
|
159
145
|
end
|
|
160
146
|
end
|
|
161
147
|
end
|
data/lib/capybara/node/base.rb
CHANGED
|
@@ -33,29 +33,52 @@ module Capybara
|
|
|
33
33
|
@base = base
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# overridden in subclasses, e.g. Capybara::Node::Element
|
|
36
37
|
def reload
|
|
37
38
|
self
|
|
38
39
|
end
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
##
|
|
42
|
+
#
|
|
43
|
+
# This method is Capybara's primary defence agains asynchronicity
|
|
44
|
+
# problems. It works by attempting to run a given block of code until it
|
|
45
|
+
# succeeds. The exact behaviour of this method depends on a number of
|
|
46
|
+
# factors. Basically there are certain exceptions which, when raised
|
|
47
|
+
# from the block, instead of bubbling up, are caught, and the block is
|
|
48
|
+
# re-run.
|
|
49
|
+
#
|
|
50
|
+
# Certain drivers, such as RackTest, have no support for aynchronous
|
|
51
|
+
# processes, these drivers run the block, and any error raised bubbles up
|
|
52
|
+
# immediately. This allows faster turn around in the case where an
|
|
53
|
+
# expectation fails.
|
|
54
|
+
#
|
|
55
|
+
# Only exceptions that are {Capybara::ElementNotFound} or any subclass
|
|
56
|
+
# thereof cause the block to be rerun. Drivers may specify additional
|
|
57
|
+
# exceptions which also cause reruns. This usually occurs when a node is
|
|
58
|
+
# manipulated which no longer exists on the page. For example, the
|
|
59
|
+
# Selenium driver specifies
|
|
60
|
+
# `Selenium::WebDriver::Error::ObsoleteElementError`.
|
|
61
|
+
#
|
|
62
|
+
# As long as any of these exceptions are thrown, the block is re-run,
|
|
63
|
+
# until a certain amount of time passes. The amount of time defaults to
|
|
64
|
+
# {Capybara.default_wait_time} and can be overriden through the `seconds`
|
|
65
|
+
# argument. This time is compared with the system time to see how much
|
|
66
|
+
# time has passed. If the return value of {Time.now} is stubbed out,
|
|
67
|
+
# Capybara will raise `Capybara::FrozenInTime`.
|
|
68
|
+
#
|
|
69
|
+
# @param [Integer] seconds Number of seconds to retry this block
|
|
70
|
+
# @return [Object] The result of the given block
|
|
71
|
+
# @raise [Capybara::FrozenInTime] If the return value of {Time.now} appears stuck
|
|
72
|
+
#
|
|
73
|
+
def synchronize(seconds=Capybara.default_wait_time)
|
|
51
74
|
start_time = Time.now
|
|
52
75
|
|
|
53
76
|
begin
|
|
54
77
|
yield
|
|
55
78
|
rescue => e
|
|
56
|
-
raise e if @
|
|
79
|
+
raise e if @unsynchronized
|
|
57
80
|
raise e unless driver.wait?
|
|
58
|
-
raise e unless
|
|
81
|
+
raise e unless driver.invalid_element_errors.include?(e.class) || e.is_a?(Capybara::ElementNotFound)
|
|
59
82
|
raise e if (Time.now - start_time) >= seconds
|
|
60
83
|
sleep(0.05)
|
|
61
84
|
raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Time.now == start_time
|
|
@@ -64,6 +87,26 @@ module Capybara
|
|
|
64
87
|
end
|
|
65
88
|
end
|
|
66
89
|
|
|
90
|
+
##
|
|
91
|
+
#
|
|
92
|
+
# Within the given block, prevent synchronize from having any effect.
|
|
93
|
+
#
|
|
94
|
+
# This is an internal method which should not be called unless you are
|
|
95
|
+
# absolutely sure of what you're doing.
|
|
96
|
+
#
|
|
97
|
+
# @api private
|
|
98
|
+
# @return [Object] The result of the given block
|
|
99
|
+
#
|
|
100
|
+
def unsynchronized
|
|
101
|
+
orig = @unsynchronized
|
|
102
|
+
@unsynchronized = true
|
|
103
|
+
yield
|
|
104
|
+
ensure
|
|
105
|
+
@unsynchronized = orig
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
protected
|
|
109
|
+
|
|
67
110
|
def driver
|
|
68
111
|
session.driver
|
|
69
112
|
end
|
|
@@ -22,10 +22,10 @@ module Capybara
|
|
|
22
22
|
#
|
|
23
23
|
class Element < Base
|
|
24
24
|
|
|
25
|
-
def initialize(session, base, parent,
|
|
25
|
+
def initialize(session, base, parent, query)
|
|
26
26
|
super(session, base)
|
|
27
27
|
@parent = parent
|
|
28
|
-
@
|
|
28
|
+
@query = query
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def allow_reload!
|
|
@@ -37,7 +37,7 @@ module Capybara
|
|
|
37
37
|
# @return [Object] The native element from the driver, this allows access to driver specific methods
|
|
38
38
|
#
|
|
39
39
|
def native
|
|
40
|
-
|
|
40
|
+
synchronize { base.native }
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
##
|
|
@@ -45,7 +45,7 @@ module Capybara
|
|
|
45
45
|
# @return [String] The text of the element
|
|
46
46
|
#
|
|
47
47
|
def text
|
|
48
|
-
|
|
48
|
+
synchronize { base.text }
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
##
|
|
@@ -58,7 +58,7 @@ module Capybara
|
|
|
58
58
|
# @return [String] The value of the attribute
|
|
59
59
|
#
|
|
60
60
|
def [](attribute)
|
|
61
|
-
|
|
61
|
+
synchronize { base[attribute] }
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
##
|
|
@@ -66,7 +66,7 @@ module Capybara
|
|
|
66
66
|
# @return [String] The value of the form element
|
|
67
67
|
#
|
|
68
68
|
def value
|
|
69
|
-
|
|
69
|
+
synchronize { base.value }
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
##
|
|
@@ -76,7 +76,7 @@ module Capybara
|
|
|
76
76
|
# @param [String] value The new value
|
|
77
77
|
#
|
|
78
78
|
def set(value)
|
|
79
|
-
|
|
79
|
+
synchronize { base.set(value) }
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
##
|
|
@@ -84,7 +84,7 @@ module Capybara
|
|
|
84
84
|
# Select this node if is an option element inside a select tag
|
|
85
85
|
#
|
|
86
86
|
def select_option
|
|
87
|
-
|
|
87
|
+
synchronize { base.select_option }
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
##
|
|
@@ -92,7 +92,7 @@ module Capybara
|
|
|
92
92
|
# Unselect this node if is an option element inside a multiple select tag
|
|
93
93
|
#
|
|
94
94
|
def unselect_option
|
|
95
|
-
|
|
95
|
+
synchronize { base.unselect_option }
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
##
|
|
@@ -100,7 +100,7 @@ module Capybara
|
|
|
100
100
|
# Click the Element
|
|
101
101
|
#
|
|
102
102
|
def click
|
|
103
|
-
|
|
103
|
+
synchronize { base.click }
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
##
|
|
@@ -108,7 +108,7 @@ module Capybara
|
|
|
108
108
|
# @return [String] The tag name of the element
|
|
109
109
|
#
|
|
110
110
|
def tag_name
|
|
111
|
-
|
|
111
|
+
synchronize { base.tag_name }
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
##
|
|
@@ -119,7 +119,7 @@ module Capybara
|
|
|
119
119
|
# @return [Boolean] Whether the element is visible
|
|
120
120
|
#
|
|
121
121
|
def visible?
|
|
122
|
-
|
|
122
|
+
synchronize { base.visible? }
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
##
|
|
@@ -129,7 +129,7 @@ module Capybara
|
|
|
129
129
|
# @return [Boolean] Whether the element is checked
|
|
130
130
|
#
|
|
131
131
|
def checked?
|
|
132
|
-
|
|
132
|
+
synchronize { base.checked? }
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
##
|
|
@@ -139,7 +139,7 @@ module Capybara
|
|
|
139
139
|
# @return [Boolean] Whether the element is selected
|
|
140
140
|
#
|
|
141
141
|
def selected?
|
|
142
|
-
|
|
142
|
+
synchronize { base.selected? }
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
##
|
|
@@ -149,7 +149,7 @@ module Capybara
|
|
|
149
149
|
# @return [String] An XPath expression
|
|
150
150
|
#
|
|
151
151
|
def path
|
|
152
|
-
|
|
152
|
+
synchronize { base.path }
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
##
|
|
@@ -160,7 +160,7 @@ module Capybara
|
|
|
160
160
|
# @param [String] event The name of the event to trigger
|
|
161
161
|
#
|
|
162
162
|
def trigger(event)
|
|
163
|
-
|
|
163
|
+
synchronize { base.trigger(event) }
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
##
|
|
@@ -174,24 +174,12 @@ module Capybara
|
|
|
174
174
|
# @param [Capybara::Element] node The element to drag to
|
|
175
175
|
#
|
|
176
176
|
def drag_to(node)
|
|
177
|
-
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def find(*args)
|
|
181
|
-
wait_until { super }
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def first(*args)
|
|
185
|
-
wait_until { super }
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def all(*args)
|
|
189
|
-
wait_until { super }
|
|
177
|
+
synchronize { base.drag_to(node.base) }
|
|
190
178
|
end
|
|
191
179
|
|
|
192
180
|
def reload
|
|
193
181
|
if @allow_reload
|
|
194
|
-
reloaded = parent.reload.first(@
|
|
182
|
+
reloaded = parent.reload.first(@query.name, @query.locator, @query.options)
|
|
195
183
|
@base = reloaded.base if reloaded
|
|
196
184
|
end
|
|
197
185
|
self
|