Commit 93b4049a authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '216279-add-source-and-destination-branch-data-to-compliance-dashboard' into 'master'

Add source and destination branch data to Compliance Dashboard

See merge request gitlab-org/gitlab!37628
parents 270f0a1b c5006455
<script>
import { GlLink, GlSprintf, GlTooltipDirective, GlTruncate } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
directives: {
GlTooltip: GlTooltipDirective,
},
components: {
GlLink,
GlSprintf,
GlTruncate,
},
props: {
sourceBranch: {
type: Object,
required: true,
},
targetBranch: {
type: Object,
required: true,
},
},
strings: {
branchDetails: __('%{sourceBranch} into %{targetBranch}'),
},
};
</script>
<template>
<div class="gl-display-flex gl-align-items-center gl-justify-content-end">
<gl-sprintf :message="this.$options.strings.branchDetails">
<template #sourceBranch>
<span class="gl-mr-2 gl-min-w-0">
<gl-link v-if="sourceBranch.uri" :href="targetBranch.uri" data-testid="source-branch-uri">
<gl-truncate
v-gl-tooltip
:title="sourceBranch.name"
:text="sourceBranch.name"
position="middle"
/>
</gl-link>
<gl-truncate
v-else
v-gl-tooltip
:title="sourceBranch.name"
:text="sourceBranch.name"
position="middle"
/>
</span>
</template>
<template #targetBranch>
<span class="gl-ml-2 gl-min-w-0">
<gl-link v-if="targetBranch.uri" :href="targetBranch.uri" data-testid="target-branch-uri">
<gl-truncate
v-gl-tooltip
:title="targetBranch.name"
:text="targetBranch.name"
position="middle"
/>
</gl-link>
<gl-truncate
v-else
v-gl-tooltip
:title="targetBranch.name"
:text="targetBranch.name"
position="middle"
/>
</span>
</template>
</gl-sprintf>
</div>
</template>
......@@ -7,6 +7,7 @@ import timeagoMixin from '~/vue_shared/mixins/timeago';
import ApprovalStatus from './approval_status.vue';
import Approvers from './approvers.vue';
import BranchDetails from './branch_details.vue';
import MergeRequest from './merge_request.vue';
import PipelineStatus from './pipeline_status.vue';
import GridColumnHeading from '../shared/grid_column_heading.vue';
......@@ -19,6 +20,7 @@ export default {
components: {
ApprovalStatus,
Approvers,
BranchDetails,
GridColumnHeading,
MergeRequest,
PipelineStatus,
......@@ -51,6 +53,9 @@ export default {
hasStatus(status) {
return !isEmpty(status);
},
hasBranchDetails(mergeRequest) {
return mergeRequest.target_branch && mergeRequest.source_branch;
},
},
strings: {
mergeRequestLabel: __('Merge Request'),
......@@ -105,6 +110,17 @@ export default {
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers :approvers="mergeRequest.approved_by_users" />
<branch-details
v-if="hasBranchDetails(mergeRequest)"
:source-branch="{
name: mergeRequest.source_branch,
uri: mergeRequest.source_branch_uri,
}"
:target-branch="{
name: mergeRequest.target_branch,
uri: mergeRequest.target_branch_uri,
}"
/>
<span class="gl-text-gray-700">
<time v-gl-tooltip.bottom="timeTooltip(mergeRequest.merged_at)">{{
timeAgoString(mergeRequest.merged_at)
......
......@@ -3,6 +3,6 @@
min-width: 550px;
.dashboard-grid {
grid-template-columns: 1fr auto auto auto;
grid-template-columns: 1fr auto auto 35%;
}
}
---
title: Add source and destination branch data to Compliance Dashboard
merge_request: 37628
author:
type: added
......@@ -49,6 +49,8 @@ exports[`MergeRequestsGrid component when intialized matches the snapshot 1`] =
approvers=""
/>
<!---->
<span
class="gl-text-gray-700"
>
......@@ -82,6 +84,8 @@ exports[`MergeRequestsGrid component when intialized matches the snapshot 1`] =
approvers=""
/>
<!---->
<span
class="gl-text-gray-700"
>
......
import { mount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import BranchDetails from 'ee/compliance_dashboard/components/merge_requests/branch_details.vue';
describe('BranchDetails component', () => {
let wrapper;
// The truncate component adds left-to-right marks into the text that we have to remove
const getText = () => wrapper.text().replace(/\u200E/gi, '');
const linkExists = testId => wrapper.find(`[data-testid="${testId}"]`).exists();
const createComponent = ({ sourceUri = '', targetUri = '' } = {}) => {
return mount(BranchDetails, {
propsData: {
sourceBranch: {
name: 'feature',
uri: sourceUri,
},
targetBranch: {
name: 'master',
uri: targetUri,
},
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('with branch details', () => {
describe('and no branch URIs', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('has no links', () => {
expect(wrapper.find(GlLink).exists()).toBe(false);
});
it('has the correct text', () => {
expect(getText()).toEqual('feature into master');
});
});
describe('and one branch URI', () => {
beforeEach(() => {
wrapper = createComponent({ targetUri: '/master-uri' });
});
it('has one link', () => {
expect(wrapper.findAll(GlLink)).toHaveLength(1);
});
it('has a link to the target branch', () => {
expect(linkExists('target-branch-uri')).toBe(true);
});
it('has the correct text', () => {
expect(getText()).toEqual('feature into master');
});
});
describe('and both branch URIs', () => {
beforeEach(() => {
wrapper = createComponent({ sourceUri: '/feature-uri', targetUri: '/master-uri' });
});
it('has two links', () => {
expect(wrapper.findAll(GlLink)).toHaveLength(2);
});
it('has a link to the source branch', () => {
expect(linkExists('source-branch-uri')).toBe(true);
});
it('has a link to the target branch', () => {
expect(linkExists('target-branch-uri')).toBe(true);
});
it('has the correct text', () => {
expect(getText()).toEqual('feature into master');
});
});
});
});
......@@ -2,9 +2,10 @@ import { shallowMount } from '@vue/test-utils';
import MergeRequestsGrid from 'ee/compliance_dashboard/components/merge_requests/grid.vue';
import ApprovalStatus from 'ee/compliance_dashboard/components/merge_requests/approval_status.vue';
import BranchDetails from 'ee/compliance_dashboard/components/merge_requests/branch_details.vue';
import PipelineStatus from 'ee/compliance_dashboard/components/merge_requests/pipeline_status.vue';
import Approvers from 'ee/compliance_dashboard/components/merge_requests/approvers.vue';
import { createMergeRequests } from '../../mock_data';
import { createMergeRequests, createPipelineStatus } from '../../mock_data';
describe('MergeRequestsGrid component', () => {
let wrapper;
......@@ -14,11 +15,12 @@ describe('MergeRequestsGrid component', () => {
const findApprovalStatus = () => wrapper.find(ApprovalStatus);
const findPipelineStatus = () => wrapper.find(PipelineStatus);
const findApprovers = () => wrapper.find(Approvers);
const findBranchDetails = () => wrapper.find(BranchDetails);
const createComponent = (options = {}) => {
const createComponent = (mergeRequestProps = {}) => {
return shallowMount(MergeRequestsGrid, {
propsData: {
mergeRequests: createMergeRequests({ count: 2, options }),
mergeRequests: createMergeRequests({ count: 2, props: mergeRequestProps }),
isLastPage: false,
},
stubs: {
......@@ -53,7 +55,7 @@ describe('MergeRequestsGrid component', () => {
});
it('renders if there is an approval status', () => {
wrapper = createComponent({ approvalStatus: 'success' });
wrapper = createComponent({ approval_status: 'success' });
expect(findApprovalStatus().exists()).toBe(true);
});
});
......@@ -64,11 +66,22 @@ describe('MergeRequestsGrid component', () => {
});
it('renders if there is a pipeline', () => {
wrapper = createComponent({ addPipeline: true });
wrapper = createComponent({ pipeline_status: createPipelineStatus('success') });
expect(findPipelineStatus().exists()).toBe(true);
});
});
describe('branch details', () => {
it('does not render if there are no branch details', () => {
expect(findBranchDetails().exists()).toBe(false);
});
it('renders if there are branch details', () => {
wrapper = createComponent({ target_branch: 'master', source_branch: 'feature' });
expect(findBranchDetails().exists()).toBe(true);
});
});
it('renders the approvers list', () => {
expect(findApprovers().exists()).toBe(true);
});
......
......@@ -13,7 +13,7 @@ const createUser = id => ({
web_url: `http://localhost:3000/user-${id}`,
});
export const createMergeRequest = ({ id = 1, pipeline, approvers, approvalStatus } = {}) => {
export const createMergeRequest = ({ id = 1, props } = {}) => {
const mergeRequest = {
id,
approved_by_users: [],
......@@ -26,19 +26,7 @@ export const createMergeRequest = ({ id = 1, pipeline, approvers, approvalStatus
mergeRequest.author = createUser(id);
if (pipeline) {
mergeRequest.pipeline_status = pipeline;
}
if (approvers) {
mergeRequest.approved_by_users = approvers;
}
if (approvalStatus) {
mergeRequest.approval_status = approvalStatus;
}
return mergeRequest;
return { ...mergeRequest, ...props };
};
export const createPipelineStatus = status => ({
......@@ -59,14 +47,13 @@ export const createApprovers = count => {
.map((_, id) => createUser(id));
};
export const createMergeRequests = ({ count = 1, options = {} } = {}) => {
export const createMergeRequests = ({ count = 1, props = {} } = {}) => {
return Array(count)
.fill()
.map((_, id) =>
createMergeRequest({
id,
approvalStatus: options.approvalStatus,
pipeline: options.addPipeline ? createPipelineStatus('success') : null,
props,
}),
);
};
......@@ -670,6 +670,9 @@ msgstr ""
msgid "%{size} bytes"
msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
......
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