diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 48976932..ffa565a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,8 +28,10 @@ jobs: ruby-version: ${{ matrix.ruby }} - name: Setup project run: bundle install - - name: Run test - run: bundle exec rake + - name: RSpec specs + run: bundle exec rake spec + - name: Minitest tests + run: bundle exec rake test standard: name: Run standard diff --git a/Gemfile.lock b/Gemfile.lock index a229f840..c1510d57 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -24,12 +24,21 @@ GEM erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) + activejob (7.1.2) + activesupport (= 7.1.2) + globalid (>= 0.3.6) activemodel (7.1.2) activesupport (= 7.1.2) activerecord (7.1.2) activemodel (= 7.1.2) activesupport (= 7.1.2) timeout (>= 0.4.0) + activestorage (7.1.2) + actionpack (= 7.1.2) + activejob (= 7.1.2) + activerecord (= 7.1.2) + activesupport (= 7.1.2) + marcel (~> 1.0) activesupport (7.1.2) base64 bigdecimal @@ -92,6 +101,8 @@ GEM activesupport (>= 5.0.0) ffi (1.15.5) ffi (1.15.5-java) + globalid (1.2.1) + activesupport (>= 6.1) i18n (1.14.5) concurrent-ruby (~> 1.0) io-console (0.7.2) @@ -107,6 +118,7 @@ GEM loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) + marcel (1.0.4) mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2023.0218.1) @@ -228,6 +240,7 @@ PLATFORMS DEPENDENCIES activerecord (>= 5.0.0) + activestorage (>= 5.0.0) appraisal aruba cucumber diff --git a/README.md b/README.md index 6aef3130..a1191086 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,19 @@ using an empty array: config.factory_bot.definition_file_paths = [] ``` +### File Fixture Support + +Factories have access to [ActiveSupport::Testing::FileFixtures#file_fixture][] +helper to read files from tests. + +To disable file fixture support, set `file_fixture_support = false`: + +```rb +config.factory_bot.file_fixture_support = false +``` + +[ActiveSupport::Testing::FileFixtures#file_fixture]: https://api.rubyonrails.org/classes/ActiveSupport/Testing/FileFixtures.html#method-i-file_fixture + ### Generators Including factory\_bot\_rails in the development group of your Gemfile diff --git a/Rakefile b/Rakefile index 1b8ba844..e02b199c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,7 @@ require "bundler/setup" require "cucumber/rake/task" require "rspec/core/rake_task" +require "minitest/test_task" require "standard/rake" Bundler::GemHelper.install_tasks name: "factory_bot_rails" @@ -12,5 +13,7 @@ end RSpec::Core::RakeTask.new(:spec) +Minitest::TestTask.create + desc "Run the test suite and standard" task default: %w[spec cucumber standard] diff --git a/factory_bot_rails.gemspec b/factory_bot_rails.gemspec index d86dae01..3f58232f 100644 --- a/factory_bot_rails.gemspec +++ b/factory_bot_rails.gemspec @@ -24,4 +24,5 @@ Gem::Specification.new do |s| s.add_development_dependency("sqlite3") s.add_development_dependency("activerecord", ">= 5.0.0") + s.add_development_dependency("activestorage", ">= 5.0.0") end diff --git a/lib/factory_bot_rails/file_fixture_support.rb b/lib/factory_bot_rails/file_fixture_support.rb new file mode 100644 index 00000000..465a91a0 --- /dev/null +++ b/lib/factory_bot_rails/file_fixture_support.rb @@ -0,0 +1,9 @@ +module FactoryBotRails + module FileFixtureSupport + def self.included(klass) + klass.cattr_accessor :file_fixture_support + + klass.delegate :file_fixture, to: "self.class.file_fixture_support" + end + end +end diff --git a/lib/factory_bot_rails/railtie.rb b/lib/factory_bot_rails/railtie.rb index 0e7dfdb8..2b122370 100644 --- a/lib/factory_bot_rails/railtie.rb +++ b/lib/factory_bot_rails/railtie.rb @@ -4,6 +4,7 @@ require "factory_bot_rails/generator" require "factory_bot_rails/reloader" require "factory_bot_rails/factory_validator" +require "factory_bot_rails/file_fixture_support" require "rails" module FactoryBotRails @@ -11,6 +12,7 @@ class Railtie < Rails::Railtie config.factory_bot = ActiveSupport::OrderedOptions.new config.factory_bot.definition_file_paths = FactoryBot.definition_file_paths config.factory_bot.validator = FactoryBotRails::FactoryValidator.new + config.factory_bot.file_fixture_support = true initializer "factory_bot.set_fixture_replacement" do Generator.new(config).run @@ -20,6 +22,22 @@ class Railtie < Rails::Railtie FactoryBot.definition_file_paths = definition_file_paths end + config.after_initialize do + if config.factory_bot.file_fixture_support + FactoryBot::SyntaxRunner.include FactoryBotRails::FileFixtureSupport + + ActiveSupport.on_load :active_support_test_case do + setup { FactoryBot::SyntaxRunner.file_fixture_support = self } + end + + if defined?(RSpec) && RSpec.respond_to?(:configure) + RSpec.configure do |config| + config.before { FactoryBot::SyntaxRunner.file_fixture_support = self } + end + end + end + end + config.after_initialize do |app| FactoryBot.find_definitions Reloader.new(app).run diff --git a/spec/factory_bot_rails/factory_spec.rb b/spec/factory_bot_rails/factory_spec.rb new file mode 100644 index 00000000..479ff1f9 --- /dev/null +++ b/spec/factory_bot_rails/factory_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "factory extensions" do + describe "#file_fixture" do + it "delegates to the test harness" do + FactoryBot.define do + factory :upload, class: Struct.new(:filename) do + filename { file_fixture("file.txt") } + end + end + + upload = FactoryBot.build(:upload) + + expect(Pathname(upload.filename)).to eq(file_fixture("file.txt")) + end + + it "uploads an ActiveStorage::Blob" do + FactoryBot.define do + factory :active_storage_blob, class: ActiveStorage::Blob do + filename { pathname.basename } + + transient do + pathname { file_fixture("file.txt") } + end + + after :build do |model, factory| + model.upload factory.pathname.open + end + end + end + + blob = FactoryBot.create(:active_storage_blob) + + expect(blob.filename.to_s).to eq("file.txt") + expect(blob.download).to eq(file_fixture("file.txt").read) + end + end +end diff --git a/spec/fake_app.rb b/spec/fake_app.rb index 958850a1..7f329893 100644 --- a/spec/fake_app.rb +++ b/spec/fake_app.rb @@ -1,5 +1,10 @@ # frozen_string_literal: true +ENV["DATABASE_URL"] = "sqlite3::memory:" + +require "active_record/railtie" +require "active_storage/engine" + module Dummy class Application < Rails::Application config.eager_load = false @@ -8,6 +13,14 @@ class Application < Rails::Application if Rails.gem_version >= Gem::Version.new("7.1") config.active_support.cache_format_version = 7 end + + config.active_storage.service = :local + config.active_storage.service_configurations = { + local: { + root: root.join("tmp/storage"), + service: "Disk" + } + } end end diff --git a/spec/fixtures/files/file.txt b/spec/fixtures/files/file.txt new file mode 100644 index 00000000..e69de29b diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1c77212d..725f2be4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,6 +4,7 @@ require "factory_bot_rails" require "fake_app" +require "rspec/rails" Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) } diff --git a/spec/support/active_record/migrations.rb b/spec/support/active_record/migrations.rb new file mode 100644 index 00000000..17adcd6d --- /dev/null +++ b/spec/support/active_record/migrations.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +ActiveStorage::Engine.root.glob("db/migrate/*.rb").each { |file| require file } + +ActiveSupport.on_load :active_support_test_case do + setup do + CreateActiveStorageTables.migrate :up + end + + teardown do + CreateActiveStorageTables.migrate :down + rescue ActiveRecord::StatementInvalid + # no-op + end +end + +defined?(RSpec) && RSpec.configure do |config| + config.before do + CreateActiveStorageTables.migrate :up + end + + config.after do + CreateActiveStorageTables.migrate :down + rescue ActiveRecord::StatementInvalid + # no-op + end +end diff --git a/spec/support/macros/define_constant.rb b/spec/support/macros/define_constant.rb deleted file mode 100644 index 5184f29f..00000000 --- a/spec/support/macros/define_constant.rb +++ /dev/null @@ -1,67 +0,0 @@ -require "active_record" - -module DefineConstantMacros - def define_class(path, base = Object, &block) - const = stub_const(path, Class.new(base)) - const.class_eval(&block) if block - const - end - - def define_model(name, columns = {}, &) - model = define_class(name, ActiveRecord::Base, &) - create_table(model.table_name) do |table| - columns.each do |column_name, type| - table.column column_name, type - end - end - model - end - - def create_table(table_name, &) - connection = ActiveRecord::Base.connection - - begin - connection.execute("DROP TABLE IF EXISTS #{table_name}") - connection.create_table(table_name, &) - created_tables << table_name - connection - rescue Exception => e # rubocop:disable Lint/RescueException - connection.execute("DROP TABLE IF EXISTS #{table_name}") - raise e - end - end - - def clear_generated_tables - created_tables.each do |table_name| - clear_generated_table(table_name) - end - created_tables.clear - end - - def clear_generated_table(table_name) - ActiveRecord::Base - .connection - .execute("DROP TABLE IF EXISTS #{table_name}") - end - - private - - def created_tables - @created_tables ||= [] - end -end - -RSpec.configure do |config| - config.include DefineConstantMacros - - config.before(:all) do - ActiveRecord::Base.establish_connection( - adapter: "sqlite3", - database: ":memory:" - ) - end - - config.after do - clear_generated_tables - end -end diff --git a/test/factory_bot_rails/factory_test.rb b/test/factory_bot_rails/factory_test.rb new file mode 100644 index 00000000..0c657ef7 --- /dev/null +++ b/test/factory_bot_rails/factory_test.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "test_helper" + +class FactoryBotRails::FactoryTest < ActiveSupport::TestCase + self.file_fixture_path = "test/fixtures/files" + + test "delegates #file_fixture to the test harness" do + FactoryBot.define do + factory :upload, class: Struct.new(:filename) do + filename { file_fixture("file.txt") } + end + end + + upload = FactoryBot.build(:upload) + + assert_equal file_fixture("file.txt"), upload.filename + end + + test "uploads an ActiveStorage::Blob" do + FactoryBot.define do + factory :active_storage_blob, class: ActiveStorage::Blob do + filename { pathname.basename } + + transient do + pathname { file_fixture("file.txt") } + end + + after :build do |model, factory| + model.upload factory.pathname.open + end + end + end + + blob = FactoryBot.create(:active_storage_blob) + + assert_equal "file.txt", blob.filename.to_s + assert_equal file_fixture("file.txt").read, blob.download + end +end diff --git a/test/fixtures/files/file.txt b/test/fixtures/files/file.txt new file mode 100644 index 00000000..e69de29b diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 00000000..b9f45a5b --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require_relative "../spec/fake_app" + +require "rails/test_help" +require "factory_bot_rails" + +Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) }