import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import VulnerabilityCountList from 'ee/security_dashboard/components/vulnerability_count_list.vue';
import VulnerabilityCountListLayout from 'ee/security_dashboard/components/vulnerability_count_list_layout.vue';
import countQuery from 'ee/security_dashboard/graphql/queries/vulnerability_severities_count.query.graphql';
import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
import eventHub from 'ee/security_dashboard/utils/event_hub';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mockVulnerabilitySeveritiesGraphQLResponse } from '../mock_data';

const localVue = createLocalVue();
localVue.use(VueApollo);

describe('Vulnerabilities count list component', () => {
  let wrapper;
  let refetchSpy;

  const findVulnerabilityLayout = () => wrapper.findComponent(VulnerabilityCountListLayout);

  const createWrapper = ({ query = { isLoading: false }, provide, data = {} } = {}) => {
    refetchSpy = jest.fn();

    return shallowMount(VulnerabilityCountList, {
      provide: {
        dashboardType: DASHBOARD_TYPES.PROJECT,
        projectFullPath: 'path-to-project',
        groupFullPath: undefined,
        ...provide,
      },
      data: () => data,
      mocks: {
        $apollo: { queries: { vulnerabilitiesCount: { ...query, refetch: refetchSpy } } },
      },
    });
  };

  const createWrapperWithApollo = ({ query, provide, propsData, stubs }) => {
    wrapper = shallowMount(VulnerabilityCountList, {
      localVue,
      apolloProvider: createMockApollo([[countQuery, query]]),
      provide: { projectFullPath: undefined, groupFullPath: undefined, ...provide },
      propsData,
      stubs,
    });
  };

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

  describe('when loading', () => {
    it('passes down to the loading indicator', () => {
      wrapper = createWrapper({ query: { loading: true } });
      expect(findVulnerabilityLayout().props('isLoading')).toBe(true);
    });
  });

  describe('when counts are loaded', () => {
    beforeEach(() => {
      wrapper = createWrapper({ query: { loading: false } });
      wrapper.setData({
        vulnerabilitiesCount: {
          critical: 5,
          high: 3,
          low: 19,
          info: 4,
          medium: 2,
          unknown: 4,
        },
      });
    });

    it('sets the loading indicator false and passes it down', () => {
      expect(findVulnerabilityLayout().props('isLoading')).toBe(false);
    });

    it('should load the vulnerabilities and pass them down to the layout', () => {
      expect(findVulnerabilityLayout().props('vulnerabilitiesCount')).toEqual({
        critical: 5,
        high: 3,
        low: 19,
        info: 4,
        medium: 2,
        unknown: 4,
      });
    });

    it('refetches the query when vulnerabilities-updated event is triggered', () => {
      eventHub.$emit('vulnerabilities-updated', wrapper.vm);
      expect(refetchSpy).toHaveBeenCalled();
    });
  });

  describe.each`
    dashboardType               | provide                                | expectedContainedQueryVariables
    ${DASHBOARD_TYPES.INSTANCE} | ${undefined}                           | ${{ isInstance: true, isGroup: false, isProject: false }}
    ${DASHBOARD_TYPES.GROUP}    | ${{ groupFullPath: 'group/path' }}     | ${{ isInstance: false, isGroup: true, isProject: false }}
    ${DASHBOARD_TYPES.PROJECT}  | ${{ projectFullPath: 'project/path' }} | ${{ isInstance: false, isGroup: false, isProject: true }}
  `(
    'when the dashboard type is $dashboardType',
    ({ dashboardType, provide, expectedContainedQueryVariables }) => {
      beforeEach(() => {
        const mockResponse = jest
          .fn()
          .mockResolvedValue(mockVulnerabilitySeveritiesGraphQLResponse({ dashboardType }));

        createWrapperWithApollo({
          provide: { dashboardType, ...provide },
          propsData: { filters: { someFilter: 1 } },
          query: mockResponse,
          stubs: { VulnerabilityCountListLayout },
        });

        return wrapper.vm.$nextTick();
      });

      it('should pass the correct variables to the GraphQL query', () => {
        expect(
          wrapper.vm.$options.apollo.vulnerabilitiesCount.variables.call(wrapper.vm),
        ).toMatchObject(expectedContainedQueryVariables);
      });

      it('should set the data properly', () => {
        expect(findVulnerabilityLayout().props('vulnerabilitiesCount')).toEqual({
          critical: 0,
          high: 0,
          info: 0,
          low: 0,
          medium: 4,
          unknown: 2,
        });
      });
    },
  );

  describe('when there is an error', () => {
    beforeEach(() => {
      wrapper = createWrapper({ data: { queryError: true } });
    });

    it('should tell the layout to display an error', () => {
      expect(findVulnerabilityLayout().props('showError')).toBe(true);
    });
  });

  describe('filters prop', () => {
    const mockQuery = jest.fn().mockResolvedValue(null);

    it('does not run the query when filters is null', () => {
      createWrapperWithApollo({
        query: mockQuery,
        propsData: { filters: null },
        provide: { dashboardType: DASHBOARD_TYPES.PROJECT },
      });

      expect(mockQuery).not.toHaveBeenCalled();
    });

    it('runs query when filters is an object', () => {
      createWrapperWithApollo({
        query: mockQuery,
        propsData: { filters: {} },
        provide: { dashboardType: DASHBOARD_TYPES.PROJECT },
      });

      expect(mockQuery).toHaveBeenCalled();
    });
  });
});
