forked from rubocop/rubocop-rspec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
expect_output.rb
52 lines (45 loc) · 1.66 KB
/
expect_output.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks for opportunities to use `expect { ... }.to output`.
#
# @example
# # bad
# $stdout = StringIO.new
# my_app.print_report
# $stdout = STDOUT
# expect($stdout.string).to eq('Hello World')
#
# # good
# expect { my_app.print_report }.to output('Hello World').to_stdout
class ExpectOutput < Cop
MSG = 'Use `expect { ... }.to output(...).to_%<name>s` '\
'instead of mutating $%<name>s.'
def on_gvasgn(node)
return unless inside_example_scope?(node)
# rubocop:disable InternalAffairs/NodeDestructuring
variable_name, _rhs = *node
# rubocop:enable InternalAffairs/NodeDestructuring
name = variable_name[1..-1]
return unless name.eql?('stdout') || name.eql?('stderr')
add_offense(node, location: :name, message: format(MSG, name: name))
end
private
# Detect if we are inside the scope of a single example
#
# We want to encourage using `expect { ... }.to output` so
# we only care about situations where you would replace with
# an expectation. Therefore, assignments to stderr or stdout
# within a `before(:all)` or otherwise outside of an example
# don't matter.
def inside_example_scope?(node)
return false if node.nil? || example_group?(node)
return true if example?(node)
return RuboCop::RSpec::Hook.new(node).example? if hook?(node)
inside_example_scope?(node.parent)
end
end
end
end
end