diff options
Diffstat (limited to 'actionpack/test/dispatch')
13 files changed, 351 insertions, 44 deletions
diff --git a/actionpack/test/dispatch/content_security_policy_test.rb b/actionpack/test/dispatch/content_security_policy_test.rb index 30c340ae9e..3d60dc1661 100644 --- a/actionpack/test/dispatch/content_security_policy_test.rb +++ b/actionpack/test/dispatch/content_security_policy_test.rb @@ -128,12 +128,36 @@ class ContentSecurityPolicyTest < ActiveSupport::TestCase @policy.script_src false assert_no_match %r{script-src}, @policy.build + @policy.script_src_attr :self + assert_match %r{script-src-attr 'self'}, @policy.build + + @policy.script_src_attr false + assert_no_match %r{script-src-attr}, @policy.build + + @policy.script_src_elem :self + assert_match %r{script-src-elem 'self'}, @policy.build + + @policy.script_src_elem false + assert_no_match %r{script-src-elem}, @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.style_src_attr :self + assert_match %r{style-src-attr 'self'}, @policy.build + + @policy.style_src_attr false + assert_no_match %r{style-src-attr}, @policy.build + + @policy.style_src_elem :self + assert_match %r{style-src-elem 'self'}, @policy.build + + @policy.style_src_elem false + assert_no_match %r{style-src-elem}, @policy.build + @policy.worker_src :self assert_match %r{worker-src 'self'}, @policy.build @@ -542,3 +566,57 @@ class DisabledContentSecurityPolicyIntegrationTest < ActionDispatch::Integration assert_equal "default-src https://example.com", response.headers["Content-Security-Policy"] end end + +class NonceDirectiveContentSecurityPolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + def index + head :ok + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "nonce_directive_content_security_policy_integration_test" do + get "/", to: "policy#index" + end + end + + POLICY = ActionDispatch::ContentSecurityPolicy.new do |p| + p.default_src -> { :self } + p.script_src -> { :https } + p.style_src -> { :https } + 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_nonce_generator"] = proc { "iyhD0Yc0W+c=" } + env["action_dispatch.content_security_policy_report_only"] = false + env["action_dispatch.content_security_policy_nonce_directives"] = %w(script-src) + 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_generate_nonce_only_specified_in_nonce_directives + get "/" + + assert_response :success + assert_match "script-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"] + assert_no_match "style-src https: 'nonce-iyhD0Yc0W+c='", response.headers["Content-Security-Policy"] + end +end diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 68817ccdea..5d12b62fd4 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -466,6 +466,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest end test "logs exception backtrace when all lines silenced" do + @app = DevelopmentApp + output = StringIO.new backtrace_cleaner = ActiveSupport::BacktraceCleaner.new backtrace_cleaner.add_silencer { true } @@ -478,6 +480,27 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_operator((output.rewind && output.read).lines.count, :>, 10) end + test "doesn't log the framework backtrace when error type is a routing error" do + @app = ProductionApp + + output = StringIO.new + backtrace_cleaner = ActiveSupport::BacktraceCleaner.new + backtrace_cleaner.add_silencer { true } + + env = { "action_dispatch.show_exceptions" => true, + "action_dispatch.logger" => Logger.new(output), + "action_dispatch.backtrace_cleaner" => backtrace_cleaner } + + assert_raises ActionController::RoutingError do + get "/pass", headers: env + end + + log = output.rewind && output.read + + assert_includes log, "ActionController::RoutingError (No route matches [GET] \"/pass\")" + assert_equal 3, log.lines.count + end + test "display backtrace when error type is SyntaxError" do @app = DevelopmentApp @@ -521,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 } @@ -573,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 } @@ -608,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/feature_policy_test.rb b/actionpack/test/dispatch/feature_policy_test.rb new file mode 100644 index 0000000000..ebcc8a8b6d --- /dev/null +++ b/actionpack/test/dispatch/feature_policy_test.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class FeaturePolicyTest < ActiveSupport::TestCase + def setup + @policy = ActionDispatch::FeaturePolicy.new + end + + def test_mappings + @policy.midi :self + assert_equal "midi 'self'", @policy.build + + @policy.midi :none + assert_equal "midi 'none'", @policy.build + end + + def test_multiple_sources_for_a_single_directive + @policy.geolocation :self, "https://example.com" + assert_equal "geolocation 'self' https://example.com", @policy.build + end + + def test_single_directive_for_multiple_directives + @policy.geolocation :self + @policy.usb :none + assert_equal "geolocation 'self'; usb 'none'", @policy.build + end + + def test_multiple_directives_for_multiple_directives + @policy.geolocation :self, "https://example.com" + @policy.usb :none, "https://example.com" + assert_equal "geolocation 'self' https://example.com; usb 'none' https://example.com", @policy.build + end + + def test_invalid_directive_source + exception = assert_raises(ArgumentError) do + @policy.vr [:non_existent] + end + + assert_equal "Invalid HTTP feature policy source: [:non_existent]", exception.message + end +end + +class FeaturePolicyIntegrationTest < ActionDispatch::IntegrationTest + class PolicyController < ActionController::Base + feature_policy only: :index do |f| + f.gyroscope :none + end + + feature_policy only: :sample_controller do |f| + f.gyroscope nil + f.usb :self + end + + feature_policy only: :multiple_directives do |f| + f.gyroscope nil + f.usb :self + f.autoplay "https://example.com" + f.payment "https://secure.example.com" + end + + def index + head :ok + end + + def sample_controller + head :ok + end + + def multiple_directives + head :ok + end + end + + ROUTES = ActionDispatch::Routing::RouteSet.new + ROUTES.draw do + scope module: "feature_policy_integration_test" do + get "/", to: "policy#index" + get "/sample_controller", to: "policy#sample_controller" + get "/multiple_directives", to: "policy#multiple_directives" + end + end + + POLICY = ActionDispatch::FeaturePolicy.new do |p| + p.gyroscope :self + end + + class PolicyConfigMiddleware + def initialize(app) + @app = app + end + + def call(env) + env["action_dispatch.feature_policy"] = POLICY + env["action_dispatch.show_exceptions"] = false + + @app.call(env) + end + end + + APP = build_app(ROUTES) do |middleware| + middleware.use PolicyConfigMiddleware + middleware.use ActionDispatch::FeaturePolicy::Middleware + end + + def app + APP + end + + def test_generates_feature_policy_header + get "/" + assert_policy "gyroscope 'none'" + end + + def test_generates_per_controller_feature_policy_header + get "/sample_controller" + assert_policy "usb 'self'" + end + + def test_generates_multiple_directives_feature_policy_header + get "/multiple_directives" + assert_policy "usb 'self'; autoplay https://example.com; payment https://secure.example.com" + end + + private + def env_config + Rails.application.env_config + end + + def feature_policy + env_config["action_dispatch.feature_policy"] + end + + def feature_policy=(policy) + env_config["action_dispatch.feature_policy"] = policy + end + + def assert_policy(expected) + assert_response :success + assert_equal expected, response.headers["Feature-Policy"] + end +end 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/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index 63c147cb1b..f20043b9ac 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -304,7 +304,7 @@ module TestGenerationPrefix assert_equal "/omg/blog/posts/1", path end - test "[OBJECT] generating engine's route with named helpers" do + test "[OBJECT] generating engine's route with named route helpers" do path = engine_object.posts_path assert_equal "/awesome/blog/posts", path 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 0ec8dd25e0..806b7d0559 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -865,12 +865,28 @@ class RequestFormat < BaseRequestTest assert_not_predicate request.format, :json? end - test "format does not throw exceptions when malformed parameters" do + test "format does not throw exceptions when malformed GET parameters" do request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2") assert request.formats assert_predicate request.format, :html? end + test "format does not throw exceptions when invalid POST parameters" do + body = "{record:{content:127.0.0.1}}" + request = stub_request( + "REQUEST_METHOD" => "POST", + "CONTENT_LENGTH" => body.length, + "CONTENT_TYPE" => "application/json", + "rack.input" => StringIO.new(body), + "action_dispatch.logger" => Logger.new(output = StringIO.new) + ) + assert request.formats + assert request.format.html? + + output.rewind && (err = output.read) + assert_match(/Error occurred while parsing request parameters/, err) + end + test "formats with xhr request" do request = stub_request "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", "QUERY_STRING" => "" 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/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb index 7ef306d04b..d3b16d0328 100644 --- a/actionpack/test/dispatch/system_testing/driver_test.rb +++ b/actionpack/test/dispatch/system_testing/driver_test.rb @@ -120,4 +120,17 @@ class DriverTest < ActiveSupport::TestCase driver.use end end + + test "preloads browser's driver_path" do + called = false + + original_driver_path = ::Selenium::WebDriver::Chrome::Service.driver_path + ::Selenium::WebDriver::Chrome::Service.driver_path = -> { called = true } + + ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome) + + assert called + ensure + ::Selenium::WebDriver::Chrome::Service.driver_path = original_driver_path + end end 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 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 3319db1665..d235f7ad89 100644 --- a/actionpack/test/dispatch/system_testing/system_test_case_test.rb +++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb @@ -36,12 +36,10 @@ class SetDriverToSeleniumHeadlessFirefoxTest < DrivenBySeleniumWithHeadlessFiref end class SetHostTest < DrivenByRackTest - test "sets default host" do - assert_equal "http://127.0.0.1", Capybara.app_host - end - test "overrides host" do - host! "http://example.com" + assert_deprecated do + host! "http://example.com" + end assert_equal "http://example.com", Capybara.app_host end |