From cb91c12852d6b7cc1770e140eb8d541379742544 Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Tue, 9 Jul 2024 16:06:53 +0300 Subject: [PATCH] Add :on_rails hook to rspec --- Gemfile | 1 + Rakefile | 38 ++++++++++++++++--- spec/rspec_settings.rb | 7 ++++ spec/support/rails_manager.rb | 47 ++++++++++++++++++++++++ spec/support_specs/rails_manager_spec.rb | 47 ++++++++++++++++++++++++ spec/uber_task/internal/path_spec.rb | 9 +---- spec/uber_task/logger_spec.rb | 8 ++++ 7 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 spec/support/rails_manager.rb create mode 100644 spec/support_specs/rails_manager_spec.rb diff --git a/Gemfile b/Gemfile index 07e5742..560cc00 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,7 @@ group :development do end group :test do + gem 'rails', '~> 7.1.1' gem 'rspec', '~> 3.2' gem 'simplecov', '~> 0.21' end diff --git a/Rakefile b/Rakefile index 4f957ff..19279ee 100644 --- a/Rakefile +++ b/Rakefile @@ -1,10 +1,38 @@ # frozen_string_literal: true require 'bundler/gem_tasks' -require 'rubocop/rake_task' -require 'rspec/core/rake_task' -RSpec::Core::RakeTask.new(:spec) -RuboCop::RakeTask.new +task default: %i[rubocop spec] -task default: %i[spec rubocop] +desc 'Run RuboCop' +task :rubocop do + require 'rubocop/rake_task' + + RuboCop::RakeTask.new +end + +desc 'Run tests' +task :spec do + require 'rspec/core/rake_task' + + RSpec::Core::RakeTask.new(:spec) +end + +RAILS_DUMMY_DIR = 'tmp/test/dummy/' + +namespace :rails do + desc 'Generate dummy rails application' + task :generate_dummy_app, [:dir] do |_t, args| + dummy_app_dir = args.fetch(:dir, RAILS_DUMMY_DIR) + exit(0) if Dir.exist?(dummy_app_dir) + + system( + "rails new #{dummy_app_dir} " \ + '--skip-git --skip-asset-pipeline --skip-action-cable ' \ + '--skip-action-mailer --skip-action-mailbox --skip-action-text ' \ + '--skip-active-record --skip-active-job --skip-active-storage ' \ + '--skip-javascript --skip-hotwire --skip-jbuilder ' \ + '--skip-test --skip-system-test --skip-bootsnap ', + ) + end +end diff --git a/spec/rspec_settings.rb b/spec/rspec_settings.rb index 031370b..64ad968 100644 --- a/spec/rspec_settings.rb +++ b/spec/rspec_settings.rb @@ -3,13 +3,20 @@ require 'simplecov' SimpleCov.start do add_filter '/spec/' + add_filter %r{^/tmp/} end require 'uber_task' require 'colorize' +require_relative 'support/rails_manager' + RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = :expect end + + config.around(:example, :on_rails) do |example| + RailsManager.new(config).on_rails { example.run } + end end diff --git a/spec/support/rails_manager.rb b/spec/support/rails_manager.rb new file mode 100644 index 0000000..5ce8eae --- /dev/null +++ b/spec/support/rails_manager.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'rake' + +class RailsManager + APP_PATH = '../../tmp/test/dummy/' + + attr_reader :config, :app_path + + def initialize(config, app_path: APP_PATH) + @config = config + @app_path = app_path + end + + def on_rails + load_rails! + yield + unload_rails! + end + + private + + def load_rails! + return if defined?(Rails) + + if config.instance_variable_defined?(:@rails) + Object.const_set(:Rails, config.instance_variable_get(:@rails)) + return + end + + absolute_app_path = File.expand_path(app_path, __dir__) + system("rake rails:generate_dummy_app[#{absolute_app_path}]") + + require File.expand_path( + File.join(absolute_app_path, '/config/environment.rb'), + __dir__, + ) + + ENV['RAILS_ROOT'] = absolute_app_path + config.instance_variable_set(:@rails, Object.const_get(:Rails)) + end + + def unload_rails! + ENV.delete('RAILS_ROOT') + Object.send(:remove_const, :Rails) + end +end diff --git a/spec/support_specs/rails_manager_spec.rb b/spec/support_specs/rails_manager_spec.rb new file mode 100644 index 0000000..7f6907e --- /dev/null +++ b/spec/support_specs/rails_manager_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'tmpdir' + +describe RailsManager do + let(:manager) { described_class.new(config, app_path: app_path) } + + let(:config) { double } + let(:app_path) { File.join(tmp_dir, 'test/dummy') } + + after do + FileUtils.remove_entry(tmp_dir) + end + + describe '#on_rails', skip: 'Conflicts with manager in rspec_settings' do + it 'runs isolated code as if on Rails application' do + ### 1st run + # + expect(Dir.exist?(app_path)).to eq false + expect(defined?(Rails)).to be_nil + expect(config.instance_variable_defined?(:@rails)).to eq false + + manager.on_rails do + expect(defined?(Rails)).not_to be_nil + end + + expect(Dir.exist?(app_path)).to eq true + expect(defined?(Rails)).to be_nil + expect(config.instance_variable_defined?(:@rails)).to eq true + + ### 2nd run + # + manager.on_rails do + expect(defined?(Rails)).not_to be_nil + expect(Rails).to eq config.instance_variable_get(:@rails) + end + + expect(Dir.exist?(app_path)).to eq true + expect(defined?(Rails)).to be_nil + expect(config.instance_variable_defined?(:@rails)).to eq true + end + end + + def tmp_dir + @tmp_dir ||= Dir.mktmpdir + end +end diff --git a/spec/uber_task/internal/path_spec.rb b/spec/uber_task/internal/path_spec.rb index dd95db7..fbbbe0c 100644 --- a/spec/uber_task/internal/path_spec.rb +++ b/spec/uber_task/internal/path_spec.rb @@ -25,20 +25,15 @@ end end - context 'in rails' do - let(:rails_root) { Pathname.new(Dir.pwd) } + context 'on rails', :on_rails do let(:paths) do [ - rails_root.join('abc').to_s, + Rails.root.join('abc').to_s, '/foo/bar/baz/gems/bar', '/foo/bar/rubygems/baz', ] end - before do - stub_const('Rails', double(root: rails_root)) - end - it 'shortens paths' do shortened = paths.map do |path| described_class.shorten(path) diff --git a/spec/uber_task/logger_spec.rb b/spec/uber_task/logger_spec.rb index 14f87dc..17c76b4 100644 --- a/spec/uber_task/logger_spec.rb +++ b/spec/uber_task/logger_spec.rb @@ -21,6 +21,14 @@ end.to output(/some message/).to_stdout end end + + context 'when Rails logger is defined', :on_rails do + it 'uses Rails logger by default' do + expect(Rails.logger).to receive(:info).with('some message') + + described_class.logger.info('some message') + end + end end describe '.logger=' do