# frozen_string_literal: true require "active_support/core_ext/class/attribute" require "active_support/core_ext/module/delegation" require "active_support/core_ext/hash/reverse_merge" require "active_support/core_ext/kernel/reporting" require "active_support/testing/stream" require "active_support/concern" require "rails/generators" module Rails module Generators module Testing module Behaviour extend ActiveSupport::Concern include ActiveSupport::Testing::Stream included do # Generators frequently change the current path using +FileUtils.cd+. # So we need to store the path at file load and revert back to it after each test. class_attribute :current_path, default: File.expand_path(Dir.pwd) class_attribute :default_arguments, default: [] class_attribute :destination_root class_attribute :generator_class end module ClassMethods # Sets which generator should be tested: # # tests AppGenerator def tests(klass) self.generator_class = klass end # Sets default arguments on generator invocation. This can be overwritten when # invoking it. # # arguments %w(app_name --skip-active-record) def arguments(array) self.default_arguments = array end # Sets the destination of generator files: # # destination File.expand_path("../tmp", __dir__) def destination(path) self.destination_root = path end end # Runs the generator configured for this class. The first argument is an array like # command line arguments: # # class AppGeneratorTest < Rails::Generators::TestCase # tests AppGenerator # destination File.expand_path("../tmp", __dir__) # setup :prepare_destination # # test "database.yml is not created when skipping Active Record" do # run_generator %w(myapp --skip-active-record) # assert_no_file "config/database.yml" # end # end # # You can provide a configuration hash as second argument. This method returns the output # printed by the generator. def run_generator(args = default_arguments, config = {}) capture(:stdout) do args += ["--skip-bundle"] unless args.include? "--dev" args |= ["--skip-bootsnap"] unless args.include? "--no-skip-bootsnap" generator_class.start(args, config.reverse_merge(destination_root: destination_root)) end end # Instantiate the generator. def generator(args = default_arguments, options = {}, config = {}) @generator ||= generator_class.new(args, options, config.reverse_merge(destination_root: destination_root)) end # Create a Rails::Generators::GeneratedAttribute by supplying the # attribute type and, optionally, the attribute name: # # create_generated_attribute(:string, "name") def create_generated_attribute(attribute_type, name = "test", index = nil) Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(":")) end private def destination_root_is_set? raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root end def ensure_current_path cd current_path end # Clears all files and directories in destination. def prepare_destination # :doc: rm_rf(destination_root) mkdir_p(destination_root) end def migration_file_name(relative) absolute = File.expand_path(relative, destination_root) dirname, file_name = File.dirname(absolute), File.basename(absolute).delete_suffix(".rb") Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first end end end end end