rubocop-capybara 2.22.1 → 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.
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Capybara
6
- # Enforces use of `have_no_*` or `not_to` for negated expectations.
7
- #
8
- # @example EnforcedStyle: have_no (default)
9
- # # bad
10
- # expect(page).not_to have_selector 'a'
11
- # expect(page).not_to have_css('a')
12
- #
13
- # # good
14
- # expect(page).to have_no_selector 'a'
15
- # expect(page).to have_no_css('a')
16
- #
17
- # @example EnforcedStyle: not_to
18
- # # bad
19
- # expect(page).to have_no_selector 'a'
20
- # expect(page).to have_no_css('a')
21
- #
22
- # # good
23
- # expect(page).not_to have_selector 'a'
24
- # expect(page).not_to have_css('a')
25
- #
26
- class NegationMatcher < ::RuboCop::Cop::Base
27
- extend AutoCorrector
28
- include ConfigurableEnforcedStyle
29
- include CapybaraHelp
30
-
31
- MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
32
- RESTRICT_ON_SEND = (POSITIVE_MATCHERS + NEGATIVE_MATCHERS).freeze
33
-
34
- # @!method not_to?(node)
35
- def_node_matcher :not_to?, <<~PATTERN
36
- (send ... {:not_to :to_not}
37
- (send nil? %POSITIVE_MATCHERS ...))
38
- PATTERN
39
-
40
- # @!method have_no?(node)
41
- def_node_matcher :have_no?, <<~PATTERN
42
- (send ... :to
43
- (send nil? %NEGATIVE_MATCHERS ...))
44
- PATTERN
45
-
46
- def on_send(node)
47
- return unless offense?(node)
48
-
49
- matcher = node.method_name.to_s
50
- add_offense(offense_range(node),
51
- message: message(matcher)) do |corrector|
52
- corrector.replace(node.parent.loc.selector, replaced_runner)
53
- corrector.replace(node.loc.selector,
54
- replaced_matcher(matcher))
55
- end
56
- end
57
-
58
- private
59
-
60
- def offense?(node)
61
- node.arguments? &&
62
- ((style == :have_no && not_to?(node.parent)) ||
63
- (style == :not_to && have_no?(node.parent)))
64
- end
65
-
66
- def offense_range(node)
67
- node.parent.loc.selector.with(end_pos: node.loc.selector.end_pos)
68
- end
69
-
70
- def message(matcher)
71
- format(MSG,
72
- runner: replaced_runner,
73
- matcher: replaced_matcher(matcher))
74
- end
75
-
76
- def replaced_runner
77
- case style
78
- when :have_no
79
- 'to'
80
- when :not_to
81
- 'not_to'
82
- else
83
- # :nocov:
84
- :noop
85
- # :nocov:
86
- end
87
- end
88
-
89
- def replaced_matcher(matcher)
90
- case style
91
- when :have_no
92
- matcher.sub('have_', 'have_no_')
93
- when :not_to
94
- matcher.sub('have_no_', 'have_')
95
- else
96
- # :nocov:
97
- :noop
98
- # :nocov:
99
- end
100
- end
101
- end
102
- end
103
- end
104
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Capybara
6
- # Do not allow negative matchers to be used immediately after `visit`.
7
- #
8
- # @example
9
- # # bad
10
- # visit foo_path
11
- # expect(page).to have_no_link('bar')
12
- # expect(page).to have_css('a')
13
- #
14
- # # good
15
- # visit foo_path
16
- # expect(page).to have_css('a')
17
- # expect(page).to have_no_link('bar')
18
- #
19
- # # bad
20
- # visit foo_path
21
- # expect(page).not_to have_link('bar')
22
- # expect(page).to have_css('a')
23
- #
24
- # # good
25
- # visit foo_path
26
- # expect(page).to have_css('a')
27
- # expect(page).not_to have_link('bar')
28
- #
29
- class NegationMatcherAfterVisit < ::RuboCop::Cop::Base
30
- include CapybaraHelp
31
-
32
- MSG = 'Do not use negation matcher immediately after visit.'
33
- RESTRICT_ON_SEND = %i[visit].freeze
34
-
35
- # @!method negation_matcher?(node)
36
- def_node_matcher :negation_matcher?, <<~PATTERN
37
- {
38
- (send (send nil? :expect _) :to (send nil? %NEGATIVE_MATCHERS ...))
39
- (send (send nil? :expect _) :not_to (send nil? %POSITIVE_MATCHERS ...))
40
- }
41
- PATTERN
42
-
43
- def on_send(node)
44
- negation_matcher?(node.right_sibling) do
45
- add_offense(node.right_sibling)
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Capybara
6
- # Checks for there is a more specific matcher offered by Capybara.
7
- #
8
- # @example
9
- #
10
- # # bad
11
- # expect(page).to have_selector('button')
12
- # expect(page).to have_no_selector('button.cls')
13
- # expect(page).to have_css('button')
14
- # expect(page).to have_no_css('a.cls', href: 'http://example.com')
15
- # expect(page).to have_css('table.cls')
16
- # expect(page).to have_css('select')
17
- # expect(page).to have_css('input', exact_text: 'foo')
18
- #
19
- # # good
20
- # expect(page).to have_button
21
- # expect(page).to have_no_button(class: 'cls')
22
- # expect(page).to have_button
23
- # expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com')
24
- # expect(page).to have_table(class: 'cls')
25
- # expect(page).to have_select
26
- # expect(page).to have_field(with: 'foo')
27
- #
28
- class SpecificMatcher < ::RuboCop::Cop::Base
29
- MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
30
- RESTRICT_ON_SEND = %i[have_selector have_no_selector have_css
31
- have_no_css].freeze
32
- SPECIFIC_MATCHER = {
33
- 'button' => 'button',
34
- 'a' => 'link',
35
- 'table' => 'table',
36
- 'select' => 'select',
37
- 'input' => 'field'
38
- }.freeze
39
-
40
- # @!method first_argument(node)
41
- def_node_matcher :first_argument, <<~PATTERN
42
- (send nil? _ (str $_) ... )
43
- PATTERN
44
-
45
- # @!method text_with_regexp?(node)
46
- def_node_search :text_with_regexp?, <<~PATTERN
47
- (pair (sym {:text :exact_text}) (regexp ...))
48
- PATTERN
49
-
50
- def on_send(node)
51
- first_argument(node) do |arg|
52
- next unless (matcher = specific_matcher(arg))
53
- next if CssSelector.multiple_selectors?(arg)
54
- next unless replaceable?(node, arg, matcher)
55
-
56
- add_offense(node, message: message(node, matcher))
57
- end
58
- end
59
-
60
- private
61
-
62
- def specific_matcher(arg)
63
- splitted_arg = arg[/^\w+/, 0]
64
- SPECIFIC_MATCHER[splitted_arg]
65
- end
66
-
67
- def replaceable?(node, arg, matcher)
68
- replaceable_attributes?(arg) &&
69
- !text_with_regexp?(node) &&
70
- CapybaraHelp.replaceable_option?(node, arg, matcher) &&
71
- CapybaraHelp.replaceable_pseudo_classes?(arg)
72
- end
73
-
74
- def replaceable_attributes?(selector)
75
- CapybaraHelp.replaceable_attributes?(
76
- CssSelector.attributes(selector)
77
- )
78
- end
79
-
80
- def message(node, matcher)
81
- format(MSG,
82
- good_matcher: good_matcher(node, matcher),
83
- bad_matcher: node.method_name)
84
- end
85
-
86
- def good_matcher(node, matcher)
87
- node.method_name
88
- .to_s
89
- .gsub(/selector|css/, matcher.to_s)
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module Capybara
6
- # Checks for boolean visibility in Capybara finders.
7
- #
8
- # Capybara lets you find elements that match a certain visibility using
9
- # the `:visible` option. `:visible` accepts both boolean and symbols as
10
- # values, however using booleans can have unwanted effects. `visible:
11
- # false` does not find just invisible elements, but both visible and
12
- # invisible elements. For expressiveness and clarity, use one of the
13
- # symbol values, `:all`, `:hidden` or `:visible`.
14
- # Read more in
15
- # https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all[the documentation].
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