capybara 2.0.0.beta2 → 2.0.0.beta4

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