Commit ec088cf1 authored by Fernando Arias's avatar Fernando Arias Committed by Mark Florian

Add tooltips for security approval rules

* Add tooltips for License-Check and Vulnerability-Check
parent deb72ebb
...@@ -42,9 +42,6 @@ class ProjectsController < Projects::ApplicationController ...@@ -42,9 +42,6 @@ class ProjectsController < Projects::ApplicationController
before_action only: [:edit] do before_action only: [:edit] do
push_frontend_feature_flag(:service_desk_custom_address, @project) push_frontend_feature_flag(:service_desk_custom_address, @project)
end
before_action only: [:edit] do
push_frontend_feature_flag(:approval_suggestions, @project) push_frontend_feature_flag(:approval_suggestions, @project)
end end
......
<script> <script>
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import RuleName from 'ee/approvals/components/rule_name.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { n__, sprintf } from '~/locale'; import { n__, sprintf } from '~/locale';
import { RULE_TYPE_ANY_APPROVER, RULE_TYPE_REGULAR } from '../../constants'; import { RULE_TYPE_ANY_APPROVER, RULE_TYPE_REGULAR } from '../../constants';
...@@ -20,6 +21,7 @@ export default { ...@@ -20,6 +21,7 @@ export default {
EmptyRule, EmptyRule,
RuleInput, RuleInput,
RuleBranches, RuleBranches,
RuleName,
UnconfiguredSecurityRules, UnconfiguredSecurityRules,
}, },
// TODO: Remove feature flag in https://gitlab.com/gitlab-org/gitlab/-/issues/235114 // TODO: Remove feature flag in https://gitlab.com/gitlab-org/gitlab/-/issues/235114
...@@ -102,7 +104,7 @@ export default { ...@@ -102,7 +104,7 @@ export default {
<rules :rules="rules"> <rules :rules="rules">
<template #thead="{ name, members, approvalsRequired, branches }"> <template #thead="{ name, members, approvalsRequired, branches }">
<tr class="d-none d-sm-table-row"> <tr class="d-none d-sm-table-row">
<th class="w-25">{{ hasNamedRule ? name : members }}</th> <th class="w-50">{{ hasNamedRule ? name : members }}</th>
<th :class="settings.allowMultiRule ? 'w-50 d-none d-sm-table-cell' : 'w-75'"> <th :class="settings.allowMultiRule ? 'w-50 d-none d-sm-table-cell' : 'w-75'">
<span v-if="hasNamedRule">{{ members }}</span> <span v-if="hasNamedRule">{{ members }}</span>
</th> </th>
...@@ -123,7 +125,9 @@ export default { ...@@ -123,7 +125,9 @@ export default {
:can-edit="canEdit(rule)" :can-edit="canEdit(rule)"
/> />
<tr v-else :key="index"> <tr v-else :key="index">
<td class="js-name">{{ rule.name }}</td> <td class="js-name">
<rule-name :name="rule.name" />
</td>
<td <td
class="js-members" class="js-members"
:class="settings.allowMultiRule ? 'd-none d-sm-table-cell' : null" :class="settings.allowMultiRule ? 'd-none d-sm-table-cell' : null"
......
<script>
import { GlLink, GlPopover, GlIcon } from '@gitlab/ui';
import {
LICENSE_CHECK_NAME,
VULNERABILITY_CHECK_NAME,
APPROVAL_RULE_CONFIGS,
} from 'ee/approvals/constants';
export default {
components: {
GlLink,
GlPopover,
GlIcon,
},
inject: {
vulnerabilityCheckHelpPagePath: {
from: 'vulnerabilityCheckHelpPagePath',
default: '',
},
licenseCheckHelpPagePath: {
from: 'licenseCheckHelpPagePath',
default: '',
},
},
props: {
name: {
type: String,
required: true,
},
},
computed: {
rulesWithTooltips() {
return {
[VULNERABILITY_CHECK_NAME]: {
description: APPROVAL_RULE_CONFIGS[VULNERABILITY_CHECK_NAME].popoverText,
linkPath: this.vulnerabilityCheckHelpPagePath,
},
[LICENSE_CHECK_NAME]: {
description: APPROVAL_RULE_CONFIGS[LICENSE_CHECK_NAME].popoverText,
linkPath: this.licenseCheckHelpPagePath,
},
};
},
description() {
return this.rulesWithTooltips[this.name]?.description;
},
linkPath() {
return this.rulesWithTooltips[this.name]?.linkPath;
},
},
};
</script>
<template>
<div class="gl-display-flex gl-align-items-center">
<span class="gl-mt-n2">{{ name }}</span>
<span v-if="description" class="gl-ml-3">
<gl-icon
ref="helpIcon"
name="question"
:aria-label="__('Help')"
:size="14"
class="author-link suggestion-help-hover"
/>
<gl-popover :target="() => $refs.helpIcon.$el" placement="top" triggers="hover focus">
<template #title>{{ __('Who can be an approver?') }}</template>
<p>{{ description }}</p>
<gl-link v-if="linkPath" :href="linkPath" class="gl-font-sm" target="_blank">{{
__('More information')
}}</gl-link>
</gl-popover>
</span>
</div>
</template>
<script> <script>
import { GlButton, GlLink, GlSprintf } from '@gitlab/ui'; import { GlButton, GlLink, GlSprintf } from '@gitlab/ui';
import RuleName from 'ee/approvals/components/rule_name.vue';
export default { export default {
components: { components: {
GlButton, GlButton,
GlLink, GlLink,
GlSprintf, GlSprintf,
RuleName,
}, },
props: { props: {
rule: { rule: {
...@@ -21,7 +23,8 @@ export default { ...@@ -21,7 +23,8 @@ export default {
<!-- Suggested approval rule creation row --> <!-- Suggested approval rule creation row -->
<template v-if="rule.hasConfiguredJob"> <template v-if="rule.hasConfiguredJob">
<td class="js-name" colspan="4"> <td class="js-name" colspan="4">
<div>{{ rule.name }}</div> <rule-name :name="rule.name" />
<div class="gl-text-gray-500"> <div class="gl-text-gray-500">
<gl-sprintf :message="rule.enableDescription"> <gl-sprintf :message="rule.enableDescription">
<template #link="{ content }"> <template #link="{ content }">
...@@ -39,7 +42,8 @@ export default { ...@@ -39,7 +42,8 @@ export default {
<!-- Approval rule suggestion when lacking appropriate CI job for the rule --> <!-- Approval rule suggestion when lacking appropriate CI job for the rule -->
<td v-else class="js-name" colspan="5"> <td v-else class="js-name" colspan="5">
<div>{{ rule.name }}</div> <rule-name :name="rule.name" />
<div class="gl-text-gray-500"> <div class="gl-text-gray-500">
<gl-sprintf :message="rule.description"> <gl-sprintf :message="rule.description">
<template #link="{ content }"> <template #link="{ content }">
......
...@@ -34,7 +34,7 @@ export const APPROVAL_RULE_CONFIGS = { ...@@ -34,7 +34,7 @@ export const APPROVAL_RULE_CONFIGS = {
[LICENSE_CHECK_NAME]: { [LICENSE_CHECK_NAME]: {
title: __('License-Check'), title: __('License-Check'),
popoverText: __( popoverText: __(
'A merge request approval is required when the license compliance report contains a blacklisted license.', 'A merge request approval is required when the license compliance report contains a denied license.',
), ),
documentationText: __('Learn more about License-Check'), documentationText: __('Learn more about License-Check'),
}, },
......
---
title: Show tooltips for License-Check and Vulnerability-Check approval rules
merge_request: 39579
author:
type: added
...@@ -5,6 +5,7 @@ import projectSettingsModule from 'ee/approvals/stores/modules/project_settings' ...@@ -5,6 +5,7 @@ import projectSettingsModule from 'ee/approvals/stores/modules/project_settings'
import ProjectRules from 'ee/approvals/components/project_settings/project_rules.vue'; import ProjectRules from 'ee/approvals/components/project_settings/project_rules.vue';
import RuleInput from 'ee/approvals/components/mr_edit/rule_input.vue'; import RuleInput from 'ee/approvals/components/mr_edit/rule_input.vue';
import UnconfiguredSecurityRules from 'ee/approvals/components/security_configuration/unconfigured_security_rules.vue'; import UnconfiguredSecurityRules from 'ee/approvals/components/security_configuration/unconfigured_security_rules.vue';
import RuleName from 'ee/approvals/components/rule_name.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 { createProjectRules } from '../../mocks'; import { createProjectRules } from '../../mocks';
...@@ -66,6 +67,8 @@ describe('Approvals ProjectRules', () => { ...@@ -66,6 +67,8 @@ describe('Approvals ProjectRules', () => {
approvalsRequired: rule.approvalsRequired, approvalsRequired: rule.approvalsRequired,
})), })),
); );
expect(wrapper.findAll(RuleName).length).toBe(rows.length);
}); });
it('should always have any_approver rule', () => { it('should always have any_approver rule', () => {
...@@ -93,6 +96,7 @@ describe('Approvals ProjectRules', () => { ...@@ -93,6 +96,7 @@ describe('Approvals ProjectRules', () => {
it('does not render name', () => { it('does not render name', () => {
expect(findCell(row, 'name').exists()).toBe(false); expect(findCell(row, 'name').exists()).toBe(false);
expect(wrapper.find(RuleName).exists()).toBe(false);
}); });
it('should only display 1 rule', () => { it('should only display 1 rule', () => {
...@@ -137,9 +141,7 @@ describe('Approvals ProjectRules', () => { ...@@ -137,9 +141,7 @@ describe('Approvals ProjectRules', () => {
rules[0].name = 'Vulnerability-Check'; rules[0].name = 'Vulnerability-Check';
store.modules.approvals.state.rules = rules; store.modules.approvals.state.rules = rules;
store.state.settings.allowMultiRule = true; store.state.settings.allowMultiRule = true;
});
beforeEach(() => {
factory( factory(
{}, {},
{ {
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { GlLink, GlPopover, GlIcon } from '@gitlab/ui';
import RuleName from 'ee/approvals/components/rule_name.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('RuleName component', () => {
let wrapper;
const createWrapper = (props = {}) => {
wrapper = shallowMount(RuleName, {
localVue,
propsData: {
...props,
},
provide: {
vulnerabilityCheckHelpPagePath: '/vuln-check-docs',
licenseCheckHelpPagePath: '/liceene-check-docs',
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe.each`
name | hasTooltip | hasLink
${'Foo'} | ${false} | ${false}
${'Vulnerability-Check'} | ${true} | ${true}
${'License-Check'} | ${true} | ${true}
`('with job name set to $name', ({ name, hasTooltip, hasLink }) => {
beforeEach(() => {
createWrapper({ name });
});
it(`should ${hasTooltip ? '' : 'not'} render the tooltip`, () => {
expect(wrapper.find(GlPopover).exists()).toBe(hasTooltip);
expect(wrapper.find(GlIcon).exists()).toBe(hasTooltip);
});
it(`should ${hasLink ? '' : 'not'} render the tooltip more info link`, () => {
expect(wrapper.find(GlLink).exists()).toBe(hasLink);
});
it('should render the name', () => {
expect(wrapper.text()).toContain(name);
});
});
});
...@@ -1162,7 +1162,7 @@ msgstr "" ...@@ -1162,7 +1162,7 @@ msgstr ""
msgid "A merge request approval is required when a security report contains a new vulnerability of high, critical, or unknown severity." msgid "A merge request approval is required when a security report contains a new vulnerability of high, critical, or unknown severity."
msgstr "" msgstr ""
msgid "A merge request approval is required when the license compliance report contains a blacklisted license." msgid "A merge request approval is required when the license compliance report contains a denied license."
msgstr "" msgstr ""
msgid "A new Auto DevOps pipeline has been created, go to %{pipelines_link_start}Pipelines page%{pipelines_link_end} for details" msgid "A new Auto DevOps pipeline has been created, go to %{pipelines_link_start}Pipelines page%{pipelines_link_end} for details"
......
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