Commit 11464845 authored by Markus Koller's avatar Markus Koller

Merge branch '223924-remove-banner' into 'master'

Remove the introduction banner once and for all

See merge request gitlab-org/gitlab!38967
parents e676795f e02f19e2
<script> <script>
import { GlBanner } from '@gitlab/ui';
import Cookies from 'js-cookie';
import { parseBoolean } from '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
import ProjectVulnerabilitiesApp from './project_vulnerabilities.vue'; import ProjectVulnerabilitiesApp from './project_vulnerabilities.vue';
import ReportsNotConfigured from './empty_states/reports_not_configured.vue'; import ReportsNotConfigured from './empty_states/reports_not_configured.vue';
import SecurityDashboardLayout from './security_dashboard_layout.vue'; import SecurityDashboardLayout from './security_dashboard_layout.vue';
...@@ -20,7 +16,6 @@ export default { ...@@ -20,7 +16,6 @@ export default {
VulnerabilitiesCountList, VulnerabilitiesCountList,
CsvExportButton, CsvExportButton,
Filters, Filters,
GlBanner,
}, },
props: { props: {
securityDashboardHelpPath: { securityDashboardHelpPath: {
...@@ -42,25 +37,10 @@ export default { ...@@ -42,25 +37,10 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
showIntroductionBanner: {
type: Boolean,
required: true,
},
userCalloutId: {
type: String,
required: false,
default: '',
},
userCalloutsPath: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
return { return {
filters: {}, filters: {},
isBannerVisible: this.showIntroductionBanner && !parseBoolean(Cookies.get(BANNER_COOKIE_KEY)), // The and statement is for backward compatibility. See https://gitlab.com/gitlab-org/gitlab/-/issues/213671 for more information.
}; };
}, },
inject: ['dashboardDocumentation'], inject: ['dashboardDocumentation'],
...@@ -68,13 +48,6 @@ export default { ...@@ -68,13 +48,6 @@ export default {
handleFilterChange(filters) { handleFilterChange(filters) {
this.filters = filters; this.filters = filters;
}, },
handleBannerClose() {
this.isBannerVisible = false;
axios.post(this.userCalloutsPath, {
feature_name: this.userCalloutId,
});
},
}, },
}; };
</script> </script>
...@@ -84,23 +57,6 @@ export default { ...@@ -84,23 +57,6 @@ export default {
<template v-if="hasVulnerabilities"> <template v-if="hasVulnerabilities">
<security-dashboard-layout> <security-dashboard-layout>
<template #header> <template #header>
<gl-banner
v-if="isBannerVisible"
class="mt-4"
variant="introduction"
:title="s__('SecurityReports|Introducing standalone vulnerabilities')"
:button-text="s__('SecurityReports|Learn more')"
:button-link="dashboardDocumentation"
@close="handleBannerClose"
>
<div class="mb-2">
{{
s__(
'SecurityReports|Each vulnerability now has a unique page that can be directly linked to, shared, referenced, and tracked as the single source of truth. Vulnerability occurrences also persist across scanner runs, which improves tracking and visibility and reduces duplicates between scans.',
)
}}
</div>
</gl-banner>
<div class="mt-4 d-flex"> <div class="mt-4 d-flex">
<h4 class="flex-grow mt-0 mb-0">{{ __('Vulnerabilities') }}</h4> <h4 class="flex-grow mt-0 mb-0">{{ __('Vulnerabilities') }}</h4>
<csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" /> <csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" />
......
import Vue from 'vue'; import Vue from 'vue';
import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants'; import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
import { parseBoolean } from '~/lib/utils/common_utils';
import FirstClassProjectSecurityDashboard from './components/first_class_project_security_dashboard.vue'; import FirstClassProjectSecurityDashboard from './components/first_class_project_security_dashboard.vue';
import FirstClassGroupSecurityDashboard from './components/first_class_group_security_dashboard.vue'; import FirstClassGroupSecurityDashboard from './components/first_class_group_security_dashboard.vue';
import FirstClassInstanceSecurityDashboard from './components/first_class_instance_security_dashboard.vue'; import FirstClassInstanceSecurityDashboard from './components/first_class_instance_security_dashboard.vue';
...@@ -42,9 +41,6 @@ export default (el, dashboardType) => { ...@@ -42,9 +41,6 @@ export default (el, dashboardType) => {
if (dashboardType === DASHBOARD_TYPES.PROJECT) { if (dashboardType === DASHBOARD_TYPES.PROJECT) {
component = FirstClassProjectSecurityDashboard; component = FirstClassProjectSecurityDashboard;
props.projectFullPath = el.dataset.projectFullPath; props.projectFullPath = el.dataset.projectFullPath;
props.userCalloutId = el.dataset.userCalloutId;
props.userCalloutsPath = el.dataset.userCalloutsPath;
props.showIntroductionBanner = parseBoolean(el.dataset.showIntroductionBanner);
} else if (dashboardType === DASHBOARD_TYPES.GROUP) { } else if (dashboardType === DASHBOARD_TYPES.GROUP) {
component = FirstClassGroupSecurityDashboard; component = FirstClassGroupSecurityDashboard;
props.groupFullPath = el.dataset.groupFullPath; props.groupFullPath = el.dataset.groupFullPath;
......
...@@ -198,10 +198,7 @@ module EE ...@@ -198,10 +198,7 @@ module EE
dashboard_documentation: help_page_path('user/application_security/security_dashboard/index'), dashboard_documentation: help_page_path('user/application_security/security_dashboard/index'),
not_enabled_scanners_help_path: help_page_path('user/application_security/index', anchor: 'quick-start'), not_enabled_scanners_help_path: help_page_path('user/application_security/index', anchor: 'quick-start'),
no_pipeline_run_scanners_help_path: new_project_pipeline_path(project), no_pipeline_run_scanners_help_path: new_project_pipeline_path(project),
security_dashboard_help_path: help_page_path('user/application_security/security_dashboard/index'), security_dashboard_help_path: help_page_path('user/application_security/security_dashboard/index')
user_callouts_path: user_callouts_path,
user_callout_id: UserCalloutsHelper::STANDALONE_VULNERABILITIES_INTRODUCTION_BANNER,
show_introduction_banner: show_standalone_vulnerabilities_introduction_banner?.to_s
} }
end end
end end
......
...@@ -12,7 +12,6 @@ module EE ...@@ -12,7 +12,6 @@ module EE
THREAT_MONITORING_INFO = 'threat_monitoring_info' THREAT_MONITORING_INFO = 'threat_monitoring_info'
ACCOUNT_RECOVERY_REGULAR_CHECK = 'account_recovery_regular_check' ACCOUNT_RECOVERY_REGULAR_CHECK = 'account_recovery_regular_check'
USERS_OVER_LICENSE_BANNER = 'users_over_license_banner' USERS_OVER_LICENSE_BANNER = 'users_over_license_banner'
STANDALONE_VULNERABILITIES_INTRODUCTION_BANNER = 'standalone_vulnerabilities_introduction_banner'
ACTIVE_USER_COUNT_THRESHOLD = 'active_user_count_threshold' ACTIVE_USER_COUNT_THRESHOLD = 'active_user_count_threshold'
PERSONAL_ACCESS_TOKEN_EXPIRY = 'personal_access_token_expiry' PERSONAL_ACCESS_TOKEN_EXPIRY = 'personal_access_token_expiry'
...@@ -84,10 +83,6 @@ module EE ...@@ -84,10 +83,6 @@ module EE
!user_dismissed?(THREAT_MONITORING_INFO) !user_dismissed?(THREAT_MONITORING_INFO)
end end
def show_standalone_vulnerabilities_introduction_banner?
!user_dismissed?(STANDALONE_VULNERABILITIES_INTRODUCTION_BANNER)
end
def show_token_expiry_notification? def show_token_expiry_notification?
return false unless current_user return false unless current_user
......
...@@ -20,7 +20,6 @@ module EE ...@@ -20,7 +20,6 @@ module EE
threat_monitoring_info: 11, threat_monitoring_info: 11,
account_recovery_regular_check: 12, account_recovery_regular_check: 12,
users_over_license_banner: 16, users_over_license_banner: 16,
standalone_vulnerabilities_introduction_banner: 17,
active_user_count_threshold: 18, active_user_count_threshold: 18,
buy_pipeline_minutes_notification_dot: 19 buy_pipeline_minutes_notification_dot: 19
) )
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlBanner } from '@gitlab/ui'; import { GlBanner } from '@gitlab/ui';
import Cookies from 'js-cookie'; import FirstClassProjectSecurityDashboard from 'ee/security_dashboard/components/first_class_project_security_dashboard.vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises';
import { TEST_HOST } from 'helpers/test_constants';
import FirstClassProjectSecurityDashboard, {
BANNER_COOKIE_KEY,
} from 'ee/security_dashboard/components/first_class_project_security_dashboard.vue';
import Filters from 'ee/security_dashboard/components/first_class_vulnerability_filters.vue'; import Filters from 'ee/security_dashboard/components/first_class_vulnerability_filters.vue';
import SecurityDashboardLayout from 'ee/security_dashboard/components/security_dashboard_layout.vue'; import SecurityDashboardLayout from 'ee/security_dashboard/components/security_dashboard_layout.vue';
import ProjectVulnerabilitiesApp from 'ee/security_dashboard/components/project_vulnerabilities.vue'; import ProjectVulnerabilitiesApp from 'ee/security_dashboard/components/project_vulnerabilities.vue';
...@@ -20,9 +13,6 @@ const props = { ...@@ -20,9 +13,6 @@ const props = {
projectFullPath: '/group/project', projectFullPath: '/group/project',
securityDashboardHelpPath: '/security/dashboard/help-path', securityDashboardHelpPath: '/security/dashboard/help-path',
vulnerabilitiesExportEndpoint: '/vulnerabilities/exports', vulnerabilitiesExportEndpoint: '/vulnerabilities/exports',
userCalloutId: 'standalone_vulnerabilities_introduction_banner',
userCalloutsPath: `${TEST_HOST}/user_callouts`,
showIntroductionBanner: false,
}; };
const provide = { const provide = {
...@@ -39,7 +29,6 @@ describe('First class Project Security Dashboard component', () => { ...@@ -39,7 +29,6 @@ describe('First class Project Security Dashboard component', () => {
const findVulnerabilities = () => wrapper.find(ProjectVulnerabilitiesApp); const findVulnerabilities = () => wrapper.find(ProjectVulnerabilitiesApp);
const findUnconfiguredState = () => wrapper.find(ReportsNotConfigured); const findUnconfiguredState = () => wrapper.find(ReportsNotConfigured);
const findCsvExportButton = () => wrapper.find(CsvExportButton); const findCsvExportButton = () => wrapper.find(CsvExportButton);
const findIntroductionBanner = () => wrapper.find(GlBanner);
const createComponent = options => { const createComponent = options => {
wrapper = shallowMount(FirstClassProjectSecurityDashboard, { wrapper = shallowMount(FirstClassProjectSecurityDashboard, {
...@@ -86,60 +75,6 @@ describe('First class Project Security Dashboard component', () => { ...@@ -86,60 +75,6 @@ describe('First class Project Security Dashboard component', () => {
}); });
}); });
describe('when user visits for the first time', () => {
let mockAxios;
beforeEach(() => {
mockAxios = new MockAdapter(axios);
mockAxios.onPost(props.userCalloutsPath, { feature_name: props.userCalloutId }).reply(200);
createComponent({ props: { hasVulnerabilities: true, showIntroductionBanner: true } });
});
afterEach(() => {
mockAxios.restore();
});
it('displays a banner which the title highlights the new functionality', () => {
expect(findIntroductionBanner().text()).toContain('Introducing standalone vulnerabilities');
});
it('displays a banner which the content describes the new functionality', () => {
expect(findIntroductionBanner().text()).toContain(
'Each vulnerability now has a unique page that can be directly linked to, shared, referenced, and tracked as the single source of truth. Vulnerability occurrences also persist across scanner runs, which improves tracking and visibility and reduces duplicates between scans.',
);
});
it('links the banner to the proper documentation page', () => {
expect(findIntroductionBanner().props('buttonLink')).toBe(provide.dashboardDocumentation);
});
it('hides the banner when the user clicks on the dismiss button', () => {
findIntroductionBanner()
.find('button.close')
.trigger('click');
return waitForPromises().then(() => {
expect(findIntroductionBanner().exists()).toBe(false);
expect(mockAxios.history.post).toHaveLength(1);
});
});
});
describe('when user already dismissed the banner in the past', () => {
beforeEach(() => {
Cookies.set(BANNER_COOKIE_KEY, 'true');
createComponent({ props: { hasVulnerabilities: true, showIntroductionBanner: true } });
});
afterEach(() => {
Cookies.remove(BANNER_COOKIE_KEY);
});
it('does not display the banner despite showIntroductionBanner is true', () => {
expect(findIntroductionBanner().exists()).toBe(false);
});
});
describe('with filter data', () => { describe('with filter data', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
......
...@@ -384,26 +384,4 @@ RSpec.describe EE::UserCalloutsHelper do ...@@ -384,26 +384,4 @@ RSpec.describe EE::UserCalloutsHelper do
end end
end end
end end
describe '.show_standalone_vulnerabilities_introduction_banner?' do
subject { helper.show_standalone_vulnerabilities_introduction_banner? }
let(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'when the introduction banner has not been dismissed' do
it { is_expected.to be_truthy }
end
context 'when the introduction banner was dismissed' do
before do
create(:user_callout, user: user, feature_name: described_class::STANDALONE_VULNERABILITIES_INTRODUCTION_BANNER)
end
it { is_expected.to be_falsy }
end
end
end end
...@@ -157,9 +157,6 @@ RSpec.describe ProjectsHelper do ...@@ -157,9 +157,6 @@ RSpec.describe ProjectsHelper do
empty_state_svg_path: start_with('/assets/illustrations/security-dashboard-empty-state'), empty_state_svg_path: start_with('/assets/illustrations/security-dashboard-empty-state'),
dashboard_documentation: '/help/user/application_security/security_dashboard/index', dashboard_documentation: '/help/user/application_security/security_dashboard/index',
security_dashboard_help_path: '/help/user/application_security/security_dashboard/index', security_dashboard_help_path: '/help/user/application_security/security_dashboard/index',
user_callouts_path: '/-/user_callouts',
user_callout_id: 'standalone_vulnerabilities_introduction_banner',
show_introduction_banner: 'true',
not_enabled_scanners_help_path: help_page_path('user/application_security/index', anchor: 'quick-start'), not_enabled_scanners_help_path: help_page_path('user/application_security/index', anchor: 'quick-start'),
no_pipeline_run_scanners_help_path: new_project_pipeline_path(project) no_pipeline_run_scanners_help_path: new_project_pipeline_path(project)
} }
......
...@@ -21498,9 +21498,6 @@ msgstr "" ...@@ -21498,9 +21498,6 @@ msgstr ""
msgid "SecurityReports|Download Report" msgid "SecurityReports|Download Report"
msgstr "" msgstr ""
msgid "SecurityReports|Each vulnerability now has a unique page that can be directly linked to, shared, referenced, and tracked as the single source of truth. Vulnerability occurrences also persist across scanner runs, which improves tracking and visibility and reduces duplicates between scans."
msgstr ""
msgid "SecurityReports|Edit dashboard" msgid "SecurityReports|Edit dashboard"
msgstr "" msgstr ""
...@@ -21522,15 +21519,9 @@ msgstr "" ...@@ -21522,15 +21519,9 @@ msgstr ""
msgid "SecurityReports|Hide dismissed" msgid "SecurityReports|Hide dismissed"
msgstr "" msgstr ""
msgid "SecurityReports|Introducing standalone vulnerabilities"
msgstr ""
msgid "SecurityReports|Issue Created" msgid "SecurityReports|Issue Created"
msgstr "" msgstr ""
msgid "SecurityReports|Learn more"
msgstr ""
msgid "SecurityReports|Learn more about setting up your dashboard" msgid "SecurityReports|Learn more about setting up your dashboard"
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