h13ronim-capybara 0.3.0.1

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 +34 -0
  2. data/Manifest.txt +87 -0
  3. data/README.rdoc +405 -0
  4. data/Rakefile +29 -0
  5. data/config.ru +6 -0
  6. data/lib/capybara.rb +58 -0
  7. data/lib/capybara/cucumber.rb +32 -0
  8. data/lib/capybara/driver/base.rb +37 -0
  9. data/lib/capybara/driver/celerity_driver.rb +143 -0
  10. data/lib/capybara/driver/culerity_driver.rb +25 -0
  11. data/lib/capybara/driver/rack_test_driver.rb +250 -0
  12. data/lib/capybara/driver/selenium_driver.rb +149 -0
  13. data/lib/capybara/dsl.rb +60 -0
  14. data/lib/capybara/node.rb +60 -0
  15. data/lib/capybara/rails.rb +17 -0
  16. data/lib/capybara/save_and_open_page.rb +33 -0
  17. data/lib/capybara/searchable.rb +53 -0
  18. data/lib/capybara/server.rb +114 -0
  19. data/lib/capybara/session.rb +275 -0
  20. data/lib/capybara/wait_until.rb +23 -0
  21. data/lib/capybara/xpath.rb +176 -0
  22. data/script/console +10 -0
  23. data/script/destroy +14 -0
  24. data/script/generate +14 -0
  25. data/spec/capybara_spec.rb +18 -0
  26. data/spec/driver/celerity_driver_spec.rb +17 -0
  27. data/spec/driver/culerity_driver_spec.rb +12 -0
  28. data/spec/driver/rack_test_driver_spec.rb +11 -0
  29. data/spec/driver/remote_culerity_driver_spec.rb +23 -0
  30. data/spec/driver/remote_selenium_driver_spec.rb +18 -0
  31. data/spec/driver/selenium_driver_spec.rb +11 -0
  32. data/spec/drivers_spec.rb +127 -0
  33. data/spec/dsl/all_spec.rb +69 -0
  34. data/spec/dsl/attach_file_spec.rb +64 -0
  35. data/spec/dsl/check_spec.rb +39 -0
  36. data/spec/dsl/choose_spec.rb +26 -0
  37. data/spec/dsl/click_button_spec.rb +236 -0
  38. data/spec/dsl/click_link_spec.rb +108 -0
  39. data/spec/dsl/click_spec.rb +24 -0
  40. data/spec/dsl/current_url_spec.rb +8 -0
  41. data/spec/dsl/fill_in_spec.rb +103 -0
  42. data/spec/dsl/find_button_spec.rb +16 -0
  43. data/spec/dsl/find_by_id_spec.rb +16 -0
  44. data/spec/dsl/find_field_spec.rb +22 -0
  45. data/spec/dsl/find_link_spec.rb +17 -0
  46. data/spec/dsl/find_spec.rb +57 -0
  47. data/spec/dsl/has_button_spec.rb +32 -0
  48. data/spec/dsl/has_content_spec.rb +101 -0
  49. data/spec/dsl/has_css_spec.rb +107 -0
  50. data/spec/dsl/has_field_spec.rb +96 -0
  51. data/spec/dsl/has_link_spec.rb +33 -0
  52. data/spec/dsl/has_xpath_spec.rb +123 -0
  53. data/spec/dsl/locate_spec.rb +59 -0
  54. data/spec/dsl/select_spec.rb +71 -0
  55. data/spec/dsl/uncheck_spec.rb +21 -0
  56. data/spec/dsl/within_spec.rb +153 -0
  57. data/spec/dsl_spec.rb +140 -0
  58. data/spec/fixtures/capybara.jpg +0 -0
  59. data/spec/fixtures/test_file.txt +1 -0
  60. data/spec/public/jquery-ui.js +35 -0
  61. data/spec/public/jquery.js +19 -0
  62. data/spec/public/test.js +33 -0
  63. data/spec/save_and_open_page_spec.rb +43 -0
  64. data/spec/searchable_spec.rb +61 -0
  65. data/spec/server_spec.rb +53 -0
  66. data/spec/session/celerity_session_spec.rb +27 -0
  67. data/spec/session/culerity_session_spec.rb +25 -0
  68. data/spec/session/rack_test_session_spec.rb +25 -0
  69. data/spec/session/selenium_session_spec.rb +25 -0
  70. data/spec/session_spec.rb +77 -0
  71. data/spec/session_with_headers_support_spec.rb +13 -0
  72. data/spec/session_with_javascript_support_spec.rb +192 -0
  73. data/spec/session_without_headers_support_spec.rb +15 -0
  74. data/spec/session_without_javascript_support_spec.rb +15 -0
  75. data/spec/spec_helper.rb +27 -0
  76. data/spec/test_app.rb +71 -0
  77. data/spec/views/buttons.erb +4 -0
  78. data/spec/views/fieldsets.erb +29 -0
  79. data/spec/views/form.erb +232 -0
  80. data/spec/views/postback.erb +13 -0
  81. data/spec/views/tables.erb +122 -0
  82. data/spec/views/with_html.erb +38 -0
  83. data/spec/views/with_js.erb +39 -0
  84. data/spec/views/with_scope.erb +36 -0
  85. data/spec/views/with_simple_html.erb +1 -0
  86. data/spec/wait_until_spec.rb +28 -0
  87. data/spec/xpath_spec.rb +271 -0
  88. metadata +282 -0
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+
3
+ gem 'hoe', '>= 2.1.0'
4
+ require 'hoe'
5
+
6
+ Hoe.plugin :newgem
7
+
8
+ # Generate all the Rake tasks
9
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
10
+ Hoe.spec 'capybara' do
11
+ developer 'Jonas Nicklas', 'jonas.nicklas@gmail.com'
12
+
13
+ self.readme_file = 'README.rdoc'
14
+ self.extra_rdoc_files = Dir['*.rdoc']
15
+
16
+ self.extra_deps = [
17
+ ['nokogiri', '>= 1.3.3'],
18
+ ['mime-types', '>= 1.16'],
19
+ ['culerity', '>= 0.2.4'],
20
+ ['selenium-webdriver', '>= 0.0.3'],
21
+ ['rack', '>= 1.0.0'],
22
+ ['rack-test', '>= 0.5.2'],
23
+ ]
24
+
25
+ self.extra_dev_deps = [
26
+ ['sinatra', '>= 0.9.4'],
27
+ ['rspec', '>= 1.2.9']
28
+ ]
29
+ end
@@ -0,0 +1,6 @@
1
+ ## This is not needed for Thin > 1.0.0
2
+ ENV['RACK_ENV'] = "production"
3
+
4
+ require File.expand_path('spec/test_app', File.dirname(__FILE__))
5
+
6
+ run TestApp
@@ -0,0 +1,58 @@
1
+ require 'timeout'
2
+ require 'nokogiri'
3
+
4
+ module Capybara
5
+ VERSION = '0.3.5'
6
+
7
+ class CapybaraError < StandardError; end
8
+ class DriverNotFoundError < CapybaraError; end
9
+ class ElementNotFound < CapybaraError; end
10
+ class OptionNotFound < ElementNotFound; end
11
+ class UnselectNotAllowed < CapybaraError; end
12
+ class NotSupportedByDriverError < CapybaraError; end
13
+ class TimeoutError < CapybaraError; end
14
+ class LocateHiddenElementError < CapybaraError; end
15
+ class InfiniteRedirectError < TimeoutError; end
16
+
17
+ class << self
18
+ attr_accessor :debug, :asset_root, :app_host, :run_server, :default_host
19
+ attr_accessor :default_selector, :default_wait_time, :default_timeout, :ignore_hidden_elements
20
+
21
+ def default_selector
22
+ @default_selector ||= :xpath
23
+ end
24
+
25
+ def default_wait_time
26
+ @default_wait_time ||= 2
27
+ end
28
+
29
+ def default_timeout
30
+ @default_timeout ||= 4
31
+ end
32
+
33
+ def log(message)
34
+ puts "[capybara] #{message}" if debug
35
+ true
36
+ end
37
+ end
38
+
39
+ autoload :Server, 'capybara/server'
40
+ autoload :Session, 'capybara/session'
41
+ autoload :Node, 'capybara/node'
42
+ autoload :XPath, 'capybara/xpath'
43
+ autoload :Searchable, 'capybara/searchable'
44
+
45
+ module Driver
46
+ autoload :Base, 'capybara/driver/base'
47
+ autoload :RackTest, 'capybara/driver/rack_test_driver'
48
+ autoload :Celerity, 'capybara/driver/celerity_driver'
49
+ autoload :Culerity, 'capybara/driver/culerity_driver'
50
+ autoload :Selenium, 'capybara/driver/selenium_driver'
51
+ end
52
+ end
53
+
54
+ Capybara.run_server = true
55
+ Capybara.default_selector = :xpath
56
+ Capybara.default_wait_time = 2
57
+ Capybara.default_timeout = 4
58
+ 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,37 @@
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 response_headers
23
+ raise Capybara::NotSupportedByDriverError
24
+ end
25
+
26
+ def body
27
+ raise NotImplementedError
28
+ end
29
+
30
+ def source
31
+ raise NotImplementedError
32
+ end
33
+
34
+ def cleanup!
35
+ end
36
+
37
+ 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,250 @@
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='#{self[:name]}']").each { |node| node.remove_attribute("checked") }
33
+ node['checked'] = 'checked'
34
+ elsif tag_name == 'input' and type == 'checkbox'
35
+ if value
36
+ node['checked'] = 'checked'
37
+ else
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()='#{option}']").first ||
53
+ node.xpath(".//option[contains(.,'#{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()='#{option}']").first ||
67
+ node.xpath(".//option[contains(.,'#{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'
77
+ driver.visit(self[:href].to_s)
78
+ elsif (tag_name == 'input' or tag_name == 'button') and %w(submit image).include?(type)
79
+ Form.new(driver, form).submit(self)
80
+ end
81
+ end
82
+
83
+ def tag_name
84
+ node.node_name
85
+ end
86
+
87
+ def visible?
88
+ node.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none')]").size == 0
89
+ end
90
+
91
+ def path
92
+ node.path
93
+ end
94
+
95
+ private
96
+
97
+ def all_unfiltered(locator)
98
+ node.xpath(locator).map { |n| self.class.new(driver, n) }
99
+ end
100
+
101
+ def type
102
+ node[:type]
103
+ end
104
+
105
+ def form
106
+ node.ancestors('form').first
107
+ end
108
+ end
109
+
110
+ class Form < Node
111
+ def params(button)
112
+ params = {}
113
+
114
+ node.xpath(".//input[@type!='radio' and @type!='checkbox' and @type!='submit']").map do |input|
115
+ merge_param!(params, input['name'].to_s, input['value'].to_s)
116
+ end
117
+ node.xpath(".//textarea").map do |textarea|
118
+ merge_param!(params, textarea['name'].to_s, textarea.text.to_s)
119
+ end
120
+ node.xpath(".//input[@type='radio' or @type='checkbox']").map do |input|
121
+ merge_param!(params, input['name'].to_s, input['value'].to_s) if input['checked']
122
+ end
123
+ node.xpath(".//select").map do |select|
124
+ if select['multiple'] == 'multiple'
125
+ options = select.xpath(".//option[@selected]")
126
+ options.each do |option|
127
+ merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s)
128
+ end
129
+ else
130
+ option = select.xpath(".//option[@selected]").first
131
+ option ||= select.xpath('.//option').first
132
+ merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s) if option
133
+ end
134
+ end
135
+ node.xpath(".//input[@type='file']").map do |input|
136
+ unless input['value'].to_s.empty?
137
+ if multipart?
138
+ content_type = MIME::Types.type_for(input['value'].to_s).first.to_s
139
+ file = Rack::Test::UploadedFile.new(input['value'].to_s, content_type)
140
+ merge_param!(params, input['name'].to_s, file)
141
+ else
142
+ merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
143
+ end
144
+ end
145
+ end
146
+ merge_param!(params, button[:name], button[:value] || "") if button[:name]
147
+ params
148
+ end
149
+
150
+ def submit(button)
151
+ driver.submit(method, node['action'].to_s, params(button))
152
+ end
153
+
154
+ def multipart?
155
+ self[:enctype] == "multipart/form-data"
156
+ end
157
+
158
+ private
159
+
160
+ def method
161
+ self[:method] =~ /post/i ? :post : :get
162
+ end
163
+
164
+ def merge_param!(params, key, value)
165
+ collection = key.sub!(/\[\]$/, '')
166
+ if collection
167
+ if params[key]
168
+ params[key] << value
169
+ else
170
+ params[key] = [value]
171
+ end
172
+ else
173
+ params[key] = value
174
+ end
175
+ end
176
+ end
177
+
178
+ include ::Rack::Test::Methods
179
+ attr_reader :app, :html, :body
180
+
181
+ alias_method :response, :last_response
182
+ alias_method :request, :last_request
183
+ alias_method :source, :body
184
+
185
+ def initialize(app)
186
+ @app = app
187
+ end
188
+
189
+ def visit(path, attributes = {})
190
+ return if path.gsub(/^#{current_path}/, '') =~ /^#/
191
+ get(path, attributes, env)
192
+ follow_redirects!
193
+ cache_body
194
+ end
195
+
196
+ def current_url
197
+ request.url rescue ""
198
+ end
199
+
200
+ def response_headers
201
+ response.headers
202
+ end
203
+
204
+ def submit(method, path, attributes)
205
+ path = current_path if not path or path.empty?
206
+ send(method, path, attributes, env)
207
+ follow_redirects!
208
+ cache_body
209
+ end
210
+
211
+ def find(selector)
212
+ html.xpath(selector).map { |node| Node.new(self, node) }
213
+ end
214
+
215
+ private
216
+
217
+ def build_rack_mock_session # :nodoc:
218
+ Rack::MockSession.new(app, Capybara.default_host || "www.example.com")
219
+ end
220
+
221
+ def current_path
222
+ request.path rescue ""
223
+ end
224
+
225
+ def follow_redirects!
226
+ Capybara::WaitUntil.timeout(Capybara.default_timeout) do
227
+ redirect = response.redirect?
228
+ follow_redirect! if redirect
229
+ not redirect
230
+ end
231
+ rescue Capybara::TimeoutError
232
+ raise Capybara::InfiniteRedirectError, "infinite redirect detected!"
233
+ end
234
+
235
+ def env
236
+ env = {}
237
+ begin
238
+ env["HTTP_REFERER"] = request.url
239
+ rescue Rack::Test::Error
240
+ # no request yet
241
+ end
242
+ env
243
+ end
244
+
245
+ def cache_body
246
+ @body = response.body
247
+ @html = Nokogiri::HTML(body)
248
+ end
249
+
250
+ end