Commit b08d32d3 authored by Mark Florian's avatar Mark Florian

Merge branch '207322-easy-and-quick-way-to-turn-network-policies-on-off' into 'master'

Update header and table for security config page

See merge request gitlab-org/gitlab!31471
parents fd11fe1c e61e29a0
<script>
import { GlLink } from '@gitlab/ui';
import { GlLink, GlSprintf, GlTable } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import AutoFixSettings from './auto_fix_settings.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AutoFixSettings from './auto_fix_settings.vue';
export default {
components: {
GlLink,
Icon,
GlSprintf,
GlTable,
AutoFixSettings,
},
mixins: [glFeatureFlagsMixin()],
......@@ -41,33 +41,40 @@ export default {
},
},
computed: {
headerContent() {
const body = __('Configure Security %{wordBreakOpportunity}and Compliance');
const wordBreakOpportunity = '<wbr />';
return sprintf(body, { wordBreakOpportunity }, false);
devopsMessage() {
return this.autoDevopsEnabled
? __(
'All security scans are enabled because %{linkStart}Auto DevOps%{linkEnd} is enabled on this project',
)
: __(
`The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan.`,
);
},
callOutLink() {
devopsUrl() {
return this.autoDevopsEnabled ? this.autoDevopsHelpPagePath : this.latestPipelinePath;
},
calloutContent() {
const bodyDefault = __(`The status of the table below only applies to the default branch and
is based on the %{linkStart}latest pipeline%{linkEnd}.
Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan.`);
const bodyAutoDevopsEnabled = __(
'All security scans are enabled because %{linkStart}Auto DevOps%{linkEnd} is enabled on this project',
);
const body = this.autoDevopsEnabled ? bodyAutoDevopsEnabled : bodyDefault;
const linkStart = `<a href="${this.callOutLink}" target="_blank" rel="noopener">`;
const linkEnd = '</a>';
return sprintf(body, { linkStart, linkEnd }, false);
fields() {
return [
{
key: 'feature',
label: s__('SecurityConfiguration|Security Control'),
thClass: 'gl-text-gray-900 bg-transparent border-bottom',
},
{
key: 'configured',
label: s__('SecurityConfiguration|Status'),
thClass: 'gl-text-gray-900 bg-transparent border-bottom',
formatter: this.getStatusText,
},
];
},
},
methods: {
getStatusText(value) {
return value
? s__('SecurityConfiguration|Enabled')
: s__('SecurityConfiguration|Not yet enabled');
},
getFeatureDocumentationLinkLabel(featureName) {
return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), {
featureName,
......@@ -80,73 +87,32 @@ export default {
<template>
<article>
<header>
<h2 class="h4 my-3">
<span v-html="headerContent"></span>
<gl-link
target="_blank"
:href="helpPagePath"
:aria-label="__('Security configuration help link')"
>
<icon name="question" />
</gl-link>
</h2>
<h4 class="my-3">{{ __('Security Configuration') }}</h4>
<h5 class="gl-font-lg mt-5">{{ s__('SecurityConfiguration|Testing & Compliance') }}</h5>
<p>
<gl-sprintf :message="devopsMessage">
<template #link="{ content }">
<gl-link ref="pipelinesLink" :href="devopsUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</header>
<section
ref="callout"
class="bs-callout bs-callout-info mb-3 m-md-1 text-secondary"
v-html="calloutContent"
></section>
<section ref="featuresTable" class="mt-0">
<div
class="gl-responsive-table-row table-row-header text-2 font-weight-bold px-2 gl-text-gray-900"
role="row"
>
<div class="table-section section-80">
{{ s__('SecurityConfiguration|Secure features') }}
</div>
<div class="table-section section-20">{{ s__('SecurityConfiguration|Status') }}</div>
</div>
<div
v-for="feature in features"
ref="featureRow"
:key="feature.name"
class="gl-responsive-table-row flex-md-column align-items-md-stretch px-2"
>
<div class="d-md-flex align-items-center">
<div class="table-section section-80 section-wrap pr-md-3">
<div role="rowheader" class="table-mobile-header">
{{ s__('SecurityConfiguration|Feature') }}
</div>
<div class="table-mobile-content">
<div class="d-flex align-items-center justify-content-end justify-content-md-start">
<div class="text-2 gl-text-gray-900">{{ feature.name }}</div>
</div>
<div class="text-secondary">
{{ feature.description }}
<gl-link
target="_blank"
:href="feature.link"
:aria-label="getFeatureDocumentationLinkLabel(feature.name)"
>{{ __('More information') }}</gl-link
>
</div>
</div>
</div>
<div class="table-section section-20 section-wrap pr-md-3">
<div role="rowheader" class="table-mobile-header">
{{ s__('SecurityConfiguration|Status') }}
</div>
<div ref="featureConfigStatus" class="table-mobile-content">
{{
feature.configured
? s__('SecurityConfiguration|Enabled')
: s__('SecurityConfiguration|Not yet enabled')
}}
</div>
</div>
<gl-table ref="securityControlTable" :items="features" :fields="fields" stacked="md">
<template #cell(feature)="{ item }">
<div class="gl-text-gray-900">{{ item.name }}</div>
<div>
{{ item.description }}
<gl-link
target="_blank"
:href="item.link"
:aria-label="getFeatureDocumentationLinkLabel(item.name)"
>
{{ __('More information') }}
</gl-link>
</div>
</div>
</section>
</template>
</gl-table>
<auto-fix-settings v-if="glFeatures.securityAutoFix" v-bind="autoFixSettingsProps" />
</article>
</template>
---
title: Update header for security config page and change to GlTable
merge_request: 31471
author:
type: changed
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Security Configuration App features table displays a given feature 1`] = `
<section
class="mt-0"
>
<div
class="gl-responsive-table-row table-row-header text-2 font-weight-bold px-2 gl-text-gray-900"
role="row"
>
<div
class="table-section section-80"
>
Secure features
</div>
<div
class="table-section section-20"
>
Status
</div>
</div>
<div
class="gl-responsive-table-row flex-md-column align-items-md-stretch px-2"
>
<div
class="d-md-flex align-items-center"
>
<div
class="table-section section-80 section-wrap pr-md-3"
>
<div
class="table-mobile-header"
role="rowheader"
>
Feature
</div>
<div
class="table-mobile-content"
>
<div
class="d-flex align-items-center justify-content-end justify-content-md-start"
>
<div
class="text-2 gl-text-gray-900"
>
name-feature-0
</div>
</div>
<div
class="text-secondary"
>
description-feature-0
<gl-link-stub
aria-label="Feature documentation for name-feature-0"
href="link-feature-0"
target="_blank"
>
More information
</gl-link-stub>
</div>
</div>
</div>
<div
class="table-section section-20 section-wrap pr-md-3"
>
<div
class="table-mobile-header"
role="rowheader"
>
Status
</div>
<div
class="table-mobile-content"
>
Not yet enabled
</div>
</div>
</div>
</div>
</section>
`;
import { shallowMount } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import SecurityConfigurationApp from 'ee/security_configuration/components/app.vue';
import stubChildren from 'helpers/stub_children';
describe('Security Configuration App', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(SecurityConfigurationApp, {
wrapper = mount(SecurityConfigurationApp, {
stubs: {
...stubChildren(SecurityConfigurationApp),
GlTable: false,
GlSprintf: false,
},
propsData: {
features: [],
autoDevopsEnabled: false,
......@@ -23,28 +28,19 @@ describe('Security Configuration App', () => {
wrapper.destroy();
});
const generateFeatures = n =>
[...Array(n).keys()].map(i => ({
const generateFeatures = n => {
return [...Array(n).keys()].map(i => ({
name: `name-feature-${i}`,
description: `description-feature-${i}`,
link: `link-feature-${i}`,
configured: i % 2 === 0,
}));
};
const getHelpLink = () => wrapper.find('header').find(GlLink);
const getNotification = () => wrapper.find({ ref: 'callout' });
const getPipelinesLink = () => getNotification().find('a');
const getFeaturesTable = () => wrapper.find({ ref: 'featuresTable' });
const getFeatureConfigStatus = () => wrapper.find({ ref: 'featureConfigStatus' });
const getPipelinesLink = () => wrapper.find({ ref: 'pipelinesLink' });
const getFeaturesTable = () => wrapper.find({ ref: 'securityControlTable' });
describe('header', () => {
it('displays a link to the given help page', () => {
const helpPagePath = 'http://foo';
createComponent({ helpPagePath });
expect(getHelpLink().attributes('href')).toBe(helpPagePath);
});
it.each`
autoDevopsEnabled | expectedUrl
${true} | ${'http://autoDevopsHelpPagePath'}
......@@ -55,41 +51,28 @@ describe('Security Configuration App', () => {
createComponent({ autoDevopsEnabled });
expect(getPipelinesLink().attributes('href')).toBe(expectedUrl);
expect(getPipelinesLink().attributes('rel')).toBe('noopener');
expect(getPipelinesLink().attributes('target')).toBe('_blank');
},
);
});
describe('features table', () => {
it('displays a row for each given feature', () => {
it('passes the expected data to the GlTable', () => {
const features = generateFeatures(5);
createComponent({ features });
expect(wrapper.findAll({ ref: 'featureRow' })).toHaveLength(5);
expect(getFeaturesTable().classes('b-table-stacked-md')).toBeTruthy();
const rows = getFeaturesTable().findAll('tbody tr');
expect(rows).toHaveLength(5);
for (let i = 0; i < features.length; i += 1) {
const [feature, status] = rows.at(i).findAll('td').wrappers;
expect(feature.text()).toMatch(features[i].name);
expect(feature.text()).toMatch(features[i].description);
expect(feature.find(GlLink).attributes('href')).toBe(features[i].link);
expect(status.text()).toMatch(features[i].configured ? 'Enabled' : 'Not yet enabled');
}
});
it('displays a given feature', () => {
const features = generateFeatures(1);
createComponent({ features });
expect(getFeaturesTable().element).toMatchSnapshot();
});
it.each`
configured | statusText
${true} | ${'Enabled'}
${false} | ${'Not yet enabled'}
`(
`displays "$statusText" if the given feature's configuration status is: "$configured"`,
({ configured, statusText }) => {
const features = [{ configured }];
createComponent({ features });
expect(getFeatureConfigStatus().text()).toBe(statusText);
},
);
});
});
......@@ -5699,9 +5699,6 @@ msgstr ""
msgid "Configure Prometheus"
msgstr ""
msgid "Configure Security %{wordBreakOpportunity}and Compliance"
msgstr ""
msgid "Configure Tracing"
msgstr ""
......@@ -18991,9 +18988,6 @@ msgstr ""
msgid "Security Dashboard"
msgstr ""
msgid "Security configuration help link"
msgstr ""
msgid "Security dashboard"
msgstr ""
......@@ -19006,21 +19000,21 @@ msgstr ""
msgid "SecurityConfiguration|Enabled"
msgstr ""
msgid "SecurityConfiguration|Feature"
msgstr ""
msgid "SecurityConfiguration|Feature documentation for %{featureName}"
msgstr ""
msgid "SecurityConfiguration|Not yet enabled"
msgstr ""
msgid "SecurityConfiguration|Secure features"
msgid "SecurityConfiguration|Security Control"
msgstr ""
msgid "SecurityConfiguration|Status"
msgstr ""
msgid "SecurityConfiguration|Testing & Compliance"
msgstr ""
msgid "SecurityReports|%{firstProject} and %{secondProject}"
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