diff --git a/CHANGELOG.md b/CHANGELOG.md index 47e0ca077..57318ba7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Add `require_implicit` style to `RSpec/ImplicitSubject`. ([@r7kamura][]) * Fix a false positive for `RSpec/Capybara/SpecificMatcher` when `have_css("a")` without attribute. ([@ydah][]) +* Update `RSpec/ExampleWording` cop to raise error for insufficient descriptions. ([@akrox58][]) ## 2.13.2 (2022-09-23) @@ -650,6 +651,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@abrom]: https://github.com/abrom [@ahukkanen]: https://github.com/ahukkanen [@akiomik]: https://github.com/akiomik +[@akrox58]: https://github.com/akrox58 [@AlexWayfer]: https://github.com/AlexWayfer [@andrykonchin]: https://github.com/andrykonchin [@andyw8]: https://github.com/andyw8 diff --git a/config/default.yml b/config/default.yml index fa622f46d..86d99a964 100644 --- a/config/default.yml +++ b/config/default.yml @@ -194,7 +194,7 @@ RSpec/ChangeByZero: Description: Prefer negated matchers over `to change.by(0)`. Enabled: pending VersionAdded: '2.11' - VersionChanged: '2.13' + VersionChanged: "<>" Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ChangeByZero NegatedMatcher: ~ @@ -383,8 +383,10 @@ RSpec/ExampleWording: have: has HAVE: HAS IgnoredWords: [] + DisallowedExamples: + - works VersionAdded: '1.0' - VersionChanged: '1.2' + VersionChanged: '2.13' StyleGuide: https://rspec.rubystyle.guide/#should-in-example-docstrings Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleWording diff --git a/docs/modules/ROOT/pages/cops_rspec.adoc b/docs/modules/ROOT/pages/cops_rspec.adoc index f7a1a004e..1eecedc6d 100644 --- a/docs/modules/ROOT/pages/cops_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_rspec.adoc @@ -392,7 +392,7 @@ end | Yes | Yes | 2.11 -| 2.13 +| <> |=== Prefer negated matchers over `to change.by(0)`. @@ -1534,16 +1534,21 @@ end | Yes | Yes | 1.0 -| 1.2 +| 2.13 |=== Checks for common mistakes in example descriptions. This cop will correct docstrings that begin with 'should' and 'it'. +This cop will also look for insufficient examples and call them out. The autocorrect is experimental - use with care! It can be configured with CustomTransform (e.g. have => has) and IgnoredWords (e.g. only). +Use the DisallowedExamples setting to prevent unclear or insufficient +descriptions. Please note that this config will not be treated as +case sensitive. + === Examples [source,ruby] @@ -1568,6 +1573,19 @@ it 'does things' do end ---- +==== `DisallowedExamples: ['works']` (default) + +[source,ruby] +---- +# bad +it 'works' do +end + +# good +it 'marks the task as done' do +end +---- + === Configurable attributes |=== @@ -1580,6 +1598,10 @@ end | IgnoredWords | `[]` | Array + +| DisallowedExamples +| `works` +| Array |=== === References diff --git a/lib/rubocop/cop/rspec/example_wording.rb b/lib/rubocop/cop/rspec/example_wording.rb index 90850b489..88703299f 100644 --- a/lib/rubocop/cop/rspec/example_wording.rb +++ b/lib/rubocop/cop/rspec/example_wording.rb @@ -6,12 +6,17 @@ module RSpec # Checks for common mistakes in example descriptions. # # This cop will correct docstrings that begin with 'should' and 'it'. + # This cop will also look for insufficient examples and call them out. # # @see http://betterspecs.org/#should # # The autocorrect is experimental - use with care! It can be configured # with CustomTransform (e.g. have => has) and IgnoredWords (e.g. only). # + # Use the DisallowedExamples setting to prevent unclear or insufficient + # descriptions. Please note that this config will not be treated as + # case sensitive. + # # @example # # bad # it 'should find nothing' do @@ -30,11 +35,21 @@ module RSpec # it 'does things' do # end # + # @example `DisallowedExamples: ['works']` (default) + # # bad + # it 'works' do + # end + # + # # good + # it 'marks the task as done' do + # end class ExampleWording < Base extend AutoCorrector MSG_SHOULD = 'Do not use should when describing your tests.' MSG_IT = "Do not repeat 'it' when describing your tests." + MSG_INSUFFICIENT_DESCRIPTION = 'Your example description is ' \ + 'insufficient.' SHOULD_PREFIX = /\Ashould(?:n't)?\b/i.freeze IT_PREFIX = /\Ait /i.freeze @@ -53,12 +68,20 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler add_wording_offense(description_node, MSG_SHOULD) elsif message.match?(IT_PREFIX) add_wording_offense(description_node, MSG_IT) + else + check_and_handle_insufficient_examples(description_node) end end end private + def check_and_handle_insufficient_examples(description) + if insufficient_examples.include?(preprocess(text(description))) + add_wording_offense(description, MSG_INSUFFICIENT_DESCRIPTION) + end + end + def add_wording_offense(node, message) docstring = docstring(node) @@ -113,6 +136,15 @@ def custom_transform def ignored_words cop_config.fetch('IgnoredWords', []) end + + def insufficient_examples + examples = cop_config.fetch('DisallowedExamples', []) + examples.map! { |example| preprocess(example) } + end + + def preprocess(message) + message.strip.squeeze(' ').downcase + end end end end diff --git a/spec/rubocop/cop/rspec/example_wording_spec.rb b/spec/rubocop/cop/rspec/example_wording_spec.rb index 2d7798bec..0a76ad836 100644 --- a/spec/rubocop/cop/rspec/example_wording_spec.rb +++ b/spec/rubocop/cop/rspec/example_wording_spec.rb @@ -187,4 +187,65 @@ end RUBY end + + it 'flags an unclear description' do + expect_offense(<<-'RUBY') + it "works" do + ^^^^^ Your example description is insufficient. + end + RUBY + end + + it 'flags an unclear description despite extra spaces' do + expect_offense(<<-'RUBY') + it " works " do + ^^^^^^^^^^^ Your example description is insufficient. + end + RUBY + end + + it 'flags an unclear description despite uppercase and lowercase strings' do + expect_offense(<<-'RUBY') + it "WOrKs " do + ^^^^^^ Your example description is insufficient. + end + RUBY + end + + context 'when `DisallowedExamples: Workz`' do + let(:cop_config) { { 'DisallowedExamples' => ['Workz'] } } + + it 'finds a valid sentence across two lines' do + expect_no_offenses(<<-'RUBY') + it "workz " \ + "totally fine " do + end + RUBY + end + + it 'finds an invalid example across two lines' do + expect_offense(<<-'RUBY') + it "workz" \ + ^^^^^^^^ Your example description is insufficient. + " " do + end + RUBY + end + + it 'flags an unclear description' do + expect_offense(<<-'RUBY') + it "workz" do + ^^^^^ Your example description is insufficient. + end + RUBY + end + + it 'flags an unclear description despite uppercase and lowercase strings' do + expect_offense(<<-'RUBY') + it "WOrKz " do + ^^^^^^ Your example description is insufficient. + end + RUBY + end + end end