capybara 0.2.0 → 0.3.0

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 (71) hide show
  1. data/Manifest.txt +39 -0
  2. data/README.rdoc +181 -57
  3. data/Rakefile +7 -16
  4. data/config.ru +6 -0
  5. data/lib/capybara.rb +19 -9
  6. data/lib/capybara/cucumber.rb +4 -0
  7. data/lib/capybara/driver/base.rb +25 -8
  8. data/lib/capybara/driver/celerity_driver.rb +108 -0
  9. data/lib/capybara/driver/culerity_driver.rb +1 -70
  10. data/lib/capybara/driver/rack_test_driver.rb +57 -25
  11. data/lib/capybara/driver/selenium_driver.rb +28 -10
  12. data/lib/capybara/dsl.rb +7 -4
  13. data/lib/capybara/node.rb +50 -29
  14. data/lib/capybara/rails.rb +1 -1
  15. data/lib/capybara/save_and_open_page.rb +1 -1
  16. data/lib/capybara/searchable.rb +45 -0
  17. data/lib/capybara/server.rb +11 -4
  18. data/lib/capybara/session.rb +94 -94
  19. data/lib/capybara/wait_until.rb +23 -0
  20. data/lib/capybara/xpath.rb +55 -41
  21. data/spec/capybara_spec.rb +18 -0
  22. data/spec/driver/celerity_driver_spec.rb +17 -0
  23. data/spec/driver/culerity_driver_spec.rb +3 -0
  24. data/spec/driver/rack_test_driver_spec.rb +3 -0
  25. data/spec/driver/remote_culerity_driver_spec.rb +19 -0
  26. data/spec/driver/remote_selenium_driver_spec.rb +18 -0
  27. data/spec/driver/selenium_driver_spec.rb +2 -0
  28. data/spec/drivers_spec.rb +51 -5
  29. data/spec/dsl/all_spec.rb +38 -0
  30. data/spec/dsl/attach_file_spec.rb +66 -0
  31. data/spec/dsl/check_spec.rb +28 -0
  32. data/spec/dsl/choose_spec.rb +28 -0
  33. data/spec/dsl/click_button_spec.rb +183 -0
  34. data/spec/dsl/click_link_spec.rb +88 -0
  35. data/spec/dsl/click_spec.rb +26 -0
  36. data/spec/dsl/current_url_spec.rb +10 -0
  37. data/spec/dsl/fill_in_spec.rb +83 -0
  38. data/spec/dsl/find_button_spec.rb +18 -0
  39. data/spec/dsl/find_field_spec.rb +24 -0
  40. data/spec/dsl/find_link_spec.rb +19 -0
  41. data/spec/dsl/find_spec.rb +36 -0
  42. data/spec/dsl/has_content_spec.rb +103 -0
  43. data/spec/dsl/has_css_spec.rb +109 -0
  44. data/spec/dsl/has_xpath_spec.rb +115 -0
  45. data/spec/dsl/locate_spec.rb +38 -0
  46. data/spec/dsl/select_spec.rb +27 -0
  47. data/spec/dsl/uncheck_spec.rb +29 -0
  48. data/spec/dsl/within_spec.rb +145 -0
  49. data/spec/fixtures/capybara.jpg +0 -0
  50. data/spec/public/test.js +27 -0
  51. data/spec/searchable_spec.rb +61 -0
  52. data/spec/server_spec.rb +47 -0
  53. data/spec/session/celerity_session_spec.rb +27 -0
  54. data/spec/session/culerity_session_spec.rb +1 -0
  55. data/spec/session/rack_test_session_spec.rb +1 -0
  56. data/spec/session/selenium_session_spec.rb +1 -0
  57. data/spec/session_spec.rb +32 -903
  58. data/spec/session_with_headers_support_spec.rb +13 -0
  59. data/spec/session_with_javascript_support_spec.rb +165 -0
  60. data/spec/session_without_headers_support_spec.rb +15 -0
  61. data/spec/session_without_javascript_support_spec.rb +15 -0
  62. data/spec/spec_helper.rb +12 -0
  63. data/spec/test_app.rb +9 -1
  64. data/spec/views/form.erb +38 -0
  65. data/spec/views/postback.erb +13 -0
  66. data/spec/views/with_html.erb +13 -0
  67. data/spec/views/with_js.erb +9 -24
  68. data/spec/views/with_simple_html.erb +1 -1
  69. data/spec/wait_until_spec.rb +28 -0
  70. data/spec/xpath_spec.rb +34 -3
  71. metadata +54 -5
@@ -47,10 +47,13 @@ module Capybara
47
47
  end
48
48
 
49
49
  SESSION_METHODS = [
50
- :visit, :body, :click_link, :click_button, :drag, :fill_in, :choose, :has_xpath?, :has_css?,
51
- :check, :uncheck, :attach_file, :select, :has_content?, :within, :within_fieldset,
52
- :within_table, :save_and_open_page, :find, :find_field, :find_link, :find_button,
53
- :field_labeled, :all, :wait_for, :evaluate_script, :click
50
+ :visit, :current_url, :body, :click_link, :click_button, :drag, :fill_in,
51
+ :choose, :has_xpath?, :has_no_xpath?, :has_css?, :has_no_css?,
52
+ :check, :uncheck, :attach_file, :select, :source,
53
+ :has_content?, :has_no_content?, :within, :within_fieldset, :within_table,
54
+ :save_and_open_page, :find, :find_field, :find_link, :find_button,
55
+ :field_labeled, :all, :locate, :evaluate_script,
56
+ :click, :wait_until
54
57
  ]
55
58
  SESSION_METHODS.each do |method|
56
59
  class_eval <<-RUBY, __FILE__, __LINE__+1
@@ -1,40 +1,61 @@
1
- class Capybara::Node
2
- attr_reader :driver, :node
1
+ module Capybara
2
+ class Node
3
+ include Searchable
3
4
 
4
- def initialize(driver, node)
5
- @driver = driver
6
- @node = node
7
- end
5
+ attr_reader :driver, :node
8
6
 
9
- def text
10
- raise "Not implemented"
11
- end
7
+ def initialize(driver, node)
8
+ @driver = driver
9
+ @node = node
10
+ end
12
11
 
13
- def [](name)
14
- raise "Not implemented"
15
- end
12
+ def text
13
+ raise NotImplementedError
14
+ end
16
15
 
17
- def value
18
- self[:value]
19
- end
16
+ def [](name)
17
+ raise NotImplementedError
18
+ end
20
19
 
21
- def set(value)
22
- raise "Not implemented"
23
- end
20
+ def value
21
+ self[:value]
22
+ end
24
23
 
25
- def select(option)
26
- raise "Not implemented"
27
- end
24
+ def set(value)
25
+ raise NotImplementedError
26
+ end
28
27
 
29
- def click
30
- raise "Not implemented"
31
- end
28
+ def select(option)
29
+ raise NotImplementedError
30
+ end
32
31
 
33
- def drag_to(element)
34
- raise "Not implemented"
35
- end
32
+ def click
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def drag_to(element)
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def tag_name
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def visible?
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def path
49
+ raise NotSupportedByDriverError
50
+ end
51
+
52
+ private
53
+
54
+ def all_unfiltered(locator)
55
+ nodes = XPath.wrap(locator).scope(path).paths.map do |path|
56
+ driver.find(path)
57
+ end.flatten
58
+ end
36
59
 
37
- def tag_name
38
- raise "Not implemented"
39
60
  end
40
61
  end
@@ -6,6 +6,6 @@ Capybara.app = Rack::Builder.new do
6
6
  use Rails::Rack::Static
7
7
  run ActionController::Dispatcher.new
8
8
  end
9
- end.to_app
9
+ end.to_app
10
10
 
11
11
  Capybara.asset_root = Rails.root.join('public')
@@ -23,7 +23,7 @@ module Capybara
23
23
 
24
24
  def rewrite_css_and_image_references(response_html) # :nodoc:
25
25
  return response_html unless Capybara.asset_root
26
- response_html.gsub(/("|')\/(stylesheets|images)/, '\1' + Capybara.asset_root + '/\2')
26
+ response_html.gsub(/("|')\/(stylesheets|images|javascripts)/, '\1' + Capybara.asset_root + '/\2')
27
27
  end
28
28
  end
29
29
  end
@@ -0,0 +1,45 @@
1
+ module Capybara
2
+ module Searchable
3
+ def find(locator, options = {})
4
+ all(locator, options).first
5
+ end
6
+
7
+ def find_field(locator)
8
+ find(XPath.field(locator))
9
+ end
10
+ alias_method :field_labeled, :find_field
11
+
12
+ def find_link(locator)
13
+ find(XPath.link(locator))
14
+ end
15
+
16
+ def find_button(locator)
17
+ find(XPath.button(locator))
18
+ end
19
+
20
+ def find_by_id(id)
21
+ find(Xpath.for_css("##{id}"))
22
+ end
23
+
24
+ def all(locator, options = {})
25
+ results = all_unfiltered(locator)
26
+
27
+ if options[:text]
28
+ results = results.select { |n| n.text.match(options[:text]) }
29
+ end
30
+
31
+ if options[:visible] == true
32
+ results.reject! { |n| !n.visible? }
33
+ end
34
+
35
+ results
36
+ end
37
+
38
+ private
39
+
40
+ def all_unfiltered(locator)
41
+ raise "Must be overridden"
42
+ end
43
+
44
+ end
45
+ end
@@ -1,7 +1,11 @@
1
1
  require 'uri'
2
2
  require 'net/http'
3
3
  require 'rack'
4
- require 'rack/handler/mongrel'
4
+ begin
5
+ require 'rack/handler/mongrel'
6
+ rescue LoadError
7
+ require 'rack/handler/webrick'
8
+ end
5
9
 
6
10
  class Capybara::Server
7
11
  class Identify
@@ -10,7 +14,7 @@ class Capybara::Server
10
14
  end
11
15
 
12
16
  def call(env)
13
- if env["REQUEST_PATH"] == "/__identify__"
17
+ if env["PATH_INFO"] == "/__identify__"
14
18
  [200, {}, @app.object_id.to_s]
15
19
  else
16
20
  @app.call(env)
@@ -18,7 +22,6 @@ class Capybara::Server
18
22
  end
19
23
  end
20
24
 
21
-
22
25
  attr_reader :app, :port
23
26
 
24
27
  def initialize(app)
@@ -48,7 +51,11 @@ private
48
51
 
49
52
  Timeout.timeout(10) do
50
53
  Thread.new do
51
- Rack::Handler::Mongrel.run Identify.new(@app), :Port => port
54
+ if defined?(Rack::Handler::Mongrel)
55
+ Rack::Handler::Mongrel.run(Identify.new(@app), :Port => port)
56
+ else
57
+ Rack::Handler::WEBrick.run(Identify.new(@app), :Port => port, :AccessLog => [])
58
+ end
52
59
  end
53
60
  Capybara.log "checking if application has booted"
54
61
 
@@ -1,5 +1,8 @@
1
+ require 'capybara/wait_until'
2
+
1
3
  module Capybara
2
4
  class Session
5
+ include Searchable
3
6
 
4
7
  attr_reader :mode, :app
5
8
 
@@ -12,109 +15,92 @@ module Capybara
12
15
  @driver ||= case mode
13
16
  when :rack_test
14
17
  Capybara::Driver::RackTest.new(app)
15
- when :culerity
16
- Capybara::Driver::Culerity.new(app)
17
18
  when :selenium
18
19
  Capybara::Driver::Selenium.new(app)
20
+ when :celerity
21
+ Capybara::Driver::Celerity.new(app)
22
+ when :culerity
23
+ Capybara::Driver::Culerity.new(app)
19
24
  else
20
25
  raise Capybara::DriverNotFoundError, "no driver called #{mode} was found"
21
26
  end
22
27
  end
23
28
 
29
+ def current_url
30
+ driver.current_url
31
+ end
32
+
33
+ def response_headers
34
+ driver.response_headers
35
+ end
36
+
24
37
  def visit(path)
25
38
  driver.visit(path)
26
39
  end
27
40
 
28
41
  def click(locator)
29
- link = wait_for(XPath.link(locator).button(locator))
30
- raise Capybara::ElementNotFound, "no link or button '#{locator}' found" unless link
31
- link.click
42
+ msg = "no link or button '#{locator}' found"
43
+ locate(XPath.link(locator).button(locator), msg).click
32
44
  end
33
45
 
34
46
  def click_link(locator)
35
- link = wait_for(XPath.link(locator))
36
- raise Capybara::ElementNotFound, "no link with title, id or text '#{locator}' found" unless link
37
- link.click
47
+ msg = "no link with title, id or text '#{locator}' found"
48
+ locate(XPath.link(locator), msg).click
38
49
  end
39
50
 
40
51
  def click_button(locator)
41
- button = wait_for(XPath.button(locator))
42
- raise Capybara::ElementNotFound, "no button with value or id or text '#{locator}' found" unless button
43
- button.click
52
+ msg = "no button with value or id or text '#{locator}' found"
53
+ locate(XPath.button(locator)).click
44
54
  end
45
55
 
46
56
  def drag(source_locator, target_locator)
47
- source = find(source_locator)
48
- raise Capybara::ElementNotFound, "drag source '#{source_locator}' not found on page" unless source
49
- target = find(target_locator)
50
- raise Capybara::ElementNotFound, "drag target '#{target_locator}' not found on page" unless target
57
+ source = locate(source_locator, "drag source '#{source_locator}' not found on page")
58
+ target = locate(target_locator, "drag target '#{target_locator}' not found on page")
51
59
  source.drag_to(target)
52
60
  end
53
61
 
54
62
  def fill_in(locator, options={})
55
- field = wait_for(XPath.fillable_field(locator))
56
- raise Capybara::ElementNotFound, "cannot fill in, no text field, text area or password field with id or label '#{locator}' found" unless field
57
- field.set(options[:with])
63
+ msg = "cannot fill in, no text field, text area or password field with id, name, or label '#{locator}' found"
64
+ locate(XPath.fillable_field(locator), msg).set(options[:with])
58
65
  end
59
66
 
60
67
  def choose(locator)
61
- field = wait_for(XPath.radio_button(locator))
62
- raise Capybara::ElementNotFound, "cannot choose field, no radio button with id or label '#{locator}' found" unless field
63
- field.set(true)
68
+ msg = "cannot choose field, no radio button with id, name, or label '#{locator}' found"
69
+ locate(XPath.radio_button(locator), msg).set(true)
64
70
  end
65
71
 
66
72
  def check(locator)
67
- field = wait_for(XPath.checkbox(locator))
68
- raise Capybara::ElementNotFound, "cannot check field, no checkbox with id or label '#{locator}' found" unless field
69
- field.set(true)
73
+ msg = "cannot check field, no checkbox with id, name, or label '#{locator}' found"
74
+ locate(XPath.checkbox(locator), msg).set(true)
70
75
  end
71
76
 
72
77
  def uncheck(locator)
73
- field = wait_for(XPath.checkbox(locator))
74
- raise Capybara::ElementNotFound, "cannot uncheck field, no checkbox with id or label '#{locator}' found" unless field
75
- field.set(false)
78
+ msg = "cannot uncheck field, no checkbox with id, name, or label '#{locator}' found"
79
+ locate(XPath.checkbox(locator), msg).set(false)
76
80
  end
77
81
 
78
82
  def select(value, options={})
79
- field = wait_for(XPath.select(options[:from]))
80
- raise Capybara::ElementNotFound, "cannot select option, no select box with id or label '#{options[:from]}' found" unless field
81
- field.select(value)
83
+ msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found"
84
+ locate(XPath.select(options[:from]), msg).select(value)
82
85
  end
83
86
 
84
87
  def attach_file(locator, path)
85
- field = wait_for(XPath.file_field(locator))
86
- raise Capybara::ElementNotFound, "cannot attach file, no file field with id or label '#{locator}' found" unless field
87
- field.set(path)
88
+ msg = "cannot attach file, no file field with id, name, or label '#{locator}' found"
89
+ locate(XPath.file_field(locator), msg).set(path)
88
90
  end
89
91
 
90
92
  def body
91
93
  driver.body
92
94
  end
93
95
 
94
- def has_content?(content)
95
- has_xpath?(XPath.content(content).to_s)
96
- end
97
-
98
- def has_xpath?(path, options={})
99
- results = all(path)
100
- if options[:text]
101
- results = filter_by_text(results, options[:text])
102
- end
103
- if options[:count]
104
- results.size == options[:count]
105
- else
106
- results.size > 0
107
- end
108
- end
109
-
110
- def has_css?(path, options={})
111
- has_xpath?(css_to_xpath(path), options)
96
+ def source
97
+ driver.source
112
98
  end
113
99
 
114
100
  def within(kind, scope=nil)
115
101
  kind, scope = Capybara.default_selector, kind unless scope
116
- scope = css_to_xpath(scope) if kind == :css
117
- raise Capybara::ElementNotFound, "scope '#{scope}' not found on page" unless wait_for(scope)
102
+ scope = XPath.from_css(scope) if kind == :css
103
+ locate(scope, "scope '#{scope}' not found on page")
118
104
  scopes.push(scope)
119
105
  yield
120
106
  scopes.pop
@@ -132,67 +118,81 @@ module Capybara
132
118
  end
133
119
  end
134
120
 
135
- def save_and_open_page
136
- require 'capybara/save_and_open_page'
137
- Capybara::SaveAndOpenPage.save_and_open_page(body)
121
+ def has_xpath?(path, options={})
122
+ wait_conditionally_until do
123
+ results = all(path, options)
124
+
125
+ if options[:count]
126
+ results.size == options[:count]
127
+ else
128
+ results.size > 0
129
+ end
130
+ end
131
+ rescue Capybara::TimeoutError
132
+ return false
138
133
  end
139
134
 
140
- def all(locator)
141
- XPath.wrap(locator).scope(current_scope).paths.map do |path|
142
- driver.find(path)
143
- end.flatten
135
+ def has_no_xpath?(path, options={})
136
+ wait_conditionally_until do
137
+ results = all(path, options)
138
+
139
+ if options[:count]
140
+ results.size != options[:count]
141
+ else
142
+ results.empty?
143
+ end
144
+ end
145
+ rescue Capybara::TimeoutError
146
+ return false
144
147
  end
145
148
 
146
- def find(locator)
147
- all(locator).first
149
+ def has_css?(path, options={})
150
+ has_xpath?(XPath.from_css(path), options)
148
151
  end
149
-
150
- def wait_for(locator)
151
- return find(locator) unless driver.wait?
152
- 8.times do
153
- result = find(locator)
154
- return result if result
155
- sleep(0.1)
156
- end
157
- nil
152
+
153
+ def has_no_css?(path, options={})
154
+ has_no_xpath?(XPath.from_css(path), options)
155
+ end
156
+
157
+ def has_content?(content)
158
+ has_xpath?(XPath.content(content))
159
+ end
160
+
161
+ def has_no_content?(content)
162
+ has_no_xpath?(XPath.content(content))
158
163
  end
159
164
 
160
- def find_field(locator)
161
- find(XPath.field(locator))
165
+ def save_and_open_page
166
+ require 'capybara/save_and_open_page'
167
+ Capybara::SaveAndOpenPage.save_and_open_page(body)
162
168
  end
163
- alias_method :field_labeled, :find_field
164
169
 
165
- def find_link(locator)
166
- find(XPath.link(locator))
170
+ #return node identified by locator or raise ElementNotFound(using desc)
171
+ def locate(locator, fail_msg = nil)
172
+ node = wait_conditionally_until { find(locator) }
173
+ ensure
174
+ raise Capybara::ElementNotFound, fail_msg || "Unable to locate '#{locator}'" unless node
175
+ return node
167
176
  end
168
177
 
169
- def find_button(locator)
170
- find(XPath.button(locator))
178
+ def wait_until(timeout = Capybara.default_wait_time)
179
+ WaitUntil.timeout(timeout) { yield }
171
180
  end
172
181
 
173
182
  def evaluate_script(script)
174
- begin
175
- driver.evaluate_script(script)
176
- rescue NoMethodError
177
- raise NotSupportedByDriverError
178
- end
183
+ driver.evaluate_script(script)
179
184
  end
180
185
 
181
186
  private
182
187
 
183
- def css_to_xpath(css)
184
- Nokogiri::CSS.xpath_for(css).first
188
+ def wait_conditionally_until
189
+ if driver.wait? then wait_until { yield } else yield end
185
190
  end
186
191
 
187
- def filter_by_text(nodes, text)
188
- nodes.select do |node|
189
- case text
190
- when String
191
- node.text.include?(text)
192
- when Regexp
193
- node.text =~ text
194
- end
195
- end
192
+ def all_unfiltered(locator)
193
+ XPath.wrap(locator).scope(current_scope).paths.map do |path|
194
+ driver.find(path)
195
+ end.flatten
196
196
  end
197
197
 
198
198
  def current_scope