Skip to content

Commit e76f7d4

Browse files
authored
Merge pull request #858 from rubocop-hq/expect_actual-autocorrect
Adds an autocorrect to `RSpec/ExpectActual`, swapping the arguments to `expect` and matcher when possible.
2 parents e2fbc21 + f152aaa commit e76f7d4

File tree

4 files changed

+201
-3
lines changed

4 files changed

+201
-3
lines changed

CHANGELOG.md

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

55
* Fix `RSpec/InstanceVariable` detection inside custom matchers. ([@pirj][])
66
* Fix `RSpec/ScatteredSetup` to distinguish hooks with different metadata. ([@pirj][])
7+
* Add autocorrect support for `RSpec/ExpectActual` cop. ([@dduugg][], [@pirj][])
78

89
## 1.37.1 (2019-12-16)
910

@@ -475,3 +476,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
475476
[@mkrawc]: https://github.com/mkrawc
476477
[@jfragoulis]: https://github.com/jfragoulis
477478
[@ybiquitous]: https://github.com/ybiquitous
479+
[@dduugg]: https://github.com/dduugg

lib/rubocop/cop/rspec/expect_actual.rb

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,31 @@ class ExpectActual < Cop
4141
regexp
4242
].freeze
4343

44-
def_node_matcher :expect_literal, '(send _ :expect $#literal?)'
44+
SUPPORTED_MATCHERS = %i[eq eql equal be].freeze
45+
46+
def_node_matcher :expect_literal, <<~PATTERN
47+
(send
48+
(send nil? :expect $#literal?)
49+
#{Runners::ALL.node_pattern_union}
50+
{
51+
(send (send nil? $:be) :== $_)
52+
(send nil? $_ $_ ...)
53+
}
54+
)
55+
PATTERN
4556

4657
def on_send(node)
4758
expect_literal(node) do |argument|
48-
add_offense(argument)
59+
add_offense(node, location: argument.source_range)
60+
end
61+
end
62+
63+
def autocorrect(node)
64+
actual, matcher, expected = expect_literal(node)
65+
lambda do |corrector|
66+
return unless SUPPORTED_MATCHERS.include?(matcher)
67+
68+
swap(corrector, actual, expected)
4969
end
5070
end
5171

@@ -65,6 +85,11 @@ def complex_literal?(node)
6585
COMPLEX_LITERALS.include?(node.type) &&
6686
node.each_child_node.all?(&method(:literal?))
6787
end
88+
89+
def swap(corrector, actual, expected)
90+
corrector.replace(actual.source_range, expected.source)
91+
corrector.replace(expected.source_range, actual.source)
92+
end
6893
end
6994
end
7095
end

manual/cops_rspec.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ IgnoredWords | `[]` | Array
977977

978978
Enabled by default | Supports autocorrection
979979
--- | ---
980-
Enabled | No
980+
Enabled | Yes
981981

982982
Checks for `expect(...)` calls containing literal values.
983983

spec/rubocop/cop/rspec/expect_actual_spec.rb

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@
1818
end
1919
end
2020
RUBY
21+
22+
expect_correction(<<-RUBY)
23+
describe Foo do
24+
it 'uses expect incorrectly' do
25+
expect(bar).to eq(123)
26+
expect(bar).to eq(12.3)
27+
expect(bar).to eq(1i)
28+
expect(bar).to eq(1r)
29+
end
30+
end
31+
RUBY
2132
end
2233

2334
it 'flags boolean literal values within expect(...)' do
@@ -31,6 +42,15 @@
3142
end
3243
end
3344
RUBY
45+
46+
expect_correction(<<-RUBY)
47+
describe Foo do
48+
it 'uses expect incorrectly' do
49+
expect(bar).to eq(true)
50+
expect(bar).to eq(false)
51+
end
52+
end
53+
RUBY
3454
end
3555

3656
it 'flags string and symbol literal values within expect(...)' do
@@ -44,6 +64,15 @@
4464
end
4565
end
4666
RUBY
67+
68+
expect_correction(<<-RUBY)
69+
describe Foo do
70+
it 'uses expect incorrectly' do
71+
expect(bar).to eq("foo")
72+
expect(bar).to eq(:foo)
73+
end
74+
end
75+
RUBY
4776
end
4877

4978
it 'flags literal nil value within expect(...)' do
@@ -55,6 +84,14 @@
5584
end
5685
end
5786
RUBY
87+
88+
expect_correction(<<-RUBY)
89+
describe Foo do
90+
it 'uses expect incorrectly' do
91+
expect(bar).to eq(nil)
92+
end
93+
end
94+
RUBY
5895
end
5996

6097
it 'does not flag dynamic values within expect(...)' do
@@ -80,6 +117,15 @@
80117
end
81118
end
82119
RUBY
120+
121+
expect_correction(<<-RUBY)
122+
describe Foo do
123+
it 'uses expect incorrectly' do
124+
expect(bar).to eq([123])
125+
expect(bar).to eq([[123]])
126+
end
127+
end
128+
RUBY
83129
end
84130

85131
it 'flags hashes containing only literal values within expect(...)' do
@@ -93,6 +139,15 @@
93139
end
94140
end
95141
RUBY
142+
143+
expect_correction(<<-RUBY)
144+
describe Foo do
145+
it 'uses expect incorrectly' do
146+
expect(bar).to eq(foo: 1, bar: 2)
147+
expect(bar).to eq(foo: 1, bar: [{}])
148+
end
149+
end
150+
RUBY
96151
end
97152

98153
it 'flags ranges containing only literal values within expect(...)' do
@@ -106,6 +161,15 @@
106161
end
107162
end
108163
RUBY
164+
165+
expect_correction(<<-RUBY)
166+
describe Foo do
167+
it 'uses expect incorrectly' do
168+
expect(bar).to eq(1..2)
169+
expect(bar).to eq(1...2)
170+
end
171+
end
172+
RUBY
109173
end
110174

111175
it 'flags regexps containing only literal values within expect(...)' do
@@ -117,6 +181,14 @@
117181
end
118182
end
119183
RUBY
184+
185+
expect_correction(<<-RUBY)
186+
describe Foo do
187+
it 'uses expect incorrectly' do
188+
expect(bar).to eq(/foo|bar/)
189+
end
190+
end
191+
RUBY
120192
end
121193

122194
it 'does not flag complex values with dynamic parts within expect(...)' do
@@ -135,6 +207,105 @@
135207
RUBY
136208
end
137209

210+
it 'ignores `be` with no argument' do
211+
expect_no_offenses(<<~RUBY)
212+
describe Foo do
213+
it 'uses expect legitimately' do
214+
expect(1).to be
215+
end
216+
end
217+
RUBY
218+
end
219+
220+
it 'flags `be` with an argument' do
221+
expect_offense(<<~RUBY)
222+
describe Foo do
223+
it 'uses expect incorrectly' do
224+
expect(true).to be(a)
225+
^^^^ Provide the actual you are testing to `expect(...)`.
226+
end
227+
end
228+
RUBY
229+
230+
expect_correction(<<~RUBY)
231+
describe Foo do
232+
it 'uses expect incorrectly' do
233+
expect(a).to be(true)
234+
end
235+
end
236+
RUBY
237+
end
238+
239+
it 'flags `be ==`' do
240+
expect_offense(<<~RUBY)
241+
describe Foo do
242+
it 'uses expect incorrectly' do
243+
expect(1).to be == a
244+
^ Provide the actual you are testing to `expect(...)`.
245+
end
246+
end
247+
RUBY
248+
249+
expect_correction(<<~RUBY)
250+
describe Foo do
251+
it 'uses expect incorrectly' do
252+
expect(a).to be == 1
253+
end
254+
end
255+
RUBY
256+
end
257+
258+
it 'flags with `eql` matcher' do
259+
expect_offense(<<-RUBY)
260+
describe Foo do
261+
it 'uses expect incorrectly' do
262+
expect(1).to eql(bar)
263+
^ Provide the actual you are testing to `expect(...)`.
264+
end
265+
end
266+
RUBY
267+
268+
expect_correction(<<-RUBY)
269+
describe Foo do
270+
it 'uses expect incorrectly' do
271+
expect(bar).to eql(1)
272+
end
273+
end
274+
RUBY
275+
end
276+
277+
it 'flags with `equal` matcher' do
278+
expect_offense(<<-RUBY)
279+
describe Foo do
280+
it 'uses expect incorrectly' do
281+
expect(1).to equal(bar)
282+
^ Provide the actual you are testing to `expect(...)`.
283+
end
284+
end
285+
RUBY
286+
287+
expect_correction(<<-RUBY)
288+
describe Foo do
289+
it 'uses expect incorrectly' do
290+
expect(bar).to equal(1)
291+
end
292+
end
293+
RUBY
294+
end
295+
296+
it 'flags but does not autocorrect violations for other matchers' do
297+
expect_offense(<<-RUBY)
298+
describe Foo do
299+
it 'uses expect incorrectly' do
300+
expect([1,2,3]).to include(a)
301+
^^^^^^^ Provide the actual you are testing to `expect(...)`.
302+
end
303+
end
304+
RUBY
305+
306+
expect_no_corrections
307+
end
308+
138309
context 'when inspecting rspec-rails routing specs' do
139310
let(:cop_config) { {} }
140311

0 commit comments

Comments
 (0)