1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks that `context` docstring starts with an allowed prefix.
#
# The default list of prefixes is minimal. Users are encouraged to tailor
# the configuration to meet project needs. Other acceptable prefixes may
# include `if`, `unless`, `for`, `before`, `after`, or `during`.
# They may consist of multiple words if desired.
#
# @see https://rspec.rubystyle.guide/#context-descriptions
# @see http://www.betterspecs.org/#contexts
#
# @example `Prefixes` configuration
# # .rubocop.yml
# # RSpec/ContextWording:
# # Prefixes:
# # - when
# # - with
# # - without
# # - if
# # - unless
# # - for
#
# @example
# # bad
# context 'the display name not present' do
# # ...
# end
#
# # good
# context 'when the display name is not present' do
# # ...
# end
#
# This cop can be customized allowed context description pattern
# with `AllowedPatterns`. By default, there are no checking by pattern.
#
# @example `AllowedPatterns` configuration
#
# # .rubocop.yml
# # RSpec/ContextWording:
# # AllowedPatterns:
# # - とき$
#
# @example
# # bad
# context '条件を満たす' do
# # ...
# end
#
# # good
# context '条件を満たすとき' do
# # ...
# end
#
class ContextWording < Base
include AllowedPattern
MSG = 'Context description should match %<patterns>s.'
# @!method context_wording(node)
def_node_matcher :context_wording, <<-PATTERN
(block (send #rspec? { :context :shared_context } $(str $_) ...) ...)
PATTERN
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
context_wording(node) do |context, description|
if bad_pattern?(description)
message = format(MSG, patterns: expect_patterns)
add_offense(context, message: message)
end
end
end
private
def allowed_patterns
super + prefix_regexes
end
def prefix_regexes
@prefix_regexes ||= prefixes.map { |pre| /^#{Regexp.escape(pre)}\b/ }
end
def bad_pattern?(description)
return false if allowed_patterns.empty?
!matches_allowed_pattern?(description)
end
def expect_patterns
inspected = allowed_patterns.map do |pattern|
pattern.inspect.gsub(/\A"|"\z/, '/')
end
return inspected.first if inspected.size == 1
inspected << "or #{inspected.pop}"
inspected.join(', ')
end
def prefixes
Array(cop_config.fetch('Prefixes', []))
end
end
end
end
end
|