/*
 * SonarQube
 * Copyright (C) 2009-2024 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { keyBy, times } from 'lodash';
import React from 'react';
import { Route } from 'react-router-dom';
import ApplicationServiceMock from '../../../../api/mocks/ApplicationServiceMock';
import { ProjectActivityServiceMock } from '../../../../api/mocks/ProjectActivityServiceMock';
import { TimeMachineServiceMock } from '../../../../api/mocks/TimeMachineServiceMock';
import { mockBranchList } from '../../../../api/mocks/data/branches';
import { parseDate } from '../../../../helpers/dates';
import { mockComponent } from '../../../../helpers/mocks/component';
import {
  mockAnalysis,
  mockAnalysisEvent,
  mockHistoryItem,
  mockMeasureHistory,
} from '../../../../helpers/mocks/project-activity';
import { get } from '../../../../helpers/storage';
import { mockMetric } from '../../../../helpers/testMocks';
import { renderAppWithComponentContext } from '../../../../helpers/testReactTestingUtils';
import { byLabelText, byRole, byTestId, byText } from '../../../../helpers/testSelector';
import { ComponentQualifier } from '../../../../types/component';
import { MetricKey, MetricType } from '../../../../types/metrics';
import {
  ApplicationAnalysisEventCategory,
  GraphType,
  ProjectAnalysisEventCategory,
} from '../../../../types/project-activity';
import ProjectActivityAppContainer from '../ProjectActivityApp';

jest.mock('../../../../api/projectActivity');
jest.mock('../../../../api/time-machine');

jest.mock('../../../../helpers/storage', () => ({
  ...jest.requireActual('../../../../helpers/storage'),
  get: jest.fn(),
  save: jest.fn(),
}));

jest.mock('../../../../api/branches', () => ({
  getBranches: () => {
    isBranchReady = true;
    return Promise.resolve(mockBranchList());
  },
}));

const applicationHandler = new ApplicationServiceMock();
const projectActivityHandler = new ProjectActivityServiceMock();
const timeMachineHandler = new TimeMachineServiceMock();

let isBranchReady = false;

beforeEach(() => {
  isBranchReady = false;

  jest.clearAllMocks();
  applicationHandler.reset();
  projectActivityHandler.reset();
  timeMachineHandler.reset();

  timeMachineHandler.setMeasureHistory(
    [
      MetricKey.violations,
      MetricKey.bugs,
      MetricKey.reliability_rating,
      MetricKey.code_smells,
      MetricKey.sqale_rating,
      MetricKey.security_hotspots_reviewed,
      MetricKey.security_review_rating,
      MetricKey.maintainability_issues,
    ].map((metric) =>
      mockMeasureHistory({
        metric,
        history: projectActivityHandler.getAnalysesList().map(({ date }) =>
          mockHistoryItem({
            value: '3',
            date: parseDate(date),
          }),
        ),
      }),
    ),
  );
});

describe('rendering', () => {
  it('should render issues as default graph', async () => {
    const { ui } = getPageObject();
    renderProjectActivityAppContainer();

    await ui.appLoaded();
    expect(ui.graphTypeIssues.get()).toBeInTheDocument();
  });

  it('should render new code legend for applications', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        qualifier: ComponentQualifier.Application,
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Application },
        ],
      }),
    );
    await ui.appLoaded();
    expect(ui.newCodeLegend.get()).toBeInTheDocument();
  });

  it('should render new code legend for projects', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        qualifier: ComponentQualifier.Project,
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
        leakPeriodDate: parseDate('2017-03-01T22:00:00.000Z').toDateString(),
      }),
    );

    await ui.appLoaded();
    expect(ui.newCodeLegend.get()).toBeInTheDocument();
  });

  it.each([ComponentQualifier.Portfolio, ComponentQualifier.SubPortfolio])(
    'should not render new code legend for %s',
    async (qualifier) => {
      const { ui } = getPageObject();

      renderProjectActivityAppContainer(
        mockComponent({
          qualifier,
          breadcrumbs: [{ key: 'breadcrumb', name: 'breadcrumb', qualifier }],
        }),
      );

      await ui.appLoaded({ doNotWaitForBranch: true });
      expect(ui.newCodeLegend.query()).not.toBeInTheDocument();
    },
  );

  it('should correctly show the baseline marker', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        leakPeriodDate: parseDate('2017-03-01T22:00:00.000Z').toDateString(),
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
      }),
    );

    await ui.appLoaded();
    expect(ui.baseline.get()).toBeInTheDocument();
  });

  it('should correctly show the baseline marker when first new code analysis is not present but baseline analysis is present', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        leakPeriodDate: parseDate('2017-03-03T22:00:00.000Z').toDateString(),
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
      }),
    );

    await ui.appLoaded();
    expect(ui.baseline.get()).toBeInTheDocument();
  });

  it('should not show the baseline marker when first new code analysis and baseline analysis is not present', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        leakPeriodDate: parseDate('2017-03-10T22:00:00.000Z').toDateString(),
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
      }),
    );

    await ui.appLoaded();
    expect(ui.baseline.query()).not.toBeInTheDocument();
  });

  it('should only show certain security hotspot-related metrics for a project', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
      }),
    );

    await ui.changeGraphType(GraphType.custom);
    await ui.openMetricsDropdown();
    expect(ui.metricCheckbox(MetricKey.security_hotspots_reviewed).get()).toBeInTheDocument();
    expect(ui.metricCheckbox(MetricKey.security_review_rating).query()).not.toBeInTheDocument();
  });

  it.each([ComponentQualifier.Portfolio, ComponentQualifier.SubPortfolio])(
    'should only show certain security hotspot-related metrics for a %s',
    async (qualifier) => {
      const { ui } = getPageObject();

      renderProjectActivityAppContainer(
        mockComponent({
          qualifier,
          breadcrumbs: [{ key: 'breadcrumb', name: 'breadcrumb', qualifier }],
        }),
      );

      await ui.changeGraphType(GraphType.custom);
      await ui.openMetricsDropdown();
      expect(ui.metricCheckbox(MetricKey.security_review_rating).get()).toBeInTheDocument();

      expect(
        ui.metricCheckbox(MetricKey.security_hotspots_reviewed).query(),
      ).not.toBeInTheDocument();
    },
  );

  it('should render graph gap info message', async () => {
    timeMachineHandler.setMeasureHistory([
      mockMeasureHistory({
        metric: MetricKey.maintainability_issues,
        history: projectActivityHandler.getAnalysesList().map(({ date }, index) =>
          mockHistoryItem({
            // eslint-disable-next-line jest/no-conditional-in-test
            value: index === 0 ? '3' : undefined,
            date: parseDate(date),
          }),
        ),
      }),
    ]);
    const { ui } = getPageObject();
    renderProjectActivityAppContainer(
      mockComponent({
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Application },
        ],
      }),
    );

    await ui.changeGraphType(GraphType.custom);
    await ui.openMetricsDropdown();
    await ui.toggleMetric(MetricKey.maintainability_issues);
    expect(ui.gapInfoMessage.get()).toBeInTheDocument();
  });

  it('should not render graph gap info message if no gaps', async () => {
    const { ui } = getPageObject();
    renderProjectActivityAppContainer(
      mockComponent({
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Application },
        ],
      }),
    );

    await ui.changeGraphType(GraphType.custom);
    await ui.openMetricsDropdown();
    await ui.toggleMetric(MetricKey.maintainability_issues);
    expect(ui.gapInfoMessage.query()).not.toBeInTheDocument();
  });
});

describe('CRUD', () => {
  it('should correctly create, update, and delete "VERSION" events', async () => {
    const { ui } = getPageObject();
    const initialValue = '1.1-SNAPSHOT';
    const updatedValue = '1.1--SNAPSHOT';

    renderProjectActivityAppContainer(
      mockComponent({
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
        configuration: { showHistory: true },
      }),
    );

    await ui.appLoaded();

    await ui.addVersionEvent('1.1.0.1', initialValue);
    expect(screen.getAllByText(initialValue).length).toBeGreaterThan(0);

    await ui.updateEvent(1, updatedValue);
    expect(screen.getAllByText(updatedValue).length).toBeGreaterThan(0);

    await ui.deleteEvent(0);
    expect(screen.queryByText(updatedValue)).not.toBeInTheDocument();
  });

  it('should correctly create, update, and delete "OTHER" events', async () => {
    const { ui } = getPageObject();
    const initialValue = 'Custom event name';
    const updatedValue = 'Custom event updated name';

    renderProjectActivityAppContainer(
      mockComponent({
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
        configuration: { showHistory: true },
      }),
    );

    await ui.appLoaded();

    await ui.addCustomEvent('1.1.0.1', initialValue);
    expect(screen.getAllByText(initialValue).length).toBeGreaterThan(0);

    await ui.updateEvent(1, updatedValue);
    expect(screen.getAllByText(updatedValue).length).toBeGreaterThan(0);

    await ui.deleteEvent(0);
    expect(screen.queryByText(updatedValue)).not.toBeInTheDocument();
  });

  it('should correctly allow deletion of specific analyses', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        breadcrumbs: [
          { key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project },
        ],
        configuration: { showHistory: true },
      }),
    );

    await ui.appLoaded();

    // Most recent analysis is not deletable.
    await ui.openCogMenu('1.1.0.2');
    expect(ui.deleteAnalysisBtn.query()).not.toBeInTheDocument();

    await ui.deleteAnalysis('1.1.0.1');
    expect(screen.queryByText('1.1.0.1')).not.toBeInTheDocument();
  });
});

describe('data loading', () => {
  function getMock(namespace: string) {
    // eslint-disable-next-line jest/no-conditional-in-test
    return namespace.includes('.custom') ? 'bugs,code_smells' : GraphType.custom;
  }

  it('should load all analyses', async () => {
    const count = 1000;

    projectActivityHandler.setAnalysesList(
      times(count, (i) => {
        return mockAnalysis({
          key: `analysis-${i}`,
          date: '2016-01-01T00:00:00+0200',
        });
      }),
    );

    const { ui } = getPageObject();
    renderProjectActivityAppContainer();
    await ui.appLoaded();

    expect(ui.activityItem.getAll().length).toBe(count);
  });

  it('should reload custom graph from local storage', async () => {
    jest.mocked(get).mockImplementationOnce(getMock).mockImplementationOnce(getMock);
    const { ui } = getPageObject();
    renderProjectActivityAppContainer();
    await ui.appLoaded();

    expect(ui.graphTypeCustom.get()).toBeInTheDocument();
  });

  it('should correctly fetch the top level component when dealing with sub portfolios', async () => {
    const { ui } = getPageObject();

    renderProjectActivityAppContainer(
      mockComponent({
        key: 'unknown',
        qualifier: ComponentQualifier.SubPortfolio,
        breadcrumbs: [
          { key: 'foo', name: 'foo', qualifier: ComponentQualifier.Portfolio },
          { key: 'unknown', name: 'unknown', qualifier: ComponentQualifier.SubPortfolio },
        ],
      }),
    );

    await ui.appLoaded({ doNotWaitForBranch: true });

    // If it didn't fail, it means we correctly queried for project "foo".
    expect(ui.activityItem.getAll().length).toBe(4);
  });
});

describe('filtering', () => {
  it('should correctly filter by event category', async () => {
    projectActivityHandler.setAnalysesList([
      mockAnalysis({
        key: `analysis-1`,
        events: [],
      }),
      mockAnalysis({
        key: `analysis-2`,
        events: [
          mockAnalysisEvent({ key: '1', category: ProjectAnalysisEventCategory.QualityGate }),
        ],
      }),
      mockAnalysis({
        key: `analysis-3`,
        events: [mockAnalysisEvent({ key: '2', category: ProjectAnalysisEventCategory.Version })],
      }),
      mockAnalysis({
        key: `analysis-4`,
        events: [mockAnalysisEvent({ key: '3', category: ProjectAnalysisEventCategory.Version })],
      }),
      mockAnalysis({
        key: `analysis-5`,
        events: [mockAnalysisEvent({ key: '4', category: ProjectAnalysisEventCategory.SqUpgrade })],
      }),
      mockAnalysis({
        key: `analysis-6`,
        events: [mockAnalysisEvent({ key: '5', category: ProjectAnalysisEventCategory.Version })],
      }),
      mockAnalysis({
        key: `analysis-7`,
        events: [mockAnalysisEvent({ key: '6', category: ProjectAnalysisEventCategory.SqUpgrade })],
      }),
    ]);

    const { ui } = getPageObject();
    renderProjectActivityAppContainer();
    await ui.appLoaded();

    await ui.filterByCategory(ProjectAnalysisEventCategory.Version);
    expect(ui.activityItem.getAll().length).toBe(3);

    await ui.filterByCategory(ProjectAnalysisEventCategory.QualityGate);
    expect(ui.activityItem.getAll().length).toBe(1);

    await ui.filterByCategory(ProjectAnalysisEventCategory.SqUpgrade);
    expect(ui.activityItem.getAll().length).toBe(2);
  });

  it('should correctly filter by date range', async () => {
    projectActivityHandler.setAnalysesList(
      times(20, (i) => {
        const date = parseDate('2016-01-01T00:00:00.000Z');
        date.setDate(date.getDate() + i);
        return mockAnalysis({
          key: `analysis-${i}`,
          date: date.toDateString(),
        });
      }),
    );

    const { ui } = getPageObject();
    renderProjectActivityAppContainer();
    await ui.appLoaded();

    expect(ui.activityItem.getAll().length).toBe(20);

    await ui.setDateRange('2016-01-10');
    expect(ui.activityItem.getAll().length).toBe(11);
    await ui.resetDateFilters();

    expect(ui.activityItem.getAll().length).toBe(20);

    await ui.setDateRange('2016-01-10', '2016-01-11');
    expect(ui.activityItem.getAll().length).toBe(2);
    await ui.resetDateFilters();

    await ui.setDateRange(undefined, '2016-01-08');
    expect(ui.activityItem.getAll().length).toBe(8);
  });
});

describe('graph interactions', () => {
  it('should allow analyses to be clicked to see details for the analysis', async () => {
    const { ui } = getPageObject();
    renderProjectActivityAppContainer();
    await ui.appLoaded();

    expect(ui.issuesPopupCell.query()).not.toBeInTheDocument();

    await ui.showDetails('1.1.0.1');

    expect(ui.issuesPopupCell.get()).toBeInTheDocument();
  });

  it('should correctly handle customizing the graph', async () => {
    const { ui } = getPageObject();
    renderProjectActivityAppContainer();
    await ui.appLoaded();

    await ui.changeGraphType(GraphType.custom);

    expect(ui.noDataText.get()).toBeInTheDocument();

    // Add metrics.
    await ui.openMetricsDropdown();
    await ui.toggleMetric(MetricKey.bugs);
    await ui.toggleMetric(MetricKey.security_hotspots_reviewed);
    await ui.closeMetricsDropdown();

    expect(ui.graphs.getAll()).toHaveLength(2);

    // Remove metrics.
    await ui.openMetricsDropdown();
    await ui.toggleMetric(MetricKey.bugs);
    await ui.toggleMetric(MetricKey.security_hotspots_reviewed);
    await ui.closeMetricsDropdown();

    expect(ui.noDataText.get()).toBeInTheDocument();

    await ui.changeGraphType(GraphType.issues);

    expect(ui.graphs.getAll()).toHaveLength(1);
  });
});

function getPageObject() {
  const user = userEvent.setup();

  const ui = {
    // Graph types.
    graphTypeSelect: byLabelText('project_activity.graphs.choose_type'),
    graphTypeIssues: byText('project_activity.graphs.issues'),
    graphTypeCustom: byText('project_activity.graphs.custom'),

    // Graphs.
    graphs: byLabelText('project_activity.graphs.explanation_x', { exact: false }),
    noDataText: byText('project_activity.graphs.custom.no_history'),
    gapInfoMessage: byText('project_activity.graphs.data_table.data_gap', { exact: false }),

    // Add metrics.
    addMetricBtn: byRole('button', { name: 'project_activity.graphs.custom.add' }),
    metricCheckbox: (name: MetricKey) => byRole('checkbox', { name }),

    // Graph legend.
    newCodeLegend: byText('hotspot.filters.period.since_leak_period'),

    // Filtering.
    categorySelect: byLabelText('project_activity.filter_events'),
    resetDatesBtn: byRole('button', { name: 'project_activity.reset_dates' }),
    fromDateInput: byLabelText('start_date'),
    toDateInput: byLabelText('end_date'),

    // Analysis interactions.
    activityItem: byLabelText(/project_activity.show_analysis_X_on_graph/),
    cogBtn: (id: string) => byRole('button', { name: `project_activity.analysis_X_actions.${id}` }),
    seeDetailsBtn: (time: string) =>
      byLabelText(`project_activity.show_analysis_X_on_graph.${time}`),
    addCustomEventBtn: byRole('menuitem', { name: 'project_activity.add_custom_event' }),
    addVersionEvenBtn: byRole('menuitem', { name: 'project_activity.add_version' }),
    deleteAnalysisBtn: byRole('menuitem', { name: 'project_activity.delete_analysis' }),
    editEventBtn: byRole('button', { name: 'project_activity.events.tooltip.edit' }),
    deleteEventBtn: byRole('button', { name: 'project_activity.events.tooltip.delete' }),

    // Event modal.
    nameInput: byLabelText('name'),
    saveBtn: byRole('button', { name: 'save' }),
    changeBtn: byRole('button', { name: 'change_verb' }),
    deleteBtn: byRole('button', { name: 'delete' }),

    // Misc.
    loading: byText('loading'),
    baseline: byText('project_activity.new_code_period_start'),
    issuesPopupCell: byRole('cell', { name: `metric.${MetricKey.violations}.name` }),
    monthSelector: byTestId('month-select'),
    yearSelector: byTestId('year-select'),
  };

  return {
    user,
    ui: {
      ...ui,
      async appLoaded({ doNotWaitForBranch }: { doNotWaitForBranch?: boolean } = {}) {
        expect(await ui.graphs.findAll()).toHaveLength(1);

        if (!doNotWaitForBranch) {
          await waitFor(() => {
            expect(isBranchReady).toBe(true);
          });
        }
      },

      async changeGraphType(type: GraphType) {
        await user.click(ui.graphTypeSelect.get());
        const optionForType = await screen.findByText(`project_activity.graphs.${type}`);
        await user.click(optionForType);
      },

      async openMetricsDropdown() {
        await user.click(ui.addMetricBtn.get());
      },

      async toggleMetric(metric: MetricKey) {
        await user.click(ui.metricCheckbox(metric).get());
      },

      async closeMetricsDropdown() {
        await user.keyboard('{Escape}');
      },

      async openCogMenu(id: string) {
        await user.click(ui.cogBtn(id).get());
      },

      async deleteAnalysis(id: string) {
        await user.click(ui.cogBtn(id).get());
        await user.click(ui.deleteAnalysisBtn.get());
        await user.click(ui.deleteBtn.get());
      },

      async addVersionEvent(id: string, value: string) {
        await user.click(ui.cogBtn(id).get());
        await user.click(ui.addVersionEvenBtn.get());
        await user.type(ui.nameInput.get(), value);
        await user.click(ui.saveBtn.get());
      },

      async addCustomEvent(id: string, value: string) {
        await user.click(ui.cogBtn(id).get());
        await user.click(ui.addCustomEventBtn.get());
        await user.type(ui.nameInput.get(), value);
        await user.click(ui.saveBtn.get());
      },

      async updateEvent(index: number, value: string) {
        await user.click(ui.editEventBtn.getAll()[index]);
        await user.clear(ui.nameInput.get());
        await user.type(ui.nameInput.get(), value);
        await user.click(ui.changeBtn.get());
      },

      async deleteEvent(index: number) {
        await user.click(ui.deleteEventBtn.getAll()[index]);
        await user.click(ui.deleteBtn.get());
      },

      async showDetails(id: string) {
        await user.click(ui.seeDetailsBtn(id).get());
      },

      async filterByCategory(
        category: ProjectAnalysisEventCategory | ApplicationAnalysisEventCategory,
      ) {
        await user.click(ui.categorySelect.get());
        const optionForType = await screen.findByText(`event.category.${category}`);
        await user.click(optionForType);
      },

      async setDateRange(from?: string, to?: string) {
        if (from) {
          await this.selectDate(from, ui.fromDateInput.get());
        }

        if (to) {
          await this.selectDate(to, ui.toDateInput.get());
        }
      },

      async selectDate(date: string, datePickerSelector: HTMLElement) {
        const monthMap = [
          'Jan',
          'Feb',
          'Mar',
          'Apr',
          'May',
          'Jun',
          'Jul',
          'Aug',
          'Sep',
          'Oct',
          'Nov',
          'Dec',
        ];
        const parsedDate = parseDate(date);
        await user.click(datePickerSelector);
        const monthSelector = within(ui.monthSelector.get()).getByRole('combobox');

        await user.click(monthSelector);
        const selectedMonthElements = within(ui.monthSelector.get()).getAllByText(
          monthMap[parseDate(parsedDate).getMonth()],
        );
        await user.click(selectedMonthElements[selectedMonthElements.length - 1]);

        const yearSelector = within(ui.yearSelector.get()).getByRole('combobox');

        await user.click(yearSelector);
        const selectedYearElements = within(ui.yearSelector.get()).getAllByText(
          parseDate(parsedDate).getFullYear(),
        );
        await user.click(selectedYearElements[selectedYearElements.length - 1]);

        await user.click(
          screen.getByText(parseDate(parsedDate).getDate().toString(), { selector: 'button' }),
        );
      },

      async resetDateFilters() {
        await user.click(ui.resetDatesBtn.get());
      },
    },
  };
}

function renderProjectActivityAppContainer(
  component = mockComponent({
    breadcrumbs: [{ key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Project }],
  }),
) {
  return renderAppWithComponentContext(
    `project/activity?id=${component.key}`,
    () => <Route path="*" element={<ProjectActivityAppContainer />} />,
    {
      metrics: keyBy(
        [
          mockMetric({ key: MetricKey.maintainability_issues, type: MetricType.Data }),
          mockMetric({ key: MetricKey.bugs, type: MetricType.Integer }),
          mockMetric({ key: MetricKey.code_smells, type: MetricType.Integer }),
          mockMetric({ key: MetricKey.security_hotspots_reviewed }),
          mockMetric({ key: MetricKey.security_review_rating, type: MetricType.Rating }),
        ],
        'key',
      ),
    },
    { component },
  );
}
