Commit 0b5a0701 authored by Alexander Turinske's avatar Alexander Turinske

Move policy action string logic to Vue template

- the concerns of a string having a <strong> tag
  should be in the Vue component where the display
  logic is instead of in a util class
- update tests
parent caa48d1a
<script>
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import {
fromYaml,
......@@ -12,16 +12,17 @@ import PolicyInfoRow from './policy_info_row.vue';
export default {
i18n: {
summary: SUMMARY_TITLE,
multipleActionMessage: s__('SecurityOrchestration|Runs %{actions} and %{lastAction} scans'),
noActionMessage: s__('SecurityOrchestration|No actions defined - policy will not run.'),
singleActionMessage: s__(`SecurityOrchestration|Runs a %{action} scan`),
scanExecution: s__('SecurityOrchestration|Scan execution'),
summary: SUMMARY_TITLE,
},
components: {
GlSprintf,
PolicyDrawerLayout,
PolicyInfoRow,
},
directives: {
SafeHtml,
},
props: {
policy: {
type: Object,
......@@ -55,7 +56,25 @@ export default {
>
<template v-if="parsedYaml" #summary>
<policy-info-row data-testid="policy-summary" :label="$options.i18n.summary">
<p v-safe-html="humanizedActions"></p>
<p>
<template v-if="!humanizedActions.length">{{ $options.i18n.noActionMessage }}</template>
<gl-sprintf
v-else-if="humanizedActions.length === 1"
:message="$options.i18n.singleActionMessage"
>
<template #action>
<strong>{{ humanizedActions[0] }}</strong>
</template>
</gl-sprintf>
<gl-sprintf v-else :message="$options.i18n.multipleActionMessage">
<template #actions>
<strong>{{ humanizedActions.slice(0, -1).join(', ') }}</strong>
</template>
<template #lastAction>
<strong>{{ humanizedActions[humanizedActions.length - 1] }}</strong>
</template>
</gl-sprintf>
</p>
<ul>
<li v-for="(rule, idx) in humanizedRules" :key="idx">
{{ rule }}
......
......@@ -35,8 +35,4 @@ export const GRAPHQL_ERROR_MESSAGE = s__(
'SecurityOrchestration|There was a problem creating the new security policy',
);
export const NO_ACTION_MESSAGE = s__(
'SecurityOrchestration|No actions defined - policy will not run.',
);
export const NO_RULE_MESSAGE = s__('SecurityOrchestration|No rules defined - policy will not run.');
import cronstrue from 'cronstrue/i18n';
import { convertToTitleCase, humanize } from '~/lib/utils/text_utility';
import { getPreferredLocales, sprintf, s__, n__ } from '~/locale';
import { NO_ACTION_MESSAGE, NO_RULE_MESSAGE } from '../../constants';
import { NO_RULE_MESSAGE } from '../../constants';
/**
* Create a human-readable list of strings, adding the necessary punctuation and conjunctions
......@@ -62,29 +62,10 @@ const HUMANIZE_RULES_METHODS = {
/**
* Create a human-readable version of the actions
* @param {Array} actions [{"scan":"dast","scanner_profile":"Scanner Profile","site_profile":"Site Profile"},{"type":"secret_detection"}]
* @returns {String}
* @returns {Array}
*/
export const humanizeActions = (originalActions) => {
const actions = [...new Set(originalActions.map((a) => convertToTitleCase(humanize(a.scan))))];
if (!actions.length) {
return NO_ACTION_MESSAGE;
} else if (actions.length === 1) {
return sprintf(s__('SecurityOrchestration|Runs a <strong>%{actions}</strong> scan'), {
actions: actions.join(','),
});
}
const lastAction = actions.pop();
return sprintf(
s__(
'SecurityOrchestration|Runs <strong>%{actions}</strong> and <strong>%{lastAction}</strong> scans',
),
{
actions: actions.join(', '),
lastAction,
},
);
export const humanizeActions = (actions) => {
return [...new Set(actions.map((a) => convertToTitleCase(humanize(a.scan))))];
};
/**
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ScanExecutionPolicy component default policy renders the correct policy action message 1`] = `
<div
data-testid="policy-summary"
>
<policy-info-row-stub
data-testid="policy-summary"
label="Summary"
>
<p>
<gl-sprintf-stub
message="Runs a %{action} scan"
/>
</p>
<ul>
<li>
Scan to be performed on every pipeline on the main branch
</li>
</ul>
</policy-info-row-stub>
</div>
`;
exports[`ScanExecutionPolicy component multiple action policy renders the correct policy action message 1`] = `
<div
data-testid="policy-summary"
>
<policy-info-row-stub
data-testid="policy-summary"
label="Summary"
>
<p>
<gl-sprintf-stub
message="Runs %{actions} and %{lastAction} scans"
/>
</p>
<ul>
<li>
Scan to be performed on every pipeline on the main branch
</li>
</ul>
</policy-info-row-stub>
</div>
`;
exports[`ScanExecutionPolicy component no action policy renders the correct policy action message 1`] = `
<div
data-testid="policy-summary"
>
<policy-info-row-stub
data-testid="policy-summary"
label="Summary"
>
<p>
No actions defined - policy will not run.
</p>
<ul>
<li>
Scan to be performed on every pipeline on the main branch
</li>
</ul>
</policy-info-row-stub>
</div>
`;
import PolicyDrawerLayout from 'ee/threat_monitoring/components/policy_drawer/policy_drawer_layout.vue';
import ScanExecutionPolicy from 'ee/threat_monitoring/components/policy_drawer/scan_execution_policy.vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { mockScanExecutionPolicy } from '../../mocks/mock_data';
import {
mockScanExecutionPolicy,
mockScanExecutionManifestNoActions,
mockScanExecutionManifestMultipleActions,
} from '../../mocks/mock_data';
describe('ScanExecutionPolicy component', () => {
let wrapper;
const findActions = () => wrapper.findByTestId('policy-actions');
const findRules = () => wrapper.findByTestId('policy-rules');
const findSummary = () => wrapper.find('[data-testid="policy-summary"]');
const factory = ({ propsData } = {}) => {
wrapper = shallowMountExtended(ScanExecutionPolicy, {
......@@ -22,21 +25,18 @@ describe('ScanExecutionPolicy component', () => {
wrapper.destroy();
});
describe('default policy', () => {
describe.each`
title | propsData
${'default policy'} | ${{ policy: mockScanExecutionPolicy }}
${'no action policy'} | ${{ policy: { ...mockScanExecutionPolicy, yaml: mockScanExecutionManifestNoActions } }}
${'multiple action policy'} | ${{ policy: { ...mockScanExecutionPolicy, yaml: mockScanExecutionManifestMultipleActions } }}
`('$title', ({ propsData }) => {
beforeEach(() => {
factory({ propsData: { policy: mockScanExecutionPolicy } });
factory({ propsData });
});
it.each`
component | finder | text
${'actions'} | ${findActions} | ${''}
${'rules'} | ${findRules} | ${''}
`('does render the policy $component', ({ finder, text }) => {
const component = finder();
expect(component.exists()).toBe(true);
if (text) {
expect(component.text()).toBe(text);
}
it('renders the correct policy action message', () => {
expect(findSummary().element).toMatchSnapshot();
});
});
});
......@@ -3,10 +3,7 @@ import {
humanizeRules,
} from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib';
import {
NO_ACTION_MESSAGE,
NO_RULE_MESSAGE,
} from 'ee/threat_monitoring/components/policy_editor/constants';
import { NO_RULE_MESSAGE } from 'ee/threat_monitoring/components/policy_editor/constants';
jest.mock('~/locale', () => ({
getPreferredLocales: jest.fn().mockReturnValue(['en']),
......@@ -37,17 +34,19 @@ const mockRules = [
describe('humanizeActions', () => {
it('returns an empty Array of actions as an empty Set', () => {
expect(humanizeActions([])).toStrictEqual(NO_ACTION_MESSAGE);
expect(humanizeActions([])).toStrictEqual([]);
});
it('returns a single action as human-readable string', () => {
expect(humanizeActions([mockActions[0]])).toStrictEqual('Runs a <strong>Dast</strong> scan');
expect(humanizeActions([mockActions[0]])).toStrictEqual(['Dast']);
});
it('returns multiple actions as human-readable strings', () => {
expect(humanizeActions(mockActions)).toStrictEqual(
'Runs <strong>Dast, Secret Detection</strong> and <strong>Container Image Scanning</strong> scans',
);
expect(humanizeActions(mockActions)).toStrictEqual([
'Dast',
'Secret Detection',
'Container Image Scanning',
]);
});
});
......
......@@ -49,6 +49,17 @@ export const mockEnvironmentsResponse = {
stopped_count: 5,
};
export const mockScanExecutionManifestNoActions = `type: scan_execution_policy
name: Test Dast
description: This policy enforces pipeline configuration to have a job with DAST scan
enabled: false
rules:
- type: pipeline
branches:
- main
actions: []
`;
export const mockDastScanExecutionManifest = `type: scan_execution_policy
name: Test Dast
description: This policy enforces pipeline configuration to have a job with DAST scan
......@@ -63,6 +74,19 @@ actions:
scanner_profile: required_scanner_profile
`;
export const mockScanExecutionManifestMultipleActions = `type: scan_execution_policy
name: Test Dast
description: This policy enforces pipeline configuration to have a job with DAST scan
enabled: false
rules:
- type: pipeline
branches:
- main
actions:
- scan: container_scanning
- scan: secret_detection
`;
export const mockDastScanExecutionObject = {
type: 'scan_execution_policy',
name: 'Test Dast',
......
......@@ -32587,10 +32587,10 @@ msgstr ""
msgid "SecurityOrchestration|Rules"
msgstr ""
msgid "SecurityOrchestration|Runs <strong>%{actions}</strong> and <strong>%{lastAction}</strong> scans"
msgid "SecurityOrchestration|Runs %{actions} and %{lastAction} scans"
msgstr ""
msgid "SecurityOrchestration|Runs a <strong>%{actions}</strong> scan"
msgid "SecurityOrchestration|Runs a %{action} scan"
msgstr ""
msgid "SecurityOrchestration|Scan Execution"
......
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