import { shallow } from 'enzyme';
import React from 'react';
import { Checkbox } from 'semantic-ui-react';

import Context from './Context';
import { RegisterFormCheckbox } from './RegisterFormCheckbox';

describe('RegisterFormCheckbox component', () => {
  const CONTEXT_FIXTURE = {
    checkboxes: {
      testName: true,
    },
    handleCheckboxChange: jest.fn(),
  };

  let wrapper;

  beforeEach(() => {
    wrapper = shallow(<RegisterFormCheckbox name="testName" />);
  });

  /**
   * Re-renders the component using context
   * Attaches `CONTEXT_FIXTURE` to the component and then re-renders it.
   * Re-rendering would normally clear context, so we need to call render()
   * directly and re-mount the results.
   * This is a workaround for Enzyme's lack of `contextType` API support.
   * @returns {void}
   */
  const renderWithContext = () => {
    wrapper.instance().context = CONTEXT_FIXTURE;
    const render = wrapper.instance().render();
    wrapper = shallow(<div>{ render }</div>);
  };

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

  it('comes with the Context attached', () => {
    expect(RegisterFormCheckbox.contextType).toBe(Context);
  });

  it('sets the checkbox to unchecked on mount', () => {
    CONTEXT_FIXTURE.handleCheckboxChange.mockClear();
    wrapper.instance().context = CONTEXT_FIXTURE;

    wrapper.instance().componentDidMount();

    expect(CONTEXT_FIXTURE.handleCheckboxChange).toHaveBeenCalled();
    expect(CONTEXT_FIXTURE.handleCheckboxChange)
      .toHaveBeenCalledWith(null, { checked: false, name: 'testName' });
  });

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

  it('passes props down to the Checkbox', () => {
    wrapper.setProps({ label: 'test label' });
    const checkbox = wrapper.find('Checkbox');
    expect(checkbox).toHaveProp('label', 'test label');
    expect(checkbox).toHaveProp('name', 'testName');
  });

  it('copies propTypes from the Checkbox', () => {
    // Check a representative prop
    expect(RegisterFormCheckbox.propTypes.label).toBe(Checkbox.propTypes.label);
  });

  it('does not define a checked prop', () => {
    expect(RegisterFormCheckbox.propTypes).not.toHaveProperty('checked');
  });

  it('marks the name prop as required', () => {
    expect(RegisterFormCheckbox.propTypes.name)
      .toBe(Checkbox.propTypes.name.isRequired);
  });

  it('sets the checked status from the Context', () => {
    renderWithContext();
    expect(wrapper.find('Checkbox')).toHaveProp('checked', true);
  });

  it('ignores attempts to change the checked status', () => {
    wrapper.setProps({ checked: 'other' });
    renderWithContext();
    expect(wrapper.find('Checkbox')).toHaveProp('checked', true);
  });

  it('propagates changes to the Context', () => {
    CONTEXT_FIXTURE.handleCheckboxChange.mockClear();
    wrapper.instance().context = CONTEXT_FIXTURE;

    wrapper.find('Checkbox').simulate('change', 'event', 'data');

    expect(CONTEXT_FIXTURE.handleCheckboxChange).toHaveBeenCalled();
    expect(CONTEXT_FIXTURE.handleCheckboxChange)
      .toHaveBeenCalledWith('event', 'data');
  });

  it('can propagate changes to an onChange handler', () => {
    const onChange = jest.fn();
    wrapper.setProps({ onChange });
    wrapper.instance().context = CONTEXT_FIXTURE; // attach after re-render

    wrapper.find('Checkbox').simulate('change', 'event', 'data');

    expect(onChange).toHaveBeenCalled();
    expect(onChange).toHaveBeenCalledWith('event', 'data');
  });
});
