Commit 92512f8a authored by Zamir Martins's avatar Zamir Martins Committed by Paul Slaughter

Add approvers select dropdown

which allows users and groups to be added
as approvers.

EE: true
parent e5f32c63
<script>
import {
GlSprintf,
GlForm,
GlFormInput,
GlModalDirective,
GlToken,
GlAvatarLabeled,
} from '@gitlab/ui';
import { GlSprintf, GlForm, GlFormInput, GlModalDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { AVATAR_SHAPE_OPTION_CIRCLE, AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { groupApprovers, decomposeApprovers, USER_TYPE } from './lib/actions';
import ApproversSelect from 'ee/approvals/components/approvers_select.vue';
import ApproversList from 'ee/approvals/components/approvers_list.vue';
import { groupApprovers, decomposeApprovers, groupIds, userIds, USER_TYPE } from './lib/actions';
export default {
components: {
GlSprintf,
GlForm,
GlFormInput,
GlToken,
GlAvatarLabeled,
ApproversSelect,
ApproversList,
},
directives: {
GlModalDirective,
......@@ -37,12 +32,24 @@ export default {
return {
action: { ...this.initAction },
approvers: groupApprovers(this.existingApprovers),
approversToAdd: [],
};
},
computed: {
groupIds() {
return groupIds(this.approvers);
},
userIds() {
return userIds(this.approvers);
},
},
watch: {
approvers(values) {
this.action = decomposeApprovers(this.action, values);
},
approversToAdd(val) {
this.approvers.push(val[0]);
},
action: {
handler(values) {
this.$emit('changed', values);
......@@ -59,6 +66,9 @@ export default {
(approver) => approver.type !== removedApprover.type || approver.id !== removedApprover.id,
);
},
selectedApprover(approver) {
this.approvers.push(approver);
},
avatarShape(approver) {
return this.isUser(approver) ? AVATAR_SHAPE_OPTION_CIRCLE : AVATAR_SHAPE_OPTION_RECT;
},
......@@ -73,7 +83,7 @@ export default {
addAnApprover: s__('ScanResultPolicy|add an approver'),
},
humanizedTemplate: s__(
'ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers: %{approvers}',
'ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:',
),
};
</script>
......@@ -100,25 +110,18 @@ export default {
@input="approvalsRequiredChanged"
/>
</template>
<template #approvers>
<gl-token
v-for="approver in approvers"
:key="approver.type + approver.id"
class="gl-ml-3"
@close="removeApprover(approver)"
>
<gl-avatar-labeled
:src="approver.avatar_url"
:size="24"
:shape="avatarShape(approver)"
:label="approverName(approver)"
:entity-name="approver.name"
:alt="approver.name"
/>
</gl-token>
</template>
</gl-sprintf>
<div class="gl-bg-white gl-w-full gl-display-flex">
<approvers-select
v-model="approversToAdd"
:skip-user-ids="userIds"
:skip-group-ids="groupIds"
:project-id="projectId"
/>
</div>
<div class="gl-bg-white gl-w-full gl-mt-3 gl-border gl-rounded-base gl-overflow-auto h-12em">
<approvers-list v-model="approvers" />
</div>
</gl-form>
</div>
</template>
import { GlFormInput, GlToken } from '@gitlab/ui';
import { GlFormInput } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import MockAdapter from 'axios-mock-adapter';
import PolicyActionBuilder from 'ee/threat_monitoring/components/policy_editor/scan_result_policy/policy_action_builder.vue';
import ApproversSelect from 'ee/approvals/components/approvers_select.vue';
import ApproversList from 'ee/approvals/components/approvers_list.vue';
import ApproversListItem from 'ee/approvals/components/approvers_list_item.vue';
import axios from '~/lib/utils/axios_utils';
const APPROVER_1 = {
id: 1,
......@@ -21,6 +26,15 @@ const APPROVER_2 = {
avatar_url: '',
};
const NEW_APPROVER = {
id: 3,
name: 'name2',
state: 'active',
username: 'username2',
web_url: '',
avatar_url: '',
};
const APPROVERS = [APPROVER_1, APPROVER_2];
const APPROVERS_IDS = APPROVERS.map((approver) => approver.id);
......@@ -32,6 +46,7 @@ const ACTION = {
describe('PolicyActionBuilder', () => {
let wrapper;
let mock;
const factory = (propsData = {}) => {
wrapper = mount(PolicyActionBuilder, {
......@@ -47,14 +62,28 @@ describe('PolicyActionBuilder', () => {
};
const findApprovalsRequiredInput = () => wrapper.findComponent(GlFormInput);
const findAllGlTokens = () => wrapper.findAllComponents(GlToken);
const findApproversList = () => wrapper.findComponent(ApproversList);
const findAddApproversSelect = () => wrapper.findComponent(ApproversSelect);
const findAllApproversItem = () => wrapper.findAllComponents(ApproversListItem);
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('/api/undefined/projects/1/users').reply(200);
mock.onGet('/api/undefined/projects/1/groups.json').reply(200);
});
afterEach(() => {
wrapper.destroy();
mock.restore();
});
it('renders approvals required form input, gl-tokens', async () => {
it('renders approvals required form input, approvers list and approvers select', async () => {
factory();
await nextTick();
expect(findApprovalsRequiredInput().exists()).toBe(true);
expect(findAllGlTokens().length).toBe(APPROVERS.length);
expect(findApproversList().exists()).toBe(true);
expect(findAddApproversSelect().exists()).toBe(true);
});
it('triggers an update when changing approvals required', async () => {
......@@ -71,17 +100,17 @@ describe('PolicyActionBuilder', () => {
]);
});
it('removes one approver when triggering a gl-token', async () => {
it('removes one approver when triggering a remove button click', async () => {
factory();
await nextTick();
const allGlTokens = findAllGlTokens();
const glToken = allGlTokens.at(0);
const allApproversItems = findAllApproversItem();
const approversItem = allApproversItems.at(0);
const approversLengthMinusOne = APPROVERS.length - 1;
expect(allGlTokens.length).toBe(APPROVERS.length);
expect(allApproversItems.length).toBe(APPROVERS.length);
await glToken.vm.$emit('close', { ...APPROVER_1, type: 'user' });
await approversItem.vm.$emit('remove', { ...APPROVER_1, type: 'user' });
expect(wrapper.emitted().changed).toEqual([
[
......@@ -92,6 +121,29 @@ describe('PolicyActionBuilder', () => {
},
],
]);
expect(findAllGlTokens()).toHaveLength(approversLengthMinusOne);
expect(findAllApproversItem()).toHaveLength(approversLengthMinusOne);
});
it('adds one approver when triggering a new user is selected', async () => {
factory();
await nextTick();
const allApproversItems = findAllApproversItem();
const approversLengthPlusOne = APPROVERS.length + 1;
expect(allApproversItems.length).toBe(APPROVERS.length);
await findAddApproversSelect().vm.$emit('input', [{ ...NEW_APPROVER, type: 'user' }]);
expect(wrapper.emitted().changed).toEqual([
[
{
approvals_required: ACTION.approvals_required,
user_approvers_ids: [APPROVER_1.id, APPROVER_2.id, NEW_APPROVER.id],
group_approvers_ids: [],
},
],
]);
expect(findAllApproversItem()).toHaveLength(approversLengthPlusOne);
});
});
......@@ -31965,7 +31965,7 @@ msgstr ""
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} scan in an open merge request targeting the %{branches} branch(es) finds %{vulnerabilitiesAllowed} or more %{severities} vulnerabilities that are %{vulnerabilityStates}"
msgstr ""
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers: %{approvers}"
msgid "ScanResultPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require approval from %{approvalsRequired} of the following approvers:"
msgstr ""
msgid "ScanResultPolicy|add an approver"
......
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