Commit 0838bbc8 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch '220328-refactor-compliance-dashboard-merge-requests' into 'master'

Refactor compliance dashboard merge requests

See merge request gitlab-org/gitlab!38168
parents 04a53f93 b6f66433
<script>
import { GlTabs, GlTab, GlTooltipDirective } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { sprintf, __, s__ } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import ApprovalStatus from './approval_status.vue';
import Approvers from './approvers.vue';
import Cookies from 'js-cookie';
import { GlTabs, GlTab } from '@gitlab/ui';
import { __ } from '~/locale';
import MergeRequestsGrid from './merge_requests/grid.vue';
import EmptyState from './empty_state.vue';
import MergeRequest from './merge_request.vue';
import Pagination from './pagination.vue';
import PipelineStatus from './pipeline_status.vue';
import GridColumnHeading from './grid_column_heading.vue';
import { COMPLIANCE_TAB_COOKIE_KEY } from '../constants';
export default {
name: 'ComplianceDashboard',
directives: {
GlTooltip: GlTooltipDirective,
},
components: {
ApprovalStatus,
Approvers,
MergeRequestsGrid,
EmptyState,
GridColumnHeading,
MergeRequest,
Pagination,
PipelineStatus,
GlTab,
GlTabs,
},
mixins: [timeagoMixin],
props: {
emptyStateSvgPath: {
type: String,
......@@ -51,28 +35,13 @@ export default {
},
},
methods: {
key(id, value) {
return `${id}-${value}`;
},
timeAgoString(mergedAt) {
return sprintf(s__('merged %{timeAgo}'), {
timeAgo: this.timeFormatted(mergedAt),
});
},
timeTooltip(mergedAt) {
return this.tooltipTitle(mergedAt);
},
hasStatus(status) {
return !isEmpty(status);
showTabs() {
return Cookies.get(COMPLIANCE_TAB_COOKIE_KEY) === 'true';
},
},
strings: {
heading: __('Compliance Dashboard'),
subheading: __('Here you will find recent merge request activity'),
mergeRequestLabel: __('Merge Request'),
approvalStatusLabel: __('Approval Status'),
pipelineStatusLabel: __('Pipeline'),
updatesLabel: __('Updates'),
mergeRequestsTabLabel: __('Merge Requests'),
},
};
......@@ -84,61 +53,16 @@ export default {
<h4>{{ $options.strings.heading }}</h4>
<p>{{ $options.strings.subheading }}</p>
</header>
<gl-tabs>
<gl-tabs v-if="showTabs()">
<gl-tab>
<template #title>
<span>{{ $options.strings.mergeRequestsTabLabel }}</span>
</template>
<div class="dashboard-grid">
<grid-column-heading :heading="$options.strings.mergeRequestLabel" />
<grid-column-heading
:heading="$options.strings.approvalStatusLabel"
class="gl-text-center"
/>
<grid-column-heading
:heading="$options.strings.pipelineStatusLabel"
class="gl-text-center"
/>
<grid-column-heading :heading="$options.strings.updatesLabel" class="gl-text-right" />
<template v-for="mergeRequest in mergeRequests">
<merge-request :key="key(mergeRequest.id, 'MR')" :merge-request="mergeRequest" />
<div
:key="key(mergeRequest.id, 'approvalStatus')"
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<approval-status
v-if="hasStatus(mergeRequest.approval_status)"
:status="mergeRequest.approval_status"
/>
</div>
<div
:key="key(mergeRequest.id, 'pipeline')"
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<pipeline-status
v-if="hasStatus(mergeRequest.pipeline_status)"
:status="mergeRequest.pipeline_status"
/>
</div>
<div
:key="key(mergeRequest.id, 'updates')"
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" />
<span class="gl-text-gray-700">
<time v-gl-tooltip.bottom="timeTooltip(mergeRequest.merged_at)">{{
timeAgoString(mergeRequest.merged_at)
}}</time>
</span>
</div>
</template>
</div>
<pagination :is-last-page="isLastPage" />
<merge-requests-grid :merge-requests="mergeRequests" :is-last-page="isLastPage" />
</gl-tab>
</gl-tabs>
<merge-requests-grid v-else :merge-requests="mergeRequests" :is-last-page="isLastPage" />
</div>
<empty-state v-else :image-path="emptyStateSvgPath" />
</template>
<script>
import { sprintf, __ } from '~/locale';
import { GlAvatarLink, GlAvatar, GlAvatarsInline, GlTooltipDirective } from '@gitlab/ui';
import { PRESENTABLE_APPROVERS_LIMIT } from '../constants';
import { PRESENTABLE_APPROVERS_LIMIT } from '../../constants';
export default {
directives: {
......
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { sprintf, __ } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import ApprovalStatus from './approval_status.vue';
import Approvers from './approvers.vue';
import MergeRequest from './merge_request.vue';
import PipelineStatus from './pipeline_status.vue';
import GridColumnHeading from '../shared/grid_column_heading.vue';
import Pagination from '../shared/pagination.vue';
export default {
directives: {
GlTooltip: GlTooltipDirective,
},
components: {
ApprovalStatus,
Approvers,
GridColumnHeading,
MergeRequest,
PipelineStatus,
Pagination,
},
mixins: [timeagoMixin],
props: {
mergeRequests: {
type: Array,
required: true,
},
isLastPage: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
key(id, value) {
return `${id}-${value}`;
},
timeAgoString(mergedAt) {
return sprintf(__('merged %{timeAgo}'), {
timeAgo: this.timeFormatted(mergedAt),
});
},
timeTooltip(mergedAt) {
return this.tooltipTitle(mergedAt);
},
hasStatus(status) {
return !isEmpty(status);
},
},
strings: {
mergeRequestLabel: __('Merge Request'),
approvalStatusLabel: __('Approval Status'),
pipelineStatusLabel: __('Pipeline'),
updatesLabel: __('Updates'),
},
keyTypes: {
mergeRequest: 'MR',
approvalStatus: 'approvalStatus',
pipeline: 'pipeline',
updates: 'updates',
},
};
</script>
<template>
<div>
<div class="dashboard-grid">
<grid-column-heading :heading="$options.strings.mergeRequestLabel" />
<grid-column-heading :heading="$options.strings.approvalStatusLabel" class="gl-text-center" />
<grid-column-heading :heading="$options.strings.pipelineStatusLabel" class="gl-text-center" />
<grid-column-heading :heading="$options.strings.updatesLabel" class="gl-text-right" />
<template v-for="mergeRequest in mergeRequests">
<merge-request
:key="key(mergeRequest.id, $options.keyTypes.mergeRequest)"
:merge-request="mergeRequest"
/>
<div
:key="key(mergeRequest.id, $options.keyTypes.approvalStatus)"
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<approval-status
v-if="hasStatus(mergeRequest.approval_status)"
:status="mergeRequest.approval_status"
/>
</div>
<div
:key="key(mergeRequest.id, $options.keyTypes.pipeline)"
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<pipeline-status
v-if="hasStatus(mergeRequest.pipeline_status)"
:status="mergeRequest.pipeline_status"
/>
</div>
<div
:key="key(mergeRequest.id, $options.keyTypes.updates)"
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" />
<span class="gl-text-gray-700">
<time v-gl-tooltip.bottom="timeTooltip(mergeRequest.merged_at)">{{
timeAgoString(mergeRequest.merged_at)
}}</time>
</span>
</div>
</template>
</div>
<pagination class="gl-mt-5" :is-last-page="isLastPage" />
</div>
</template>
export const PRESENTABLE_APPROVERS_LIMIT = 2;
export default {};
export const COMPLIANCE_TAB_COOKIE_KEY = 'compliance_dashboard_tabs';
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ComplianceDashboard component when there are merge requests matches the snapshot 1`] = `
exports[`ComplianceDashboard component when there are merge requests and the show tabs cookie is true matches the snapshot 1`] = `
<div
class="compliance-dashboard"
>
......@@ -26,97 +26,9 @@ exports[`ComplianceDashboard component when there are merge requests matches the
>
<template>
<div
class="dashboard-grid"
>
<grid-column-heading-stub
heading="Merge Request"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Approval Status"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Pipeline"
/>
<grid-column-heading-stub
class="gl-text-right"
heading="Updates"
/>
<div
data-testid="merge-request"
>
Merge request 0
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
<div
data-testid="merge-request"
>
Merge request 1
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
</div>
<pagination-stub />
<merge-requests-grid-stub
mergerequests="[object Object],[object Object]"
/>
</template>
<template>
<span>
......@@ -128,6 +40,28 @@ exports[`ComplianceDashboard component when there are merge requests matches the
</div>
`;
exports[`ComplianceDashboard component when there are merge requests matches the snapshot 1`] = `
<div
class="compliance-dashboard"
>
<header
class="gl-my-5"
>
<h4>
Compliance Dashboard
</h4>
<p>
Here you will find recent merge request activity
</p>
</header>
<merge-requests-grid-stub
mergerequests="[object Object],[object Object]"
/>
</div>
`;
exports[`ComplianceDashboard component when there are no merge requests matches the snapshot 1`] = `
<empty-state-stub
imagepath="empty.svg"
......
import Cookies from 'js-cookie';
import { shallowMount } from '@vue/test-utils';
import { GlTabs, GlTab } from '@gitlab/ui';
import ComplianceDashboard from 'ee/compliance_dashboard/components/dashboard.vue';
import ApprovalStatus from 'ee/compliance_dashboard/components/approval_status.vue';
import PipelineStatus from 'ee/compliance_dashboard/components/pipeline_status.vue';
import Approvers from 'ee/compliance_dashboard/components/approvers.vue';
import MergeRequestGrid from 'ee/compliance_dashboard/components/merge_requests/grid.vue';
import { COMPLIANCE_TAB_COOKIE_KEY } from 'ee/compliance_dashboard/constants';
import { createMergeRequests } from '../mock_data';
describe('ComplianceDashboard component', () => {
let wrapper;
const findMergeRequests = () => wrapper.findAll('[data-testid="merge-request"]');
const findTime = () => wrapper.find('time');
const findApprovalStatus = () => wrapper.find(ApprovalStatus);
const findPipelineStatus = () => wrapper.find(PipelineStatus);
const findApprovers = () => wrapper.find(Approvers);
const isLastPage = false;
const mergeRequests = createMergeRequests({ count: 2 });
const findMergeRequestsGrid = () => wrapper.find(MergeRequestGrid);
const findDashboardTabs = () => wrapper.find(GlTabs);
const createComponent = (props = {}, options = {}) => {
const createComponent = (props = {}) => {
return shallowMount(ComplianceDashboard, {
propsData: {
mergeRequests: createMergeRequests({ count: 2, options }),
isLastPage: false,
mergeRequests,
isLastPage,
emptyStateSvgPath: 'empty.svg',
...props,
},
stubs: {
GlTab,
MergeRequest: {
props: { mergeRequest: Object },
template: `<div data-testid="merge-request">{{ mergeRequest.title }}</div>`,
},
},
});
};
......@@ -41,50 +36,41 @@ describe('ComplianceDashboard component', () => {
describe('when there are merge requests', () => {
beforeEach(() => {
Cookies.set(COMPLIANCE_TAB_COOKIE_KEY, false);
wrapper = createComponent();
});
afterEach(() => {
Cookies.remove(COMPLIANCE_TAB_COOKIE_KEY);
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders a list of merge requests', () => {
expect(findMergeRequests().length).toEqual(2);
it('renders the merge requests', () => {
expect(findMergeRequestsGrid().exists()).toBe(true);
});
it('renders the dashboard tabs', () => {
expect(findDashboardTabs().exists()).toEqual(true);
it('sets the MergeRequestGrid properties', () => {
expect(findMergeRequestsGrid().props('mergeRequests')).toBe(mergeRequests);
expect(findMergeRequestsGrid().props('isLastPage')).toBe(isLastPage);
});
describe('approval status', () => {
it('does not render if there is no approval status', () => {
expect(findApprovalStatus().exists()).toBe(false);
describe('and the show tabs cookie is true', () => {
beforeEach(() => {
Cookies.set(COMPLIANCE_TAB_COOKIE_KEY, true);
wrapper = createComponent();
});
it('renders if there is an approval status', () => {
wrapper = createComponent({}, { approvalStatus: 'success' });
expect(findApprovalStatus().exists()).toBe(true);
});
});
describe('pipeline status', () => {
it('does not render if there is no pipeline', () => {
expect(findPipelineStatus().exists()).toBe(false);
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders if there is a pipeline', () => {
wrapper = createComponent({}, { addPipeline: true });
expect(findPipelineStatus().exists()).toBe(true);
it('renders the dashboard tabs', () => {
expect(findDashboardTabs().exists()).toBe(true);
});
});
it('renders the approvers list', () => {
expect(findApprovers().exists()).toBe(true);
});
it('renders the "merged at" time', () => {
expect(findTime().text()).toEqual('merged 2 days ago');
});
});
describe('when there are no merge requests', () => {
......@@ -97,11 +83,7 @@ describe('ComplianceDashboard component', () => {
});
it('does not render merge requests', () => {
expect(findMergeRequests().exists()).toEqual(false);
});
it('does not render the dashboard tabs', () => {
expect(findDashboardTabs().exists()).toEqual(false);
expect(findMergeRequestsGrid().exists()).toBe(false);
});
});
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MergeRequestsGrid component when intialized matches the snapshot 1`] = `
<div>
<div
class="dashboard-grid"
>
<grid-column-heading-stub
heading="Merge Request"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Approval Status"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Pipeline"
/>
<grid-column-heading-stub
class="gl-text-right"
heading="Updates"
/>
<div
data-testid="merge-request"
>
Merge request 0
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
<div
data-testid="merge-request"
>
Merge request 1
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
</div>
<pagination-stub
class="gl-mt-5"
/>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import ApprovalStatus from 'ee/compliance_dashboard/components/approval_status.vue';
import ApprovalStatus from 'ee/compliance_dashboard/components/merge_requests/approval_status.vue';
describe('ApprovalStatus component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import { GlAvatarLink } from '@gitlab/ui';
import Approvers from 'ee/compliance_dashboard/components/approvers.vue';
import Approvers from 'ee/compliance_dashboard/components/merge_requests/approvers.vue';
import { PRESENTABLE_APPROVERS_LIMIT } from 'ee/compliance_dashboard/constants';
import { createApprovers } from '../mock_data';
import { createApprovers } from '../../mock_data';
describe('MergeRequest component', () => {
let wrapper;
......
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 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';
describe('MergeRequestsGrid component', () => {
let wrapper;
const findMergeRequests = () => wrapper.findAll('[data-testid="merge-request"]');
const findTime = () => wrapper.find('time');
const findApprovalStatus = () => wrapper.find(ApprovalStatus);
const findPipelineStatus = () => wrapper.find(PipelineStatus);
const findApprovers = () => wrapper.find(Approvers);
const createComponent = (options = {}) => {
return shallowMount(MergeRequestsGrid, {
propsData: {
mergeRequests: createMergeRequests({ count: 2, options }),
isLastPage: false,
},
stubs: {
MergeRequest: {
props: { mergeRequest: Object },
template: `<div data-testid="merge-request">{{ mergeRequest.title }}</div>`,
},
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('when intialized', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders a list of merge requests', () => {
expect(findMergeRequests().length).toBe(2);
});
describe('approval status', () => {
it('does not render if there is no approval status', () => {
expect(findApprovalStatus().exists()).toBe(false);
});
it('renders if there is an approval status', () => {
wrapper = createComponent({ approvalStatus: 'success' });
expect(findApprovalStatus().exists()).toBe(true);
});
});
describe('pipeline status', () => {
it('does not render if there is no pipeline', () => {
expect(findPipelineStatus().exists()).toBe(false);
});
it('renders if there is a pipeline', () => {
wrapper = createComponent({ addPipeline: true });
expect(findPipelineStatus().exists()).toBe(true);
});
});
it('renders the approvers list', () => {
expect(findApprovers().exists()).toBe(true);
});
it('renders the "merged at" time', () => {
expect(findTime().text()).toBe('merged 2 days ago');
});
});
});
import { shallowMount } from '@vue/test-utils';
import { GlAvatarLink, GlAvatar } from '@gitlab/ui';
import MergeRequest from 'ee/compliance_dashboard/components/merge_request.vue';
import { createMergeRequest } from '../mock_data';
import MergeRequest from 'ee/compliance_dashboard/components/merge_requests/merge_request.vue';
import { createMergeRequest } from '../../mock_data';
describe('MergeRequest component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import PipelineStatus from 'ee/compliance_dashboard/components/pipeline_status.vue';
import { createPipelineStatus } from '../mock_data';
import PipelineStatus from 'ee/compliance_dashboard/components/merge_requests/pipeline_status.vue';
import { createPipelineStatus } from '../../mock_data';
describe('PipelineStatus component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import GridColumnHeading from 'ee/compliance_dashboard/components/grid_column_heading.vue';
import GridColumnHeading from 'ee/compliance_dashboard/components/shared/grid_column_heading.vue';
describe('GridColumnHeading component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import { GlPagination } from '@gitlab/ui';
import Pagination from 'ee/compliance_dashboard/components/pagination.vue';
import Pagination from 'ee/compliance_dashboard/components/shared/pagination.vue';
describe('MergeRequest component', () => {
describe('Pagination component', () => {
let wrapper;
const findGlPagination = () => wrapper.find(GlPagination);
......
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