diff --git a/.circleci/gemspecs/compatible b/.circleci/gemspecs/compatible index a589134..e5a5999 100644 --- a/.circleci/gemspecs/compatible +++ b/.circleci/gemspecs/compatible @@ -17,6 +17,7 @@ Gem::Specification.new do |spec| spec.require_paths = %w[lib] spec.add_runtime_dependency 'colorize', '>= 0.8.1' + spec.add_runtime_dependency 'rake', '~> 13.2', '>= 13.2.1' spec.add_runtime_dependency 'rspec-core', '~> 3.10' spec.add_runtime_dependency 'rspec-mocks', '~> 3.10' spec.add_runtime_dependency 'terminal-table', '~> 3.0' diff --git a/.circleci/gemspecs/latest b/.circleci/gemspecs/latest index c90a7a9..effb688 100644 --- a/.circleci/gemspecs/latest +++ b/.circleci/gemspecs/latest @@ -17,6 +17,7 @@ Gem::Specification.new do |spec| spec.require_paths = %w[lib] spec.add_runtime_dependency 'colorize', '>= 0.8.1' + spec.add_runtime_dependency 'rake', '~> 13.2', '>= 13.2.1' spec.add_runtime_dependency 'rspec-core', '~> 3.10' spec.add_runtime_dependency 'rspec-mocks', '~> 3.10' spec.add_runtime_dependency 'terminal-table', '~> 3.0' diff --git a/CHANGELOG.md b/CHANGELOG.md index 16eabc0..0f30953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,17 +2,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.0] - 2024-11-09 + +### Added + +- Added rake task to analyze Flexmock usage and track migration progress to RSpec mocks + +### Removed + +- Removed CLI + ## [0.3.1] - 2024-11-08 ### Fixed -- Fixed CLI broken import. +- Fixed CLI broken import ## [0.3.0] - 2024-11-08 ### Added -- Added CLI to analyze Flexmock usage and track migration progress to RSpec mocks. +- Added CLI to analyze Flexmock usage and track migration progress to RSpec mocks ## [0.2.0] - 2024-11-04 @@ -25,4 +35,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added -- First release of `RSpec::Mock`. +- First release of `RSpec::Mock` diff --git a/README.md b/README.md index 6ce6a06..9a760cb 100644 --- a/README.md +++ b/README.md @@ -130,25 +130,18 @@ end ### Migration Analytics -You can create a Rake task to analyze Flexmock usage and track migration progress to RSpec mocks. Or use the CLI directly. +This time implemented migration analytics for [Flexmock](https://github.com/doudou/flexmock) only. You can run Rake task to analyze Flexmock usage and track migration progress to RSpec mocks. -Example of the Rake task: +For non-Rails applications to use the task, you need to load it: ```ruby -namespace :rspec_mock do - namespace :migration_analytics do - desc 'Analyze Flexmock usage and track migration progress to RSpec mocks' - task :flexmock do - require 'rspec/mock/migration_analytics/cli' - - path = ::ARGV[1] || 'spec' - puts("\nšŸ” Analyzing Flexmock usage in: #{path}") - RSpec::Mock::MigrationAnalytics::Cli.verify_path(path) - end - end -end +require 'rspec/mock/task' + +RSpec::Mock::Task.load ``` +For Rails applications it will be automatically loaded, so just run: + ```bash # Analyze entire spec directory (default) rake rspec_mock:migration_analytics:flexmock @@ -160,14 +153,6 @@ rake rspec_mock:migration_analytics:flexmock spec/services rake rspec_mock:migration_analytics:flexmock spec/services/sandbox_service_spec.rb ``` -Example of the CLI usage: - -```bash -ruby cli.rb spec -ruby cli.rb spec/services -ruby cli.rb spec/services/sandbox_service_spec.rb -``` - ## Contributing Bug reports and pull requests are welcome on GitHub at . This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. Please check the [open tickets](https://github.com/mocktools/ruby-rspec-mock/issues). Be sure to follow Contributor Code of Conduct below and our [Contributing Guidelines](CONTRIBUTING.md). diff --git a/lib/rspec/mock/core.rb b/lib/rspec/mock/core.rb index 59f0084..06fde4c 100644 --- a/lib/rspec/mock/core.rb +++ b/lib/rspec/mock/core.rb @@ -13,13 +13,15 @@ module Tracker end require_relative 'migration_analytics/file_analyzer' - require_relative 'migration_analytics/cli' + require_relative 'migration_analytics/printer' end require_relative 'configuration' require_relative 'context' require_relative 'methods' require_relative 'version' + + require_relative(defined?(::Rails) ? 'railtie' : 'task') end module Core diff --git a/lib/rspec/mock/migration_analytics/cli.rb b/lib/rspec/mock/migration_analytics/printer.rb old mode 100755 new mode 100644 similarity index 86% rename from lib/rspec/mock/migration_analytics/cli.rb rename to lib/rspec/mock/migration_analytics/printer.rb index 3e634a3..cae9ec7 --- a/lib/rspec/mock/migration_analytics/cli.rb +++ b/lib/rspec/mock/migration_analytics/printer.rb @@ -1,4 +1,3 @@ -#!/usr/bin/env ruby # frozen_string_literal: true require 'colorize' @@ -12,39 +11,16 @@ module RSpec module Mock module MigrationAnalytics - class Cli + class Printer class << self - def call - if ::ARGV.empty? - print_usage - exit 1 - end - - begin - verify_path(::ARGV[0]) - rescue => error - puts("\nāŒ Error: #{error.message}".red) - puts(error.backtrace) if ENV['DEBUG'] - end - end + def call(path) + return verify_directory(path) if ::File.directory?(path) - def verify_path(path) - case - when ::File.directory?(path) then verify_directory(path) - else verify_file(path) - end + verify_file(path) end private - def print_usage - puts('Usage: ruby cli.rb '.yellow) - puts("\nExamples:".blue) - puts(' ruby cli.rb spec/models/user_spec.rb') - puts(' ruby cli.rb spec/models/') - puts(' ruby cli.rb spec/') - end - def verify_directory(dir_path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength results = [] stats = { @@ -188,5 +164,3 @@ def create_file_row(result) end end end - -RSpec::Mock::MigrationAnalytics::Cli.call if __FILE__.eql?($PROGRAM_NAME) diff --git a/lib/rspec/mock/railtie.rb b/lib/rspec/mock/railtie.rb new file mode 100644 index 0000000..1a7ba5f --- /dev/null +++ b/lib/rspec/mock/railtie.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module RSpec + module Mock + class Railtie < ::Rails::Railtie + rake_tasks do + load(::File.join(::File.dirname(__FILE__), 'tasks', 'rspec_mock.rake')) + end + end + end +end diff --git a/lib/rspec/mock/task.rb b/lib/rspec/mock/task.rb new file mode 100644 index 0000000..9e78e4d --- /dev/null +++ b/lib/rspec/mock/task.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'rake' + +module RSpec + module Mock + class Task + def self.load + ::Kernel.load(::File.join(::File.dirname(__FILE__), 'tasks', 'rspec_mock.rake')) + end + end + end +end diff --git a/lib/rspec/mock/tasks/rspec_mock.rake b/lib/rspec/mock/tasks/rspec_mock.rake new file mode 100644 index 0000000..8112d38 --- /dev/null +++ b/lib/rspec/mock/tasks/rspec_mock.rake @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +namespace :rspec_mock do + namespace :migration_analytics do + desc 'Analyze Flexmock usage and track migration progress to RSpec mocks' + # :nocov: + task :flexmock do + require 'rspec/mock/migration_analytics/printer' + + path = ::ARGV[1] || 'spec' + puts("\nšŸ” Analyzing Flexmock usage in: #{path}") + RSpec::Mock::MigrationAnalytics::Printer.call(path) + end + # :nocov: + end +end diff --git a/lib/rspec/mock/version.rb b/lib/rspec/mock/version.rb index 2e311ba..aeef602 100644 --- a/lib/rspec/mock/version.rb +++ b/lib/rspec/mock/version.rb @@ -2,6 +2,6 @@ module RSpec module Mock - VERSION = '0.3.1' + VERSION = '0.4.0' end end diff --git a/rspec-mock.gemspec b/rspec-mock.gemspec index c97c220..fab4248 100644 --- a/rspec-mock.gemspec +++ b/rspec-mock.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |spec| spec.require_paths = %w[lib] spec.add_runtime_dependency 'colorize', '>= 0.8.1' + spec.add_runtime_dependency 'rake', '~> 13.2', '>= 13.2.1' spec.add_runtime_dependency 'rspec-core', '~> 3.10' spec.add_runtime_dependency 'rspec-mocks', '~> 3.10' spec.add_runtime_dependency 'terminal-table', '~> 3.0' diff --git a/spec/rspec/mock/migration_analytics/cli_spec.rb b/spec/rspec/mock/migration_analytics/printer_spec.rb similarity index 61% rename from spec/rspec/mock/migration_analytics/cli_spec.rb rename to spec/rspec/mock/migration_analytics/printer_spec.rb index 2a5b24b..993b906 100644 --- a/spec/rspec/mock/migration_analytics/cli_spec.rb +++ b/spec/rspec/mock/migration_analytics/printer_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe RSpec::Mock::MigrationAnalytics::Cli do +RSpec.describe RSpec::Mock::MigrationAnalytics::Printer do let(:sample_file_path) { 'spec/models/user_spec.rb' } let(:sample_dir_path) { 'spec/models' } @@ -12,69 +12,21 @@ end describe '.call' do - context 'when no arguments are provided' do - before { stub_const('ARGV', []) } - - it 'prints usage and exits with status 1' do - expect(described_class).to receive(:print_usage) - expect { described_class.call }.to raise_error(SystemExit) do |error| - expect(error.status).to eq(1) - end - end - end - - context 'when path is provided' do - before { stub_const('ARGV', [sample_file_path]) } - - it 'verifies the provided path' do - expect(described_class).to receive(:verify_path).with(sample_file_path) - described_class.call - end - - context 'when an error occurs' do - let(:error_message) { 'Something went wrong' } - - before do - allow(described_class).to receive(:verify_path).and_raise(StandardError.new(error_message)) - allow(ENV).to receive(:[]).and_return(nil) - allow(ENV).to receive(:[]).with('COLUMNS').and_return('80') - end - - it 'prints the error message in red' do - expect { described_class.call } - .to output(/āŒ Error: #{error_message}/).to_stdout - end - - context 'when DEBUG env is set' do - before do - allow(ENV).to receive(:[]).with('DEBUG').and_return('true') - end - - it 'prints the backtrace' do - expect { described_class.call } - .to output(%r{#{error_message}.*gems/rspec}m).to_stdout - end - end - end - end - end - - describe '.verify_path' do context 'when path is a directory' do - before { allow(File).to receive(:directory?).with(sample_dir_path).and_return(true) } + before { allow(::File).to receive(:directory?).with(sample_dir_path).and_return(true) } it 'calls verify_directory' do expect(described_class).to receive(:verify_directory).with(sample_dir_path) - described_class.verify_path(sample_dir_path) + described_class.call(sample_dir_path) end end context 'when path is a file' do - before { allow(File).to receive(:directory?).with(sample_file_path).and_return(false) } + before { allow(::File).to receive(:directory?).with(sample_file_path).and_return(false) } it 'calls verify_file' do expect(described_class).to receive(:verify_file).with(sample_file_path) - described_class.verify_path(sample_file_path) + described_class.call(sample_file_path) end end end @@ -83,7 +35,7 @@ include_context 'with stubbed ENV' context 'when file does not exist' do - before { allow(File).to receive(:exist?).with(sample_file_path).and_return(false) } + before { allow(::File).to receive(:exist?).with(sample_file_path).and_return(false) } it 'prints error message' do expect { described_class.send(:verify_file, sample_file_path) } @@ -94,7 +46,7 @@ context 'when file is not a spec file' do let(:non_spec_file) { 'app/models/user.rb' } - before { allow(File).to receive(:exist?).with(non_spec_file).and_return(true) } + before { allow(::File).to receive(:exist?).with(non_spec_file).and_return(true) } it 'prints warning message' do expect { described_class.send(:verify_file, non_spec_file) } @@ -115,7 +67,7 @@ end before do - allow(File).to receive(:exist?).with(sample_file_path).and_return(true) + allow(::File).to receive(:exist?).with(sample_file_path).and_return(true) allow(RSpec::Mock::MigrationAnalytics::FileAnalyzer).to receive(:call) .with(sample_file_path).and_return(analysis_result) end @@ -140,7 +92,7 @@ end before do - allow(File).to receive(:exist?).with(sample_file_path).and_return(true) + allow(::File).to receive(:exist?).with(sample_file_path).and_return(true) allow(RSpec::Mock::MigrationAnalytics::FileAnalyzer).to receive(:call) .with(sample_file_path).and_return(analysis_result) end @@ -180,14 +132,16 @@ end before do - allow(Dir).to receive(:glob).with("#{sample_dir_path}/**/*_spec.rb").and_return(spec_files) - allow(RSpec::Mock::MigrationAnalytics::FileAnalyzer).to receive(:call) + allow(::Dir).to receive(:glob).with("#{sample_dir_path}/**/*_spec.rb").and_return(spec_files) + allow(RSpec::Mock::MigrationAnalytics::FileAnalyzer) + .to receive(:call) .and_return(*analysis_results) end it 'analyzes all spec files and prints summary' do expect { described_class.send(:verify_directory, sample_dir_path) } - .to output(/=== Migration Status Report ===.*Files Requiring Migration/m).to_stdout + .to output(/=== Migration Status Report ===.*Files Requiring Migration/m) + .to_stdout end end @@ -215,23 +169,4 @@ end end end - - describe '.print_usage' do - include_context 'with stubbed ENV' - - it 'prints usage instructions with examples' do - expected_lines = [ - 'Usage: ruby cli.rb ', - '', - 'Examples:', - ' ruby cli.rb spec/models/user_spec.rb', - ' ruby cli.rb spec/models/', - ' ruby cli.rb spec/' - ] - - expect { described_class.send(:print_usage) } - .to output(/#{expected_lines.join('.*')}/m) - .to_stdout - end - end end diff --git a/spec/rspec/mock/task_spec.rb b/spec/rspec/mock/task_spec.rb new file mode 100644 index 0000000..430df64 --- /dev/null +++ b/spec/rspec/mock/task_spec.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +RSpec.describe RSpec::Mock::Task do + it 'loads the rake task' do + described_class.load + expect(::Rake::Task.task_defined?('rspec_mock:migration_analytics:flexmock')).to be(true) + end +end diff --git a/spec/rspec/mock/tasks/rspec_mock_rake_spec.rb b/spec/rspec/mock/tasks/rspec_mock_rake_spec.rb new file mode 100644 index 0000000..11c9704 --- /dev/null +++ b/spec/rspec/mock/tasks/rspec_mock_rake_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +RSpec.describe 'RSpec::Mock rake task' do # rubocop:disable RSpec/DescribeClass + include Rake::DSL + + let(:printer_class) { RSpec::Mock::MigrationAnalytics::Printer } + + before do + Rake::Task.clear + RSpec::Mock::Task.load + end + + describe 'rspec_mock:migration_analytics:flexmock' do + let(:task) { Rake::Task['rspec_mock:migration_analytics:flexmock'] } + + it { expect(task).to be_instance_of(Rake::Task) } + + context 'when executing the task' do + context 'with default path' do + before { stub_const('ARGV', ['rspec_mock:migration_analytics:flexmock']) } + + it 'calls Printer with default path' do + expect(printer_class).to receive(:call).with('spec') + task.execute + end + end + + context 'with custom path argument' do + before { stub_const('ARGV', ['rspec_mock:migration_analytics:flexmock', 'custom/path']) } + + it 'calls Printer with custom path' do + expect(printer_class).to receive(:call).with('custom/path') + task.execute + end + end + end + end +end