Commit 980bb468 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Minor review feedback

Fix capitalisation of Value Streams

Minor spec updates to make use of
components instead of data-testids
parent efd7dde9
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { sprintf, __ } from '~/locale'; import { sprintf, __ } from '~/locale';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { DATA_REFETCH_DELAY } from '../../shared/constants';
const ERRORS = { const ERRORS = {
MIN_LENGTH: __('Name is required'), MIN_LENGTH: __('Name is required'),
...@@ -67,10 +68,10 @@ export default { ...@@ -67,10 +68,10 @@ export default {
return Boolean(this.data.length); return Boolean(this.data.length);
}, },
selectedValueStreamName() { selectedValueStreamName() {
return this?.selectedValueStream?.name || ''; return this.selectedValueStream?.name || '';
}, },
selectedValueStreamId() { selectedValueStreamId() {
return this?.selectedValueStream?.id || null; return this.selectedValueStream?.id || null;
}, },
}, },
mounted() { mounted() {
...@@ -95,9 +96,9 @@ export default { ...@@ -95,9 +96,9 @@ export default {
onHandleInput: debounce(function debouncedValidation() { onHandleInput: debounce(function debouncedValidation() {
const { name } = this; const { name } = this;
this.errors = validate({ name }); this.errors = validate({ name });
}, 250), }, DATA_REFETCH_DELAY),
isSelected(id) { isSelected(id) {
return this.selectedValueStreamId && this.selectedValueStreamId === id; return Boolean(this.selectedValueStreamId && this.selectedValueStreamId === id);
}, },
onSelect(id) { onSelect(id) {
this.setSelectedValueStream(id); this.setSelectedValueStream(id);
...@@ -107,12 +108,7 @@ export default { ...@@ -107,12 +108,7 @@ export default {
</script> </script>
<template> <template>
<gl-form> <gl-form>
<gl-dropdown <gl-dropdown v-if="hasValueStreams" :text="selectedValueStreamName" right>
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"
...@@ -123,22 +119,18 @@ export default { ...@@ -123,22 +119,18 @@ export default {
> >
<gl-dropdown-divider /> <gl-dropdown-divider />
<gl-dropdown-item v-gl-modal-directive="'create-value-stream-modal'" @click="onHandleInput">{{ <gl-dropdown-item v-gl-modal-directive="'create-value-stream-modal'" @click="onHandleInput">{{
__('Create new value stream') __('Create new Value Stream')
}}</gl-dropdown-item> }}</gl-dropdown-item>
</gl-dropdown> </gl-dropdown>
<gl-button <gl-button v-else v-gl-modal-directive="'create-value-stream-modal'" @click="onHandleInput">{{
v-else __('Create new Value Stream')
v-gl-modal-directive="'create-value-stream-modal'" }}</gl-button>
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"
:title="__('Value Stream Name')" :title="__('Value Stream Name')"
:action-primary="{ :action-primary="{
text: __('Create value stream'), text: __('Create Value Stream'),
attributes: [ attributes: [
{ variant: 'success' }, { variant: 'success' },
{ {
...@@ -160,7 +152,7 @@ export default { ...@@ -160,7 +152,7 @@ export default {
id="create-value-stream-name" id="create-value-stream-name"
v-model.trim="name" v-model.trim="name"
name="create-value-stream-name" name="create-value-stream-name"
:placeholder="__('Example: My value stream')" :placeholder="__('Example: My Value Stream')"
:state="isValid" :state="isValid"
required required
@input="onHandleInput" @input="onHandleInput"
......
...@@ -1019,10 +1019,10 @@ RSpec.describe 'Group Value Stream Analytics', :js do ...@@ -1019,10 +1019,10 @@ RSpec.describe 'Group Value Stream Analytics', :js do
end end
it 'can create a value stream' do it 'can create a value stream' do
page.find_button(_('Create new value stream')).click page.find_button(_('Create new Value Stream')).click
fill_in 'create-value-stream-name', with: custom_value_stream_name fill_in 'create-value-stream-name', with: custom_value_stream_name
page.find_button(_('Create value stream')).click page.find_button(_('Create Value Stream')).click
expect(page).to have_text(_("'%{name}' Value Stream created") % { name: custom_value_stream_name }) expect(page).to have_text(_("'%{name}' Value Stream created") % { name: custom_value_stream_name })
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 { GlButton, GlModal, GlNewDropdown as GlDropdown } from '@gitlab/ui';
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 { valueStreams } from '../mock_data';
import { findDropdownItemText } from '../helpers'; import { findDropdownItemText } from '../helpers';
...@@ -49,10 +49,9 @@ describe('ValueStreamSelect', () => { ...@@ -49,10 +49,9 @@ 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 findSelectValueStreamDropdown = () => wrapper.find(GlDropdown);
const findSelectValueStreamDropdownOptions = _wrapper => findDropdownItemText(_wrapper); const findSelectValueStreamDropdownOptions = _wrapper => findDropdownItemText(_wrapper);
const findCreateValueStreamButton = () => wrapper.find(GlButton);
const findCreateValueStreamButton = () => wrapper.find('[data-testid="create-value-stream"]');
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
...@@ -66,18 +65,20 @@ describe('ValueStreamSelect', () => { ...@@ -66,18 +65,20 @@ describe('ValueStreamSelect', () => {
wrapper.destroy(); wrapper.destroy();
}); });
it('does not display the create value stream button', () => { describe('with value streams available', () => {
expect(findCreateValueStreamButton().exists()).toBe(false); it('does not display the create value stream button', () => {
}); expect(findCreateValueStreamButton().exists()).toBe(false);
});
it('displays the select value stream dropdown', () => { it('displays the select value stream dropdown', () => {
expect(findSelectValueStreamDropdown().exists()).toBe(true); expect(findSelectValueStreamDropdown().exists()).toBe(true);
}); });
it('renders each value stream including a create button', () => { it('renders each value stream including a create button', () => {
const opts = findSelectValueStreamDropdownOptions(wrapper); const opts = findSelectValueStreamDropdownOptions(wrapper);
[...valueStreams.map(v => v.name), 'Create new value stream'].forEach(vs => { [...valueStreams.map(v => v.name), 'Create new Value Stream'].forEach(vs => {
expect(opts).toContain(vs); expect(opts).toContain(vs);
});
}); });
}); });
......
...@@ -21,6 +21,7 @@ const error = new Error(`Request failed with status code ${httpStatusCodes.NOT_F ...@@ -21,6 +21,7 @@ const error = new Error(`Request failed with status code ${httpStatusCodes.NOT_F
const flashErrorMessage = 'There was an error while fetching value stream analytics data.'; const flashErrorMessage = 'There was an error while fetching value stream analytics data.';
const [selectedStage] = stages; const [selectedStage] = stages;
const selectedStageSlug = selectedStage.slug; const selectedStageSlug = selectedStage.slug;
const [selectedValueStream] = valueStreams;
const stageEndpoint = ({ stageId }) => const stageEndpoint = ({ stageId }) =>
`/groups/${selectedGroup.fullPath}/-/analytics/value_stream_analytics/stages/${stageId}`; `/groups/${selectedGroup.fullPath}/-/analytics/value_stream_analytics/stages/${stageId}`;
...@@ -135,7 +136,7 @@ describe('Cycle analytics actions', () => { ...@@ -135,7 +136,7 @@ describe('Cycle analytics actions', () => {
beforeEach(() => { beforeEach(() => {
state = { ...state, selectedGroup }; state = { ...state, selectedGroup };
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onGet(endpoints.stageData).reply(200, { events: [] }); mock.onGet(endpoints.stageData).reply(httpStatusCodes.OK, { events: [] });
}); });
it('dispatches receiveStageDataSuccess with received data on success', () => { it('dispatches receiveStageDataSuccess with received data on success', () => {
...@@ -419,7 +420,7 @@ describe('Cycle analytics actions', () => { ...@@ -419,7 +420,7 @@ describe('Cycle analytics actions', () => {
const payload = { hidden: true }; const payload = { hidden: true };
beforeEach(() => { beforeEach(() => {
mock.onPut(stageEndpoint({ stageId }), payload).replyOnce(200, payload); mock.onPut(stageEndpoint({ stageId }), payload).replyOnce(httpStatusCodes.OK, payload);
state = { selectedGroup }; state = { selectedGroup };
}); });
...@@ -565,7 +566,7 @@ describe('Cycle analytics actions', () => { ...@@ -565,7 +566,7 @@ describe('Cycle analytics actions', () => {
const stageId = 'cool-stage'; const stageId = 'cool-stage';
beforeEach(() => { beforeEach(() => {
mock.onDelete(stageEndpoint({ stageId })).replyOnce(200); mock.onDelete(stageEndpoint({ stageId })).replyOnce(httpStatusCodes.OK);
state = { selectedGroup }; state = { selectedGroup };
}); });
...@@ -617,7 +618,7 @@ describe('Cycle analytics actions', () => { ...@@ -617,7 +618,7 @@ describe('Cycle analytics actions', () => {
const stageId = 'cool-stage'; const stageId = 'cool-stage';
beforeEach(() => { beforeEach(() => {
mock.onDelete(stageEndpoint({ stageId })).replyOnce(200); mock.onDelete(stageEndpoint({ stageId })).replyOnce(httpStatusCodes.OK);
state = { selectedGroup }; state = { selectedGroup };
}); });
...@@ -650,7 +651,7 @@ describe('Cycle analytics actions', () => { ...@@ -650,7 +651,7 @@ describe('Cycle analytics actions', () => {
beforeEach(() => { beforeEach(() => {
state = { ...state, stages: [{ slug: selectedStageSlug }], selectedGroup }; state = { ...state, stages: [{ slug: selectedStageSlug }], selectedGroup };
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onGet(endpoints.stageMedian).reply(200, { events: [] }); mock.onGet(endpoints.stageMedian).reply(httpStatusCodes.OK, { events: [] });
mockDispatch = jest.fn(); mockDispatch = jest.fn();
}); });
...@@ -923,7 +924,7 @@ describe('Cycle analytics actions', () => { ...@@ -923,7 +924,7 @@ describe('Cycle analytics actions', () => {
}, },
}; };
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
mock.onGet(endpoints.valueStreamData).reply(200, { stages: [], events: [] }); mock.onGet(endpoints.valueStreamData).reply(httpStatusCodes.OK, { stages: [], events: [] });
}); });
it(`commits ${types.REQUEST_VALUE_STREAMS} and dispatches receiveValueStreamsSuccess with received data on success`, () => { it(`commits ${types.REQUEST_VALUE_STREAMS} and dispatches receiveValueStreamsSuccess with received data on success`, () => {
...@@ -978,7 +979,7 @@ describe('Cycle analytics actions', () => { ...@@ -978,7 +979,7 @@ describe('Cycle analytics actions', () => {
payload: valueStreams, payload: valueStreams,
}, },
], ],
[{ type: 'setSelectedValueStream', payload: 1 }], [{ type: 'setSelectedValueStream', payload: selectedValueStream.id }],
); );
}); });
}); });
......
...@@ -63,22 +63,34 @@ describe('Cycle analytics mutations', () => { ...@@ -63,22 +63,34 @@ 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 }} ${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${valueStreams} | ${{ valueStreams, isLoadingValueStreams: false }}
${types.SET_SELECTED_VALUE_STREAM} | ${valueStreams[1].id} | ${{ selectedValueStream: {} }}
`( `(
'$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 = { selectedGroup: { fullPath: 'rad-stage' } };
valueStreams,
selectedGroup: { fullPath: 'rad-stage' },
};
mutations[mutation](state, payload); mutations[mutation](state, payload);
expect(state).toMatchObject(expectedState); expect(state).toMatchObject(expectedState);
}, },
); );
describe('with value streams available', () => {
it.each`
mutation | payload | expectedState
${types.SET_SELECTED_VALUE_STREAM} | ${valueStreams[1].id} | ${{ selectedValueStream: valueStreams[1] }}
${types.SET_SELECTED_VALUE_STREAM} | ${'fake-id'} | ${{ selectedValueStream: {} }}
`(
'$mutation with payload $payload will update state with $expectedState',
({ mutation, payload, expectedState }) => {
state = { valueStreams };
mutations[mutation](state, payload);
expect(state).toMatchObject(expectedState);
},
);
});
describe(`${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS}`, () => { describe(`${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS}`, () => {
it('will set isLoading=false and errorCode=null', () => { it('will set isLoading=false and errorCode=null', () => {
mutations[types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS](state, { mutations[types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS](state, {
......
...@@ -339,7 +339,7 @@ describe('Api', () => { ...@@ -339,7 +339,7 @@ describe('Api', () => {
const response = [{ name: 'value stream 1', id: 1 }]; const response = [{ name: 'value stream 1', id: 1 }];
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`; const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`;
mock.onGet(expectedUrl).reply(200, response); mock.onGet(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsValueStreams(groupId) Api.cycleAnalyticsValueStreams(groupId)
.then(responseObj => .then(responseObj =>
...@@ -360,7 +360,7 @@ describe('Api', () => { ...@@ -360,7 +360,7 @@ describe('Api', () => {
name: 'cool-value-stream-stage', name: 'cool-value-stream-stage',
}; };
const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`; const expectedUrl = `${dummyCycleAnalyticsUrlRoot}/-/analytics/value_stream_analytics/value_streams`;
mock.onPost(expectedUrl).reply(200, response); mock.onPost(expectedUrl).reply(httpStatus.OK, response);
Api.cycleAnalyticsCreateValueStream(groupId, customValueStream) Api.cycleAnalyticsCreateValueStream(groupId, customValueStream)
.then(({ data, config: { data: reqData, url } }) => { .then(({ data, config: { data: reqData, url } }) => {
......
...@@ -6860,6 +6860,9 @@ msgstr "" ...@@ -6860,6 +6860,9 @@ msgstr ""
msgid "Create Project" msgid "Create Project"
msgstr "" msgstr ""
msgid "Create Value Stream"
msgstr ""
msgid "Create a GitLab account first, and then connect it to your %{label} account." msgid "Create a GitLab account first, and then connect it to your %{label} account."
msgstr "" msgstr ""
...@@ -6956,6 +6959,9 @@ msgstr "" ...@@ -6956,6 +6959,9 @@ msgstr ""
msgid "Create new" msgid "Create new"
msgstr "" msgstr ""
msgid "Create new Value Stream"
msgstr ""
msgid "Create new board" msgid "Create new board"
msgstr "" msgstr ""
...@@ -6977,9 +6983,6 @@ msgstr "" ...@@ -6977,9 +6983,6 @@ msgstr ""
msgid "Create new label" msgid "Create new label"
msgstr "" msgstr ""
msgid "Create new value stream"
msgstr ""
msgid "Create new..." msgid "Create new..."
msgstr "" msgstr ""
...@@ -6995,9 +6998,6 @@ msgstr "" ...@@ -6995,9 +6998,6 @@ msgstr ""
msgid "Create snippet" msgid "Create snippet"
msgstr "" msgstr ""
msgid "Create value stream"
msgstr ""
msgid "Create wildcard: %{searchTerm}" msgid "Create wildcard: %{searchTerm}"
msgstr "" msgstr ""
...@@ -9709,7 +9709,7 @@ msgstr "" ...@@ -9709,7 +9709,7 @@ msgstr ""
msgid "Example: @sub\\.company\\.com$" msgid "Example: @sub\\.company\\.com$"
msgstr "" msgstr ""
msgid "Example: My value stream" msgid "Example: My Value Stream"
msgstr "" msgstr ""
msgid "Example: Usage = single query. (Requested) / (Capacity) = multiple queries combined into a formula." msgid "Example: Usage = single query. (Requested) / (Capacity) = multiple queries combined into a formula."
......
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