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,26 @@
1
+ require 'culerity'
2
+
3
+ class Capybara::Driver::Culerity < Capybara::Driver::Celerity
4
+
5
+ def self.server
6
+ unless @_server
7
+ @_server = ::Culerity::run_server
8
+ at_exit do
9
+ @_server.close
10
+ end
11
+ end
12
+ @_server
13
+ end
14
+
15
+ def browser
16
+ unless @_browser
17
+ @_browser = ::Culerity::RemoteBrowserProxy.new self.class.server, options
18
+ at_exit do
19
+ @_browser.close
20
+ @_browser.exit
21
+ end
22
+ end
23
+ @_browser
24
+ end
25
+
26
+ end
@@ -0,0 +1,74 @@
1
+ module Capybara
2
+ module Driver
3
+ class Node
4
+ attr_reader :driver, :native
5
+
6
+ def initialize(driver, native)
7
+ @driver = driver
8
+ @native = native
9
+ end
10
+
11
+ def text
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def [](name)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def value
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def set(value)
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def select_option
28
+ raise NotImplementedError
29
+ end
30
+
31
+ def unselect_option
32
+ raise NotImplementedError
33
+ end
34
+
35
+ def click
36
+ raise NotImplementedError
37
+ end
38
+
39
+ def drag_to(element)
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def tag_name
44
+ raise NotImplementedError
45
+ end
46
+
47
+ def visible?
48
+ raise NotImplementedError
49
+ end
50
+
51
+ def checked?
52
+ raise NotImplementedError
53
+ end
54
+
55
+ def selected?
56
+ raise NotImplementedError
57
+ end
58
+
59
+ def path
60
+ raise NotSupportedByDriverError
61
+ end
62
+
63
+ def trigger(event)
64
+ raise NotSupportedByDriverError
65
+ end
66
+
67
+ def inspect
68
+ %(#<Capybara::Driver::Node tag="#{tag_name}" path="#{path}">)
69
+ rescue NotSupportedByDriverError
70
+ %(#<Capybara::Driver::Node tag="#{tag_name}">)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,303 @@
1
+ require 'rack/test'
2
+ require 'rack/utils'
3
+ require 'mime/types'
4
+ require 'nokogiri'
5
+ require 'cgi'
6
+
7
+ class Capybara::Driver::RackTest < Capybara::Driver::Base
8
+ class Node < Capybara::Driver::Node
9
+ def text
10
+ native.text
11
+ end
12
+
13
+ def [](name)
14
+ string_node[name]
15
+ end
16
+
17
+ def value
18
+ string_node.value
19
+ end
20
+
21
+ def set(value)
22
+ if tag_name == 'input' and type == 'radio'
23
+ other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
24
+ driver.html.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
25
+ native['checked'] = 'checked'
26
+ elsif tag_name == 'input' and type == 'checkbox'
27
+ if value && !native['checked']
28
+ native['checked'] = 'checked'
29
+ elsif !value && native['checked']
30
+ native.remove_attribute('checked')
31
+ end
32
+ elsif tag_name == 'input'
33
+ native['value'] = value.to_s
34
+ elsif tag_name == "textarea"
35
+ native.content = value.to_s
36
+ end
37
+ end
38
+
39
+ def select_option
40
+ if select_node['multiple'] != 'multiple'
41
+ select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
42
+ end
43
+ native["selected"] = 'selected'
44
+ end
45
+
46
+ def unselect_option
47
+ if select_node['multiple'] != 'multiple'
48
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
49
+ end
50
+ native.remove_attribute('selected')
51
+ end
52
+
53
+ def click
54
+ if tag_name == 'a'
55
+ method = self["data-method"] || :get
56
+ driver.process(method, self[:href].to_s)
57
+ elsif (tag_name == 'input' and %w(submit image).include?(type)) or
58
+ ((tag_name == 'button') and type.nil? or type == "submit")
59
+ Form.new(driver, form).submit(self)
60
+ end
61
+ end
62
+
63
+ def tag_name
64
+ native.node_name
65
+ end
66
+
67
+ def visible?
68
+ string_node.visible?
69
+ end
70
+
71
+ def checked?
72
+ self[:checked]
73
+ end
74
+
75
+ def selected?
76
+ self[:selected]
77
+ end
78
+
79
+ def path
80
+ native.path
81
+ end
82
+
83
+ def find(locator)
84
+ native.xpath(locator).map { |n| self.class.new(driver, n) }
85
+ end
86
+
87
+ private
88
+
89
+ def string_node
90
+ @string_node ||= Capybara::Node::Simple.new(native)
91
+ end
92
+
93
+ # a reference to the select node if this is an option node
94
+ def select_node
95
+ find('./ancestor::select').first
96
+ end
97
+
98
+ def type
99
+ native[:type]
100
+ end
101
+
102
+ def form
103
+ native.ancestors('form').first
104
+ end
105
+ end
106
+
107
+ class Form < Node
108
+ # This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
109
+ # the class specifically when determing whether to consturct the request as multipart.
110
+ # That check should be based solely on the form element's 'enctype' attribute value,
111
+ # which should probably be provided to Rack::Test in its non-GET request methods.
112
+ class NilUploadedFile < Rack::Test::UploadedFile
113
+ def initialize
114
+ @empty_file = Tempfile.new("nil_uploaded_file")
115
+ @empty_file.close
116
+ end
117
+
118
+ def original_filename; ""; end
119
+ def content_type; "application/octet-stream"; end
120
+ def path; @empty_file.path; end
121
+ end
122
+
123
+ def params(button)
124
+ params = {}
125
+
126
+ native.xpath("(.//input|.//select|.//textarea)[not(@disabled)]").map do |field|
127
+ case field.name
128
+ when 'input'
129
+ if %w(radio checkbox).include? field['type']
130
+ merge_param!(params, field['name'].to_s, field['value'].to_s) if field['checked']
131
+ elsif %w(submit image).include? field['type']
132
+ # TO DO identify the click button here (in document order, rather
133
+ # than leaving until the end of the params)
134
+ elsif field['type'] =='file'
135
+ if multipart?
136
+ file = \
137
+ if (value = field['value']).to_s.empty?
138
+ NilUploadedFile.new
139
+ else
140
+ content_type = MIME::Types.type_for(value).first.to_s
141
+ Rack::Test::UploadedFile.new(value, content_type)
142
+ end
143
+ merge_param!(params, field['name'].to_s, file)
144
+ else
145
+ merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
146
+ end
147
+ else
148
+ merge_param!(params, field['name'].to_s, field['value'].to_s)
149
+ end
150
+ when 'select'
151
+ if field['multiple'] == 'multiple'
152
+ options = field.xpath(".//option[@selected]")
153
+ options.each do |option|
154
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
155
+ end
156
+ else
157
+ option = field.xpath(".//option[@selected]").first
158
+ option ||= field.xpath('.//option').first
159
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
160
+ end
161
+ when 'textarea'
162
+ merge_param!(params, field['name'].to_s, field.text.to_s)
163
+ end
164
+ end
165
+ merge_param!(params, button[:name], button[:value] || "") if button[:name]
166
+ params
167
+ end
168
+
169
+ def submit(button)
170
+ driver.submit(method, native['action'].to_s, params(button))
171
+ end
172
+
173
+ def multipart?
174
+ self[:enctype] == "multipart/form-data"
175
+ end
176
+
177
+ private
178
+
179
+ def method
180
+ self[:method] =~ /post/i ? :post : :get
181
+ end
182
+
183
+ def merge_param!(params, key, value)
184
+ Rack::Utils.normalize_params(params, key, value)
185
+ end
186
+ end
187
+
188
+ include ::Rack::Test::Methods
189
+ attr_reader :app
190
+
191
+ alias_method :response, :last_response
192
+ alias_method :request, :last_request
193
+
194
+ def initialize(app)
195
+ raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
196
+ @app = app
197
+ end
198
+
199
+ def visit(path, attributes = {})
200
+ process(:get, path, attributes)
201
+ end
202
+
203
+ def process(method, path, attributes = {})
204
+ return if path.gsub(/^#{request_path}/, '') =~ /^#/
205
+ path = request_path + path if path =~ /^\?/
206
+ send(method, to_binary(path), to_binary( attributes ), env)
207
+ follow_redirects!
208
+ end
209
+
210
+ def current_url
211
+ request.url rescue ""
212
+ end
213
+
214
+ def response_headers
215
+ response.headers
216
+ end
217
+
218
+ def status_code
219
+ response.status
220
+ end
221
+
222
+ def to_binary(object)
223
+ return object unless Kernel.const_defined?(:Encoding)
224
+
225
+ if object.respond_to?(:force_encoding)
226
+ object.dup.force_encoding(Encoding::ASCII_8BIT)
227
+ elsif object.respond_to?(:each_pair) #Hash
228
+ {}.tap { |x| object.each_pair {|k,v| x[to_binary(k)] = to_binary(v) } }
229
+ elsif object.respond_to?(:each) #Array
230
+ object.map{|x| to_binary(x)}
231
+ else
232
+ object
233
+ end
234
+ end
235
+
236
+ def submit(method, path, attributes)
237
+ path = request_path if not path or path.empty?
238
+ send(method, to_binary(path), to_binary(attributes), env)
239
+ follow_redirects!
240
+ end
241
+
242
+ def find(selector)
243
+ html.xpath(selector).map { |node| Node.new(self, node) }
244
+ end
245
+
246
+ def body
247
+ @body ||= response.body
248
+ end
249
+
250
+ def html
251
+ @html ||= Nokogiri::HTML(body)
252
+ end
253
+ alias_method :source, :body
254
+
255
+ def reset!
256
+ clear_rack_mock_session
257
+ end
258
+
259
+ def get(*args, &block); reset_cache; super; end
260
+ def post(*args, &block); reset_cache; super; end
261
+ def put(*args, &block); reset_cache; super; end
262
+ def delete(*args, &block); reset_cache; super; end
263
+
264
+ def follow_redirects!
265
+ 5.times do
266
+ follow_redirect! if response.redirect?
267
+ end
268
+ raise Capybara::InfiniteRedirectError, "redirected more than 5 times, check for infinite redirects." if response.redirect?
269
+ end
270
+
271
+ private
272
+
273
+ def reset_cache
274
+ @body = nil
275
+ @html = nil
276
+ end
277
+
278
+ def build_rack_mock_session # :nodoc:
279
+ Rack::MockSession.new(app, Capybara.default_host || "www.example.com")
280
+ end
281
+
282
+ # Rack::Test::Methods does not provide methods for manipulating the session
283
+ # list so these must be manipulated directly.
284
+ def clear_rack_mock_session
285
+ @_rack_test_sessions = nil
286
+ @_rack_mock_sessions = nil
287
+ end
288
+
289
+ def request_path
290
+ request.path rescue ""
291
+ end
292
+
293
+ def env
294
+ env = {}
295
+ begin
296
+ env["HTTP_REFERER"] = request.url
297
+ rescue Rack::Test::Error
298
+ # no request yet
299
+ end
300
+ env
301
+ end
302
+
303
+ end
@@ -0,0 +1,161 @@
1
+ require 'selenium-webdriver'
2
+
3
+ class Capybara::Driver::Selenium < Capybara::Driver::Base
4
+ class Node < Capybara::Driver::Node
5
+ def text
6
+ native.text
7
+ end
8
+
9
+ def [](name)
10
+ if name == :value
11
+ value
12
+ else
13
+ native.attribute(name.to_s)
14
+ end
15
+ rescue Selenium::WebDriver::Error::WebDriverError
16
+ nil
17
+ end
18
+
19
+ def value
20
+ if tag_name == "select" and self[:multiple] and not self[:multiple] == "false"
21
+ native.find_elements(:xpath, ".//option").select { |n| n.selected? }.map { |n| n.value || n.text }
22
+ else
23
+ native.value
24
+ end
25
+ end
26
+
27
+ def set(value)
28
+ if tag_name == 'input' and type == 'radio'
29
+ native.click
30
+ elsif tag_name == 'input' and type == 'checkbox'
31
+ native.click if value ^ native.attribute('checked').to_s.eql?("true")
32
+ elsif tag_name == 'textarea' or tag_name == 'input'
33
+ native.clear
34
+ native.send_keys(value.to_s)
35
+ end
36
+ end
37
+
38
+ def select_option
39
+ native.select
40
+ end
41
+
42
+ def unselect_option
43
+ if select_node['multiple'] != 'multiple' and select_node['multiple'] != 'true'
44
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
45
+ end
46
+ native.toggle if selected?
47
+ end
48
+
49
+ def click
50
+ native.click
51
+ end
52
+
53
+ def drag_to(element)
54
+ native.drag_and_drop_on(element.native)
55
+ end
56
+
57
+ def tag_name
58
+ native.tag_name
59
+ end
60
+
61
+ def visible?
62
+ displayed = native.displayed?
63
+ displayed and displayed != "false"
64
+ end
65
+
66
+ def selected?
67
+ selected = native.selected?
68
+ selected and selected != "false"
69
+ end
70
+
71
+ alias :checked? :selected?
72
+
73
+ def find(locator)
74
+ native.find_elements(:xpath, locator).map { |n| self.class.new(driver, n) }
75
+ end
76
+
77
+ private
78
+
79
+ # a reference to the select node if this is an option node
80
+ def select_node
81
+ find('./ancestor::select').first
82
+ end
83
+
84
+ def type
85
+ self[:type]
86
+ end
87
+
88
+ end
89
+
90
+ attr_reader :app, :rack_server, :options
91
+
92
+ def browser
93
+ unless @browser
94
+ @browser = Selenium::WebDriver.for(options[:browser] || :firefox, options)
95
+ at_exit do
96
+ @browser.quit
97
+ end
98
+ end
99
+ @browser
100
+ end
101
+
102
+ def initialize(app, options={})
103
+ @app = app
104
+ @options = options
105
+ @rack_server = Capybara::Server.new(@app)
106
+ @rack_server.boot if Capybara.run_server
107
+ end
108
+
109
+ def visit(path)
110
+ browser.navigate.to(url(path))
111
+ end
112
+
113
+ def source
114
+ browser.page_source
115
+ end
116
+
117
+ def body
118
+ browser.page_source
119
+ end
120
+
121
+ def current_url
122
+ browser.current_url
123
+ end
124
+
125
+ def find(selector)
126
+ browser.find_elements(:xpath, selector).map { |node| Node.new(self, node) }
127
+ end
128
+
129
+ def wait?; true; end
130
+
131
+ def execute_script(script)
132
+ browser.execute_script script
133
+ end
134
+
135
+ def evaluate_script(script)
136
+ browser.execute_script "return #{script}"
137
+ end
138
+
139
+ def reset!
140
+ # Use instance variable directly so we avoid starting the browser just to reset the session
141
+ @browser.manage.delete_all_cookies if @browser
142
+ end
143
+
144
+ def within_frame(frame_id)
145
+ old_window = browser.window_handle
146
+ browser.switch_to.frame(frame_id)
147
+ yield
148
+ browser.switch_to.window old_window
149
+ end
150
+
151
+ def within_window(handle, &blk)
152
+ browser.switch_to.window(handle, &blk)
153
+ end
154
+
155
+ private
156
+
157
+ def url(path)
158
+ rack_server.url(path)
159
+ end
160
+
161
+ end