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