class RuboCop::Cop::RSpec::FilePath

Checks that spec file paths are consistent with the test subject.

Checks the path of the spec file and enforces that it reflects the described class/module and its optionally called out method.

With the configuration option `IgnoreMethods` the called out method will be ignored when determining the enforced path.

With the configuration option `CustomTransform` modules or classes can be specified that should not as usual be transformed from CamelCase to snake_case (e.g. 'RuboCop' => 'rubocop' ).

@example

# bad
whatever_spec.rb         # describe MyClass

# bad
my_class_spec.rb         # describe MyClass, '#method'

# good
my_class_spec.rb         # describe MyClass

# good
my_class_method_spec.rb  # describe MyClass, '#method'

# good
my_class/method_spec.rb  # describe MyClass, '#method'

@example when configuration is `IgnoreMethods: true`

# bad
whatever_spec.rb         # describe MyClass

# good
my_class_spec.rb         # describe MyClass

# good
my_class_spec.rb         # describe MyClass, '#method'

Constants

MSG

Public Instance Methods

on_top_level_describe(node, args) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 52
def on_top_level_describe(node, args)
  return unless const_described?(node) && single_top_level_describe?
  return if routing_spec?(args)

  glob = glob_for(args)

  return if filename_ends_with?(glob)

  add_offense(
    node,
    location: :expression,
    message: format(MSG, suffix: glob)
  )
end

Private Instance Methods

camel_to_snake_case(string) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 91
def camel_to_snake_case(string)
  string
    .gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
    .gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2')
    .downcase
end
custom_transform() click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 98
def custom_transform
  cop_config.fetch('CustomTransform', {})
end
expected_path(constant) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 83
def expected_path(constant)
  File.join(
    constant.const_name.split('::').map do |name|
      custom_transform.fetch(name) { camel_to_snake_case(name) }
    end
  )
end
filename_ends_with?(glob) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 106
def filename_ends_with?(glob)
  File.fnmatch?("*#{glob}", processed_source.buffer.name)
end
glob_for((described_class, method_name)) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 73
def glob_for((described_class, method_name))
  "#{expected_path(described_class)}#{name_glob(method_name)}*_spec.rb"
end
ignore_methods?() click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 102
def ignore_methods?
  cop_config['IgnoreMethods']
end
name_glob(name) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 77
def name_glob(name)
  return unless name&.str_type?

  "*#{name.str_content.gsub(/\W/, '')}" unless ignore_methods?
end
relevant_rubocop_rspec_file?(_file) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 110
def relevant_rubocop_rspec_file?(_file)
  true
end
routing_spec?(args) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 69
def routing_spec?(args)
  args.any?(&method(:routing_metadata?))
end