Commit 31c9a406 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'network-policy-editor-rules-builder-func' into 'master'

Implement rule builder component for the policy editor

See merge request gitlab-org/gitlab!39276
parents 2abd09dc af67f31e
<script>
import { mapActions } from 'vuex';
import {
GlFormGroup,
GlFormSelect,
......@@ -6,7 +7,6 @@ import {
GlFormTextarea,
GlToggle,
GlSegmentedControl,
GlLink,
GlButton,
} from '@gitlab/ui';
import { s__ } from '~/locale';
......@@ -15,7 +15,13 @@ import NetworkPolicyEditor from '../network_policy_editor.vue';
import PolicyRuleBuilder from './policy_rule_builder.vue';
import PolicyPreview from './policy_preview.vue';
import PolicyActionPicker from './policy_action_picker.vue';
import { EditorModeRule, EditorModeYAML } from './constants';
import {
EditorModeRule,
EditorModeYAML,
EndpointMatchModeAny,
RuleTypeEndpoint,
} from './constants';
import { buildRule } from './lib/rules';
export default {
components: {
......@@ -25,7 +31,6 @@ export default {
GlFormTextarea,
GlToggle,
GlSegmentedControl,
GlLink,
GlButton,
EnvironmentPicker,
NetworkPolicyEditor,
......@@ -34,7 +39,17 @@ export default {
PolicyActionPicker,
},
data() {
return { editorMode: EditorModeRule };
return {
editorMode: EditorModeRule,
policy: {
name: '',
description: '',
isEnabled: false,
endpointMatchMode: EndpointMatchModeAny,
endpointLabels: '',
rules: [],
},
};
},
computed: {
shouldShowRuleEditor() {
......@@ -44,6 +59,25 @@ export default {
return this.editorMode === EditorModeYAML;
},
},
created() {
this.fetchEnvironments();
},
methods: {
...mapActions('threatMonitoring', ['fetchEnvironments']),
addRule() {
this.policy.rules.push(buildRule(RuleTypeEndpoint));
},
updateEndpointMatchMode(mode) {
this.policy.endpointMatchMode = mode;
},
updateEndpointLabels(labels) {
this.policy.endpointLabels = labels;
},
updateRuleType(ruleIdx, ruleType) {
const rule = this.policy.rules[ruleIdx];
this.policy.rules.splice(ruleIdx, 1, buildRule(ruleType, rule));
},
},
policyTypes: [{ value: 'networkPolicy', text: s__('NetworkPolicies|Network Policy') }],
editorModes: [
{ value: EditorModeRule, text: s__('NetworkPolicies|Rule mode') },
......@@ -73,14 +107,14 @@ export default {
</div>
<div class="col-sm-6 col-md-6 col-lg-5 col-xl-4">
<gl-form-group :label="s__('NetworkPolicies|Name')" label-for="policyName">
<gl-form-input id="policyName" />
<gl-form-input id="policyName" v-model="policy.name" />
</gl-form-group>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-10 col-lg-8 col-xl-6">
<gl-form-group :label="s__('NetworkPolicies|Description')" label-for="policyDescription">
<gl-form-textarea id="policyDescription" />
<gl-form-textarea id="policyDescription" v-model="policy.description" />
</gl-form-group>
</div>
</div>
......@@ -90,7 +124,7 @@ export default {
<div class="row">
<div class="col-md-auto">
<gl-form-group :label="s__('NetworkPolicies|Policy status')" label-for="policyStatus">
<gl-toggle id="policyStatus" />
<gl-toggle id="policyStatus" v-model="policy.isEnabled" />
</gl-form-group>
</div>
</div>
......@@ -105,10 +139,23 @@ export default {
<div v-if="shouldShowRuleEditor" class="row" data-testid="rule-editor">
<div class="col-sm-12 col-md-6 col-lg-7 col-xl-8">
<h4>{{ s__('NetworkPolicies|Rules') }}</h4>
<policy-rule-builder />
<policy-rule-builder
v-for="(rule, idx) in policy.rules"
:key="idx"
class="gl-mb-4"
:rule="rule"
:endpoint-match-mode="policy.endpointMatchMode"
:endpoint-labels="policy.endpointLabels"
:endpoint-selector-disabled="idx > 0"
@rule-type-change="updateRuleType(idx, $event)"
@endpoint-match-mode-change="updateEndpointMatchMode"
@endpoint-labels-change="updateEndpointLabels"
/>
<div class="gl-my-2 gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100">
<gl-link href="#">{{ s__('Network Policy|New rule') }}</gl-link>
<div class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100">
<gl-button variant="link" category="primary" data-testid="add-rule" @click="addRule">{{
s__('Network Policy|New rule')
}}</gl-button>
</div>
<h4>{{ s__('NetworkPolicies|Actions') }}</h4>
......
<script>
export default {};
import { GlSprintf, GlForm, GlFormSelect, GlFormInput } from '@gitlab/ui';
import { s__ } from '~/locale';
import {
RuleTypeNetwork,
RuleDirectionInbound,
RuleDirectionOutbound,
EndpointMatchModeAny,
EndpointMatchModeLabel,
RuleTypeEndpoint,
RuleTypeEntity,
RuleTypeCIDR,
RuleTypeFQDN,
PortMatchModeAny,
PortMatchModePortProtocol,
} from './constants';
import PolicyRuleEndpoint from './policy_rule_endpoint.vue';
import PolicyRuleEntity from './policy_rule_entity.vue';
import PolicyRuleCIDR from './policy_rule_cidr.vue';
import PolicyRuleFQDN from './policy_rule_fqdn.vue';
export default {
components: {
GlSprintf,
GlForm,
GlFormSelect,
GlFormInput,
PolicyRuleEndpoint,
PolicyRuleEntity,
'policy-rule-cidr': PolicyRuleCIDR,
'policy-rule-fqdn': PolicyRuleFQDN,
},
props: {
rule: {
type: Object,
required: true,
},
endpointMatchMode: {
type: String,
required: true,
},
endpointLabels: {
type: String,
required: true,
},
endpointSelectorDisabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return { ruleType: RuleTypeNetwork };
},
computed: {
shouldShowEndpointLabels() {
return this.endpointMatchMode === EndpointMatchModeLabel;
},
sprintfTemplate() {
if (this.rule.direction === RuleDirectionInbound) {
return s__(
'NetworkPolicies|%{ifLabelStart}if%{ifLabelEnd} %{ruleType} %{isLabelStart}is%{isLabelEnd} %{ruleDirection} %{ruleSelector} %{directionLabelStart}and is inbound from a%{directionLabelEnd} %{rule} %{portsLabelStart}on%{portsLabelEnd} %{ports}',
);
}
return s__(
'NetworkPolicies|%{ifLabelStart}if%{ifLabelEnd} %{ruleType} %{isLabelStart}is%{isLabelEnd} %{ruleDirection} %{ruleSelector} %{directionLabelStart}and is outbound to a%{directionLabelEnd} %{rule} %{portsLabelStart}on%{portsLabelEnd} %{ports}',
);
},
currentRuleComponent() {
const { ruleComponents } = this.$options;
return ruleComponents[this.rule.ruleType] || ruleComponents[RuleTypeEndpoint];
},
ruleComponentName() {
const { component } = this.currentRuleComponent;
return component;
},
ruleComponentModel: {
get() {
const { field } = this.currentRuleComponent;
return this.rule[field];
},
set(value) {
const { field } = this.currentRuleComponent;
this.rule[field] = value;
},
},
shouldShowPorts() {
return this.rule.portMatchMode === PortMatchModePortProtocol;
},
},
ruleTypes: [{ value: RuleTypeNetwork, text: s__('NetworkPolicies|Network traffic') }],
trafficDirections: [
{ value: RuleDirectionInbound, text: s__('NetworkPolicies|inbound to') },
{ value: RuleDirectionOutbound, text: s__('NetworkPolicies|outbound from') },
],
endpointMatchModes: [
{ value: EndpointMatchModeAny, text: s__('NetworkPolicies|any pod') },
{ value: EndpointMatchModeLabel, text: s__('NetworkPolicies|pods with labels') },
],
ruleModes: [
{ value: RuleTypeEndpoint, text: s__('NetworkPolicies|pod with labels') },
{ value: RuleTypeEntity, text: s__('NetworkPolicies|entity') },
{ value: RuleTypeCIDR, text: s__('NetworkPolicies|IP/subnet') },
{ value: RuleTypeFQDN, text: s__('NetworkPolicies|domain name') },
],
portMatchModes: [
{ value: PortMatchModeAny, text: s__('NetworkPolicies|any port') },
{ value: PortMatchModePortProtocol, text: s__('NetworkPolicies|ports/protocols') },
],
ruleComponents: {
[RuleTypeEndpoint]: {
component: 'policy-rule-endpoint',
field: 'matchLabels',
},
[RuleTypeEntity]: {
component: 'policy-rule-entity',
field: 'entities',
},
[RuleTypeCIDR]: {
component: 'policy-rule-cidr',
field: 'cidr',
},
[RuleTypeFQDN]: {
component: 'policy-rule-fqdn',
field: 'fqdn',
},
},
};
</script>
<template>
<div class="gl-bg-gray-100 p-7"></div>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base px-3 pt-3"
>
<gl-form inline>
<gl-sprintf :message="sprintfTemplate">
<template #ifLabel="{ content }">
<label for="ruleType" class="text-uppercase gl-font-lg gl-mr-4 gl-mb-5!">{{
content
}}</label>
</template>
<template #ruleType>
<gl-form-select
id="ruleType"
class="gl-mr-4 gl-mb-5"
:value="ruleType"
:options="$options.ruleTypes"
/>
</template>
<template #isLabel="{ content }">
<label for="direction" class="gl-mr-4 gl-mb-5!">{{ content }}</label>
</template>
<template #ruleDirection>
<gl-form-select
id="direction"
v-model="rule.direction"
class="gl-mr-4 gl-mb-5"
:options="$options.trafficDirections"
/>
</template>
<template #ruleSelector>
<gl-form-select
data-testid="endpoint-match-mode"
class="gl-mr-4 gl-mb-5"
:value="endpointMatchMode"
:disabled="endpointSelectorDisabled"
:options="$options.endpointMatchModes"
@change="$emit('endpoint-match-mode-change', $event)"
/>
<!-- placeholder is the same in all languages-->
<!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
<gl-form-input
v-if="shouldShowEndpointLabels"
data-testid="endpoint-labels"
class="gl-mr-4 gl-mb-5"
placeholder="key:value"
:value="endpointLabels"
:disabled="endpointSelectorDisabled"
@update="$emit('endpoint-labels-change', $event)"
/>
<!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
</template>
<template #directionLabel="{ content }">
<label for="ruleMode" class="gl-mr-4 gl-mb-5!">{{ content }}</label>
</template>
<template #rule>
<gl-form-select
id="ruleMode"
class="gl-mr-4 gl-mb-5"
:value="rule.ruleType"
:options="$options.ruleModes"
@change="$emit('rule-type-change', $event)"
/>
<component :is="ruleComponentName" v-model="ruleComponentModel" class="gl-mr-4 gl-mb-5" />
</template>
<template #portsLabel="{ content }">
<label for="portMatch" class="gl-mr-4 gl-mb-5!">{{ content }}</label>
</template>
<template #ports>
<gl-form-select
id="portMatch"
v-model="rule.portMatchMode"
class="gl-mr-4 gl-mb-5"
:options="$options.portMatchModes"
/>
<!-- placeholder is the same in all languages-->
<!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
<gl-form-input
v-if="shouldShowPorts"
v-model="rule.ports"
data-testid="ports"
class="gl-mr-4 gl-mb-5"
placeholder="80/tcp"
/>
<!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
</template>
</gl-sprintf>
</gl-form>
</div>
</template>
<script>
import { GlFormInput } from '@gitlab/ui';
export default {
components: {
GlFormInput,
},
props: {
value: {
type: String,
required: true,
},
},
};
</script>
<template>
<!-- placeholder is the same in all languages-->
<!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
<gl-form-input placeholder="0.0.0.0/24" :value="value" @input="$emit('input', $event)" />
<!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
</template>
<script>
import { GlFormInput } from '@gitlab/ui';
export default {
components: {
GlFormInput,
},
props: {
value: {
type: String,
required: true,
},
},
};
</script>
<template>
<!-- placeholder is the same in all languages-->
<!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
<gl-form-input placeholder="key:value" :value="value" @input="$emit('input', $event)" />
<!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
</template>
<script>
import { GlNewDropdown as GlDropdown, GlNewDropdownItem as GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import { EntityTypes } from './constants';
export default {
components: {
GlDropdown,
GlDropdownItem,
},
model: {
prop: 'value',
event: 'change',
},
props: {
value: {
type: Array,
required: true,
},
},
computed: {
selectedEntities() {
const { value } = this;
if (value.length === 0) {
return s__('NetworkPolicies|None selected');
}
if (value.includes(EntityTypes.ALL)) return s__('NetworkPolicies|All selected');
if (value.length > 3) {
return sprintf(s__('NetworkPolicies|%{number} selected'), { number: value.length });
}
return value.join(', ');
},
},
methods: {
selectEntity(entity) {
const { value } = this;
let entitiesList = [];
if (value.includes(entity)) {
entitiesList = value.filter(e => e !== entity);
} else {
entitiesList = [...value, entity];
}
if (
entitiesList.includes(EntityTypes.ALL) ||
entitiesList.length === Object.keys(EntityTypes).length - 1
) {
entitiesList = [EntityTypes.ALL];
}
this.$emit('change', entitiesList);
},
isSelectedEntity(entity) {
const { value } = this;
if (value.includes(EntityTypes.ALL)) return true;
return value.includes(entity);
},
},
entities: Object.keys(EntityTypes).map(type => ({
value: EntityTypes[type],
text: EntityTypes[type],
})),
};
</script>
<template>
<gl-dropdown :text="selectedEntities" multiple>
<gl-dropdown-item
v-for="entity in $options.entities"
:key="entity.value"
is-check-item
:is-checked="isSelectedEntity(entity.value)"
@click="selectEntity(entity.value)"
>{{ entity.text }}</gl-dropdown-item
>
</gl-dropdown>
</template>
<script>
import { GlFormInput } from '@gitlab/ui';
export default {
components: {
GlFormInput,
},
props: {
value: {
type: String,
required: true,
},
},
};
</script>
<template>
<!-- placeholder is the same in all languages-->
<!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
<gl-form-input placeholder="remote-service.com" :value="value" @input="$emit('input', $event)" />
<!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
</template>
......@@ -4,9 +4,12 @@ import createStore from './store';
export default () => {
const el = document.querySelector('#js-policy-builder-app');
const { networkPoliciesEndpoint } = el.dataset;
const { environmentsEndpoint, networkPoliciesEndpoint } = el.dataset;
const store = createStore();
store.dispatch('threatMonitoring/setEndpoints', {
environmentsEndpoint,
});
store.dispatch('networkPolicies/setEndpoints', {
networkPoliciesEndpoint,
});
......
......@@ -2,4 +2,6 @@
- breadcrumb_title s_("NetworkPolicies|New policy")
- page_title s_("NetworkPolicies|Policy editor")
#js-policy-builder-app{ data: { network_policies_endpoint: project_security_network_policies_path(@project) } }
#js-policy-builder-app{ data: { network_policies_endpoint: project_security_network_policies_path(@project),
environments_endpoint: project_environments_path(@project),
} }
......@@ -70,6 +70,7 @@ exports[`PolicyEditorApp component renders the policy editor layout 1`] = `
>
<gl-form-input-stub
id="policyName"
value=""
/>
</gl-form-group-stub>
</div>
......@@ -148,17 +149,19 @@ exports[`PolicyEditorApp component renders the policy editor layout 1`] = `
<h4>
Rules
</h4>
<policy-rule-builder-stub />
<div
class="gl-my-2 gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
>
<gl-link-stub
href="#"
<gl-button-stub
category="primary"
data-testid="add-rule"
icon=""
size="medium"
variant="link"
>
New rule
</gl-link-stub>
</gl-button-stub>
</div>
<h4>
......
import { shallowMount } from '@vue/test-utils';
import PolicyEditorApp from 'ee/threat_monitoring/components/policy_editor/policy_editor.vue';
import PolicyRuleBuilder from 'ee/threat_monitoring/components/policy_editor/policy_rule_builder.vue';
import createStore from 'ee/threat_monitoring/store';
import {
RuleDirectionInbound,
PortMatchModeAny,
RuleTypeEndpoint,
} from 'ee/threat_monitoring/components/policy_editor/constants';
describe('PolicyEditorApp component', () => {
let store;
......@@ -25,7 +31,7 @@ describe('PolicyEditorApp component', () => {
const findYamlEditor = () => wrapper.find('[data-testid="yaml-editor"]');
beforeEach(() => {
factory({});
factory();
});
afterEach(() => {
......@@ -60,4 +66,25 @@ describe('PolicyEditorApp component', () => {
expect(editor.element).toMatchSnapshot();
});
});
it('adds a new rule', async () => {
expect(wrapper.findAll(PolicyRuleBuilder).length).toEqual(0);
const button = wrapper.find("[data-testid='add-rule']");
button.vm.$emit('click');
button.vm.$emit('click');
await wrapper.vm.$nextTick();
const elements = wrapper.findAll(PolicyRuleBuilder);
expect(elements.length).toEqual(2);
elements.wrappers.forEach((builder, idx) => {
expect(builder.props().rule).toMatchObject({
ruleType: RuleTypeEndpoint,
direction: RuleDirectionInbound,
matchLabels: '',
portMatchMode: PortMatchModeAny,
ports: '',
});
expect(builder.props().endpointSelectorDisabled).toEqual(idx !== 0);
});
});
});
import { mount } from '@vue/test-utils';
import PolicyRuleBuilder from 'ee/threat_monitoring/components/policy_editor/policy_rule_builder.vue';
import PolicyRuleEndpoint from 'ee/threat_monitoring/components/policy_editor/policy_rule_endpoint.vue';
import PolicyRuleEntity from 'ee/threat_monitoring/components/policy_editor/policy_rule_entity.vue';
import PolicyRuleCIDR from 'ee/threat_monitoring/components/policy_editor/policy_rule_cidr.vue';
import PolicyRuleFQDN from 'ee/threat_monitoring/components/policy_editor/policy_rule_fqdn.vue';
import { buildRule } from 'ee/threat_monitoring/components/policy_editor/lib/rules';
import {
RuleDirectionOutbound,
EndpointMatchModeAny,
EndpointMatchModeLabel,
RuleTypeEntity,
RuleTypeCIDR,
RuleTypeFQDN,
PortMatchModePortProtocol,
} from 'ee/threat_monitoring/components/policy_editor/constants';
describe('PolicyRuleBuilder component', () => {
let wrapper;
let rule;
const factory = ({ propsData } = {}) => {
wrapper = mount(PolicyRuleBuilder, {
propsData: {
rule,
endpointMatchMode: EndpointMatchModeAny,
endpointLabels: '',
...propsData,
},
});
};
beforeEach(() => {
rule = buildRule();
factory();
});
function selectFirstOption(sel) {
const el = wrapper.find(sel);
el.findAll('option')
.at(1)
.setSelected();
el.trigger('change');
}
const findEndpointLabels = () => wrapper.find("[data-testid='endpoint-labels']");
const findRuleEndpoint = () => wrapper.find(PolicyRuleEndpoint);
const findRuleEntity = () => wrapper.find(PolicyRuleEntity);
const findRuleCIDR = () => wrapper.find(PolicyRuleCIDR);
const findRuleFQDN = () => wrapper.find(PolicyRuleFQDN);
const findPorts = () => wrapper.find("[data-testid='ports']");
afterEach(() => {
wrapper.destroy();
});
it('updates rule direction upon selecting', async () => {
selectFirstOption("[id='direction']");
await wrapper.vm.$nextTick();
expect(rule.direction).toEqual(RuleDirectionOutbound);
});
it('emits endpoint-match-mode-change upon selecting', async () => {
selectFirstOption("[data-testid='endpoint-match-mode']");
await wrapper.vm.$nextTick();
const event = wrapper.emitted()['endpoint-match-mode-change'];
expect(event.length).toEqual(2);
expect(event[0]).toEqual([EndpointMatchModeLabel]);
});
it('does not render endpoint labels input', () => {
expect(findEndpointLabels().exists()).toBe(false);
});
describe('when endpoint match mode is labels', () => {
beforeEach(() => {
factory({
propsData: {
endpointMatchMode: EndpointMatchModeLabel,
},
});
});
it('renders endpoint labels input', () => {
expect(findEndpointLabels().exists()).toBe(true);
});
it('emits endpoint-labels-change on change', async () => {
const input = findEndpointLabels();
input.setValue('foo:bar');
await wrapper.vm.$nextTick();
const event = wrapper.emitted()['endpoint-labels-change'];
expect(event.length).toEqual(1);
expect(event[0]).toEqual(['foo:bar']);
});
});
it('emits rule-type-change upon selecting', async () => {
selectFirstOption("[id='ruleMode']");
await wrapper.vm.$nextTick();
const event = wrapper.emitted()['rule-type-change'];
expect(event.length).toEqual(2);
expect(event[0]).toEqual([RuleTypeEntity]);
});
it('renders only endpoint rule component', () => {
expect(findRuleEndpoint().exists()).toBe(true);
expect(findRuleEntity().exists()).toBe(false);
expect(findRuleCIDR().exists()).toBe(false);
expect(findRuleFQDN().exists()).toBe(false);
});
describe('when policy type is entity', () => {
beforeEach(() => {
rule = buildRule(RuleTypeEntity);
factory();
});
it('renders only entity rule component', () => {
expect(findRuleEndpoint().exists()).toBe(false);
expect(findRuleEntity().exists()).toBe(true);
expect(findRuleCIDR().exists()).toBe(false);
expect(findRuleFQDN().exists()).toBe(false);
});
it('updates entity types', async () => {
const el = findRuleEntity();
el.findAll('button')
.filter(e => e.text() === 'host')
.trigger('click');
await wrapper.vm.$nextTick();
expect(rule.entities).toEqual(['host']);
});
});
describe('when policy type is cidr', () => {
beforeEach(() => {
rule = buildRule(RuleTypeCIDR);
factory();
});
it('renders only cidr rule component', () => {
expect(findRuleEndpoint().exists()).toBe(false);
expect(findRuleEntity().exists()).toBe(false);
expect(findRuleCIDR().exists()).toBe(true);
expect(findRuleFQDN().exists()).toBe(false);
});
it('updates cidr', async () => {
const el = findRuleCIDR();
el.setValue('0.0.0.0/24');
el.trigger('change');
await wrapper.vm.$nextTick();
expect(rule.cidr).toEqual('0.0.0.0/24');
});
});
describe('when policy type is fqdn', () => {
beforeEach(() => {
rule = buildRule(RuleTypeFQDN);
factory();
});
it('renders only fqdn rule component', () => {
expect(findRuleEndpoint().exists()).toBe(false);
expect(findRuleEntity().exists()).toBe(false);
expect(findRuleCIDR().exists()).toBe(false);
expect(findRuleFQDN().exists()).toBe(true);
});
it('updates fqdn', async () => {
const el = findRuleFQDN();
el.setValue('some-service.com');
el.trigger('change');
await wrapper.vm.$nextTick();
expect(rule.fqdn).toEqual('some-service.com');
});
});
it('updates port match mode upon selecting', async () => {
selectFirstOption("[id='portMatch']");
await wrapper.vm.$nextTick();
expect(rule.portMatchMode).toEqual(PortMatchModePortProtocol);
});
it('does not render ports input', () => {
expect(findPorts().exists()).toBe(false);
});
describe('when port match mode is port/protocol', () => {
beforeEach(() => {
rule.portMatchMode = PortMatchModePortProtocol;
factory();
});
it('renders ports input', () => {
expect(findPorts().exists()).toBe(true);
});
it('updates ports', async () => {
const input = findPorts();
input.setValue('80/tcp');
await wrapper.vm.$nextTick();
expect(rule.ports).toEqual('80/tcp');
});
});
});
import { shallowMount } from '@vue/test-utils';
import PolicyRuleEntity from 'ee/threat_monitoring/components/policy_editor/policy_rule_entity.vue';
import { GlNewDropdown as GlDropdown, GlNewDropdownItem as GlDropdownItem } from '@gitlab/ui';
import { EntityTypes } from 'ee/threat_monitoring/components/policy_editor/constants';
describe('PolicyRuleEntity component', () => {
let wrapper;
const factory = ({ value = [] } = {}) => {
wrapper = shallowMount(PolicyRuleEntity, {
propsData: {
value,
},
});
};
beforeEach(() => {
factory();
});
afterEach(() => {
wrapper.destroy();
});
const findDropdown = () => wrapper.find(GlDropdown);
describe("when value has 'all' entity", () => {
beforeEach(() => {
factory({ value: [EntityTypes.ALL, EntityTypes.HOST] });
});
it('selects all items', () => {
const dropdown = findDropdown();
const selectedItems = dropdown.findAll(GlDropdownItem).filter(el => el.props('isChecked'));
expect(selectedItems.length).toEqual(Object.keys(EntityTypes).length);
expect(dropdown.props('text')).toEqual('All selected');
});
});
describe('when all entities are selected', () => {
beforeEach(() => {
const value = Object.keys(EntityTypes)
.map(key => EntityTypes[key])
.filter(entity => entity !== EntityTypes.ALL && entity !== EntityTypes.HOST);
factory({ value });
});
it("emits change with 'all' entity", () => {
const dropdown = findDropdown();
dropdown
.findAll(GlDropdownItem)
.filter(el => el.text() === EntityTypes.HOST)
.at(0)
.vm.$emit('click');
const emitted = wrapper.emitted().change;
expect(emitted.length).toEqual(1);
expect(emitted[0]).toEqual([[EntityTypes.ALL]]);
});
});
});
......@@ -16110,12 +16110,24 @@ msgstr ""
msgid "Network Policy|New rule"
msgstr ""
msgid "NetworkPolicies|%{ifLabelStart}if%{ifLabelEnd} %{ruleType} %{isLabelStart}is%{isLabelEnd} %{ruleDirection} %{ruleSelector} %{directionLabelStart}and is inbound from a%{directionLabelEnd} %{rule} %{portsLabelStart}on%{portsLabelEnd} %{ports}"
msgstr ""
msgid "NetworkPolicies|%{ifLabelStart}if%{ifLabelEnd} %{ruleType} %{isLabelStart}is%{isLabelEnd} %{ruleDirection} %{ruleSelector} %{directionLabelStart}and is outbound to a%{directionLabelEnd} %{rule} %{portsLabelStart}on%{portsLabelEnd} %{ports}"
msgstr ""
msgid "NetworkPolicies|%{number} selected"
msgstr ""
msgid "NetworkPolicies|.yaml mode"
msgstr ""
msgid "NetworkPolicies|Actions"
msgstr ""
msgid "NetworkPolicies|All selected"
msgstr ""
msgid "NetworkPolicies|Choose whether to enforce this policy."
msgstr ""
......@@ -16137,6 +16149,9 @@ msgstr ""
msgid "NetworkPolicies|Environment does not have deployment platform"
msgstr ""
msgid "NetworkPolicies|IP/subnet"
msgstr ""
msgid "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}."
msgstr ""
......@@ -16155,12 +16170,18 @@ msgstr ""
msgid "NetworkPolicies|Network Policy"
msgstr ""
msgid "NetworkPolicies|Network traffic"
msgstr ""
msgid "NetworkPolicies|New policy"
msgstr ""
msgid "NetworkPolicies|No policies detected"
msgstr ""
msgid "NetworkPolicies|None selected"
msgstr ""
msgid "NetworkPolicies|Policies are a specification of how groups of pods are allowed to communicate with each other's network endpoints."
msgstr ""
......@@ -16203,6 +16224,33 @@ msgstr ""
msgid "NetworkPolicies|YAML editor"
msgstr ""
msgid "NetworkPolicies|any pod"
msgstr ""
msgid "NetworkPolicies|any port"
msgstr ""
msgid "NetworkPolicies|domain name"
msgstr ""
msgid "NetworkPolicies|entity"
msgstr ""
msgid "NetworkPolicies|inbound to"
msgstr ""
msgid "NetworkPolicies|outbound from"
msgstr ""
msgid "NetworkPolicies|pod with labels"
msgstr ""
msgid "NetworkPolicies|pods with labels"
msgstr ""
msgid "NetworkPolicies|ports/protocols"
msgstr ""
msgid "Never"
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