File tree Expand file tree Collapse file tree 5 files changed +97
-4
lines changed
spec/rubocop/cop/capybara Expand file tree Collapse file tree 5 files changed +97
-4
lines changed Original file line number Diff line number Diff line change 1616- Add a new ` Capybara/NegationMatcherAfterVisit ` cop. ([ @ydah ] )
1717- Fix an error for ` Capybara/RSpec/HaveSelector ` when passing no arguments. ([ @earlopain ] )
1818- Make RuboCop Capybara work as a RuboCop plugin. ([ @bquorning ] )
19+ - Fix an incorrect autocorrect for ` Capybara/RedundantWithinFind ` when escape required css selector. ([ @ydah ] )
1920
2021## 2.21.0 (2024-06-08)
2122
Original file line number Diff line number Diff line change @@ -83,6 +83,18 @@ def multiple_selectors?(selector)
8383 normalize = selector . gsub ( /(\\ [>,+~]|\( .*\) )/ , '' )
8484 normalize . match? ( /[ >,+~]/ )
8585 end
86+
87+ # @param str [String]
88+ # @param quote [String] the quote character used (' or ")
89+ # @return [String]
90+ # @example
91+ # css_escape('foo.bar', "'") # => 'foo\.bar'
92+ # css_escape('foo.bar', '"') # => 'foo\\.bar'
93+ # css_escape('foo', "'") # => 'foo'
94+ def css_escape ( str , quote = "'" )
95+ escaped_dot = quote == '"' ? '\\\\\\\\.' : '\\\\.'
96+ str . gsub ( '.' , escaped_dot )
97+ end
8698 end
8799 end
88100 end
Original file line number Diff line number Diff line change @@ -27,6 +27,7 @@ module Capybara
2727 # end
2828 #
2929 class RedundantWithinFind < ::RuboCop ::Cop ::Base
30+ include CssSelector
3031 extend AutoCorrector
3132 MSG = 'Redundant `within %<method>s(...)` call detected.'
3233 RESTRICT_ON_SEND = %i[ within ] . freeze
@@ -53,13 +54,25 @@ def msg(node)
5354 end
5455
5556 def replaced ( node )
56- replaced = node . arguments . map ( &:source ) . join ( ', ' )
57- if node . method? ( :find_by_id )
58- replaced . sub ( /\A (["'])/ , '\1#' )
57+ unless node . method? ( :find_by_id )
58+ return node . arguments . map ( &:source ) . join ( ', ' )
59+ end
60+
61+ if node . first_argument . str_type?
62+ build_escaped_selector ( node . first_argument , node )
5963 else
60- replaced
64+ node . arguments . map ( &:source ) . join ( ', ' )
65+ . sub ( /\A (["'])/ , '\1#' )
6166 end
6267 end
68+
69+ def build_escaped_selector ( first_arg , node )
70+ quote = first_arg . source [ 0 ]
71+ escaped_id = CssSelector . css_escape ( first_arg . value , quote )
72+ rest_args = node . arguments . drop ( 1 ) . map ( &:source )
73+
74+ [ "#{ quote } ##{ escaped_id } #{ quote } " , *rest_args ] . join ( ', ' )
75+ end
6376 end
6477 end
6578 end
Original file line number Diff line number Diff line change 143143 expect ( described_class . multiple_selectors? ( 'a.cls\>b' ) ) . to be false
144144 end
145145 end
146+
147+ describe 'CssSelector.css_escape' do
148+ context 'with single quote' do
149+ it 'escapes dots with single backslash' do
150+ expect ( described_class . css_escape ( 'foo.bar' , "'" ) ) . to eq 'foo\\.bar'
151+ end
152+
153+ it 'does not escape when no dots' do
154+ expect ( described_class . css_escape ( 'foo' , "'" ) ) . to eq 'foo'
155+ end
156+
157+ it 'escapes multiple dots' do
158+ expect ( described_class . css_escape ( 'foo.bar.baz' , "'" ) ) . to eq 'foo\\.bar\\.baz'
159+ end
160+ end
161+
162+ context 'with double quote' do
163+ it 'escapes dots with double backslash for double-quoted strings' do
164+ expect ( described_class . css_escape ( 'foo.bar' , '"' ) ) . to eq 'foo\\\\.bar'
165+ end
166+
167+ it 'does not escape when no dots' do
168+ expect ( described_class . css_escape ( 'foo' , '"' ) ) . to eq 'foo'
169+ end
170+
171+ it 'escapes multiple dots' do
172+ expect ( described_class . css_escape ( 'foo.bar.baz' , '"' ) ) . to eq 'foo\\\\.bar\\\\.baz'
173+ end
174+ end
175+
176+ context 'with default quote (single quote)' do
177+ it 'uses single quote escaping by default' do
178+ expect ( described_class . css_escape ( 'foo.bar' ) ) . to eq 'foo\\.bar'
179+ end
180+ end
181+ end
146182end
Original file line number Diff line number Diff line change 6666 RUBY
6767 end
6868
69+ it 'registers an offense when using `within find_by_id("foo.bar")`' do
70+ expect_offense ( <<~RUBY )
71+ within find_by_id('foo.bar') do
72+ ^^^^^^^^^^^^^^^^^^^^^ Redundant `within find_by_id(...)` call detected.
73+ end
74+ within find_by_id("foo.bar") do
75+ ^^^^^^^^^^^^^^^^^^^^^ Redundant `within find_by_id(...)` call detected.
76+ end
77+ RUBY
78+
79+ expect_correction ( <<~'RUBY' )
80+ within '#foo\.bar' do
81+ end
82+ within "#foo\\.bar" do
83+ end
84+ RUBY
85+ end
86+
6987 it 'registers an offense when using `within find_by_id(...)` with ' \
7088 'other argument' do
7189 expect_offense ( <<~RUBY )
8098 RUBY
8199 end
82100
101+ it 'registers an offense when using `within find_by_id(variable)`' do
102+ expect_offense ( <<~RUBY )
103+ within find_by_id(id_variable) do
104+ ^^^^^^^^^^^^^^^^^^^^^^^ Redundant `within find_by_id(...)` call detected.
105+ end
106+ RUBY
107+
108+ expect_correction ( <<~RUBY )
109+ within id_variable do
110+ end
111+ RUBY
112+ end
113+
83114 it 'does not register an offense when using `within` without `find`' do
84115 expect_no_offenses ( <<~RUBY )
85116 within 'foo.bar' do
You can’t perform that action at this time.
0 commit comments