capybara-rails-2-2 0.4.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/History.txt +202 -0
  2. data/README.rdoc +540 -0
  3. data/lib/capybara.rb +231 -0
  4. data/lib/capybara/cucumber.rb +32 -0
  5. data/lib/capybara/driver/base.rb +60 -0
  6. data/lib/capybara/driver/celerity_driver.rb +164 -0
  7. data/lib/capybara/driver/culerity_driver.rb +26 -0
  8. data/lib/capybara/driver/node.rb +74 -0
  9. data/lib/capybara/driver/rack_test_driver.rb +303 -0
  10. data/lib/capybara/driver/selenium_driver.rb +161 -0
  11. data/lib/capybara/dsl.rb +109 -0
  12. data/lib/capybara/node/actions.rb +160 -0
  13. data/lib/capybara/node/base.rb +47 -0
  14. data/lib/capybara/node/document.rb +17 -0
  15. data/lib/capybara/node/element.rb +178 -0
  16. data/lib/capybara/node/finders.rb +201 -0
  17. data/lib/capybara/node/matchers.rb +391 -0
  18. data/lib/capybara/node/simple.rb +116 -0
  19. data/lib/capybara/rails.rb +16 -0
  20. data/lib/capybara/rspec.rb +18 -0
  21. data/lib/capybara/selector.rb +70 -0
  22. data/lib/capybara/server.rb +90 -0
  23. data/lib/capybara/session.rb +281 -0
  24. data/lib/capybara/spec/driver.rb +243 -0
  25. data/lib/capybara/spec/fixtures/capybara.jpg +0 -0
  26. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  27. data/lib/capybara/spec/public/jquery-ui.js +35 -0
  28. data/lib/capybara/spec/public/jquery.js +19 -0
  29. data/lib/capybara/spec/public/test.js +33 -0
  30. data/lib/capybara/spec/session.rb +110 -0
  31. data/lib/capybara/spec/session/all_spec.rb +78 -0
  32. data/lib/capybara/spec/session/attach_file_spec.rb +70 -0
  33. data/lib/capybara/spec/session/check_spec.rb +65 -0
  34. data/lib/capybara/spec/session/choose_spec.rb +26 -0
  35. data/lib/capybara/spec/session/click_button_spec.rb +252 -0
  36. data/lib/capybara/spec/session/click_link_or_button_spec.rb +36 -0
  37. data/lib/capybara/spec/session/click_link_spec.rb +113 -0
  38. data/lib/capybara/spec/session/current_url_spec.rb +15 -0
  39. data/lib/capybara/spec/session/fill_in_spec.rb +119 -0
  40. data/lib/capybara/spec/session/find_button_spec.rb +18 -0
  41. data/lib/capybara/spec/session/find_by_id_spec.rb +18 -0
  42. data/lib/capybara/spec/session/find_field_spec.rb +26 -0
  43. data/lib/capybara/spec/session/find_link_spec.rb +19 -0
  44. data/lib/capybara/spec/session/find_spec.rb +121 -0
  45. data/lib/capybara/spec/session/first_spec.rb +72 -0
  46. data/lib/capybara/spec/session/has_button_spec.rb +32 -0
  47. data/lib/capybara/spec/session/has_content_spec.rb +106 -0
  48. data/lib/capybara/spec/session/has_css_spec.rb +213 -0
  49. data/lib/capybara/spec/session/has_field_spec.rb +156 -0
  50. data/lib/capybara/spec/session/has_link_spec.rb +37 -0
  51. data/lib/capybara/spec/session/has_select_spec.rb +129 -0
  52. data/lib/capybara/spec/session/has_selector_spec.rb +129 -0
  53. data/lib/capybara/spec/session/has_table_spec.rb +96 -0
  54. data/lib/capybara/spec/session/has_xpath_spec.rb +123 -0
  55. data/lib/capybara/spec/session/headers.rb +19 -0
  56. data/lib/capybara/spec/session/javascript.rb +223 -0
  57. data/lib/capybara/spec/session/response_code.rb +19 -0
  58. data/lib/capybara/spec/session/select_spec.rb +105 -0
  59. data/lib/capybara/spec/session/uncheck_spec.rb +21 -0
  60. data/lib/capybara/spec/session/unselect_spec.rb +61 -0
  61. data/lib/capybara/spec/session/within_spec.rb +160 -0
  62. data/lib/capybara/spec/test_app.rb +117 -0
  63. data/lib/capybara/spec/views/buttons.erb +4 -0
  64. data/lib/capybara/spec/views/fieldsets.erb +29 -0
  65. data/lib/capybara/spec/views/form.erb +348 -0
  66. data/lib/capybara/spec/views/frame_one.erb +8 -0
  67. data/lib/capybara/spec/views/frame_two.erb +8 -0
  68. data/lib/capybara/spec/views/popup_one.erb +8 -0
  69. data/lib/capybara/spec/views/popup_two.erb +8 -0
  70. data/lib/capybara/spec/views/postback.erb +13 -0
  71. data/lib/capybara/spec/views/tables.erb +122 -0
  72. data/lib/capybara/spec/views/with_html.erb +69 -0
  73. data/lib/capybara/spec/views/with_js.erb +39 -0
  74. data/lib/capybara/spec/views/with_scope.erb +36 -0
  75. data/lib/capybara/spec/views/with_simple_html.erb +1 -0
  76. data/lib/capybara/spec/views/within_frames.erb +10 -0
  77. data/lib/capybara/spec/views/within_popups.erb +25 -0
  78. data/lib/capybara/util/save_and_open_page.rb +40 -0
  79. data/lib/capybara/util/timeout.rb +27 -0
  80. data/lib/capybara/version.rb +3 -0
  81. data/spec/basic_node_spec.rb +77 -0
  82. data/spec/capybara_spec.rb +46 -0
  83. data/spec/driver/celerity_driver_spec.rb +13 -0
  84. data/spec/driver/culerity_driver_spec.rb +14 -0
  85. data/spec/driver/rack_test_driver_spec.rb +84 -0
  86. data/spec/driver/remote_culerity_driver_spec.rb +22 -0
  87. data/spec/driver/remote_selenium_driver_spec.rb +16 -0
  88. data/spec/driver/selenium_driver_spec.rb +14 -0
  89. data/spec/dsl_spec.rb +157 -0
  90. data/spec/rspec_spec.rb +47 -0
  91. data/spec/save_and_open_page_spec.rb +159 -0
  92. data/spec/server_spec.rb +85 -0
  93. data/spec/session/celerity_session_spec.rb +24 -0
  94. data/spec/session/culerity_session_spec.rb +26 -0
  95. data/spec/session/rack_test_session_spec.rb +44 -0
  96. data/spec/session/selenium_session_spec.rb +26 -0
  97. data/spec/spec_helper.rb +40 -0
  98. data/spec/string_spec.rb +77 -0
  99. data/spec/timeout_spec.rb +28 -0
  100. metadata +360 -0
@@ -0,0 +1,116 @@
1
+ module Capybara
2
+ module Node
3
+
4
+ ##
5
+ #
6
+ # A {Capybara::Node::Simple} is a simpler version of {Capybara::Node::Base} which
7
+ # includes only {Capybara::Node::Finders} and {Capybara::Node::Matchers} and does
8
+ # not include {Capybara::Node::Actions}. This type of node is returned when
9
+ # using {Capybara.string}.
10
+ #
11
+ # It is useful in that it does not require a session, an application or a driver,
12
+ # but can still use Capybara's finders and matchers on any string that contains HTML.
13
+ #
14
+ class Simple
15
+ include Capybara::Node::Finders
16
+ include Capybara::Node::Matchers
17
+
18
+ attr_reader :native
19
+
20
+ def initialize(native)
21
+ native = Nokogiri::HTML(native) if native.is_a?(String)
22
+ @native = native
23
+ end
24
+
25
+ ##
26
+ #
27
+ # @return [String] The text of the element
28
+ #
29
+ def text
30
+ native.text
31
+ end
32
+
33
+ ##
34
+ #
35
+ # Retrieve the given attribute
36
+ #
37
+ # element[:title] # => HTML title attribute
38
+ #
39
+ # @param [Symbol] attribute The attribute to retrieve
40
+ # @return [String] The value of the attribute
41
+ #
42
+ def [](name)
43
+ attr_name = name.to_s
44
+ if attr_name == 'value'
45
+ value
46
+ elsif 'input' == tag_name and 'checkbox' == native[:type] and 'checked' == attr_name
47
+ native['checked'] == 'checked'
48
+ else
49
+ native[attr_name]
50
+ end
51
+ end
52
+
53
+ ##
54
+ #
55
+ # @return [String] The tag name of the element
56
+ #
57
+ def tag_name
58
+ native.node_name
59
+ end
60
+
61
+ ##
62
+ #
63
+ # An XPath expression describing where on the page the element can be found
64
+ #
65
+ # @return [String] An XPath expression
66
+ #
67
+ def path
68
+ native.path
69
+ end
70
+
71
+ ##
72
+ #
73
+ # @return [String] The value of the form element
74
+ #
75
+ def value
76
+ if tag_name == 'textarea'
77
+ native.content
78
+ elsif tag_name == 'select'
79
+ if native['multiple'] == 'multiple'
80
+ native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content }
81
+ else
82
+ option = native.xpath(".//option[@selected='selected']").first || native.xpath(".//option").first
83
+ option[:value] || option.content if option
84
+ end
85
+ else
86
+ native[:value]
87
+ end
88
+ end
89
+
90
+ ##
91
+ #
92
+ # Whether or not the element is visible. Does not support CSS, so
93
+ # the result may be inaccurate.
94
+ #
95
+ # @return [Boolean] Whether the element is visible
96
+ #
97
+ def visible?
98
+ native.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none')]").size == 0
99
+ end
100
+
101
+ protected
102
+
103
+ def find_in_base(xpath)
104
+ native.xpath(xpath).map { |node| self.class.new(node) }
105
+ end
106
+
107
+ def convert_element(element)
108
+ element
109
+ end
110
+
111
+ def wait?
112
+ false
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,16 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+
4
+ Capybara.app = Rack::Builder.new do
5
+ map "/" do
6
+ if Rails.version.to_f >= 3.0
7
+ run Rails.application
8
+ else # Rails 2
9
+ use Rails::Rack::Static
10
+ run ActionController::Dispatcher.new
11
+ end
12
+ end
13
+ end.to_app
14
+
15
+ Capybara.asset_root = Pathname.new File.join(Rails.root, 'public')
16
+ Capybara.save_and_open_page_path = Pathname.new File.join(Rails.root, 'tmp', 'capybara')
@@ -0,0 +1,18 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+
4
+ RSpec.configure do |config|
5
+ config.include Capybara, :type => :acceptance
6
+ config.after do
7
+ if example.metadata[:type] == :acceptance
8
+ Capybara.reset_sessions!
9
+ Capybara.use_default_driver
10
+ end
11
+ end
12
+ config.before do
13
+ if example.metadata[:type] == :acceptance
14
+ Capybara.current_driver = Capybara.javascript_driver if example.metadata[:js]
15
+ Capybara.current_driver = example.metadata[:driver] if example.metadata[:driver]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,70 @@
1
+ module Capybara
2
+ class Selector
3
+ attr_reader :name
4
+
5
+ class << self
6
+ def all
7
+ @selectors ||= {}
8
+ end
9
+
10
+ def add(name, &block)
11
+ all[name.to_sym] = Capybara::Selector.new(name.to_sym, &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, &block)
35
+ @name = name
36
+ instance_eval(&block)
37
+ end
38
+
39
+ def xpath(&block)
40
+ @xpath = block if block
41
+ @xpath
42
+ end
43
+
44
+ def match(&block)
45
+ @match = block if block
46
+ @match
47
+ end
48
+
49
+ def call(locator)
50
+ @xpath.call(locator)
51
+ end
52
+
53
+ def match?(locator)
54
+ @match and @match.call(locator)
55
+ end
56
+ end
57
+ end
58
+
59
+ Capybara.add_selector(:xpath) do
60
+ xpath { |xpath| xpath }
61
+ end
62
+
63
+ Capybara.add_selector(:css) do
64
+ xpath { |css| XPath.css(css) }
65
+ end
66
+
67
+ Capybara.add_selector(:id) do
68
+ xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
69
+ match { |value| value.is_a?(Symbol) }
70
+ end
@@ -0,0 +1,90 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'rack'
4
+ require 'capybara/util/timeout'
5
+
6
+ module Capybara
7
+ class Server
8
+ class Identify
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
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
19
+ end
20
+ end
21
+
22
+ class << self
23
+ def ports
24
+ @ports ||= {}
25
+ end
26
+ end
27
+
28
+ attr_reader :app, :port
29
+
30
+ def initialize(app)
31
+ @app = app
32
+ end
33
+
34
+ def host
35
+ "127.0.0.1"
36
+ end
37
+
38
+ def url(path)
39
+ if path =~ /^http/
40
+ path
41
+ else
42
+ (Capybara.app_host || "http://#{host}:#{port}") + path.to_s
43
+ end
44
+ end
45
+
46
+ def responsive?
47
+ res = Net::HTTP.start(host, @port) { |http| http.get('/__identify__') }
48
+
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
54
+ end
55
+
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
+ Capybara.server.call(Identify.new(@app), @port)
66
+ end
67
+
68
+ Capybara.timeout(Capybara.server_boot_timeout) do
69
+ if responsive? then true else sleep(0.5) and false end
70
+ end
71
+ end
72
+ end
73
+ rescue TimeoutError
74
+ puts "Rack application timed out during boot"
75
+ exit
76
+ else
77
+ self
78
+ end
79
+
80
+ private
81
+
82
+ def find_available_port
83
+ server = TCPServer.new('127.0.0.1', 0)
84
+ server.addr[1]
85
+ ensure
86
+ server.close if server
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,281 @@
1
+ require 'capybara/util/timeout'
2
+
3
+ module Capybara
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
29
+ DSL_METHODS = [
30
+ :all, :first, :attach_file, :body, :check, :choose, :click_link_or_button, :click_button, :click_link, :current_url, :drag, :evaluate_script,
31
+ :field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_css?,
32
+ :has_no_content?, :has_no_css?, :has_no_xpath?, :has_xpath?, :locate, :save_and_open_page, :select, :source, :uncheck,
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, :click, :has_selector?, :has_no_selector?, :click_on
36
+ ]
37
+
38
+ attr_reader :mode, :app
39
+
40
+ def initialize(mode, app=nil)
41
+ @mode = mode
42
+ @app = app
43
+ end
44
+
45
+ def driver
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)
52
+ end
53
+ end
54
+
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 (see Capybara::Node::Finders#all)
155
+ # @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
156
+ #
157
+ def within(*args)
158
+ new_scope = find(*args)
159
+ begin
160
+ scopes.push(new_scope)
161
+ yield
162
+ ensure
163
+ scopes.pop
164
+ end
165
+ end
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
+ #
173
+ def within_fieldset(locator)
174
+ within :xpath, XPath::HTML.fieldset(locator) do
175
+ yield
176
+ end
177
+ end
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
+ #
185
+ def within_table(locator)
186
+ within :xpath, XPath::HTML.table(locator) do
187
+ yield
188
+ end
189
+ end
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
+ #
198
+ def within_frame(frame_id)
199
+ driver.within_frame(frame_id) do
200
+ yield
201
+ end
202
+ end
203
+
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)
213
+ end
214
+
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 }
223
+ end
224
+
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)
235
+ end
236
+
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)
248
+ end
249
+
250
+ ##
251
+ #
252
+ # Save a snapshot of the page and open it in a browser for inspection
253
+ #
254
+ def save_and_open_page
255
+ require 'capybara/util/save_and_open_page'
256
+ Capybara.save_and_open_page(body)
257
+ end
258
+
259
+ def document
260
+ Capybara::Node::Document.new(self, driver)
261
+ end
262
+
263
+ def method_missing(*args)
264
+ current_node.send(*args)
265
+ end
266
+
267
+ def respond_to?(method)
268
+ super || current_node.respond_to?(method)
269
+ end
270
+
271
+ private
272
+
273
+ def current_node
274
+ scopes.last
275
+ end
276
+
277
+ def scopes
278
+ @scopes ||= [document]
279
+ end
280
+ end
281
+ end