File: context_wording.rb

package info (click to toggle)
ruby-rubocop-rspec 2.16.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid
  • size: 1,892 kB
  • sloc: ruby: 22,283; makefile: 4
file content (110 lines) | stat: -rw-r--r-- 3,068 bytes parent folder | download
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