rubocop-rspec 1.43.0 → 2.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -3
- data/config/default.yml +42 -8
- data/lib/rubocop/cop/rspec/around_block.rb +1 -1
- data/lib/rubocop/cop/rspec/base.rb +2 -0
- data/lib/rubocop/cop/rspec/describe_class.rb +32 -13
- data/lib/rubocop/cop/rspec/empty_example_group.rb +35 -1
- data/lib/rubocop/cop/rspec/file_path.rb +1 -1
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -2
- data/lib/rubocop/cop/rspec/leading_subject.rb +5 -1
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +103 -0
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +172 -0
- data/lib/rubocop/cop/rspec/subject_stub.rb +4 -4
- data/lib/rubocop/cop/rspec_cops.rb +2 -0
- data/lib/rubocop/rspec/hook.rb +1 -5
- data/lib/rubocop/rspec/language.rb +6 -1
- data/lib/rubocop/rspec/top_level_group.rb +6 -4
- data/lib/rubocop/rspec/version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0940bab64e9ccca8168ff22b5d0f46e6777d015316da687deca6072f0e8c25f0'
|
|
4
|
+
data.tar.gz: c7a518dbbcbcb63567b4491849a8ef79834051350614a8b8bf284f7f8a31f83d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f8b127bdccb77c1f85268adc1cd60f614423914f5889557e71b59a36b040e8dc8c42ad409b4310dd039d5621f928bac3f60293d07e6f419762c02ddfbd5ddb0
|
|
7
|
+
data.tar.gz: 23922bf8587a2ac38a1bf76b973ba64ec586cf0ea6d32f65602257fd9782ddc8d4553ff964f9ff833c423e19bdb59f733593ca91539ecab709e0b6b29bf3ae53
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Master (Unreleased)
|
|
4
4
|
|
|
5
|
+
## 2.0.0.pre (2020-10-22)
|
|
6
|
+
|
|
7
|
+
* Update RuboCop dependency to v1.0.0. ([@bquorning][])
|
|
8
|
+
* **(Potentially breaking)** Change namespace of several cops (`Capybara/*` -> `RSpec/Capybara/*`, `FactoryBot/*` -> `RSpec/FactoryBot/*`, `Rails/*` -> `RSpec/Rails/*`). ([@pirj][], [@bquorning][])
|
|
9
|
+
|
|
10
|
+
## 1.44.1 (2020-10-20)
|
|
11
|
+
|
|
12
|
+
* Relax `rubocop-ast` version constraint. ([@PhilCoggins][])
|
|
13
|
+
|
|
14
|
+
## 1.44.0 (2020-10-20)
|
|
15
|
+
|
|
16
|
+
* Move our documentation from rubocop-rspec.readthedocs.io to docs.rubocop.org/rubocop-rspec. ([@bquorning][])
|
|
17
|
+
* Add `RSpec/RepeatedIncludeExample` cop. ([@biinari][])
|
|
18
|
+
* Add `RSpec/StubbedMock` cop. ([@bquorning][], [@pirj][])
|
|
19
|
+
* Add `IgnoredMetadata` configuration option to `RSpec/DescribeClass`. ([@Rafix02][])
|
|
20
|
+
* Fix false positives in `RSpec/EmptyExampleGroup`. ([@pirj][])
|
|
21
|
+
* Fix a false positive for `RSpec/EmptyExampleGroup` when example is defined in an `if` branch. ([@koic][])
|
|
22
|
+
|
|
23
|
+
## 1.43.2 (2020-08-25)
|
|
24
|
+
|
|
25
|
+
* Fix `RSpec/FilePath` when checking a file with a shared example. ([@pirj][])
|
|
26
|
+
* Fix subject nesting detection in `RSpec/LeadingSubject`. ([@pirj][])
|
|
27
|
+
|
|
28
|
+
## 1.43.1 (2020-08-17)
|
|
29
|
+
|
|
30
|
+
* Fix `RSpec/FilePath` when checking a file defining e.g. an empty class. ([@bquorning][])
|
|
31
|
+
|
|
5
32
|
## 1.43.0 (2020-08-17)
|
|
6
33
|
|
|
7
34
|
* Add a new base cop class `::RuboCop::Cop::RSpec::Base`. The old base class `::RuboCop::Cop::RSpec::Cop` is deprecated, and will be removed in the next major release. ([@bquorning][])
|
|
@@ -497,8 +524,8 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
497
524
|
[@rspeicher]: https://github.com/rspeicher
|
|
498
525
|
[@jonatas]: https://github.com/jonatas
|
|
499
526
|
[@pocke]: https://github.com/pocke
|
|
500
|
-
[@bmorrall]: https
|
|
501
|
-
[@zverok]: https
|
|
527
|
+
[@bmorrall]: https://github.com/bmorrall
|
|
528
|
+
[@zverok]: https://github.com/zverok
|
|
502
529
|
[@timrogers]: https://github.com/timrogers
|
|
503
530
|
[@yevhene]: https://github.com/yevhene
|
|
504
531
|
[@walf443]: https://github.com/walf443
|
|
@@ -543,6 +570,9 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
543
570
|
[@andrykonchin]: https://github.com/andrykonchin
|
|
544
571
|
[@harrylewis]: https://github.com/harrylewis
|
|
545
572
|
[@elliterate]: https://github.com/elliterate
|
|
546
|
-
[@mlarraz]: https://github.com/mlarraz
|
|
547
573
|
[@jtannas]: https://github.com/jtannas
|
|
548
574
|
[@mockdeep]: https://github.com/mockdeep
|
|
575
|
+
[@biinari]: https://github.com/biinari
|
|
576
|
+
[@koic]: https://github.com/koic
|
|
577
|
+
[@Rafix02]: https://github.com/Rafix02
|
|
578
|
+
[@PhilCoggins]: https://github.com/PhilCoggins
|
data/config/default.yml
CHANGED
|
@@ -76,7 +76,23 @@ RSpec/ContextWording:
|
|
|
76
76
|
RSpec/DescribeClass:
|
|
77
77
|
Description: Check that the first argument to the top-level describe is a constant.
|
|
78
78
|
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
|
|
93
|
+
- aruba
|
|
79
94
|
VersionAdded: '1.0'
|
|
95
|
+
VersionChanged: '1.44'
|
|
80
96
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass
|
|
81
97
|
|
|
82
98
|
RSpec/DescribeMethod:
|
|
@@ -494,6 +510,12 @@ RSpec/RepeatedExampleGroupDescription:
|
|
|
494
510
|
VersionAdded: '1.38'
|
|
495
511
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription
|
|
496
512
|
|
|
513
|
+
RSpec/RepeatedIncludeExample:
|
|
514
|
+
Description: Check for repeated include of shared examples.
|
|
515
|
+
Enabled: true
|
|
516
|
+
VersionAdded: '1.44'
|
|
517
|
+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedIncludeExample
|
|
518
|
+
|
|
497
519
|
RSpec/ReturnFromStub:
|
|
498
520
|
Description: Checks for consistent style of stub's return setting.
|
|
499
521
|
Enabled: true
|
|
@@ -537,6 +559,12 @@ RSpec/SingleArgumentMessageChain:
|
|
|
537
559
|
VersionChanged: '1.10'
|
|
538
560
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SingleArgumentMessageChain
|
|
539
561
|
|
|
562
|
+
RSpec/StubbedMock:
|
|
563
|
+
Description: Checks that message expectations do not have a configured response.
|
|
564
|
+
Enabled: pending
|
|
565
|
+
VersionAdded: '1.44'
|
|
566
|
+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/StubbedMock
|
|
567
|
+
|
|
540
568
|
RSpec/SubjectStub:
|
|
541
569
|
Description: Checks for stubbed test subjects.
|
|
542
570
|
Enabled: true
|
|
@@ -592,33 +620,36 @@ RSpec/Yield:
|
|
|
592
620
|
VersionAdded: '1.32'
|
|
593
621
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Yield
|
|
594
622
|
|
|
595
|
-
Capybara/CurrentPathExpectation:
|
|
623
|
+
RSpec/Capybara/CurrentPathExpectation:
|
|
596
624
|
Description: Checks that no expectations are set on Capybara's `current_path`.
|
|
597
625
|
Enabled: true
|
|
598
626
|
VersionAdded: '1.18'
|
|
627
|
+
VersionChanged: '2.0'
|
|
599
628
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/CurrentPathExpectation
|
|
600
629
|
|
|
601
|
-
Capybara/FeatureMethods:
|
|
630
|
+
RSpec/Capybara/FeatureMethods:
|
|
602
631
|
Description: Checks for consistent method usage in feature specs.
|
|
603
632
|
Enabled: true
|
|
604
633
|
EnabledMethods: []
|
|
605
634
|
VersionAdded: '1.17'
|
|
606
|
-
VersionChanged: '
|
|
635
|
+
VersionChanged: '2.0'
|
|
607
636
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods
|
|
608
637
|
|
|
609
|
-
Capybara/VisibilityMatcher:
|
|
638
|
+
RSpec/Capybara/VisibilityMatcher:
|
|
610
639
|
Description: Checks for boolean visibility in capybara finders.
|
|
611
640
|
Enabled: true
|
|
612
641
|
VersionAdded: '1.39'
|
|
642
|
+
VersionChanged: '2.0'
|
|
613
643
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher
|
|
614
644
|
|
|
615
|
-
FactoryBot/AttributeDefinedStatically:
|
|
645
|
+
RSpec/FactoryBot/AttributeDefinedStatically:
|
|
616
646
|
Description: Always declare attribute values as blocks.
|
|
617
647
|
Enabled: true
|
|
618
648
|
VersionAdded: '1.28'
|
|
649
|
+
VersionChanged: '2.0'
|
|
619
650
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/AttributeDefinedStatically
|
|
620
651
|
|
|
621
|
-
FactoryBot/CreateList:
|
|
652
|
+
RSpec/FactoryBot/CreateList:
|
|
622
653
|
Description: Checks for create_list usage.
|
|
623
654
|
Enabled: true
|
|
624
655
|
EnforcedStyle: create_list
|
|
@@ -626,15 +657,17 @@ FactoryBot/CreateList:
|
|
|
626
657
|
- create_list
|
|
627
658
|
- n_times
|
|
628
659
|
VersionAdded: '1.25'
|
|
660
|
+
VersionChanged: '2.0'
|
|
629
661
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/CreateList
|
|
630
662
|
|
|
631
|
-
FactoryBot/FactoryClassName:
|
|
663
|
+
RSpec/FactoryBot/FactoryClassName:
|
|
632
664
|
Description: Use string value when setting the class attribute explicitly.
|
|
633
665
|
Enabled: true
|
|
634
666
|
VersionAdded: '1.37'
|
|
667
|
+
VersionChanged: '2.0'
|
|
635
668
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryClassName
|
|
636
669
|
|
|
637
|
-
Rails/HttpStatus:
|
|
670
|
+
RSpec/Rails/HttpStatus:
|
|
638
671
|
Description: Enforces use of symbolic or numeric value to describe HTTP status.
|
|
639
672
|
Enabled: true
|
|
640
673
|
EnforcedStyle: symbolic
|
|
@@ -642,4 +675,5 @@ Rails/HttpStatus:
|
|
|
642
675
|
- numeric
|
|
643
676
|
- symbolic
|
|
644
677
|
VersionAdded: '1.23'
|
|
678
|
+
VersionChanged: '2.0'
|
|
645
679
|
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus
|
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
|
31
31
|
'or `%<arg>s.run`.'
|
|
32
32
|
|
|
33
33
|
def_node_matcher :hook, <<-PATTERN
|
|
34
|
-
(block
|
|
34
|
+
(block (send nil? :around sym ?) (args $...) ...)
|
|
35
35
|
PATTERN
|
|
36
36
|
|
|
37
37
|
def_node_search :find_arg_usage, <<-PATTERN
|
|
@@ -29,6 +29,8 @@ module RuboCop
|
|
|
29
29
|
.map(&Regexp.public_method(:new))
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
+
exclude_from_registry
|
|
33
|
+
|
|
32
34
|
# Invoke the original inherited hook so our cops are recognized
|
|
33
35
|
def self.inherited(subclass) # rubocop:disable Lint/MissingSuper
|
|
34
36
|
RuboCop::Cop::Base.inherited(subclass)
|
|
@@ -5,6 +5,19 @@ module RuboCop
|
|
|
5
5
|
module RSpec
|
|
6
6
|
# Check that the first argument to the top-level describe is a constant.
|
|
7
7
|
#
|
|
8
|
+
# It can be configured to ignore strings when certain metadata is passed.
|
|
9
|
+
#
|
|
10
|
+
# Ignores Rails and Aruba `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
|
+
#
|
|
8
21
|
# @example
|
|
9
22
|
# # bad
|
|
10
23
|
# describe 'Do something' do
|
|
@@ -27,36 +40,42 @@ module RuboCop
|
|
|
27
40
|
MSG = 'The first argument to describe should be '\
|
|
28
41
|
'the class or module being tested.'
|
|
29
42
|
|
|
30
|
-
def_node_matcher :
|
|
31
|
-
(
|
|
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? ...>))
|
|
40
45
|
PATTERN
|
|
41
46
|
|
|
42
47
|
def_node_matcher :not_a_const_described, <<~PATTERN
|
|
43
48
|
(send #rspec? :describe $[!const !#string_constant?] ...)
|
|
44
49
|
PATTERN
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
def_node_matcher :sym_pair, <<~PATTERN
|
|
52
|
+
(pair $sym $sym)
|
|
53
|
+
PATTERN
|
|
48
54
|
|
|
49
|
-
|
|
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|
|
|
50
59
|
add_offense(described)
|
|
51
60
|
end
|
|
52
61
|
end
|
|
53
62
|
|
|
54
63
|
private
|
|
55
64
|
|
|
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
|
+
|
|
56
71
|
def string_constant?(described)
|
|
57
72
|
described.str_type? &&
|
|
58
73
|
described.value.match?(/^(?:(?:::)?[A-Z]\w*)+$/)
|
|
59
74
|
end
|
|
75
|
+
|
|
76
|
+
def ignored_metadata
|
|
77
|
+
cop_config['IgnoredMetadata'] || {}
|
|
78
|
+
end
|
|
60
79
|
end
|
|
61
80
|
end
|
|
62
81
|
end
|
|
@@ -33,6 +33,11 @@ module RuboCop
|
|
|
33
33
|
# end
|
|
34
34
|
# end
|
|
35
35
|
#
|
|
36
|
+
# # good
|
|
37
|
+
# describe Bacon do
|
|
38
|
+
# pending 'will add tests later'
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
36
41
|
# @example configuration
|
|
37
42
|
#
|
|
38
43
|
# # .rubocop.yml
|
|
@@ -83,11 +88,14 @@ module RuboCop
|
|
|
83
88
|
# it_behaves_like 'an animal'
|
|
84
89
|
# it_behaves_like('a cat') { let(:food) { 'milk' } }
|
|
85
90
|
# it_has_root_access
|
|
91
|
+
# skip
|
|
92
|
+
# it 'will be implemented later'
|
|
86
93
|
#
|
|
87
94
|
# @param node [RuboCop::AST::Node]
|
|
88
95
|
# @return [Array<RuboCop::AST::Node>] matching nodes
|
|
89
96
|
def_node_matcher :example_or_group_or_include?, <<~PATTERN
|
|
90
97
|
{
|
|
98
|
+
#{Examples::ALL.send_pattern}
|
|
91
99
|
#{Examples::ALL.block_pattern}
|
|
92
100
|
#{ExampleGroups::ALL.block_pattern}
|
|
93
101
|
#{Includes::ALL.send_pattern}
|
|
@@ -152,13 +160,39 @@ module RuboCop
|
|
|
152
160
|
PATTERN
|
|
153
161
|
|
|
154
162
|
def on_block(node)
|
|
163
|
+
return if node.each_ancestor(:def, :defs).any?
|
|
164
|
+
return if node.each_ancestor(:block).any? { |block| example?(block) }
|
|
165
|
+
|
|
155
166
|
example_group_body(node) do |body|
|
|
156
|
-
add_offense(node.send_node)
|
|
167
|
+
add_offense(node.send_node) if offensive?(body)
|
|
157
168
|
end
|
|
158
169
|
end
|
|
159
170
|
|
|
160
171
|
private
|
|
161
172
|
|
|
173
|
+
def offensive?(body)
|
|
174
|
+
return true unless body
|
|
175
|
+
return false if conditionals_with_examples?(body)
|
|
176
|
+
|
|
177
|
+
if body.if_type?
|
|
178
|
+
!examples_in_branches?(body)
|
|
179
|
+
else
|
|
180
|
+
!examples?(body)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def conditionals_with_examples?(body)
|
|
185
|
+
return unless body.begin_type?
|
|
186
|
+
|
|
187
|
+
body.each_descendant(:if).any? do |if_node|
|
|
188
|
+
examples_in_branches?(if_node)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def examples_in_branches?(if_node)
|
|
193
|
+
if_node.branches.any? { |branch| examples?(branch) }
|
|
194
|
+
end
|
|
195
|
+
|
|
162
196
|
def custom_include?(method_name)
|
|
163
197
|
custom_include_methods.include?(method_name)
|
|
164
198
|
end
|
|
@@ -69,7 +69,7 @@ module RuboCop
|
|
|
69
69
|
|
|
70
70
|
def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))'
|
|
71
71
|
|
|
72
|
-
def
|
|
72
|
+
def on_top_level_example_group(node)
|
|
73
73
|
return unless top_level_groups.one?
|
|
74
74
|
|
|
75
75
|
const_described(node) do |send_node, described_class, arguments|
|
|
@@ -54,13 +54,17 @@ module RuboCop
|
|
|
54
54
|
private
|
|
55
55
|
|
|
56
56
|
def offending_node(node)
|
|
57
|
-
node.
|
|
57
|
+
parent(node).each_child_node.find do |sibling|
|
|
58
58
|
break if sibling.equal?(node)
|
|
59
59
|
|
|
60
60
|
yield sibling if offending?(sibling)
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def parent(node)
|
|
65
|
+
node.each_ancestor(:block).first.body
|
|
66
|
+
end
|
|
67
|
+
|
|
64
68
|
def autocorrect(corrector, node, sibling)
|
|
65
69
|
RuboCop::RSpec::Corrector::MoveNode.new(
|
|
66
70
|
node, corrector, processed_source
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpec
|
|
6
|
+
# Check for repeated include of shared examples.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
#
|
|
10
|
+
# # bad
|
|
11
|
+
# describe 'foo' do
|
|
12
|
+
# include_examples 'cool stuff'
|
|
13
|
+
# include_examples 'cool stuff'
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # bad
|
|
17
|
+
# describe 'foo' do
|
|
18
|
+
# it_behaves_like 'a cool', 'thing'
|
|
19
|
+
# it_behaves_like 'a cool', 'thing'
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# # bad
|
|
23
|
+
# context 'foo' do
|
|
24
|
+
# it_should_behave_like 'a duck'
|
|
25
|
+
# it_should_behave_like 'a duck'
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# # good
|
|
29
|
+
# describe 'foo' do
|
|
30
|
+
# include_examples 'cool stuff'
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# describe 'bar' do
|
|
34
|
+
# include_examples 'cool stuff'
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# # good
|
|
38
|
+
# describe 'foo' do
|
|
39
|
+
# it_behaves_like 'a cool', 'thing'
|
|
40
|
+
# it_behaves_like 'a cool', 'person'
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# # good
|
|
44
|
+
# context 'foo' do
|
|
45
|
+
# it_should_behave_like 'a duck'
|
|
46
|
+
# it_should_behave_like 'a goose'
|
|
47
|
+
# end
|
|
48
|
+
#
|
|
49
|
+
class RepeatedIncludeExample < Base
|
|
50
|
+
MSG = 'Repeated include of shared_examples %<name>s ' \
|
|
51
|
+
'on line(s) %<repeat>s'
|
|
52
|
+
|
|
53
|
+
def_node_matcher :several_include_examples?, <<-PATTERN
|
|
54
|
+
(begin <#include_examples? #include_examples? ...>)
|
|
55
|
+
PATTERN
|
|
56
|
+
|
|
57
|
+
def_node_matcher :include_examples?, Includes::EXAMPLES.send_pattern
|
|
58
|
+
|
|
59
|
+
def_node_matcher :shared_examples_name, <<-PATTERN
|
|
60
|
+
(send _ #{Includes::EXAMPLES.node_pattern_union} $_ ...)
|
|
61
|
+
PATTERN
|
|
62
|
+
|
|
63
|
+
def on_begin(node)
|
|
64
|
+
return unless several_include_examples?(node)
|
|
65
|
+
|
|
66
|
+
repeated_include_examples(node).each do |item, repeats|
|
|
67
|
+
add_offense(item, message: message(item, repeats))
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def repeated_include_examples(node)
|
|
74
|
+
node
|
|
75
|
+
.children
|
|
76
|
+
.select { |child| literal_include_examples?(child) }
|
|
77
|
+
.group_by { |child| signature_keys(child) }
|
|
78
|
+
.values
|
|
79
|
+
.reject(&:one?)
|
|
80
|
+
.flat_map { |items| add_repeated_lines(items) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def literal_include_examples?(node)
|
|
84
|
+
include_examples?(node) &&
|
|
85
|
+
node.arguments.all?(&:recursive_literal_or_const?)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def add_repeated_lines(items)
|
|
89
|
+
repeated_lines = items.map(&:first_line)
|
|
90
|
+
items.map { |item| [item, repeated_lines - [item.first_line]] }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def signature_keys(item)
|
|
94
|
+
item.arguments
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def message(item, repeats)
|
|
98
|
+
format(MSG, name: shared_examples_name(item).source, repeat: repeats)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpec
|
|
6
|
+
# Checks that message expectations do not have a configured response.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
#
|
|
10
|
+
# # bad
|
|
11
|
+
# expect(foo).to receive(:bar).with(42).and_return("hello world")
|
|
12
|
+
#
|
|
13
|
+
# # good (without spies)
|
|
14
|
+
# allow(foo).to receive(:bar).with(42).and_return("hello world")
|
|
15
|
+
# expect(foo).to receive(:bar).with(42)
|
|
16
|
+
#
|
|
17
|
+
class StubbedMock < Base
|
|
18
|
+
MSG = 'Prefer `%<replacement>s` over `%<method_name>s` when ' \
|
|
19
|
+
'configuring a response.'
|
|
20
|
+
|
|
21
|
+
# @!method message_expectation?(node)
|
|
22
|
+
# Match message expectation matcher
|
|
23
|
+
#
|
|
24
|
+
# @example source that matches
|
|
25
|
+
# receive(:foo)
|
|
26
|
+
#
|
|
27
|
+
# @example source that matches
|
|
28
|
+
# receive_message_chain(:foo, :bar)
|
|
29
|
+
#
|
|
30
|
+
# @example source that matches
|
|
31
|
+
# receive(:foo).with('bar')
|
|
32
|
+
#
|
|
33
|
+
# @param node [RuboCop::AST::Node]
|
|
34
|
+
# @return [Array<RuboCop::AST::Node>] matching nodes
|
|
35
|
+
def_node_matcher :message_expectation?, <<-PATTERN
|
|
36
|
+
{
|
|
37
|
+
(send nil? { :receive :receive_message_chain } ...) # receive(:foo)
|
|
38
|
+
(send (send nil? :receive ...) :with ...) # receive(:foo).with('bar')
|
|
39
|
+
}
|
|
40
|
+
PATTERN
|
|
41
|
+
|
|
42
|
+
def_node_matcher :configured_response?, <<~PATTERN
|
|
43
|
+
{ :and_return :and_raise :and_throw :and_yield
|
|
44
|
+
:and_call_original :and_wrap_original }
|
|
45
|
+
PATTERN
|
|
46
|
+
|
|
47
|
+
# @!method expectation(node)
|
|
48
|
+
# Match expectation
|
|
49
|
+
#
|
|
50
|
+
# @example source that matches
|
|
51
|
+
# is_expected.to be_in_the_bar
|
|
52
|
+
#
|
|
53
|
+
# @example source that matches
|
|
54
|
+
# expect(cocktail).to contain_exactly(:fresh_orange_juice, :campari)
|
|
55
|
+
#
|
|
56
|
+
# @example source that matches
|
|
57
|
+
# expect_any_instance_of(Officer).to be_alert
|
|
58
|
+
#
|
|
59
|
+
# @param node [RuboCop::AST::Node]
|
|
60
|
+
# @yield [RuboCop::AST::Node] expectation, method name, matcher
|
|
61
|
+
def_node_matcher :expectation, <<~PATTERN
|
|
62
|
+
(send
|
|
63
|
+
$(send nil? $#{Expectations::ALL.node_pattern_union} ...)
|
|
64
|
+
:to $_)
|
|
65
|
+
PATTERN
|
|
66
|
+
|
|
67
|
+
# @!method matcher_with_configured_response(node)
|
|
68
|
+
# Match matcher with a configured response
|
|
69
|
+
#
|
|
70
|
+
# @example source that matches
|
|
71
|
+
# receive(:foo).and_return('bar')
|
|
72
|
+
#
|
|
73
|
+
# @example source that matches
|
|
74
|
+
# receive(:lower).and_raise(SomeError)
|
|
75
|
+
#
|
|
76
|
+
# @example source that matches
|
|
77
|
+
# receive(:redirect).and_call_original
|
|
78
|
+
#
|
|
79
|
+
# @param node [RuboCop::AST::Node]
|
|
80
|
+
# @yield [RuboCop::AST::Node] matcher
|
|
81
|
+
def_node_matcher :matcher_with_configured_response, <<~PATTERN
|
|
82
|
+
(send #message_expectation? #configured_response? _)
|
|
83
|
+
PATTERN
|
|
84
|
+
|
|
85
|
+
# @!method matcher_with_return_block(node)
|
|
86
|
+
# Match matcher with a return block
|
|
87
|
+
#
|
|
88
|
+
# @example source that matches
|
|
89
|
+
# receive(:foo) { 'bar' }
|
|
90
|
+
#
|
|
91
|
+
# @param node [RuboCop::AST::Node]
|
|
92
|
+
# @yield [RuboCop::AST::Node] matcher
|
|
93
|
+
def_node_matcher :matcher_with_return_block, <<~PATTERN
|
|
94
|
+
(block #message_expectation? args _) # receive(:foo) { 'bar' }
|
|
95
|
+
PATTERN
|
|
96
|
+
|
|
97
|
+
# @!method matcher_with_hash(node)
|
|
98
|
+
# Match matcher with a configured response defined as a hash
|
|
99
|
+
#
|
|
100
|
+
# @example source that matches
|
|
101
|
+
# receive_messages(foo: 'bar', baz: 'qux')
|
|
102
|
+
#
|
|
103
|
+
# @example source that matches
|
|
104
|
+
# receive_message_chain(:foo, bar: 'baz')
|
|
105
|
+
#
|
|
106
|
+
# @param node [RuboCop::AST::Node]
|
|
107
|
+
# @yield [RuboCop::AST::Node] matcher
|
|
108
|
+
def_node_matcher :matcher_with_hash, <<~PATTERN
|
|
109
|
+
{
|
|
110
|
+
(send nil? :receive_messages hash) # receive_messages(foo: 'bar', baz: 'qux')
|
|
111
|
+
(send nil? :receive_message_chain ... hash) # receive_message_chain(:foo, bar: 'baz')
|
|
112
|
+
}
|
|
113
|
+
PATTERN
|
|
114
|
+
|
|
115
|
+
# @!method matcher_with_blockpass(node)
|
|
116
|
+
# Match matcher with a configured response in block-pass
|
|
117
|
+
#
|
|
118
|
+
# @example source that matches
|
|
119
|
+
# receive(:foo, &canned)
|
|
120
|
+
#
|
|
121
|
+
# @example source that matches
|
|
122
|
+
# receive_message_chain(:foo, :bar, &canned)
|
|
123
|
+
#
|
|
124
|
+
# @example source that matches
|
|
125
|
+
# receive(:foo).with('bar', &canned)
|
|
126
|
+
#
|
|
127
|
+
# @param node [RuboCop::AST::Node]
|
|
128
|
+
# @yield [RuboCop::AST::Node] matcher
|
|
129
|
+
def_node_matcher :matcher_with_blockpass, <<~PATTERN
|
|
130
|
+
{
|
|
131
|
+
(send nil? { :receive :receive_message_chain } ... block_pass) # receive(:foo, &canned)
|
|
132
|
+
(send (send nil? :receive ...) :with ... block_pass) # receive(:foo).with('foo', &canned)
|
|
133
|
+
}
|
|
134
|
+
PATTERN
|
|
135
|
+
|
|
136
|
+
def on_send(node)
|
|
137
|
+
expectation(node, &method(:on_expectation))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
def on_expectation(expectation, method_name, matcher)
|
|
143
|
+
flag_expectation = lambda do
|
|
144
|
+
add_offense(expectation, message: msg(method_name))
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
matcher_with_configured_response(matcher, &flag_expectation)
|
|
148
|
+
matcher_with_return_block(matcher, &flag_expectation)
|
|
149
|
+
matcher_with_hash(matcher, &flag_expectation)
|
|
150
|
+
matcher_with_blockpass(matcher, &flag_expectation)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def msg(method_name)
|
|
154
|
+
format(MSG,
|
|
155
|
+
method_name: method_name,
|
|
156
|
+
replacement: replacement(method_name))
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def replacement(method_name)
|
|
160
|
+
case method_name
|
|
161
|
+
when :expect
|
|
162
|
+
:allow
|
|
163
|
+
when :is_expected
|
|
164
|
+
'allow(subject)'
|
|
165
|
+
when :expect_any_instance_of
|
|
166
|
+
:allow_any_instance_of
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -43,10 +43,10 @@ module RuboCop
|
|
|
43
43
|
#
|
|
44
44
|
# @yield [Symbol] subject name
|
|
45
45
|
def_node_matcher :subject, <<-PATTERN
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
(block
|
|
47
|
+
(send nil?
|
|
48
|
+
{:subject (sym $_) | $:subject}
|
|
49
|
+
) args ...)
|
|
50
50
|
PATTERN
|
|
51
51
|
|
|
52
52
|
# @!method message_expectation?(node, method_name)
|
|
@@ -79,12 +79,14 @@ require_relative 'rspec/repeated_description'
|
|
|
79
79
|
require_relative 'rspec/repeated_example'
|
|
80
80
|
require_relative 'rspec/repeated_example_group_body'
|
|
81
81
|
require_relative 'rspec/repeated_example_group_description'
|
|
82
|
+
require_relative 'rspec/repeated_include_example'
|
|
82
83
|
require_relative 'rspec/return_from_stub'
|
|
83
84
|
require_relative 'rspec/scattered_let'
|
|
84
85
|
require_relative 'rspec/scattered_setup'
|
|
85
86
|
require_relative 'rspec/shared_context'
|
|
86
87
|
require_relative 'rspec/shared_examples'
|
|
87
88
|
require_relative 'rspec/single_argument_message_chain'
|
|
89
|
+
require_relative 'rspec/stubbed_mock'
|
|
88
90
|
require_relative 'rspec/subject_stub'
|
|
89
91
|
require_relative 'rspec/unspecified_exception'
|
|
90
92
|
require_relative 'rspec/variable_definition'
|
data/lib/rubocop/rspec/hook.rb
CHANGED
|
@@ -7,7 +7,8 @@ module RuboCop
|
|
|
7
7
|
# Set of method selectors
|
|
8
8
|
class SelectorSet
|
|
9
9
|
def initialize(selectors)
|
|
10
|
-
@selectors = selectors
|
|
10
|
+
@selectors = selectors.freeze
|
|
11
|
+
freeze
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def ==(other)
|
|
@@ -50,6 +51,10 @@ module RuboCop
|
|
|
50
51
|
selectors.map(&:inspect).join(' ')
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
def to_a
|
|
55
|
+
selectors
|
|
56
|
+
end
|
|
57
|
+
|
|
53
58
|
protected
|
|
54
59
|
|
|
55
60
|
attr_reader :selectors
|
|
@@ -16,7 +16,7 @@ module RuboCop
|
|
|
16
16
|
return unless root_node
|
|
17
17
|
|
|
18
18
|
top_level_groups.each do |node|
|
|
19
|
-
example_group?(node
|
|
19
|
+
on_top_level_example_group(node) if example_group?(node)
|
|
20
20
|
on_top_level_group(node)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -29,16 +29,18 @@ module RuboCop
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
31
|
# Dummy methods to be overridden in the consumer
|
|
32
|
-
def on_top_level_example_group; end
|
|
32
|
+
def on_top_level_example_group(_node); end
|
|
33
33
|
|
|
34
|
-
def on_top_level_group; end
|
|
34
|
+
def on_top_level_group(_node); end
|
|
35
35
|
|
|
36
36
|
def top_level_group?(node)
|
|
37
37
|
top_level_groups.include?(node)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def top_level_nodes(node)
|
|
41
|
-
if node.
|
|
41
|
+
if node.nil?
|
|
42
|
+
[]
|
|
43
|
+
elsif node.begin_type?
|
|
42
44
|
node.children
|
|
43
45
|
elsif node.module_type? || node.class_type?
|
|
44
46
|
top_level_nodes(node.body)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-rspec
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0.pre
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John Backus
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2020-
|
|
13
|
+
date: 2020-10-22 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rubocop
|
|
@@ -18,14 +18,14 @@ dependencies:
|
|
|
18
18
|
requirements:
|
|
19
19
|
- - "~>"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: '0
|
|
21
|
+
version: '1.0'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
25
|
requirements:
|
|
26
26
|
- - "~>"
|
|
27
27
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: '0
|
|
28
|
+
version: '1.0'
|
|
29
29
|
- !ruby/object:Gem::Dependency
|
|
30
30
|
name: rack
|
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -203,12 +203,14 @@ files:
|
|
|
203
203
|
- lib/rubocop/cop/rspec/repeated_example.rb
|
|
204
204
|
- lib/rubocop/cop/rspec/repeated_example_group_body.rb
|
|
205
205
|
- lib/rubocop/cop/rspec/repeated_example_group_description.rb
|
|
206
|
+
- lib/rubocop/cop/rspec/repeated_include_example.rb
|
|
206
207
|
- lib/rubocop/cop/rspec/return_from_stub.rb
|
|
207
208
|
- lib/rubocop/cop/rspec/scattered_let.rb
|
|
208
209
|
- lib/rubocop/cop/rspec/scattered_setup.rb
|
|
209
210
|
- lib/rubocop/cop/rspec/shared_context.rb
|
|
210
211
|
- lib/rubocop/cop/rspec/shared_examples.rb
|
|
211
212
|
- lib/rubocop/cop/rspec/single_argument_message_chain.rb
|
|
213
|
+
- lib/rubocop/cop/rspec/stubbed_mock.rb
|
|
212
214
|
- lib/rubocop/cop/rspec/subject_stub.rb
|
|
213
215
|
- lib/rubocop/cop/rspec/unspecified_exception.rb
|
|
214
216
|
- lib/rubocop/cop/rspec/variable_definition.rb
|
|
@@ -243,7 +245,7 @@ licenses:
|
|
|
243
245
|
- MIT
|
|
244
246
|
metadata:
|
|
245
247
|
changelog_uri: https://github.com/rubocop-hq/rubocop-rspec/blob/master/CHANGELOG.md
|
|
246
|
-
documentation_uri: https://rubocop-rspec
|
|
248
|
+
documentation_uri: https://docs.rubocop.org/rubocop-rspec/
|
|
247
249
|
post_install_message:
|
|
248
250
|
rdoc_options: []
|
|
249
251
|
require_paths:
|
|
@@ -255,9 +257,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
255
257
|
version: 2.4.0
|
|
256
258
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
257
259
|
requirements:
|
|
258
|
-
- - "
|
|
260
|
+
- - ">"
|
|
259
261
|
- !ruby/object:Gem::Version
|
|
260
|
-
version:
|
|
262
|
+
version: 1.3.1
|
|
261
263
|
requirements: []
|
|
262
264
|
rubygems_version: 3.0.3
|
|
263
265
|
signing_key:
|