Skip to content

Commit 6be3abd

Browse files
Rafael Menesespirj
authored andcommitted
Add IgnoredMetadata configuration option to RSpec/DescribeClass
1 parent 26c6d4a commit 6be3abd

File tree

5 files changed

+129
-86
lines changed

5 files changed

+129
-86
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
* Fix `RSpec/FilePath` when checking a file with a shared example. ([@pirj][])
1414
* Fix subject nesting detection in `RSpec/LeadingSubject`. ([@pirj][])
15+
* Add `IgnoredMetadata` configuration option to `RSpec/DescribeClass`. ([@Rafix02][])
1516

1617
## 1.43.1 (2020-08-17)
1718

@@ -563,3 +564,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
563564
[@mockdeep]: https://github.com/mockdeep
564565
[@biinari]: https://github.com/biinari
565566
[@koic]: https://github.com/koic
567+
[@Rafix02]: https://github.com/Rafix02

config/default.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,22 @@ RSpec/ContextWording:
7676
RSpec/DescribeClass:
7777
Description: Check that the first argument to the top-level describe is a constant.
7878
Enabled: true
79+
IgnoredMetadata:
80+
type:
81+
- channel
82+
- controller
83+
- helper
84+
- job
85+
- mailer
86+
- model
87+
- request
88+
- routing
89+
- view
90+
- feature
91+
- system
92+
- mailbox
7993
VersionAdded: '1.0'
94+
VersionChanged: '1.44'
8095
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass
8196

8297
RSpec/DescribeMethod:

docs/modules/ROOT/pages/cops_rspec.adoc

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,29 @@ end
398398
| Yes
399399
| No
400400
| 1.0
401-
| -
401+
| 1.44
402402
|===
403403

404404
Check that the first argument to the top-level describe is a constant.
405405

406+
It can be configured to ignore strings when certain metadata is passed.
407+
408+
Ignores Rails `type` metadata by default.
409+
406410
=== Examples
407411

412+
==== `IgnoredMetadata` configuration
413+
414+
[source,ruby]
415+
----
416+
# .rubocop.yml
417+
# RSpec/DescribeClass:
418+
# IgnoredMetadata:
419+
# type:
420+
# - request
421+
# - controller
422+
----
423+
408424
[source,ruby]
409425
----
410426
# bad
@@ -424,6 +440,16 @@ describe "A feature example", type: :feature do
424440
end
425441
----
426442

443+
=== Configurable attributes
444+
445+
|===
446+
| Name | Default value | Configurable values
447+
448+
| IgnoredMetadata
449+
| `{"type"=>["channel", "controller", "helper", "job", "mailer", "model", "request", "routing", "view", "feature", "system", "mailbox"]}`
450+
|
451+
|===
452+
427453
=== References
428454

429455
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass

lib/rubocop/cop/rspec/describe_class.rb

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ module Cop
55
module RSpec
66
# Check that the first argument to the top-level describe is a constant.
77
#
8+
# It can be configured to ignore strings when certain metadata is passed.
9+
#
10+
# Ignores Rails `type` metadata by default.
11+
#
12+
# @example `IgnoredMetadata` configuration
13+
#
14+
# # .rubocop.yml
15+
# # RSpec/DescribeClass:
16+
# # IgnoredMetadata:
17+
# # type:
18+
# # - request
19+
# # - controller
20+
#
821
# @example
922
# # bad
1023
# describe 'Do something' do
@@ -27,36 +40,42 @@ class DescribeClass < Base
2740
MSG = 'The first argument to describe should be '\
2841
'the class or module being tested.'
2942

30-
def_node_matcher :rails_metadata?, <<-PATTERN
31-
(pair
32-
(sym :type)
33-
(sym { :channel :controller :helper :job :mailer :model :request
34-
:routing :view :feature :system :mailbox })
35-
)
36-
PATTERN
37-
38-
def_node_matcher :example_group_with_rails_metadata?, <<~PATTERN
39-
(send #rspec? :describe ... (hash <#rails_metadata? ...>))
43+
def_node_matcher :example_group_with_ignored_metadata?, <<~PATTERN
44+
(send #rspec? :describe ... (hash <#ignored_metadata? ...>))
4045
PATTERN
4146

4247
def_node_matcher :not_a_const_described, <<~PATTERN
4348
(send #rspec? :describe $[!const !#string_constant?] ...)
4449
PATTERN
4550

46-
def on_top_level_group(top_level_node)
47-
return if example_group_with_rails_metadata?(top_level_node.send_node)
51+
def_node_matcher :sym_pair?, <<~PATTERN
52+
(pair $sym $sym)
53+
PATTERN
4854

49-
not_a_const_described(top_level_node.send_node) do |described|
55+
def on_top_level_group(node)
56+
return if example_group_with_ignored_metadata?(node.send_node)
57+
58+
not_a_const_described(node.send_node) do |described|
5059
add_offense(described)
5160
end
5261
end
5362

5463
private
5564

65+
def ignored_metadata?(node)
66+
sym_pair?(node) do |key, value|
67+
ignored_metadata[key.value.to_s].to_a.include?(value.value.to_s)
68+
end
69+
end
70+
5671
def string_constant?(described)
5772
described.str_type? &&
5873
described.value.match?(/^(?:(?:::)?[A-Z]\w*)+$/)
5974
end
75+
76+
def ignored_metadata
77+
cop_config['IgnoredMetadata'] || {}
78+
end
6079
end
6180
end
6281
end

spec/rubocop/cop/rspec/describe_class_spec.rb

Lines changed: 53 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# frozen_string_literal: true
22

3-
RSpec.describe RuboCop::Cop::RSpec::DescribeClass do
4-
subject(:cop) { described_class.new }
5-
3+
RSpec.describe RuboCop::Cop::RSpec::DescribeClass, :config do
64
it 'checks first-line describe statements' do
75
expect_offense(<<-RUBY)
86
describe "bad describe" do
@@ -120,57 +118,6 @@
120118
end
121119
end
122120

123-
it 'ignores request specs' do
124-
expect_no_offenses(<<-RUBY)
125-
describe 'my new feature', type: :request do
126-
end
127-
RUBY
128-
end
129-
130-
it 'ignores feature specs' do
131-
expect_no_offenses(<<-RUBY)
132-
describe 'my new feature', type: :feature do
133-
end
134-
RUBY
135-
end
136-
137-
it 'ignores system specs' do
138-
expect_no_offenses(<<-RUBY)
139-
describe 'my new system test', type: :system do
140-
end
141-
RUBY
142-
end
143-
144-
it 'ignores feature specs when RSpec.describe is used' do
145-
expect_no_offenses(<<-RUBY)
146-
RSpec.describe 'my new feature', type: :feature do
147-
end
148-
RUBY
149-
end
150-
151-
it 'flags specs with non :type metadata' do
152-
expect_offense(<<-RUBY)
153-
describe 'my new feature', foo: :feature do
154-
^^^^^^^^^^^^^^^^ The first argument to describe should be the class or module being tested.
155-
end
156-
RUBY
157-
end
158-
159-
it 'flags normal metadata in describe' do
160-
expect_offense(<<-RUBY)
161-
describe 'my new feature', blah, type: :wow do
162-
^^^^^^^^^^^^^^^^ The first argument to describe should be the class or module being tested.
163-
end
164-
RUBY
165-
end
166-
167-
it 'ignores feature specs - also with complex options' do
168-
expect_no_offenses(<<-RUBY)
169-
describe 'my new feature', :test, :type => :feature, :foo => :bar do
170-
end
171-
RUBY
172-
end
173-
174121
it 'ignores an empty describe' do
175122
expect_no_offenses(<<-RUBY)
176123
RSpec.describe do
@@ -181,24 +128,6 @@
181128
RUBY
182129
end
183130

184-
it 'ignores routing specs' do
185-
expect_no_offenses(<<-RUBY)
186-
describe 'my new route', type: :routing do
187-
end
188-
RUBY
189-
end
190-
191-
it 'ignores view specs' do
192-
expect_no_offenses(<<-RUBY)
193-
describe 'widgets/index', type: :view do
194-
end
195-
RUBY
196-
end
197-
198-
it "doesn't blow up on single-line describes" do
199-
expect_no_offenses('describe Some::Class')
200-
end
201-
202131
it "doesn't flag top level describe in a shared example" do
203132
expect_no_offenses(<<-RUBY)
204133
shared_examples 'Common::Interface' do
@@ -234,4 +163,56 @@
234163
end
235164
RUBY
236165
end
166+
167+
it 'ignores `type` metadata ignored by default' do
168+
expect_no_offenses(<<-RUBY)
169+
describe 'widgets/index', type: :view do
170+
end
171+
RUBY
172+
end
173+
174+
it 'flags specs with non `type` metadata' do
175+
expect_offense(<<-RUBY)
176+
describe 'foo bar', foo: :bar do
177+
^^^^^^^^^ The first argument to describe should be the class or module being tested.
178+
end
179+
RUBY
180+
end
181+
182+
it 'ignores feature specs - also with complex options' do
183+
expect_no_offenses(<<-RUBY)
184+
describe 'my new feature', :test, foo: :bar, type: :feature do
185+
end
186+
RUBY
187+
end
188+
189+
it 'flags non-ignored `type` metadata' do
190+
expect_offense(<<-RUBY)
191+
describe 'wow', blah, type: :wow do
192+
^^^^^ The first argument to describe should be the class or module being tested.
193+
end
194+
RUBY
195+
end
196+
197+
context 'when IgnoredMetadata is configured' do
198+
let(:cop_config) do
199+
{ 'IgnoredMetadata' =>
200+
{ 'foo' => ['bar'],
201+
'type' => ['wow'] } }
202+
end
203+
204+
it 'ignores configured metadata' do
205+
expect_no_offenses(<<-RUBY)
206+
describe 'foo bar', foo: :bar do
207+
end
208+
RUBY
209+
end
210+
211+
it 'ignores configured `type` metadata' do
212+
expect_no_offenses(<<-RUBY)
213+
describe 'my new system test', type: :wow do
214+
end
215+
RUBY
216+
end
217+
end
237218
end

0 commit comments

Comments
 (0)