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,275 @@
1
+ require 'capybara/wait_until'
2
+
3
+ module Capybara
4
+ class Session
5
+ include Searchable
6
+
7
+ DSL_METHODS = [
8
+ :all, :attach_file, :body, :check, :choose, :click, :click_button, :click_link, :current_url, :drag, :evaluate_script,
9
+ :field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_css?,
10
+ :has_no_content?, :has_no_css?, :has_no_xpath?, :has_xpath?, :locate, :save_and_open_page, :select, :source, :uncheck,
11
+ :visit, :wait_until, :within, :within_fieldset, :within_table, :has_link?, :has_no_link?, :has_button?, :has_no_button?,
12
+ :has_field?, :has_no_field?, :has_checked_field?, :has_unchecked_field?, :has_no_table?, :has_table?, :unselect,
13
+ :has_select?, :has_no_select?
14
+ ]
15
+
16
+ attr_reader :mode, :app
17
+
18
+ def initialize(mode, app=nil)
19
+ @mode = mode
20
+ @app = app
21
+ end
22
+
23
+ def driver
24
+ @driver ||= case mode
25
+ when :rack_test
26
+ Capybara::Driver::RackTest.new(app)
27
+ when :selenium
28
+ Capybara::Driver::Selenium.new(app)
29
+ when :celerity
30
+ Capybara::Driver::Celerity.new(app)
31
+ when :culerity
32
+ Capybara::Driver::Culerity.new(app)
33
+ else
34
+ raise Capybara::DriverNotFoundError, "no driver called #{mode} was found"
35
+ end
36
+ end
37
+
38
+ def cleanup!
39
+ driver.cleanup!
40
+ end
41
+
42
+ def current_url
43
+ driver.current_url
44
+ end
45
+
46
+ def response_headers
47
+ driver.response_headers
48
+ end
49
+
50
+ def visit(path)
51
+ driver.visit(path)
52
+ end
53
+
54
+ def click(locator)
55
+ msg = "no link or button '#{locator}' found"
56
+ locate(:xpath, XPath.link(locator).button(locator), msg).click
57
+ end
58
+
59
+ def click_link(locator)
60
+ msg = "no link with title, id or text '#{locator}' found"
61
+ locate(:xpath, XPath.link(locator), msg).click
62
+ end
63
+
64
+ def click_button(locator)
65
+ msg = "no button with value or id or text '#{locator}' found"
66
+ locate(:xpath, XPath.button(locator), msg).click
67
+ end
68
+
69
+ def drag(source_locator, target_locator)
70
+ source = locate(:xpath, source_locator, "drag source '#{source_locator}' not found on page")
71
+ target = locate(:xpath, target_locator, "drag target '#{target_locator}' not found on page")
72
+ source.drag_to(target)
73
+ end
74
+
75
+ def fill_in(locator, options={})
76
+ msg = "cannot fill in, no text field, text area or password field with id, name, or label '#{locator}' found"
77
+ locate(:xpath, XPath.fillable_field(locator), msg).set(options[:with])
78
+ end
79
+
80
+ def choose(locator)
81
+ msg = "cannot choose field, no radio button with id, name, or label '#{locator}' found"
82
+ locate(:xpath, XPath.radio_button(locator), msg).set(true)
83
+ end
84
+
85
+ def check(locator)
86
+ msg = "cannot check field, no checkbox with id, name, or label '#{locator}' found"
87
+ locate(:xpath, XPath.checkbox(locator), msg).set(true)
88
+ end
89
+
90
+ def uncheck(locator)
91
+ msg = "cannot uncheck field, no checkbox with id, name, or label '#{locator}' found"
92
+ locate(:xpath, XPath.checkbox(locator), msg).set(false)
93
+ end
94
+
95
+ def select(value, options={})
96
+ msg = "cannot select option, no select box with id, name, or label '#{options[:from]}' found"
97
+ locate(:xpath, XPath.select(options[:from]), msg).select(value)
98
+ end
99
+
100
+ def unselect(value, options={})
101
+ msg = "cannot unselect option, no select box with id, name, or label '#{options[:from]}' found"
102
+ locate(:xpath, XPath.select(options[:from]), msg).unselect(value)
103
+ end
104
+
105
+ def attach_file(locator, path)
106
+ msg = "cannot attach file, no file field with id, name, or label '#{locator}' found"
107
+ locate(:xpath, XPath.file_field(locator), msg).set(path)
108
+ end
109
+
110
+ def body
111
+ driver.body
112
+ end
113
+
114
+ def source
115
+ driver.source
116
+ end
117
+
118
+ def within(kind, scope=nil)
119
+ kind, scope = Capybara.default_selector, kind unless scope
120
+ scope = XPath.from_css(scope) if kind == :css
121
+ locate(:xpath, scope, "scope '#{scope}' not found on page")
122
+ begin
123
+ scopes.push(scope)
124
+ yield
125
+ ensure
126
+ scopes.pop
127
+ end
128
+ end
129
+
130
+ def within_fieldset(locator)
131
+ within :xpath, XPath.fieldset(locator) do
132
+ yield
133
+ end
134
+ end
135
+
136
+ def within_table(locator)
137
+ within :xpath, XPath.table(locator) do
138
+ yield
139
+ end
140
+ end
141
+
142
+ def has_xpath?(path, options={})
143
+ wait_conditionally_until do
144
+ results = all(:xpath, path, options)
145
+
146
+ if options[:count]
147
+ results.size == options[:count]
148
+ else
149
+ results.size > 0
150
+ end
151
+ end
152
+ rescue Capybara::TimeoutError
153
+ return false
154
+ end
155
+
156
+ def has_no_xpath?(path, options={})
157
+ wait_conditionally_until do
158
+ results = all(:xpath, path, options)
159
+
160
+ if options[:count]
161
+ results.size != options[:count]
162
+ else
163
+ results.empty?
164
+ end
165
+ end
166
+ rescue Capybara::TimeoutError
167
+ return false
168
+ end
169
+
170
+ def has_css?(path, options={})
171
+ has_xpath?(XPath.from_css(path), options)
172
+ end
173
+
174
+ def has_no_css?(path, options={})
175
+ has_no_xpath?(XPath.from_css(path), options)
176
+ end
177
+
178
+ def has_content?(content)
179
+ has_xpath?(XPath.content(content))
180
+ end
181
+
182
+ def has_no_content?(content)
183
+ has_no_xpath?(XPath.content(content))
184
+ end
185
+
186
+ def has_link?(locator)
187
+ has_xpath?(XPath.link(locator))
188
+ end
189
+
190
+ def has_no_link?(locator)
191
+ has_no_xpath?(XPath.link(locator))
192
+ end
193
+
194
+ def has_button?(locator)
195
+ has_xpath?(XPath.button(locator))
196
+ end
197
+
198
+ def has_no_button?(locator)
199
+ has_no_xpath?(XPath.button(locator))
200
+ end
201
+
202
+ def has_field?(locator, options={})
203
+ has_xpath?(XPath.field(locator, options))
204
+ end
205
+
206
+ def has_no_field?(locator, options={})
207
+ has_no_xpath?(XPath.field(locator, options))
208
+ end
209
+
210
+ def has_checked_field?(locator)
211
+ has_xpath?(XPath.field(locator, :checked => true))
212
+ end
213
+
214
+ def has_unchecked_field?(locator)
215
+ has_xpath?(XPath.field(locator, :unchecked => true))
216
+ end
217
+
218
+ def has_select?(locator, options={})
219
+ has_xpath?(XPath.select(locator, options))
220
+ end
221
+
222
+ def has_no_select?(locator, options={})
223
+ has_no_xpath?(XPath.select(locator, options))
224
+ end
225
+
226
+ def has_table?(locator, options={})
227
+ has_xpath?(XPath.table(locator, options))
228
+ end
229
+
230
+ def has_no_table?(locator, options={})
231
+ has_no_xpath?(XPath.table(locator, options))
232
+ end
233
+
234
+ def save_and_open_page
235
+ require 'capybara/save_and_open_page'
236
+ Capybara::SaveAndOpenPage.save_and_open_page(body)
237
+ end
238
+
239
+ #return node identified by locator or raise ElementNotFound(using desc)
240
+ def locate(kind_or_locator, locator=nil, fail_msg = nil)
241
+ node = wait_conditionally_until { find(kind_or_locator, locator) }
242
+ ensure
243
+ raise Capybara::ElementNotFound, fail_msg || "Unable to locate '#{kind_or_locator}'" unless node
244
+ return node
245
+ end
246
+
247
+ def wait_until(timeout = Capybara.default_wait_time)
248
+ WaitUntil.timeout(timeout) { yield }
249
+ end
250
+
251
+ def evaluate_script(script)
252
+ driver.evaluate_script(script)
253
+ end
254
+
255
+ private
256
+
257
+ def wait_conditionally_until
258
+ if driver.wait? then wait_until { yield } else yield end
259
+ end
260
+
261
+ def all_unfiltered(locator)
262
+ XPath.wrap(locator).scope(current_scope).paths.map do |path|
263
+ driver.find(path)
264
+ end.flatten
265
+ end
266
+
267
+ def current_scope
268
+ scopes.join('')
269
+ end
270
+
271
+ def scopes
272
+ @scopes ||= []
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,23 @@
1
+ module Capybara
2
+ #Provides timeout similar to standard library Timeout, but avoids threads
3
+ class WaitUntil
4
+
5
+ class << self
6
+
7
+ def timeout(seconds = 1, &block)
8
+ start_time = Time.now
9
+
10
+ result = nil
11
+
12
+ until result
13
+ return result if result = yield
14
+
15
+ if (Time.now - start_time) > seconds
16
+ raise TimeoutError
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,176 @@
1
+ module Capybara
2
+ # this is a class for generating XPath queries, use it like this:
3
+ # Xpath.text_field('foo').link('blah').to_s
4
+ # this will generate an XPath that matches either a text field or a link
5
+ class XPath
6
+
7
+ class << self
8
+ def wrap(path)
9
+ if path.is_a?(self)
10
+ path
11
+ else
12
+ new(path.to_s)
13
+ end
14
+ end
15
+
16
+ def respond_to?(method)
17
+ new.respond_to?(method)
18
+ end
19
+
20
+ def method_missing(*args)
21
+ new.send(*args)
22
+ end
23
+ end
24
+
25
+ attr_reader :paths
26
+
27
+ def initialize(*paths)
28
+ @paths = paths
29
+ end
30
+
31
+ def scope(scope)
32
+ XPath.new(*paths.map { |p| scope + p })
33
+ end
34
+
35
+ def to_s
36
+ @paths.join(' | ')
37
+ end
38
+
39
+ def append(path)
40
+ XPath.new(*[@paths, XPath.wrap(path).paths].flatten)
41
+ end
42
+
43
+ def prepend(path)
44
+ XPath.new(*[XPath.wrap(path).paths, @paths].flatten)
45
+ end
46
+
47
+ def from_css(css)
48
+ append(Nokogiri::CSS.xpath_for(css).first)
49
+ end
50
+ alias_method :for_css, :from_css
51
+
52
+ def field(locator, options={})
53
+ if options[:with]
54
+ fillable_field(locator, options)
55
+ else
56
+ xpath = fillable_field(locator)
57
+ xpath = xpath.input_field(:file, locator, options)
58
+ xpath = xpath.checkbox(locator, options)
59
+ xpath = xpath.radio_button(locator, options)
60
+ xpath.select(locator, options)
61
+ end
62
+ end
63
+
64
+ def fillable_field(locator, options={})
65
+ text_area(locator, options).text_field(locator, options)
66
+ end
67
+
68
+ def content(locator)
69
+ append("/descendant-or-self::*[contains(.,#{s(locator)})]")
70
+ end
71
+
72
+ def table(locator, options={})
73
+ conditions = ""
74
+ if options[:rows]
75
+ row_conditions = options[:rows].map do |row|
76
+ row = row.map { |column| "*[self::td or self::th][text()=#{s(column)}]" }.join(sibling)
77
+ "tr[./#{row}]"
78
+ end.join(sibling)
79
+ conditions << "[.//#{row_conditions}]"
80
+ end
81
+ append("//table[@id=#{s(locator)} or contains(caption,#{s(locator)})]#{conditions}")
82
+ end
83
+
84
+ def fieldset(locator)
85
+ append("//fieldset[@id=#{s(locator)} or contains(legend,#{s(locator)})]")
86
+ end
87
+
88
+ def link(locator)
89
+ xpath = append("//a[@href][@id=#{s(locator)} or contains(.,#{s(locator)}) or contains(@title,#{s(locator)}) or img[contains(@alt,#{s(locator)})]]")
90
+ xpath.prepend("//a[@href][text()=#{s(locator)} or @title=#{s(locator)} or img[@alt=#{s(locator)}]]")
91
+ end
92
+
93
+ def button(locator)
94
+ xpath = append("//input[@type='submit' or @type='image' or @type='button'][@id=#{s(locator)} or contains(@value,#{s(locator)})]")
95
+ xpath = xpath.append("//button[@id=#{s(locator)} or contains(@value,#{s(locator)}) or contains(.,#{s(locator)})]")
96
+ xpath = xpath.prepend("//input[@type='submit' or @type='image' or @type='button'][@value=#{s(locator)}]")
97
+ xpath = xpath.prepend("//input[@type='image'][@alt=#{s(locator)} or contains(@alt,#{s(locator)})]")
98
+ xpath = xpath.prepend("//button[@value=#{s(locator)} or text()=#{s(locator)}]")
99
+ end
100
+
101
+ def text_field(locator, options={})
102
+ options = options.merge(:value => options[:with]) if options.has_key?(:with)
103
+ add_field(locator, "//input[@type!='radio' and @type!='checkbox' and @type!='hidden']", options)
104
+ end
105
+
106
+ def text_area(locator, options={})
107
+ options = options.merge(:text => options[:with]) if options.has_key?(:with)
108
+ add_field(locator, "//textarea", options)
109
+ end
110
+
111
+ def select(locator, options={})
112
+ add_field(locator, "//select", options)
113
+ end
114
+
115
+ def checkbox(locator, options={})
116
+ input_field(:checkbox, locator, options)
117
+ end
118
+
119
+ def radio_button(locator, options={})
120
+ input_field(:radio, locator, options)
121
+ end
122
+
123
+ def file_field(locator, options={})
124
+ input_field(:file, locator, options)
125
+ end
126
+
127
+ protected
128
+
129
+ def input_field(type, locator, options={})
130
+ options = options.merge(:value => options[:with]) if options.has_key?(:with)
131
+ add_field(locator, "//input[@type='#{type}']", options)
132
+ end
133
+
134
+ # place this between to nodes to indicate that they should be siblings
135
+ def sibling
136
+ '/following-sibling::*[1]/self::'
137
+ end
138
+
139
+ def add_field(locator, field, options={})
140
+ postfix = extract_postfix(options)
141
+ xpath = append("#{field}[@id=#{s(locator)}]#{postfix}")
142
+ xpath = xpath.append("#{field}[@name=#{s(locator)}]#{postfix}")
143
+ xpath = xpath.append("#{field}[@id=//label[contains(.,#{s(locator)})]/@for]#{postfix}")
144
+ xpath = xpath.append("//label[contains(.,#{s(locator)})]#{field}#{postfix}")
145
+ xpath.prepend("#{field}[@id=//label[text()=#{s(locator)}]/@for]#{postfix}")
146
+ end
147
+
148
+ def extract_postfix(options)
149
+ options.inject("") do |postfix, (key, value)|
150
+ case key
151
+ when :value then postfix += "[@value=#{s(value)}]"
152
+ when :text then postfix += "[text()=#{s(value)}]"
153
+ when :checked then postfix += "[@checked]"
154
+ when :unchecked then postfix += "[not(@checked)]"
155
+ when :options then postfix += value.map { |o| "[./option/text()=#{s(o)}]" }.join
156
+ when :selected then postfix += [value].flatten.map { |o| "[./option[@selected]/text()=#{s(o)}]" }.join
157
+ end
158
+ postfix
159
+ end
160
+ end
161
+
162
+ # Sanitize a String for putting it into an xpath query
163
+ def s(string)
164
+ if string.include?("'")
165
+ string = string.split("'", -1).map do |substr|
166
+ "'#{substr}'"
167
+ end.join(%q{,"'",})
168
+ "concat(#{string})"
169
+ else
170
+ "'#{string}'"
171
+ end
172
+
173
+ end
174
+
175
+ end
176
+ end