Skip to content
Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Fix false positive in `RSpec/Pending`, where it would mark the default block `it` as an offense. ([@bquorning])
- Fix issue when `Style/ContextWording` is configured with a Prefix being interpreted as a boolean, like `on`. ([@sakuro])
- Add new `RSpec/IncludeExamples` cop to enforce using `it_behaves_like` over `include_examples`. ([@dvandersluis])
- Change `RSpec/ScatteredSetup` to allow `around` hooks to be scattered. ([@ydah])
Comment thread
pirj marked this conversation as resolved.
Outdated
- Fix an error `RSpec/ChangeByZero` cop when without expect block. ([@lee266])

## 3.5.0 (2025-02-16)

Expand Down Expand Up @@ -1002,6 +1004,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
[@krororo]: https://github.com/krororo
[@kuahyeow]: https://github.com/kuahyeow
[@lazycoder9]: https://github.com/lazycoder9
[@lee266]: https://github.com/lee266
[@leoarnold]: https://github.com/leoarnold
[@liberatys]: https://github.com/Liberatys
[@lokhi]: https://github.com/lokhi
Expand Down
10 changes: 9 additions & 1 deletion docs/modules/ROOT/pages/cops_rspec.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5524,7 +5524,9 @@ end

Checks for setup scattered across multiple hooks in an example group.

Unify `before`, `after`, and `around` hooks when possible.
Unify `before` and `after` hooks when possible.
However, `around` hooks are allowed to be defined multiple times,
as unifying them would typically make the code harder to read.

[#examples-rspecscatteredsetup]
=== Examples
Expand All @@ -5544,6 +5546,12 @@ describe Foo do
setup2
end
end

# good
describe Foo do
around { |example| before1; example.call; after1 }
around { |example| before2; example.call; after2 }
end
----

[#references-rspecscatteredsetup]
Expand Down
5 changes: 3 additions & 2 deletions lib/rubocop/cop/rspec/change_by_zero.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ def on_send(node)
private

def register_offense(node, change_node)
return unless node.parent.send_type?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated?


if compound_expectations?(node)
add_offense(node,
message: message_compound(change_node)) do |corrector|
Expand All @@ -116,8 +118,7 @@ def register_offense(node, change_node)
end

def compound_expectations?(node)
node.parent.send_type? &&
%i[and or & |].include?(node.parent.method_name)
%i[and or & |].include?(node.parent.method_name)
end

def message(change_node)
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/rspec/empty_example_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class EmptyExampleGroup < Base
PATTERN

def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
return if node.each_ancestor(:def, :defs).any?
return if node.each_ancestor(:any_def).any?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, too, and related specs

return if node.each_ancestor(:block).any? { |block| example?(block) }

example_group_body(node) do |body|
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/rspec/focus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Focus < Base
PATTERN

def on_send(node)
return if node.chained? || node.each_ancestor(:def, :defs).any?
return if node.chained? || node.each_ancestor(:any_def).any?

if focused_block?(node)
on_focused_block(node)
Expand Down
12 changes: 10 additions & 2 deletions lib/rubocop/cop/rspec/scattered_setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ module Cop
module RSpec
# Checks for setup scattered across multiple hooks in an example group.
#
# Unify `before`, `after`, and `around` hooks when possible.
# Unify `before` and `after` hooks when possible.
# However, `around` hooks are allowed to be defined multiple times,
# as unifying them would typically make the code harder to read.
#
# @example
# # bad
Expand All @@ -22,6 +24,12 @@ module RSpec
# end
# end
#
# # good
# describe Foo do
# around { |example| before1; example.call; after1 }
# around { |example| before2; example.call; after2 }
# end
#
class ScatteredSetup < Base
include FinalEndLocation
include RangeHelp
Expand All @@ -48,7 +56,7 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
def repeated_hooks(node)
hooks = RuboCop::RSpec::ExampleGroup.new(node)
.hooks
.select(&:knowable_scope?)
.select { |hook| hook.knowable_scope? && hook.name != :around }
.group_by { |hook| [hook.name, hook.scope, hook.metadata] }
.values
.reject(&:one?)
Expand Down
9 changes: 8 additions & 1 deletion lib/rubocop/cop/rspec/spec_file_path_format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ module RSpec
# # good
# whatever_spec.rb # describe MyClass, type: :routing do; end
#
# @example `IgnoreMetadata: {type=>[routing,models]}` (default)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really configured like this in YAML?

# # good
# whatever_spec.rb # describe MyClass, type: :routing do; end
# whatever_spec.rb # describe MyClass, type: :models do; end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the metadata's value IS an array?

RSpec.describe Airplane, prepare: [:fuel] do

How do we define IgnoreMetadata in YAML?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: how do we ignore any value for a key?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm hinting towards:

IgnoreMetadata:
  - prepare
  - type: model
  - type: routing
  - skip: [database]

Luckily enough, YAML allows to mix all that, and to cover all ignored cases unambiguously.

#
class SpecFilePathFormat < Base
include TopLevelGroup
include Namespace
Expand Down Expand Up @@ -73,7 +78,9 @@ def ensure_correct_file_path(send_node, class_name, arguments)
def ignore_metadata?(arguments)
arguments.any? do |argument|
metadata_key_value(argument).any? do |key, value|
ignore_metadata.values_at(key.to_s).include?(value.to_s)
ignore_values = Array(ignore_metadata[key.to_s])

ignore_values.include?(value.to_s)
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions spec/rubocop/cop/rspec/change_by_zero_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,12 @@
end
RUBY
end

it 'does not register an offense when without expect block' do
expect_no_offenses(<<~RUBY)
it do
change(foo, :bar).by(0)
end
RUBY
end
end
9 changes: 9 additions & 0 deletions spec/rubocop/cop/rspec/scattered_setup_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@
RUBY
end

it 'ignores around hooks' do
expect_no_offenses(<<~RUBY)
describe Foo do
around { bar }
around { baz }
end
RUBY
end

it 'ignores different hooks' do
expect_no_offenses(<<~RUBY)
describe Foo do
Expand Down
31 changes: 31 additions & 0 deletions spec/rubocop/cop/rspec/spec_file_path_format_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,35 @@ class Foo
RUBY
end
end

context \
'when configured with `IgnoreMetadata: { "foo" => ["bar", "baz"] }`' do
let(:cop_config) { { 'IgnoreMetadata' => { 'foo' => %w[bar baz] } } }

it 'registers no offense when including an ignored metadata value' do
expect_no_offenses(<<~RUBY, 'wrong_class_spec.rb')
describe MyClass, foo: :bar do; end
RUBY
end

it 'registers no offense when including another ignored metadata value' do
expect_no_offenses(<<~RUBY, 'wrong_class_spec.rb')
describe MyClass, foo: :baz do; end
RUBY
end

it 'registers an offense when the metadata is not to be ignored' do
expect_offense(<<~RUBY, 'wrong_class_spec.rb')
describe MyClass, foo: :quux do; end
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Spec path should end with `my_class*_spec.rb`.
RUBY
end

it 'registers an offense when no metadata is present' do
expect_offense(<<~RUBY, 'wrong_class_spec.rb')
describe MyClass do; end
^^^^^^^^^^^^^^^^ Spec path should end with `my_class*_spec.rb`.
RUBY
end
end
end