david-capybara 0.3.8

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.
Files changed (88) hide show
  1. data/History.txt +87 -0
  2. data/README.rdoc +389 -0
  3. data/lib/capybara.rb +52 -0
  4. data/lib/capybara/cucumber.rb +32 -0
  5. data/lib/capybara/driver/base.rb +48 -0
  6. data/lib/capybara/driver/celerity_driver.rb +143 -0
  7. data/lib/capybara/driver/culerity_driver.rb +25 -0
  8. data/lib/capybara/driver/rack_test_driver.rb +272 -0
  9. data/lib/capybara/driver/selenium_driver.rb +156 -0
  10. data/lib/capybara/dsl.rb +60 -0
  11. data/lib/capybara/node.rb +60 -0
  12. data/lib/capybara/rails.rb +17 -0
  13. data/lib/capybara/save_and_open_page.rb +33 -0
  14. data/lib/capybara/searchable.rb +54 -0
  15. data/lib/capybara/server.rb +114 -0
  16. data/lib/capybara/session.rb +262 -0
  17. data/lib/capybara/spec/driver.rb +162 -0
  18. data/lib/capybara/spec/fixtures/capybara.jpg +0 -0
  19. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  20. data/lib/capybara/spec/public/canvas.jpg +0 -0
  21. data/lib/capybara/spec/public/jquery-ui.js +35 -0
  22. data/lib/capybara/spec/public/jquery.js +19 -0
  23. data/lib/capybara/spec/public/test.js +33 -0
  24. data/lib/capybara/spec/session.rb +81 -0
  25. data/lib/capybara/spec/session/all_spec.rb +69 -0
  26. data/lib/capybara/spec/session/attach_file_spec.rb +64 -0
  27. data/lib/capybara/spec/session/check_spec.rb +67 -0
  28. data/lib/capybara/spec/session/choose_spec.rb +26 -0
  29. data/lib/capybara/spec/session/click_button_spec.rb +236 -0
  30. data/lib/capybara/spec/session/click_link_spec.rb +118 -0
  31. data/lib/capybara/spec/session/click_spec.rb +24 -0
  32. data/lib/capybara/spec/session/current_url_spec.rb +8 -0
  33. data/lib/capybara/spec/session/fill_in_spec.rb +108 -0
  34. data/lib/capybara/spec/session/find_button_spec.rb +16 -0
  35. data/lib/capybara/spec/session/find_by_id_spec.rb +16 -0
  36. data/lib/capybara/spec/session/find_field_spec.rb +22 -0
  37. data/lib/capybara/spec/session/find_link_spec.rb +17 -0
  38. data/lib/capybara/spec/session/find_spec.rb +57 -0
  39. data/lib/capybara/spec/session/has_button_spec.rb +32 -0
  40. data/lib/capybara/spec/session/has_content_spec.rb +106 -0
  41. data/lib/capybara/spec/session/has_css_spec.rb +107 -0
  42. data/lib/capybara/spec/session/has_field_spec.rb +96 -0
  43. data/lib/capybara/spec/session/has_link_spec.rb +33 -0
  44. data/lib/capybara/spec/session/has_select_spec.rb +89 -0
  45. data/lib/capybara/spec/session/has_table_spec.rb +96 -0
  46. data/lib/capybara/spec/session/has_xpath_spec.rb +123 -0
  47. data/lib/capybara/spec/session/headers.rb +19 -0
  48. data/lib/capybara/spec/session/javascript.rb +204 -0
  49. data/lib/capybara/spec/session/locate_spec.rb +59 -0
  50. data/lib/capybara/spec/session/select_spec.rb +83 -0
  51. data/lib/capybara/spec/session/uncheck_spec.rb +21 -0
  52. data/lib/capybara/spec/session/unselect_spec.rb +54 -0
  53. data/lib/capybara/spec/session/within_spec.rb +153 -0
  54. data/lib/capybara/spec/test_app.rb +75 -0
  55. data/lib/capybara/spec/views/buttons.erb +4 -0
  56. data/lib/capybara/spec/views/fieldsets.erb +29 -0
  57. data/lib/capybara/spec/views/form.erb +235 -0
  58. data/lib/capybara/spec/views/frame_one.erb +8 -0
  59. data/lib/capybara/spec/views/frame_two.erb +8 -0
  60. data/lib/capybara/spec/views/postback.erb +13 -0
  61. data/lib/capybara/spec/views/tables.erb +122 -0
  62. data/lib/capybara/spec/views/with_html.erb +46 -0
  63. data/lib/capybara/spec/views/with_js.erb +39 -0
  64. data/lib/capybara/spec/views/with_scope.erb +36 -0
  65. data/lib/capybara/spec/views/with_simple_html.erb +1 -0
  66. data/lib/capybara/spec/views/within_frames.erb +10 -0
  67. data/lib/capybara/version.rb +3 -0
  68. data/lib/capybara/wait_until.rb +28 -0
  69. data/lib/capybara/xpath.rb +180 -0
  70. data/spec/capybara_spec.rb +18 -0
  71. data/spec/driver/celerity_driver_spec.rb +16 -0
  72. data/spec/driver/culerity_driver_spec.rb +12 -0
  73. data/spec/driver/rack_test_driver_spec.rb +17 -0
  74. data/spec/driver/remote_culerity_driver_spec.rb +23 -0
  75. data/spec/driver/remote_selenium_driver_spec.rb +18 -0
  76. data/spec/driver/selenium_driver_spec.rb +11 -0
  77. data/spec/dsl_spec.rb +140 -0
  78. data/spec/save_and_open_page_spec.rb +43 -0
  79. data/spec/searchable_spec.rb +66 -0
  80. data/spec/server_spec.rb +53 -0
  81. data/spec/session/celerity_session_spec.rb +27 -0
  82. data/spec/session/culerity_session_spec.rb +25 -0
  83. data/spec/session/rack_test_session_spec.rb +33 -0
  84. data/spec/session/selenium_session_spec.rb +25 -0
  85. data/spec/spec_helper.rb +19 -0
  86. data/spec/wait_until_spec.rb +28 -0
  87. data/spec/xpath_spec.rb +180 -0
  88. metadata +260 -0
data/lib/capybara.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'timeout'
2
+ require 'nokogiri'
3
+
4
+ module Capybara
5
+ class CapybaraError < StandardError; end
6
+ class DriverNotFoundError < CapybaraError; end
7
+ class ElementNotFound < CapybaraError; end
8
+ class OptionNotFound < ElementNotFound; end
9
+ class UnselectNotAllowed < CapybaraError; end
10
+ class NotSupportedByDriverError < CapybaraError; end
11
+ class TimeoutError < CapybaraError; end
12
+ class LocateHiddenElementError < CapybaraError; end
13
+ class InfiniteRedirectError < TimeoutError; end
14
+
15
+ class << self
16
+ attr_accessor :debug, :asset_root, :app_host, :run_server, :default_host
17
+ attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements
18
+
19
+ def default_selector
20
+ @default_selector ||= :xpath
21
+ end
22
+
23
+ def default_wait_time
24
+ @default_wait_time ||= 2
25
+ end
26
+
27
+ def log(message)
28
+ puts "[capybara] #{message}" if debug
29
+ true
30
+ end
31
+ end
32
+
33
+ autoload :Server, 'capybara/server'
34
+ autoload :Session, 'capybara/session'
35
+ autoload :Node, 'capybara/node'
36
+ autoload :XPath, 'capybara/xpath'
37
+ autoload :Searchable, 'capybara/searchable'
38
+ autoload :VERSION, 'capybara/version'
39
+
40
+ module Driver
41
+ autoload :Base, 'capybara/driver/base'
42
+ autoload :RackTest, 'capybara/driver/rack_test_driver'
43
+ autoload :Celerity, 'capybara/driver/celerity_driver'
44
+ autoload :Culerity, 'capybara/driver/culerity_driver'
45
+ autoload :Selenium, 'capybara/driver/selenium_driver'
46
+ end
47
+ end
48
+
49
+ Capybara.run_server = true
50
+ Capybara.default_selector = :xpath
51
+ Capybara.default_wait_time = 2
52
+ Capybara.ignore_hidden_elements = false
@@ -0,0 +1,32 @@
1
+ require 'capybara'
2
+ require 'capybara/dsl'
3
+
4
+ World(Capybara)
5
+
6
+ After do
7
+ Capybara.reset_sessions!
8
+ end
9
+
10
+ Before('@javascript') do
11
+ Capybara.current_driver = Capybara.javascript_driver
12
+ end
13
+
14
+ Before('@selenium') do
15
+ Capybara.current_driver = :selenium
16
+ end
17
+
18
+ Before('@celerity') do
19
+ Capybara.current_driver = :celerity
20
+ end
21
+
22
+ Before('@culerity') do
23
+ Capybara.current_driver = :culerity
24
+ end
25
+
26
+ Before('@rack_test') do
27
+ Capybara.current_driver = :rack_test
28
+ end
29
+
30
+ After do
31
+ Capybara.use_default_driver
32
+ end
@@ -0,0 +1,48 @@
1
+ class Capybara::Driver::Base
2
+ def current_url
3
+ raise NotImplementedError
4
+ end
5
+
6
+ def visit(path)
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def find(query)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def evaluate_script(script)
15
+ raise Capybara::NotSupportedByDriverError
16
+ end
17
+
18
+ def wait?
19
+ false
20
+ end
21
+
22
+ def wait_until *args
23
+ end
24
+
25
+ def response_headers
26
+ raise Capybara::NotSupportedByDriverError
27
+ end
28
+
29
+ def body
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def within_frame(frame_id)
34
+ raise Capybara::NotSupportedByDriverError
35
+ end
36
+
37
+ def source
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def cleanup!
42
+ end
43
+
44
+ def has_shortcircuit_timeout?
45
+ false
46
+ end
47
+
48
+ end
@@ -0,0 +1,143 @@
1
+ class Capybara::Driver::Celerity < Capybara::Driver::Base
2
+ class Node < Capybara::Node
3
+ def text
4
+ node.text
5
+ end
6
+
7
+ def [](name)
8
+ value = if name.to_sym == :class
9
+ node.class_name
10
+ else
11
+ node.send(name.to_sym)
12
+ end
13
+ return value if value and not value.to_s.empty?
14
+ end
15
+
16
+ def value
17
+ if tag_name == "select" and node.multiple?
18
+ node.selected_options
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def set(value)
25
+ node.set(value)
26
+ end
27
+
28
+ def select(option)
29
+ node.select(option)
30
+ rescue
31
+ options = all(:xpath, "//option").map { |o| "'#{o.text}'" }.join(', ')
32
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
33
+ end
34
+
35
+ def unselect(option)
36
+ unless node.multiple?
37
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
38
+ end
39
+
40
+ # FIXME: couldn't find a clean way to unselect, so clear and reselect
41
+ selected_options = node.selected_options
42
+ if unselect_option = selected_options.detect { |value| value == option } ||
43
+ selected_options.detect { |value| value.index(option) }
44
+ node.clear
45
+ (selected_options - [unselect_option]).each { |value| node.select_value(value) }
46
+ else
47
+ options = all(:xpath, "//option").map { |o| "'#{o.text}'" }.join(', ')
48
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
49
+ end
50
+ end
51
+
52
+ def click
53
+ node.click
54
+ end
55
+
56
+ def drag_to(element)
57
+ node.fire_event('mousedown')
58
+ element.node.fire_event('mousemove')
59
+ element.node.fire_event('mouseup')
60
+ end
61
+
62
+ def tag_name
63
+ # FIXME: this might be the dumbest way ever of getting the tag name
64
+ # there has to be something better...
65
+ node.to_xml[/^\s*<([a-z0-9\-\:]+)/, 1]
66
+ end
67
+
68
+ def visible?
69
+ node.visible?
70
+ end
71
+
72
+ def path
73
+ node.xpath
74
+ end
75
+
76
+ def trigger(event)
77
+ node.fire_event(event.to_s)
78
+ end
79
+
80
+ private
81
+
82
+ def all_unfiltered(locator)
83
+ noko_node = Nokogiri::HTML(driver.body).xpath(node.xpath).first
84
+ all_nodes = noko_node.xpath(locator).map { |n| n.path }.join(' | ')
85
+ driver.find(all_nodes)
86
+ end
87
+
88
+ end
89
+
90
+ attr_reader :app, :rack_server
91
+
92
+ def initialize(app)
93
+ @app = app
94
+ @rack_server = Capybara::Server.new(@app)
95
+ @rack_server.boot if Capybara.run_server
96
+ end
97
+
98
+ def visit(path)
99
+ browser.goto(url(path))
100
+ end
101
+
102
+ def current_url
103
+ browser.url
104
+ end
105
+
106
+ def source
107
+ browser.html
108
+ end
109
+
110
+ def body
111
+ browser.document.as_xml
112
+ end
113
+
114
+ def response_headers
115
+ browser.response_headers
116
+ end
117
+
118
+ def find(selector)
119
+ browser.elements_by_xpath(selector).map { |node| Node.new(self, node) }
120
+ end
121
+
122
+ def wait?; true; end
123
+
124
+ def evaluate_script(script)
125
+ browser.execute_script "#{script}"
126
+ end
127
+
128
+ def browser
129
+ unless @_browser
130
+ require 'celerity'
131
+ @_browser = ::Celerity::Browser.new(:browser => :firefox, :log_level => :off)
132
+ end
133
+
134
+ @_browser
135
+ end
136
+
137
+ private
138
+
139
+ def url(path)
140
+ rack_server.url(path)
141
+ end
142
+
143
+ end
@@ -0,0 +1,25 @@
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, {:browser => :firefox, :log_level => :off}
18
+ at_exit do
19
+ @_browser.exit
20
+ end
21
+ end
22
+ @_browser
23
+ end
24
+
25
+ end
@@ -0,0 +1,272 @@
1
+ require 'rack/test'
2
+ require 'mime/types'
3
+ require 'nokogiri'
4
+ require 'cgi'
5
+
6
+ class Capybara::Driver::RackTest < Capybara::Driver::Base
7
+ class Node < Capybara::Node
8
+ def text
9
+ node.text
10
+ end
11
+
12
+ def [](name)
13
+ attr_name = name.to_s
14
+ case
15
+ when 'select' == tag_name && 'value' == attr_name
16
+ if node['multiple'] == 'multiple'
17
+ node.xpath(".//option[@selected='selected']").map { |option| option.content }
18
+ else
19
+ option = node.xpath(".//option[@selected='selected']").first || node.xpath(".//option").first
20
+ option.content if option
21
+ end
22
+ when 'input' == tag_name && 'checkbox' == type && 'checked' == attr_name
23
+ node[attr_name] == 'checked' ? true : false
24
+ else
25
+ node[attr_name]
26
+ end
27
+ end
28
+
29
+
30
+ def set(value)
31
+ if tag_name == 'input' and type == 'radio'
32
+ driver.html.xpath("//input[@name=#{Capybara::XPath.escape(self[:name])}]").each { |node| node.remove_attribute("checked") }
33
+ node['checked'] = 'checked'
34
+ elsif tag_name == 'input' and type == 'checkbox'
35
+ if value && !node['checked']
36
+ node['checked'] = 'checked'
37
+ elsif !value && node['checked']
38
+ node.remove_attribute('checked')
39
+ end
40
+ elsif tag_name == 'input'
41
+ node['value'] = value.to_s
42
+ elsif tag_name == "textarea"
43
+ node.content = value.to_s
44
+ end
45
+ end
46
+
47
+ def select(option)
48
+ if node['multiple'] != 'multiple'
49
+ node.xpath(".//option[@selected]").each { |node| node.remove_attribute("selected") }
50
+ end
51
+
52
+ if option_node = node.xpath(".//option[text()=#{Capybara::XPath.escape(option)}]").first ||
53
+ node.xpath(".//option[contains(.,#{Capybara::XPath.escape(option)})]").first
54
+ option_node["selected"] = 'selected'
55
+ else
56
+ options = node.xpath(".//option").map { |o| "'#{o.text}'" }.join(', ')
57
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
58
+ end
59
+ end
60
+
61
+ def unselect(option)
62
+ if node['multiple'] != 'multiple'
63
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
64
+ end
65
+
66
+ if option_node = node.xpath(".//option[text()=#{Capybara::XPath.escape(option)}]").first ||
67
+ node.xpath(".//option[contains(.,#{Capybara::XPath.escape(option)})]").first
68
+ option_node.remove_attribute('selected')
69
+ else
70
+ options = node.xpath(".//option").map { |o| "'#{o.text}'" }.join(', ')
71
+ raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
72
+ end
73
+ end
74
+
75
+ def click
76
+ if tag_name == 'a' || tag_name == 'area'
77
+ method = self["data-method"] || :get
78
+ driver.process(method, self[:href].to_s)
79
+ elsif (tag_name == 'input' or tag_name == 'button') and %w(submit image).include?(type)
80
+ Form.new(driver, form).submit(self)
81
+ end
82
+ end
83
+
84
+ def tag_name
85
+ node.node_name
86
+ end
87
+
88
+ def visible?
89
+ node.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none')]").size == 0
90
+ end
91
+
92
+ def path
93
+ node.path
94
+ end
95
+
96
+ private
97
+
98
+ def all_unfiltered(locator)
99
+ node.xpath(locator).map { |n| self.class.new(driver, n) }
100
+ end
101
+
102
+ def type
103
+ node[:type]
104
+ end
105
+
106
+ def form
107
+ node.ancestors('form').first
108
+ end
109
+ end
110
+
111
+ class Form < Node
112
+ def params(button)
113
+ params = {}
114
+
115
+ node.xpath(".//input[@type!='radio' and @type!='checkbox' and @type!='submit']").map do |input|
116
+ merge_param!(params, input['name'].to_s, input['value'].to_s)
117
+ end
118
+ node.xpath(".//textarea").map do |textarea|
119
+ merge_param!(params, textarea['name'].to_s, textarea.text.to_s)
120
+ end
121
+ node.xpath(".//input[@type='radio' or @type='checkbox']").map do |input|
122
+ merge_param!(params, input['name'].to_s, input['value'].to_s) if input['checked']
123
+ end
124
+ node.xpath(".//select").map do |select|
125
+ if select['multiple'] == 'multiple'
126
+ options = select.xpath(".//option[@selected]")
127
+ options.each do |option|
128
+ merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s)
129
+ end
130
+ else
131
+ option = select.xpath(".//option[@selected]").first
132
+ option ||= select.xpath('.//option').first
133
+ merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s) if option
134
+ end
135
+ end
136
+ node.xpath(".//input[@type='file']").map do |input|
137
+ unless input['value'].to_s.empty?
138
+ if multipart?
139
+ content_type = MIME::Types.type_for(input['value'].to_s).first.to_s
140
+ file = Rack::Test::UploadedFile.new(input['value'].to_s, content_type)
141
+ merge_param!(params, input['name'].to_s, file)
142
+ else
143
+ merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
144
+ end
145
+ end
146
+ end
147
+ merge_param!(params, button[:name], button[:value] || "") if button[:name]
148
+ params
149
+ end
150
+
151
+ def submit(button)
152
+ driver.submit(method, node['action'].to_s, params(button))
153
+ end
154
+
155
+ def multipart?
156
+ self[:enctype] == "multipart/form-data"
157
+ end
158
+
159
+ private
160
+
161
+ def method
162
+ self[:method] =~ /post/i ? :post : :get
163
+ end
164
+
165
+ def merge_param!(params, key, value)
166
+ collection = key.sub!(/\[\]$/, '')
167
+ if collection
168
+ if params[key]
169
+ params[key] << value
170
+ else
171
+ params[key] = [value]
172
+ end
173
+ else
174
+ params[key] = value
175
+ end
176
+ end
177
+ end
178
+
179
+ include ::Rack::Test::Methods
180
+ attr_reader :app
181
+
182
+ alias_method :response, :last_response
183
+ alias_method :request, :last_request
184
+
185
+ def initialize(app)
186
+ raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
187
+ @app = app
188
+ end
189
+
190
+ def visit(path, attributes = {})
191
+ process(:get, path, attributes)
192
+ end
193
+
194
+ def process(method, path, attributes = {})
195
+ return if path.gsub(/^#{current_path}/, '') =~ /^#/
196
+ send(method, path, attributes, env)
197
+ follow_redirects!
198
+ end
199
+
200
+ def current_url
201
+ request.url rescue ""
202
+ end
203
+
204
+ def response_headers
205
+ response.headers
206
+ end
207
+
208
+ def submit(method, path, attributes)
209
+ path = current_path if not path or path.empty?
210
+ send(method, path, attributes, env)
211
+ follow_redirects!
212
+ end
213
+
214
+ def find(selector)
215
+ html.xpath(selector).map { |node| Node.new(self, node) }
216
+ end
217
+
218
+ def body
219
+ @body ||= response.body
220
+ end
221
+
222
+ def html
223
+ @html ||= Nokogiri::HTML(body)
224
+ end
225
+ alias_method :source, :body
226
+
227
+ def get(*args, &block); reset_cache; super; end
228
+ def post(*args, &block); reset_cache; super; end
229
+ def put(*args, &block); reset_cache; super; end
230
+ def delete(*args, &block); reset_cache; super; end
231
+
232
+ private
233
+
234
+ def reset_cache
235
+ @body = nil
236
+ @html = nil
237
+ end
238
+
239
+ def build_rack_mock_session # :nodoc:
240
+ Rack::MockSession.new(app, Capybara.default_host || "www.example.com")
241
+ end
242
+
243
+ def current_path
244
+ request.path rescue ""
245
+ end
246
+
247
+ def follow_redirects!
248
+ Capybara::WaitUntil.timeout(4) do
249
+ redirect = response.redirect?
250
+ follow_redirect! if redirect
251
+ not redirect
252
+ end
253
+ rescue Capybara::TimeoutError
254
+ raise Capybara::InfiniteRedirectError, "infinite redirect detected!"
255
+ end
256
+
257
+ def env
258
+ env = {}
259
+ begin
260
+ env["HTTP_REFERER"] = request.url
261
+ rescue Rack::Test::Error
262
+ # no request yet
263
+ end
264
+ env
265
+ end
266
+
267
+ def reset_cache
268
+ @body = nil
269
+ @html = nil
270
+ end
271
+
272
+ end