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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/config/default.yml +59 -43
- data/config/obsoletion.yml +18 -0
- data/lib/rubocop/capybara/version.rb +1 -1
- data/lib/rubocop/cop/capybara/assert_style.rb +30 -0
- data/lib/rubocop/cop/capybara/find_all_first.rb +16 -2
- data/lib/rubocop/cop/capybara/mixin/css_selector.rb +12 -0
- data/lib/rubocop/cop/capybara/redundant_within_find.rb +17 -4
- data/lib/rubocop/cop/capybara/rspec/current_path_expectation.rb +146 -0
- data/lib/rubocop/cop/capybara/rspec/have_content.rb +51 -0
- data/lib/rubocop/cop/capybara/rspec/have_selector.rb +2 -1
- data/lib/rubocop/cop/capybara/rspec/match_style.rb +53 -0
- data/lib/rubocop/cop/capybara/rspec/negation_matcher.rb +106 -0
- data/lib/rubocop/cop/capybara/rspec/negation_matcher_after_visit.rb +53 -0
- data/lib/rubocop/cop/capybara/rspec/specific_matcher.rb +96 -0
- data/lib/rubocop/cop/capybara/rspec/visibility_matcher.rb +72 -0
- data/lib/rubocop/cop/capybara_cops.rb +8 -6
- data/lib/rubocop-capybara.rb +2 -1
- metadata +14 -17
- data/lib/rubocop/cop/capybara/current_path_expectation.rb +0 -141
- data/lib/rubocop/cop/capybara/match_style.rb +0 -58
- data/lib/rubocop/cop/capybara/negation_matcher.rb +0 -104
- data/lib/rubocop/cop/capybara/negation_matcher_after_visit.rb +0 -51
- data/lib/rubocop/cop/capybara/specific_matcher.rb +0 -94
- data/lib/rubocop/cop/capybara/visibility_matcher.rb +0 -71
|
@@ -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'
|
data/lib/rubocop-capybara.rb
CHANGED
|
@@ -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.
|
|
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:
|
|
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.
|
|
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.
|
|
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:
|
|
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
|