Commit 632524a7 authored by Jannik Lehmann's avatar Jannik Lehmann

Add compliance tab to redesigned Security-Configuration Page

This Commit solves https://gitlab.com/gitlab-org/gitlab/-/issues/328395
It introduces the compliance Tab to the
redesigned Security-Configuration Page..

Changelog: added
parent 0d9ae330
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui'; import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import FeatureCard from './feature_card.vue'; import FeatureCard from './feature_card.vue';
import SectionLayout from './section_layout.vue';
export const i18n = { export const i18n = {
compliance: s__('SecurityConfiguration|Compliance'), compliance: s__('SecurityConfiguration|Compliance'),
...@@ -23,12 +24,18 @@ export default { ...@@ -23,12 +24,18 @@ export default {
GlTabs, GlTabs,
GlSprintf, GlSprintf,
FeatureCard, FeatureCard,
SectionLayout,
}, },
props: { props: {
augmentedSecurityFeatures: { augmentedSecurityFeatures: {
type: Array, type: Array,
required: true, required: true,
}, },
augmentedComplianceFeatures: {
type: Array,
required: true,
},
latestPipelinePath: { latestPipelinePath: {
type: String, type: String,
required: false, required: false,
...@@ -46,12 +53,11 @@ export default { ...@@ -46,12 +53,11 @@ export default {
<gl-tabs content-class="gl-pt-6"> <gl-tabs content-class="gl-pt-6">
<gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting"> <gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting">
<div class="row"> <section-layout :heading="$options.i18n.securityTesting">
<div class="col-lg-5"> <template #description>
<h2 class="gl-font-size-h2 gl-mt-0">{{ $options.i18n.securityTesting }}</h2>
<p <p
v-if="latestPipelinePath" v-if="latestPipelinePath"
data-testid="latest-pipeline-info" data-testid="latest-pipeline-info-security"
class="gl-line-height-20" class="gl-line-height-20"
> >
<gl-sprintf :message="$options.i18n.securityTestingDescription"> <gl-sprintf :message="$options.i18n.securityTestingDescription">
...@@ -60,16 +66,42 @@ export default { ...@@ -60,16 +66,42 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</p> </p>
</div> </template>
<div class="col-lg-7">
<template #features>
<feature-card <feature-card
v-for="feature in augmentedSecurityFeatures" v-for="feature in augmentedSecurityFeatures"
:key="feature.type" :key="feature.type"
:feature="feature" :feature="feature"
class="gl-mb-6" class="gl-mb-6"
/> />
</div> </template>
</div> </section-layout>
</gl-tab>
<gl-tab data-testid="compliance-testing-tab" :title="$options.i18n.compliance">
<section-layout :heading="$options.i18n.compliance">
<template #description>
<p
v-if="latestPipelinePath"
class="gl-line-height-20"
data-testid="latest-pipeline-info-compliance"
>
<gl-sprintf :message="$options.i18n.securityTestingDescription">
<template #link="{ content }">
<gl-link :href="latestPipelinePath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</template>
<template #features>
<feature-card
v-for="feature in augmentedComplianceFeatures"
:key="feature.type"
:feature="feature"
class="gl-mb-6"
/>
</template>
</section-layout>
</gl-tab> </gl-tab>
</gl-tabs> </gl-tabs>
</article> </article>
......
<script>
export default {
name: 'SectionLayout',
props: {
heading: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="row">
<div class="col-lg-5">
<h2 class="gl-font-size-h2 gl-mt-0">{{ heading }}</h2>
<slot name="description"></slot>
</div>
<div class="col-lg-7">
<slot name="features"></slot>
</div>
</div>
</template>
...@@ -20,7 +20,7 @@ export const initStaticSecurityConfiguration = (el) => { ...@@ -20,7 +20,7 @@ export const initStaticSecurityConfiguration = (el) => {
const { projectPath, upgradePath, features, latestPipelinePath } = el.dataset; const { projectPath, upgradePath, features, latestPipelinePath } = el.dataset;
if (gon.features.securityConfigurationRedesign) { if (gon.features.securityConfigurationRedesign) {
const { augmentedSecurityFeatures } = augmentFeatures( const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures(
securityFeatures, securityFeatures,
complianceFeatures, complianceFeatures,
features ? JSON.parse(features) : [], features ? JSON.parse(features) : [],
...@@ -36,6 +36,7 @@ export const initStaticSecurityConfiguration = (el) => { ...@@ -36,6 +36,7 @@ export const initStaticSecurityConfiguration = (el) => {
render(createElement) { render(createElement) {
return createElement(RedesignedSecurityConfigurationApp, { return createElement(RedesignedSecurityConfigurationApp, {
props: { props: {
augmentedComplianceFeatures,
augmentedSecurityFeatures, augmentedSecurityFeatures,
latestPipelinePath, latestPipelinePath,
}, },
......
import { GlTab, GlTabs } from '@gitlab/ui'; import { GlTab } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { import {
...@@ -7,14 +7,20 @@ import { ...@@ -7,14 +7,20 @@ import {
SAST_DESCRIPTION, SAST_DESCRIPTION,
SAST_HELP_PATH, SAST_HELP_PATH,
SAST_CONFIG_HELP_PATH, SAST_CONFIG_HELP_PATH,
LICENSE_COMPLIANCE_NAME,
LICENSE_COMPLIANCE_DESCRIPTION,
LICENSE_COMPLIANCE_HELP_PATH,
} from '~/security_configuration/components/constants'; } from '~/security_configuration/components/constants';
import FeatureCard from '~/security_configuration/components/feature_card.vue'; import FeatureCard from '~/security_configuration/components/feature_card.vue';
import RedesignedSecurityConfigurationApp, { import RedesignedSecurityConfigurationApp, {
i18n, i18n,
} from '~/security_configuration/components/redesigned_app.vue'; } from '~/security_configuration/components/redesigned_app.vue';
import { REPORT_TYPE_SAST } from '~/vue_shared/security_reports/constants'; import {
REPORT_TYPE_LICENSE_COMPLIANCE,
REPORT_TYPE_SAST,
} from '~/vue_shared/security_reports/constants';
describe('NewApp component', () => { describe('redesigned App component', () => {
let wrapper; let wrapper;
const createComponent = (propsData) => { const createComponent = (propsData) => {
...@@ -26,9 +32,8 @@ describe('NewApp component', () => { ...@@ -26,9 +32,8 @@ describe('NewApp component', () => {
}; };
const findMainHeading = () => wrapper.find('h1'); const findMainHeading = () => wrapper.find('h1');
const findSubHeading = () => wrapper.find('h2');
const findTab = () => wrapper.findComponent(GlTab); const findTab = () => wrapper.findComponent(GlTab);
const findTabs = () => wrapper.findAllComponents(GlTabs); const findTabs = () => wrapper.findAllComponents(GlTab);
const findByTestId = (id) => wrapper.findByTestId(id); const findByTestId = (id) => wrapper.findByTestId(id);
const findFeatureCards = () => wrapper.findAllComponents(FeatureCard); const findFeatureCards = () => wrapper.findAllComponents(FeatureCard);
...@@ -44,6 +49,16 @@ describe('NewApp component', () => { ...@@ -44,6 +49,16 @@ describe('NewApp component', () => {
}, },
]; ];
const complianceFeaturesMock = [
{
name: LICENSE_COMPLIANCE_NAME,
description: LICENSE_COMPLIANCE_DESCRIPTION,
helpPath: LICENSE_COMPLIANCE_HELP_PATH,
type: REPORT_TYPE_LICENSE_COMPLIANCE,
configurationHelpPath: LICENSE_COMPLIANCE_HELP_PATH,
},
];
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
...@@ -52,6 +67,7 @@ describe('NewApp component', () => { ...@@ -52,6 +67,7 @@ describe('NewApp component', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
augmentedSecurityFeatures: securityFeaturesMock, augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock,
}); });
}); });
...@@ -66,23 +82,22 @@ describe('NewApp component', () => { ...@@ -66,23 +82,22 @@ describe('NewApp component', () => {
}); });
it('renders right amount of tabs with correct title ', () => { it('renders right amount of tabs with correct title ', () => {
expect(findTabs().length).toEqual(1); expect(findTabs().length).toEqual(2);
}); });
it('renders security-testing tab', () => { it('renders security-testing tab', () => {
expect(findByTestId('security-testing-tab')).toExist(); expect(findByTestId('security-testing-tab').exists()).toBe(true);
}); });
it('renders sub-heading with correct text', () => { it('renders compliance-testing tab', () => {
const subHeading = findSubHeading(); expect(findByTestId('compliance-testing-tab').exists()).toBe(true);
expect(subHeading).toExist();
expect(subHeading.text()).toContain(i18n.securityTesting);
}); });
it('renders right amount of feature cards for given props with correct props', () => { it('renders right amount of feature cards for given props with correct props', () => {
const cards = findFeatureCards(); const cards = findFeatureCards();
expect(cards.length).toEqual(1); expect(cards.length).toEqual(2);
expect(cards.at(0).props()).toEqual({ feature: securityFeaturesMock[0] }); expect(cards.at(0).props()).toEqual({ feature: securityFeaturesMock[0] });
expect(cards.at(1).props()).toEqual({ feature: complianceFeaturesMock[0] });
}); });
it('should not show latest pipeline link when latestPipelinePath is not defined', () => { it('should not show latest pipeline link when latestPipelinePath is not defined', () => {
...@@ -94,16 +109,29 @@ describe('NewApp component', () => { ...@@ -94,16 +109,29 @@ describe('NewApp component', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
augmentedSecurityFeatures: securityFeaturesMock, augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock,
latestPipelinePath: 'test/path', latestPipelinePath: 'test/path',
}); });
}); });
it('should show latest pipeline info with correct link when latestPipelinePath is defined', () => { it('should show latest pipeline info on the security tab with correct link when latestPipelinePath is defined', () => {
expect(findByTestId('latest-pipeline-info').exists()).toBe(true); expect(findByTestId('latest-pipeline-info-security').exists()).toBe(true);
expect(findByTestId('latest-pipeline-info').text()).toMatchInterpolatedText( expect(findByTestId('latest-pipeline-info-security').text()).toMatchInterpolatedText(
i18n.securityTestingDescription, i18n.securityTestingDescription,
); );
expect(findByTestId('latest-pipeline-info').find('a').attributes('href')).toBe('test/path'); expect(findByTestId('latest-pipeline-info-security').find('a').attributes('href')).toBe(
'test/path',
);
});
it('should show latest pipeline info on the compliance tab with correct link when latestPipelinePath is defined', () => {
expect(findByTestId('latest-pipeline-info-compliance').exists()).toBe(true);
expect(findByTestId('latest-pipeline-info-compliance').text()).toMatchInterpolatedText(
i18n.securityTestingDescription,
);
expect(findByTestId('latest-pipeline-info-compliance').find('a').attributes('href')).toBe(
'test/path',
);
}); });
}); });
}); });
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import SectionLayout from '~/security_configuration/components/section_layout.vue';
describe('Section Layout component', () => {
let wrapper;
const createComponent = (propsData) => {
wrapper = extendedWrapper(
mount(SectionLayout, {
propsData,
scopedSlots: {
description: '<span>foo</span>',
features: '<span>bar</span>',
},
}),
);
};
const findHeading = () => wrapper.find('h2');
afterEach(() => {
wrapper.destroy();
});
describe('basic structure', () => {
beforeEach(() => {
createComponent({ heading: 'testheading' });
});
const slots = {
description: 'foo',
features: 'bar',
};
it('should render heading when passed in as props', () => {
expect(findHeading().exists()).toBe(true);
expect(findHeading().text()).toBe('testheading');
});
Object.keys(slots).forEach((slot) => {
it('renders the slots', () => {
const slotContent = slots[slot];
createComponent({ heading: '' });
expect(wrapper.text()).toContain(slotContent);
});
});
});
});
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