Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
88d762ca
Commit
88d762ca
authored
Sep 14, 2020
by
Simon Knox
Committed by
Miguel Rincon
Sep 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use burnup graphQL endpoint for milestone charts
Requires padding data as endpoint only returns changes
parent
ffea1ee9
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
370 additions
and
58 deletions
+370
-58
app/assets/javascripts/lib/utils/datetime_utility.js
app/assets/javascripts/lib/utils/datetime_utility.js
+10
-0
ee/app/assets/javascripts/burndown_chart/components/burn_charts.vue
...ets/javascripts/burndown_chart/components/burn_charts.vue
+109
-5
ee/app/assets/javascripts/burndown_chart/components/burndown_chart.vue
.../javascripts/burndown_chart/components/burndown_chart.vue
+6
-4
ee/app/assets/javascripts/burndown_chart/components/burnup_chart.vue
...ts/javascripts/burndown_chart/components/burnup_chart.vue
+49
-9
ee/app/assets/javascripts/burndown_chart/index.js
ee/app/assets/javascripts/burndown_chart/index.js
+13
-17
ee/app/assets/javascripts/burndown_chart/queries/burnup.query.graphql
...s/javascripts/burndown_chart/queries/burnup.query.graphql
+13
-0
ee/app/views/shared/milestones/_burndown.html.haml
ee/app/views/shared/milestones/_burndown.html.haml
+1
-1
ee/spec/frontend/burndown_chart/components/burn_charts_spec.js
...ec/frontend/burndown_chart/components/burn_charts_spec.js
+96
-12
ee/spec/frontend/burndown_chart/components/burnup_chart_spec.js
...c/frontend/burndown_chart/components/burnup_chart_spec.js
+21
-7
ee/spec/frontend/burndown_chart/mock_data.js
ee/spec/frontend/burndown_chart/mock_data.js
+31
-0
locale/gitlab.pot
locale/gitlab.pot
+21
-3
No files found.
app/assets/javascripts/lib/utils/datetime_utility.js
View file @
88d762ca
...
@@ -642,6 +642,16 @@ export const secondsToMilliseconds = seconds => seconds * 1000;
...
@@ -642,6 +642,16 @@ export const secondsToMilliseconds = seconds => seconds * 1000;
*/
*/
export
const
secondsToDays
=
seconds
=>
Math
.
round
(
seconds
/
86400
);
export
const
secondsToDays
=
seconds
=>
Math
.
round
(
seconds
/
86400
);
/**
* Returns the date n days after the date provided
*
* @param {Date} date the initial date
* @param {Number} numberOfDays number of days after
* @return {Date} the date following the date provided
*/
export
const
nDaysAfter
=
(
date
,
numberOfDays
)
=>
new
Date
(
newDate
(
date
)).
setDate
(
date
.
getDate
()
+
numberOfDays
);
/**
/**
* Returns the date after the date provided
* Returns the date after the date provided
*
*
...
...
ee/app/assets/javascripts/burndown_chart/components/burn_charts.vue
View file @
88d762ca
<
script
>
<
script
>
import
{
GlButton
,
GlButtonGroup
}
from
'
@gitlab/ui
'
;
import
{
GlAlert
,
GlButton
,
GlButtonGroup
}
from
'
@gitlab/ui
'
;
import
dateFormat
from
'
dateformat
'
;
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
getDayDifference
,
nDaysAfter
,
newDateAsLocaleTime
}
from
'
~/lib/utils/datetime_utility
'
;
import
BurndownChart
from
'
./burndown_chart.vue
'
;
import
BurndownChart
from
'
./burndown_chart.vue
'
;
import
BurnupChart
from
'
./burnup_chart.vue
'
;
import
BurnupChart
from
'
./burnup_chart.vue
'
;
import
BurnupQuery
from
'
../queries/burnup.query.graphql
'
;
export
default
{
export
default
{
components
:
{
components
:
{
GlAlert
,
GlButton
,
GlButton
,
GlButtonGroup
,
GlButtonGroup
,
BurndownChart
,
BurndownChart
,
...
@@ -32,15 +36,38 @@ export default {
...
@@ -32,15 +36,38 @@ export default {
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
[],
},
},
burnupScope
:
{
milestoneId
:
{
type
:
Array
,
type
:
String
,
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
''
,
},
},
apollo
:
{
burnupData
:
{
skip
()
{
return
!
this
.
glFeatures
.
burnupCharts
||
!
this
.
milestoneId
;
},
query
:
BurnupQuery
,
variables
()
{
return
{
milestoneId
:
this
.
milestoneId
,
};
},
update
(
data
)
{
const
sparseBurnupData
=
data
?.
milestone
?.
burnupTimeSeries
||
[];
return
this
.
padSparseBurnupData
(
sparseBurnupData
);
},
error
()
{
this
.
error
=
__
(
'
Error fetching burnup chart data
'
);
},
},
},
},
},
data
()
{
data
()
{
return
{
return
{
issuesSelected
:
true
,
issuesSelected
:
true
,
burnupData
:
[],
error
:
''
,
};
};
},
},
computed
:
{
computed
:
{
...
@@ -58,6 +85,79 @@ export default {
...
@@ -58,6 +85,79 @@ export default {
setIssueSelected
(
selected
)
{
setIssueSelected
(
selected
)
{
this
.
issuesSelected
=
selected
;
this
.
issuesSelected
=
selected
;
},
},
padSparseBurnupData
(
sparseBurnupData
)
{
// if we don't have data for the startDate, we still want to draw a point at 0
// on the chart, so add an item to the start of the array
const
hasDataForStartDate
=
sparseBurnupData
.
find
(
d
=>
d
.
date
===
this
.
startDate
);
if
(
!
hasDataForStartDate
)
{
sparseBurnupData
.
unshift
({
date
:
this
.
startDate
,
completedCount
:
0
,
completedWeight
:
0
,
scopeCount
:
0
,
scopeWeight
:
0
,
});
}
// chart runs to dueDate or the current date, whichever is earlier
const
lastDate
=
dateFormat
(
Math
.
min
(
Date
.
parse
(
this
.
dueDate
),
Date
.
parse
(
new
Date
())),
'
yyyy-mm-dd
'
,
);
// similar to the startDate padding, if we don't have a value for the
// last item in the array, we should add one. If no events occur on
// a day then we don't get any data for that day in the response
const
hasDataForLastDate
=
sparseBurnupData
.
find
(
d
=>
d
.
date
===
lastDate
);
if
(
!
hasDataForLastDate
)
{
const
lastItem
=
sparseBurnupData
[
sparseBurnupData
.
length
-
1
];
sparseBurnupData
.
push
({
...
lastItem
,
date
:
lastDate
,
});
}
return
sparseBurnupData
.
reduce
(
this
.
addMissingDates
,
[]);
},
addMissingDates
(
acc
,
current
)
{
const
{
date
}
=
current
;
// we might not have data for every day in the timebox, as graphql
// endpoint only returns days when events have happened
// if the previous array item is >1 day, then fill in the gap
// using the data from the previous entry.
// example: [
// { date: '2020-08-01', count: 10 }
// { date: '2020-08-04', count: 12 }
// ]
// should be transformed to
// example: [
// { date: '2020-08-01', count: 10 }
// { date: '2020-08-02', count: 10 }
// { date: '2020-08-03', count: 10 }
// { date: '2020-08-04', count: 12 }
// ]
// skip the start date since we have no previous values
if
(
date
!==
this
.
startDate
)
{
const
{
date
:
prevDate
,
...
previousValues
}
=
acc
[
acc
.
length
-
1
]
||
{};
const
currentDateUTC
=
newDateAsLocaleTime
(
date
);
const
prevDateUTC
=
newDateAsLocaleTime
(
prevDate
);
const
gap
=
getDayDifference
(
prevDateUTC
,
currentDateUTC
);
for
(
let
i
=
1
;
i
<
gap
;
i
+=
1
)
{
acc
.
push
({
date
:
dateFormat
(
nDaysAfter
(
prevDateUTC
,
i
),
'
yyyy-mm-dd
'
),
...
previousValues
,
});
}
}
acc
.
push
(
current
);
return
acc
;
},
},
},
};
};
</
script
>
</
script
>
...
@@ -89,6 +189,9 @@ export default {
...
@@ -89,6 +189,9 @@ export default {
</gl-button-group>
</gl-button-group>
</div>
</div>
<div
v-if=
"glFeatures.burnupCharts"
class=
"row"
>
<div
v-if=
"glFeatures.burnupCharts"
class=
"row"
>
<gl-alert
v-if=
"error"
variant=
"danger"
class=
"col-12"
@
dismiss=
"error = ''"
>
{{
error
}}
</gl-alert>
<burndown-chart
<burndown-chart
:start-date=
"startDate"
:start-date=
"startDate"
:due-date=
"dueDate"
:due-date=
"dueDate"
...
@@ -100,7 +203,8 @@ export default {
...
@@ -100,7 +203,8 @@ export default {
<burnup-chart
<burnup-chart
:start-date=
"startDate"
:start-date=
"startDate"
:due-date=
"dueDate"
:due-date=
"dueDate"
:scope=
"burnupScope"
:burnup-data=
"burnupData"
:issues-selected=
"issuesSelected"
class=
"col-md-6"
class=
"col-md-6"
/>
/>
</div>
</div>
...
...
ee/app/assets/javascripts/burndown_chart/components/burndown_chart.vue
View file @
88d762ca
...
@@ -3,7 +3,7 @@ import { merge } from 'lodash';
...
@@ -3,7 +3,7 @@ import { merge } from 'lodash';
import
{
GlLineChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
{
GlLineChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
dateFormat
from
'
dateformat
'
;
import
dateFormat
from
'
dateformat
'
;
import
ResizableChartContainer
from
'
~/vue_shared/components/resizable_chart/resizable_chart_container.vue
'
;
import
ResizableChartContainer
from
'
~/vue_shared/components/resizable_chart/resizable_chart_container.vue
'
;
import
{
s__
,
__
,
sprintf
}
from
'
~/locale
'
;
import
{
__
,
n__
,
s
__
,
sprintf
}
from
'
~/locale
'
;
import
commonChartOptions
from
'
./common_chart_options
'
;
import
commonChartOptions
from
'
./common_chart_options
'
;
export
default
{
export
default
{
...
@@ -103,12 +103,14 @@ export default {
...
@@ -103,12 +103,14 @@ export default {
methods
:
{
methods
:
{
formatTooltipText
(
params
)
{
formatTooltipText
(
params
)
{
const
[
seriesData
]
=
params
.
seriesData
;
const
[
seriesData
]
=
params
.
seriesData
;
if
(
!
seriesData
)
{
return
;
}
this
.
tooltip
.
title
=
dateFormat
(
params
.
value
,
'
dd mmm yyyy
'
);
this
.
tooltip
.
title
=
dateFormat
(
params
.
value
,
'
dd mmm yyyy
'
);
if
(
this
.
issuesSelected
)
{
if
(
this
.
issuesSelected
)
{
this
.
tooltip
.
content
=
sprintf
(
__
(
'
%{total} open issues
'
),
{
this
.
tooltip
.
content
=
n__
(
'
%d open issue
'
,
'
%d open issues
'
,
seriesData
.
value
[
1
]);
total
:
seriesData
.
value
[
1
],
});
}
else
{
}
else
{
this
.
tooltip
.
content
=
sprintf
(
__
(
'
%{total} open issue weight
'
),
{
this
.
tooltip
.
content
=
sprintf
(
__
(
'
%{total} open issue weight
'
),
{
total
:
seriesData
.
value
[
1
],
total
:
seriesData
.
value
[
1
],
...
...
ee/app/assets/javascripts/burndown_chart/components/burnup_chart.vue
View file @
88d762ca
...
@@ -3,7 +3,7 @@ import { merge } from 'lodash';
...
@@ -3,7 +3,7 @@ import { merge } from 'lodash';
import
{
GlLineChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
{
GlLineChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
dateFormat
from
'
dateformat
'
;
import
dateFormat
from
'
dateformat
'
;
import
ResizableChartContainer
from
'
~/vue_shared/components/resizable_chart/resizable_chart_container.vue
'
;
import
ResizableChartContainer
from
'
~/vue_shared/components/resizable_chart/resizable_chart_container.vue
'
;
import
{
__
,
sprintf
}
from
'
~/locale
'
;
import
{
__
,
n__
,
sprintf
}
from
'
~/locale
'
;
import
commonChartOptions
from
'
./common_chart_options
'
;
import
commonChartOptions
from
'
./common_chart_options
'
;
export
default
{
export
default
{
...
@@ -20,7 +20,12 @@ export default {
...
@@ -20,7 +20,12 @@ export default {
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
scope
:
{
issuesSelected
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
burnupData
:
{
type
:
Array
,
type
:
Array
,
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
[],
...
@@ -35,11 +40,27 @@ export default {
...
@@ -35,11 +40,27 @@ export default {
};
};
},
},
computed
:
{
computed
:
{
scopeCount
()
{
return
this
.
transform
(
'
scopeCount
'
);
},
completedCount
()
{
return
this
.
transform
(
'
completedCount
'
);
},
scopeWeight
()
{
return
this
.
transform
(
'
scopeWeight
'
);
},
completedWeight
()
{
return
this
.
transform
(
'
completedWeight
'
);
},
dataSeries
()
{
dataSeries
()
{
const
series
=
[
const
series
=
[
{
{
name
:
__
(
'
Total
'
),
name
:
__
(
'
Total
'
),
data
:
this
.
scope
,
data
:
this
.
issuesSelected
?
this
.
scopeCount
:
this
.
scopeWeight
,
},
{
name
:
__
(
'
Completed
'
),
data
:
this
.
issuesSelected
?
this
.
completedCount
:
this
.
completedWeight
,
},
},
];
];
...
@@ -58,15 +79,31 @@ export default {
...
@@ -58,15 +79,31 @@ export default {
},
},
},
},
methods
:
{
methods
:
{
// transform the object to a chart-friendly array of date + value
transform
(
key
)
{
return
this
.
burnupData
.
map
(
val
=>
[
val
.
date
,
val
[
key
]]);
},
formatTooltipText
(
params
)
{
formatTooltipText
(
params
)
{
const
[
seriesData
]
=
params
.
seriesData
;
const
[
total
,
completed
]
=
params
.
seriesData
;
if
(
!
total
||
!
completed
)
{
return
;
}
this
.
tooltip
.
title
=
dateFormat
(
params
.
value
,
'
dd mmm yyyy
'
);
this
.
tooltip
.
title
=
dateFormat
(
params
.
value
,
'
dd mmm yyyy
'
);
const
text
=
__
(
'
%{total} open issues
'
);
const
count
=
total
.
value
[
1
];
const
completedCount
=
completed
.
value
[
1
];
this
.
tooltip
.
content
=
sprintf
(
text
,
{
let
totalText
=
n__
(
'
%d open issue
'
,
'
%d open issues
'
,
count
);
total
:
seriesData
.
value
[
1
],
let
completedText
=
n__
(
'
%d completed issue
'
,
'
%d completed issues
'
,
completedCount
);
});
if
(
!
this
.
issuesSelected
)
{
totalText
=
sprintf
(
__
(
'
%{count} total weight
'
),
{
count
});
completedText
=
sprintf
(
__
(
'
%{completedCount} completed weight
'
),
{
completedCount
});
}
this
.
tooltip
.
total
=
totalText
;
this
.
tooltip
.
completed
=
completedText
;
},
},
},
},
};
};
...
@@ -85,7 +122,10 @@ export default {
...
@@ -85,7 +122,10 @@ export default {
:include-legend-avg-max=
"false"
:include-legend-avg-max=
"false"
>
>
<template
slot=
"tooltipTitle"
>
{{
tooltip
.
title
}}
</
template
>
<template
slot=
"tooltipTitle"
>
{{
tooltip
.
title
}}
</
template
>
<
template
slot=
"tooltipContent"
>
{{
tooltip
.
content
}}
</
template
>
<
template
slot=
"tooltipContent"
>
<div>
{{
tooltip
.
total
}}
</div>
<div>
{{
tooltip
.
completed
}}
</div>
</
template
>
</gl-line-chart>
</gl-line-chart>
</resizable-chart-container>
</resizable-chart-container>
</div>
</div>
...
...
ee/app/assets/javascripts/burndown_chart/index.js
View file @
88d762ca
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
$
from
'
jquery
'
;
import
$
from
'
jquery
'
;
import
Cookies
from
'
js-cookie
'
;
import
Cookies
from
'
js-cookie
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
BurnCharts
from
'
./components/burn_charts.vue
'
;
import
BurnCharts
from
'
./components/burn_charts.vue
'
;
import
BurndownChartData
from
'
./burn_chart_data
'
;
import
BurndownChartData
from
'
./burn_chart_data
'
;
import
{
deprecatedCreateFlash
as
createFlash
}
from
'
~/flash
'
;
import
{
deprecatedCreateFlash
as
createFlash
}
from
'
~/flash
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
Vue
.
use
(
VueApollo
);
const
apolloProvider
=
new
VueApollo
({
defaultClient
:
createDefaultClient
(),
});
export
default
()
=>
{
export
default
()
=>
{
// handle hint dismissal
// handle hint dismissal
const
hint
=
$
(
'
.burndown-hint
'
);
const
hint
=
$
(
'
.burndown-hint
'
);
...
@@ -24,16 +32,10 @@ export default () => {
...
@@ -24,16 +32,10 @@ export default () => {
const
dueDate
=
$chartEl
.
data
(
'
dueDate
'
);
const
dueDate
=
$chartEl
.
data
(
'
dueDate
'
);
const
milestoneId
=
$chartEl
.
data
(
'
milestoneId
'
);
const
milestoneId
=
$chartEl
.
data
(
'
milestoneId
'
);
const
burndownEventsPath
=
$chartEl
.
data
(
'
burndownEventsPath
'
);
const
burndownEventsPath
=
$chartEl
.
data
(
'
burndownEventsPath
'
);
const
burnupEventsPath
=
$chartEl
.
data
(
'
burnupEventsPath
'
);
const
fetchData
=
[
axios
.
get
(
burndownEventsPath
)];
if
(
gon
.
features
.
burnupCharts
)
{
axios
fetchData
.
push
(
axios
.
get
(
burnupEventsPath
));
.
get
(
burndownEventsPath
)
}
.
then
(
burndownResponse
=>
{
Promise
.
all
(
fetchData
)
.
then
(([
burndownResponse
,
burnupResponse
])
=>
{
const
burndownEvents
=
burndownResponse
.
data
;
const
burndownEvents
=
burndownResponse
.
data
;
const
burndownChartData
=
new
BurndownChartData
(
const
burndownChartData
=
new
BurndownChartData
(
burndownEvents
,
burndownEvents
,
...
@@ -41,13 +43,6 @@ export default () => {
...
@@ -41,13 +43,6 @@ export default () => {
dueDate
,
dueDate
,
).
generateBurndownTimeseries
();
).
generateBurndownTimeseries
();
const
burnupEvents
=
burnupResponse
?.
data
||
[];
const
{
burnupScope
}
=
new
BurndownChartData
(
burnupEvents
,
startDate
,
dueDate
).
generateBurnupTimeseries
({
milestoneId
,
})
||
{};
const
openIssuesCount
=
burndownChartData
.
map
(
d
=>
[
d
[
0
],
d
[
1
]]);
const
openIssuesCount
=
burndownChartData
.
map
(
d
=>
[
d
[
0
],
d
[
1
]]);
const
openIssuesWeight
=
burndownChartData
.
map
(
d
=>
[
d
[
0
],
d
[
2
]]);
const
openIssuesWeight
=
burndownChartData
.
map
(
d
=>
[
d
[
0
],
d
[
2
]]);
...
@@ -56,6 +51,7 @@ export default () => {
...
@@ -56,6 +51,7 @@ export default () => {
components
:
{
components
:
{
BurnCharts
,
BurnCharts
,
},
},
apolloProvider
,
render
(
createElement
)
{
render
(
createElement
)
{
return
createElement
(
'
burn-charts
'
,
{
return
createElement
(
'
burn-charts
'
,
{
props
:
{
props
:
{
...
@@ -63,7 +59,7 @@ export default () => {
...
@@ -63,7 +59,7 @@ export default () => {
dueDate
,
dueDate
,
openIssuesCount
,
openIssuesCount
,
openIssuesWeight
,
openIssuesWeight
,
burnupScope
,
milestoneId
,
},
},
});
});
},
},
...
...
ee/app/assets/javascripts/burndown_chart/queries/burnup.query.graphql
0 → 100644
View file @
88d762ca
query
IterationBurnupTimesSeriesData
(
$milestoneId
:
MilestoneID
!)
{
milestone
(
id
:
$milestoneId
)
{
title
id
burnupTimeSeries
{
date
scopeCount
scopeWeight
completedCount
completedWeight
}
}
}
ee/app/views/shared/milestones/_burndown.html.haml
View file @
88d762ca
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
-
if
can_generate_chart?
(
milestone
,
burndown
)
-
if
can_generate_chart?
(
milestone
,
burndown
)
.burndown-chart.mb-2
{
data:
{
start_date:
burndown
.
start_date
.
strftime
(
"%Y-%m-%d"
),
.burndown-chart.mb-2
{
data:
{
start_date:
burndown
.
start_date
.
strftime
(
"%Y-%m-%d"
),
due_date:
burndown
.
due_date
.
strftime
(
"%Y-%m-%d"
),
due_date:
burndown
.
due_date
.
strftime
(
"%Y-%m-%d"
),
milestone_id:
milestone
.
id
,
milestone_id:
milestone
.
to_global_
id
,
burndown_events_path:
expose_url
(
burndown_endpoint
),
burnup_events_path:
expose_url
(
burnup_endpoint
)
}
}
burndown_events_path:
expose_url
(
burndown_endpoint
),
burnup_events_path:
expose_url
(
burnup_endpoint
)
}
}
-
elsif
show_burndown_placeholder?
(
milestone
,
warning
)
-
elsif
show_burndown_placeholder?
(
milestone
,
warning
)
...
...
ee/spec/frontend/burndown_chart/components/burn_charts_spec.js
View file @
88d762ca
...
@@ -2,6 +2,9 @@ import { shallowMount } from '@vue/test-utils';
...
@@ -2,6 +2,9 @@ import { shallowMount } from '@vue/test-utils';
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
BurnCharts
from
'
ee/burndown_chart/components/burn_charts.vue
'
;
import
BurnCharts
from
'
ee/burndown_chart/components/burn_charts.vue
'
;
import
BurndownChart
from
'
ee/burndown_chart/components/burndown_chart.vue
'
;
import
BurndownChart
from
'
ee/burndown_chart/components/burndown_chart.vue
'
;
import
BurnupChart
from
'
ee/burndown_chart/components/burnup_chart.vue
'
;
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
import
{
day1
,
day2
,
day3
,
day4
}
from
'
../mock_data
'
;
describe
(
'
burndown_chart
'
,
()
=>
{
describe
(
'
burndown_chart
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
...
@@ -12,10 +15,11 @@ describe('burndown_chart', () => {
...
@@ -12,10 +15,11 @@ describe('burndown_chart', () => {
const
findActiveButtons
=
()
=>
const
findActiveButtons
=
()
=>
wrapper
.
findAll
(
GlButton
).
filter
(
button
=>
button
.
attributes
().
category
===
'
primary
'
);
wrapper
.
findAll
(
GlButton
).
filter
(
button
=>
button
.
attributes
().
category
===
'
primary
'
);
const
findBurndownChart
=
()
=>
wrapper
.
find
(
BurndownChart
);
const
findBurndownChart
=
()
=>
wrapper
.
find
(
BurndownChart
);
const
findBurnupChart
=
()
=>
wrapper
.
find
(
BurnupChart
);
const
defaultProps
=
{
const
defaultProps
=
{
startDate
:
'
2019-08-07
T00:00:00.000Z
'
,
startDate
:
'
2019-08-07
'
,
dueDate
:
'
2019-09-09
T00:00:00.000Z
'
,
dueDate
:
'
2019-09-09
'
,
openIssuesCount
:
[],
openIssuesCount
:
[],
openIssuesWeight
:
[],
openIssuesWeight
:
[],
};
};
...
@@ -48,22 +52,23 @@ describe('burndown_chart', () => {
...
@@ -48,22 +52,23 @@ describe('burndown_chart', () => {
.
at
(
0
)
.
at
(
0
)
.
text
(),
.
text
(),
).
toBe
(
'
Issues
'
);
).
toBe
(
'
Issues
'
);
expect
(
findBurndownChart
().
props
(
).
issuesSelected
).
toBe
(
true
);
expect
(
findBurndownChart
().
props
(
'
issuesSelected
'
)
).
toBe
(
true
);
});
});
it
(
'
toggles Issue weight
'
,
()
=>
{
it
(
'
toggles Issue weight
'
,
async
()
=>
{
createComponent
();
createComponent
();
findWeightButton
().
vm
.
$emit
(
'
click
'
);
findWeightButton
().
vm
.
$emit
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
await
wrapper
.
vm
.
$nextTick
();
expect
(
findActiveButtons
()).
toHaveLength
(
1
);
expect
(
expect
(
findActiveButtons
()).
toHaveLength
(
1
);
findActiveButtons
()
expect
(
.
at
(
0
)
findActiveButtons
()
.
text
(),
.
at
(
0
)
).
toBe
(
'
Issue weight
'
);
.
text
(),
});
).
toBe
(
'
Issue weight
'
);
expect
(
findBurndownChart
().
props
(
'
issuesSelected
'
)).
toBe
(
false
);
});
});
describe
(
'
feature disabled
'
,
()
=>
{
describe
(
'
feature disabled
'
,
()
=>
{
...
@@ -94,5 +99,84 @@ describe('burndown_chart', () => {
...
@@ -94,5 +99,84 @@ describe('burndown_chart', () => {
expect
(
findChartsTitle
().
text
()).
toBe
(
'
Charts
'
);
expect
(
findChartsTitle
().
text
()).
toBe
(
'
Charts
'
);
expect
(
findBurndownChart
().
props
().
showTitle
).
toBe
(
true
);
expect
(
findBurndownChart
().
props
().
showTitle
).
toBe
(
true
);
});
});
it
(
'
sets weight prop of burnup chart
'
,
async
()
=>
{
findWeightButton
().
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
findBurnupChart
().
props
(
'
issuesSelected
'
)).
toBe
(
false
);
});
});
// some separate tests for the update function since it has a bunch of logic
describe
(
'
padSparseBurnupData function
'
,
()
=>
{
function
fakeDate
({
date
})
{
const
[
year
,
month
,
day
]
=
date
.
split
(
'
-
'
);
useFakeDate
(
year
,
month
-
1
,
day
);
}
beforeEach
(()
=>
{
createComponent
({
props
:
{
startDate
:
day1
.
date
,
dueDate
:
day4
.
date
},
featureEnabled
:
true
,
});
fakeDate
(
day4
);
});
it
(
'
pads data from startDate if no startDate values
'
,
()
=>
{
const
result
=
wrapper
.
vm
.
padSparseBurnupData
([
day2
,
day3
,
day4
]);
expect
(
result
.
length
).
toBe
(
4
);
expect
(
result
[
0
]).
toEqual
({
date
:
day1
.
date
,
completedCount
:
0
,
completedWeight
:
0
,
scopeCount
:
0
,
scopeWeight
:
0
,
});
});
it
(
'
if dueDate is in the past, pad data using last existing value
'
,
()
=>
{
const
result
=
wrapper
.
vm
.
padSparseBurnupData
([
day1
,
day2
]);
expect
(
result
.
length
).
toBe
(
4
);
expect
(
result
[
2
]).
toEqual
({
...
day2
,
date
:
day3
.
date
,
});
expect
(
result
[
3
]).
toEqual
({
...
day2
,
date
:
day4
.
date
,
});
});
it
(
'
if dueDate is in the future, pad data up to current date using last existing value
'
,
()
=>
{
fakeDate
(
day3
);
const
result
=
wrapper
.
vm
.
padSparseBurnupData
([
day1
,
day2
]);
expect
(
result
.
length
).
toBe
(
3
);
expect
(
result
[
2
]).
toEqual
({
...
day2
,
date
:
day3
.
date
,
});
});
it
(
'
pads missing days with data from previous days
'
,
()
=>
{
const
result
=
wrapper
.
vm
.
padSparseBurnupData
([
day1
,
day4
]);
expect
(
result
.
length
).
toBe
(
4
);
expect
(
result
[
1
]).
toEqual
({
...
day1
,
date
:
day2
.
date
,
});
expect
(
result
[
2
]).
toEqual
({
...
day1
,
date
:
day3
.
date
,
});
});
});
});
});
});
ee/spec/frontend/burndown_chart/components/burnup_chart_spec.js
View file @
88d762ca
...
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
...
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import
{
GlLineChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
{
GlLineChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
BurnupChart
from
'
ee/burndown_chart/components/burnup_chart.vue
'
;
import
BurnupChart
from
'
ee/burndown_chart/components/burnup_chart.vue
'
;
import
ResizableChartContainer
from
'
~/vue_shared/components/resizable_chart/resizable_chart_container.vue
'
;
import
ResizableChartContainer
from
'
~/vue_shared/components/resizable_chart/resizable_chart_container.vue
'
;
import
{
day1
,
day2
,
day3
}
from
'
../mock_data
'
;
describe
(
'
Burnup chart
'
,
()
=>
{
describe
(
'
Burnup chart
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
...
@@ -25,18 +26,31 @@ describe('Burnup chart', () => {
...
@@ -25,18 +26,31 @@ describe('Burnup chart', () => {
});
});
};
};
it
.
each
`
it
(
'
renders the lineChart correctly
'
,
()
=>
{
scope
const
burnupData
=
[
day1
,
day2
,
day3
];
${[{
'
2019-08-07T00:00:00.000Z
'
:
100
}]}
${[{
'
2019-08-07T00:00:00.000Z
'
:
100
},
{
'
2019-08-08T00:00:00.000Z
'
:
99
},
{
'
2019-09-08T00:00:00.000Z
'
:
1
}]}
const
expectedScopeCount
=
[
`
(
'
renders the lineChart correctly
'
,
({
scope
})
=>
{
[
day1
.
date
,
day1
.
scopeCount
],
createComponent
({
scope
});
[
day2
.
date
,
day2
.
scopeCount
],
[
day3
.
date
,
day3
.
scopeCount
],
];
const
expectedCompletedCount
=
[
[
day1
.
date
,
day1
.
completedCount
],
[
day2
.
date
,
day2
.
completedCount
],
[
day3
.
date
,
day3
.
completedCount
],
];
createComponent
({
burnupData
});
const
chartData
=
findChart
().
props
(
'
data
'
);
const
chartData
=
findChart
().
props
(
'
data
'
);
expect
(
chartData
).
toEqual
([
expect
(
chartData
).
toEqual
([
{
{
name
:
'
Total
'
,
name
:
'
Total
'
,
data
:
scope
,
data
:
expectedScopeCount
,
},
{
name
:
'
Completed
'
,
data
:
expectedCompletedCount
,
},
},
]);
]);
});
});
...
...
ee/spec/frontend/burndown_chart/mock_data.js
0 → 100644
View file @
88d762ca
export
const
day1
=
{
date
:
'
2020-08-08
'
,
completedCount
:
0
,
completedWeight
:
0
,
scopeCount
:
10
,
scopeWeight
:
20
,
};
export
const
day2
=
{
date
:
'
2020-08-09
'
,
completedCount
:
1
,
completedWeight
:
1
,
scopeCount
:
11
,
scopeWeight
:
20
,
};
export
const
day3
=
{
date
:
'
2020-08-10
'
,
completedCount
:
2
,
completedWeight
:
4
,
scopeCount
:
11
,
scopeWeight
:
22
,
};
export
const
day4
=
{
date
:
'
2020-08-11
'
,
completedCount
:
3
,
completedWeight
:
5
,
scopeCount
:
11
,
scopeWeight
:
22
,
};
locale/gitlab.pot
View file @
88d762ca
...
@@ -8,6 +8,8 @@ msgid ""
...
@@ -8,6 +8,8 @@ msgid ""
msgstr ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-11 14:23+1000\n"
"PO-Revision-Date: 2020-09-11 14:23+1000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"Language: \n"
...
@@ -140,6 +142,11 @@ msgstr[1] ""
...
@@ -140,6 +142,11 @@ msgstr[1] ""
msgid "%d commits"
msgid "%d commits"
msgstr ""
msgstr ""
msgid "%d completed issue"
msgid_plural "%d completed issues"
msgstr[0] ""
msgstr[1] ""
msgid "%d contribution"
msgid "%d contribution"
msgid_plural "%d contributions"
msgid_plural "%d contributions"
msgstr[0] ""
msgstr[0] ""
...
@@ -240,6 +247,11 @@ msgid_plural "%d more comments"
...
@@ -240,6 +247,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[0] ""
msgstr[1] ""
msgstr[1] ""
msgid "%d open issue"
msgid_plural "%d open issues"
msgstr[0] ""
msgstr[1] ""
msgid "%d personal project will be removed and cannot be restored."
msgid "%d personal project will be removed and cannot be restored."
msgid_plural "%d personal projects will be removed and cannot be restored."
msgid_plural "%d personal projects will be removed and cannot be restored."
msgstr[0] ""
msgstr[0] ""
...
@@ -333,6 +345,9 @@ msgstr ""
...
@@ -333,6 +345,9 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
msgstr ""
msgid "%{completedCount} completed weight"
msgstr ""
msgid "%{completedWeight} of %{totalWeight} weight completed"
msgid "%{completedWeight} of %{totalWeight} weight completed"
msgstr ""
msgstr ""
...
@@ -389,6 +404,9 @@ msgstr[1] ""
...
@@ -389,6 +404,9 @@ msgstr[1] ""
msgid "%{count} related %{pluralized_subject}: %{links}"
msgid "%{count} related %{pluralized_subject}: %{links}"
msgstr ""
msgstr ""
msgid "%{count} total weight"
msgstr ""
msgid "%{dashboard_path} could not be found."
msgid "%{dashboard_path} could not be found."
msgstr ""
msgstr ""
...
@@ -806,9 +824,6 @@ msgstr ""
...
@@ -806,9 +824,6 @@ msgstr ""
msgid "%{total} open issue weight"
msgid "%{total} open issue weight"
msgstr ""
msgstr ""
msgid "%{total} open issues"
msgstr ""
msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
msgstr ""
msgstr ""
...
@@ -9996,6 +10011,9 @@ msgstr ""
...
@@ -9996,6 +10011,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgid "Error deleting project. Check logs for error details."
msgstr ""
msgstr ""
msgid "Error fetching burnup chart data"
msgstr ""
msgid "Error fetching diverging counts for branches. Please try again."
msgid "Error fetching diverging counts for branches. Please try again."
msgstr ""
msgstr ""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment