import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper';
import { severityGroupTypes } from 'ee/security_dashboard/store/modules/vulnerable_projects/constants';
import { Accordion, AccordionItem } from 'ee/vue_shared/components/accordion';
import VulnerabilitySeverity from 'ee/security_dashboard/components/first_class_vulnerability_severities.vue';
import { n__ } from '~/locale';
import { generateProjectsWithSeverityCounts } from './mock_data';

describe('Vulnerability Severity component', () => {
  let wrapper;

  const helpPagePath = 'http://localhost/help-me';
  const projects = generateProjectsWithSeverityCounts();
  const vulnerabilityGrades = {
    F: [projects[0]],
    D: [projects[1]],
    C: [projects[2], projects[3]],
    B: [projects[4]],
    A: [projects[5], projects[6]],
  };
  const responseData = {
    vulnerabilityGrades: Object.entries(vulnerabilityGrades).map(([grade, gradeProjects]) => ({
      grade,
      projects: { nodes: gradeProjects },
    })),
  };

  const findAccordionItemsText = () =>
    wrapper
      .findAll('[data-testid="vulnerability-severity-groups"]')
      .wrappers.map(item => trimText(item.text()));

  const mockAccordianItemsText = () =>
    Object.entries(vulnerabilityGrades).map(
      ([grade, relatedProjects]) =>
        `${grade} ${n__('%d project', '%d projects', relatedProjects.length)}`,
    );

  const createComponent = ({ propsData, data }) => {
    return shallowMount(VulnerabilitySeverity, {
      propsData: {
        query: {},
        helpPagePath,
        ...propsData,
      },
      stubs: {
        Accordion,
        AccordionItem,
      },
      data,
    });
  };

  const findHelpLink = () => wrapper.find(GlLink);
  const findHeader = () => wrapper.find('h4');
  const findDescription = () => wrapper.find('p');
  const findAccordionItemByGrade = grade => wrapper.find({ ref: `accordionItem${grade}` });
  const findProjectName = accordion => accordion.findAll(GlLink);

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

  describe('when loading the project severity component for group level dashboard', () => {
    beforeEach(() => {
      wrapper = createComponent({ propsData: { groupFullPath: 'gitlab-org' } });
    });

    it('should process the data returned from GraphQL properly', async () => {
      wrapper.setData({ vulnerabilityGrades: wrapper.vm.processRawData({ group: responseData }) });
      await wrapper.vm.$nextTick();
      expect(findAccordionItemsText()).toEqual(mockAccordianItemsText());
    });
  });

  describe('when loading the project severity component for instance level dashboard', () => {
    beforeEach(() => {
      wrapper = createComponent({});
    });

    it('should process the data returned from GraphQL properly', async () => {
      wrapper.setData({
        vulnerabilityGrades: wrapper.vm.processRawData({ instanceSecurityDashboard: responseData }),
      });
      await wrapper.vm.$nextTick();
      expect(findAccordionItemsText()).toEqual(mockAccordianItemsText());
    });
  });

  describe('for all cases', () => {
    beforeEach(() => {
      wrapper = createComponent({});
    });

    it('has the link to the help page', () => {
      expect(findHelpLink().attributes('href')).toBe(helpPagePath);
    });

    it('has a correct header', () => {
      expect(findHeader().text()).toBe('Project security status');
    });

    it('has a correct description', () => {
      expect(findDescription().text()).toBe(
        'Projects are graded based on the highest severity vulnerability present',
      );
    });
  });

  describe.each`
    grade                   | relatedProjects               | correspondingMostSevereVulnerability                            | levels
    ${severityGroupTypes.F} | ${[projects[0]]}              | ${['2 Critical']}                                               | ${'Critical'}
    ${severityGroupTypes.D} | ${[projects[1]]}              | ${['1 High']}                                                   | ${'High or unknown'}
    ${severityGroupTypes.C} | ${[projects[2], projects[3]]} | ${['5 Medium', '4 Medium']}                                     | ${'Medium'}
    ${severityGroupTypes.B} | ${[projects[4]]}              | ${['2 Low']}                                                    | ${'Low'}
    ${severityGroupTypes.A} | ${[projects[5], projects[6]]} | ${['No vulnerabilities present', 'No vulnerabilities present']} | ${'No'}
  `(
    'for grade $grade',
    ({ grade, relatedProjects, correspondingMostSevereVulnerability, levels }) => {
      let accordion;
      let text;

      beforeEach(() => {
        wrapper = createComponent({ data: () => ({ vulnerabilityGrades }) });
        accordion = findAccordionItemByGrade(grade);
        text = trimText(accordion.text());
      });

      it('has a corresponding accordion item', () => {
        expect(accordion.exists()).toBe(true);
      });

      it('has the projects listed in the accordion item', () => {
        relatedProjects.forEach((project, i) => {
          const projectLink = findProjectName(accordion).at(i);
          expect(projectLink.text()).toBe(project.nameWithNamespace);
          expect(projectLink.attributes('href')).toBe(project.securityDashboardPath);
        });
      });

      it('states how many projects are there in the group', () => {
        expect(text).toContain(n__('%d project', '%d projects', relatedProjects.length));
      });

      it('states which levels belong to the group', () => {
        expect(text).toContain(`${levels} vulnerabilities present`);
      });

      it('states the most severe vulnerability', () => {
        relatedProjects.forEach((_, i) => {
          expect(text).toContain(correspondingMostSevereVulnerability[i]);
        });
      });
    },
  );
});
