import { shallow } from 'enzyme';
import { propEq } from 'ramda';
import React from 'react';
import { Placeholder } from 'semantic-ui-react';

import { ItemSelect, mapStateToProps } from './ItemSelect';
import { Site } from '../../../tests/storyshare';

describe('mapStateToProps', () => {
  const site = {
    getCollectionByRoute: jest.fn().mockReturnValue('test items'),
  };

  beforeEach(() => {
    site.getCollectionByRoute.mockClear();
  });

  it('retrieves items from the DataLayer cache', () => {
    mapStateToProps(null, { site, typeSlug: 'test-slug' });
    expect(site.getCollectionByRoute).toHaveBeenCalled();
    expect(site.getCollectionByRoute)
      .toHaveBeenCalledWith('test-slug', { per_page: 100 });
  });

  it('can pass additional parameters to the DataLayer', () => {
    const requestParams = { extra: 'param' };
    mapStateToProps(null, { requestParams, site, typeSlug: 'test-slug' });
    expect(site.getCollectionByRoute)
      .toHaveBeenCalledWith('test-slug', { extra: 'param', per_page: 100 });
  });

  it('passes retrieved items to the component', () => {
    expect(mapStateToProps(null, { site }).items).toBe('test items');
  });
});

describe('ItemSelect component', () => {
  const POSTS_FIXTURE = [
    { id: 2827, title: { rendered: 'Corporate' } },
    { id: 2828, title: { rendered: 'Employment' } },
    { id: 2829, title: { rendered: 'Finance' } },
  ];

  const TERMS_FIXTURE = [
    { id: 2827, name: 'Corporate' },
    { id: 2828, name: 'Employment' },
    { id: 2829, name: 'Finance' },
  ];

  const EXPECTED_OPTIONS = [
    { text: 'Corporate', value: 2827 },
    { text: 'Employment', value: 2828 },
    { text: 'Finance', value: 2829 },
  ];

  const onChange = jest.fn();

  let wrapper;

  beforeEach(() => {
    wrapper = shallow(
      <ItemSelect
        items={ POSTS_FIXTURE }
        site={ new Site() }
        typeSlug="test-slug"
        onChange={ onChange }
      />
    );
  });

  const chooseOption = (event = null, data = { value: 2827 }) =>
    wrapper.find('Select').simulate('change', event, data);

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

  it('renders an SUI Select', () => {
    expect(wrapper.name()).toBe('Select');
  });

  it('requests posts or terms when it mounts', () => {
    const request = {
      read: jest.fn(),
    };
    const site = { req: jest.fn().mockReturnValue(request) };
    shallow(
      <ItemSelect
        site={ site }
        typeSlug="a-slug"
      />
    );

    expect(site.req).toHaveBeenCalledWith('a-slug');
    expect(request.read).toHaveBeenCalled();
  });

  it('passes options to the Select', () => {
    expect(wrapper.find('Select').prop('options')).toEqual(EXPECTED_OPTIONS);
  });

  it('can render terms as well as posts', () => {
    wrapper.setProps({ items: TERMS_FIXTURE });
    expect(wrapper.find('Select').prop('options')).toEqual(EXPECTED_OPTIONS);
  });

  it('calls onChange in response to a change', () => {
    onChange.mockClear();
    chooseOption();
    expect(onChange).toHaveBeenCalled();
  });

  it('passes the event through to onChange', () => {
    const event = {};
    onChange.mockClear();

    chooseOption(event);

    expect(onChange.mock.calls[0][0]).toBe(event);
  });

  it('passes the proposed prop through to onChange', () => {
    const data = { };
    onChange.mockClear();

    chooseOption(undefined, data);

    expect(onChange.mock.calls[0][1]).toBe(data);
  });

  it('passes the item to onChange', () => {
    onChange.mockClear();
    chooseOption();
    expect(onChange.mock.calls[0][2]).toBe(POSTS_FIXTURE[0]);
  });

  it('copes if no item can be found', () => {
    const data = { value: 23 };
    onChange.mockClear();

    chooseOption(undefined, data);

    expect(onChange.mock.calls[0][2]).toBeUndefined();
  });

  it('copes if no onChange is provided', () => {
    wrapper.setProps({ onChange: undefined });
    chooseOption();
    // Test succeeds if no errors
  });

  it('can filter items', () => {
    const filtered = [{ text: 'Employment', value: 2828 }];
    wrapper.setProps({ filterPredicate: propEq('id', 2828) });
    expect(wrapper.find('Select').prop('options')).toEqual(filtered);
  });

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

  it('avoids passing react-redux dispatch to the Select', () => {
    wrapper.setProps({ dispatch: () => null });
    expect(wrapper.find('Select')).not.toHaveProp('dispatch');
  });

  it('renders a Loader if items have not yet loaded', () => {
    wrapper.setProps({ items: undefined });
    expect(wrapper.name()).not.toBe('Select');
    const loader = wrapper.find('Loader');
    expect(loader).toExist();
    expect(loader).toHaveProp('active');
    expect(loader).toHaveProp('inline');
  });

  it('renders a Loader if no items are defined', () => {
    wrapper.setProps({ items: [] });
    expect(wrapper.name()).not.toBe('Select');
    const loader = wrapper.find('Loader');
    expect(loader).toExist();
    expect(loader).toHaveProp('active');
    expect(loader).toHaveProp('inline');
  });

  it('allows the placeholder to be customised', () => {
    wrapper.setProps({ items: undefined, loadingPlaceholder: <Placeholder /> });
    expect(wrapper.find('Loader')).not.toExist();
    expect(wrapper.find('Placeholder')).toExist();
  });
});
