/* eslint-env jest */
import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import { css } from 'glamor';

import ToastContainer from './../ToastContainer';
import toaster from './../toaster';

import { ACTION } from './../constant';
import EventManager from './../util/EventManager';

jest.useFakeTimers();

function hasProp(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

// extract props from component instance
function getToastProps(component) {
  const collection = component.instance().collection;
  const toast = collection[Object.keys(collection)[0]];

  return toast.options;
}

describe('ToastContainer', () => {
  it('Should bind event when mounted and unbind them when unmounted', () => {
    const component = mount(<ToastContainer />);

    expect(EventManager.eventList.has(ACTION.SHOW)).toBeTruthy();
    expect(EventManager.eventList.has(ACTION.CLEAR)).toBeTruthy();

    component.unmount();
    expect(EventManager.eventList.has(ACTION.SHOW)).toBeFalsy();
    expect(EventManager.eventList.has(ACTION.CLEAR)).toBeFalsy();
  });

  it(`Should always pass down to Toast the props: 
    -autoClose
    -closeButton
    -position
    -pauseOnHover
    -transition
    -closeToast`, () => {
    const component = mount(<ToastContainer />);
    // Create a toast
    toaster('coucou');
    jest.runAllTimers();

    const props = getToastProps(component);

    [
      'autoClose',
      'closeButton',
      'position',
      'closeToast',
      'transition',
      'pauseOnHover'
    ].forEach(key => expect(hasProp(props, key)).toBeTruthy());
  });

  it('Should clear all toast when clear is called without id', () => {
    const component = mount(<ToastContainer />);

    toaster('coucou');
    toaster('coucou');
    jest.runAllTimers();

    expect(component.state().toast).toHaveLength(2);

    toaster.dismiss();
    jest.runAllTimers();

    expect(component.state().toast).toHaveLength(0);
  });

  it('Should be able to render a react element, a string, a number, a render props without crashing', () => {
    const component = mount(<ToastContainer />);
    toaster('coucou');
    toaster(123);
    toaster(<div>plop</div>);
    toaster(() => <div>plop</div>);
    jest.runAllTimers();

    expect(component.state().toast).toHaveLength(4);
  });

  it('Should be able to display new toast on top', () => {
    /*eslint no-extend-native: 0 */
    Array.prototype.reverse = jest.fn(Array.prototype.reverse);
    mount(<ToastContainer newestOnTop />);
    toaster('hello');
    toaster(123);
    jest.runAllTimers();

    expect(Array.prototype.reverse).toHaveBeenCalled();
  });

  it('Toast options should supersede ToastContainer props', () => {
    const component = mount(<ToastContainer />);
    const CloseBtn = () => <div>Close</div>;
    const fn = () => {};
    const desiredProps = {
      pauseOnHover: false,
      closeOnClick: false,
      onOpen: fn,
      onClose: fn,
      autoClose: false,
      hideProgressBar: true,
      position: 'top-left',
      closeButton: <CloseBtn />
    };

    toaster('hello', desiredProps);
    jest.runAllTimers();
    const props = getToastProps(component);

    expect(props).toMatchObject(desiredProps);
  });

  it('Should pass closeToast function and type when using a custom CloseButton', () => {
    const component = mount(<ToastContainer />);
    const CloseBtn = () => <div>x</div>;
    const Msg = () => <div>Plop</div>;

    toaster(<Msg />, {
      closeButton: CloseBtn
    });
    jest.runAllTimers();

    const props = getToastProps(component);
    expect(Object.keys(props.closeButton.props)).toMatchObject([
      'closeToast',
      'type'
    ]);
  });

  it('Should be able to disable the close button', () => {
    let component = mount(<ToastContainer />);
    toaster('hello');
    jest.runAllTimers();
    // ensure that close button is present by default
    expect(component.html()).toMatch(/✖/);
    component.unmount();

    component = mount(<ToastContainer closeButton={false} />);
    toaster('hello');
    jest.runAllTimers();

    expect(component.html()).not.toMatch(/toastify__close/);
  });

  it('Should merge className and style', () => {
    const component = mount(
      <ToastContainer className="foo" style={{ background: 'red' }} />
    );
    toaster('hello');
    jest.runAllTimers();

    expect(component.html()).toMatch(/class="foo"/);
    expect(component.html()).toMatch(/style="background: red;"/);
  });

  it('Should allow glamor rule as className', () => {
    const component = mount(
      <ToastContainer className={css({ background: 'red' })} />
    );
    toaster('hello');
    jest.runAllTimers();

    expect(toJson(component)).toMatchSnapshot();
  });

  it('Should pass a closeToast function when displaying a react component', () => {
    const component = mount(<ToastContainer />);
    const Msg = () => <div>Plop</div>;

    toaster(<Msg />);
    jest.runAllTimers();

    const props = getToastProps(component);

    expect(props).toHaveProperty('closeToast');
  });

  it('Should update state when document visibility change', () => {
    let trigger;
    let event;
    document.addEventListener = (evt, cb) => {
      trigger = cb;
      event = evt;
    };

    const component = mount(<ToastContainer />);
    expect(event).toBe('visibilitychange');
    expect(component.state().isDocumentHidden).toBe(false);
    trigger();
    expect(component.state().isDocumentHidden).toBe(true);
  });

  describe('closeToast function', () => {
    it('Should remove toast when closeToast is called', () => {
      const component = mount(<ToastContainer />);
      const Msg = () => <div>Plop</div>;

      toaster(<Msg />);
      jest.runAllTimers();

      let props = getToastProps(component);

      //ensure that the toast is present
      expect(component.state().toast).toHaveLength(1);

      // close the toast with the function passed to the close button
      props.closeToast();
      jest.runAllTimers();

      expect(component.state().toast).toHaveLength(0);

      //do the same but with the closeButton this time
      toaster(<Msg />);
      jest.runAllTimers();

      props = getToastProps(component);

      //ensure that the toast is present
      expect(component.state().toast).toHaveLength(1);

      // close the toast with the function passed to the close button
      props.closeButton.props.closeToast();
      jest.runAllTimers();

      expect(component.state().toast).toHaveLength(0);
    });
  });

  it('Should include only the style needed for a given position', () => {
    Object.keys(toaster.POSITION).forEach(k => {
      const component = mount(
        <ToastContainer position={toaster.POSITION[k]} />
      );
      const id = toaster('test');
      jest.runAllTimers();
      expect(component.instance().collection[id].position).toBe(
        toaster.POSITION[k]
      );
    });
  });

  // This test should be executed after all the others because the execption mess all the rest
  it("Should throw an error if can't render a toast", () => {
    expect(() => {
      mount(<ToastContainer />);
      toaster(false);
      jest.runAllTimers();
      jest.clearAllTimers();
    }).toThrow(/The element you provided cannot be rendered/);
  });
});

@@ -3664,16 +3664,35 @@
 bject(%5B%0A
+      'ariaLabel',%0A
       'c

Fix test