rubocop-capybara 2.23.0 → 3.1.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 +4 -4
- data/CHANGELOG.md +53 -0
- data/README.md +1 -1
- data/config/default.yml +97 -32
- data/lib/rubocop/capybara/plugin.rb +2 -3
- data/lib/rubocop/capybara/version.rb +1 -1
- data/lib/rubocop/cop/capybara/absolute_x_path_in_element_scope.rb +104 -0
- data/lib/rubocop/cop/capybara/always_true_count_query.rb +75 -0
- data/lib/rubocop/cop/capybara/ambiguous_click.rb +1 -1
- data/lib/rubocop/cop/capybara/assert_style.rb +1 -1
- data/lib/rubocop/cop/capybara/chained_find.rb +175 -0
- data/lib/rubocop/cop/capybara/find_all_first.rb +13 -41
- data/lib/rubocop/cop/capybara/ignored_query_option.rb +242 -0
- data/lib/rubocop/cop/capybara/mixin/capybara_help.rb +86 -2
- data/lib/rubocop/cop/capybara/mixin/css_attributes_parser.rb +23 -5
- data/lib/rubocop/cop/capybara/mixin/css_selector.rb +13 -10
- data/lib/rubocop/cop/capybara/mixin/query_methods.rb +120 -0
- data/lib/rubocop/cop/capybara/mixin/visibility.rb +53 -0
- data/lib/rubocop/cop/capybara/modal_method_without_block.rb +37 -0
- data/lib/rubocop/cop/capybara/native_attribute.rb +76 -0
- data/lib/rubocop/cop/capybara/redundant_within_find.rb +10 -21
- data/lib/rubocop/cop/capybara/rspec/current_path_expectation.rb +13 -16
- data/lib/rubocop/cop/capybara/rspec/current_url_expectation.rb +122 -0
- data/lib/rubocop/cop/capybara/rspec/find_element_value_eq.rb +173 -0
- data/lib/rubocop/cop/capybara/rspec/have_content.rb +1 -1
- data/lib/rubocop/cop/capybara/rspec/have_selector.rb +26 -15
- data/lib/rubocop/cop/capybara/rspec/match_style.rb +1 -1
- data/lib/rubocop/cop/capybara/rspec/negation_matcher.rb +1 -1
- data/lib/rubocop/cop/capybara/rspec/negation_matcher_after_visit.rb +2 -2
- data/lib/rubocop/cop/capybara/rspec/predicate_matcher.rb +43 -7
- data/lib/rubocop/cop/capybara/rspec/specific_matcher.rb +8 -2
- data/lib/rubocop/cop/capybara/rspec/visibility_matcher.rb +29 -24
- data/lib/rubocop/cop/capybara/rspec.rb +25 -0
- data/lib/rubocop/cop/capybara/specific_actions.rb +12 -7
- data/lib/rubocop/cop/capybara/specific_finders.rb +149 -28
- data/lib/rubocop/cop/capybara/unordered_window_access.rb +40 -0
- data/lib/rubocop/cop/capybara/visibility_option.rb +64 -0
- data/lib/rubocop/cop/capybara.rb +28 -0
- data/lib/rubocop/cop/capybara_cops.rb +4 -17
- data/lib/rubocop-capybara.rb +7 -5
- metadata +18 -6
- data/config/obsoletion.yml +0 -18
- data/lib/rubocop/cop/capybara/click_link_or_button_style.rb +0 -87
|
@@ -3,6 +3,43 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Capybara
|
|
6
|
+
# Help methods for SpecificFinders.
|
|
7
|
+
# @api private
|
|
8
|
+
module SpecificFindersHelp
|
|
9
|
+
MESSAGE = 'Prefer `%<good_finder>s` over `find`.'
|
|
10
|
+
FIELD_OPTIONS = CapybaraHelp::SPECIFIC_OPTIONS['field']
|
|
11
|
+
FIELD_SELECTOR_PATTERN = /\Ainput(?:\[.+\])+\z/.freeze
|
|
12
|
+
UNSAFE_FIELD_TYPES = %w[hidden image submit].freeze
|
|
13
|
+
FIELD_STATE_OPTIONS = %w[checked disabled multiple readonly].freeze
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
def field_selector?(arg)
|
|
18
|
+
return false unless FIELD_SELECTOR_PATTERN.match?(arg)
|
|
19
|
+
|
|
20
|
+
attrs = CssSelector.attributes(arg)
|
|
21
|
+
type = attrs['type'].to_s.downcase
|
|
22
|
+
return false if UNSAFE_FIELD_TYPES.include?(type)
|
|
23
|
+
return false if (attrs.keys & FIELD_STATE_OPTIONS).any?
|
|
24
|
+
|
|
25
|
+
CapybaraHelp.replaceable_attributes?(attrs) &&
|
|
26
|
+
attrs.keys.all? { |attr| FIELD_OPTIONS.include?(attr) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def options(attrs)
|
|
30
|
+
attrs.filter_map { |key, value| "#{key}: #{yield(value)}" }.join(', ')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def unsupported_selector?(arg)
|
|
34
|
+
CssSelector.pseudo_classes(arg).any? ||
|
|
35
|
+
CssSelector.multiple_selectors?(arg)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def message(finder)
|
|
39
|
+
format(MESSAGE, good_finder: finder)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
6
43
|
# Checks if there is a more specific finder offered by Capybara.
|
|
7
44
|
#
|
|
8
45
|
# @example
|
|
@@ -11,43 +48,90 @@ module RuboCop
|
|
|
11
48
|
# find('[id=some-id]')
|
|
12
49
|
# find(:css, '#some-id')
|
|
13
50
|
# find(:id, 'some-id')
|
|
51
|
+
# find(:link, 'Home')
|
|
52
|
+
# find(:field, 'Name')
|
|
53
|
+
# find('input[placeholder="Email"]')
|
|
54
|
+
# find(:css, 'input[type="checkbox"]')
|
|
14
55
|
#
|
|
15
56
|
# # good
|
|
16
57
|
# find_by_id('some-id')
|
|
58
|
+
# find_link('Home')
|
|
59
|
+
# find_field('Name')
|
|
60
|
+
# find_field(placeholder: 'Email')
|
|
61
|
+
# find_field(type: 'checkbox')
|
|
17
62
|
#
|
|
18
|
-
class SpecificFinders <
|
|
63
|
+
class SpecificFinders < RuboCop::Cop::Base # rubocop:disable Metrics/ClassLength
|
|
19
64
|
extend AutoCorrector
|
|
20
65
|
include RangeHelp
|
|
21
66
|
|
|
22
|
-
MSG = 'Prefer `
|
|
67
|
+
MSG = 'Prefer `%<replacement>s` over `find`.'
|
|
23
68
|
RESTRICT_ON_SEND = %i[find].freeze
|
|
24
69
|
|
|
25
70
|
# @!method find_argument(node)
|
|
26
71
|
def_node_matcher :find_argument, <<~PATTERN
|
|
27
|
-
(send _ :find $(sym {:css :id})? (str $_) ...)
|
|
72
|
+
(send _ :find $(sym {:css :id :link :field})? (str $_) ...)
|
|
28
73
|
PATTERN
|
|
29
74
|
|
|
30
|
-
# @!method
|
|
31
|
-
|
|
32
|
-
(pair (sym :class) $_
|
|
75
|
+
# @!method class_option(node)
|
|
76
|
+
def_node_matcher :class_option, <<~PATTERN
|
|
77
|
+
(send _ :find ... (hash <(pair (sym :class) $_) ...>))
|
|
33
78
|
PATTERN
|
|
34
79
|
|
|
35
80
|
def on_send(node)
|
|
36
81
|
find_argument(node) do |sym, arg|
|
|
37
|
-
|
|
38
|
-
next if CssSelector.multiple_selectors?(arg)
|
|
39
|
-
|
|
40
|
-
on_attr(node, sym, arg) if attribute?(arg)
|
|
41
|
-
on_id(node, sym, arg) if CssSelector.id?(arg)
|
|
42
|
-
on_sym_id(node, sym, arg) if sym.first&.value == :id
|
|
82
|
+
handle_find(node, sym, arg)
|
|
43
83
|
end
|
|
44
84
|
end
|
|
45
85
|
|
|
46
86
|
private
|
|
47
87
|
|
|
88
|
+
def selector?(sym)
|
|
89
|
+
%i[link field].include?(sym.first.value)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def id_symbol?(sym)
|
|
93
|
+
sym.first&.value == :id
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def selector_symbol?(sym)
|
|
97
|
+
sym.first && selector?(sym)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def on_sym_selector(node, sym)
|
|
101
|
+
replacement = "find_#{sym.first.value}"
|
|
102
|
+
register_selector_offense(node, replacement)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def register_selector_offense(node, replacement)
|
|
106
|
+
message = format(MSG, replacement: replacement)
|
|
107
|
+
add_offense(offense_range(node), message: message) do |corrector|
|
|
108
|
+
corrector.replace(node.loc.selector, replacement)
|
|
109
|
+
remove_selector_argument(corrector, node)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def remove_selector_argument(corrector, node)
|
|
114
|
+
range = range_between(node.first_argument.source_range.begin_pos,
|
|
115
|
+
node.arguments[1].source_range.begin_pos)
|
|
116
|
+
corrector.remove(range)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def handle_find(node, sym, arg)
|
|
120
|
+
return on_sym_id(node, sym, arg) if id_symbol?(sym)
|
|
121
|
+
return on_sym_selector(node, sym) if selector_symbol?(sym)
|
|
122
|
+
return if SpecificFindersHelp.unsupported_selector?(arg)
|
|
123
|
+
|
|
124
|
+
if SpecificFindersHelp.field_selector?(arg)
|
|
125
|
+
return on_field(node, sym, arg)
|
|
126
|
+
end
|
|
127
|
+
return on_attr(node, sym, arg) if attribute?(arg)
|
|
128
|
+
|
|
129
|
+
on_id(node, sym, arg) if CssSelector.id?(arg)
|
|
130
|
+
end
|
|
131
|
+
|
|
48
132
|
def on_attr(node, sym, arg)
|
|
49
133
|
attrs = CssSelector.attributes(arg)
|
|
50
|
-
return unless (id = attrs['id'])
|
|
134
|
+
return unless (id = attrs['id']&.to_s)
|
|
51
135
|
return if attrs['class']
|
|
52
136
|
|
|
53
137
|
register_offense(node, sym, replaced_arguments(arg, id))
|
|
@@ -57,12 +141,29 @@ module RuboCop
|
|
|
57
141
|
return if CssSelector.attributes(arg).any?
|
|
58
142
|
|
|
59
143
|
id = CssSelector.id(arg)
|
|
60
|
-
register_offense(node, sym,
|
|
144
|
+
register_offense(node, sym, ruby_literal(id.delete('\\')),
|
|
61
145
|
CssSelector.classes(arg.sub("##{id}", '')))
|
|
62
146
|
end
|
|
63
147
|
|
|
64
148
|
def on_sym_id(node, sym, id)
|
|
65
|
-
register_offense(node, sym,
|
|
149
|
+
register_offense(node, sym, ruby_literal(id))
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def on_field(node, sym, arg)
|
|
153
|
+
add_offense(
|
|
154
|
+
offense_range(node),
|
|
155
|
+
message: SpecificFindersHelp.message('find_field')
|
|
156
|
+
) do |corrector|
|
|
157
|
+
corrector.replace(node.loc.selector, 'find_field')
|
|
158
|
+
corrector.replace(node.first_argument, field_options(arg))
|
|
159
|
+
corrector.remove(deletion_range(node)) unless sym.empty?
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def field_options(arg)
|
|
164
|
+
SpecificFindersHelp.options(CssSelector.attributes(arg)) do |value|
|
|
165
|
+
ruby_literal(value)
|
|
166
|
+
end
|
|
66
167
|
end
|
|
67
168
|
|
|
68
169
|
def attribute?(arg)
|
|
@@ -70,24 +171,36 @@ module RuboCop
|
|
|
70
171
|
CapybaraHelp.common_attributes?(arg)
|
|
71
172
|
end
|
|
72
173
|
|
|
73
|
-
def register_offense(node, sym,
|
|
74
|
-
|
|
174
|
+
def register_offense(node, sym, replacement, classes = [])
|
|
175
|
+
message = format(MSG, replacement: 'find_by_id')
|
|
176
|
+
add_offense(offense_range(node), message: message) do |corrector|
|
|
177
|
+
next unless autocorrectable_classes?(node, classes)
|
|
178
|
+
|
|
75
179
|
corrector.replace(node.loc.selector, 'find_by_id')
|
|
76
|
-
corrector.replace(node.first_argument,
|
|
77
|
-
|
|
78
|
-
autocorrect_classes(corrector, node, classes)
|
|
79
|
-
end
|
|
180
|
+
corrector.replace(node.first_argument, replacement)
|
|
181
|
+
autocorrect_id_classes(corrector, node, classes)
|
|
80
182
|
corrector.remove(deletion_range(node)) unless sym.empty?
|
|
81
183
|
end
|
|
82
184
|
end
|
|
83
185
|
|
|
186
|
+
def autocorrectable_classes?(node, classes)
|
|
187
|
+
return true if classes.empty? || !(options = class_option(node))
|
|
188
|
+
|
|
189
|
+
options.str_type? ||
|
|
190
|
+
(options.array_type? && options.values.all?(&:str_type?))
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def autocorrect_id_classes(corrector, node, classes)
|
|
194
|
+
autocorrect_classes(corrector, node, classes) unless classes.empty?
|
|
195
|
+
end
|
|
196
|
+
|
|
84
197
|
def deletion_range(node)
|
|
85
198
|
range_between(node.first_argument.source_range.end_pos,
|
|
86
199
|
node.arguments[1].source_range.end_pos)
|
|
87
200
|
end
|
|
88
201
|
|
|
89
202
|
def autocorrect_classes(corrector, node, classes)
|
|
90
|
-
if (options =
|
|
203
|
+
if (options = class_option(node))
|
|
91
204
|
append_options(classes, options)
|
|
92
205
|
corrector.replace(options, classes.to_s)
|
|
93
206
|
else
|
|
@@ -108,6 +221,8 @@ module RuboCop
|
|
|
108
221
|
|
|
109
222
|
def replaced_arguments(arg, id)
|
|
110
223
|
options = to_options(CssSelector.attributes(arg))
|
|
224
|
+
id = ruby_literal(id.delete('\\'))
|
|
225
|
+
|
|
111
226
|
options.empty? ? id : "#{id}, #{options}"
|
|
112
227
|
end
|
|
113
228
|
|
|
@@ -115,20 +230,26 @@ module RuboCop
|
|
|
115
230
|
attrs.each.filter_map do |key, value|
|
|
116
231
|
next if key == 'id'
|
|
117
232
|
|
|
118
|
-
"#{key}: #{value}"
|
|
233
|
+
"#{key}: #{ruby_literal(value)}"
|
|
119
234
|
end.join(', ')
|
|
120
235
|
end
|
|
121
236
|
|
|
237
|
+
def ruby_literal(value)
|
|
238
|
+
case value
|
|
239
|
+
when String
|
|
240
|
+
"'#{value.gsub(/['\\]/) { |char| "\\#{char}" }}'"
|
|
241
|
+
when nil then 'nil'
|
|
242
|
+
else
|
|
243
|
+
value.to_s
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
122
247
|
def offense_range(node)
|
|
123
248
|
range_between(node.loc.selector.begin_pos, end_pos(node))
|
|
124
249
|
end
|
|
125
250
|
|
|
126
251
|
def end_pos(node)
|
|
127
|
-
|
|
128
|
-
node.loc.end.end_pos
|
|
129
|
-
else
|
|
130
|
-
node.source_range.end_pos
|
|
131
|
-
end
|
|
252
|
+
node.loc.end ? node.loc.end.end_pos : node.source_range.end_pos
|
|
132
253
|
end
|
|
133
254
|
end
|
|
134
255
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Capybara
|
|
6
|
+
# Checks positional access to Capybara windows, whose order is undefined.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # bad
|
|
10
|
+
# popup = windows.last
|
|
11
|
+
# original = page.windows.first
|
|
12
|
+
#
|
|
13
|
+
# # good
|
|
14
|
+
# popup = window_opened_by { click_link 'Open' }
|
|
15
|
+
# original = current_window
|
|
16
|
+
#
|
|
17
|
+
class UnorderedWindowAccess < RuboCop::Cop::Base
|
|
18
|
+
MSG = 'Selecting a window by position relies on undefined order. ' \
|
|
19
|
+
'Use `window_opened_by` or `current_window`.'
|
|
20
|
+
RESTRICT_ON_SEND = %i[first last [] at fetch].freeze
|
|
21
|
+
|
|
22
|
+
# @!method capybara_windows?(node)
|
|
23
|
+
def_node_matcher :capybara_windows?, <<~PATTERN
|
|
24
|
+
(call
|
|
25
|
+
{nil? self
|
|
26
|
+
(call {nil? self} :page)
|
|
27
|
+
(call (const {nil? cbase} :Capybara) :current_session)}
|
|
28
|
+
:windows)
|
|
29
|
+
PATTERN
|
|
30
|
+
|
|
31
|
+
def on_send(node)
|
|
32
|
+
return if node.method?(:[]) && node.first_argument&.type?(:range)
|
|
33
|
+
|
|
34
|
+
add_offense(node) if capybara_windows?(node.receiver)
|
|
35
|
+
end
|
|
36
|
+
alias on_csend on_send
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Capybara
|
|
6
|
+
# Checks for boolean values passed to Capybara's `visible` option.
|
|
7
|
+
#
|
|
8
|
+
# `visible: false` finds both visible and hidden elements, rather than
|
|
9
|
+
# only hidden elements. Use `:all`, `:hidden` or `:visible` instead.
|
|
10
|
+
# Only `true` is autocorrected, to `:visible`.
|
|
11
|
+
#
|
|
12
|
+
# This cop targets Capybara's built-in selectors. Set `DefaultSelector`
|
|
13
|
+
# to match `Capybara.default_selector`, or to `null` if it varies at
|
|
14
|
+
# runtime. Queries using custom or unknown selectors are skipped.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# # bad
|
|
18
|
+
# find('.menu', visible: true)
|
|
19
|
+
# expect(page).to have_css('.menu', visible: false)
|
|
20
|
+
#
|
|
21
|
+
# # good
|
|
22
|
+
# find('.menu', visible: :visible)
|
|
23
|
+
# find('.menu', visible: :all)
|
|
24
|
+
# find('.menu', visible: :hidden)
|
|
25
|
+
#
|
|
26
|
+
# @example DefaultSelector: custom
|
|
27
|
+
# # bad
|
|
28
|
+
# find(:css, '.menu', visible: true)
|
|
29
|
+
#
|
|
30
|
+
# # good
|
|
31
|
+
# find('.menu', visible: true)
|
|
32
|
+
#
|
|
33
|
+
class VisibilityOption < RuboCop::Cop::Base
|
|
34
|
+
extend AutoCorrector
|
|
35
|
+
include CapybaraHelp
|
|
36
|
+
include Visibility
|
|
37
|
+
|
|
38
|
+
RESTRICT_ON_SEND = QueryMethods.names(
|
|
39
|
+
:finder, :collection, :selector, :match, :action, :grouped
|
|
40
|
+
).difference(CapybaraHelp::VISIBILITY_MATCHER_METHODS).freeze
|
|
41
|
+
|
|
42
|
+
# @!method visibility_options(node)
|
|
43
|
+
def_node_matcher :visibility_options, <<~PATTERN
|
|
44
|
+
(call #capybara_receiver? _ <$(hash ...) ...>)
|
|
45
|
+
PATTERN
|
|
46
|
+
|
|
47
|
+
# @!method boolean_visibility?(node)
|
|
48
|
+
def_node_matcher :boolean_visibility?, <<~PATTERN
|
|
49
|
+
(pair (sym :visible) boolean)
|
|
50
|
+
PATTERN
|
|
51
|
+
|
|
52
|
+
def on_send(node)
|
|
53
|
+
return unless (options = visibility_options(node))
|
|
54
|
+
return unless supported_visibility_query?(node, options)
|
|
55
|
+
|
|
56
|
+
options.pairs.each do |pair|
|
|
57
|
+
check_visibility(pair) if boolean_visibility?(pair)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
alias on_csend on_send
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'capybara/rspec'
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
module Cop
|
|
7
|
+
# Cops for the `Capybara` department. The department's cops are
|
|
8
|
+
# registered for lazy loading and their files are loaded on demand.
|
|
9
|
+
module Capybara
|
|
10
|
+
extend LazyLoader
|
|
11
|
+
|
|
12
|
+
register_cop :AbsoluteXPathInElementScope, "#{__dir__}/capybara/absolute_x_path_in_element_scope"
|
|
13
|
+
register_cop :AlwaysTrueCountQuery, "#{__dir__}/capybara/always_true_count_query"
|
|
14
|
+
register_cop :AmbiguousClick, "#{__dir__}/capybara/ambiguous_click"
|
|
15
|
+
register_cop :AssertStyle, "#{__dir__}/capybara/assert_style"
|
|
16
|
+
register_cop :ChainedFind, "#{__dir__}/capybara/chained_find"
|
|
17
|
+
register_cop :FindAllFirst, "#{__dir__}/capybara/find_all_first"
|
|
18
|
+
register_cop :IgnoredQueryOption, "#{__dir__}/capybara/ignored_query_option"
|
|
19
|
+
register_cop :ModalMethodWithoutBlock, "#{__dir__}/capybara/modal_method_without_block"
|
|
20
|
+
register_cop :NativeAttribute, "#{__dir__}/capybara/native_attribute"
|
|
21
|
+
register_cop :RedundantWithinFind, "#{__dir__}/capybara/redundant_within_find"
|
|
22
|
+
register_cop :SpecificActions, "#{__dir__}/capybara/specific_actions"
|
|
23
|
+
register_cop :SpecificFinders, "#{__dir__}/capybara/specific_finders"
|
|
24
|
+
register_cop :UnorderedWindowAccess, "#{__dir__}/capybara/unordered_window_access"
|
|
25
|
+
register_cop :VisibilityOption, "#{__dir__}/capybara/visibility_option"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
require_relative 'capybara
|
|
7
|
-
require_relative 'capybara/rspec/negation_matcher'
|
|
8
|
-
require_relative 'capybara/rspec/negation_matcher_after_visit'
|
|
9
|
-
require_relative 'capybara/rspec/predicate_matcher'
|
|
10
|
-
require_relative 'capybara/rspec/specific_matcher'
|
|
11
|
-
require_relative 'capybara/rspec/visibility_matcher'
|
|
12
|
-
|
|
13
|
-
require_relative 'capybara/ambiguous_click'
|
|
14
|
-
require_relative 'capybara/assert_style'
|
|
15
|
-
require_relative 'capybara/click_link_or_button_style'
|
|
16
|
-
require_relative 'capybara/find_all_first'
|
|
17
|
-
require_relative 'capybara/redundant_within_find'
|
|
18
|
-
require_relative 'capybara/specific_actions'
|
|
19
|
-
require_relative 'capybara/specific_finders'
|
|
3
|
+
# @deprecated This file is deprecated. Cops are registered for lazy loading in
|
|
4
|
+
# `rubocop/cop/capybara`; this file is kept for compatibility with code that
|
|
5
|
+
# requires it directly.
|
|
6
|
+
require_relative 'capybara'
|
data/lib/rubocop-capybara.rb
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'pathname'
|
|
4
|
-
require 'yaml'
|
|
5
|
-
|
|
6
3
|
require 'rubocop'
|
|
7
4
|
|
|
8
5
|
require_relative 'rubocop/capybara/plugin'
|
|
@@ -11,13 +8,18 @@ require_relative 'rubocop/capybara/version'
|
|
|
11
8
|
require_relative 'rubocop/cop/capybara/mixin/capybara_help'
|
|
12
9
|
require_relative 'rubocop/cop/capybara/mixin/css_attributes_parser'
|
|
13
10
|
require_relative 'rubocop/cop/capybara/mixin/css_selector'
|
|
11
|
+
require_relative 'rubocop/cop/capybara/mixin/query_methods'
|
|
12
|
+
require_relative 'rubocop/cop/capybara/mixin/visibility'
|
|
14
13
|
|
|
15
|
-
require_relative 'rubocop/cop/
|
|
14
|
+
require_relative 'rubocop/cop/capybara'
|
|
16
15
|
|
|
17
16
|
RuboCop::Cop::Style::TrailingCommaInArguments.singleton_class.prepend(
|
|
18
17
|
Module.new do
|
|
19
18
|
def autocorrect_incompatible_with
|
|
20
|
-
super.push(
|
|
19
|
+
super.push(
|
|
20
|
+
RuboCop::Cop::Capybara::RSpec::CurrentPathExpectation,
|
|
21
|
+
RuboCop::Cop::Capybara::RSpec::CurrentUrlExpectation
|
|
22
|
+
)
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-capybara
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yudai Takada
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '1.
|
|
32
|
+
version: '1.89'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '1.
|
|
39
|
+
version: '1.89'
|
|
40
40
|
description: |
|
|
41
41
|
Code style checking for Capybara test files (RSpec, Cucumber, Minitest).
|
|
42
42
|
A plugin for the RuboCop code style enforcing & linting tool.
|
|
@@ -51,22 +51,32 @@ files:
|
|
|
51
51
|
- MIT-LICENSE.md
|
|
52
52
|
- README.md
|
|
53
53
|
- config/default.yml
|
|
54
|
-
- config/obsoletion.yml
|
|
55
54
|
- lib/rubocop-capybara.rb
|
|
56
55
|
- lib/rubocop/capybara/config_formatter.rb
|
|
57
56
|
- lib/rubocop/capybara/cop/generator.rb
|
|
58
57
|
- lib/rubocop/capybara/description_extractor.rb
|
|
59
58
|
- lib/rubocop/capybara/plugin.rb
|
|
60
59
|
- lib/rubocop/capybara/version.rb
|
|
60
|
+
- lib/rubocop/cop/capybara.rb
|
|
61
|
+
- lib/rubocop/cop/capybara/absolute_x_path_in_element_scope.rb
|
|
62
|
+
- lib/rubocop/cop/capybara/always_true_count_query.rb
|
|
61
63
|
- lib/rubocop/cop/capybara/ambiguous_click.rb
|
|
62
64
|
- lib/rubocop/cop/capybara/assert_style.rb
|
|
63
|
-
- lib/rubocop/cop/capybara/
|
|
65
|
+
- lib/rubocop/cop/capybara/chained_find.rb
|
|
64
66
|
- lib/rubocop/cop/capybara/find_all_first.rb
|
|
67
|
+
- lib/rubocop/cop/capybara/ignored_query_option.rb
|
|
65
68
|
- lib/rubocop/cop/capybara/mixin/capybara_help.rb
|
|
66
69
|
- lib/rubocop/cop/capybara/mixin/css_attributes_parser.rb
|
|
67
70
|
- lib/rubocop/cop/capybara/mixin/css_selector.rb
|
|
71
|
+
- lib/rubocop/cop/capybara/mixin/query_methods.rb
|
|
72
|
+
- lib/rubocop/cop/capybara/mixin/visibility.rb
|
|
73
|
+
- lib/rubocop/cop/capybara/modal_method_without_block.rb
|
|
74
|
+
- lib/rubocop/cop/capybara/native_attribute.rb
|
|
68
75
|
- lib/rubocop/cop/capybara/redundant_within_find.rb
|
|
76
|
+
- lib/rubocop/cop/capybara/rspec.rb
|
|
69
77
|
- lib/rubocop/cop/capybara/rspec/current_path_expectation.rb
|
|
78
|
+
- lib/rubocop/cop/capybara/rspec/current_url_expectation.rb
|
|
79
|
+
- lib/rubocop/cop/capybara/rspec/find_element_value_eq.rb
|
|
70
80
|
- lib/rubocop/cop/capybara/rspec/have_content.rb
|
|
71
81
|
- lib/rubocop/cop/capybara/rspec/have_selector.rb
|
|
72
82
|
- lib/rubocop/cop/capybara/rspec/match_style.rb
|
|
@@ -77,6 +87,8 @@ files:
|
|
|
77
87
|
- lib/rubocop/cop/capybara/rspec/visibility_matcher.rb
|
|
78
88
|
- lib/rubocop/cop/capybara/specific_actions.rb
|
|
79
89
|
- lib/rubocop/cop/capybara/specific_finders.rb
|
|
90
|
+
- lib/rubocop/cop/capybara/unordered_window_access.rb
|
|
91
|
+
- lib/rubocop/cop/capybara/visibility_option.rb
|
|
80
92
|
- lib/rubocop/cop/capybara_cops.rb
|
|
81
93
|
homepage: https://github.com/rubocop/rubocop-capybara
|
|
82
94
|
licenses:
|
|
@@ -100,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
112
|
- !ruby/object:Gem::Version
|
|
101
113
|
version: '0'
|
|
102
114
|
requirements: []
|
|
103
|
-
rubygems_version: 4.0.
|
|
115
|
+
rubygems_version: 4.0.20
|
|
104
116
|
specification_version: 4
|
|
105
117
|
summary: Code style checking for Capybara test files
|
|
106
118
|
test_files: []
|
data/config/obsoletion.yml
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Configuration of obsolete/deprecated cops used by `ConfigObsoletion`.
|
|
3
|
-
#
|
|
4
|
-
# See: https://docs.rubocop.org/rubocop/extensions.html#config-obsoletions
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
renamed:
|
|
8
|
-
Capybara/CurrentPathExpectation: Capybara/RSpec/CurrentPathExpectation
|
|
9
|
-
Capybara/NegationMatcher: Capybara/RSpec/NegationMatcher
|
|
10
|
-
Capybara/NegationMatcherAfterVisit: Capybara/RSpec/NegationMatcherAfterVisit
|
|
11
|
-
Capybara/SpecificMatcher: Capybara/RSpec/SpecificMatcher
|
|
12
|
-
Capybara/VisibilityMatcher: Capybara/RSpec/VisibilityMatcher
|
|
13
|
-
|
|
14
|
-
split:
|
|
15
|
-
Capybara/MatchStyle:
|
|
16
|
-
alternatives:
|
|
17
|
-
- Capybara/AssertStyle
|
|
18
|
-
- Capybara/RSpec/MatchStyle
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RuboCop
|
|
4
|
-
module Cop
|
|
5
|
-
module Capybara
|
|
6
|
-
# Checks for methods of button or link clicks.
|
|
7
|
-
#
|
|
8
|
-
# This cop is deprecated.
|
|
9
|
-
# We plan to remove this in the next major version update to 3.0.
|
|
10
|
-
#
|
|
11
|
-
# The migration target is `Capybara/AmbiguousClick`.
|
|
12
|
-
# It is only migration target when `EnforcedStyle: strict`.
|
|
13
|
-
# If you are using this cop, please plan for migration.
|
|
14
|
-
# There is no migration target when `EnforcedStyle: link_or_button`.
|
|
15
|
-
#
|
|
16
|
-
# By default, prefer to use `click_link_or_button` or `click_on`.
|
|
17
|
-
# These methods offer a weaker coupling between the test and HTML,
|
|
18
|
-
# allowing for a more faithful reflection of how the user behaves.
|
|
19
|
-
#
|
|
20
|
-
# You can set `EnforcedStyle: strict` to prefer the use of
|
|
21
|
-
# `click_link` and `click_button`, but this is a deprecated setting.
|
|
22
|
-
#
|
|
23
|
-
# @example EnforcedStyle: link_or_button (default)
|
|
24
|
-
# # bad
|
|
25
|
-
# click_link('foo')
|
|
26
|
-
# click_button('foo')
|
|
27
|
-
#
|
|
28
|
-
# # good
|
|
29
|
-
# click_link_or_button('foo')
|
|
30
|
-
# click_on('foo')
|
|
31
|
-
#
|
|
32
|
-
# @example EnforcedStyle: strict
|
|
33
|
-
# # bad
|
|
34
|
-
# click_link_or_button('foo')
|
|
35
|
-
# click_on('foo')
|
|
36
|
-
#
|
|
37
|
-
# # good
|
|
38
|
-
# click_link('foo')
|
|
39
|
-
# click_button('foo')
|
|
40
|
-
#
|
|
41
|
-
class ClickLinkOrButtonStyle < ::RuboCop::Cop::Base
|
|
42
|
-
include ConfigurableEnforcedStyle
|
|
43
|
-
|
|
44
|
-
MSG_STRICT =
|
|
45
|
-
'Use `click_link` or `click_button` instead of `%<method>s`.'
|
|
46
|
-
MSG_CLICK_LINK_OR_BUTTON =
|
|
47
|
-
'Use `click_link_or_button` or `click_on` instead of `%<method>s`.'
|
|
48
|
-
STRICT_METHODS = %i[click_link click_button].freeze
|
|
49
|
-
CLICK_LINK_OR_BUTTON = %i[click_link_or_button click_on].freeze
|
|
50
|
-
RESTRICT_ON_SEND = (STRICT_METHODS + CLICK_LINK_OR_BUTTON).freeze
|
|
51
|
-
|
|
52
|
-
def on_send(node)
|
|
53
|
-
return unless offense?(node)
|
|
54
|
-
|
|
55
|
-
add_offense(node, message: offense_message(node))
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
private
|
|
59
|
-
|
|
60
|
-
def offense?(node)
|
|
61
|
-
(style == :strict && !strict_method?(node)) ||
|
|
62
|
-
(style == :link_or_button && !link_or_button_method?(node))
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def offense_message(node)
|
|
66
|
-
if style == :strict
|
|
67
|
-
format(MSG_STRICT, method: node.method_name)
|
|
68
|
-
elsif style == :link_or_button
|
|
69
|
-
format(MSG_CLICK_LINK_OR_BUTTON, method: node.method_name)
|
|
70
|
-
else
|
|
71
|
-
# :nocov:
|
|
72
|
-
:noop
|
|
73
|
-
# :nocov:
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def strict_method?(node)
|
|
78
|
-
STRICT_METHODS.include?(node.method_name)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def link_or_button_method?(node)
|
|
82
|
-
CLICK_LINK_OR_BUTTON.include?(node.method_name)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|