Class: RuboCop::Cop::Kaizo::SpecDescriptionProse
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::Kaizo::SpecDescriptionProse
- Includes:
- AllowedPattern
- Defined in:
- lib/rubocop/cop/kaizo/spec_description_prose.rb
Overview
Requires RSpec it/context descriptions to read as one-behavior prose
specifications, after the spec-skeleton naming law.
An it/specify/example description must not contain a comma (a list
is several behaviors), a forbidden word (ForbiddenWords -- conjunctions
join clauses that are separate examples; a conditional signals a hidden
context), or code (_ : # = { } ! [ ], a backtick, or a nested quoted
literal -- a description is prose, not identifiers or wire values). Each
rule is structural: it signals that one example is really more than one,
or that the assertion is leaking into the name.
A context description must not contain code, and must open with one of
RequiredContextPrefixes (when/with/without/after). describe
strings name the unit under test and are exempt. An error class name
(Foo::Error, Timeout::DeadlineException) reads as prose, not code:
the error is part of the specified behavior and is what the user
ultimately sees raised.
Wording preferences that do not change the spec's structure (e.g. should
vs a present-tense verb) are out of scope -- see rubocop-rspec's
RSpec/ExampleWording.
There is no autocorrection: splitting an example, or extracting a
condition into a context, is a modelling decision for a human.
Configuration
[+ForbiddenWords+] Words that force a split when they appear in an
example description, matched as whole words, case
insensitively. Default: and, but, or, nor,
so, yet, when, whenever, if, unless,
while, until, because, although, though.
[+RequiredContextPrefixes+] Words a context description may open
with. Default: when, with, without,
after.
[+AllowedPatterns+] Regexps matched against the whole description; a
match exempts it from every rule. The escape hatch
for a description that must quote something
code-shaped. Default: none.
[+Include+] Files the cop runs on. Default: **/*_spec.rb.
Kaizo/SpecDescriptionProse:
inherit_mode:
merge:
- ForbiddenWords
- AllowedPatterns
ForbiddenWords:
- given # flag `given ...` too
AllowedPatterns:
- 'Foo::Widget' # allow this one identifier
Constant Summary collapse
- EXAMPLE_METHODS =
%i[ it specify example fit xit fspecify xspecify fexample xexample ].freeze
- CONTEXT_METHODS =
%i[context fcontext xcontext].freeze
- RESTRICT_ON_SEND =
(EXAMPLE_METHODS + CONTEXT_METHODS).freeze
- COMMA_MSG =
"Split this example: its description contains a comma.".freeze
- FORBIDDEN_WORD_MSG =
"Split this example: its description contains `%<word>s`; " \ "use separate examples or a `context`.".freeze
- CODE_MSG =
"Write the description as prose; it contains code, not English.".freeze
- CONTEXT_CODE_MSG =
"Write the context description as prose; it contains code, not English.".freeze
- CONTEXT_PREFIX_MSG =
"Begin the context description with %<prefixes>s.".freeze
- DEFAULT_FORBIDDEN_WORDS =
Coordinating (FANBOYS, minus the preposition-heavy
for) plus the conditional subordinators that reliably signal a hidden "given". Homographs likeeven/given/regardlessare deliberately absent -- they collide with adjectives/nouns (even numbers) -- add them via config if you want them. %w[ and but or nor so yet when whenever if unless while until because although though ].freeze
- DEFAULT_REQUIRED_CONTEXT_PREFIXES =
%w[when with without after].freeze
- CODE_CHARS =
/[_:#={}!`\[\]]/- NESTED_QUOTE =
/(['"]).+\1/- ERROR_CONSTANT =
An error class name is part of the specified behavior -- it is what the user sees raised -- so it is masked to prose before the checks.
/(?:::)?\b(?:[A-Z]\w*::)*(?:[A-Z]\w*)?(?:Error|Exception)\b/
Instance Method Summary collapse
Instance Method Details
#description(node) ⇒ Object
105 106 107 |
# File 'lib/rubocop/cop/kaizo/spec_description_prose.rb', line 105 def_node_matcher :description, <<~PATTERN (send nil? _ (str $_) ...) PATTERN |
#on_send(node) ⇒ Object
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/rubocop/cop/kaizo/spec_description_prose.rb', line 109 def on_send(node) text = description(node) return unless text return if matches_allowed_pattern?(text) = violation(node.method_name, without_error_constants(text)) return unless add_offense(node.first_argument, message:) end |