Commit 2dd07926 authored by Paul Slaughter's avatar Paul Slaughter Committed by Mark Chao

FE: Remove legacy approval feature in MR files

parent 1deb4536
import $ from 'jquery';
import _ from 'underscore';
export default () => {
$('.approver-list').on(
'confirm:complete',
'.unsaved-approvers.approver .btn-remove',
function approverListClickCallback(ev, answer) {
if (answer) {
const removeElement = $(this).closest('li');
const approverId = parseInt(removeElement.attr('id').replace('user_', ''), 10);
const approverIds = $('input#merge_request_approver_ids');
const skipUsers = approverIds.data('skipUsers') || [];
const approverIndex = skipUsers.indexOf(approverId);
removeElement.remove();
if (approverIndex > -1) {
approverIds.data('skipUsers', skipUsers.splice(approverIndex, 1));
}
}
return false;
},
);
$('.approver-list').on(
'confirm:complete',
'.unsaved-approvers.approver-group .btn-remove',
function approverListRemoveClickCallback(ev, answer) {
if (answer) {
const removeElement = $(this).closest('li');
const approverGroupId = parseInt(removeElement.attr('id').replace('group_', ''), 10);
const approverGroupIds = $('input#merge_request_approver_group_ids');
const skipGroups = approverGroupIds.data('skipGroups') || [];
const approverGroupIndex = skipGroups.indexOf(approverGroupId);
removeElement.remove();
if (approverGroupIndex > -1) {
approverGroupIds.data('skipGroups', skipGroups.splice(approverGroupIndex, 1));
}
}
return false;
},
);
$('form.merge-request-form').on('submit', function mergeRequestFormSubmitCallback() {
if ($('input#merge_request_approver_ids').length) {
let approverIds = $.map($('li.unsaved-approvers.approver').not('.approver-template'), li =>
li.id.replace('user_', ''),
);
const approversInput = $(this).find('input#merge_request_approver_ids');
approverIds = approverIds.concat(approversInput.val().split(','));
approversInput.val(_.compact(approverIds).join(','));
}
if ($('input#merge_request_approver_group_ids').length) {
let approverGroupIds = $.map($('li.unsaved-approvers.approver-group'), li =>
li.id.replace('group_', ''),
);
const approverGroupsInput = $(this).find('input#merge_request_approver_group_ids');
approverGroupIds = approverGroupIds.concat(approverGroupsInput.val().split(','));
approverGroupsInput.val(_.compact(approverGroupIds).join(','));
}
});
$('.suggested-approvers a').on('click', function sugestedApproversClickCallback() {
const userId = this.id.replace('user_', '');
const username = this.text;
if ($(`.approver-list #user_${userId}`).length) {
return false;
}
const approverItemHTML = $('.unsaved-approvers.approver-template')
.clone()
.removeClass('hide approver-template')[0]
.outerHTML.replace(/\{approver_name\}/g, username)
.replace(/\{user_id\}/g, userId);
$('.no-approvers').remove();
$('.approver-list').append(approverItemHTML);
return false;
});
};
import initApprovals from 'ee/approvals/setup_single_rule_approvals';
import mountApprovals from 'ee/approvals/mount_mr_edit'; import mountApprovals from 'ee/approvals/mount_mr_edit';
export default () => { export default () => {
initApprovals();
mountApprovals(document.getElementById('js-mr-approvals-input')); mountApprovals(document.getElementById('js-mr-approvals-input'));
}; };
...@@ -10,7 +10,7 @@ import ApprovalsSummary from './approvals_summary.vue'; ...@@ -10,7 +10,7 @@ import ApprovalsSummary from './approvals_summary.vue';
import ApprovalsSummaryOptional from './approvals_summary_optional.vue'; import ApprovalsSummaryOptional from './approvals_summary_optional.vue';
import ApprovalsFooter from './approvals_footer.vue'; import ApprovalsFooter from './approvals_footer.vue';
import ApprovalsAuth from './approvals_auth.vue'; import ApprovalsAuth from './approvals_auth.vue';
import { FETCH_LOADING, FETCH_ERROR, APPROVE_ERROR, UNAPPROVE_ERROR } from '../messages'; import { FETCH_LOADING, FETCH_ERROR, APPROVE_ERROR, UNAPPROVE_ERROR } from './messages';
export default { export default {
name: 'MRWidgetMultipleRuleApprovals', name: 'MRWidgetMultipleRuleApprovals',
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { n__, sprintf } from '~/locale'; import { n__, sprintf } from '~/locale';
import { toNounSeriesText } from '~/lib/utils/grammar'; import { toNounSeriesText } from '~/lib/utils/grammar';
import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue'; import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue';
import { APPROVED_MESSAGE } from '../messages'; import { APPROVED_MESSAGE } from './messages';
export default { export default {
components: { components: {
......
<script> <script>
import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { OPTIONAL, OPTIONAL_CAN_APPROVE } from '../messages'; import { OPTIONAL, OPTIONAL_CAN_APPROVE } from './messages';
export default { export default {
components: { components: {
......
import MultipleRuleApprovals from './multiple_rule/approvals.vue';
import SingleRuleApprovals from './single_rule/approvals.vue';
export default {
functional: true,
render(h, context) {
const component = gon.features.approvalRules ? MultipleRuleApprovals : SingleRuleApprovals;
return h(component, context.data, context.children);
},
};
<script>
/* === WARNING ===
* This file will be removed pending the removal of the `approval_rules` feature flag.
*
* If a new feature needs to be added, please please make changes in the `./multiple_rule`
* directory (see the feature issue https://gitlab.com/gitlab-org/gitlab-ee/issues/1979).
*
* Follow along via this issue: https://gitlab.com/gitlab-org/gitlab-ee/issues/10685.
*/
import createFlash from '~/flash';
import MrWidgetContainer from '~/vue_merge_request_widget/components/mr_widget_container.vue';
import MrWidgetIcon from '~/vue_merge_request_widget/components/mr_widget_icon.vue';
import ApprovalsBody from './approvals_body.vue';
import ApprovalsFooter from './approvals_footer.vue';
import { FETCH_LOADING, FETCH_ERROR } from '../messages';
export default {
name: 'MRWidgetSingleRuleApprovals',
components: {
ApprovalsBody,
ApprovalsFooter,
MrWidgetContainer,
MrWidgetIcon,
},
props: {
mr: {
type: Object,
required: true,
},
service: {
type: Object,
required: true,
},
},
data() {
return {
fetchingApprovals: true,
};
},
computed: {
status() {
if (this.mr.approvals.approvals_left > 0) {
return 'warning';
}
return 'success';
},
approvalsOptional() {
return (
!this.fetchingApprovals &&
this.mr.approvals.approvals_required === 0 &&
this.mr.approvals.approved_by.length === 0
);
},
},
created() {
this.service
.fetchApprovals()
.then(data => {
this.mr.setApprovals(data);
this.fetchingApprovals = false;
})
.catch(() => createFlash(FETCH_ERROR));
},
FETCH_LOADING,
};
</script>
<template>
<mr-widget-container>
<div
v-if="mr.hasApprovalsAvailable"
class="media media-section js-mr-approvals align-items-center"
>
<mr-widget-icon name="approval" />
<div v-show="fetchingApprovals" class="mr-approvals-loading-state media-body">
<span class="approvals-loading-text"> {{ $options.FETCH_LOADING }} </span>
</div>
<div v-if="!fetchingApprovals" class="approvals-components media-body">
<approvals-body
:mr="mr"
:service="service"
:user-can-approve="mr.approvals.user_can_approve"
:user-has-approved="mr.approvals.user_has_approved"
:approved-by="mr.approvals.approved_by"
:approvals-left="mr.approvals.approvals_left"
:approvals-optional="approvalsOptional"
:suggested-approvers="mr.approvals.suggested_approvers"
/>
<approvals-footer
:mr="mr"
:service="service"
:user-can-approve="mr.approvals.user_can_approve"
:user-has-approved="mr.approvals.user_has_approved"
:approved-by="mr.approvals.approved_by"
:approvals-left="mr.approvals.approvals_left"
/>
</div>
</div>
</mr-widget-container>
</template>
<script>
/* === WARNING ===
* This file will be removed pending the removal of the `approval_rules` feature flag.
*
* If a new feature needs to be added, please please make changes in the `./multiple_rule`
* directory (see the feature issue https://gitlab.com/gitlab-org/gitlab-ee/issues/1979).
*
* Follow along via this issue: https://gitlab.com/gitlab-org/gitlab-ee/issues/10685.
*/
import { n__, s__, sprintf } from '~/locale';
import Flash from '~/flash';
import Icon from '~/vue_shared/components/icon.vue';
import MrWidgetAuthor from '~/vue_merge_request_widget/components/mr_widget_author.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '~/vue_merge_request_widget/event_hub';
import { APPROVE_ERROR, OPTIONAL_CAN_APPROVE, OPTIONAL } from '../messages';
export default {
name: 'ApprovalsBody',
components: {
MrWidgetAuthor,
Icon,
},
directives: {
tooltip,
},
props: {
mr: {
type: Object,
required: true,
},
service: {
type: Object,
required: true,
},
approvedBy: {
type: Array,
required: false,
default: () => [],
},
approvalsOptional: {
type: Boolean,
required: false,
default: false,
},
approvalsLeft: {
type: Number,
required: false,
default: 0,
},
userCanApprove: {
type: Boolean,
required: false,
default: false,
},
userHasApproved: {
type: Boolean,
required: false,
default: false,
},
suggestedApprovers: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
approving: false,
};
},
computed: {
approvalsRequiredStringified() {
if (this.approvalsOptional) {
if (this.userCanApprove) {
return OPTIONAL_CAN_APPROVE;
}
return OPTIONAL;
}
if (this.approvalsLeft === 0) {
return this.userCanApprove
? s__('mrWidget|Merge request approved; you can approve additionally')
: s__('mrWidget|Merge request approved');
}
if (this.suggestedApprovers.length >= 1) {
return sprintf(
n__(
'mrWidget|Requires 1 more approval by',
'mrWidget|Requires %d more approvals by',
this.approvalsLeft,
),
);
}
return sprintf(
n__(
'mrWidget|Requires 1 more approval',
'mrWidget|Requires %d more approvals',
this.approvalsLeft,
),
);
},
approveButtonText() {
let approveButtonText = s__('mrWidget|Approve');
if (this.approvalsLeft <= 0) {
approveButtonText = s__('mrWidget|Add approval');
}
return approveButtonText;
},
approveButtonClass() {
return {
'btn-inverted': this.showApproveButton && this.approvalsLeft <= 0,
};
},
showApprovalDocLink() {
return this.approvalsOptional && this.showApproveButton;
},
showApproveButton() {
return this.userCanApprove && !this.userHasApproved && this.mr.isOpen;
},
showSuggestedApprovers() {
return this.approvalsLeft > 0 && this.suggestedApprovers && this.suggestedApprovers.length;
},
},
methods: {
approveMergeRequest() {
this.approving = true;
this.service
.approveMergeRequest()
.then(data => {
this.mr.setApprovals(data);
eventHub.$emit('MRWidgetUpdateRequested');
this.approving = false;
})
.catch(() => {
this.approving = false;
Flash(APPROVE_ERROR);
});
},
},
};
</script>
<template>
<div class="approvals-body space-children">
<span v-if="showApproveButton" class="approvals-approve-button-wrap">
<button
:disabled="approving"
:class="approveButtonClass"
class="btn btn-primary btn-sm approve-btn"
@click="approveMergeRequest"
>
<i v-if="approving" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
{{ approveButtonText }}
</button>
</span>
<span :class="approvalsOptional ? 'text-muted' : 'bold'" class="approvals-required-text">
{{ approvalsRequiredStringified }}
<a
v-if="showApprovalDocLink"
v-tooltip
:href="mr.approvalsHelpPath"
:title="__('About this feature')"
data-placement="bottom"
target="_blank"
rel="noopener noreferrer nofollow"
data-container="body"
>
<icon name="question-o" />
</a>
<span v-if="showSuggestedApprovers">
<mr-widget-author
v-for="approver in suggestedApprovers"
:key="approver.username"
:author="approver"
:show-author-name="false"
:show-author-tooltip="true"
/>
</span>
</span>
</div>
</template>
<script>
/* === WARNING ===
* This file will be removed pending the removal of the `approval_rules` feature flag.
*
* If a new feature needs to be added, please please make changes in the `./multiple_rule`
* directory (see the feature issue https://gitlab.com/gitlab-org/gitlab-ee/issues/1979).
*
* Follow along via this issue: https://gitlab.com/gitlab-org/gitlab-ee/issues/10685.
*/
import Flash from '~/flash';
import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { s__ } from '~/locale';
import eventHub from '~/vue_merge_request_widget/event_hub';
import { UNAPPROVE_ERROR } from '../messages';
export default {
name: 'ApprovalsFooter',
components: {
Icon,
UserAvatarLink,
},
props: {
mr: {
type: Object,
required: true,
},
service: {
type: Object,
required: true,
},
approvedBy: {
type: Array,
required: false,
default: () => [],
},
approvalsLeft: {
type: Number,
required: false,
default: 0,
},
userCanApprove: {
type: Boolean,
required: false,
default: false,
},
userHasApproved: {
type: Boolean,
required: false,
default: false,
},
suggestedApprovers: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
unapproving: false,
};
},
computed: {
showUnapproveButton() {
const isMerged = this.mr.state === 'merged';
return this.userHasApproved && !this.userCanApprove && !isMerged;
},
approvedByText() {
return s__('mrWidget|Approved by');
},
removeApprovalText() {
return s__('mrWidget|Remove your approval');
},
},
methods: {
unapproveMergeRequest() {
this.unapproving = true;
this.service
.unapproveMergeRequest()
.then(data => {
this.mr.setApprovals(data);
eventHub.$emit('MRWidgetUpdateRequested');
this.unapproving = false;
})
.catch(() => {
this.unapproving = false;
Flash(UNAPPROVE_ERROR);
});
},
},
};
</script>
<template>
<div v-if="approvedBy.length" class="approved-by-users approvals-footer clearfix mr-info-list">
<div class="approvers-prefix">
<p>{{ approvedByText }}</p>
<div class="approvers-list">
<user-avatar-link
v-for="approver in approvedBy"
:key="approver.user.username"
class="js-approver-list-member"
:img-size="20"
:img-src="approver.user.avatar_url"
:img-alt="approver.user.name"
:link-href="approver.user.web_url"
:tooltip-text="approver.user.name"
tooltip-placement="bottom"
/>
<icon
v-for="n in approvalsLeft"
:key="n"
name="dotted-circle"
class="avatar avatar-placeholder s20"
/>
</div>
<button
v-if="showUnapproveButton"
:disabled="unapproving"
type="button"
class="btn btn-sm unapprove-btn-wrap"
@click="unapproveMergeRequest"
>
<i v-if="unapproving" class="fa fa-spinner fa-spin" aria-hidden="true"> </i>
{{ removeApprovalText }}
</button>
</div>
</div>
</template>
...@@ -8,7 +8,7 @@ import MrWidgetLicenses from 'ee/vue_shared/license_management/mr_widget_license ...@@ -8,7 +8,7 @@ import MrWidgetLicenses from 'ee/vue_shared/license_management/mr_widget_license
import { n__, s__, __, sprintf } from '~/locale'; import { n__, s__, __, sprintf } from '~/locale';
import CEWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue'; import CEWidgetOptions from '~/vue_merge_request_widget/mr_widget_options.vue';
import MrWidgetApprovals from './components/approvals'; import MrWidgetApprovals from './components/approvals/approvals.vue';
import MrWidgetGeoSecondaryNode from './components/states/mr_widget_secondary_geo_node.vue'; import MrWidgetGeoSecondaryNode from './components/states/mr_widget_secondary_geo_node.vue';
export default { export default {
...@@ -145,7 +145,6 @@ export default { ...@@ -145,7 +145,6 @@ export default {
return { return {
...base, ...base,
approvalsPath: store.approvalsPath,
apiApprovalsPath: store.apiApprovalsPath, apiApprovalsPath: store.apiApprovalsPath,
apiApprovalSettingsPath: store.apiApprovalSettingsPath, apiApprovalSettingsPath: store.apiApprovalSettingsPath,
apiApprovePath: store.apiApprovePath, apiApprovePath: store.apiApprovePath,
......
...@@ -5,38 +5,31 @@ export default class MRWidgetService extends CEWidgetService { ...@@ -5,38 +5,31 @@ export default class MRWidgetService extends CEWidgetService {
constructor(mr) { constructor(mr) {
super(mr); super(mr);
this.approvalsPath = mr.approvalsPath; this.apiApprovalsPath = mr.apiApprovalsPath;
this.apiApprovalSettingsPath = mr.apiApprovalSettingsPath;
// This feature flag will be the default behavior when this.apiApprovePath = mr.apiApprovePath;
// https://gitlab.com/gitlab-org/gitlab-ee/issues/1979 is closed this.apiUnapprovePath = mr.apiUnapprovePath;
if (gon.features.approvalRules) {
this.apiApprovalsPath = mr.apiApprovalsPath;
this.apiApprovalSettingsPath = mr.apiApprovalSettingsPath;
this.apiApprovePath = mr.apiApprovePath;
this.apiUnapprovePath = mr.apiUnapprovePath;
this.fetchApprovals = () => axios.get(this.apiApprovalsPath).then(res => res.data);
this.fetchApprovalSettings = () =>
axios.get(this.apiApprovalSettingsPath).then(res => res.data);
this.approveMergeRequest = () => axios.post(this.apiApprovePath).then(res => res.data);
this.approveMergeRequestWithAuth = approvalPassword =>
axios
.post(this.apiApprovePath, { approval_password: approvalPassword })
.then(res => res.data);
this.unapproveMergeRequest = () => axios.post(this.apiUnapprovePath).then(res => res.data);
}
} }
fetchApprovals() { fetchApprovals() {
return axios.get(this.approvalsPath).then(res => res.data); return axios.get(this.apiApprovalsPath).then(res => res.data);
}
fetchApprovalSettings() {
return axios.get(this.apiApprovalSettingsPath).then(res => res.data);
} }
approveMergeRequest() { approveMergeRequest() {
return axios.post(this.approvalsPath).then(res => res.data); return axios.post(this.apiApprovePath).then(res => res.data);
}
approveMergeRequestWithAuth(approvalPassword) {
return axios
.post(this.apiApprovePath, { approval_password: approvalPassword })
.then(res => res.data);
} }
unapproveMergeRequest() { unapproveMergeRequest() {
return axios.delete(this.approvalsPath).then(res => res.data); return axios.post(this.apiUnapprovePath).then(res => res.data);
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
......
...@@ -59,7 +59,6 @@ export default class MergeRequestStore extends CEMergeRequestStore { ...@@ -59,7 +59,6 @@ export default class MergeRequestStore extends CEMergeRequestStore {
this.hasApprovalsAvailable = Boolean( this.hasApprovalsAvailable = Boolean(
data.has_approvals_available || this.hasApprovalsAvailable, data.has_approvals_available || this.hasApprovalsAvailable,
); );
this.approvalsPath = data.approvals_path || this.approvalsPath;
this.apiApprovalsPath = data.api_approvals_path || this.apiApprovalsPath; this.apiApprovalsPath = data.api_approvals_path || this.apiApprovalsPath;
this.apiApprovalSettingsPath = data.api_approval_settings_path || this.apiApprovalSettingsPath; this.apiApprovalSettingsPath = data.api_approval_settings_path || this.apiApprovalSettingsPath;
this.apiApprovePath = data.api_approve_path || this.apiApprovePath; this.apiApprovePath = data.api_approve_path || this.apiApprovePath;
...@@ -69,13 +68,8 @@ export default class MergeRequestStore extends CEMergeRequestStore { ...@@ -69,13 +68,8 @@ export default class MergeRequestStore extends CEMergeRequestStore {
setApprovals(data) { setApprovals(data) {
this.approvals = mapApprovalsResponse(data); this.approvals = mapApprovalsResponse(data);
this.approvalsLeft = !!data.approvals_left; this.approvalsLeft = !!data.approvals_left;
if (gon.features.approvalRules) { this.isApproved = data.approved || false;
this.isApproved = data.approved || false; this.preventMerge = !this.isApproved;
this.preventMerge = !this.isApproved;
} else {
this.isApproved = !this.approvalsLeft || false;
this.preventMerge = this.hasApprovalsAvailable && this.approvalsLeft;
}
} }
setApprovalRules(data) { setApprovalRules(data) {
......
...@@ -3,8 +3,8 @@ import { createLocalVue, shallowMount } from '@vue/test-utils'; ...@@ -3,8 +3,8 @@ import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue'; import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue';
import ApprovalsList from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_list.vue'; import ApprovalsList from 'ee/vue_merge_request_widget/components/approvals/approvals_list.vue';
import ApprovalsFooter from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_footer.vue'; import ApprovalsFooter from 'ee/vue_merge_request_widget/components/approvals/approvals_footer.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import _ from 'underscore'; import _ from 'underscore';
import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue'; import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue';
import ApprovedIcon from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approved_icon.vue'; import ApprovedIcon from 'ee/vue_merge_request_widget/components/approvals/approved_icon.vue';
import ApprovalsList from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_list.vue'; import ApprovalsList from 'ee/vue_merge_request_widget/components/approvals/approvals_list.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
...@@ -4,7 +4,7 @@ import { ...@@ -4,7 +4,7 @@ import {
OPTIONAL, OPTIONAL,
OPTIONAL_CAN_APPROVE, OPTIONAL_CAN_APPROVE,
} from 'ee/vue_merge_request_widget/components/approvals/messages'; } from 'ee/vue_merge_request_widget/components/approvals/messages';
import ApprovalsSummaryOptional from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_summary_optional.vue'; import ApprovalsSummaryOptional from 'ee/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
...@@ -3,7 +3,7 @@ import _ from 'underscore'; ...@@ -3,7 +3,7 @@ import _ from 'underscore';
import { toNounSeriesText } from '~/lib/utils/grammar'; import { toNounSeriesText } from '~/lib/utils/grammar';
import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue'; import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue';
import { APPROVED_MESSAGE } from 'ee/vue_merge_request_widget/components/approvals/messages'; import { APPROVED_MESSAGE } from 'ee/vue_merge_request_widget/components/approvals/messages';
import ApprovalsSummary from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_summary.vue'; import ApprovalsSummary from 'ee/vue_merge_request_widget/components/approvals/approvals_summary.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import ApprovedIcon from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approved_icon.vue'; import ApprovedIcon from 'ee/vue_merge_request_widget/components/approvals/approved_icon.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
const EXPECTED_SIZE = 16; const EXPECTED_SIZE = 16;
......
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import ApprovalsAuth from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_auth.vue'; import ApprovalsAuth from 'ee/vue_merge_request_widget/components/approvals/approvals_auth.vue';
const TEST_PASSWORD = 'password'; const TEST_PASSWORD = 'password';
......
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import eventHub from '~/vue_merge_request_widget/event_hub'; import eventHub from '~/vue_merge_request_widget/event_hub';
import Approvals from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals.vue'; import Approvals from 'ee/vue_merge_request_widget/components/approvals/approvals.vue';
import ApprovalsSummary from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_summary.vue'; import ApprovalsSummary from 'ee/vue_merge_request_widget/components/approvals/approvals_summary.vue';
import ApprovalsSummaryOptional from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_summary_optional.vue'; import ApprovalsSummaryOptional from 'ee/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue';
import ApprovalsFooter from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_footer.vue'; import ApprovalsFooter from 'ee/vue_merge_request_widget/components/approvals/approvals_footer.vue';
import ApprovalsAuth from 'ee/vue_merge_request_widget/components/approvals/multiple_rule/approvals_auth.vue'; import ApprovalsAuth from 'ee/vue_merge_request_widget/components/approvals/approvals_auth.vue';
import { import {
FETCH_LOADING, FETCH_LOADING,
......
import Vue from 'vue';
import ApprovalsBody from 'ee/vue_merge_request_widget/components/approvals/single_rule/approvals_body.vue';
import {
OPTIONAL,
OPTIONAL_CAN_APPROVE,
} from 'ee/vue_merge_request_widget/components/approvals/messages';
describe('Approvals Body Component', () => {
let vm;
const initialData = {
mr: {
isOpen: true,
},
service: {},
suggestedApprovers: [{ name: 'Approver 1' }],
userCanApprove: false,
userHasApproved: true,
approvedBy: [],
approvalsLeft: 1,
approvalsOptional: false,
pendingAvatarSvg: '<svg></svg>',
checkmarkSvg: '<svg></svg>',
};
beforeEach(() => {
setFixtures('<div id="mock-container"></div>');
const ApprovalsBodyComponent = Vue.extend(ApprovalsBody);
vm = new ApprovalsBodyComponent({
el: '#mock-container',
propsData: initialData,
});
});
afterEach(() => {
vm.$destroy();
});
it('should correctly set component props', () => {
Object.keys(vm).forEach(propKey => {
if (initialData[propKey]) {
expect(vm[propKey]).toBe(initialData[propKey]);
}
});
});
describe('Computed properties', () => {
describe('approvalsRequiredStringified', () => {
it('should display the correct string for 1 possible approver', () => {
const correctText = 'Requires 1 more approval by';
expect(vm.approvalsRequiredStringified).toBe(correctText);
});
it('should display the correct string for 2 possible approvers', done => {
const correctText = 'Requires 2 more approvals by';
vm.approvalsLeft = 2;
vm.suggestedApprovers.push({ name: 'Approver 2' });
Vue.nextTick(() => {
expect(vm.approvalsRequiredStringified).toBe(correctText);
done();
});
});
it('should display the correct string for 0 approvals required', done => {
const correctText = OPTIONAL;
vm.approvalsOptional = true;
Vue.nextTick(() => {
expect(vm.approvalsRequiredStringified).toBe(correctText);
done();
});
});
it('should display the correct string for 0 approvals required and if the user is able to approve', done => {
const correctText = OPTIONAL_CAN_APPROVE;
vm.approvalsOptional = true;
vm.userCanApprove = true;
Vue.nextTick(() => {
expect(vm.approvalsRequiredStringified).toBe(correctText);
done();
});
});
it('shows the "Merge request approved" copy when there are enough approvals in place', done => {
vm.approvalsLeft = 0;
Vue.nextTick(() => {
expect(vm.approvalsRequiredStringified).toBe('Merge request approved');
done();
});
});
it('shows the correct copy when there are enough approvals in place but user can approve', done => {
vm.approvalsLeft = 0;
vm.userCanApprove = true;
Vue.nextTick(() => {
expect(vm.approvalsRequiredStringified).toBe(
'Merge request approved; you can approve additionally',
);
done();
});
});
it('shows the "Requires 1 more approval" without by when no suggested approvals are available', done => {
const correctText = 'Requires 1 more approval';
vm.suggestedApprovers = [];
Vue.nextTick(() => {
expect(vm.approvalsRequiredStringified).toBe(correctText);
done();
});
});
it('shows the "Requires 2 more approvals" without by when no suggested approvals are available', done => {
const correctText = 'Requires 2 more approvals';
vm.approvalsLeft = 2;
vm.suggestedApprovers = [];
Vue.nextTick(() => {
expect(vm.approvalsRequiredStringified).toBe(correctText);
done();
});
});
});
describe('showApproveButton', () => {
it('should not be true when the user cannot approve', done => {
vm.userCanApprove = false;
vm.userHasApproved = true;
Vue.nextTick(() => {
expect(vm.showApproveButton).toBe(false);
done();
});
});
it('should be true when the user can approve', done => {
vm.userCanApprove = true;
vm.userHasApproved = false;
Vue.nextTick(() => {
expect(vm.showApproveButton).toBe(true);
done();
});
});
});
describe('approveButtonText', () => {
it('The approve button should have the "Approve" text', done => {
vm.approvalsLeft = 1;
vm.userHasApproved = false;
vm.userCanApprove = true;
Vue.nextTick(() => {
expect(vm.approveButtonText).toBe('Approve');
done();
});
});
it('The approve button should have the "Add approval" text', done => {
vm.approvalsLeft = 0;
vm.userHasApproved = false;
vm.userCanApprove = true;
Vue.nextTick(() => {
expect(vm.approveButtonText).toBe('Add approval');
done();
});
});
});
});
});
import Vue from 'vue';
import ApprovalsFooter from 'ee/vue_merge_request_widget/components/approvals/single_rule/approvals_footer.vue';
import { TEST_HOST } from 'spec/test_constants';
describe('Approvals Footer Component', () => {
let vm;
const initialData = {
mr: {
state: 'readyToMerge',
},
service: {},
userCanApprove: false,
userHasApproved: true,
approvedBy: [],
approvalsLeft: 3,
};
beforeEach(() => {
setFixtures('<div id="mock-container"></div>');
const ApprovalsFooterComponent = Vue.extend(ApprovalsFooter);
vm = new ApprovalsFooterComponent({
el: '#mock-container',
propsData: initialData,
});
});
afterEach(() => {
vm.$destroy();
});
it('should correctly set component props', () => {
Object.keys(vm).forEach(propKey => {
if (initialData[propKey]) {
expect(vm[propKey]).toBe(initialData[propKey]);
}
});
});
describe('Computed properties', () => {
it('should correctly set showUnapproveButton when the user can unapprove', () => {
expect(vm.showUnapproveButton).toBeTruthy();
vm.mr.state = 'merged';
expect(vm.showUnapproveButton).toBeFalsy();
});
it('should correctly set showUnapproveButton when the user can not unapprove', done => {
vm.userCanApprove = true;
Vue.nextTick(() => {
expect(vm.showUnapproveButton).toBe(false);
done();
});
});
});
describe('approvers list', () => {
const avatarUrl = `${TEST_HOST}/dummy.jpg`;
it('shows link to member avatar for for each approver', done => {
vm.approvedBy = [
{
user: {
username: 'Tanuki',
avatar_url: avatarUrl,
},
},
];
Vue.nextTick(() => {
const memberImage = document.querySelector('.approvers-list img');
expect(memberImage.src).toContain(avatarUrl);
done();
});
});
it('allows to add multiple approvers withoutd duplicate-key errors', done => {
vm.approvedBy = [
{
user: {
username: 'Tanuki',
avatar_url: avatarUrl,
},
},
{
user: {
username: 'Tanuki2',
avatar_url: avatarUrl,
},
},
];
Vue.nextTick(() => {
const approvers = document.querySelectorAll('.approvers-list img');
expect(approvers.length).toBe(2);
done();
});
});
});
});
...@@ -6,6 +6,7 @@ import MRWidgetService from 'ee/vue_merge_request_widget/services/mr_widget_serv ...@@ -6,6 +6,7 @@ import MRWidgetService from 'ee/vue_merge_request_widget/services/mr_widget_serv
import MRWidgetStore from 'ee/vue_merge_request_widget/stores/mr_widget_store'; import MRWidgetStore from 'ee/vue_merge_request_widget/stores/mr_widget_store';
import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { TEST_HOST } from 'spec/test_constants'; import { TEST_HOST } from 'spec/test_constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import state from 'ee/vue_shared/security_reports/store/state'; import state from 'ee/vue_shared/security_reports/store/state';
import mockData, { import mockData, {
...@@ -39,7 +40,6 @@ describe('ee merge request widget options', () => { ...@@ -39,7 +40,6 @@ describe('ee merge request widget options', () => {
} }
beforeEach(() => { beforeEach(() => {
gon.features = { approvalRules: false };
delete mrWidgetOptions.extends.el; // Prevent component mounting delete mrWidgetOptions.extends.el; // Prevent component mounting
Component = Vue.extend(mrWidgetOptions); Component = Vue.extend(mrWidgetOptions);
...@@ -47,7 +47,6 @@ describe('ee merge request widget options', () => { ...@@ -47,7 +47,6 @@ describe('ee merge request widget options', () => {
}); });
afterEach(() => { afterEach(() => {
gon.features = null;
vm.$destroy(); vm.$destroy();
mock.restore(); mock.restore();
...@@ -850,16 +849,22 @@ describe('ee merge request widget options', () => { ...@@ -850,16 +849,22 @@ describe('ee merge request widget options', () => {
}); });
describe('data', () => { describe('data', () => {
it('passes approvals_path to service', () => { it('passes approval api paths to service', () => {
const approvalsPath = `${TEST_HOST}/approvals/path`; const paths = {
api_approvals_path: `${TEST_HOST}/api/approvals/path`,
api_approval_settings_path: `${TEST_HOST}/api/approval/settings/path`,
api_approve_path: `${TEST_HOST}/api/approve/path`,
api_unapprove_path: `${TEST_HOST}/api/unapprove/path`,
};
vm = mountComponent(Component, { vm = mountComponent(Component, {
mrData: { mrData: {
...mockData, ...mockData,
approvals_path: approvalsPath, ...paths,
}, },
}); });
expect(vm.service.approvalsPath).toEqual(approvalsPath); expect(vm.service).toEqual(jasmine.objectContaining(convertObjectPropsToCamelCase(paths)));
}); });
}); });
}); });
...@@ -21,7 +21,6 @@ describe('mrWidgetOptions', () => { ...@@ -21,7 +21,6 @@ describe('mrWidgetOptions', () => {
const COLLABORATION_MESSAGE = 'Allows commits from members who can merge to the target branch'; const COLLABORATION_MESSAGE = 'Allows commits from members who can merge to the target branch';
beforeEach(() => { beforeEach(() => {
gon.features = { approvalRules: false };
// Prevent component mounting // Prevent component mounting
delete mrWidgetOptions.el; delete mrWidgetOptions.el;
...@@ -32,7 +31,6 @@ describe('mrWidgetOptions', () => { ...@@ -32,7 +31,6 @@ describe('mrWidgetOptions', () => {
}); });
afterEach(() => { afterEach(() => {
gon.features = null;
vm.$destroy(); vm.$destroy();
}); });
......
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