import { GlButton } from '@gitlab/ui';
import { createWrapper, mount, shallowMount } from '@vue/test-utils';
import VulnerabilityActionButtons, {
  i18n,
} from 'ee/security_dashboard/components/pipeline/vulnerability_action_buttons.vue';
import createStore from 'ee/security_dashboard/store';
import { VULNERABILITY_MODAL_ID } from 'ee/vue_shared/security_reports/components/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { visitUrl } from '~/lib/utils/url_utility';
import { resetStore } from '../../helpers';
import mockDataVulnerabilities from '../../store/modules/vulnerabilities/data/mock_data_vulnerabilities';

jest.mock('~/lib/utils/url_utility', () => ({
  visitUrl: jest.fn(),
}));

describe('Security Dashboard Action Buttons', () => {
  let store;
  let wrapper;

  const wrapperFactory = (mountFn) => ({ ...options }) =>
    extendedWrapper(
      mountFn(VulnerabilityActionButtons, {
        ...options,
        store,
      }),
    );

  const createShallowComponent = wrapperFactory(shallowMount);
  const createFullComponent = wrapperFactory(mount);

  const findAllButtons = () => wrapper.findAllComponents(GlButton);
  const findMoreInfoButton = () => wrapper.findByTestId('more-info');
  const findCreateIssueButton = () => wrapper.findByTestId('create-issue');
  const findDismissVulnerabilityButton = () => wrapper.findByTestId('dismiss-vulnerability');
  const findUndoDismissButton = () => wrapper.findByTestId('undo-dismiss');

  beforeEach(() => {
    store = createStore();
  });

  afterEach(() => {
    resetStore(store);
    wrapper.destroy();
  });

  describe('with a fresh vulnerability', () => {
    beforeEach(() => {
      wrapper = createFullComponent({
        propsData: {
          vulnerability: mockDataVulnerabilities[0],
          canCreateIssue: true,
          canDismissVulnerability: true,
        },
      });

      jest.spyOn(wrapper.vm.$store, 'dispatch').mockReturnValue(Promise.resolve());
    });

    it('should render three buttons in a button group', () => {
      expect(findAllButtons()).toHaveLength(3);
    });

    describe('More Info Button', () => {
      it('should render the More info button', () => {
        expect(findMoreInfoButton().exists()).toBe(true);
      });

      it('should emit an `setModalData` event and open the modal when clicked', async () => {
        await findMoreInfoButton().trigger('click');

        expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('vulnerabilities/setModalData', {
          vulnerability: mockDataVulnerabilities[0],
        });
        expect(createWrapper(wrapper.vm.$root).emitted(BV_SHOW_MODAL)).toEqual([
          [VULNERABILITY_MODAL_ID],
        ]);
      });
    });

    describe('Create Issue Button', () => {
      it('should render the create issue button', () => {
        expect(findCreateIssueButton().exists()).toBe(true);
      });

      it('should render the correct tooltip', () => {
        expect(findCreateIssueButton().attributes('title')).toBe(i18n.createIssue);
      });

      it('should emit an `createIssue` event when clicked', async () => {
        await findCreateIssueButton().trigger('click');

        expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('vulnerabilities/createIssue', {
          vulnerability: mockDataVulnerabilities[0],
          flashError: true,
        });
      });

      describe('with Jira issues for vulnerabilities enabled', () => {
        beforeEach(() => {
          wrapper = createFullComponent({
            propsData: {
              vulnerability: mockDataVulnerabilities[8],
              canCreateIssue: true,
            },
          });
        });

        it('should render the correct tooltip', () => {
          expect(findCreateIssueButton().attributes('title')).toBe(i18n.createJiraIssue);
        });

        it('should open a new window when the create-issue button is clicked', async () => {
          expect(visitUrl).not.toHaveBeenCalled();

          await findCreateIssueButton().trigger('click');

          expect(visitUrl).toHaveBeenCalledWith(
            mockDataVulnerabilities[8].create_jira_issue_url,
            true, // external link flag
          );
        });
      });
    });

    describe('Dismiss Vulnerability Button', () => {
      it('should render the dismiss vulnerability button', () => {
        expect(findDismissVulnerabilityButton().exists()).toBe(true);
      });

      it('should emit an `dismissVulnerability` event when clicked', async () => {
        await findDismissVulnerabilityButton().trigger('click');

        expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith(
          'vulnerabilities/dismissVulnerability',
          {
            vulnerability: mockDataVulnerabilities[0],
            flashError: true,
          },
        );
      });
    });
  });

  describe('with a vulnerability that has an issue', () => {
    beforeEach(() => {
      wrapper = createShallowComponent({
        propsData: {
          vulnerability: mockDataVulnerabilities[3],
        },
      });
    });

    it('should only render one button', () => {
      expect(findAllButtons()).toHaveLength(1);
    });

    it('should not render the create issue button', () => {
      expect(findCreateIssueButton().exists()).toBe(false);
    });
  });

  describe('with a vulnerability that has been dismissed', () => {
    beforeEach(() => {
      wrapper = createShallowComponent({
        propsData: {
          vulnerability: mockDataVulnerabilities[2],
          canDismissVulnerability: true,
          isDismissed: true,
        },
      });
    });

    it('should render two buttons in a button group', () => {
      expect(findAllButtons()).toHaveLength(2);
    });

    it('should not render the dismiss vulnerability button', () => {
      expect(findDismissVulnerabilityButton().exists()).toBe(false);
    });

    it('should render the undo dismiss button', () => {
      expect(findUndoDismissButton().exists()).toBe(true);
    });
  });
});
