david-capybara 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
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