diff --git a/config/default.yml b/config/default.yml index b81dd8b..0d22e07 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1,5 +1,10 @@ # This is the default configuration file. +Packaging/BundlerSetupInTests: + Description: 'Avoid using `bundler/setup` in your tests.' + Enabled: true + VersionAdded: '0.4' + Packaging/GemspecGit: Description: 'Use pure Ruby alternative instead of `git ls-files`.' Enabled: true diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 58e5f62..f2a8923 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -2,6 +2,7 @@ === Department xref:cops_packaging.adoc[Packaging] +* xref:cops_packaging.adoc#packagingbundlersetupintests[Packaging/BundlerSetupInTests] * xref:cops_packaging.adoc#packaginggemspecgit[Packaging/GemspecGit] * xref:cops_packaging.adoc#packagingrequirerelativehardcodinglib[Packaging/RequireRelativeHardcodingLib] diff --git a/docs/modules/ROOT/pages/cops_packaging.adoc b/docs/modules/ROOT/pages/cops_packaging.adoc index 073ab67..e84ddbc 100644 --- a/docs/modules/ROOT/pages/cops_packaging.adoc +++ b/docs/modules/ROOT/pages/cops_packaging.adoc @@ -1,5 +1,32 @@ = Packaging +== Packaging/BundlerSetupInTests + +|=== +| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged + +| Enabled +| Yes +| No +| 0.4 +| - +|=== + +This cop flags the `require "bundler/setup"` calls if they're +made from inside the tests directory. + +=== Examples + +[source,ruby] +---- +# bad +require "foo" +require "bundler/setup" + +# good +require "foo" +---- + == Packaging/GemspecGit |=== diff --git a/lib/rubocop/cop/packaging/bundler_setup_in_tests.rb b/lib/rubocop/cop/packaging/bundler_setup_in_tests.rb new file mode 100644 index 0000000..3ced57a --- /dev/null +++ b/lib/rubocop/cop/packaging/bundler_setup_in_tests.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require "rubocop/packaging/lib_helper_module" + +module RuboCop # :nodoc: + module Cop # :nodoc: + module Packaging # :nodoc: + # This cop flags the `require "bundler/setup"` calls if they're + # made from inside the tests directory. + # + # @example + # + # # bad + # require "foo" + # require "bundler/setup" + # + # # good + # require "foo" + # + class BundlerSetupInTests < Base + include RuboCop::Packaging::LibHelperModule + + # This is the message that will be displayed when RuboCop::Packaging finds + # an offense of using `require "bundler/setup"` in the tests directory. + MSG = "Avoid using `bundler/setup` in your tests." + + def_node_matcher :bundler_setup?, <<~PATTERN + (send nil? :require + (str #bundler_setup_in_test_dir?)) + PATTERN + + # Extended from the Base class. + # More about the `#on_new_investigation` method can be found here: + # https://github.com/rubocop-hq/rubocop/blob/343f62e4555be0470326f47af219689e21c61a37/lib/rubocop/cop/base.rb + # + # Processing of the AST happens here. + def on_new_investigation + @file_path = processed_source.file_path + @file_directory = File.dirname(@file_path) + end + + # Extended from AST::Traversal. + # More about the `#on_send` method can be found here: + # https://github.com/rubocop-hq/rubocop-ast/blob/08d0f49a47af1e9a30a6d8f67533ba793c843d67/lib/rubocop/ast/traversal.rb#L112 + def on_send(node) + return unless bundler_setup?(node) + + add_offense(node) + end + + # This method is called from inside `#def_node_matcher`. + # It flags an offense if the `require "bundler/setup"` + # call is made from the tests directory. + def bundler_setup_in_test_dir?(str) + str.eql?("bundler/setup") && falls_in_test_dir? + end + + # This method determines if the call is made *from* the tests directory. + def falls_in_test_dir? + %w[spec specs test tests].any? { |dir| File.expand_path(@file_directory).start_with?("#{root_dir}/#{dir}") } + end + end + end + end +end diff --git a/lib/rubocop/cop/packaging_cops.rb b/lib/rubocop/cop/packaging_cops.rb index ba25c36..e00d302 100644 --- a/lib/rubocop/cop/packaging_cops.rb +++ b/lib/rubocop/cop/packaging_cops.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +require_relative "packaging/bundler_setup_in_tests" require_relative "packaging/gemspec_git" require_relative "packaging/require_relative_hardcoding_lib" diff --git a/spec/rubocop/cop/packaging/bundler_setup_in_tests_spec.rb b/spec/rubocop/cop/packaging/bundler_setup_in_tests_spec.rb new file mode 100644 index 0000000..db397b5 --- /dev/null +++ b/spec/rubocop/cop/packaging/bundler_setup_in_tests_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Packaging::BundlerSetupInTests, :config do + let(:message) { RuboCop::Cop::Packaging::BundlerSetupInTests::MSG } + + let(:project_root) { RuboCop::ConfigLoader.project_root } + + context "when `require bundler/setup` is used in specs/" do + let(:filename) { "#{project_root}/spec/spec_helper.rb" } + let(:source) { "require 'bundler/setup'" } + + it "registers an offense" do + expect_offense(<<~RUBY, filename) + #{source} + #{"^" * source.length} #{message} + RUBY + end + end + + context "when `require bundler/setup` is used in test/foo" do + let(:filename) { "#{project_root}/tests/foo/test_bar.rb" } + let(:source) { "require 'bundler/setup'" } + + it "registers an offense" do + expect_offense(<<~RUBY, filename) + #{source} + #{"^" * source.length} #{message} + RUBY + end + end + + context "when `require bundler/setup` is used in a Rakefile" do + let(:filename) { "#{project_root}/Rakefile" } + let(:source) { "require 'bundler/setup'" } + + it "does not register an offense" do + expect_no_offenses(<<~RUBY, filename) + #{source} + RUBY + end + end + + context "when `require bundler/setup` is used in bin/console" do + let(:filename) { "#{project_root}/bin/console" } + let(:source) { "require 'bundler/setup'" } + + it "does not register an offense" do + expect_no_offenses(<<~RUBY, filename) + #{source} + RUBY + end + end +end