Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/cycle_analytics')
-rw-r--r--spec/frontend/cycle_analytics/metric_popover_spec.js102
-rw-r--r--spec/frontend/cycle_analytics/mock_data.js41
-rw-r--r--spec/frontend/cycle_analytics/store/actions_spec.js144
-rw-r--r--spec/frontend/cycle_analytics/store/mutations_spec.js1
-rw-r--r--spec/frontend/cycle_analytics/utils_spec.js96
-rw-r--r--spec/frontend/cycle_analytics/value_stream_metrics_spec.js58
6 files changed, 351 insertions, 91 deletions
diff --git a/spec/frontend/cycle_analytics/metric_popover_spec.js b/spec/frontend/cycle_analytics/metric_popover_spec.js
new file mode 100644
index 00000000000..5a622fcacd5
--- /dev/null
+++ b/spec/frontend/cycle_analytics/metric_popover_spec.js
@@ -0,0 +1,102 @@
+import { GlLink, GlIcon } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import MetricPopover from '~/cycle_analytics/components/metric_popover.vue';
+
+const MOCK_METRIC = {
+ key: 'deployment-frequency',
+ label: 'Deployment Frequency',
+ value: '10.0',
+ unit: 'per day',
+ description: 'Average number of deployments to production per day.',
+ links: [],
+};
+
+describe('MetricPopover', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ return shallowMountExtended(MetricPopover, {
+ propsData: {
+ target: 'deployment-frequency',
+ ...props,
+ },
+ stubs: {
+ 'gl-popover': { template: '<div><slot name="title"></slot><slot></slot></div>' },
+ },
+ });
+ };
+
+ const findMetricLabel = () => wrapper.findByTestId('metric-label');
+ const findAllMetricLinks = () => wrapper.findAll('[data-testid="metric-link"]');
+ const findMetricDescription = () => wrapper.findByTestId('metric-description');
+ const findMetricDocsLink = () => wrapper.findByTestId('metric-docs-link');
+ const findMetricDocsLinkIcon = () => findMetricDocsLink().find(GlIcon);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the metric label', () => {
+ wrapper = createComponent({ metric: MOCK_METRIC });
+ expect(findMetricLabel().text()).toBe(MOCK_METRIC.label);
+ });
+
+ it('renders the metric description', () => {
+ wrapper = createComponent({ metric: MOCK_METRIC });
+ expect(findMetricDescription().text()).toBe(MOCK_METRIC.description);
+ });
+
+ describe('with links', () => {
+ const links = [
+ {
+ name: 'Deployment frequency',
+ url: '/groups/gitlab-org/-/analytics/ci_cd?tab=deployment-frequency',
+ label: 'Dashboard',
+ },
+ {
+ name: 'Another link',
+ url: '/groups/gitlab-org/-/analytics/another-link',
+ label: 'Another link',
+ },
+ ];
+ const docsLink = {
+ name: 'Deployment frequency',
+ url: '/help/user/analytics/index#definitions',
+ label: 'Go to docs',
+ docs_link: true,
+ };
+ const linksWithDocs = [...links, docsLink];
+
+ describe.each`
+ hasDocsLink | allLinks | displayedMetricLinks
+ ${true} | ${linksWithDocs} | ${links}
+ ${false} | ${links} | ${links}
+ `(
+ 'when one link has docs_link=$hasDocsLink',
+ ({ hasDocsLink, allLinks, displayedMetricLinks }) => {
+ beforeEach(() => {
+ wrapper = createComponent({ metric: { ...MOCK_METRIC, links: allLinks } });
+ });
+
+ displayedMetricLinks.forEach((link, idx) => {
+ it(`renders a link for "${link.name}"`, () => {
+ const allLinkContainers = findAllMetricLinks();
+
+ expect(allLinkContainers.at(idx).text()).toContain(link.name);
+ expect(allLinkContainers.at(idx).find(GlLink).attributes('href')).toBe(link.url);
+ });
+ });
+
+ it(`${hasDocsLink ? 'renders' : "doesn't render"} a docs link`, () => {
+ expect(findMetricDocsLink().exists()).toBe(hasDocsLink);
+
+ if (hasDocsLink) {
+ expect(findMetricDocsLink().attributes('href')).toBe(docsLink.url);
+ expect(findMetricDocsLink().text()).toBe(docsLink.label);
+ expect(findMetricDocsLinkIcon().attributes('name')).toBe('external-link');
+ }
+ });
+ },
+ );
+ });
+});
diff --git a/spec/frontend/cycle_analytics/mock_data.js b/spec/frontend/cycle_analytics/mock_data.js
index 1882457960a..c482bd4e910 100644
--- a/spec/frontend/cycle_analytics/mock_data.js
+++ b/spec/frontend/cycle_analytics/mock_data.js
@@ -1,10 +1,14 @@
-/* eslint-disable import/no-deprecated */
+import valueStreamAnalyticsStages from 'test_fixtures/projects/analytics/value_stream_analytics/stages.json';
+import issueStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/issue.json';
+import planStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/plan.json';
+import reviewStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/review.json';
+import codeStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/code.json';
+import testStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/test.json';
+import stagingStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/staging.json';
-import { getJSONFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import {
DEFAULT_VALUE_STREAM,
- DEFAULT_DAYS_IN_PAST,
PAGINATION_TYPE,
PAGINATION_SORT_DIRECTION_DESC,
PAGINATION_SORT_FIELD_END_EVENT,
@@ -12,6 +16,7 @@ import {
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { getDateInPast } from '~/lib/utils/datetime_utility';
+const DEFAULT_DAYS_IN_PAST = 30;
export const createdBefore = new Date(2019, 0, 14);
export const createdAfter = getDateInPast(createdBefore, DEFAULT_DAYS_IN_PAST);
@@ -20,28 +25,16 @@ export const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep:
export const getStageByTitle = (stages, title) =>
stages.find((stage) => stage.title && stage.title.toLowerCase().trim() === title) || {};
-const fixtureEndpoints = {
- customizableCycleAnalyticsStagesAndEvents:
- 'projects/analytics/value_stream_analytics/stages.json',
- stageEvents: (stage) => `projects/analytics/value_stream_analytics/events/${stage}.json`,
- metricsData: 'projects/analytics/value_stream_analytics/summary.json',
-};
-
-export const metricsData = getJSONFixture(fixtureEndpoints.metricsData);
-
-export const customizableStagesAndEvents = getJSONFixture(
- fixtureEndpoints.customizableCycleAnalyticsStagesAndEvents,
-);
-
export const defaultStages = ['issue', 'plan', 'review', 'code', 'test', 'staging'];
-const stageFixtures = defaultStages.reduce((acc, stage) => {
- const events = getJSONFixture(fixtureEndpoints.stageEvents(stage));
- return {
- ...acc,
- [stage]: events,
- };
-}, {});
+const stageFixtures = {
+ issue: issueStageFixtures,
+ plan: planStageFixtures,
+ review: reviewStageFixtures,
+ code: codeStageFixtures,
+ test: testStageFixtures,
+ staging: stagingStageFixtures,
+};
export const summary = [
{ value: '20', title: 'New Issues' },
@@ -260,7 +253,7 @@ export const selectedProjects = [
},
];
-export const rawValueStreamStages = customizableStagesAndEvents.stages;
+export const rawValueStreamStages = valueStreamAnalyticsStages.stages;
export const valueStreamStages = rawValueStreamStages.map((s) =>
convertObjectPropsToCamelCase(s, { deep: true }),
diff --git a/spec/frontend/cycle_analytics/store/actions_spec.js b/spec/frontend/cycle_analytics/store/actions_spec.js
index 993e6b6b73a..e775e941b4c 100644
--- a/spec/frontend/cycle_analytics/store/actions_spec.js
+++ b/spec/frontend/cycle_analytics/store/actions_spec.js
@@ -57,22 +57,12 @@ describe('Project Value Stream Analytics actions', () => {
const mutationTypes = (arr) => arr.map(({ type }) => type);
- const mockFetchStageDataActions = [
- { type: 'setLoading', payload: true },
- { type: 'fetchCycleAnalyticsData' },
- { type: 'fetchStageData' },
- { type: 'fetchStageMedians' },
- { type: 'fetchStageCountValues' },
- { type: 'setLoading', payload: false },
- ];
-
describe.each`
- action | payload | expectedActions | expectedMutations
- ${'setLoading'} | ${true} | ${[]} | ${[{ type: 'SET_LOADING', payload: true }]}
- ${'setDateRange'} | ${{ createdAfter, createdBefore }} | ${mockFetchStageDataActions} | ${[mockSetDateActionCommit]}
- ${'setFilters'} | ${[]} | ${mockFetchStageDataActions} | ${[]}
- ${'setSelectedStage'} | ${{ selectedStage }} | ${[{ type: 'fetchStageData' }]} | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
- ${'setSelectedValueStream'} | ${{ selectedValueStream }} | ${[{ type: 'fetchValueStreamStages' }, { type: 'fetchCycleAnalyticsData' }]} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
+ action | payload | expectedActions | expectedMutations
+ ${'setDateRange'} | ${{ createdAfter, createdBefore }} | ${[{ type: 'refetchStageData' }]} | ${[mockSetDateActionCommit]}
+ ${'setFilters'} | ${[]} | ${[{ type: 'refetchStageData' }]} | ${[]}
+ ${'setSelectedStage'} | ${{ selectedStage }} | ${[{ type: 'refetchStageData' }]} | ${[{ type: 'SET_SELECTED_STAGE', payload: { selectedStage } }]}
+ ${'setSelectedValueStream'} | ${{ selectedValueStream }} | ${[{ type: 'fetchValueStreamStages' }]} | ${[{ type: 'SET_SELECTED_VALUE_STREAM', payload: { selectedValueStream } }]}
`('$action', ({ action, payload, expectedActions, expectedMutations }) => {
const types = mutationTypes(expectedMutations);
it(`will dispatch ${expectedActions} and commit ${types}`, () =>
@@ -86,9 +76,18 @@ describe('Project Value Stream Analytics actions', () => {
});
describe('initializeVsa', () => {
- let mockDispatch;
- let mockCommit;
- const payload = { endpoints: mockEndpoints };
+ const selectedAuthor = 'Author';
+ const selectedMilestone = 'Milestone 1';
+ const selectedAssigneeList = ['Assignee 1', 'Assignee 2'];
+ const selectedLabelList = ['Label 1', 'Label 2'];
+ const payload = {
+ endpoints: mockEndpoints,
+ selectedAuthor,
+ selectedMilestone,
+ selectedAssigneeList,
+ selectedLabelList,
+ selectedStage,
+ };
const mockFilterEndpoints = {
groupEndpoint: 'foo',
labelsEndpoint: mockLabelsPath,
@@ -96,27 +95,63 @@ describe('Project Value Stream Analytics actions', () => {
projectEndpoint: '/namespace/-/analytics/value_stream_analytics/value_streams',
};
+ it('will dispatch fetchValueStreams actions and commit SET_LOADING and INITIALIZE_VSA', () => {
+ return testAction({
+ action: actions.initializeVsa,
+ state: {},
+ payload,
+ expectedMutations: [
+ { type: 'INITIALIZE_VSA', payload },
+ { type: 'SET_LOADING', payload: true },
+ { type: 'SET_LOADING', payload: false },
+ ],
+ expectedActions: [
+ { type: 'filters/setEndpoints', payload: mockFilterEndpoints },
+ {
+ type: 'filters/initialize',
+ payload: { selectedAuthor, selectedMilestone, selectedAssigneeList, selectedLabelList },
+ },
+ { type: 'fetchValueStreams' },
+ { type: 'setInitialStage', payload: selectedStage },
+ ],
+ });
+ });
+ });
+
+ describe('setInitialStage', () => {
beforeEach(() => {
- mockDispatch = jest.fn(() => Promise.resolve());
- mockCommit = jest.fn();
+ state = { ...state, stages: allowedStages };
});
- it('will dispatch the setLoading and fetchValueStreams actions and commit INITIALIZE_VSA', async () => {
- await actions.initializeVsa(
- {
- ...state,
- dispatch: mockDispatch,
- commit: mockCommit,
- },
- payload,
- );
- expect(mockCommit).toHaveBeenCalledWith('INITIALIZE_VSA', { endpoints: mockEndpoints });
-
- expect(mockDispatch).toHaveBeenCalledTimes(4);
- expect(mockDispatch).toHaveBeenCalledWith('filters/setEndpoints', mockFilterEndpoints);
- expect(mockDispatch).toHaveBeenCalledWith('setLoading', true);
- expect(mockDispatch).toHaveBeenCalledWith('fetchValueStreams');
- expect(mockDispatch).toHaveBeenCalledWith('setLoading', false);
+ describe('with a selected stage', () => {
+ it('will commit `SET_SELECTED_STAGE` and fetchValueStreamStageData actions', () => {
+ const fakeStage = { ...selectedStage, id: 'fake', name: 'fake-stae' };
+ return testAction({
+ action: actions.setInitialStage,
+ state,
+ payload: fakeStage,
+ expectedMutations: [
+ {
+ type: 'SET_SELECTED_STAGE',
+ payload: fakeStage,
+ },
+ ],
+ expectedActions: [{ type: 'fetchValueStreamStageData' }],
+ });
+ });
+ });
+
+ describe('without a selected stage', () => {
+ it('will select the first stage from the value stream', () => {
+ const [firstStage] = allowedStages;
+ testAction({
+ action: actions.setInitialStage,
+ state,
+ payload: null,
+ expectedMutations: [{ type: 'SET_SELECTED_STAGE', payload: firstStage }],
+ expectedActions: [{ type: 'fetchValueStreamStageData' }],
+ });
+ });
});
});
@@ -270,12 +305,7 @@ describe('Project Value Stream Analytics actions', () => {
state,
payload: {},
expectedMutations: [{ type: 'REQUEST_VALUE_STREAMS' }],
- expectedActions: [
- { type: 'receiveValueStreamsSuccess' },
- { type: 'setSelectedStage' },
- { type: 'fetchStageMedians' },
- { type: 'fetchStageCountValues' },
- ],
+ expectedActions: [{ type: 'receiveValueStreamsSuccess' }],
}));
describe('with a failing request', () => {
@@ -483,4 +513,34 @@ describe('Project Value Stream Analytics actions', () => {
}));
});
});
+
+ describe('refetchStageData', () => {
+ it('will commit SET_LOADING and dispatch fetchValueStreamStageData actions', () =>
+ testAction({
+ action: actions.refetchStageData,
+ state,
+ payload: {},
+ expectedMutations: [
+ { type: 'SET_LOADING', payload: true },
+ { type: 'SET_LOADING', payload: false },
+ ],
+ expectedActions: [{ type: 'fetchValueStreamStageData' }],
+ }));
+ });
+
+ describe('fetchValueStreamStageData', () => {
+ it('will dispatch the fetchCycleAnalyticsData, fetchStageData, fetchStageMedians and fetchStageCountValues actions', () =>
+ testAction({
+ action: actions.fetchValueStreamStageData,
+ state,
+ payload: {},
+ expectedMutations: [],
+ expectedActions: [
+ { type: 'fetchCycleAnalyticsData' },
+ { type: 'fetchStageData' },
+ { type: 'fetchStageMedians' },
+ { type: 'fetchStageCountValues' },
+ ],
+ }));
+ });
});
diff --git a/spec/frontend/cycle_analytics/store/mutations_spec.js b/spec/frontend/cycle_analytics/store/mutations_spec.js
index 4860225c995..2670a390e9c 100644
--- a/spec/frontend/cycle_analytics/store/mutations_spec.js
+++ b/spec/frontend/cycle_analytics/store/mutations_spec.js
@@ -101,6 +101,7 @@ describe('Project Value Stream Analytics mutations', () => {
${types.SET_SELECTED_VALUE_STREAM} | ${selectedValueStream} | ${'selectedValueStream'} | ${selectedValueStream}
${types.SET_PAGINATION} | ${pagination} | ${'pagination'} | ${{ ...pagination, sort: PAGINATION_SORT_FIELD_END_EVENT, direction: PAGINATION_SORT_DIRECTION_DESC }}
${types.SET_PAGINATION} | ${{ ...pagination, sort: 'duration', direction: 'asc' }} | ${'pagination'} | ${{ ...pagination, sort: 'duration', direction: 'asc' }}
+ ${types.SET_SELECTED_STAGE} | ${selectedStage} | ${'selectedStage'} | ${selectedStage}
${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${[selectedValueStream]} | ${'valueStreams'} | ${[selectedValueStream]}
${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS} | ${{ stages: rawValueStreamStages }} | ${'stages'} | ${valueStreamStages}
${types.RECEIVE_STAGE_MEDIANS_SUCCESS} | ${rawStageMedians} | ${'medians'} | ${formattedStageMedians}
diff --git a/spec/frontend/cycle_analytics/utils_spec.js b/spec/frontend/cycle_analytics/utils_spec.js
index 74d64cd8d71..a6d6d022781 100644
--- a/spec/frontend/cycle_analytics/utils_spec.js
+++ b/spec/frontend/cycle_analytics/utils_spec.js
@@ -1,11 +1,11 @@
-import { useFakeDate } from 'helpers/fake_date';
+import metricsData from 'test_fixtures/projects/analytics/value_stream_analytics/summary.json';
import {
transformStagesForPathNavigation,
medianTimeToParsedSeconds,
formatMedianValues,
filterStagesByHiddenStatus,
- calculateFormattedDayInPast,
prepareTimeMetricsData,
+ buildCycleAnalyticsInitialData,
} from '~/cycle_analytics/utils';
import { slugify } from '~/lib/utils/text_utility';
import {
@@ -14,7 +14,6 @@ import {
stageMedians,
pathNavIssueMetric,
rawStageMedians,
- metricsData,
} from './mock_data';
describe('Value stream analytics utils', () => {
@@ -90,14 +89,6 @@ describe('Value stream analytics utils', () => {
});
});
- describe('calculateFormattedDayInPast', () => {
- useFakeDate(1815, 11, 10);
-
- it('will return 2 dates, now and past', () => {
- expect(calculateFormattedDayInPast(5)).toEqual({ now: '1815-12-10', past: '1815-12-05' });
- });
- });
-
describe('prepareTimeMetricsData', () => {
let prepared;
const [first, second] = metricsData;
@@ -125,4 +116,87 @@ describe('Value stream analytics utils', () => {
]);
});
});
+
+ describe('buildCycleAnalyticsInitialData', () => {
+ let res = null;
+ const projectId = '5';
+ const createdAfter = '2021-09-01';
+ const createdBefore = '2021-11-06';
+ const groupId = '146';
+ const groupPath = 'fake-group';
+ const fullPath = 'fake-group/fake-project';
+ const labelsPath = '/fake-group/fake-project/-/labels.json';
+ const milestonesPath = '/fake-group/fake-project/-/milestones.json';
+ const requestPath = '/fake-group/fake-project/-/value_stream_analytics';
+
+ const rawData = {
+ projectId,
+ createdBefore,
+ createdAfter,
+ fullPath,
+ requestPath,
+ labelsPath,
+ milestonesPath,
+ groupId,
+ groupPath,
+ };
+
+ describe('with minimal data', () => {
+ beforeEach(() => {
+ res = buildCycleAnalyticsInitialData(rawData);
+ });
+
+ it('sets the projectId', () => {
+ expect(res.projectId).toBe(parseInt(projectId, 10));
+ });
+
+ it('sets the date range', () => {
+ expect(res.createdBefore).toEqual(new Date(createdBefore));
+ expect(res.createdAfter).toEqual(new Date(createdAfter));
+ });
+
+ it('sets the endpoints', () => {
+ const { endpoints } = res;
+ expect(endpoints.fullPath).toBe(fullPath);
+ expect(endpoints.requestPath).toBe(requestPath);
+ expect(endpoints.labelsPath).toBe(labelsPath);
+ expect(endpoints.milestonesPath).toBe(milestonesPath);
+ expect(endpoints.groupId).toBe(parseInt(groupId, 10));
+ expect(endpoints.groupPath).toBe(groupPath);
+ });
+
+ it('returns null when there is no stage', () => {
+ expect(res.selectedStage).toBeNull();
+ });
+
+ it('returns false for missing features', () => {
+ expect(res.features.cycleAnalyticsForGroups).toBe(false);
+ });
+ });
+
+ describe('with a stage set', () => {
+ const jsonStage = '{"id":"fakeStage","title":"fakeStage"}';
+
+ it('parses the selectedStage data', () => {
+ res = buildCycleAnalyticsInitialData({ ...rawData, stage: jsonStage });
+
+ const { selectedStage: stage } = res;
+
+ expect(stage.id).toBe('fakeStage');
+ expect(stage.title).toBe('fakeStage');
+ });
+ });
+
+ describe('with features set', () => {
+ const fakeFeatures = { cycleAnalyticsForGroups: true };
+
+ it('sets the feature flags', () => {
+ res = buildCycleAnalyticsInitialData({
+ ...rawData,
+ gon: { licensed_features: fakeFeatures },
+ });
+ expect(res.features).toEqual(fakeFeatures);
+ });
+ });
+ });
});
diff --git a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
index ffdb49a828c..c97e4845bc2 100644
--- a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
+++ b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
@@ -1,13 +1,16 @@
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { shallowMount } from '@vue/test-utils';
+import metricsData from 'test_fixtures/projects/analytics/value_stream_analytics/summary.json';
import waitForPromises from 'helpers/wait_for_promises';
import { METRIC_TYPE_SUMMARY } from '~/api/analytics_api';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import createFlash from '~/flash';
-import { group, metricsData } from './mock_data';
+import { redirectTo } from '~/lib/utils/url_utility';
+import { group } from './mock_data';
jest.mock('~/flash');
+jest.mock('~/lib/utils/url_utility');
describe('ValueStreamMetrics', () => {
let wrapper;
@@ -43,7 +46,6 @@ describe('ValueStreamMetrics', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
describe('with successful requests', () => {
@@ -55,7 +57,23 @@ describe('ValueStreamMetrics', () => {
it('will display a loader with pending requests', async () => {
await wrapper.vm.$nextTick();
- expect(wrapper.find(GlSkeletonLoading).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSkeletonLoading).exists()).toBe(true);
+ });
+
+ it('renders hidden GlSingleStat components for each metric', async () => {
+ await waitForPromises();
+
+ wrapper.setData({ isLoading: true });
+
+ await wrapper.vm.$nextTick();
+
+ const components = findMetrics();
+
+ expect(components).toHaveLength(metricsData.length);
+
+ metricsData.forEach((metric, index) => {
+ expect(components.at(index).isVisible()).toBe(false);
+ });
});
describe('with data loaded', () => {
@@ -67,19 +85,31 @@ describe('ValueStreamMetrics', () => {
expectToHaveRequest({ params: {} });
});
- it.each`
- index | value | title | unit
- ${0} | ${metricsData[0].value} | ${metricsData[0].title} | ${metricsData[0].unit}
- ${1} | ${metricsData[1].value} | ${metricsData[1].title} | ${metricsData[1].unit}
- ${2} | ${metricsData[2].value} | ${metricsData[2].title} | ${metricsData[2].unit}
- ${3} | ${metricsData[3].value} | ${metricsData[3].title} | ${metricsData[3].unit}
- `(
- 'renders a single stat component for the $title with value and unit',
- ({ index, value, title, unit }) => {
+ describe.each`
+ index | value | title | unit | clickable
+ ${0} | ${metricsData[0].value} | ${metricsData[0].title} | ${metricsData[0].unit} | ${false}
+ ${1} | ${metricsData[1].value} | ${metricsData[1].title} | ${metricsData[1].unit} | ${false}
+ ${2} | ${metricsData[2].value} | ${metricsData[2].title} | ${metricsData[2].unit} | ${false}
+ ${3} | ${metricsData[3].value} | ${metricsData[3].title} | ${metricsData[3].unit} | ${true}
+ `('metric tiles', ({ index, value, title, unit, clickable }) => {
+ it(`renders a single stat component for "${title}" with value and unit`, () => {
const metric = findMetrics().at(index);
expect(metric.props()).toMatchObject({ value, title, unit: unit ?? '' });
- },
- );
+ expect(metric.isVisible()).toBe(true);
+ });
+
+ it(`${
+ clickable ? 'redirects' : "doesn't redirect"
+ } when the user clicks the "${title}" metric`, () => {
+ const metric = findMetrics().at(index);
+ metric.vm.$emit('click');
+ if (clickable) {
+ expect(redirectTo).toHaveBeenCalledWith(metricsData[index].links[0].url);
+ } else {
+ expect(redirectTo).not.toHaveBeenCalled();
+ }
+ });
+ });
it('will not display a loading icon', () => {
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false);