Skip to content

Commit dc10758

Browse files
authored
Merge pull request #1099 from ahukkanen/fix/1091
Fix false positive for relative file path runs with long namespaces (fixes #1091)
2 parents f9f14e2 + b5b503c commit dc10758

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)