diff options
Diffstat (limited to 'actionpack')
39 files changed, 202 insertions, 109 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 8bb2c73e52..90217755bc 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,12 @@ +* Allow system test screen shots to be taken more than once in + a test by prefixing the file name with an incrementing counter. + + Add an environment variable `RAILS_SYSTEM_TESTING_SCREENSHOT_HTML` to + enable saving of HTML during a screenshot in addition to the image. + This uses the same image name, with the extension replaced with `.html` + + *Tom Fakes* + * Add `Vary: Accept` header when using `Accept` header for response For some requests like `/users/1`, Rails uses requests' `Accept` @@ -80,7 +89,7 @@ *Gustavo Gutierrez* -* Calling `ActionController::Parameters#transform_keys/!` without a block now returns +* Calling `ActionController::Parameters#transform_keys`/`!` without a block now returns an enumerator for the parameters instead of the underlying hash. *Eugene Kenny* diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index 42bab411d2..983d65f944 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -37,7 +37,7 @@ module AbstractController # Override <tt>AbstractController::Base#process_action</tt> to run the # <tt>process_action</tt> callbacks around the normal behavior. - def process_action(*args) + def process_action(*) run_callbacks(:process_action) do super end diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index d8b04d8ddb..033ad10d4c 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -22,8 +22,7 @@ module ActionController additions = ActionController::Base.log_process_action(payload) status = payload[:status] - if status.nil? && payload[:exception].present? - exception_class_name = payload[:exception].first + if status.nil? && (exception_class_name = payload[:exception].first) status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name) end diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 3c84bebb85..f290272055 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -29,7 +29,7 @@ module ActionController content_type = options.delete(:content_type) options.each do |key, value| - headers[key.to_s.dasherize.split("-").each { |v| v[0] = v[0].chr.upcase }.join("-")] = value.to_s + headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s end self.status = status diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 6a274d35cb..ec0c9ecc67 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -482,7 +482,7 @@ module ActionController def raw_params(auth) _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/) - if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}}) + if !(%r{\A#{TOKEN_KEY}}.match?(_raw_params.first)) _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}" end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index 6f7fc0d624..594c4c1dc8 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -16,7 +16,7 @@ module ActionController attr_internal :view_runtime - def process_action(*args) + def process_action(*) raw_payload = { controller: self.class.name, action: action_name, @@ -27,18 +27,18 @@ module ActionController path: request.fullpath } - ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup) + ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload) ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload| - super.tap do - payload[:status] = response.status - end + result = super + payload[:status] = response.status + result ensure append_info_to_payload(payload) end end - def render(*args) + def render(*) render_output = nil self.view_runtime = cleanup_view_runtime do Benchmark.ms { render_output = super } @@ -59,7 +59,7 @@ module ActionController end end - def redirect_to(*args) + def redirect_to(*) ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload| result = super payload[:status] = response.status diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 5c6f7fe396..a993c76af9 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -142,7 +142,7 @@ module ActionController #:nodoc: # # You can set the variant in a +before_action+: # - # request.variant = :tablet if request.user_agent =~ /iPad/ + # request.variant = :tablet if /iPad/.match?(request.user_agent) # # Respond to variants in the action just like you respond to formats: # diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 150ae2666c..15c9937405 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -240,7 +240,7 @@ module ActionController # Performs parameters wrapping upon the request. Called automatically # by the metal call stack. - def process_action(*args) + def process_action(*) _perform_parameter_wrapping if _wrapper_enabled? super end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 5a5c04234b..e923afb17c 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -280,7 +280,7 @@ module ActionController #:nodoc: # Check for cross-origin JavaScript responses. def non_xhr_javascript_response? # :doc: - content_type =~ %r(\A(?:text|application)/javascript) && !request.xhr? + %r(\A(?:text|application)/javascript).match?(content_type) && !request.xhr? end AUTHENTICITY_TOKEN_LENGTH = 32 diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index 44f7fb7a07..59704f2797 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -18,7 +18,7 @@ module ActionController #:nodoc: end private - def process_action(*args) + def process_action(*) super rescue Exception => exception request.env["action_dispatch.show_detailed_exceptions"] ||= show_detailed_exceptions? diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 920ae52f2b..4c9eb20c65 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -225,7 +225,7 @@ module ActionController class << self def nested_attribute?(key, value) # :nodoc: - key =~ /\A-?\d+\z/ && (value.is_a?(Hash) || value.is_a?(Parameters)) + /\A-?\d+\z/.match?(key) && (value.is_a?(Hash) || value.is_a?(Parameters)) end end diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb index dadf6d3445..b48c7a1afa 100644 --- a/actionpack/lib/action_controller/renderer.rb +++ b/actionpack/lib/action_controller/renderer.rb @@ -65,7 +65,7 @@ module ActionController def initialize(controller, env, defaults) @controller = controller @defaults = defaults - @env = normalize_keys defaults.merge(env) + @env = normalize_keys defaults, env end # Render templates with any options from ActionController::Base#render_to_string. @@ -97,8 +97,9 @@ module ActionController end private - def normalize_keys(env) + def normalize_keys(defaults, env) new_env = {} + defaults.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) } env.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) } new_env["rack.url_scheme"] = new_env["HTTPS"] == "on" ? "https" : "http" new_env @@ -120,7 +121,7 @@ module ActionController } def rack_key_for(key) - RACK_KEY_TRANSLATION.fetch(key, key.to_s) + RACK_KEY_TRANSLATION[key] || key.to_s end def rack_value_for(key, value) diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 47e0099f20..1632ac3ae8 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -593,8 +593,8 @@ module ActionController private def scrub_env!(env) - env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } - env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } + env.delete_if { |k, v| k.match?(/^(action_dispatch|rack)\.request/) } + env.delete_if { |k, v| k.match?(/^action_dispatch\.rescue/) } env.delete "action_dispatch.request.query_parameters" env.delete "action_dispatch.request.request_parameters" env["rack.input"] = StringIO.new diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb index 7be30be77a..0258d85564 100644 --- a/actionpack/lib/action_dispatch/http/cache.rb +++ b/actionpack/lib/action_dispatch/http/cache.rb @@ -150,8 +150,8 @@ module ActionDispatch directive, argument = segment.split("=", 2) if SPECIAL_KEYS.include? directive - key = directive.tr("-", "_") - cache_control[key.to_sym] = argument || true + directive.tr!("-", "_") + cache_control[directive.to_sym] = argument || true else cache_control[:extras] ||= [] cache_control[:extras] << segment diff --git a/actionpack/lib/action_dispatch/http/content_security_policy.rb b/actionpack/lib/action_dispatch/http/content_security_policy.rb index 9c430b57e3..e8cf1b95a5 100644 --- a/actionpack/lib/action_dispatch/http/content_security_policy.rb +++ b/actionpack/lib/action_dispatch/http/content_security_policy.rb @@ -33,7 +33,7 @@ module ActionDispatch #:nodoc: private def html_response?(headers) if content_type = headers[CONTENT_TYPE] - content_type =~ /html/ + /html/.match?(content_type) end end diff --git a/actionpack/lib/action_dispatch/http/feature_policy.rb b/actionpack/lib/action_dispatch/http/feature_policy.rb index 592b6e4393..78e982918d 100644 --- a/actionpack/lib/action_dispatch/http/feature_policy.rb +++ b/actionpack/lib/action_dispatch/http/feature_policy.rb @@ -33,7 +33,7 @@ module ActionDispatch #:nodoc: private def html_response?(headers) if content_type = headers[CONTENT_TYPE] - content_type =~ /html/ + /html/.match?(content_type) end end diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 7a7a493f64..7ad1ba3e0e 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -23,7 +23,7 @@ module ActionDispatch # change { file: { code: "xxxx"} } # # env["action_dispatch.parameter_filter"] = -> (k, v) do - # v.reverse! if k =~ /secret/i + # v.reverse! if k.match?(/secret/i) # end # => reverses the value to all keys matching /secret/i module FilterParameters diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb index d780d5f793..3bd1f5109d 100644 --- a/actionpack/lib/action_dispatch/http/filter_redirect.rb +++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb @@ -27,7 +27,7 @@ module ActionDispatch if String === filter location.include?(filter) elsif Regexp === filter - location =~ filter + location.match?(filter) end end end diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index ac0ff133eb..6bf4e652d3 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -162,7 +162,7 @@ module ActionDispatch def valid_accept_header # :doc: (xhr? && (accept.present? || content_mime_type)) || - (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS) + (accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS)) end def use_accept_header # :doc: diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index ed1d50f3b9..60b78c0582 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -202,7 +202,7 @@ module Mime # For an input of <tt>'application'</tt>, returns <tt>[Mime[:html], Mime[:js], # Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]</tt>. def parse_data_with_trailing_star(type) - Mime::SET.select { |m| m =~ type } + Mime::SET.select { |m| m.match?(type) } end # This method is opposite of register method. @@ -283,8 +283,14 @@ module Mime @synonyms.any? { |synonym| synonym.to_s =~ regexp } || @string =~ regexp end + def match?(mime_type) + return false unless mime_type + regexp = Regexp.new(Regexp.quote(mime_type.to_s)) + @synonyms.any? { |synonym| synonym.to_s.match?(regexp) } || @string.match?(regexp) + end + def html? - symbol == :html || @string =~ /html/ + (symbol == :html) || /html/.match?(@string) end def all?; false; end diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 4ac7c5c2bd..54dbb536c1 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -85,7 +85,7 @@ module ActionDispatch def controller_class_for(name) if name controller_param = name.underscore - const_name = "#{controller_param.camelize}Controller" + const_name = controller_param.camelize << "Controller" ActiveSupport::Dependencies.constantize(const_name) else PASS_NOT_FOUND @@ -265,7 +265,7 @@ module ActionDispatch # (case-insensitive), which may need to be manually added depending on the # choice of JavaScript libraries and frameworks. def xml_http_request? - get_header("HTTP_X_REQUESTED_WITH") =~ /XMLHttpRequest/i + /XMLHttpRequest/i.match?(get_header("HTTP_X_REQUESTED_WITH")) end alias :xhr? :xml_http_request? @@ -400,7 +400,7 @@ module ActionDispatch # True if the request came from localhost, 127.0.0.1, or ::1. def local? - LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip + LOCALHOST.match?(remote_addr) && LOCALHOST.match?(remote_ip) end def request_parameters=(params) diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 3b0f6378ea..225ae0a497 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -133,7 +133,7 @@ module ActionDispatch end def named_host?(host) - IP_HOST_REGEXP !~ host + !IP_HOST_REGEXP.match?(host) end def normalize_protocol(protocol) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 9b5a5cf2b0..96bdf570af 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -439,7 +439,7 @@ module ActionDispatch # If host is not ip and matches domain regexp. # (ip confirms to domain regexp so we explicitly check for ip) - options[:domain] = if (request.host !~ /^[\d.]+$/) && (request.host =~ domain_regexp) + options[:domain] = if !request.host.match?(/^[\d.]+$/) && (request.host =~ domain_regexp) ".#{$&}" end elsif options[:domain].is_a? Array diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb index 00902ede21..237eccf45f 100644 --- a/actionpack/lib/action_dispatch/middleware/ssl.rb +++ b/actionpack/lib/action_dispatch/middleware/ssl.rb @@ -13,7 +13,7 @@ module ActionDispatch # # Requests can opt-out of redirection with +exclude+: # - # config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } } + # config.ssl_options = { redirect: { exclude: -> request { /healthcheck/.match?(request.path) } } } # # Cookies will not be flagged as secure for excluded requests. # @@ -126,7 +126,7 @@ module ActionDispatch [ @redirect.fetch(:status, redirection_status(request)), { "Content-Type" => "text/html", "Location" => https_location_for(request) }, - @redirect.fetch(:body, []) ] + (@redirect[:body] || []) ] end def redirection_status(request) diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 1f2f7757a3..eddcdbaeac 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -32,18 +32,13 @@ module ActionDispatch return false unless ::Rack::Utils.valid_path? path path = ::Rack::Utils.clean_path_info path - paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"] + return ::Rack::Utils.escape_path(path).b if file_readable?(path) - if match = paths.detect { |p| - path = File.join(@root, p.b) - begin - File.file?(path) && File.readable?(path) - rescue SystemCallError - false - end - } - return ::Rack::Utils.escape_path(match).b - end + path_with_ext = path + ext + return ::Rack::Utils.escape_path(path_with_ext).b if file_readable?(path_with_ext) + + path << "/" << @index << ext + return ::Rack::Utils.escape_path(path).b if file_readable?(path) end def call(env) @@ -83,11 +78,11 @@ module ActionDispatch end def gzip_encoding_accepted?(request) - request.accept_encoding.any? { |enc, quality| enc =~ /\bgzip\b/i } + request.accept_encoding.any? { |enc, quality| /\bgzip\b/i.match?(enc) } end def gzip_file_path(path) - can_gzip_mime = content_type(path) =~ /\A(?:text\/|application\/javascript)/ + can_gzip_mime = /\A(?:text\/|application\/javascript)/.match?(content_type(path)) gzip_path = "#{path}.gz" if can_gzip_mime && File.exist?(File.join(@root, ::Rack::Utils.unescape_path(gzip_path))) gzip_path @@ -95,6 +90,12 @@ module ActionDispatch false end end + + def file_readable?(path) + file_path = File.join(@root, path.b) + File.file?(file_path) && File.readable?(file_path) + rescue SystemCallError + end end # This middleware will attempt to return the contents of a file's body from diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index 6e40a18009..bf286c299d 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -94,7 +94,7 @@ module ActionDispatch if filter @routes.select do |route| route_wrapper = RouteWrapper.new(route) - filter.any? { |default, value| route_wrapper.send(default) =~ value } + filter.any? { |default, value| value.match?(route_wrapper.send(default)) } end else @routes diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index d1100089b1..da95e14cbb 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -112,7 +112,7 @@ module ActionDispatch end def self.optional_format?(path, format) - format != false && path !~ OPTIONAL_FORMAT_REGEX + format != false && !path.match?(OPTIONAL_FORMAT_REGEX) end def initialize(set:, ast:, controller:, default_action:, to:, formatted:, via:, options_constraints:, anchor:, scope_params:, options:) @@ -367,7 +367,7 @@ module ActionDispatch def translate_controller(controller) return controller if Regexp === controller - return controller.to_s if controller =~ /\A[a-z_0-9][a-z_0-9\/]*\z/ + return controller.to_s if /\A[a-z_0-9][a-z_0-9\/]*\z/.match?(controller) yield end @@ -403,7 +403,7 @@ module ActionDispatch # for root cases, where the latter is the correct one. def self.normalize_path(path) path = Journey::Router::Utils.normalize_path(path) - path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/(\(+[^)]+\)){1,}$} + path.gsub!(%r{/(\(+)/?}, '\1/') unless %r{^/(\(+[^)]+\)){1,}$}.match?(path) path end @@ -996,7 +996,7 @@ module ActionDispatch # # Requests to routes can be constrained based on specific criteria: # - # constraints(-> (req) { req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do + # constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do # resources :iphones # end # @@ -1006,7 +1006,7 @@ module ActionDispatch # # class Iphone # def self.matches?(request) - # request.env["HTTP_USER_AGENT"] =~ /iPhone/ + # /iPhone/.match?(request.env["HTTP_USER_AGENT"]) # end # end # @@ -1833,7 +1833,7 @@ module ActionDispatch # and return nil in case it isn't. Otherwise, we pass the invalid name # forward so the underlying router engine treats it and raises an exception. if as.nil? - candidate unless candidate !~ /\A[_a-z]/i || has_named_route?(candidate) + candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate) else candidate end @@ -1887,7 +1887,7 @@ module ActionDispatch options_constraints = options.delete(:constraints) || {} path_types = paths.group_by(&:class) - path_types.fetch(String, []).each do |_path| + (path_types[String] || []).each do |_path| route_options = options.dup if _path && option_path raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings." @@ -1896,7 +1896,7 @@ module ActionDispatch decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints) end - path_types.fetch(Symbol, []).each do |action| + (path_types[Symbol] || []).each do |action| route_options = options.dup decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints) end @@ -1916,7 +1916,7 @@ module ActionDispatch end def using_match_shorthand?(path) - path =~ %r{^/?[-\w]+/[-\w/]+$} + %r{^/?[-\w]+/[-\w/]+$}.match?(path) end def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 5b35b68c44..db8c54ba84 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -836,7 +836,7 @@ module ActionDispatch def recognize_path(path, environment = {}) method = (environment[:method] || "GET").to_s.upcase - path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://} + path = Journey::Router::Utils.normalize_path(path) unless %r{://}.match?(path) extras = environment[:extras] || {} begin 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 056ce51a61..cba053ee4c 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 @@ -9,10 +9,16 @@ module ActionDispatch # # +take_screenshot+ can be used at any point in your system tests to take # a screenshot of the current state. This can be useful for debugging or - # automating visual testing. + # automating visual testing. You can take multiple screenshots per test + # to investigate changes at different points during your test. These will be + # named with a sequential prefix (or 'failed' for failing tests) # # The screenshot will be displayed in your console, if supported. # + # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT_HTML+ environment variable to + # save the HTML from the page that is being screenhoted so you can investigate the + # elements on the page at the time of the screenshot + # # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to # control the output. Possible values are: # * [+simple+ (default)] Only displays the screenshot path. @@ -22,6 +28,8 @@ module ActionDispatch # * [+artifact+] Display the screenshot in the terminal, using the terminal # artifact format (https://buildkite.github.io/terminal-to-html/inline-images/). def take_screenshot + increment_unique + save_html if save_html? save_image puts display_image end @@ -38,17 +46,48 @@ module ActionDispatch end private + attr_accessor :_screenshot_counter + + def save_html? + ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] == "1" + end + + def increment_unique + @_screenshot_counter ||= 0 + @_screenshot_counter += 1 + end + + def unique + failed? ? "failures" : (_screenshot_counter || 0).to_s + end + def image_name - name = method_name[0...225] - failed? ? "failures_#{name}" : name + name = "#{unique}_#{method_name}" + name[0...225] end def image_path - @image_path ||= absolute_image_path.to_s + absolute_image_path.to_s + end + + def html_path + absolute_html_path.to_s + end + + def absolute_path + Rails.root.join("tmp/screenshots/#{image_name}") end def absolute_image_path - Rails.root.join("tmp/screenshots/#{image_name}.png") + "#{absolute_path}.png" + end + + def absolute_html_path + "#{absolute_path}.html" + end + + def save_html + page.save_page(absolute_html_path) end def save_image @@ -66,7 +105,8 @@ module ActionDispatch end def display_image - message = +"[Screenshot]: #{image_path}\n" + message = +"[Screenshot Image]: #{image_path}\n" + message << +"[Screenshot HTML]: #{html_path}\n" if save_html? case output_type when "artifact" diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index cb7c2467ac..4f5f5b71ae 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -606,7 +606,7 @@ class MetalIntegrationTest < ActionDispatch::IntegrationTest class Poller def self.call(env) - if env["PATH_INFO"] =~ /^\/success/ + if /^\/success/.match?(env["PATH_INFO"]) [200, { "Content-Type" => "text/plain", "Content-Length" => "12" }, ["Hello World!"]] else [404, { "Content-Type" => "text/plain", "Content-Length" => "0" }, []] diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 1a7e7f6cbb..46ab7b4f74 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -139,7 +139,7 @@ class ACLogSubscriberTest < ActionController::TestCase def test_process_action_without_parameters get :show wait - assert_nil logs.detect { |l| l =~ /Parameters/ } + assert_nil logs.detect { |l| /Parameters/.match?(l) } end def test_process_action_with_parameters diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index fa629bc761..5d12b62fd4 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -544,8 +544,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest @app = DevelopmentApp Rails.stub :root, Pathname.new(".") do cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| - bc.add_silencer { |line| line =~ /method_that_raises/ } - bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } + bc.add_silencer { |line| line.match?(/method_that_raises/) } + bc.add_silencer { |line| !line.match?(%r{test/dispatch/debug_exceptions_test.rb}) } end get "/framework_raises", headers: { "action_dispatch.backtrace_cleaner" => cleaner } @@ -596,7 +596,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest @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} } + bc.add_silencer { |line| !line.match?(%r{test/dispatch/debug_exceptions_test.rb}) } end get "/nested_exceptions", headers: { "action_dispatch.backtrace_cleaner" => cleaner } @@ -631,7 +631,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest @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} } + bc.add_silencer { |line| !line.match?(%r{test/dispatch/debug_exceptions_test.rb}) } end get "/actionable_error", headers: { "action_dispatch.backtrace_cleaner" => cleaner } diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb index 668469a01d..3c395ca5ee 100644 --- a/actionpack/test/dispatch/exception_wrapper_test.rb +++ b/actionpack/test/dispatch/exception_wrapper_test.rb @@ -21,7 +21,7 @@ module ActionDispatch setup do @cleaner = ActiveSupport::BacktraceCleaner.new @cleaner.remove_filters! - @cleaner.add_silencer { |line| line !~ /^lib/ } + @cleaner.add_silencer { |line| !line.match?(/^lib/) } end test "#source_extracts fetches source fragments for every backtrace entry" do diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 50f6c06fee..b0b2aa0cc1 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -175,6 +175,12 @@ class MimeTypeTest < ActiveSupport::TestCase assert Mime[:html] =~ "application/xhtml+xml" end + test "match?" do + assert Mime[:js].match?("text/javascript") + assert Mime[:js].match?("application/javascript") + assert_not Mime[:js].match?("text/html") + end + test "can be initialized with wildcards" do assert_equal "*/*", Mime::Type.new("*/*").to_s assert_equal "text/*", Mime::Type.new("text/*").to_s diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index 2a48a12497..bbf98912f3 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -68,7 +68,7 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest post "/parse", params: json, headers: { "CONTENT_TYPE" => "application/json", "action_dispatch.show_exceptions" => true, "action_dispatch.logger" => ActiveSupport::Logger.new(output) } assert_response :bad_request output.rewind && err = output.read - assert err =~ /Error occurred while parsing request parameters/ + assert err.match?(/Error occurred while parsing request parameters/) end end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index c4cb27429d..806b7d0559 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -884,7 +884,7 @@ class RequestFormat < BaseRequestTest assert request.format.html? output.rewind && (err = output.read) - assert_match /Error occurred while parsing request parameters/, err + assert_match(/Error occurred while parsing request parameters/, err) end test "formats with xhr request" do diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index b67b1dd347..d4a667a13a 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -12,7 +12,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest class IpRestrictor def self.matches?(request) - request.ip =~ /192\.168\.1\.1\d\d/ + /192\.168\.1\.1\d\d/.match?(request.ip) end end @@ -3823,7 +3823,7 @@ private end def method_missing(method, *args, &block) - if method.to_s =~ /_(path|url)$/ + if method.to_s.match?(/_(path|url)$/) @app.routes.url_helpers.send(method, *args, &block) else super @@ -5137,7 +5137,7 @@ class TestRecognizePath < ActionDispatch::IntegrationTest end def matches?(request) - request.path_parameters[key] =~ pattern + pattern.match?(request.path_parameters[key]) end end @@ -5147,8 +5147,8 @@ class TestRecognizePath < ActionDispatch::IntegrationTest get "/hash/:foo", to: "pages#show", constraints: { foo: /foo/ } get "/hash/:bar", to: "pages#show", constraints: { bar: /bar/ } - get "/proc/:foo", to: "pages#show", constraints: proc { |r| r.path_parameters[:foo] =~ /foo/ } - get "/proc/:bar", to: "pages#show", constraints: proc { |r| r.path_parameters[:bar] =~ /bar/ } + get "/proc/:foo", to: "pages#show", constraints: proc { |r| /foo/.match?(r.path_parameters[:foo]) } + get "/proc/:bar", to: "pages#show", constraints: proc { |r| /bar/.match?(r.path_parameters[:bar]) } get "/class/:foo", to: "pages#show", constraints: PageConstraint.new(:foo, /foo/) get "/class/:bar", to: "pages#show", constraints: PageConstraint.new(:bar, /bar/) diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb index baf46e7c7e..44fe433c5f 100644 --- a/actionpack/test/dispatch/ssl_test.rb +++ b/actionpack/test/dispatch/ssl_test.rb @@ -42,7 +42,7 @@ class RedirectSSLTest < SSLTest end test "exclude can avoid redirect" do - excluding = { exclude: -> request { request.path =~ /healthcheck/ } } + excluding = { exclude: -> request { request.path.match?(/healthcheck/) } } assert_not_redirected "http://example.org/healthcheck", redirect: excluding assert_redirected from: "http://example.org/", redirect: excluding @@ -209,7 +209,7 @@ class SecureCookiesTest < SSLTest end def test_cookies_as_not_secure_with_exclude - excluding = { exclude: -> request { request.domain =~ /example/ } } + excluding = { exclude: -> request { /example/.match?(request.domain) } } get headers: { "Set-Cookie" => DEFAULT }, ssl_options: { redirect: excluding } assert_cookies(*DEFAULT.split("\n")) diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb index b0b36f9d74..5d69a887ef 100644 --- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb +++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb @@ -6,60 +6,93 @@ require "capybara/dsl" require "selenium/webdriver" class ScreenshotHelperTest < ActiveSupport::TestCase + def setup + @new_test = DrivenBySeleniumWithChrome.new("x") + @new_test.send("_screenshot_counter=", nil) + end + test "image path is saved in tmp directory" do - new_test = DrivenBySeleniumWithChrome.new("x") + Rails.stub :root, Pathname.getwd do + assert_equal Rails.root.join("tmp/screenshots/0_x.png").to_s, @new_test.send(:image_path) + end + end + + test "image path unique counter is changed when incremented" do + @new_test.send(:increment_unique) Rails.stub :root, Pathname.getwd do - assert_equal Rails.root.join("tmp/screenshots/x.png").to_s, new_test.send(:image_path) + assert_equal Rails.root.join("tmp/screenshots/1_x.png").to_s, @new_test.send(:image_path) end end - test "image path includes failures text if test did not pass" do - new_test = DrivenBySeleniumWithChrome.new("x") + # To allow multiple screenshots in same test + test "image path unique counter generates different path in same test" do + Rails.stub :root, Pathname.getwd do + @new_test.send(:increment_unique) + assert_equal Rails.root.join("tmp/screenshots/1_x.png").to_s, @new_test.send(:image_path) + + @new_test.send(:increment_unique) + assert_equal Rails.root.join("tmp/screenshots/2_x.png").to_s, @new_test.send(:image_path) + end + end + test "image path includes failures text if test did not pass" do Rails.stub :root, Pathname.getwd do - new_test.stub :passed?, false do - assert_equal Rails.root.join("tmp/screenshots/failures_x.png").to_s, new_test.send(:image_path) + @new_test.stub :passed?, false do + assert_equal Rails.root.join("tmp/screenshots/failures_x.png").to_s, @new_test.send(:image_path) + assert_equal Rails.root.join("tmp/screenshots/failures_x.html").to_s, @new_test.send(:html_path) end end end test "image path does not include failures text if test skipped" do - new_test = DrivenBySeleniumWithChrome.new("x") - Rails.stub :root, Pathname.getwd do - new_test.stub :passed?, false do - new_test.stub :skipped?, true do - assert_equal Rails.root.join("tmp/screenshots/x.png").to_s, new_test.send(:image_path) + @new_test.stub :passed?, false do + @new_test.stub :skipped?, true do + assert_equal Rails.root.join("tmp/screenshots/0_x.png").to_s, @new_test.send(:image_path) + assert_equal Rails.root.join("tmp/screenshots/0_x.html").to_s, @new_test.send(:html_path) end end end end - test "image name truncates names over 225 characters" do - new_test = DrivenBySeleniumWithChrome.new("x" * 400) + test "image name truncates names over 225 characters including counter" do + long_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) + assert_equal Rails.root.join("tmp/screenshots/0_#{"x" * 223}.png").to_s, long_test.send(:image_path) + assert_equal Rails.root.join("tmp/screenshots/0_#{"x" * 223}.html").to_s, long_test.send(:html_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) + assert_equal "simple", @new_test.send(:output_type) + end + + test "display_image return html path when RAILS_SYSTEM_TESTING_SCREENSHOT_HTML environment" do + original_html_setting = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] + ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] = "1" + + assert @new_test.send(:save_html?) + + Rails.stub :root, Pathname.getwd do + @new_test.stub :passed?, false do + assert_match %r|\[Screenshot HTML\].+?tmp/screenshots/failures_x\.html|, @new_test.send(:display_image) + end + end + ensure + ENV["RAILS_SYSTEM_TESTING_SCREENSHOT_HTML"] = original_html_setting end test "display_image return artifact format when specify RAILS_SYSTEM_TESTING_SCREENSHOT environment" do original_output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] = "artifact" - new_test = DrivenBySeleniumWithChrome.new("x") - - assert_equal "artifact", new_test.send(:output_type) + assert_equal "artifact", @new_test.send(:output_type) Rails.stub :root, Pathname.getwd do - new_test.stub :passed?, false do - assert_match %r|url=artifact://.+?tmp/screenshots/failures_x\.png|, new_test.send(:display_image) + @new_test.stub :passed?, false do + assert_match %r|url=artifact://.+?tmp/screenshots/failures_x\.png|, @new_test.send(:display_image) end end ensure @@ -67,10 +100,8 @@ class ScreenshotHelperTest < ActiveSupport::TestCase end test "image path returns the absolute path from root" do - new_test = DrivenBySeleniumWithChrome.new("x") - Rails.stub :root, Pathname.getwd.join("..") do - assert_equal Rails.root.join("tmp/screenshots/x.png").to_s, new_test.send(:image_path) + assert_equal Rails.root.join("tmp/screenshots/0_x.png").to_s, @new_test.send(:image_path) end end end |