actionpack 3.0.20 → 3.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +88 -142
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -6
- data/lib/abstract_controller.rb +1 -0
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +24 -19
- data/lib/abstract_controller/callbacks.rb +19 -19
- data/lib/abstract_controller/helpers.rb +11 -13
- data/lib/abstract_controller/layouts.rb +4 -5
- data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
- data/lib/abstract_controller/rendering.rb +34 -31
- data/lib/abstract_controller/url_for.rb +27 -0
- data/lib/abstract_controller/view_paths.rb +31 -6
- data/lib/action_controller.rb +5 -3
- data/lib/action_controller/base.rb +15 -16
- data/lib/action_controller/caching.rb +2 -2
- data/lib/action_controller/caching/actions.rb +11 -12
- data/lib/action_controller/caching/fragments.rb +41 -19
- data/lib/action_controller/caching/pages.rb +3 -9
- data/lib/action_controller/caching/sweeping.rb +0 -1
- data/lib/action_controller/deprecated.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +1 -1
- data/lib/action_controller/metal.rb +78 -20
- data/lib/action_controller/metal/compatibility.rb +0 -9
- data/lib/action_controller/metal/conditional_get.rb +9 -9
- data/lib/action_controller/metal/data_streaming.rb +145 -0
- data/lib/action_controller/metal/force_ssl.rb +35 -0
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +37 -44
- data/lib/action_controller/metal/hide_actions.rb +2 -3
- data/lib/action_controller/metal/http_authentication.rb +41 -38
- data/lib/action_controller/metal/implicit_render.rb +13 -13
- data/lib/action_controller/metal/instrumentation.rb +2 -2
- data/lib/action_controller/metal/mime_responds.rb +25 -19
- data/lib/action_controller/metal/params_wrapper.rb +224 -0
- data/lib/action_controller/metal/redirecting.rb +6 -2
- data/lib/action_controller/metal/renderers.rb +50 -36
- data/lib/action_controller/metal/rendering.rb +34 -25
- data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
- data/lib/action_controller/metal/responder.rb +47 -12
- data/lib/action_controller/metal/streaming.rb +244 -138
- data/lib/action_controller/metal/testing.rb +0 -9
- data/lib/action_controller/metal/url_for.rb +12 -14
- data/lib/action_controller/railtie.rb +19 -37
- data/lib/action_controller/railties/paths.rb +24 -0
- data/lib/action_controller/record_identifier.rb +4 -10
- data/lib/action_controller/test_case.rb +36 -19
- data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
- data/lib/action_dispatch.rb +4 -1
- data/lib/action_dispatch/http/cache.rb +5 -32
- data/lib/action_dispatch/http/filter_parameters.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
- data/lib/action_dispatch/http/mime_type.rb +45 -5
- data/lib/action_dispatch/http/rack_cache.rb +58 -0
- data/lib/action_dispatch/http/request.rb +27 -41
- data/lib/action_dispatch/http/response.rb +56 -54
- data/lib/action_dispatch/http/upload.rb +1 -11
- data/lib/action_dispatch/http/url.rb +102 -42
- data/lib/action_dispatch/middleware/callbacks.rb +8 -25
- data/lib/action_dispatch/middleware/closed_error.rb +7 -0
- data/lib/action_dispatch/middleware/cookies.rb +37 -15
- data/lib/action_dispatch/middleware/flash.rb +80 -11
- data/lib/action_dispatch/middleware/params_parser.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
- data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
- data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
- data/lib/action_dispatch/middleware/stack.rb +50 -17
- data/lib/action_dispatch/middleware/static.rb +41 -29
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
- data/lib/action_dispatch/railtie.rb +8 -0
- data/lib/action_dispatch/routing.rb +13 -1
- data/lib/action_dispatch/routing/mapper.rb +345 -227
- data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
- data/lib/action_dispatch/routing/redirection.rb +110 -0
- data/lib/action_dispatch/routing/route.rb +15 -13
- data/lib/action_dispatch/routing/route_set.rb +116 -90
- data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
- data/lib/action_dispatch/routing/url_for.rb +25 -1
- data/lib/action_dispatch/testing/assertions/response.rb +8 -10
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
- data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
- data/lib/action_dispatch/testing/integration.rb +37 -28
- data/lib/action_dispatch/testing/performance_test.rb +1 -3
- data/lib/action_dispatch/testing/test_process.rb +1 -1
- data/lib/action_dispatch/testing/test_request.rb +9 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -111
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +3 -3
- data/lib/action_view.rb +39 -24
- data/lib/action_view/base.rb +61 -86
- data/lib/action_view/buffers.rb +43 -0
- data/lib/action_view/context.rb +21 -24
- data/lib/action_view/flows.rb +79 -0
- data/lib/action_view/helpers.rb +8 -6
- data/lib/action_view/helpers/active_model_helper.rb +0 -23
- data/lib/action_view/helpers/asset_paths.rb +79 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
- data/lib/action_view/helpers/cache_helper.rb +11 -19
- data/lib/action_view/helpers/capture_helper.rb +19 -8
- data/lib/action_view/helpers/controller_helper.rb +21 -0
- data/lib/action_view/helpers/csrf_helper.rb +22 -4
- data/lib/action_view/helpers/date_helper.rb +36 -22
- data/lib/action_view/helpers/form_helper.rb +199 -113
- data/lib/action_view/helpers/form_options_helper.rb +10 -11
- data/lib/action_view/helpers/form_tag_helper.rb +94 -22
- data/lib/action_view/helpers/javascript_helper.rb +24 -107
- data/lib/action_view/helpers/number_helper.rb +36 -33
- data/lib/action_view/helpers/output_safety_helper.rb +38 -0
- data/lib/action_view/helpers/record_tag_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +90 -0
- data/lib/action_view/helpers/sanitize_helper.rb +2 -2
- data/lib/action_view/helpers/sprockets_helper.rb +69 -0
- data/lib/action_view/helpers/tag_helper.rb +34 -12
- data/lib/action_view/helpers/text_helper.rb +30 -145
- data/lib/action_view/helpers/translation_helper.rb +10 -17
- data/lib/action_view/helpers/url_helper.rb +70 -67
- data/lib/action_view/locale/en.yml +1 -1
- data/lib/action_view/lookup_context.rb +36 -14
- data/lib/action_view/{paths.rb → path_set.rb} +9 -8
- data/lib/action_view/railtie.rb +12 -4
- data/lib/action_view/renderer/abstract_renderer.rb +36 -0
- data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
- data/lib/action_view/renderer/renderer.rb +54 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
- data/lib/action_view/renderer/template_renderer.rb +74 -0
- data/lib/action_view/template.rb +91 -54
- data/lib/action_view/template/error.rb +11 -8
- data/lib/action_view/template/handler.rb +9 -1
- data/lib/action_view/template/handlers.rb +9 -9
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb.rb +21 -41
- data/lib/action_view/template/resolver.rb +171 -57
- data/lib/action_view/template/text.rb +0 -4
- data/lib/action_view/test_case.rb +32 -16
- data/lib/action_view/testing/resolvers.rb +16 -10
- data/lib/sprockets/railtie.rb +100 -0
- metadata +162 -140
- checksums.yaml +0 -7
- data/lib/action_controller/deprecated/base.rb +0 -143
- data/lib/action_controller/deprecated/dispatcher.rb +0 -28
- data/lib/action_controller/deprecated/url_writer.rb +0 -14
- data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
- data/lib/action_view/helpers/prototype_helper.rb +0 -851
- data/lib/action_view/helpers/raw_output_helper.rb +0 -18
- data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
- data/lib/action_view/render/layouts.rb +0 -83
- data/lib/action_view/render/rendering.rb +0 -67
- data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActionDispatch
|
2
|
+
module Routing
|
3
|
+
class RoutesProxy #:nodoc:
|
4
|
+
include ActionDispatch::Routing::UrlFor
|
5
|
+
|
6
|
+
attr_accessor :scope, :routes
|
7
|
+
alias :_routes :routes
|
8
|
+
|
9
|
+
def initialize(routes, scope)
|
10
|
+
@routes, @scope = routes, scope
|
11
|
+
end
|
12
|
+
|
13
|
+
def url_options
|
14
|
+
scope.send(:_with_routes, routes) do
|
15
|
+
scope.url_options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(method, *args)
|
20
|
+
if routes.url_helpers.respond_to?(method)
|
21
|
+
self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
22
|
+
def #{method}(*args)
|
23
|
+
options = args.extract_options!
|
24
|
+
args << url_options.merge((options || {}).symbolize_keys)
|
25
|
+
routes.url_helpers.#{method}(*args)
|
26
|
+
end
|
27
|
+
RUBY
|
28
|
+
send(method, *args)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -98,6 +98,11 @@ module ActionDispatch
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
+
def initialize(*)
|
102
|
+
@_routes = nil
|
103
|
+
super
|
104
|
+
end
|
105
|
+
|
101
106
|
def url_options
|
102
107
|
default_url_options
|
103
108
|
end
|
@@ -110,6 +115,13 @@ module ActionDispatch
|
|
110
115
|
# * <tt>:host</tt> - Specifies the host the link should be targeted at.
|
111
116
|
# If <tt>:only_path</tt> is false, this option must be
|
112
117
|
# provided either explicitly, or via +default_url_options+.
|
118
|
+
# * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+
|
119
|
+
# to split the domain from the host.
|
120
|
+
# * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
|
121
|
+
# to split the subdomain from the host.
|
122
|
+
# * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if
|
123
|
+
# <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to
|
124
|
+
# <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
|
113
125
|
# * <tt>:port</tt> - Optionally specify the port to connect to.
|
114
126
|
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
|
115
127
|
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
|
@@ -128,11 +140,23 @@ module ActionDispatch
|
|
128
140
|
when String
|
129
141
|
options
|
130
142
|
when nil, Hash
|
131
|
-
_routes.url_for((options || {}).reverse_merge(url_options).symbolize_keys)
|
143
|
+
_routes.url_for((options || {}).reverse_merge!(url_options).symbolize_keys)
|
132
144
|
else
|
133
145
|
polymorphic_url(options)
|
134
146
|
end
|
135
147
|
end
|
148
|
+
|
149
|
+
protected
|
150
|
+
def _with_routes(routes)
|
151
|
+
old_routes, @_routes = @_routes, routes
|
152
|
+
yield
|
153
|
+
ensure
|
154
|
+
@_routes = old_routes
|
155
|
+
end
|
156
|
+
|
157
|
+
def _routes_context
|
158
|
+
self
|
159
|
+
end
|
136
160
|
end
|
137
161
|
end
|
138
162
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/object/inclusion'
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
module Assertions
|
3
5
|
# A small suite of assertions that test responses from \Rails applications.
|
@@ -20,7 +22,7 @@ module ActionDispatch
|
|
20
22
|
#
|
21
23
|
# You can also pass an explicit status number like <tt>assert_response(501)</tt>
|
22
24
|
# or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
|
23
|
-
# See
|
25
|
+
# See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
|
24
26
|
#
|
25
27
|
# ==== Examples
|
26
28
|
#
|
@@ -33,14 +35,14 @@ module ActionDispatch
|
|
33
35
|
def assert_response(type, message = nil)
|
34
36
|
validate_request!
|
35
37
|
|
36
|
-
if [
|
38
|
+
if type.in?([:success, :missing, :redirect, :error]) && @response.send("#{type}?")
|
37
39
|
assert_block("") { true } # to count the assertion
|
38
40
|
elsif type.is_a?(Fixnum) && @response.response_code == type
|
39
41
|
assert_block("") { true } # to count the assertion
|
40
42
|
elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
|
41
43
|
assert_block("") { true } # to count the assertion
|
42
44
|
else
|
43
|
-
|
45
|
+
flunk(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code))
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
@@ -81,20 +83,16 @@ module ActionDispatch
|
|
81
83
|
|
82
84
|
def normalize_argument_to_redirection(fragment)
|
83
85
|
case fragment
|
84
|
-
when %r{^\w[\
|
86
|
+
when %r{^\w[A-Za-z\d+.-]*:.*}
|
85
87
|
fragment
|
86
88
|
when String
|
87
|
-
|
88
|
-
fragment
|
89
|
-
else
|
90
|
-
@request.protocol + @request.host_with_port + fragment
|
91
|
-
end
|
89
|
+
@request.protocol + @request.host_with_port + fragment
|
92
90
|
when :back
|
93
91
|
raise RedirectBackError unless refer = @request.headers["Referer"]
|
94
92
|
refer
|
95
93
|
else
|
96
94
|
@controller.url_for(fragment)
|
97
|
-
end.gsub(/[\
|
95
|
+
end.gsub(/[\r\n]/, '')
|
98
96
|
end
|
99
97
|
|
100
98
|
def validate_request!
|
@@ -46,7 +46,7 @@ module ActionDispatch
|
|
46
46
|
expected_options.stringify_keys!
|
47
47
|
msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
|
48
48
|
request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
|
49
|
-
|
49
|
+
assert_equal(expected_options, request.path_parameters, msg)
|
50
50
|
end
|
51
51
|
|
52
52
|
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
|
@@ -84,11 +84,11 @@ module ActionDispatch
|
|
84
84
|
found_extras = options.reject {|k, v| ! extra_keys.include? k}
|
85
85
|
|
86
86
|
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
|
87
|
-
|
87
|
+
assert_equal(extras, found_extras, msg)
|
88
88
|
|
89
89
|
msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
|
90
90
|
expected_path)
|
91
|
-
|
91
|
+
assert_equal(expected_path, generated_path, msg)
|
92
92
|
end
|
93
93
|
|
94
94
|
# Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
|
@@ -144,16 +144,16 @@ module ActionDispatch
|
|
144
144
|
#
|
145
145
|
def with_routing
|
146
146
|
old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
147
|
+
if defined?(@controller) && @controller
|
148
|
+
old_controller, @controller = @controller, @controller.clone
|
149
|
+
_routes = @routes
|
150
|
+
|
151
|
+
# Unfortunately, there is currently an abstraction leak between AC::Base
|
152
|
+
# and AV::Base which requires having the URL helpers in both AC and AV.
|
153
|
+
# To do this safely at runtime for tests, we need to bump up the helper serial
|
154
|
+
# to that the old AV subclass isn't cached.
|
155
|
+
#
|
156
|
+
# TODO: Make this unnecessary
|
157
157
|
@controller.singleton_class.send(:include, _routes.url_helpers)
|
158
158
|
@controller.view_context_class = Class.new(@controller.view_context_class) do
|
159
159
|
include _routes.url_helpers
|
@@ -162,14 +162,14 @@ module ActionDispatch
|
|
162
162
|
yield @routes
|
163
163
|
ensure
|
164
164
|
@routes = old_routes
|
165
|
-
if @controller
|
165
|
+
if defined?(@controller) && @controller
|
166
166
|
@controller = old_controller
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
170
|
# ROUTES TODO: These assertions should really work in an integration context
|
171
171
|
def method_missing(selector, *args, &block)
|
172
|
-
if @controller && @routes && @routes.named_routes.helpers.include?(selector)
|
172
|
+
if defined?(@controller) && @controller && @routes && @routes.named_routes.helpers.include?(selector)
|
173
173
|
@controller.send(selector, *args, &block)
|
174
174
|
else
|
175
175
|
super
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'action_controller/vendor/html-scanner'
|
2
|
+
require 'active_support/core_ext/object/inclusion'
|
2
3
|
|
3
4
|
#--
|
4
5
|
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
|
@@ -18,16 +19,12 @@ module ActionDispatch
|
|
18
19
|
# from the response HTML or elements selected by the enclosing assertion.
|
19
20
|
#
|
20
21
|
# In addition to HTML responses, you can make the following assertions:
|
21
|
-
#
|
22
|
+
#
|
22
23
|
# * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
|
23
24
|
# * +assert_select_email+ - Assertions on the HTML body of an e-mail.
|
24
25
|
#
|
25
26
|
# Also see HTML::Selector to learn how to use selectors.
|
26
27
|
module SelectorAssertions
|
27
|
-
# :call-seq:
|
28
|
-
# css_select(selector) => array
|
29
|
-
# css_select(element, selector) => array
|
30
|
-
#
|
31
28
|
# Select and return all matching elements.
|
32
29
|
#
|
33
30
|
# If called with a single argument, uses that argument as a selector
|
@@ -71,7 +68,7 @@ module ActionDispatch
|
|
71
68
|
arg = args.shift
|
72
69
|
elsif arg == nil
|
73
70
|
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
|
74
|
-
elsif @selected
|
71
|
+
elsif defined?(@selected) && @selected
|
75
72
|
matches = []
|
76
73
|
|
77
74
|
@selected.each do |selected|
|
@@ -83,7 +80,7 @@ module ActionDispatch
|
|
83
80
|
|
84
81
|
return matches
|
85
82
|
else
|
86
|
-
root =
|
83
|
+
root = response_from_page
|
87
84
|
end
|
88
85
|
|
89
86
|
case arg
|
@@ -99,10 +96,6 @@ module ActionDispatch
|
|
99
96
|
selector.select(root)
|
100
97
|
end
|
101
98
|
|
102
|
-
# :call-seq:
|
103
|
-
# assert_select(selector, equality?, message?)
|
104
|
-
# assert_select(element, selector, equality?, message?)
|
105
|
-
#
|
106
99
|
# An assertion that selects elements and makes one or more equality tests.
|
107
100
|
#
|
108
101
|
# If the first argument is an element, selects all matching elements
|
@@ -195,6 +188,7 @@ module ActionDispatch
|
|
195
188
|
def assert_select(*args, &block)
|
196
189
|
# Start with optional element followed by mandatory selector.
|
197
190
|
arg = args.shift
|
191
|
+
@selected ||= nil
|
198
192
|
|
199
193
|
if arg.is_a?(HTML::Node)
|
200
194
|
# First argument is a node (tag or text, but also HTML root),
|
@@ -210,7 +204,7 @@ module ActionDispatch
|
|
210
204
|
root.children.concat @selected
|
211
205
|
else
|
212
206
|
# Otherwise just operate on the response document.
|
213
|
-
root =
|
207
|
+
root = response_from_page
|
214
208
|
end
|
215
209
|
|
216
210
|
# First or second argument is the selector: string and we pass
|
@@ -332,151 +326,6 @@ module ActionDispatch
|
|
332
326
|
end
|
333
327
|
end
|
334
328
|
|
335
|
-
# :call-seq:
|
336
|
-
# assert_select_rjs(id?) { |elements| ... }
|
337
|
-
# assert_select_rjs(statement, id?) { |elements| ... }
|
338
|
-
# assert_select_rjs(:insert, position, id?) { |elements| ... }
|
339
|
-
#
|
340
|
-
# Selects content from the RJS response.
|
341
|
-
#
|
342
|
-
# === Narrowing down
|
343
|
-
#
|
344
|
-
# With no arguments, asserts that one or more elements are updated or
|
345
|
-
# inserted by RJS statements.
|
346
|
-
#
|
347
|
-
# Use the +id+ argument to narrow down the assertion to only statements
|
348
|
-
# that update or insert an element with that identifier.
|
349
|
-
#
|
350
|
-
# Use the first argument to narrow down assertions to only statements
|
351
|
-
# of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
|
352
|
-
# <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tta>,
|
353
|
-
# <tt>:insert_html</tt> and <tt>:redirect</tt>.
|
354
|
-
#
|
355
|
-
# Use the argument <tt>:insert</tt> followed by an insertion position to narrow
|
356
|
-
# down the assertion to only statements that insert elements in that
|
357
|
-
# position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt>
|
358
|
-
# and <tt>:after</tt>.
|
359
|
-
#
|
360
|
-
# Use the argument <tt>:redirect</tt> followed by a path to check that an statement
|
361
|
-
# which redirects to the specified path is generated.
|
362
|
-
#
|
363
|
-
# Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will
|
364
|
-
# be ignored as there is no HTML passed for this statement.
|
365
|
-
#
|
366
|
-
# === Using blocks
|
367
|
-
#
|
368
|
-
# Without a block, +assert_select_rjs+ merely asserts that the response
|
369
|
-
# contains one or more RJS statements that replace or update content.
|
370
|
-
#
|
371
|
-
# With a block, +assert_select_rjs+ also selects all elements used in
|
372
|
-
# these statements and passes them to the block. Nested assertions are
|
373
|
-
# supported.
|
374
|
-
#
|
375
|
-
# Calling +assert_select_rjs+ with no arguments and using nested asserts
|
376
|
-
# asserts that the HTML content is returned by one or more RJS statements.
|
377
|
-
# Using +assert_select+ directly makes the same assertion on the content,
|
378
|
-
# but without distinguishing whether the content is returned in an HTML
|
379
|
-
# or JavaScript.
|
380
|
-
#
|
381
|
-
# ==== Examples
|
382
|
-
#
|
383
|
-
# # Replacing the element foo.
|
384
|
-
# # page.replace 'foo', ...
|
385
|
-
# assert_select_rjs :replace, "foo"
|
386
|
-
#
|
387
|
-
# # Replacing with the chained RJS proxy.
|
388
|
-
# # page[:foo].replace ...
|
389
|
-
# assert_select_rjs :chained_replace, 'foo'
|
390
|
-
#
|
391
|
-
# # Inserting into the element bar, top position.
|
392
|
-
# assert_select_rjs :insert, :top, "bar"
|
393
|
-
#
|
394
|
-
# # Remove the element bar
|
395
|
-
# assert_select_rjs :remove, "bar"
|
396
|
-
#
|
397
|
-
# # Changing the element foo, with an image.
|
398
|
-
# assert_select_rjs "foo" do
|
399
|
-
# assert_select "img[src=/images/logo.gif""
|
400
|
-
# end
|
401
|
-
#
|
402
|
-
# # RJS inserts or updates a list with four items.
|
403
|
-
# assert_select_rjs do
|
404
|
-
# assert_select "ol>li", 4
|
405
|
-
# end
|
406
|
-
#
|
407
|
-
# # The same, but shorter.
|
408
|
-
# assert_select "ol>li", 4
|
409
|
-
#
|
410
|
-
# # Checking for a redirect.
|
411
|
-
# assert_select_rjs :redirect, root_path
|
412
|
-
def assert_select_rjs(*args, &block)
|
413
|
-
rjs_type = args.first.is_a?(Symbol) ? args.shift : nil
|
414
|
-
id = args.first.is_a?(String) ? args.shift : nil
|
415
|
-
|
416
|
-
# If the first argument is a symbol, it's the type of RJS statement we're looking
|
417
|
-
# for (update, replace, insertion, etc). Otherwise, we're looking for just about
|
418
|
-
# any RJS statement.
|
419
|
-
if rjs_type
|
420
|
-
if rjs_type == :insert
|
421
|
-
position = args.shift
|
422
|
-
id = args.shift
|
423
|
-
insertion = "insert_#{position}".to_sym
|
424
|
-
raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion]
|
425
|
-
statement = "(#{RJS_STATEMENTS[insertion]})"
|
426
|
-
else
|
427
|
-
raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
|
428
|
-
statement = "(#{RJS_STATEMENTS[rjs_type]})"
|
429
|
-
end
|
430
|
-
else
|
431
|
-
statement = "#{RJS_STATEMENTS[:any]}"
|
432
|
-
end
|
433
|
-
|
434
|
-
# Next argument we're looking for is the element identifier. If missing, we pick
|
435
|
-
# any element, otherwise we replace it in the statement.
|
436
|
-
pattern = Regexp.new(
|
437
|
-
id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
|
438
|
-
)
|
439
|
-
|
440
|
-
# Duplicate the body since the next step involves destroying it.
|
441
|
-
matches = nil
|
442
|
-
case rjs_type
|
443
|
-
when :remove, :show, :hide, :toggle
|
444
|
-
matches = @response.body.match(pattern)
|
445
|
-
else
|
446
|
-
@response.body.gsub(pattern) do |match|
|
447
|
-
html = unescape_rjs(match)
|
448
|
-
matches ||= []
|
449
|
-
matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
|
450
|
-
""
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
if matches
|
455
|
-
assert_block("") { true } # to count the assertion
|
456
|
-
if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
|
457
|
-
begin
|
458
|
-
in_scope, @selected = @selected, matches
|
459
|
-
yield matches
|
460
|
-
ensure
|
461
|
-
@selected = in_scope
|
462
|
-
end
|
463
|
-
end
|
464
|
-
matches
|
465
|
-
else
|
466
|
-
# RJS statement not found.
|
467
|
-
case rjs_type
|
468
|
-
when :remove, :show, :hide, :toggle
|
469
|
-
flunk_message = "No RJS statement that #{rjs_type.to_s}s '#{id}' was rendered."
|
470
|
-
else
|
471
|
-
flunk_message = "No RJS statement that replaces or inserts HTML content."
|
472
|
-
end
|
473
|
-
flunk args.shift || flunk_message
|
474
|
-
end
|
475
|
-
end
|
476
|
-
|
477
|
-
# :call-seq:
|
478
|
-
# assert_select_encoded(element?) { |elements| ... }
|
479
|
-
#
|
480
329
|
# Extracts the content of an element, treats it as encoded HTML and runs
|
481
330
|
# nested assertion on it.
|
482
331
|
#
|
@@ -529,8 +378,8 @@ module ActionDispatch
|
|
529
378
|
node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
|
530
379
|
end
|
531
380
|
|
532
|
-
selected = elements.map do |
|
533
|
-
text =
|
381
|
+
selected = elements.map do |_element|
|
382
|
+
text = _element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
|
534
383
|
root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
|
535
384
|
css_select(root, "encoded:root", &block)[0]
|
536
385
|
end
|
@@ -543,9 +392,6 @@ module ActionDispatch
|
|
543
392
|
end
|
544
393
|
end
|
545
394
|
|
546
|
-
# :call-seq:
|
547
|
-
# assert_select_email { }
|
548
|
-
#
|
549
395
|
# Extracts the body of an email and runs nested assertions on it.
|
550
396
|
#
|
551
397
|
# You must enable deliveries for this assertion to work, use:
|
@@ -569,9 +415,9 @@ module ActionDispatch
|
|
569
415
|
assert !deliveries.empty?, "No e-mail in delivery list"
|
570
416
|
|
571
417
|
for delivery in deliveries
|
572
|
-
for part in
|
418
|
+
for part in delivery.parts
|
573
419
|
if part["Content-Type"].to_s =~ /^text\/html\W/
|
574
|
-
root = HTML::Document.new(part.body
|
420
|
+
root = HTML::Document.new(part.body).root
|
575
421
|
assert_select root, ":root", &block
|
576
422
|
end
|
577
423
|
end
|
@@ -579,62 +425,9 @@ module ActionDispatch
|
|
579
425
|
end
|
580
426
|
|
581
427
|
protected
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
:chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)",
|
586
|
-
:chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)",
|
587
|
-
:replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
|
588
|
-
:replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
|
589
|
-
:redirect => "window.location.href = #{RJS_ANY_ID}"
|
590
|
-
}
|
591
|
-
[:remove, :show, :hide, :toggle].each do |action|
|
592
|
-
RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)"
|
593
|
-
end
|
594
|
-
RJS_INSERTIONS = ["top", "bottom", "before", "after"]
|
595
|
-
RJS_INSERTIONS.each do |insertion|
|
596
|
-
RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)"
|
597
|
-
end
|
598
|
-
RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)"
|
599
|
-
RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
|
600
|
-
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
|
601
|
-
|
602
|
-
# +assert_select+ and +css_select+ call this to obtain the content in the HTML
|
603
|
-
# page, or from all the RJS statements, depending on the type of response.
|
604
|
-
def response_from_page_or_rjs()
|
605
|
-
content_type = @response.content_type
|
606
|
-
|
607
|
-
if content_type && Mime::JS =~ content_type
|
608
|
-
body = @response.body.dup
|
609
|
-
root = HTML::Node.new(nil)
|
610
|
-
|
611
|
-
while true
|
612
|
-
next if body.sub!(RJS_STATEMENTS[:any]) do |match|
|
613
|
-
html = unescape_rjs(match)
|
614
|
-
matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
|
615
|
-
root.children.concat matches
|
616
|
-
""
|
617
|
-
end
|
618
|
-
break
|
619
|
-
end
|
620
|
-
|
621
|
-
root
|
622
|
-
else
|
623
|
-
html_document.root
|
624
|
-
end
|
625
|
-
end
|
626
|
-
|
627
|
-
# Unescapes a RJS string.
|
628
|
-
def unescape_rjs(rjs_string)
|
629
|
-
# RJS encodes double quotes and line breaks.
|
630
|
-
unescaped= rjs_string.gsub('\"', '"')
|
631
|
-
unescaped.gsub!(/\\\//, '/')
|
632
|
-
unescaped.gsub!('\n', "\n")
|
633
|
-
unescaped.gsub!('\076', '>')
|
634
|
-
unescaped.gsub!('\074', '<')
|
635
|
-
# RJS encodes non-ascii characters.
|
636
|
-
unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
|
637
|
-
unescaped
|
428
|
+
# +assert_select+ and +css_select+ call this to obtain the content in the HTML page.
|
429
|
+
def response_from_page
|
430
|
+
html_document.root
|
638
431
|
end
|
639
432
|
end
|
640
433
|
end
|