Commit 4910dca3 authored by Andrew Fontaine's avatar Andrew Fontaine Committed by David O'Regan

Add Ability to Edit Freeze Periods

Allows users to modify existing freeze periods within the same modal
that is used to create them. Uses a nice little higher order function to
keep the same request/receive path as adding a new freeze period.
parent 4c6e9e98
......@@ -79,6 +79,7 @@ const Api = {
issuePath: '/api/:version/projects/:id/issues/:issue_iid',
tagsPath: '/api/:version/projects/:id/repository/tags',
freezePeriodsPath: '/api/:version/projects/:id/freeze_periods',
freezePeriodPath: '/api/:version/projects/:id/freeze_periods/:freeze_period_id',
usageDataIncrementCounterPath: '/api/:version/usage_data/increment_counter',
usageDataIncrementUniqueUsersPath: '/api/:version/usage_data/increment_unique_users',
featureFlagUserLists: '/api/:version/projects/:id/feature_flags_user_lists',
......@@ -832,6 +833,14 @@ const Api = {
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) {
if (!gon.features?.usageDataApi) {
return null;
......
......@@ -18,7 +18,6 @@ export default {
modalOptions: {
ref: 'modal',
modalId: 'deploy-freeze-modal',
title: __('Add deploy freeze'),
actionCancel: {
text: __('Cancel'),
},
......@@ -30,10 +29,13 @@ export default {
cronSyntaxInstructions: __(
'Define a custom deploy freeze pattern with %{cronSyntaxStart}cron syntax%{cronSyntaxEnd}',
),
addTitle: __('Add deploy freeze'),
editTitle: __('Edit deploy freeze'),
},
computed: {
...mapState([
'projectId',
'selectedId',
'selectedTimezone',
'timezoneData',
'freezeStartCron',
......@@ -45,9 +47,9 @@ export default {
]),
addDeployFreezeButton() {
return {
text: __('Add deploy freeze'),
text: this.isEditing ? __('Save deploy freeze') : __('Add deploy freeze'),
attributes: [
{ variant: 'success' },
{ variant: 'confirm' },
{
disabled:
!isValidCron(this.freezeStartCron) ||
......@@ -77,9 +79,17 @@ export default {
this.setSelectedTimezone(selectedTimezone);
},
},
isEditing() {
return Boolean(this.selectedId);
},
modalTitle() {
return this.isEditing
? this.$options.translations.editTitle
: this.$options.translations.addTitle;
},
},
methods: {
...mapActions(['addFreezePeriod', 'setSelectedTimezone', 'resetModal']),
...mapActions(['addFreezePeriod', 'updateFreezePeriod', 'setSelectedTimezone', 'resetModal']),
resetModalHandler() {
this.resetModal();
},
......@@ -89,6 +99,13 @@ export default {
}
return '';
},
submit() {
if (this.isEditing) {
this.updateFreezePeriod();
} else {
this.addFreezePeriod();
}
},
},
};
</script>
......@@ -96,8 +113,9 @@ export default {
<template>
<gl-modal
v-bind="$options.modalOptions"
:title="modalTitle"
:action-primary="addDeployFreezeButton"
@primary="addFreezePeriod"
@primary="submit"
@canceled="resetModalHandler"
>
<p>
......
<script>
import { GlTable, GlButton, GlModalDirective, GlSprintf } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { s__, __ } from '~/locale';
import { s__ } from '~/locale';
export default {
fields: [
......@@ -17,9 +17,16 @@ export default {
key: 'cronTimezone',
label: s__('DeployFreeze|Time zone'),
},
{
key: 'edit',
label: s__('DeployFreeze|Edit'),
},
],
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: {
GlTable,
......@@ -39,7 +46,7 @@ export default {
this.fetchFreezePeriods();
},
methods: {
...mapActions(['fetchFreezePeriods']),
...mapActions(['fetchFreezePeriods', 'setFreezePeriod']),
},
};
</script>
......@@ -53,15 +60,20 @@ export default {
show-empty
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>
<p data-testid="empty-freeze-periods" class="gl-text-center text-plain">
<gl-sprintf
:message="
s__(
'DeployFreeze|No deploy freezes exist for this project. To add one, click %{strongStart}Add deploy freeze%{strongEnd}',
)
"
>
<gl-sprintf :message="$options.translations.emptyStateText">
<template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
......@@ -73,7 +85,7 @@ export default {
v-gl-modal.deploy-freeze-modal
data-testid="add-deploy-freeze"
category="primary"
variant="success"
variant="confirm"
>
{{ $options.translations.addDeployFreeze }}
</gl-button>
......
......@@ -3,37 +3,53 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __ } from '~/locale';
import * as types from './mutation_types';
export const requestAddFreezePeriod = ({ commit }) => {
export const requestFreezePeriod = ({ commit }) => {
commit(types.REQUEST_ADD_FREEZE_PERIOD);
};
export const receiveAddFreezePeriodSuccess = ({ commit }) => {
export const receiveFreezePeriodSuccess = ({ commit }) => {
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);
};
export const addFreezePeriod = ({ state, dispatch, commit }) => {
dispatch('requestAddFreezePeriod');
const receiveFreezePeriod = (store, request) => {
const { dispatch, commit } = store;
dispatch('requestFreezePeriod');
return Api.createFreezePeriod(state.projectId, {
freeze_start: state.freezeStartCron,
freeze_end: state.freezeEndCron,
cron_timezone: state.selectedTimezoneIdentifier,
})
request(store)
.then(() => {
dispatch('receiveAddFreezePeriodSuccess');
dispatch('receiveFreezePeriodSuccess');
commit(types.RESET_MODAL);
dispatch('fetchFreezePeriods');
})
.catch((error) => {
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 }) => {
commit(types.REQUEST_FREEZE_PERIODS);
......@@ -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) => {
commit(types.SET_SELECTED_TIMEZONE, timezone);
};
......
......@@ -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 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_END_CRON = 'SET_FREEZE_END_CRON';
......
......@@ -4,7 +4,11 @@ import * as types from './mutation_types';
const formatTimezoneName = (freezePeriod, timezoneList) =>
convertObjectPropsToCamelCase({
...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 {
......@@ -45,10 +49,15 @@ export default {
state.freezeEndCron = freezeEndCron;
},
[types.SET_SELECTED_ID](state, id) {
state.selectedId = id;
},
[types.RESET_MODAL](state) {
state.freezeStartCron = '';
state.freezeEndCron = '';
state.selectedTimezone = '';
state.selectedTimezoneIdentifier = '';
state.selectedId = '';
},
};
......@@ -6,6 +6,7 @@ export default ({
selectedTimezoneIdentifier = '',
freezeStartCron = '',
freezeEndCron = '',
selectedId = '',
}) => ({
projectId,
freezePeriods,
......@@ -14,4 +15,5 @@ export default ({
selectedTimezoneIdentifier,
freezeStartCron,
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:
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. Click **Add deploy freeze** in the modal.
![Deploy freeze modal for setting a deploy freeze period](img/deploy_freeze_v13_2.png)
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_10.png)
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
complete overlapping period.
......
......@@ -10221,13 +10221,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}"
msgstr ""
msgid "DeployFreeze|Add deploy freeze"
msgstr ""
msgid "DeployFreeze|Edit"
msgstr ""
msgid "DeployFreeze|Freeze end"
msgstr ""
msgid "DeployFreeze|Freeze start"
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 ""
msgid "DeployFreeze|Specify deploy freezes using %{cron_syntax_link_start}cron syntax%{cron_syntax_link_end}."
......@@ -11167,6 +11173,9 @@ msgstr ""
msgid "Edit comment"
msgstr ""
msgid "Edit deploy freeze"
msgstr ""
msgid "Edit description"
msgstr ""
......@@ -26397,6 +26406,9 @@ msgstr ""
msgid "Save comment"
msgstr ""
msgid "Save deploy freeze"
msgstr ""
msgid "Save password"
msgstr ""
......
......@@ -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', () => {
it('creates new pipeline', () => {
const redirectUrl = 'ci-project/-/pipelines/95';
......
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 Api from '~/api';
import DeployFreezeModal from '~/deploy_freeze/components/deploy_freeze_modal.vue';
import createStore from '~/deploy_freeze/store';
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown.vue';
import { freezePeriodsFixture, timezoneDataFixture } from '../helpers';
const localVue = createLocalVue();
localVue.use(Vuex);
jest.mock('~/api');
Vue.use(Vuex);
describe('Deploy freeze modal', () => {
let wrapper;
......@@ -23,18 +26,19 @@ describe('Deploy freeze modal', () => {
stubs: {
GlModal,
},
localVue,
store,
});
});
const findModal = () => wrapper.find(GlModal);
const addDeployFreezeButton = () => findModal().findAll(GlButton).at(1);
const findModal = () => wrapper.findComponent(GlModal);
const submitDeployFreezeButton = () => findModal().findAllComponents(GlButton).at(1);
const setInput = (freezeStartCron, freezeEndCron, selectedTimezone) => {
const setInput = (freezeStartCron, freezeEndCron, selectedTimezone, id = '') => {
store.state.freezeStartCron = freezeStartCron;
store.state.freezeEndCron = freezeEndCron;
store.state.selectedTimezone = selectedTimezone;
store.state.selectedTimezoneIdentifier = selectedTimezone;
store.state.selectedId = id;
wrapper.find('#deploy-freeze-start').trigger('input');
wrapper.find('#deploy-freeze-end').trigger('input');
......@@ -48,18 +52,36 @@ describe('Deploy freeze modal', () => {
describe('Basic interactions', () => {
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', () => {
const { freeze_start, freeze_end, cron_timezone } = freezePeriodsFixture[0];
beforeEach(() => {
const { freeze_start, freeze_end, cron_timezone } = freezePeriodsFixture[0];
setInput(freeze_start, freeze_end, cron_timezone);
});
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', () => {
});
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', () => {
});
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';
import Vuex from 'vuex';
import DeployFreezeTable from '~/deploy_freeze/components/deploy_freeze_table.vue';
import createStore from '~/deploy_freeze/store';
import { RECEIVE_FREEZE_PERIODS_SUCCESS } from '~/deploy_freeze/store/mutation_types';
import { freezePeriodsFixture, timezoneDataFixture } from '../helpers';
const localVue = createLocalVue();
......@@ -26,6 +27,7 @@ describe('Deploy freeze table', () => {
const findEmptyFreezePeriods = () => wrapper.find('[data-testid="empty-freeze-periods"]');
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"]');
beforeEach(() => {
......@@ -45,17 +47,31 @@ describe('Deploy freeze table', () => {
it('displays empty', () => {
expect(findEmptyFreezePeriods().exists()).toBe(true);
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', () => {
store.state.freezePeriods = freezePeriodsFixture;
describe('with data', () => {
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');
expect(tableRows.length).toBe(freezePeriodsFixture.length);
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', () => {
});
Api.freezePeriods.mockResolvedValue({ data: freezePeriodsFixture });
Api.createFreezePeriod.mockResolvedValue();
Api.updateFreezePeriod.mockResolvedValue();
});
afterEach(() => {
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', () => {
it('commits SET_SELECTED_TIMEZONE mutation', () => {
testAction(actions.setSelectedTimezone, {}, {}, [
......@@ -68,10 +102,16 @@ describe('deploy freeze store actions', () => {
state,
[{ type: 'RESET_MODAL' }],
[
{ type: 'requestAddFreezePeriod' },
{ type: 'receiveAddFreezePeriodSuccess' },
{ type: 'requestFreezePeriod' },
{ type: 'receiveFreezePeriodSuccess' },
{ 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', () => {
{},
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(),
);
});
......
......@@ -33,7 +33,10 @@ describe('Deploy freeze mutations', () => {
const expectedFreezePeriods = freezePeriodsFixture.map((freezePeriod, index) => ({
...convertObjectPropsToCamelCase(freezePeriod),
cronTimezone: timezoneNames[index],
cronTimezone: {
formattedTimezone: timezoneNames[index],
identifier: freezePeriod.cronTimezone,
},
}));
expect(stateCopy.freezePeriods).toMatchObject(expectedFreezePeriods);
......@@ -62,11 +65,19 @@ describe('Deploy freeze mutations', () => {
});
});
describe('SET_FREEZE_ENDT_CRON', () => {
describe('SET_FREEZE_END_CRON', () => {
it('should set freezeEndCron', () => {
mutations[types.SET_FREEZE_END_CRON](stateCopy, '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