Commit 483817ce authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch...

Merge branch '350440-add-promotion-callout-for-security-training-feature-to-security-dashboard-ui-only' into 'master'

Add security training promo to security dashboard

See merge request gitlab-org/gitlab!78432
parents 6b4a983d 2260e639
......@@ -2,7 +2,9 @@
import { GlLoadingIcon } from '@gitlab/ui';
import { GlLineChart } from '@gitlab/ui/dist/charts';
import projectsHistoryQuery from 'ee/security_dashboard/graphql/queries/project_vulnerabilities_by_day_and_count.query.graphql';
import SecurityTrainingPromo from 'ee/security_dashboard/components/shared/security_training_promo.vue';
import { PROJECT_LOADING_ERROR_MESSAGE } from 'ee/security_dashboard/helpers';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import createFlash from '~/flash';
import { formatDate, getDateInPast } from '~/lib/utils/datetime_utility';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
......@@ -26,9 +28,11 @@ export default {
components: {
ReportNotConfigured,
SecurityDashboardLayout,
SecurityTrainingPromo,
GlLoadingIcon,
GlLineChart,
},
mixins: [glFeatureFlagsMixin()],
props: {
projectFullPath: {
type: String,
......@@ -172,6 +176,7 @@ export default {
<report-not-configured />
</template>
<template v-else-if="shouldShowCharts" #default>
<security-training-promo v-if="glFeatures.secureVulnerabilityTraining" />
<gl-line-chart
class="gl-mt-6"
:width="chartWidth"
......
<script>
import { GlBanner } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlBanner,
},
inject: ['securityConfigurationPath'],
i18n: {
title: __('Reduce risk and triage fewer vulnerabilities with security training'),
buttonText: __('Enable security training'),
content: __(
'Enable security training to help your developers learn how to fix vulnerabilities. Developers can view security training from selected educational providers, relevant to the detected vulnerability.',
),
},
computed: {
buttonLink() {
return `${this.securityConfigurationPath}?tab=vulnerability-management`;
},
},
};
</script>
<template>
<gl-banner
:title="$options.i18n.title"
:button-text="$options.i18n.buttonText"
:button-link="buttonLink"
variant="introduction"
>
<p>{{ $options.i18n.content }}</p>
</gl-banner>
</template>
......@@ -12,6 +12,7 @@ module Projects
push_frontend_feature_flag(:security_auto_fix, project, default_enabled: false)
push_frontend_feature_flag(:vulnerability_management_survey, type: :ops, default_enabled: :yaml)
push_frontend_feature_flag(:sbom_survey, @user, default_enabled: :yaml)
push_frontend_feature_flag(:secure_vulnerability_training, @project, default_enabled: :yaml)
end
feature_category :vulnerability_management
......
......@@ -204,7 +204,8 @@ module EE
scanners: VulnerabilityScanners::ListService.new(project).execute.to_json,
can_admin_vulnerability: can?(current_user, :admin_vulnerability, project).to_s,
false_positive_doc_url: help_page_path('user/application_security/vulnerabilities/index'),
can_view_false_positive: can_view_false_positive?
can_view_false_positive: can_view_false_positive?,
security_configuration_path: project_security_configuration_path(@project)
}.merge!(security_dashboard_pipeline_data(project))
end
end
......
......@@ -5,6 +5,7 @@ import VueApollo from 'vue-apollo';
import ProjectSecurityDashboard from 'ee/security_dashboard/components/project/project_security_dashboard.vue';
import ReportNotConfigured from 'ee/security_dashboard/components/shared/empty_states/report_not_configured_project.vue';
import SecurityDashboardLayout from 'ee/security_dashboard/components/shared/security_dashboard_layout.vue';
import SecurityTrainingPromo from 'ee/security_dashboard/components/shared/security_training_promo.vue';
import projectsHistoryQuery from 'ee/security_dashboard/graphql/queries/project_vulnerabilities_by_day_and_count.query.graphql';
import { useFakeDate } from 'helpers/fake_date';
import createMockApollo from 'helpers/mock_apollo_helper';
......@@ -26,15 +27,21 @@ describe('Project Security Dashboard component', () => {
const projectFullPath = 'project/path';
const helpPath = 'docs/security/dashboard';
const findLineChart = () => wrapper.find(GlLineChart);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findEmptyState = () => wrapper.find(ReportNotConfigured);
const findLineChart = () => wrapper.findComponent(GlLineChart);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findEmptyState = () => wrapper.findComponent(ReportNotConfigured);
const findSecurityTrainingPromo = () => wrapper.findComponent(SecurityTrainingPromo);
const createApolloProvider = (...queries) => {
return createMockApollo([...queries]);
};
const createComponent = ({ query, propsData, chartWidth = 1024 }) => {
const createComponent = ({
query,
propsData,
chartWidth = 1024,
secureVulnerabilityTrainingEnabled = true,
}) => {
const component = shallowMount(ProjectSecurityDashboard, {
localVue,
apolloProvider: createApolloProvider([
......@@ -49,6 +56,9 @@ describe('Project Security Dashboard component', () => {
provide: {
// To be consumed by SecurityDashboardLayout
sbomSurveySvgPath: '/',
glFeatures: {
secureVulnerabilityTraining: secureVulnerabilityTrainingEnabled,
},
},
stubs: {
SecurityDashboardLayout,
......@@ -125,6 +135,10 @@ describe('Project Security Dashboard component', () => {
startValue: '2021-03-12',
});
});
it('contains a promotion for the security training feature', () => {
expect(findSecurityTrainingPromo().exists()).toBe(true);
});
});
describe('when there is no history data', () => {
......@@ -169,4 +183,18 @@ describe('Project Security Dashboard component', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
});
describe('with the "secureVulnerabilityTraining" feature flag disabled', () => {
it('does not contain a promotion for the security training feature', async () => {
wrapper = createComponent({
query: mockProjectSecurityChartsWithData(),
propsData: { hasVulnerabilities: true },
secureVulnerabilityTrainingEnabled: false,
});
await wrapper.vm.$nextTick();
expect(findSecurityTrainingPromo().exists()).toBe(false);
});
});
});
import { shallowMount } from '@vue/test-utils';
import { GlBanner } from '@gitlab/ui';
import SecurityTrainingPromo from 'ee/security_dashboard/components/shared/security_training_promo.vue';
const SECURITY_CONFIGURATION_PATH = 'foo/bar';
const VULNERABILITY_MANAGEMENT_TAB_NAME = 'vulnerability-management';
describe('Security training promo component', () => {
let wrapper;
const createWrapper = () =>
shallowMount(SecurityTrainingPromo, {
provide: {
securityConfigurationPath: SECURITY_CONFIGURATION_PATH,
},
});
beforeEach(() => {
wrapper = createWrapper();
});
afterEach(() => {
wrapper.destroy();
});
const findBanner = () => wrapper.findComponent(GlBanner);
describe('banner', () => {
it('should be an introduction that announces the security training feature', () => {
const { title, buttonText, content } = SecurityTrainingPromo.i18n;
expect(findBanner().props()).toMatchObject({
variant: 'introduction',
title,
buttonText,
});
expect(findBanner().text()).toBe(content);
});
it(`should link to the security configuration's vulnerability management tab`, () => {
expect(findBanner().props('buttonLink')).toBe(
`${SECURITY_CONFIGURATION_PATH}?tab=${VULNERABILITY_MANAGEMENT_TAB_NAME}`,
);
});
});
});
......@@ -228,7 +228,8 @@ RSpec.describe ProjectsHelper do
auto_fix_mrs_path: end_with('/merge_requests?label_name=GitLab-auto-fix'),
scanners: '[{"id":123,"vendor":"Security Vendor","report_type":"SAST"}]',
can_admin_vulnerability: 'true',
can_view_false_positive: 'false'
can_view_false_positive: 'false',
security_configuration_path: kind_of(String)
}
end
......
......@@ -13300,6 +13300,12 @@ msgstr ""
msgid "Enable repository checks"
msgstr ""
msgid "Enable security training"
msgstr ""
msgid "Enable security training to help your developers learn how to fix vulnerabilities. Developers can view security training from selected educational providers, relevant to the detected vulnerability."
msgstr ""
msgid "Enable shared runners for all projects and subgroups in this group."
msgstr ""
......@@ -29353,6 +29359,9 @@ msgstr ""
msgid "Reduce project visibility"
msgstr ""
msgid "Reduce risk and triage fewer vulnerabilities with security training"
msgstr ""
msgid "Reduce this project’s visibility?"
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