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
RSpec/NestedGroups:
Max: 5
RSpec/MultipleMemoizedHelpers:
Max: 12

View File

@ -112,8 +112,8 @@ GEM
parser (>= 2.7.1.4)
rubocop-performance (1.7.1)
rubocop (>= 0.82.0)
rubocop-rspec (1.42.0)
rubocop (>= 0.87.0)
rubocop-rspec (1.43.1)
rubocop (~> 0.87)
ruby-macho (2.2.0)
ruby-progressbar (1.10.1)
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/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/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/patchelf-1.2.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/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-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/sorbet-static-0.5.5866-universal-darwin-19/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.5866/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.5869/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"

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
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
VersionAdded: '1.0'
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
RSpec/MultipleDescribes:
Description: Checks for multiple top level describes.
Description: Checks for multiple top-level example groups.
Enabled: true
VersionAdded: '1.0'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleDescribes
@ -394,6 +394,14 @@ RSpec/MultipleExpectations:
VersionChanged: '1.21'
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:
Description: Checks if an example group defines `subject` multiple times.
Enabled: true
@ -558,7 +566,9 @@ RSpec/VariableName:
SupportedStyles:
- snake_case
- camelCase
IgnoredPatterns: []
VersionAdded: '1.40'
VersionChanged: '1.43'
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName
RSpec/VerifiedDoubles:

View File

@ -19,11 +19,12 @@ require_relative 'rubocop/rspec/example_group'
require_relative 'rubocop/rspec/example'
require_relative 'rubocop/rspec/hook'
require_relative 'rubocop/rspec/variable'
require_relative 'rubocop/cop/rspec/base'
require_relative 'rubocop/cop/rspec/cop'
require_relative 'rubocop/rspec/align_let_brace'
require_relative 'rubocop/rspec/factory_bot'
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'
RuboCop::RSpec::Inject.defaults!

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ module RuboCop
# expect(foo).to be 1.0
# expect(foo).to be(true)
#
class Be < Cop
class Be < Base
MSG = 'Don\'t use `be` without an argument.'
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
# coerce objects for comparison.
#
class BeEql < Cop
class BeEql < Base
extend AutoCorrector
MSG = 'Prefer `be` over `eql`.'

View File

@ -23,7 +23,7 @@ module RuboCop
# before(:each) { Widget.create }
# after(:each) { Widget.delete_all }
# end
class BeforeAfterAll < Cop
class BeforeAfterAll < Base
MSG = 'Beware of using `%<hook>s` as it may cause state to leak '\
'between tests. If you are using `rspec-rails`, and '\
'`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(/widgets/)
#
class CurrentPathExpectation < Cop
class CurrentPathExpectation < Base
extend AutoCorrector
MSG = 'Do not set an RSpec expectation on `current_path` in ' \

View File

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

View File

@ -26,7 +26,7 @@ module RuboCop
# expect(page).to have_css('.foo', visible: :all)
# 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_TRUE = 'Use `:visible` instead of `true`.'
CAPYBARA_MATCHER_METHODS = %i[

View File

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

View File

@ -34,11 +34,11 @@ module RuboCop
# context 'when the display name is not present' do
# # ...
# end
class ContextWording < Cop
class ContextWording < Base
MSG = 'Start context description with %<prefixes>s.'
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
def on_block(node)
@ -51,7 +51,7 @@ module RuboCop
private
def bad_prefix?(description)
!prefixes.include?(description.split.first)
!prefixes.include?(description.split(/\b/).first)
end
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
# end
class DescribeMethod < Cop
include RuboCop::RSpec::TopLevelDescribe
class DescribeMethod < Base
include RuboCop::RSpec::TopLevelGroup
MSG = 'The second argument to describe should be the method '\
"being tested. '#instance' or '.class'."
def on_top_level_describe(_node, (_, second_arg))
return unless second_arg&.str_type?
return if second_arg.str_content.start_with?('#', '.')
def_node_matcher :second_argument, <<~PATTERN
(block
(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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ module RuboCop
# end
#
# @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.'
def_node_search :find_rspec_blocks,

View File

@ -41,7 +41,7 @@ module RuboCop
# describe 'display name presence' do
# # ...
# end
class Dialect < Cop
class Dialect < Base
extend AutoCorrector
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
# end
# after(:all) { cleanup_feed }
class EmptyHook < Cop
class EmptyHook < Base
extend AutoCorrector
include RuboCop::Cop::RangeHelp

View File

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

View File

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

View File

@ -16,24 +16,21 @@ module RuboCop
# let(:something) { other }
#
# it { does_something }
class EmptyLineAfterFinalLet < Cop
class EmptyLineAfterFinalLet < Base
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)
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 last_child?(latest_let)
return if final_let.nil?
missing_separating_line(latest_let) do |location|
add_offense(location) do |corrector|
corrector.insert_after(location.end, "\n")
end
missing_separating_line_offense(final_let) do |method|
format(MSG, let: method)
end
end
end

View File

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

View File

@ -14,20 +14,17 @@ module RuboCop
# subject(:obj) { described_class }
#
# let(:foo) { bar }
class EmptyLineAfterSubject < Cop
class EmptyLineAfterSubject < Base
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)
return unless subject?(node) && !in_spec_block?(node)
return if last_child?(node)
missing_separating_line(node) do |location|
add_offense(location) do |corrector|
corrector.insert_after(location.end, "\n")
end
missing_separating_line_offense(node) do |method|
format(MSG, subject: method)
end
end

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ module RuboCop
# expect(pattern).to eq(/foo/)
# expect(name).to eq("John")
#
class ExpectActual < Cop
class ExpectActual < Base
extend AutoCorrector
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 { user.reload.name }
#
class ExpectChange < Cop
class ExpectChange < Base
extend AutoCorrector
include ConfigurableEnforcedStyle

View File

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

View File

@ -14,7 +14,7 @@ module RuboCop
#
# # good
# 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` '\
'instead of mutating $%<name>s.'

View File

@ -24,7 +24,7 @@ module RuboCop
#
# # good
# count { 1 }
class AttributeDefinedStatically < Cop
class AttributeDefinedStatically < Base
extend AutoCorrector
MSG = 'Use a block to declare attribute values.'
@ -39,7 +39,7 @@ module RuboCop
def on_block(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|
next unless offensive_receiver?(attribute.receiver, node)
@ -84,7 +84,7 @@ module RuboCop
def autocorrect_replacing_parens(corrector, 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)
end

View File

@ -24,7 +24,7 @@ module RuboCop
#
# # good
# 3.times { create :user }
class CreateList < Cop
class CreateList < Base
extend AutoCorrector
include ConfigurableEnforcedStyle
@ -44,7 +44,7 @@ module RuboCop
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
def on_block(node)
@ -60,7 +60,7 @@ module RuboCop
def on_send(node)
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)
add_offense(node.loc.selector, message: message) do |corrector|
TimesCorrector.new(node).call(corrector)
@ -79,7 +79,7 @@ module RuboCop
end
# :nodoc
class Corrector
module Corrector
private
def build_options_string(options)
@ -102,7 +102,9 @@ module RuboCop
end
# :nodoc
class TimesCorrector < Corrector
class TimesCorrector
include Corrector
def initialize(node)
@node = node
end
@ -130,7 +132,9 @@ module RuboCop
end
# :nodoc:
class CreateListCorrector < Corrector
class CreateListCorrector
include Corrector
def initialize(node)
@node = node.parent
end

View File

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

View File

@ -56,35 +56,43 @@ module RuboCop
# # good
# my_class_spec.rb # describe MyClass, '#method'
#
class FilePath < Cop
include RuboCop::RSpec::TopLevelDescribe
class FilePath < Base
include RuboCop::RSpec::TopLevelGroup
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 on_top_level_describe(node, args)
return unless const_described?(node) && single_top_level_describe?
return if routing_spec?(args)
def on_top_level_group(node)
return unless top_level_groups.one?
glob = glob_for(args)
const_described(node) do |send_node, described_class, arguments|
next if routing_spec?(arguments)
return if filename_ends_with?(glob)
add_offense(
node,
message: format(MSG, suffix: glob)
)
ensure_correct_file_path(send_node, described_class, arguments)
end
end
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)
args.any?(&method(:routing_metadata?))
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?
"#{expected_path(described_class)}#{name_glob(method_name)}*_spec.rb"
@ -94,10 +102,10 @@ module RuboCop
'*_spec.rb'
end
def name_glob(name)
return unless name&.str_type?
def name_glob(method_name)
return unless method_name&.str_type?
"*#{name.str_content.gsub(/\W/, '')}" unless ignore_methods?
"*#{method_name.str_content.gsub(/\W/, '')}" unless ignore_methods?
end
def expected_path(constant)

View File

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

View File

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

View File

@ -23,7 +23,7 @@ module RuboCop
# expect(foo).to be
# end
#
class HooksBeforeExamples < Cop
class HooksBeforeExamples < Base
extend AutoCorrector
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
# expect { do_something }.to change(something).to(new_value)
# end
class ImplicitBlockExpectation < Cop
class ImplicitBlockExpectation < Base
MSG = 'Avoid implicit block expectations.'
def_node_matcher :lambda?, <<-PATTERN

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -93,7 +93,7 @@ module RuboCop
# stub_const('SomeModule::SomeClass', foo_class)
# end
# end
class LeakyConstantDeclaration < Cop
class LeakyConstantDeclaration < Base
MSG_CONST = 'Stub constant instead of declaring explicitly.'
MSG_CLASS = 'Stub class 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
# expect(some).to be
# end
class LetBeforeExamples < Cop
class LetBeforeExamples < Base
extend AutoCorrector
MSG = 'Move `let` before the examples in the group.'

View File

@ -25,7 +25,7 @@ module RuboCop
# it 'counts widgets' do
# expect(Widget.count).to eq(1)
# end
class LetSetup < Cop
class LetSetup < Base
MSG = 'Do not use `let!` to setup objects not referenced in tests.'
def_node_matcher :example_or_shared_group_or_including?,
@ -35,7 +35,10 @@ module RuboCop
).block_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
def_node_search :method_called?, '(send nil? %)'
@ -52,7 +55,7 @@ module RuboCop
def unused_let_bang(node)
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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
module RuboCop
module Cop
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
# be nested or separated into different test files.
@ -22,17 +22,20 @@ module RuboCop
# describe '.do_something_else' do
# end
# end
class MultipleDescribes < Cop
include RuboCop::RSpec::TopLevelDescribe
class MultipleDescribes < Base
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.'
def on_top_level_describe(node, _args)
return if single_top_level_describe?
return unless top_level_nodes.first.equal?(node)
def on_top_level_group(node)
top_level_example_groups =
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

View File

@ -45,22 +45,18 @@ module RuboCop
# end
# end
#
class MultipleExpectations < Cop
class MultipleExpectations < Base
include ConfigurableMax
MSG = 'Example has too many expectations [%<total>d/%<max>d].'
ANYTHING = ->(_node) { true }
TRUE = ->(node) { node.true_type? }
def_node_matcher :aggregate_failures?, <<-PATTERN
(block {
(send _ _ <(sym :aggregate_failures) ...>)
(send _ _ ... (hash <(pair (sym :aggregate_failures) true) ...>))
} ...)
PATTERN
def_node_matcher :aggregate_failures_present?, <<-PATTERN
(block {
(send _ _ <(sym :aggregate_failures) ...>)
(send _ _ ... (hash <(pair (sym :aggregate_failures) _) ...>))
(send _ _ ... (hash <(pair (sym :aggregate_failures) %1) ...>))
} ...)
PATTERN
@ -89,12 +85,12 @@ module RuboCop
node_with_aggregate_failures = find_aggregate_failures(example_node)
return false unless node_with_aggregate_failures
aggregate_failures?(node_with_aggregate_failures)
aggregate_failures?(node_with_aggregate_failures, TRUE)
end
def find_aggregate_failures(example_node)
example_node.send_node.each_ancestor(:block)
.find { |block_node| aggregate_failures_present?(block_node) }
.find { |block_node| aggregate_failures?(block_node, ANYTHING) }
end
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.
# This is enough of an edge case that people can just move this to
# a `before` hook on their own
class MultipleSubjects < Cop
class MultipleSubjects < Base
extend AutoCorrector
include RangeHelp

View File

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

View File

@ -85,9 +85,9 @@ module RuboCop
# end
# end
#
class NestedGroups < Cop
class NestedGroups < Base
include ConfigurableMax
include RuboCop::RSpec::TopLevelDescribe
include RuboCop::RSpec::TopLevelGroup
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 " \
'deprecated in favor of `Max`. Please use that instead.'
def on_top_level_describe(node, _args)
find_nested_example_groups(node.parent) do |example_group, nesting|
def on_top_level_group(node)
find_nested_example_groups(node) do |example_group, nesting|
self.max = nesting
add_offense(
example_group.send_node,

View File

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

View File

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

View File

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

View File

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

View File

@ -13,14 +13,14 @@ module RuboCop
# # good
# expect(foo).not_to receive(:bar)
#
class ReceiveNever < Cop
class ReceiveNever < Base
extend AutoCorrector
MSG = 'Use `not_to receive` instead of `never`.'
def_node_search :method_on_stub?, '(send nil? :receive ...)'
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|
autocorrect(corrector, node)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,7 +20,7 @@ module RuboCop
# shared_examples_for 'foo bar baz'
# include_examples 'foo bar baz'
#
class SharedExamples < Cop
class SharedExamples < Base
extend AutoCorrector
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")
#
class SingleArgumentMessageChain < Cop
class SingleArgumentMessageChain < Base
extend AutoCorrector
MSG = 'Use `%<recommended>s` instead of calling '\

View File

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

View File

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

View File

@ -7,22 +7,22 @@ module RuboCop
#
# @example EnforcedStyle: symbols (default)
# # bad
# let('user_name') { 'Adam' }
# subject('user') { create_user }
# let('user_name') { 'Adam' }
#
# # good
# let(:user_name) { 'Adam' }
# subject(:user) { create_user }
# let(:user_name) { 'Adam' }
#
# @example EnforcedStyle: strings
# # bad
# let(:user_name) { 'Adam' }
# subject(:user) { create_user }
# let(:user_name) { 'Adam' }
#
# # good
# let('user_name') { 'Adam' }
# subject('user') { create_user }
class VariableDefinition < Cop
# let('user_name') { 'Adam' }
class VariableDefinition < Base
include ConfigurableEnforcedStyle
include RuboCop::RSpec::Variable
@ -44,7 +44,7 @@ module RuboCop
end
def string?(node)
node.str_type? || node.dstr_type?
node.str_type?
end
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
# instance_double("ClassName", method_name: 'returned value')
# end
class VerifiedDoubles < Cop
class VerifiedDoubles < Base
MSG = 'Prefer using verifying doubles over normal doubles.'
def_node_matcher :unverified_double, <<-PATTERN

View File

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

View File

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

View File

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

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