Skip to content

Commit b5b503c

Browse files
committed
Fix incorrect matches for long namespaces when run in relative dir
When rubocop is run in a relative path to a spec with long namespace, e.g. in the same directory, the FilePath spec will return incorrect matches. This fixes them by switching to a regular expression based pattern matching which allows more specific matchign than the previously used globs. Adds new specs to ensure the file extension is matched against the ruby file extension (.rb). Fixes #1091
1 parent f9f14e2 commit b5b503c

File tree

3 files changed

+51
-31
lines changed

3 files changed

+51
-31
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Master (Unreleased)
44

5+
* Fix `RSpec/FilePath` false positive for relative file path runs with long namespaces. ([@ahukkanen][])
6+
57
## 2.0.1 (2020-12-02)
68

79
* Fixed infinite loop in `RSpec/ExpectActual` autocorrection when both expected and actual values are literals. ([@Darhazer][])
@@ -590,3 +592,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
590592
[@Rafix02]: https://github.com/Rafix02
591593
[@PhilCoggins]: https://github.com/PhilCoggins
592594
[@sl4vr]: https://github.com/sl4vr
595+
[@ahukkanen]: https://github.com/ahukkanen

lib/rubocop/cop/rspec/file_path.rb

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,30 +82,39 @@ def on_top_level_example_group(node)
8282
private
8383

8484
def ensure_correct_file_path(send_node, described_class, arguments)
85-
glob = glob_for(described_class, arguments.first)
86-
return if filename_ends_with?(glob)
87-
88-
add_offense(send_node, message: format(MSG, suffix: glob))
85+
pattern = pattern_for(described_class, arguments.first)
86+
return if filename_ends_with?(pattern)
87+
88+
# For the suffix shown in the offense message, modify the regular
89+
# expression pattern to resemble a glob pattern for clearer error
90+
# messages.
91+
offense_suffix = pattern.gsub('.*', '*').sub('[^/]', '')
92+
.sub('\.', '.')
93+
add_offense(send_node, message: format(MSG, suffix: offense_suffix))
8994
end
9095

9196
def routing_spec?(args)
9297
args.any?(&method(:routing_metadata?))
9398
end
9499

95-
def glob_for(described_class, method_name)
96-
return glob_for_spec_suffix_only? if spec_suffix_only?
100+
def pattern_for(described_class, method_name)
101+
return pattern_for_spec_suffix_only? if spec_suffix_only?
97102

98-
"#{expected_path(described_class)}#{name_glob(method_name)}*_spec.rb"
103+
[
104+
expected_path(described_class),
105+
name_pattern(method_name),
106+
'[^/]*_spec\.rb'
107+
].join
99108
end
100109

101-
def glob_for_spec_suffix_only?
102-
'*_spec.rb'
110+
def pattern_for_spec_suffix_only?
111+
'.*_spec\.rb'
103112
end
104113

105-
def name_glob(method_name)
114+
def name_pattern(method_name)
106115
return unless method_name&.str_type?
107116

108-
"*#{method_name.str_content.gsub(/\W/, '')}" unless ignore_methods?
117+
".*#{method_name.str_content.gsub(/\W/, '')}" unless ignore_methods?
109118
end
110119

111120
def expected_path(constant)
@@ -131,11 +140,9 @@ def ignore_methods?
131140
cop_config['IgnoreMethods']
132141
end
133142

134-
def filename_ends_with?(glob)
135-
filename =
136-
RuboCop::PathUtil.relative_path(processed_source.buffer.name)
137-
.gsub('../', '')
138-
File.fnmatch?("*#{glob}", filename)
143+
def filename_ends_with?(pattern)
144+
filename = File.expand_path(processed_source.buffer.name)
145+
filename.match?("#{pattern}$")
139146
end
140147

141148
def relevant_rubocop_rspec_file?(_file)

spec/rubocop/cop/rspec/file_path_spec.rb

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@
6464
RUBY
6565
end
6666

67+
it 'registers an offense for a file without the .rb extension' do
68+
expect_offense(<<-RUBY, 'spec/models/user_specxrb')
69+
describe User do; end
70+
^^^^^^^^^^^^^ Spec path should end with `user*_spec.rb`.
71+
RUBY
72+
end
73+
6774
it 'ignores shared examples' do
6875
expect_no_offenses(<<-RUBY, 'spec/models/user.rb')
6976
shared_examples_for 'foo' do; end
@@ -183,30 +190,26 @@
183190
RUBY
184191
end
185192

186-
it 'uses relative path' do
187-
allow(RuboCop::PathUtil)
188-
.to receive(:relative_path).and_return('spec/models/bar_spec.rb')
193+
it 'registers an offense for a path containing the class name' do
189194
expect_offense(<<-RUBY, '/home/foo/spec/models/bar_spec.rb')
190195
describe Foo do; end
191196
^^^^^^^^^^^^ Spec path should end with `foo*_spec.rb`.
192197
RUBY
193198
end
194199

195-
it 'uses relative path for sibling directory project' do
196-
allow(RuboCop::PathUtil)
197-
.to receive(:relative_path)
198-
.and_return('../ext-project/spec/models/bar_spec.rb')
199-
expect_no_offenses(<<-RUBY, '/home/ext-project/spec/models/bar_spec.rb')
200-
describe Bar do; end
200+
it 'detects the path based on a class name with long module' do
201+
expect_offense(<<-RUBY, '/home/foo/spec/very/my_class_spec.rb')
202+
describe Very::Long::Namespace::MyClass do; end
203+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Spec path should end with `very/long/namespace/my_class*_spec.rb`.
201204
RUBY
202205
end
203206

204-
it 'uses relative path for different path project' do
205-
allow(RuboCop::PathUtil)
206-
.to receive(:relative_path)
207-
.and_return('../../opt/ext-project/spec/models/bar_spec.rb')
208-
expect_no_offenses(<<-RUBY, '/opt/ext-project/spec/models/bar_spec.rb')
209-
describe Bar do; end
207+
it 'detects the path based the absolute path of the file' do
208+
allow(File).to receive(:expand_path).with('my_class_spec.rb').and_return(
209+
'/home/foo/spec/very/long/namespace/my_class_spec.rb'
210+
)
211+
expect_no_offenses(<<-RUBY, 'my_class_spec.rb')
212+
describe Very::Long::Namespace::MyClass do; end
210213
RUBY
211214
end
212215

@@ -259,5 +262,12 @@ class Foo
259262
^^^^^^^^^^^^^^^^ Spec path should end with `*_spec.rb`.
260263
RUBY
261264
end
265+
266+
it 'registers an offense when the file extension is not .rb' do
267+
expect_offense(<<-RUBY, 'whatever_specxrb')
268+
describe MyClass do; end
269+
^^^^^^^^^^^^^^^^ Spec path should end with `*_spec.rb`.
270+
RUBY
271+
end
262272
end
263273
end

0 commit comments

Comments
 (0)