capybara 2.0.0.beta2 → 2.0.0.beta4

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 (93) hide show
  1. data/History.txt +30 -1
  2. data/README.md +52 -31
  3. data/lib/capybara.rb +1 -0
  4. data/lib/capybara/driver/base.rb +1 -1
  5. data/lib/capybara/driver/node.rb +1 -0
  6. data/lib/capybara/dsl.rb +12 -5
  7. data/lib/capybara/helpers.rb +33 -0
  8. data/lib/capybara/node/actions.rb +4 -2
  9. data/lib/capybara/node/base.rb +52 -1
  10. data/lib/capybara/node/element.rb +0 -12
  11. data/lib/capybara/node/finders.rb +1 -1
  12. data/lib/capybara/node/matchers.rb +57 -47
  13. data/lib/capybara/node/simple.rb +4 -0
  14. data/lib/capybara/query.rb +11 -11
  15. data/lib/capybara/rack_test/browser.rb +14 -15
  16. data/lib/capybara/rack_test/driver.rb +2 -2
  17. data/lib/capybara/rack_test/node.rb +15 -2
  18. data/lib/capybara/result.rb +7 -19
  19. data/lib/capybara/rspec.rb +7 -4
  20. data/lib/capybara/rspec/features.rb +4 -1
  21. data/lib/capybara/rspec/matchers.rb +8 -3
  22. data/lib/capybara/selector.rb +1 -2
  23. data/lib/capybara/selenium/driver.rb +2 -2
  24. data/lib/capybara/selenium/node.rb +9 -7
  25. data/lib/capybara/session.rb +47 -31
  26. data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
  27. data/lib/capybara/spec/public/test.js +1 -1
  28. data/lib/capybara/spec/session/all_spec.rb +60 -62
  29. data/lib/capybara/spec/session/assert_selector.rb +123 -0
  30. data/lib/capybara/spec/session/attach_file_spec.rb +72 -58
  31. data/lib/capybara/spec/session/body_spec.rb +21 -0
  32. data/lib/capybara/spec/session/check_spec.rb +67 -50
  33. data/lib/capybara/spec/session/choose_spec.rb +32 -21
  34. data/lib/capybara/spec/session/click_button_spec.rb +261 -221
  35. data/lib/capybara/spec/session/click_link_or_button_spec.rb +40 -30
  36. data/lib/capybara/spec/session/click_link_spec.rb +95 -81
  37. data/lib/capybara/spec/session/current_url_spec.rb +70 -60
  38. data/lib/capybara/spec/session/evaluate_script_spec.rb +6 -0
  39. data/lib/capybara/spec/session/execute_script_spec.rb +7 -0
  40. data/lib/capybara/spec/session/fill_in_spec.rb +118 -92
  41. data/lib/capybara/spec/session/find_button_spec.rb +16 -14
  42. data/lib/capybara/spec/session/find_by_id_spec.rb +16 -14
  43. data/lib/capybara/spec/session/find_field_spec.rb +23 -21
  44. data/lib/capybara/spec/session/find_link_spec.rb +15 -14
  45. data/lib/capybara/spec/session/find_spec.rb +96 -91
  46. data/lib/capybara/spec/session/first_spec.rb +53 -55
  47. data/lib/capybara/spec/session/has_button_spec.rb +22 -24
  48. data/lib/capybara/spec/session/has_css_spec.rb +190 -205
  49. data/lib/capybara/spec/session/has_field_spec.rb +167 -169
  50. data/lib/capybara/spec/session/has_link_spec.rb +26 -29
  51. data/lib/capybara/spec/session/has_select_spec.rb +175 -176
  52. data/lib/capybara/spec/session/has_selector_spec.rb +94 -100
  53. data/lib/capybara/spec/session/has_table_spec.rb +22 -26
  54. data/lib/capybara/spec/session/has_text_spec.rb +159 -132
  55. data/lib/capybara/spec/session/has_xpath_spec.rb +100 -96
  56. data/lib/capybara/spec/session/headers.rb +4 -17
  57. data/lib/capybara/spec/session/html_spec.rb +15 -0
  58. data/lib/capybara/spec/session/node_spec.rb +172 -82
  59. data/lib/capybara/spec/session/reset_session_spec.rb +42 -0
  60. data/lib/capybara/spec/session/response_code.rb +4 -17
  61. data/lib/capybara/spec/session/save_page_spec.rb +46 -0
  62. data/lib/capybara/spec/session/screenshot.rb +8 -24
  63. data/lib/capybara/spec/session/select_spec.rb +100 -89
  64. data/lib/capybara/spec/session/source_spec.rb +12 -0
  65. data/lib/capybara/spec/session/text_spec.rb +15 -17
  66. data/lib/capybara/spec/session/uncheck_spec.rb +22 -17
  67. data/lib/capybara/spec/session/unselect_spec.rb +57 -52
  68. data/lib/capybara/spec/session/visit_spec.rb +58 -60
  69. data/lib/capybara/spec/session/within_frame_spec.rb +24 -26
  70. data/lib/capybara/spec/session/within_spec.rb +119 -121
  71. data/lib/capybara/spec/session/within_window_spec.rb +29 -31
  72. data/lib/capybara/spec/spec_helper.rb +84 -0
  73. data/lib/capybara/spec/test_app.rb +5 -3
  74. data/lib/capybara/spec/views/form.erb +1 -0
  75. data/lib/capybara/spec/views/with_html.erb +6 -1
  76. data/lib/capybara/spec/views/with_js.erb +1 -0
  77. data/lib/capybara/version.rb +1 -1
  78. data/spec/basic_node_spec.rb +2 -2
  79. data/spec/capybara_spec.rb +9 -0
  80. data/spec/dsl_spec.rb +22 -10
  81. data/spec/rack_test_spec.rb +28 -23
  82. data/spec/result_spec.rb +51 -0
  83. data/spec/rspec/features_spec.rb +19 -0
  84. data/spec/rspec/matchers_spec.rb +6 -0
  85. data/spec/rspec_spec.rb +1 -1
  86. data/spec/selenium_spec.rb +11 -25
  87. data/spec/server_spec.rb +2 -2
  88. data/spec/spec_helper.rb +1 -46
  89. metadata +41 -98
  90. data/lib/capybara/spec/session.rb +0 -183
  91. data/lib/capybara/spec/session/javascript.rb +0 -290
  92. data/lib/capybara/util/save_and_open_page.rb +0 -45
  93. data/spec/save_and_open_page_spec.rb +0 -155
@@ -2,6 +2,35 @@
2
2
 
3
3
  ### Changed
4
4
 
5
+ * `require 'capybara/rails'` will automatically enable `:respect_data_method`
6
+ on the RackTest driver, so the behavior matches Capybara 1.1.2 [Jo Liss]
7
+ * Dropped official support for Ruby 1.8.x. [Jonas Nicklas]
8
+ * All methods which find or manipulate fields or buttons now ignore them when
9
+ they are disabled. [Jonas Nicklas]
10
+ * Can no longer find elements by id via `find(:foo)`, use `find("#foo")` or
11
+ `find_by_id("foo")` instead. [Jonas Nicklas]
12
+ * Rename `Driver#body` to `Driver#html` (relevant only for driver authors) [Jo
13
+ Liss]
14
+
15
+ ### Added
16
+
17
+ * Multiple files can be uploaded with `attach_file` [Jarl Friis]
18
+
19
+ ### Fixed
20
+
21
+ * `has_text` (`has_content`) now accepts non-string arguments, like numbers.
22
+ [Jo Liss]
23
+ * `has_text` and `text` now correctly normalize Unicode whitespace, such as
24
+ ` `. [Jo Liss]
25
+ * RackTest allows protocol relative URLs [Jonas Nicklas]
26
+ * Arguments are cast to string where necessary, so that e.g. `click_link(:foo)` works
27
+ as expected. [Jonas Nicklas]
28
+ * `:count => 0` now works as expected [Jarl Friis]
29
+
30
+ # Version 2.0.0.beta2
31
+
32
+ ### Changed
33
+
5
34
  * `respect_data_method` default to `false` for the RackTest driver, which means
6
35
  that Capybara no longer picks up `:method => :post` et. al. from links in Rails
7
36
  by default. [Jonas Nicklas]
@@ -77,7 +106,7 @@
77
106
  session is reset [James Tucker, Jonas Nicklas]
78
107
  * A ton of new selectors built in out of the box, like `field`, `link`, `button`,
79
108
  etc... [Adam McCrea, Jonas Nicklas]
80
- * `has_text`, `has_content` ... [Jonas Nicklas]
109
+ * `has_text?` has been added as an alias for `has_content?` [Jonas Nicklas]
81
110
  * Add `Capybara.server_host` option (default: 127.0.0.1) [David Balatero]
82
111
  * Add `:type` option for `page.has_field?` [Gonzalo Rodríguez]
83
112
  * Custom matchers can now be specified in CSS in addition to XPath [Jonas Nicklas]
data/README.md CHANGED
@@ -4,14 +4,23 @@
4
4
  [![Dependency Status](https://gemnasium.com/jnicklas/capybara.png)](https://gemnasium.com/jnicklas/capybara)
5
5
  [![Code Quality](https://codeclimate.com/badge.png)](https://codeclimate.com/github/jnicklas/capybara)
6
6
 
7
- Capybara helps you test Rails and Rack applications by simulating how a real
8
- user would interact with your app. It is agnostic about the driver running your
9
- tests and comes with Rack::Test and Selenium support built in. WebKit is
10
- supported through an external gem.
7
+ Capybara helps you test web applications by simulating how a real user would
8
+ interact with your app. It is agnostic about the driver running your tests and
9
+ comes with Rack::Test and Selenium support built in. WebKit is supported
10
+ through an external gem.
11
11
 
12
12
  **Need help?** Ask on the mailing list (please do not open an issue on
13
13
  GitHub): http://groups.google.com/group/ruby-capybara
14
14
 
15
+ ## Key benefits
16
+
17
+ - **No setup** necessary for Rails and Rack application. Works out of the box.
18
+ - **Intuitive API** which mimics the language an actual user would use.
19
+ - **Switch the backend** your tests run against from fast headless mode
20
+ to an actual browser with no changes to your tests.
21
+ - **Powerful synchronization** features mean you never have to manually wait
22
+ for asynchronous processes to complete.
23
+
15
24
  ## Setup
16
25
 
17
26
  To install, type
@@ -32,6 +41,9 @@ If you are not using Rails, set Capybara.app to your rack app:
32
41
  Capybara.app = MyRackApp
33
42
  ```
34
43
 
44
+ If you need to test JavaScript, or if your app interacts with (or is located at)
45
+ a remote URL, you'll need to [use a different driver](#drivers).
46
+
35
47
  ## Using Capybara with Cucumber
36
48
 
37
49
  The `cucumber-rails` gem comes with Capybara support built-in. If you
@@ -126,11 +138,21 @@ feature "Signing up" do
126
138
  end
127
139
  click_link 'Sign in'
128
140
  end
141
+
142
+ given(:other_user) { User.make(:email => 'other@example.com', :password => 'rous') }
143
+
144
+ scenario "Signing in as another user" do
145
+ within("#session") do
146
+ fill_in 'Login', :with => other_user.email
147
+ fill_in 'Password', :with => other_user.password
148
+ end
149
+ click_link 'Sign in'
150
+ end
129
151
  end
130
152
  ```
131
153
 
132
154
  `feature` is in fact just an alias for `describe ..., :type => :request`,
133
- `background` is an alias for `before`, and `scenario` for `it`.
155
+ `background` is an alias for `before`, `scenario` for `it`, and `given`/`given!` aliases for `let`/`let!`, respectively.
134
156
 
135
157
  ## Using Capybara with Test::Unit
136
158
 
@@ -217,10 +239,11 @@ Capybara uses the same DSL to drive a variety of browser and headless drivers.
217
239
 
218
240
  ### Selecting the Driver
219
241
 
220
- By default, Capybara uses the `:rack_test` driver, which is fast but does not
221
- support JavaScript. You can set up a different default driver for your
222
- features. For example if you'd prefer to run everything in Selenium, you could
223
- do:
242
+ By default, Capybara uses the `:rack_test` driver, which is fast but limited: it
243
+ does not support JavaScript, nor is it able to access HTTP resources outside of
244
+ your Rack application, such as remote APIs and OAuth services. To get around
245
+ these limitations, you can set up a different default driver for your features.
246
+ For example if you'd prefer to run everything in Selenium, you could do:
224
247
 
225
248
  ```ruby
226
249
  Capybara.default_driver = :selenium
@@ -248,15 +271,17 @@ switch in the middle of a test.
248
271
  ### RackTest
249
272
 
250
273
  RackTest is Capybara's default driver. It is written in pure Ruby and does not
251
- have any support for executing JavaScript. Since the RackTest driver works
252
- directly against the Rack interface, it does not need any server to be started,
253
- it can work directly against any Rack app. This means that if your
254
- application is not a Rack application (Rails, Sinatra and most other Ruby
255
- frameworks are Rack applications) then you cannot use this driver. You cannot
256
- use the RackTest driver to test a remote application.
274
+ have any support for executing JavaScript. Since the RackTest driver interacts
275
+ directly with Rack interfaces, it does not require a server to be started.
276
+ However, this means that if your application is not a Rack application (Rails,
277
+ Sinatra and most other Ruby frameworks are Rack applications) then you cannot
278
+ use this driver. Furthermore, you cannot use the RackTest driver to test a
279
+ remote application, or to access remote URLs (e.g., redirects to external
280
+ sites, external APIs, or OAuth services) that your application might interact
281
+ with.
282
+
257
283
  [capybara-mechanize](https://github.com/jeroenvandijk/capybara-mechanize)
258
- intends to provide a similar driver which works against remote servers, it is a
259
- separate project.
284
+ provides a similar driver that can access remote servers.
260
285
 
261
286
  RackTest can be configured with a set of headers like this:
262
287
 
@@ -474,14 +499,6 @@ that this may break with more complicated expressions:
474
499
  result = page.evaluate_script('4 + 4');
475
500
  ```
476
501
 
477
- ### Saving screenshot
478
-
479
- In drivers which support it, you can save screenshot:
480
-
481
- ```ruby
482
- page.save_screenshot('screenshot.png')
483
- ```
484
-
485
502
  ### Debugging
486
503
 
487
504
  It can be useful to take a snapshot of the page as it currently is and take a
@@ -501,6 +518,12 @@ print page.html
501
518
  This is mostly useful for debugging. You should avoid testing against the
502
519
  contents of `page.html` and use the more expressive finder methods instead.
503
520
 
521
+ Finally, in drivers that support it, you can save a screenshot:
522
+
523
+ ```ruby
524
+ page.save_screenshot('screenshot.png')
525
+ ```
526
+
504
527
  ## Transactions and database setup
505
528
 
506
529
  Some Capybara drivers need to run against an actual HTTP server. Capybara takes
@@ -814,12 +837,6 @@ additional info about how the underlying driver can be configured.
814
837
 
815
838
  ## Development
816
839
 
817
- If you found a _reproducible_ bug, open a [GitHub
818
- Issue](http://github.com/jnicklas/capybara/issues) to submit a bug report.
819
-
820
- Even better, send a pull request! Make sure all changes are well tested,
821
- Capybara is a testing tool after all. Topic branches are good.
822
-
823
840
  To set up a development environment, simply do:
824
841
 
825
842
  ```bash
@@ -827,3 +844,7 @@ git submodule update --init
827
844
  bundle install
828
845
  bundle exec rake # run the test suite
829
846
  ```
847
+
848
+ See
849
+ [CONTRIBUTING.md](https://github.com/jnicklas/capybara/blob/master/CONTRIBUTING.md)
850
+ for how to send issues and pull requests.
@@ -308,6 +308,7 @@ module Capybara
308
308
  autoload :Selector, 'capybara/selector'
309
309
  autoload :Query, 'capybara/query'
310
310
  autoload :Result, 'capybara/result'
311
+ autoload :Helpers, 'capybara/helpers'
311
312
  autoload :VERSION, 'capybara/version'
312
313
 
313
314
  module Node
@@ -15,7 +15,7 @@ class Capybara::Driver::Base
15
15
  raise NotImplementedError
16
16
  end
17
17
 
18
- def body
18
+ def html
19
19
  raise NotImplementedError
20
20
  end
21
21
 
@@ -20,6 +20,7 @@ module Capybara
20
20
  raise NotImplementedError
21
21
  end
22
22
 
23
+ # @param value String or Array. Array is only allowed if node has 'multiple' attribute
23
24
  def set(value)
24
25
  raise NotImplementedError
25
26
  end
@@ -2,6 +2,15 @@ require 'capybara'
2
2
 
3
3
  module Capybara
4
4
  module DSL
5
+ def self.included(base)
6
+ warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
7
+ super
8
+ end
9
+
10
+ def self.extended(base)
11
+ warn "extending the main object with Capybara::DSL is not recommended!" if base == TOPLEVEL_BINDING.eval("self")
12
+ super
13
+ end
5
14
 
6
15
  ##
7
16
  #
@@ -38,11 +47,9 @@ module Capybara
38
47
  end
39
48
 
40
49
  Session::DSL_METHODS.each do |method|
41
- class_eval <<-RUBY, __FILE__, __LINE__+1
42
- def #{method}(*args, &block)
43
- page.#{method}(*args, &block)
44
- end
45
- RUBY
50
+ define_method method do |*args, &block|
51
+ page.send method, *args, &block
52
+ end
46
53
  end
47
54
  end
48
55
 
@@ -0,0 +1,33 @@
1
+ module Capybara
2
+ module Helpers
3
+ class << self
4
+ ##
5
+ #
6
+ # Normalizes whitespace space by stripping leading and trailing
7
+ # whitespace and replacing sequences of whitespace characters
8
+ # with a single space.
9
+ #
10
+ # @param [String] text Text to normalize
11
+ # @return [String] Normalized text
12
+ #
13
+ def normalize_whitespace(text)
14
+ # http://en.wikipedia.org/wiki/Whitespace_character#Unicode
15
+ # We should have a better reference.
16
+ # See also http://stackoverflow.com/a/11758133/525872
17
+ text.to_s.gsub(/[\s\u0085\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+/, ' ').strip
18
+ end
19
+
20
+ ##
21
+ #
22
+ # Escapes any characters that would have special meaning in a regexp
23
+ # if text is not a regexp
24
+ #
25
+ # @param [String] text Text to escape
26
+ # @return [String] Escaped text
27
+ #
28
+ def to_regexp(text)
29
+ text.is_a?(Regexp) ? text : Regexp.escape(normalize_whitespace(text))
30
+ end
31
+ end
32
+ end
33
+ end
@@ -135,10 +135,12 @@ module Capybara
135
135
  # page.attach_file(locator, '/path/to/file.png')
136
136
  #
137
137
  # @param [String] locator Which field to attach the file to
138
- # @param [String] path The path of the file that will be attached
138
+ # @param [String] path The path of the file that will be attached, or an array of paths
139
139
  #
140
140
  def attach_file(locator, path)
141
- raise Capybara::FileNotFound, "cannot attach file, #{path} does not exist" unless File.exist?(path.to_s)
141
+ (String === path ? [path] : path).each do |p|
142
+ raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
143
+ end
142
144
  find(:file_field, locator).set(path)
143
145
  end
144
146
  end
@@ -38,14 +38,47 @@ module Capybara
38
38
  self
39
39
  end
40
40
 
41
+ ##
42
+ #
43
+ # This method is Capybara's primary defence agains asynchronicity
44
+ # problems. It works by attempting to run a given block of code until it
45
+ # succeeds. The exact behaviour of this method depends on a number of
46
+ # factors. Basically there are certain exceptions which, when raised
47
+ # from the block, instead of bubbling up, are caught, and the block is
48
+ # re-run.
49
+ #
50
+ # Certain drivers, such as RackTest, have no support for aynchronous
51
+ # processes, these drivers run the block, and any error raised bubbles up
52
+ # immediately. This allows faster turn around in the case where an
53
+ # expectation fails.
54
+ #
55
+ # Only exceptions that are {Capybara::ElementNotFound} or any subclass
56
+ # thereof cause the block to be rerun. Drivers may specify additional
57
+ # exceptions which also cause reruns. This usually occurs when a node is
58
+ # manipulated which no longer exists on the page. For example, the
59
+ # Selenium driver specifies
60
+ # `Selenium::WebDriver::Error::ObsoleteElementError`.
61
+ #
62
+ # As long as any of these exceptions are thrown, the block is re-run,
63
+ # until a certain amount of time passes. The amount of time defaults to
64
+ # {Capybara.default_wait_time} and can be overriden through the `seconds`
65
+ # argument. This time is compared with the system time to see how much
66
+ # time has passed. If the return value of {Time.now} is stubbed out,
67
+ # Capybara will raise `Capybara::FrozenInTime`.
68
+ #
69
+ # @param [Integer] seconds Number of seconds to retry this block
70
+ # @return [Object] The result of the given block
71
+ # @raise [Capybara::FrozenInTime] If the return value of {Time.now} appears stuck
72
+ #
41
73
  def synchronize(seconds=Capybara.default_wait_time)
42
74
  start_time = Time.now
43
75
 
44
76
  begin
45
77
  yield
46
78
  rescue => e
79
+ raise e if @unsynchronized
47
80
  raise e unless driver.wait?
48
- raise e unless driver.invalid_element_errors.include?(e.class) or e.is_a?(Capybara::ElementNotFound)
81
+ raise e unless driver.invalid_element_errors.include?(e.class) || e.is_a?(Capybara::ElementNotFound)
49
82
  raise e if (Time.now - start_time) >= seconds
50
83
  sleep(0.05)
51
84
  raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Time.now == start_time
@@ -54,6 +87,24 @@ module Capybara
54
87
  end
55
88
  end
56
89
 
90
+ ##
91
+ #
92
+ # Within the given block, prevent synchronize from having any effect.
93
+ #
94
+ # This is an internal method which should not be called unless you are
95
+ # absolutely sure of what you're doing.
96
+ #
97
+ # @api private
98
+ # @return [Object] The result of the given block
99
+ #
100
+ def unsynchronized
101
+ orig = @unsynchronized
102
+ @unsynchronized = true
103
+ yield
104
+ ensure
105
+ @unsynchronized = orig
106
+ end
107
+
57
108
  protected
58
109
 
59
110
  def driver
@@ -177,18 +177,6 @@ module Capybara
177
177
  synchronize { base.drag_to(node.base) }
178
178
  end
179
179
 
180
- def find(*args)
181
- synchronize { super }
182
- end
183
-
184
- def first(*args)
185
- synchronize { super }
186
- end
187
-
188
- def all(*args)
189
- synchronize { super }
190
- end
191
-
192
180
  def reload
193
181
  if @allow_reload
194
182
  reloaded = parent.reload.first(@query.name, @query.locator, @query.options)
@@ -52,7 +52,7 @@ module Capybara
52
52
 
53
53
  ##
54
54
  #
55
- # Find a button on the page. The link can be found by its id, name or value.
55
+ # Find a button on the page. The button can be found by its id, name or value.
56
56
  #
57
57
  # @param [String] locator Which button to find
58
58
  # @return [Capybara::Element] The found element
@@ -25,8 +25,7 @@ module Capybara
25
25
  # has_selector? can also accept XPath expressions generated by the
26
26
  # XPath gem:
27
27
  #
28
- # xpath = XPath.generate { |x| x.descendant(:p) }
29
- # page.has_selector?(:xpath, xpath)
28
+ # page.has_selector?(:xpath, XPath.descendant(:p))
30
29
  #
31
30
  # @param (see Capybara::Node::Finders#all)
32
31
  # @option options [Integer] :count (nil) Number of times the expression should occur
@@ -38,14 +37,6 @@ module Capybara
38
37
  return false
39
38
  end
40
39
 
41
- def assert_selector(*args)
42
- synchronize do
43
- result = all(*args)
44
- result.matches_count? or raise Capybara::ExpectationNotMet, result.failure_message
45
- end
46
- return true
47
- end
48
-
49
40
  ##
50
41
  #
51
42
  # Checks if a given selector is not on the page or current node.
@@ -60,6 +51,51 @@ module Capybara
60
51
  return false
61
52
  end
62
53
 
54
+ ##
55
+ #
56
+ # Asserts that a given selector is on the page or current node.
57
+ #
58
+ # page.assert_selector('p#foo')
59
+ # page.assert_selector(:xpath, './/p[@id="foo"]')
60
+ # page.assert_selector(:foo)
61
+ #
62
+ # By default it will check if the expression occurs at least once,
63
+ # but a different number can be specified.
64
+ #
65
+ # page.assert_selector('p#foo', :count => 4)
66
+ #
67
+ # This will check if the expression occurs exactly 4 times.
68
+ #
69
+ # It also accepts all options that {Capybara::Node::Finders#all} accepts,
70
+ # such as :text and :visible.
71
+ #
72
+ # page.assert_selector('li', :text => 'Horse', :visible => true)
73
+ #
74
+ # {assert_selector} can also accept XPath expressions generated by the
75
+ # XPath gem:
76
+ #
77
+ # page.assert_selector(:xpath, XPath.descendant(:p))
78
+ #
79
+ # @param (see Capybara::Node::Finders#all)
80
+ # @option options [Integer] :count (nil) Number of times the expression should occur
81
+ # @raise [Capybara::ExpectationNotMet] If the selector does not exist
82
+ #
83
+ def assert_selector(*args)
84
+ synchronize do
85
+ result = all(*args)
86
+ result.matches_count? or raise Capybara::ExpectationNotMet, result.failure_message
87
+ end
88
+ return true
89
+ end
90
+
91
+ ##
92
+ #
93
+ # Asserts that a given selector is not on the page or current node.
94
+ # Usage is identical to Capybara::Node::Matchers#assert_selector
95
+ #
96
+ # @param (see Capybara::Node::Finders#assert_selector)
97
+ # @raise [Capybara::ExpectationNotMet] If the selector exists
98
+ #
63
99
  def assert_no_selector(*args)
64
100
  synchronize do
65
101
  result = all(*args)
@@ -157,18 +193,17 @@ module Capybara
157
193
  # Checks if the page or current node has the given text content,
158
194
  # ignoring any HTML tags and normalizing whitespace.
159
195
  #
160
- # Unlike has_content this only matches displayable text and specifically
161
- # excludes text contained within non-display nodes such as script or head tags.
196
+ # This only matches displayable text and specifically excludes text
197
+ # contained within non-display nodes such as script or head tags.
162
198
  #
163
199
  # @param [String] content The text to check for
164
200
  # @return [Boolean] Whether it exists
165
201
  #
166
202
  def has_text?(content)
167
- normalized_content = normalize_whitespace(content)
168
-
169
203
  synchronize do
170
- normalize_whitespace(text).match(escape_regexp(normalized_content)) or
171
- raise ExpectationNotMet
204
+ unless Capybara::Helpers.normalize_whitespace(text).match(Capybara::Helpers.to_regexp(content))
205
+ raise ExpectationNotMet
206
+ end
172
207
  end
173
208
  return true
174
209
  rescue Capybara::ExpectationNotMet
@@ -181,18 +216,17 @@ module Capybara
181
216
  # Checks if the page or current node does not have the given text
182
217
  # content, ignoring any HTML tags and normalizing whitespace.
183
218
  #
184
- # Unlike has_content this only matches displayable text and specifically
185
- # excludes text contained within non-display nodes such as script or head tags.
219
+ # This only matches displayable text and specifically excludes text
220
+ # contained within non-display nodes such as script or head tags.
186
221
  #
187
222
  # @param [String] content The text to check for
188
- # @return [Boolean] Whether it exists
223
+ # @return [Boolean] Whether it doesn't exist
189
224
  #
190
225
  def has_no_text?(content)
191
- normalized_content = normalize_whitespace(content)
192
-
193
226
  synchronize do
194
- !normalize_whitespace(text).match(escape_regexp(normalized_content)) or
195
- raise ExpectationNotMet
227
+ if Capybara::Helpers.normalize_whitespace(text).match(Capybara::Helpers.to_regexp(content))
228
+ raise ExpectationNotMet
229
+ end
196
230
  end
197
231
  return true
198
232
  rescue Capybara::ExpectationNotMet
@@ -426,30 +460,6 @@ module Capybara
426
460
 
427
461
  private
428
462
 
429
- ##
430
- #
431
- # Normalizes whitespace space by stripping leading and trailing
432
- # whitespace and replacing sequences of whitespace characters
433
- # with a single space.
434
- #
435
- # @param [String] text Text to normalize
436
- # @return [String] Normalized text
437
- #
438
- def normalize_whitespace(text)
439
- text.is_a?(Regexp) ? text : text.gsub(/\s+/, ' ').strip
440
- end
441
-
442
- ##
443
- #
444
- # Escapes any characters that would have special meaning in a regexp
445
- # if text is not a regexp
446
- #
447
- # @param [String] text Text to escape
448
- # @return [String] Escaped text
449
- #
450
- def escape_regexp(text)
451
- text.is_a?(Regexp) ? text : Regexp.escape(text)
452
- end
453
463
  end
454
464
  end
455
465
  end