rubocop-capybara 2.22.0 → 2.23.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.
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ module RSpec
7
+ # Enforces use of `have_no_*` or `not_to` for negated expectations.
8
+ #
9
+ # @example EnforcedStyle: have_no (default)
10
+ # # bad
11
+ # expect(page).not_to have_selector 'a'
12
+ # expect(page).not_to have_css('a')
13
+ #
14
+ # # good
15
+ # expect(page).to have_no_selector 'a'
16
+ # expect(page).to have_no_css('a')
17
+ #
18
+ # @example EnforcedStyle: not_to
19
+ # # bad
20
+ # expect(page).to have_no_selector 'a'
21
+ # expect(page).to have_no_css('a')
22
+ #
23
+ # # good
24
+ # expect(page).not_to have_selector 'a'
25
+ # expect(page).not_to have_css('a')
26
+ #
27
+ class NegationMatcher < ::RuboCop::Cop::Base
28
+ extend AutoCorrector
29
+ include ConfigurableEnforcedStyle
30
+ include CapybaraHelp
31
+
32
+ MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
33
+ RESTRICT_ON_SEND = (POSITIVE_MATCHERS + NEGATIVE_MATCHERS).freeze
34
+
35
+ # @!method not_to?(node)
36
+ def_node_matcher :not_to?, <<~PATTERN
37
+ (send ... {:not_to :to_not}
38
+ (send nil? %POSITIVE_MATCHERS ...))
39
+ PATTERN
40
+
41
+ # @!method have_no?(node)
42
+ def_node_matcher :have_no?, <<~PATTERN
43
+ (send ... :to
44
+ (send nil? %NEGATIVE_MATCHERS ...))
45
+ PATTERN
46
+
47
+ def on_send(node)
48
+ return unless offense?(node)
49
+
50
+ matcher = node.method_name.to_s
51
+ add_offense(offense_range(node),
52
+ message: message(matcher)) do |corrector|
53
+ corrector.replace(node.parent.loc.selector, replaced_runner)
54
+ corrector.replace(node.loc.selector,
55
+ replaced_matcher(matcher))
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def offense?(node)
62
+ node.arguments? &&
63
+ ((style == :have_no && not_to?(node.parent)) ||
64
+ (style == :not_to && have_no?(node.parent)))
65
+ end
66
+
67
+ def offense_range(node)
68
+ node.parent.loc.selector.with(end_pos: node.loc.selector.end_pos)
69
+ end
70
+
71
+ def message(matcher)
72
+ format(MSG,
73
+ runner: replaced_runner,
74
+ matcher: replaced_matcher(matcher))
75
+ end
76
+
77
+ def replaced_runner
78
+ case style
79
+ when :have_no
80
+ 'to'
81
+ when :not_to
82
+ 'not_to'
83
+ else
84
+ # :nocov:
85
+ :noop
86
+ # :nocov:
87
+ end
88
+ end
89
+
90
+ def replaced_matcher(matcher)
91
+ case style
92
+ when :have_no
93
+ matcher.sub('have_', 'have_no_')
94
+ when :not_to
95
+ matcher.sub('have_no_', 'have_')
96
+ else
97
+ # :nocov:
98
+ :noop
99
+ # :nocov:
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ module RSpec
7
+ # Do not allow negative matchers to be used immediately after `visit`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # visit foo_path
12
+ # expect(page).to have_no_link('bar')
13
+ # expect(page).to have_css('a')
14
+ #
15
+ # # good
16
+ # visit foo_path
17
+ # expect(page).to have_css('a')
18
+ # expect(page).to have_no_link('bar')
19
+ #
20
+ # # bad
21
+ # visit foo_path
22
+ # expect(page).not_to have_link('bar')
23
+ # expect(page).to have_css('a')
24
+ #
25
+ # # good
26
+ # visit foo_path
27
+ # expect(page).to have_css('a')
28
+ # expect(page).not_to have_link('bar')
29
+ #
30
+ class NegationMatcherAfterVisit < ::RuboCop::Cop::Base
31
+ include CapybaraHelp
32
+
33
+ MSG = 'Do not use negation matcher immediately after visit.'
34
+ RESTRICT_ON_SEND = %i[visit].freeze
35
+
36
+ # @!method negation_matcher?(node)
37
+ def_node_matcher :negation_matcher?, <<~PATTERN
38
+ {
39
+ (send (send nil? :expect _) :to (send nil? %NEGATIVE_MATCHERS ...))
40
+ (send (send nil? :expect _) :not_to (send nil? %POSITIVE_MATCHERS ...))
41
+ }
42
+ PATTERN
43
+
44
+ def on_send(node)
45
+ negation_matcher?(node.right_sibling) do
46
+ add_offense(node.right_sibling)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ module RSpec
7
+ # Checks for there is a more specific matcher offered by Capybara.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # expect(page).to have_selector('button')
13
+ # expect(page).to have_no_selector('button.cls')
14
+ # expect(page).to have_css('button')
15
+ # expect(page).to have_no_css('a.cls', href: 'http://example.com')
16
+ # expect(page).to have_css('table.cls')
17
+ # expect(page).to have_css('select')
18
+ # expect(page).to have_css('input', exact_text: 'foo')
19
+ #
20
+ # # good
21
+ # expect(page).to have_button
22
+ # expect(page).to have_no_button(class: 'cls')
23
+ # expect(page).to have_button
24
+ # expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com')
25
+ # expect(page).to have_table(class: 'cls')
26
+ # expect(page).to have_select
27
+ # expect(page).to have_field(with: 'foo')
28
+ #
29
+ class SpecificMatcher < ::RuboCop::Cop::Base
30
+ MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
31
+ RESTRICT_ON_SEND = %i[have_selector have_no_selector have_css
32
+ have_no_css].freeze
33
+ SPECIFIC_MATCHER = {
34
+ 'button' => 'button',
35
+ 'a' => 'link',
36
+ 'table' => 'table',
37
+ 'select' => 'select',
38
+ 'input' => 'field'
39
+ }.freeze
40
+
41
+ # @!method first_argument(node)
42
+ def_node_matcher :first_argument, <<~PATTERN
43
+ (send nil? _ (str $_) ... )
44
+ PATTERN
45
+
46
+ # @!method text_with_regexp?(node)
47
+ def_node_search :text_with_regexp?, <<~PATTERN
48
+ (pair (sym {:text :exact_text}) (regexp ...))
49
+ PATTERN
50
+
51
+ def on_send(node)
52
+ first_argument(node) do |arg|
53
+ next unless (matcher = specific_matcher(arg))
54
+ next if CssSelector.multiple_selectors?(arg)
55
+ next unless replaceable?(node, arg, matcher)
56
+
57
+ add_offense(node, message: message(node, matcher))
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def specific_matcher(arg)
64
+ splitted_arg = arg[/^\w+/, 0]
65
+ SPECIFIC_MATCHER[splitted_arg]
66
+ end
67
+
68
+ def replaceable?(node, arg, matcher)
69
+ replaceable_attributes?(arg) &&
70
+ !text_with_regexp?(node) &&
71
+ CapybaraHelp.replaceable_option?(node, arg, matcher) &&
72
+ CapybaraHelp.replaceable_pseudo_classes?(arg)
73
+ end
74
+
75
+ def replaceable_attributes?(selector)
76
+ CapybaraHelp.replaceable_attributes?(
77
+ CssSelector.attributes(selector)
78
+ )
79
+ end
80
+
81
+ def message(node, matcher)
82
+ format(MSG,
83
+ good_matcher: good_matcher(node, matcher),
84
+ bad_matcher: node.method_name)
85
+ end
86
+
87
+ def good_matcher(node, matcher)
88
+ node.method_name
89
+ .to_s
90
+ .gsub(/selector|css/, matcher.to_s)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ module RSpec
7
+ # Checks for boolean visibility in Capybara finders.
8
+ #
9
+ # Capybara lets you find elements that match a certain visibility using
10
+ # the `:visible` option. `:visible` accepts both boolean and symbols as
11
+ # values, however using booleans can have unwanted effects. `visible:
12
+ # false` does not find just invisible elements, but both visible and
13
+ # invisible elements. For expressiveness and clarity, use one of the
14
+ # symbol values, `:all`, `:hidden` or `:visible`.
15
+ # Read more at: https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all
16
+ #
17
+ # @example
18
+ # # bad
19
+ # expect(page).to have_selector('.foo', visible: false)
20
+ # expect(page).to have_css('.foo', visible: true)
21
+ # expect(page).to have_link('my link', visible: false)
22
+ #
23
+ # # good
24
+ # expect(page).to have_selector('.foo', visible: :visible)
25
+ # expect(page).to have_css('.foo', visible: :all)
26
+ # expect(page).to have_link('my link', visible: :hidden)
27
+ #
28
+ class VisibilityMatcher < ::RuboCop::Cop::Base
29
+ MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.'
30
+ MSG_TRUE = 'Use `:visible` instead of `true`.'
31
+ CAPYBARA_MATCHER_METHODS = %w[
32
+ button
33
+ checked_field
34
+ css
35
+ field
36
+ link
37
+ select
38
+ selector
39
+ table
40
+ unchecked_field
41
+ xpath
42
+ ].flat_map do |element|
43
+ ["have_#{element}".to_sym, "have_no_#{element}".to_sym]
44
+ end
45
+
46
+ RESTRICT_ON_SEND = CAPYBARA_MATCHER_METHODS
47
+
48
+ # @!method visible_true?(node)
49
+ def_node_matcher :visible_true?, <<~PATTERN
50
+ (send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) true) ...>))
51
+ PATTERN
52
+
53
+ # @!method visible_false?(node)
54
+ def_node_matcher :visible_false?, <<~PATTERN
55
+ (send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) false) ...>))
56
+ PATTERN
57
+
58
+ def on_send(node)
59
+ visible_false?(node) { |arg| add_offense(arg, message: MSG_FALSE) }
60
+ visible_true?(node) { |arg| add_offense(arg, message: MSG_TRUE) }
61
+ end
62
+
63
+ private
64
+
65
+ def capybara_matcher?(method_name)
66
+ CAPYBARA_MATCHER_METHODS.include? method_name
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,17 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'capybara/rspec/current_path_expectation'
4
+ require_relative 'capybara/rspec/have_content'
3
5
  require_relative 'capybara/rspec/have_selector'
6
+ require_relative 'capybara/rspec/match_style'
7
+ require_relative 'capybara/rspec/negation_matcher'
8
+ require_relative 'capybara/rspec/negation_matcher_after_visit'
4
9
  require_relative 'capybara/rspec/predicate_matcher'
10
+ require_relative 'capybara/rspec/specific_matcher'
11
+ require_relative 'capybara/rspec/visibility_matcher'
5
12
 
6
13
  require_relative 'capybara/ambiguous_click'
14
+ require_relative 'capybara/assert_style'
7
15
  require_relative 'capybara/click_link_or_button_style'
8
- require_relative 'capybara/current_path_expectation'
9
16
  require_relative 'capybara/find_all_first'
10
- require_relative 'capybara/match_style'
11
- require_relative 'capybara/negation_matcher'
12
- require_relative 'capybara/negation_matcher_after_visit'
13
17
  require_relative 'capybara/redundant_within_find'
14
18
  require_relative 'capybara/specific_actions'
15
19
  require_relative 'capybara/specific_finders'
16
- require_relative 'capybara/specific_matcher'
17
- require_relative 'capybara/visibility_matcher'
@@ -6,6 +6,7 @@ require 'yaml'
6
6
  require 'rubocop'
7
7
 
8
8
  require_relative 'rubocop/capybara/plugin'
9
+ require_relative 'rubocop/capybara/version'
9
10
 
10
11
  require_relative 'rubocop/cop/capybara/mixin/capybara_help'
11
12
  require_relative 'rubocop/cop/capybara/mixin/css_attributes_parser'
@@ -16,7 +17,7 @@ require_relative 'rubocop/cop/capybara_cops'
16
17
  RuboCop::Cop::Style::TrailingCommaInArguments.singleton_class.prepend(
17
18
  Module.new do
18
19
  def autocorrect_incompatible_with
19
- super.push(RuboCop::Cop::Capybara::CurrentPathExpectation)
20
+ super.push(RuboCop::Cop::Capybara::RSpec::CurrentPathExpectation)
20
21
  end
21
22
  end
22
23
  )
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.22.0
4
+ version: 2.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: lint_roller
@@ -29,20 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '1.72'
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: 1.72.1
32
+ version: '1.81'
36
33
  type: :runtime
37
34
  prerelease: false
38
35
  version_requirements: !ruby/object:Gem::Requirement
39
36
  requirements:
40
37
  - - "~>"
41
38
  - !ruby/object:Gem::Version
42
- version: '1.72'
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- version: 1.72.1
39
+ version: '1.81'
46
40
  description: |
47
41
  Code style checking for Capybara test files (RSpec, Cucumber, Minitest).
48
42
  A plugin for the RuboCop code style enforcing & linting tool.
@@ -57,6 +51,7 @@ files:
57
51
  - MIT-LICENSE.md
58
52
  - README.md
59
53
  - config/default.yml
54
+ - config/obsoletion.yml
60
55
  - lib/rubocop-capybara.rb
61
56
  - lib/rubocop/capybara/config_formatter.rb
62
57
  - lib/rubocop/capybara/cop/generator.rb
@@ -64,22 +59,24 @@ files:
64
59
  - lib/rubocop/capybara/plugin.rb
65
60
  - lib/rubocop/capybara/version.rb
66
61
  - lib/rubocop/cop/capybara/ambiguous_click.rb
62
+ - lib/rubocop/cop/capybara/assert_style.rb
67
63
  - lib/rubocop/cop/capybara/click_link_or_button_style.rb
68
- - lib/rubocop/cop/capybara/current_path_expectation.rb
69
64
  - lib/rubocop/cop/capybara/find_all_first.rb
70
- - lib/rubocop/cop/capybara/match_style.rb
71
65
  - lib/rubocop/cop/capybara/mixin/capybara_help.rb
72
66
  - lib/rubocop/cop/capybara/mixin/css_attributes_parser.rb
73
67
  - lib/rubocop/cop/capybara/mixin/css_selector.rb
74
- - lib/rubocop/cop/capybara/negation_matcher.rb
75
- - lib/rubocop/cop/capybara/negation_matcher_after_visit.rb
76
68
  - lib/rubocop/cop/capybara/redundant_within_find.rb
69
+ - lib/rubocop/cop/capybara/rspec/current_path_expectation.rb
70
+ - lib/rubocop/cop/capybara/rspec/have_content.rb
77
71
  - lib/rubocop/cop/capybara/rspec/have_selector.rb
72
+ - lib/rubocop/cop/capybara/rspec/match_style.rb
73
+ - lib/rubocop/cop/capybara/rspec/negation_matcher.rb
74
+ - lib/rubocop/cop/capybara/rspec/negation_matcher_after_visit.rb
78
75
  - lib/rubocop/cop/capybara/rspec/predicate_matcher.rb
76
+ - lib/rubocop/cop/capybara/rspec/specific_matcher.rb
77
+ - lib/rubocop/cop/capybara/rspec/visibility_matcher.rb
79
78
  - lib/rubocop/cop/capybara/specific_actions.rb
80
79
  - lib/rubocop/cop/capybara/specific_finders.rb
81
- - lib/rubocop/cop/capybara/specific_matcher.rb
82
- - lib/rubocop/cop/capybara/visibility_matcher.rb
83
80
  - lib/rubocop/cop/capybara_cops.rb
84
81
  homepage: https://github.com/rubocop/rubocop-capybara
85
82
  licenses:
@@ -103,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
100
  - !ruby/object:Gem::Version
104
101
  version: '0'
105
102
  requirements: []
106
- rubygems_version: 3.6.2
103
+ rubygems_version: 4.0.6
107
104
  specification_version: 4
108
105
  summary: Code style checking for Capybara test files
109
106
  test_files: []
@@ -1,141 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Capybara
6
- # Checks that no expectations are set on Capybara's `current_path`.
7
- #
8
- # The
9
- # https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers#have_current_path-instance_method[`have_current_path` matcher]
10
- # should be used on `page` to set expectations on Capybara's
11
- # current path, since it uses
12
- # https://github.com/teamcapybara/capybara/blob/master/README.md#asynchronous-javascript-ajax-and-friends[Capybara's waiting functionality]
13
- # which ensures that preceding actions (like `click_link`) have
14
- # completed.
15
- #
16
- # This cop does not support autocorrection in some cases.
17
- #
18
- # @example
19
- # # bad
20
- # expect(current_path).to eq('/callback')
21
- # expect(page.current_path).to eq('/callback')
22
- #
23
- # # good
24
- # expect(page).to have_current_path('/callback', ignore_query: true)
25
- #
26
- # # bad (does not support autocorrection when `match` with a variable)
27
- # expect(page).to match(variable)
28
- #
29
- class CurrentPathExpectation < ::RuboCop::Cop::Base
30
- extend AutoCorrector
31
- include RangeHelp
32
-
33
- MSG = 'Do not set an RSpec expectation on `current_path` in ' \
34
- 'Capybara feature specs - instead, use the ' \
35
- '`have_current_path` matcher on `page`'
36
-
37
- RESTRICT_ON_SEND = %i[expect].freeze
38
-
39
- # @!method expectation_set_on_current_path(node)
40
- def_node_matcher :expectation_set_on_current_path, <<~PATTERN
41
- (send nil? :expect (send {(send nil? :page) nil?} :current_path))
42
- PATTERN
43
-
44
- # Supported matchers: eq(...) / match(/regexp/) / match('regexp')
45
- # @!method as_is_matcher(node)
46
- def_node_matcher :as_is_matcher, <<~PATTERN
47
- (send
48
- #expectation_set_on_current_path ${:to :to_not :not_to}
49
- ${(send nil? :eq ...) (send nil? :match (regexp ...))})
50
- PATTERN
51
-
52
- # @!method regexp_node_matcher(node)
53
- def_node_matcher :regexp_node_matcher, <<~PATTERN
54
- (send
55
- #expectation_set_on_current_path ${:to :to_not :not_to}
56
- $(send nil? :match ${str dstr xstr}))
57
- PATTERN
58
-
59
- def self.autocorrect_incompatible_with
60
- [Style::TrailingCommaInArguments]
61
- end
62
-
63
- def on_send(node)
64
- expectation_set_on_current_path(node) do
65
- add_offense(node.loc.selector) do |corrector|
66
- next unless node.chained?
67
-
68
- autocorrect(corrector, node)
69
- end
70
- end
71
- end
72
-
73
- private
74
-
75
- def autocorrect(corrector, node)
76
- as_is_matcher(node.parent) do |to_sym, matcher_node|
77
- rewrite_expectation(corrector, node, to_sym, matcher_node)
78
- end
79
-
80
- regexp_node_matcher(node.parent) do |to_sym, matcher_node, regexp|
81
- rewrite_expectation(corrector, node, to_sym, matcher_node)
82
- convert_regexp_node_to_literal(corrector, matcher_node, regexp)
83
- end
84
- end
85
-
86
- def rewrite_expectation(corrector, node, to_symbol, matcher_node)
87
- corrector.replace(node.first_argument, 'page')
88
- corrector.replace(node.parent.loc.selector, 'to')
89
- matcher_method = if to_symbol == :to
90
- 'have_current_path'
91
- else
92
- 'have_no_current_path'
93
- end
94
- corrector.replace(matcher_node.loc.selector, matcher_method)
95
- add_argument_parentheses(corrector, matcher_node.first_argument)
96
- add_ignore_query_options(corrector, node, matcher_node)
97
- end
98
-
99
- def convert_regexp_node_to_literal(corrector, matcher_node, regexp_node)
100
- str_node = matcher_node.first_argument
101
- regexp_expr = regexp_node_to_regexp_expr(regexp_node)
102
- corrector.replace(str_node, regexp_expr)
103
- end
104
-
105
- def regexp_node_to_regexp_expr(regexp_node)
106
- if regexp_node.xstr_type?
107
- "/\#{`#{regexp_node.value.value}`}/"
108
- else
109
- Regexp.new(regexp_node.value).inspect
110
- end
111
- end
112
-
113
- def add_argument_parentheses(corrector, arg_node)
114
- return unless method_call_with_no_parentheses?(arg_node)
115
-
116
- first_argument_range = range_with_surrounding_space(
117
- arg_node.first_argument.source_range, side: :left
118
- )
119
- corrector.insert_before(first_argument_range, '(')
120
- corrector.insert_after(arg_node.last_argument, ')')
121
- end
122
-
123
- def method_call_with_no_parentheses?(arg_node)
124
- arg_node.send_type? && arg_node.arguments? && !arg_node.parenthesized?
125
- end
126
-
127
- # `have_current_path` with no options will include the querystring
128
- # while `page.current_path` does not.
129
- # This ensures the option `ignore_query: true` is added
130
- # except when `match` matcher.
131
- def add_ignore_query_options(corrector, node, matcher_node)
132
- return if matcher_node.method?(:match)
133
-
134
- expectation_node = node.parent.last_argument
135
- expectation_last_child = expectation_node.children.last
136
- corrector.insert_after(expectation_last_child, ', ignore_query: true')
137
- end
138
- end
139
- end
140
- end
141
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Capybara
6
- # Checks for usage of deprecated style methods.
7
- #
8
- # @example when using `assert_style`
9
- # # bad
10
- # page.find(:css, '#first').assert_style(display: 'block')
11
- #
12
- # # good
13
- # page.find(:css, '#first').assert_matches_style(display: 'block')
14
- #
15
- # @example when using `has_style?`
16
- # # bad
17
- # expect(page.find(:css, 'first')
18
- # .has_style?(display: 'block')).to be true
19
- #
20
- # # good
21
- # expect(page.find(:css, 'first')
22
- # .matches_style?(display: 'block')).to be true
23
- #
24
- # @example when using `have_style`
25
- # # bad
26
- # expect(page).to have_style(display: 'block')
27
- #
28
- # # good
29
- # expect(page).to match_style(display: 'block')
30
- #
31
- class MatchStyle < ::RuboCop::Cop::Base
32
- extend AutoCorrector
33
-
34
- MSG = 'Use `%<good>s` instead of `%<bad>s`.'
35
- RESTRICT_ON_SEND = %i[assert_style has_style? have_style].freeze
36
- PREFERRED_METHOD = {
37
- 'assert_style' => 'assert_matches_style',
38
- 'has_style?' => 'matches_style?',
39
- 'have_style' => 'match_style'
40
- }.freeze
41
-
42
- def on_send(node)
43
- method_node = node.loc.selector
44
- add_offense(method_node) do |corrector|
45
- corrector.replace(method_node,
46
- PREFERRED_METHOD[method_node.source])
47
- end
48
- end
49
-
50
- private
51
-
52
- def message(node)
53
- format(MSG, good: PREFERRED_METHOD[node.source], bad: node.source)
54
- end
55
- end
56
- end
57
- end
58
- end