# frozen_string_literal: true
require "isolation/abstract_unit"
require "active_support/dependencies/zeitwerk_integration"
class ZeitwerkIntegrationTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
def setup
build_app
end
def boot(env = "development")
app(env)
end
def teardown
teardown_app
end
def deps
ActiveSupport::Dependencies
end
def decorated?
deps.singleton_class < deps::ZeitwerkIntegration::Decorations
end
test "ActiveSupport::Dependencies is decorated by default" do
boot
assert decorated?
assert_instance_of Zeitwerk::Loader, Rails.autoloader
assert_instance_of Zeitwerk::Loader, Rails.once_autoloader
assert_equal [Rails.autoloader, Rails.once_autoloader], Rails.autoloaders
end
test "ActiveSupport::Dependencies is not decorated in classic mode" do
add_to_config "config.autoloader = :classic"
boot
assert_not decorated?
assert_nil Rails.autoloader
assert_nil Rails.once_autoloader
assert_empty Rails.autoloaders
end
test "constantize returns the value stored in the constant" do
app_file "app/models/admin/user.rb", "class Admin::User; end"
boot
assert_same Admin::User, deps.constantize("Admin::User")
end
test "constantize raises if the constant is unknown" do
boot
assert_raises(NameError) { deps.constantize("Admin") }
end
test "safe_constantize returns the value stored in the constant" do
app_file "app/models/admin/user.rb", "class Admin::User; end"
boot
assert_same Admin::User, deps.safe_constantize("Admin::User")
end
test "safe_constantize returns nil for unknown constants" do
boot
assert_nil deps.safe_constantize("Admin")
end
test "autoloaded_constants returns autoloaded constant paths" do
app_file "app/models/admin/user.rb", "class Admin::User; end"
app_file "app/models/post.rb", "class Post; end"
boot
assert Admin::User
assert_equal ["Admin", "Admin::User"], deps.autoloaded_constants
end
test "autoloaded? says if a constant has been autoloaded" do
app_file "app/models/user.rb", "class User; end"
app_file "app/models/post.rb", "class Post; end"
boot
assert Post
assert deps.autoloaded?("Post")
assert deps.autoloaded?(Post)
assert_not deps.autoloaded?("User")
end
test "eager loading loads the application code" do
$zeitwerk_integration_test_user = false
$zeitwerk_integration_test_post = false
app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
app_file "app/models/post.rb", "class Post; end; $zeitwerk_integration_test_post = true"
boot("production")
assert $zeitwerk_integration_test_user
assert $zeitwerk_integration_test_post
end
test "eager loading loads anything managed by Zeitwerk" do
$zeitwerk_integration_test_user = false
app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true"
$zeitwerk_integration_test_extras = false
app_dir "extras"
app_file "extras/webhook_hacks.rb", "WebhookHacks = 1; $zeitwerk_integration_test_extras = true"
require "zeitwerk"
autoloader = Zeitwerk::Loader.new
autoloader.push_dir("#{app_path}/extras")
autoloader.setup
boot("production")
assert $zeitwerk_integration_test_user
assert $zeitwerk_integration_test_extras
end
test "autoload paths that are below Gem.path go to the once autoloader" do
app_dir "extras"
add_to_config 'config.autoload_paths << "#{Rails.root}/extras"'
# Mocks Gem.path to include the extras directory.
Gem.singleton_class.prepend(
Module.new do
def path
super + ["#{Rails.root}/extras"]
end
end
)
boot
assert_not_includes Rails.autoloader.dirs, "#{app_path}/extras"
assert_includes Rails.once_autoloader.dirs, "#{app_path}/extras"
end
test "clear reloads the main autoloader, and does not reload the once one" do
boot
$zeitwerk_integration_reload_test = []
autoloader = Rails.autoloader
def autoloader.reload
$zeitwerk_integration_reload_test << :autoloader
super
end
once_autoloader = Rails.once_autoloader
def once_autoloader.reload
$zeitwerk_integration_reload_test << :once_autoloader
super
end
ActiveSupport::Dependencies.clear
assert_equal %i(autoloader), $zeitwerk_integration_reload_test
end
end