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
If running in an interactive console, the user will be given the option to correct the error and re-run the tests.

Co-authored-by: Gannon McGibbon <gannon.mcgibbon@gmail.com>
  • Loading branch information
andrewn617 and gmcgibbon committed Apr 3, 2024
1 parent 02f6c29 commit bc9c665
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 7 deletions.
29 changes: 29 additions & 0 deletions railties/CHANGELOG.md
@@ -1,3 +1,32 @@
* Allow Actionable Errors encountered when running tests to be retried.

```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
```

This feature will only be present on interactive terminals.

*Andrew Novoselac & Gannon McGibbon*

* Skip generating a `test` job in ci.yml when a new application is generated with the
`--skip-test` option.

Expand Down
20 changes: 20 additions & 0 deletions railties/lib/rails/command/base.rb
Expand Up @@ -179,6 +179,26 @@ 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 tty?

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

def tty?
STDOUT.tty?
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
rescue Rails::TestUnit::InvalidTestError => error
say error.message
end
Expand Down
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
39 changes: 39 additions & 0 deletions railties/test/application/test_test.rb
Expand Up @@ -345,6 +345,45 @@ def self.load_schema!
assert_unsuccessful_run "models/user_test.rb", "SCHEMA LOADED!"
end

def test_actionable_command_line_error_with_tty
rails "generate", "scaffold", "user", "name:string"
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
def tty?
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_without_tty
rails "generate", "scaffold", "user", "name:string"
app_file "config/initializers/thor_yes.rb", <<-RUBY
Rails::Command::Base.class_eval <<-INITIALIZER
def tty?
false
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_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 bc9c665

Please sign in to comment.