import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';

import { GlAvatar, GlLink } from '@gitlab/ui';

import { trimText } from 'helpers/text_helper';

import { Accordion, AccordionItem } from 'ee/vue_shared/components/accordion';
import VulnerabilitySeverity from 'ee/security_dashboard/components/vulnerability_severity.vue';

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

describe('Vulnerability Severity component', () => {
  let actions;
  let getters;
  let store;
  let propsData;
  let wrapper;

  const getMockProject = () => ({
    fullPath: '/foo/bar',
    fullName: 'baz',
    mostSevereVulnerability: { level: 'qux', count: 10 },
  });

  const getMockSeverityGroups = ({ projects = [] } = {}) => ({
    A: { type: 'A', projects },
    B: { type: 'B', projects },
    D: { type: 'D', projects },
    E: { type: 'E', projects },
    F: { type: 'F', projects },
  });

  const factory = () => {
    const state = {
      isLoading: false,
      projects: [],
    };

    propsData = {
      endpoint: 'http://foo.com',
    };

    actions = {
      fetchProjects: jest.fn(),
    };

    getters = {
      severityGroups: getMockSeverityGroups,
    };

    store = new Vuex.Store({
      modules: {
        vulnerableProjects: {
          namespaced: true,
          actions,
          getters,
          state,
        },
      },
    });

    wrapper = mount(VulnerabilitySeverity, {
      localVue,
      store,
      propsData,
    });
  };

  const accordion = () => wrapper.find(Accordion);
  const accordionItems = () => wrapper.findAll(AccordionItem);
  const firstAccordionItem = () => accordionItems().at(0);
  const accordionItemForSeverityGroup = groupName =>
    wrapper.find({ ref: `accordionItem${groupName}` });
  const hasAccordionItemForEachSeverityLevel = () =>
    expect(accordionItems().length).toBe(Object.keys(getMockSeverityGroups()).length);

  const hasEachAccordionItemDisabled = () =>
    accordionItems().wrappers.every(item => item.props('disabled'));

  beforeEach(factory);

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

    jest.restoreAllMocks();
  });

  describe('when being created', () => {
    it('dispatches the "fetchProjects" action with the given endpoint as an argument', () => {
      expect(actions.fetchProjects).toHaveBeenCalledTimes(1);
      expect(actions.fetchProjects.mock.calls[0][1]).toBe(propsData.endpoint);
    });
  });

  describe('while the data is being loaded', () => {
    beforeEach(() => {
      store.state.vulnerableProjects.isLoading = true;
      return wrapper.vm.$nextTick();
    });

    it('contains an accordion item with a loading state for each of the severity levels', () => {
      hasAccordionItemForEachSeverityLevel();

      accordionItems().wrappers.forEach(itemWrapper => {
        expect(itemWrapper.props('isLoading')).toBe(true);
      });
    });
  });

  describe('when the data has loaded', () => {
    it('contains an accordion', () => {
      expect(accordion().exists()).toBe(true);
    });

    it('contains an accordion item for each of the severity levels', () => {
      hasAccordionItemForEachSeverityLevel();
    });

    it('sets accordion items to be disabled if its given severity level has no projects', () => {
      store.state.vulnerableProjects.projects = [];

      return wrapper.vm.$nextTick().then(() => {
        expect(hasEachAccordionItemDisabled()).toBe(true);
      });
    });

    it('does not set accordion items to be disabled if its given severity level has projects', () => {
      store.state.vulnerableProjects.projects = [getMockProject()];

      return wrapper.vm.$nextTick().then(() => {
        expect(hasEachAccordionItemDisabled()).toBe(false);
      });
    });

    it.each(['A', 'B', 'D', 'E', 'F'])('contains an avatar for severity group: "%s"', groupName => {
      expect(
        accordionItemForSeverityGroup(groupName)
          .find(GlAvatar)
          .props('entityName'),
      ).toBe(groupName);
    });

    it('links to a given project', () => {
      const mockProject = getMockProject();
      store.state.vulnerableProjects.projects = [mockProject];

      return wrapper.vm.$nextTick().then(() => {
        expect(
          firstAccordionItem()
            .find(GlLink)
            .attributes('href'),
        ).toContain(mockProject.fullPath);

        expect(firstAccordionItem().text()).toContain(mockProject.fullName);
      });
    });

    it('shows a count for the most severe vulnerability level', () => {
      const project = { mostSevereVulnerability: { level: 'critical', count: 10 } };
      store.state.vulnerableProjects.projects = [project];

      return wrapper.vm.$nextTick().then(() => {
        expect(trimText(wrapper.find({ ref: 'mostSevereCount' }).text())).toBe('10 Critical');
      });
    });
  });
});
