rubocop-rspec 1.30.1 → 1.31.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89853728f31f151cfe129bc754317563747529c95f5298be06478247b0a91bf0
4
- data.tar.gz: 18a0909fe1352450cd69e289323e3c06f69a38cfb15fb54b3bc79b4f22c089d0
3
+ metadata.gz: 7dc31ff59557e82627745015742087123734c2d1abda96cc277ac243c8bee1f2
4
+ data.tar.gz: 819b5fea22acb7db40512b8291ce8cddec7c9580fb62ccb38bad146bdeaa27e1
5
5
  SHA512:
6
- metadata.gz: d75b3a00d3da18979fd4a9effd3a8b231b54267695998cb8f61a57c0cae64121dc3d5d740422a8283abf425adee11ea4f6aff9d5247d9e8785971a52d949f6c0
7
- data.tar.gz: feedb0ccb5ddc363c7c71fdaf4647316be0a9dc3063079621188fb2fa81b6e3165a1bc0b7ce7850342a10e216bf355ec5df682bcce7a957c40f2ee794394a64e
6
+ metadata.gz: 1998ae7af3934178c41e2c8c965d33af83d1eb30e1becf04ef69b9b6eda8b023d7f2f8e63e2d4a5b2edc74d254d3beb93b34d48d1edbfa318c32d8f389cded80
7
+ data.tar.gz: 59cdfdb2dd906222e08e4839f3c9b8ee8d0dcb81d28ad65de6e7fd6055849d7eac4393d21197b8de621934e71ae4405345f6be71f6fe9eb0d1b09562060a388a
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 1.31.0 (2019-01-02)
6
+
7
+ * Add `IgnoreSharedExamples` option for `RSpec/NamedSubject`. ([@RST-J][])
8
+ * Add autocorrect support for `Capybara/CurrentPathExpectation` cop. ([@ypresto][])
9
+ * Add support for built-in `exists` matcher for `RSpec/PredicateMatcher` cop. ([@mkenyon][])
10
+ * `SingleArgumentMessageChain` no longer reports an array as it's only argument as an offense. ([@Darhazer][])
11
+
5
12
  ## 1.30.1 (2018-11-01)
6
13
 
7
14
  * `FactoryBot/CreateList` now ignores `times` blocks with an argument. ([@Darhazer][])
@@ -389,3 +396,6 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
389
396
  [@vzvu3k6k]: https://github.com/vzvu3k6k
390
397
  [@BrentWheeldon]: https://github.com/BrentWheeldon
391
398
  [@daveworth]: https://github.com/daveworth
399
+ [@RST-J]: https://github.com/RST-J
400
+ [@ypresto]: https://github.com/ypresto
401
+ [@mkenyon]: https://github.com/mkenyon
@@ -303,6 +303,7 @@ RSpec/MultipleSubjects:
303
303
  RSpec/NamedSubject:
304
304
  Description: Checks for explicitly referenced test subjects.
305
305
  Enabled: true
306
+ IgnoreSharedExamples: true
306
307
  StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NamedSubject
307
308
 
308
309
  RSpec/NestedGroups:
@@ -30,11 +30,59 @@ module RuboCop
30
30
  (send nil? :expect (send {(send nil? :page) nil?} :current_path))
31
31
  PATTERN
32
32
 
33
+ # Supported matchers: eq(...) / match(/regexp/) / match('regexp')
34
+ def_node_matcher :as_is_matcher, <<-PATTERN
35
+ (send
36
+ #expectation_set_on_current_path ${:to :not_to :to_not}
37
+ ${(send nil? :eq ...) (send nil? :match (regexp ...))})
38
+ PATTERN
39
+
40
+ def_node_matcher :regexp_str_matcher, <<-PATTERN
41
+ (send
42
+ #expectation_set_on_current_path ${:to :not_to :to_not}
43
+ $(send nil? :match (str $_)))
44
+ PATTERN
45
+
33
46
  def on_send(node)
34
47
  expectation_set_on_current_path(node) do
35
48
  add_offense(node, location: :selector)
36
49
  end
37
50
  end
51
+
52
+ def autocorrect(node)
53
+ lambda do |corrector|
54
+ return unless node.chained?
55
+
56
+ as_is_matcher(node.parent) do |to_sym, matcher_node|
57
+ rewrite_expectation(corrector, node, to_sym, matcher_node)
58
+ end
59
+
60
+ regexp_str_matcher(node.parent) do |to_sym, matcher_node, regexp|
61
+ rewrite_expectation(corrector, node, to_sym, matcher_node)
62
+ convert_regexp_str_to_literal(corrector, matcher_node, regexp)
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def rewrite_expectation(corrector, node, to_symbol, matcher_node)
70
+ current_path_node = node.first_argument
71
+ corrector.replace(current_path_node.loc.expression, 'page')
72
+ corrector.replace(node.parent.loc.selector, 'to')
73
+ matcher_method = if to_symbol == :to
74
+ 'have_current_path'
75
+ else
76
+ 'have_no_current_path'
77
+ end
78
+ corrector.replace(matcher_node.loc.selector, matcher_method)
79
+ end
80
+
81
+ def convert_regexp_str_to_literal(corrector, matcher_node, regexp_str)
82
+ str_node = matcher_node.first_argument
83
+ regexp_expr = Regexp.new(regexp_str).inspect
84
+ corrector.replace(str_node.loc.expression, regexp_expr)
85
+ end
38
86
  end
39
87
  end
40
88
  end
@@ -12,6 +12,10 @@ module RuboCop
12
12
  # the most important object in your tests so they deserve a descriptive
13
13
  # name.
14
14
  #
15
+ # This cop can be configured in your configuration using the
16
+ # `IgnoreSharedExamples` which will not report offenses for implicit
17
+ # subjects in shared example groups.
18
+ #
15
19
  # @example
16
20
  # # bad
17
21
  # RSpec.describe User do
@@ -48,15 +52,24 @@ module RuboCop
48
52
  }
49
53
  PATTERN
50
54
 
55
+ def_node_matcher :shared_example?, <<-PATTERN
56
+ #{SharedGroups::EXAMPLES.block_pattern}
57
+ PATTERN
58
+
51
59
  def_node_search :subject_usage, '$(send nil? :subject)'
52
60
 
53
61
  def on_block(node)
54
- return unless rspec_block?(node)
62
+ return if !rspec_block?(node) || ignored_shared_example?(node)
55
63
 
56
64
  subject_usage(node) do |subject_node|
57
65
  add_offense(subject_node, location: :selector)
58
66
  end
59
67
  end
68
+
69
+ def ignored_shared_example?(node)
70
+ cop_config['IgnoreSharedExamples'] &&
71
+ node.each_ancestor(:block).any?(&method(:shared_example?))
72
+ end
60
73
  end
61
74
  end
62
75
  end
@@ -64,6 +64,8 @@ module RuboCop
64
64
  'be_an_instance_of'
65
65
  when 'include?', 'respond_to?'
66
66
  name[0..-2]
67
+ when 'exist?', 'exists?'
68
+ 'exist'
67
69
  when /^has_/
68
70
  name.sub('has_', 'have_')[0..-2]
69
71
  else
@@ -174,9 +176,10 @@ module RuboCop
174
176
 
175
177
  def predicate_matcher_name?(name)
176
178
  name = name.to_s
177
- name.start_with?('be_', 'have_') &&
178
- !BUILT_IN_MATCHERS.include?(name) &&
179
- !name.end_with?('?')
179
+
180
+ return false if BUILT_IN_MATCHERS.include?(name)
181
+
182
+ name.start_with?('be_', 'have_') && !name.end_with?('?')
180
183
  end
181
184
 
182
185
  def message_explicit(matcher)
@@ -26,9 +26,7 @@ module RuboCop
26
26
 
27
27
  def on_send(node)
28
28
  message_chain(node) do |arg|
29
- return if arg.to_s.include?('.')
30
-
31
- return if arg.hash_type? && !single_key_hash?(arg)
29
+ return if valid_usage?(arg)
32
30
 
33
31
  add_offense(node, location: :selector)
34
32
  end
@@ -39,12 +37,27 @@ module RuboCop
39
37
  corrector.replace(node.loc.selector, replacement(node.method_name))
40
38
  message_chain(node) do |arg|
41
39
  autocorrect_hash_arg(corrector, arg) if single_key_hash?(arg)
40
+ autocorrect_array_arg(corrector, arg) if arg.array_type?
42
41
  end
43
42
  end
44
43
  end
45
44
 
46
45
  private
47
46
 
47
+ def valid_usage?(node)
48
+ return true unless node.literal? || node.array_type?
49
+
50
+ case node.type
51
+ when :hash then !single_key_hash?(node)
52
+ when :array then !single_element_array?(node)
53
+ else node.to_s.include?('.')
54
+ end
55
+ end
56
+
57
+ def single_element_array?(node)
58
+ node.child_nodes.one?
59
+ end
60
+
48
61
  def autocorrect_hash_arg(corrector, arg)
49
62
  key, value = *arg.children.first
50
63
 
@@ -53,6 +66,12 @@ module RuboCop
53
66
  ".and_return(#{value.source})")
54
67
  end
55
68
 
69
+ def autocorrect_array_arg(corrector, arg)
70
+ value = arg.children.first
71
+
72
+ corrector.replace(arg.loc.expression, value.source)
73
+ end
74
+
56
75
  def key_to_arg(node)
57
76
  key, = *node
58
77
  node.sym_type? ? ":#{key}" : node.source
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '1.30.1'.freeze
7
+ STRING = '1.31.0'.freeze
8
8
  end
9
9
  end
10
10
  end
@@ -26,4 +26,38 @@ RSpec.describe RuboCop::Cop::RSpec::Capybara::CurrentPathExpectation do
26
26
  current_path = WalkingRoute.last.path
27
27
  RUBY
28
28
  end
29
+
30
+ include_examples 'autocorrect',
31
+ 'expect(current_path).to eq expected_path',
32
+ 'expect(page).to have_current_path expected_path'
33
+
34
+ include_examples 'autocorrect',
35
+ 'expect(page.current_path).to eq(foo(bar).path)',
36
+ 'expect(page).to have_current_path(foo(bar).path)'
37
+
38
+ include_examples 'autocorrect',
39
+ 'expect(current_path).not_to eq expected_path',
40
+ 'expect(page).to have_no_current_path expected_path'
41
+
42
+ include_examples 'autocorrect',
43
+ 'expect(current_path).to_not eq expected_path',
44
+ 'expect(page).to have_no_current_path expected_path'
45
+
46
+ include_examples 'autocorrect',
47
+ 'expect(page.current_path).to match(/regexp/i)',
48
+ 'expect(page).to have_current_path(/regexp/i)'
49
+
50
+ include_examples 'autocorrect',
51
+ 'expect(page.current_path).to match("string/")',
52
+ 'expect(page).to have_current_path(/string\//)'
53
+
54
+ # Unsupported, no change.
55
+ include_examples 'autocorrect',
56
+ 'expect(page.current_path).to match(variable)',
57
+ 'expect(page.current_path).to match(variable)'
58
+
59
+ # Unsupported, no change.
60
+ include_examples 'autocorrect',
61
+ 'expect(page.current_path)',
62
+ 'expect(page.current_path)'
29
63
  end
@@ -1,62 +1,123 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe RuboCop::Cop::RSpec::NamedSubject do
4
- subject(:cop) { described_class.new }
3
+ RSpec.describe RuboCop::Cop::RSpec::NamedSubject, :config do
4
+ subject(:cop) { described_class.new(config) }
5
5
 
6
- it 'checks `it` and `specify` for explicit subject usage' do
7
- expect_offense(<<-RUBY)
8
- RSpec.describe User do
9
- subject { described_class.new }
6
+ shared_examples_for 'checking subject outside of shared examples' do
7
+ it 'checks `it` and `specify` for explicit subject usage' do
8
+ expect_offense(<<-RUBY)
9
+ RSpec.describe User do
10
+ subject { described_class.new }
10
11
 
11
- it "is valid" do
12
- expect(subject.valid?).to be(true)
13
- ^^^^^^^ Name your test subject if you need to reference it explicitly.
12
+ it "is valid" do
13
+ expect(subject.valid?).to be(true)
14
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
15
+ end
16
+
17
+ specify do
18
+ expect(subject.valid?).to be(true)
19
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
20
+ end
14
21
  end
22
+ RUBY
23
+ end
24
+
25
+ it 'checks before and after for explicit subject usage' do
26
+ expect_offense(<<-RUBY)
27
+ RSpec.describe User do
28
+ subject { described_class.new }
15
29
 
16
- specify do
17
- expect(subject.valid?).to be(true)
18
- ^^^^^^^ Name your test subject if you need to reference it explicitly.
30
+ before(:each) do
31
+ do_something_with(subject)
32
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
33
+ end
34
+
35
+ after do
36
+ do_something_with(subject)
37
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
38
+ end
19
39
  end
20
- end
21
- RUBY
22
- end
40
+ RUBY
41
+ end
23
42
 
24
- it 'checks before and after for explicit subject usage' do
25
- expect_offense(<<-RUBY)
26
- RSpec.describe User do
27
- subject { described_class.new }
43
+ it 'checks around(:each) for explicit subject usage' do
44
+ expect_offense(<<-RUBY)
45
+ RSpec.describe User do
46
+ subject { described_class.new }
28
47
 
29
- before(:each) do
30
- do_something_with(subject)
31
- ^^^^^^^ Name your test subject if you need to reference it explicitly.
48
+ around(:each) do |test|
49
+ do_something_with(subject)
50
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
51
+ end
32
52
  end
53
+ RUBY
54
+ end
33
55
 
34
- after do
35
- do_something_with(subject)
36
- ^^^^^^^ Name your test subject if you need to reference it explicitly.
56
+ it 'ignores subject when not wrapped inside a test' do
57
+ expect_no_offenses(<<-RUBY)
58
+ def foo
59
+ it(subject)
37
60
  end
38
- end
39
- RUBY
61
+ RUBY
62
+ end
40
63
  end
41
64
 
42
- it 'checks around(:each) for explicit subject usage' do
43
- expect_offense(<<-RUBY)
44
- RSpec.describe User do
45
- subject { described_class.new }
65
+ context 'when IgnoreSharedExamples is false' do
66
+ let(:cop_config) { { 'IgnoreSharedExamples' => false } }
67
+
68
+ it_behaves_like 'checking subject outside of shared examples'
69
+
70
+ it 'checks shared_examples for explicit subject usage' do
71
+ expect_offense(<<-RUBY)
72
+ RSpec.describe User do
73
+ subject(:new_user) { described_class.new }
74
+
75
+ shared_examples_for 'a valid new user' do
76
+ it "is a User" do
77
+ expect(subject).to be_a(User)
78
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
79
+ end
46
80
 
47
- around(:each) do |test|
48
- do_something_with(subject)
49
- ^^^^^^^ Name your test subject if you need to reference it explicitly.
81
+ it "is valid" do
82
+ expect(subject.valid?).to be(true)
83
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
84
+ end
85
+
86
+ it "is new" do
87
+ expect(subject).to be_new_record
88
+ ^^^^^^^ Name your test subject if you need to reference it explicitly.
89
+ end
90
+ end
50
91
  end
51
- end
52
- RUBY
92
+ RUBY
93
+ end
53
94
  end
54
95
 
55
- it 'ignores subject when not wrapped inside a test' do
56
- expect_no_offenses(<<-RUBY)
57
- def foo
58
- it(subject)
59
- end
60
- RUBY
96
+ context 'when IgnoreSharedExamples is true' do
97
+ let(:cop_config) { { 'IgnoreSharedExamples' => true } }
98
+
99
+ it_behaves_like 'checking subject outside of shared examples'
100
+
101
+ it 'ignores explicit subject in shared_examples' do
102
+ expect_no_offenses(<<-RUBY)
103
+ RSpec.describe User do
104
+ subject(:new_user) { described_class.new }
105
+
106
+ shared_examples_for 'a valid new user' do
107
+ it "is a User" do
108
+ expect(subject).to be_a(User)
109
+ end
110
+
111
+ it "is valid" do
112
+ expect(subject.valid?).to be(true)
113
+ end
114
+
115
+ it "is new" do
116
+ expect(subject).to be_new_record
117
+ end
118
+ end
119
+ end
120
+ RUBY
121
+ end
61
122
  end
62
123
  end
@@ -20,6 +20,15 @@ RSpec.describe RuboCop::Cop::RSpec::PredicateMatcher, :config do
20
20
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `be_empty` matcher over `empty?`.
21
21
  expect(foo.empty?).to be_falsey
22
22
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `be_empty` matcher over `empty?`.
23
+ RUBY
24
+ end
25
+
26
+ it 'registers an offense for a predicate method with built-in equiv' do
27
+ expect_offense(<<-RUBY)
28
+ expect(foo.exist?).to be_truthy
29
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `exist` matcher over `exist?`.
30
+ expect(foo.exists?).to be_truthy
31
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `exist` matcher over `exists?`.
23
32
  expect(foo.has_something?).to be_truthy
24
33
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `have_something` matcher over `has_something?`.
25
34
  expect(foo.include?(something)).to be_truthy
@@ -106,6 +115,9 @@ RSpec.describe RuboCop::Cop::RSpec::PredicateMatcher, :config do
106
115
  include_examples 'autocorrect',
107
116
  'expect(foo.respond_to?(:bar)).to be_truthy',
108
117
  'expect(foo).to respond_to(:bar)'
118
+ include_examples 'autocorrect',
119
+ 'expect(foo.exists?).to be_truthy',
120
+ 'expect(foo).to exist'
109
121
 
110
122
  include_examples 'autocorrect',
111
123
  'expect(foo.something?()).to be_truthy',
@@ -252,6 +264,7 @@ RSpec.describe RuboCop::Cop::RSpec::PredicateMatcher, :config do
252
264
  expect(foo).to have_received(:foo)
253
265
  expect(foo).to be_between(1, 10)
254
266
  expect(foo).to be_within(0.1).of(10.0)
267
+ expect(foo).to exist
255
268
  RUBY
256
269
  end
257
270
 
@@ -48,8 +48,52 @@ RSpec.describe RuboCop::Cop::RSpec::SingleArgumentMessageChain do
48
48
  RUBY
49
49
  end
50
50
 
51
+ it 'accepts single-argument calls with variable' do
52
+ expect_no_offenses(<<-RUBY)
53
+ before do
54
+ foo = %i[:one :two]
55
+ allow(foo).to receive_message_chain(foo) { :many }
56
+ end
57
+ RUBY
58
+ end
59
+
60
+ it 'accepts single-argument calls with send node' do
61
+ expect_no_offenses(<<-RUBY)
62
+ before do
63
+ allow(foo).to receive_message_chain(foo) { :many }
64
+ end
65
+ RUBY
66
+ end
67
+
68
+ context 'with single-element array argument' do
69
+ it 'reports an offense' do
70
+ expect_offense(<<-RUBY)
71
+ before do
72
+ allow(foo).to receive_message_chain([:one]) { :two }
73
+ ^^^^^^^^^^^^^^^^^^^^^ Use `receive` instead of calling `receive_message_chain` with a single argument.
74
+ end
75
+ RUBY
76
+ end
77
+
78
+ include_examples(
79
+ 'autocorrect',
80
+ 'before { allow(foo).to receive_message_chain([:one]) { :two } }',
81
+ 'before { allow(foo).to receive(:one) { :two } }'
82
+ )
83
+ end
84
+
85
+ context 'with multiple-element array argument' do
86
+ it "doesn't report an offense" do
87
+ expect_no_offenses(<<-RUBY)
88
+ before do
89
+ allow(foo).to receive_message_chain([:one, :two]) { :many }
90
+ end
91
+ RUBY
92
+ end
93
+ end
94
+
51
95
  context 'with single-key hash argument' do
52
- it 'reports an offence' do
96
+ it 'reports an offense' do
53
97
  expect_offense(<<-RUBY)
54
98
  before do
55
99
  allow(foo).to receive_message_chain(bar: 42)
@@ -78,7 +122,7 @@ RSpec.describe RuboCop::Cop::RSpec::SingleArgumentMessageChain do
78
122
  end
79
123
 
80
124
  context 'with multiple keys hash argument' do
81
- it "doesn't report an offence" do
125
+ it "doesn't report an offense" do
82
126
  expect_no_offenses(<<-RUBY)
83
127
  before do
84
128
  allow(foo).to receive_message_chain(bar: 42, baz: 42)
@@ -21,14 +21,6 @@ RSpec.configure do |config|
21
21
 
22
22
  config.order = :random
23
23
 
24
- config.expect_with :rspec do |expectations|
25
- expectations.syntax = :expect # Disable `should`
26
- end
27
-
28
- config.mock_with :rspec do |mocks|
29
- mocks.syntax = :expect # Disable `should_receive` and `stub`
30
- end
31
-
32
24
  # Forbid RSpec from monkey patching any of our objects
33
25
  config.disable_monkey_patching!
34
26
 
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: 1.30.1
4
+ version: 1.31.0
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: 2018-11-01 00:00:00.000000000 Z
13
+ date: 2019-01-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -319,8 +319,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
319
319
  - !ruby/object:Gem::Version
320
320
  version: '0'
321
321
  requirements: []
322
- rubyforge_project:
323
- rubygems_version: 2.7.6
322
+ rubygems_version: 3.0.1
324
323
  signing_key:
325
324
  specification_version: 4
326
325
  summary: Code style checking for RSpec files