Skip to content

Commit

Permalink
Add AllowedPatterns configuration option to RSpec/ContextWording
Browse files Browse the repository at this point in the history
Resolve: #1356
  • Loading branch information
ydah committed Aug 30, 2022
1 parent 1a98909 commit 9f7cdd7
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Add new `RSpec/Capybara/SpecificFinders` cop. ([@ydah][])
* Add support for numblocks to `RSpec/AroundBlock`, `RSpec/EmptyLineAfterHook`, `RSpec/ExpectInHook`, `RSpec/HookArgument`, `RSpec/HooksBeforeExamples`, `RSpec/IteratedExpectation`, and `RSpec/NoExpectationExample`. ([@ydah][])
* Fix incorrect documentation URLs when using `rubocop --show-docs-url`. ([@r7kamura][])
* Add `AllowedPatterns` configuration option to `RSpec/ContextWording`. ([@ydah][])

## 2.12.1 (2022-07-03)

Expand Down
3 changes: 2 additions & 1 deletion config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,9 @@ RSpec/ContextWording:
- when
- with
- without
AllowedPatterns: []
VersionAdded: '1.20'
VersionChanged: 1.20.1
VersionChanged: '2.13'
StyleGuide: https://rspec.rubystyle.guide/#context-descriptions
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContextWording

Expand Down
32 changes: 31 additions & 1 deletion docs/modules/ROOT/pages/cops_rspec.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ end
| Yes
| No
| 1.20
| 1.20.1
| 2.13
|===

Checks that `context` docstring starts with an allowed prefix.
Expand All @@ -534,6 +534,9 @@ 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.

This cop can be customized allowed context description pattern
with `AllowedPatterns`. By default, there are no checking by pattern.

=== Examples

==== `Prefixes` configuration
Expand Down Expand Up @@ -564,6 +567,29 @@ context 'when the display name is not present' do
end
----

==== `AllowedPatterns` configuration

[source,ruby]
----
# .rubocop.yml
# RSpec/ContextWording:
# AllowedPatterns:
# - /success$/
----

[source,ruby]
----
# bad
context 'when this test is failed' do
# ...
end
# good
context 'when this test is success' do
# ...
end
----

=== Configurable attributes

|===
Expand All @@ -572,6 +598,10 @@ end
| Prefixes
| `when`, `with`, `without`
| Array

| AllowedPatterns
| `[]`
| Array
|===

=== References
Expand Down
65 changes: 52 additions & 13 deletions lib/rubocop/cop/rspec/context_wording.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,41 +35,80 @@ module RSpec
# # ...
# 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:
# # - /success$/
#
# @example
# # bad
# context 'when this test is failed' do
# # ...
# end
#
# # good
# context 'when this test is success' do
# # ...
# end
#
class ContextWording < Base
MSG = 'Start context description with %<prefixes>s.'
include AllowedPattern

PREFIX_MSG = 'Start context description with %<prefixes>s.'
PATTERN_MSG = 'Context description is not descriptions ' \
'of allowed patterns.'

# @!method context_wording(node)
def_node_matcher :context_wording, <<-PATTERN
(block (send #rspec? { :context :shared_context } $(str #bad_prefix?) ...) ...)
(block (send #rspec? { :context :shared_context } $(str $_) ...) ...)
PATTERN

def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
context_wording(node) do |context|
add_offense(context,
message: format(MSG, prefixes: joined_prefixes))
context_wording(node) do |context, description|
offense_prefix(context) if bad_prefix?(description)
offense_patterns(context) if bad_pattern?(description)
end
end

private

def offense_prefix(context)
add_offense(context,
message: format(PREFIX_MSG,
prefixes: expect_word(prefixes)))
end

def offense_patterns(context)
add_offense(context, message: format(PATTERN_MSG))
end

def bad_prefix?(description)
!prefix_regex.match?(description)
return false if prefixes.empty?

!/^#{Regexp.union(prefixes)}\b/.match?(description)
end

def bad_pattern?(description)
return false if allowed_patterns.empty?

!matches_allowed_pattern?(description)
end

def joined_prefixes
quoted = prefixes.map { |prefix| "'#{prefix}'" }
def expect_word(config_values)
quoted = config_values.map { |value| "'#{value}'" }
return quoted.first if quoted.size == 1

quoted << "or #{quoted.pop}"
quoted.join(', ')
end

def prefixes
cop_config['Prefixes'] || []
end

def prefix_regex
/^#{Regexp.union(prefixes)}\b/
cop_config['Prefixes']
end
end
end
Expand Down
40 changes: 39 additions & 1 deletion spec/rubocop/cop/rspec/context_wording_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::ContextWording do
let(:cop_config) { { 'Prefixes' => %w[when with] } }
let(:cop_config) do
{ 'Prefixes' => %w[when with], 'AllowedPatterns' => %w[] }
end

it 'skips describe blocks' do
expect_no_offenses(<<-RUBY)
Expand Down Expand Up @@ -125,5 +127,41 @@
RUBY
end
end

context "when `AllowedPatterns: ['success']`" do
let(:cop_config) do
{ 'Prefixes' => %w[when with], 'AllowedPatterns' => [/success$/] }
end

it 'finds context without `success` at the ending' do
expect_offense(<<-RUBY)
context 'when the display name not present' do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Context description is not descriptions of allowed patterns.
end
RUBY
end

it 'finds shared_context without `success` at the ending' do
expect_offense(<<-RUBY)
shared_context 'when the display name not present' do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Context description is not descriptions of allowed patterns.
end
RUBY
end

it "skips descriptions ending with 'success'" do
expect_no_offenses(<<-RUBY)
context 'when the display name is not present success' do
end
RUBY
end

it "skips descriptions ending with 'unsuccess'" do
expect_no_offenses(<<-RUBY)
context 'when the display name is not unsuccess' do
end
RUBY
end
end
end
end

0 comments on commit 9f7cdd7

Please sign in to comment.