Skip to content
代码片段 群组 项目
未验证 提交 4c577fcd 编辑于 作者: Artur Fedorov's avatar Artur Fedorov 提交者: GitLab
浏览文件

Merge branch '505173-add-schedule-form-branch-selector' into 'master'

Add branch and timezone selector

See merge request https://gitlab.com/gitlab-org/gitlab/-/merge_requests/183754



Merged-by: default avatarArtur Fedorov <afedorov@gitlab.com>
Approved-by: default avatarAnastasia Khomchenko <akhomchenko@gitlab.com>
Approved-by: default avatarArtur Fedorov <afedorov@gitlab.com>
Reviewed-by: default avatarAlexander Turinske <aturinske@gitlab.com>
Reviewed-by: default avatarArtur Fedorov <afedorov@gitlab.com>
Reviewed-by: default avatarAnastasia Khomchenko <akhomchenko@gitlab.com>
Co-authored-by: default avatarAlexander Turinske <aturinske@gitlab.com>
No related branches found
No related tags found
2 合并请求!3031Merge per-main-jh to main-jh by luzhiyuan,!3030Merge per-main-jh to main-jh
......@@ -65,6 +65,7 @@ content:
export const HOUR_IN_SECONDS = 3600;
export const DAILY = 'daily';
export const DEFAULT_SCHEDULE = {
branch_type: 'protected',
type: DAILY,
start_time: '00:00',
time_window: {
......
<script>
import { GlCollapsibleListbox, GlSprintf } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { s__, __, sprintf } from '~/locale';
import BranchSelection from 'ee/security_orchestration/components/policy_editor/scan_result/rule/branch_selection.vue';
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
import { CADENCE_OPTIONS, updateScheduleCadence } from './utils';
export default {
......@@ -8,40 +10,100 @@ export default {
CADENCE_OPTIONS,
i18n: {
cadence: __('Cadence'),
message: s__(
'SecurityOrchestration|Schedule a pipeline on a %{cadenceSelector} cadence for branches',
details: s__(
'SecurityOrchestration|at the following times: %{cadenceSelector}, start at %{start}, run for: %{duration}, and timezone is %{timezoneSelector}',
),
message: s__('SecurityOrchestration|Schedule to run for %{branchSelector}'),
timezoneLabel: s__('ScanExecutionPolicy|on %{hostname}'),
timezonePlaceholder: s__('ScanExecutionPolicy|Select timezone'),
},
components: {
BranchSelection,
GlCollapsibleListbox,
GlSprintf,
TimezoneDropdown,
},
inject: ['timezones'],
props: {
schedule: {
type: Object,
required: true,
},
},
computed: {
hostname() {
return window?.location?.host;
},
branchInfo() {
return {
branch_type: this.schedule?.branch_type,
branches: this.schedule?.branches,
};
},
timezoneTooltipText() {
return sprintf(this.$options.i18n.timezoneLabel, { hostname: this.hostname });
},
},
methods: {
updateBranchType({ branch_type, branches }) {
const {
branch_type: oldBranchType,
branches: oldBranches,
...updatedSchedule
} = this.schedule;
this.$emit('changed', {
...updatedSchedule,
...(branch_type ? { branch_type } : { branches }), // eslint-disable-line camelcase
});
},
updateCadence(value) {
const updatedSchedule = updateScheduleCadence({ schedule: this.schedule, cadence: value });
this.$emit('changed', updatedSchedule);
},
handleTimeZoneInput({ identifier }) {
this.$emit('changed', { ...this.schedule, timezone: identifier });
},
},
};
</script>
<template>
<div class="gl-flex gl-flex-wrap gl-items-center gl-gap-3">
<gl-sprintf :message="$options.i18n.message">
<template #cadenceSelector>
<gl-collapsible-listbox
:aria-label="$options.i18n.cadence"
:items="$options.CADENCE_OPTIONS"
:selected="schedule.type"
@select="updateCadence"
/>
</template>
</gl-sprintf>
<div>
<div class="gl-mb-3 gl-flex gl-flex-wrap gl-items-center gl-gap-3">
<gl-sprintf :message="$options.i18n.message">
<template #branchSelector>
<branch-selection :init-rule="branchInfo" @set-branch-type="updateBranchType" />
</template>
</gl-sprintf>
</div>
<div class="gl-flex gl-flex-wrap gl-items-center gl-gap-3">
<gl-sprintf :message="$options.i18n.details">
<template #cadenceSelector>
<gl-collapsible-listbox
:aria-label="$options.i18n.cadence"
:items="$options.CADENCE_OPTIONS"
:selected="schedule.type"
@select="updateCadence"
/>
</template>
<template #start> </template>
<template #duration> </template>
<template #timezoneSelector>
<timezone-dropdown
:aria-label="$options.i18n.timezonePlaceholder"
class="gl-max-w-26"
:header-text="$options.i18n.timezonePlaceholder"
:timezone-data="timezones"
:title="timezoneTooltipText"
:value="schedule.timezone"
@input="handleTimeZoneInput"
/>
</template>
</gl-sprintf>
</div>
</div>
</template>
import { shallowMount } from '@vue/test-utils';
import { GlCollapsibleListbox, GlSprintf } from '@gitlab/ui';
import ScheduleForm from 'ee/security_orchestration/components/policy_editor/pipeline_execution/rule/schedule_form.vue';
import BranchSelection from 'ee/security_orchestration/components/policy_editor/scan_result/rule/branch_selection.vue';
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
describe('ScheduleForm', () => {
let wrapper;
const defaultSchedule = { type: 'daily', time_window: { value: 3600 } };
const defaultSchedule = {
type: 'daily',
time_window: { value: 3600 },
branch_type: 'protected',
timezone: 'America/New_York',
};
const mockTimezones = [
{ identifier: 'America/New_York', name: 'Eastern Time' },
{ identifier: 'America/Los_Angeles', name: 'Pacific Time' },
];
const createComponent = (props = {}) => {
const createComponent = (props = {}, provide = {}) => {
wrapper = shallowMount(ScheduleForm, {
propsData: { schedule: defaultSchedule, ...props },
stubs: { GlSprintf },
provide: { timezones: mockTimezones, ...provide },
});
};
const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
const findBranchSelection = () => wrapper.findComponent(BranchSelection);
const findTimezoneDropdown = () => wrapper.findComponent(TimezoneDropdown);
describe('rendering', () => {
it('renders the component', () => {
it('displays the message', () => {
createComponent();
expect(wrapper.exists()).toBe(true);
expect(wrapper.text()).toContain('Schedule to run for');
});
it('displays the correct message', () => {
it('displays the details', () => {
createComponent();
expect(wrapper.text()).toContain('Schedule a pipeline on a cadence for branches');
expect(wrapper.text()).toContain(
'at the following times: , start at , run for: , and timezone is',
);
});
it('renders the cadence selector with correct options', () => {
......@@ -41,56 +57,101 @@ describe('ScheduleForm', () => {
createComponent({ schedule: { type: 'weekly' } });
expect(findListbox().props('selected')).toBe('weekly');
});
});
describe('updateCadence', () => {
it('emits changed event with daily schedule when daily is selected', async () => {
it('renders the branch selection component', () => {
createComponent();
await findListbox().vm.$emit('select', 'daily');
expect(wrapper.emitted('changed')).toHaveLength(1);
expect(wrapper.emitted('changed')).toMatchObject([
[{ type: 'daily', start_time: '00:00', time_window: { value: 3600 } }],
]);
expect(findBranchSelection().exists()).toBe(true);
expect(findBranchSelection().props('initRule')).toEqual({
branch_type: 'protected',
branches: undefined,
});
});
it('emits changed event with weekly schedule when weekly is selected', async () => {
it('renders the timezone dropdown', () => {
createComponent();
await findListbox().vm.$emit('select', 'weekly');
expect(wrapper.emitted('changed')).toHaveLength(1);
expect(wrapper.emitted('changed')).toMatchObject([
[{ type: 'weekly', days: 'monday', time_window: { value: 86400 } }],
]);
const timezoneDropdown = findTimezoneDropdown();
expect(timezoneDropdown.exists()).toBe(true);
expect(timezoneDropdown.props()).toMatchObject({
timezoneData: mockTimezones,
value: 'America/New_York',
headerText: 'Select timezone',
});
expect(timezoneDropdown.attributes('title')).toBe(`on ${window.location.host}`);
});
});
it('emits changed event with monthly schedule when monthly is selected', async () => {
describe('event handling', () => {
beforeEach(() => {
createComponent();
await findListbox().vm.$emit('select', 'monthly');
});
expect(wrapper.emitted('changed')).toHaveLength(1);
expect(wrapper.emitted('changed')).toMatchObject([
[{ type: 'monthly', days_of_month: '1', time_window: { value: 86400 } }],
]);
describe('branch selection', () => {
it('handles branch selection changes', async () => {
const branchData = { branch_type: 'all' };
await findBranchSelection().vm.$emit('set-branch-type', branchData);
expect(wrapper.emitted('changed')).toMatchObject([[{ ...branchData }]]);
});
});
it('removes irrelevant properties when changing cadence type', async () => {
createComponent({
schedule: {
type: 'daily',
start_time: '12:00',
days: 'friday',
days_of_month: '15',
time_window: { value: 3600 },
},
describe('cadence', () => {
it('emits changed event with daily schedule when daily is selected', async () => {
createComponent();
await findListbox().vm.$emit('select', 'daily');
expect(wrapper.emitted('changed')).toHaveLength(1);
expect(wrapper.emitted('changed')).toMatchObject([
[{ type: 'daily', start_time: '00:00', time_window: { value: 3600 } }],
]);
});
it('emits changed event with weekly schedule when weekly is selected', async () => {
createComponent();
await findListbox().vm.$emit('select', 'weekly');
expect(wrapper.emitted('changed')).toHaveLength(1);
expect(wrapper.emitted('changed')).toMatchObject([
[{ type: 'weekly', days: 'monday', time_window: { value: 86400 } }],
]);
});
await findListbox().vm.$emit('select', 'weekly');
it('emits changed event with monthly schedule when monthly is selected', async () => {
createComponent();
await findListbox().vm.$emit('select', 'monthly');
const emittedSchedule = wrapper.emitted('changed')[0][0];
expect(emittedSchedule).toHaveProperty('days');
expect(emittedSchedule).not.toHaveProperty('start_time');
expect(emittedSchedule).not.toHaveProperty('days_of_month');
expect(wrapper.emitted('changed')).toHaveLength(1);
expect(wrapper.emitted('changed')).toMatchObject([
[{ type: 'monthly', days_of_month: '1', time_window: { value: 86400 } }],
]);
});
it('removes irrelevant properties when changing cadence type', async () => {
createComponent({
schedule: {
type: 'daily',
start_time: '12:00',
days: 'friday',
days_of_month: '15',
time_window: { value: 3600 },
},
});
await findListbox().vm.$emit('select', 'weekly');
const emittedSchedule = wrapper.emitted('changed')[0][0];
expect(emittedSchedule).toHaveProperty('days');
expect(emittedSchedule).not.toHaveProperty('start_time');
expect(emittedSchedule).not.toHaveProperty('days_of_month');
});
});
describe('timezone', () => {
it('handles timezone selection changes', async () => {
const timezoneData = { identifier: 'America/Los_Angeles' };
await findTimezoneDropdown().vm.$emit('input', timezoneData);
expect(wrapper.emitted('changed')).toEqual([
[expect.objectContaining({ timezone: timezoneData.identifier })],
]);
});
});
});
});
......@@ -53297,7 +53297,7 @@ msgstr ""
msgid "SecurityOrchestration|Schedule a new"
msgstr ""
 
msgid "SecurityOrchestration|Schedule a pipeline on a %{cadenceSelector} cadence for branches"
msgid "SecurityOrchestration|Schedule to run for %{branchSelector}"
msgstr ""
 
msgid "SecurityOrchestration|Scope"
......@@ -53657,6 +53657,9 @@ msgstr ""
msgid "SecurityOrchestration|are not false positives"
msgstr ""
 
msgid "SecurityOrchestration|at the following times: %{cadenceSelector}, start at %{start}, run for: %{duration}, and timezone is %{timezoneSelector}"
msgstr ""
msgid "SecurityOrchestration|branch"
msgstr ""
 
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册