From 7f9ce6f62b8a3014d605efd655532275ef8b3883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A9=20Dupuis?= Date: Wed, 13 Sep 2023 12:21:19 -0700 Subject: [PATCH] Add `save_and_open_page` helper to IntegrationTest `save_and_open_page` is a capybara helper that lets developers inspect the status of the page at any given point in their test. This is helpful when trying to keep a short feedback loop while working on a test. This change adds a similar helper with matching signature to integration tests. --- Gemfile | 1 + Gemfile.lock | 3 + actionpack/CHANGELOG.md | 6 ++ .../action_dispatch/testing/integration.rb | 2 + .../testing/test_helpers/page_dump_helper.rb | 35 ++++++++ .../test/controller/integration_test.rb | 85 +++++++++++++++++++ 6 files changed, 132 insertions(+) create mode 100644 actionpack/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb diff --git a/Gemfile b/Gemfile index 9b0e2a3bfb6..d8ab4be6682 100644 --- a/Gemfile +++ b/Gemfile @@ -196,3 +196,4 @@ gem "wdm", ">= 0.1.0", platforms: [:windows] if RUBY_VERSION < "3.2" gem "error_highlight", ">= 0.4.0", platforms: [:ruby] end +gem "launchy" diff --git a/Gemfile.lock b/Gemfile.lock index 2d194ce2f12..82be41de689 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -305,6 +305,8 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) language_server-protocol (3.17.0.3) + launchy (2.5.2) + addressable (~> 2.8) libxml-ruby (5.0.3) listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) @@ -613,6 +615,7 @@ DEPENDENCIES jbuilder jsbundling-rails json (>= 2.0.0, != 2.7.0) + launchy libxml-ruby listen (~> 3.3) mdl (!= 0.13.0) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 9d1c3079358..b3e2ed4d2c3 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,9 @@ +* Add `save_and_open_page` helper to IntegrationTest + `save_and_open_page` is a helpful helper to keep a short feedback loop when working on system tests. + A similar helper with matching signature has been added to integration tests. + + *JoƩ Dupuis* + * Fix a regression in 7.1.3 passing a `to:` option without a controller when the controller is already defined by a scope. ```ruby diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 1acec055f7f..8cd6992ccb4 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -8,6 +8,7 @@ require "rack/test" require "active_support/test_case" require "action_dispatch/testing/request_encoder" +require "action_dispatch/testing/test_helpers/page_dump_helper" module ActionDispatch module Integration # :nodoc: @@ -651,6 +652,7 @@ module ActionDispatch include Integration::Runner include ActionController::TemplateAssertions + include TestHelpers::PageDumpHelper included do include ActionDispatch::Routing::UrlFor diff --git a/actionpack/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb b/actionpack/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb new file mode 100644 index 00000000000..f9309adb1f1 --- /dev/null +++ b/actionpack/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module ActionDispatch + module TestHelpers + module PageDumpHelper + class InvalidResponse < StandardError; end + + # Saves the content of response body to a file and tries to open it in your browser. + # Launchy must be present in your Gemfile for the page to open automatically. + def save_and_open_page(path = html_dump_defaul_path) + save_page(path).tap { |s_path| open_file(s_path) } + end + + private + def save_page(path = html_dump_defaul_path) + raise InvalidResponse.new("Response is a redirection!") if response.redirection? + path = Pathname.new(path) + path.dirname.mkpath + File.write(path, response.body) + path + end + + def open_file(path) + require "launchy" + Launchy.open(path) + rescue LoadError + warn "File saved to #{path}.\nPlease install the launchy gem to open the file automatically." + end + + def html_dump_defaul_path + Rails.root.join("tmp/html_dump", "#{method_name}_#{DateTime.current.to_i}.html").to_s + end + end + end +end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 4925135d31a..43d8f85fe17 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -3,6 +3,7 @@ require "abstract_unit" require "controller/fake_controllers" require "rails/engine" +require "launchy" class SessionTest < ActiveSupport::TestCase StubApp = lambda { |env| @@ -1307,3 +1308,87 @@ class IntegrationFileUploadTest < ActionDispatch::IntegrationTest assert_equal "45142", @response.body end end + +class PageDumpIntegrationTest < ActionDispatch::IntegrationTest + class FooController < ActionController::Base + def index + render plain: "Hello world" + end + + def redirect + redirect_to action: :index + end + end + + def with_root(&block) + Rails.stub(:root, Pathname.getwd.join("test"), &block) + end + + def setup + with_root do + remove_dumps + end + end + + def teardown + with_root do + remove_dumps + end + end + + def self.routes + @routes ||= ActionDispatch::Routing::RouteSet.new + end + + def self.call(env) + routes.call(env) + end + + def app + self.class + end + + def dump_path + Pathname.new(Dir["#{Rails.root}/tmp/html_dump/#{method_name}*"].sole) + end + + def remove_dumps + Dir["#{Rails.root}/tmp/html_dump/#{method_name}*"].each(&File.method(:delete)) + end + + routes.draw do + get "/" => "page_dump_integration_test/foo#index" + get "/redirect" => "page_dump_integration_test/foo#redirect" + end + + test "save_and_open_page saves a copy of the page and call to Launchy" do + launchy_called = false + get "/" + with_root do + Launchy.stub(:open, ->(path) { launchy_called = (path == dump_path) }) do + save_and_open_page + end + assert launchy_called + assert_equal File.read(dump_path), response.body + end + end + + test "prints a warning to install launchy if it can't be loaded" do + get "/" + with_root do + Launchy.stub(:open, ->(path) { raise LoadError.new }) do + self.stub(:warn, ->(warning) { warning.include?("Please install the launchy gem to open the file automatically.") }) do + save_and_open_page + end + end + assert_equal File.read(dump_path), response.body + end + end + + test "raises when called after a redirect" do + with_root do + get "/redirect" + assert_raise(InvalidResponse) { save_and_open_page } + end + end +end