diff options
Diffstat (limited to 'actionpack')
61 files changed, 625 insertions, 372 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 8eaaee5100..9fcff6a6ca 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,222 +1,3 @@ -## Rails 6.0.0.beta2 (February 25, 2019) ## -* Make debug exceptions works in an environment where ActiveStorage is not loaded. - *Tomoyuki Kurosawa* - -* `ActionDispatch::SystemTestCase.driven_by` can now be called with a block - to define specific browser capabilities. - - *Edouard Chin* - - -## Rails 6.0.0.beta1 (January 18, 2019) ## - -* Remove deprecated `fragment_cache_key` helper in favor of `combined_fragment_cache_key`. - - *Rafael Mendonça França* - -* Remove deprecated methods in `ActionDispatch::TestResponse`. - - `#success?`, `missing?` and `error?` were deprecated in Rails 5.2 in favor of - `#successful?`, `not_found?` and `server_error?`. - - *Rafael Mendonça França* - -* Introduce ActionDispatch::HostAuthorization - - This is a new middleware that guards against DNS rebinding attacks by - explicitly permitting the hosts a request can be made to. - - Each host is checked with the case operator (`#===`) to support `RegExp`, - `Proc`, `IPAddr` and custom objects as host allowances. - - *Genadi Samokovarov* - -* Allow using `parsed_body` in `ActionController::TestCase`. - - In addition to `ActionDispatch::IntegrationTest`, allow using - `parsed_body` in `ActionController::TestCase`: - - ``` - class SomeControllerTest < ActionController::TestCase - def test_some_action - post :action, body: { foo: 'bar' } - assert_equal({ "foo" => "bar" }, response.parsed_body) - end - end - ``` - - Fixes #34676. - - *Tobias Bühlmann* - -* Raise an error on root route naming conflicts. - - Raises an ArgumentError when multiple root routes are defined in the - same context instead of assigning nil names to subsequent roots. - - *Gannon McGibbon* - -* Allow rescue from parameter parse errors: - - ``` - rescue_from ActionDispatch::Http::Parameters::ParseError do - head :unauthorized - end - ``` - - *Gannon McGibbon*, *Josh Cheek* - -* Reset Capybara sessions if failed system test screenshot raising an exception. - - Reset Capybara sessions if `take_failed_screenshot` raise exception - in system test `after_teardown`. - - *Maxim Perepelitsa* - -* Use request object for context if there's no controller - - There is no controller instance when using a redirect route or a - mounted rack application so pass the request object as the context - when resolving dynamic CSP sources in this scenario. - - Fixes #34200. - - *Andrew White* - -* Apply mapping to symbols returned from dynamic CSP sources - - Previously if a dynamic source returned a symbol such as :self it - would be converted to a string implicitly, e.g: - - policy.default_src -> { :self } - - would generate the header: - - Content-Security-Policy: default-src self - - and now it generates: - - Content-Security-Policy: default-src 'self' - - *Andrew White* - -* Add `ActionController::Parameters#each_value`. - - *Lukáš Zapletal* - -* Deprecate `ActionDispatch::Http::ParameterFilter` in favor of `ActiveSupport::ParameterFilter`. - - *Yoshiyuki Kinjo* - -* Encode Content-Disposition filenames on `send_data` and `send_file`. - Previously, `send_data 'data', filename: "\u{3042}.txt"` sends - `"filename=\"\u{3042}.txt\""` as Content-Disposition and it can be - garbled. - Now it follows [RFC 2231](https://tools.ietf.org/html/rfc2231) and - [RFC 5987](https://tools.ietf.org/html/rfc5987) and sends - `"filename=\"%3F.txt\"; filename*=UTF-8''%E3%81%82.txt"`. - Most browsers can find filename correctly and old browsers fallback to ASCII - converted name. - - *Fumiaki Matsushima* - -* Expose `ActionController::Parameters#each_key` which allows iterating over - keys without allocating an array. - - *Richard Schneeman* - -* Purpose metadata for signed/encrypted cookies. - - Rails can now thwart attacks that attempt to copy signed/encrypted value - of a cookie and use it as the value of another cookie. - - It does so by stashing the cookie-name in the purpose field which is - then signed/encrypted along with the cookie value. Then, on a server-side - read, we verify the cookie-names and discard any attacked cookies. - - Enable `action_dispatch.use_cookies_with_metadata` to use this feature, which - writes cookies with the new purpose and expiry metadata embedded. - - *Assain Jaleel* - -* Raises `ActionController::RespondToMismatchError` with conflicting `respond_to` invocations. - - `respond_to` can match multiple types and lead to undefined behavior when - multiple invocations are made and the types do not match: - - respond_to do |outer_type| - outer_type.js do - respond_to do |inner_type| - inner_type.html { render body: "HTML" } - end - end - end - - *Patrick Toomey* - -* `ActionDispatch::Http::UploadedFile` now delegates `to_path` to its tempfile. - - This allows uploaded file objects to be passed directly to `File.read` - without raising a `TypeError`: - - uploaded_file = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file) - File.read(uploaded_file) - - *Aaron Kromer* - -* Pass along arguments to underlying `get` method in `follow_redirect!` - - Now all arguments passed to `follow_redirect!` are passed to the underlying - `get` method. This for example allows to set custom headers for the - redirection request to the server. - - follow_redirect!(params: { foo: :bar }) - - *Remo Fritzsche* - -* Introduce a new error page to when the implicit render page is accessed in the browser. - - Now instead of showing an error page that with exception and backtraces we now show only - one informative page. - - *Vinicius Stock* - -* Introduce `ActionDispatch::DebugExceptions.register_interceptor`. - - Exception aware plugin authors can use the newly introduced - `.register_interceptor` method to get the processed exception, instead of - monkey patching DebugExceptions. - - ActionDispatch::DebugExceptions.register_interceptor do |request, exception| - HypoteticalPlugin.capture_exception(request, exception) - end - - *Genadi Samokovarov* - -* Output only one Content-Security-Policy nonce header value per request. - - Fixes #32597. - - *Andrey Novikov*, *Andrew White* - -* Move default headers configuration into their own module that can be included in controllers. - - *Kevin Deisz* - -* Add method `dig` to `session`. - - *claudiob*, *Takumi Shotoku* - -* Controller level `force_ssl` has been deprecated in favor of - `config.force_ssl`. - - *Derek Prior* - -* Rails 6 requires Ruby 2.5.0 or newer. - - *Jeremy Daer*, *Kasper Timm Hansen* - - -Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actionpack/CHANGELOG.md) for previous changes. +Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionpack/CHANGELOG.md) for previous changes. diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc index f56230ffa0..fe85bc5b7a 100644 --- a/actionpack/README.rdoc +++ b/actionpack/README.rdoc @@ -23,6 +23,7 @@ by default and Action View rendering is implicitly triggered by Action Controller. However, these modules are designed to function on their own and can be used outside of Rails. +You can read more about Action Pack in the {Action Controller Overview}[https://guides.rubyonrails.org/action_controller_overview.html] guide. == Download and installation @@ -46,7 +47,7 @@ Action Pack is released under the MIT license: API documentation is at: -* http://api.rubyonrails.org +* https://api.rubyonrails.org Bug reports for the Ruby on Rails project can be filed here: diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index c4c755b7ac..735eb734d0 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.author = "David Heinemeier Hansson" s.email = "david@loudthinking.com" - s.homepage = "http://rubyonrails.org" + s.homepage = "https://rubyonrails.org" s.files = Dir["CHANGELOG.md", "README.rdoc", "MIT-LICENSE", "lib/**/*"] s.require_path = "lib" diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index f875aa5e6b..b9088e6d86 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -26,10 +26,10 @@ module ActionController end end - def build(action, app = Proc.new) + def build(action, app = nil, &block) action = action.to_s - middlewares.reverse.inject(app) do |a, middleware| + middlewares.reverse.inject(app || block) do |a, middleware| middleware.valid?(action) ? middleware.build(a) : a end end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index f1fb7ab0f7..193b488f6c 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -34,7 +34,7 @@ module ActionController # end # end # - # Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called: + # Then, in any view rendered by <tt>EventsController</tt>, the <tt>format_time</tt> method can be called: # # <% @events.each do |event| -%> # <p> diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index eb43ff9c63..dd69930e25 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -305,7 +305,7 @@ module ActionController logger.fatal do message = +"\n#{exception.class} (#{exception.message}):\n" - message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) + message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code) message << " " << exception.backtrace.join("\n ") "#{message}\n\n" end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 09716f7588..e635abec8e 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -93,7 +93,7 @@ module ActionController end def model - super || synchronize { super || self.model = _default_wrap_model } + super || self.model = _default_wrap_model end def include diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index cb109c6ad8..4bf8d90b69 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -431,7 +431,7 @@ module ActionController #:nodoc: The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually means you have the 'no-referrer' Referrer-Policy header enabled, or that the request came from a site that refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the - best solution is to change your referrer policy to something less strict like same-origin or strict-same-origin. + best solution is to change your referrer policy to something less strict like same-origin or strict-origin. If you cannot change the referrer policy, you can disable origin checking with the Rails.application.config.action_controller.forgery_protection_origin_check setting. MSG diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 815f82a1f2..ae774b01f1 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -4,7 +4,6 @@ require "active_support/core_ext/hash/indifferent_access" require "active_support/core_ext/array/wrap" require "active_support/core_ext/string/filters" require "active_support/core_ext/object/to_query" -require "active_support/rescuable" require "action_dispatch/http/upload" require "rack/test" require "stringio" @@ -1092,9 +1091,6 @@ module ActionController # See ActionController::Parameters.require and ActionController::Parameters.permit # for more information. module StrongParameters - extend ActiveSupport::Concern - include ActiveSupport::Rescuable - # Returns a new ActionController::Parameters object that # has been instantiated with the <tt>request.parameters</tt>. def params diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb index 8c16308ce7..dadf6d3445 100644 --- a/actionpack/lib/action_controller/renderer.rb +++ b/actionpack/lib/action_controller/renderer.rb @@ -116,7 +116,7 @@ module ActionController RACK_VALUE_TRANSLATION = { https: ->(v) { v ? "on" : "off" }, - method: ->(v) { v.upcase }, + method: ->(v) { -v.upcase }, } def rack_key_for(key) diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 8f39b88d56..6a4ba9af4a 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -53,6 +53,7 @@ module ActionDispatch autoload :RequestId autoload :Callbacks autoload :Cookies + autoload :ActionableExceptions autoload :DebugExceptions autoload :DebugLocks autoload :DebugView diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 498b1e6695..4e81ba12a5 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -79,6 +79,11 @@ module ActionDispatch else [Mime[:html]] end + + v = v.select do |format| + format.symbol || format.ref == "*/*" + end + set_header k, v end end diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index c3e0ea3c89..88b3a93211 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -170,6 +170,7 @@ module Mime def parse(accept_header) if !accept_header.include?(",") accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first + return [] unless accept_header parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact else list, index = [], 0 @@ -221,7 +222,18 @@ module Mime attr_reader :hash + MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}" + MIME_PARAMETER_KEY = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}" + MIME_PARAMETER_VALUE = "#{Regexp.escape('"')}?[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}#{Regexp.escape('"')}?" + MIME_PARAMETER = "\s*\;\s+#{MIME_PARAMETER_KEY}(?:\=#{MIME_PARAMETER_VALUE})?" + MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?:\s*#{MIME_PARAMETER}\s*)*)\z/ + + class InvalidMimeType < StandardError; end + def initialize(string, symbol = nil, synonyms = []) + unless MIME_REGEXP.match?(string) + raise InvalidMimeType, "#{string.inspect} is not a valid MIME type" + end @symbol, @synonyms = symbol, synonyms @string = string @hash = [@string, @synonyms, @symbol].hash @@ -303,7 +315,7 @@ module Mime include Singleton def initialize - super "*/*", :all + super "*/*", nil end def all?; true; end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 1d38942a31..69798f99e0 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -82,6 +82,7 @@ module ActionDispatch # :nodoc: SET_COOKIE = "Set-Cookie" LOCATION = "Location" NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304] + CONTENT_TYPE_PARSER = /\A(?<type>[^;\s]+)?(?:.*;\s*charset=(?<quote>"?)(?<charset>[^;\s]+)\k<quote>)?/ # :nodoc: cattr_accessor :default_charset, default: "utf-8" cattr_accessor :default_headers @@ -409,10 +410,8 @@ module ActionDispatch # :nodoc: NullContentTypeHeader = ContentTypeHeader.new nil, nil def parse_content_type(content_type) - if content_type - type, charset = content_type.split(/;\s*charset=/) - type = nil if type && type.empty? - ContentTypeHeader.new(type, charset) + if content_type && match = CONTENT_TYPE_PARSER.match(content_type) + ContentTypeHeader.new(match[:type], match[:charset]) else NullContentTypeHeader end diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb index 697f5b9d8b..dee2980eb1 100644 --- a/actionpack/lib/action_dispatch/journey/path/pattern.rb +++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb @@ -119,7 +119,8 @@ module ActionDispatch class UnanchoredRegexp < AnchoredRegexp # :nodoc: def accept(node) - %r{\A#{visit node}(?:\b|\Z)} + path = visit node + path == "/" ? %r{\A/} : %r{\A#{path}(?:\b|\Z|/)} end end @@ -136,6 +137,10 @@ module ActionDispatch Array.new(length - 1) { |i| self[i + 1] } end + def named_captures + @names.zip(captures).to_h + end + def [](x) idx = @offsets[x - 1] + x @match[idx] diff --git a/actionpack/lib/action_dispatch/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb index c0377459d5..3ba8361d77 100644 --- a/actionpack/lib/action_dispatch/journey/routes.rb +++ b/actionpack/lib/action_dispatch/journey/routes.rb @@ -56,7 +56,6 @@ module ActionDispatch end def simulator - return if ast.nil? @simulator ||= begin gtg = GTG::Builder.new(ast).transition_table GTG::Simulator.new(gtg) diff --git a/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb b/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb new file mode 100644 index 0000000000..e94cc46603 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "erb" +require "action_dispatch/http/request" +require "active_support/actionable_error" + +module ActionDispatch + class ActionableExceptions # :nodoc: + cattr_accessor :endpoint, default: "/rails/actions" + + def initialize(app) + @app = app + end + + def call(env) + request = ActionDispatch::Request.new(env) + return @app.call(env) unless actionable_request?(request) + + ActiveSupport::ActionableError.dispatch(request.params[:error].to_s.safe_constantize, request.params[:action]) + + redirect_to request.params[:location] + end + + private + def actionable_request?(request) + request.show_exceptions? && request.post? && request.path == endpoint + end + + def redirect_to(location) + body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>" + + [302, { + "Content-Type" => "text/html; charset=#{Response.default_charset}", + "Content-Length" => body.bytesize.to_s, + "Location" => location, + }, [body]] + end + end +end diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 1611a8b3dd..b69bcab05c 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -338,7 +338,7 @@ module ActionDispatch def update_cookies_from_jar request_jar = @request.cookie_jar.instance_variable_get(:@cookies) - set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) } + set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) || @set_cookies.key?(k) } @cookies.update set_cookies if set_cookies end diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb index 61773d97a2..0b15c94122 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb @@ -4,6 +4,8 @@ require "action_dispatch/http/request" require "action_dispatch/middleware/exception_wrapper" require "action_dispatch/routing/inspector" +require "active_support/actionable_error" + require "action_view" require "action_view/base" @@ -60,7 +62,11 @@ module ActionDispatch log_error(request, wrapper) if request.get_header("action_dispatch.show_detailed_exceptions") - content_type = request.formats.first + begin + content_type = request.formats.first + rescue Mime::Type::InvalidMimeType + render_for_api_request(Mime[:text], wrapper) + end if api_request?(content_type) render_for_api_request(content_type, wrapper) @@ -142,7 +148,7 @@ module ActionDispatch message = [] message << " " message << "#{exception.class} (#{exception.message}):" - message.concat(exception.annoted_source_code) if exception.respond_to?(:annoted_source_code) + message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code) message << " " message.concat(trace) diff --git a/actionpack/lib/action_dispatch/middleware/debug_view.rb b/actionpack/lib/action_dispatch/middleware/debug_view.rb index 43c0a84504..a03650254e 100644 --- a/actionpack/lib/action_dispatch/middleware/debug_view.rb +++ b/actionpack/lib/action_dispatch/middleware/debug_view.rb @@ -52,5 +52,9 @@ module ActionDispatch super end end + + def protect_against_forgery? + false + end end end diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index 1fb3e9db00..0cc56f5013 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -12,6 +12,7 @@ module ActionDispatch "ActionController::UnknownHttpMethod" => :method_not_allowed, "ActionController::NotImplemented" => :not_implemented, "ActionController::UnknownFormat" => :not_acceptable, + "Mime::Type::InvalidMimeType" => :not_acceptable, "ActionController::MissingExactTemplate" => :not_acceptable, "ActionController::InvalidAuthenticityToken" => :unprocessable_entity, "ActionController::InvalidCrossOriginRequest" => :unprocessable_entity, diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb index 3feb3a19f3..a88ad40f21 100644 --- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb @@ -21,8 +21,12 @@ module ActionDispatch def call(env) request = ActionDispatch::Request.new(env) status = request.path_info[1..-1].to_i - content_type = request.formats.first - body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) } + begin + content_type = request.formats.first + rescue Mime::Type::InvalidMimeType + content_type = Mime[:text] + end + body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) } render(status, content_type, body) end diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 02ccfbc81a..7c43c781c7 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -24,9 +24,10 @@ module ActionDispatch # # Rails.application.config.session_store :cookie_store, key: '_your_app_session' # - # By default, your secret key base is derived from your application name in - # the test and development environments. In all other environments, it is stored - # encrypted in the <tt>config/credentials.yml.enc</tt> file. + # In the development and test environments your application's secret key base is + # generated by Rails and stored in a temporary file in <tt>tmp/development_secret.txt</tt>. + # In all other environments, it is stored encrypted in the + # <tt>config/credentials.yml.enc</tt> file. # # If your application was not updated to Rails 5.2 defaults, the secret_key_base # will be found in the old <tt>config/secrets.yml</tt> file. diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index b82f8aa3a3..f0c869fba0 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -34,7 +34,28 @@ module ActionDispatch end def build(app) - klass.new(app, *args, &block) + InstrumentationProxy.new(klass.new(app, *args, &block), inspect) + end + end + + # This class is used to instrument the execution of a single middleware. + # It proxies the `call` method transparently and instruments the method + # call. + class InstrumentationProxy + EVENT_NAME = "process_middleware.action_dispatch" + + def initialize(middleware, class_name) + @middleware = middleware + + @payload = { + middleware: class_name, + } + end + + def call(env) + ActiveSupport::Notifications.instrument(EVENT_NAME, @payload) do + @middleware.call(env) + end end end @@ -97,8 +118,8 @@ module ActionDispatch middlewares.push(build_middleware(klass, args, block)) end - def build(app = Proc.new) - middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) } + def build(app = nil, &block) + middlewares.freeze.reverse.inject(app || block) { |a, e| e.build(a) } end private diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb new file mode 100644 index 0000000000..b6c6d2f50d --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb @@ -0,0 +1,13 @@ +<% actions = ActiveSupport::ActionableError.actions(exception) %> + +<% if actions.any? %> + <div class="actions"> + <% actions.each do |action, _| %> + <%= button_to action, ActionDispatch::ActionableExceptions.endpoint, params: { + error: exception.class.name, + action: action, + location: request.path + } %> + <% end %> + </div> +<% end %> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb index bde26f46c2..999e84e4d6 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb @@ -8,7 +8,11 @@ </header> <div id="container"> - <h2><%= h @exception.message %></h2> + <h2> + <%= h @exception.message %> + + <%= render "rescues/actions", exception: @exception, request: @request %> + </h2> <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx, error_index: 0 %> <%= render "rescues/trace", traces: @traces, trace_to_show: @trace_to_show, error_index: 0 %> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb index d144fc1cd2..77cfdd20c8 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb @@ -13,6 +13,9 @@ <% if defined?(ActiveStorage) && @exception.message.match?(%r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}) %> <br />To resolve this issue run: rails active_storage:install <% end %> + <% if defined?(ActionMailbox) && @exception.message.match?(%r{#{ActionMailbox::InboundEmail.table_name}}) %> + <br />To resolve this issue run: rails action_mailbox:install + <% end %> </h2> <%= render "rescues/source", source_extracts: @source_extracts, show_source_idx: @show_source_idx %> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb index 55aaf58713..16c3ecc331 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb @@ -6,6 +6,8 @@ <%= @exception.message %> <% if defined?(ActiveStorage) && @exception.message.match?(%r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}) %> To resolve this issue run: rails active_storage:install +<% if defined?(ActionMailbox) && @exception.message.match?(%r{#{ActionMailbox::InboundEmail.table_name}}) %> +To resolve this issue run: rails action_mailbox:install <% end %> <%= render template: "rescues/_source" %> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb index 39ea25bdfc..0f78e23b7f 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb @@ -117,6 +117,10 @@ background-color: #FFCCCC; } + .button_to { + display: inline-block; + } + .hidden { display: none; } diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index d67044b4ac..f29f66990d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -115,9 +115,9 @@ module ActionDispatch @defaults = defaults @set = set - @to = to - @default_controller = controller - @default_action = default_action + @to = intern(to) + @default_controller = intern(controller) + @default_action = intern(default_action) @ast = ast @anchor = anchor @via = via @@ -222,6 +222,10 @@ module ActionDispatch private :build_path private + def intern(object) + object.is_a?(String) ? -object : object + end + def add_wildcard_options(options, formatted, path_ast) # Add a constraint for wildcard route to make it non-greedy and match the # optional format part of the route by default. @@ -1141,6 +1145,10 @@ module ActionDispatch attr_reader :controller, :path, :param def initialize(entities, api_only, shallow, options = {}) + if options[:param].to_s.include?(":") + raise ArgumentError, ":param option can't contain colons" + end + @name = entities.to_s @path = (options[:path] || @name).to_s @controller = (options[:controller] || @name).to_s @@ -1398,6 +1406,8 @@ module ActionDispatch # as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt> # to be shortened to just <tt>/comments/1234</tt>. # + # Set <tt>shallow: false</tt> on a child resource to ignore a parent's shallow parameter. + # # [:shallow_path] # Prefixes nested shallow routes with the specified path. # @@ -1440,6 +1450,9 @@ module ActionDispatch # Allows you to specify the default value for optional +format+ # segment or disable it by supplying +false+. # + # [:param] + # Allows you to override the default param name of +:id+ in the URL. + # # === Examples # # # routes call <tt>Admin::PostsController</tt> @@ -1665,7 +1678,8 @@ module ActionDispatch return true end - if options.delete(:shallow) + if options[:shallow] + options.delete(:shallow) shallow do send(method, resources.pop, options, &block) end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 972953d4f3..bbb5762b3c 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -317,23 +317,21 @@ module ActionDispatch # def define_url_helper(mod, route, name, opts, route_key, url_strategy) helper = UrlHelper.create(route, opts, route_key, url_strategy) - mod.module_eval do - define_method(name) do |*args| - last = args.last - options = \ - case last - when Hash - args.pop - when ActionController::Parameters - args.pop.to_h - end - helper.call self, args, options - end + mod.define_method(name) do |*args| + last = args.last + options = \ + case last + when Hash + args.pop + when ActionController::Parameters + args.pop.to_h + end + helper.call self, args, options end end end - # strategy for building urls to send to the client + # strategy for building URLs to send to the client PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) } UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) } @@ -593,14 +591,14 @@ module ActionDispatch if route.segment_keys.include?(:controller) ActiveSupport::Deprecation.warn(<<-MSG.squish) Using a dynamic :controller segment in a route is deprecated and - will be removed in Rails 6.0. + will be removed in Rails 6.1. MSG end if route.segment_keys.include?(:action) ActiveSupport::Deprecation.warn(<<-MSG.squish) Using a dynamic :action segment in a route is deprecated and - will be removed in Rails 6.0. + will be removed in Rails 6.1. MSG end diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb index 79359a0c8b..056ce51a61 100644 --- a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb @@ -39,7 +39,8 @@ module ActionDispatch private def image_name - failed? ? "failures_#{method_name}" : method_name + name = method_name[0...225] + failed? ? "failures_#{name}" : name end def image_path diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb index 600e9c733b..7080dbe022 100644 --- a/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb @@ -16,12 +16,14 @@ module ActionDispatch super end + def before_teardown + take_failed_screenshot + ensure + super + end + def after_teardown - begin - take_failed_screenshot - ensure - Capybara.reset_sessions! - end + Capybara.reset_sessions! ensure super end diff --git a/actionpack/lib/action_dispatch/testing/request_encoder.rb b/actionpack/lib/action_dispatch/testing/request_encoder.rb index 9889f61951..6c65bec62f 100644 --- a/actionpack/lib/action_dispatch/testing/request_encoder.rb +++ b/actionpack/lib/action_dispatch/testing/request_encoder.rb @@ -38,8 +38,8 @@ module ActionDispatch end def self.parser(content_type) - mime = Mime::Type.lookup(content_type) - encoder(mime ? mime.ref : nil).response_parser + type = Mime::Type.lookup(content_type).ref if content_type + encoder(type).response_parser end def self.encoder(name) diff --git a/actionpack/lib/action_pack/gem_version.rb b/actionpack/lib/action_pack/gem_version.rb index 8007cfe35b..5f8905139d 100644 --- a/actionpack/lib/action_pack/gem_version.rb +++ b/actionpack/lib/action_pack/gem_version.rb @@ -8,9 +8,9 @@ module ActionPack module VERSION MAJOR = 6 - MINOR = 0 + MINOR = 1 TINY = 0 - PRE = "beta2" + PRE = "alpha" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index f23151e518..32a0b8efeb 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -96,6 +96,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware| middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public") middleware.use ActionDispatch::DebugExceptions + middleware.use ActionDispatch::ActionableExceptions middleware.use ActionDispatch::Callbacks middleware.use ActionDispatch::Cookies middleware.use ActionDispatch::Flash @@ -382,3 +383,5 @@ end class DrivenBySeleniumWithHeadlessFirefox < ActionDispatch::SystemTestCase driven_by :selenium, using: :headless_firefox end + +require_relative "../../tools/test_common" diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index ecb8c37e6b..51286155b9 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -282,7 +282,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase assert_no_match( /#{request.protocol}#{request.host}\/\/www.rubyonrails.org/, ex.message, - "protocol relative url was incorrectly normalized" + "protocol relative URL was incorrectly normalized" ) end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index b5503a9c64..4dddd98f9f 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -808,17 +808,17 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest end end - test "session uses default url options from routes" do + test "session uses default URL options from routes" do assert_equal "http://foo.com/foo", foos_url end - test "current host overrides default url options from routes" do + test "current host overrides default URL options from routes" do get "/foo" assert_response :success assert_equal "http://www.example.com/foo", foos_url end - test "controller can override default url options from request" do + test "controller can override default URL options from request" do get "/bar" assert_response :success assert_equal "http://bar.com/foo", foos_url diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 0562c16284..1a7e7f6cbb 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -98,6 +98,7 @@ class ACLogSubscriberTest < ActionController::TestCase @cache_path = Dir.mktmpdir(%w[tmp cache]) @controller.cache_store = :file_store, @cache_path + @controller.config.perform_caching = true ActionController::LogSubscriber.attach_to :action_controller end @@ -249,19 +250,15 @@ class ACLogSubscriberTest < ActionController::TestCase end def test_with_fragment_cache - @controller.config.perform_caching = true get :with_fragment_cache wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_when_log_disabled - @controller.config.perform_caching = true ActionController::Base.enable_fragment_cache_logging = false get :with_fragment_cache wait @@ -269,69 +266,52 @@ class ACLogSubscriberTest < ActionController::TestCase assert_equal 2, logs.size assert_equal "Processing by Another::LogSubscribersController#with_fragment_cache as HTML", logs[0] assert_match(/Completed 200 OK in \d+ms/, logs[1]) - ensure - @controller.config.perform_caching = true ActionController::Base.enable_fragment_cache_logging = true end def test_with_fragment_cache_if_with_true - @controller.config.perform_caching = true get :with_fragment_cache_if_with_true_condition wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_if_with_false - @controller.config.perform_caching = true get :with_fragment_cache_if_with_false_condition wait assert_equal 2, logs.size assert_no_match(/Read fragment views\/foo/, logs[1]) assert_no_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_unless_with_true - @controller.config.perform_caching = true get :with_fragment_cache_unless_with_true_condition wait assert_equal 2, logs.size assert_no_match(/Read fragment views\/foo/, logs[1]) assert_no_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_unless_with_false - @controller.config.perform_caching = true get :with_fragment_cache_unless_with_false_condition wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_with_fragment_cache_and_percent_in_key - @controller.config.perform_caching = true get :with_fragment_cache_and_percent_in_key wait assert_equal 4, logs.size assert_match(/Read fragment views\/foo/, logs[1]) assert_match(/Write fragment views\/foo/, logs[2]) - ensure - @controller.config.perform_caching = true end def test_process_action_with_exception_includes_http_status_code diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index 00e1d5f3b3..2f8f191828 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -125,7 +125,7 @@ class RespondToController < ActionController::Base def custom_type_handling respond_to do |type| type.html { render body: "HTML" } - type.custom("application/crazy-xml") { render body: "Crazy XML" } + type.custom("application/fancy-xml") { render body: "Fancy XML" } type.all { render body: "Nothing" } end end @@ -158,6 +158,12 @@ class RespondToController < ActionController::Base end end + def handle_any_with_template + respond_to do |type| + type.any { render "test/hello_world" } + end + end + def all_types_with_layout respond_to do |type| type.html @@ -314,12 +320,14 @@ class RespondToControllerTest < ActionController::TestCase @request.host = "www.example.com" Mime::Type.register_alias("text/html", :iphone) Mime::Type.register("text/x-mobile", :mobile) + Mime::Type.register("application/fancy-xml", :fancy_xml) end def teardown super Mime::Type.unregister(:iphone) Mime::Type.unregister(:mobile) + Mime::Type.unregister(:fancy_xml) end def test_html @@ -489,10 +497,10 @@ class RespondToControllerTest < ActionController::TestCase end def test_custom_types - @request.accept = "application/crazy-xml" + @request.accept = "application/fancy-xml" get :custom_type_handling - assert_equal "application/crazy-xml", @response.content_type - assert_equal "Crazy XML", @response.body + assert_equal "application/fancy-xml", @response.content_type + assert_equal "Fancy XML", @response.body @request.accept = "text/html" get :custom_type_handling @@ -570,6 +578,13 @@ class RespondToControllerTest < ActionController::TestCase assert_equal "HTML", @response.body end + def test_handle_any_with_template + @request.accept = "*/*" + + get :handle_any_with_template + assert_equal "Hello world!", @response.body + end + def test_html_type_with_layout @request.accept = "text/html" get :all_types_with_layout diff --git a/actionpack/test/controller/new_base/content_negotiation_test.rb b/actionpack/test/controller/new_base/content_negotiation_test.rb index 7205e90176..548fa4300d 100644 --- a/actionpack/test/controller/new_base/content_negotiation_test.rb +++ b/actionpack/test/controller/new_base/content_negotiation_test.rb @@ -20,9 +20,19 @@ module ContentNegotiation assert_body "Hello world */*!" end - test "Not all mimes are converted to symbol" do + test "A js or */* Accept header will return HTML" do + get "/content_negotiation/basic/hello", headers: { "HTTP_ACCEPT" => "text/javascript, */*" } + assert_body "Hello world text/html!" + end + + test "A js or */* Accept header on xhr will return JavaScript" do + get "/content_negotiation/basic/hello", headers: { "HTTP_ACCEPT" => "text/javascript, */*" }, xhr: true + assert_body "Hello world text/javascript!" + end + + test "Unregistered mimes are ignored" do get "/content_negotiation/basic/all", headers: { "HTTP_ACCEPT" => "text/plain, mime/another" } - assert_body '[:text, "mime/another"]' + assert_body "[:text]" end end end diff --git a/actionpack/test/controller/new_base/render_file_test.rb b/actionpack/test/controller/new_base/render_file_test.rb index de8af029e0..01d0223519 100644 --- a/actionpack/test/controller/new_base/render_file_test.rb +++ b/actionpack/test/controller/new_base/render_file_test.rb @@ -17,12 +17,12 @@ module RenderFile def relative_path @secret = "in the sauce" - render file: "../../fixtures/test/render_file_with_ivar" + render file: "../actionpack/test/fixtures/test/render_file_with_ivar" end def relative_path_with_dot @secret = "in the sauce" - render file: "../../fixtures/test/dot.directory/render_file_with_ivar" + render file: "../actionpack/test/fixtures/test/dot.directory/render_file_with_ivar" end def pathname @@ -40,32 +40,44 @@ module RenderFile testing RenderFile::BasicController test "rendering simple template" do - get :index + assert_deprecated do + get :index + end assert_response "Hello world!" end test "rendering template with ivar" do - get :with_instance_variables + assert_deprecated do + get :with_instance_variables + end assert_response "The secret is in the sauce\n" end test "rendering a relative path" do - get :relative_path + assert_deprecated do + get :relative_path + end assert_response "The secret is in the sauce\n" end test "rendering a relative path with dot" do - get :relative_path_with_dot + assert_deprecated do + get :relative_path_with_dot + end assert_response "The secret is in the sauce\n" end test "rendering a Pathname" do - get :pathname + assert_deprecated do + get :pathname + end assert_response "The secret is in the sauce\n" end test "rendering file with locals" do - get :with_locals + assert_deprecated do + get :with_locals + end assert_response "The secret is in the sauce\n" end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 4750093c5c..8bb6617eaa 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -323,11 +323,12 @@ class ExpiresInRenderTest < ActionController::TestCase end def test_dynamic_render_with_file - # This is extremely bad, but should be possible to do. assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__)) - response = get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' } - assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)), - response.body + assert_deprecated do + assert_raises ActionView::MissingTemplate do + get :dynamic_render_with_file, params: { id: '../\\../test/abstract_unit.rb' } + end + end end def test_dynamic_render_with_absolute_path @@ -351,9 +352,11 @@ class ExpiresInRenderTest < ActionController::TestCase def test_permitted_dynamic_render_file_hash assert File.exist?(File.expand_path("../../test/abstract_unit.rb", __dir__)) - response = get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } } - assert_equal File.read(File.expand_path("../../test/abstract_unit.rb", __dir__)), - response.body + assert_deprecated do + assert_raises ActionView::MissingTemplate do + get :dynamic_render_permit, params: { id: { file: '../\\../test/abstract_unit.rb' } } + end + end end def test_dynamic_render_file_hash diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb index ae8330e029..ea79f4de85 100644 --- a/actionpack/test/controller/renderer_test.rb +++ b/actionpack/test/controller/renderer_test.rb @@ -40,7 +40,7 @@ class RendererTest < ActiveSupport::TestCase test "rendering with an instance renderer" do renderer = ApplicationController.renderer.new - content = renderer.render file: "test/hello_world" + content = assert_deprecated { renderer.render file: "test/hello_world" } assert_equal "Hello world!", content end @@ -115,14 +115,14 @@ class RendererTest < ActiveSupport::TestCase assert_equal "true", content end - test "return valid asset url with defaults" do + test "return valid asset URL with defaults" do renderer = ApplicationController.renderer content = renderer.render inline: "<%= asset_url 'asset.jpg' %>" assert_equal "http://example.org/asset.jpg", content end - test "return valid asset url when https is true" do + test "return valid asset URL when https is true" do renderer = ApplicationController.renderer.new https: true content = renderer.render inline: "<%= asset_url 'asset.jpg' %>" diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 2094aa1aed..8724f9bcdb 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -99,15 +99,16 @@ module ShowExceptions class ShowFailsafeExceptionsTest < ActionDispatch::IntegrationTest def test_render_failsafe_exception @app = ShowExceptionsOverriddenController.action(:boom) - @exceptions_app = @app.instance_variable_get(:@exceptions_app) - @app.instance_variable_set(:@exceptions_app, nil) + middleware = @app.instance_variable_get(:@middleware) + @exceptions_app = middleware.instance_variable_get(:@exceptions_app) + middleware.instance_variable_set(:@exceptions_app, nil) $stderr = StringIO.new get "/", headers: { "HTTP_ACCEPT" => "text/json" } assert_response :internal_server_error assert_equal "text/plain", response.content_type.to_s ensure - @app.instance_variable_set(:@exceptions_app, @exceptions_app) + middleware.instance_variable_set(:@exceptions_app, @exceptions_app) $stderr = STDERR end end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index d1cd190747..998a495d0d 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -952,7 +952,7 @@ XML get :create assert_response :created - # Redirect url doesn't care that it wasn't a :redirect response. + # Redirect URL doesn't care that it wasn't a :redirect response. assert_equal "/resource", @response.redirect_url assert_equal @response.redirect_url, redirect_to_url diff --git a/actionpack/test/dispatch/actionable_exceptions_test.rb b/actionpack/test/dispatch/actionable_exceptions_test.rb new file mode 100644 index 0000000000..9215a91e9c --- /dev/null +++ b/actionpack/test/dispatch/actionable_exceptions_test.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class ActionableExceptionsTest < ActionDispatch::IntegrationTest + Actions = [] + + class ActionError < StandardError + include ActiveSupport::ActionableError + + action "Successful action" do + Actions << "Action!" + end + + action "Failed action" do + raise "Inaction!" + end + end + + Noop = -> env { [200, {}, [""]] } + + setup do + @app = ActionDispatch::ActionableExceptions.new(Noop) + + Actions.clear + end + + test "dispatches an actionable error" do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: ActionError.name, + action: "Successful action", + location: "/", + } + + assert_equal ["Action!"], Actions + + assert_equal 302, response.status + assert_equal "/", response.headers["Location"] + end + + test "cannot dispatch errors if not allowed" do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: ActionError.name, + action: "Successful action", + location: "/", + }, headers: { "action_dispatch.show_exceptions" => false } + + assert_empty Actions + end + + test "dispatched action can fail" do + assert_raise RuntimeError do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: ActionError.name, + action: "Failed action", + location: "/", + } + end + end + + test "cannot dispatch non-actionable errors" do + assert_raise ActiveSupport::ActionableError::NonActionable do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: RuntimeError.name, + action: "Inexistent action", + location: "/", + } + end + end + + test "cannot dispatch Inexistent errors" do + assert_raise ActiveSupport::ActionableError::NonActionable do + post ActionDispatch::ActionableExceptions.endpoint, params: { + error: "", + action: "Inexistent action", + location: "/", + } + end + end +end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 4aaac1320e..d129fa717d 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -123,6 +123,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_cookie_if_not_present + cookies["user_name"] = "alice" unless cookies["user_name"].present? + head :ok + end + def logout cookies.delete("user_name") head :ok @@ -312,7 +317,7 @@ class CookiesTest < ActionController::TestCase end def rails_5_2_stable_encrypted_cookie_with_authenticated_encryption_flag_off - cookies[:favorite] = "Wmg4amgvcVVvWGcwK3c4WjJEbTdRQUgrWXhBdDliUTR0cVNidXpmVTMrc2RjcitwUzVsWWEwZGtuVGtFUjJwNi0tcVhVMTFMOTQ1d0hIVE1FK0pJc05SQT09--8b2a55c375049a50f7a959b9d42b31ef0b2bb594" + cookies[:favorite] = "rTG4zs5UufEFAr+ppKwh+MDMymKyAUMOSaWyYa3uUVmD8sMQqyiyQBxgYeAncDHVZIlo4y+kDVSzp66u1/7BNYpnmFe8ES/YT2m8ckNA23jBDmnRZ9CTNfMIRXjFtfxO9YxEOzzhn0ZiA0/zFtr5wkluXtxplOz959Q7MgLOyvTze2h9p8A=--QHOS3rAEGq/HCxXs--xQNra8dk24Idc2qBtpMLpg==" head :ok end @@ -336,7 +341,7 @@ class CookiesTest < ActionController::TestCase SECRET_KEY_BASE = "b3c631c314c0bbca50c1b2843150fe33" SIGNED_COOKIE_SALT = "signed cookie" ENCRYPTED_COOKIE_SALT = "encrypted cookie" - ENCRYPTED_SIGNED_COOKIE_SALT = "sigend encrypted cookie" + ENCRYPTED_SIGNED_COOKIE_SALT = "signed encrypted cookie" AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "authenticated encrypted cookie" def setup @@ -1128,6 +1133,14 @@ class CookiesTest < ActionController::TestCase assert_equal "bar", @controller.encrypted_cookie end + def test_cookie_override + get :set_cookie_if_not_present + assert_equal "alice", cookies["user_name"] + cookies["user_name"] = "bob" + get :set_cookie_if_not_present + assert_equal "bob", cookies["user_name"] + end + def test_signed_cookie_with_expires_set_relatively request.env["action_dispatch.use_cookies_with_metadata"] = true diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index c85476fa38..5ae8a20ae4 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -5,6 +5,18 @@ require "abstract_unit" class DebugExceptionsTest < ActionDispatch::IntegrationTest InterceptedErrorInstance = StandardError.new + class CustomActionableError < StandardError + include ActiveSupport::ActionableError + + action "Action 1" do + nil + end + + action "Action 2" do + nil + end + end + class Boomer attr_accessor :closed @@ -58,6 +70,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest raise ActionController::NotImplemented when "/unprocessable_entity" raise ActionController::InvalidAuthenticityToken + when "/invalid_mimetype" + raise Mime::Type::InvalidMimeType when "/not_found_original_exception" begin raise AbstractController::ActionNotFound.new @@ -90,6 +104,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest method_that_raises when "/nested_exceptions" raise_nested_exceptions + when %r{/actionable_error} + raise CustomActionableError else raise "puke!" end @@ -178,6 +194,10 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/parameter_missing", headers: { "action_dispatch.show_exceptions" => true } assert_response 400 assert_match(/ActionController::ParameterMissing/, body) + + get "/invalid_mimetype", headers: { "Accept" => "text/html,*", "action_dispatch.show_exceptions" => true } + assert_response 406 + assert_match(/Mime::Type::InvalidMimeType/, body) end test "rescue with text error for xhr request" do @@ -335,7 +355,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_match %r{NameError}, body end - test "named urls missing keys raise 500 level error" do + test "named URLs missing keys raise 500 level error" do @app = DevelopmentApp get "/missing_keys", headers: { "action_dispatch.show_exceptions" => true } @@ -583,4 +603,21 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end end end + + test "shows a buttons for every action in an actionable error" do + @app = DevelopmentApp + Rails.stub :root, Pathname.new(".") do + cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| + bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } + end + + get "/actionable_error", headers: { "action_dispatch.backtrace_cleaner" => cleaner } + + # Assert correct error + assert_response 500 + + assert_select 'input[value="Action 1"]' + assert_select 'input[value="Action 2"]' + end + end end diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb index 5f43e5a3c5..90f2eccd19 100644 --- a/actionpack/test/dispatch/middleware_stack_test.rb +++ b/actionpack/test/dispatch/middleware_stack_test.rb @@ -3,13 +3,24 @@ require "abstract_unit" class MiddlewareStackTest < ActiveSupport::TestCase - class FooMiddleware; end - class BarMiddleware; end - class BazMiddleware; end - class HiyaMiddleware; end - class BlockMiddleware + class Base + def initialize(app) + @app = app + end + + def call(env) + @app.call(env) + end + end + + class FooMiddleware < Base; end + class BarMiddleware < Base; end + class BazMiddleware < Base; end + class HiyaMiddleware < Base; end + class BlockMiddleware < Base attr_reader :block - def initialize(&block) + def initialize(app, &block) + super(app) @block = block end end @@ -109,6 +120,24 @@ class MiddlewareStackTest < ActiveSupport::TestCase assert_equal @stack.last, @stack.last end + test "instruments the execution of middlewares" do + app = @stack.build(proc { |env| [200, {}, []] }) + env = {} + + events = [] + + subscriber = proc do |*args| + events << ActiveSupport::Notifications::Event.new(*args) + end + + ActiveSupport::Notifications.subscribed(subscriber, "process_middleware.action_dispatch") do + app.call(env) + end + + assert_equal 2, events.count + assert_equal ["MiddlewareStackTest::BarMiddleware", "MiddlewareStackTest::FooMiddleware"], events.map { |e| e.payload[:middleware] } + end + test "includes a middleware" do assert_equal true, @stack.include?(ActionDispatch::MiddlewareStack::Middleware.new(BarMiddleware, nil, nil)) end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 45d91883c0..50f6c06fee 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -174,4 +174,51 @@ class MimeTypeTest < ActiveSupport::TestCase assert_not (Mime[:js] !~ "application/javascript") assert Mime[:html] =~ "application/xhtml+xml" end + + test "can be initialized with wildcards" do + assert_equal "*/*", Mime::Type.new("*/*").to_s + assert_equal "text/*", Mime::Type.new("text/*").to_s + assert_equal "video/*", Mime::Type.new("video/*").to_s + end + + test "can be initialized with parameters" do + assert_equal "text/html; parameter", Mime::Type.new("text/html; parameter").to_s + assert_equal "text/html; parameter=abc", Mime::Type.new("text/html; parameter=abc").to_s + assert_equal 'text/html; parameter="abc"', Mime::Type.new('text/html; parameter="abc"').to_s + assert_equal 'text/html; parameter=abc; parameter2="xyz"', Mime::Type.new('text/html; parameter=abc; parameter2="xyz"').to_s + end + + test "invalid mime types raise error" do + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("too/many/slash") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("missingslash") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("improper/semicolon;") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new('improper/semicolon; parameter=abc; parameter2="xyz";') + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("text/html, text/plain") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("*/html") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new("") + end + + assert_raises Mime::Type::InvalidMimeType do + Mime::Type.new(nil) + end + end end diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index e42ea89f6f..758cee9930 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -27,6 +27,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest } mount SprocketsApp, at: "/sprockets" + mount SprocketsApp, at: "/star*" mount SprocketsApp => "/shorthand" mount SinatraLikeApp, at: "/fakeengine", as: :fake @@ -58,6 +59,14 @@ class TestRoutingMount < ActionDispatch::IntegrationTest def test_mounting_at_root_path get "/omg" assert_equal " -- /omg", response.body + + get "/~omg" + assert_equal " -- /~omg", response.body + end + + def test_mounting_at_path_with_non_word_character + get "/star*/omg" + assert_equal "/star* -- /omg", response.body end def test_mounting_sets_script_name diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index 7a7a201b11..63c147cb1b 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -151,17 +151,17 @@ module TestGenerationPrefix include BlogEngine.routes.mounted_helpers # Inside Engine - test "[ENGINE] generating engine's url use SCRIPT_NAME from request" do + test "[ENGINE] generating engine's URL use SCRIPT_NAME from request" do get "/pure-awesomeness/blog/posts/1" assert_equal "/pure-awesomeness/blog/posts/1", response.body end - test "[ENGINE] generating application's url never uses SCRIPT_NAME from request" do + test "[ENGINE] generating application's URL never uses SCRIPT_NAME from request" do get "/pure-awesomeness/blog/url_to_application" assert_equal "/generate", response.body end - test "[ENGINE] generating engine's url with polymorphic path" do + test "[ENGINE] generating engine's URL with polymorphic path" do get "/pure-awesomeness/blog/polymorphic_path_for_engine" assert_equal "/pure-awesomeness/blog/posts/1", response.body end @@ -243,7 +243,7 @@ module TestGenerationPrefix assert_equal "/something/awesome/blog/posts/1", response.body end - test "[APP] generating engine's url with polymorphic path" do + test "[APP] generating engine's URL with polymorphic path" do get "/polymorphic_path_for_engine" assert_equal "/awesome/blog/posts/1", response.body end @@ -253,7 +253,7 @@ module TestGenerationPrefix assert_equal "/posts/1", response.body end - test "[APP] generating engine's url with url_for(@post)" do + test "[APP] generating engine's URL with url_for(@post)" do get "/polymorphic_with_url_for" assert_equal "http://www.example.com/awesome/blog/posts/1", response.body end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 2a4d59affe..eb49396145 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -411,7 +411,7 @@ class RequestPath < BaseRequestTest assert_equal "/foo?bar", path end - test "original_url returns url built using ORIGINAL_FULLPATH" do + test "original_url returns URL built using ORIGINAL_FULLPATH" do request = stub_request("ORIGINAL_FULLPATH" => "/foo?bar", "HTTP_HOST" => "example.org", "rack.url_scheme" => "http") diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 60817c1c4d..7758b0406a 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -539,4 +539,38 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers["ETag"]) assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag) end + + test "response Content-Type with optional parameters" do + @app = lambda { |env| + [ + 200, + { "Content-Type" => "text/csv; charset=utf-16; header=present" }, + ["Hello"] + ] + } + + get "/" + assert_response :success + + assert_equal("text/csv; charset=utf-16; header=present", @response.headers["Content-Type"]) + assert_equal("text/csv", @response.content_type) + assert_equal("utf-16", @response.charset) + end + + test "response Content-Type with quoted-string" do + @app = lambda { |env| + [ + 200, + { "Content-Type" => 'text/csv; header=present; charset="utf-16"' }, + ["Hello"] + ] + } + + get "/" + assert_response :success + + assert_equal('text/csv; header=present; charset="utf-16"', @response.headers["Content-Type"]) + assert_equal("text/csv", @response.content_type) + assert_equal("utf-16", @response.charset) + end end diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb index e61d47b160..e6a2c35798 100644 --- a/actionpack/test/dispatch/routing/route_set_test.rb +++ b/actionpack/test/dispatch/routing/route_set_test.rb @@ -29,7 +29,7 @@ module ActionDispatch assert_not empty? end - test "url helpers are added when route is added" do + test "URL helpers are added when route is added" do draw do get "foo", to: SimpleApp.new("foo#index") end @@ -48,7 +48,7 @@ module ActionDispatch assert_equal "/bar", url_helpers.bar_path end - test "url helpers are updated when route is updated" do + test "URL helpers are updated when route is updated" do draw do get "bar", to: SimpleApp.new("bar#index"), as: :bar end @@ -62,7 +62,7 @@ module ActionDispatch assert_equal "/baz", url_helpers.bar_path end - test "url helpers are removed when route is removed" do + test "URL helpers are removed when route is removed" do draw do get "foo", to: SimpleApp.new("foo#index") get "bar", to: SimpleApp.new("bar#index") diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 897d17885e..362488d585 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -2200,6 +2200,37 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal "cards#destroy", @response.body end + def test_shallow_false_inside_nested_shallow_resource + draw do + resources :blogs, shallow: true do + resources :posts do + resources :comments, shallow: false + resources :tags + end + end + end + + get "/posts/1/comments" + assert_equal "comments#index", @response.body + assert_equal "/posts/1/comments", post_comments_path("1") + + get "/posts/1/comments/new" + assert_equal "comments#new", @response.body + assert_equal "/posts/1/comments/new", new_post_comment_path("1") + + get "/posts/1/comments/2" + assert_equal "comments#show", @response.body + assert_equal "/posts/1/comments/2", post_comment_path("1", "2") + + get "/posts/1/comments/2/edit" + assert_equal "comments#edit", @response.body + assert_equal "/posts/1/comments/2/edit", edit_post_comment_path("1", "2") + + get "/tags/3" + assert_equal "tags#show", @response.body + assert_equal "/tags/3", tag_path("3") + end + def test_shallow_deeply_nested_resources draw do resources :blogs do @@ -3338,13 +3369,23 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal "0c0c0b68-d24b-11e1-a861-001ff3fffe6f", @request.params[:download] end - def test_action_from_path_is_not_frozen + def test_colon_containing_custom_param + ex = assert_raises(ArgumentError) { + draw do + resources :profiles, param: "username/:is_admin" + end + } + + assert_match(/:param option can't contain colon/, ex.message) + end + + def test_action_from_path_is_frozen draw do get "search" => "search" end get "/search" - assert_not_predicate @request.params[:action], :frozen? + assert_predicate @request.params[:action], :frozen? end def test_multiple_positional_args_with_the_same_name @@ -4382,7 +4423,7 @@ class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest include Routes.url_helpers - test "url helpers do not ignore nil parameters when using non-optimized routes" do + test "URL helpers do not ignore nil parameters when using non-optimized routes" do Routes.stub :optimize_routes_generation?, false do get "/categories/1" assert_response :success @@ -4754,7 +4795,7 @@ class TestUrlGenerationErrors < ActionDispatch::IntegrationTest include Routes.url_helpers - test "url helpers raise a 'missing keys' error for a nil param with optimized helpers" do + test "URL helpers raise a 'missing keys' error for a nil param with optimized helpers" do url, missing = { action: "show", controller: "products", id: nil }, [:id] message = "No route matches #{url.inspect}, missing required keys: #{missing.inspect}" @@ -4762,7 +4803,7 @@ class TestUrlGenerationErrors < ActionDispatch::IntegrationTest assert_equal message, error.message end - test "url helpers raise a 'constraint failure' error for a nil param with non-optimized helpers" do + test "URL helpers raise a 'constraint failure' error for a nil param with non-optimized helpers" do url, missing = { action: "show", controller: "products", id: nil }, [:id] message = "No route matches #{url.inspect}, possible unmatched constraints: #{missing.inspect}" @@ -4770,15 +4811,15 @@ class TestUrlGenerationErrors < ActionDispatch::IntegrationTest assert_equal message, error.message end - test "url helpers raise message with mixed parameters when generation fails" do + test "URL helpers raise message with mixed parameters when generation fails" do url, missing = { action: "show", controller: "products", id: nil, "id" => "url-tested" }, [:id] message = "No route matches #{url.inspect}, possible unmatched constraints: #{missing.inspect}" - # Optimized url helper + # Optimized URL helper error = assert_raises(ActionController::UrlGenerationError) { product_path(nil, "id" => "url-tested") } assert_equal message, error.message - # Non-optimized url helper + # Non-optimized URL helper error = assert_raises(ActionController::UrlGenerationError, message) { product_path(id: nil, "id" => "url-tested") } assert_equal message, error.message end @@ -4996,7 +5037,7 @@ class FlashRedirectTest < ActionDispatch::IntegrationTest ) Rotations = ActiveSupport::Messages::RotationConfiguration.new SIGNED_COOKIE_SALT = "signed cookie" - ENCRYPTED_SIGNED_COOKIE_SALT = "sigend encrypted cookie" + ENCRYPTED_SIGNED_COOKIE_SALT = "signed encrypted cookie" class KeyGeneratorMiddleware def initialize(app) diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index f802abc653..6fafa4e426 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -9,6 +9,8 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest case req.path when "/not_found" raise AbstractController::ActionNotFound + when "/invalid_mimetype" + raise Mime::Type::InvalidMimeType when "/bad_params", "/bad_params.json" begin raise StandardError.new @@ -62,6 +64,10 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest get "/unknown_http_method", env: { "action_dispatch.show_exceptions" => true } assert_response 405 assert_equal "", body + + get "/invalid_mimetype", headers: { "Accept" => "text/html,*", "action_dispatch.show_exceptions" => true } + assert_response 406 + assert_equal "", body end test "localize rescue error page" do diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb index b756b91379..b0b36f9d74 100644 --- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb +++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb @@ -36,6 +36,14 @@ class ScreenshotHelperTest < ActiveSupport::TestCase end end + test "image name truncates names over 225 characters" do + new_test = DrivenBySeleniumWithChrome.new("x" * 400) + + Rails.stub :root, Pathname.getwd do + assert_equal Rails.root.join("tmp/screenshots/#{"x" * 225}.png").to_s, new_test.send(:image_path) + end + end + test "defaults to simple output for the screenshot" do new_test = DrivenBySeleniumWithChrome.new("x") assert_equal "simple", new_test.send(:output_type) diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb index fcfaba96b0..77c19369b0 100644 --- a/actionpack/test/journey/path/pattern_test.rb +++ b/actionpack/test/journey/path/pattern_test.rb @@ -34,17 +34,17 @@ module ActionDispatch end { - "/:controller(/:action)" => %r{\A/(#{x})(?:/([^/.?]+))?(?:\b|\Z)}, - "/:controller/foo" => %r{\A/(#{x})/foo(?:\b|\Z)}, - "/:controller/:action" => %r{\A/(#{x})/([^/.?]+)(?:\b|\Z)}, - "/:controller" => %r{\A/(#{x})(?:\b|\Z)}, - "/:controller(/:action(/:id))" => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?(?:\b|\Z)}, - "/:controller/:action.xml" => %r{\A/(#{x})/([^/.?]+)\.xml(?:\b|\Z)}, - "/:controller.:format" => %r{\A/(#{x})\.([^/.?]+)(?:\b|\Z)}, - "/:controller(.:format)" => %r{\A/(#{x})(?:\.([^/.?]+))?(?:\b|\Z)}, - "/:controller/*foo" => %r{\A/(#{x})/(.+)(?:\b|\Z)}, - "/:controller/*foo/bar" => %r{\A/(#{x})/(.+)/bar(?:\b|\Z)}, - "/:foo|*bar" => %r{\A/(?:([^/.?]+)|(.+))(?:\b|\Z)}, + "/:controller(/:action)" => %r{\A/(#{x})(?:/([^/.?]+))?(?:\b|\Z|/)}, + "/:controller/foo" => %r{\A/(#{x})/foo(?:\b|\Z|/)}, + "/:controller/:action" => %r{\A/(#{x})/([^/.?]+)(?:\b|\Z|/)}, + "/:controller" => %r{\A/(#{x})(?:\b|\Z|/)}, + "/:controller(/:action(/:id))" => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?(?:\b|\Z|/)}, + "/:controller/:action.xml" => %r{\A/(#{x})/([^/.?]+)\.xml(?:\b|\Z|/)}, + "/:controller.:format" => %r{\A/(#{x})\.([^/.?]+)(?:\b|\Z|/)}, + "/:controller(.:format)" => %r{\A/(#{x})(?:\.([^/.?]+))?(?:\b|\Z|/)}, + "/:controller/*foo" => %r{\A/(#{x})/(.+)(?:\b|\Z|/)}, + "/:controller/*foo/bar" => %r{\A/(#{x})/(.+)/bar(?:\b|\Z|/)}, + "/:foo|*bar" => %r{\A/(?:([^/.?]+)|(.+))(?:\b|\Z|/)}, }.each do |path, expected| define_method(:"test_to_non_anchored_regexp_#{Regexp.escape(path)}") do path = Pattern.build( @@ -280,6 +280,15 @@ module ActionDispatch assert_equal "list", match[1] assert_equal "rss", match[2] end + + def test_named_captures + path = Path::Pattern.from_string "/books(/:action(.:format))" + + uri = "/books/list.rss" + match = path =~ uri + named_captures = { "action" => "list", "format" => "rss" } + assert_equal named_captures, match.named_captures + end end end end |