capybara 0.2.0 → 0.3.0

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