-
-
Notifications
You must be signed in to change notification settings - Fork 277
/
feature_methods.rb
117 lines (101 loc) · 3.2 KB
/
feature_methods.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
module Capybara
# Checks for consistent method usage in feature specs.
#
# By default, the cop disables all Capybara-specific methods that have
# the same native RSpec method (e.g. are just aliases). Some teams
# however may prefer using some of the Capybara methods (like `feature`)
# to make it obvious that the test uses Capybara, while still disable
# the rest of the methods, like `given` (alias for `let`), `background`
# (alias for `before`), etc. You can configure which of the methods to
# be enabled by using the EnabledMethods configuration option.
#
# @example
# # bad
# feature 'User logs in' do
# given(:user) { User.new }
#
# background do
# visit new_session_path
# end
#
# scenario 'with OAuth' do
# # ...
# end
# end
#
# # good
# describe 'User logs in' do
# let(:user) { User.new }
#
# before do
# visit new_session_path
# end
#
# it 'with OAuth' do
# # ...
# end
# end
class FeatureMethods < Cop
extend AutoCorrector
MSG = 'Use `%<replacement>s` instead of `%<method>s`.'
# https://git.io/v7Kwr
MAP = {
background: :before,
scenario: :it,
xscenario: :xit,
given: :let,
given!: :let!,
feature: :describe
}.freeze
def_node_matcher :spec?, <<-PATTERN
(block
(send #{RSPEC} {:describe :feature} ...)
...)
PATTERN
def_node_matcher :feature_method, <<-PATTERN
(block
$(send #{RSPEC} ${#{MAP.keys.map(&:inspect).join(' ')}} ...)
...)
PATTERN
def on_block(node)
return unless inside_spec?(node)
feature_method(node) do |send_node, match|
next if enabled?(match)
add_offense(send_node.loc.selector) do |corrector|
corrector.replace(send_node.loc.selector, MAP[match].to_s)
end
end
end
def message(range)
name = range.source.to_sym
format(MSG, method: name, replacement: MAP[name])
end
private
def inside_spec?(node)
return spec?(node) if root_node?(node)
root = node.ancestors.find { |parent| root_node?(parent) }
spec?(root)
end
def root_node?(node)
node.parent.nil? || root_with_siblings?(node.parent)
end
def root_with_siblings?(node)
node.begin_type? && node.parent.nil?
end
def enabled?(method_name)
enabled_methods.include?(method_name)
end
def enabled_methods
cop_config
.fetch('EnabledMethods', [])
.map(&:to_sym)
end
end
end
end
end
end