Skip to content
Snippets Groups Projects
Commit acedd036 authored by Samantha Ming's avatar Samantha Ming
Browse files

Merge branch '373274-use-sec-training-promo-vul-page' into 'master'

Update the security_training_promo_banner.vue to use the shared security_training_promo.vue component

See merge request !97997



Merged-by: default avatarSamantha Ming <sming@gitlab.com>
Approved-by: default avatarDaniel Tian <dtian@gitlab.com>
parents a304b352 537f0fd4
No related branches found
No related tags found
No related merge requests found
Pipeline #654990779 passed
Pipeline: GitLab

#655008922

    <script>
    import { GlBanner } from '@gitlab/ui';
    import { __ } from '~/locale';
    import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
    import Tracking from '~/tracking';
    import {
    TRACK_PROMOTION_BANNER_CTA_CLICK_ACTION,
    TRACK_PROMOTION_BANNER_CTA_CLICK_LABEL,
    } from '~/security_configuration/constants';
    import SecurityTrainingPromo from 'ee/vue_shared/security_reports/components/security_training_promo.vue';
    export default {
    components: {
    GlBanner,
    UserCalloutDismisser,
    SecurityTrainingPromo,
    },
    mixins: [Tracking.mixin()],
    inject: ['securityConfigurationPath', 'projectFullPath'],
    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`;
    },
    },
    methods: {
    trackCTAClick() {
    this.track(TRACK_PROMOTION_BANNER_CTA_CLICK_ACTION, {
    label: TRACK_PROMOTION_BANNER_CTA_CLICK_LABEL,
    property: this.projectFullPath,
    });
    },
    },
    };
    </script>
    <template>
    <user-callout-dismisser feature-name="security_training_feature_promotion">
    <template #default="{ dismiss, shouldShowCallout }">
    <security-training-promo
    :security-configuration-path="securityConfigurationPath"
    :project-full-path="projectFullPath"
    >
    <template #default="{ buttonLink, buttonText, dismiss, trackCTAClick }">
    <gl-banner
    v-if="shouldShowCallout"
    :title="$options.i18n.title"
    :button-text="$options.i18n.buttonText"
    :button-text="buttonText"
    :button-link="buttonLink"
    variant="introduction"
    @primary="trackCTAClick"
    ......@@ -53,5 +35,5 @@ export default {
    <p>{{ $options.i18n.content }}</p>
    </gl-banner>
    </template>
    </user-callout-dismisser>
    </security-training-promo>
    </template>
    import { shallowMount } from '@vue/test-utils';
    import { GlBanner } from '@gitlab/ui';
    import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
    import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
    import SecurityTrainingPromoBanner from 'ee/security_dashboard/components/project/security_training_promo_banner.vue';
    import {
    TRACK_PROMOTION_BANNER_CTA_CLICK_ACTION,
    TRACK_PROMOTION_BANNER_CTA_CLICK_LABEL,
    } from '~/security_configuration/constants';
    import { stubComponent } from 'helpers/stub_component';
    import SecurityTrainingPromo from 'ee/vue_shared/security_reports/components/security_training_promo.vue';
    const dismissSpy = jest.fn();
    const trackCTAClickSpy = jest.fn();
    const SECURITY_CONFIGURATION_PATH = 'foo/bar';
    const VULNERABILITY_MANAGEMENT_TAB_NAME = 'vulnerability-management';
    const PROJECT_FULL_PATH = 'namespace/project';
    const MOCK_SLOT_PROPS = {
    buttonText: 'Enable security training',
    buttonLink: 'some/link',
    trackCTAClick: trackCTAClickSpy,
    dismiss: dismissSpy,
    };
    describe('Security training promo banner component', () => {
    let wrapper;
    const userCalloutDismissSpy = jest.fn();
    const createWrapper = ({ shouldShowCallout = true } = {}) =>
    shallowMount(SecurityTrainingPromoBanner, {
    const createWrapper = () => {
    wrapper = shallowMount(SecurityTrainingPromoBanner, {
    provide: {
    projectFullPath: PROJECT_FULL_PATH,
    securityConfigurationPath: SECURITY_CONFIGURATION_PATH,
    },
    stubs: {
    UserCalloutDismisser: makeMockUserCalloutDismisser({
    dismiss: userCalloutDismissSpy,
    shouldShowCallout,
    SecurityTrainingPromo: stubComponent(SecurityTrainingPromo, {
    render() {
    return this.$scopedSlots.default(MOCK_SLOT_PROPS);
    },
    }),
    },
    });
    };
    afterEach(() => {
    wrapper.destroy();
    });
    const findSecurityTrainingPromo = () => wrapper.findComponent(SecurityTrainingPromo);
    const findBanner = () => wrapper.findComponent(GlBanner);
    describe('banner', () => {
    beforeEach(() => {
    wrapper = createWrapper();
    beforeEach(() => {
    createWrapper();
    });
    describe('SecurityTrainingPromo', () => {
    it('renders the component with the correct props', () => {
    expect(findSecurityTrainingPromo().props()).toMatchObject({
    securityConfigurationPath: SECURITY_CONFIGURATION_PATH,
    projectFullPath: PROJECT_FULL_PATH,
    });
    });
    });
    it('should be an introduction that announces the security training feature', () => {
    const { title, buttonText, content } = SecurityTrainingPromoBanner.i18n;
    describe('GlBanner', () => {
    it('renders the component with the correct props', () => {
    const { buttonText, buttonLink } = MOCK_SLOT_PROPS;
    const { title } = SecurityTrainingPromoBanner.i18n;
    expect(findBanner().props()).toMatchObject({
    variant: 'introduction',
    title,
    buttonText,
    buttonLink,
    title,
    });
    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}`,
    );
    });
    });
    describe('dismissal', () => {
    it('should dismiss the callout when the banner is closed', () => {
    wrapper = createWrapper();
    expect(userCalloutDismissSpy).not.toHaveBeenCalled();
    it('should trigger the dismiss method when the banner is closed', () => {
    expect(dismissSpy).not.toHaveBeenCalled();
    findBanner().vm.$emit('close');
    expect(userCalloutDismissSpy).toHaveBeenCalled();
    });
    it('should not show the banner once it has been dismissed', () => {
    wrapper = createWrapper({ shouldShowCallout: false });
    expect(findBanner().exists()).toBe(false);
    expect(dismissSpy).toHaveBeenCalled();
    });
    });
    describe('metrics', () => {
    let trackingSpy;
    beforeEach(async () => {
    trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
    wrapper = createWrapper();
    });
    afterEach(() => {
    unmockTracking();
    });
    it('tracks clicks on the CTA button', () => {
    expect(trackingSpy).not.toHaveBeenCalled();
    it('should trigger the trackCTAClick method when the banner is clicked', () => {
    expect(trackCTAClickSpy).not.toHaveBeenCalled();
    findBanner().vm.$emit('primary');
    expect(trackingSpy).toHaveBeenCalledWith(undefined, TRACK_PROMOTION_BANNER_CTA_CLICK_ACTION, {
    label: TRACK_PROMOTION_BANNER_CTA_CLICK_LABEL,
    property: PROJECT_FULL_PATH,
    });
    expect(trackCTAClickSpy).toHaveBeenCalled();
    });
    });
    });
    ......@@ -52,6 +52,7 @@ describe('Vulnerability Report', () => {
    pipelineSecurityBuildsFailedCount: 1,
    pipelineSecurityBuildsFailedPath: '/test/faild_pipeline_02',
    projectFullPath: '/test/project',
    securityConfigurationPath: '/test/configuration',
    },
    type: DASHBOARD_TYPES.PROJECT,
    });
    ......
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment