test-unit-capybara 1.0.0 → 1.0.1
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.
- data/Gemfile +29 -0
- data/README.textile +87 -0
- data/Rakefile +67 -19
- data/doc/text/news.textile +15 -0
- data/lib/test/unit/capybara.rb +448 -26
- data/lib/test/unit/capybara/version.rb +25 -0
- data/test/run-test.rb +30 -0
- data/test/test-assertions.rb +278 -0
- data/test/test-unit-capybara-test-utils.rb +28 -0
- data/test/test-wrapper.rb +96 -0
- metadata +117 -103
- data/History.txt +0 -5
- data/Manifest.txt +0 -6
- data/README.txt +0 -30
data/Gemfile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- coding: utf-8; mode: ruby -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
gem "test-unit", ">= 2.4.4"
|
20
|
+
gem "capybara", ">= 1.1.3"
|
21
|
+
gem "json"
|
22
|
+
|
23
|
+
group :development, :test do
|
24
|
+
gem "rake"
|
25
|
+
gem "jeweler"
|
26
|
+
gem "yard"
|
27
|
+
gem "packnga"
|
28
|
+
gem "test-unit-notify"
|
29
|
+
end
|
data/README.textile
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
h1. test-unit-capybara
|
2
|
+
|
3
|
+
"Web site":http://test-unit.rubyforge.org/#test-unit-capybara
|
4
|
+
|
5
|
+
h2. Description
|
6
|
+
|
7
|
+
test-unit-capybara is a Capybara adapter for test-unit 2. You can get "Capybara":https://rubygems.org/gems/capybara integrated Test::Unit::TestCase. It also provides useful assertions for Capybara.
|
8
|
+
|
9
|
+
h2. Install
|
10
|
+
|
11
|
+
<pre>
|
12
|
+
% sudo gem install test-unit-capybara
|
13
|
+
</pre>
|
14
|
+
|
15
|
+
h2. Usage
|
16
|
+
|
17
|
+
<pre>
|
18
|
+
require 'test/unit/capybara'
|
19
|
+
|
20
|
+
class MyRackApplication
|
21
|
+
def call(env)
|
22
|
+
html = <<-HTML
|
23
|
+
<html>
|
24
|
+
<head>
|
25
|
+
<title>Welcome! - my site</title>
|
26
|
+
</head>
|
27
|
+
<body>
|
28
|
+
<h1>Welcome!</h1>
|
29
|
+
<div class="header">
|
30
|
+
<p>No navigation.</p>
|
31
|
+
</div>
|
32
|
+
</body>
|
33
|
+
</html>
|
34
|
+
HTML
|
35
|
+
[200, {"Content-Type" => "text/html"}, [html]]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TestMyRackApplication < Test::Unit::TestCase
|
40
|
+
include Capybara::DSL
|
41
|
+
|
42
|
+
def setup
|
43
|
+
Capybara.app = MyRackApplication.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_title
|
47
|
+
visit("/")
|
48
|
+
within("h1") do
|
49
|
+
assert_equal("Welcome!", text)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_no_sidebar
|
54
|
+
visit("/")
|
55
|
+
within("body") do
|
56
|
+
assert_not_find(".sidebar")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_header_content
|
61
|
+
visit("/")
|
62
|
+
within(".header") do
|
63
|
+
find("ol.navi")
|
64
|
+
# This fails with the following message:
|
65
|
+
#
|
66
|
+
# <"ol.navi">(:css) expected to find a element in
|
67
|
+
# <<div class="header">
|
68
|
+
# <p>No navigation.</p>
|
69
|
+
# </div>>
|
70
|
+
#
|
71
|
+
# This messages shows the current context. You don't need to
|
72
|
+
# entire HTML. You just see the current context moved by "within".
|
73
|
+
# It helps you debug a problem without save_and_open_page.
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
</pre>
|
78
|
+
|
79
|
+
h2. License
|
80
|
+
|
81
|
+
LGPLv2.1 or later.
|
82
|
+
|
83
|
+
(Kouhei Sutou has a right to change the license including contributed patches.)
|
84
|
+
|
85
|
+
h2. Authors
|
86
|
+
|
87
|
+
* Kouhei Sutou
|
data/Rakefile
CHANGED
@@ -1,33 +1,81 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2
18
|
|
3
|
-
require '
|
19
|
+
require './lib/test/unit/capybara/version'
|
4
20
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
21
|
+
require 'rubygems'
|
22
|
+
require 'rubygems/package_task'
|
23
|
+
require 'yard'
|
24
|
+
require 'jeweler'
|
25
|
+
require 'packnga'
|
9
26
|
|
10
|
-
|
11
|
-
|
27
|
+
def cleanup_white_space(entry)
|
28
|
+
entry.gsub(/(\A\n+|\n+\z)/, '') + "\n"
|
29
|
+
end
|
12
30
|
|
13
|
-
|
31
|
+
ENV["VERSION"] ||= Test::Unit::Capybara::VERSION
|
32
|
+
version = ENV["VERSION"].dup
|
33
|
+
spec = nil
|
34
|
+
Jeweler::Tasks.new do |_spec|
|
35
|
+
spec = _spec
|
36
|
+
spec.name = "test-unit-capybara"
|
37
|
+
spec.version = version
|
38
|
+
spec.rubyforge_project = "test-unit"
|
39
|
+
spec.homepage = "http://test-unit.rubyforge.org/#test-unit-capybara"
|
40
|
+
spec.authors = ["Kouhei Sutou"]
|
41
|
+
spec.email = ["kou@clear-code.com"]
|
42
|
+
entries = File.read("README.textile").split(/^h2\.\s(.*)$/)
|
43
|
+
description = cleanup_white_space(entries[entries.index("Description") + 1])
|
44
|
+
spec.summary, spec.description, = description.split(/\n\n+/, 3)
|
45
|
+
spec.license = "LGPLv2 or later"
|
46
|
+
spec.files = FileList["lib/**/*.rb",
|
47
|
+
"bin/*",
|
48
|
+
"doc/text/*",
|
49
|
+
"README.textile",
|
50
|
+
"COPYING",
|
51
|
+
"Rakefile",
|
52
|
+
"Gemfile"]
|
53
|
+
spec.test_files = FileList["test/**/*.rb"]
|
54
|
+
end
|
14
55
|
|
15
|
-
|
16
|
-
|
56
|
+
Rake::Task["release"].prerequisites.clear
|
57
|
+
Jeweler::RubygemsDotOrgTasks.new do
|
58
|
+
end
|
17
59
|
|
18
|
-
|
60
|
+
Gem::PackageTask.new(spec) do |pkg|
|
61
|
+
pkg.need_tar_gz = true
|
62
|
+
end
|
19
63
|
|
20
|
-
|
21
|
-
|
22
|
-
Hoe.spec('test-unit-capybara') do
|
23
|
-
self.version = version
|
24
|
-
self.rubyforge_name = "test-unit"
|
64
|
+
document_task = Packnga::DocumentTask.new(spec) do
|
65
|
+
end
|
25
66
|
|
26
|
-
|
67
|
+
Packnga::ReleaseTask.new(spec) do |task|
|
68
|
+
end
|
27
69
|
|
28
|
-
|
29
|
-
|
70
|
+
# XXX: Workaround. This should be fixed in packnga.
|
71
|
+
task :htaccess do
|
72
|
+
htaccess = "doc/html/test-unit-capybara/.htaccess"
|
73
|
+
htaccess_content = File.read(htaccess)
|
74
|
+
File.open(htaccess, "w") do |htaccess_file|
|
75
|
+
htaccess_file.print(htaccess_content.gsub(/#test-unit-capybara/, ""))
|
76
|
+
end
|
30
77
|
end
|
78
|
+
task "release:reference:publish" => :htaccess
|
31
79
|
|
32
80
|
desc "Tag the current revision."
|
33
81
|
task :tag do
|
@@ -0,0 +1,15 @@
|
|
1
|
+
h1. News
|
2
|
+
|
3
|
+
h2(#1-0-1). 1.0.1 - 2012-01-16
|
4
|
+
|
5
|
+
A Capybara integration improvement release.
|
6
|
+
|
7
|
+
h3. Improvments
|
8
|
+
|
9
|
+
* Added {Test::Unit::Capybara::Assertions#assert_all}.
|
10
|
+
* Added {Test::Unit::Capybara::Assertions#assert_not_find}.
|
11
|
+
* Supported Capybara::ElementNotFound as a failure.
|
12
|
+
|
13
|
+
h2(#1-0-0). 1.0.0 - 2011-05-01
|
14
|
+
|
15
|
+
The first release!!!
|
data/lib/test/unit/capybara.rb
CHANGED
@@ -1,25 +1,37 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
3
|
+
# Copyright (C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
require "test/unit/capybara/version"
|
7
20
|
|
8
21
|
require 'capybara'
|
9
22
|
require 'capybara/dsl'
|
23
|
+
require "json"
|
10
24
|
require 'test/unit'
|
11
25
|
|
12
26
|
module Test::Unit
|
13
27
|
module Capybara
|
14
|
-
VERSION = "1.0.0"
|
15
|
-
|
16
28
|
module Adapter
|
17
29
|
class << self
|
18
30
|
def included(mod)
|
19
31
|
mod.module_eval do
|
20
32
|
setup :before => :prepend
|
21
33
|
def setup_capybara
|
22
|
-
return unless self.class.include?(::Capybara)
|
34
|
+
return unless self.class.include?(::Capybara::DSL)
|
23
35
|
extend(Assertions)
|
24
36
|
if self[:js]
|
25
37
|
::Capybara.current_driver = ::Capybara.javascript_driver
|
@@ -30,7 +42,7 @@ module Test::Unit
|
|
30
42
|
|
31
43
|
teardown :after => :append
|
32
44
|
def teardown_capybara
|
33
|
-
return unless self.class.include?(::Capybara)
|
45
|
+
return unless self.class.include?(::Capybara::DSL)
|
34
46
|
::Capybara.reset_sessions!
|
35
47
|
::Capybara.use_default_driver
|
36
48
|
end
|
@@ -39,33 +51,443 @@ module Test::Unit
|
|
39
51
|
end
|
40
52
|
end
|
41
53
|
|
54
|
+
# @private
|
55
|
+
class ElementNotFound < ::Capybara::ElementNotFound
|
56
|
+
attr_reader :node, :query
|
57
|
+
def initialize(node, query)
|
58
|
+
@node = node
|
59
|
+
@query = query
|
60
|
+
super(@query.failure_message(:find, @node))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @private
|
65
|
+
module FindError
|
66
|
+
class << self
|
67
|
+
def included(base)
|
68
|
+
base.module_eval do
|
69
|
+
alias_method :raise_find_error_original, :raise_find_error
|
70
|
+
alias_method :raise_find_error, :raise_find_error_for_test_unit
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def raise_find_error_for_test_unit(*args)
|
76
|
+
query = ::Capybara::Query.new(*args)
|
77
|
+
raise ElementNotFound.new(self, query)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @private
|
82
|
+
class ::Capybara::Node::Base
|
83
|
+
include FindError
|
84
|
+
end
|
85
|
+
|
86
|
+
# @private
|
87
|
+
module ElementNotFoundHandler
|
88
|
+
class << self
|
89
|
+
def included(base)
|
90
|
+
base.exception_handler(:handle_capybara_element_not_found)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def handle_capybara_element_not_found(exception)
|
96
|
+
return false unless exception.is_a?(ElementNotFound)
|
97
|
+
return false unless respond_to?(:flunk_find)
|
98
|
+
query = exception.query
|
99
|
+
begin
|
100
|
+
flunk_find(exception.node,
|
101
|
+
:kind => query.selector.name,
|
102
|
+
:locator => query.locator)
|
103
|
+
rescue AssertionFailedError => assertion_failed_error
|
104
|
+
assertion_failed_error.backtrace.replace(exception.backtrace)
|
105
|
+
handle_exception(assertion_failed_error)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @private
|
111
|
+
class NodeInspector
|
112
|
+
Inspector = ::Test::Unit::Assertions::AssertionMessage::Inspector
|
113
|
+
Inspector.register_inspector_class(self)
|
114
|
+
|
115
|
+
class << self
|
116
|
+
def target?(object)
|
117
|
+
object.is_a?(::Capybara::Node::Base)
|
118
|
+
end
|
119
|
+
|
120
|
+
def source(node)
|
121
|
+
if node.base.respond_to?(:source)
|
122
|
+
node.base.source
|
123
|
+
else
|
124
|
+
node.base.native.to_s
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def initialize(node, inspected_objects)
|
130
|
+
@node = node
|
131
|
+
@inspected_objects = inspected_objects
|
132
|
+
end
|
133
|
+
|
134
|
+
def inspect
|
135
|
+
@node.inspect.gsub(/>\z/, " #{self.class.source(@node)}>")
|
136
|
+
end
|
137
|
+
|
138
|
+
def pretty_print(q)
|
139
|
+
q.text(@node.inspect.gsub(/>\z/, ""))
|
140
|
+
q.breakable
|
141
|
+
q.text("#{self.class.source(@node)}>")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
42
145
|
module Assertions
|
43
|
-
|
146
|
+
# @private
|
147
|
+
AssertionMessage = ::Test::Unit::Assertions::AssertionMessage
|
148
|
+
|
149
|
+
# Passes if @expected@ == @source@. @source@ is a
|
150
|
+
# method provided by Capybara::DSL.
|
151
|
+
#
|
152
|
+
# @source@ may be parsed depended on response
|
153
|
+
# Content-Type before comparing. Here are parsed
|
154
|
+
# Content-Types:
|
155
|
+
#
|
156
|
+
# - @"application/json"@ := It's parsed by @JSON.parse@.
|
157
|
+
#
|
158
|
+
# @param [Object] expected the expected body
|
159
|
+
# content. The actual body may be parsed. It
|
160
|
+
# depends on @:content_type@ option.
|
161
|
+
#
|
162
|
+
# @option options [String] :content_type (nil)
|
163
|
+
# the expected Content-Type. If this value is @nil@,
|
164
|
+
# Content-Type will not be compared.
|
165
|
+
#
|
166
|
+
# This value can be specified by abbreviated. Here
|
167
|
+
# are abbreviations:
|
168
|
+
#
|
169
|
+
# - @:json@ := @"application/json"@
|
170
|
+
#
|
171
|
+
# @yield [expected_response, actual_response] the
|
172
|
+
# optional compared responses normalizer.
|
173
|
+
# @yieldparam [Hash] expected_response the expected
|
174
|
+
# response constructed in the method.
|
175
|
+
# @yieldparam [Hash] actual_response the actual
|
176
|
+
# response constructed in the method.
|
177
|
+
# @yieldreturn [expected_response, actual_response] the
|
178
|
+
# normalized compared responses.
|
179
|
+
#
|
180
|
+
# @example Pass case
|
181
|
+
# # Actual response:
|
182
|
+
# # Content-Type: application/json
|
183
|
+
# # Body: {"status": true}
|
184
|
+
# assert_body({"status" => true}, :content_type => :json)
|
185
|
+
#
|
186
|
+
# @example Failure case
|
187
|
+
# # Actual response:
|
188
|
+
# # Content-Type: text/html
|
189
|
+
# # Body: <html><body>Hello</body></html>
|
190
|
+
# assert_body("<html><body>World</body></html>")
|
191
|
+
def assert_body(expected, options={}, &block)
|
44
192
|
content_type = options[:content_type]
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
193
|
+
actual_response = {
|
194
|
+
:content_type => page_content_type,
|
195
|
+
:body => parsed_page_body,
|
196
|
+
}
|
197
|
+
expected_response = {:body => expected}
|
198
|
+
if content_type
|
199
|
+
expected_response[:content_type] = normalize_content_type(content_type)
|
200
|
+
else
|
201
|
+
actual_response.delete(:content_type)
|
202
|
+
end
|
203
|
+
if block_given?
|
204
|
+
expected_response, actual_response = yield(expected_response,
|
205
|
+
actual_response)
|
206
|
+
end
|
207
|
+
assert_equal(expected_response, actual_response)
|
208
|
+
end
|
209
|
+
|
210
|
+
# @param [...] args (see {::Capybara::Node::Finders#all})
|
211
|
+
# @return [Array<::Capybara::Element>] The found elements.
|
212
|
+
#
|
213
|
+
# @see Capybara::Node::Finders#all
|
214
|
+
#
|
215
|
+
# @overload assert_all(*args)
|
216
|
+
# Passes if the selector finds one or more elements
|
217
|
+
# from the current node.
|
218
|
+
#
|
219
|
+
# @example Pass case
|
220
|
+
# # Actual response:
|
221
|
+
# # <html>
|
222
|
+
# # <body>
|
223
|
+
# # <h1>Hello</h1>
|
224
|
+
# # <h2>Yay!</h2>
|
225
|
+
# # <div class="section">
|
226
|
+
# # <h2>World</h2>
|
227
|
+
# # </div>
|
228
|
+
# # </body>
|
229
|
+
# # </html>
|
230
|
+
# h2_elements = assert_page_all("h2")
|
231
|
+
# p h2_elements
|
232
|
+
# # => [#<Capybara::Element tag="h2" path="/html/body/h2">,
|
233
|
+
# # #<Capybara::Element tag="h2" path="/html/body/div/h2">]
|
234
|
+
#
|
235
|
+
# @example Failure case
|
236
|
+
# # Actual response:
|
237
|
+
# # <html>
|
238
|
+
# # <body>
|
239
|
+
# # <h1>Hello</h1>
|
240
|
+
# # <h2>Yay!</h2>
|
241
|
+
# # <div class="section">
|
242
|
+
# # <h2>World</h2>
|
243
|
+
# # </div>
|
244
|
+
# # </body>
|
245
|
+
# # </html>
|
246
|
+
# assert_page_all("h3")
|
247
|
+
#
|
248
|
+
# @overload assert_all(node, *args)
|
249
|
+
# Passes if the selector finds one or more elements
|
250
|
+
# from @node@.
|
251
|
+
#
|
252
|
+
# @param [::Capybara::Node::Base] node The target node.
|
253
|
+
#
|
254
|
+
# @example Pass case (simple)
|
255
|
+
# # Actual response:
|
256
|
+
# # <html>
|
257
|
+
# # <body>
|
258
|
+
# # <h1>Hello</h1>
|
259
|
+
# # <h2>Yay!</h2>
|
260
|
+
# # <div class="section">
|
261
|
+
# # <h2>World</h2>
|
262
|
+
# # </div>
|
263
|
+
# # </body>
|
264
|
+
# # </html>
|
265
|
+
# section = assert_find("div.section")
|
266
|
+
# p section
|
267
|
+
# # => #<Capybara::Element tag="h2" path="/html/body/div">
|
268
|
+
# h2_elements = assert_all(section, "h2")
|
269
|
+
# p h2_elements
|
270
|
+
# # => [#<Capybara::Element tag="h2" path="/html/body/div/h2">]
|
271
|
+
def assert_all(*args)
|
272
|
+
node = nil
|
273
|
+
node = args.shift if args[0].is_a?(::Capybara::Node::Base)
|
274
|
+
args = normalize_page_finder_arguments(args)
|
275
|
+
format = <<-EOT
|
276
|
+
<?>(?) expected to find one or more elements in
|
277
|
+
<?>
|
278
|
+
EOT
|
279
|
+
current_context = node || page.send(:current_node)
|
280
|
+
current_context_source = node_source(current_context)
|
281
|
+
source_in_message = AssertionMessage.literal(current_context_source)
|
282
|
+
full_message = build_message(args[:message],
|
283
|
+
format,
|
284
|
+
args[:locator],
|
285
|
+
args[:kind],
|
286
|
+
source_in_message)
|
287
|
+
if node
|
288
|
+
elements = node.all(*args[:finder_arguments])
|
289
|
+
else
|
290
|
+
elements = all(*args[:finder_arguments])
|
291
|
+
end
|
292
|
+
assert_block(full_message) do
|
293
|
+
not elements.empty?
|
294
|
+
end
|
295
|
+
elements
|
296
|
+
end
|
297
|
+
|
298
|
+
# @param [...] args (see {::Capybara::Node::Finders#find})
|
299
|
+
#
|
300
|
+
# @see ::Capybara::Node::Finders#find
|
301
|
+
#
|
302
|
+
# @overload assert_not_find(*args, &block)
|
303
|
+
# Passes if the selector doesn't find any elements
|
304
|
+
# from the current node.
|
305
|
+
#
|
306
|
+
# @example Pass case
|
307
|
+
# # Actual response:
|
308
|
+
# # <html>
|
309
|
+
# # <body>
|
310
|
+
# # <h1>Hello</h1>
|
311
|
+
# # <h2>Yay!</h2>
|
312
|
+
# # <div class="section">
|
313
|
+
# # <h2>World</h2>
|
314
|
+
# # </div>
|
315
|
+
# # </body>
|
316
|
+
# # </html>
|
317
|
+
# assert_not_find("h3")
|
318
|
+
#
|
319
|
+
# @example Failure case
|
320
|
+
# # Actual response:
|
321
|
+
# # <html>
|
322
|
+
# # <body>
|
323
|
+
# # <h1>Hello</h1>
|
324
|
+
# # <h2>Yay!</h2>
|
325
|
+
# # <div class="section">
|
326
|
+
# # <h2>World</h2>
|
327
|
+
# # </div>
|
328
|
+
# # </body>
|
329
|
+
# # </html>
|
330
|
+
# assert_not_find("h1")
|
331
|
+
#
|
332
|
+
# @overload assert_not_find(node, *args, &block)
|
333
|
+
# Passes if the selector doesn't find any element from @node@.
|
334
|
+
#
|
335
|
+
# @param [::Capybara::Node::Base] node The target node.
|
336
|
+
#
|
337
|
+
# @example Pass case
|
338
|
+
# # Actual response:
|
339
|
+
# # <html>
|
340
|
+
# # <body>
|
341
|
+
# # <h1>Hello</h1>
|
342
|
+
# # <h2>Yay!</h2>
|
343
|
+
# # <div class="section">
|
344
|
+
# # <h2>World</h2>
|
345
|
+
# # </div>
|
346
|
+
# # </body>
|
347
|
+
# # </html>
|
348
|
+
# section = assert_find("section")
|
349
|
+
# p section
|
350
|
+
# # => #<Capybara::Element tag="h2" path="/html/body/div">
|
351
|
+
# assert_not_find(section, "h1")
|
352
|
+
#
|
353
|
+
# @example Failure case
|
354
|
+
# # Actual response:
|
355
|
+
# # <html>
|
356
|
+
# # <body>
|
357
|
+
# # <h1>Hello</h1>
|
358
|
+
# # <h2>Yay!</h2>
|
359
|
+
# # <div class="section">
|
360
|
+
# # <h2>World</h2>
|
361
|
+
# # </div>
|
362
|
+
# # </body>
|
363
|
+
# # </html>
|
364
|
+
# section = assert_find("section")
|
365
|
+
# p section
|
366
|
+
# # => #<Capybara::Element tag="h2" path="/html/body/div">
|
367
|
+
# assert_not_find(section, "h2")
|
368
|
+
def assert_not_find(*args, &block)
|
369
|
+
node = nil
|
370
|
+
node = args.shift if args[0].is_a?(::Capybara::Node::Base)
|
371
|
+
args = normalize_page_finder_arguments(args)
|
372
|
+
if node
|
373
|
+
element = node.first(*args[:finder_arguments])
|
374
|
+
else
|
375
|
+
element = first(*args[:finder_arguments])
|
376
|
+
end
|
377
|
+
format = <<-EOT
|
378
|
+
<?>(?) expected to not find a element but was
|
379
|
+
<?> in
|
380
|
+
<?>
|
381
|
+
EOT
|
382
|
+
element_source = nil
|
383
|
+
element_source = node_source(element) if element
|
384
|
+
current_context = node || page.send(:current_node)
|
385
|
+
current_context_source = node_source(current_context)
|
386
|
+
source_in_message = AssertionMessage.literal(current_context_source)
|
387
|
+
full_message = build_message(args[:message],
|
388
|
+
format,
|
389
|
+
args[:locator],
|
390
|
+
args[:kind],
|
391
|
+
AssertionMessage.literal(element_source),
|
392
|
+
source_in_message)
|
393
|
+
assert_block(full_message) do
|
394
|
+
element.nil?
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
# Fails always with {::Capybara::Node::Element} is not
|
399
|
+
# found message.
|
400
|
+
#
|
401
|
+
# @param [::Capybara::Node::Element] base_node The
|
402
|
+
# node used as search target.
|
403
|
+
# @option options [String] :message The user custom
|
404
|
+
# message added to failure message.
|
405
|
+
# @option options [String] :locator The query used to
|
406
|
+
# find a node.
|
407
|
+
#
|
408
|
+
# It should be specified for useful failure message.
|
409
|
+
# @option options [String] :kind The kind of query.
|
410
|
+
#
|
411
|
+
# It should be specified for useful failure message.
|
412
|
+
def flunk_find(base_node, options={})
|
413
|
+
format = <<-EOT
|
414
|
+
<?>(?) expected to find a element in
|
415
|
+
<?>
|
416
|
+
EOT
|
417
|
+
base_html = AssertionMessage.literal(node_source(base_node))
|
418
|
+
full_message = build_message(options[:message],
|
419
|
+
format,
|
420
|
+
options[:locator],
|
421
|
+
options[:kind],
|
422
|
+
base_html)
|
423
|
+
assert_block(full_message) do
|
424
|
+
false
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
private
|
429
|
+
def page_content_type
|
430
|
+
page.response_headers["Content-Type"]
|
431
|
+
end
|
432
|
+
|
433
|
+
def parsed_page_body
|
434
|
+
case page_content_type
|
435
|
+
when "application/json"
|
436
|
+
::JSON.parse(source)
|
437
|
+
else
|
438
|
+
source
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def normalize_page_finder_arguments(args)
|
443
|
+
args = args.dup
|
444
|
+
options = {}
|
445
|
+
options = args.pop if args.last.is_a?(Hash)
|
446
|
+
if args.size == 1
|
447
|
+
locator = args[0]
|
448
|
+
if locator[0, 1] == "/"
|
449
|
+
kind = :xpath
|
450
|
+
args.unshift(kind)
|
451
|
+
else
|
452
|
+
kind = ::Capybara.default_selector
|
453
|
+
end
|
55
454
|
else
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
455
|
+
kind, locator, = args
|
456
|
+
end
|
457
|
+
args << options
|
458
|
+
|
459
|
+
{
|
460
|
+
:kind => kind,
|
461
|
+
:locator => locator,
|
462
|
+
:message => options.delete(:message),
|
463
|
+
:finder_arguments => args,
|
464
|
+
}
|
465
|
+
end
|
466
|
+
|
467
|
+
def node_source(node)
|
468
|
+
if node
|
469
|
+
if node.base.respond_to?(:source)
|
470
|
+
node.base.source
|
471
|
+
else
|
472
|
+
node.base.native.to_s
|
62
473
|
end
|
474
|
+
else
|
475
|
+
source
|
63
476
|
end
|
64
477
|
end
|
478
|
+
|
479
|
+
# @private
|
480
|
+
CONTENT_TYPE_SHORTCUTS = {
|
481
|
+
:json => "application/json",
|
482
|
+
}
|
483
|
+
def normalize_content_type(content_type)
|
484
|
+
CONTENT_TYPE_SHORTCUTS[content_type] || content_type
|
485
|
+
end
|
65
486
|
end
|
66
487
|
end
|
67
488
|
|
68
489
|
class TestCase
|
69
490
|
include Capybara::Adapter
|
491
|
+
include Capybara::ElementNotFoundHandler
|
70
492
|
end
|
71
493
|
end
|