Commit 3a6ec5d5 authored by Savas Vedova's avatar Savas Vedova Committed by Enrique Alcántara

Move scan policy logic to modal

parent 58776173
<script>
import PoliciesHeader from './policies_header.vue';
export default {
components: {
PoliciesHeader,
},
};
</script>
<template>
<policies-header />
</template>
<script>
import { GlAlert, GlSprintf, GlButton } from '@gitlab/ui';
import { s__ } from '~/locale';
import ScanNewPolicyModal from './scan_new_policy_modal.vue';
export default {
components: {
GlSprintf,
GlButton,
GlAlert,
ScanNewPolicyModal,
},
inject: ['documentationPath', 'assignedPolicyProject', 'newPolicyPath'],
i18n: {
title: s__('NetworkPolicies|Policies'),
subtitle: s__(
'NetworkPolicies|Enforce security for this project. %{linkStart}More information.%{linkEnd}',
),
newPolicyButtonText: s__('NetworkPolicies|New policy'),
editPolicyButtonText: s__('NetworkPolicies|Edit policy project'),
},
data() {
return {
projectIsBeingLinked: false,
showAlert: false,
alertVariant: '',
alertText: '',
modalVisible: false,
};
},
computed: {
hasAssignedPolicyProject() {
return Boolean(this.assignedPolicyProject?.id);
},
},
methods: {
updateAlertText({ text, variant }) {
this.projectIsBeingLinked = false;
if (text) {
this.showAlert = true;
this.alertVariant = variant;
this.alertText = text;
}
},
isUpdatingProject() {
this.projectIsBeingLinked = true;
this.showAlert = false;
this.alertVariant = '';
this.alertText = '';
},
dismissAlert() {
this.showAlert = false;
},
showNewPolicyModal() {
this.modalVisible = true;
},
},
};
</script>
<template>
<div>
<gl-alert
v-if="showAlert"
class="gl-mt-3"
data-testid="policy-project-alert"
:dismissible="true"
:variant="alertVariant"
@dismiss="dismissAlert"
>
{{ alertText }}
</gl-alert>
<header class="gl-my-6 gl-display-flex gl-align-items-flex-start">
<div class="gl-flex-grow-1 gl-my-0">
<h2 class="gl-mt-0">
{{ $options.i18n.title }}
</h2>
<p data-testid="policies-subheader">
<gl-sprintf :message="$options.i18n.subtitle">
<template #link="{ content }">
<gl-button class="gl-pb-1!" variant="link" :href="documentationPath" target="_blank">
{{ content }}
</gl-button>
</template>
</gl-sprintf>
</p>
</div>
<gl-button
data-testid="edit-project-policy-button"
class="gl-mr-4"
:loading="projectIsBeingLinked"
@click="showNewPolicyModal"
>
{{ $options.i18n.editPolicyButtonText }}
</gl-button>
<gl-button data-testid="new-policy-button" variant="confirm" :href="newPolicyPath">
{{ $options.i18n.newPolicyButtonText }}
</gl-button>
<scan-new-policy-modal
:visible="modalVisible"
@close="modalVisible = false"
@project-updated="updateAlertText"
@updating-project="isUpdatingProject"
/>
</header>
</div>
</template>
<script> <script>
import { GlAlert, GlButton, GlDropdown, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; import { GlButton, GlDropdown, GlSprintf, GlAlert, GlModal } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__, __ } from '~/locale';
import assignSecurityPolicyProject from '../graphql/mutations/assign_security_policy_project.mutation.graphql'; import assignSecurityPolicyProject from '../../graphql/mutations/assign_security_policy_project.mutation.graphql';
import InstanceProjectSelector from './instance_project_selector.vue'; import InstanceProjectSelector from '../instance_project_selector.vue';
export default { export default {
PROJECT_SELECTOR_HEIGHT: 204, PROJECT_SELECTOR_HEIGHT: 204,
i18n: { i18n: {
assignError: s__( modal: {
'SecurityOrchestration|An error occurred assigning your security policy project', okTitle: __('Save'),
), header: s__('SecurityOrchestration|Select security project'),
assignSuccess: s__('SecurityOrchestration|Security policy project was linked successfully'), },
disabledButtonTooltip: s__( save: {
'SecurityOrchestration|Only owners can update Security Policy Project', ok: s__('SecurityOrchestration|Security policy project was linked successfully'),
), error: s__('SecurityOrchestration|An error occurred assigning your security policy project'),
securityProject: s__( },
'SecurityOrchestration|A security policy project can enforce policies for a given project, group, or instance. With a security policy project, you can specify security policies that are important to you and enforce them with every commit. %{linkStart}More information.%{linkEnd}', disabledWarning: s__('SecurityOrchestration|Only owners can update Security Policy Project'),
description: s__(
'SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}',
), ),
}, },
components: { components: {
GlAlert,
GlButton, GlButton,
GlDropdown, GlDropdown,
GlSprintf, GlSprintf,
GlModal,
GlAlert,
InstanceProjectSelector, InstanceProjectSelector,
}, },
directives: { inject: [
GlTooltip: GlTooltipDirective, 'disableSecurityPolicyProject',
}, 'documentationPath',
inject: ['disableSecurityPolicyProject', 'documentationPath', 'projectPath'], 'projectPath',
'assignedPolicyProject',
],
props: { props: {
assignedPolicyProject: { visible: {
type: Object, type: Boolean,
required: false, required: false,
default: () => { default: false,
return { id: '', name: '' };
},
}, },
}, },
data() { data() {
return { return {
currentProjectId: this.assignedPolicyProject.id, selectedProject: { ...this.assignedPolicyProject },
selectedProject: this.assignedPolicyProject, hasSelectedNewProject: false,
isAssigningProject: false,
showAssignError: false,
showAssignSuccess: false,
}; };
}, },
computed: { computed: {
hasSelectedNewProject() { selectedProjectId() {
return this.currentProjectId !== this.selectedProject.id; return this.selectedProject?.id || '';
},
selectedProjectName() {
return this.selectedProject?.name || '';
},
isModalOkButtonDisabled() {
return this.disableSecurityPolicyProject || !this.hasSelectedNewProject;
}, },
}, },
methods: { methods: {
dismissAlert(type) {
this[type] = false;
},
async saveChanges() { async saveChanges() {
this.isAssigningProject = true; this.$emit('updating-project');
this.showAssignError = false;
this.showAssignSuccess = false;
const { id } = this.selectedProject;
try { try {
const { data } = await this.$apollo.mutate({ const { data } = await this.$apollo.mutate({
mutation: assignSecurityPolicyProject, mutation: assignSecurityPolicyProject,
variables: { variables: {
input: { input: {
projectPath: this.projectPath, projectPath: this.projectPath,
securityPolicyProjectId: id, securityPolicyProjectId: this.selectedProjectId,
}, },
}, },
}); });
if (data?.securityPolicyProjectAssign?.errors?.length) { if (data?.securityPolicyProjectAssign?.errors?.length) {
this.showAssignError = true; throw new Error(data.securityPolicyProjectAssign.errors);
} else {
this.showAssignSuccess = true;
this.currentProjectId = id;
} }
this.$emit('project-updated', { text: this.$options.i18n.save.ok, variant: 'success' });
} catch { } catch {
this.showAssignError = true; this.$emit('project-updated', { text: this.$options.i18n.save.error, variant: 'danger' });
} finally { } finally {
this.isAssigningProject = false; this.hasSelectedNewProject = false;
} }
}, },
setSelectedProject(data) { setSelectedProject(data) {
this.hasSelectedNewProject = true;
this.selectedProject = data; this.selectedProject = data;
this.$refs.dropdown.hide(true); this.$refs.dropdown.hide();
},
closeModal() {
this.$emit('close');
}, },
}, },
}; };
</script> </script>
<template> <template>
<section> <gl-modal
<gl-alert v-bind="$attrs"
v-if="showAssignError" ref="modal"
class="gl-mt-3" cancel-variant="light"
data-testid="policy-project-assign-error" size="sm"
variant="danger" modal-id="scan-new-policy"
:dismissible="true" :scrollable="false"
@dismiss="dismissAlert('showAssignError')" :ok-title="$options.i18n.modal.okTitle"
> :title="$options.i18n.modal.header"
{{ $options.i18n.assignError }} :ok-disabled="isModalOkButtonDisabled"
</gl-alert> :visible="visible"
<gl-alert @ok="saveChanges"
v-else-if="showAssignSuccess" @change="closeModal"
class="gl-mt-3" >
data-testid="policy-project-assign-success" <div>
variant="success" <gl-alert
:dismissible="true" v-if="disableSecurityPolicyProject"
@dismiss="dismissAlert('showAssignSuccess')" class="gl-mb-4"
> variant="warning"
{{ $options.i18n.assignSuccess }} :dismissible="false"
</gl-alert> >
<h2 class="gl-mb-8"> {{ $options.i18n.disabledWarning }}
{{ s__('SecurityOrchestration|Create a policy') }} </gl-alert>
</h2>
<div class="gl-w-half">
<h4>
{{ s__('SecurityOrchestration|Security policy project') }}
</h4>
<gl-dropdown <gl-dropdown
ref="dropdown" ref="dropdown"
class="gl-w-full gl-pb-5 security-policy-dropdown" class="gl-w-full gl-pb-5"
menu-class="gl-w-full! gl-max-w-full!" menu-class="gl-w-full! gl-max-w-full!"
:disabled="disableSecurityPolicyProject" :disabled="disableSecurityPolicyProject"
:text="selectedProject.name || ''" :text="selectedProjectName"
> >
<instance-project-selector <instance-project-selector
class="gl-w-full" class="gl-w-full"
...@@ -135,7 +135,7 @@ export default { ...@@ -135,7 +135,7 @@ export default {
/> />
</gl-dropdown> </gl-dropdown>
<div class="gl-pb-5"> <div class="gl-pb-5">
<gl-sprintf :message="$options.i18n.securityProject"> <gl-sprintf :message="$options.i18n.description">
<template #link="{ content }"> <template #link="{ content }">
<gl-button class="gl-pb-1!" variant="link" :href="documentationPath" target="_blank"> <gl-button class="gl-pb-1!" variant="link" :href="documentationPath" target="_blank">
{{ content }} {{ content }}
...@@ -143,24 +143,6 @@ export default { ...@@ -143,24 +143,6 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</div> </div>
<span
v-gl-tooltip="{
disabled: !disableSecurityPolicyProject,
title: $options.i18n.disabledButtonTooltip,
placement: 'bottom',
}"
data-testid="disabled-button-tooltip"
>
<gl-button
data-testid="save-policy-project"
variant="confirm"
:disabled="disableSecurityPolicyProject || !hasSelectedNewProject"
:loading="isAssigningProject"
@click="saveChanges"
>
{{ __('Save changes') }}
</gl-button>
</span>
</div> </div>
</section> </gl-modal>
</template> </template>
...@@ -2,7 +2,7 @@ import Vue from 'vue'; ...@@ -2,7 +2,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo'; 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 SecurityPolicyProjectSelector from './components/security_policy_project_selector.vue'; import SecurityPoliciesApp from './components/policies/policies_app.vue';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -16,24 +16,22 @@ export default () => { ...@@ -16,24 +16,22 @@ export default () => {
assignedPolicyProject, assignedPolicyProject,
disableSecurityPolicyProject, disableSecurityPolicyProject,
documentationPath, documentationPath,
newPolicyPath,
projectPath, projectPath,
} = el.dataset; } = el.dataset;
const policyProject = JSON.parse(assignedPolicyProject);
const props = policyProject ? { assignedPolicyProject: policyProject } : {};
return new Vue({ return new Vue({
apolloProvider, apolloProvider,
el, el,
provide: { provide: {
assignedPolicyProject: JSON.parse(assignedPolicyProject),
disableSecurityPolicyProject: parseBoolean(disableSecurityPolicyProject), disableSecurityPolicyProject: parseBoolean(disableSecurityPolicyProject),
documentationPath, documentationPath,
newPolicyPath,
projectPath, projectPath,
}, },
render(createElement) { render(createElement) {
return createElement(SecurityPolicyProjectSelector, { return createElement(SecurityPoliciesApp);
props,
});
}, },
}); });
}; };
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
#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,
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/project/clusters/protect/container_network_security/quick_start_guide'), documentation_path: help_page_path('user/project/clusters/protect/container_network_security/quick_start_guide'),
new_policy_path: new_project_threat_monitoring_policy_path(project),
project_path: project.full_path } } project_path: project.full_path } }
import PoliciesApp from 'ee/threat_monitoring/components/policies/policies_app.vue';
import PoliciesHeader from 'ee/threat_monitoring/components/policies/policies_header.vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
describe('Policies App', () => {
let wrapper;
const findPoliciesHeader = () => wrapper.findComponent(PoliciesHeader);
beforeEach(() => {
wrapper = shallowMountExtended(PoliciesApp);
});
afterEach(() => {
wrapper.destroy();
});
it('mounts the policies header component', () => {
expect(findPoliciesHeader().exists()).toBe(true);
});
});
import { GlAlert, GlButton, GlSprintf } from '@gitlab/ui';
import PoliciesHeader from 'ee/threat_monitoring/components/policies/policies_header.vue';
import ScanNewPolicyModal from 'ee/threat_monitoring/components/policies/scan_new_policy_modal.vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
describe('Policies Header Component', () => {
let wrapper;
const documentationPath = '/path/to/docs';
const newPolicyPath = '/path/to/new/policy/page';
const findAlert = () => wrapper.findComponent(GlAlert);
const findScanNewPolicyModal = () => wrapper.findComponent(ScanNewPolicyModal);
const findHeader = () => wrapper.findByRole('heading');
const findMoreInformationLink = () => wrapper.findComponent(GlButton);
const findEditPolicyProjectButton = () => wrapper.findByTestId('edit-project-policy-button');
const findNewPolicyButton = () => wrapper.findByTestId('new-policy-button');
const findSubheader = () => wrapper.findByTestId('policies-subheader');
const createWrapper = ({ provide } = {}) => {
wrapper = shallowMountExtended(PoliciesHeader, {
provide: {
documentationPath,
newPolicyPath,
assignedPolicyProject: null,
...provide,
},
stubs: {
GlSprintf,
GlButton,
},
});
};
afterEach(() => {
wrapper.destroy();
});
beforeEach(() => {
createWrapper();
});
it('displays New policy button with correct text and link', () => {
expect(findNewPolicyButton().text()).toBe('New policy');
expect(findNewPolicyButton().attributes('href')).toBe(newPolicyPath);
});
it('displays the Edit policy project button', () => {
expect(findEditPolicyProjectButton().text()).toBe('Edit policy project');
});
it('does not display the alert component by default', () => {
expect(findAlert().exists()).toBe(false);
});
it('displays the alert component when scan new modal policy emits events', async () => {
const text = 'Project was linked successfully.';
findScanNewPolicyModal().vm.$emit('project-updated', {
text,
variant: 'success',
});
// When the project is updated it displays the output message.
await wrapper.vm.$nextTick();
expect(findAlert().text()).toBe(text);
// When the project is being updated once again, it removes the alert so that
// the new one will be displayed.
findScanNewPolicyModal().vm.$emit('updating-project');
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(false);
});
it('mounts the scan new policy modal', () => {
expect(findScanNewPolicyModal().exists()).toBe(true);
});
it('displays scan new policy modal when the action button is clicked', async () => {
await findEditPolicyProjectButton().trigger('click');
expect(findScanNewPolicyModal().props().visible).toBe(true);
});
it('displays the header', () => {
expect(findHeader().text()).toBe('Policies');
});
it('displays the subheader', () => {
expect(findSubheader().text()).toContain('Enforce security for this project.');
expect(findMoreInformationLink().attributes('href')).toBe(documentationPath);
});
});
import { GlDropdown } from '@gitlab/ui'; import { GlDropdown, GlModal, GlAlert } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils'; import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import InstanceProjectSelector from 'ee/threat_monitoring/components/instance_project_selector.vue'; import InstanceProjectSelector from 'ee/threat_monitoring/components/instance_project_selector.vue';
import SecurityPolicyProjectSelector from 'ee/threat_monitoring/components/security_policy_project_selector.vue'; import ScanNewPolicyModal from 'ee/threat_monitoring/components/policies/scan_new_policy_modal.vue';
import assignSecurityPolicyProject from 'ee/threat_monitoring/graphql/mutations/assign_security_policy_project.mutation.graphql'; import assignSecurityPolicyProject from 'ee/threat_monitoring/graphql/mutations/assign_security_policy_project.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { stubComponent } from 'helpers/stub_component';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises'; import { mockAssignSecurityPolicyProjectResponses } from '../../mocks/mock_apollo';
import {
apolloFailureResponse,
mockAssignSecurityPolicyProjectResponses,
} from '../mocks/mock_apollo';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
describe('SecurityPolicyProjectSelector Component', () => { describe('ScanNewPolicyModal Component', () => {
let wrapper; let wrapper;
let projectUpdatedListener;
const findSaveButton = () => wrapper.findByTestId('save-policy-project');
const findDropdown = () => wrapper.findComponent(GlDropdown); const findDropdown = () => wrapper.findComponent(GlDropdown);
const findErrorAlert = () => wrapper.findByTestId('policy-project-assign-error');
const findInstanceProjectSelector = () => wrapper.findComponent(InstanceProjectSelector); const findInstanceProjectSelector = () => wrapper.findComponent(InstanceProjectSelector);
const findSuccessAlert = () => wrapper.findByTestId('policy-project-assign-success'); const findAlert = () => wrapper.findComponent(GlAlert);
const findTooltip = () => wrapper.findByTestId('disabled-button-tooltip'); const findModal = () => wrapper.findComponent(GlModal);
const selectProject = async () => { const selectProject = async (
findInstanceProjectSelector().vm.$emit('projectClicked', { project = {
id: 'gid://gitlab/Project/1', id: 'gid://gitlab/Project/1',
name: 'Test 1', name: 'Test 1',
}); },
) => {
findInstanceProjectSelector().vm.$emit('projectClicked', project);
await wrapper.vm.$nextTick();
findModal().vm.$emit('ok');
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
findSaveButton().vm.$emit('click');
await waitForPromises();
}; };
const createWrapper = ({ const createWrapper = ({
mount = shallowMountExtended,
mutationResult = mockAssignSecurityPolicyProjectResponses.success, mutationResult = mockAssignSecurityPolicyProjectResponses.success,
propsData = {},
provide = {}, provide = {},
} = {}) => { } = {}) => {
wrapper = mount(SecurityPolicyProjectSelector, { wrapper = mountExtended(ScanNewPolicyModal, {
localVue, localVue,
apolloProvider: createMockApollo([[assignSecurityPolicyProject, mutationResult]]), apolloProvider: createMockApollo([[assignSecurityPolicyProject, mutationResult]]),
directives: { stubs: {
GlTooltip: createMockDirective(), GlModal: stubComponent(GlModal, {
template:
'<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>',
}),
}, },
propsData,
provide: { provide: {
disableSecurityPolicyProject: false, disableSecurityPolicyProject: false,
documentationPath: 'test/path/index.md', documentationPath: 'test/path/index.md',
projectPath: 'path/to/project', projectPath: 'path/to/project',
assignedPolicyProject: null,
...provide, ...provide,
}, },
}); });
projectUpdatedListener = jest.fn();
wrapper.vm.$on('project-updated', projectUpdatedListener);
};
const createWrapperAndSelectProject = async (data) => {
createWrapper(data);
await selectProject();
}; };
afterEach(() => { afterEach(() => {
...@@ -67,72 +73,69 @@ describe('SecurityPolicyProjectSelector Component', () => { ...@@ -67,72 +73,69 @@ describe('SecurityPolicyProjectSelector Component', () => {
createWrapper(); createWrapper();
}); });
it.each` it('passes down correct properties/attributes to the gl-modal component', () => {
findComponent | state | title expect(findModal().props()).toMatchObject({
${findDropdown} | ${true} | ${'does display the dropdown'} modalId: 'scan-new-policy',
${findInstanceProjectSelector} | ${true} | ${'does display the project selector'} size: 'sm',
${findErrorAlert} | ${false} | ${'does not display the error alert'} visible: false,
${findSuccessAlert} | ${false} | ${'does not display the success alert'} title: 'Select security project',
`('$title', ({ findComponent, state }) => { });
expect(findComponent().exists()).toBe(state);
});
it('renders the "Save Changes" button', () => { expect(findModal().attributes()).toEqual({
const button = findSaveButton(); 'ok-disabled': 'true',
expect(button.exists()).toBe(true); 'ok-title': 'Save',
expect(button.attributes('disabled')).toBe('true'); 'cancel-variant': 'light',
});
}); });
it('does not display a tooltip', () => { it('does not display a warning', () => {
const tooltip = getBinding(findTooltip().element, 'gl-tooltip'); expect(findAlert().exists()).toBe(false);
expect(tooltip.value.disabled).toBe(true);
}); });
}); });
it('emits close event when gl-modal emits change event', () => {
createWrapper();
findModal().vm.$emit('change');
expect(wrapper.emitted('close')).toEqual([[]]);
});
describe('project selection', () => { describe('project selection', () => {
it('enables the "Save Changes" button if a new project is selected', async () => { it('enables the "Save" button only if a new project is selected', async () => {
createWrapper({ createWrapper({
mount: mountExtended, provide: { assignedPolicyProject: { id: 'gid://gitlab/Project/0', name: 'Test 0' } },
propsData: { assignedPolicyProject: { id: 'gid://gitlab/Project/0', name: 'Test 0' } },
}); });
const button = findSaveButton();
expect(button.attributes('disabled')).toBe('disabled'); expect(findModal().attributes('ok-disabled')).toBe('true');
findInstanceProjectSelector().vm.$emit('projectClicked', { findInstanceProjectSelector().vm.$emit('projectClicked', {
id: 'gid://gitlab/Project/1', id: 'gid://gitlab/Project/1',
name: 'Test 1', name: 'Test 1',
}); });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(button.attributes('disabled')).toBe(undefined);
expect(findModal().attributes('ok-disabled')).toBeUndefined();
}); });
it('displays an alert if the security policy project selection succeeds', async () => { it('emits an event with success message', async () => {
createWrapper({ mount: mountExtended }); await createWrapperAndSelectProject();
expect(findErrorAlert().exists()).toBe(false);
expect(findSuccessAlert().exists()).toBe(false); expect(projectUpdatedListener).toHaveBeenCalledWith({
await selectProject(); text: 'Security policy project was linked successfully',
expect(findErrorAlert().exists()).toBe(false); variant: 'success',
expect(findSuccessAlert().exists()).toBe(true); });
}); });
it('shows an alert if the security policy project selection fails', async () => { it('emits an event with an error message', async () => {
createWrapper({ await createWrapperAndSelectProject({
mount: mountExtended,
mutationResult: mockAssignSecurityPolicyProjectResponses.failure, mutationResult: mockAssignSecurityPolicyProjectResponses.failure,
}); });
expect(findErrorAlert().exists()).toBe(false);
expect(findSuccessAlert().exists()).toBe(false);
await selectProject();
expect(findErrorAlert().exists()).toBe(true);
expect(findSuccessAlert().exists()).toBe(false);
});
it('shows an alert if GraphQL fails', async () => { expect(projectUpdatedListener).toHaveBeenCalledWith({
createWrapper({ mount: mountExtended, mutationResult: apolloFailureResponse }); text: 'An error occurred assigning your security policy project',
expect(findErrorAlert().exists()).toBe(false); variant: 'danger',
expect(findSuccessAlert().exists()).toBe(false); });
await selectProject();
expect(findErrorAlert().exists()).toBe(true);
expect(findSuccessAlert().exists()).toBe(false);
}); });
}); });
...@@ -142,12 +145,11 @@ describe('SecurityPolicyProjectSelector Component', () => { ...@@ -142,12 +145,11 @@ describe('SecurityPolicyProjectSelector Component', () => {
}); });
it('disables the dropdown', () => { it('disables the dropdown', () => {
expect(findDropdown().attributes('disabled')).toBe('true'); expect(findDropdown().props('disabled')).toBe(true);
}); });
it('displays a tooltip', () => { it('displays a warning', () => {
const tooltip = getBinding(findTooltip().element, 'gl-tooltip'); expect(findAlert().text()).toBe('Only owners can update Security Policy Project');
expect(tooltip.value.disabled).toBe(false);
}); });
}); });
}); });
...@@ -21819,6 +21819,12 @@ msgstr "" ...@@ -21819,6 +21819,12 @@ msgstr ""
msgid "NetworkPolicies|Edit policy" msgid "NetworkPolicies|Edit policy"
msgstr "" msgstr ""
msgid "NetworkPolicies|Edit policy project"
msgstr ""
msgid "NetworkPolicies|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
msgstr ""
msgid "NetworkPolicies|Enforcement status" msgid "NetworkPolicies|Enforcement status"
msgstr "" msgstr ""
...@@ -21861,6 +21867,9 @@ msgstr "" ...@@ -21861,6 +21867,9 @@ msgstr ""
msgid "NetworkPolicies|Please %{installLinkStart}install%{installLinkEnd} and %{configureLinkStart}configure a Kubernetes Agent for this project%{configureLinkEnd} to enable alerts." msgid "NetworkPolicies|Please %{installLinkStart}install%{installLinkEnd} and %{configureLinkStart}configure a Kubernetes Agent for this project%{configureLinkEnd} to enable alerts."
msgstr "" msgstr ""
msgid "NetworkPolicies|Policies"
msgstr ""
msgid "NetworkPolicies|Policies are a specification of how groups of pods are allowed to communicate with each other's network endpoints." msgid "NetworkPolicies|Policies are a specification of how groups of pods are allowed to communicate with each other's network endpoints."
msgstr "" msgstr ""
...@@ -29313,22 +29322,19 @@ msgstr "" ...@@ -29313,22 +29322,19 @@ msgstr ""
msgid "SecurityConfiguration|You can quickly enable all security scanning tools by enabling %{linkStart}Auto DevOps%{linkEnd}." msgid "SecurityConfiguration|You can quickly enable all security scanning tools by enabling %{linkStart}Auto DevOps%{linkEnd}."
msgstr "" msgstr ""
msgid "SecurityOrchestration|A security policy project can enforce policies for a given project, group, or instance. With a security policy project, you can specify security policies that are important to you and enforce them with every commit. %{linkStart}More information.%{linkEnd}"
msgstr ""
msgid "SecurityOrchestration|An error occurred assigning your security policy project" msgid "SecurityOrchestration|An error occurred assigning your security policy project"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Create a policy" msgid "SecurityOrchestration|Only owners can update Security Policy Project"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Only owners can update Security Policy Project" msgid "SecurityOrchestration|Security policy project was linked successfully"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Security policy project" msgid "SecurityOrchestration|Select a project to store your security policies in. %{linkStart}More information.%{linkEnd}"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Security policy project was linked successfully" msgid "SecurityOrchestration|Select security project"
msgstr "" msgstr ""
msgid "SecurityPolicies|+%{count} more" msgid "SecurityPolicies|+%{count} more"
......
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