capybara 0.3.9 → 0.4.0.rc
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 +43 -1
- data/README.rdoc +168 -98
- data/lib/capybara.rb +77 -15
- data/lib/capybara/driver/base.rb +21 -16
- data/lib/capybara/driver/celerity_driver.rb +39 -41
- data/lib/capybara/driver/culerity_driver.rb +2 -1
- data/lib/capybara/driver/node.rb +66 -0
- data/lib/capybara/driver/rack_test_driver.rb +66 -67
- data/lib/capybara/driver/selenium_driver.rb +43 -47
- data/lib/capybara/dsl.rb +44 -6
- data/lib/capybara/node.rb +185 -24
- data/lib/capybara/node/actions.rb +170 -0
- data/lib/capybara/node/finders.rb +150 -0
- data/lib/capybara/node/matchers.rb +360 -0
- data/lib/capybara/rails.rb +1 -0
- data/lib/capybara/selector.rb +52 -0
- data/lib/capybara/server.rb +68 -87
- data/lib/capybara/session.rb +221 -207
- data/lib/capybara/spec/driver.rb +45 -35
- data/lib/capybara/spec/public/test.js +1 -1
- data/lib/capybara/spec/session.rb +28 -53
- data/lib/capybara/spec/session/all_spec.rb +7 -3
- data/lib/capybara/spec/session/check_spec.rb +50 -52
- data/lib/capybara/spec/session/click_button_spec.rb +9 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +37 -0
- data/lib/capybara/spec/session/current_url_spec.rb +7 -0
- data/lib/capybara/spec/session/find_button_spec.rb +4 -2
- data/lib/capybara/spec/session/find_by_id_spec.rb +4 -2
- data/lib/capybara/spec/session/find_field_spec.rb +7 -3
- data/lib/capybara/spec/session/find_link_spec.rb +5 -3
- data/lib/capybara/spec/session/find_spec.rb +71 -6
- data/lib/capybara/spec/session/has_field_spec.rb +1 -1
- data/lib/capybara/spec/session/has_selector_spec.rb +129 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +4 -4
- data/lib/capybara/spec/session/javascript.rb +25 -5
- data/lib/capybara/spec/session/select_spec.rb +16 -2
- data/lib/capybara/spec/session/unselect_spec.rb +8 -1
- data/lib/capybara/spec/session/within_spec.rb +5 -5
- data/lib/capybara/spec/views/form.erb +65 -1
- data/lib/capybara/spec/views/popup_one.erb +8 -0
- data/lib/capybara/spec/views/popup_two.erb +8 -0
- data/lib/capybara/spec/views/with_html.erb +5 -0
- data/lib/capybara/spec/views/within_popups.erb +25 -0
- data/lib/capybara/{save_and_open_page.rb → util/save_and_open_page.rb} +3 -3
- data/lib/capybara/util/timeout.rb +27 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/capybara_spec.rb +18 -8
- data/spec/driver/celerity_driver_spec.rb +10 -14
- data/spec/driver/culerity_driver_spec.rb +4 -3
- data/spec/driver/rack_test_driver_spec.rb +39 -2
- data/spec/driver/remote_culerity_driver_spec.rb +5 -7
- data/spec/driver/remote_selenium_driver_spec.rb +7 -10
- data/spec/driver/selenium_driver_spec.rb +3 -2
- data/spec/dsl_spec.rb +5 -14
- data/spec/save_and_open_page_spec.rb +19 -19
- data/spec/server_spec.rb +22 -10
- data/spec/session/celerity_session_spec.rb +17 -21
- data/spec/session/culerity_session_spec.rb +3 -3
- data/spec/session/rack_test_session_spec.rb +2 -2
- data/spec/session/selenium_session_spec.rb +2 -2
- data/spec/spec_helper.rb +27 -6
- data/spec/{wait_until_spec.rb → timeout_spec.rb} +14 -14
- metadata +88 -46
- data/lib/capybara/searchable.rb +0 -54
- data/lib/capybara/spec/session/click_spec.rb +0 -24
- data/lib/capybara/spec/session/locate_spec.rb +0 -65
- data/lib/capybara/wait_until.rb +0 -28
- data/lib/capybara/xpath.rb +0 -179
- data/spec/searchable_spec.rb +0 -66
- data/spec/xpath_spec.rb +0 -180
data/lib/capybara/rails.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Capybara
|
2
|
+
class Selector
|
3
|
+
attr_reader :name, :options, :block
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def all
|
7
|
+
@selectors ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(name, options={}, &block)
|
11
|
+
all[name.to_sym] = Capybara::Selector.new(name.to_sym, options, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove(name)
|
15
|
+
all.delete(name.to_sym)
|
16
|
+
end
|
17
|
+
|
18
|
+
def normalize(name_or_locator, locator=nil)
|
19
|
+
xpath = if locator
|
20
|
+
all[name_or_locator.to_sym].call(locator)
|
21
|
+
else
|
22
|
+
selector = all.values.find { |s| s.match?(name_or_locator) }
|
23
|
+
selector ||= all[Capybara.default_selector]
|
24
|
+
selector.call(name_or_locator)
|
25
|
+
end
|
26
|
+
if xpath.respond_to?(:to_xpaths)
|
27
|
+
xpath.to_xpaths
|
28
|
+
else
|
29
|
+
[xpath.to_s].flatten
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(name, options={}, &block)
|
35
|
+
@name = name
|
36
|
+
@options = options
|
37
|
+
@block = block
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(locator)
|
41
|
+
@block.call(locator)
|
42
|
+
end
|
43
|
+
|
44
|
+
def match?(locator)
|
45
|
+
@options[:for] and @options[:for] === locator
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Capybara::Selector.add(:xpath) { |xpath| xpath }
|
51
|
+
Capybara::Selector.add(:css) { |css| XPath.css(css) }
|
52
|
+
Capybara::Selector.add(:id, :for => Symbol) { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
|
data/lib/capybara/server.rb
CHANGED
@@ -1,114 +1,95 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'net/http'
|
3
3
|
require 'rack'
|
4
|
+
require 'capybara/util/timeout'
|
4
5
|
|
5
|
-
|
6
|
-
class
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
module Capybara
|
7
|
+
class Server
|
8
|
+
class Identify
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def call(env)
|
14
|
+
if env["PATH_INFO"] == "/__identify__"
|
15
|
+
[200, {}, @app.object_id.to_s]
|
16
|
+
else
|
17
|
+
@app.call(env)
|
18
|
+
end
|
16
19
|
end
|
17
20
|
end
|
18
|
-
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
class << self
|
23
|
+
def ports
|
24
|
+
@ports ||= {}
|
25
|
+
end
|
26
|
+
end
|
25
27
|
|
26
|
-
|
27
|
-
"localhost"
|
28
|
-
end
|
28
|
+
attr_reader :app, :port
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
path
|
33
|
-
else
|
34
|
-
(Capybara.app_host || "http://#{host}:#{port}") + path.to_s
|
30
|
+
def initialize(app)
|
31
|
+
@app = app
|
35
32
|
end
|
36
|
-
end
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
def host
|
35
|
+
"127.0.0.1"
|
36
|
+
end
|
41
37
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
begin
|
48
|
-
require 'rack/handler/mongrel'
|
49
|
-
Rack::Handler::Mongrel
|
50
|
-
rescue LoadError
|
51
|
-
require 'rack/handler/webrick'
|
52
|
-
Rack::Handler::WEBrick
|
38
|
+
def url(path)
|
39
|
+
if path =~ /^http/
|
40
|
+
path
|
41
|
+
else
|
42
|
+
(Capybara.app_host || "http://#{host}:#{port}") + path.to_s
|
53
43
|
end
|
54
44
|
end
|
55
|
-
end
|
56
45
|
|
57
|
-
|
58
|
-
|
59
|
-
find_available_port
|
60
|
-
Capybara.log "application has already booted" and return self if responsive?
|
61
|
-
Capybara.log "booting Rack applicartion on port #{port}"
|
46
|
+
def responsive?
|
47
|
+
res = Net::HTTP.start(host, @port) { |http| http.get('/__identify__') }
|
62
48
|
|
63
|
-
|
64
|
-
|
49
|
+
if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
|
50
|
+
return res.body == @app.object_id.to_s
|
51
|
+
end
|
52
|
+
rescue Errno::ECONNREFUSED, Errno::EBADF
|
53
|
+
return false
|
65
54
|
end
|
66
|
-
Capybara.log "checking if application has booted"
|
67
55
|
|
68
|
-
|
69
|
-
if
|
70
|
-
Capybara.
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
56
|
+
def boot
|
57
|
+
if @app
|
58
|
+
@port = Capybara::Server.ports[@app.object_id]
|
59
|
+
|
60
|
+
if not @port or not responsive?
|
61
|
+
@port = Capybara.server_port || find_available_port
|
62
|
+
Capybara::Server.ports[@app.object_id] = @port
|
63
|
+
|
64
|
+
Thread.new do
|
65
|
+
begin
|
66
|
+
require 'rack/handler/thin'
|
67
|
+
Thin::Logging.silent = true
|
68
|
+
Rack::Handler::Thin.run(Identify.new(@app), :Port => @port)
|
69
|
+
rescue LoadError
|
70
|
+
require 'rack/handler/webrick'
|
71
|
+
Rack::Handler::WEBrick.run(Identify.new(@app), :Port => @port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
Capybara.timeout(10) { if responsive? then true else sleep(0.5) and false end }
|
76
|
+
end
|
75
77
|
end
|
78
|
+
rescue Timeout::Error
|
79
|
+
puts "Rack application timed out during boot"
|
80
|
+
exit
|
81
|
+
else
|
82
|
+
self
|
76
83
|
end
|
77
|
-
self
|
78
|
-
rescue Timeout::Error
|
79
|
-
Capybara.log "Rack application timed out during boot"
|
80
|
-
exit
|
81
|
-
end
|
82
84
|
|
83
|
-
private
|
85
|
+
private
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def is_running_on_port?(tested_port)
|
91
|
-
res = Net::HTTP.start(host, tested_port) { |http| http.get('/__identify__') }
|
92
|
-
|
93
|
-
if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
|
94
|
-
return res.body == @app.object_id.to_s
|
87
|
+
def find_available_port
|
88
|
+
server = TCPServer.new('127.0.0.1', 0)
|
89
|
+
server.addr[1]
|
90
|
+
ensure
|
91
|
+
server.close if server
|
95
92
|
end
|
96
|
-
rescue Errno::ECONNREFUSED, Errno::EBADF
|
97
|
-
return false
|
98
|
-
end
|
99
93
|
|
100
|
-
def is_port_open?(tested_port)
|
101
|
-
Timeout::timeout(1) do
|
102
|
-
begin
|
103
|
-
s = TCPSocket.new(host, tested_port)
|
104
|
-
s.close
|
105
|
-
return true
|
106
|
-
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
107
|
-
return false
|
108
|
-
end
|
109
|
-
end
|
110
|
-
rescue Timeout::Error
|
111
|
-
return false
|
112
94
|
end
|
113
|
-
|
114
95
|
end
|
data/lib/capybara/session.rb
CHANGED
@@ -1,18 +1,38 @@
|
|
1
|
-
require '
|
2
|
-
require 'capybara/wait_until'
|
1
|
+
require 'capybara/util/timeout'
|
3
2
|
|
4
3
|
module Capybara
|
5
|
-
class Session
|
6
|
-
extend Forwardable
|
7
|
-
include Searchable
|
8
4
|
|
5
|
+
##
|
6
|
+
#
|
7
|
+
# The Session class represents a single user's interaction with the system. The Session can use
|
8
|
+
# any of the underlying drivers. A session can be initialized manually like this:
|
9
|
+
#
|
10
|
+
# session = Capybara::Session.new(:culerity, MyRackApp)
|
11
|
+
#
|
12
|
+
# The application given as the second argument is optional. When running Capybara against an external
|
13
|
+
# page, you might want to leave it out:
|
14
|
+
#
|
15
|
+
# session = Capybara::Session.new(:culerity)
|
16
|
+
# session.visit('http://www.google.com')
|
17
|
+
#
|
18
|
+
# Session provides a number of methods for controlling the navigation of the page, such as +visit+,
|
19
|
+
# +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
|
20
|
+
# the current HTML document. This allows interaction:
|
21
|
+
#
|
22
|
+
# session.fill_in('q', :with => 'Capybara')
|
23
|
+
# session.click_button('Search')
|
24
|
+
# session.should have_content('Capybara')
|
25
|
+
#
|
26
|
+
# When using capybara/dsl, the Session is initialized automatically for you.
|
27
|
+
#
|
28
|
+
class Session
|
9
29
|
DSL_METHODS = [
|
10
|
-
:all, :attach_file, :body, :check, :choose, :
|
30
|
+
:all, :attach_file, :body, :check, :choose, :click_link_or_button, :click_button, :click_link, :current_url, :drag, :evaluate_script,
|
11
31
|
:field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_css?,
|
12
32
|
:has_no_content?, :has_no_css?, :has_no_xpath?, :has_xpath?, :locate, :save_and_open_page, :select, :source, :uncheck,
|
13
|
-
:visit, :wait_until, :within, :within_fieldset, :within_table, :within_frame, :has_link?, :has_no_link?, :has_button?,
|
14
|
-
:has_no_button?,
|
15
|
-
:unselect, :has_select?, :has_no_select?, :current_path, :scope_to
|
33
|
+
:visit, :wait_until, :within, :within_fieldset, :within_table, :within_frame, :within_window, :has_link?, :has_no_link?, :has_button?,
|
34
|
+
:has_no_button?, :has_field?, :has_no_field?, :has_checked_field?, :has_unchecked_field?, :has_no_table?, :has_table?,
|
35
|
+
:unselect, :has_select?, :has_no_select?, :current_path, :scope_to, :click
|
16
36
|
]
|
17
37
|
|
18
38
|
attr_reader :mode, :app
|
@@ -23,254 +43,248 @@ module Capybara
|
|
23
43
|
end
|
24
44
|
|
25
45
|
def driver
|
26
|
-
@driver ||= begin
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
46
|
+
@driver ||= begin
|
47
|
+
unless Capybara.drivers.has_key?(mode)
|
48
|
+
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
|
49
|
+
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
50
|
+
end
|
51
|
+
Capybara.drivers[mode].call(app)
|
32
52
|
end
|
33
53
|
end
|
34
54
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
55
|
+
##
|
56
|
+
#
|
57
|
+
# Reset the session, removing all cookies.
|
58
|
+
#
|
59
|
+
def reset!
|
60
|
+
driver.reset!
|
61
|
+
end
|
62
|
+
alias_method :cleanup!, :reset!
|
63
|
+
|
64
|
+
##
|
65
|
+
#
|
66
|
+
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
|
67
|
+
#
|
68
|
+
# @return [Hash{String => String}] A hash of response headers.
|
69
|
+
#
|
70
|
+
def response_headers
|
71
|
+
driver.response_headers
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
#
|
76
|
+
# Returns the current HTTP status code as an Integer. Not supported by all drivers (e.g. Selenium)
|
77
|
+
#
|
78
|
+
# @return [Integer] Current HTTP status code
|
79
|
+
#
|
80
|
+
def status_code
|
81
|
+
driver.status_code
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
#
|
86
|
+
# @return [String] A snapshot of the HTML of the current document, as it looks right now
|
87
|
+
#
|
88
|
+
def body
|
89
|
+
driver.body
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
#
|
94
|
+
# @return [String] HTML source of the document, before being modified by JavaScript.
|
95
|
+
#
|
96
|
+
def source
|
97
|
+
driver.source
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
#
|
102
|
+
# @return [String] Path of the current page, without any domain information
|
103
|
+
#
|
104
|
+
def current_path
|
105
|
+
URI.parse(current_url).path
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
#
|
110
|
+
# @return [String] Fully qualified URL of the current page
|
111
|
+
#
|
112
|
+
def current_url
|
113
|
+
driver.current_url
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
#
|
118
|
+
# Navigate to the given URL. The URL can either be a relative URL or an absolute URL
|
119
|
+
# The behaviour of either depends on the driver.
|
120
|
+
#
|
121
|
+
# session.visit('/foo')
|
122
|
+
# session.visit('http://google.com')
|
123
|
+
#
|
124
|
+
# For drivers which can run against an external application, such as culerity and selenium
|
125
|
+
# giving an absolute URL will navigate to that page. This allows testing applications
|
126
|
+
# running on remote servers. For these drivers, setting Capybara.app_host will make the
|
127
|
+
# remote server the default. For example:
|
128
|
+
#
|
129
|
+
# Capybara.app_host = 'http://google.com'
|
130
|
+
# session.visit('/') # visits the google homepage
|
131
|
+
#
|
132
|
+
# @param [String] url The URL to navigate to
|
133
|
+
#
|
134
|
+
def visit(url)
|
135
|
+
driver.visit(url)
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
#
|
140
|
+
# Execute the given block for a particular scope on the page. Within will find the first
|
141
|
+
# element matching the given selector and execute the block scoped to that element:
|
142
|
+
#
|
143
|
+
# within(:xpath, '//div[@id="delivery-address"]') do
|
144
|
+
# fill_in('Street', :with => '12 Main Street')
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# It is possible to omit the first parameter, in that case, the selector is assumed to be
|
148
|
+
# of the type set in Capybara.default_selector.
|
149
|
+
#
|
150
|
+
# within('div#delivery-address') do
|
151
|
+
# fill_in('Street', :with => '12 Main Street')
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# @param [:css, :xpath, String] kind The type of selector or the selector if the second argument is blank
|
155
|
+
# @param [String] selector The selector within which to execute the given block
|
156
|
+
#
|
157
|
+
def within(kind, selector=nil)
|
158
|
+
new_scope = find(kind, selector, :message => "scope '#{selector || kind}' not found on page")
|
105
159
|
begin
|
106
|
-
scopes.push(
|
160
|
+
scopes.push(new_scope)
|
107
161
|
yield
|
108
162
|
ensure
|
109
163
|
scopes.pop
|
110
164
|
end
|
111
165
|
end
|
112
166
|
|
167
|
+
##
|
168
|
+
#
|
169
|
+
# Execute the given block within the a specific fieldset given the id or legend of that fieldset.
|
170
|
+
#
|
171
|
+
# @param [String] locator Id or legend of the fieldset
|
172
|
+
#
|
113
173
|
def within_fieldset(locator)
|
114
|
-
within :xpath, XPath.fieldset(locator) do
|
174
|
+
within :xpath, XPath::HTML.fieldset(locator) do
|
115
175
|
yield
|
116
176
|
end
|
117
177
|
end
|
118
178
|
|
179
|
+
##
|
180
|
+
#
|
181
|
+
# Execute the given block within the a specific table given the id or caption of that table.
|
182
|
+
#
|
183
|
+
# @param [String] locator Id or caption of the table
|
184
|
+
#
|
119
185
|
def within_table(locator)
|
120
|
-
within :xpath, XPath.table(locator) do
|
186
|
+
within :xpath, XPath::HTML.table(locator) do
|
121
187
|
yield
|
122
188
|
end
|
123
189
|
end
|
124
190
|
|
191
|
+
##
|
192
|
+
#
|
193
|
+
# Execute the given block within the given iframe given the id of that iframe. Only works on
|
194
|
+
# some drivers (e.g. Selenium)
|
195
|
+
#
|
196
|
+
# @param [String] locator Id of the frame
|
197
|
+
#
|
125
198
|
def within_frame(frame_id)
|
126
199
|
driver.within_frame(frame_id) do
|
127
200
|
yield
|
128
201
|
end
|
129
202
|
end
|
130
203
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
wait_conditionally_until do
|
141
|
-
results = all(:xpath, path, options)
|
142
|
-
|
143
|
-
if options[:count]
|
144
|
-
results.size == options[:count]
|
145
|
-
else
|
146
|
-
results.size > 0
|
147
|
-
end
|
148
|
-
end
|
149
|
-
rescue Capybara::TimeoutError
|
150
|
-
return false
|
151
|
-
end
|
152
|
-
|
153
|
-
def has_no_xpath?(path, options={})
|
154
|
-
wait_conditionally_until do
|
155
|
-
results = all(:xpath, path, options)
|
156
|
-
|
157
|
-
if options[:count]
|
158
|
-
results.size != options[:count]
|
159
|
-
else
|
160
|
-
results.empty?
|
161
|
-
end
|
162
|
-
end
|
163
|
-
rescue Capybara::TimeoutError
|
164
|
-
return false
|
165
|
-
end
|
166
|
-
|
167
|
-
def has_css?(path, options={})
|
168
|
-
has_xpath?(XPath.from_css(path), options)
|
204
|
+
##
|
205
|
+
#
|
206
|
+
# Execute the given block within the given window. Only works on
|
207
|
+
# some drivers (e.g. Selenium)
|
208
|
+
#
|
209
|
+
# @param [String] locator of the window
|
210
|
+
#
|
211
|
+
def within_window(handle, &blk)
|
212
|
+
driver.within_window(handle, &blk)
|
169
213
|
end
|
170
214
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
def has_no_content?(content)
|
180
|
-
has_no_xpath?(XPath.content(content))
|
181
|
-
end
|
182
|
-
|
183
|
-
def has_link?(locator)
|
184
|
-
has_xpath?(XPath.link(locator))
|
185
|
-
end
|
186
|
-
|
187
|
-
def has_no_link?(locator)
|
188
|
-
has_no_xpath?(XPath.link(locator))
|
189
|
-
end
|
190
|
-
|
191
|
-
def has_button?(locator)
|
192
|
-
has_xpath?(XPath.button(locator))
|
193
|
-
end
|
194
|
-
|
195
|
-
def has_no_button?(locator)
|
196
|
-
has_no_xpath?(XPath.button(locator))
|
197
|
-
end
|
198
|
-
|
199
|
-
def has_field?(locator, options={})
|
200
|
-
has_xpath?(XPath.field(locator, options))
|
201
|
-
end
|
202
|
-
|
203
|
-
def has_no_field?(locator, options={})
|
204
|
-
has_no_xpath?(XPath.field(locator, options))
|
205
|
-
end
|
206
|
-
|
207
|
-
def has_checked_field?(locator)
|
208
|
-
has_xpath?(XPath.field(locator, :checked => true))
|
209
|
-
end
|
210
|
-
|
211
|
-
def has_unchecked_field?(locator)
|
212
|
-
has_xpath?(XPath.field(locator, :unchecked => true))
|
213
|
-
end
|
214
|
-
|
215
|
-
def has_select?(locator, options={})
|
216
|
-
has_xpath?(XPath.select(locator, options))
|
215
|
+
##
|
216
|
+
#
|
217
|
+
# Retry executing the block until a truthy result is returned or the timeout time is exceeded
|
218
|
+
#
|
219
|
+
# @param [Integer] timeout The amount of seconds to retry executing the given block
|
220
|
+
#
|
221
|
+
def wait_until(timeout = Capybara.default_wait_time)
|
222
|
+
Capybara.timeout(timeout,driver) { yield }
|
217
223
|
end
|
218
224
|
|
219
|
-
|
220
|
-
|
225
|
+
##
|
226
|
+
#
|
227
|
+
# Execute the given script, not returning a result. This is useful for scripts that return
|
228
|
+
# complex objects, such as jQuery statements. +execute_script+ should always be used over
|
229
|
+
# +evaluate_script+ whenever possible.
|
230
|
+
#
|
231
|
+
# @param [String] script A string of JavaScript to execute
|
232
|
+
#
|
233
|
+
def execute_script(script)
|
234
|
+
driver.execute_script(script)
|
221
235
|
end
|
222
236
|
|
223
|
-
|
224
|
-
|
237
|
+
##
|
238
|
+
#
|
239
|
+
# Evaluate the given JavaScript and return the result. Be careful when using this with
|
240
|
+
# scripts that return complex objects, such as jQuery statements. +execute_script+ might
|
241
|
+
# be a better alternative.
|
242
|
+
#
|
243
|
+
# @param [String] script A string of JavaScript to evaluate
|
244
|
+
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
245
|
+
#
|
246
|
+
def evaluate_script(script)
|
247
|
+
driver.evaluate_script(script)
|
225
248
|
end
|
226
249
|
|
227
|
-
|
228
|
-
|
250
|
+
##
|
251
|
+
#
|
252
|
+
# @deprecated click is deprecated, please use {Capybara::Node::Actions#click_link_or_button} instead
|
253
|
+
#
|
254
|
+
def click(locator)
|
255
|
+
Capybara.deprecate("click", "click_link_or_button")
|
256
|
+
current_node.click_link_or_button(locator)
|
229
257
|
end
|
230
258
|
|
259
|
+
##
|
260
|
+
#
|
261
|
+
# Save a snapshot of the page and open it in a browser for inspection
|
262
|
+
#
|
231
263
|
def save_and_open_page
|
232
|
-
require 'capybara/save_and_open_page'
|
233
|
-
Capybara
|
264
|
+
require 'capybara/util/save_and_open_page'
|
265
|
+
Capybara.save_and_open_page(body)
|
234
266
|
end
|
235
267
|
|
236
|
-
|
237
|
-
|
238
|
-
node = wait_conditionally_until { find(kind_or_locator, locator) }
|
239
|
-
ensure
|
240
|
-
raise Capybara::ElementNotFound, fail_msg || "Unable to locate '#{locator || kind_or_locator}'" unless node
|
241
|
-
return node
|
268
|
+
def document
|
269
|
+
Capybara::Document.new(self, driver)
|
242
270
|
end
|
243
271
|
|
244
|
-
def
|
245
|
-
|
272
|
+
def method_missing(*args)
|
273
|
+
current_node.send(*args)
|
246
274
|
end
|
247
275
|
|
248
|
-
def
|
249
|
-
|
250
|
-
end
|
251
|
-
|
252
|
-
def evaluate_script(script)
|
253
|
-
driver.evaluate_script(script)
|
276
|
+
def respond_to?(method)
|
277
|
+
super || current_node.respond_to?(method)
|
254
278
|
end
|
255
279
|
|
256
280
|
private
|
257
281
|
|
258
|
-
def
|
259
|
-
|
260
|
-
end
|
261
|
-
|
262
|
-
def all_unfiltered(locator)
|
263
|
-
XPath.wrap(locator).scope(current_scope).paths.map do |path|
|
264
|
-
driver.find(path)
|
265
|
-
end.flatten
|
266
|
-
end
|
267
|
-
|
268
|
-
def current_scope
|
269
|
-
scopes.join('')
|
282
|
+
def current_node
|
283
|
+
scopes.last
|
270
284
|
end
|
271
285
|
|
272
286
|
def scopes
|
273
|
-
@scopes ||= []
|
287
|
+
@scopes ||= [document]
|
274
288
|
end
|
275
289
|
end
|
276
290
|
end
|