From 88607e9737569f31bce19a9eef3dba14b3f8466b Mon Sep 17 00:00:00 2001 From: Alexander Turinske <aturinske@gitlab.com> Date: Thu, 18 Apr 2024 00:48:09 +0000 Subject: [PATCH] Add file path component to pipeline policy - add file path component - add override selector for pipeline policy - conditionally show other message --- .../action/action_section.vue | 6 ++- .../pipeline_execution/constants.js | 1 - .../pipeline_execution/editor_component.vue | 10 +---- .../action/code_block_file_path.vue | 43 ++++++++++++++++++- .../action/code_block_override_selector.vue | 40 +++++++++++++++++ .../policy_editor/scan_execution/constants.js | 13 ++++++ .../scan_execution/editor_component.vue | 6 ++- .../scan_execution/lib/actions.js | 4 +- .../action/action_section_spec.js | 5 ++- .../editor_component_spec.js | 33 ++++++++------ .../action/code_block_file_path_spec.js | 39 ++++++++++++++++- .../code_block_override_selector_spec.js | 40 +++++++++++++++++ ....js => code_block_source_selector_spec.js} | 2 +- .../scan_execution/editor_component_spec.js | 31 +++++++++---- .../scan_execution/lib/actions_spec.js | 13 ++++++ locale/gitlab.pot | 12 ++++++ 16 files changed, 259 insertions(+), 39 deletions(-) create mode 100644 ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector.vue create mode 100644 ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector_spec.js rename ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/{action_type_selector_spec.js => code_block_source_selector_spec.js} (96%) diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/action/action_section.vue b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/action/action_section.vue index 3a2318f89ab04..e117c1035fca3 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/action/action_section.vue +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/action/action_section.vue @@ -1,13 +1,17 @@ <script> import { ACTIONS_LABEL } from '../../constants'; +import CodeBlockFilePath from '../../scan_execution/action/code_block_file_path.vue'; export default { i18n: { ACTIONS_LABEL, }, + components: { + CodeBlockFilePath, + }, }; </script> <template> - <div>{{ $options.i18n.ACTIONS_LABEL }}</div> + <code-block-file-path /> </template> diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/constants.js b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/constants.js index dbebcb5f43b34..5e9327e397890 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/constants.js +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/constants.js @@ -8,5 +8,4 @@ content: include: '' `; -export const ADD_CONDITION_LABEL = s__('ScanExecutionPolicy|Add condition'); export const CONDITIONS_LABEL = s__('ScanExecutionPolicy|Conditions'); diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/editor_component.vue b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/editor_component.vue index f25bf3f7877a2..d98784c2bb001 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/editor_component.vue +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/pipeline_execution/editor_component.vue @@ -11,8 +11,8 @@ import { } from '../constants'; import EditorLayout from '../editor_layout.vue'; import DimDisableContainer from '../dim_disable_container.vue'; -import RuleSection from './rule/rule_section.vue'; import ActionSection from './action/action_section.vue'; +import RuleSection from './rule/rule_section.vue'; import { createPolicyObject, policyToYaml } from './utils'; import { CONDITIONS_LABEL, DEFAULT_PIPELINE_EXECUTION_POLICY } from './constants'; @@ -143,13 +143,7 @@ export default { <div class="gl-bg-gray-10 gl-rounded-base gl-p-6"></div> </template> - <action-section - v-for="(action, index) in policy.actions" - :key="action.id" - :data-testid="`action-${index}`" - :action-index="index" - :init-action="action" - /> + <action-section class="gl-mb-4 security-policies-bg-gray-10 gl-rounded-base gl-p-5" /> </dim-disable-container> </template> </editor-layout> diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path.vue b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path.vue index 3e508b91251cb..b6fbd7a3a4382 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path.vue +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path.vue @@ -2,6 +2,7 @@ import { GlFormGroup, GlFormInputGroup, + GlIcon, GlInputGroupText, GlSprintf, GlFormInput, @@ -11,10 +12,12 @@ import { import { s__, __ } from '~/locale'; import { BV_SHOW_TOOLTIP, BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import RefSelector from '~/ref/components/ref_selector.vue'; -import CodeBlockSourceSelector from 'ee/security_orchestration/components/policy_editor/scan_execution/action/code_block_source_selector.vue'; import GroupProjectsDropdown from 'ee/security_orchestration/components/group_projects_dropdown.vue'; import { isGroup } from 'ee/security_orchestration/components/utils'; +import CodeBlockSourceSelector from './code_block_source_selector.vue'; +import CodeBlockOverrideSelector from './code_block_override_selector.vue'; export default { i18n: { @@ -22,6 +25,9 @@ export default { filePathCopy: s__( 'ScanExecutionPolicy|%{boldStart}Run%{boldEnd} %{typeSelector} from the project %{projectSelector} with ref %{refSelector}', ), + pipelineFilePathCopy: s__( + 'ScanExecutionPolicy|%{overrideSelector}into the %{boldStart}.gitlab-ci.yml%{boldEnd} with the following %{boldStart}pipeline execution file%{boldEnd} from %{projectSelector} And run with reference (Optional) %{refSelector}', + ), filePathPrependLabel: __('No project selected'), fileRefLabel: s__('ScanExecutionPolicy|Select ref'), filePathInputPlaceholder: s__('ScanExecutionPolicy|Link existing CI file'), @@ -30,6 +36,9 @@ export default { "ScanExecutionPolicy|The file at that project, ref, and path doesn't exist", ), formGroupLabel: s__('ScanExecutionPolicy|file path group'), + selectedProjectInformation: s__( + 'ScanExecutionPolicy|The content of this pipeline execution YAML file is included in the .gitlab-ci.yml file of the target project. All GitLab CI/CD features are supported.', + ), tooltipText: s__('ScanExecutionPolicy|Select project first, and then insert a file path'), }, refSelectorTranslations: { @@ -38,7 +47,9 @@ export default { SELECTED_PROJECT_TOOLTIP: 'selected-project-tooltip', name: 'CodeBlockFilePath', components: { + CodeBlockOverrideSelector, CodeBlockSourceSelector, + GlIcon, GlFormGroup, GlFormInputGroup, GlFormInput, @@ -49,8 +60,14 @@ export default { RefSelector, }, directives: { GlTooltip: GlTooltipDirective }, + mixins: [glFeatureFlagMixin()], inject: ['namespacePath', 'rootNamespacePath', 'namespaceType'], props: { + overrideType: { + type: String, + required: false, + default: undefined, + }, selectedType: { type: String, required: false, @@ -78,6 +95,14 @@ export default { }, }, computed: { + isPipelineExecution() { + return this.glFeatures.pipelineExecutionPolicyType; + }, + fileBlockMessage() { + return this.isPipelineExecution + ? this.$options.i18n.pipelineFilePathCopy + : this.$options.i18n.filePathCopy; + }, isValidFilePath() { if (this.filePath === null) { return null; @@ -123,6 +148,10 @@ export default { updatedFilePath(value) { this.$emit('update-file-path', value); }, + setOverride(override) { + this.$emit('select-override', override); + }, + setSelectedProject(project) { this.$emit('select-project', project); }, @@ -143,7 +172,11 @@ export default { <template> <div class="gl-display-flex gl-w-full gl-flex-direction-column gl-gap-3"> <div class="gl-display-flex gl-gap-3 gl-align-items-center gl-flex-wrap"> - <gl-sprintf :message="$options.i18n.filePathCopy"> + <gl-sprintf :message="fileBlockMessage"> + <template #overrideSelector> + <code-block-override-selector :override-type="overrideType" @select="setOverride" /> + </template> + <template #bold="{ content }"> <b>{{ content }}</b> </template> @@ -161,6 +194,12 @@ export default { :state="projectAndRefState" @select="setSelectedProject" /> + <gl-icon + v-if="isPipelineExecution" + v-gl-tooltip + name="question-o" + :title="$options.i18n.selectedProjectInformation" + /> </template> <template #refSelector> diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector.vue b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector.vue new file mode 100644 index 0000000000000..68330231e8d6d --- /dev/null +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector.vue @@ -0,0 +1,40 @@ +<script> +import { GlCollapsibleListbox } from '@gitlab/ui'; +import { + CUSTOM_OVERRIDE_OPTIONS, + CUSTOM_OVERRIDE_OPTIONS_LISTBOX_ITEMS, + INJECT, +} from 'ee/security_orchestration/components/policy_editor/scan_execution/constants'; +import { validateOverrideValues } from 'ee/security_orchestration/components/policy_editor/scan_execution//lib'; + +export default { + CUSTOM_OVERRIDE_OPTIONS_LISTBOX_ITEMS, + name: 'CodeBlockOverrideSelector', + components: { + GlCollapsibleListbox, + }, + props: { + overrideType: { + type: String, + required: false, + default: INJECT, + validator: validateOverrideValues, + }, + }, + computed: { + toggleText() { + return CUSTOM_OVERRIDE_OPTIONS[this.overrideType]; + }, + }, +}; +</script> + +<template> + <gl-collapsible-listbox + label-for="file-path" + :items="$options.CUSTOM_OVERRIDE_OPTIONS_LISTBOX_ITEMS" + :toggle-text="toggleText" + :selected="overrideType" + @select="$emit('select', $event)" + /> +</template> diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/constants.js b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/constants.js index 23514ee723e64..cec76f4646833 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/constants.js +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/constants.js @@ -93,3 +93,16 @@ export const CUSTOM_ACTION_OPTIONS_KEYS = Object.keys(CUSTOM_ACTION_OPTIONS); export const CUSTOM_ACTION_OPTIONS_LISTBOX_ITEMS = Object.entries( CUSTOM_ACTION_OPTIONS, ).map(([value, text]) => ({ value, text })); + +export const INJECT = 'inject'; +export const OVERRIDE = 'override'; + +export const CUSTOM_OVERRIDE_OPTIONS = { + [INJECT]: s__('ScanExecutionPolicy|Inject'), + [OVERRIDE]: s__('ScanExecutionPolicy|Override'), +}; + +export const CUSTOM_OVERRIDE_OPTIONS_KEYS = Object.keys(CUSTOM_OVERRIDE_OPTIONS); +export const CUSTOM_OVERRIDE_OPTIONS_LISTBOX_ITEMS = Object.entries( + CUSTOM_OVERRIDE_OPTIONS, +).map(([value, text]) => ({ value, text })); diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/editor_component.vue b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/editor_component.vue index a4336cd4e55d9..5537a8c847158 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/editor_component.vue +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/editor_component.vue @@ -126,7 +126,11 @@ export default { return this.existingPolicy?.name; }, showActionSection() { - return this.glFeatures.compliancePipelineInPolicies && this.customCiToggleEnabled; + return ( + this.glFeatures.compliancePipelineInPolicies && + this.customCiToggleEnabled && + !this.glFeatures.pipelineExecutionPolicyType + ); }, }, methods: { diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/lib/actions.js b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/lib/actions.js index 6aa89eea0632a..346d53a63cd02 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/lib/actions.js +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scan_execution/lib/actions.js @@ -1,6 +1,6 @@ import { uniqueId } from 'lodash'; import { REPORT_TYPE_DAST } from '~/vue_shared/security_reports/constants'; -import { CUSTOM_ACTION_KEY } from '../constants'; +import { CUSTOM_ACTION_KEY, CUSTOM_OVERRIDE_OPTIONS_KEYS } from '../constants'; export const buildScannerAction = ({ scanner, siteProfile = '', scannerProfile = '', id }) => { const action = { scan: scanner, id: id ?? uniqueId('action_') }; @@ -16,3 +16,5 @@ export const buildScannerAction = ({ scanner, siteProfile = '', scannerProfile = export const buildCustomCodeAction = (id) => { return { scan: CUSTOM_ACTION_KEY, id: id || uniqueId('action_') }; }; + +export const validateOverrideValues = (value) => CUSTOM_OVERRIDE_OPTIONS_KEYS.includes(value); diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/action/action_section_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/action/action_section_spec.js index 919c81255863d..2ab7115d6a8a0 100644 --- a/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/action/action_section_spec.js +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/action/action_section_spec.js @@ -1,5 +1,6 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import ActionSection from 'ee/security_orchestration/components/policy_editor/pipeline_execution/action/action_section.vue'; +import CodeBlockFilePath from 'ee/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path.vue'; describe('ActionSection', () => { let wrapper; @@ -15,8 +16,10 @@ describe('ActionSection', () => { }); }; + const findCodeBlockFilePath = () => wrapper.findComponent(CodeBlockFilePath); + it('renders', () => { factory(); - expect(wrapper.find('div').exists()).toBe(true); + expect(findCodeBlockFilePath().exists()).toBe(true); }); }); diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/editor_component_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/editor_component_spec.js index 1f649b3f31c94..239b142f2b8ea 100644 --- a/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/editor_component_spec.js +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/pipeline_execution/editor_component_spec.js @@ -2,9 +2,11 @@ import { GlEmptyState } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { DEFAULT_ASSIGNED_POLICY_PROJECT } from 'ee/security_orchestration/constants'; import EditorComponent from 'ee/security_orchestration/components/policy_editor/pipeline_execution/editor_component.vue'; +import ActionSection from 'ee/security_orchestration/components/policy_editor/pipeline_execution/action/action_section.vue'; import RuleSection from 'ee/security_orchestration/components/policy_editor/pipeline_execution/rule/rule_section.vue'; import EditorLayout from 'ee/security_orchestration/components/policy_editor/editor_layout.vue'; import { DEFAULT_PIPELINE_EXECUTION_POLICY } from 'ee/security_orchestration/components/policy_editor/pipeline_execution/constants'; + import { configFileManifest, configFileObject } from './mock_data'; describe('EditorComponent', () => { @@ -29,17 +31,16 @@ describe('EditorComponent', () => { const findEmptyState = () => wrapper.findComponent(GlEmptyState); const findPolicyEditorLayout = () => wrapper.findComponent(EditorLayout); + const findActionSection = () => wrapper.findComponent(ActionSection); const findRuleSection = () => wrapper.findComponent(RuleSection); describe('rule mode', () => { it('renders the editor', () => { factory(); - expect(findEmptyState().exists()).toBe(false); - }); - - it('renders the rule section', () => { - factory(); + expect(findPolicyEditorLayout().exists()).toBe(true); + expect(findActionSection().exists()).toBe(true); expect(findRuleSection().exists()).toBe(true); + expect(findEmptyState().exists()).toBe(false); }); it('renders the default policy editor layout', () => { @@ -53,7 +54,7 @@ describe('EditorComponent', () => { ); }); - it('updates the policy', async () => { + it('updates the general policy properties', async () => { const name = 'New name'; factory(); expect(findPolicyEditorLayout().props('policy').name).toBe(''); @@ -73,14 +74,18 @@ describe('EditorComponent', () => { }); }); - it('renders the empty page', () => { - factory({ provide: { disableScanPolicyUpdate: true } }); - expect(findPolicyEditorLayout().exists()).toBe(false); + describe('empty page', () => { + it('renders', () => { + factory({ provide: { disableScanPolicyUpdate: true } }); + expect(findPolicyEditorLayout().exists()).toBe(false); + expect(findActionSection().exists()).toBe(false); + expect(findRuleSection().exists()).toBe(false); - const emptyState = findEmptyState(); - expect(emptyState.exists()).toBe(true); - expect(emptyState.props('primaryButtonLink')).toMatch(scanPolicyDocumentationPath); - expect(emptyState.props('primaryButtonLink')).toMatch('pipeline-execution-policy-editor'); - expect(emptyState.props('svgPath')).toBe(policyEditorEmptyStateSvgPath); + const emptyState = findEmptyState(); + expect(emptyState.exists()).toBe(true); + expect(emptyState.props('primaryButtonLink')).toMatch(scanPolicyDocumentationPath); + expect(emptyState.props('primaryButtonLink')).toMatch('pipeline-execution-policy-editor'); + expect(emptyState.props('svgPath')).toBe(policyEditorEmptyStateSvgPath); + }); }); }); diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path_spec.js index b227654b88153..48b10707f3889 100644 --- a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path_spec.js +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path_spec.js @@ -1,5 +1,6 @@ import { GlFormInput, GlSprintf, GlFormGroup, GlFormInputGroup, GlTruncate } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import CodeBlockOverrideSelector from 'ee/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector.vue'; import CodeBlockSourceSelector from 'ee/security_orchestration/components/policy_editor/scan_execution/action/code_block_source_selector.vue'; import CodeBlockFilePath from 'ee/security_orchestration/components/policy_editor/scan_execution/action/code_block_file_path.vue'; import GroupProjectsDropdown from 'ee/security_orchestration/components/group_projects_dropdown.vue'; @@ -12,7 +13,7 @@ describe('CodeBlockFilePath', () => { const PROJECT_ID = 'gid://gitlab/Project/29'; - const createComponent = ({ propsData = {}, provide = {} } = {}) => { + const createComponent = ({ propsData = {}, provide = {}, stubs = {} } = {}) => { wrapper = shallowMount(CodeBlockFilePath, { propsData: { selectedType: INSERTED_CODE_BLOCK, @@ -20,6 +21,7 @@ describe('CodeBlockFilePath', () => { }, stubs: { GlSprintf, + ...stubs, }, provide: { namespacePath: 'gitlab-org', @@ -34,7 +36,9 @@ describe('CodeBlockFilePath', () => { const findFormInput = () => wrapper.findComponent(GlFormInput); const findFormInputGroup = () => wrapper.findComponent(GlFormInputGroup); const findFormGroup = () => wrapper.findComponent(GlFormGroup); + const findGlSprintf = () => wrapper.findComponent(GlSprintf); const findGroupProjectsDropdown = () => wrapper.findComponent(GroupProjectsDropdown); + const findOverrideSelector = () => wrapper.findComponent(CodeBlockOverrideSelector); const findRefSelector = () => wrapper.findComponent(RefSelector); const findTruncate = () => wrapper.findComponent(GlTruncate); @@ -65,6 +69,25 @@ describe('CodeBlockFilePath', () => { }); }); + describe('pipeline execution policy', () => { + it('renders message for scan execution policy', () => { + createComponent({ stubs: { GlSprintf: false } }); + expect(findGlSprintf().attributes('message')).toBe( + '%{boldStart}Run%{boldEnd} %{typeSelector} from the project %{projectSelector} with ref %{refSelector}', + ); + }); + + it('renders message for pipeline execution policy', () => { + createComponent({ + provide: { glFeatures: { pipelineExecutionPolicyType: true } }, + stubs: { GlSprintf: false }, + }); + expect(findGlSprintf().attributes('message')).toBe( + '%{overrideSelector}into the %{boldStart}.gitlab-ci.yml%{boldEnd} with the following %{boldStart}pipeline execution file%{boldEnd} from %{projectSelector} And run with reference (Optional) %{refSelector}', + ); + }); + }); + describe('selected state', () => { it('render selected ref input', () => { createComponent({ @@ -128,6 +151,14 @@ describe('CodeBlockFilePath', () => { expect(findFormInput().exists()).toBe(true); expect(findGroupProjectsDropdown().props('selected')).toEqual([]); }); + + it('renders selected override', () => { + createComponent({ + propsData: { overrideType: 'override' }, + provide: { glFeatures: { pipelineExecutionPolicyType: true } }, + }); + expect(findOverrideSelector().props('overrideType')).toBe('override'); + }); }); describe('actions', () => { @@ -168,6 +199,12 @@ describe('CodeBlockFilePath', () => { expect(wrapper.emitted('update-file-path')).toEqual([['file-path']]); }); + + it('can select override for pipeline execution policy', () => { + createComponent({ provide: { glFeatures: { pipelineExecutionPolicyType: true } } }); + findOverrideSelector().vm.$emit('select', 'override'); + expect(wrapper.emitted('select-override')).toEqual([['override']]); + }); }); describe('group projects dropdown', () => { diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector_spec.js new file mode 100644 index 0000000000000..60f8bb05e95c4 --- /dev/null +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector_spec.js @@ -0,0 +1,40 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlCollapsibleListbox } from '@gitlab/ui'; +import CodeBlockOverrideSelector from 'ee/security_orchestration/components/policy_editor/scan_execution/action/code_block_override_selector.vue'; +import { + INJECT, + OVERRIDE, + CUSTOM_OVERRIDE_OPTIONS, +} from 'ee/security_orchestration/components/policy_editor/scan_execution/constants'; + +describe('CodeBlockOverrideSelector', () => { + let wrapper; + + const createComponent = ({ propsData = {} } = {}) => { + wrapper = shallowMount(CodeBlockOverrideSelector, { + propsData, + }); + }; + + const findListBox = () => wrapper.findComponent(GlCollapsibleListbox); + + it('selects action type', () => { + createComponent(); + expect(findListBox().props('selected')).toBe('inject'); + findListBox().vm.$emit('select', INJECT); + expect(wrapper.emitted('select')).toEqual([[INJECT]]); + findListBox().vm.$emit('select', OVERRIDE); + expect(wrapper.emitted('select')[1]).toEqual([OVERRIDE]); + }); + + it.each([INJECT, OVERRIDE])('renders override type', (overrideType) => { + createComponent({ + propsData: { + overrideType, + }, + }); + + expect(findListBox().props('selected')).toBe(overrideType); + expect(findListBox().props('toggleText')).toBe(CUSTOM_OVERRIDE_OPTIONS[overrideType]); + }); +}); diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/action_type_selector_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_source_selector_spec.js similarity index 96% rename from ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/action_type_selector_spec.js rename to ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_source_selector_spec.js index 69e0fc3e57227..604b4fdd1ddd5 100644 --- a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/action_type_selector_spec.js +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/action/code_block_source_selector_spec.js @@ -7,7 +7,7 @@ import { CUSTOM_ACTION_OPTIONS, } from 'ee/security_orchestration/components/policy_editor/scan_execution/constants'; -describe('ActionTypeSelector', () => { +describe('CodeBlockSourceSelector', () => { let wrapper; const createComponent = ({ propsData = {} } = {}) => { diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/editor_component_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/editor_component_spec.js index 2919785e81e81..3c45ea3922532 100644 --- a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/editor_component_spec.js +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/editor_component_spec.js @@ -396,19 +396,34 @@ enabled: true`; }); describe('execute yaml block section', () => { - it.each([true, false])( - 'should render action builder when feature flag is enabled', - (flagEnabled) => { + it.each` + compliancePipelineInPolicies | customCiToggleEnabled | pipelineExecutionPolicyType | output + ${true} | ${true} | ${true} | ${false} + ${true} | ${true} | ${false} | ${true} + ${true} | ${false} | ${true} | ${false} + ${true} | ${false} | ${false} | ${false} + ${false} | ${true} | ${true} | ${false} + ${false} | ${true} | ${false} | ${false} + ${false} | ${false} | ${true} | ${false} + ${false} | ${false} | ${false} | ${false} + `( + 'should render the correct action builder when compliancePipelineInPolicies is $compliancePipelineInPolicies, customCiToggleEnabled is $customCiToggleEnabled, and pipelineExecutionPolicyType is $pipelineExecutionPolicyType', + ({ + compliancePipelineInPolicies, + customCiToggleEnabled, + pipelineExecutionPolicyType, + output, + }) => { factory({ provide: { - glFeatures: { compliancePipelineInPolicies: flagEnabled }, - customCiToggleEnabled: flagEnabled, + glFeatures: { compliancePipelineInPolicies, pipelineExecutionPolicyType }, + customCiToggleEnabled, }, }); - expect(findActionSection().exists()).toBe(flagEnabled); - expect(findScanFilterSelector().exists()).toBe(flagEnabled); - expect(findAddActionButton().exists()).toBe(!flagEnabled); + expect(findActionSection().exists()).toBe(output); + expect(findScanFilterSelector().exists()).toBe(output); + expect(findAddActionButton().exists()).toBe(!output); }, ); diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/lib/actions_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/lib/actions_spec.js index c69dc270a6eaa..6eff6e046d5ad 100644 --- a/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/lib/actions_spec.js +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/scan_execution/lib/actions_spec.js @@ -1,6 +1,8 @@ +import { CUSTOM_OVERRIDE_OPTIONS_KEYS } from 'ee/security_orchestration/components/policy_editor/scan_execution/constants'; import { buildCustomCodeAction, buildScannerAction, + validateOverrideValues, } from 'ee/security_orchestration/components/policy_editor/scan_execution/lib/actions'; import { REPORT_TYPE_DAST } from '~/vue_shared/security_reports/constants'; @@ -49,3 +51,14 @@ describe('buildScannerAction', () => { }); }); }); + +describe('validateOverrideValues', () => { + it.each` + input | expected + ${CUSTOM_OVERRIDE_OPTIONS_KEYS[0]} | ${true} + ${CUSTOM_OVERRIDE_OPTIONS_KEYS[1]} | ${true} + ${'other string'} | ${false} + `('validates correctly for $input', ({ input, expected }) => { + expect(validateOverrideValues(input)).toBe(expected); + }); +}); diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e1a369f76e765..02acb1ed495a5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -44932,6 +44932,9 @@ msgstr "" msgid "ScanExecutionPolicy|%{labelStart}File path:%{labelEnd} %{filePath}" msgstr "" +msgid "ScanExecutionPolicy|%{overrideSelector}into the %{boldStart}.gitlab-ci.yml%{boldEnd} with the following %{boldStart}pipeline execution file%{boldEnd} from %{projectSelector} And run with reference (Optional) %{refSelector}" +msgstr "" + msgid "ScanExecutionPolicy|%{period} %{days} at %{time} %{timezoneLabel} %{timezone}" msgstr "" @@ -44977,6 +44980,9 @@ msgstr "" msgid "ScanExecutionPolicy|If there are any conflicting variables with the local pipeline configuration (Ex, gitlab-ci.yml) then variables defined here will take precedence. %{linkStart}Learn more%{linkEnd}." msgstr "" +msgid "ScanExecutionPolicy|Inject" +msgstr "" + msgid "ScanExecutionPolicy|Inserted CI code block" msgstr "" @@ -44998,6 +45004,9 @@ msgstr "" msgid "ScanExecutionPolicy|Only one variable can be added at a time." msgstr "" +msgid "ScanExecutionPolicy|Override" +msgstr "" + msgid "ScanExecutionPolicy|Run CI/CD code" msgstr "" @@ -45046,6 +45055,9 @@ msgstr "" msgid "ScanExecutionPolicy|Select timezone" msgstr "" +msgid "ScanExecutionPolicy|The content of this pipeline execution YAML file is included in the .gitlab-ci.yml file of the target project. All GitLab CI/CD features are supported." +msgstr "" + msgid "ScanExecutionPolicy|The file at that project, ref, and path doesn't exist" msgstr "" -- GitLab