Commit 5beac427 authored by Nathan Friend's avatar Nathan Friend

Merge branch 'nfriend-add-mr-stats-to-release-progress-view' into 'master'

Add merge request stats to release progress view

See merge request gitlab-org/gitlab!46080
parents 2cfd792a a3458c7a
......@@ -54,9 +54,7 @@ export default {
</script>
<template>
<div
class="gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5 js-issues-container"
>
<div class="gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5">
<span class="gl-mb-2">
{{ label }}
<gl-badge variant="muted" size="sm">{{ total }}</gl-badge>
......
<script>
import { GlProgressBar, GlLink, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { sum } from 'lodash';
import { __, n__, sprintf } from '~/locale';
import { MAX_MILESTONES_TO_DISPLAY } from '../constants';
import IssuableStats from './issuable_stats.vue';
......@@ -31,6 +30,21 @@ export default {
required: false,
default: '',
},
openMergeRequestsPath: {
type: String,
required: false,
default: '',
},
mergedMergeRequestsPath: {
type: String,
required: false,
default: '',
},
closedMergeRequestsPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
......@@ -45,17 +59,45 @@ export default {
});
},
percentComplete() {
const percent = Math.round((this.closedIssuesCount / this.totalIssuesCount) * 100);
const percent = Math.round((this.issueCounts.closed / this.issueCounts.total) * 100);
return Number.isNaN(percent) ? 0 : percent;
},
allIssueStats() {
return this.milestones.map(m => m.issueStats || {});
issueCounts() {
return this.milestones
.map(m => m.issueStats || {})
.reduce(
(acc, current) => {
acc.total += current.total || 0;
acc.closed += current.closed || 0;
return acc;
},
{
total: 0,
closed: 0,
},
);
},
totalIssuesCount() {
return sum(this.allIssueStats.map(stats => stats.total || 0));
showMergeRequestStats() {
return this.milestones.some(m => m.mrStats);
},
closedIssuesCount() {
return sum(this.allIssueStats.map(stats => stats.closed || 0));
mergeRequestCounts() {
return this.milestones
.map(m => m.mrStats || {})
.reduce(
(acc, current) => {
acc.total += current.total || 0;
acc.merged += current.merged || 0;
acc.closed += current.closed || 0;
return acc;
},
{
total: 0,
merged: 0,
closed: 0,
},
);
},
milestoneLabelText() {
return n__('Milestone', 'Milestones', this.milestones.length);
......@@ -98,7 +140,7 @@ export default {
>
<span class="gl-mb-3">{{ percentCompleteText }}</span>
<span class="gl-w-full">
<gl-progress-bar :value="closedIssuesCount" :max="totalIssuesCount" variant="success" />
<gl-progress-bar :value="issueCounts.closed" :max="issueCounts.total" variant="success" />
</span>
</div>
<div
......@@ -129,10 +171,22 @@ export default {
</div>
<issuable-stats
:label="__('Issues')"
:total="totalIssuesCount"
:closed="closedIssuesCount"
:total="issueCounts.total"
:closed="issueCounts.closed"
:open-path="openIssuesPath"
:closed-path="closedIssuesPath"
data-testid="issue-stats"
/>
<issuable-stats
v-if="showMergeRequestStats"
:label="__('Merge Requests')"
:total="mergeRequestCounts.total"
:merged="mergeRequestCounts.merged"
:closed="mergeRequestCounts.closed"
:open-path="openMergeRequestsPath"
:merged-path="mergedMergeRequestsPath"
:closed-path="closedMergeRequestsPath"
data-testid="merge-request-stats"
/>
</div>
</template>
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`~/releases/components/issuable_stats.vue matches snapshot 1`] = `
"<div class=\\"gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5 js-issues-container\\"><span class=\\"gl-mb-2\\">
"<div class=\\"gl-display-flex gl-flex-direction-column gl-flex-shrink-0 gl-mr-6 gl-mb-5\\"><span class=\\"gl-mb-2\\">
Items
<span class=\\"badge badge-muted badge-pill gl-badge sm\\"><!----> 10</span></span>
<div class=\\"gl-display-flex\\"><span data-testid=\\"open-stat\\" class=\\"gl-white-space-pre-wrap\\">Open: <a href=\\"path/to/open/items\\" class=\\"gl-link\\">1</a></span> <span class=\\"gl-mx-2\\">•</span> <span data-testid=\\"merged-stat\\" class=\\"gl-white-space-pre-wrap\\">Merged: <a href=\\"path/to/merged/items\\" class=\\"gl-link\\">7</a></span> <span class=\\"gl-mx-2\\">•</span> <span data-testid=\\"closed-stat\\" class=\\"gl-white-space-pre-wrap\\">Closed: <a href=\\"path/to/closed/items\\" class=\\"gl-link\\">2</a></span></div>
......
......@@ -31,7 +31,8 @@ describe('Release block milestone info', () => {
const milestoneProgressBarContainer = () => wrapper.find('.js-milestone-progress-bar-container');
const milestoneListContainer = () => wrapper.find('.js-milestone-list-container');
const issuesContainer = () => wrapper.find('.js-issues-container');
const issuesContainer = () => wrapper.find('[data-testid="issue-stats"]');
const mergeRequestsContainer = () => wrapper.find('[data-testid="merge-request-stats"]');
describe('with default props', () => {
beforeEach(() => factory({ milestones }));
......@@ -187,4 +188,33 @@ describe('Release block milestone info', () => {
expectAllZeros();
});
describe('if the API response is missing the "mr_stats" property', () => {
beforeEach(() => factory({ milestones }));
it('does not render merge request stats', () => {
expect(mergeRequestsContainer().exists()).toBe(false);
});
});
describe('if the API response includes the "mr_stats" property', () => {
beforeEach(() => {
milestones = milestones.map(m => ({
...m,
mrStats: {
total: 15,
merged: 12,
closed: 1,
},
}));
return factory({ milestones });
});
it('renders merge request stats', () => {
expect(trimText(mergeRequestsContainer().text())).toBe(
'Merge Requests 30 Open: 4 • Merged: 24 • Closed: 2',
);
});
});
});
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