Commit 00014f65 authored by Simon Knox's avatar Simon Knox Committed by Ezekiel Kigbo

Add burnup charts to Iterations report

Allow passing either milestone to iteration to query
parent 58827738
...@@ -35,6 +35,11 @@ export default { ...@@ -35,6 +35,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
iterationId: {
type: String,
required: false,
default: '',
},
burndownEventsPath: { burndownEventsPath: {
type: String, type: String,
required: false, required: false,
...@@ -49,16 +54,17 @@ export default { ...@@ -49,16 +54,17 @@ export default {
apollo: { apollo: {
burnupData: { burnupData: {
skip() { skip() {
return !this.glFeatures.burnupCharts || !this.milestoneId; return !this.glFeatures.burnupCharts || (!this.milestoneId && !this.iterationId);
}, },
query: BurnupQuery, query: BurnupQuery,
variables() { variables() {
return { return {
milestoneId: this.milestoneId, id: this.iterationId || this.milestoneId,
isIteration: Boolean(this.iterationId),
}; };
}, },
update(data) { update(data) {
const sparseBurnupData = data?.milestone?.burnupTimeSeries || []; const sparseBurnupData = data?.[this.parent]?.burnupTimeSeries || [];
return this.padSparseBurnupData(sparseBurnupData); return this.padSparseBurnupData(sparseBurnupData);
}, },
...@@ -79,6 +85,9 @@ export default { ...@@ -79,6 +85,9 @@ export default {
}; };
}, },
computed: { computed: {
parent() {
return this.iterationId ? 'iteration' : 'milestone';
},
title() { title() {
return this.glFeatures.burnupCharts ? __('Charts') : __('Burndown chart'); return this.glFeatures.burnupCharts ? __('Charts') : __('Burndown chart');
}, },
......
query IterationBurnupTimesSeriesData($milestoneId: MilestoneID!) { query IterationBurnupTimesSeriesData($id: ID!, $isIteration: Boolean = false) {
milestone(id: $milestoneId) { milestone(id: $id) @skip(if: $isIteration) {
title
id
burnupTimeSeries {
date
scopeCount
scopeWeight
completedCount
completedWeight
}
}
iteration(id: $id) @include(if: $isIteration) {
title title
id id
burnupTimeSeries { burnupTimeSeries {
......
...@@ -9,7 +9,9 @@ import { ...@@ -9,7 +9,9 @@ import {
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
} from '@gitlab/ui'; } from '@gitlab/ui';
import BurnCharts from 'ee/burndown_chart/components/burn_charts.vue';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __ } from '~/locale'; import { __ } from '~/locale';
import IterationReportSummary from './iteration_report_summary.vue'; import IterationReportSummary from './iteration_report_summary.vue';
import IterationForm from './iteration_form.vue'; import IterationForm from './iteration_form.vue';
...@@ -30,6 +32,7 @@ const page = { ...@@ -30,6 +32,7 @@ const page = {
export default { export default {
components: { components: {
BurnCharts,
GlAlert, GlAlert,
GlBadge, GlBadge,
GlLoadingIcon, GlLoadingIcon,
...@@ -61,6 +64,7 @@ export default { ...@@ -61,6 +64,7 @@ export default {
}, },
}, },
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
fullPath: { fullPath: {
type: String, type: String,
...@@ -215,6 +219,12 @@ export default { ...@@ -215,6 +219,12 @@ export default {
:iteration-id="iteration.id" :iteration-id="iteration.id"
:namespace-type="namespaceType" :namespace-type="namespaceType"
/> />
<burn-charts
v-if="glFeatures.burnupCharts"
:start-date="iteration.startDate"
:due-date="iteration.dueDate"
:iteration-id="iteration.id"
/>
<iteration-report-tabs <iteration-report-tabs
:full-path="fullPath" :full-path="fullPath"
:iteration-id="iteration.id" :iteration-id="iteration.id"
......
...@@ -4,6 +4,9 @@ class Groups::IterationsController < Groups::ApplicationController ...@@ -4,6 +4,9 @@ class Groups::IterationsController < Groups::ApplicationController
before_action :check_iterations_available! before_action :check_iterations_available!
before_action :authorize_show_iteration!, only: [:index, :show] before_action :authorize_show_iteration!, only: [:index, :show]
before_action :authorize_create_iteration!, only: [:new, :edit] before_action :authorize_create_iteration!, only: [:new, :edit]
before_action do
push_frontend_feature_flag(:burnup_charts, group)
end
feature_category :issue_tracking feature_category :issue_tracking
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
class Projects::Iterations::InheritedController < Projects::ApplicationController class Projects::Iterations::InheritedController < Projects::ApplicationController
before_action :check_iterations_available! before_action :check_iterations_available!
before_action :authorize_show_iteration! before_action :authorize_show_iteration!
before_action do
push_frontend_feature_flag(:burnup_charts, project)
end
feature_category :issue_tracking feature_category :issue_tracking
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
class Projects::IterationsController < Projects::ApplicationController class Projects::IterationsController < Projects::ApplicationController
before_action :check_iterations_available! before_action :check_iterations_available!
before_action :authorize_show_iteration! before_action :authorize_show_iteration!
before_action do
push_frontend_feature_flag(:burnup_charts, project)
end
feature_category :issue_tracking feature_category :issue_tracking
......
...@@ -10,7 +10,7 @@ RSpec.describe 'User views iteration' do ...@@ -10,7 +10,7 @@ RSpec.describe 'User views iteration' do
let_it_be(:sub_project) { create(:project, group: sub_group) } let_it_be(:sub_project) { create(:project, group: sub_group) }
let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group).user } let_it_be(:user) { create(:group_member, :maintainer, user: create(:user), group: group).user }
let_it_be(:guest_user) { create(:group_member, :guest, user: create(:user), group: group).user } let_it_be(:guest_user) { create(:group_member, :guest, user: create(:user), group: group).user }
let_it_be(:iteration) { create(:iteration, :skip_future_date_validation, iid: 1, id: 2, group: group, title: 'Correct Iteration', start_date: now - 1.day, due_date: now) } let_it_be(:iteration) { create(:iteration, :skip_future_date_validation, iid: 1, id: 2, group: group, title: 'Correct Iteration', description: 'iteration description', start_date: now - 1.day, due_date: now) }
let_it_be(:other_iteration) { create(:iteration, :skip_future_date_validation, iid: 2, id: 1, group: group, title: 'Wrong Iteration', start_date: now - 4.days, due_date: now - 3.days) } let_it_be(:other_iteration) { create(:iteration, :skip_future_date_validation, iid: 2, id: 1, group: group, title: 'Wrong Iteration', start_date: now - 4.days, due_date: now - 3.days) }
let_it_be(:sub_group_iteration) { create(:iteration, id: 3, group: sub_group) } let_it_be(:sub_group_iteration) { create(:iteration, id: 3, group: sub_group) }
let_it_be(:issue) { create(:issue, project: project, iteration: iteration) } let_it_be(:issue) { create(:issue, project: project, iteration: iteration) }
...@@ -20,60 +20,64 @@ RSpec.describe 'User views iteration' do ...@@ -20,60 +20,64 @@ RSpec.describe 'User views iteration' do
let_it_be(:other_issue) { create(:issue, project: project, iteration: other_iteration) } let_it_be(:other_issue) { create(:issue, project: project, iteration: other_iteration) }
context 'with license', :js do context 'with license', :js do
context 'view an iteration' do before do
stub_licensed_features(iterations: true)
end
shared_examples 'shows iteration info' do
before do before do
stub_licensed_features(iterations: true) sign_in(current_user)
sign_in(user)
visit group_iteration_path(iteration.group, iteration.iid) visit group_iteration_path(iteration.group, iteration.iid)
end end
it 'shows iteration info and dates' do it 'shows iteration info' do
expect(page).to have_content(iteration.title) aggregate_failures 'expect title, description, and dates' do
expect(page).to have_content(iteration.description) expect(page).to have_content(iteration.title)
expect(page).to have_content(iteration.start_date.strftime('%b %-d, %Y')) expect(page).to have_content(iteration.description)
expect(page).to have_content(iteration.due_date.strftime('%b %-d, %Y')) expect(page).to have_content(iteration.start_date.strftime('%b %-d, %Y'))
end expect(page).to have_content(iteration.due_date.strftime('%b %-d, %Y'))
end
it 'shows correct summary information' do
expect(page).to have_content("Complete 25%") aggregate_failures 'expect summary information' do
expect(page).to have_content("Open 2") expect(page).to have_content("Complete 25%")
expect(page).to have_content("In progress 1") expect(page).to have_content("Open 2")
expect(page).to have_content("Completed 1") expect(page).to have_content("In progress 1")
end expect(page).to have_content("Completed 1")
end
it 'shows all issues within the group' do
expect(page).to have_content(issue.title) aggregate_failures 'expect burnup and burndown charts' do
expect(page).to have_content(assigned_issue.title) expect(page).to have_content('Burndown chart')
expect(page).to have_content(closed_issue.title) expect(page).to have_content('Burnup chart')
expect(page).to have_content(sub_group_issue.title) end
expect(page).not_to have_content(other_issue.title)
aggregate_failures 'expect list of assigned issues' do
expect(page).to have_content(issue.title)
expect(page).to have_content(assigned_issue.title)
expect(page).to have_content(closed_issue.title)
expect(page).to have_content(sub_group_issue.title)
expect(page).not_to have_content(other_issue.title)
end
if shows_actions
expect(page).to have_button('Actions')
else
expect(page).not_to have_button('Actions')
end
end end
end end
context 'when user has edit permissions' do context 'when user has edit permissions' do
before do it_behaves_like 'shows iteration info' do
stub_licensed_features(iterations: true) let(:current_user) { user }
sign_in(user) let(:shows_actions) { true }
visit group_iteration_path(iteration.group, iteration.iid)
end
it 'shows action dropdown for editing the iteration' do
expect(page).to have_button('Actions')
end end
end end
context 'when user does not have edit permissions' do context 'when user does not have edit permissions' do
before do it_behaves_like 'shows iteration info' do
stub_licensed_features(iterations: true) let(:current_user) { guest_user }
sign_in(guest_user) let(:shows_actions) { false }
visit group_iteration_path(iteration.group, iteration.iid)
end
it 'hides action dropdown for editing the iteration' do
expect(page).not_to have_button('Actions')
end end
end end
end end
......
...@@ -26,29 +26,36 @@ RSpec.describe 'User views iteration' do ...@@ -26,29 +26,36 @@ RSpec.describe 'User views iteration' do
visit project_iterations_inherited_path(project, iteration.id) visit project_iterations_inherited_path(project, iteration.id)
end end
it 'shows iteration info and dates' do it 'shows iteration info' do
expect(page).to have_content(iteration.title) aggregate_failures 'shows iteration info and dates' do
expect(page).to have_content(iteration.description) expect(page).to have_content(iteration.title)
expect(page).to have_content(iteration.start_date.strftime('%b %-d, %Y')) expect(page).to have_content(iteration.description)
expect(page).to have_content(iteration.due_date.strftime('%b %-d, %Y')) expect(page).to have_content(iteration.start_date.strftime('%b %-d, %Y'))
end expect(page).to have_content(iteration.due_date.strftime('%b %-d, %Y'))
end
it 'shows correct summary information' do aggregate_failures 'shows correct summary information' do
expect(page).to have_content("Complete 50%") expect(page).to have_content("Complete 50%")
expect(page).to have_content("Open 1") expect(page).to have_content("Open 1")
expect(page).to have_content("In progress 0") expect(page).to have_content("In progress 0")
expect(page).to have_content("Completed 1") expect(page).to have_content("Completed 1")
end end
it 'shows only issues that are part of the project' do aggregate_failures 'expect burnup and burndown charts' do
expect(page).to have_content(issue.title) expect(page).to have_content('Burndown chart')
expect(page).not_to have_content(assigned_issue.title) expect(page).to have_content('Burnup chart')
expect(page).to have_content(closed_issue.title) end
expect(page).not_to have_content(other_issue.title)
end aggregate_failures 'shows only issues that are part of the project' do
expect(page).to have_content(issue.title)
expect(page).not_to have_content(assigned_issue.title)
expect(page).to have_content(closed_issue.title)
expect(page).not_to have_content(other_issue.title)
end
it 'hides action dropdown for editing the iteration' do aggregate_failures 'hides action dropdown for editing the iteration' do
expect(page).not_to have_button('Actions') expect(page).not_to have_button('Actions')
end
end end
end end
end end
......
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