capybara 0.3.9 → 0.4.0.rc

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/History.txt +43 -1
  2. data/README.rdoc +168 -98
  3. data/lib/capybara.rb +77 -15
  4. data/lib/capybara/driver/base.rb +21 -16
  5. data/lib/capybara/driver/celerity_driver.rb +39 -41
  6. data/lib/capybara/driver/culerity_driver.rb +2 -1
  7. data/lib/capybara/driver/node.rb +66 -0
  8. data/lib/capybara/driver/rack_test_driver.rb +66 -67
  9. data/lib/capybara/driver/selenium_driver.rb +43 -47
  10. data/lib/capybara/dsl.rb +44 -6
  11. data/lib/capybara/node.rb +185 -24
  12. data/lib/capybara/node/actions.rb +170 -0
  13. data/lib/capybara/node/finders.rb +150 -0
  14. data/lib/capybara/node/matchers.rb +360 -0
  15. data/lib/capybara/rails.rb +1 -0
  16. data/lib/capybara/selector.rb +52 -0
  17. data/lib/capybara/server.rb +68 -87
  18. data/lib/capybara/session.rb +221 -207
  19. data/lib/capybara/spec/driver.rb +45 -35
  20. data/lib/capybara/spec/public/test.js +1 -1
  21. data/lib/capybara/spec/session.rb +28 -53
  22. data/lib/capybara/spec/session/all_spec.rb +7 -3
  23. data/lib/capybara/spec/session/check_spec.rb +50 -52
  24. data/lib/capybara/spec/session/click_button_spec.rb +9 -0
  25. data/lib/capybara/spec/session/click_link_or_button_spec.rb +37 -0
  26. data/lib/capybara/spec/session/current_url_spec.rb +7 -0
  27. data/lib/capybara/spec/session/find_button_spec.rb +4 -2
  28. data/lib/capybara/spec/session/find_by_id_spec.rb +4 -2
  29. data/lib/capybara/spec/session/find_field_spec.rb +7 -3
  30. data/lib/capybara/spec/session/find_link_spec.rb +5 -3
  31. data/lib/capybara/spec/session/find_spec.rb +71 -6
  32. data/lib/capybara/spec/session/has_field_spec.rb +1 -1
  33. data/lib/capybara/spec/session/has_selector_spec.rb +129 -0
  34. data/lib/capybara/spec/session/has_xpath_spec.rb +4 -4
  35. data/lib/capybara/spec/session/javascript.rb +25 -5
  36. data/lib/capybara/spec/session/select_spec.rb +16 -2
  37. data/lib/capybara/spec/session/unselect_spec.rb +8 -1
  38. data/lib/capybara/spec/session/within_spec.rb +5 -5
  39. data/lib/capybara/spec/views/form.erb +65 -1
  40. data/lib/capybara/spec/views/popup_one.erb +8 -0
  41. data/lib/capybara/spec/views/popup_two.erb +8 -0
  42. data/lib/capybara/spec/views/with_html.erb +5 -0
  43. data/lib/capybara/spec/views/within_popups.erb +25 -0
  44. data/lib/capybara/{save_and_open_page.rb → util/save_and_open_page.rb} +3 -3
  45. data/lib/capybara/util/timeout.rb +27 -0
  46. data/lib/capybara/version.rb +1 -1
  47. data/spec/capybara_spec.rb +18 -8
  48. data/spec/driver/celerity_driver_spec.rb +10 -14
  49. data/spec/driver/culerity_driver_spec.rb +4 -3
  50. data/spec/driver/rack_test_driver_spec.rb +39 -2
  51. data/spec/driver/remote_culerity_driver_spec.rb +5 -7
  52. data/spec/driver/remote_selenium_driver_spec.rb +7 -10
  53. data/spec/driver/selenium_driver_spec.rb +3 -2
  54. data/spec/dsl_spec.rb +5 -14
  55. data/spec/save_and_open_page_spec.rb +19 -19
  56. data/spec/server_spec.rb +22 -10
  57. data/spec/session/celerity_session_spec.rb +17 -21
  58. data/spec/session/culerity_session_spec.rb +3 -3
  59. data/spec/session/rack_test_session_spec.rb +2 -2
  60. data/spec/session/selenium_session_spec.rb +2 -2
  61. data/spec/spec_helper.rb +27 -6
  62. data/spec/{wait_until_spec.rb → timeout_spec.rb} +14 -14
  63. metadata +88 -46
  64. data/lib/capybara/searchable.rb +0 -54
  65. data/lib/capybara/spec/session/click_spec.rb +0 -24
  66. data/lib/capybara/spec/session/locate_spec.rb +0 -65
  67. data/lib/capybara/wait_until.rb +0 -28
  68. data/lib/capybara/xpath.rb +0 -179
  69. data/spec/searchable_spec.rb +0 -66
  70. data/spec/xpath_spec.rb +0 -180
@@ -13,4 +13,5 @@ Capybara.app = Rack::Builder.new do
13
13
  end.to_app
14
14
 
15
15
  Capybara.asset_root = Rails.root.join('public')
16
+ Capybara.save_and_open_page_path = Rails.root.join('tmp/capybara')
16
17
 
@@ -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] }
@@ -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
- class Capybara::Server
6
- class Identify
7
- def initialize(app)
8
- @app = app
9
- end
6
+ module Capybara
7
+ class Server
8
+ class Identify
9
+ def initialize(app)
10
+ @app = app
11
+ end
10
12
 
11
- def call(env)
12
- if env["PATH_INFO"] == "/__identify__"
13
- [200, {}, @app.object_id.to_s]
14
- else
15
- @app.call(env)
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
- attr_reader :app, :port
21
-
22
- def initialize(app)
23
- @app = app
24
- end
22
+ class << self
23
+ def ports
24
+ @ports ||= {}
25
+ end
26
+ end
25
27
 
26
- def host
27
- "localhost"
28
- end
28
+ attr_reader :app, :port
29
29
 
30
- def url(path)
31
- if path =~ /^http/
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
- def responsive?
39
- is_running_on_port?(port)
40
- end
34
+ def host
35
+ "127.0.0.1"
36
+ end
41
37
 
42
- def handler
43
- begin
44
- require 'rack/handler/thin'
45
- Rack::Handler::Thin
46
- rescue LoadError
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
- def boot
58
- return self unless @app
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
- Thread.new do
64
- handler.run(Identify.new(@app), :Port => port, :AccessLog => [])
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
- Capybara::WaitUntil.timeout(10) do
69
- if responsive?
70
- Capybara.log("application has booted")
71
- true
72
- else
73
- sleep 0.5
74
- false
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
- def find_available_port
86
- @port = 9887
87
- @port += 1 while is_port_open?(@port) and not is_running_on_port?(@port)
88
- end
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
@@ -1,18 +1,38 @@
1
- require 'forwardable'
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, :click, :click_button, :click_link, :current_url, :drag, :evaluate_script,
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?, :has_field?, :has_no_field?, :has_checked_field?, :has_unchecked_field?, :has_no_table?, :has_table?,
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
- string = mode.to_s
28
- string.gsub!(%r{(^.)|(_.)}) { |m| m[m.length-1,1].upcase }
29
- Capybara::Driver.const_get(string.to_sym).new(app)
30
- rescue NameError
31
- raise Capybara::DriverNotFoundError, "no driver called #{mode} was found"
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
- def_delegator :driver, :cleanup!
36
- def_delegator :driver, :current_url
37
- def_delegator :driver, :current_path
38
- def_delegator :driver, :response_headers
39
- def_delegator :driver, :status_code
40
- def_delegator :driver, :visit
41
- def_delegator :driver, :body
42
- def_delegator :driver, :source
43
-
44
- def click(locator)
45
- msg = "no link or button '#{locator}' found"
46
- locate(:xpath, XPath.link(locator).button(locator), msg).click
47
- end
48
-
49
- def click_link(locator)
50
- msg = "no link with title, id or text '#{locator}' found"
51
- locate(:xpath, XPath.link(locator), msg).click
52
- end
53
-
54
- def click_button(locator)
55
- msg = "no button with value or id or text '#{locator}' found"
56
- locate(:xpath, XPath.button(locator), msg).click
57
- end
58
-
59
- def drag(source_locator, target_locator)
60
- source = locate(:xpath, source_locator, "drag source '#{source_locator}' not found on page")
61
- target = locate(:xpath, target_locator, "drag target '#{target_locator}' not found on page")
62
- source.drag_to(target)
63
- end
64
-
65
- def fill_in(locator, options={})
66
- msg = "cannot fill in, no text field, text area or password field with id, name, or label '#{locator}' found"
67
- raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
68
- locate(:xpath, XPath.fillable_field(locator), msg).set(options[:with])
69
- end
70
-
71
- def choose(locator)
72
- msg = "cannot choose field, no radio button with id, name, or label '#{locator}' found"
73
- locate(:xpath, XPath.radio_button(locator), msg).set(true)
74
- end
75
-
76
- def check(locator)
77
- msg = "cannot check field, no checkbox with id, name, or label '#{locator}' found"
78
- locate(:xpath, XPath.checkbox(locator), msg).set(true)
79
- end
80
-
81
- def uncheck(locator)
82
- msg = "cannot uncheck field, no checkbox with id, name, or label '#{locator}' found"
83
- locate(:xpath, XPath.checkbox(locator), msg).set(false)
84
- end
85
-
86
- def select(value, options={})
87
- msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found"
88
- locate(:xpath, XPath.select(options[:from]), msg).select(value)
89
- end
90
-
91
- def unselect(value, options={})
92
- msg = "cannot unselect option, no select box with id, name, or label '#{options[:from]}' found"
93
- locate(:xpath, XPath.select(options[:from]), msg).unselect(value)
94
- end
95
-
96
- def attach_file(locator, path)
97
- msg = "cannot attach file, no file field with id, name, or label '#{locator}' found"
98
- locate(:xpath, XPath.file_field(locator), msg).set(path)
99
- end
100
-
101
- def within(kind, scope=nil)
102
- kind, scope = Capybara.default_selector, kind unless scope
103
- scope = XPath.from_css(scope) if kind == :css
104
- locate(:xpath, scope, "scope '#{scope}' not found on page")
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(scope)
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
- def scope_to(*locator)
132
- scoped_session = self.clone
133
- scoped_session.instance_eval do
134
- @scopes = scopes + locator
135
- end
136
- scoped_session
137
- end
138
-
139
- def has_xpath?(path, options={})
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
- def has_no_css?(path, options={})
172
- has_no_xpath?(XPath.from_css(path), options)
173
- end
174
-
175
- def has_content?(content)
176
- has_xpath?(XPath.content(content))
177
- end
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
- def has_no_select?(locator, options={})
220
- has_no_xpath?(XPath.select(locator, options))
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
- def has_table?(locator, options={})
224
- has_xpath?(XPath.table(locator, options))
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
- def has_no_table?(locator, options={})
228
- has_no_xpath?(XPath.table(locator, options))
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::SaveAndOpenPage.save_and_open_page(body)
264
+ require 'capybara/util/save_and_open_page'
265
+ Capybara.save_and_open_page(body)
234
266
  end
235
267
 
236
- #return node identified by locator or raise ElementNotFound(using desc)
237
- def locate(kind_or_locator, locator=nil, fail_msg = nil)
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 wait_until(timeout = Capybara.default_wait_time)
245
- WaitUntil.timeout(timeout,driver) { yield }
272
+ def method_missing(*args)
273
+ current_node.send(*args)
246
274
  end
247
275
 
248
- def execute_script(script)
249
- driver.execute_script(script)
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 wait_conditionally_until
259
- if driver.wait? then wait_until { yield } else yield end
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