Commit b501c17b authored by Alexander Turinske's avatar Alexander Turinske Committed by Simon Knox

Update scan execution empty state view

- previously if a user could not create a scan execution
  policy, they couldn't click the save button
- now it shows the user an empty state
- update documentation paths to be more specific
- update tests
parent 8abd656e
......@@ -66,7 +66,12 @@ export default {
PolicyEditorLayout,
DimDisableContainer,
},
inject: ['networkDocumentationPath', 'noEnvironmentSvgPath', 'projectId', 'policiesPath'],
inject: [
'networkDocumentationPath',
'policyEditorEmptyStateSvgPath',
'projectId',
'policiesPath',
],
props: {
existingPolicy: {
type: Object,
......@@ -303,7 +308,7 @@ export default {
:description="$options.i18n.noEnvironmentDescription"
:primary-button-link="networkDocumentationPath"
:primary-button-text="$options.i18n.noEnvironmentButton"
:svg-path="noEnvironmentSvgPath"
:svg-path="policyEditorEmptyStateSvgPath"
title=""
/>
</template>
<script>
import { GlEmptyState } from '@gitlab/ui';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
import { EDITOR_MODES, EDITOR_MODE_YAML } from '../constants';
import PolicyEditorLayout from '../policy_editor_layout.vue';
import {
......@@ -18,11 +19,22 @@ export default {
EDITOR_MODES: [EDITOR_MODES[1]],
i18n: {
createMergeRequest: __('Create merge request'),
notOwnerButtonText: __('Learn more'),
notOwnerDescription: s__(
'SecurityOrchestration|Scan execution policies can only be created by project owners.',
),
},
components: {
GlEmptyState,
PolicyEditorLayout,
},
inject: ['disableScanExecutionUpdate', 'projectId', 'projectPath'],
inject: [
'disableScanExecutionUpdate',
'policyEditorEmptyStateSvgPath',
'projectId',
'projectPath',
'scanExecutionDocumentationPath',
],
props: {
assignedPolicyProject: {
type: Object,
......@@ -110,9 +122,9 @@ export default {
<template>
<policy-editor-layout
v-if="!disableScanExecutionUpdate"
:custom-save-button-text="$options.i18n.createMergeRequest"
:default-editor-mode="$options.DEFAULT_EDITOR_MODE"
:disable-update="disableScanExecutionUpdate"
:editor-modes="$options.EDITOR_MODES"
:is-editing="isEditing"
:is-removing-policy="isRemovingPolicy"
......@@ -123,4 +135,12 @@ export default {
@save-policy="handleModifyPolicy()"
@update-yaml="updateYaml"
/>
<gl-empty-state
v-else
:description="$options.i18n.notOwnerDescription"
:primary-button-link="scanExecutionDocumentationPath"
:primary-button-text="$options.i18n.notOwnerButtonText"
:svg-path="policyEditorEmptyStateSvgPath"
title=""
/>
</template>
......@@ -21,15 +21,16 @@ export default () => {
environmentsEndpoint,
configureAgentHelpPath,
createAgentHelpPath,
networkDocumentationPath,
networkPoliciesEndpoint,
noEnvironmentSvgPath,
networkDocumentationPath,
policiesPath,
policy,
policyEditorEmptyStateSvgPath,
policyType,
projectPath,
projectId,
environmentId,
scanExecutionDocumentationPath,
} = el.dataset;
// We require the project to have at least one available environment.
......@@ -66,12 +67,13 @@ export default () => {
configureAgentHelpPath,
createAgentHelpPath,
disableScanExecutionUpdate: parseBoolean(disableScanExecutionUpdate),
policyType,
networkDocumentationPath,
noEnvironmentSvgPath,
policyEditorEmptyStateSvgPath,
policyType,
projectId,
projectPath,
policiesPath,
scanExecutionDocumentationPath,
},
store,
render(createElement) {
......
......@@ -29,13 +29,14 @@ module Projects::Security::PoliciesHelper
create_agent_help_path: help_page_url('user/clusters/agent/index.md', anchor: 'create-an-agent-record-in-gitlab'),
environments_endpoint: project_environments_path(project),
environment_id: environment&.id,
network_documentation_path: help_page_path('user/application_security/policies/index'),
no_environment_svg_path: image_path('illustrations/monitoring/unable_to_connect.svg'),
network_documentation_path: help_page_path('user/application_security/policies/index', anchor: 'container-network-policy'),
policy: policy&.to_json,
policy_editor_empty_state_svg_path: image_path('illustrations/monitoring/unable_to_connect.svg'),
policy_type: policy_type,
project_path: project.full_path,
project_id: project.id,
policies_path: project_security_policies_path(project)
policies_path: project_security_policies_path(project),
scan_execution_documentation_path: help_page_path('user/application_security/policies/index', anchor: 'scan-execution-policy-editor')
}
end
end
......@@ -52,7 +52,7 @@ describe('NetworkPolicyEditor component', () => {
},
provide: {
networkDocumentationPath: 'path/to/docs',
noEnvironmentSvgPath: 'path/to/svg',
policyEditorEmptyStateSvgPath: 'path/to/svg',
policiesPath: '/threat-monitoring',
projectId: '21',
...provide,
......
import { shallowMount } from '@vue/test-utils';
import { GlEmptyState } from '@gitlab/ui';
import PolicyEditorLayout from 'ee/threat_monitoring/components/policy_editor/policy_editor_layout.vue';
import {
DEFAULT_SCAN_EXECUTION_POLICY,
......@@ -40,8 +41,10 @@ jest.mock('ee/threat_monitoring/components/policy_editor/scan_execution_policy/l
describe('ScanExecutionPolicyEditor', () => {
let wrapper;
const defaultProjectPath = 'path/to/project';
const policyEditorEmptyStateSvgPath = 'path/to/svg';
const scanExecutionDocumentationPath = 'path/to/docs';
const factory = ({ propsData = {} } = {}) => {
const factory = ({ propsData = {}, provide = {} } = {}) => {
wrapper = shallowMount(ScanExecutionPolicyEditor, {
propsData: {
assignedPolicyProject: DEFAULT_ASSIGNED_POLICY_PROJECT,
......@@ -49,8 +52,11 @@ describe('ScanExecutionPolicyEditor', () => {
},
provide: {
disableScanExecutionUpdate: false,
policyEditorEmptyStateSvgPath,
projectId: 1,
projectPath: defaultProjectPath,
scanExecutionDocumentationPath,
...provide,
},
});
};
......@@ -59,45 +65,59 @@ describe('ScanExecutionPolicyEditor', () => {
return factory({ propsData: { existingPolicy: mockDastScanExecutionObject, isEditing: true } });
};
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findPolicyEditorLayout = () => wrapper.findComponent(PolicyEditorLayout);
afterEach(() => {
wrapper.destroy();
});
it('updates the policy yaml when "update-yaml" is emitted', async () => {
factory();
await wrapper.vm.$nextTick();
const newManifest = 'new yaml!';
expect(findPolicyEditorLayout().attributes('yamleditorvalue')).toBe(
DEFAULT_SCAN_EXECUTION_POLICY,
describe('default', () => {
it('updates the policy yaml when "update-yaml" is emitted', async () => {
factory();
await wrapper.vm.$nextTick();
const newManifest = 'new yaml!';
expect(findPolicyEditorLayout().attributes('yamleditorvalue')).toBe(
DEFAULT_SCAN_EXECUTION_POLICY,
);
await findPolicyEditorLayout().vm.$emit('update-yaml', newManifest);
expect(findPolicyEditorLayout().attributes('yamleditorvalue')).toBe(newManifest);
});
it.each`
status | action | event | factoryFn | yamlEditorValue
${'to save a new policy'} | ${SECURITY_POLICY_ACTIONS.APPEND} | ${'save-policy'} | ${factory} | ${DEFAULT_SCAN_EXECUTION_POLICY}
${'to update an existing policy'} | ${SECURITY_POLICY_ACTIONS.REPLACE} | ${'save-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest}
${'to delete an existing policy'} | ${SECURITY_POLICY_ACTIONS.REMOVE} | ${'remove-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest}
`(
'navigates to the new merge request when "modifyPolicy" is emitted $status',
async ({ action, event, factoryFn, yamlEditorValue }) => {
factoryFn();
await wrapper.vm.$nextTick();
findPolicyEditorLayout().vm.$emit(event);
await wrapper.vm.$nextTick();
expect(modifyPolicy).toHaveBeenCalledTimes(1);
expect(modifyPolicy).toHaveBeenCalledWith({
action,
assignedPolicyProject: DEFAULT_ASSIGNED_POLICY_PROJECT,
projectPath: defaultProjectPath,
yamlEditorValue,
});
await wrapper.vm.$nextTick();
expect(visitUrl).toHaveBeenCalled();
expect(visitUrl).toHaveBeenCalledWith('/tests/-/merge_requests/2');
},
);
await findPolicyEditorLayout().vm.$emit('update-yaml', newManifest);
expect(findPolicyEditorLayout().attributes('yamleditorvalue')).toBe(newManifest);
});
it.each`
status | action | event | factoryFn | yamlEditorValue
${'to save a new policy'} | ${SECURITY_POLICY_ACTIONS.APPEND} | ${'save-policy'} | ${factory} | ${DEFAULT_SCAN_EXECUTION_POLICY}
${'to update an existing policy'} | ${SECURITY_POLICY_ACTIONS.REPLACE} | ${'save-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest}
${'to delete an existing policy'} | ${SECURITY_POLICY_ACTIONS.REMOVE} | ${'remove-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest}
`(
'navigates to the new merge request when "modifyPolicy" is emitted $status',
async ({ action, event, factoryFn, yamlEditorValue }) => {
factoryFn();
await wrapper.vm.$nextTick();
findPolicyEditorLayout().vm.$emit(event);
describe('when a user is not an owner of the project', () => {
it('displays the empty state with the appropriate properties', async () => {
factory({ provide: { disableScanExecutionUpdate: true } });
await wrapper.vm.$nextTick();
expect(modifyPolicy).toHaveBeenCalledTimes(1);
expect(modifyPolicy).toHaveBeenCalledWith({
action,
assignedPolicyProject: DEFAULT_ASSIGNED_POLICY_PROJECT,
projectPath: defaultProjectPath,
yamlEditorValue,
expect(findEmptyState().props()).toMatchObject({
primaryButtonLink: scanExecutionDocumentationPath,
svgPath: policyEditorEmptyStateSvgPath,
});
await wrapper.vm.$nextTick();
expect(visitUrl).toHaveBeenCalled();
expect(visitUrl).toHaveBeenCalledWith('/tests/-/merge_requests/2');
},
);
});
});
});
......@@ -46,13 +46,14 @@ RSpec.describe Projects::Security::PoliciesHelper do
create_agent_help_path: kind_of(String),
environments_endpoint: kind_of(String),
network_documentation_path: kind_of(String),
no_environment_svg_path: kind_of(String),
policy_editor_empty_state_svg_path: kind_of(String),
project_path: project.full_path,
project_id: project.id,
policies_path: kind_of(String),
environment_id: environment&.id,
policy: policy&.to_json,
policy_type: policy_type
policy_type: policy_type,
scan_execution_documentation_path: kind_of(String)
}
end
......
......@@ -30397,6 +30397,9 @@ msgstr ""
msgid "SecurityOrchestration|Scan execution"
msgstr ""
msgid "SecurityOrchestration|Scan execution policies can only be created by project owners."
msgstr ""
msgid "SecurityOrchestration|Scan to be performed every %{cadence} on the %{branches}"
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