Skip to content

Commit

Permalink
Allow Actionable Errors encountered when running tests to be retried.…
Browse files Browse the repository at this point in the history
… This can be configured by config.actionable_command_line_errors and is true in the test environment unless the "CI" env variable is set, and false otherwise.

Co-authored-by: Gannon McGibbon <gannon.mcgibbon@gmail.com>
  • Loading branch information
andrewn617 and gmcgibbon committed Feb 2, 2024
1 parent 979c6b0 commit 59a5422
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 8 deletions.
6 changes: 6 additions & 0 deletions guides/source/configuring.md
Expand Up @@ -158,6 +158,12 @@ Below are the default values associated with each target version. In cases of co

The following configuration methods are to be called on a `Rails::Railtie` object, such as a subclass of `Rails::Engine` or `Rails::Application`.

#### `config.actionable_command_line_errors`

Says whether the user will be prompted to retry `ActiveSupport::ActionableError`s on the command line.

By default it is `true` in the test environment unless the `"CI"` env variable is set, and false otherwise.

#### `config.add_autoload_paths_to_load_path`

Says whether autoload paths have to be added to `$LOAD_PATH`. It is recommended to be set to `false` in `:zeitwerk` mode early, in `config/application.rb`. Zeitwerk uses absolute paths internally, and applications running in `:zeitwerk` mode do not need `require_dependency`, so models, controllers, jobs, etc. do not need to be in `$LOAD_PATH`. Setting this to `false` saves Ruby from checking these directories when resolving `require` calls with relative paths, and saves Bootsnap work and RAM, since it does not need to build an index for them.
Expand Down
29 changes: 29 additions & 0 deletions railties/CHANGELOG.md
@@ -1,3 +1,32 @@
* Allow Actionable Errors encountered when running tests to be retried. This can be configured by
`config.actionable_command_line_errors` and is `true` in the test environment unless the `"CI"` env variable
is set, and false otherwise.

```txt
Migrations are pending. To resolve this issue, run:
bin/rails db:migrate
You have 1 pending migration:
db/migrate/20240201213806_add_a_to_b.rb
Run pending migrations? [Yn] Y
== 20240201213806 AddAToB: migrating =========================================
== 20240201213806 AddAToB: migrated (0.0000s) ================================
Running 7 tests in a single process (parallelization threshold is 50)
Run options: --seed 22200
# Running:
.......
Finished in 0.243394s, 28.7600 runs/s, 45.1942 assertions/s.
7 runs, 11 assertions, 0 failures, 0 errors, 0 skips
```

*Andrew Novoselac & Gannon McGibbon*

* Commented out lines in .railsrc file should not be treated as arguments when using
rails new generator command. Update ARGVScrubber to ignore text after # symbols.

Expand Down
2 changes: 1 addition & 1 deletion railties/lib/rails/application/configuration.rb
Expand Up @@ -23,7 +23,7 @@ class Configuration < ::Rails::Engine::Configuration
:content_security_policy_nonce_generator, :content_security_policy_nonce_directives,
:require_master_key, :credentials, :disable_sandbox, :sandbox_by_default,
:add_autoload_paths_to_load_path, :rake_eager_load, :server_timing, :log_file_size,
:dom_testing_default_html_version
:dom_testing_default_html_version, :actionable_command_line_errors

attr_reader :encoding, :api_only, :loaded_config_version, :log_level

Expand Down
16 changes: 16 additions & 0 deletions railties/lib/rails/command/base.rb
Expand Up @@ -179,6 +179,22 @@ def invoke_command(command, *) # :nodoc:
ensure
@current_subcommand = original_subcommand
end

protected
def with_actionable_errors_retried(&block)
block.call
rescue ActiveSupport::ActionableError => e
puts e.to_s.strip
exit 1 unless Rails.application.config.actionable_command_line_errors

ActiveSupport::ActionableError.actions(e).each_key do |action_name|
if yes? "#{action_name}? [Yn]"
ActiveSupport::ActionableError.dispatch(e, action_name)
return with_actionable_errors_retried(&block)
end
end
exit 1
end
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion railties/lib/rails/commands/test/test_command.rb
Expand Up @@ -30,7 +30,9 @@ def perform(*args)

Rails::TestUnit::Runner.parse_options(args)
run_prepare_task if self.args.none?(EXACT_TEST_ARGUMENT_PATTERN)
Rails::TestUnit::Runner.run(args)
with_actionable_errors_retried do
Rails::TestUnit::Runner.run(args)
end
end

# Define Thor tasks to avoid going through Rake and booting twice when using bin/rails test:*
Expand Down
Expand Up @@ -64,4 +64,7 @@ Rails.application.configure do

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

# Prompt user to retry actionable errors on the command line
config.actionable_command_line_errors = !ENV["CI"]
end
8 changes: 2 additions & 6 deletions railties/lib/rails/testing/maintain_test_schema.rb
@@ -1,12 +1,8 @@
# frozen_string_literal: true

if defined?(ActiveRecord::Base)
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end

ActiveRecord::Migration.maintain_test_schema!

if Rails.configuration.eager_load
ActiveRecord::Base.descendants.each do |model|
Expand Down
49 changes: 49 additions & 0 deletions railties/test/application/test_test.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "isolation/abstract_unit"
require "thor"

module ApplicationTests
class TestTest < ActiveSupport::TestCase
Expand Down Expand Up @@ -110,6 +111,11 @@ def test_failure
output = rails("generate", "model", "user", "name:string")
version = output.match(/(\d+)_create_users\.rb/)[1]

app_file "config/environments/test.rb", <<-RUBY
Rails.application.configure do
config.actionable_command_line_errors = false
end
RUBY
app_file "test/models/user_test.rb", <<-RUBY
require "test_helper"
Expand Down Expand Up @@ -147,6 +153,11 @@ class UserTest < ActiveSupport::TestCase
output = rails("generate", "model", "user", "name:string")
version = output.match(/(\d+)_create_users\.rb/)[1]

app_file "config/environments/test.rb", <<-RUBY
Rails.application.configure do
config.actionable_command_line_errors = false
end
RUBY
app_file "test/models/user_test.rb", <<-RUBY
require "test_helper"
Expand Down Expand Up @@ -345,6 +356,44 @@ def self.load_schema!
assert_unsuccessful_run "models/user_test.rb", "SCHEMA LOADED!"
end

def test_actionable_command_line_error
rails "generate", "scaffold", "user", "name:string"
app_file "config/environments/test.rb", <<-RUBY
Rails.application.configure do
config.actionable_command_line_errors = true
end
RUBY
app_file "config/initializers/thor_yes.rb", <<-RUBY
Rails::Command::Base.class_eval <<-INITIALIZER
def yes?(statement, color = nil)
raise ArgumentError unless statement == "Run pending migrations? [Yn]"
true
end
INITIALIZER
RUBY

run_test_file("models/user_test.rb").tap do |output|
assert_match "Migrations are pending. To resolve this issue, run:", output
assert_match "CreateUsers: migrating", output
assert_match "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output
end
end

def test_actionable_command_line_errors_false
rails "generate", "scaffold", "user", "name:string"
app_file "config/environments/test.rb", <<-RUBY
Rails.application.configure do
config.actionable_command_line_errors = false
end
RUBY

run_test_file("models/user_test.rb").tap do |output|
assert_match "Migrations are pending. To resolve this issue, run:", output
assert_no_match "CreateUsers: migrating", output
assert_no_match "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips", output
end
end

private
def assert_unsuccessful_run(name, message)
result = run_test_file(name)
Expand Down

0 comments on commit 59a5422

Please sign in to comment.