Commit de25a9ed authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Add aggregating warning component

Introduces a alert message for when a newly
created value stream is aggregating data.
parent 5fc82e3e
...@@ -291,6 +291,9 @@ To create a value stream: ...@@ -291,6 +291,9 @@ To create a value stream:
![New value stream](img/new_value_stream_v13_12.png "Creating a new value stream") ![New value stream](img/new_value_stream_v13_12.png "Creating a new value stream")
NOTE:
If you have recently upgraded to GitLab Premium, it can take up to 30 minutes for data to collect and display.
### Create a value stream with stages ### Create a value stream with stages
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50229) in GitLab 13.7. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50229) in GitLab 13.7.
......
...@@ -15,6 +15,7 @@ import { METRICS_REQUESTS } from '../constants'; ...@@ -15,6 +15,7 @@ import { METRICS_REQUESTS } from '../constants';
import DurationChart from './duration_chart.vue'; import DurationChart from './duration_chart.vue';
import TypeOfWorkCharts from './type_of_work_charts.vue'; import TypeOfWorkCharts from './type_of_work_charts.vue';
import ValueStreamAggregationStatus from './value_stream_aggregation_status.vue'; import ValueStreamAggregationStatus from './value_stream_aggregation_status.vue';
import ValueStreamAggregatingWarning from './value_stream_aggregating_warning.vue';
import ValueStreamEmptyState from './value_stream_empty_state.vue'; import ValueStreamEmptyState from './value_stream_empty_state.vue';
import ValueStreamSelect from './value_stream_select.vue'; import ValueStreamSelect from './value_stream_select.vue';
...@@ -27,6 +28,7 @@ export default { ...@@ -27,6 +28,7 @@ export default {
StageTable, StageTable,
PathNavigation, PathNavigation,
ValueStreamAggregationStatus, ValueStreamAggregationStatus,
ValueStreamAggregatingWarning,
ValueStreamEmptyState, ValueStreamEmptyState,
ValueStreamFilters, ValueStreamFilters,
ValueStreamMetrics, ValueStreamMetrics,
...@@ -66,6 +68,7 @@ export default { ...@@ -66,6 +68,7 @@ export default {
'pagination', 'pagination',
'aggregation', 'aggregation',
'isUpdatingAggregation', 'isUpdatingAggregation',
'isCreatingAggregation',
]), ]),
...mapGetters([ ...mapGetters([
'hasNoAccessError', 'hasNoAccessError',
...@@ -78,8 +81,16 @@ export default { ...@@ -78,8 +81,16 @@ export default {
'selectedStageCount', 'selectedStageCount',
'hasValueStreams', 'hasValueStreams',
]), ]),
isWaitingForNextAggregation() {
return Boolean(
this.selectedValueStream && this.isAggregationEnabled && !this.aggregation.lastRunAt,
);
},
shouldRenderEmptyState() { shouldRenderEmptyState() {
return this.isLoadingValueStreams || !this.hasValueStreams; return this.isLoadingValueStreams || (!this.isCreatingAggregation && !this.hasValueStreams);
},
shouldRenderAggregationWarning() {
return this.isCreatingAggregation || this.isWaitingForNextAggregation;
}, },
shouldDisplayFilters() { shouldDisplayFilters() {
return !this.errorCode && !this.hasNoAccessError; return !this.errorCode && !this.hasNoAccessError;
...@@ -88,7 +99,9 @@ export default { ...@@ -88,7 +99,9 @@ export default {
return !this.hasNoAccessError && this.selectedStage; return !this.hasNoAccessError && this.selectedStage;
}, },
shouldDisplayCreateMultipleValueStreams() { shouldDisplayCreateMultipleValueStreams() {
return Boolean(!this.shouldRenderEmptyState && !this.isLoadingValueStreams); return Boolean(
!this.shouldRenderEmptyState && !this.isLoadingValueStreams && !this.isCreatingAggregation,
);
}, },
hasDateRangeSet() { hasDateRangeSet() {
return this.createdAfter && this.createdBefore; return this.createdAfter && this.createdBefore;
...@@ -102,6 +115,9 @@ export default { ...@@ -102,6 +115,9 @@ export default {
isAggregationStatusAvailable() { isAggregationStatusAvailable() {
return this.isAggregationEnabled && this.aggregation.lastRunAt; return this.isAggregationEnabled && this.aggregation.lastRunAt;
}, },
selectedValueStreamName() {
return this.selectedValueStream?.name;
},
query() { query() {
const { project_ids, created_after, created_before } = this.cycleAnalyticsRequestParams; const { project_ids, created_after, created_before } = this.cycleAnalyticsRequestParams;
const paginationUrlParams = !this.isOverviewStageSelected const paginationUrlParams = !this.isOverviewStageSelected
...@@ -160,6 +176,9 @@ export default { ...@@ -160,6 +176,9 @@ export default {
onHandleUpdatePagination(data) { onHandleUpdatePagination(data) {
this.updateStageTablePagination(data); this.updateStageTablePagination(data);
}, },
onHandleReloadPage() {
refreshCurrentPage();
},
onToggleAggregation(value) { onToggleAggregation(value) {
this.updateAggregation(value) this.updateAggregation(value)
.then(() => { .then(() => {
...@@ -229,6 +248,13 @@ export default { ...@@ -229,6 +248,13 @@ export default {
:selected-stage="selectedStage" :selected-stage="selectedStage"
@selected="onStageSelect" @selected="onStageSelect"
/> />
<value-stream-aggregating-warning
v-if="shouldRenderAggregationWarning"
class="gl-my-6"
:value-stream-title="selectedValueStreamName"
@reload="onHandleReloadPage"
/>
<template v-else>
<value-stream-filters <value-stream-filters
:group-id="currentGroup.id" :group-id="currentGroup.id"
:group-path="currentGroupPath" :group-path="currentGroupPath"
...@@ -261,7 +287,11 @@ export default { ...@@ -261,7 +287,11 @@ export default {
:request-params="cycleAnalyticsRequestParams" :request-params="cycleAnalyticsRequestParams"
:requests="$options.METRICS_REQUESTS" :requests="$options.METRICS_REQUESTS"
/> />
<duration-chart class="gl-mt-3" :stages="activeStages" :selected-stage="selectedStage" /> <duration-chart
class="gl-mt-3"
:stages="activeStages"
:selected-stage="selectedStage"
/>
<type-of-work-charts v-if="isOverviewStageSelected" /> <type-of-work-charts v-if="isOverviewStageSelected" />
<stage-table <stage-table
v-if="!isOverviewStageSelected" v-if="!isOverviewStageSelected"
...@@ -279,6 +309,7 @@ export default { ...@@ -279,6 +309,7 @@ export default {
</div> </div>
<url-sync v-if="selectedStageReady" :query="query" /> <url-sync v-if="selectedStageReady" :query="query" />
</template> </template>
</template>
</div> </div>
</div> </div>
</template> </template>
<script>
import { GlAlert } from '@gitlab/ui';
import { sprintf } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import {
AGGREGATING_DATA_WARNING_TITLE,
AGGREGATING_DATA_WARNING_MESSAGE,
AGGREGATING_DATA_WARNING_NEXT_UPDATE,
AGGREGATING_DATA_PRIMARY_ACTION_TEXT,
AGGREGATING_DATA_SECONDARY_ACTION_TEXT,
} from '../constants';
export default {
name: 'ValueStreamAggregatingWarning',
components: {
GlAlert,
},
props: {
valueStreamTitle: {
type: String,
required: true,
},
},
computed: {
message() {
return sprintf(AGGREGATING_DATA_WARNING_MESSAGE, { name: this.valueStreamTitle });
},
},
i18n: {
title: AGGREGATING_DATA_WARNING_TITLE,
nextUpdate: AGGREGATING_DATA_WARNING_NEXT_UPDATE,
primaryText: AGGREGATING_DATA_PRIMARY_ACTION_TEXT,
secondaryText: AGGREGATING_DATA_SECONDARY_ACTION_TEXT,
},
docsPath: helpPagePath('user/group/value_stream_analytics', {
anchor: 'create-a-value-stream',
}),
};
</script>
<template>
<gl-alert
:title="$options.i18n.title"
:dismissible="false"
:primary-button-text="$options.i18n.primaryText"
:secondary-button-text="$options.i18n.secondaryText"
:secondary-button-link="$options.docsPath"
@primaryAction="$emit('reload')"
>
<p>{{ message }}</p>
<p>{{ $options.i18n.nextUpdate }}</p>
</gl-alert>
</template>
...@@ -70,3 +70,13 @@ export const EMPTY_STATE_FILTER_ERROR_TITLE = __( ...@@ -70,3 +70,13 @@ export const EMPTY_STATE_FILTER_ERROR_TITLE = __(
export const EMPTY_STATE_FILTER_ERROR_DESCRIPTION = __( export const EMPTY_STATE_FILTER_ERROR_DESCRIPTION = __(
'Filter parameters are not valid. Make sure that the end date is after the start date.', 'Filter parameters are not valid. Make sure that the end date is after the start date.',
); );
export const AGGREGATING_DATA_WARNING_TITLE = s__('CycleAnalytics|Data is collecting and loading.');
export const AGGREGATING_DATA_WARNING_MESSAGE = s__(
"CycleAnalytics|'%{name}' is collecting the data. This can take a few minutes.",
);
export const AGGREGATING_DATA_WARNING_NEXT_UPDATE = s__(
'CycleAnalytics|If you have recently upgraded to GitLab Premium, it can take up to 30 minutes for data to collect and display.',
);
export const AGGREGATING_DATA_PRIMARY_ACTION_TEXT = __('Reload page');
export const AGGREGATING_DATA_SECONDARY_ACTION_TEXT = __('Learn more');
...@@ -38,6 +38,9 @@ export default () => { ...@@ -38,6 +38,9 @@ export default () => {
selectedAssigneeList, selectedAssigneeList,
selectedLabelList, selectedLabelList,
pagination, pagination,
featureFlags: {
useVsaAggregatedTables: gon.features.useVsaAggregatedTables,
},
}); });
return new Vue({ return new Vue({
......
...@@ -14,9 +14,14 @@ export const updateAggregation = ({ commit, getters }, status) => { ...@@ -14,9 +14,14 @@ export const updateAggregation = ({ commit, getters }, status) => {
}); });
}; };
export const receiveCreateValueStreamSuccess = ({ commit, dispatch }, valueStream = {}) => { export const receiveCreateValueStreamSuccess = (
{ commit, dispatch, state: { featureFlags } },
valueStream = {},
) => {
commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, valueStream); commit(types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, valueStream);
return dispatch('fetchCycleAnalyticsData'); return featureFlags.useVsaAggregatedTables
? commit(types.SET_CREATING_AGGREGATION, true)
: dispatch('fetchCycleAnalyticsData');
}; };
export const createValueStream = ({ commit, dispatch, getters }, data) => { export const createValueStream = ({ commit, dispatch, getters }, data) => {
......
...@@ -6,6 +6,7 @@ export const SET_DATE_RANGE = 'SET_DATE_RANGE'; ...@@ -6,6 +6,7 @@ export const SET_DATE_RANGE = 'SET_DATE_RANGE';
export const SET_SELECTED_VALUE_STREAM = 'SET_SELECTED_VALUE_STREAM'; export const SET_SELECTED_VALUE_STREAM = 'SET_SELECTED_VALUE_STREAM';
export const SET_PAGINATION = 'SET_PAGINATION'; export const SET_PAGINATION = 'SET_PAGINATION';
export const SET_STAGE_EVENTS = 'SET_STAGE_EVENTS'; export const SET_STAGE_EVENTS = 'SET_STAGE_EVENTS';
export const SET_CREATING_AGGREGATION = 'SET_CREATING_AGGREGATION';
export const REQUEST_VALUE_STREAM_DATA = 'REQUEST_VALUE_STREAM_DATA'; export const REQUEST_VALUE_STREAM_DATA = 'REQUEST_VALUE_STREAM_DATA';
export const RECEIVE_VALUE_STREAM_DATA_SUCCESS = 'RECEIVE_VALUE_STREAM_DATA_SUCCESS'; export const RECEIVE_VALUE_STREAM_DATA_SUCCESS = 'RECEIVE_VALUE_STREAM_DATA_SUCCESS';
......
...@@ -140,10 +140,13 @@ export default { ...@@ -140,10 +140,13 @@ export default {
state.createValueStreamErrors = { ...rest, stages: prepareStageErrors(stages, stageErrors) }; state.createValueStreamErrors = { ...rest, stages: prepareStageErrors(stages, stageErrors) };
state.isCreatingValueStream = false; state.isCreatingValueStream = false;
}, },
[types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS](state, valueStream) { [types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS](state, valueStream = {}) {
state.isCreatingValueStream = false; state.isCreatingValueStream = false;
state.createValueStreamErrors = {}; state.createValueStreamErrors = {};
state.selectedValueStream = convertObjectPropsToCamelCase(valueStream, { deep: true }); state.selectedValueStream = convertObjectPropsToCamelCase(valueStream, { deep: true });
const { stages = [] } = valueStream;
state.stages = transformRawStages(stages);
}, },
[types.REQUEST_UPDATE_VALUE_STREAM](state) { [types.REQUEST_UPDATE_VALUE_STREAM](state) {
state.isEditingValueStream = true; state.isEditingValueStream = true;
...@@ -209,4 +212,7 @@ export default { ...@@ -209,4 +212,7 @@ export default {
[types.RECEIVE_UPDATE_AGGREGATION_ERROR](state) { [types.RECEIVE_UPDATE_AGGREGATION_ERROR](state) {
state.isUpdatingAggregation = false; state.isUpdatingAggregation = false;
}, },
[types.SET_CREATING_AGGREGATION](state, value) {
state.isCreatingAggregation = value;
},
}; };
...@@ -29,6 +29,7 @@ export default () => ({ ...@@ -29,6 +29,7 @@ export default () => ({
isDeletingValueStream: false, isDeletingValueStream: false,
isFetchingGroupLabels: false, isFetchingGroupLabels: false,
isUpdatingAggregation: false, isUpdatingAggregation: false,
isCreatingAggregation: false,
createValueStreamErrors: {}, createValueStreamErrors: {},
deleteValueStreamError: null, deleteValueStreamError: null,
......
...@@ -44,8 +44,7 @@ RSpec.describe 'Value stream analytics charts', :js do ...@@ -44,8 +44,7 @@ RSpec.describe 'Value stream analytics charts', :js do
context 'Duration chart' do context 'Duration chart' do
before do before do
select_group(group) select_group_and_custom_value_stream(group, custom_value_stream_name)
select_value_stream(custom_value_stream_name)
end end
it 'displays data for all stages on the overview' do it 'displays data for all stages on the overview' do
...@@ -87,8 +86,7 @@ RSpec.describe 'Value stream analytics charts', :js do ...@@ -87,8 +86,7 @@ RSpec.describe 'Value stream analytics charts', :js do
create(:labeled_issue, created_at: i.days.ago, project: create(:project, group: group), labels: [group_label2]) create(:labeled_issue, created_at: i.days.ago, project: create(:project, group: group), labels: [group_label2])
end end
select_group(group) select_group_and_custom_value_stream(group, custom_value_stream_name)
select_value_stream(custom_value_stream_name)
end end
it 'displays the chart' do it 'displays the chart' do
...@@ -125,7 +123,7 @@ RSpec.describe 'Value stream analytics charts', :js do ...@@ -125,7 +123,7 @@ RSpec.describe 'Value stream analytics charts', :js do
context 'no data available' do context 'no data available' do
before do before do
select_group(group) select_group_and_custom_value_stream(group, custom_value_stream_name)
end end
it 'shows the no data available message' do it 'shows the no data available message' do
......
...@@ -12,6 +12,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -12,6 +12,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
let_it_be(:sub_group_project) { create(:project, :repository, namespace: group, group: sub_group, name: 'Cool sub group project') } let_it_be(:sub_group_project) { create(:project, :repository, namespace: group, group: sub_group, name: 'Cool sub group project') }
let_it_be(:group_label1) { create(:group_label, group: group) } let_it_be(:group_label1) { create(:group_label, group: group) }
let_it_be(:group_label2) { create(:group_label, group: group) } let_it_be(:group_label2) { create(:group_label, group: group) }
let_it_be(:custom_value_stream_name) { "First custom value stream" }
let(:milestone) { create(:milestone, project: project) } let(:milestone) { create(:milestone, project: project) }
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") } let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
...@@ -191,10 +192,15 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -191,10 +192,15 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
end end
let_it_be(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:value_stream) { create(:cycle_analytics_group_value_stream, group: group, name: 'First value stream', stages: vsa_stages(group)) } let_it_be(:value_stream) { create(:cycle_analytics_group_value_stream, group: group, name: custom_value_stream_name, stages: vsa_stages(group)) }
let_it_be(:subgroup_value_stream) { create(:cycle_analytics_group_value_stream, group: sub_group, name: 'First subgroup value stream', stages: vsa_stages(sub_group)) } let_it_be(:subgroup_value_stream) { create(:cycle_analytics_group_value_stream, group: sub_group, name: 'First subgroup value stream', stages: vsa_stages(sub_group)) }
context 'without valid query parameters set' do context 'without valid query parameters set' do
before do
create_value_stream_group_aggregation(group)
end
context 'with created_after date > created_before date' do context 'with created_after date > created_before date' do
before do before do
visit "#{group_analytics_cycle_analytics_path(group)}?created_after=2019-12-31&created_before=2019-11-01" visit "#{group_analytics_cycle_analytics_path(group)}?created_after=2019-12-31&created_before=2019-11-01"
...@@ -207,7 +213,8 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -207,7 +213,8 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
before do before do
visit "#{group_analytics_cycle_analytics_path(group)}?beans=not-cool" visit "#{group_analytics_cycle_analytics_path(group)}?beans=not-cool"
select_value_stream('First value stream') select_value_stream(custom_value_stream_name)
select_stage("Issue") select_stage("Issue")
end end
...@@ -218,6 +225,10 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -218,6 +225,10 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
context 'with valid query parameters set' do context 'with valid query parameters set' do
projects_dropdown = '.js-projects-dropdown-filter' projects_dropdown = '.js-projects-dropdown-filter'
before do
create_value_stream_group_aggregation(group)
end
context 'with project_ids set' do context 'with project_ids set' do
before do before do
visit "#{group_analytics_cycle_analytics_path(group)}?project_ids[]=#{project.id}" visit "#{group_analytics_cycle_analytics_path(group)}?project_ids[]=#{project.id}"
...@@ -250,8 +261,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -250,8 +261,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
let(:selected_group) { group } let(:selected_group) { group }
before do before do
select_group(group) select_group_and_custom_value_stream(group, custom_value_stream_name)
select_value_stream('First value stream')
end end
it_behaves_like 'group value stream analytics' it_behaves_like 'group value stream analytics'
...@@ -265,8 +275,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -265,8 +275,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
let(:selected_group) { sub_group } let(:selected_group) { sub_group }
before do before do
select_group(sub_group) select_group_and_custom_value_stream(sub_group, 'First subgroup value stream')
select_value_stream('First subgroup value stream')
end end
it_behaves_like 'group value stream analytics' it_behaves_like 'group value stream analytics'
...@@ -293,11 +302,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do ...@@ -293,11 +302,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
deploy_master(user, project, environment: 'staging') deploy_master(user, project, environment: 'staging')
deploy_master(user, project) deploy_master(user, project)
aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_group(group) select_group_and_custom_value_stream(group, custom_value_stream_name)
Analytics::CycleAnalytics::AggregatorService.new(aggregation: aggregation).execute
select_group(group)
select_value_stream('First value stream')
end end
stages_with_data = [ stages_with_data = [
......
...@@ -30,13 +30,6 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -30,13 +30,6 @@ RSpec.describe 'Multiple value streams', :js do
sign_in(user) sign_in(user)
end end
def select_value_stream(value_stream_name)
toggle_value_stream_dropdown
page.find('[data-testid="dropdown-value-streams"]').all('li button').find { |item| item.text == value_stream_name.to_s }.click
wait_for_requests
end
def path_nav_elem def path_nav_elem
page.find('[data-testid="vsa-path-navigation"]') page.find('[data-testid="vsa-path-navigation"]')
end end
...@@ -45,6 +38,19 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -45,6 +38,19 @@ RSpec.describe 'Multiple value streams', :js do
page.find("[data-testid='stage-action-#{action}-#{index}']").click page.find("[data-testid='stage-action-#{action}-#{index}']").click
end end
def reload_value_stream
click_button 'Reload page'
end
def create_and_select_value_stream(name, with_aggregation = true)
create_custom_value_stream(name)
return unless with_aggregation
reload_value_stream
select_value_stream(name)
end
shared_examples 'create a value stream' do |custom_value_stream_name| shared_examples 'create a value stream' do |custom_value_stream_name|
before do before do
toggle_value_stream_dropdown toggle_value_stream_dropdown
...@@ -86,11 +92,11 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -86,11 +92,11 @@ RSpec.describe 'Multiple value streams', :js do
end end
end end
shared_examples 'update a value stream' do |custom_value_stream_name| shared_examples 'update a value stream' do |custom_value_stream_name, with_aggregation|
before do before do
select_group(group) select_group(group)
create_custom_value_stream(custom_value_stream_name) create_and_select_value_stream(custom_value_stream_name, with_aggregation)
end end
it 'can reorder stages' do it 'can reorder stages' do
...@@ -198,7 +204,7 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -198,7 +204,7 @@ RSpec.describe 'Multiple value streams', :js do
end end
end end
shared_examples 'create group value streams' do shared_examples 'create group value streams' do |with_aggregation|
name = 'group value stream' name = 'group value stream'
before do before do
...@@ -206,11 +212,11 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -206,11 +212,11 @@ RSpec.describe 'Multiple value streams', :js do
end end
it_behaves_like 'create a value stream', name it_behaves_like 'create a value stream', name
it_behaves_like 'update a value stream', name it_behaves_like 'update a value stream', name, with_aggregation
it_behaves_like 'delete a value stream', name it_behaves_like 'delete a value stream', name
end end
shared_examples 'create sub group value streams' do shared_examples 'create sub group value streams' do |with_aggregation|
name = 'sub group value stream' name = 'sub group value stream'
before do before do
...@@ -218,7 +224,7 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -218,7 +224,7 @@ RSpec.describe 'Multiple value streams', :js do
end end
it_behaves_like 'create a value stream', name it_behaves_like 'create a value stream', name
it_behaves_like 'update a value stream', name it_behaves_like 'update a value stream', name, with_aggregation
it_behaves_like 'delete a value stream', name it_behaves_like 'delete a value stream', name
end end
...@@ -227,8 +233,8 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -227,8 +233,8 @@ RSpec.describe 'Multiple value streams', :js do
stub_feature_flags(use_vsa_aggregated_tables: false) stub_feature_flags(use_vsa_aggregated_tables: false)
end end
it_behaves_like 'create group value streams' it_behaves_like 'create group value streams', false
it_behaves_like 'create sub group value streams' it_behaves_like 'create sub group value streams', false
end end
context 'use_vsa_aggregated_tables feature flag on' do context 'use_vsa_aggregated_tables feature flag on' do
...@@ -249,14 +255,39 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -249,14 +255,39 @@ RSpec.describe 'Multiple value streams', :js do
end end
context 'with a value stream' do context 'with a value stream' do
context 'without an aggregation created' do
before do before do
create(:cycle_analytics_group_value_stream, group: group, name: 'default')
select_group(group)
end
it 'renders the aggregating status banner' do
expect(page).to have_text(s_('CycleAnalytics|Data is collecting and loading.'))
end
it 'displays the value stream once an aggregation is run' do
create_value_stream_group_aggregation(group)
reload_value_stream
expect(page).not_to have_button(_('Reload page'))
expect(page).to have_text('Last updated less than a minute ago')
end
end
context 'with an aggregation created' do
before do
create_value_stream_group_aggregation(group)
create_value_stream_group_aggregation(sub_group)
# ensure we have a value stream already available # ensure we have a value stream already available
create(:cycle_analytics_group_value_stream, group: group, name: 'default') create(:cycle_analytics_group_value_stream, group: group, name: 'default')
create(:cycle_analytics_group_value_stream, group: sub_group, name: 'default') create(:cycle_analytics_group_value_stream, group: sub_group, name: 'default')
end end
it_behaves_like 'create group value streams' it_behaves_like 'create group value streams', true
it_behaves_like 'create sub group value streams' it_behaves_like 'create sub group value streams', true
end
end end
end end
end end
...@@ -37,7 +37,9 @@ describe('Value Stream Analytics actions / value streams', () => { ...@@ -37,7 +37,9 @@ describe('Value Stream Analytics actions / value streams', () => {
beforeEach(() => { beforeEach(() => {
state = { state = {
stages: [], stages: [],
featureFlags: {}, featureFlags: {
useVsaAggregatedTables: true,
},
activeStages, activeStages,
selectedValueStream, selectedValueStream,
...mockGetters, ...mockGetters,
...@@ -50,6 +52,35 @@ describe('Value Stream Analytics actions / value streams', () => { ...@@ -50,6 +52,35 @@ describe('Value Stream Analytics actions / value streams', () => {
state = { ...state, currentGroup: null }; state = { ...state, currentGroup: null };
}); });
describe('useVsaAggregatedTables = false', () => {
beforeEach(() => {
state = {
...state,
featureFlags: {
useVsaAggregatedTables: false,
},
};
});
describe('receiveCreateValueStreamSuccess', () => {
beforeEach(() => {
state = { ...state, valueStream: {} };
});
it(`will dispatch the "fetchCycleAnalyticsData" action and commit the ${types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS} mutation`, () => {
return testAction({
action: actions.receiveCreateValueStreamSuccess,
payload: selectedValueStream,
state,
expectedMutations: [
{ type: types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, payload: selectedValueStream },
],
expectedActions: [{ type: 'fetchCycleAnalyticsData' }],
});
});
});
});
describe('setSelectedValueStream', () => { describe('setSelectedValueStream', () => {
const vs = { id: 'vs-1', name: 'Value stream 1' }; const vs = { id: 'vs-1', name: 'Value stream 1' };
...@@ -141,8 +172,8 @@ describe('Value Stream Analytics actions / value streams', () => { ...@@ -141,8 +172,8 @@ describe('Value Stream Analytics actions / value streams', () => {
state, state,
expectedMutations: [ expectedMutations: [
{ type: types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, payload: selectedValueStream }, { type: types.RECEIVE_CREATE_VALUE_STREAM_SUCCESS, payload: selectedValueStream },
{ type: types.SET_CREATING_AGGREGATION, payload: true },
], ],
expectedActions: [{ type: 'fetchCycleAnalyticsData' }],
}); });
}); });
}); });
......
...@@ -11152,6 +11152,9 @@ msgstr "" ...@@ -11152,6 +11152,9 @@ msgstr ""
msgid "CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)" msgid "CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)"
msgstr "" msgstr ""
msgid "CycleAnalytics|'%{name}' is collecting the data. This can take a few minutes."
msgstr ""
msgid "CycleAnalytics|Aggregation disabled" msgid "CycleAnalytics|Aggregation disabled"
msgstr "" msgstr ""
...@@ -11170,6 +11173,9 @@ msgstr "" ...@@ -11170,6 +11173,9 @@ msgstr ""
msgid "CycleAnalytics|Custom value streams to measure your DevSecOps lifecycle" msgid "CycleAnalytics|Custom value streams to measure your DevSecOps lifecycle"
msgstr "" msgstr ""
msgid "CycleAnalytics|Data is collecting and loading."
msgstr ""
msgid "CycleAnalytics|Date" msgid "CycleAnalytics|Date"
msgstr "" msgstr ""
...@@ -11179,6 +11185,9 @@ msgstr "" ...@@ -11179,6 +11185,9 @@ msgstr ""
msgid "CycleAnalytics|Filter by stop date" msgid "CycleAnalytics|Filter by stop date"
msgstr "" msgstr ""
msgid "CycleAnalytics|If you have recently upgraded to GitLab Premium, it can take up to 30 minutes for data to collect and display."
msgstr ""
msgid "CycleAnalytics|Lead Time for Changes" msgid "CycleAnalytics|Lead Time for Changes"
msgstr "" msgstr ""
......
...@@ -93,6 +93,18 @@ module CycleAnalyticsHelpers ...@@ -93,6 +93,18 @@ module CycleAnalyticsHelpers
wait_for_requests wait_for_requests
end end
def create_value_stream_group_aggregation(group)
aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_group(group)
Analytics::CycleAnalytics::AggregatorService.new(aggregation: aggregation).execute
end
def select_group_and_custom_value_stream(group, custom_value_stream_name)
create_value_stream_group_aggregation(group)
select_group(group)
select_value_stream(custom_value_stream_name)
end
def toggle_dropdown(field) def toggle_dropdown(field)
page.within("[data-testid*='#{field}']") do page.within("[data-testid*='#{field}']") do
find('.dropdown-toggle').click find('.dropdown-toggle').click
......
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