diff --git a/lib/generators/factory_bot/factory_bot_generator.rb b/lib/generators/factory_bot/factory_bot_generator.rb new file mode 100644 index 000000000..8dbb40236 --- /dev/null +++ b/lib/generators/factory_bot/factory_bot_generator.rb @@ -0,0 +1,66 @@ +class Suspenders::FactoryBotGenerator < Rails::Generators::Base + source_root File.expand_path("templates", __dir__) + + def install_gem + gem_group :development, :test do + gem "factory_bot_rails" + end + + Bundler.with_unbundled_env { run "bundle install" } + end + + def remove_fixture_definitions + if default_test_helper_present? + comment_lines "test/test_helper.rb", /fixtures :all/ + end + end + + def include_syntax_methods + if default_test_helper_present? + insert_into_file Rails.root.join("test/test_helper.rb"), after: "class TestCase" do + "\n include FactoryBot::Syntax::Methods" + end + elsif rspec_test_helper_present? + copy_file "factory_bot_rspec.rb", "spec/support/factory_bot.rb" + insert_into_file Rails.root.join("spec/rails_helper.rb") do + %(Dir[Rails.root.join("spec/support/**/*.rb")].sort.each { |file| require file }) + end + end + end + + def create_linting_test + if default_test_suite? + copy_file "factories_test.rb", "test/factory_bots/factories_test.rb" + end + end + + def provide_dev_prime_task + copy_file "dev.rake", "lib/tasks/dev.rake" + end + + def create_definition_file + if default_test_suite? + copy_file "factories.rb", "test/factories.rb" + elsif rspec_test_suite? + copy_file "factories.rb", "spec/factories.rb" + end + end + + private + + def default_test_suite? + File.exist? Rails.root.join("test") + end + + def rspec_test_suite? + File.exist? Rails.root.join("spec") + end + + def default_test_helper_present? + File.exist? Rails.root.join("test/test_helper.rb") + end + + def rspec_test_helper_present? + File.exist? Rails.root.join("spec/rails_helper.rb") + end +end diff --git a/lib/generators/factory_bot/templates/dev.rake b/lib/generators/factory_bot/templates/dev.rake new file mode 100644 index 000000000..7a94175f1 --- /dev/null +++ b/lib/generators/factory_bot/templates/dev.rake @@ -0,0 +1,12 @@ +if Rails.env.development? || Rails.env.test? + require "factory_bot" + + namespace :dev do + desc "Sample data for local development environment" + task prime: "db:setup" do + include FactoryBot::Syntax::Methods + + # create(:user, email: "user@example.com", password: "password") + end + end +end diff --git a/lib/generators/factory_bot/templates/factories.rb b/lib/generators/factory_bot/templates/factories.rb new file mode 100644 index 000000000..3bfcbd203 --- /dev/null +++ b/lib/generators/factory_bot/templates/factories.rb @@ -0,0 +1,2 @@ +FactoryBot.define do +end diff --git a/lib/generators/factory_bot/templates/factories_test.rb b/lib/generators/factory_bot/templates/factories_test.rb new file mode 100644 index 000000000..23b78663d --- /dev/null +++ b/lib/generators/factory_bot/templates/factories_test.rb @@ -0,0 +1,9 @@ +require "test_helper" + +class FactoryBotsTest < ActiveSupport::TestCase + class FactoryLintingTest < FactoryBotsTest + test "linting of factories" do + FactoryBot.lint traits: true + end + end +end diff --git a/lib/generators/factory_bot/templates/factory_bot_rspec.rb b/lib/generators/factory_bot/templates/factory_bot_rspec.rb new file mode 100644 index 000000000..4943f172e --- /dev/null +++ b/lib/generators/factory_bot/templates/factory_bot_rspec.rb @@ -0,0 +1,5 @@ +FactoryBot.use_parent_strategy = true + +RSpec.configure do |config| + config.include FactoryBot::Syntax::Methods +end diff --git a/test/lib/generators/factory_bot/factory_bot_generator_test.rb b/test/lib/generators/factory_bot/factory_bot_generator_test.rb new file mode 100644 index 000000000..f623899cc --- /dev/null +++ b/test/lib/generators/factory_bot/factory_bot_generator_test.rb @@ -0,0 +1,211 @@ +require "test_helper" +require "generators/factory_bot/factory_bot_generator" + +class Suspenders::FactoryBotGenerator::DefaultTest < Rails::Generators::TestCase + include Suspenders::TestHelpers + + tests Suspenders::FactoryBotGenerator + destination Rails.root + setup :prepare_destination + teardown :restore_destination + + test "generator runs without errors" do + assert_nothing_raised do + run_generator + end + end + + test "installs gem with Bundler" do + Bundler.stubs(:with_unbundled_env).yields + generator.expects(:run).with("bundle install").once + + capture(:stdout) do + generator.install_gem + end + end + + test "removes fixture definitions" do + File.open(app_root("test/test_helper.rb"), "w") { _1.write test_helper } + + run_generator + + assert_file app_root("test/test_helper.rb") do |file| + assert_match(/# fixtures :all/, file) + end + end + + test "adds gem to Gemfile" do + run_generator + + assert_file app_root("Gemfile") do |file| + assert_match(/group :development, :test do\n gem "factory_bot_rails"\nend/, file) + end + end + + test "includes syntax methods" do + File.open(app_root("test/test_helper.rb"), "w") { _1.write test_helper } + + run_generator + + assert_file app_root("test/test_helper.rb") do |file| + assert_match(/class TestCase\n include FactoryBot::Syntax::Methods/, file) + end + end + + test "creates definition file" do + run_generator + + assert_file app_root("test/factories.rb") do |file| + assert_match definition_file, file + end + end + + test "creates linting test" do + run_generator + + assert_file app_root("test/factory_bots/factories_test.rb") do |file| + assert_match factories_test, file + end + end + + test "creates dev prime task" do + run_generator + + assert_file app_root("lib/tasks/dev.rake") do |file| + assert_match dev_rake, file + end + end + + private + + def prepare_destination + mkdir "test" + touch "Gemfile" + end + + def restore_destination + remove_dir_if_exists "test" + remove_dir_if_exists "spec" + remove_file_if_exists "Gemfile" + remove_dir_if_exists "lib/tasks" + end + + def test_helper + <<-RUBY + ENV["RAILS_ENV"] ||= "test" + require_relative "../config/environment" + require "rails/test_help" + + module ActiveSupport + class TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) + + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + # Add more helper methods to be used by all tests here... + end + end + RUBY + end + + def factories_test + <<~RUBY + require "test_helper" + + class FactoryBotsTest < ActiveSupport::TestCase + class FactoryLintingTest < FactoryBotsTest + test "linting of factories" do + FactoryBot.lint traits: true + end + end + end + RUBY + end + + def definition_file + <<~RUBY + FactoryBot.define do + end + RUBY + end + + def dev_rake + <<~RUBY + if Rails.env.development? || Rails.env.test? + require "factory_bot" + + namespace :dev do + desc "Sample data for local development environment" + task prime: "db:setup" do + include FactoryBot::Syntax::Methods + + # create(:user, email: "user@example.com", password: "password") + end + end + end + RUBY + end +end + +class Suspenders::FactoryBotGenerator::RSpecTest < Rails::Generators::TestCase + include Suspenders::TestHelpers + + tests Suspenders::FactoryBotGenerator + destination Rails.root + setup :prepare_destination + teardown :restore_destination + + test "includes syntax methods" do + touch("spec/rails_helper.rb") + + run_generator + + assert_file app_root("spec/support/factory_bot.rb") do |file| + assert_match factory_bot_config, file + end + assert_file app_root("spec/rails_helper.rb") do |file| + assert_match(/Dir\[Rails\.root\.join\("spec\/support\/\*\*\/\*\.rb"\)\]\.sort\.each { \|file\| require file }/, file) + end + end + + test "creates definition file" do + run_generator + + assert_file app_root("spec/factories.rb") do |file| + assert_match definition_file, file + end + end + + private + + def prepare_destination + mkdir "spec" + touch "Gemfile" + end + + def restore_destination + remove_dir_if_exists "test" + remove_dir_if_exists "spec" + remove_file_if_exists "Gemfile" + remove_dir_if_exists "lib/tasks" + end + + def factory_bot_config + <<~RUBY + FactoryBot.use_parent_strategy = true + + RSpec.configure do |config| + config.include FactoryBot::Syntax::Methods + end + RUBY + end + + def definition_file + <<~RUBY + FactoryBot.define do + end + RUBY + end +end