Studiosity/grover

View on GitHub
spec/grover/utils_spec.rb

Summary

Maintainability
A
0 mins
Test Coverage
C
75%
# frozen_string_literal: true

require 'spec_helper'

describe Grover::Utils do
  describe '.squish' do
    subject(:squish) { described_class.squish string }

    context 'with an empty string' do
      let(:string) { '' }

      it { is_expected.to eq '' }
    end

    context 'with leading spaces' do
      let(:string) { '   Foo' }

      it { is_expected.to eq 'Foo' }
    end

    context 'with trailing spaces' do
      let(:string) { 'Bar   ' }

      it { is_expected.to eq 'Bar' }
    end

    context 'with spaces in the middle' do
      let(:string) { 'Foo Bar    Baz' }

      it { is_expected.to eq 'Foo Bar Baz' }
    end

    context 'with newlines' do
      let(:string) { "\nFoo\nBar Baz\nBoop\n" }

      it { is_expected.to eq 'Foo Bar Baz Boop' }
    end

    context 'with tabs' do
      let(:string) { "Foo\tBar" }

      it { is_expected.to eq 'Foo Bar' }
    end
  end

  describe '.deep_assign' do
    subject(:deep_assign) { described_class.deep_assign(hash, keys, value) }

    let(:value) { 'baz' }

    context 'when hash is empty' do
      let(:hash) { {} }
      let(:keys) { ['foo'] }

      it do
        deep_assign
        expect(hash).to eq('foo' => 'baz')
      end
    end

    context 'when hash already contains matching key' do
      let(:hash) { { 'foo' => 'bar' } }
      let(:keys) { ['foo'] }

      it do
        deep_assign
        expect(hash).to eq('foo' => 'baz')
      end
    end

    context 'with multiple keys provided' do
      let(:hash) { {} }
      let(:keys) { %w[foo bar] }

      it do
        deep_assign
        expect(hash).to eq('foo' => { 'bar' => 'baz' })
      end
    end

    context 'with symbol keys' do
      let(:hash) { {} }
      let(:keys) { %i[foo bar] }

      it do
        deep_assign
        expect(hash).to eq(foo: { bar: 'baz' })
      end
    end
  end

  describe '.deep_transform_keys_in_object' do
    subject(:deep_transform_keys_in_object) do
      described_class.deep_transform_keys_in_object(hash) { |key| key.to_s.upcase }
    end

    context 'when hash is empty' do
      let(:hash) { {} }

      it { is_expected.to eq({}) }
    end

    context 'when hash has basic keys' do
      let(:hash) { { foo: 'bar' } }

      it { is_expected.to eq('FOO' => 'bar') }

      it 'doesnt modify the original hash' do
        deep_transform_keys_in_object
        expect(hash).to eq(foo: 'bar')
      end
    end

    context 'when hash contains an array of hashes' do
      let(:hash) { { foo: [{ bar: 'baz' }] } }

      it { is_expected.to eq('FOO' => [{ 'BAR' => 'baz' }]) }

      it 'doesnt modify the original hash' do
        deep_transform_keys_in_object
        expect(hash).to eq(foo: [{ bar: 'baz' }])
      end
    end

    context 'when key is included in `excluding` list' do
      subject(:deep_transform_keys_in_object) do
        described_class.deep_transform_keys_in_object(hash, excluding: ['FOO']) { |key| key.to_s.upcase }
      end

      let(:hash) { { foo: [{ bar: 'baz' }] } }

      it { is_expected.to eq('FOO' => [{ bar: 'baz' }]) }

      it 'doesnt modify the original hash' do
        deep_transform_keys_in_object
        expect(hash).to eq(foo: [{ bar: 'baz' }])
      end
    end
  end

  describe '.deep_stringify_keys' do
    subject(:deep_stringify_keys) { described_class.deep_stringify_keys(hash) }

    context 'when hash is empty' do
      let(:hash) { {} }

      it { is_expected.to eq({}) }
    end

    context 'when hash has keys' do
      let(:hash) { { foo: 'bar' } }

      it { is_expected.to eq('foo' => 'bar') }

      it 'doesnt modify the original hash' do
        deep_stringify_keys
        expect(hash).to eq(foo: 'bar')
      end
    end
  end

  describe '.deep_merge!' do
    subject(:deep_merge!) { described_class.deep_merge! first_hash, second_hash }

    context 'when both hashes are empty' do
      let(:first_hash) { {} }
      let(:second_hash) { {} }

      it { is_expected.to eq({}) }

      it 'leaves the first hash empty' do
        deep_merge!
        expect(first_hash).to eq({})
      end

      it 'leaves the second hash empty' do
        deep_merge!
        expect(second_hash).to eq({})
      end
    end

    context 'when hash1 has some keys' do
      let(:first_hash) { { foo: 'bar' } }
      let(:second_hash) { {} }

      it { is_expected.to eq(foo: 'bar') }

      it 'leaves the first hash as it was' do
        deep_merge!
        expect(first_hash).to eq(foo: 'bar')
      end

      it 'leaves the second hash empty' do
        deep_merge!
        expect(second_hash).to eq({})
      end
    end

    context 'when hash2 has some keys' do
      let(:first_hash) { {} }
      let(:second_hash) { { foo: 'bar' } }

      it { is_expected.to eq(foo: 'bar') }

      it 'updates hash1 to include the contents from hash2' do
        deep_merge!
        expect(first_hash).to eq(foo: 'bar')
      end

      it 'leaves the second hash as it was' do
        deep_merge!
        expect(second_hash).to eq(foo: 'bar')
      end
    end

    context 'when both hashes have keys (different)' do
      let(:first_hash) { { bar: 'baz' } }
      let(:second_hash) { { foo: 'bar' } }

      it { is_expected.to eq(foo: 'bar', bar: 'baz') }

      it 'merges the contents of hash1 and hash2' do
        deep_merge!
        expect(first_hash).to eq(foo: 'bar', bar: 'baz')
      end

      it 'leaves the second hash as it was' do
        deep_merge!
        expect(second_hash).to eq(foo: 'bar')
      end
    end

    context 'when both hashes have keys (same)' do
      let(:first_hash) { { foo: 'baz', baz: 'foo' } }
      let(:second_hash) { { foo: 'bar' } }

      it { is_expected.to eq(foo: 'bar', baz: 'foo') }

      it 'overloads existing key values in hash1' do
        deep_merge!
        expect(first_hash).to eq(foo: 'bar', baz: 'foo')
      end

      it 'leaves the second hash as it was' do
        deep_merge!
        expect(second_hash).to eq(foo: 'bar')
      end
    end

    context 'when both hashes have keys (same/deep)' do
      let(:first_hash) { { foo: { bar: 'baz' }, fizz: 'buzz' } }
      let(:second_hash) { { foo: { baz: 'bar', bar: 'foo' } } }

      it { is_expected.to eq(foo: { bar: 'foo', baz: 'bar' }, fizz: 'buzz') }

      it 'overloads existing deep key values in hash1' do
        deep_merge!
        expect(first_hash).to eq(foo: { bar: 'foo', baz: 'bar' }, fizz: 'buzz')
      end

      it 'leaves the second hash as it was' do
        deep_merge!
        expect(second_hash).to eq(foo: { baz: 'bar', bar: 'foo' })
      end
    end
  end

  describe '.normalize_object' do
    subject(:normalize_object) { described_class.normalize_object(object) }

    context 'when key is a single-word symbol' do
      let(:object) { { foo: 'bar' } }

      it { is_expected.to eq('foo' => 'bar') }
    end

    context 'when key is a multi-word symbol' do
      let(:object) { { foo_bar: 'baz' } }

      it { is_expected.to eq('fooBar' => 'baz') }
    end

    context 'when key is a single-word string' do
      let(:object) { { 'foo' => 'bar' } }

      it { is_expected.to eq('foo' => 'bar') }
    end

    context 'when key is a multi-word string' do
      let(:object) { { 'foo_bar' => 'baz' } }

      it { is_expected.to eq('fooBar' => 'baz') }
    end

    context 'when key is up-case' do
      let(:object) { { 'FOO_BAR' => 'baz' } }

      it { is_expected.to eq('fooBar' => 'baz') }
    end

    context 'when key has an acronym in it' do
      let(:object) { { prefer_css_page_size: true, bypass_csp: false, extra_http_headers: { 'Foo' => 'Bar' } } }

      it 'returns the acronym components of the keys uppercase' do
        expect(normalize_object).to(
          eq('preferCSSPageSize' => true, 'bypassCSP' => false, 'extraHTTPHeaders' => { 'foo' => 'Bar' })
        )
      end

      context 'when excluding the transform of a specific keys values' do
        subject(:normalize_object) { described_class.normalize_object(object, excluding: ['extraHTTPHeaders']) }

        it 'returns the acronym components of the keys uppercase (but does not transform the extraHTTPHeaders value' do
          expect(normalize_object).to(
            eq('preferCSSPageSize' => true, 'bypassCSP' => false, 'extraHTTPHeaders' => { 'Foo' => 'Bar' })
          )
        end
      end
    end

    context 'with nested Hashes' do
      let(:object) { { margin: { top: '5px' } } }

      it { is_expected.to eq('margin' => { 'top' => '5px' }) }
    end

    context 'when value is a number' do
      let(:object) { { scale: 1.34 } }

      it { is_expected.to eq('scale' => 1.34) }
    end
  end
end