Commit 26ea66b0 authored by Mark Florian's avatar Mark Florian

Merge branch '214993-add-license-compliance-tooltips' into 'master'

Resolve "Align license compliance `deny` classification outcome with user expectations - Add tooltip and text copy to policy tab"

See merge request gitlab-org/gitlab!34039
parents a64504a7 621e7b07
......@@ -57,9 +57,6 @@ export default {
hasEmptyState() {
return Boolean(!this.isJobSetUp || this.isJobFailed);
},
hasLicensePolicyList() {
return Boolean(this.glFeatures.licensePolicyList);
},
licenseCount() {
return this.pageInfo.total;
},
......@@ -134,31 +131,24 @@ export default {
<template v-else>{{ s__('Licenses|Specified policies in this project') }}</template>
</header>
<!-- TODO: Remove feature flag -->
<template v-if="hasLicensePolicyList">
<gl-tabs v-model="tabIndex" content-class="pt-0">
<gl-tab data-testid="licensesTab">
<template #title>
<span data-testid="licensesTabTitle">{{ s__('Licenses|Detected in Project') }}</span>
<gl-badge pill>{{ licenseCount }}</gl-badge>
</template>
<detected-licenses-table />
</gl-tab>
<gl-tabs v-model="tabIndex" content-class="pt-0">
<gl-tab data-testid="licensesTab">
<template #title>
<span data-testid="licensesTabTitle">{{ s__('Licenses|Detected in Project') }}</span>
<gl-badge pill>{{ licenseCount }}</gl-badge>
</template>
<gl-tab data-testid="policiesTab">
<template #title>
<span data-testid="policiesTabTitle">{{ s__('Licenses|Policies') }}</span>
<gl-badge pill>{{ policyCount }}</gl-badge>
</template>
<detected-licenses-table />
</gl-tab>
<license-management />
</gl-tab>
</gl-tabs>
</template>
<gl-tab data-testid="policiesTab">
<template #title>
<span data-testid="policiesTabTitle">{{ s__('Licenses|Policies') }}</span>
<gl-badge pill>{{ policyCount }}</gl-badge>
</template>
<template v-else>
<detected-licenses-table class="mt-3" />
</template>
<license-management />
</gl-tab>
</gl-tabs>
</div>
</template>
<script>
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { mapState, mapGetters, mapActions } from 'vuex';
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { GlButton, GlLoadingIcon, GlIcon, GlPopover } from '@gitlab/ui';
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { LICENSE_MANAGEMENT } from 'ee/vue_shared/license_compliance/store/constants';
import AddLicenseForm from './components/add_license_form.vue';
import AdminLicenseManagementRow from './components/admin_license_management_row.vue';
......@@ -20,6 +20,8 @@ export default {
LicenseManagementRow,
GlButton,
GlLoadingIcon,
GlIcon,
GlPopover,
PaginatedList,
LicenseApprovals,
},
......@@ -27,10 +29,6 @@ export default {
data() {
return {
formIsOpen: false,
tableHeaders: [
{ className: 'section-70', label: s__('Licenses|Policy') },
{ className: 'section-30', label: s__('Licenses|Name') },
],
};
},
computed: {
......@@ -46,6 +44,9 @@ export default {
showLoadingSpinner() {
return this.isLoadingManagedLicenses && !this.hasPendingLicenses;
},
isTooltipEnabled() {
return Boolean(this.glFeatures.licenseComplianceDeniesMr);
},
},
watch: {
isAddingNewLicense(isAddingNewLicense) {
......@@ -103,14 +104,40 @@ export default {
</div>
<template v-else>
<div
v-for="header in tableHeaders"
:key="header.label"
class="table-section"
:class="header.className"
role="rowheader"
>
{{ header.label }}
<div class="table-section gl-d-flex gl-pl-2 section-70" role="rowheader">
{{ s__('Licenses|Policy') }}
<template v-if="isTooltipEnabled">
<gl-icon
ref="reportInfo"
name="question"
class="text-info gl-ml-1 gl-cursor-pointer"
:aria-label="__('help')"
:size="14"
/>
<gl-popover
:target="() => $refs.reportInfo.$el"
placement="bottom"
triggers="click blur"
:css-classes="['gl-mt-3']"
>
<div class="h5">{{ __('Allowed') }}</div>
<span class="text-secondary">
{{ s__('Licenses|Acceptable license to be used in the project') }}</span
>
<div class="h5">{{ __('Denied') }}</div>
<span class="text-secondary">
{{
s__(
'Licenses|Disallow Merge request if detected and will instruct the developer to remove',
)
}}</span
>
</gl-popover>
</template>
</div>
<div class="table-section section-30" role="rowheader">
{{ s__('Licenses|Name') }}
</div>
</template>
</template>
......
......@@ -5,8 +5,8 @@ module Projects
before_action :authorize_read_licenses!, only: [:index]
before_action :authorize_admin_software_license_policy!, only: [:create, :update]
before_action do
push_frontend_feature_flag(:license_policy_list, default_enabled: true)
push_frontend_feature_flag(:license_approvals, default_enabled: false)
push_frontend_feature_flag(:license_compliance_denies_mr, default_enabled: false)
end
def index
......
......@@ -164,7 +164,7 @@ describe('Project Licenses', () => {
});
});
describe('when licensePolicyList feature flag is enabled', () => {
describe('when page is shown', () => {
beforeEach(() => {
createComponent({
state: {
......@@ -175,11 +175,6 @@ describe('Project Licenses', () => {
status: REPORT_STATUS.ok,
},
},
options: {
provide: {
glFeatures: { licensePolicyList: true },
},
},
});
});
......@@ -231,9 +226,6 @@ describe('Project Licenses', () => {
pageInfo: 1,
},
options: {
provide: {
glFeatures: { licensePolicyList: true },
},
mount: true,
},
});
......@@ -274,9 +266,6 @@ describe('Project Licenses', () => {
pageInfo,
},
options: {
provide: {
glFeatures: { licensePolicyList: true },
},
mount: true,
},
});
......@@ -334,43 +323,4 @@ describe('Project Licenses', () => {
});
});
});
describe('when licensePolicyList feature flag is disabled', () => {
beforeEach(() => {
createComponent({
state: {
initialized: true,
reportInfo: {
jobPath: '/',
generatedAt: '',
status: REPORT_STATUS.ok,
},
},
options: {
provide: {
glFeatures: { licensePolicyList: false },
},
},
});
});
it('only renders the "Detected in project" table', () => {
expect(wrapper.find(DetectedLicensesTable).exists()).toBe(true);
expect(wrapper.find(LicenseManagement).exists()).toBe(false);
});
it('renders no "Policies" table', () => {
expect(wrapper.find(GlTabs).exists()).toBe(false);
expect(wrapper.find(GlTab).exists()).toBe(false);
});
it('renders the pipeline info', () => {
expect(wrapper.find(PipelineInfo).exists()).toBe(true);
});
it('renders no tabs', () => {
expect(wrapper.find(GlTabs).exists()).toBe(false);
expect(wrapper.find(GlTab).exists()).toBe(false);
});
});
});
import { shallowMount } from '@vue/test-utils';
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { GlButton, GlLoadingIcon, GlIcon, GlPopover } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
import LicenseManagement from 'ee/vue_shared/license_compliance/license_management.vue';
......@@ -29,8 +29,10 @@ const PaginatedListMock = {
};
const noop = () => {};
const findIcon = () => wrapper.find(GlIcon);
const findPopover = () => wrapper.find(GlPopover);
const createComponent = ({ state, getters, props, actionMocks, isAdmin, options }) => {
const createComponent = ({ state, getters, props, actionMocks, isAdmin, options, provide }) => {
const fakeStore = new Vuex.Store({
modules: {
licenseManagement: {
......@@ -63,6 +65,10 @@ const createComponent = ({ state, getters, props, actionMocks, isAdmin, options
stubs: {
PaginatedList: PaginatedListMock,
},
provide: {
glFeatures: { licenseComplianceDeniesMr: false },
...provide,
},
store: fakeStore,
...options,
});
......@@ -190,6 +196,24 @@ describe('License Management', () => {
expect(wrapper.find(AdminLicenseManagementRow).exists()).toBe(true);
});
});
describe.each([true, false])(
'when licenseComplianceDeniesMr feature flag is %p',
licenseComplianceDeniesMr => {
it('should not show the developer only tooltip', () => {
createComponent({
state: { isLoadingManagedLicenses: false },
isAdmin: true,
provide: {
glFeatures: { licenseComplianceDeniesMr },
},
});
expect(findIcon().exists()).toBe(false);
expect(findPopover().exists()).toBe(false);
});
},
);
});
describe('when developer', () => {
......@@ -228,6 +252,28 @@ describe('License Management', () => {
expect(wrapper.find(AdminLicenseManagementRow).exists()).toBe(false);
});
});
describe.each`
licenseComplianceDeniesMr | should
${true} | ${'should'}
${false} | ${'should not'}
`(
'when licenseComplianceDeniesMr feature flag is $licenseComplianceDeniesMr',
({ licenseComplianceDeniesMr, should }) => {
it(`${should} show the developer only tooltip`, () => {
createComponent({
state: { isLoadingManagedLicenses: false },
isAdmin: false,
provide: {
glFeatures: { licenseComplianceDeniesMr },
},
});
expect(findIcon().exists()).toBe(licenseComplianceDeniesMr);
expect(findPopover().exists()).toBe(licenseComplianceDeniesMr);
});
},
);
});
});
});
......@@ -2238,6 +2238,9 @@ msgstr ""
msgid "Allow users to request access (if visibility is public or internal)"
msgstr ""
msgid "Allowed"
msgstr ""
msgid "Allowed Geo IP"
msgstr ""
......@@ -7441,6 +7444,9 @@ msgstr ""
msgid "Deletion pending. This project will be removed on %{date}. Repository and other project resources are read-only."
msgstr ""
msgid "Denied"
msgstr ""
msgid "Denied authorization of chat nickname %{user_name}."
msgstr ""
......@@ -13466,6 +13472,9 @@ msgstr ""
msgid "Licenses|%{remainingComponentsCount} more"
msgstr ""
msgid "Licenses|Acceptable license to be used in the project"
msgstr ""
msgid "Licenses|Component"
msgstr ""
......@@ -13478,6 +13487,9 @@ msgstr ""
msgid "Licenses|Detected licenses that are out-of-compliance with the project's assigned policies"
msgstr ""
msgid "Licenses|Disallow Merge request if detected and will instruct the developer to remove"
msgstr ""
msgid "Licenses|Displays licenses detected in the project, based on the %{linkStart}latest successful%{linkEnd} scan"
msgstr ""
......
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