Commit 3c4d5efb authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '321884-move-policies-page-to-its-own-page' into 'master'

Mount the policies list component on the scan policy page

See merge request gitlab-org/gitlab!67419
parents dcb9f8d6 43e2cef2
<script> <script>
import { mapActions } from 'vuex';
import NoEnvironmentEmptyState from '../no_environment_empty_state.vue';
import PoliciesHeader from './policies_header.vue'; import PoliciesHeader from './policies_header.vue';
import PoliciesList from './policies_list.vue';
export default { export default {
components: { components: {
PoliciesHeader, PoliciesHeader,
PoliciesList,
NoEnvironmentEmptyState,
},
inject: ['defaultEnvironmentId'],
data() {
return {
// We require the project to have at least one available environment.
// An invalid default environment id means there there are no available
// environments, therefore infrastructure cannot be set up. A valid default
// environment id only means that infrastructure *might* be set up.
shouldFetchEnvironment: this.isValidEnvironmentId(this.defaultEnvironmentId),
};
},
created() {
if (this.shouldFetchEnvironment) {
this.setCurrentEnvironmentId(this.defaultEnvironmentId);
this.fetchEnvironments();
}
},
methods: {
...mapActions('threatMonitoring', ['fetchEnvironments', 'setCurrentEnvironmentId']),
isValidEnvironmentId(id) {
return Number.isInteger(id) && id >= 0;
},
}, },
}; };
</script> </script>
<template> <template>
<policies-header /> <div>
<policies-header />
<no-environment-empty-state v-if="!shouldFetchEnvironment" />
<policies-list v-else />
</div>
</template> </template>
<script>
import {
GlTable,
GlEmptyState,
GlAlert,
GlSprintf,
GlLink,
GlIcon,
GlTooltipDirective,
} from '@gitlab/ui';
import { mapState, mapGetters } from 'vuex';
import { PREDEFINED_NETWORK_POLICIES } from 'ee/threat_monitoring/constants';
import createFlash from '~/flash';
import { getTimeago } from '~/lib/utils/datetime_utility';
import { setUrlFragment, mergeUrlParams } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import networkPoliciesQuery from '../../graphql/queries/network_policies.query.graphql';
import scanExecutionPoliciesQuery from '../../graphql/queries/scan_execution_policies.query.graphql';
import { POLICY_TYPE_OPTIONS } from '../constants';
import EnvironmentPicker from '../environment_picker.vue';
import PolicyDrawer from '../policy_drawer/policy_drawer.vue';
import PolicyEnvironments from '../policy_environments.vue';
import PolicyTypeFilter from '../policy_type_filter.vue';
const createPolicyFetchError = ({ gqlError, networkError }) => {
const error =
gqlError?.message ||
networkError?.message ||
s__('NetworkPolicies|Something went wrong, unable to fetch policies');
createFlash({
message: error,
});
};
const getPoliciesWithType = (policies, policyType) =>
policies.map((policy) => ({
...policy,
policyType,
}));
export default {
components: {
GlTable,
GlEmptyState,
GlAlert,
GlSprintf,
GlLink,
GlIcon,
EnvironmentPicker,
PolicyTypeFilter,
PolicyDrawer,
PolicyEnvironments,
},
directives: {
GlTooltip: GlTooltipDirective,
},
inject: ['projectPath', 'documentationPath', 'newPolicyPath'],
apollo: {
networkPolicies: {
query: networkPoliciesQuery,
variables() {
return {
fullPath: this.projectPath,
environmentId: this.allEnvironments ? null : this.currentEnvironmentGid,
};
},
update(data) {
const policies = data?.project?.networkPolicies?.nodes ?? [];
const predefined = PREDEFINED_NETWORK_POLICIES.filter(
({ name }) => !policies.some((policy) => name === policy.name),
);
return [...policies, ...predefined];
},
error: createPolicyFetchError,
skip() {
return this.isLoadingEnvironments || !this.shouldShowNetworkPolicies;
},
},
scanExecutionPolicies: {
query: scanExecutionPoliciesQuery,
variables() {
return {
fullPath: this.projectPath,
};
},
update(data) {
return data?.project?.scanExecutionPolicies?.nodes ?? [];
},
error: createPolicyFetchError,
},
},
data() {
return {
selectedPolicy: null,
networkPolicies: [],
scanExecutionPolicies: [],
selectedPolicyType: POLICY_TYPE_OPTIONS.ALL.value,
};
},
computed: {
...mapState('threatMonitoring', [
'currentEnvironmentId',
'allEnvironments',
'isLoadingEnvironments',
]),
...mapGetters('threatMonitoring', ['currentEnvironmentGid']),
allPolicyTypes() {
return {
[POLICY_TYPE_OPTIONS.POLICY_TYPE_NETWORK.value]: this.networkPolicies,
[POLICY_TYPE_OPTIONS.POLICY_TYPE_SCAN_EXECUTION.value]: this.scanExecutionPolicies,
};
},
documentationFullPath() {
return setUrlFragment(this.documentationPath, 'container-network-policy');
},
shouldShowNetworkPolicies() {
return [
POLICY_TYPE_OPTIONS.ALL.value,
POLICY_TYPE_OPTIONS.POLICY_TYPE_NETWORK.value,
].includes(this.selectedPolicyType);
},
policies() {
const policyTypes =
this.selectedPolicyType === POLICY_TYPE_OPTIONS.ALL.value
? Object.keys(this.allPolicyTypes)
: [this.selectedPolicyType];
const policies = policyTypes.map((type) =>
getPoliciesWithType(this.allPolicyTypes[type], POLICY_TYPE_OPTIONS[type].text),
);
return policies.flat();
},
isLoadingPolicies() {
return (
this.isLoadingEnvironments ||
this.$apollo.queries.networkPolicies.loading ||
this.$apollo.queries.scanExecutionPolicies.loading
);
},
hasSelectedPolicy() {
return Boolean(this.selectedPolicy);
},
hasAutoDevopsPolicy() {
return Boolean(this.networkPolicies?.some((policy) => policy.fromAutoDevops));
},
editPolicyPath() {
return this.hasSelectedPolicy
? mergeUrlParams(
!this.selectedPolicy.kind
? { environment_id: this.currentEnvironmentId }
: { environment_id: this.currentEnvironmentId, kind: this.selectedPolicy.kind },
this.newPolicyPath.replace('new', `${this.selectedPolicy.name}/edit`),
)
: '';
},
fields() {
const environments = {
key: 'environments',
label: s__('SecurityPolicies|Environment(s)'),
};
const fields = [
{
key: 'status',
label: '',
thClass: 'gl-w-3',
tdAttr: {
'data-testid': 'policy-status-cell',
},
},
{
key: 'name',
label: __('Name'),
thClass: 'gl-w-half',
},
{
key: 'policyType',
label: s__('SecurityPolicies|Policy type'),
sortable: true,
},
{
key: 'updatedAt',
label: __('Last modified'),
sortable: true,
},
];
// Adds column 'environments' only while 'all environments' option is selected
if (this.allEnvironments) fields.splice(2, 0, environments);
return fields;
},
},
methods: {
getTimeAgoString(updatedAt) {
if (!updatedAt) return '';
return getTimeago().format(updatedAt);
},
presentPolicyDrawer(rows) {
if (rows.length === 0) return;
const [selectedPolicy] = rows;
this.selectedPolicy = selectedPolicy;
},
deselectPolicy() {
this.selectedPolicy = null;
const bTable = this.$refs.policiesTable.$children[0];
bTable.clearSelected();
},
},
i18n: {
emptyStateDescription: s__(
`NetworkPolicies|Policies are a specification of how groups of pods are allowed to communicate with each other's network endpoints.`,
),
autodevopsNoticeDescription: s__(
`NetworkPolicies|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}.`,
),
emptyStateButton: __('Learn more'),
emptyStateTitle: s__('NetworkPolicies|No policies detected'),
statusEnabled: __('Enabled'),
statusDisabled: __('Disabled'),
},
};
</script>
<template>
<div>
<gl-alert
v-if="hasAutoDevopsPolicy"
data-testid="autodevopsAlert"
variant="info"
:dismissible="false"
class="gl-mb-3"
>
<gl-sprintf :message="$options.i18n.autodevopsNoticeDescription">
<template #monospaced="{ content }">
<span class="gl-font-monospace">{{ content }}</span>
</template>
<template #link="{ content }">
<gl-link :href="documentationFullPath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<div class="gl-pt-5 gl-px-5 gl-bg-gray-10">
<div class="row gl-justify-content-space-between gl-align-items-center">
<div class="col-12 col-sm-8 col-md-6 col-lg-5 row">
<environment-picker data-testid="environment-picker" class="col-6" :include-all="true" />
<policy-type-filter
v-model="selectedPolicyType"
class="col-6"
data-testid="policy-type-filter"
/>
</div>
</div>
</div>
<gl-table
ref="policiesTable"
:busy="isLoadingPolicies"
:items="policies"
:fields="fields"
sort-icon-left
sort-by="updatedAt"
sort-desc
head-variant="white"
stacked="md"
thead-class="gl-text-gray-900 border-bottom"
tbody-class="gl-text-gray-900"
show-empty
hover
selectable
select-mode="single"
selected-variant="primary"
@row-selected="presentPolicyDrawer"
>
<template #cell(status)="value">
<gl-icon
v-if="value.item.enabled"
v-gl-tooltip="$options.i18n.statusEnabled"
:aria-label="$options.i18n.statusEnabled"
name="check-circle-filled"
class="gl-text-green-700"
/>
<span v-else class="gl-sr-only">{{ $options.i18n.statusDisabled }}</span>
</template>
<template #cell(environments)="value">
<policy-environments :environments="value.item.environments" />
</template>
<template #cell(updatedAt)="value">
{{ getTimeAgoString(value.item.updatedAt) }}
</template>
<template #empty>
<slot name="empty-state">
<gl-empty-state
ref="tableEmptyState"
:title="$options.i18n.emptyStateTitle"
:description="$options.i18n.emptyStateDescription"
:primary-button-link="documentationFullPath"
:primary-button-text="$options.i18n.emptyStateButton"
/>
</slot>
</template>
</gl-table>
<policy-drawer
:open="hasSelectedPolicy"
:policy="selectedPolicy"
:edit-policy-path="editPolicyPath"
data-testid="policyDrawer"
@close="deselectPolicy"
/>
</div>
</template>
...@@ -39,10 +39,8 @@ export default () => { ...@@ -39,10 +39,8 @@ export default () => {
} = el.dataset; } = el.dataset;
const store = createStore(); const store = createStore();
store.dispatch('threatMonitoring/setEndpoints', { store.dispatch('threatMonitoring/setStatisticsEndpoint', networkPolicyStatisticsEndpoint);
networkPolicyStatisticsEndpoint, store.dispatch('threatMonitoring/setEnvironmentEndpoint', environmentsEndpoint);
environmentsEndpoint,
});
return new Vue({ return new Vue({
apolloProvider, apolloProvider,
......
...@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo'; ...@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import SecurityPoliciesApp from './components/policies/policies_app.vue'; import SecurityPoliciesApp from './components/policies/policies_app.vue';
import createStore from './store';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -15,13 +16,20 @@ export default () => { ...@@ -15,13 +16,20 @@ export default () => {
const { const {
assignedPolicyProject, assignedPolicyProject,
disableSecurityPolicyProject, disableSecurityPolicyProject,
defaultEnvironmentId,
environmentsEndpoint,
emptyStateSvgPath,
documentationPath, documentationPath,
newPolicyPath, newPolicyPath,
projectPath, projectPath,
} = el.dataset; } = el.dataset;
const store = createStore();
store.dispatch('threatMonitoring/setEnvironmentEndpoint', environmentsEndpoint);
return new Vue({ return new Vue({
apolloProvider, apolloProvider,
store,
el, el,
provide: { provide: {
assignedPolicyProject: JSON.parse(assignedPolicyProject), assignedPolicyProject: JSON.parse(assignedPolicyProject),
...@@ -29,6 +37,8 @@ export default () => { ...@@ -29,6 +37,8 @@ export default () => {
documentationPath, documentationPath,
newPolicyPath, newPolicyPath,
projectPath, projectPath,
emptyStateSvgPath,
defaultEnvironmentId: parseInt(defaultEnvironmentId, 10),
}, },
render(createElement) { render(createElement) {
return createElement(SecurityPoliciesApp); return createElement(SecurityPoliciesApp);
......
...@@ -3,13 +3,12 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -3,13 +3,12 @@ import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import * as types from './mutation_types'; import * as types from './mutation_types';
export const setEndpoints = ({ commit }, endpoints) => { export const setEnvironmentEndpoint = ({ commit }, endpoint) => {
commit(types.SET_ENDPOINT, endpoints.environmentsEndpoint); commit(types.SET_ENDPOINT, endpoint);
commit( };
`threatMonitoringNetworkPolicy/${types.SET_ENDPOINT}`,
endpoints.networkPolicyStatisticsEndpoint, export const setStatisticsEndpoint = ({ commit }, endpoint) => {
{ root: true }, commit(`threatMonitoringNetworkPolicy/${types.SET_ENDPOINT}`, endpoint, { root: true });
);
}; };
export const requestEnvironments = ({ commit }) => commit(types.REQUEST_ENVIRONMENTS); export const requestEnvironments = ({ commit }) => commit(types.REQUEST_ENVIRONMENTS);
......
- breadcrumb_title _("Policies") - breadcrumb_title _("Policies")
- disable_security_policy_project = !can_update_security_orchestration_policy_project?(project) - disable_security_policy_project = !can_update_security_orchestration_policy_project?(project)
- default_environment_id = project.default_environment&.id || -1
#js-security-policies-list{ data: { assigned_policy_project: assigned_policy_project(project).to_json, #js-security-policies-list{ data: { assigned_policy_project: assigned_policy_project(project).to_json,
default_environment_id: default_environment_id,
disable_security_policy_project: disable_security_policy_project.to_s, disable_security_policy_project: disable_security_policy_project.to_s,
documentation_path: help_page_path('user/application_security/policies/index.md'), documentation_path: help_page_path('user/application_security/policies/index.md'),
empty_state_svg_path: image_path('illustrations/monitoring/unable_to_connect.svg'),
new_policy_path: new_project_threat_monitoring_policy_path(project), new_policy_path: new_project_threat_monitoring_policy_path(project),
environments_endpoint: project_environments_path(project),
project_path: project.full_path } } project_path: project.full_path } }
import NoEnvironmentEmptyState from 'ee/threat_monitoring/components/no_environment_empty_state.vue';
import PoliciesApp from 'ee/threat_monitoring/components/policies/policies_app.vue'; import PoliciesApp from 'ee/threat_monitoring/components/policies/policies_app.vue';
import PoliciesHeader from 'ee/threat_monitoring/components/policies/policies_header.vue'; import PoliciesHeader from 'ee/threat_monitoring/components/policies/policies_header.vue';
import PoliciesList from 'ee/threat_monitoring/components/policies/policies_list.vue';
import createStore from 'ee/threat_monitoring/store';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
describe('Policies App', () => { describe('Policies App', () => {
let wrapper; let wrapper;
let store;
let setCurrentEnvironmentIdSpy;
let fetchEnvironmentsSpy;
const findPoliciesHeader = () => wrapper.findComponent(PoliciesHeader); const findPoliciesHeader = () => wrapper.findComponent(PoliciesHeader);
const findPoliciesList = () => wrapper.findComponent(PoliciesList);
const findEmptyState = () => wrapper.findComponent(NoEnvironmentEmptyState);
beforeEach(() => { const createWrapper = ({ provide } = {}) => {
wrapper = shallowMountExtended(PoliciesApp); store = createStore();
});
setCurrentEnvironmentIdSpy = jest
.spyOn(PoliciesApp.methods, 'setCurrentEnvironmentId')
.mockImplementation(() => {});
fetchEnvironmentsSpy = jest
.spyOn(PoliciesApp.methods, 'fetchEnvironments')
.mockImplementation(() => {});
wrapper = shallowMountExtended(PoliciesApp, {
store,
provide: {
defaultEnvironmentId: -1,
...provide,
},
});
};
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
it('mounts the policies header component', () => { describe('when does have an environment enabled', () => {
expect(findPoliciesHeader().exists()).toBe(true); beforeEach(() => {
createWrapper({ provide: { defaultEnvironmentId: 22 } });
});
it('mounts the policies header component', () => {
expect(findPoliciesHeader().exists()).toBe(true);
});
it('mounts the policies list component', () => {
expect(findPoliciesList().exists()).toBe(true);
});
it('does not mount the empty state', () => {
expect(findEmptyState().exists()).toBe(false);
});
it('fetches the environments when created', async () => {
expect(setCurrentEnvironmentIdSpy).toHaveBeenCalled();
expect(fetchEnvironmentsSpy).toHaveBeenCalled();
});
});
describe('when does not have an environment enabled', () => {
beforeEach(() => {
createWrapper();
});
it('mounts the policies header component', () => {
expect(findPoliciesHeader().exists()).toBe(true);
});
it('does not mount the policies list component', () => {
expect(findPoliciesList().exists()).toBe(false);
});
it('mounts the empty state', () => {
expect(findEmptyState().exists()).toBe(true);
});
it('does not fetch the environments when created', () => {
expect(setCurrentEnvironmentIdSpy).not.toHaveBeenCalled();
expect(fetchEnvironmentsSpy).not.toHaveBeenCalled();
});
}); });
}); });
import { GlTable, GlDrawer } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import { merge } from 'lodash';
import VueApollo from 'vue-apollo';
import { POLICY_TYPE_OPTIONS } from 'ee/threat_monitoring/components/constants';
import PoliciesList from 'ee/threat_monitoring/components/policies/policies_list.vue';
import PolicyDrawer from 'ee/threat_monitoring/components/policy_drawer/policy_drawer.vue';
import networkPoliciesQuery from 'ee/threat_monitoring/graphql/queries/network_policies.query.graphql';
import scanExecutionPoliciesQuery from 'ee/threat_monitoring/graphql/queries/scan_execution_policies.query.graphql';
import createStore from 'ee/threat_monitoring/store';
import createMockApollo from 'helpers/mock_apollo_helper';
import { stubComponent } from 'helpers/stub_component';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { networkPolicies, scanExecutionPolicies } from '../../mocks/mock_apollo';
import {
mockNetworkPoliciesResponse,
mockScanExecutionPoliciesResponse,
} from '../../mocks/mock_data';
const localVue = createLocalVue();
localVue.use(VueApollo);
const fullPath = 'project/path';
const environments = [
{
id: 2,
global_id: 'gid://gitlab/Environment/2',
},
];
const defaultRequestHandlers = {
networkPolicies: networkPolicies(mockNetworkPoliciesResponse),
scanExecutionPolicies: scanExecutionPolicies(mockScanExecutionPoliciesResponse),
};
const pendingHandler = jest.fn(() => new Promise(() => {}));
describe('PoliciesList component', () => {
let store;
let wrapper;
let requestHandlers;
const factory = (mountFn = mountExtended) => (options = {}) => {
store = createStore();
const { state, handlers, ...wrapperOptions } = options;
Object.assign(store.state.networkPolicies, {
...state,
});
store.state.threatMonitoring.environments = environments;
requestHandlers = {
...defaultRequestHandlers,
...handlers,
};
jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve());
wrapper = mountFn(
PoliciesList,
merge(
{
propsData: {
documentationPath: 'documentation_path',
newPolicyPath: '/policies/new',
},
store,
provide: {
documentationPath: 'path/to/docs',
newPolicyPath: 'path/to/policy',
projectPath: fullPath,
},
apolloProvider: createMockApollo([
[networkPoliciesQuery, requestHandlers.networkPolicies],
[scanExecutionPoliciesQuery, requestHandlers.scanExecutionPolicies],
]),
stubs: {
PolicyDrawer: stubComponent(PolicyDrawer, {
props: {
...PolicyDrawer.props,
...GlDrawer.props,
},
}),
},
localVue,
},
wrapperOptions,
),
);
};
const mountShallowWrapper = factory(shallowMountExtended);
const mountWrapper = factory();
const findPolicyTypeFilter = () => wrapper.findByTestId('policy-type-filter');
const findEnvironmentsPicker = () => wrapper.findByTestId('environment-picker');
const findPoliciesTable = () => wrapper.findComponent(GlTable);
const findPolicyStatusCells = () => wrapper.findAllByTestId('policy-status-cell');
const findPolicyDrawer = () => wrapper.findByTestId('policyDrawer');
const findAutodevopsAlert = () => wrapper.findByTestId('autodevopsAlert');
afterEach(() => {
wrapper.destroy();
});
describe('initial state', () => {
beforeEach(() => {
mountShallowWrapper({
handlers: {
networkPolicies: pendingHandler,
},
});
});
it('renders EnvironmentPicker', () => {
expect(findEnvironmentsPicker().exists()).toBe(true);
});
it('renders closed editor drawer', () => {
const editorDrawer = findPolicyDrawer();
expect(editorDrawer.exists()).toBe(true);
expect(editorDrawer.props('open')).toBe(false);
});
it('does not render autodevops alert', () => {
expect(findAutodevopsAlert().exists()).toBe(false);
});
it('fetches policies', () => {
expect(requestHandlers.networkPolicies).toHaveBeenCalledWith({
fullPath,
});
expect(requestHandlers.scanExecutionPolicies).toHaveBeenCalledWith({
fullPath,
});
});
it("sets table's loading state", () => {
expect(findPoliciesTable().attributes('busy')).toBe('true');
});
});
describe('given policies have been fetched', () => {
let rows;
beforeEach(async () => {
mountWrapper();
await waitForPromises();
rows = wrapper.findAll('tr');
});
it('fetches network policies on environment change', async () => {
store.dispatch.mockReset();
await store.commit('threatMonitoring/SET_CURRENT_ENVIRONMENT_ID', 2);
expect(requestHandlers.networkPolicies).toHaveBeenCalledTimes(2);
expect(requestHandlers.networkPolicies.mock.calls[1][0]).toEqual({
fullPath: 'project/path',
environmentId: environments[0].global_id,
});
});
it('if network policies are filtered out, changing the environment does not trigger a fetch', async () => {
store.dispatch.mockReset();
expect(requestHandlers.networkPolicies).toHaveBeenCalledTimes(1);
findPolicyTypeFilter().vm.$emit(
'input',
POLICY_TYPE_OPTIONS.POLICY_TYPE_SCAN_EXECUTION.value,
);
await store.commit('threatMonitoring/SET_CURRENT_ENVIRONMENT_ID', 2);
expect(requestHandlers.networkPolicies).toHaveBeenCalledTimes(1);
});
describe.each`
rowIndex | expectedPolicyName | expectedPolicyType
${1} | ${mockScanExecutionPoliciesResponse[0].name} | ${'Scan execution'}
${2} | ${mockNetworkPoliciesResponse[0].name} | ${'Network'}
${3} | ${'drop-outbound'} | ${'Network'}
${4} | ${'allow-inbound-http'} | ${'Network'}
`('policy in row #$rowIndex', ({ rowIndex, expectedPolicyName, expectedPolicyType }) => {
let row;
beforeEach(() => {
row = rows.at(rowIndex);
});
it(`renders ${expectedPolicyName} in the name cell`, () => {
expect(row.findAll('td').at(1).text()).toBe(expectedPolicyName);
});
it(`renders ${expectedPolicyType} in the policy type cell`, () => {
expect(row.findAll('td').at(2).text()).toBe(expectedPolicyType);
});
});
it.each`
description | filterBy | hiddenTypes
${'network'} | ${POLICY_TYPE_OPTIONS.POLICY_TYPE_NETWORK} | ${[POLICY_TYPE_OPTIONS.POLICY_TYPE_SCAN_EXECUTION]}
${'scan execution'} | ${POLICY_TYPE_OPTIONS.POLICY_TYPE_SCAN_EXECUTION} | ${[POLICY_TYPE_OPTIONS.POLICY_TYPE_NETWORK]}
`('policies filtered by $description type', async ({ filterBy, hiddenTypes }) => {
findPolicyTypeFilter().vm.$emit('input', filterBy.value);
await wrapper.vm.$nextTick();
expect(findPoliciesTable().text()).toContain(filterBy.text);
hiddenTypes.forEach((hiddenType) => {
expect(findPoliciesTable().text()).not.toContain(hiddenType.text);
});
});
});
describe('status column', () => {
beforeEach(() => {
mountWrapper();
});
it('renders a checkmark icon for enabled policies', () => {
const icon = findPolicyStatusCells().at(1).find('svg');
expect(icon.exists()).toBe(true);
expect(icon.props()).toMatchObject({
name: 'check-circle-filled',
ariaLabel: 'Enabled',
});
});
it('renders a "Disabled" label for screen readers for disabled policies', () => {
const span = findPolicyStatusCells().at(2).find('span');
expect(span.exists()).toBe(true);
expect(span.attributes('class')).toBe('gl-sr-only');
expect(span.text()).toBe('Disabled');
});
});
describe('with allEnvironments enabled', () => {
beforeEach(() => {
mountWrapper();
wrapper.vm.$store.state.threatMonitoring.allEnvironments = true;
});
it('renders environments column', () => {
const environmentsHeader = findPoliciesTable().findAll('[role="columnheader"]').at(2);
expect(environmentsHeader.text()).toContain('Environment(s)');
});
});
describe.each`
description | policy
${'network'} | ${mockNetworkPoliciesResponse[0]}
${'scan execution'} | ${mockScanExecutionPoliciesResponse[0]}
`('given there is a $description policy selected', ({ policy }) => {
beforeEach(() => {
mountShallowWrapper();
findPoliciesTable().vm.$emit('row-selected', [policy]);
});
it('renders opened editor drawer', () => {
const editorDrawer = findPolicyDrawer();
expect(editorDrawer.exists()).toBe(true);
expect(editorDrawer.props()).toMatchObject({
open: true,
policy,
});
});
});
describe('given an autodevops policy', () => {
beforeEach(() => {
const autoDevOpsPolicy = {
...mockNetworkPoliciesResponse[0],
name: 'auto-devops',
fromAutoDevops: true,
};
mountShallowWrapper({
handlers: {
networkPolicies: networkPolicies([autoDevOpsPolicy]),
},
});
});
it('renders autodevops alert', () => {
expect(findAutodevopsAlert().exists()).toBe(true);
});
});
});
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import * as actions from 'ee/threat_monitoring/store/modules/threat_monitoring/actions'; import * as actions from 'ee/threat_monitoring/store/modules/threat_monitoring/actions';
import * as types from 'ee/threat_monitoring/store/modules/threat_monitoring/mutation_types'; import * as types from 'ee/threat_monitoring/store/modules/threat_monitoring/mutation_types';
import getInitialState from 'ee/threat_monitoring/store/modules/threat_monitoring/state'; import getInitialState from 'ee/threat_monitoring/store/modules/threat_monitoring/state';
...@@ -26,20 +25,13 @@ describe('Threat Monitoring actions', () => { ...@@ -26,20 +25,13 @@ describe('Threat Monitoring actions', () => {
createFlash.mockClear(); createFlash.mockClear();
}); });
describe('setEndpoints', () => { describe('threatMonitoring/setStatisticsEndpoint', () => {
it('commits the SET_ENDPOINT mutation', () => it('commits the SET_ENDPOINT mutation', () =>
testAction( testAction(
actions.setEndpoints, actions.setStatisticsEndpoint,
{ networkPolicyStatisticsEndpoint,
environmentsEndpoint,
networkPolicyStatisticsEndpoint,
},
state, state,
[ [
{
type: types.SET_ENDPOINT,
payload: environmentsEndpoint,
},
{ {
type: `threatMonitoringNetworkPolicy/${types.SET_ENDPOINT}`, type: `threatMonitoringNetworkPolicy/${types.SET_ENDPOINT}`,
payload: networkPolicyStatisticsEndpoint, payload: networkPolicyStatisticsEndpoint,
...@@ -49,6 +41,22 @@ describe('Threat Monitoring actions', () => { ...@@ -49,6 +41,22 @@ describe('Threat Monitoring actions', () => {
)); ));
}); });
describe('threatMonitoring/setEnvironmentEndpoint', () => {
it('commits the SET_ENDPOINT mutation', () =>
testAction(
actions.setEnvironmentEndpoint,
environmentsEndpoint,
state,
[
{
type: types.SET_ENDPOINT,
payload: environmentsEndpoint,
},
],
[],
));
});
describe('requestEnvironments', () => { describe('requestEnvironments', () => {
it('commits the REQUEST_ENVIRONMENTS mutation', () => it('commits the REQUEST_ENVIRONMENTS mutation', () =>
testAction( testAction(
......
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