capybara 2.0.3 → 2.1.0.beta1

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 (81) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.md +73 -0
  3. data/README.md +52 -5
  4. data/lib/capybara.rb +44 -62
  5. data/lib/capybara/cucumber.rb +4 -1
  6. data/lib/capybara/driver/base.rb +13 -9
  7. data/lib/capybara/driver/node.rb +18 -6
  8. data/lib/capybara/helpers.rb +111 -22
  9. data/lib/capybara/node/actions.rb +24 -19
  10. data/lib/capybara/node/base.rb +25 -32
  11. data/lib/capybara/node/document.rb +6 -2
  12. data/lib/capybara/node/element.rb +45 -7
  13. data/lib/capybara/node/finders.rb +48 -21
  14. data/lib/capybara/node/matchers.rb +38 -21
  15. data/lib/capybara/node/simple.rb +29 -7
  16. data/lib/capybara/query.rb +72 -26
  17. data/lib/capybara/rack_test/browser.rb +11 -3
  18. data/lib/capybara/rack_test/css_handlers.rb +8 -0
  19. data/lib/capybara/rack_test/driver.rb +11 -3
  20. data/lib/capybara/rack_test/form.rb +8 -1
  21. data/lib/capybara/rack_test/node.rb +92 -36
  22. data/lib/capybara/rails.rb +1 -2
  23. data/lib/capybara/result.rb +21 -37
  24. data/lib/capybara/rspec/matchers.rb +60 -31
  25. data/lib/capybara/selector.rb +46 -13
  26. data/lib/capybara/selenium/driver.rb +42 -6
  27. data/lib/capybara/selenium/node.rb +23 -5
  28. data/lib/capybara/session.rb +46 -11
  29. data/lib/capybara/spec/public/test.js +12 -0
  30. data/lib/capybara/spec/session/all_spec.rb +9 -8
  31. data/lib/capybara/spec/session/attach_file_spec.rb +14 -0
  32. data/lib/capybara/spec/session/check_spec.rb +14 -0
  33. data/lib/capybara/spec/session/choose_spec.rb +14 -0
  34. data/lib/capybara/spec/session/click_button_spec.rb +77 -1
  35. data/lib/capybara/spec/session/click_link_or_button_spec.rb +65 -0
  36. data/lib/capybara/spec/session/click_link_spec.rb +24 -0
  37. data/lib/capybara/spec/session/current_scope_spec.rb +29 -0
  38. data/lib/capybara/spec/session/fill_in_spec.rb +14 -0
  39. data/lib/capybara/spec/session/find_button_spec.rb +12 -0
  40. data/lib/capybara/spec/session/find_by_id_spec.rb +12 -1
  41. data/lib/capybara/spec/session/find_field_spec.rb +30 -0
  42. data/lib/capybara/spec/session/find_link_spec.rb +12 -0
  43. data/lib/capybara/spec/session/find_spec.rb +258 -16
  44. data/lib/capybara/spec/session/first_spec.rb +25 -8
  45. data/lib/capybara/spec/session/has_css_spec.rb +2 -2
  46. data/lib/capybara/spec/session/has_field_spec.rb +10 -2
  47. data/lib/capybara/spec/session/has_selector_spec.rb +9 -0
  48. data/lib/capybara/spec/session/has_text_spec.rb +96 -0
  49. data/lib/capybara/spec/session/has_title_spec.rb +47 -0
  50. data/lib/capybara/spec/session/node_spec.rb +43 -0
  51. data/lib/capybara/spec/session/reset_session_spec.rb +10 -2
  52. data/lib/capybara/spec/session/save_page_spec.rb +30 -0
  53. data/lib/capybara/spec/session/select_spec.rb +65 -0
  54. data/lib/capybara/spec/session/text_spec.rb +31 -0
  55. data/lib/capybara/spec/session/title_spec.rb +16 -0
  56. data/lib/capybara/spec/session/uncheck_spec.rb +14 -0
  57. data/lib/capybara/spec/session/unselect_spec.rb +42 -0
  58. data/lib/capybara/spec/session/visit_spec.rb +3 -3
  59. data/lib/capybara/spec/session/within_frame_spec.rb +14 -0
  60. data/lib/capybara/spec/session/within_spec.rb +3 -3
  61. data/lib/capybara/spec/spec_helper.rb +6 -1
  62. data/lib/capybara/spec/views/form.erb +39 -2
  63. data/lib/capybara/spec/views/frame_child.erb +9 -0
  64. data/lib/capybara/spec/views/frame_parent.erb +8 -0
  65. data/lib/capybara/spec/views/with_base_tag.erb +10 -0
  66. data/lib/capybara/spec/views/with_count.erb +7 -0
  67. data/lib/capybara/spec/views/with_hover.erb +17 -0
  68. data/lib/capybara/spec/views/with_html.erb +21 -2
  69. data/lib/capybara/spec/views/with_js.erb +5 -0
  70. data/lib/capybara/spec/views/with_scope.erb +6 -1
  71. data/lib/capybara/spec/views/with_title.erb +1 -0
  72. data/lib/capybara/spec/views/within_frames.erb +1 -0
  73. data/lib/capybara/version.rb +1 -1
  74. data/spec/basic_node_spec.rb +75 -24
  75. data/spec/dsl_spec.rb +2 -1
  76. data/spec/rack_test_spec.rb +4 -3
  77. data/spec/rspec/matchers_spec.rb +105 -17
  78. data/spec/server_spec.rb +8 -8
  79. data/spec/spec_helper.rb +2 -1
  80. metadata +83 -23
  81. metadata.gz.sig +0 -0
@@ -1,9 +1,14 @@
1
1
  class Capybara::Selenium::Node < Capybara::Driver::Node
2
- def text
2
+ def visible_text
3
3
  # Selenium doesn't normalize Unicode whitespace.
4
4
  Capybara::Helpers.normalize_whitespace(native.text)
5
5
  end
6
6
 
7
+ def all_text
8
+ text = driver.browser.execute_script("return arguments[0].textContent", native)
9
+ Capybara::Helpers.normalize_whitespace(text)
10
+ end
11
+
7
12
  def [](name)
8
13
  native.attribute(name.to_s)
9
14
  rescue Selenium::WebDriver::Error::WebDriverError
@@ -32,7 +37,8 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
32
37
  path_names = value.to_s.empty? ? [] : value
33
38
  native.send_keys(*path_names)
34
39
  elsif tag_name == 'textarea' or tag_name == 'input'
35
- driver.browser.execute_script "arguments[0].value = ''", native
40
+ #script can change a readonly element which user input cannot, so dont execute if readonly
41
+ driver.browser.execute_script "arguments[0].value = ''", native unless self[:readonly]
36
42
  native.send_keys(value.to_s)
37
43
  end
38
44
  end
@@ -52,6 +58,10 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
52
58
  native.click
53
59
  end
54
60
 
61
+ def hover
62
+ driver.browser.action.move_to(native).perform
63
+ end
64
+
55
65
  def drag_to(element)
56
66
  driver.browser.action.drag_and_drop(native, element.native).perform
57
67
  end
@@ -70,12 +80,20 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
70
80
  selected and selected != "false"
71
81
  end
72
82
 
83
+ def disabled?
84
+ !native.enabled?
85
+ end
86
+
73
87
  alias :checked? :selected?
74
88
 
75
- def find(locator)
89
+ def find_xpath(locator)
76
90
  native.find_elements(:xpath, locator).map { |n| self.class.new(driver, n) }
77
91
  end
78
-
92
+
93
+ def find_css(locator)
94
+ native.find_elements(:css, locator).map { |n| self.class.new(driver, n) }
95
+ end
96
+
79
97
  def ==(other)
80
98
  native == other.native
81
99
  end
@@ -84,6 +102,6 @@ private
84
102
 
85
103
  # a reference to the select node if this is an option node
86
104
  def select_node
87
- find('./ancestor::select').first
105
+ find_xpath('./ancestor::select').first
88
106
  end
89
107
  end
@@ -40,11 +40,13 @@ module Capybara
40
40
  :body, :html, :current_url, :current_host, :evaluate_script, :source,
41
41
  :visit, :within, :within_fieldset, :within_table, :within_frame,
42
42
  :within_window, :current_path, :save_page, :save_and_open_page,
43
- :save_screenshot, :reset_session!, :response_headers, :status_code
43
+ :save_screenshot, :reset_session!, :response_headers, :status_code,
44
+ :title, :has_title?, :has_no_title?, :current_scope
44
45
  ]
45
46
  DSL_METHODS = NODE_METHODS + SESSION_METHODS
46
47
 
47
48
  attr_reader :mode, :app, :server
49
+ attr_accessor :synchronized
48
50
 
49
51
  def initialize(mode, app=nil)
50
52
  @mode = mode
@@ -74,7 +76,7 @@ module Capybara
74
76
  def reset!
75
77
  driver.reset! if @touched
76
78
  @touched = false
77
- raise @server.error if @server and @server.error
79
+ raise @server.error if Capybara.raise_server_errors and @server and @server.error
78
80
  ensure
79
81
  @server.reset_error! if @server
80
82
  end
@@ -137,6 +139,14 @@ module Capybara
137
139
  driver.current_url
138
140
  end
139
141
 
142
+ ##
143
+ #
144
+ # @return [String] Title of the current page
145
+ #
146
+ def title
147
+ driver.title
148
+ end
149
+
140
150
  ##
141
151
  #
142
152
  # Navigate to the given URL. The URL can either be a relative URL or an absolute URL
@@ -243,13 +253,16 @@ module Capybara
243
253
 
244
254
  ##
245
255
  #
246
- # Execute the given block within the given iframe given the id of that iframe. Only works on
247
- # some drivers (e.g. Selenium)
256
+ # Execute the given block within the given iframe using given frame name or index.
257
+ # May be supported by not all drivers. Drivers that support it, may provide additional options.
248
258
  #
249
- # @param [String] frame_id Id of the frame
259
+ # @overload within_frame(index)
260
+ # @param [Integer] index index of a frame
261
+ # @overload within_frame(name)
262
+ # @param [String] name name of a frame
250
263
  #
251
- def within_frame(frame_id)
252
- driver.within_frame(frame_id) do
264
+ def within_frame(frame_handle)
265
+ driver.within_frame(frame_handle) do
253
266
  yield
254
267
  end
255
268
  end
@@ -304,7 +317,7 @@ module Capybara
304
317
 
305
318
  FileUtils.mkdir_p(File.dirname(path))
306
319
 
307
- File.open(path,'w') { |f| f.write(body) }
320
+ File.open(path,'w') { |f| f.write(Capybara::Helpers.inject_asset_host(body)) }
308
321
  path
309
322
  end
310
323
 
@@ -338,7 +351,7 @@ module Capybara
338
351
  NODE_METHODS.each do |method|
339
352
  define_method method do |*args, &block|
340
353
  @touched = true
341
- current_node.send(method, *args, &block)
354
+ current_scope.send(method, *args, &block)
342
355
  end
343
356
  end
344
357
 
@@ -346,12 +359,34 @@ module Capybara
346
359
  %(#<Capybara::Session>)
347
360
  end
348
361
 
349
- private
362
+ def has_title?(content)
363
+ document.synchronize do
364
+ unless title.match(Capybara::Helpers.to_regexp(content))
365
+ raise ExpectationNotMet
366
+ end
367
+ end
368
+ return true
369
+ rescue Capybara::ExpectationNotMet
370
+ return false
371
+ end
372
+
373
+ def has_no_title?(content)
374
+ document.synchronize do
375
+ if title.match(Capybara::Helpers.to_regexp(content))
376
+ raise ExpectationNotMet
377
+ end
378
+ end
379
+ return true
380
+ rescue Capybara::ExpectationNotMet
381
+ return false
382
+ end
350
383
 
351
- def current_node
384
+ def current_scope
352
385
  scopes.last
353
386
  end
354
387
 
388
+ private
389
+
355
390
  def scopes
356
391
  @scopes ||= [document]
357
392
  end
@@ -18,6 +18,13 @@ $(function() {
18
18
  }, 500);
19
19
  return false;
20
20
  });
21
+ $('#slow-click').click(function() {
22
+ var link = $(this);
23
+ setTimeout(function() {
24
+ $(link).after('<a id="slow-clicked" href="#">Slow link clicked</a>');
25
+ }, 4000);
26
+ return false;
27
+ });
21
28
  $('#waiter').change(function() {
22
29
  activeRequests = 1;
23
30
  setTimeout(function() {
@@ -48,4 +55,9 @@ $(function() {
48
55
  $('#the-list').html('<li>Foo</li><li>Bar</li>');
49
56
  }, 250)
50
57
  });
58
+ $('#change-title').click(function() {
59
+ setTimeout(function() {
60
+ $('title').text('changed title')
61
+ }, 250)
62
+ });
51
63
  });
@@ -48,22 +48,23 @@ Capybara::SpecHelper.spec "#all" do
48
48
  @session.all('h1').first.text.should == 'This is a test'
49
49
  @session.all("input[id='test_field']").first[:value].should == 'monkey'
50
50
  end
51
- after { Capybara.default_selector = :xpath }
52
51
  end
53
52
 
54
53
  context "with visible filter" do
55
- after { Capybara.ignore_hidden_elements = false }
56
- it "should only find visible nodes" do
57
- @session.all(:css, "a.simple").should have(2).elements
54
+ it "should only find visible nodes when true" do
58
55
  @session.all(:css, "a.simple", :visible => true).should have(1).elements
59
- Capybara.ignore_hidden_elements = true
60
- @session.all(:css, "a.simple").should have(1).elements
61
56
  end
62
57
 
63
- it "should only find invisible nodes" do
64
- Capybara.ignore_hidden_elements = true
58
+ it "should find nodes regardless of whether they are invisible when false" do
65
59
  @session.all(:css, "a.simple", :visible => false).should have(2).elements
66
60
  end
61
+
62
+ it "should default to Capybara.ignore_hidden_elements" do
63
+ Capybara.ignore_hidden_elements = true
64
+ @session.all(:css, "a.simple").should have(1).elements
65
+ Capybara.ignore_hidden_elements = false
66
+ @session.all(:css, "a.simple").should have(2).elements
67
+ end
67
68
  end
68
69
 
69
70
  context "within a scope" do
@@ -87,4 +87,18 @@ Capybara::SpecHelper.spec "#attach_file" do
87
87
  expect { @session.attach_file('Image', '/no_such_file.png') }.to raise_error(Capybara::FileNotFound)
88
88
  end
89
89
  end
90
+
91
+ context "with :exact option" do
92
+ it "should set a file path by partial label when false" do
93
+ @session.attach_file "Imag", __FILE__, :exact => false
94
+ @session.click_button('awesome')
95
+ extract_results(@session)['image'].should == File.basename(__FILE__)
96
+ end
97
+
98
+ it "not allow partial matches when true" do
99
+ expect do
100
+ @session.attach_file "Imag", __FILE__, :exact => true
101
+ end.to raise_error(Capybara::ElementNotFound)
102
+ end
103
+ end
90
104
  end
@@ -82,4 +82,18 @@ Capybara::SpecHelper.spec "#check" do
82
82
  end.to raise_error(Capybara::ElementNotFound)
83
83
  end
84
84
  end
85
+
86
+ context "with :exact option" do
87
+ it "should accept partial matches when false" do
88
+ @session.check('Ham', :exact => false)
89
+ @session.click_button('awesome')
90
+ extract_results(@session)['pets'].should include('hamster')
91
+ end
92
+
93
+ it "should not accept partial matches when true" do
94
+ expect do
95
+ @session.check('Ham', :exact => true)
96
+ end.to raise_error(Capybara::ElementNotFound)
97
+ end
98
+ end
85
99
  end
@@ -37,4 +37,18 @@ Capybara::SpecHelper.spec "#choose" do
37
37
  end.to raise_error(Capybara::ElementNotFound)
38
38
  end
39
39
  end
40
+
41
+ context "with :exact option" do
42
+ it "should accept partial matches when false" do
43
+ @session.choose("Mal", :exact => false)
44
+ @session.click_button('awesome')
45
+ extract_results(@session)['gender'].should == 'male'
46
+ end
47
+
48
+ it "should not accept partial matches when true" do
49
+ expect do
50
+ @session.choose("Mal", :exact => true)
51
+ end.to raise_error(Capybara::ElementNotFound)
52
+ end
53
+ end
40
54
  end
@@ -63,7 +63,7 @@ Capybara::SpecHelper.spec '#click_button' do
63
63
  end
64
64
 
65
65
  it "should serialise and submit color fields" do
66
- @results['html5_color'].should == '#FFFFFF'
66
+ @results['html5_color'].upcase.should == '#FFFFFF'
67
67
  end
68
68
  end
69
69
 
@@ -162,6 +162,69 @@ Capybara::SpecHelper.spec '#click_button' do
162
162
  end
163
163
  end
164
164
 
165
+ context "with fields associated with the form using the form attribute" do
166
+ before do
167
+ @session.click_button('submit_form1')
168
+ @results = extract_results(@session)
169
+ end
170
+
171
+ it "should serialize and submit text fields" do
172
+ @results['outside_input'].should == 'outside_input'
173
+ end
174
+
175
+ it "should serialize text areas" do
176
+ @results['outside_textarea'].should == 'Some text here'
177
+ end
178
+
179
+ it "should serialize select tags" do
180
+ @results['outside_select'].should == 'Ruby'
181
+ end
182
+
183
+ it "should not serliaze fields associated with a different form" do
184
+ @results['for_form2'].should be_nil
185
+ end
186
+ end
187
+
188
+
189
+ context "with submit button outside the form defined by <button> tag" do
190
+ before do
191
+ @session.click_button('outside_button')
192
+ @results = extract_results(@session)
193
+ end
194
+
195
+ it "should submit the associated form" do
196
+ @results['which_form'].should == 'form2'
197
+ end
198
+
199
+ it "should submit the button that was clicked, but not other buttons" do
200
+ @results['outside_button'].should == 'outside_button'
201
+ @results['unused'].should be_nil
202
+ end
203
+ end
204
+
205
+ context "with submit button outside the form defined by <input type='submit'> tag" do
206
+ before do
207
+ @session.click_button('outside_submit')
208
+ @results = extract_results(@session)
209
+ end
210
+
211
+ it "should submit the associated form" do
212
+ @results['which_form'].should == 'form1'
213
+ end
214
+
215
+ it "should submit the button that was clicked, but not other buttons" do
216
+ @results['outside_submit'].should == 'outside_submit'
217
+ @results['submit_form1'].should be_nil
218
+ end
219
+ end
220
+
221
+ context "with submit button for form1 located within form2" do
222
+ it "should submit the form associated with the button" do
223
+ @session.click_button('other_form_button')
224
+ extract_results(@session)['which_form'].should == "form1"
225
+ end
226
+ end
227
+
165
228
  context "with alt given on an image button" do
166
229
  it "should submit the associated form" do
167
230
  @session.click_button('oh hai thar')
@@ -332,4 +395,17 @@ Capybara::SpecHelper.spec '#click_button' do
332
395
  addresses[1]["city"].should == 'Mikolaiv'
333
396
  addresses[1]["country"].should == 'Ukraine'
334
397
  end
398
+
399
+ context "with :exact option" do
400
+ it "should accept partial matches when false" do
401
+ @session.click_button('What an Awesome', :exact => false)
402
+ extract_results(@session)['first_name'].should == 'John'
403
+ end
404
+
405
+ it "should not accept partial matches when true" do
406
+ expect do
407
+ @session.click_button('What an Awesome', :exact => true)
408
+ end.to raise_error(Capybara::ElementNotFound)
409
+ end
410
+ end
335
411
  end
@@ -35,6 +35,41 @@ Capybara::SpecHelper.spec '#click_link_or_button' do
35
35
  extract_results(@session)['first_name'].should == 'John'
36
36
  end
37
37
 
38
+ context "with :exact option" do
39
+ context "when `true`" do
40
+ it "clicks on approximately matching link" do
41
+ @session.visit('/with_html')
42
+ @session.click_link_or_button('abore', :exact => false)
43
+ @session.should have_content('Bar')
44
+ end
45
+
46
+ it "clicks on approximately matching button" do
47
+ @session.visit('/form')
48
+ @session.click_link_or_button('awe')
49
+ extract_results(@session)['first_name'].should == 'John'
50
+ end
51
+ end
52
+
53
+ context "when `false`" do
54
+ it "does not click on link which matches approximately" do
55
+ @session.visit('/with_html')
56
+ msg = "Unable to find link or button \"abore\""
57
+ expect do
58
+ @session.click_link_or_button('abore', :exact => true)
59
+ end.to raise_error(Capybara::ElementNotFound, msg)
60
+ end
61
+
62
+ it "does not click on approximately matching button" do
63
+ @session.visit('/form')
64
+ msg = "Unable to find link or button \"awe\""
65
+
66
+ expect do
67
+ @session.click_link_or_button('awe', :exact => true)
68
+ end.to raise_error(Capybara::ElementNotFound, msg)
69
+ end
70
+ end
71
+ end
72
+
38
73
  context "with a locator that doesn't exist" do
39
74
  it "should raise an error" do
40
75
  @session.visit('/with_html')
@@ -44,4 +79,34 @@ Capybara::SpecHelper.spec '#click_link_or_button' do
44
79
  end.to raise_error(Capybara::ElementNotFound, msg)
45
80
  end
46
81
  end
82
+
83
+ context "with :disabled option" do
84
+ it "ignores disabled buttons when false" do
85
+ @session.visit('/form')
86
+ expect do
87
+ @session.click_link_or_button('Disabled button', :disabled => false)
88
+ end.to raise_error(Capybara::ElementNotFound)
89
+ end
90
+
91
+ it "ignores disabled buttons by default" do
92
+ @session.visit('/form')
93
+ expect do
94
+ @session.click_link_or_button('Disabled button')
95
+ end.to raise_error(Capybara::ElementNotFound)
96
+ end
97
+
98
+ it "happily clicks on links which incorrectly have the disabled attribute" do
99
+ @session.visit('/with_html')
100
+ @session.click_link_or_button('Disabled link')
101
+ @session.should have_content("Bar")
102
+ end
103
+
104
+ it "does nothing when button is disabled" do
105
+ @session.visit('/form')
106
+ expect do
107
+ @session.click_link_or_button('Disabled button', :disabled => false)
108
+ end.to raise_error(Capybara::ElementNotFound)
109
+ end
110
+
111
+ end
47
112
  end