Commit 38c1dad1 authored by David O'Regan's avatar David O'Regan

Merge branch 'afontaine/edit-deploy-freeze' into 'master'

Add Ability to Edit Freeze Periods

See merge request gitlab-org/gitlab!56407
parents 35ec92a5 4910dca3
...@@ -79,6 +79,7 @@ const Api = { ...@@ -79,6 +79,7 @@ const Api = {
issuePath: '/api/:version/projects/:id/issues/:issue_iid', issuePath: '/api/:version/projects/:id/issues/:issue_iid',
tagsPath: '/api/:version/projects/:id/repository/tags', tagsPath: '/api/:version/projects/:id/repository/tags',
freezePeriodsPath: '/api/:version/projects/:id/freeze_periods', freezePeriodsPath: '/api/:version/projects/:id/freeze_periods',
freezePeriodPath: '/api/:version/projects/:id/freeze_periods/:freeze_period_id',
usageDataIncrementCounterPath: '/api/:version/usage_data/increment_counter', usageDataIncrementCounterPath: '/api/:version/usage_data/increment_counter',
usageDataIncrementUniqueUsersPath: '/api/:version/usage_data/increment_unique_users', usageDataIncrementUniqueUsersPath: '/api/:version/usage_data/increment_unique_users',
featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists', featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists',
...@@ -832,6 +833,14 @@ const Api = { ...@@ -832,6 +833,14 @@ const Api = {
return axios.post(url, freezePeriod); return axios.post(url, freezePeriod);
}, },
updateFreezePeriod(id, freezePeriod = {}) {
const url = Api.buildUrl(this.freezePeriodPath)
.replace(':id', encodeURIComponent(id))
.replace(':freeze_period_id', encodeURIComponent(freezePeriod.id));
return axios.put(url, freezePeriod);
},
trackRedisCounterEvent(event) { trackRedisCounterEvent(event) {
if (!gon.features?.usageDataApi) { if (!gon.features?.usageDataApi) {
return null; return null;
......
...@@ -18,7 +18,6 @@ export default { ...@@ -18,7 +18,6 @@ export default {
modalOptions: { modalOptions: {
ref: 'modal', ref: 'modal',
modalId: 'deploy-freeze-modal', modalId: 'deploy-freeze-modal',
title: __('Add deploy freeze'),
actionCancel: { actionCancel: {
text: __('Cancel'), text: __('Cancel'),
}, },
...@@ -30,10 +29,13 @@ export default { ...@@ -30,10 +29,13 @@ export default {
cronSyntaxInstructions: __( cronSyntaxInstructions: __(
'Define a custom deploy freeze pattern with %{cronSyntaxStart}cron syntax%{cronSyntaxEnd}', 'Define a custom deploy freeze pattern with %{cronSyntaxStart}cron syntax%{cronSyntaxEnd}',
), ),
addTitle: __('Add deploy freeze'),
editTitle: __('Edit deploy freeze'),
}, },
computed: { computed: {
...mapState([ ...mapState([
'projectId', 'projectId',
'selectedId',
'selectedTimezone', 'selectedTimezone',
'timezoneData', 'timezoneData',
'freezeStartCron', 'freezeStartCron',
...@@ -45,9 +47,9 @@ export default { ...@@ -45,9 +47,9 @@ export default {
]), ]),
addDeployFreezeButton() { addDeployFreezeButton() {
return { return {
text: __('Add deploy freeze'), text: this.isEditing ? __('Save deploy freeze') : __('Add deploy freeze'),
attributes: [ attributes: [
{ variant: 'success' }, { variant: 'confirm' },
{ {
disabled: disabled:
!isValidCron(this.freezeStartCron) || !isValidCron(this.freezeStartCron) ||
...@@ -77,9 +79,17 @@ export default { ...@@ -77,9 +79,17 @@ export default {
this.setSelectedTimezone(selectedTimezone); this.setSelectedTimezone(selectedTimezone);
}, },
}, },
isEditing() {
return Boolean(this.selectedId);
},
modalTitle() {
return this.isEditing
? this.$options.translations.editTitle
: this.$options.translations.addTitle;
},
}, },
methods: { methods: {
...mapActions(['addFreezePeriod', 'setSelectedTimezone', 'resetModal']), ...mapActions(['addFreezePeriod', 'updateFreezePeriod', 'setSelectedTimezone', 'resetModal']),
resetModalHandler() { resetModalHandler() {
this.resetModal(); this.resetModal();
}, },
...@@ -89,6 +99,13 @@ export default { ...@@ -89,6 +99,13 @@ export default {
} }
return ''; return '';
}, },
submit() {
if (this.isEditing) {
this.updateFreezePeriod();
} else {
this.addFreezePeriod();
}
},
}, },
}; };
</script> </script>
...@@ -96,8 +113,9 @@ export default { ...@@ -96,8 +113,9 @@ export default {
<template> <template>
<gl-modal <gl-modal
v-bind="$options.modalOptions" v-bind="$options.modalOptions"
:title="modalTitle"
:action-primary="addDeployFreezeButton" :action-primary="addDeployFreezeButton"
@primary="addFreezePeriod" @primary="submit"
@canceled="resetModalHandler" @canceled="resetModalHandler"
> >
<p> <p>
......
<script> <script>
import { GlTable, GlButton, GlModalDirective, GlSprintf } from '@gitlab/ui'; import { GlTable, GlButton, GlModalDirective, GlSprintf } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { s__, __ } from '~/locale'; import { s__ } from '~/locale';
export default { export default {
fields: [ fields: [
...@@ -17,9 +17,16 @@ export default { ...@@ -17,9 +17,16 @@ export default {
key: 'cronTimezone', key: 'cronTimezone',
label: s__('DeployFreeze|Time zone'), label: s__('DeployFreeze|Time zone'),
}, },
{
key: 'edit',
label: s__('DeployFreeze|Edit'),
},
], ],
translations: { translations: {
addDeployFreeze: __('Add deploy freeze'), addDeployFreeze: s__('DeployFreeze|Add deploy freeze'),
emptyStateText: s__(
'DeployFreeze|No deploy freezes exist for this project. To add one, select %{strongStart}Add deploy freeze%{strongEnd}',
),
}, },
components: { components: {
GlTable, GlTable,
...@@ -39,7 +46,7 @@ export default { ...@@ -39,7 +46,7 @@ export default {
this.fetchFreezePeriods(); this.fetchFreezePeriods();
}, },
methods: { methods: {
...mapActions(['fetchFreezePeriods']), ...mapActions(['fetchFreezePeriods', 'setFreezePeriod']),
}, },
}; };
</script> </script>
...@@ -53,15 +60,20 @@ export default { ...@@ -53,15 +60,20 @@ export default {
show-empty show-empty
stacked="lg" stacked="lg"
> >
<template #cell(cronTimezone)="{ item }">
{{ item.cronTimezone.formattedTimezone }}
</template>
<template #cell(edit)="{ item }">
<gl-button
v-gl-modal.deploy-freeze-modal
icon="pencil"
data-testid="edit-deploy-freeze"
@click="setFreezePeriod(item)"
/>
</template>
<template #empty> <template #empty>
<p data-testid="empty-freeze-periods" class="gl-text-center text-plain"> <p data-testid="empty-freeze-periods" class="gl-text-center text-plain">
<gl-sprintf <gl-sprintf :message="$options.translations.emptyStateText">
:message="
s__(
'DeployFreeze|No deploy freezes exist for this project. To add one, click %{strongStart}Add deploy freeze%{strongEnd}',
)
"
>
<template #strong="{ content }"> <template #strong="{ content }">
<strong>{{ content }}</strong> <strong>{{ content }}</strong>
</template> </template>
...@@ -73,7 +85,7 @@ export default { ...@@ -73,7 +85,7 @@ export default {
v-gl-modal.deploy-freeze-modal v-gl-modal.deploy-freeze-modal
data-testid="add-deploy-freeze" data-testid="add-deploy-freeze"
category="primary" category="primary"
variant="success" variant="confirm"
> >
{{ $options.translations.addDeployFreeze }} {{ $options.translations.addDeployFreeze }}
</gl-button> </gl-button>
......
...@@ -3,37 +3,53 @@ import { deprecatedCreateFlash as createFlash } from '~/flash'; ...@@ -3,37 +3,53 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import * as types from './mutation_types'; import * as types from './mutation_types';
export const requestAddFreezePeriod = ({ commit }) => { export const requestFreezePeriod = ({ commit }) => {
commit(types.REQUEST_ADD_FREEZE_PERIOD); commit(types.REQUEST_ADD_FREEZE_PERIOD);
}; };
export const receiveAddFreezePeriodSuccess = ({ commit }) => { export const receiveFreezePeriodSuccess = ({ commit }) => {
commit(types.RECEIVE_ADD_FREEZE_PERIOD_SUCCESS); commit(types.RECEIVE_ADD_FREEZE_PERIOD_SUCCESS);
}; };
export const receiveAddFreezePeriodError = ({ commit }, error) => { export const receiveFreezePeriodError = ({ commit }, error) => {
commit(types.RECEIVE_ADD_FREEZE_PERIOD_ERROR, error); commit(types.RECEIVE_ADD_FREEZE_PERIOD_ERROR, error);
}; };
export const addFreezePeriod = ({ state, dispatch, commit }) => { const receiveFreezePeriod = (store, request) => {
dispatch('requestAddFreezePeriod'); const { dispatch, commit } = store;
dispatch('requestFreezePeriod');
return Api.createFreezePeriod(state.projectId, { request(store)
freeze_start: state.freezeStartCron,
freeze_end: state.freezeEndCron,
cron_timezone: state.selectedTimezoneIdentifier,
})
.then(() => { .then(() => {
dispatch('receiveAddFreezePeriodSuccess'); dispatch('receiveFreezePeriodSuccess');
commit(types.RESET_MODAL); commit(types.RESET_MODAL);
dispatch('fetchFreezePeriods'); dispatch('fetchFreezePeriods');
}) })
.catch((error) => { .catch((error) => {
createFlash(__('Error: Unable to create deploy freeze')); createFlash(__('Error: Unable to create deploy freeze'));
dispatch('receiveAddFreezePeriodError', error); dispatch('receiveFreezePeriodError', error);
}); });
}; };
export const addFreezePeriod = (store) =>
receiveFreezePeriod(store, ({ state }) =>
Api.createFreezePeriod(state.projectId, {
freeze_start: state.freezeStartCron,
freeze_end: state.freezeEndCron,
cron_timezone: state.selectedTimezoneIdentifier,
}),
);
export const updateFreezePeriod = (store) =>
receiveFreezePeriod(store, ({ state }) =>
Api.updateFreezePeriod(state.projectId, {
id: state.selectedId,
freeze_start: state.freezeStartCron,
freeze_end: state.freezeEndCron,
cron_timezone: state.selectedTimezoneIdentifier,
}),
);
export const fetchFreezePeriods = ({ commit, state }) => { export const fetchFreezePeriods = ({ commit, state }) => {
commit(types.REQUEST_FREEZE_PERIODS); commit(types.REQUEST_FREEZE_PERIODS);
...@@ -46,6 +62,13 @@ export const fetchFreezePeriods = ({ commit, state }) => { ...@@ -46,6 +62,13 @@ export const fetchFreezePeriods = ({ commit, state }) => {
}); });
}; };
export const setFreezePeriod = ({ commit }, freezePeriod) => {
commit(types.SET_SELECTED_ID, freezePeriod.id);
commit(types.SET_SELECTED_TIMEZONE, freezePeriod.cronTimezone);
commit(types.SET_FREEZE_START_CRON, freezePeriod.freezeStart);
commit(types.SET_FREEZE_END_CRON, freezePeriod.freezeEnd);
};
export const setSelectedTimezone = ({ commit }, timezone) => { export const setSelectedTimezone = ({ commit }, timezone) => {
commit(types.SET_SELECTED_TIMEZONE, timezone); commit(types.SET_SELECTED_TIMEZONE, timezone);
}; };
......
...@@ -6,6 +6,7 @@ export const RECEIVE_ADD_FREEZE_PERIOD_SUCCESS = 'RECEIVE_ADD_FREEZE_PERIOD_SUCC ...@@ -6,6 +6,7 @@ export const RECEIVE_ADD_FREEZE_PERIOD_SUCCESS = 'RECEIVE_ADD_FREEZE_PERIOD_SUCC
export const RECEIVE_ADD_FREEZE_PERIOD_ERROR = 'RECEIVE_ADD_FREEZE_PERIOD_ERROR'; export const RECEIVE_ADD_FREEZE_PERIOD_ERROR = 'RECEIVE_ADD_FREEZE_PERIOD_ERROR';
export const SET_SELECTED_TIMEZONE = 'SET_SELECTED_TIMEZONE'; export const SET_SELECTED_TIMEZONE = 'SET_SELECTED_TIMEZONE';
export const SET_SELECTED_ID = 'SET_SELECTED_ID';
export const SET_FREEZE_START_CRON = 'SET_FREEZE_START_CRON'; export const SET_FREEZE_START_CRON = 'SET_FREEZE_START_CRON';
export const SET_FREEZE_END_CRON = 'SET_FREEZE_END_CRON'; export const SET_FREEZE_END_CRON = 'SET_FREEZE_END_CRON';
......
...@@ -4,7 +4,11 @@ import * as types from './mutation_types'; ...@@ -4,7 +4,11 @@ import * as types from './mutation_types';
const formatTimezoneName = (freezePeriod, timezoneList) => const formatTimezoneName = (freezePeriod, timezoneList) =>
convertObjectPropsToCamelCase({ convertObjectPropsToCamelCase({
...freezePeriod, ...freezePeriod,
cron_timezone: timezoneList.find((tz) => tz.identifier === freezePeriod.cron_timezone)?.name, cron_timezone: {
formattedTimezone: timezoneList.find((tz) => tz.identifier === freezePeriod.cron_timezone)
?.name,
identifier: freezePeriod.cronTimezone,
},
}); });
export default { export default {
...@@ -45,10 +49,15 @@ export default { ...@@ -45,10 +49,15 @@ export default {
state.freezeEndCron = freezeEndCron; state.freezeEndCron = freezeEndCron;
}, },
[types.SET_SELECTED_ID](state, id) {
state.selectedId = id;
},
[types.RESET_MODAL](state) { [types.RESET_MODAL](state) {
state.freezeStartCron = ''; state.freezeStartCron = '';
state.freezeEndCron = ''; state.freezeEndCron = '';
state.selectedTimezone = ''; state.selectedTimezone = '';
state.selectedTimezoneIdentifier = ''; state.selectedTimezoneIdentifier = '';
state.selectedId = '';
}, },
}; };
...@@ -6,6 +6,7 @@ export default ({ ...@@ -6,6 +6,7 @@ export default ({
selectedTimezoneIdentifier = '', selectedTimezoneIdentifier = '',
freezeStartCron = '', freezeStartCron = '',
freezeEndCron = '', freezeEndCron = '',
selectedId = '',
}) => ({ }) => ({
projectId, projectId,
freezePeriods, freezePeriods,
...@@ -14,4 +15,5 @@ export default ({ ...@@ -14,4 +15,5 @@ export default ({
selectedTimezoneIdentifier, selectedTimezoneIdentifier,
freezeStartCron, freezeStartCron,
freezeEndCron, freezeEndCron,
selectedId,
}); });
---
title: Add Ability to Edit Freeze Periods
merge_request: 56407
author:
type: added
...@@ -217,11 +217,11 @@ To set a deploy freeze window in the UI, complete these steps: ...@@ -217,11 +217,11 @@ To set a deploy freeze window in the UI, complete these steps:
1. Click **Add deploy freeze** to open the deploy freeze modal. 1. Click **Add deploy freeze** to open the deploy freeze modal.
1. Enter the start time, end time, and timezone of the desired deploy freeze period. 1. Enter the start time, end time, and timezone of the desired deploy freeze period.
1. Click **Add deploy freeze** in the modal. 1. Click **Add deploy freeze** in the modal.
1. After the deploy freeze is saved, you can edit it by selecting the edit button (**{pencil}**).
![Deploy freeze modal for setting a deploy freeze period](img/deploy_freeze_v13_2.png) ![Deploy freeze modal for setting a deploy freeze period](img/deploy_freeze_v13_10.png)
WARNING: WARNING:
To edit or delete a deploy freeze, use the [Freeze Periods API](../../../api/freeze_periods.md). To delete a deploy freeze, use the [Freeze Periods API](../../../api/freeze_periods.md).
If a project contains multiple freeze periods, all periods apply. If they overlap, the freeze covers the If a project contains multiple freeze periods, all periods apply. If they overlap, the freeze covers the
complete overlapping period. complete overlapping period.
......
...@@ -10245,13 +10245,19 @@ msgstr "" ...@@ -10245,13 +10245,19 @@ msgstr ""
msgid "DeployFreeze|Add a freeze period to prevent unintended releases during a period of time for a given environment. You must update the deployment jobs in %{filename} according to the deploy freezes added here. %{freeze_period_link_start}Learn more.%{freeze_period_link_end}" msgid "DeployFreeze|Add a freeze period to prevent unintended releases during a period of time for a given environment. You must update the deployment jobs in %{filename} according to the deploy freezes added here. %{freeze_period_link_start}Learn more.%{freeze_period_link_end}"
msgstr "" msgstr ""
msgid "DeployFreeze|Add deploy freeze"
msgstr ""
msgid "DeployFreeze|Edit"
msgstr ""
msgid "DeployFreeze|Freeze end" msgid "DeployFreeze|Freeze end"
msgstr "" msgstr ""
msgid "DeployFreeze|Freeze start" msgid "DeployFreeze|Freeze start"
msgstr "" msgstr ""
msgid "DeployFreeze|No deploy freezes exist for this project. To add one, click %{strongStart}Add deploy freeze%{strongEnd}" msgid "DeployFreeze|No deploy freezes exist for this project. To add one, select %{strongStart}Add deploy freeze%{strongEnd}"
msgstr "" msgstr ""
msgid "DeployFreeze|Specify deploy freezes using %{cron_syntax_link_start}cron syntax%{cron_syntax_link_end}." msgid "DeployFreeze|Specify deploy freezes using %{cron_syntax_link_start}cron syntax%{cron_syntax_link_end}."
...@@ -11191,6 +11197,9 @@ msgstr "" ...@@ -11191,6 +11197,9 @@ msgstr ""
msgid "Edit comment" msgid "Edit comment"
msgstr "" msgstr ""
msgid "Edit deploy freeze"
msgstr ""
msgid "Edit description" msgid "Edit description"
msgstr "" msgstr ""
...@@ -26436,6 +26445,9 @@ msgstr "" ...@@ -26436,6 +26445,9 @@ msgstr ""
msgid "Save comment" msgid "Save comment"
msgstr "" msgstr ""
msgid "Save deploy freeze"
msgstr ""
msgid "Save password" msgid "Save password"
msgstr "" msgstr ""
......
...@@ -1382,6 +1382,38 @@ describe('Api', () => { ...@@ -1382,6 +1382,38 @@ describe('Api', () => {
}); });
}); });
describe('updateFreezePeriod', () => {
const options = {
id: 10,
freeze_start: '* * * * *',
freeze_end: '* * * * *',
cron_timezone: 'America/Juneau',
created_at: '2020-07-11T07:04:50.153Z',
updated_at: '2020-07-11T07:04:50.153Z',
};
const projectId = 8;
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${projectId}/freeze_periods/${options.id}`;
const expectedResult = {
id: 10,
freeze_start: '* * * * *',
freeze_end: '* * * * *',
cron_timezone: 'America/Juneau',
created_at: '2020-07-11T07:04:50.153Z',
updated_at: '2020-07-11T07:04:50.153Z',
};
describe('when the freeze period is successfully updated', () => {
it('resolves the Promise', () => {
mock.onPut(expectedUrl, options).replyOnce(httpStatus.OK, expectedResult);
return Api.updateFreezePeriod(projectId, options).then(({ data }) => {
expect(data).toStrictEqual(expectedResult);
});
});
});
});
describe('createPipeline', () => { describe('createPipeline', () => {
it('creates new pipeline', () => { it('creates new pipeline', () => {
const redirectUrl = 'ci-project/-/pipelines/95'; const redirectUrl = 'ci-project/-/pipelines/95';
......
import { GlButton, GlModal } from '@gitlab/ui'; import { GlButton, GlModal } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import Api from '~/api';
import DeployFreezeModal from '~/deploy_freeze/components/deploy_freeze_modal.vue'; import DeployFreezeModal from '~/deploy_freeze/components/deploy_freeze_modal.vue';
import createStore from '~/deploy_freeze/store'; import createStore from '~/deploy_freeze/store';
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown.vue'; import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown.vue';
import { freezePeriodsFixture, timezoneDataFixture } from '../helpers'; import { freezePeriodsFixture, timezoneDataFixture } from '../helpers';
const localVue = createLocalVue(); jest.mock('~/api');
localVue.use(Vuex);
Vue.use(Vuex);
describe('Deploy freeze modal', () => { describe('Deploy freeze modal', () => {
let wrapper; let wrapper;
...@@ -23,18 +26,19 @@ describe('Deploy freeze modal', () => { ...@@ -23,18 +26,19 @@ describe('Deploy freeze modal', () => {
stubs: { stubs: {
GlModal, GlModal,
}, },
localVue,
store, store,
}); });
}); });
const findModal = () => wrapper.find(GlModal); const findModal = () => wrapper.findComponent(GlModal);
const addDeployFreezeButton = () => findModal().findAll(GlButton).at(1); const submitDeployFreezeButton = () => findModal().findAllComponents(GlButton).at(1);
const setInput = (freezeStartCron, freezeEndCron, selectedTimezone) => { const setInput = (freezeStartCron, freezeEndCron, selectedTimezone, id = '') => {
store.state.freezeStartCron = freezeStartCron; store.state.freezeStartCron = freezeStartCron;
store.state.freezeEndCron = freezeEndCron; store.state.freezeEndCron = freezeEndCron;
store.state.selectedTimezone = selectedTimezone; store.state.selectedTimezone = selectedTimezone;
store.state.selectedTimezoneIdentifier = selectedTimezone;
store.state.selectedId = id;
wrapper.find('#deploy-freeze-start').trigger('input'); wrapper.find('#deploy-freeze-start').trigger('input');
wrapper.find('#deploy-freeze-end').trigger('input'); wrapper.find('#deploy-freeze-end').trigger('input');
...@@ -48,18 +52,36 @@ describe('Deploy freeze modal', () => { ...@@ -48,18 +52,36 @@ describe('Deploy freeze modal', () => {
describe('Basic interactions', () => { describe('Basic interactions', () => {
it('button is disabled when freeze period is invalid', () => { it('button is disabled when freeze period is invalid', () => {
expect(addDeployFreezeButton().attributes('disabled')).toBeTruthy(); expect(submitDeployFreezeButton().attributes('disabled')).toBeTruthy();
}); });
}); });
describe('Adding a new deploy freeze', () => { describe('Adding a new deploy freeze', () => {
beforeEach(() => {
const { freeze_start, freeze_end, cron_timezone } = freezePeriodsFixture[0]; const { freeze_start, freeze_end, cron_timezone } = freezePeriodsFixture[0];
beforeEach(() => {
setInput(freeze_start, freeze_end, cron_timezone); setInput(freeze_start, freeze_end, cron_timezone);
}); });
it('button is enabled when valid freeze period settings are present', () => { it('button is enabled when valid freeze period settings are present', () => {
expect(addDeployFreezeButton().attributes('disabled')).toBeUndefined(); expect(submitDeployFreezeButton().attributes('disabled')).toBeUndefined();
});
it('should display Add deploy freeze', () => {
expect(findModal().props('title')).toBe('Add deploy freeze');
expect(submitDeployFreezeButton().text()).toBe('Add deploy freeze');
});
it('should call the add deploy freze API', () => {
Api.createFreezePeriod.mockResolvedValue();
findModal().vm.$emit('primary');
expect(Api.createFreezePeriod).toHaveBeenCalledTimes(1);
expect(Api.createFreezePeriod).toHaveBeenCalledWith(store.state.projectId, {
freeze_start,
freeze_end,
cron_timezone,
});
}); });
}); });
...@@ -70,7 +92,7 @@ describe('Deploy freeze modal', () => { ...@@ -70,7 +92,7 @@ describe('Deploy freeze modal', () => {
}); });
it('disables the add deploy freeze button', () => { it('disables the add deploy freeze button', () => {
expect(addDeployFreezeButton().attributes('disabled')).toBeTruthy(); expect(submitDeployFreezeButton().attributes('disabled')).toBeTruthy();
}); });
}); });
...@@ -81,7 +103,32 @@ describe('Deploy freeze modal', () => { ...@@ -81,7 +103,32 @@ describe('Deploy freeze modal', () => {
}); });
it('does not disable the submit button', () => { it('does not disable the submit button', () => {
expect(addDeployFreezeButton().attributes('disabled')).toBeFalsy(); expect(submitDeployFreezeButton().attributes('disabled')).toBeFalsy();
});
});
});
describe('Editing an existing deploy freeze', () => {
const { freeze_start, freeze_end, cron_timezone, id } = freezePeriodsFixture[0];
beforeEach(() => {
setInput(freeze_start, freeze_end, cron_timezone, id);
});
it('should display Edit deploy freeze', () => {
expect(findModal().props('title')).toBe('Edit deploy freeze');
expect(submitDeployFreezeButton().text()).toBe('Save deploy freeze');
});
it('should call the update deploy freze API', () => {
Api.updateFreezePeriod.mockResolvedValue();
findModal().vm.$emit('primary');
expect(Api.updateFreezePeriod).toHaveBeenCalledTimes(1);
expect(Api.updateFreezePeriod).toHaveBeenCalledWith(store.state.projectId, {
id,
freeze_start,
freeze_end,
cron_timezone,
}); });
}); });
}); });
......
...@@ -2,6 +2,7 @@ import { createLocalVue, mount } from '@vue/test-utils'; ...@@ -2,6 +2,7 @@ import { createLocalVue, mount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import DeployFreezeTable from '~/deploy_freeze/components/deploy_freeze_table.vue'; import DeployFreezeTable from '~/deploy_freeze/components/deploy_freeze_table.vue';
import createStore from '~/deploy_freeze/store'; import createStore from '~/deploy_freeze/store';
import { RECEIVE_FREEZE_PERIODS_SUCCESS } from '~/deploy_freeze/store/mutation_types';
import { freezePeriodsFixture, timezoneDataFixture } from '../helpers'; import { freezePeriodsFixture, timezoneDataFixture } from '../helpers';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -26,6 +27,7 @@ describe('Deploy freeze table', () => { ...@@ -26,6 +27,7 @@ describe('Deploy freeze table', () => {
const findEmptyFreezePeriods = () => wrapper.find('[data-testid="empty-freeze-periods"]'); const findEmptyFreezePeriods = () => wrapper.find('[data-testid="empty-freeze-periods"]');
const findAddDeployFreezeButton = () => wrapper.find('[data-testid="add-deploy-freeze"]'); const findAddDeployFreezeButton = () => wrapper.find('[data-testid="add-deploy-freeze"]');
const findEditDeployFreezeButton = () => wrapper.find('[data-testid="edit-deploy-freeze"]');
const findDeployFreezeTable = () => wrapper.find('[data-testid="deploy-freeze-table"]'); const findDeployFreezeTable = () => wrapper.find('[data-testid="deploy-freeze-table"]');
beforeEach(() => { beforeEach(() => {
...@@ -45,17 +47,31 @@ describe('Deploy freeze table', () => { ...@@ -45,17 +47,31 @@ describe('Deploy freeze table', () => {
it('displays empty', () => { it('displays empty', () => {
expect(findEmptyFreezePeriods().exists()).toBe(true); expect(findEmptyFreezePeriods().exists()).toBe(true);
expect(findEmptyFreezePeriods().text()).toBe( expect(findEmptyFreezePeriods().text()).toBe(
'No deploy freezes exist for this project. To add one, click Add deploy freeze', 'No deploy freezes exist for this project. To add one, select Add deploy freeze',
); );
}); });
it('displays data', () => { describe('with data', () => {
store.state.freezePeriods = freezePeriodsFixture; beforeEach(async () => {
store.commit(RECEIVE_FREEZE_PERIODS_SUCCESS, freezePeriodsFixture);
await wrapper.vm.$nextTick();
});
return wrapper.vm.$nextTick(() => { it('displays data', () => {
const tableRows = findDeployFreezeTable().findAll('tbody tr'); const tableRows = findDeployFreezeTable().findAll('tbody tr');
expect(tableRows.length).toBe(freezePeriodsFixture.length); expect(tableRows.length).toBe(freezePeriodsFixture.length);
expect(findEmptyFreezePeriods().exists()).toBe(false); expect(findEmptyFreezePeriods().exists()).toBe(false);
expect(findEditDeployFreezeButton().exists()).toBe(true);
});
it('allows user to edit deploy freeze', async () => {
findEditDeployFreezeButton().trigger('click');
await wrapper.vm.$nextTick();
expect(store.dispatch).toHaveBeenCalledWith(
'setFreezePeriod',
store.state.freezePeriods[0],
);
}); });
}); });
}); });
......
...@@ -23,12 +23,46 @@ describe('deploy freeze store actions', () => { ...@@ -23,12 +23,46 @@ describe('deploy freeze store actions', () => {
}); });
Api.freezePeriods.mockResolvedValue({ data: freezePeriodsFixture }); Api.freezePeriods.mockResolvedValue({ data: freezePeriodsFixture });
Api.createFreezePeriod.mockResolvedValue(); Api.createFreezePeriod.mockResolvedValue();
Api.updateFreezePeriod.mockResolvedValue();
}); });
afterEach(() => { afterEach(() => {
mock.restore(); mock.restore();
}); });
describe('setSelectedFreezePeriod', () => {
it('commits SET_SELECTED_TIMEZONE mutation', () => {
testAction(
actions.setFreezePeriod,
{
id: 3,
cronTimezone: 'UTC',
freezeStart: 'start',
freezeEnd: 'end',
},
{},
[
{
payload: 3,
type: types.SET_SELECTED_ID,
},
{
payload: 'UTC',
type: types.SET_SELECTED_TIMEZONE,
},
{
payload: 'start',
type: types.SET_FREEZE_START_CRON,
},
{
payload: 'end',
type: types.SET_FREEZE_END_CRON,
},
],
);
});
});
describe('setSelectedTimezone', () => { describe('setSelectedTimezone', () => {
it('commits SET_SELECTED_TIMEZONE mutation', () => { it('commits SET_SELECTED_TIMEZONE mutation', () => {
testAction(actions.setSelectedTimezone, {}, {}, [ testAction(actions.setSelectedTimezone, {}, {}, [
...@@ -68,10 +102,16 @@ describe('deploy freeze store actions', () => { ...@@ -68,10 +102,16 @@ describe('deploy freeze store actions', () => {
state, state,
[{ type: 'RESET_MODAL' }], [{ type: 'RESET_MODAL' }],
[ [
{ type: 'requestAddFreezePeriod' }, { type: 'requestFreezePeriod' },
{ type: 'receiveAddFreezePeriodSuccess' }, { type: 'receiveFreezePeriodSuccess' },
{ type: 'fetchFreezePeriods' }, { type: 'fetchFreezePeriods' },
], ],
() =>
expect(Api.createFreezePeriod).toHaveBeenCalledWith(state.projectId, {
freeze_start: state.freezeStartCron,
freeze_end: state.freezeEndCron,
cron_timezone: state.selectedTimezoneIdentifier,
}),
); );
}); });
...@@ -83,7 +123,43 @@ describe('deploy freeze store actions', () => { ...@@ -83,7 +123,43 @@ describe('deploy freeze store actions', () => {
{}, {},
state, state,
[], [],
[{ type: 'requestAddFreezePeriod' }, { type: 'receiveAddFreezePeriodError' }], [{ type: 'requestFreezePeriod' }, { type: 'receiveFreezePeriodError' }],
() => expect(createFlash).toHaveBeenCalled(),
);
});
});
describe('updateFreezePeriod', () => {
it('dispatch correct actions on updating a freeze period', () => {
testAction(
actions.updateFreezePeriod,
{},
state,
[{ type: 'RESET_MODAL' }],
[
{ type: 'requestFreezePeriod' },
{ type: 'receiveFreezePeriodSuccess' },
{ type: 'fetchFreezePeriods' },
],
() =>
expect(Api.updateFreezePeriod).toHaveBeenCalledWith(state.projectId, {
id: state.selectedId,
freeze_start: state.freezeStartCron,
freeze_end: state.freezeEndCron,
cron_timezone: state.selectedTimezoneIdentifier,
}),
);
});
it('should show flash error and set error in state on add failure', () => {
Api.updateFreezePeriod.mockRejectedValue();
testAction(
actions.updateFreezePeriod,
{},
state,
[],
[{ type: 'requestFreezePeriod' }, { type: 'receiveFreezePeriodError' }],
() => expect(createFlash).toHaveBeenCalled(), () => expect(createFlash).toHaveBeenCalled(),
); );
}); });
......
...@@ -33,7 +33,10 @@ describe('Deploy freeze mutations', () => { ...@@ -33,7 +33,10 @@ describe('Deploy freeze mutations', () => {
const expectedFreezePeriods = freezePeriodsFixture.map((freezePeriod, index) => ({ const expectedFreezePeriods = freezePeriodsFixture.map((freezePeriod, index) => ({
...convertObjectPropsToCamelCase(freezePeriod), ...convertObjectPropsToCamelCase(freezePeriod),
cronTimezone: timezoneNames[index], cronTimezone: {
formattedTimezone: timezoneNames[index],
identifier: freezePeriod.cronTimezone,
},
})); }));
expect(stateCopy.freezePeriods).toMatchObject(expectedFreezePeriods); expect(stateCopy.freezePeriods).toMatchObject(expectedFreezePeriods);
...@@ -62,11 +65,19 @@ describe('Deploy freeze mutations', () => { ...@@ -62,11 +65,19 @@ describe('Deploy freeze mutations', () => {
}); });
}); });
describe('SET_FREEZE_ENDT_CRON', () => { describe('SET_FREEZE_END_CRON', () => {
it('should set freezeEndCron', () => { it('should set freezeEndCron', () => {
mutations[types.SET_FREEZE_END_CRON](stateCopy, '5 0 * 8 *'); mutations[types.SET_FREEZE_END_CRON](stateCopy, '5 0 * 8 *');
expect(stateCopy.freezeEndCron).toBe('5 0 * 8 *'); expect(stateCopy.freezeEndCron).toBe('5 0 * 8 *');
}); });
}); });
describe('SET_SELECTED_ID', () => {
it('should set selectedId', () => {
mutations[types.SET_SELECTED_ID](stateCopy, 5);
expect(stateCopy.selectedId).toBe(5);
});
});
}); });
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