capybara 2.0.3 → 2.1.0.beta1

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