Commit 7260ed2b authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Add action specs

Adds related vuex specs to cover the
actions, mutations and getters

Added mutation specs

Added loading state key for the value
streams api request

Clean up specs
parent 625d2a1d
...@@ -79,6 +79,7 @@ export default { ...@@ -79,6 +79,7 @@ export default {
'startDate', 'startDate',
'endDate', 'endDate',
'medians', 'medians',
'isLoadingValueStreams',
]), ]),
// NOTE: formEvents are fetched in the same request as the list of stages (fetchGroupStagesAndEvents) // NOTE: formEvents are fetched in the same request as the list of stages (fetchGroupStagesAndEvents)
// so i think its ok to bind formEvents here even though its only used as a prop to the custom-stage-form // so i think its ok to bind formEvents here even though its only used as a prop to the custom-stage-form
...@@ -114,7 +115,9 @@ export default { ...@@ -114,7 +115,9 @@ export default {
return this.featureFlags.hasFilterBar && this.currentGroupPath; return this.featureFlags.hasFilterBar && this.currentGroupPath;
}, },
shouldDisplayCreateMultipleValueStreams() { shouldDisplayCreateMultipleValueStreams() {
return Boolean(this.featureFlags.hasCreateMultipleValueStreams); return Boolean(
this.featureFlags.hasCreateMultipleValueStreams && !this.isLoadingValueStreams,
);
}, },
isLoadingTypeOfWork() { isLoadingTypeOfWork() {
return this.isLoadingTasksByTypeChartTopLabels || this.isLoadingTasksByTypeChart; return this.isLoadingTasksByTypeChartTopLabels || this.isLoadingTasksByTypeChart;
......
...@@ -108,7 +108,12 @@ export default { ...@@ -108,7 +108,12 @@ export default {
</script> </script>
<template> <template>
<gl-form> <gl-form>
<gl-dropdown v-if="hasValueStreams" :text="selectedValueStreamName" right> <gl-dropdown
v-if="hasValueStreams"
data-testid="select-value-stream"
:text="selectedValueStreamName"
right
>
<gl-dropdown-item <gl-dropdown-item
v-for="{ id, name: streamName } in data" v-for="{ id, name: streamName } in data"
:key="id" :key="id"
...@@ -122,9 +127,13 @@ export default { ...@@ -122,9 +127,13 @@ export default {
__('Create new value stream') __('Create new value stream')
}}</gl-dropdown-item> }}</gl-dropdown-item>
</gl-dropdown> </gl-dropdown>
<gl-button v-else v-gl-modal-directive="'create-value-stream-modal'" @click="onHandleInput">{{ <gl-button
__('Create new value stream') v-else
}}</gl-button> v-gl-modal-directive="'create-value-stream-modal'"
data-testid="create-value-stream"
@click="onHandleInput"
>{{ __('Create new value stream') }}</gl-button
>
<gl-modal <gl-modal
ref="modal" ref="modal"
modal-id="create-value-stream-modal" modal-id="create-value-stream-modal"
......
...@@ -109,10 +109,6 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => { ...@@ -109,10 +109,6 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => {
removeFlash(); removeFlash();
dispatch('requestCycleAnalyticsData'); dispatch('requestCycleAnalyticsData');
// TODO: we will need to do some extra refactoring here
// We need the selected value stream to be selected first
// once we have that, we can then fetch the groups stages and events,
// this is because they will now live under the /value_streams entity
return Promise.resolve() return Promise.resolve()
.then(() => dispatch('fetchValueStreams')) .then(() => dispatch('fetchValueStreams'))
...@@ -302,7 +298,12 @@ export const reorderStage = ({ dispatch, state }, initialData) => { ...@@ -302,7 +298,12 @@ export const reorderStage = ({ dispatch, state }, initialData) => {
); );
}; };
export const createValueStream = ({ commit, rootState }, data) => { export const receiveCreateValueStreamSuccess = ({ commit, dispatch }) => {
commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS);
return dispatch('fetchValueStreams');
};
export const createValueStream = ({ commit, dispatch, rootState }, data) => {
const { const {
selectedGroup: { fullPath }, selectedGroup: { fullPath },
} = rootState; } = rootState;
...@@ -310,10 +311,7 @@ export const createValueStream = ({ commit, rootState }, data) => { ...@@ -310,10 +311,7 @@ export const createValueStream = ({ commit, rootState }, data) => {
commit(types.REQUEST_CREATE_VALUE_STREAM); commit(types.REQUEST_CREATE_VALUE_STREAM);
return Api.cycleAnalyticsCreateValueStream(fullPath, data) return Api.cycleAnalyticsCreateValueStream(fullPath, data)
.then(response => { .then(() => dispatch('receiveCreateValueStreamSuccess'))
const { status, data: responseData } = response;
commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, { status, data: responseData });
})
.catch(({ response } = {}) => { .catch(({ response } = {}) => {
const { data: { message, errors } = null } = response; const { data: { message, errors } = null } = response;
commit(types.RECEIVE_CREATE_VALUE_STREAM_ERROR, { data, message, errors }); commit(types.RECEIVE_CREATE_VALUE_STREAM_ERROR, { data, message, errors });
...@@ -323,13 +321,16 @@ export const createValueStream = ({ commit, rootState }, data) => { ...@@ -323,13 +321,16 @@ export const createValueStream = ({ commit, rootState }, data) => {
export const setSelectedValueStream = ({ commit }, streamId) => export const setSelectedValueStream = ({ commit }, streamId) =>
commit(types.SET_SELECTED_VALUE_STREAM, streamId); commit(types.SET_SELECTED_VALUE_STREAM, streamId);
export const receiveValueStreams = ({ commit, dispatch }, { data }) => { export const receiveValueStreamsSuccess = ({ commit, dispatch }, data = []) => {
commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data); commit(types.RECEIVE_VALUE_STREAMS_SUCCESS, data);
if (data.length) {
const [firstStream] = data; const [firstStream] = data;
return dispatch('setSelectedValueStream', firstStream.id); return dispatch('setSelectedValueStream', firstStream.id);
}
return Promise.resolve();
}; };
export const fetchValueStreams = ({ commit, dispatch, getters, state }, data) => { export const fetchValueStreams = ({ commit, dispatch, getters, state }) => {
const { const {
featureFlags: { hasCreateMultipleValueStreams = false }, featureFlags: { hasCreateMultipleValueStreams = false },
} = state; } = state;
...@@ -339,13 +340,10 @@ export const fetchValueStreams = ({ commit, dispatch, getters, state }, data) => ...@@ -339,13 +340,10 @@ export const fetchValueStreams = ({ commit, dispatch, getters, state }, data) =>
commit(types.REQUEST_VALUE_STREAMS); commit(types.REQUEST_VALUE_STREAMS);
return Api.cycleAnalyticsValueStreams(currentGroupPath) return Api.cycleAnalyticsValueStreams(currentGroupPath)
.then(response => { .then(({ data }) => dispatch('receiveValueStreamsSuccess', data))
const { status, data: responseData } = response; .catch(response => {
return dispatch('receiveValueStreams', { status, data: responseData }); const { data } = response;
}) commit(types.RECEIVE_VALUE_STREAMS_ERROR, data);
.catch(({ response } = {}) => {
const { data: { message, errors } = null } = response;
commit(types.RECEIVE_VALUE_STREAMS_ERROR, { data, message, errors });
}); });
} }
return Promise.resolve(); return Promise.resolve();
......
...@@ -135,15 +135,18 @@ export default { ...@@ -135,15 +135,18 @@ export default {
state.createValueStreamErrors = {}; state.createValueStreamErrors = {};
}, },
[types.SET_SELECTED_VALUE_STREAM](state, streamId) { [types.SET_SELECTED_VALUE_STREAM](state, streamId) {
state.selectedValueStream = state.valueStreams.find(({ id }) => id === streamId); state.selectedValueStream = state.valueStreams?.find(({ id }) => id === streamId) || null;
}, },
[types.REQUEST_VALUE_STREAMS](state) { [types.REQUEST_VALUE_STREAMS](state) {
state.isLoadingValueStreams = true;
state.valueStreams = []; state.valueStreams = [];
}, },
[types.RECEIVE_VALUE_STREAMS_ERROR](state) { [types.RECEIVE_VALUE_STREAMS_ERROR](state) {
state.isLoadingValueStreams = false;
state.valueStreams = []; state.valueStreams = [];
}, },
[types.RECEIVE_VALUE_STREAMS_SUCCESS](state, data) { [types.RECEIVE_VALUE_STREAMS_SUCCESS](state, data) {
state.isLoadingValueStreams = false;
state.valueStreams = data; state.valueStreams = data;
}, },
}; };
...@@ -24,6 +24,7 @@ export default () => ({ ...@@ -24,6 +24,7 @@ export default () => ({
currentStageEvents: [], currentStageEvents: [],
isLoadingValueStreams: false,
isCreatingValueStream: false, isCreatingValueStream: false,
createValueStreamErrors: {}, createValueStreamErrors: {},
......
...@@ -143,14 +143,6 @@ export default { ...@@ -143,14 +143,6 @@ export default {
return axios.get(url, { params }); return axios.get(url, { params });
}, },
cycleAnalyticsValueStreamGroupStagesAndEvents(groupId, valueStreamId, params = {}) {
const url = Api.buildUrl(this.cycleAnalyticsValueStreamGroupStagesAndEventsPath)
.replace(':id', groupId)
.replace(':value_stream_id', valueStreamId);
return axios.get(url, { params });
},
cycleAnalyticsStageEvents(groupId, stageId, params = {}) { cycleAnalyticsStageEvents(groupId, stageId, params = {}) {
const url = Api.buildUrl(this.cycleAnalyticsStageEventsPath) const url = Api.buildUrl(this.cycleAnalyticsStageEventsPath)
.replace(':id', groupId) .replace(':id', groupId)
......
...@@ -4,6 +4,8 @@ module Analytics ...@@ -4,6 +4,8 @@ module Analytics
module CycleAnalytics module CycleAnalytics
module Stages module Stages
class ListService < BaseService class ListService < BaseService
extend ::Gitlab::Utils::Override
def execute def execute
return forbidden unless can?(current_user, :read_group_cycle_analytics, parent) return forbidden unless can?(current_user, :read_group_cycle_analytics, parent)
...@@ -21,6 +23,11 @@ module Analytics ...@@ -21,6 +23,11 @@ module Analytics
scope = scope.by_value_stream(params[:value_stream]) if params[:value_stream] scope = scope.by_value_stream(params[:value_stream]) if params[:value_stream]
scope.for_list scope.for_list
end end
override :value_stream
def value_stream
@value_stream ||= (params[:value_stream] || parent.value_streams.new(name: DEFAULT_VALUE_STREAM_NAME))
end
end end
end end
end end
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import store from 'ee/analytics/cycle_analytics/store';
import ValueStreamSelect from 'ee/analytics/cycle_analytics/components/value_stream_select.vue'; import ValueStreamSelect from 'ee/analytics/cycle_analytics/components/value_stream_select.vue';
import { valueStreams } from '../mock_data';
import { findDropdownItemText } from '../helpers';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -15,10 +16,21 @@ describe('ValueStreamSelect', () => { ...@@ -15,10 +16,21 @@ describe('ValueStreamSelect', () => {
const mockModalHide = jest.fn(); const mockModalHide = jest.fn();
const mockToastShow = jest.fn(); const mockToastShow = jest.fn();
const createComponent = ({ data = {}, methods = {} } = {}) => const fakeStore = ({ initialState = {} }) =>
new Vuex.Store({
state: {
isLoading: false,
createValueStreamErrors: {},
valueStreams: [],
selectedValueStream: {},
...initialState,
},
});
const createComponent = ({ data = {}, initialState = {}, methods = {} } = {}) =>
shallowMount(ValueStreamSelect, { shallowMount(ValueStreamSelect, {
localVue, localVue,
store, store: fakeStore({ initialState }),
data() { data() {
return { return {
...data, ...data,
...@@ -38,15 +50,56 @@ describe('ValueStreamSelect', () => { ...@@ -38,15 +50,56 @@ describe('ValueStreamSelect', () => {
const findModal = () => wrapper.find(GlModal); const findModal = () => wrapper.find(GlModal);
const submitButtonDisabledState = () => findModal().props('actionPrimary').attributes[1].disabled; const submitButtonDisabledState = () => findModal().props('actionPrimary').attributes[1].disabled;
const submitForm = () => findModal().vm.$emit('primary', mockEvent); const submitForm = () => findModal().vm.$emit('primary', mockEvent);
const findSelectValueStreamDropdown = () => wrapper.find('[data-testid="select-value-stream"]');
const findSelectValueStreamDropdownOptions = _wrapper => findDropdownItemText(_wrapper);
const findCreateValueStreamButton = () => wrapper.find('[data-testid="create-value-stream"]');
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent({
initialState: {
valueStreams,
},
});
}); });
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
it('does not display the create value stream button', () => {
expect(findCreateValueStreamButton().exists()).toBe(false);
});
it('displays the select value stream dropdown', () => {
expect(findSelectValueStreamDropdown().exists()).toBe(true);
});
it('renders each value stream including a create button', () => {
const opts = findSelectValueStreamDropdownOptions(wrapper);
[...valueStreams.map(v => v.name), 'Create new value stream'].forEach(vs => {
expect(opts).toContain(vs);
});
});
describe('No value streams available', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: {
valueStreams: [],
},
});
});
it('displays the create value stream button', () => {
expect(findCreateValueStreamButton().exists()).toBe(true);
});
it('does not display the select value stream dropdown', () => {
expect(findSelectValueStreamDropdown().exists()).toBe(false);
});
});
describe('Create value stream form', () => { describe('Create value stream form', () => {
it('submit button is disabled', () => { it('submit button is disabled', () => {
expect(submitButtonDisabledState()).toBe(true); expect(submitButtonDisabledState()).toBe(true);
...@@ -68,6 +121,7 @@ describe('ValueStreamSelect', () => { ...@@ -68,6 +121,7 @@ describe('ValueStreamSelect', () => {
beforeEach(() => { beforeEach(() => {
submitForm(); submitForm();
}); });
it('calls the "createValueStream" event when submitted', () => { it('calls the "createValueStream" event when submitted', () => {
expect(createValueStreamMock).toHaveBeenCalledWith({ name: streamName }); expect(createValueStreamMock).toHaveBeenCalledWith({ name: streamName });
}); });
...@@ -93,8 +147,8 @@ describe('ValueStreamSelect', () => { ...@@ -93,8 +147,8 @@ describe('ValueStreamSelect', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
data: { name: streamName }, data: { name: streamName },
methods: { actions: {
createValueStream: createValueStreamMockFail, createValueStream: () => createValueStreamMockFail,
}, },
}); });
wrapper.vm.$refs.modal.hide = mockModalHide; wrapper.vm.$refs.modal.hide = mockModalHide;
......
import { GlNewDropdownItem as GlDropdownItem } from '@gitlab/ui';
export function renderTotalTime(selector, element, totalTime = {}) { export function renderTotalTime(selector, element, totalTime = {}) {
const { days, hours, mins, seconds } = totalTime; const { days, hours, mins, seconds } = totalTime;
if (days) { if (days) {
...@@ -17,7 +19,14 @@ export function renderTotalTime(selector, element, totalTime = {}) { ...@@ -17,7 +19,14 @@ export function renderTotalTime(selector, element, totalTime = {}) {
export const shouldFlashAMessage = (msg = '') => export const shouldFlashAMessage = (msg = '') =>
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg); expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg);
export const findDropdownItems = wrapper => wrapper.findAll(GlDropdownItem);
export const findDropdownItemText = wrapper =>
findDropdownItems(wrapper).wrappers.map(w => w.text());
export default { export default {
renderTotalTime, renderTotalTime,
shouldFlashAMessage, shouldFlashAMessage,
findDropdownItems,
findDropdownItemText,
}; };
...@@ -38,6 +38,8 @@ export const endpoints = { ...@@ -38,6 +38,8 @@ export const endpoints = {
valueStreamData: /analytics\/value_stream_analytics\/value_streams/, valueStreamData: /analytics\/value_stream_analytics\/value_streams/,
}; };
export const valueStreams = [{ id: 1, name: 'Value stream 1' }, { id: 2, name: 'Value stream 2' }];
export const groupLabels = getJSONFixture(fixtureEndpoints.groupLabels).map( export const groupLabels = getJSONFixture(fixtureEndpoints.groupLabels).map(
convertObjectPropsToCamelCase, convertObjectPropsToCamelCase,
); );
......
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
endDate, endDate,
customizableStagesAndEvents, customizableStagesAndEvents,
endpoints, endpoints,
valueStreams,
} from '../mock_data'; } from '../mock_data';
const stageData = { events: [] }; const stageData = { events: [] };
...@@ -58,6 +59,7 @@ describe('Cycle analytics actions', () => { ...@@ -58,6 +59,7 @@ describe('Cycle analytics actions', () => {
${'setFeatureFlags'} | ${'SET_FEATURE_FLAGS'} | ${'featureFlags'} | ${{ hasDurationChart: true }} ${'setFeatureFlags'} | ${'SET_FEATURE_FLAGS'} | ${'featureFlags'} | ${{ hasDurationChart: true }}
${'setSelectedProjects'} | ${'SET_SELECTED_PROJECTS'} | ${'selectedProjectIds'} | ${[10, 20, 30, 40]} ${'setSelectedProjects'} | ${'SET_SELECTED_PROJECTS'} | ${'selectedProjectIds'} | ${[10, 20, 30, 40]}
${'setSelectedStage'} | ${'SET_SELECTED_STAGE'} | ${'selectedStage'} | ${{ id: 'someStageId' }} ${'setSelectedStage'} | ${'SET_SELECTED_STAGE'} | ${'selectedStage'} | ${{ id: 'someStageId' }}
${'setSelectedValueStream'} | ${'SET_SELECTED_VALUE_STREAM'} | ${'selectedValueStream'} | ${{ id: 'vs-1', name: 'Value stream 1' }}
`('$action should set $stateKey with $payload and type $type', ({ action, type, payload }) => { `('$action should set $stateKey with $payload and type $type', ({ action, type, payload }) => {
return testAction( return testAction(
actions[action], actions[action],
...@@ -876,13 +878,11 @@ describe('Cycle analytics actions', () => { ...@@ -876,13 +878,11 @@ describe('Cycle analytics actions', () => {
payload, payload,
state, state,
[ [
{ type: types.REQUEST_CREATE_VALUE_STREAM },
{ {
type: types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, type: types.REQUEST_CREATE_VALUE_STREAM,
payload: { status: httpStatusCodes.OK, data: {} },
}, },
], ],
[], [{ type: 'receiveCreateValueStreamSuccess' }],
); );
}); });
}); });
...@@ -910,4 +910,92 @@ describe('Cycle analytics actions', () => { ...@@ -910,4 +910,92 @@ describe('Cycle analytics actions', () => {
}); });
}); });
}); });
describe('fetchValueStreams', () => {
beforeEach(() => {
state = {
...state,
stages: [{ slug: selectedStageSlug }],
selectedGroup,
featureFlags: {
...state.featureFlags,
hasCreateMultipleValueStreams: true,
},
};
mock = new MockAdapter(axios);
mock.onGet(endpoints.valueStreamData).reply(200, { stages: [], events: [] });
});
it(`commits ${types.REQUEST_VALUE_STREAMS} and dispatches receiveValueStreamsSuccess with received data on success`, () => {
return testAction(
actions.fetchValueStreams,
null,
state,
[{ type: types.REQUEST_VALUE_STREAMS }],
[
{
payload: {
events: [],
stages: [],
},
type: 'receiveValueStreamsSuccess',
},
],
);
});
describe('with a failing request', () => {
const resp = { data: {} };
beforeEach(() => {
mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.NOT_FOUND, resp);
});
it(`will commit ${types.RECEIVE_VALUE_STREAMS_ERROR}`, () => {
return testAction(
actions.fetchValueStreams,
null,
state,
[
{ type: types.REQUEST_VALUE_STREAMS },
{
type: types.RECEIVE_VALUE_STREAMS_ERROR,
},
],
[],
);
});
});
describe('receiveValueStreamsSuccess', () => {
it(`commits the ${types.RECEIVE_VALUE_STREAMS_SUCCESS} mutation`, () => {
return testAction(
actions.receiveValueStreamsSuccess,
valueStreams,
state,
[
{
type: types.RECEIVE_VALUE_STREAMS_SUCCESS,
payload: valueStreams,
},
],
[{ type: 'setSelectedValueStream', payload: 1 }],
);
});
});
describe('with hasCreateMultipleValueStreams disabled', () => {
beforeEach(() => {
state = {
...state,
featureFlags: {
...state.featureFlags,
hasCreateMultipleValueStreams: false,
},
};
});
it(`will skip making a request`, () =>
testAction(actions.fetchValueStreams, null, state, [], []));
});
});
}); });
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
endDate, endDate,
selectedProjects, selectedProjects,
customizableStagesAndEvents, customizableStagesAndEvents,
valueStreams,
} from '../mock_data'; } from '../mock_data';
let state = null; let state = null;
...@@ -27,6 +28,10 @@ describe('Cycle analytics mutations', () => { ...@@ -27,6 +28,10 @@ describe('Cycle analytics mutations', () => {
it.each` it.each`
mutation | stateKey | value mutation | stateKey | value
${types.REQUEST_VALUE_STREAMS} | ${'valueStreams'} | ${[]}
${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'valueStreams'} | ${[]}
${types.REQUEST_VALUE_STREAMS} | ${'isLoadingValueStreams'} | ${true}
${types.RECEIVE_VALUE_STREAMS_ERROR} | ${'isLoadingValueStreams'} | ${false}
${types.REQUEST_STAGE_DATA} | ${'isLoadingStage'} | ${true} ${types.REQUEST_STAGE_DATA} | ${'isLoadingStage'} | ${true}
${types.RECEIVE_STAGE_DATA_ERROR} | ${'isEmptyStage'} | ${true} ${types.RECEIVE_STAGE_DATA_ERROR} | ${'isEmptyStage'} | ${true}
${types.RECEIVE_STAGE_DATA_ERROR} | ${'isLoadingStage'} | ${false} ${types.RECEIVE_STAGE_DATA_ERROR} | ${'isLoadingStage'} | ${false}
...@@ -58,11 +63,14 @@ describe('Cycle analytics mutations', () => { ...@@ -58,11 +63,14 @@ describe('Cycle analytics mutations', () => {
${types.SET_SELECTED_PROJECTS} | ${selectedProjects} | ${{ selectedProjects }} ${types.SET_SELECTED_PROJECTS} | ${selectedProjects} | ${{ selectedProjects }}
${types.SET_DATE_RANGE} | ${{ startDate, endDate }} | ${{ startDate, endDate }} ${types.SET_DATE_RANGE} | ${{ startDate, endDate }} | ${{ startDate, endDate }}
${types.SET_SELECTED_STAGE} | ${{ id: 'first-stage' }} | ${{ selectedStage: { id: 'first-stage' } }} ${types.SET_SELECTED_STAGE} | ${{ id: 'first-stage' }} | ${{ selectedStage: { id: 'first-stage' } }}
${types.SET_SELECTED_VALUE_STREAM} | ${valueStreams[1].id} | ${{ selectedValueStream: valueStreams[1] }}
${types.RECEIVE_CREATE_VALUE_STREAM_ERROR} | ${{ name: ['is required'] }} | ${{ createValueStreamErrors: { name: ['is required'] }, isCreatingValueStream: false }} ${types.RECEIVE_CREATE_VALUE_STREAM_ERROR} | ${{ name: ['is required'] }} | ${{ createValueStreamErrors: { name: ['is required'] }, isCreatingValueStream: false }}
${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${valueStreams} | ${{ valueStreams, isLoadingValueStreams: false }}
`( `(
'$mutation with payload $payload will update state with $expectedState', '$mutation with payload $payload will update state with $expectedState',
({ mutation, payload, expectedState }) => { ({ mutation, payload, expectedState }) => {
state = { state = {
valueStreams,
selectedGroup: { fullPath: 'rad-stage' }, selectedGroup: { fullPath: 'rad-stage' },
}; };
mutations[mutation](state, payload); mutations[mutation](state, payload);
......
...@@ -334,6 +334,45 @@ describe('Api', () => { ...@@ -334,6 +334,45 @@ describe('Api', () => {
}); });
}); });
describe('cycleAnalyticsValueStreams', () => {
it('fetches custom value streams', done => {
const response = [{ name: 'value stream 1', id: 1 }];
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`;
mock.onGet(expectedUrl).reply(200, response);
Api.cycleAnalyticsValueStreams(groupId)
.then(responseObj =>
expectRequestWithCorrectParameters(responseObj, {
response,
expectedUrl,
}),
)
.then(done)
.catch(done.fail);
});
});
describe('cycleAnalyticsCreateValueStream', () => {
it('submit the custom value stream data', done => {
const response = {};
const customValueStream = {
name: 'cool-value-stream-stage',
};
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`;
mock.onPost(expectedUrl).reply(200, response);
Api.cycleAnalyticsCreateValueStream(groupId, customValueStream)
.then(({ data, config: { data: reqData, url } }) => {
expect(data).toEqual(response);
expect(JSON.parse(reqData)).toMatchObject(customValueStream);
expect(url).toEqual(expectedUrl);
})
.then(done)
.catch(done.fail);
});
});
describe('cycleAnalyticsGroupStagesAndEvents', () => { describe('cycleAnalyticsGroupStagesAndEvents', () => {
it('fetches custom stage events and all stages', done => { it('fetches custom stage events and all stages', done => {
const response = { events: [], stages: [] }; const response = { events: [], stages: [] };
......
...@@ -28,6 +28,10 @@ RSpec.describe Analytics::CycleAnalytics::Stages::ListService do ...@@ -28,6 +28,10 @@ RSpec.describe Analytics::CycleAnalytics::Stages::ListService do
expect(stages.map(&:id)).to all(be_nil) expect(stages.map(&:id)).to all(be_nil)
end end
it 'does not persist the value stream record' do
expect { subject }.not_to change { Analytics::CycleAnalytics::GroupValueStream.count }
end
context 'when there are persisted stages' do context 'when there are persisted stages' do
let_it_be(:stage1) { create(:cycle_analytics_group_stage, parent: group, relative_position: 2, value_stream: value_stream) } let_it_be(:stage1) { create(:cycle_analytics_group_stage, parent: group, relative_position: 2, value_stream: value_stream) }
let_it_be(:stage2) { create(:cycle_analytics_group_stage, parent: group, relative_position: 3, value_stream: value_stream) } let_it_be(:stage2) { create(:cycle_analytics_group_stage, parent: group, relative_position: 3, value_stream: value_stream) }
......
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