Class: RuboCop::Cop::Kaizo::SpecDescriptionProse

Inherits:
Base
  • Object
show all
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 coordinating or conditional conjunction (Conjunctions -- joined clauses are separate examples; a condition belongs in a 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 ContextPrefixes (when/with/without/after). describe strings name the unit under test and are exempt.

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.

Examples:

# bad
it "renders the name, image, and flag"
it "omits the key when the role is unset"
it "renders the :cpu member"
context "the role is unset" do
end

# good
it "renders the name"
it "renders the cpu member"
context "when the role is unset" do
  it "omits the key"
end

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
CONJUNCTION_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_CONJUNCTIONS =

Coordinating (FANBOYS, minus the preposition-heavy for) plus the conditional subordinators that reliably signal a hidden "given". Homographs like even/given/regardless are 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_CONTEXT_PREFIXES =
%w[when with without after].freeze
CODE_CHARS =
/[_:#={}!`\[\]]/
NESTED_QUOTE =
/(['"]).+\1/

Instance Method Summary collapse

Instance Method Details

#description(node) ⇒ Object



70
71
72
# File 'lib/rubocop/cop/kaizo/spec_description_prose.rb', line 70

def_node_matcher :description, <<~PATTERN
  (send nil? _ (str $_) ...)
PATTERN

#on_send(node) ⇒ Object



74
75
76
77
78
79
80
81
82
# File 'lib/rubocop/cop/kaizo/spec_description_prose.rb', line 74

def on_send(node)
  text = description(node)
  return unless text

  message = violation(node.method_name, text)
  return unless message

  add_offense(node.first_argument, message: message)
end