Commit 6e61ded5 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch 'nfriend-restructure-pipeline-charts' into 'master'

[RUN AS-IF-FOSS] Restructure pipeline charts: follow-up to !51517

See merge request gitlab-org/gitlab!52511
parents 3aa4a9d2 44b9f494
<script> <script>
import { GlAlert, GlTabs, GlTab } from '@gitlab/ui'; import { GlTabs, GlTab } from '@gitlab/ui';
import { s__ } from '~/locale';
import getPipelineCountByStatus from '../graphql/queries/get_pipeline_count_by_status.query.graphql';
import getProjectPipelineStatistics from '../graphql/queries/get_project_pipeline_statistics.query.graphql';
import PipelineCharts from './pipeline_charts.vue'; import PipelineCharts from './pipeline_charts.vue';
import { mergeUrlParams, updateHistory, getParameterValues } from '~/lib/utils/url_utility'; import { mergeUrlParams, updateHistory, getParameterValues } from '~/lib/utils/url_utility';
import {
DEFAULT,
LOAD_ANALYTICS_FAILURE,
LOAD_PIPELINES_FAILURE,
PARSE_FAILURE,
UNSUPPORTED_DATA,
} from '../constants';
const defaultAnalyticsValues = {
weekPipelinesTotals: [],
weekPipelinesLabels: [],
weekPipelinesSuccessful: [],
monthPipelinesLabels: [],
monthPipelinesTotals: [],
monthPipelinesSuccessful: [],
yearPipelinesLabels: [],
yearPipelinesTotals: [],
yearPipelinesSuccessful: [],
pipelineTimesLabels: [],
pipelineTimesValues: [],
};
const defaultCountValues = {
totalPipelines: {
count: 0,
},
successfulPipelines: {
count: 0,
},
};
const charts = ['pipelines', 'deployments']; const charts = ['pipelines', 'deployments'];
export default { export default {
components: { components: {
GlAlert,
GlTabs, GlTabs,
GlTab, GlTab,
PipelineCharts, PipelineCharts,
...@@ -53,10 +18,6 @@ export default { ...@@ -53,10 +18,6 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
projectPath: {
type: String,
default: '',
},
}, },
data() { data() {
const [chart] = getParameterValues('chart') || charts; const [chart] = getParameterValues('chart') || charts;
...@@ -64,169 +25,27 @@ export default { ...@@ -64,169 +25,27 @@ export default {
return { return {
chart, chart,
selectedTab: tab >= 0 ? tab : 0, selectedTab: tab >= 0 ? tab : 0,
showFailureAlert: false,
failureType: null,
analytics: { ...defaultAnalyticsValues },
counts: { ...defaultCountValues },
}; };
}, },
apollo: {
counts: {
query: getPipelineCountByStatus,
variables() {
return {
projectPath: this.projectPath,
};
},
update(data) {
return data?.project;
},
error() {
this.reportFailure(LOAD_PIPELINES_FAILURE);
},
},
analytics: {
query: getProjectPipelineStatistics,
variables() {
return {
projectPath: this.projectPath,
};
},
update(data) {
return data?.project?.pipelineAnalytics;
},
error() {
this.reportFailure(LOAD_ANALYTICS_FAILURE);
},
},
},
computed: {
failure() {
switch (this.failureType) {
case LOAD_ANALYTICS_FAILURE:
return {
text: this.$options.errorTexts[LOAD_ANALYTICS_FAILURE],
variant: 'danger',
};
case PARSE_FAILURE:
return {
text: this.$options.errorTexts[PARSE_FAILURE],
variant: 'danger',
};
case UNSUPPORTED_DATA:
return {
text: this.$options.errorTexts[UNSUPPORTED_DATA],
variant: 'info',
};
default:
return {
text: this.$options.errorTexts[DEFAULT],
variant: 'danger',
};
}
},
lastWeekChartData() {
return {
labels: this.analytics.weekPipelinesLabels,
totals: this.analytics.weekPipelinesTotals,
success: this.analytics.weekPipelinesSuccessful,
};
},
lastMonthChartData() {
return {
labels: this.analytics.monthPipelinesLabels,
totals: this.analytics.monthPipelinesTotals,
success: this.analytics.monthPipelinesSuccessful,
};
},
lastYearChartData() {
return {
labels: this.analytics.yearPipelinesLabels,
totals: this.analytics.yearPipelinesTotals,
success: this.analytics.yearPipelinesSuccessful,
};
},
timesChartData() {
return {
labels: this.analytics.pipelineTimesLabels,
values: this.analytics.pipelineTimesValues,
};
},
successRatio() {
const { successfulPipelines, failedPipelines } = this.counts;
const successfulCount = successfulPipelines?.count;
const failedCount = failedPipelines?.count;
const ratio = (successfulCount / (successfulCount + failedCount)) * 100;
return failedCount === 0 ? 100 : ratio;
},
formattedCounts() {
const { totalPipelines, successfulPipelines, failedPipelines } = this.counts;
return {
total: totalPipelines?.count,
success: successfulPipelines?.count,
failed: failedPipelines?.count,
successRatio: this.successRatio,
};
},
},
methods: { methods: {
hideAlert() {
this.showFailureAlert = false;
},
reportFailure(type) {
this.showFailureAlert = true;
this.failureType = type;
},
onTabChange(index) { onTabChange(index) {
this.selectedTab = index; this.selectedTab = index;
const path = mergeUrlParams({ chart: charts[index] }, window.location.pathname); const path = mergeUrlParams({ chart: charts[index] }, window.location.pathname);
updateHistory({ url: path }); updateHistory({ url: path });
}, },
}, },
errorTexts: {
[LOAD_ANALYTICS_FAILURE]: s__(
'PipelineCharts|An error has ocurred when retrieving the analytics data',
),
[LOAD_PIPELINES_FAILURE]: s__(
'PipelineCharts|An error has ocurred when retrieving the pipelines data',
),
[PARSE_FAILURE]: s__('PipelineCharts|There was an error parsing the data for the charts.'),
[DEFAULT]: s__('PipelineCharts|An unknown error occurred while processing CI/CD analytics.'),
},
}; };
</script> </script>
<template> <template>
<div> <div>
<gl-alert v-if="showFailureAlert" :variant="failure.variant" @dismiss="hideAlert">{{
failure.text
}}</gl-alert>
<gl-tabs v-if="shouldRenderDeploymentFrequencyCharts" :value="selectedTab" @input="onTabChange"> <gl-tabs v-if="shouldRenderDeploymentFrequencyCharts" :value="selectedTab" @input="onTabChange">
<gl-tab :title="__('Pipelines')"> <gl-tab :title="__('Pipelines')">
<pipeline-charts <pipeline-charts />
:counts="formattedCounts"
:last-week="lastWeekChartData"
:last-month="lastMonthChartData"
:last-year="lastYearChartData"
:times-chart="timesChartData"
:loading="$apollo.queries.counts.loading"
@report-failure="reportFailure"
/>
</gl-tab> </gl-tab>
<gl-tab :title="__('Deployments')"> <gl-tab :title="__('Deployments')">
<deployment-frequency-charts /> <deployment-frequency-charts />
</gl-tab> </gl-tab>
</gl-tabs> </gl-tabs>
<pipeline-charts <pipeline-charts v-else />
v-else
:counts="formattedCounts"
:last-week="lastWeekChartData"
:last-month="lastMonthChartData"
:last-year="lastYearChartData"
:times-chart="timesChartData"
:loading="$apollo.queries.counts.loading"
@report-failure="reportFailure"
/>
</div> </div>
</template> </template>
<script> <script>
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import { GlColumnChart } from '@gitlab/ui/dist/charts'; import { GlColumnChart } from '@gitlab/ui/dist/charts';
import { GlSkeletonLoader } from '@gitlab/ui'; import { GlAlert, GlSkeletonLoader } from '@gitlab/ui';
import getPipelineCountByStatus from '../graphql/queries/get_pipeline_count_by_status.query.graphql';
import getProjectPipelineStatistics from '../graphql/queries/get_project_pipeline_statistics.query.graphql';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { getDateInPast } from '~/lib/utils/datetime_utility'; import { getDateInPast } from '~/lib/utils/datetime_utility';
import { import {
DEFAULT,
CHART_CONTAINER_HEIGHT, CHART_CONTAINER_HEIGHT,
CHART_DATE_FORMAT, CHART_DATE_FORMAT,
INNER_CHART_HEIGHT, INNER_CHART_HEIGHT,
...@@ -13,51 +16,167 @@ import { ...@@ -13,51 +16,167 @@ import {
X_AXIS_LABEL_ROTATION, X_AXIS_LABEL_ROTATION,
X_AXIS_TITLE_OFFSET, X_AXIS_TITLE_OFFSET,
PARSE_FAILURE, PARSE_FAILURE,
LOAD_ANALYTICS_FAILURE,
LOAD_PIPELINES_FAILURE,
UNSUPPORTED_DATA,
} from '../constants'; } from '../constants';
import StatisticsList from './statistics_list.vue'; import StatisticsList from './statistics_list.vue';
import CiCdAnalyticsAreaChart from './ci_cd_analytics_area_chart.vue'; import CiCdAnalyticsAreaChart from './ci_cd_analytics_area_chart.vue';
const defaultAnalyticsValues = {
weekPipelinesTotals: [],
weekPipelinesLabels: [],
weekPipelinesSuccessful: [],
monthPipelinesLabels: [],
monthPipelinesTotals: [],
monthPipelinesSuccessful: [],
yearPipelinesLabels: [],
yearPipelinesTotals: [],
yearPipelinesSuccessful: [],
pipelineTimesLabels: [],
pipelineTimesValues: [],
};
const defaultCountValues = {
totalPipelines: {
count: 0,
},
successfulPipelines: {
count: 0,
},
};
export default { export default {
components: { components: {
GlAlert,
GlColumnChart, GlColumnChart,
GlSkeletonLoader, GlSkeletonLoader,
StatisticsList, StatisticsList,
CiCdAnalyticsAreaChart, CiCdAnalyticsAreaChart,
}, },
props: { inject: {
projectPath: {
type: String,
default: '',
},
},
data() {
return {
showFailureAlert: false,
failureType: null,
analytics: { ...defaultAnalyticsValues },
counts: { ...defaultCountValues },
};
},
apollo: {
counts: { counts: {
required: true, query: getPipelineCountByStatus,
type: Object, variables() {
return {
projectPath: this.projectPath,
};
},
update(data) {
return data?.project;
},
error() {
this.reportFailure(LOAD_PIPELINES_FAILURE);
},
}, },
loading: { analytics: {
required: false, query: getProjectPipelineStatistics,
default: false, variables() {
type: Boolean, return {
projectPath: this.projectPath,
};
},
update(data) {
return data?.project?.pipelineAnalytics;
},
error() {
this.reportFailure(LOAD_ANALYTICS_FAILURE);
},
}, },
lastWeek: { },
required: true, computed: {
type: Object, loading() {
return this.$apollo.queries.counts.loading;
}, },
lastMonth: { failure() {
required: true, switch (this.failureType) {
type: Object, case LOAD_ANALYTICS_FAILURE:
return {
text: this.$options.errorTexts[LOAD_ANALYTICS_FAILURE],
variant: 'danger',
};
case PARSE_FAILURE:
return {
text: this.$options.errorTexts[PARSE_FAILURE],
variant: 'danger',
};
case UNSUPPORTED_DATA:
return {
text: this.$options.errorTexts[UNSUPPORTED_DATA],
variant: 'info',
};
default:
return {
text: this.$options.errorTexts[DEFAULT],
variant: 'danger',
};
}
}, },
lastYear: { lastWeekChartData() {
required: true, return {
type: Object, labels: this.analytics.weekPipelinesLabels,
totals: this.analytics.weekPipelinesTotals,
success: this.analytics.weekPipelinesSuccessful,
};
}, },
timesChart: { lastMonthChartData() {
required: true, return {
type: Object, labels: this.analytics.monthPipelinesLabels,
totals: this.analytics.monthPipelinesTotals,
success: this.analytics.monthPipelinesSuccessful,
};
},
lastYearChartData() {
return {
labels: this.analytics.yearPipelinesLabels,
totals: this.analytics.yearPipelinesTotals,
success: this.analytics.yearPipelinesSuccessful,
};
},
timesChartData() {
return {
labels: this.analytics.pipelineTimesLabels,
values: this.analytics.pipelineTimesValues,
};
},
successRatio() {
const { successfulPipelines, failedPipelines } = this.counts;
const successfulCount = successfulPipelines?.count;
const failedCount = failedPipelines?.count;
const ratio = (successfulCount / (successfulCount + failedCount)) * 100;
return failedCount === 0 ? 100 : ratio;
},
formattedCounts() {
const { totalPipelines, successfulPipelines, failedPipelines } = this.counts;
return {
total: totalPipelines?.count,
success: successfulPipelines?.count,
failed: failedPipelines?.count,
successRatio: this.successRatio,
};
}, },
},
computed: {
areaCharts() { areaCharts() {
const { lastWeek, lastMonth, lastYear } = this.$options.chartTitles; const { lastWeek, lastMonth, lastYear } = this.$options.chartTitles;
const charts = [ const charts = [
{ title: lastWeek, data: this.lastWeek }, { title: lastWeek, data: this.lastWeekChartData },
{ title: lastMonth, data: this.lastMonth }, { title: lastMonth, data: this.lastMonthChartData },
{ title: lastYear, data: this.lastYear }, { title: lastYear, data: this.lastYearChartData },
]; ];
let areaChartsData = []; let areaChartsData = [];
...@@ -65,7 +184,7 @@ export default { ...@@ -65,7 +184,7 @@ export default {
areaChartsData = charts.map(this.buildAreaChartData); areaChartsData = charts.map(this.buildAreaChartData);
} catch { } catch {
areaChartsData = []; areaChartsData = [];
this.vm.$emit('report-failure', PARSE_FAILURE); this.reportFailure(PARSE_FAILURE);
} }
return areaChartsData; return areaChartsData;
...@@ -74,12 +193,19 @@ export default { ...@@ -74,12 +193,19 @@ export default {
return [ return [
{ {
name: 'full', name: 'full',
data: this.mergeLabelsAndValues(this.timesChart.labels, this.timesChart.values), data: this.mergeLabelsAndValues(this.timesChartData.labels, this.timesChartData.values),
}, },
]; ];
}, },
}, },
methods: { methods: {
hideAlert() {
this.showFailureAlert = false;
},
reportFailure(type) {
this.showFailureAlert = true;
this.failureType = type;
},
mergeLabelsAndValues(labels, values) { mergeLabelsAndValues(labels, values) {
return labels.map((label, index) => [label, values[index]]); return labels.map((label, index) => [label, values[index]]);
}, },
...@@ -121,6 +247,16 @@ export default { ...@@ -121,6 +247,16 @@ export default {
minInterval: 1, minInterval: 1,
}, },
}, },
errorTexts: {
[LOAD_ANALYTICS_FAILURE]: s__(
'PipelineCharts|An error has ocurred when retrieving the analytics data',
),
[LOAD_PIPELINES_FAILURE]: s__(
'PipelineCharts|An error has ocurred when retrieving the pipelines data',
),
[PARSE_FAILURE]: s__('PipelineCharts|There was an error parsing the data for the charts.'),
[DEFAULT]: s__('PipelineCharts|An unknown error occurred while processing CI/CD analytics.'),
},
get chartTitles() { get chartTitles() {
const today = dateFormat(new Date(), CHART_DATE_FORMAT); const today = dateFormat(new Date(), CHART_DATE_FORMAT);
const pastDate = (timeScale) => const pastDate = (timeScale) =>
...@@ -141,6 +277,9 @@ export default { ...@@ -141,6 +277,9 @@ export default {
</script> </script>
<template> <template>
<div> <div>
<gl-alert v-if="showFailureAlert" :variant="failure.variant" @dismiss="hideAlert">{{
failure.text
}}</gl-alert>
<div class="gl-mb-3"> <div class="gl-mb-3">
<h3>{{ s__('PipelineCharts|CI / CD Analytics') }}</h3> <h3>{{ s__('PipelineCharts|CI / CD Analytics') }}</h3>
</div> </div>
...@@ -148,7 +287,7 @@ export default { ...@@ -148,7 +287,7 @@ export default {
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<gl-skeleton-loader v-if="loading" :lines="5" /> <gl-skeleton-loader v-if="loading" :lines="5" />
<statistics-list v-else :counts="counts" /> <statistics-list v-else :counts="formattedCounts" />
</div> </div>
<div v-if="!loading" class="col-md-6"> <div v-if="!loading" class="col-md-6">
<strong>{{ __('Duration for the last 30 commits') }}</strong> <strong>{{ __('Duration for the last 30 commits') }}</strong>
......
import { merge } from 'lodash'; import { merge } from 'lodash';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import { GlTabs, GlTab } from '@gitlab/ui'; import { GlTabs, GlTab } from '@gitlab/ui';
import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper'; import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { mergeUrlParams, updateHistory, getParameterValues } from '~/lib/utils/url_utility'; import { mergeUrlParams, updateHistory, getParameterValues } from '~/lib/utils/url_utility';
import Component from '~/projects/pipelines/charts/components/app.vue'; import Component from '~/projects/pipelines/charts/components/app.vue';
import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue'; import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue';
import getPipelineCountByStatus from '~/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql';
import getProjectPipelineStatistics from '~/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql';
import { mockPipelineCount, mockPipelineStatistics } from '../mock_data';
jest.mock('~/lib/utils/url_utility'); jest.mock('~/lib/utils/url_utility');
const projectPath = 'gitlab-org/gitlab';
const localVue = createLocalVue();
localVue.use(VueApollo);
const DeploymentFrequencyChartsStub = { name: 'DeploymentFrequencyCharts', render: () => {} }; const DeploymentFrequencyChartsStub = { name: 'DeploymentFrequencyCharts', render: () => {} };
describe('ProjectsPipelinesChartsApp', () => { describe('ProjectsPipelinesChartsApp', () => {
let wrapper; let wrapper;
function createMockApolloProvider() {
const requestHandlers = [
[getPipelineCountByStatus, jest.fn().mockResolvedValue(mockPipelineCount)],
[getProjectPipelineStatistics, jest.fn().mockResolvedValue(mockPipelineStatistics)],
];
return createMockApollo(requestHandlers);
}
function createComponent(mountOptions = {}) { function createComponent(mountOptions = {}) {
wrapper = shallowMount( wrapper = shallowMount(
Component, Component,
...@@ -39,11 +21,8 @@ describe('ProjectsPipelinesChartsApp', () => { ...@@ -39,11 +21,8 @@ describe('ProjectsPipelinesChartsApp', () => {
{}, {},
{ {
provide: { provide: {
projectPath,
shouldRenderDeploymentFrequencyCharts: false, shouldRenderDeploymentFrequencyCharts: false,
}, },
localVue,
apolloProvider: createMockApolloProvider(),
stubs: { stubs: {
DeploymentFrequencyCharts: DeploymentFrequencyChartsStub, DeploymentFrequencyCharts: DeploymentFrequencyChartsStub,
}, },
...@@ -62,52 +41,15 @@ describe('ProjectsPipelinesChartsApp', () => { ...@@ -62,52 +41,15 @@ describe('ProjectsPipelinesChartsApp', () => {
wrapper = null; wrapper = null;
}); });
describe('pipelines charts', () => {
it('displays the pipeline charts', () => {
const chart = wrapper.find(PipelineCharts);
const analytics = mockPipelineStatistics.data.project.pipelineAnalytics;
const {
totalPipelines: total,
successfulPipelines: success,
failedPipelines: failed,
} = mockPipelineCount.data.project;
expect(chart.exists()).toBe(true);
expect(chart.props()).toMatchObject({
counts: {
failed: failed.count,
success: success.count,
total: total.count,
successRatio: (success.count / (success.count + failed.count)) * 100,
},
lastWeek: {
labels: analytics.weekPipelinesLabels,
totals: analytics.weekPipelinesTotals,
success: analytics.weekPipelinesSuccessful,
},
lastMonth: {
labels: analytics.monthPipelinesLabels,
totals: analytics.monthPipelinesTotals,
success: analytics.monthPipelinesSuccessful,
},
lastYear: {
labels: analytics.yearPipelinesLabels,
totals: analytics.yearPipelinesTotals,
success: analytics.yearPipelinesSuccessful,
},
timesChart: {
labels: analytics.pipelineTimesLabels,
values: analytics.pipelineTimesValues,
},
});
});
});
const findDeploymentFrequencyCharts = () => wrapper.find(DeploymentFrequencyChartsStub);
const findGlTabs = () => wrapper.find(GlTabs); const findGlTabs = () => wrapper.find(GlTabs);
const findAllGlTab = () => wrapper.findAll(GlTab); const findAllGlTab = () => wrapper.findAll(GlTab);
const findGlTabAt = (i) => findAllGlTab().at(i); const findGlTabAt = (i) => findAllGlTab().at(i);
const findDeploymentFrequencyCharts = () => wrapper.find(DeploymentFrequencyChartsStub);
const findPipelineCharts = () => wrapper.find(PipelineCharts);
it('renders the pipeline charts', () => {
expect(findPipelineCharts().exists()).toBe(true);
});
describe('when shouldRenderDeploymentFrequencyCharts is true', () => { describe('when shouldRenderDeploymentFrequencyCharts is true', () => {
beforeEach(() => { beforeEach(() => {
......
import { shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import { GlColumnChart } from '@gitlab/ui/dist/charts'; import { GlColumnChart } from '@gitlab/ui/dist/charts';
import createMockApollo from 'helpers/mock_apollo_helper';
import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue'; import StatisticsList from '~/projects/pipelines/charts/components/statistics_list.vue';
import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue'; import CiCdAnalyticsAreaChart from '~/projects/pipelines/charts/components/ci_cd_analytics_area_chart.vue';
import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue'; import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue';
import { import getPipelineCountByStatus from '~/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql';
counts, import getProjectPipelineStatistics from '~/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql';
timesChartData as timesChart, import { mockPipelineCount, mockPipelineStatistics } from '../mock_data';
areaChartData as lastWeek,
areaChartData as lastMonth,
lastYearChartData as lastYear,
} from '../mock_data';
describe('ProjectsPipelinesChartsApp', () => { const projectPath = 'gitlab-org/gitlab';
const localVue = createLocalVue();
localVue.use(VueApollo);
describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
let wrapper; let wrapper;
function createMockApolloProvider() {
const requestHandlers = [
[getPipelineCountByStatus, jest.fn().mockResolvedValue(mockPipelineCount)],
[getProjectPipelineStatistics, jest.fn().mockResolvedValue(mockPipelineStatistics)],
];
return createMockApollo(requestHandlers);
}
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(PipelineCharts, { wrapper = shallowMount(PipelineCharts, {
propsData: {
counts,
timesChart,
lastWeek,
lastMonth,
lastYear,
},
provide: { provide: {
projectPath: 'test/project', projectPath,
shouldRenderDeploymentFrequencyCharts: true,
},
stubs: {
DeploymentFrequencyCharts: true,
}, },
localVue,
apolloProvider: createMockApolloProvider(),
}); });
}); });
...@@ -43,7 +45,12 @@ describe('ProjectsPipelinesChartsApp', () => { ...@@ -43,7 +45,12 @@ describe('ProjectsPipelinesChartsApp', () => {
const list = wrapper.find(StatisticsList); const list = wrapper.find(StatisticsList);
expect(list.exists()).toBe(true); expect(list.exists()).toBe(true);
expect(list.props('counts')).toBe(counts); expect(list.props('counts')).toEqual({
total: 34,
success: 23,
failed: 1,
successRatio: (23 / (23 + 1)) * 100,
});
}); });
it('displays the commit duration chart', () => { it('displays the commit duration chart', () => {
......
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