# frozen_string_literal: true

require_relative '../../spec_helper'
require_lib 'reek/smell_detectors/uncommunicative_parameter_name'

RSpec.describe Reek::SmellDetectors::UncommunicativeParameterName do
  it 'reports the right values' do
    src = <<-RUBY
      def alfa(x)
        x
      end
    RUBY

    expect(src).to reek_of(:UncommunicativeParameterName,
                           lines:   [1],
                           context: 'alfa',
                           message: "has the parameter name 'x'",
                           source:  'string',
                           name:    'x')
  end

  it 'does count all occurrences' do
    src = <<-RUBY
      def alfa(x, y)
        [x, y]
      end
    RUBY

    expect(src).
      to reek_of(:UncommunicativeParameterName, lines: [1], name: 'x').
      and reek_of(:UncommunicativeParameterName, lines: [1], name: 'y')
  end

  { 'alfa.' => 'with a receiver',
    ''      => 'without a receiver' }.each do |host, description|
    describe "in a method definition #{description}" do
      it 'does not report two-letter parameter names' do
        src = "def #{host}bravo(ab); charlie(ab); end"
        expect(src).not_to reek_of(:UncommunicativeParameterName)
      end

      it 'reports names of the form "x2"' do
        src = "def #{host}bravo(x2) charlie(x2) end"
        expect(src).to reek_of(:UncommunicativeParameterName, name: 'x2')
      end

      it 'reports long name ending in a number' do
        src = "def #{host}bravo(param2) charlie(param2) end"
        expect(src).to reek_of(:UncommunicativeParameterName, name: 'param2')
      end

      it 'reports unused parameters' do
        src = "def #{host}bravo(x); charlie; end"
        expect(src).to reek_of(:UncommunicativeParameterName)
      end

      it 'reports splat parameters' do
        expect("def #{host}bravo(*a); charlie(a); end").
          to reek_of(:UncommunicativeParameterName, name: 'a')
      end

      it 'reports double splat parameters' do
        expect("def #{host}bravo(**a); charlie(a); end").
          to reek_of(:UncommunicativeParameterName, name: 'a')
      end

      it 'reports block parameters' do
        expect("def #{host}bravo(&a); charlie(a); end").
          to reek_of(:UncommunicativeParameterName, name: 'a')
      end

      it 'does not report unused anonymous parameter' do
        src = "def #{host}bravo(_); charlie; end"
        expect(src).not_to reek_of(:UncommunicativeParameterName)
      end

      it 'reports used anonymous parameter' do
        src = "def #{host}bravo(_); charlie(_) end"
        expect(src).to reek_of(:UncommunicativeParameterName, name: '_')
      end

      it 'reports used parameters marked as unused' do
        src = "def #{host}bravo(_unused) charlie(_unused) end"
        expect(src).to reek_of(:UncommunicativeParameterName, name: '_unused')
      end

      it 'does not report anonymous splat' do
        expect("def #{host}bravo(*); end").not_to reek_of(:UncommunicativeParameterName)
      end

      it 'does not report anonymous double splat' do
        expect("def #{host}bravo(**); end").not_to reek_of(:UncommunicativeParameterName)
      end

      it 'reports names inside array decomposition' do
        src = "def #{host}bravo((x, charlie)) delta(x, charlie) end"
        expect(src).to reek_of(:UncommunicativeParameterName,
                               name: 'x')
      end

      it 'reports names inside nested array decomposition' do
        src = "def #{host}bravo((charlie, (delta, x))) echo(charlie, x) end"
        expect(src).to reek_of(:UncommunicativeParameterName,
                               name: 'x')
      end
    end
  end

  describe '`accept` patterns' do
    let(:source) { 'def alfa(bar2); charlie(bar2); end' }

    it 'make smelly names pass' do
      patterns = [/bar2/, /ar2/]
      patterns.each do |pattern|
        expect(source).not_to reek_of(:UncommunicativeParameterName).with_config('accept' => pattern)
      end
    end
  end

  describe '`reject` patterns' do
    let(:source) { 'def alfa(bravo); charlie(bravo); end' }

    it 'reject smelly names' do
      patterns = [/brav/, /ravo/]
      patterns.each do |pattern|
        expect(source).to reek_of(:UncommunicativeParameterName).with_config('reject' => pattern)
      end
    end
  end

  describe '.default_config' do
    it 'merges in the default accept and reject patterns' do
      expected = {
        'enabled' => true,
        'exclude' => [],
        'reject'  => [/^.$/, /[0-9]$/, /[A-Z]/, /^_/],
        'accept'  => []
      }

      expect(described_class.default_config).to eq(expected)
    end
  end

  describe '.contexts' do
    it 'indicates that this smell is scoped to method definitions' do
      expect(described_class.contexts).to eq([:def, :defs])
    end
  end
end
