Skip to content
代码片段 群组 项目
提交 ee3c3318 编辑于 作者: Andrew Fontaine's avatar Andrew Fontaine 提交者: Heinrich Lee Yu
浏览文件

Refactor out new deployment approval rule form

This is going to be used as part of adding new approval rules to
existing protected environments. It handles the fetching of details for
selected access levels,  displaying avatars (if any), and setting
required approval counts for the rule.
上级 9f3da4bc
No related branches found
No related tags found
无相关合并请求
...@@ -268,6 +268,7 @@ class Application < Rails::Application ...@@ -268,6 +268,7 @@ class Application < Rails::Application
config.assets.precompile << "page_bundles/branches.css" config.assets.precompile << "page_bundles/branches.css"
config.assets.precompile << "page_bundles/build.css" config.assets.precompile << "page_bundles/build.css"
config.assets.precompile << "page_bundles/ci_status.css" config.assets.precompile << "page_bundles/ci_status.css"
config.assets.precompile << "page_bundles/ci_cd_settings.css"
config.assets.precompile << "page_bundles/cluster_agents.css" config.assets.precompile << "page_bundles/cluster_agents.css"
config.assets.precompile << "page_bundles/clusters.css" config.assets.precompile << "page_bundles/clusters.css"
config.assets.precompile << "page_bundles/cycle_analytics.css" config.assets.precompile << "page_bundles/cycle_analytics.css"
......
<script>
import { GlFormGroup, GlCollapse, GlAvatar, GlLink, GlFormInput } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { uniqueId } from 'lodash';
import Api from 'ee/api';
import { getUser } from '~/rest_api';
import { s__ } from '~/locale';
import AccessDropdown from '~/projects/settings/components/access_dropdown.vue';
import { ACCESS_LEVELS } from './constants';
const mapUserToApprover = (user) => ({
name: user.name,
entityName: user.name,
webUrl: user.web_url,
avatarUrl: user.avatar_url,
id: user.id,
avatarShape: 'circle',
approvals: 1,
inputDisabled: true,
type: 'user',
});
const mapGroupToApprover = (group) => ({
name: group.full_name,
entityName: group.name,
webUrl: group.web_url,
avatarUrl: group.avatar_url,
id: group.id,
avatarShape: 'rect',
approvals: 1,
type: 'group',
});
const MIN_APPROVALS_COUNT = 1;
const MAX_APPROVALS_COUNT = 5;
export default {
ACCESS_LEVELS,
components: {
GlFormGroup,
GlCollapse,
GlAvatar,
GlLink,
GlFormInput,
AccessDropdown,
},
inject: { accessLevelsData: { default: [] } },
props: {
disabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
approvers: [],
approverInfo: [],
uniqueId: uniqueId('deployment-approvers-'),
};
},
computed: {
approvalRules() {
return this.approverInfo.map((info) => {
switch (info.type) {
case 'user':
return { user_id: info.id, required_approvals: info.approvals };
case 'group':
return { group_id: info.id, required_approvals: info.approvals };
case 'access':
return { access_level: info.accessLevel, required_approvals: info.approvals };
default:
return {};
}
});
},
hasSelectedApprovers() {
return Boolean(this.approvers.length);
},
},
watch: {
async approvers() {
try {
this.$emit('error', '');
this.approverInfo = await Promise.all(
this.approvers.map((approver) => {
if (approver.user_id) {
return getUser(approver.user_id).then(({ data }) => mapUserToApprover(data));
}
if (approver.group_id) {
return Api.group(approver.group_id).then(mapGroupToApprover);
}
return Promise.resolve({
accessLevel: approver.access_level,
name: this.accessLevelsData.find(({ id }) => id === approver.access_level).text,
approvals: 1,
type: 'access',
});
}),
);
} catch (e) {
Sentry.captureException(e);
this.$emit(
'error',
s__(
'ProtectedEnvironments|An error occurred while fetching information on the selected approvers.',
),
);
}
},
approvalRules() {
this.$emit('change', this.approvalRules);
},
},
methods: {
updateApprovers(permissions) {
this.approvers = permissions;
},
isApprovalValid(approvals) {
const count = parseFloat(approvals);
return count >= MIN_APPROVALS_COUNT && count <= MAX_APPROVALS_COUNT;
},
approvalsId(index) {
return `${this.uniqueId}-${index}`;
},
},
i18n: {
approverLabel: s__('ProtectedEnvironment|Approvers'),
approverHelp: s__(
'ProtectedEnvironments|Set which groups, access levels or users are required to approve.',
),
approvalRulesLabel: s__('ProtectedEnvironments|Approval rules'),
approvalsInvalid: s__('ProtectedEnvironments|Number of approvals must be between 1 and 5'),
},
};
</script>
<template>
<div>
<gl-form-group
data-testid="create-approver-dropdown"
label-for="create-approver-dropdown"
:label="$options.i18n.approverLabel"
>
<template #label-description>
{{ $options.i18n.approverHelp }}
</template>
<access-dropdown
id="create-approver-dropdown"
:access-levels-data="accessLevelsData"
:access-level="$options.ACCESS_LEVELS.DEPLOY"
:disabled="disabled"
:preselected-items="approvers"
@hidden="updateApprovers"
/>
</gl-form-group>
<gl-collapse :visible="hasSelectedApprovers">
<span class="gl-font-weight-bold">{{ $options.i18n.approvalRulesLabel }}</span>
<div
data-testid="approval-rules"
class="protected-environment-approvers gl-display-grid gl-gap-5 gl-align-items-center"
>
<span class="protected-environment-approvers-label">{{ __('Approvers') }}</span>
<span>{{ __('Approvals required') }}</span>
<template v-for="(approver, index) in approverInfo">
<gl-avatar
v-if="approver.avatarShape"
:key="`${index}-avatar`"
:src="approver.avatarUrl"
:size="24"
:entity-id="approver.id"
:entity-name="approver.entityName"
:shape="approver.avatarShape"
/>
<span v-else :key="`${index}-avatar`" class="gl-w-6"></span>
<gl-link v-if="approver.webUrl" :key="`${index}-name`" :href="approver.webUrl">
{{ approver.name }}
</gl-link>
<span v-else :key="`${index}-name`">{{ approver.name }}</span>
<gl-form-group
:key="`${index}-approvals`"
:state="isApprovalValid(approver.approvals)"
:label="$options.i18n.approverLabel"
:label-for="approvalsId(index)"
label-sr-only
>
<gl-form-input
:id="approvalsId(index)"
v-model="approver.approvals"
:disabled="approver.inputDisabled"
:state="isApprovalValid(approver.approvals)"
:name="`approval-count-${approver.name}`"
type="number"
/>
<template #invalid-feedback>
{{ $options.i18n.approvalsInvalid }}
</template>
</gl-form-group>
</template>
</div>
</gl-collapse>
</div>
</template>
...@@ -6,61 +6,31 @@ import { ...@@ -6,61 +6,31 @@ import {
GlFormGroup, GlFormGroup,
GlCollapse, GlCollapse,
GlCollapsibleListbox, GlCollapsibleListbox,
GlAvatar,
GlLink, GlLink,
GlFormInput,
GlSprintf, GlSprintf,
} from '@gitlab/ui'; } from '@gitlab/ui';
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import Api from 'ee/api'; import Api from 'ee/api';
import { getUser } from '~/rest_api';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AccessDropdown from '~/projects/settings/components/access_dropdown.vue'; import AccessDropdown from '~/projects/settings/components/access_dropdown.vue';
import AddApprovers from './add_approvers.vue';
import { ACCESS_LEVELS } from './constants'; import { ACCESS_LEVELS } from './constants';
const mapUserToApprover = (user) => ({
name: user.name,
entityName: user.name,
webUrl: user.web_url,
avatarUrl: user.avatar_url,
id: user.id,
avatarShape: 'circle',
approvals: 1,
inputDisabled: true,
type: 'user',
});
const mapGroupToApprover = (group) => ({
name: group.full_name,
entityName: group.name,
webUrl: group.web_url,
avatarUrl: group.avatar_url,
id: group.id,
avatarShape: 'rect',
approvals: 1,
type: 'group',
});
const MIN_APPROVALS_COUNT = 1;
const MAX_APPROVALS_COUNT = 5;
export default { export default {
ACCESS_LEVELS, ACCESS_LEVELS,
components: { components: {
GlAlert, GlAlert,
GlAvatar,
GlButton, GlButton,
GlCard, GlCard,
GlCollapse, GlCollapse,
GlFormGroup, GlFormGroup,
GlCollapsibleListbox, GlCollapsibleListbox,
GlLink, GlLink,
GlFormInput,
GlSprintf, GlSprintf,
AccessDropdown, AccessDropdown,
AddApprovers,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
inject: { accessLevelsData: { default: [] }, apiLink: {}, docsLink: {} }, inject: { accessLevelsData: { default: [] }, apiLink: {}, docsLink: {} },
...@@ -84,7 +54,6 @@ export default { ...@@ -84,7 +54,6 @@ export default {
environments: [], environments: [],
environmentsLoading: false, environmentsLoading: false,
errorMessage: '', errorMessage: '',
approverInfo: [],
alertDismissed: false, alertDismissed: false,
}; };
}, },
...@@ -101,53 +70,6 @@ export default { ...@@ -101,53 +70,6 @@ export default {
hasSelectedEnvironment() { hasSelectedEnvironment() {
return Boolean(this.environment); return Boolean(this.environment);
}, },
hasSelectedApprovers() {
return Boolean(this.approvers.length);
},
approvalRules() {
return this.approverInfo.map((info) => {
switch (info.type) {
case 'user':
return { user_id: info.id, required_approvals: info.approvals };
case 'group':
return { group_id: info.id, required_approvals: info.approvals };
case 'access':
return { access_level: info.accessLevel, required_approvals: info.approvals };
default:
return {};
}
});
},
},
watch: {
async approvers() {
try {
this.errorMessage = '';
this.approverInfo = await Promise.all(
this.approvers.map((approver) => {
if (approver.user_id) {
return getUser(approver.user_id).then(({ data }) => mapUserToApprover(data));
}
if (approver.group_id) {
return Api.group(approver.group_id).then(mapGroupToApprover);
}
return Promise.resolve({
accessLevel: approver.access_level,
name: this.accessLevelsData.find(({ id }) => id === approver.access_level).text,
approvals: 1,
type: 'access',
});
}),
);
} catch (e) {
Sentry.captureException(e);
this.errorMessage = s__(
'ProtectedEnvironments|An error occurred while fetching information on the selected approvers.',
);
}
},
}, },
methods: { methods: {
updateDeployers(permissions) { updateDeployers(permissions) {
...@@ -187,7 +109,7 @@ export default { ...@@ -187,7 +109,7 @@ export default {
name: this.environment, name: this.environment,
deploy_access_levels: this.deployers, deploy_access_levels: this.deployers,
...(this.canCreateMultipleRules ...(this.canCreateMultipleRules
? { approval_rules: this.approvalRules } ? { approval_rules: this.approvers }
: { required_approval_count: this.approvals }), : { required_approval_count: this.approvals }),
}; };
Api.createProtectedEnvironment(this.projectId, protectedEnvironment) Api.createProtectedEnvironment(this.projectId, protectedEnvironment)
...@@ -199,10 +121,6 @@ export default { ...@@ -199,10 +121,6 @@ export default {
this.errorMessage = __('Failed to protect the environment'); this.errorMessage = __('Failed to protect the environment');
}); });
}, },
isApprovalValid(approvals) {
const count = parseFloat(approvals);
return count >= MIN_APPROVALS_COUNT && count <= MAX_APPROVALS_COUNT;
},
}, },
i18n: { i18n: {
unifiedRulesAlertHeader: s__( unifiedRulesAlertHeader: s__(
...@@ -215,16 +133,10 @@ export default { ...@@ -215,16 +133,10 @@ export default {
environmentLabel: s__('ProtectedEnvironment|Select environment'), environmentLabel: s__('ProtectedEnvironment|Select environment'),
environmentText: s__('ProtectedEnvironment|Select an environment'), environmentText: s__('ProtectedEnvironment|Select an environment'),
approvalLabel: s__('ProtectedEnvironment|Required approvals'), approvalLabel: s__('ProtectedEnvironment|Required approvals'),
approverLabel: s__('ProtectedEnvironment|Approvers'),
approverHelp: s__(
'ProtectedEnvironments|Set which groups, access levels or users are required to approve.',
),
deployerLabel: s__('ProtectedEnvironments|Allowed to deploy'), deployerLabel: s__('ProtectedEnvironments|Allowed to deploy'),
deployerHelp: s__( deployerHelp: s__(
'ProtectedEnvironments|Set which groups, access levels or users that are allowed to deploy to this environment', 'ProtectedEnvironments|Set which groups, access levels or users that are allowed to deploy to this environment',
), ),
approvalRulesLabel: s__('ProtectedEnvironments|Approval rules'),
approvalsInvalid: s__('ProtectedEnvironments|Number of approvals must be between 1 and 5'),
buttonText: s__('ProtectedEnvironment|Protect'), buttonText: s__('ProtectedEnvironment|Protect'),
}, },
APPROVAL_COUNT_OPTIONS: ['0', '1', '2', '3', '4', '5'].map((value) => ({ value, text: value })), APPROVAL_COUNT_OPTIONS: ['0', '1', '2', '3', '4', '5'].map((value) => ({ value, text: value })),
...@@ -291,65 +203,11 @@ export default { ...@@ -291,65 +203,11 @@ export default {
@hidden="updateDeployers" @hidden="updateDeployers"
/> />
</gl-form-group> </gl-form-group>
<gl-form-group <add-approvers
data-testid="create-approver-dropdown" :project-id="projectId"
label-for="create-approver-dropdown" @change="updateApprovers"
:label="$options.i18n.approverLabel" @error="errorMessage = $event"
> />
<template #label-description>
{{ $options.i18n.approverHelp }}
</template>
<access-dropdown
id="create-approver-dropdown"
:access-levels-data="accessLevelsData"
:access-level="$options.ACCESS_LEVELS.DEPLOY"
:disabled="disabled"
:preselected-items="approvers"
@hidden="updateApprovers"
/>
</gl-form-group>
<gl-collapse :visible="hasSelectedApprovers">
<span class="gl-font-weight-bold">{{ $options.i18n.approvalRulesLabel }}</span>
<div
data-testid="approval-rules"
class="protected-environment-approvers gl-display-grid gl-gap-5 gl-align-items-center"
>
<span class="protected-environment-approvers-label">{{ __('Approvers') }}</span>
<span>{{ __('Approvals required') }}</span>
<template v-for="(approver, index) in approverInfo">
<gl-avatar
v-if="approver.avatarShape"
:key="`${index}-avatar`"
:src="approver.avatarUrl"
:size="24"
:entity-id="approver.id"
:entity-name="approver.entityName"
:shape="approver.avatarShape"
/>
<span v-else :key="`${index}-avatar`" class="gl-w-6"></span>
<gl-link v-if="approver.webUrl" :key="`${index}-name`" :href="approver.webUrl">
{{ approver.name }}
</gl-link>
<span v-else :key="`${index}-name`">{{ approver.name }}</span>
<gl-form-group
:key="`${index}-approvals`"
:state="isApprovalValid(approver.approvals)"
>
<gl-form-input
v-model="approver.approvals"
:disabled="approver.inputDisabled"
:state="isApprovalValid(approver.approvals)"
:name="`approval-count-${approver.name}`"
type="number"
/>
<template #invalid-feedback>
{{ $options.i18n.approvalsInvalid }}
</template>
</gl-form-group>
</template>
</div>
</gl-collapse>
</gl-collapse> </gl-collapse>
</template> </template>
<template v-else> <template v-else>
...@@ -387,12 +245,3 @@ export default { ...@@ -387,12 +245,3 @@ export default {
</template> </template>
</gl-card> </gl-card>
</template> </template>
<style>
.protected-environment-approvers {
grid-template-columns: repeat(3, max-content);
}
.protected-environment-approvers-label {
grid-column: span 2;
}
</style>
.protected-environment-approvers {
grid-template-columns: repeat(3, max-content);
}
.protected-environment-approvers-label {
grid-column: span 2;
}
- add_page_specific_style 'page_bundles/ci_cd_settings'
.js-protected-environment-create-form{ data: { project_id: @project.id, .js-protected-environment-create-form{ data: { project_id: @project.id,
api_link: help_page_path('api/protected_environments.md'), api_link: help_page_path('api/protected_environments.md'),
docs_link: help_page_path('ci/environments/deployment_approvals.md', anchor: 'multiple-approval-rules') } } docs_link: help_page_path('ci/environments/deployment_approvals.md', anchor: 'multiple-approval-rules') } }
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { GlAvatar, GlFormInput } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
import AccessDropdown from '~/projects/settings/components/access_dropdown.vue';
import { ACCESS_LEVELS } from 'ee/protected_environments/constants';
import AddApprovers from 'ee/protected_environments/add_approvers.vue';
import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { __, s__ } from '~/locale';
const PROJECT_ID = '0';
describe('ee/protected_environments/add_approvers.vue', () => {
let wrapper;
let originalGon;
let mockAxios;
const createComponent = ({ projectId = PROJECT_ID, disabled = false } = {}) => {
wrapper = mountExtended(AddApprovers, {
propsData: {
projectId,
disabled,
},
provide: {
accessLevelsData: [
{
id: 40,
text: 'Maintainers',
before_divider: true,
},
{
id: 30,
text: 'Developers + Maintainers',
before_divider: true,
},
],
},
});
};
const findApproverDropdown = () => wrapper.findComponent(AccessDropdown);
const findRequiredCountForApprover = (name) =>
wrapper
.findAllComponents(GlFormInput)
.wrappers.find((w) => w.attributes('name') === `approval-count-${name}`);
beforeEach(() => {
originalGon = window.gon;
window.gon = {
...window.gon,
api_version: 'v4',
deploy_access_levels: {
roles: [],
},
};
mockAxios = new MockAdapter(axios);
});
afterEach(() => {
window.gon = originalGon;
});
it('renders a dropdown for selecting approvers', () => {
createComponent();
const approvers = findApproverDropdown();
expect(approvers.props()).toMatchObject({
accessLevel: ACCESS_LEVELS.DEPLOY,
label: __('Select users'),
});
});
it('emits an error if unable to fetch details for an approver', async () => {
mockAxios.onGet().replyOnce(HTTP_STATUS_BAD_REQUEST);
createComponent();
findApproverDropdown().vm.$emit('hidden', [{ group_id: 1 }]);
await waitForPromises();
const [[event]] = wrapper.emitted('error').reverse();
expect(event).toBe(
s__(
'ProtectedEnvironments|An error occurred while fetching information on the selected approvers.',
),
);
});
it('emits an empty error value when fetching new details', async () => {
createComponent();
findApproverDropdown().vm.$emit('hidden', [{ group_id: 1 }]);
await waitForPromises();
mockAxios.onGet('/api/v4/users/1').replyOnce(HTTP_STATUS_OK, {
name: 'root',
web_url: `${TEST_HOST}/root`,
avatar_url: '/root.png',
id: 1,
});
findApproverDropdown().vm.$emit('hidden', [{ user_id: 1 }]);
await waitForPromises();
const [[event]] = wrapper.emitted('error').reverse();
expect(event).toBe('');
});
describe('information for approvers', () => {
beforeEach(() => {
mockAxios.onGet('/api/v4/users/1').replyOnce(HTTP_STATUS_OK, {
name: 'root',
web_url: `${TEST_HOST}/root`,
avatar_url: '/root.png',
id: 1,
});
mockAxios.onGet('/api/v4/groups/1').replyOnce(HTTP_STATUS_OK, {
full_name: 'root / group',
name: 'group',
web_url: `${TEST_HOST}/root/group`,
avatar_url: '/root/group.png',
id: 1,
});
});
describe.each`
type | access | details
${'access level'} | ${{ access_level: 30 }} | ${{ name: 'Developers + Maintainers' }}
${'group'} | ${{ group_id: 1 }} | ${{ avatarUrl: '/root/group.png', href: `${TEST_HOST}/root/group`, name: 'root / group' }}
${'user'} | ${{ user_id: 1 }} | ${{ avatarUrl: '/root.png', href: `${TEST_HOST}/root`, name: 'root', inputDisabled: true }}
`('it displays correct information for $type', ({ access, details }) => {
beforeEach(async () => {
createComponent();
findApproverDropdown().vm.$emit('hidden', [access]);
await nextTick();
await waitForPromises();
});
if (details.href) {
it('should link to the entity', () => {
const link = wrapper.findByRole('link', { name: details.name });
expect(link.attributes('href')).toBe(details.href);
});
} else {
it('should display the name of the entity', () => {
expect(wrapper.text()).toContain(details.name);
});
}
if (details.avatarUrl) {
it('should show an avatar', () => {
const avatar = wrapper.findComponent(GlAvatar);
expect(avatar.props('src')).toBe(details.avatarUrl);
});
}
if (details.inputDisabled) {
it('should have the input disabled and set to 1', () => {
const input = findRequiredCountForApprover(details.name);
expect(input.element.value).toBe('1');
expect(input.attributes('disabled')).toBeDefined();
});
} else {
it('should not have the input disabled and set to 1', () => {
const input = findRequiredCountForApprover(details.name);
expect(input.element.value).toBe('1');
expect(input.attributes('disabled')).toBeUndefined();
});
}
it('emits approver info', async () => {
const input = findRequiredCountForApprover(details.name);
input.vm.$emit('input', 3);
await nextTick();
const [[[event]]] = wrapper.emitted('change').reverse();
expect(event).toEqual({ ...access, required_approvals: 3 });
});
});
});
});
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { GlAlert, GlCollapsibleListbox, GlFormInput, GlAvatar } from '@gitlab/ui'; import { GlAlert, GlCollapsibleListbox, GlFormInput } from '@gitlab/ui';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
...@@ -9,6 +9,7 @@ import Api from 'ee/api'; ...@@ -9,6 +9,7 @@ import Api from 'ee/api';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import AccessDropdown from '~/projects/settings/components/access_dropdown.vue'; import AccessDropdown from '~/projects/settings/components/access_dropdown.vue';
import { ACCESS_LEVELS } from 'ee/protected_environments/constants'; import { ACCESS_LEVELS } from 'ee/protected_environments/constants';
import AddApprovers from 'ee/protected_environments/add_approvers.vue';
import CreateProtectedEnvironment from 'ee/protected_environments/create_protected_environment.vue'; import CreateProtectedEnvironment from 'ee/protected_environments/create_protected_environment.vue';
import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
...@@ -32,12 +33,12 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { ...@@ -32,12 +33,12 @@ describe('ee/protected_environments/create_protected_environment.vue', () => {
wrapper.findByTestId('create-deployer-dropdown').findComponent(AccessDropdown); wrapper.findByTestId('create-deployer-dropdown').findComponent(AccessDropdown);
const findRequiredCountSelect = () => const findRequiredCountSelect = () =>
wrapper.findByTestId('create-approval-count').findComponent(GlCollapsibleListbox); wrapper.findByTestId('create-approval-count').findComponent(GlCollapsibleListbox);
const findRequredCountForApprover = (name) => const findRequiredCountForApprover = (name) =>
wrapper wrapper
.findAllComponents(GlFormInput) .findAllComponents(GlFormInput)
.wrappers.find((w) => w.attributes('name') === `approval-count-${name}`); .wrappers.find((w) => w.attributes('name') === `approval-count-${name}`);
const findApproverDropdown = () => const findAddApprovers = () => wrapper.findComponent(AddApprovers);
wrapper.findByTestId('create-approver-dropdown').findComponent(AccessDropdown); const findApproverDropdown = () => findAddApprovers().findComponent(AccessDropdown);
const findSubmitButton = () => const findSubmitButton = () =>
wrapper.findByRole('button', { name: s__('ProtectedEnvironment|Protect') }); wrapper.findByRole('button', { name: s__('ProtectedEnvironment|Protect') });
...@@ -99,6 +100,7 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { ...@@ -99,6 +100,7 @@ describe('ee/protected_environments/create_protected_environment.vue', () => {
findAccessDropdown().vm.$emit('hidden', deployAccessLevels); findAccessDropdown().vm.$emit('hidden', deployAccessLevels);
findEnvironmentsListbox().vm.$emit('select', name); findEnvironmentsListbox().vm.$emit('select', name);
findRequiredCountSelect().vm.$emit('select', requiredApprovalCount); findRequiredCountSelect().vm.$emit('select', requiredApprovalCount);
await findSubmitButton().vm.$emit('click'); await findSubmitButton().vm.$emit('click');
}; };
...@@ -196,7 +198,8 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { ...@@ -196,7 +198,8 @@ describe('ee/protected_environments/create_protected_environment.vue', () => {
findEnvironmentsListbox().vm.$emit('select', name); findEnvironmentsListbox().vm.$emit('select', name);
findApproverDropdown().vm.$emit('hidden', deployAccessLevels); findApproverDropdown().vm.$emit('hidden', deployAccessLevels);
await waitForPromises(); await waitForPromises();
findRequredCountForApprover('root').vm.$emit('input', requiredApprovalCount); findRequiredCountForApprover('root').vm.$emit('input', requiredApprovalCount);
await nextTick();
await findSubmitButton().vm.$emit('click'); await findSubmitButton().vm.$emit('click');
}; };
...@@ -264,11 +267,10 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { ...@@ -264,11 +267,10 @@ describe('ee/protected_environments/create_protected_environment.vue', () => {
it('renders a dropdown for selecting approvers', () => { it('renders a dropdown for selecting approvers', () => {
createComponent(); createComponent();
const approvers = findApproverDropdown(); const approvers = findAddApprovers();
expect(approvers.props()).toMatchObject({ expect(approvers.props()).toMatchObject({
accessLevel: ACCESS_LEVELS.DEPLOY, disabled: false,
label: __('Select users'),
}); });
}); });
...@@ -284,7 +286,7 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { ...@@ -284,7 +286,7 @@ describe('ee/protected_environments/create_protected_environment.vue', () => {
expect(Api.createProtectedEnvironment).toHaveBeenCalledWith(PROJECT_ID, { expect(Api.createProtectedEnvironment).toHaveBeenCalledWith(PROJECT_ID, {
deploy_access_levels: deployAccessLevels, deploy_access_levels: deployAccessLevels,
approval_rules: [{ user_id: 1, required_approvals: '3' }], approval_rules: [{ user_id: 1, required_approvals: requiredApprovalCount }],
name, name,
}); });
}); });
...@@ -312,71 +314,5 @@ describe('ee/protected_environments/create_protected_environment.vue', () => { ...@@ -312,71 +314,5 @@ describe('ee/protected_environments/create_protected_environment.vue', () => {
expect(window.location.reload).not.toHaveBeenCalled(); expect(window.location.reload).not.toHaveBeenCalled();
}); });
}); });
describe('information for approvers', () => {
unmockLocation();
beforeEach(() => {
mockAxios.onGet('/api/v4/users/1').replyOnce(HTTP_STATUS_OK, {
name: 'root',
web_url: `${TEST_HOST}/root`,
avatar_url: '/root.png',
id: 1,
});
mockAxios.onGet('/api/v4/groups/1').replyOnce(HTTP_STATUS_OK, {
full_name: 'root / group',
name: 'group',
web_url: `${TEST_HOST}/root/group`,
avatar_url: '/root/group.png',
id: 1,
});
});
describe.each`
type | access | details
${'access level'} | ${{ access_level: 30 }} | ${{ name: 'Developers + Maintainers' }}
${'group'} | ${{ group_id: 1 }} | ${{ avatarUrl: '/root/group.png', href: `${TEST_HOST}/root/group`, name: 'root / group' }}
${'user'} | ${{ user_id: 1 }} | ${{ avatarUrl: '/root.png', href: `${TEST_HOST}/root`, name: 'root', inputDisabled: true }}
`('it displays correct information for $type', ({ access, details }) => {
beforeEach(async () => {
createComponent();
findEnvironmentsListbox().vm.$emit('select', 'production');
findApproverDropdown().vm.$emit('hidden', [access]);
await waitForPromises();
await nextTick();
});
if (details.href) {
it('should link to the entity', () => {
const link = wrapper.findByRole('link', { name: details.name });
expect(link.attributes('href')).toBe(details.href);
});
} else {
it('should display the name of the entity', () => {
expect(wrapper.text()).toContain(details.name);
});
}
if (details.avatarUrl) {
it('should show an avatar', () => {
const avatar = wrapper.findComponent(GlAvatar);
expect(avatar.props('src')).toBe(details.avatarUrl);
});
}
if (details.inputDisabled) {
it('should have the input disabled and set to 1', () => {
const input = findRequredCountForApprover(details.name);
expect(input.element.value).toBe('1');
expect(input.attributes('disabled')).toBeDefined();
});
} else {
it('should not have the input disabled and set to 0', () => {
const input = findRequredCountForApprover(details.name);
expect(input.element.value).toBe('1');
expect(input.attributes('disabled')).toBeUndefined();
});
}
});
});
}); });
}); });
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册