Commit 093ba5b2 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '296179-change-empty-state-location' into 'master'

Move empty state into the threat monitoring tabs

See merge request gitlab-org/gitlab!51748
parents 0e570cc8 a6e10ea3
<script>
import { mapActions } from 'vuex';
import {
GlAlert,
GlEmptyState,
GlIcon,
GlLink,
GlPopover,
GlSprintf,
GlTabs,
GlTab,
} from '@gitlab/ui';
import { GlAlert, GlIcon, GlLink, GlPopover, GlTabs, GlTab } from '@gitlab/ui';
import { s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
......@@ -17,22 +8,22 @@ import Alerts from './alerts/alerts.vue';
import ThreatMonitoringFilters from './threat_monitoring_filters.vue';
import ThreatMonitoringSection from './threat_monitoring_section.vue';
import NetworkPolicyList from './network_policy_list.vue';
import NoEnvironmentEmptyState from './no_environment_empty_state.vue';
export default {
name: 'ThreatMonitoring',
components: {
GlAlert,
GlEmptyState,
GlIcon,
GlLink,
GlPopover,
GlSprintf,
GlTabs,
GlTab,
Alerts,
ThreatMonitoringFilters,
ThreatMonitoringSection,
NetworkPolicyList,
NoEnvironmentEmptyState,
},
mixins: [glFeatureFlagsMixin()],
inject: ['documentationPath'],
......@@ -45,10 +36,6 @@ export default {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
wafNoDataSvgPath: {
type: String,
required: true,
......@@ -122,10 +109,6 @@ export default {
`ThreatMonitoring|Container Network Policies are not installed or have been disabled. To view
this data, ensure your Network Policies are installed and enabled for your cluster.`,
),
emptyStateDescription: s__(
`ThreatMonitoring|To view this data, ensure you have configured an environment
for this project and that at least one threat monitoring feature is enabled. %{linkStart}More information%{linkEnd}`,
),
alertText: s__(
`ThreatMonitoring|The graph below is an overview of traffic coming to your
application as tracked by the Web Application Firewall (WAF). View the docs
......@@ -138,22 +121,7 @@ export default {
</script>
<template>
<gl-empty-state
v-if="!isSetUpMaybe"
ref="emptyState"
:title="s__('ThreatMonitoring|No environments detected')"
:svg-path="emptyStateSvgPath"
>
<template #description>
<gl-sprintf :message="$options.emptyStateDescription">
<template #link="{ content }">
<gl-link :href="documentationPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
</gl-empty-state>
<section v-else>
<section>
<gl-alert
v-if="showAlert"
class="my-3"
......@@ -190,45 +158,55 @@ export default {
<alerts />
</gl-tab>
<gl-tab ref="networkPolicyTab" :title="s__('ThreatMonitoring|Policies')">
<no-environment-empty-state v-if="!isSetUpMaybe" />
<network-policy-list
v-else
:documentation-path="documentationPath"
:new-policy-path="newPolicyPath"
/>
</gl-tab>
<gl-tab :title="s__('ThreatMonitoring|Statistics')">
<threat-monitoring-filters />
<gl-tab
:title="s__('ThreatMonitoring|Statistics')"
data-testid="threat-monitoring-statistics-tab"
>
<no-environment-empty-state v-if="!isSetUpMaybe" />
<template v-else>
<threat-monitoring-filters />
<threat-monitoring-section
ref="wafSection"
store-namespace="threatMonitoringWaf"
:title="s__('ThreatMonitoring|Web Application Firewall')"
:subtitle="s__('ThreatMonitoring|Requests')"
:anomalous-title="s__('ThreatMonitoring|Anomalous Requests')"
:nominal-title="s__('ThreatMonitoring|Total Requests')"
:y-legend="s__('ThreatMonitoring|Requests')"
:chart-empty-state-title="s__('ThreatMonitoring|Application firewall not detected')"
:chart-empty-state-text="$options.wafChartEmptyStateDescription"
:chart-empty-state-svg-path="wafNoDataSvgPath"
:documentation-path="documentationPath"
documentation-anchor="web-application-firewall"
/>
<threat-monitoring-section
ref="wafSection"
store-namespace="threatMonitoringWaf"
:title="s__('ThreatMonitoring|Web Application Firewall')"
:subtitle="s__('ThreatMonitoring|Requests')"
:anomalous-title="s__('ThreatMonitoring|Anomalous Requests')"
:nominal-title="s__('ThreatMonitoring|Total Requests')"
:y-legend="s__('ThreatMonitoring|Requests')"
:chart-empty-state-title="s__('ThreatMonitoring|Application firewall not detected')"
:chart-empty-state-text="$options.wafChartEmptyStateDescription"
:chart-empty-state-svg-path="wafNoDataSvgPath"
:documentation-path="documentationPath"
documentation-anchor="web-application-firewall"
/>
<hr />
<hr />
<threat-monitoring-section
ref="networkPolicySection"
store-namespace="threatMonitoringNetworkPolicy"
:title="s__('ThreatMonitoring|Container Network Policy')"
:subtitle="s__('ThreatMonitoring|Packet Activity')"
:anomalous-title="s__('ThreatMonitoring|Dropped Packets')"
:nominal-title="s__('ThreatMonitoring|Total Packets')"
:y-legend="s__('ThreatMonitoring|Operations Per Second')"
:chart-empty-state-title="s__('ThreatMonitoring|Container NetworkPolicies not detected')"
:chart-empty-state-text="$options.networkPolicyChartEmptyStateDescription"
:chart-empty-state-svg-path="networkPolicyNoDataSvgPath"
:documentation-path="documentationPath"
documentation-anchor="container-network-policy"
/>
<threat-monitoring-section
ref="networkPolicySection"
store-namespace="threatMonitoringNetworkPolicy"
:title="s__('ThreatMonitoring|Container Network Policy')"
:subtitle="s__('ThreatMonitoring|Packet Activity')"
:anomalous-title="s__('ThreatMonitoring|Dropped Packets')"
:nominal-title="s__('ThreatMonitoring|Total Packets')"
:y-legend="s__('ThreatMonitoring|Operations Per Second')"
:chart-empty-state-title="
s__('ThreatMonitoring|Container NetworkPolicies not detected')
"
:chart-empty-state-text="$options.networkPolicyChartEmptyStateDescription"
:chart-empty-state-svg-path="networkPolicyNoDataSvgPath"
:documentation-path="documentationPath"
documentation-anchor="container-network-policy"
/>
</template>
</gl-tab>
</gl-tabs>
</section>
......
......@@ -5,6 +5,11 @@ export const TOTAL_REQUESTS = s__('ThreatMonitoring|Total Requests');
export const ANOMALOUS_REQUESTS = s__('ThreatMonitoring|Anomalous Requests');
export const TIME = s__('ThreatMonitoring|Time');
export const REQUESTS = s__('ThreatMonitoring|Requests');
export const NO_ENVIRONMENT_TITLE = s__('ThreatMonitoring|No environments detected');
export const EMPTY_STATE_DESCRIPTION = s__(
`ThreatMonitoring|To view this data, ensure you have configured an environment
for this project and that at least one threat monitoring feature is enabled. %{linkStart}More information%{linkEnd}`,
);
export const COLORS = {
nominal: gray700,
......
<script>
import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import { EMPTY_STATE_DESCRIPTION, NO_ENVIRONMENT_TITLE } from './constants';
export default {
i18n: { EMPTY_STATE_DESCRIPTION, NO_ENVIRONMENT_TITLE },
name: 'NoEnvironmentEmptyState',
components: {
GlEmptyState,
GlLink,
GlSprintf,
},
inject: {
documentationPath: { type: String, default: '' },
emptyStateSvgPath: { type: String, default: '' },
},
};
</script>
<template>
<gl-empty-state :title="$options.i18n.NO_ENVIRONMENT_TITLE" :svg-path="emptyStateSvgPath">
<template #description>
<gl-sprintf :message="$options.i18n.EMPTY_STATE_DESCRIPTION">
<template #link="{ content }">
<gl-link :href="documentationPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
</gl-empty-state>
</template>
......@@ -46,6 +46,7 @@ export default () => {
el,
provide: {
documentationPath,
emptyStateSvgPath,
projectPath,
},
store,
......@@ -53,7 +54,6 @@ export default () => {
return createElement(ThreatMonitoringApp, {
props: {
chartEmptyStateSvgPath,
emptyStateSvgPath,
wafNoDataSvgPath,
networkPolicyNoDataSvgPath,
defaultEnvironmentId: parseInt(defaultEnvironmentId, 10),
......
---
title: Move empty state into the threat monitoring tabs
merge_request: 51748
author:
type: changed
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NoEnvironmentEmptyState component default state matches the snapshot 1`] = `
<gl-empty-state-stub
svgpath="/svgs"
title="No environments detected"
>
<gl-sprintf-stub
message="To view this data, ensure you have configured an environment for this project and that at least one threat monitoring feature is enabled. %{linkStart}More information%{linkEnd}"
/>
</gl-empty-state-stub>
`;
import { GlAlert, GlSprintf } from '@gitlab/ui';
import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ThreatMonitoringAlerts from 'ee/threat_monitoring/components/alerts/alerts.vue';
import ThreatMonitoringApp from 'ee/threat_monitoring/components/app.vue';
import ThreatMonitoringFilters from 'ee/threat_monitoring/components/threat_monitoring_filters.vue';
import NetworkPolicyList from 'ee/threat_monitoring/components/network_policy_list.vue';
import NoEnvironmentEmptyState from 'ee/threat_monitoring/components/no_environment_empty_state.vue';
import createStore from 'ee/threat_monitoring/store';
import { TEST_HOST } from 'helpers/test_constants';
import axios from '~/lib/utils/axios_utils';
......@@ -25,7 +28,7 @@ describe('ThreatMonitoringApp component', () => {
let store;
let wrapper;
const factory = ({ propsData, provide = {}, state, options } = {}) => {
const factory = ({ propsData, provide = {}, state, stubs = {} } = {}) => {
store = createStore();
Object.assign(store.state.threatMonitoring, {
environmentsEndpoint,
......@@ -36,38 +39,41 @@ describe('ThreatMonitoringApp component', () => {
jest.spyOn(store, 'dispatch').mockImplementation();
wrapper = shallowMount(ThreatMonitoringApp, {
propsData: {
defaultEnvironmentId,
chartEmptyStateSvgPath,
emptyStateSvgPath,
wafNoDataSvgPath,
networkPolicyNoDataSvgPath,
newPolicyPath,
showUserCallout: true,
userCalloutId,
userCalloutsPath,
...propsData,
},
provide: {
documentationPath,
glFeatures: { threatMonitoringAlerts: false },
...provide,
},
store,
...options,
});
wrapper = extendedWrapper(
shallowMount(ThreatMonitoringApp, {
propsData: {
defaultEnvironmentId,
chartEmptyStateSvgPath,
emptyStateSvgPath,
wafNoDataSvgPath,
networkPolicyNoDataSvgPath,
newPolicyPath,
showUserCallout: true,
userCalloutId,
userCalloutsPath,
...propsData,
},
provide: {
documentationPath,
glFeatures: { threatMonitoringAlerts: false },
...provide,
},
store,
stubs,
}),
);
};
const findAlert = () => wrapper.find(GlAlert);
const findAlertsView = () => wrapper.find(ThreatMonitoringAlerts);
const findNetworkPolicyList = () => wrapper.find(NetworkPolicyList);
const findFilters = () => wrapper.find(ThreatMonitoringFilters);
const findWafSection = () => wrapper.find({ ref: 'wafSection' });
const findNetworkPolicySection = () => wrapper.find({ ref: 'networkPolicySection' });
const findEmptyState = () => wrapper.find({ ref: 'emptyState' });
const findEmptyStateMessage = () => wrapper.find(GlSprintf);
const findNoEnvironmentEmptyStates = () => wrapper.findAll(NoEnvironmentEmptyState);
const findNetworkPolicyTab = () => wrapper.find({ ref: 'networkPolicyTab' });
const findAlertTab = () => wrapper.find('[data-testid="threat-monitoring-alerts-tab"]');
const findAlertTab = () => wrapper.findByTestId('threat-monitoring-alerts-tab');
const findStatisticsTab = () => wrapper.findByTestId('threat-monitoring-statistics-tab');
afterEach(() => {
wrapper.destroy();
......@@ -82,6 +88,7 @@ describe('ThreatMonitoringApp component', () => {
propsData: {
defaultEnvironmentId: invalidEnvironmentId,
},
stubs: { GlTabs: false },
});
});
......@@ -89,13 +96,21 @@ describe('ThreatMonitoringApp component', () => {
expect(store.dispatch).not.toHaveBeenCalled();
});
it('shows only the empty state', () => {
const emptyState = findEmptyState();
expect(wrapper.element).toBe(emptyState.element);
expect(findEmptyStateMessage().exists()).toBe(true);
expect(emptyState.props()).toMatchObject({
svgPath: emptyStateSvgPath,
});
it('shows the "no environment" empty state', () => {
expect(findNoEnvironmentEmptyStates().length).toBe(2);
});
it('shows the tabs', () => {
expect(findNetworkPolicyTab().exists()).toBe(true);
expect(findStatisticsTab().exists()).toBe(true);
});
it('does not show the network policy list', () => {
expect(findNetworkPolicyList().exists()).toBe(false);
});
it('does not show the threat monitoring section', () => {
expect(findNetworkPolicySection().exists()).toBe(false);
});
},
);
......
import { shallowMount } from '@vue/test-utils';
import { GlEmptyState, GlSprintf } from '@gitlab/ui';
import {
EMPTY_STATE_DESCRIPTION,
NO_ENVIRONMENT_TITLE,
} from 'ee/threat_monitoring/components/constants';
import NoEnvironmentEmptyState from 'ee/threat_monitoring/components/no_environment_empty_state.vue';
const documentationPath = '/docs';
const emptyStateSvgPath = '/svgs';
describe('NoEnvironmentEmptyState component', () => {
let wrapper;
const findGlEmptyState = () => wrapper.find(GlEmptyState);
const findGlSprintf = () => wrapper.find(GlSprintf);
const factory = () => {
wrapper = shallowMount(NoEnvironmentEmptyState, {
provide: {
documentationPath,
emptyStateSvgPath,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('default state', () => {
beforeEach(() => {
factory();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('shows the GlEmptyState component', () => {
expect(findGlEmptyState().exists()).toBe(true);
expect(findGlEmptyState().attributes()).toMatchObject({
title: NO_ENVIRONMENT_TITLE,
svgpath: emptyStateSvgPath,
});
});
it('shows the message', () => {
expect(findGlSprintf().exists()).toBe(true);
expect(findGlSprintf().attributes('message')).toBe(EMPTY_STATE_DESCRIPTION);
});
});
});
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