import { shallow } from 'enzyme';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Placeholder } from 'semantic-ui-react';

import createItemComponent from './createItemComponent';
import DefaultItem from './Item';

describe('createItemComponent HOC', () => {
  class WrappedComponent extends Component {
    render = () => <div>Test component</div>;
  }

  WrappedComponent.propTypes = {
    other: PropTypes.string,
  };

  let EnhancedComponent;

  beforeEach(() => {
    EnhancedComponent = createItemComponent()(WrappedComponent);
  });

  describe('can be given an item directly', () => {
    const ITEM_FIXTURE = { };

    let wrapper;

    beforeEach(() => {
      wrapper = shallow(<EnhancedComponent item={ ITEM_FIXTURE } />);
    });

    it('renders', () => {
      expect(wrapper).toExist();
    });

    it('renders the wrapped component', () => {
      expect(wrapper.find(WrappedComponent)).toExist();
    });

    it('passes the item to the wrapped component', () => {
      expect(wrapper.find(WrappedComponent)).toHaveProp('item', ITEM_FIXTURE);
    });

    it('passes unknown props to the wrapped component', () => {
      wrapper.setProps({ unknown: 'value' });
      expect(wrapper.find(WrappedComponent)).toHaveProp('unknown', 'value');
    });

    it('avoids passing request props to the wrapped component', () => {
      wrapper.setProps({
        id: 42,
        itemSlug: 'item-slug',
        placeholder: <Placeholder />,
        typeSlug: 'type-slug',
      });

      const wrapped = wrapper.find(WrappedComponent);
      expect(wrapped).not.toHaveProp('id');
      expect(wrapped).not.toHaveProp('itemSlug');
      expect(wrapped).not.toHaveProp('placeholder');
      expect(wrapped).not.toHaveProp('typeSlug');
    });

    it('can forward request props to the wrapped component if desired', () => {
      EnhancedComponent = createItemComponent(['id'])(WrappedComponent);
      wrapper = shallow(
        <EnhancedComponent
          id={ 42 }
          item={ ITEM_FIXTURE }
          itemSlug="item-slug"
          placeholder={ <Placeholder /> }
          typeSlug="type-slug"
        />
      );

      const wrapped = wrapper.find(WrappedComponent);
      expect(wrapped).toHaveProp('id', 42);
      expect(wrapped).not.toHaveProp('itemSlug');
      expect(wrapped).not.toHaveProp('placeholder');
      expect(wrapped).not.toHaveProp('typeSlug');
    });
  });

  describe('can be given props to load an item', () => {
    let wrapper;

    beforeEach(() => {
      wrapper = shallow(
        <EnhancedComponent
          id={ 42 }
          itemSlug="item-slug"
          placeholder={ <Placeholder /> }
          typeSlug="type-slug"
        />
      );
    });

    const getWrappedComponent = (item = { }) => {
      const render = wrapper.find(DefaultItem).prop('children');
      return shallow(<div>{ render(item) }</div>).find(WrappedComponent);
    };

    it('renders', () => {
      expect(wrapper).toExist();
    });

    it('renders an Item', () => {
      expect(wrapper.find(DefaultItem)).toExist();
    });

    it('passes request props to the Item', () => {
      const item = wrapper.find(DefaultItem);
      expect(item).toHaveProp('id', 42);
      expect(item).toHaveProp('itemSlug', 'item-slug');
      expect(item).toHaveProp('placeholder', <Placeholder />);
      expect(item).toHaveProp('typeSlug', 'type-slug');
    });

    it('renders the wrapped component', () => {
      expect(getWrappedComponent()).toExist();
    });

    it('passes the item to the wrapped component', () => {
      const item = { };
      expect(getWrappedComponent(item)).toHaveProp('item', item);
    });

    it('passes unknown props to the wrapped component', () => {
      wrapper.setProps({ unknown: 'value' });
      expect(getWrappedComponent()).toHaveProp('unknown', 'value');
    });

    it('avoids passing request props to the wrapped component', () => {
      const wrapped = getWrappedComponent();
      expect(wrapped).not.toHaveProp('id');
      expect(wrapped).not.toHaveProp('itemSlug');
      expect(wrapped).not.toHaveProp('placeholder');
      expect(wrapped).not.toHaveProp('typeSlug');
    });

    it('can forward request props to the wrapped component if desired', () => {
      EnhancedComponent = createItemComponent(['id'])(WrappedComponent);
      wrapper = shallow(
        <EnhancedComponent
          id={ 42 }
          itemSlug="item-slug"
          placeholder={ <Placeholder /> }
          typeSlug="type-slug"
        />
      );

      const wrapped = getWrappedComponent();
      expect(wrapped).toHaveProp('id', 42);
      expect(wrapped).not.toHaveProp('itemSlug');
      expect(wrapped).not.toHaveProp('placeholder');
      expect(wrapped).not.toHaveProp('typeSlug');
    });
  });

  it('maintains the component display name', () => {
    expect(EnhancedComponent.displayName).toBe('WrappedComponent');
  });

  it('maintains the component propTypes', () => {
    expect(EnhancedComponent.propTypes.other)
      .toBe(WrappedComponent.propTypes.other);
  });
});
