Always run rails app:update in app update tests

Prior to this commit, several tests in `AppGeneratorTest` were testing
app update behavior without actually running `rails app:update`.  This
meant the logic in `Rails::AppUpdater#generator_options` that detects
which frameworks and components are installed was not being tested.

Additionally, because `ENV["BUNDLE_GEMFILE"]` is set when tests are run
(by `require "bundler/setup"` in `tools/test.rb`), any tests which did
run `rails app:update` used the Rails repo Gemfile, instead of the
generated app Gemfile.  The difference becomes obvious when running
`rails app:update` after generating an app without Sprockets (as in
`test_app_update_does_not_generate_manifest_config_when_propshaft_is_used`),
because `rails app:update` will load the Sprockets railtie (due to
`Bundler.require` using the Rails repo Gemfile), and then exit with a
`Sprockets::Railtie::ManifestNeededError`.  However, if
`rails app:update` is run within a `quietly` block, such an error will
be swallowed.

This commit changes all such tests to run `rails app:update` via a
`run_app_update` helper that: (1) overrides the `BUNDLE_GEMFILE`
environment variable to point to the generated app Gemfile, (2) points
the `rails` gem in the generated app Gemfile to the Rails repo
(otherwise the `rails` gem version cannot be resolved), and (3) sets
`exception: true` so that the `system` call will raise an error if
`rails app:update` exits with an error code.

This commit also adds `jbuilder` and `web-console` to the Rails repo
Gemfile to ensure they are already installed when evaluating the
generated app Gemfile.

These changes do add a couple dozen seconds to the test suite run time,
but the thorough test coverage seems worth it.
This commit is contained in:
Jonathan Hefner 2022-10-03 17:26:04 -05:00
parent 9ec453d9a3
commit 21f1199448
3 changed files with 83 additions and 123 deletions

@ -62,6 +62,8 @@ gem "rexml", require: false
# for railties
gem "bootsnap", ">= 1.4.4", require: false
gem "webrick", require: false
gem "jbuilder", require: false
gem "web-console", require: false
# Active Job
group :job do

@ -140,6 +140,7 @@ GEM
bcrypt (3.1.16)
beaneater (1.1.1)
benchmark-ips (2.9.2)
bindex (0.8.1)
blade (0.7.3)
activesupport (>= 3.0.0)
blade-qunit_adapter (>= 2.0.1)
@ -298,6 +299,9 @@ GEM
io-console (0.5.11)
irb (1.4.1)
reline (>= 0.3.0)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
jmespath (1.4.0)
jsbundling-rails (1.0.2)
railties (>= 6.0.0)
@ -531,6 +535,11 @@ GEM
json (>= 1.8)
nokogiri (~> 1.6)
rexml (~> 3.2)
web-console (4.2.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webdrivers (5.0.0)
nokogiri (~> 1.6)
rubyzip (>= 1.3.0)
@ -576,6 +585,7 @@ DEPENDENCIES
google-cloud-storage (~> 1.11)
image_processing (~> 1.2)
importmap-rails
jbuilder
jsbundling-rails
json (>= 2.0.0)
libxml-ruby
@ -624,6 +634,7 @@ DEPENDENCIES
tzinfo-data
w3c_validators (~> 1.3.6)
wdm (>= 0.1.0)
web-console
webdrivers
webmock
webrick

@ -153,21 +153,10 @@ def test_application_name_is_detected_if_it_exists_and_app_folder_renamed
app_moved_root = File.join(destination_root, "myapp_moved")
run_generator [app_root]
stub_rails_application(app_moved_root) do
Rails.application.stub(:is_a?, -> *args { Rails::Application }) do
FileUtils.mv(app_root, app_moved_root)
run_app_update(app_moved_root)
# make sure we are in correct dir
FileUtils.cd(app_moved_root)
generator = Rails::Generators::AppGenerator.new ["rails"], [],
destination_root: app_moved_root, shell: @shell
generator.send(:app_const)
quietly { generator.update_config_files }
assert_file "myapp_moved/config/environment.rb", /Rails\.application\.initialize!/
end
end
assert_file "#{app_moved_root}/config/environment.rb", /Rails\.application\.initialize!/
end
def test_new_application_not_include_api_initializers
@ -187,77 +176,49 @@ def test_new_application_load_defaults
end
def test_app_update_create_new_framework_defaults
run_generator
defaults_path = "config/initializers/new_framework_defaults_#{Rails::VERSION::MAJOR}_#{Rails::VERSION::MINOR}.rb"
run_generator
assert_no_file defaults_path
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, { destination_root: destination_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update
assert_file defaults_path
end
end
def test_app_update_does_not_create_rack_cors
run_generator
run_app_update
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: destination_root, shell: @shell
generator.send(:app_const)
quietly { generator.update_config_files }
assert_no_file "config/initializers/cors.rb"
end
end
def test_app_update_does_not_remove_rack_cors_if_already_present
run_generator
FileUtils.touch("#{destination_root}/config/initializers/cors.rb")
run_app_update
FileUtils.touch("config/initializers/cors.rb")
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: destination_root, shell: @shell
generator.send(:app_const)
quietly { generator.update_config_files }
assert_file "config/initializers/cors.rb"
end
end
def test_app_update_does_not_generate_assets_initializer_when_sprockets_and_propshaft_are_not_used
run_generator [destination_root, "-a", "none"]
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, asset_pipeline: "none" }, { destination_root: destination_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update
assert_no_file "config/initializers/assets.rb"
assert_no_file "app/assets/config/manifest.js"
end
end
def test_app_update_does_not_generate_manifest_config_when_propshaft_is_used
run_generator [destination_root, "-a", "propshaft"]
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, asset_pipeline: "propshaft" }, { destination_root: destination_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update
assert_file "config/initializers/assets.rb"
assert_no_file "app/assets/config/manifest.js"
end
end
def test_app_update_does_not_generate_action_cable_contents_when_skip_action_cable_is_given
run_generator [destination_root, "--skip-action-cable"]
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_action_cable: true }, { destination_root: destination_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update
assert_no_file "config/cable.yml"
assert_file "config/environments/production.rb" do |content|
@ -265,21 +226,15 @@ def test_app_update_does_not_generate_action_cable_contents_when_skip_action_cab
end
assert_no_file "test/channels/application_cable/connection_test.rb"
end
end
def test_app_update_does_not_generate_bootsnap_contents_when_skip_bootsnap_is_given
run_generator [destination_root, "--skip-bootsnap"]
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_bootsnap: true }, { destination_root: destination_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update
assert_file "config/boot.rb" do |content|
assert_no_match(/require "bootsnap\/setup"/, content)
end
end
end
def test_app_update_preserves_skip_active_job
run_generator [ destination_root, "--skip-active-job" ]
@ -287,7 +242,7 @@ def test_app_update_preserves_skip_active_job
FileUtils.cd(destination_root) do
config = "config/application.rb"
assert_no_changes -> { File.readlines(config).grep(/require /) } do
quietly { system("yes | bin/rails app:update") }
run_app_update
end
end
end
@ -298,7 +253,7 @@ def test_app_update_preserves_skip_action_mailbox
FileUtils.cd(destination_root) do
config = "config/application.rb"
assert_no_changes -> { File.readlines(config).grep(/require /) } do
quietly { system("yes | bin/rails app:update") }
run_app_update
end
end
end
@ -309,7 +264,7 @@ def test_app_update_preserves_skip_action_text
FileUtils.cd(destination_root) do
config = "config/application.rb"
assert_no_changes -> { File.readlines(config).grep(/require /) } do
quietly { system("yes | bin/rails app:update") }
run_app_update
end
end
end
@ -320,7 +275,7 @@ def test_app_update_preserves_skip_test
FileUtils.cd(destination_root) do
config = "config/application.rb"
assert_no_changes -> { File.readlines(config).grep(/require /) } do
quietly { system("yes | bin/rails app:update") }
run_app_update
end
end
end
@ -332,7 +287,7 @@ def test_app_update_preserves_skip_system_test
config = "config/application.rb"
assert_file config, /generators\.system_tests/
assert_no_changes -> { File.readlines(config).grep(/generators\.system_tests/) } do
quietly { system("yes | bin/rails app:update") }
run_app_update
end
end
end
@ -350,11 +305,7 @@ def test_gem_for_active_storage_when_skip_active_storage_is_given
def test_app_update_does_not_generate_active_storage_contents_when_skip_active_storage_is_given
run_generator [destination_root, "--skip-active-storage"]
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_active_storage: true }, { destination_root: destination_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update
assert_file "config/environments/development.rb" do |content|
assert_no_match(/config\.active_storage/, content)
@ -370,15 +321,10 @@ def test_app_update_does_not_generate_active_storage_contents_when_skip_active_s
assert_no_file "config/storage.yml"
end
end
def test_app_update_does_not_generate_active_storage_contents_when_skip_active_record_is_given
run_generator [destination_root, "--skip-active-record"]
stub_rails_application do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true, skip_active_record: true }, { destination_root: destination_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update
assert_file "config/environments/development.rb" do |content|
assert_no_match(/config\.active_storage/, content)
@ -394,7 +340,6 @@ def test_app_update_does_not_generate_active_storage_contents_when_skip_active_r
assert_no_file "config/storage.yml"
end
end
def test_generator_skips_action_mailbox_when_skip_action_mailbox_is_given
run_generator [destination_root, "--skip-action-mailbox"]
@ -433,9 +378,10 @@ def test_app_update_does_not_change_config_target_version
config = "config/application.rb"
content = File.read(config)
File.write(config, content.gsub(/config\.load_defaults #{Rails::VERSION::STRING.to_f}/, "config.load_defaults 5.1"))
quietly { system("bin/rails app:update") }
end
run_app_update
assert_file "config/application.rb", /\s+config\.load_defaults 5\.1/
end
@ -453,17 +399,13 @@ def test_app_update_does_not_change_app_name_when_app_name_is_hyphenated_name
assert_no_match(/hyphenated-app/, content)
end
stub_rails_application(app_root) do
generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, { destination_root: app_root, shell: @shell }
generator.send(:app_const)
quietly { generator.update_config_files }
run_app_update(app_root)
assert_file "#{app_root}/config/cable.yml" do |content|
assert_match(/hyphenated_app/, content)
assert_no_match(/hyphenated-app/, content)
end
end
end
def test_application_names_are_not_singularized
run_generator [File.join(destination_root, "hats")]
@ -1126,9 +1068,14 @@ def test_name_option
end
private
def stub_rails_application(root = destination_root, &block)
Rails.application.config.root = root
Rails.application.class.stub(:name, "Myapp", &block)
def run_app_update(app_root = destination_root)
Dir.chdir(app_root) do
gemfile_contents = File.read("Gemfile")
gemfile_contents.sub!(/^(gem "rails").*/, "\\1, path: #{File.expand_path("../../..", __dir__).inspect}")
File.write("Gemfile", gemfile_contents)
quietly { system({ "BUNDLE_GEMFILE" => "Gemfile" }, "yes | bin/rails app:update", exception: true) }
end
end
def action(*args, &block)