lib/rubocop/cop/rspec/capybara/specific_matcher.rb in rubocop-rspec-2.13.2 vs lib/rubocop/cop/rspec/capybara/specific_matcher.rb in rubocop-rspec-2.14.0
- old
+ new
@@ -24,121 +24,44 @@
# expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com')
# expect(page).to have_table(class: 'cls')
# expect(page).to have_select
# expect(page).to have_field('foo')
#
- class SpecificMatcher < Base # rubocop:disable Metrics/ClassLength
+ class SpecificMatcher < Base
+ include CapybaraHelp
+
MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
RESTRICT_ON_SEND = %i[have_selector have_no_selector have_css
have_no_css].freeze
SPECIFIC_MATCHER = {
'button' => 'button',
'a' => 'link',
'table' => 'table',
'select' => 'select',
'input' => 'field'
}.freeze
- SPECIFIC_MATCHER_OPTIONS = {
- 'button' => (
- CssSelector::COMMON_OPTIONS + %w[disabled name value title type]
- ).freeze,
- 'link' => (
- CssSelector::COMMON_OPTIONS + %w[href alt title download]
- ).freeze,
- 'table' => (
- CssSelector::COMMON_OPTIONS + %w[
- caption with_cols cols with_rows rows
- ]
- ).freeze,
- 'select' => (
- CssSelector::COMMON_OPTIONS + %w[
- disabled name placeholder options enabled_options
- disabled_options selected with_selected multiple with_options
- ]
- ).freeze,
- 'field' => (
- CssSelector::COMMON_OPTIONS + %w[
- checked unchecked disabled valid name placeholder
- validation_message readonly with type multiple
- ]
- ).freeze
- }.freeze
- SPECIFIC_MATCHER_PSEUDO_CLASSES = %w[
- not() disabled enabled checked unchecked
- ].freeze
# @!method first_argument(node)
def_node_matcher :first_argument, <<-PATTERN
(send nil? _ (str $_) ... )
PATTERN
- # @!method option?(node)
- def_node_search :option?, <<-PATTERN
- (pair (sym %) _)
- PATTERN
-
def on_send(node)
first_argument(node) do |arg|
next unless (matcher = specific_matcher(arg))
next if CssSelector.multiple_selectors?(arg)
- next unless specific_matcher_option?(node, arg, matcher)
- next unless specific_matcher_pseudo_classes?(arg)
+ next unless specific_option?(node, arg, matcher)
+ next unless specific_pseudo_classes?(arg)
add_offense(node, message: message(node, matcher))
end
end
private
def specific_matcher(arg)
splitted_arg = arg[/^\w+/, 0]
SPECIFIC_MATCHER[splitted_arg]
- end
-
- def specific_matcher_option?(node, arg, matcher)
- attrs = CssSelector.attributes(arg).keys
- return true if attrs.empty?
- return false unless replaceable_matcher?(node, matcher, attrs)
-
- attrs.all? do |attr|
- SPECIFIC_MATCHER_OPTIONS.fetch(matcher, []).include?(attr)
- end
- end
-
- def specific_matcher_pseudo_classes?(arg)
- CssSelector.pseudo_classes(arg).all? do |pseudo_class|
- replaceable_pseudo_class?(pseudo_class, arg)
- end
- end
-
- def replaceable_pseudo_class?(pseudo_class, arg)
- unless SPECIFIC_MATCHER_PSEUDO_CLASSES.include?(pseudo_class)
- return false
- end
-
- case pseudo_class
- when 'not()' then replaceable_pseudo_class_not?(arg)
- else true
- end
- end
-
- def replaceable_pseudo_class_not?(arg)
- arg.scan(/not\(.*?\)/).all? do |not_arg|
- CssSelector.attributes(not_arg).values.all? do |v|
- v.is_a?(TrueClass) || v.is_a?(FalseClass)
- end
- end
- end
-
- def replaceable_matcher?(node, matcher, attrs)
- case matcher
- when 'link' then replaceable_to_have_link?(node, attrs)
- else true
- end
- end
-
- def replaceable_to_have_link?(node, attrs)
- option?(node, :href) || attrs.include?('href')
end
def message(node, matcher)
format(MSG,
good_matcher: good_matcher(node, matcher),