Commit f896f449 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Update related specs

Fix jest base specs

Updates the base vue js specs for
project value stream analytics

Update value stream metrics specs

Added time metric fixtures for project vsa
to generate relevant data

Fix value stream metrics specs
parent 5a549aad
# frozen_string_literal: true
class Projects::Analytics::CycleAnalytics::SummaryController < Projects::ApplicationController
include CycleAnalyticsParams
......
......@@ -6,6 +6,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import BaseComponent from '~/cycle_analytics/components/base.vue';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import StageTable from '~/cycle_analytics/components/stage_table.vue';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants';
import initState from '~/cycle_analytics/store/state';
import {
......@@ -23,6 +24,7 @@ const selectedStageEvents = issueEvents.events;
const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access';
const selectedStageCount = stageCounts[selectedStage.id];
const fullPath = 'full/path/to/foo';
Vue.use(Vuex);
......@@ -34,6 +36,7 @@ const defaultState = {
createdBefore,
createdAfter,
stageCounts,
endpoints: { fullPath },
};
function createStore({ initialState = {}, initialGetters = {} }) {
......@@ -45,6 +48,10 @@ function createStore({ initialState = {}, initialGetters = {} }) {
},
getters: {
pathNavigationData: () => transformedProjectStagePathData,
filterParams: () => ({
created_after: createdAfter,
created_before: createdBefore,
}),
...initialGetters,
},
});
......@@ -67,7 +74,7 @@ function createComponent({ initialState, initialGetters } = {}) {
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findPathNavigation = () => wrapper.findComponent(PathNavigation);
const findOverviewMetrics = () => wrapper.findByTestId('vsa-stage-overview-metrics');
const findOverviewMetrics = () => wrapper.findComponent(ValueStreamMetrics);
const findStageTable = () => wrapper.findComponent(StageTable);
const findStageEvents = () => findStageTable().props('stageEvents');
const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('title');
......@@ -121,14 +128,14 @@ describe('Value stream analytics component', () => {
expect(findPathNavigation().props('loading')).toBe(true);
});
it('does not render the overview metrics', () => {
expect(findOverviewMetrics().exists()).toBe(false);
});
it('does not render the stage table', () => {
expect(findStageTable().exists()).toBe(false);
});
it('renders the overview metrics', () => {
expect(findOverviewMetrics().exists()).toBe(true);
});
it('renders the loading icon', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
......
......@@ -15,8 +15,11 @@ export const getStageByTitle = (stages, title) =>
const fixtureEndpoints = {
customizableCycleAnalyticsStagesAndEvents: 'projects/analytics/value_stream_analytics/stages',
stageEvents: (stage) => `projects/analytics/value_stream_analytics/events/${stage}`,
metricsData: 'projects/analytics/value_stream_analytics/summary',
};
export const metricsData = getJSONFixture(fixtureEndpoints.metricsData);
export const customizableStagesAndEvents = getJSONFixture(
fixtureEndpoints.customizableCycleAnalyticsStagesAndEvents,
);
......
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import Api from 'ee/api'; // TODO: fix this for FOSS
import { group } from 'jest/cycle_analytics/mock_data';
import waitForPromises from 'helpers/wait_for_promises';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import createFlash from '~/flash';
import { timeMetricsData, recentActivityData } from '../mock_data';
const allMetrics = [...timeMetricsData, ...recentActivityData];
import { group, metricsData } from './mock_data';
jest.mock('~/flash');
describe('ValueStreamMetrics', () => {
const { full_path: requestPath } = group;
let wrapper;
let mockGetValueStreamSummaryMetrics;
const { full_path: requestPath } = group;
const fakeReqName = 'Mock metrics';
const createComponent = ({ requestParams = {} } = {}) => {
return shallowMount(ValueStreamMetrics, {
propsData: {
requestPath,
requestParams,
requests: [{ request: mockGetValueStreamSummaryMetrics, name: fakeReqName }],
},
});
};
const findMetrics = () => wrapper.findComponent(GlSingleStat).props('metrics');
const findMetrics = () => wrapper.findAllComponents(GlSingleStat);
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('will display a loading icon if `true`', async () => {
mockGetValueStreamSummaryMetrics = jest.fn().mockResolvedValue({ data: metricsData });
wrapper = createComponent();
await wrapper.vm.$nextTick();
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(true);
});
describe('with successful requests', () => {
beforeEach(async () => {
jest.spyOn(Api, 'cycleAnalyticsTimeSummaryData').mockResolvedValue({ data: timeMetricsData });
jest.spyOn(Api, 'cycleAnalyticsSummaryData').mockResolvedValue({ data: recentActivityData });
mockGetValueStreamSummaryMetrics = jest.fn().mockResolvedValue({ data: metricsData });
wrapper = createComponent();
await nextTick;
await waitForPromises();
});
it.each(['cycleAnalyticsTimeSummaryData', 'cycleAnalyticsSummaryData'])(
'fetches data for the %s request',
(request) => {
expect(Api[request]).toHaveBeenCalledWith(requestPath, {});
},
);
it('fetches data for the `getValueStreamSummaryMetrics` request', () => {
expect(mockGetValueStreamSummaryMetrics).toHaveBeenCalledWith(requestPath, {});
});
it.each`
index | value | title | unit
${0} | ${timeMetricsData[0].value} | ${timeMetricsData[0].title} | ${timeMetricsData[0].unit}
${1} | ${timeMetricsData[1].value} | ${timeMetricsData[1].title} | ${timeMetricsData[1].unit}
${2} | ${recentActivityData[0].value} | ${recentActivityData[0].title} | ${recentActivityData[0].unit}
${3} | ${recentActivityData[1].value} | ${recentActivityData[1].title} | ${recentActivityData[1].unit}
${4} | ${recentActivityData[2].value} | ${recentActivityData[2].title} | ${recentActivityData[2].unit}
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 }) => {
const metric = findAllMetrics().at(index);
const metric = findMetrics().at(index);
const expectedUnit = unit ?? '';
expect(metric.props('value')).toBe(value);
......@@ -71,11 +74,6 @@ describe('ValueStreamMetrics', () => {
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false);
});
it('will display a loading icon if `true`', () => {
wrapper = createComponent({ isLoading: true });
expect(wrapper.find(GlSkeletonLoading).exists()).toBe(true);
});
describe('with additional params', () => {
beforeEach(async () => {
wrapper = createComponent({
......@@ -86,50 +84,30 @@ describe('ValueStreamMetrics', () => {
},
});
await nextTick;
await waitForPromises();
});
it.each(['cycleAnalyticsTimeSummaryData', 'cycleAnalyticsSummaryData'])(
'sends additional parameters as query paremeters in %s request',
(request) => {
expect(Api[request]).toHaveBeenCalledWith(requestPath, {
'project_ids[]': [1],
created_after: '2020-01-01',
created_before: '2020-02-01',
});
},
);
});
describe('metrics', () => {
it('sets the metrics component props', () => {
const metricsProps = findMetrics();
allMetrics.forEach((metric, index) => {
const currentProp = metricsProps[index];
expect(currentProp.label).toEqual(metric.title);
expect(currentProp.value).toEqual(metric.value);
expect(currentProp.unit).toEqual(metric.unit);
it('fetches data for the `getValueStreamSummaryMetrics` request', () => {
expect(mockGetValueStreamSummaryMetrics).toHaveBeenCalledWith(requestPath, {
'project_ids[]': [1],
created_after: '2020-01-01',
created_before: '2020-02-01',
});
});
});
});
describe.each`
metric | failedRequest | succesfulRequest
${'time summary'} | ${'cycleAnalyticsTimeSummaryData'} | ${'cycleAnalyticsSummaryData'}
${'recent activity'} | ${'cycleAnalyticsSummaryData'} | ${'cycleAnalyticsTimeSummaryData'}
`('with the $failedRequest request failing', ({ metric, failedRequest, succesfulRequest }) => {
describe('with a request failing', () => {
beforeEach(async () => {
jest.spyOn(Api, failedRequest).mockRejectedValue();
jest.spyOn(Api, succesfulRequest).mockResolvedValue(Promise.resolve({}));
mockGetValueStreamSummaryMetrics = jest.fn().mockRejectedValue();
wrapper = createComponent();
await wrapper.vm.$nextTick();
await waitForPromises();
});
it('it should render a error message', () => {
expect(createFlash).toHaveBeenCalledWith({
message: `There was an error while fetching value stream analytics ${metric} data.`,
message: `There was an error while fetching value stream analytics ${fakeReqName} data.`,
});
});
});
......
......@@ -51,4 +51,21 @@ RSpec.describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
end
end
end
describe Projects::Analytics::CycleAnalytics::SummaryController, type: :controller do
render_views
let(:params) { { namespace_id: group, project_id: project, value_stream_id: value_stream_id } }
before do
project.add_developer(user)
sign_in(user)
end
it "projects/analytics/value_stream_analytics/summary" do
get(:show, params: params, format: :json)
expect(response).to be_successful
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment