Merge pull request #8373 from Homebrew/dependabot/bundler/Library/Homebrew/rubocop-rspec-1.43.1

build(deps): bump rubocop-rspec from 1.42.0 to 1.43.1 in /Library/Homebrew
This commit is contained in:
Mike McQuaid 2020-08-19 11:34:24 +01:00 committed by GitHub
commit 97b57393cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
118 changed files with 819 additions and 520 deletions

View File

@ -43,3 +43,5 @@ RSpec/MultipleExpectations:
Max: 26 Max: 26
RSpec/NestedGroups: RSpec/NestedGroups:
Max: 5 Max: 5
RSpec/MultipleMemoizedHelpers:
Max: 12

View File

@ -112,8 +112,8 @@ GEM
parser (>= 2.7.1.4) parser (>= 2.7.1.4)
rubocop-performance (1.7.1) rubocop-performance (1.7.1)
rubocop (>= 0.82.0) rubocop (>= 0.82.0)
rubocop-rspec (1.42.0) rubocop-rspec (1.43.1)
rubocop (>= 0.87.0) rubocop (~> 0.87)
ruby-macho (2.2.0) ruby-macho (2.2.0)
ruby-progressbar (1.10.1) ruby-progressbar (1.10.1)
simplecov (0.19.0) simplecov (0.19.0)

View File

@ -51,7 +51,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel-1.19.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.1.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.1.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parser-2.7.1.4/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parser-2.7.1.4/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rainbow-3.0.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rainbow-3.0.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.5866/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.5869/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parlour-4.0.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parlour-4.0.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/patchelf-1.2.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/patchelf-1.2.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/plist-3.5.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/plist-3.5.0/lib"
@ -74,9 +74,9 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.10
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-1.7.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-1.7.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-0.88.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-0.88.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.7.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.7.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.42.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.43.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.2.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.2.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.5866-universal-darwin-19/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.5869-universal-darwin-19/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.5866/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.5869/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.0.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.0.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tapioca-0.4.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tapioca-0.4.1/lib"

View File

@ -1,72 +0,0 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Check that the first argument to the top level describe is a constant.
#
# @example
# # bad
# describe 'Do something' do
# end
#
# # good
# describe TestedClass do
# subject { described_class }
# end
#
# describe 'TestedClass::VERSION' do
# subject { Object.const_get(self.class.description) }
# end
#
# describe "A feature example", type: :feature do
# end
class DescribeClass < Cop
include RuboCop::RSpec::TopLevelDescribe
MSG = 'The first argument to describe should be '\
'the class or module being tested.'
def_node_matcher :valid_describe?, <<-PATTERN
{
(send #{RSPEC} :describe const ...)
(send #{RSPEC} :describe)
}
PATTERN
def_node_matcher :describe_with_rails_metadata?, <<-PATTERN
(send #{RSPEC} :describe !const ...
(hash <#rails_metadata? ...>)
)
PATTERN
def_node_matcher :rails_metadata?, <<-PATTERN
(pair
(sym :type)
(sym {
:channel :controller :helper :job :mailer :model :request
:routing :view :feature :system :mailbox
}
)
)
PATTERN
def on_top_level_describe(node, (described_value, _))
return if shared_group?(root_node)
return if valid_describe?(node)
return if describe_with_rails_metadata?(node)
return if string_constant_describe?(described_value)
add_offense(described_value)
end
private
def string_constant_describe?(described_value)
described_value.str_type? &&
described_value.value =~ /^((::)?[A-Z]\w*)+$/
end
end
end
end
end

View File

@ -1,90 +0,0 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks if an example group does not include any tests.
#
# This cop is configurable using the `CustomIncludeMethods` option
#
# @example usage
#
# # bad
# describe Bacon do
# let(:bacon) { Bacon.new(chunkiness) }
# let(:chunkiness) { false }
#
# context 'extra chunky' do # flagged by rubocop
# let(:chunkiness) { true }
# end
#
# it 'is chunky' do
# expect(bacon.chunky?).to be_truthy
# end
# end
#
# # good
# describe Bacon do
# let(:bacon) { Bacon.new(chunkiness) }
# let(:chunkiness) { false }
#
# it 'is chunky' do
# expect(bacon.chunky?).to be_truthy
# end
# end
#
# @example configuration
#
# # .rubocop.yml
# # RSpec/EmptyExampleGroup:
# # CustomIncludeMethods:
# # - include_tests
#
# # spec_helper.rb
# RSpec.configure do |config|
# config.alias_it_behaves_like_to(:include_tests)
# end
#
# # bacon_spec.rb
# describe Bacon do
# let(:bacon) { Bacon.new(chunkiness) }
# let(:chunkiness) { false }
#
# context 'extra chunky' do # not flagged by rubocop
# let(:chunkiness) { true }
#
# include_tests 'shared tests'
# end
# end
#
class EmptyExampleGroup < Cop
MSG = 'Empty example group detected.'
def_node_search :contains_example?, <<-PATTERN
{
#{(Examples::ALL + Includes::ALL).send_pattern}
(send _ #custom_include? ...)
}
PATTERN
def on_block(node)
return unless example_group?(node) && !contains_example?(node)
add_offense(node.send_node)
end
private
def custom_include?(method_name)
custom_include_methods.include?(method_name)
end
def custom_include_methods
cop_config
.fetch('CustomIncludeMethods', [])
.map(&:to_sym)
end
end
end
end
end

View File

@ -1,47 +0,0 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks that memoized helper names use the configured style.
#
# @example EnforcedStyle: snake_case (default)
# # bad
# let(:userName) { 'Adam' }
# subject(:userName) { 'Adam' }
#
# # good
# let(:user_name) { 'Adam' }
# subject(:user_name) { 'Adam' }
#
# @example EnforcedStyle: camelCase
# # bad
# let(:user_name) { 'Adam' }
# subject(:user_name) { 'Adam' }
#
# # good
# let(:userName) { 'Adam' }
# subject(:userName) { 'Adam' }
class VariableName < Cop
include ConfigurableNaming
include RuboCop::RSpec::Variable
MSG = 'Use %<style>s for variable names.'
def on_send(node)
variable_definition?(node) do |variable|
return if variable.dstr_type? || variable.dsym_type?
check_name(node, variable.value, variable.loc.expression)
end
end
private
def message(style)
format(MSG, style: style)
end
end
end
end
end

View File

@ -1,44 +0,0 @@
# frozen_string_literal: true
module RuboCop
module RSpec
# Helper methods for top level example group cops
module TopLevelGroup
extend RuboCop::NodePattern::Macros
include RuboCop::RSpec::Language
def_node_matcher :example_or_shared_group?,
(ExampleGroups::ALL + SharedGroups::ALL).block_pattern
def on_block(node)
return unless respond_to?(:on_top_level_group)
return unless top_level_group?(node)
on_top_level_group(node)
end
private
def top_level_group?(node)
top_level_groups.include?(node)
end
def top_level_groups
@top_level_groups ||=
top_level_nodes.select { |n| example_or_shared_group?(n) }
end
def top_level_nodes
if root_node.begin_type?
root_node.children
else
[root_node]
end
end
def root_node
processed_source.ast
end
end
end
end

View File

@ -74,7 +74,7 @@ RSpec/ContextWording:
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContextWording StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContextWording
RSpec/DescribeClass: RSpec/DescribeClass:
Description: Check that the first argument to the top level describe is a constant. Description: Check that the first argument to the top-level describe is a constant.
Enabled: true Enabled: true
VersionAdded: '1.0' VersionAdded: '1.0'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass
@ -381,7 +381,7 @@ RSpec/MissingExampleGroupArgument:
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument
RSpec/MultipleDescribes: RSpec/MultipleDescribes:
Description: Checks for multiple top level describes. Description: Checks for multiple top-level example groups.
Enabled: true Enabled: true
VersionAdded: '1.0' VersionAdded: '1.0'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleDescribes StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleDescribes
@ -394,6 +394,14 @@ RSpec/MultipleExpectations:
VersionChanged: '1.21' VersionChanged: '1.21'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleExpectations StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleExpectations
RSpec/MultipleMemoizedHelpers:
Description: Checks if example groups contain too many `let` and `subject` calls.
Enabled: true
AllowSubject: true
Max: 5
VersionAdded: '1.43'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleMemoizedHelpers
RSpec/MultipleSubjects: RSpec/MultipleSubjects:
Description: Checks if an example group defines `subject` multiple times. Description: Checks if an example group defines `subject` multiple times.
Enabled: true Enabled: true
@ -558,7 +566,9 @@ RSpec/VariableName:
SupportedStyles: SupportedStyles:
- snake_case - snake_case
- camelCase - camelCase
IgnoredPatterns: []
VersionAdded: '1.40' VersionAdded: '1.40'
VersionChanged: '1.43'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName
RSpec/VerifiedDoubles: RSpec/VerifiedDoubles:

View File

@ -19,11 +19,12 @@ require_relative 'rubocop/rspec/example_group'
require_relative 'rubocop/rspec/example' require_relative 'rubocop/rspec/example'
require_relative 'rubocop/rspec/hook' require_relative 'rubocop/rspec/hook'
require_relative 'rubocop/rspec/variable' require_relative 'rubocop/rspec/variable'
require_relative 'rubocop/cop/rspec/base'
require_relative 'rubocop/cop/rspec/cop' require_relative 'rubocop/cop/rspec/cop'
require_relative 'rubocop/rspec/align_let_brace' require_relative 'rubocop/rspec/align_let_brace'
require_relative 'rubocop/rspec/factory_bot' require_relative 'rubocop/rspec/factory_bot'
require_relative 'rubocop/rspec/final_end_location' require_relative 'rubocop/rspec/final_end_location'
require_relative 'rubocop/rspec/blank_line_separation' require_relative 'rubocop/rspec/empty_line_separation'
require_relative 'rubocop/rspec/corrector/move_node' require_relative 'rubocop/rspec/corrector/move_node'
RuboCop::RSpec::Inject.defaults! RuboCop::RSpec::Inject.defaults!

View File

@ -17,7 +17,7 @@ module RuboCop
# let(:baz) { bar } # let(:baz) { bar }
# let(:a) { b } # let(:a) { b }
# #
class AlignLeftLetBrace < Cop class AlignLeftLetBrace < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Align left let brace' MSG = 'Align left let brace'

View File

@ -17,7 +17,7 @@ module RuboCop
# let(:baz) { bar } # let(:baz) { bar }
# let(:a) { b } # let(:a) { b }
# #
class AlignRightLetBrace < Cop class AlignRightLetBrace < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Align right let brace' MSG = 'Align right let brace'

View File

@ -22,7 +22,7 @@ module RuboCop
# allow(my_instance).to receive(:foo) # allow(my_instance).to receive(:foo)
# end # end
# end # end
class AnyInstance < Cop class AnyInstance < Base
MSG = 'Avoid stubbing using `%<method>s`.' MSG = 'Avoid stubbing using `%<method>s`.'
def_node_matcher :disallowed_stub, <<-PATTERN def_node_matcher :disallowed_stub, <<-PATTERN

View File

@ -25,7 +25,7 @@ module RuboCop
# some_method # some_method
# test.run # test.run
# end # end
class AroundBlock < Cop class AroundBlock < Base
MSG_NO_ARG = 'Test object should be passed to around block.' MSG_NO_ARG = 'Test object should be passed to around block.'
MSG_UNUSED_ARG = 'You should call `%<arg>s.call` '\ MSG_UNUSED_ARG = 'You should call `%<arg>s.call` '\
'or `%<arg>s.run`.' 'or `%<arg>s.run`.'

View File

@ -17,7 +17,7 @@ module RuboCop
# # Patterns: # # Patterns:
# # - '_test.rb$' # # - '_test.rb$'
# # - '(?:^|/)test/' # # - '(?:^|/)test/'
class Cop < ::RuboCop::Cop::Base class Base < ::RuboCop::Cop::Base
include RuboCop::RSpec::Language include RuboCop::RSpec::Language
include RuboCop::RSpec::Language::NodePattern include RuboCop::RSpec::Language::NodePattern
@ -30,8 +30,8 @@ module RuboCop
) )
# Invoke the original inherited hook so our cops are recognized # Invoke the original inherited hook so our cops are recognized
def self.inherited(subclass) def self.inherited(subclass) # rubocop:disable Lint/MissingSuper
RuboCop::Cop::Cop.inherited(subclass) RuboCop::Cop::Base.inherited(subclass)
end end
def relevant_file?(file) def relevant_file?(file)
@ -41,7 +41,7 @@ module RuboCop
private private
def relevant_rubocop_rspec_file?(file) def relevant_rubocop_rspec_file?(file)
rspec_pattern =~ file rspec_pattern.match?(file)
end end
def rspec_pattern def rspec_pattern

View File

@ -19,7 +19,7 @@ module RuboCop
# expect(foo).to be 1.0 # expect(foo).to be 1.0
# expect(foo).to be(true) # expect(foo).to be(true)
# #
class Be < Cop class Be < Base
MSG = 'Don\'t use `be` without an argument.' MSG = 'Don\'t use `be` without an argument.'
def_node_matcher :be_without_args, <<-PATTERN def_node_matcher :be_without_args, <<-PATTERN

View File

@ -35,7 +35,7 @@ module RuboCop
# necessarily the same type as `b` since the `#==` operator can # necessarily the same type as `b` since the `#==` operator can
# coerce objects for comparison. # coerce objects for comparison.
# #
class BeEql < Cop class BeEql < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Prefer `be` over `eql`.' MSG = 'Prefer `be` over `eql`.'

View File

@ -23,7 +23,7 @@ module RuboCop
# before(:each) { Widget.create } # before(:each) { Widget.create }
# after(:each) { Widget.delete_all } # after(:each) { Widget.delete_all }
# end # end
class BeforeAfterAll < Cop class BeforeAfterAll < Base
MSG = 'Beware of using `%<hook>s` as it may cause state to leak '\ MSG = 'Beware of using `%<hook>s` as it may cause state to leak '\
'between tests. If you are using `rspec-rails`, and '\ 'between tests. If you are using `rspec-rails`, and '\
'`use_transactional_fixtures` is enabled, then records created '\ '`use_transactional_fixtures` is enabled, then records created '\

View File

@ -23,7 +23,7 @@ module RuboCop
# expect(page).to have_current_path("/callback") # expect(page).to have_current_path("/callback")
# expect(page).to have_current_path(/widgets/) # expect(page).to have_current_path(/widgets/)
# #
class CurrentPathExpectation < Cop class CurrentPathExpectation < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Do not set an RSpec expectation on `current_path` in ' \ MSG = 'Do not set an RSpec expectation on `current_path` in ' \

View File

@ -40,7 +40,7 @@ module RuboCop
# # ... # # ...
# end # end
# end # end
class FeatureMethods < Cop class FeatureMethods < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Use `%<replacement>s` instead of `%<method>s`.' MSG = 'Use `%<replacement>s` instead of `%<method>s`.'
@ -55,15 +55,18 @@ module RuboCop
feature: :describe feature: :describe
}.freeze }.freeze
def_node_matcher :capybara_speak,
SelectorSet.new(MAP.keys).node_pattern_union
def_node_matcher :spec?, <<-PATTERN def_node_matcher :spec?, <<-PATTERN
(block (block
(send #{RSPEC} {:describe :feature} ...) (send #rspec? {:describe :feature} ...)
...) ...)
PATTERN PATTERN
def_node_matcher :feature_method, <<-PATTERN def_node_matcher :feature_method, <<-PATTERN
(block (block
$(send #{RSPEC} ${#{MAP.keys.map(&:inspect).join(' ')}} ...) $(send #rspec? $#capybara_speak ...)
...) ...)
PATTERN PATTERN

View File

@ -26,7 +26,7 @@ module RuboCop
# expect(page).to have_css('.foo', visible: :all) # expect(page).to have_css('.foo', visible: :all)
# expect(page).to have_link('my link', visible: :hidden) # expect(page).to have_link('my link', visible: :hidden)
# #
class VisibilityMatcher < Cop class VisibilityMatcher < Base
MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.' MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.'
MSG_TRUE = 'Use `:visible` instead of `true`.' MSG_TRUE = 'Use `:visible` instead of `true`.'
CAPYBARA_MATCHER_METHODS = %i[ CAPYBARA_MATCHER_METHODS = %i[

View File

@ -23,13 +23,13 @@ module RuboCop
# describe '.foo_bar' do # describe '.foo_bar' do
# # ... # # ...
# end # end
class ContextMethod < Cop class ContextMethod < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Use `describe` for testing methods.' MSG = 'Use `describe` for testing methods.'
def_node_matcher :context_method, <<-PATTERN def_node_matcher :context_method, <<-PATTERN
(block (send #{RSPEC} :context $(str #method_name?) ...) ...) (block (send #rspec? :context $(str #method_name?) ...) ...)
PATTERN PATTERN
def on_block(node) def on_block(node)

View File

@ -34,11 +34,11 @@ module RuboCop
# context 'when the display name is not present' do # context 'when the display name is not present' do
# # ... # # ...
# end # end
class ContextWording < Cop class ContextWording < Base
MSG = 'Start context description with %<prefixes>s.' MSG = 'Start context description with %<prefixes>s.'
def_node_matcher :context_wording, <<-PATTERN def_node_matcher :context_wording, <<-PATTERN
(block (send #{RSPEC} { :context :shared_context } $(str #bad_prefix?) ...) ...) (block (send #rspec? { :context :shared_context } $(str #bad_prefix?) ...) ...)
PATTERN PATTERN
def on_block(node) def on_block(node)
@ -51,7 +51,7 @@ module RuboCop
private private
def bad_prefix?(description) def bad_prefix?(description)
!prefixes.include?(description.split.first) !prefixes.include?(description.split(/\b/).first)
end end
def joined_prefixes def joined_prefixes

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# @deprecated Use ::RuboCop::Cop::RSpec::Base instead
Cop = Base
end
end
end

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Check that the first argument to the top-level describe is a constant.
#
# @example
# # bad
# describe 'Do something' do
# end
#
# # good
# describe TestedClass do
# subject { described_class }
# end
#
# describe 'TestedClass::VERSION' do
# subject { Object.const_get(self.class.description) }
# end
#
# describe "A feature example", type: :feature do
# end
class DescribeClass < Base
include RuboCop::RSpec::TopLevelGroup
MSG = 'The first argument to describe should be '\
'the class or module being tested.'
def_node_matcher :rails_metadata?, <<-PATTERN
(pair
(sym :type)
(sym { :channel :controller :helper :job :mailer :model :request
:routing :view :feature :system :mailbox })
)
PATTERN
def_node_matcher :example_group_with_rails_metadata?, <<~PATTERN
(send #rspec? :describe ... (hash <#rails_metadata? ...>))
PATTERN
def_node_matcher :not_a_const_described, <<~PATTERN
(send #rspec? :describe $[!const !#string_constant?] ...)
PATTERN
def on_top_level_group(top_level_node)
return if example_group_with_rails_metadata?(top_level_node.send_node)
not_a_const_described(top_level_node.send_node) do |described|
add_offense(described)
end
end
private
def string_constant?(described)
described.str_type? &&
described.value.match?(/^(?:(?:::)?[A-Z]\w*)+$/)
end
end
end
end
end

View File

@ -16,17 +16,25 @@ module RuboCop
# #
# describe MyClass, '.my_class_method' do # describe MyClass, '.my_class_method' do
# end # end
class DescribeMethod < Cop class DescribeMethod < Base
include RuboCop::RSpec::TopLevelDescribe include RuboCop::RSpec::TopLevelGroup
MSG = 'The second argument to describe should be the method '\ MSG = 'The second argument to describe should be the method '\
"being tested. '#instance' or '.class'." "being tested. '#instance' or '.class'."
def on_top_level_describe(_node, (_, second_arg)) def_node_matcher :second_argument, <<~PATTERN
return unless second_arg&.str_type? (block
return if second_arg.str_content.start_with?('#', '.') (send #rspec? :describe _first_argument $(str _) ...) ...
)
PATTERN
add_offense(second_arg) def on_top_level_group(node)
second_argument = second_argument(node)
return unless second_argument
return if second_argument.str_content.start_with?('#', '.')
add_offense(second_argument)
end end
end end
end end

View File

@ -17,11 +17,11 @@ module RuboCop
# end # end
# #
# @see https://github.com/rspec/rspec-core/issues/1610 # @see https://github.com/rspec/rspec-core/issues/1610
class DescribeSymbol < Cop class DescribeSymbol < Base
MSG = 'Avoid describing symbols.' MSG = 'Avoid describing symbols.'
def_node_matcher :describe_symbol?, <<-PATTERN def_node_matcher :describe_symbol?, <<-PATTERN
(send #{RSPEC} :describe $sym ...) (send #rspec? :describe $sym ...)
PATTERN PATTERN
def on_send(node) def on_send(node)

View File

@ -54,7 +54,7 @@ module RuboCop
# end # end
# end # end
# #
class DescribedClass < Cop class DescribedClass < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
@ -142,7 +142,7 @@ module RuboCop
if style == :described_class if style == :described_class
offensive_described_class?(node) offensive_described_class?(node)
else else
node.send_type? && node.method_name == :described_class node.send_type? && node.method?(:described_class)
end end
end end

View File

@ -19,7 +19,7 @@ module RuboCop
# end # end
# #
# @see https://github.com/rubocop-hq/rubocop-rspec/issues/735 # @see https://github.com/rubocop-hq/rubocop-rspec/issues/735
class DescribedClassModuleWrapping < Cop class DescribedClassModuleWrapping < Base
MSG = 'Avoid opening modules and defining specs within them.' MSG = 'Avoid opening modules and defining specs within them.'
def_node_search :find_rspec_blocks, def_node_search :find_rspec_blocks,

View File

@ -41,7 +41,7 @@ module RuboCop
# describe 'display name presence' do # describe 'display name presence' do
# # ... # # ...
# end # end
class Dialect < Cop class Dialect < Base
extend AutoCorrector extend AutoCorrector
include MethodPreference include MethodPreference

View File

@ -0,0 +1,174 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks if an example group does not include any tests.
#
# This cop is configurable using the `CustomIncludeMethods` option
#
# @example usage
#
# # bad
# describe Bacon do
# let(:bacon) { Bacon.new(chunkiness) }
# let(:chunkiness) { false }
#
# context 'extra chunky' do # flagged by rubocop
# let(:chunkiness) { true }
# end
#
# it 'is chunky' do
# expect(bacon.chunky?).to be_truthy
# end
# end
#
# # good
# describe Bacon do
# let(:bacon) { Bacon.new(chunkiness) }
# let(:chunkiness) { false }
#
# it 'is chunky' do
# expect(bacon.chunky?).to be_truthy
# end
# end
#
# @example configuration
#
# # .rubocop.yml
# # RSpec/EmptyExampleGroup:
# # CustomIncludeMethods:
# # - include_tests
#
# # spec_helper.rb
# RSpec.configure do |config|
# config.alias_it_behaves_like_to(:include_tests)
# end
#
# # bacon_spec.rb
# describe Bacon do
# let(:bacon) { Bacon.new(chunkiness) }
# let(:chunkiness) { false }
#
# context 'extra chunky' do # not flagged by rubocop
# let(:chunkiness) { true }
#
# include_tests 'shared tests'
# end
# end
#
class EmptyExampleGroup < Base
MSG = 'Empty example group detected.'
# @!method example_group_body(node)
# Match example group blocks and yield their body
#
# @example source that matches
# describe 'example group' do
# it { is_expected.to be }
# end
#
# @param node [RuboCop::AST::Node]
# @yield [RuboCop::AST::Node] example group body
def_node_matcher :example_group_body, <<~PATTERN
(block #{ExampleGroups::ALL.send_pattern} args $_)
PATTERN
# @!method example_or_group_or_include?(node)
# Match examples, example groups and includes
#
# @example source that matches
# it { is_expected.to fly }
# describe('non-empty example groups too') { }
# it_behaves_like 'an animal'
# it_behaves_like('a cat') { let(:food) { 'milk' } }
# it_has_root_access
#
# @param node [RuboCop::AST::Node]
# @return [Array<RuboCop::AST::Node>] matching nodes
def_node_matcher :example_or_group_or_include?, <<~PATTERN
{
#{Examples::ALL.block_pattern}
#{ExampleGroups::ALL.block_pattern}
#{Includes::ALL.send_pattern}
#{Includes::ALL.block_pattern}
(send nil? #custom_include? ...)
}
PATTERN
# @!method examples_inside_block?(node)
# Match examples defined inside a block which is not a hook
#
# @example source that matches
# %w(r g b).each do |color|
# it { is_expected.to have_color(color) }
# end
#
# @example source that does not match
# before do
# it { is_expected.to fall_into_oblivion }
# end
#
# @param node [RuboCop::AST::Node]
# @return [Array<RuboCop::AST::Node>] matching nodes
def_node_matcher :examples_inside_block?, <<~PATTERN
(block !#{Hooks::ALL.send_pattern} _ #examples?)
PATTERN
# @!method examples_directly_or_in_block?(node)
# Match examples or examples inside blocks
#
# @example source that matches
# it { expect(drink).to be_cold }
# context('when winter') { it { expect(drink).to be_hot } }
# (1..5).each { |divisor| it { is_expected.to divide_by(divisor) } }
#
# @param node [RuboCop::AST::Node]
# @return [Array<RuboCop::AST::Node>] matching nodes
def_node_matcher :examples_directly_or_in_block?, <<~PATTERN
{
#example_or_group_or_include?
#examples_inside_block?
}
PATTERN
# @!method examples?(node)
# Matches examples defined in scopes where they could run
#
# @example source that matches
# it { expect(myself).to be_run }
# describe { it { i_run_as_well } }
#
# @example source that does not match
# before { it { whatever here wont run anyway } }
#
# @param node [RuboCop::AST::Node]
# @return [Array<RuboCop::AST::Node>] matching nodes
def_node_matcher :examples?, <<~PATTERN
{
#examples_directly_or_in_block?
(begin <#examples_directly_or_in_block? ...>)
}
PATTERN
def on_block(node)
example_group_body(node) do |body|
add_offense(node.send_node) unless examples?(body)
end
end
private
def custom_include?(method_name)
custom_include_methods.include?(method_name)
end
def custom_include_methods
cop_config
.fetch('CustomIncludeMethods', [])
.map(&:to_sym)
end
end
end
end
end

View File

@ -22,7 +22,7 @@ module RuboCop
# create_feed # create_feed
# end # end
# after(:all) { cleanup_feed } # after(:all) { cleanup_feed }
class EmptyHook < Cop class EmptyHook < Base
extend AutoCorrector extend AutoCorrector
include RuboCop::Cop::RangeHelp include RuboCop::Cop::RangeHelp

View File

@ -41,22 +41,18 @@ module RuboCop
# it { two } # it { two }
# end # end
# #
class EmptyLineAfterExample < Cop class EmptyLineAfterExample < Base
extend AutoCorrector extend AutoCorrector
include RuboCop::RSpec::BlankLineSeparation include RuboCop::RSpec::EmptyLineSeparation
MSG = 'Add an empty line after `%<example>s`.' MSG = 'Add an empty line after `%<example>s`.'
def on_block(node) def on_block(node)
return unless example?(node) return unless example?(node)
return if last_child?(node)
return if allowed_one_liner?(node) return if allowed_one_liner?(node)
missing_separating_line(node) do |location| missing_separating_line_offense(node) do |method|
msg = format(MSG, example: node.method_name) format(MSG, example: method)
add_offense(location, message: msg) do |corrector|
corrector.insert_after(location.end, "\n")
end
end end
end end

View File

@ -23,21 +23,17 @@ module RuboCop
# end # end
# end # end
# #
class EmptyLineAfterExampleGroup < Cop class EmptyLineAfterExampleGroup < Base
extend AutoCorrector extend AutoCorrector
include RuboCop::RSpec::BlankLineSeparation include RuboCop::RSpec::EmptyLineSeparation
MSG = 'Add an empty line after `%<example_group>s`.' MSG = 'Add an empty line after `%<example_group>s`.'
def on_block(node) def on_block(node)
return unless example_group?(node) return unless example_group?(node)
return if last_child?(node)
missing_separating_line(node) do |location| missing_separating_line_offense(node) do |method|
msg = format(MSG, example_group: node.method_name) format(MSG, example_group: method)
add_offense(location, message: msg) do |corrector|
corrector.insert_after(location.end, "\n")
end
end end
end end
end end

View File

@ -16,24 +16,21 @@ module RuboCop
# let(:something) { other } # let(:something) { other }
# #
# it { does_something } # it { does_something }
class EmptyLineAfterFinalLet < Cop class EmptyLineAfterFinalLet < Base
extend AutoCorrector extend AutoCorrector
include RuboCop::RSpec::BlankLineSeparation include RuboCop::RSpec::EmptyLineSeparation
MSG = 'Add an empty line after the last `let` block.' MSG = 'Add an empty line after the last `%<let>s`.'
def on_block(node) def on_block(node)
return unless example_group_with_body?(node) return unless example_group_with_body?(node)
latest_let = node.body.child_nodes.select { |child| let?(child) }.last final_let = node.body.child_nodes.reverse.find { |child| let?(child) }
return if latest_let.nil? return if final_let.nil?
return if last_child?(latest_let)
missing_separating_line(latest_let) do |location| missing_separating_line_offense(final_let) do |method|
add_offense(location) do |corrector| format(MSG, let: method)
corrector.insert_after(location.end, "\n")
end
end end
end end
end end

View File

@ -33,21 +33,17 @@ module RuboCop
# #
# it { does_something } # it { does_something }
# #
class EmptyLineAfterHook < Cop class EmptyLineAfterHook < Base
extend AutoCorrector extend AutoCorrector
include RuboCop::RSpec::BlankLineSeparation include RuboCop::RSpec::EmptyLineSeparation
MSG = 'Add an empty line after `%<hook>s`.' MSG = 'Add an empty line after `%<hook>s`.'
def on_block(node) def on_block(node)
return unless hook?(node) return unless hook?(node)
return if last_child?(node)
missing_separating_line(node) do |location| missing_separating_line_offense(node) do |method|
msg = format(MSG, hook: node.method_name) format(MSG, hook: method)
add_offense(location, message: msg) do |corrector|
corrector.insert_after(location.end, "\n")
end
end end
end end
end end

View File

@ -14,20 +14,17 @@ module RuboCop
# subject(:obj) { described_class } # subject(:obj) { described_class }
# #
# let(:foo) { bar } # let(:foo) { bar }
class EmptyLineAfterSubject < Cop class EmptyLineAfterSubject < Base
extend AutoCorrector extend AutoCorrector
include RuboCop::RSpec::BlankLineSeparation include RuboCop::RSpec::EmptyLineSeparation
MSG = 'Add empty line after `subject`.' MSG = 'Add an empty line after `%<subject>s`.'
def on_block(node) def on_block(node)
return unless subject?(node) && !in_spec_block?(node) return unless subject?(node) && !in_spec_block?(node)
return if last_child?(node)
missing_separating_line(node) do |location| missing_separating_line_offense(node) do |method|
add_offense(location) do |corrector| format(MSG, subject: method)
corrector.insert_after(location.end, "\n")
end
end end
end end

View File

@ -25,7 +25,7 @@ module RuboCop
# result = service.call # result = service.call
# expect(result).to be(true) # expect(result).to be(true)
# end # end
class ExampleLength < Cop class ExampleLength < Base
include CodeLength include CodeLength
MSG = 'Example has too many lines [%<total>d/%<max>d].' MSG = 'Example has too many lines [%<total>d/%<max>d].'

View File

@ -47,7 +47,7 @@ module RuboCop
# result = service.call # result = service.call
# expect(result).to be(true) # expect(result).to be(true)
# end # end
class ExampleWithoutDescription < Cop class ExampleWithoutDescription < Base
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
MSG_DEFAULT_ARGUMENT = 'Omit the argument when you want to ' \ MSG_DEFAULT_ARGUMENT = 'Omit the argument when you want to ' \

View File

@ -29,7 +29,7 @@ module RuboCop
# # good # # good
# it 'does things' do # it 'does things' do
# end # end
class ExampleWording < Cop class ExampleWording < Base
extend AutoCorrector extend AutoCorrector
MSG_SHOULD = 'Do not use should when describing your tests.' MSG_SHOULD = 'Do not use should when describing your tests.'
@ -47,9 +47,9 @@ module RuboCop
def on_block(node) def on_block(node)
it_description(node) do |description_node, message| it_description(node) do |description_node, message|
if message =~ SHOULD_PREFIX if message.match?(SHOULD_PREFIX)
add_wording_offense(description_node, MSG_SHOULD) add_wording_offense(description_node, MSG_SHOULD)
elsif message =~ IT_PREFIX elsif message.match?(IT_PREFIX)
add_wording_offense(description_node, MSG_IT) add_wording_offense(description_node, MSG_IT)
end end
end end
@ -77,7 +77,7 @@ module RuboCop
def replacement_text(node) def replacement_text(node)
text = text(node) text = text(node)
if text =~ SHOULD_PREFIX if text.match?(SHOULD_PREFIX)
RuboCop::RSpec::Wording.new( RuboCop::RSpec::Wording.new(
text, text,
ignore: ignored_words, ignore: ignored_words,

View File

@ -16,7 +16,7 @@ module RuboCop
# expect(pattern).to eq(/foo/) # expect(pattern).to eq(/foo/)
# expect(name).to eq("John") # expect(name).to eq("John")
# #
class ExpectActual < Cop class ExpectActual < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Provide the actual you are testing to `expect(...)`.' MSG = 'Provide the actual you are testing to `expect(...)`.'

View File

@ -29,7 +29,7 @@ module RuboCop
# expect { run }.to change { Foo.bar(:count) } # expect { run }.to change { Foo.bar(:count) }
# expect { run }.to change { user.reload.name } # expect { run }.to change { user.reload.name }
# #
class ExpectChange < Cop class ExpectChange < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle

View File

@ -20,7 +20,7 @@ module RuboCop
# it do # it do
# expect(something).to eq 'foo' # expect(something).to eq 'foo'
# end # end
class ExpectInHook < Cop class ExpectInHook < Base
MSG = 'Do not use `%<expect>s` in `%<hook>s` hook' MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'
def_node_search :expectation, Expectations::ALL.send_pattern def_node_search :expectation, Expectations::ALL.send_pattern

View File

@ -14,7 +14,7 @@ module RuboCop
# #
# # good # # good
# expect { my_app.print_report }.to output('Hello World').to_stdout # expect { my_app.print_report }.to output('Hello World').to_stdout
class ExpectOutput < Cop class ExpectOutput < Base
MSG = 'Use `expect { ... }.to output(...).to_%<name>s` '\ MSG = 'Use `expect { ... }.to output(...).to_%<name>s` '\
'instead of mutating $%<name>s.' 'instead of mutating $%<name>s.'

View File

@ -24,7 +24,7 @@ module RuboCop
# #
# # good # # good
# count { 1 } # count { 1 }
class AttributeDefinedStatically < Cop class AttributeDefinedStatically < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Use a block to declare attribute values.' MSG = 'Use a block to declare attribute values.'
@ -39,7 +39,7 @@ module RuboCop
def on_block(node) def on_block(node)
attributes = factory_attributes(node) || [] attributes = factory_attributes(node) || []
attributes = [attributes] unless attributes.is_a?(Array) attributes = [attributes] unless attributes.is_a?(Array) # rubocop:disable Style/ArrayCoercion, Lint/RedundantCopDisableDirective
attributes.each do |attribute| attributes.each do |attribute|
next unless offensive_receiver?(attribute.receiver, node) next unless offensive_receiver?(attribute.receiver, node)
@ -84,7 +84,7 @@ module RuboCop
def autocorrect_replacing_parens(corrector, node) def autocorrect_replacing_parens(corrector, node)
left_braces, right_braces = braces(node) left_braces, right_braces = braces(node)
corrector.replace(node.location.begin, ' ' + left_braces) corrector.replace(node.location.begin, " #{left_braces}")
corrector.replace(node.location.end, right_braces) corrector.replace(node.location.end, right_braces)
end end

View File

@ -24,7 +24,7 @@ module RuboCop
# #
# # good # # good
# 3.times { create :user } # 3.times { create :user }
class CreateList < Cop class CreateList < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
@ -44,7 +44,7 @@ module RuboCop
PATTERN PATTERN
def_node_matcher :factory_list_call, <<-PATTERN def_node_matcher :factory_list_call, <<-PATTERN
(send ${(const nil? {:FactoryGirl :FactoryBot}) nil?} :create_list (sym $_) (int $_) $...) (send {(const nil? {:FactoryGirl :FactoryBot}) nil?} :create_list (sym _) (int $_) ...)
PATTERN PATTERN
def on_block(node) def on_block(node)
@ -60,7 +60,7 @@ module RuboCop
def on_send(node) def on_send(node)
return unless style == :n_times return unless style == :n_times
factory_list_call(node) do |_receiver, _factory, count, _| factory_list_call(node) do |count|
message = format(MSG_N_TIMES, number: count) message = format(MSG_N_TIMES, number: count)
add_offense(node.loc.selector, message: message) do |corrector| add_offense(node.loc.selector, message: message) do |corrector|
TimesCorrector.new(node).call(corrector) TimesCorrector.new(node).call(corrector)
@ -79,7 +79,7 @@ module RuboCop
end end
# :nodoc # :nodoc
class Corrector module Corrector
private private
def build_options_string(options) def build_options_string(options)
@ -102,7 +102,9 @@ module RuboCop
end end
# :nodoc # :nodoc
class TimesCorrector < Corrector class TimesCorrector
include Corrector
def initialize(node) def initialize(node)
@node = node @node = node
end end
@ -130,7 +132,9 @@ module RuboCop
end end
# :nodoc: # :nodoc:
class CreateListCorrector < Corrector class CreateListCorrector
include Corrector
def initialize(node) def initialize(node)
@node = node.parent @node = node.parent
end end

View File

@ -19,7 +19,7 @@ module RuboCop
# # good # # good
# factory :foo, class: 'Foo' do # factory :foo, class: 'Foo' do
# end # end
class FactoryClassName < Cop class FactoryClassName < Base
extend AutoCorrector extend AutoCorrector
MSG = "Pass '%<class_name>s' string instead of `%<class_name>s` " \ MSG = "Pass '%<class_name>s' string instead of `%<class_name>s` " \

View File

@ -56,35 +56,43 @@ module RuboCop
# # good # # good
# my_class_spec.rb # describe MyClass, '#method' # my_class_spec.rb # describe MyClass, '#method'
# #
class FilePath < Cop class FilePath < Base
include RuboCop::RSpec::TopLevelDescribe include RuboCop::RSpec::TopLevelGroup
MSG = 'Spec path should end with `%<suffix>s`.' MSG = 'Spec path should end with `%<suffix>s`.'
def_node_search :const_described?, '(send _ :describe (const ...) ...)' def_node_matcher :const_described, <<~PATTERN
(block
$(send #rspec? _example_group $(const ...) $...) ...
)
PATTERN
def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))' def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))'
def on_top_level_describe(node, args) def on_top_level_group(node)
return unless const_described?(node) && single_top_level_describe? return unless top_level_groups.one?
return if routing_spec?(args)
glob = glob_for(args) const_described(node) do |send_node, described_class, arguments|
next if routing_spec?(arguments)
return if filename_ends_with?(glob) ensure_correct_file_path(send_node, described_class, arguments)
end
add_offense(
node,
message: format(MSG, suffix: glob)
)
end end
private private
def ensure_correct_file_path(send_node, described_class, arguments)
glob = glob_for(described_class, arguments.first)
return if filename_ends_with?(glob)
add_offense(send_node, message: format(MSG, suffix: glob))
end
def routing_spec?(args) def routing_spec?(args)
args.any?(&method(:routing_metadata?)) args.any?(&method(:routing_metadata?))
end end
def glob_for((described_class, method_name)) def glob_for(described_class, method_name)
return glob_for_spec_suffix_only? if spec_suffix_only? return glob_for_spec_suffix_only? if spec_suffix_only?
"#{expected_path(described_class)}#{name_glob(method_name)}*_spec.rb" "#{expected_path(described_class)}#{name_glob(method_name)}*_spec.rb"
@ -94,10 +102,10 @@ module RuboCop
'*_spec.rb' '*_spec.rb'
end end
def name_glob(name) def name_glob(method_name)
return unless name&.str_type? return unless method_name&.str_type?
"*#{name.str_content.gsub(/\W/, '')}" unless ignore_methods? "*#{method_name.str_content.gsub(/\W/, '')}" unless ignore_methods?
end end
def expected_path(constant) def expected_path(constant)

View File

@ -19,23 +19,19 @@ module RuboCop
# # good # # good
# describe MyClass do # describe MyClass do
# end # end
class Focus < Cop class Focus < Base
MSG = 'Focused spec found.' MSG = 'Focused spec found.'
focusable =
ExampleGroups::GROUPS +
ExampleGroups::SKIPPED +
Examples::EXAMPLES +
Examples::SKIPPED +
Examples::PENDING
focused = ExampleGroups::FOCUSED + Examples::FOCUSED focused = ExampleGroups::FOCUSED + Examples::FOCUSED
FOCUSABLE_SELECTORS = focusable.node_pattern_union def_node_matcher :focusable_selector?,
(ExampleGroups::GROUPS + ExampleGroups::SKIPPED +
Examples::EXAMPLES + Examples::SKIPPED +
Examples::PENDING).node_pattern_union
def_node_matcher :metadata, <<-PATTERN def_node_matcher :metadata, <<-PATTERN
{(send #{RSPEC} #{FOCUSABLE_SELECTORS} <$(sym :focus) ...>) {(send #rspec? #focusable_selector? <$(sym :focus) ...>)
(send #{RSPEC} #{FOCUSABLE_SELECTORS} ... (hash <$(pair (sym :focus) true) ...>))} (send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
PATTERN PATTERN
def_node_matcher :focused_block?, focused.send_pattern def_node_matcher :focused_block?, focused.send_pattern

View File

@ -57,21 +57,20 @@ module RuboCop
# before(:example) do # before(:example) do
# # ... # # ...
# end # end
class HookArgument < Cop class HookArgument < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
IMPLICIT_MSG = 'Omit the default `%<scope>p` ' \ IMPLICIT_MSG = 'Omit the default `%<scope>p` argument for RSpec hooks.'
'argument for RSpec hooks.'
EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.' EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
HOOKS = Hooks::ALL.node_pattern_union.freeze def_node_matcher :hook?, Hooks::ALL.node_pattern_union
def_node_matcher :scoped_hook, <<-PATTERN def_node_matcher :scoped_hook, <<-PATTERN
(block $(send _ #{HOOKS} (sym ${:each :example})) ...) (block $(send _ #hook? (sym ${:each :example})) ...)
PATTERN PATTERN
def_node_matcher :unscoped_hook, "(block $(send _ #{HOOKS}) ...)" def_node_matcher :unscoped_hook, '(block $(send _ #hook?) ...)'
def on_block(node) def on_block(node)
hook(node) do |method_send, scope_name| hook(node) do |method_send, scope_name|

View File

@ -23,7 +23,7 @@ module RuboCop
# expect(foo).to be # expect(foo).to be
# end # end
# #
class HooksBeforeExamples < Cop class HooksBeforeExamples < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Move `%<hook>s` above the examples in the group.' MSG = 'Move `%<hook>s` above the examples in the group.'

View File

@ -16,7 +16,7 @@ module RuboCop
# it 'changes something to a new value' do # it 'changes something to a new value' do
# expect { do_something }.to change(something).to(new_value) # expect { do_something }.to change(something).to(new_value)
# end # end
class ImplicitBlockExpectation < Cop class ImplicitBlockExpectation < Base
MSG = 'Avoid implicit block expectations.' MSG = 'Avoid implicit block expectations.'
def_node_matcher :lambda?, <<-PATTERN def_node_matcher :lambda?, <<-PATTERN

View File

@ -24,7 +24,7 @@ module RuboCop
# # good # # good
# it { should be_truthy } # it { should be_truthy }
# #
class ImplicitExpect < Cop class ImplicitExpect < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle

View File

@ -26,7 +26,7 @@ module RuboCop
# # good # # good
# it { expect(subject).to be_truthy } # it { expect(subject).to be_truthy }
# #
class ImplicitSubject < Cop class ImplicitSubject < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
@ -49,9 +49,10 @@ module RuboCop
def autocorrect(corrector, node) def autocorrect(corrector, node)
replacement = 'expect(subject)' replacement = 'expect(subject)'
if node.method_name == :should case node.method_name
when :should
replacement += '.to' replacement += '.to'
elsif node.method_name == :should_not when :should_not
replacement += '.not_to' replacement += '.not_to'
end end
@ -62,13 +63,14 @@ module RuboCop
example = node.ancestors.find { |parent| example?(parent) } example = node.ancestors.find { |parent| example?(parent) }
return false if example.nil? return false if example.nil?
example.method_name == :its || allowed_by_style?(example) example.method?(:its) || allowed_by_style?(example)
end end
def allowed_by_style?(example) def allowed_by_style?(example)
if style == :single_line_only case style
when :single_line_only
example.single_line? example.single_line?
elsif style == :single_statement_only when :single_statement_only
!example.body.begin_type? !example.body.begin_type?
else else
false false

View File

@ -18,7 +18,7 @@ module RuboCop
# expect(foo).to have_received(:bar) # expect(foo).to have_received(:bar)
# end # end
# #
class InstanceSpy < Cop class InstanceSpy < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Use `instance_spy` when you check your double '\ MSG = 'Use `instance_spy` when you check your double '\

View File

@ -46,7 +46,7 @@ module RuboCop
# it { expect(foo).to be_empty } # it { expect(foo).to be_empty }
# end # end
# #
class InstanceVariable < Cop class InstanceVariable < Base
include RuboCop::RSpec::TopLevelGroup include RuboCop::RSpec::TopLevelGroup
MSG = 'Avoid instance variables use let, ' \ MSG = 'Avoid instance variables use let, ' \

View File

@ -15,7 +15,7 @@ module RuboCop
# #
# # good # # good
# expect(foo).to be_something # expect(foo).to be_something
class InvalidPredicateMatcher < Cop class InvalidPredicateMatcher < Base
MSG = 'Omit `?` from `%<matcher>s`.' MSG = 'Omit `?` from `%<matcher>s`.'
def_node_matcher :invalid_predicate_matcher?, <<-PATTERN def_node_matcher :invalid_predicate_matcher?, <<-PATTERN

View File

@ -18,7 +18,7 @@ module RuboCop
# #
# # good # # good
# it_should_behave_like 'a foo' # it_should_behave_like 'a foo'
class ItBehavesLike < Cop class ItBehavesLike < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle

View File

@ -15,7 +15,7 @@ module RuboCop
# it 'validates users' do # it 'validates users' do
# expect([user1, user2, user3]).to all(be_valid) # expect([user1, user2, user3]).to all(be_valid)
# end # end
class IteratedExpectation < Cop class IteratedExpectation < Base
MSG = 'Prefer using the `all` matcher instead ' \ MSG = 'Prefer using the `all` matcher instead ' \
'of iterating over an array.' 'of iterating over an array.'

View File

@ -31,7 +31,7 @@ module RuboCop
# it { expect_something } # it { expect_something }
# it { expect_something_else } # it { expect_something_else }
# #
class LeadingSubject < Cop class LeadingSubject < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Declare `subject` above any other `%<offending>s` declarations.' MSG = 'Declare `subject` above any other `%<offending>s` declarations.'
@ -43,33 +43,36 @@ module RuboCop
end end
def check_previous_nodes(node) def check_previous_nodes(node)
node.parent.each_child_node do |sibling| offending_node(node) do |offender|
if offending?(sibling) msg = format(MSG, offending: offender.method_name)
msg = format(MSG, offending: sibling.method_name) add_offense(node, message: msg) do |corrector|
add_offense(node, message: msg) do |corrector| autocorrect(corrector, node, offender)
autocorrect(corrector, node)
end
end end
break if offending?(sibling) || sibling.equal?(node)
end end
end end
private private
def autocorrect(corrector, node) def offending_node(node)
first_node = find_first_offending_node(node) node.parent.each_child_node.find do |sibling|
break if sibling.equal?(node)
yield sibling if offending?(sibling)
end
end
def autocorrect(corrector, node, sibling)
RuboCop::RSpec::Corrector::MoveNode.new( RuboCop::RSpec::Corrector::MoveNode.new(
node, corrector, processed_source node, corrector, processed_source
).move_before(first_node) ).move_before(sibling)
end end
def offending?(node) def offending?(node)
let?(node) || hook?(node) || example?(node) let?(node) ||
end hook?(node) ||
example?(node) ||
def find_first_offending_node(node) spec_group?(node) ||
node.parent.children.find { |sibling| offending?(sibling) } include?(node)
end end
def in_spec_block?(node) def in_spec_block?(node)

View File

@ -93,7 +93,7 @@ module RuboCop
# stub_const('SomeModule::SomeClass', foo_class) # stub_const('SomeModule::SomeClass', foo_class)
# end # end
# end # end
class LeakyConstantDeclaration < Cop class LeakyConstantDeclaration < Base
MSG_CONST = 'Stub constant instead of declaring explicitly.' MSG_CONST = 'Stub constant instead of declaring explicitly.'
MSG_CLASS = 'Stub class constant instead of declaring explicitly.' MSG_CLASS = 'Stub class constant instead of declaring explicitly.'
MSG_MODULE = 'Stub module constant instead of declaring explicitly.' MSG_MODULE = 'Stub module constant instead of declaring explicitly.'

View File

@ -30,7 +30,7 @@ module RuboCop
# it 'checks what some does' do # it 'checks what some does' do
# expect(some).to be # expect(some).to be
# end # end
class LetBeforeExamples < Cop class LetBeforeExamples < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Move `let` before the examples in the group.' MSG = 'Move `let` before the examples in the group.'

View File

@ -25,7 +25,7 @@ module RuboCop
# it 'counts widgets' do # it 'counts widgets' do
# expect(Widget.count).to eq(1) # expect(Widget.count).to eq(1)
# end # end
class LetSetup < Cop class LetSetup < Base
MSG = 'Do not use `let!` to setup objects not referenced in tests.' MSG = 'Do not use `let!` to setup objects not referenced in tests.'
def_node_matcher :example_or_shared_group_or_including?, def_node_matcher :example_or_shared_group_or_including?,
@ -35,7 +35,10 @@ module RuboCop
).block_pattern ).block_pattern
def_node_matcher :let_bang, <<-PATTERN def_node_matcher :let_bang, <<-PATTERN
(block $(send nil? :let! (sym $_)) args ...) {
(block $(send nil? :let! {(sym $_) (str $_)}) ...)
$(send nil? :let! {(sym $_) (str $_)} block_pass)
}
PATTERN PATTERN
def_node_search :method_called?, '(send nil? %)' def_node_search :method_called?, '(send nil? %)'
@ -52,7 +55,7 @@ module RuboCop
def unused_let_bang(node) def unused_let_bang(node)
child_let_bang(node) do |method_send, method_name| child_let_bang(node) do |method_send, method_name|
yield(method_send) unless method_called?(node, method_name) yield(method_send) unless method_called?(node, method_name.to_sym)
end end
end end

View File

@ -13,7 +13,7 @@ module RuboCop
# thing = Thing.new(baz: 42) # thing = Thing.new(baz: 42)
# allow(foo).to receive(:bar).and_return(thing) # allow(foo).to receive(:bar).and_return(thing)
# #
class MessageChain < Cop class MessageChain < Base
MSG = 'Avoid stubbing using `%<method>s`.' MSG = 'Avoid stubbing using `%<method>s`.'
def_node_matcher :message_chain, <<-PATTERN def_node_matcher :message_chain, <<-PATTERN

View File

@ -24,7 +24,7 @@ module RuboCop
# # good # # good
# expect(foo).to receive(:bar) # expect(foo).to receive(:bar)
# #
class MessageExpectation < Cop class MessageExpectation < Base
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
MSG = 'Prefer `%<style>s` for setting message expectations.' MSG = 'Prefer `%<style>s` for setting message expectations.'

View File

@ -24,7 +24,7 @@ module RuboCop
# # good # # good
# expect(foo).to receive(:bar) # expect(foo).to receive(:bar)
# #
class MessageSpies < Cop class MessageSpies < Base
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
MSG_RECEIVE = 'Prefer `receive` for setting message expectations.' MSG_RECEIVE = 'Prefer `receive` for setting message expectations.'

View File

@ -19,7 +19,7 @@ module RuboCop
# #
# describe "A feature example" do # describe "A feature example" do
# end # end
class MissingExampleGroupArgument < Cop class MissingExampleGroupArgument < Base
MSG = 'The first argument to `%<method>s` should not be empty.' MSG = 'The first argument to `%<method>s` should not be empty.'
def on_block(node) def on_block(node)

View File

@ -3,7 +3,7 @@
module RuboCop module RuboCop
module Cop module Cop
module RSpec module RSpec
# Checks for multiple top level describes. # Checks for multiple top-level example groups.
# #
# Multiple descriptions for the same class or module should either # Multiple descriptions for the same class or module should either
# be nested or separated into different test files. # be nested or separated into different test files.
@ -22,17 +22,20 @@ module RuboCop
# describe '.do_something_else' do # describe '.do_something_else' do
# end # end
# end # end
class MultipleDescribes < Cop class MultipleDescribes < Base
include RuboCop::RSpec::TopLevelDescribe include RuboCop::RSpec::TopLevelGroup
MSG = 'Do not use multiple top level describes - '\ MSG = 'Do not use multiple top-level example groups - '\
'try to nest them.' 'try to nest them.'
def on_top_level_describe(node, _args) def on_top_level_group(node)
return if single_top_level_describe? top_level_example_groups =
return unless top_level_nodes.first.equal?(node) top_level_groups.select(&method(:example_group?))
add_offense(node) return if top_level_example_groups.one?
return unless top_level_example_groups.first.equal?(node)
add_offense(node.send_node)
end end
end end
end end

View File

@ -45,22 +45,18 @@ module RuboCop
# end # end
# end # end
# #
class MultipleExpectations < Cop class MultipleExpectations < Base
include ConfigurableMax include ConfigurableMax
MSG = 'Example has too many expectations [%<total>d/%<max>d].' MSG = 'Example has too many expectations [%<total>d/%<max>d].'
ANYTHING = ->(_node) { true }
TRUE = ->(node) { node.true_type? }
def_node_matcher :aggregate_failures?, <<-PATTERN def_node_matcher :aggregate_failures?, <<-PATTERN
(block { (block {
(send _ _ <(sym :aggregate_failures) ...>) (send _ _ <(sym :aggregate_failures) ...>)
(send _ _ ... (hash <(pair (sym :aggregate_failures) true) ...>)) (send _ _ ... (hash <(pair (sym :aggregate_failures) %1) ...>))
} ...)
PATTERN
def_node_matcher :aggregate_failures_present?, <<-PATTERN
(block {
(send _ _ <(sym :aggregate_failures) ...>)
(send _ _ ... (hash <(pair (sym :aggregate_failures) _) ...>))
} ...) } ...)
PATTERN PATTERN
@ -89,12 +85,12 @@ module RuboCop
node_with_aggregate_failures = find_aggregate_failures(example_node) node_with_aggregate_failures = find_aggregate_failures(example_node)
return false unless node_with_aggregate_failures return false unless node_with_aggregate_failures
aggregate_failures?(node_with_aggregate_failures) aggregate_failures?(node_with_aggregate_failures, TRUE)
end end
def find_aggregate_failures(example_node) def find_aggregate_failures(example_node)
example_node.send_node.each_ancestor(:block) example_node.send_node.each_ancestor(:block)
.find { |block_node| aggregate_failures_present?(block_node) } .find { |block_node| aggregate_failures?(block_node, ANYTHING) }
end end
def find_expectation(node, &block) def find_expectation(node, &block)

View File

@ -0,0 +1,148 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks if example groups contain too many `let` and `subject` calls.
#
# This cop is configurable using the `Max` option and the `AllowSubject`
# which will configure the cop to only register offenses on calls to
# `let` and not calls to `subject`.
#
# @example
# # bad
# describe MyClass do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:baz) { [] }
# let(:qux) { [] }
# let(:quux) { [] }
# let(:quuz) { {} }
# end
#
# describe MyClass do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:baz) { [] }
#
# context 'when stuff' do
# let(:qux) { [] }
# let(:quux) { [] }
# let(:quuz) { {} }
# end
# end
#
# # good
# describe MyClass do
# let(:bar) { [] }
# let!(:baz) { [] }
# let(:qux) { [] }
# let(:quux) { [] }
# let(:quuz) { {} }
# end
#
# describe MyClass do
# context 'when stuff' do
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:booger) { [] }
# end
#
# context 'when other stuff' do
# let(:qux) { [] }
# let(:quux) { [] }
# let(:quuz) { {} }
# end
# end
#
# @example when disabling AllowSubject configuration
#
# # rubocop.yml
# # RSpec/MultipleMemoizedHelpers:
# # AllowSubject: false
#
# # bad - `subject` counts towards memoized helpers
# describe MyClass do
# subject { {} }
# let(:foo) { [] }
# let(:bar) { [] }
# let!(:baz) { [] }
# let(:qux) { [] }
# let(:quux) { [] }
# end
#
# @example with Max configuration
#
# # rubocop.yml
# # RSpec/MultipleMemoizedHelpers:
# # Max: 1
#
# # bad
# describe MyClass do
# let(:foo) { [] }
# let(:bar) { [] }
# end
#
class MultipleMemoizedHelpers < Base
include ConfigurableMax
include RuboCop::RSpec::Variable
MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
def on_block(node)
return unless spec_group?(node)
count = all_helpers(node).uniq.count
return if count <= max
self.max = count
add_offense(node, message: format(MSG, count: count, max: max))
end
def on_new_investigation
@example_group_memoized_helpers = {}
end
private
attr_reader :example_group_memoized_helpers
def all_helpers(node)
[
*helpers(node),
*node.each_ancestor(:block).flat_map(&method(:helpers))
]
end
def helpers(node)
@example_group_memoized_helpers[node] ||=
variable_nodes(node).map do |variable_node|
if variable_node.block_type?
variable_definition?(variable_node.send_node)
else # block-pass (`let(:foo, &bar)`)
variable_definition?(variable_node)
end
end
end
def variable_nodes(node)
example_group = RuboCop::RSpec::ExampleGroup.new(node)
if allow_subject?
example_group.lets
else
example_group.lets + example_group.subjects
end
end
def max
cop_config['Max']
end
def allow_subject?
cop_config['AllowSubject']
end
end
end
end
end

View File

@ -33,7 +33,7 @@ module RuboCop
# - If subjects are defined with `subject!` then we don't autocorrect. # - If subjects are defined with `subject!` then we don't autocorrect.
# This is enough of an edge case that people can just move this to # This is enough of an edge case that people can just move this to
# a `before` hook on their own # a `before` hook on their own
class MultipleSubjects < Cop class MultipleSubjects < Base
extend AutoCorrector extend AutoCorrector
include RangeHelp include RangeHelp

View File

@ -41,7 +41,7 @@ module RuboCop
# #
# it { is_expected.to be_valid } # it { is_expected.to be_valid }
# end # end
class NamedSubject < Cop class NamedSubject < Base
MSG = 'Name your test subject if you need '\ MSG = 'Name your test subject if you need '\
'to reference it explicitly.' 'to reference it explicitly.'

View File

@ -85,9 +85,9 @@ module RuboCop
# end # end
# end # end
# #
class NestedGroups < Cop class NestedGroups < Base
include ConfigurableMax include ConfigurableMax
include RuboCop::RSpec::TopLevelDescribe include RuboCop::RSpec::TopLevelGroup
MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].' MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].'
@ -97,8 +97,8 @@ module RuboCop
"Configuration key `#{DEPRECATED_MAX_KEY}` for #{cop_name} is " \ "Configuration key `#{DEPRECATED_MAX_KEY}` for #{cop_name} is " \
'deprecated in favor of `Max`. Please use that instead.' 'deprecated in favor of `Max`. Please use that instead.'
def on_top_level_describe(node, _args) def on_top_level_group(node)
find_nested_example_groups(node.parent) do |example_group, nesting| find_nested_example_groups(node) do |example_group, nesting|
self.max = nesting self.max = nesting
add_offense( add_offense(
example_group.send_node, example_group.send_node,

View File

@ -15,7 +15,7 @@ module RuboCop
# it '...' do # it '...' do
# expect(false).not_to be_true # expect(false).not_to be_true
# end # end
class NotToNot < Cop class NotToNot < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle

View File

@ -21,7 +21,7 @@ module RuboCop
# let(:foo) { bar } # let(:foo) { bar }
# let(:baz) { baz } # let(:baz) { baz }
# let!(:other) { other } # let!(:other) { other }
class OverwritingSetup < Cop class OverwritingSetup < Base
MSG = '`%<name>s` is already defined.' MSG = '`%<name>s` is already defined.'
def_node_matcher :setup?, (Helpers::ALL + Subject::ALL).block_pattern def_node_matcher :setup?, (Helpers::ALL + Subject::ALL).block_pattern

View File

@ -31,7 +31,7 @@ module RuboCop
# # good # # good
# describe MyClass do # describe MyClass do
# end # end
class Pending < Cop class Pending < Base
MSG = 'Pending spec found.' MSG = 'Pending spec found.'
PENDING = Examples::PENDING + Examples::SKIPPED + ExampleGroups::SKIPPED PENDING = Examples::PENDING + Examples::SKIPPED + ExampleGroups::SKIPPED

View File

@ -208,18 +208,20 @@ module RuboCop
'is_a?' 'is_a?'
when 'be_an_instance_of', 'be_instance_of', 'an_instance_of' when 'be_an_instance_of', 'be_instance_of', 'an_instance_of'
'instance_of?' 'instance_of?'
when 'include', 'respond_to' when 'include'
matcher + '?' 'include?'
when 'respond_to'
'respond_to?'
when /^have_(.+)/ when /^have_(.+)/
"has_#{Regexp.last_match(1)}?" "has_#{Regexp.last_match(1)}?"
else else
matcher[/^be_(.+)/, 1] + '?' "#{matcher[/^be_(.+)/, 1]}?"
end end
end end
# rubocop:enable Metrics/MethodLength # rubocop:enable Metrics/MethodLength
def replacement_matcher(node) def replacement_matcher(node)
case [cop_config['Strict'], node.method_name == :to] case [cop_config['Strict'], node.method?(:to)]
when [true, true] when [true, true]
'be(true)' 'be(true)'
when [true, false] when [true, false]
@ -269,7 +271,7 @@ module RuboCop
# #
# # good - the above code is rewritten to it by this cop # # good - the above code is rewritten to it by this cop
# expect(foo.something?).to be_truthy # expect(foo.something?).to be_truthy
class PredicateMatcher < Cop class PredicateMatcher < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
include InflectedHelper include InflectedHelper
@ -288,15 +290,6 @@ module RuboCop
check_explicit(node) if style == :explicit check_explicit(node) if style == :explicit
end end
def autocorrect(node)
case style
when :inflected
autocorrect_inflected(node)
when :explicit
autocorrect_explicit(node)
end
end
private private
# returns args location with whitespace # returns args location with whitespace

View File

@ -30,7 +30,7 @@ module RuboCop
# it { is_expected.to have_http_status :success } # it { is_expected.to have_http_status :success }
# it { is_expected.to have_http_status :error } # it { is_expected.to have_http_status :error }
# #
class HttpStatus < Cop class HttpStatus < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle

View File

@ -23,7 +23,7 @@ module RuboCop
# expect(foo).to receive(:bar).at_most(:once) # expect(foo).to receive(:bar).at_most(:once)
# expect(foo).to receive(:bar).at_most(:twice).times # expect(foo).to receive(:bar).at_most(:twice).times
# #
class ReceiveCounts < Cop class ReceiveCounts < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Use `%<alternative>s` instead of `%<original>s`.' MSG = 'Use `%<alternative>s` instead of `%<original>s`.'

View File

@ -13,14 +13,14 @@ module RuboCop
# # good # # good
# expect(foo).not_to receive(:bar) # expect(foo).not_to receive(:bar)
# #
class ReceiveNever < Cop class ReceiveNever < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Use `not_to receive` instead of `never`.' MSG = 'Use `not_to receive` instead of `never`.'
def_node_search :method_on_stub?, '(send nil? :receive ...)' def_node_search :method_on_stub?, '(send nil? :receive ...)'
def on_send(node) def on_send(node)
return unless node.method_name == :never && method_on_stub?(node) return unless node.method?(:never) && method_on_stub?(node)
add_offense(node.loc.selector) do |corrector| add_offense(node.loc.selector) do |corrector|
autocorrect(corrector, node) autocorrect(corrector, node)

View File

@ -40,7 +40,7 @@ module RuboCop
# end # end
# end # end
# #
class RepeatedDescription < Cop class RepeatedDescription < Base
MSG = "Don't repeat descriptions within an example group." MSG = "Don't repeat descriptions within an example group."
def on_block(node) def on_block(node)

View File

@ -15,7 +15,7 @@ module RuboCop
# expect(user).to be_valid # expect(user).to be_valid
# end # end
# #
class RepeatedExample < Cop class RepeatedExample < Base
MSG = "Don't repeat examples within an example group." MSG = "Don't repeat examples within an example group."
def on_block(node) def on_block(node)
@ -41,7 +41,7 @@ module RuboCop
def example_signature(example) def example_signature(example)
key_parts = [example.metadata, example.implementation] key_parts = [example.metadata, example.implementation]
if example.definition.method_name == :its if example.definition.method?(:its)
key_parts << example.definition.arguments key_parts << example.definition.arguments
end end

View File

@ -43,7 +43,7 @@ module RuboCop
# it { is_expected.to respond_to :each } # it { is_expected.to respond_to :each }
# end # end
# #
class RepeatedExampleGroupBody < Cop class RepeatedExampleGroupBody < Base
MSG = 'Repeated %<group>s block body on line(s) %<loc>s' MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
def_node_matcher :several_example_groups?, <<-PATTERN def_node_matcher :several_example_groups?, <<-PATTERN

View File

@ -43,7 +43,7 @@ module RuboCop
# # example group # # example group
# end # end
# #
class RepeatedExampleGroupDescription < Cop class RepeatedExampleGroupDescription < Base
MSG = 'Repeated %<group>s block description on line(s) %<loc>s' MSG = 'Repeated %<group>s block description on line(s) %<loc>s'
def_node_matcher :several_example_groups?, <<-PATTERN def_node_matcher :several_example_groups?, <<-PATTERN

View File

@ -33,7 +33,7 @@ module RuboCop
# # also good as the returned value is dynamic # # also good as the returned value is dynamic
# allow(Foo).to receive(:bar) { bar.baz } # allow(Foo).to receive(:bar) { bar.baz }
# #
class ReturnFromStub < Cop class ReturnFromStub < Base
extend AutoCorrector extend AutoCorrector
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle

View File

@ -26,7 +26,7 @@ module RuboCop
# let!(:baz) { 3 } # let!(:baz) { 3 }
# end # end
# #
class ScatteredLet < Cop class ScatteredLet < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Group all let/let! blocks in the example group together.' MSG = 'Group all let/let! blocks in the example group together.'

View File

@ -22,7 +22,7 @@ module RuboCop
# end # end
# end # end
# #
class ScatteredSetup < Cop class ScatteredSetup < Base
MSG = 'Do not define multiple `%<hook_name>s` hooks in the same '\ MSG = 'Do not define multiple `%<hook_name>s` hooks in the same '\
'example group (also defined on %<lines>s).' 'example group (also defined on %<lines>s).'

View File

@ -50,7 +50,7 @@ module RuboCop
# end # end
# end # end
# #
class SharedContext < Cop class SharedContext < Base
extend AutoCorrector extend AutoCorrector
MSG_EXAMPLES = "Use `shared_examples` when you don't "\ MSG_EXAMPLES = "Use `shared_examples` when you don't "\

View File

@ -20,7 +20,7 @@ module RuboCop
# shared_examples_for 'foo bar baz' # shared_examples_for 'foo bar baz'
# include_examples 'foo bar baz' # include_examples 'foo bar baz'
# #
class SharedExamples < Cop class SharedExamples < Base
extend AutoCorrector extend AutoCorrector
def_node_matcher :shared_examples, def_node_matcher :shared_examples,

View File

@ -16,7 +16,7 @@ module RuboCop
# allow(foo).to receive(:bar, :baz) # allow(foo).to receive(:bar, :baz)
# allow(foo).to receive("bar.baz") # allow(foo).to receive("bar.baz")
# #
class SingleArgumentMessageChain < Cop class SingleArgumentMessageChain < Base
extend AutoCorrector extend AutoCorrector
MSG = 'Use `%<recommended>s` instead of calling '\ MSG = 'Use `%<recommended>s` instead of calling '\

View File

@ -21,7 +21,7 @@ module RuboCop
# end # end
# end # end
# #
class SubjectStub < Cop class SubjectStub < Base
include RuboCop::RSpec::TopLevelGroup include RuboCop::RSpec::TopLevelGroup
MSG = 'Do not stub methods of the object under test.' MSG = 'Do not stub methods of the object under test.'
@ -39,7 +39,7 @@ module RuboCop
# name # => :thing # name # => :thing
# end # end
# #
# @param node [RuboCop::Node] # @param node [RuboCop::AST::Node]
# #
# @yield [Symbol] subject name # @yield [Symbol] subject name
def_node_matcher :subject, <<-PATTERN def_node_matcher :subject, <<-PATTERN

View File

@ -30,7 +30,7 @@ module RuboCop
# }.to raise_error(/err/) # }.to raise_error(/err/)
# #
# expect { do_something }.not_to raise_error # expect { do_something }.not_to raise_error
class UnspecifiedException < Cop class UnspecifiedException < Base
MSG = 'Specify the exception being captured' MSG = 'Specify the exception being captured'
def_node_matcher :empty_raise_error_or_exception, <<-PATTERN def_node_matcher :empty_raise_error_or_exception, <<-PATTERN

View File

@ -7,22 +7,22 @@ module RuboCop
# #
# @example EnforcedStyle: symbols (default) # @example EnforcedStyle: symbols (default)
# # bad # # bad
# let('user_name') { 'Adam' }
# subject('user') { create_user } # subject('user') { create_user }
# let('user_name') { 'Adam' }
# #
# # good # # good
# let(:user_name) { 'Adam' }
# subject(:user) { create_user } # subject(:user) { create_user }
# let(:user_name) { 'Adam' }
# #
# @example EnforcedStyle: strings # @example EnforcedStyle: strings
# # bad # # bad
# let(:user_name) { 'Adam' }
# subject(:user) { create_user } # subject(:user) { create_user }
# let(:user_name) { 'Adam' }
# #
# # good # # good
# let('user_name') { 'Adam' }
# subject('user') { create_user } # subject('user') { create_user }
class VariableDefinition < Cop # let('user_name') { 'Adam' }
class VariableDefinition < Base
include ConfigurableEnforcedStyle include ConfigurableEnforcedStyle
include RuboCop::RSpec::Variable include RuboCop::RSpec::Variable
@ -44,7 +44,7 @@ module RuboCop
end end
def string?(node) def string?(node)
node.str_type? || node.dstr_type? node.str_type?
end end
def symbol?(node) def symbol?(node)

View File

@ -0,0 +1,66 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks that memoized helper names use the configured style.
#
# Variables can be excluded from checking using the `IgnoredPatterns`
# option.
#
# @example EnforcedStyle: snake_case (default)
# # bad
# subject(:userName1) { 'Adam' }
# let(:userName2) { 'Adam' }
#
# # good
# subject(:user_name_1) { 'Adam' }
# let(:user_name_2) { 'Adam' }
#
# @example EnforcedStyle: camelCase
# # bad
# subject(:user_name_1) { 'Adam' }
# let(:user_name_2) { 'Adam' }
#
# # good
# subject(:userName1) { 'Adam' }
# let(:userName2) { 'Adam' }
#
# @example IgnoredPatterns configuration
#
# # rubocop.yml
# # RSpec/VariableName:
# # EnforcedStyle: snake_case
# # IgnoredPatterns:
# # - ^userFood
#
# @example
# # okay because it matches the `^userFood` regex in `IgnoredPatterns`
# subject(:userFood_1) { 'spaghetti' }
# let(:userFood_2) { 'fettuccine' }
#
class VariableName < Base
include ConfigurableNaming
include IgnoredPattern
include RuboCop::RSpec::Variable
MSG = 'Use %<style>s for variable names.'
def on_send(node)
variable_definition?(node) do |variable|
return if variable.dstr_type? || variable.dsym_type?
return if matches_ignored_pattern?(variable.value)
check_name(node, variable.value, variable.loc.expression)
end
end
private
def message(style)
format(MSG, style: style)
end
end
end
end
end

View File

@ -22,7 +22,7 @@ module RuboCop
# let(:foo) do # let(:foo) do
# instance_double("ClassName", method_name: 'returned value') # instance_double("ClassName", method_name: 'returned value')
# end # end
class VerifiedDoubles < Cop class VerifiedDoubles < Base
MSG = 'Prefer using verifying doubles over normal doubles.' MSG = 'Prefer using verifying doubles over normal doubles.'
def_node_matcher :unverified_double, <<-PATTERN def_node_matcher :unverified_double, <<-PATTERN

View File

@ -11,7 +11,7 @@ module RuboCop
# #
# # good # # good
# expect(something).to be(1) # expect(something).to be(1)
class VoidExpect < Cop class VoidExpect < Base
MSG = 'Do not use `expect()` without `.to` or `.not_to`. ' \ MSG = 'Do not use `expect()` without `.to` or `.not_to`. ' \
'Chain the methods or remove it.' 'Chain the methods or remove it.'

View File

@ -11,7 +11,7 @@ module RuboCop
# #
# # good # # good
# expect(foo).to be(:bar).and_yield(1) # expect(foo).to be(:bar).and_yield(1)
class Yield < Cop class Yield < Base
extend AutoCorrector extend AutoCorrector
include RangeHelp include RangeHelp

View File

@ -65,6 +65,7 @@ require_relative 'rspec/message_spies'
require_relative 'rspec/missing_example_group_argument' require_relative 'rspec/missing_example_group_argument'
require_relative 'rspec/multiple_describes' require_relative 'rspec/multiple_describes'
require_relative 'rspec/multiple_expectations' require_relative 'rspec/multiple_expectations'
require_relative 'rspec/multiple_memoized_helpers'
require_relative 'rspec/multiple_subjects' require_relative 'rspec/multiple_subjects'
require_relative 'rspec/named_subject' require_relative 'rspec/named_subject'
require_relative 'rspec/nested_groups' require_relative 'rspec/nested_groups'

Some files were not shown because too many files have changed in this diff Show More