diff options
Diffstat (limited to 'actionpack/test/dispatch')
-rw-r--r-- | actionpack/test/dispatch/content_security_policy_test.rb | 368 | ||||
-rw-r--r-- | actionpack/test/dispatch/cookies_test.rb | 10 | ||||
-rw-r--r-- | actionpack/test/dispatch/live_response_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/mime_type_test.rb | 12 | ||||
-rw-r--r-- | actionpack/test/dispatch/request/session_test.rb | 1 | ||||
-rw-r--r-- | actionpack/test/dispatch/request_id_test.rb | 5 | ||||
-rw-r--r-- | actionpack/test/dispatch/request_test.rb | 240 | ||||
-rw-r--r-- | actionpack/test/dispatch/response_test.rb | 28 | ||||
-rw-r--r-- | actionpack/test/dispatch/routing_assertions_test.rb | 6 | ||||
-rw-r--r-- | actionpack/test/dispatch/routing_test.rb | 43 | ||||
-rw-r--r-- | actionpack/test/dispatch/session/cookie_store_test.rb | 123 | ||||
-rw-r--r-- | actionpack/test/dispatch/system_testing/driver_test.rb | 13 | ||||
-rw-r--r-- | actionpack/test/dispatch/system_testing/screenshot_helper_test.rb | 7 | ||||
-rw-r--r-- | actionpack/test/dispatch/system_testing/server_test.rb | 19 | ||||
-rw-r--r-- | actionpack/test/dispatch/system_testing/system_test_case_test.rb | 6 | ||||
-rw-r--r-- | actionpack/test/dispatch/uploaded_file_test.rb | 8 |
16 files changed, 694 insertions, 197 deletions
diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb new file mode 100644 index 0000000000..7c4a65a633 --- /dev/null +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -0,0 +1,368 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class ContentSecurityPolicyTest < ActiveSupport::TestCase + def setup + @policy = ActionDispatch::ContentSecurityPolicy.new + end + + def test_build + assert_equal ";", @policy.build + + @policy.script_src :self + assert_equal "script-src 'self';", @policy.build + end + + def test_dup + @policy.img_src :self + @policy.block_all_mixed_content + @policy.upgrade_insecure_requests + @policy.sandbox + copied = @policy.dup + assert_equal copied.build, @policy.build + end + + def test_mappings + @policy.script_src :data + assert_equal "script-src data:;", @policy.build + + @policy.script_src :mediastream + assert_equal "script-src mediastream:;", @policy.build + + @policy.script_src :blob + assert_equal "script-src blob:;", @policy.build + + @policy.script_src :filesystem + assert_equal "script-src filesystem:;", @policy.build + + @policy.script_src :self + assert_equal "script-src 'self';", @policy.build + + @policy.script_src :unsafe_inline + assert_equal "script-src 'unsafe-inline';", @policy.build + + @policy.script_src :unsafe_eval + assert_equal "script-src 'unsafe-eval';", @policy.build + + @policy.script_src :none + assert_equal "script-src 'none';", @policy.build + + @policy.script_src :strict_dynamic + assert_equal "script-src 'strict-dynamic';", @policy.build + + @policy.script_src :none, :report_sample + assert_equal "script-src 'none' 'report-sample';", @policy.build + end + + def test_fetch_directives + @policy.child_src :self + assert_match %r{child-src 'self'}, @policy.build + + @policy.child_src false + assert_no_match %r{child-src}, @policy.build + + @policy.connect_src :self + assert_match %r{connect-src 'self'}, @policy.build + + @policy.connect_src false + assert_no_match %r{connect-src}, @policy.build + + @policy.default_src :self + assert_match %r{default-src 'self'}, @policy.build + + @policy.default_src false + assert_no_match %r{default-src}, @policy.build + + @policy.font_src :self + assert_match %r{font-src 'self'}, @policy.build + + @policy.font_src false + assert_no_match %r{font-src}, @policy.build + + @policy.frame_src :self + assert_match %r{frame-src 'self'}, @policy.build + + @policy.frame_src false + assert_no_match %r{frame-src}, @policy.build + + @policy.img_src :self + assert_match %r{img-src 'self'}, @policy.build + + @policy.img_src false + assert_no_match %r{img-src}, @policy.build + + @policy.manifest_src :self + assert_match %r{manifest-src 'self'}, @policy.build + + @policy.manifest_src false + assert_no_match %r{manifest-src}, @policy.build + + @policy.media_src :self + assert_match %r{media-src 'self'}, @policy.build + + @policy.media_src false + assert_no_match %r{media-src}, @policy.build + + @policy.object_src :self + assert_match %r{object-src 'self'}, @policy.build + + @policy.object_src false + assert_no_match %r{object-src}, @policy.build + + @policy.script_src :self + assert_match %r{script-src 'self'}, @policy.build + + @policy.script_src false + assert_no_match %r{script-src}, @policy.build + + @policy.style_src :self + assert_match %r{style-src 'self'}, @policy.build + + @policy.style_src false + assert_no_match %r{style-src}, @policy.build + + @policy.worker_src :self + assert_match %r{worker-src 'self'}, @policy.build + + @policy.worker_src false + assert_no_match %r{worker-src}, @policy.build + end + + def test_document_directives + @policy.base_uri "https://example.com" + assert_match %r{base-uri https://example\.com;}, @policy.build + + @policy.plugin_types "application/x-shockwave-flash" + assert_match %r{plugin-types application/x-shockwave-flash;}, @policy.build + + @policy.sandbox + assert_match %r{sandbox;}, @policy.build + + @policy.sandbox "allow-scripts", "allow-modals" + assert_match %r{sandbox allow-scripts allow-modals;}, @policy.build + + @policy.sandbox false + assert_no_match %r{sandbox}, @policy.build + end + + def test_navigation_directives + @policy.form_action :self + assert_match %r{form-action 'self';}, @policy.build + + @policy.frame_ancestors :self + assert_match %r{frame-ancestors 'self';}, @policy.build + end + + def test_reporting_directives + @policy.report_uri "/violations" + assert_match %r{report-uri /violations;}, @policy.build + end + + def test_other_directives + @policy.block_all_mixed_content + assert_match %r{block-all-mixed-content;}, @policy.build + + @policy.block_all_mixed_content false + assert_no_match %r{block-all-mixed-content}, @policy.build + + @policy.require_sri_for :script, :style + assert_match %r{require-sri-for script style;}, @policy.build + + @policy.require_sri_for "script", "style" + assert_match %r{require-sri-for script style;}, @policy.build + + @policy.require_sri_for + assert_no_match %r{require-sri-for}, @policy.build + + @policy.upgrade_insecure_requests + assert_match %r{upgrade-insecure-requests;}, @policy.build + + @policy.upgrade_insecure_requests false + assert_no_match %r{upgrade-insecure-requests}, @policy.build + end + + def test_multiple_sources + @policy.script_src :self, :https + assert_equal "script-src 'self' https:;", @policy.build + end + + def test_multiple_directives + @policy.script_src :self, :https + @policy.style_src :self, :https + assert_equal "script-src 'self' https:; style-src 'self' https:;", @policy.build + end + + def test_dynamic_directives + request = Struct.new(:host).new("www.example.com") + controller = Struct.new(:request).new(request) + + @policy.script_src -> { request.host } + assert_equal "script-src www.example.com;", @policy.build(controller) + end + + def test_mixed_static_and_dynamic_directives + @policy.script_src :self, -> { "foo.com" }, "bar.com" + assert_equal "script-src 'self' foo.com bar.com;", @policy.build(Object.new) + end + + def test_invalid_directive_source + exception = assert_raises(ArgumentError) do + @policy.script_src [:self] + end + + assert_equal "Invalid content security policy source: [:self]", exception.message + end + + def test_missing_context_for_dynamic_source + @policy.script_src -> { request.host } + + exception = assert_raises(RuntimeError) do + @policy.build + end + + assert_match %r{\AMissing context for the dynamic content security policy source:}, exception.message + end + + def test_raises_runtime_error_when_unexpected_source + @policy.plugin_types [:flash] + + exception = assert_raises(RuntimeError) do + @policy.build + end + + assert_match %r{\AUnexpected content security policy source:}, exception.message + end +end + +class ContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + content_security_policy only: :inline do |p| + p.default_src "https://example.com" + end + + content_security_policy only: :conditional, if: :condition? do |p| + p.default_src "https://true.example.com" + end + + content_security_policy only: :conditional, unless: :condition? do |p| + p.default_src "https://false.example.com" + end + + content_security_policy only: :report_only do |p| + p.report_uri "/violations" + end + + content_security_policy_report_only only: :report_only + + def index + head :ok + end + + def inline + head :ok + end + + def conditional + head :ok + end + + def report_only + head :ok + end + + private + def condition? + params[:condition] == "true" + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "content_security_policy_integration_test" do + get "/", to: "policy#index" + get "/inline", to: "policy#inline" + get "/conditional", to: "policy#conditional" + get "/report-only", to: "policy#report_only" + end + end + + POLICY = ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src :self + end + + class PolicyConfigMiddleware + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.content_security_policy"] = POLICY + env["action_dispatch.content_security_policy_report_only"] = false + env["action_dispatch.show_exceptions"] = false + + @app.call(env) + end + end + + APP = build_app(ROUTES) do |middleware| + middleware.use PolicyConfigMiddleware + middleware.use ActionDispatch::ContentSecurityPolicy::Middleware + end + + def app + APP + end + + def test_generates_content_security_policy_header + get "/" + assert_policy "default-src 'self';" + end + + def test_generates_inline_content_security_policy + get "/inline" + assert_policy "default-src https://example.com;" + end + + def test_generates_conditional_content_security_policy + get "/conditional", params: { condition: "true" } + assert_policy "default-src https://true.example.com;" + + get "/conditional", params: { condition: "false" } + assert_policy "default-src https://false.example.com;" + end + + def test_generates_report_only_content_security_policy + get "/report-only" + assert_policy "default-src 'self'; report-uri /violations;", report_only: true + end + + private + + def env_config + Rails.application.env_config + end + + def content_security_policy + env_config["action_dispatch.content_security_policy"] + end + + def content_security_policy=(policy) + env_config["action_dispatch.content_security_policy"] = policy + end + + def assert_policy(expected, report_only: false) + assert_response :success + + if report_only + expected_header = "Content-Security-Policy-Report-Only" + unexpected_header = "Content-Security-Policy" + else + expected_header = "Content-Security-Policy" + unexpected_header = "Content-Security-Policy-Report-Only" + end + + assert_nil response.headers[unexpected_header] + assert_equal expected, response.headers[expected_header] + end +end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 40cbad3b0d..94cff10fe4 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -36,6 +36,12 @@ class CookieJarTest < ActiveSupport::TestCase assert_equal "bar", request.cookie_jar.fetch(:foo) end + def test_to_hash + request.cookie_jar["foo"] = "bar" + assert_equal({ "foo" => "bar" }, request.cookie_jar.to_hash) + assert_equal({ "foo" => "bar" }, request.cookie_jar.to_h) + end + def test_fetch_type_error assert_raises(KeyError) do request.cookie_jar.fetch(:omglolwut) @@ -319,7 +325,7 @@ class CookiesTest < ActionController::TestCase def test_setting_the_same_value_to_cookie request.cookies[:user_name] = "david" get :authenticate - assert_predicate response.cookies, :empty? + assert_empty response.cookies end def test_setting_the_same_value_to_permanent_cookie @@ -401,7 +407,7 @@ class CookiesTest < ActionController::TestCase def test_delete_unexisting_cookie request.cookies.clear get :delete_cookie - assert_predicate @response.cookies, :empty? + assert_empty @response.cookies end def test_deleted_cookie_predicate diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb index 2901148a9e..a9a56f205f 100644 --- a/actionpack/test/dispatch/live_response_test.rb +++ b/actionpack/test/dispatch/live_response_test.rb @@ -73,7 +73,7 @@ module ActionController } latch.wait - assert @response.headers.frozen? + assert_predicate @response.headers, :frozen? e = assert_raises(ActionDispatch::IllegalStateError) do @response.headers["Content-Length"] = "zomg" end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 90e95e972d..6167ea46df 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -30,21 +30,21 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse text with trailing star at the beginning" do accept = "text/*, text/html, application/json, multipart/form-data" - expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml], Mime[:json], Mime[:multipart_form]] + expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml], Mime[:json], Mime[:multipart_form]] parsed = Mime::Type.parse(accept) - assert_equal expect, parsed + assert_equal expect.map(&:to_s), parsed.map(&:to_s) end test "parse text with trailing star in the end" do accept = "text/html, application/json, multipart/form-data, text/*" - expect = [Mime[:html], Mime[:json], Mime[:multipart_form], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml]] + expect = [Mime[:html], Mime[:json], Mime[:multipart_form], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml]] parsed = Mime::Type.parse(accept) - assert_equal expect, parsed + assert_equal expect.map(&:to_s), parsed.map(&:to_s) end test "parse text with trailing star" do accept = "text/*" - expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:xml], Mime[:yaml], Mime[:json]] + expect = [Mime[:html], Mime[:text], Mime[:js], Mime[:css], Mime[:ics], Mime[:csv], Mime[:vcf], Mime[:vtt], Mime[:xml], Mime[:yaml], Mime[:json]] parsed = Mime::Type.parse(accept) assert_equal expect.map(&:to_s).sort!, parsed.map(&:to_s).sort! end @@ -159,7 +159,7 @@ class MimeTypeTest < ActiveSupport::TestCase types.each do |type| mime = Mime[type] - assert mime.respond_to?("#{type}?"), "#{mime.inspect} does not respond to #{type}?" + assert_respond_to mime, "#{type}?" assert_equal type, mime.symbol, "#{mime.inspect} is not #{type}?" invalid_types = types - [type] invalid_types.delete(:html) diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb index 7b6ce31f29..bf5a74e694 100644 --- a/actionpack/test/dispatch/request/session_test.rb +++ b/actionpack/test/dispatch/request/session_test.rb @@ -22,6 +22,7 @@ module ActionDispatch s["foo"] = "bar" assert_equal "bar", s["foo"] assert_equal({ "foo" => "bar" }, s.to_hash) + assert_equal({ "foo" => "bar" }, s.to_h) end def test_create_merges_old diff --git a/actionpack/test/dispatch/request_id_test.rb b/actionpack/test/dispatch/request_id_test.rb index aa3175c986..9df4712dab 100644 --- a/actionpack/test/dispatch/request_id_test.rb +++ b/actionpack/test/dispatch/request_id_test.rb @@ -11,6 +11,11 @@ class RequestIdTest < ActiveSupport::TestCase assert_equal "X-Hacked-HeaderStuff", stub_request("HTTP_X_REQUEST_ID" => "; X-Hacked-Header: Stuff").request_id end + test "accept Apache mod_unique_id format" do + mod_unique_id = "abcxyz@ABCXYZ-0123456789" + assert_equal mod_unique_id, stub_request("HTTP_X_REQUEST_ID" => mod_unique_id).request_id + end + test "ensure that 255 char limit on the request id is being enforced" do assert_equal "X" * 255, stub_request("HTTP_X_REQUEST_ID" => "X" * 500).request_id end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 2a18395aac..84a2d1f69e 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -329,20 +329,20 @@ class RequestPort < BaseRequestTest test "standard_port?" do request = stub_request - assert !request.ssl? - assert request.standard_port? + assert_not_predicate request, :ssl? + assert_predicate request, :standard_port? request = stub_request "HTTPS" => "on" - assert request.ssl? - assert request.standard_port? + assert_predicate request, :ssl? + assert_predicate request, :standard_port? request = stub_request "HTTP_HOST" => "www.example.org:8080" - assert !request.ssl? - assert !request.standard_port? + assert_not_predicate request, :ssl? + assert_not_predicate request, :standard_port? request = stub_request "HTTP_HOST" => "www.example.org:8443", "HTTPS" => "on" - assert request.ssl? - assert !request.standard_port? + assert_predicate request, :ssl? + assert_not_predicate request, :standard_port? end test "optional port" do @@ -571,7 +571,7 @@ end class LocalhostTest < BaseRequestTest test "IPs that match localhost" do request = stub_request("REMOTE_IP" => "127.1.1.1", "REMOTE_ADDR" => "127.1.1.1") - assert request.local? + assert_predicate request, :local? end end @@ -643,37 +643,37 @@ class RequestProtocol < BaseRequestTest test "xml http request" do request = stub_request - assert !request.xml_http_request? - assert !request.xhr? + assert_not_predicate request, :xml_http_request? + assert_not_predicate request, :xhr? request = stub_request "HTTP_X_REQUESTED_WITH" => "DefinitelyNotAjax1.0" - assert !request.xml_http_request? - assert !request.xhr? + assert_not_predicate request, :xml_http_request? + assert_not_predicate request, :xhr? request = stub_request "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" - assert request.xml_http_request? - assert request.xhr? + assert_predicate request, :xml_http_request? + assert_predicate request, :xhr? end test "reports ssl" do - assert !stub_request.ssl? - assert stub_request("HTTPS" => "on").ssl? + assert_not_predicate stub_request, :ssl? + assert_predicate stub_request("HTTPS" => "on"), :ssl? end test "reports ssl when proxied via lighttpd" do - assert stub_request("HTTP_X_FORWARDED_PROTO" => "https").ssl? + assert_predicate stub_request("HTTP_X_FORWARDED_PROTO" => "https"), :ssl? end test "scheme returns https when proxied" do request = stub_request "rack.url_scheme" => "http" - assert !request.ssl? + assert_not_predicate request, :ssl? assert_equal "http", request.scheme request = stub_request( "rack.url_scheme" => "http", "HTTP_X_FORWARDED_PROTO" => "https" ) - assert request.ssl? + assert_predicate request, :ssl? assert_equal "https", request.scheme end end @@ -700,7 +700,7 @@ class RequestMethod < BaseRequestTest assert_equal "GET", request.request_method assert_equal "GET", request.env["REQUEST_METHOD"] - assert request.get? + assert_predicate request, :get? end test "invalid http method raises exception" do @@ -748,7 +748,7 @@ class RequestMethod < BaseRequestTest assert_equal "POST", request.method assert_equal "PATCH", request.request_method - assert request.patch? + assert_predicate request, :patch? end test "post masquerading as put" do @@ -758,12 +758,12 @@ class RequestMethod < BaseRequestTest ) assert_equal "POST", request.method assert_equal "PUT", request.request_method - assert request.put? + assert_predicate request, :put? end test "post uneffected by local inflections" do existing_acronyms = ActiveSupport::Inflector.inflections.acronyms.dup - existing_acronym_regex = ActiveSupport::Inflector.inflections.acronym_regex.dup + assert_deprecated { ActiveSupport::Inflector.inflections.acronym_regex.dup } begin ActiveSupport::Inflector.inflections do |inflect| inflect.acronym "POS" @@ -772,12 +772,12 @@ class RequestMethod < BaseRequestTest request = stub_request "REQUEST_METHOD" => "POST" assert_equal :post, ActionDispatch::Request::HTTP_METHOD_LOOKUP["POST"] assert_equal :post, request.method_symbol - assert request.post? + assert_predicate request, :post? ensure # Reset original acronym set ActiveSupport::Inflector.inflections do |inflect| inflect.send(:instance_variable_set, "@acronyms", existing_acronyms) - inflect.send(:instance_variable_set, "@acronym_regex", existing_acronym_regex) + inflect.send(:define_acronym_regex_patterns) end end end @@ -785,50 +785,44 @@ end class RequestFormat < BaseRequestTest test "xml format" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :xml }) do - assert_equal Mime[:xml], request.format - end + request = stub_request "QUERY_STRING" => "format=xml" + + assert_equal Mime[:xml], request.format end test "xhtml format" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :xhtml }) do - assert_equal Mime[:html], request.format - end + request = stub_request "QUERY_STRING" => "format=xhtml" + + assert_equal Mime[:html], request.format end test "txt format" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :txt }) do - assert_equal Mime[:text], request.format - end + request = stub_request "QUERY_STRING" => "format=txt" + + assert_equal Mime[:text], request.format end test "XMLHttpRequest" do request = stub_request( "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", - "HTTP_ACCEPT" => [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(",") + "HTTP_ACCEPT" => [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(","), + "QUERY_STRING" => "" ) - assert_called(request, :parameters, times: 1, returns: {}) do - assert request.xhr? - assert_equal Mime[:js], request.format - end + assert_predicate request, :xhr? + assert_equal Mime[:js], request.format end test "can override format with parameter negative" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :txt }) do - assert !request.format.xml? - end + request = stub_request("QUERY_STRING" => "format=txt") + + assert_not_predicate request.format, :xml? end test "can override format with parameter positive" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :xml }) do - assert request.format.xml? - end + request = stub_request("QUERY_STRING" => "format=xml") + + assert_predicate request.format, :xml? end test "formats text/html with accept header" do @@ -853,40 +847,37 @@ class RequestFormat < BaseRequestTest end test "formats format:text with accept header" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :txt }) do - assert_equal [Mime[:text]], request.formats - end + request = stub_request("QUERY_STRING" => "format=txt") + + assert_equal [Mime[:text]], request.formats end test "formats format:unknown with accept header" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :unknown }) do - assert_instance_of Mime::NullType, request.format - end + request = stub_request("QUERY_STRING" => "format=unknown") + + assert_instance_of Mime::NullType, request.format end test "format is not nil with unknown format" do - request = stub_request - assert_called(request, :parameters, times: 2, returns: { format: :hello }) do - assert request.format.nil? - assert_not request.format.html? - assert_not request.format.xml? - assert_not request.format.json? - end + request = stub_request("QUERY_STRING" => "format=hello") + + assert_nil request.format + assert_not_predicate request.format, :html? + assert_not_predicate request.format, :xml? + assert_not_predicate request.format, :json? end test "format does not throw exceptions when malformed parameters" do request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2") assert request.formats - assert request.format.html? + assert_predicate request.format, :html? end test "formats with xhr request" do - request = stub_request "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [Mime[:js]], request.formats - end + request = stub_request "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "QUERY_STRING" => "" + + assert_equal [Mime[:js]], request.formats end test "ignore_accept_header" do @@ -894,62 +885,58 @@ class RequestFormat < BaseRequestTest ActionDispatch::Request.ignore_accept_header = true begin - request = stub_request "HTTP_ACCEPT" => "application/xml" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [ Mime[:html] ], request.formats - end + request = stub_request "HTTP_ACCEPT" => "application/xml", + "QUERY_STRING" => "" - request = stub_request "HTTP_ACCEPT" => "koz-asked/something-crazy" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [ Mime[:html] ], request.formats - end + assert_equal [ Mime[:html] ], request.formats - request = stub_request "HTTP_ACCEPT" => "*/*;q=0.1" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [ Mime[:html] ], request.formats - end + request = stub_request "HTTP_ACCEPT" => "koz-asked/something-crazy", + "QUERY_STRING" => "" - request = stub_request "HTTP_ACCEPT" => "application/jxw" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [ Mime[:html] ], request.formats - end + assert_equal [ Mime[:html] ], request.formats + + request = stub_request "HTTP_ACCEPT" => "*/*;q=0.1", + "QUERY_STRING" => "" + + assert_equal [ Mime[:html] ], request.formats + + request = stub_request "HTTP_ACCEPT" => "application/jxw", + "QUERY_STRING" => "" + + assert_equal [ Mime[:html] ], request.formats request = stub_request "HTTP_ACCEPT" => "application/xml", - "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "QUERY_STRING" => "" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [ Mime[:js] ], request.formats - end + assert_equal [ Mime[:js] ], request.formats request = stub_request "HTTP_ACCEPT" => "application/xml", - "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest" - assert_called(request, :parameters, times: 2, returns: { format: :json }) do - assert_equal [ Mime[:json] ], request.formats - end + "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", + "QUERY_STRING" => "format=json" + + assert_equal [ Mime[:json] ], request.formats ensure ActionDispatch::Request.ignore_accept_header = old_ignore_accept_header end end test "format taken from the path extension" do - request = stub_request "PATH_INFO" => "/foo.xml" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [Mime[:xml]], request.formats - end + request = stub_request "PATH_INFO" => "/foo.xml", "QUERY_STRING" => "" - request = stub_request "PATH_INFO" => "/foo.123" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [Mime[:html]], request.formats - end + assert_equal [Mime[:xml]], request.formats + + request = stub_request "PATH_INFO" => "/foo.123", "QUERY_STRING" => "" + + assert_equal [Mime[:html]], request.formats end test "formats from accept headers have higher precedence than path extension" do request = stub_request "HTTP_ACCEPT" => "application/json", - "PATH_INFO" => "/foo.xml" + "PATH_INFO" => "/foo.xml", + "QUERY_STRING" => "" - assert_called(request, :parameters, times: 1, returns: {}) do - assert_equal [Mime[:json]], request.formats - end + assert_equal [Mime[:json]], request.formats end end @@ -997,15 +984,14 @@ end class RequestParameters < BaseRequestTest test "parameters" do - request = stub_request + request = stub_request "CONTENT_TYPE" => "application/json", + "CONTENT_LENGTH" => 9, + "RAW_POST_DATA" => '{"foo":1}', + "QUERY_STRING" => "bar=2" - assert_called(request, :request_parameters, times: 2, returns: { "foo" => 1 }) do - assert_called(request, :query_parameters, times: 2, returns: { "bar" => 2 }) do - assert_equal({ "foo" => 1, "bar" => 2 }, request.parameters) - assert_equal({ "foo" => 1 }, request.request_parameters) - assert_equal({ "bar" => 2 }, request.query_parameters) - end - end + assert_equal({ "foo" => 1, "bar" => "2" }, request.parameters) + assert_equal({ "foo" => 1 }, request.request_parameters) + assert_equal({ "bar" => "2" }, request.query_parameters) end test "parameters not accessible after rack parse error" do @@ -1248,8 +1234,8 @@ class RequestVariant < BaseRequestTest test "setting variant to a symbol" do @request.variant = :phone - assert @request.variant.phone? - assert_not @request.variant.tablet? + assert_predicate @request.variant, :phone? + assert_not_predicate @request.variant, :tablet? assert @request.variant.any?(:phone, :tablet) assert_not @request.variant.any?(:tablet, :desktop) end @@ -1257,9 +1243,9 @@ class RequestVariant < BaseRequestTest test "setting variant to an array of symbols" do @request.variant = [:phone, :tablet] - assert @request.variant.phone? - assert @request.variant.tablet? - assert_not @request.variant.desktop? + assert_predicate @request.variant, :phone? + assert_predicate @request.variant, :tablet? + assert_not_predicate @request.variant, :desktop? assert @request.variant.any?(:tablet, :desktop) assert_not @request.variant.any?(:desktop, :watch) end @@ -1267,8 +1253,8 @@ class RequestVariant < BaseRequestTest test "clearing variant" do @request.variant = nil - assert @request.variant.empty? - assert_not @request.variant.phone? + assert_empty @request.variant + assert_not_predicate @request.variant, :phone? assert_not @request.variant.any?(:phone, :tablet) end @@ -1287,13 +1273,13 @@ end class RequestFormData < BaseRequestTest test "media_type is from the FORM_DATA_MEDIA_TYPES array" do - assert stub_request("CONTENT_TYPE" => "application/x-www-form-urlencoded").form_data? - assert stub_request("CONTENT_TYPE" => "multipart/form-data").form_data? + assert_predicate stub_request("CONTENT_TYPE" => "application/x-www-form-urlencoded"), :form_data? + assert_predicate stub_request("CONTENT_TYPE" => "multipart/form-data"), :form_data? end test "media_type is not from the FORM_DATA_MEDIA_TYPES array" do - assert !stub_request("CONTENT_TYPE" => "application/xml").form_data? - assert !stub_request("CONTENT_TYPE" => "multipart/related").form_data? + assert_not_predicate stub_request("CONTENT_TYPE" => "application/xml"), :form_data? + assert_not_predicate stub_request("CONTENT_TYPE" => "multipart/related"), :form_data? end test "no Content-Type header is provided and the request_method is POST" do @@ -1301,7 +1287,7 @@ class RequestFormData < BaseRequestTest assert_equal "", request.media_type assert_equal "POST", request.request_method - assert !request.form_data? + assert_not_predicate request, :form_data? end end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index c4ee3add2a..4c8d528507 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -15,13 +15,13 @@ class ResponseTest < ActiveSupport::TestCase @response.await_commit } @response.commit! - assert @response.committed? + assert_predicate @response, :committed? assert t.join(0.5) end def test_stream_close @response.stream.close - assert @response.stream.closed? + assert_predicate @response.stream, :closed? end def test_stream_write @@ -257,9 +257,9 @@ class ResponseTest < ActiveSupport::TestCase } resp.to_a - assert resp.etag? - assert resp.weak_etag? - assert_not resp.strong_etag? + assert_predicate resp, :etag? + assert_predicate resp, :weak_etag? + assert_not_predicate resp, :strong_etag? assert_equal('W/"202cb962ac59075b964b07152d234b70"', resp.etag) assert_equal({ public: true }, resp.cache_control) @@ -275,9 +275,9 @@ class ResponseTest < ActiveSupport::TestCase } resp.to_a - assert resp.etag? - assert_not resp.weak_etag? - assert resp.strong_etag? + assert_predicate resp, :etag? + assert_not_predicate resp, :weak_etag? + assert_predicate resp, :strong_etag? assert_equal('"202cb962ac59075b964b07152d234b70"', resp.etag) end @@ -311,13 +311,16 @@ class ResponseTest < ActiveSupport::TestCase end end - test "read x_frame_options, x_content_type_options and x_xss_protection" do + test "read x_frame_options, x_content_type_options, x_xss_protection, x_download_options and x_permitted_cross_domain_policies, referrer_policy" do original_default_headers = ActionDispatch::Response.default_headers begin ActionDispatch::Response.default_headers = { "X-Frame-Options" => "DENY", "X-Content-Type-Options" => "nosniff", - "X-XSS-Protection" => "1;" + "X-XSS-Protection" => "1;", + "X-Download-Options" => "noopen", + "X-Permitted-Cross-Domain-Policies" => "none", + "Referrer-Policy" => "strict-origin-when-cross-origin" } resp = ActionDispatch::Response.create.tap { |response| response.body = "Hello" @@ -327,6 +330,9 @@ class ResponseTest < ActiveSupport::TestCase assert_equal("DENY", resp.headers["X-Frame-Options"]) assert_equal("nosniff", resp.headers["X-Content-Type-Options"]) assert_equal("1;", resp.headers["X-XSS-Protection"]) + assert_equal("noopen", resp.headers["X-Download-Options"]) + assert_equal("none", resp.headers["X-Permitted-Cross-Domain-Policies"]) + assert_equal("strict-origin-when-cross-origin", resp.headers["Referrer-Policy"]) ensure ActionDispatch::Response.default_headers = original_default_headers end @@ -350,7 +356,7 @@ class ResponseTest < ActiveSupport::TestCase end test "respond_to? accepts include_private" do - assert_not @response.respond_to?(:method_missing) + assert_not_respond_to @response, :method_missing assert @response.respond_to?(:method_missing, true) end diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb index a5198f2f13..009b6d9bc3 100644 --- a/actionpack/test/dispatch/routing_assertions_test.rb +++ b/actionpack/test/dispatch/routing_assertions_test.rb @@ -52,6 +52,8 @@ class RoutingAssertionsTest < ActionController::TestCase end mount engine => "/shelf" + + get "/shelf/foo", controller: "query_articles", action: "index" end end @@ -154,6 +156,10 @@ class RoutingAssertionsTest < ActionController::TestCase assert_match err.message, "This is a really bad msg" end + def test_assert_recognizes_continue_to_recoginize_after_it_tried_engines + assert_recognizes({ controller: "query_articles", action: "index" }, "/shelf/foo") + end + def test_assert_routing assert_routing("/articles", controller: "articles", action: "index") end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 44f902c163..4222eb4eb7 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -3313,7 +3313,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end get "/search" - assert !@request.params[:action].frozen? + assert_not_predicate @request.params[:action], :frozen? end def test_multiple_positional_args_with_the_same_name @@ -4225,7 +4225,7 @@ class TestGlobRoutingMapper < ActionDispatch::IntegrationTest end end - #include Routes.url_helpers + # include Routes.url_helpers APP = build_app Routes def app; APP end @@ -4267,7 +4267,7 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest def app; APP end test "enabled when not mounted and default_url_options is empty" do - assert Routes.url_helpers.optimize_routes_generation? + assert_predicate Routes.url_helpers, :optimize_routes_generation? end test "named route called as singleton method" do @@ -5057,3 +5057,40 @@ class TestRecognizePath < ActionDispatch::IntegrationTest Routes.recognize_path(*args) end end + +class TestRelativeUrlRootGeneration < ActionDispatch::IntegrationTest + config = ActionDispatch::Routing::RouteSet::Config.new("/blog", false) + + stub_controllers(config) do |routes| + Routes = routes + + routes.draw do + get "/", to: "posts#index", as: :posts + get "/:id", to: "posts#show", as: :post + end + end + + include Routes.url_helpers + + APP = build_app Routes + + def app + APP + end + + def test_url_helpers + assert_equal "/blog/", posts_path({}) + assert_equal "/blog/", Routes.url_helpers.posts_path({}) + + assert_equal "/blog/1", post_path(id: "1") + assert_equal "/blog/1", Routes.url_helpers.post_path(id: "1") + end + + def test_optimized_url_helpers + assert_equal "/blog/", posts_path + assert_equal "/blog/", Routes.url_helpers.posts_path + + assert_equal "/blog/1", post_path("1") + assert_equal "/blog/1", Routes.url_helpers.post_path("1") + end +end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index cf51c47068..e34426a471 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -8,11 +8,14 @@ require "active_support/messages/rotation_configuration" class CookieStoreTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" SessionSecret = "b3c631c314c0bbca50c1b2843150fe33" - Generator = ActiveSupport::LegacyKeyGenerator.new(SessionSecret) + SessionSalt = "authenticated encrypted cookie" + + Generator = ActiveSupport::KeyGenerator.new(SessionSecret, iterations: 1000) Rotations = ActiveSupport::Messages::RotationConfiguration.new - Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, digest: "SHA1") - SignedBar = Verifier.generate(foo: "bar", session_id: SecureRandom.hex(16)) + Encryptor = ActiveSupport::MessageEncryptor.new( + Generator.generate_key(SessionSalt, 32), cipher: "aes-256-gcm", serializer: Marshal + ) class TestController < ActionController::Base def no_session_access @@ -25,12 +28,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def set_session_value session[:foo] = "bar" - render plain: Rack::Utils.escape(Verifier.generate(session.to_hash)) - end - - def set_session_value_expires_in_five_hours - session[:foo] = "bar" - render plain: Rack::Utils.escape(Verifier.generate(session.to_hash, expires_in: 5.hours)) + render body: nil end def get_session_value @@ -72,19 +70,35 @@ class CookieStoreTest < ActionDispatch::IntegrationTest end end + def parse_cookie_from_header + cookie_matches = headers["Set-Cookie"].match(/#{SessionKey}=([^;]+)/) + cookie_matches && cookie_matches[1] + end + + def assert_session_cookie(cookie_string, contents) + assert_includes headers["Set-Cookie"], cookie_string + + session_value = parse_cookie_from_header + session_data = Encryptor.decrypt_and_verify(Rack::Utils.unescape(session_value)) rescue nil + + assert_not_nil session_data, "session failed to decrypt" + assert_equal session_data.slice(*contents.keys), contents + end + def test_setting_session_value with_test_route_set do get "/set_session_value" + assert_response :success - assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly", - headers["Set-Cookie"] + assert_session_cookie "path=/; HttpOnly", "foo" => "bar" end end def test_getting_session_value with_test_route_set do - cookies[SessionKey] = SignedBar + get "/set_session_value" get "/get_session_value" + assert_response :success assert_equal 'foo: "bar"', response.body end @@ -92,8 +106,9 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def test_getting_session_id with_test_route_set do - cookies[SessionKey] = SignedBar + get "/set_session_value" get "/persistent_session_id" + assert_response :success assert_equal 32, response.body.size session_id = response.body @@ -106,8 +121,12 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def test_disregards_tampered_sessions with_test_route_set do - cookies[SessionKey] = "BAh7BjoIZm9vIghiYXI%3D--123456780" + encryptor = ActiveSupport::MessageEncryptor.new("A" * 32, cipher: "aes-256-gcm", serializer: Marshal) + + cookies[SessionKey] = encryptor.encrypt_and_sign("foo" => "bar", "session_id" => "abc") + get "/get_session_value" + assert_response :success assert_equal "foo: nil", response.body end @@ -135,19 +154,19 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def test_does_set_secure_cookies_over_https with_test_route_set(secure: true) do get "/set_session_value", headers: { "HTTPS" => "on" } + assert_response :success - assert_equal "_myapp_session=#{response.body}; path=/; secure; HttpOnly", - headers["Set-Cookie"] + assert_session_cookie "path=/; secure; HttpOnly", "foo" => "bar" end end # {:foo=>#<SessionAutoloadTest::Foo bar:"baz">, :session_id=>"ce8b0752a6ab7c7af3cdb8a80e6b9e46"} - SignedSerializedCookie = "BAh7BzoIZm9vbzodU2Vzc2lvbkF1dG9sb2FkVGVzdDo6Rm9vBjoJQGJhciIIYmF6Og9zZXNzaW9uX2lkIiVjZThiMDc1MmE2YWI3YzdhZjNjZGI4YTgwZTZiOWU0Ng==--2bf3af1ae8bd4e52b9ac2099258ace0c380e601c" + EncryptedSerializedCookie = "9RZ2Fij0qLveUwM4s+CCjGqhpjyUC8jiBIf/AiBr9M3TB8xh2vQZtvSOMfN3uf6oYbbpIDHAcOFIEl69FcW1ozQYeSrCLonYCazoh34ZdYskIQfGwCiSYleVXG1OD9Z4jFqeVArw4Ewm0paOOPLbN1rc6A==--I359v/KWdZ1ok0ey--JFFhuPOY7WUo6tB/eP05Aw==" def test_deserializes_unloaded_classes_on_get_id with_test_route_set do with_autoload_path "session_autoload_test" do - cookies[SessionKey] = SignedSerializedCookie + cookies[SessionKey] = EncryptedSerializedCookie get "/get_session_id" assert_response :success assert_equal "id: ce8b0752a6ab7c7af3cdb8a80e6b9e46", response.body, "should auto-load unloaded class" @@ -158,7 +177,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def test_deserializes_unloaded_classes_on_get_value with_test_route_set do with_autoload_path "session_autoload_test" do - cookies[SessionKey] = SignedSerializedCookie + cookies[SessionKey] = EncryptedSerializedCookie get "/get_session_value" assert_response :success assert_equal 'foo: #<SessionAutoloadTest::Foo bar:"baz">', response.body, "should auto-load unloaded class" @@ -197,8 +216,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest get "/set_session_value" assert_response :success session_payload = response.body - assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly", - headers["Set-Cookie"] + assert_session_cookie "path=/; HttpOnly", "foo" => "bar" get "/call_reset_session" assert_response :success @@ -216,8 +234,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set do get "/set_session_value" assert_response :success - assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly", - headers["Set-Cookie"] + assert_session_cookie "path=/; HttpOnly", "foo" => "bar" get "/get_class_after_reset_session" assert_response :success @@ -239,8 +256,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set do get "/set_session_value" assert_response :success - assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly", - headers["Set-Cookie"] + assert_session_cookie "path=/; HttpOnly", "foo" => "bar" get "/call_session_clear" assert_response :success @@ -253,7 +269,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def test_persistent_session_id with_test_route_set do - cookies[SessionKey] = SignedBar + get "/set_session_value" get "/persistent_session_id" assert_response :success assert_equal 32, response.body.size @@ -268,8 +284,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def test_setting_session_id_to_nil_is_respected with_test_route_set do - cookies[SessionKey] = SignedBar - + get "/set_session_value" get "/get_session_id" sid = response.body assert_equal 36, sid.size @@ -283,31 +298,53 @@ class CookieStoreTest < ActionDispatch::IntegrationTest with_test_route_set(expire_after: 5.hours) do # First request accesses the session time = Time.local(2008, 4, 24) - cookie_body = nil Time.stub :now, time do expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") - cookies[SessionKey] = SignedBar + get "/set_session_value" - get "/set_session_value_expires_in_five_hours" assert_response :success - - cookie_body = response.body - assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly", - headers["Set-Cookie"] + assert_session_cookie "path=/; expires=#{expected_expiry}; HttpOnly", "foo" => "bar" end # Second request does not access the session - time = Time.local(2008, 4, 25) + time = time + 3.hours Time.stub :now, time do expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") get "/no_session_access" + + assert_response :success + assert_session_cookie "path=/; expires=#{expected_expiry}; HttpOnly", "foo" => "bar" + end + end + end + + def test_session_store_with_expire_after_does_not_accept_expired_session + with_test_route_set(expire_after: 5.hours) do + # First request accesses the session + time = Time.local(2017, 11, 12) + + Time.stub :now, time do + expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") + + get "/set_session_value" + get "/get_session_value" + assert_response :success + assert_equal 'foo: "bar"', response.body + assert_session_cookie "path=/; expires=#{expected_expiry}; HttpOnly", "foo" => "bar" + end + + # Second request is beyond the expiry time and the session is invalidated + time += 5.hours + 1.minute - assert_equal "_myapp_session=#{cookies[SessionKey]}; path=/; expires=#{expected_expiry}; HttpOnly", - headers["Set-Cookie"] + Time.stub :now, time do + get "/get_session_value" + + assert_response :success + assert_equal "foo: nil", response.body end end end @@ -347,8 +384,14 @@ class CookieStoreTest < ActionDispatch::IntegrationTest def get(path, *args) args[0] ||= {} args[0][:headers] ||= {} - args[0][:headers]["action_dispatch.key_generator"] ||= Generator - args[0][:headers]["action_dispatch.cookies_rotations"] ||= Rotations + args[0][:headers].tap do |config| + config["action_dispatch.secret_key_base"] = SessionSecret + config["action_dispatch.authenticated_encrypted_cookie_salt"] = SessionSalt + config["action_dispatch.use_authenticated_cookie_encryption"] = true + + config["action_dispatch.key_generator"] ||= Generator + config["action_dispatch.cookies_rotations"] ||= Rotations + end super(path, *args) end diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb index 75feae6fe0..a824ee0c84 100644 --- a/actionpack/test/dispatch/system_testing/driver_test.rb +++ b/actionpack/test/dispatch/system_testing/driver_test.rb @@ -12,7 +12,8 @@ class DriverTest < ActiveSupport::TestCase test "initializing the driver with a browser" do driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :chrome, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" }) assert_equal :selenium, driver.instance_variable_get(:@name) - assert_equal :chrome, driver.instance_variable_get(:@browser) + assert_equal :chrome, driver.instance_variable_get(:@browser).name + assert_nil driver.instance_variable_get(:@browser).options assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size) assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) end @@ -20,7 +21,15 @@ class DriverTest < ActiveSupport::TestCase test "initializing the driver with a headless chrome" do driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" }) assert_equal :selenium, driver.instance_variable_get(:@name) - assert_equal :headless_chrome, driver.instance_variable_get(:@browser) + assert_equal :headless_chrome, driver.instance_variable_get(:@browser).name + assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size) + assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) + end + + test "initializing the driver with a headless firefox" do + driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :headless_firefox, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" }) + assert_equal :selenium, driver.instance_variable_get(:@name) + assert_equal :headless_firefox, driver.instance_variable_get(:@browser).name assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size) assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options) end diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb index 2afda31cf5..264844fc7d 100644 --- a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb +++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb @@ -35,6 +35,11 @@ class ScreenshotHelperTest < ActiveSupport::TestCase end end + test "defaults to simple output for the screenshot" do + new_test = DrivenBySeleniumWithChrome.new("x") + assert_equal "simple", new_test.send(:output_type) + end + test "display_image return artifact format when specify RAILS_SYSTEM_TESTING_SCREENSHOT environment" do begin original_output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] @@ -42,6 +47,8 @@ class ScreenshotHelperTest < ActiveSupport::TestCase new_test = DrivenBySeleniumWithChrome.new("x") + 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) diff --git a/actionpack/test/dispatch/system_testing/server_test.rb b/actionpack/test/dispatch/system_testing/server_test.rb index 1866225fc1..95e411faf4 100644 --- a/actionpack/test/dispatch/system_testing/server_test.rb +++ b/actionpack/test/dispatch/system_testing/server_test.rb @@ -6,10 +6,27 @@ require "action_dispatch/system_testing/server" class ServerTest < ActiveSupport::TestCase setup do - ActionDispatch::SystemTesting::Server.new.run + @old_capybara_server = Capybara.server end test "port is always included" do + ActionDispatch::SystemTesting::Server.new.run assert Capybara.always_include_port, "expected Capybara.always_include_port to be true" end + + test "server is changed from `default` to `puma`" do + Capybara.server = :default + ActionDispatch::SystemTesting::Server.new.run + refute_equal Capybara.server, Capybara.servers[:default] + end + + test "server is not changed to `puma` when is different than default" do + Capybara.server = :webrick + ActionDispatch::SystemTesting::Server.new.run + assert_equal Capybara.server, Capybara.servers[:webrick] + end + + teardown do + Capybara.server = @old_capybara_server + end end diff --git a/actionpack/test/dispatch/system_testing/system_test_case_test.rb b/actionpack/test/dispatch/system_testing/system_test_case_test.rb index c6a6aef92b..b078a5abc5 100644 --- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb +++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb @@ -28,6 +28,12 @@ class SetDriverToSeleniumHeadlessChromeTest < DrivenBySeleniumWithHeadlessChrome end end +class SetDriverToSeleniumHeadlessFirefoxTest < DrivenBySeleniumWithHeadlessFirefox + test "uses selenium headless firefox" do + assert_equal :selenium, Capybara.current_driver + end +end + class SetHostTest < DrivenByRackTest test "sets default host" do assert_equal "http://127.0.0.1", Capybara.app_host diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb index 4673d7cc11..5a584b12e5 100644 --- a/actionpack/test/dispatch/uploaded_file_test.rb +++ b/actionpack/test/dispatch/uploaded_file_test.rb @@ -18,7 +18,7 @@ module ActionDispatch def test_filename_is_different_object file_str = "foo" uf = Http::UploadedFile.new(filename: file_str, tempfile: Object.new) - assert_not_equal file_str.object_id , uf.original_filename.object_id + assert_not_equal file_str.object_id, uf.original_filename.object_id end def test_filename_should_be_in_utf_8 @@ -100,14 +100,14 @@ module ActionDispatch def test_delegate_eof_to_tempfile tf = Class.new { def eof?; true end; } uf = Http::UploadedFile.new(tempfile: tf.new) - assert uf.eof? + assert_predicate uf, :eof? end def test_respond_to? tf = Class.new { def read; yield end } uf = Http::UploadedFile.new(tempfile: tf.new) - assert uf.respond_to?(:headers), "responds to headers" - assert uf.respond_to?(:read), "responds to read" + assert_respond_to uf, :headers + assert_respond_to uf, :read end end end |