From 177242a031ee8413e777d28252086b07d218fc30 Mon Sep 17 00:00:00 2001 From: Samantha Ming <sming@gitlab.com> Date: Sun, 24 Apr 2022 15:24:50 -0700 Subject: [PATCH] Display security training config based on license Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/357327 Changelog: changed --- .../security_configuration/components/app.vue | 16 ++++++- .../components/constants.js | 4 ++ .../graphql/current_license.query.graphql | 6 +++ .../components/app_spec.js | 47 +++++++++++++++++-- .../security_configuration/mock_data.js | 9 ++++ 5 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/security_configuration/graphql/current_license.query.graphql diff --git a/app/assets/javascripts/security_configuration/components/app.vue b/app/assets/javascripts/security_configuration/components/app.vue index ff0e0ed0ab2b5..866148596e381 100644 --- a/app/assets/javascripts/security_configuration/components/app.vue +++ b/app/assets/javascripts/security_configuration/components/app.vue @@ -4,9 +4,10 @@ import { __, s__ } from '~/locale'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue'; import SectionLayout from '~/vue_shared/security_configuration/components/section_layout.vue'; +import currentLicenseQuery from '~/security_configuration/graphql/current_license.query.graphql'; import AutoDevOpsAlert from './auto_dev_ops_alert.vue'; import AutoDevOpsEnabledAlert from './auto_dev_ops_enabled_alert.vue'; -import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY } from './constants'; +import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY, LICENSE_ULTIMATE } from './constants'; import FeatureCard from './feature_card.vue'; import TrainingProviderList from './training_provider_list.vue'; import UpgradeBanner from './upgrade_banner.vue'; @@ -50,6 +51,14 @@ export default { TrainingProviderList, }, inject: ['projectFullPath', 'vulnerabilityTrainingDocsPath'], + apollo: { + currentLicensePlan: { + query: currentLicenseQuery, + update({ currentLicense }) { + return currentLicense?.plan; + }, + }, + }, props: { augmentedSecurityFeatures: { type: Array, @@ -89,6 +98,7 @@ export default { return { autoDevopsEnabledAlertDismissedProjects: [], errorMessage: '', + currentLicensePlan: '', }; }, computed: { @@ -109,6 +119,9 @@ export default { !this.autoDevopsEnabledAlertDismissedProjects.includes(this.projectFullPath) ); }, + shouldShowVulnerabilityManagementTab() { + return this.currentLicensePlan === LICENSE_ULTIMATE; + }, }, methods: { dismissAutoDevopsEnabledAlert() { @@ -250,6 +263,7 @@ export default { </section-layout> </gl-tab> <gl-tab + v-if="shouldShowVulnerabilityManagementTab" data-testid="vulnerability-management-tab" :title="$options.i18n.vulnerabilityManagement" query-param-value="vulnerability-management" diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js index 6db28ef0fadc6..2fa14d4fc53de 100644 --- a/app/assets/javascripts/security_configuration/components/constants.js +++ b/app/assets/javascripts/security_configuration/components/constants.js @@ -310,3 +310,7 @@ export const TEMP_PROVIDER_URLS = { Kontra: 'https://application.security/', [__('Secure Code Warrior')]: 'https://www.securecodewarrior.com/', }; + +export const LICENSE_ULTIMATE = 'ultimate'; +export const LICENSE_FREE = 'free'; +export const LICENSE_PREMIUM = 'premium'; diff --git a/app/assets/javascripts/security_configuration/graphql/current_license.query.graphql b/app/assets/javascripts/security_configuration/graphql/current_license.query.graphql new file mode 100644 index 0000000000000..9ab4f4d434759 --- /dev/null +++ b/app/assets/javascripts/security_configuration/graphql/current_license.query.graphql @@ -0,0 +1,6 @@ +query getCurrentLicensePlan { + currentLicense { + id + plan + } +} diff --git a/spec/frontend/security_configuration/components/app_spec.js b/spec/frontend/security_configuration/components/app_spec.js index f18225f124618..a983ec4355715 100644 --- a/spec/frontend/security_configuration/components/app_spec.js +++ b/spec/frontend/security_configuration/components/app_spec.js @@ -1,6 +1,8 @@ +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; import { GlTab, GlTabs, GlLink } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; -import { nextTick } from 'vue'; + import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser'; import stubChildren from 'helpers/stub_children'; @@ -18,15 +20,22 @@ import { LICENSE_COMPLIANCE_DESCRIPTION, LICENSE_COMPLIANCE_HELP_PATH, AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY, + LICENSE_ULTIMATE, + LICENSE_PREMIUM, + LICENSE_FREE, } from '~/security_configuration/components/constants'; import FeatureCard from '~/security_configuration/components/feature_card.vue'; import TrainingProviderList from '~/security_configuration/components/training_provider_list.vue'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import currentLicenseQuery from '~/security_configuration/graphql/current_license.query.graphql'; +import waitForPromises from 'helpers/wait_for_promises'; import UpgradeBanner from '~/security_configuration/components/upgrade_banner.vue'; import { REPORT_TYPE_LICENSE_COMPLIANCE, REPORT_TYPE_SAST, } from '~/vue_shared/security_reports/constants'; +import { getCurrentLicensePlanResponse } from '../mock_data'; const upgradePath = '/upgrade'; const autoDevopsHelpPagePath = '/autoDevopsHelpPagePath'; @@ -36,14 +45,24 @@ const projectFullPath = 'namespace/project'; const vulnerabilityTrainingDocsPath = 'user/application_security/vulnerabilities/index'; useLocalStorageSpy(); +Vue.use(VueApollo); describe('App component', () => { let wrapper; let userCalloutDismissSpy; + let mockApollo; - const createComponent = ({ shouldShowCallout = true, ...propsData }) => { + const createComponent = ({ + shouldShowCallout = true, + license = LICENSE_ULTIMATE, + ...propsData + }) => { userCalloutDismissSpy = jest.fn(); + mockApollo = createMockApollo([ + [currentLicenseQuery, jest.fn().mockResolvedValue(getCurrentLicensePlanResponse(license))], + ]); + wrapper = extendedWrapper( mount(SecurityConfigurationApp, { propsData, @@ -54,6 +73,7 @@ describe('App component', () => { projectFullPath, vulnerabilityTrainingDocsPath, }, + apolloProvider: mockApollo, stubs: { ...stubChildren(SecurityConfigurationApp), GlLink: false, @@ -128,14 +148,16 @@ describe('App component', () => { afterEach(() => { wrapper.destroy(); + mockApollo = null; }); describe('basic structure', () => { - beforeEach(() => { + beforeEach(async () => { createComponent({ augmentedSecurityFeatures: securityFeaturesMock, augmentedComplianceFeatures: complianceFeaturesMock, }); + await waitForPromises(); }); it('renders main-heading with correct text', () => { @@ -438,11 +460,12 @@ describe('App component', () => { }); describe('Vulnerability management', () => { - beforeEach(() => { + beforeEach(async () => { createComponent({ augmentedSecurityFeatures: securityFeaturesMock, augmentedComplianceFeatures: complianceFeaturesMock, }); + await waitForPromises(); }); it('renders TrainingProviderList component', () => { @@ -459,5 +482,21 @@ describe('App component', () => { expect(trainingLink.text()).toBe('Learn more about vulnerability training'); expect(trainingLink.attributes('href')).toBe(vulnerabilityTrainingDocsPath); }); + + it.each` + license | display + ${LICENSE_ULTIMATE} | ${true} + ${LICENSE_PREMIUM} | ${false} + ${LICENSE_FREE} | ${false} + ${null} | ${false} + `('displays $display for license $license', async ({ license, display }) => { + createComponent({ + license, + augmentedSecurityFeatures: securityFeaturesMock, + augmentedComplianceFeatures: complianceFeaturesMock, + }); + await waitForPromises(); + expect(findVulnerabilityManagementTab().exists()).toBe(display); + }); }); }); diff --git a/spec/frontend/security_configuration/mock_data.js b/spec/frontend/security_configuration/mock_data.js index 18a480bf08223..94a36472a1d0b 100644 --- a/spec/frontend/security_configuration/mock_data.js +++ b/spec/frontend/security_configuration/mock_data.js @@ -111,3 +111,12 @@ export const tempProviderLogos = { svg: `<svg>${[testProviderName[1]]}</svg>`, }, }; + +export const getCurrentLicensePlanResponse = (plan) => ({ + data: { + currentLicense: { + id: 'gid://gitlab/License/1', + plan, + }, + }, +}); -- GitLab