class RuboCop::Cop::RSpec::FilePath

Checks that spec file paths are consistent and well-formed.

By default, this checks that spec file paths are consistent with the test subject and 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' ).

With the configuration option `SpecSuffixOnly` test files will only be checked to ensure they end in '_spec.rb'. This option disables checking for consistency in the test subject or test methods.

@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'

@example when configuration is `SpecSuffixOnly: true`

# good
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_example_group(node) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 74
def on_top_level_example_group(node)
  return unless top_level_groups.one?

  example_group(node) do |send_node, example_group, arguments|
    next if routing_spec?(arguments)

    ensure_correct_file_path(send_node, example_group, arguments)
  end
end

Private Instance Methods

camel_to_snake_case(string) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 132
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 139
def custom_transform
  cop_config.fetch('CustomTransform', {})
end
ensure_correct_file_path(send_node, example_group, arguments) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 86
def ensure_correct_file_path(send_node, example_group, arguments)
  pattern = pattern_for(example_group, arguments.first)
  return if filename_ends_with?(pattern)

  # For the suffix shown in the offense message, modify the regular
  # expression pattern to resemble a glob pattern for clearer error
  # messages.
  offense_suffix = pattern.gsub('.*', '*').sub('[^/]', '')
    .sub('\.', '.')
  add_offense(send_node, message: format(MSG, suffix: offense_suffix))
end
expected_path(constant) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 124
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?(pattern) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 147
def filename_ends_with?(pattern)
  filename = File.expand_path(processed_source.buffer.name)
  filename.match?("#{pattern}$")
end
ignore_methods?() click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 143
def ignore_methods?
  cop_config['IgnoreMethods']
end
name_pattern(method_name) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 118
def name_pattern(method_name)
  return unless method_name&.str_type?

  ".*#{method_name.str_content.gsub(/\W/, '')}" unless ignore_methods?
end
pattern_for(example_group, method_name) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 102
def pattern_for(example_group, method_name)
  if spec_suffix_only? || !example_group.const_type?
    return pattern_for_spec_suffix_only?
  end

  [
    expected_path(example_group),
    name_pattern(method_name),
    '[^/]*_spec\.rb'
  ].join
end
pattern_for_spec_suffix_only?() click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 114
def pattern_for_spec_suffix_only?
  '.*_spec\.rb'
end
relevant_rubocop_rspec_file?(_file) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 152
def relevant_rubocop_rspec_file?(_file)
  true
end
routing_spec?(args) click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 98
def routing_spec?(args)
  args.any?(&method(:routing_metadata?))
end
spec_suffix_only?() click to toggle source
# File lib/rubocop/cop/rspec/file_path.rb, line 156
def spec_suffix_only?
  cop_config['SpecSuffixOnly']
end