rubocop-rspec 1.30.1 → 1.31.0

Sign up to get free protection for your applications and to get access to all the features.
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