import { shallow } from 'enzyme';

import { Tooltip } from '..';

import Stepper from './Stepper';

jest.mock('./deviceDetection', () => ({
  isTouchDevice: jest.fn(() => false),
}));

describe('Stepper', () => {
  let props;
  let component;
  let fakeDeviceDetection;

  beforeEach(() => {
    fakeDeviceDetection = require('./deviceDetection');
    props = {
      activeStep: 0,
      steps: ['one', 'two', 'three'].map((label) => ({ label })),
    };
    component = shallow(<Stepper {...props} />);
  });

  const activeStep = (step) => component.setProps({ activeStep: step });
  const steps = (howMany) =>
    component.setProps({
      steps: new Array(...new Array(howMany)).map((_, index) => ({ label: index.toString() })),
    });

  describe('progress bar', () => {
    const totalWidth = () => component.find('.progress-bar').prop('style').width;

    describe('when no steps are passed in', () => {
      it('renders nothing', () => {
        component.setProps({ steps: [] });

        expect(component.isEmptyRender()).toBe(true);
      });
    });

    it('sets the widths of the progress bar to match where you are in the flow', () => {
      expect(totalWidth()).toBe('0px');
      activeStep(2);
      expect(totalWidth()).toBe(
        'calc(100% + var(--progress-bar-start-shift) + var(--progress-bar-border-width))',
      );
      steps(5);
      expect(totalWidth()).toBe(
        'calc(50% + var(--progress-bar-start-shift) + var(--progress-bar-border-width))',
      );
      activeStep(10000);
      expect(totalWidth()).toBe(
        'calc(100% + var(--progress-bar-start-shift) + var(--progress-bar-border-width))',
      );
      activeStep(-10);
      expect(totalWidth()).toBe('0px');
    });
  });

  describe('steps', () => {
    it('have rendered labels', () => {
      steps(5);
      [0, 1, 2, 3, 4].forEach((step) => {
        expect(component.find('.tw-stepper__step').at(step).find('span').props().children).toBe(
          step.toString(),
        );
      });
    });

    it('are active when clickable and completed', () => {
      const stepActive = (index) =>
        component.find('.tw-stepper__step').at(index).hasClass('tw-stepper__step--clickable');

      component.setProps({
        steps: [
          { label: '0' },
          { label: '1', onClick: () => null },
          { label: '2', onClick: () => null },
        ],
        activeStep: 0,
      });

      expect(stepActive(0)).toBe(false);
      activeStep(1);
      expect(stepActive(0)).toBe(false);
      expect(stepActive(1)).toBe(false);
      activeStep(2);
      expect(stepActive(0)).toBe(false);
      expect(stepActive(1)).toBe(true);
      expect(stepActive(2)).toBe(false);
    });

    it('are not clickable when active', () => {
      const clickOnStep = (index) => {
        const buttonReference = component.find('.tw-stepper__step').at(index).find('button');
        if (buttonReference.exists()) {
          buttonReference.simulate('click');
        }
      };
      const buttonDisabled = (index) =>
        !component.find('.tw-stepper__step').at(index).find('button').exists();
      const clickedOnFirstStep = jest.fn();
      const clickedOnSecondStep = jest.fn();
      component.setProps({
        steps: [
          { label: '0', onClick: clickedOnFirstStep },
          { label: '1', onClick: clickedOnSecondStep },
        ],
        activeStep: 0,
      });
      clickOnStep(0);
      expect(buttonDisabled(0)).toBe(true);
      expect(clickedOnFirstStep).not.toHaveBeenCalled();
      activeStep(1);
      expect(buttonDisabled(0)).toBe(false);
      clickOnStep(0);
      expect(clickedOnFirstStep).toHaveBeenCalledTimes(1);
      clickOnStep(1);
      expect(clickedOnSecondStep).not.toHaveBeenCalled();
    });

    it('are active when they are the currently active step', () => {
      const stepActive = (index) =>
        component.find('.tw-stepper__step').at(index).hasClass('tw-stepper__step--active');
      steps(4);
      activeStep(1);
      expect(stepActive(0)).toBe(false);
      expect(stepActive(1)).toBe(true);
      expect(stepActive(2)).toBe(false);
      expect(stepActive(3)).toBe(false);
      activeStep(2);
      expect(stepActive(0)).toBe(false);
      expect(stepActive(1)).toBe(false);
      expect(stepActive(2)).toBe(true);
      expect(stepActive(3)).toBe(false);
    });

    it('are marked as visited when active index is less than or equals to current index', () => {
      const step = (index) => {
        const buttonStates = ['tw-stepper__step--active', 'tw-stepper__step--clickable'];
        const stepElement = component.find('.tw-stepper__step').at(index);
        return {
          active: stepElement.hasClass(buttonStates[0]),
          clickable: stepElement.hasClass(buttonStates[1]),
          clickableAndActive: buttonStates.every((c) => stepElement.hasClass(c)),
          disabled: !stepElement.find('button').exists(),
        };
      };

      component.setProps({
        steps: [
          { label: '0', onClick: () => {} },
          { label: '1', onClick: () => {} },
          { label: '2', onClick: () => {} },
        ],
        activeStep: 0,
      });

      expect(step(0).active).toBe(true);
      expect(step(0).disabled).toBe(true);
      expect(step(1).clickableAndActive).toBe(false);
      expect(step(2).clickableAndActive).toBe(false);

      activeStep(2);
      activeStep(0);

      expect(step(1).clickable).toBe(true);
      expect(step(2).clickable).toBe(true);
    });

    it('are aria-current=step when active', () => {
      const stepCurrent = (index) =>
        component.find('.tw-stepper__step').at(index).props()['aria-current'];
      steps(4);
      activeStep(1);
      expect(stepCurrent(0)).toBe(false);
      expect(stepCurrent(1)).toBe('step');
      expect(stepCurrent(2)).toBe(false);
      expect(stepCurrent(3)).toBe(false);
      activeStep(2);
      expect(stepCurrent(0)).toBe(false);
      expect(stepCurrent(1)).toBe(false);
      expect(stepCurrent(2)).toBe('step');
      expect(stepCurrent(3)).toBe(false);
    });
  });

  describe('hover labels', () => {
    const step = (index) => component.find('.tw-stepper__step').at(index);
    it('will be rendered when provided', () => {
      component.setProps({
        steps: [{ hoverLabel: 'hover', label: 'label' }, { label: 'label 2' }],
      });
      const firstStepHoverLabel = step(0).children();
      expect(firstStepHoverLabel.type()).toBe(Tooltip);
      expect(firstStepHoverLabel.children().render().text()).toBe('label');
      expect(step(1).find('span').props().children).toBe('label 2');
    });

    it('renders jsx', () => {
      component.setProps({
        steps: [
          {
            hoverLabel: (
              <>
                hover <p>label</p>
              </>
            ),
            label: '1',
          },
        ],
      });

      expect(step(0).children().prop('label')).toStrictEqual(
        <>
          hover <p>label</p>
        </>,
      );
    });

    it('will not be rendered if the user is on a touch device', () => {
      jest.spyOn(fakeDeviceDetection, 'isTouchDevice').mockImplementation(() => true);
      component.setProps({
        steps: [{ hoverLabel: 'hover', label: 'label' }, { label: 'label 2' }],
      });
      const firstStepHoverLabel = step(0).children();
      expect(firstStepHoverLabel.type()).toBe('span');
      expect(firstStepHoverLabel.props().children).toBe('label');
      expect(step(1).children().props().children).toBe('label 2');
    });
  });
});
