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
24281d62
Commit
24281d62
authored
Sep 24, 2019
by
Martin Wortschack
Committed by
Filipa Lacerda
Sep 24, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add metric chart component to app
- Leverage MetricChart component in productivity analytics app
parent
91aea099
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
333 additions
and
461 deletions
+333
-461
ee/app/assets/javascripts/analytics/productivity_analytics/components/app.vue
...ripts/analytics/productivity_analytics/components/app.vue
+99
-175
ee/app/assets/javascripts/analytics/productivity_analytics/store/modules/charts/getters.js
...cs/productivity_analytics/store/modules/charts/getters.js
+1
-2
ee/spec/frontend/analytics/productivity_analytics/components/app_spec.js
...d/analytics/productivity_analytics/components/app_spec.js
+219
-280
ee/spec/frontend/analytics/productivity_analytics/store/modules/charts/getters_spec.js
...oductivity_analytics/store/modules/charts/getters_spec.js
+13
-0
locale/gitlab.pot
locale/gitlab.pot
+1
-4
No files found.
ee/app/assets/javascripts/analytics/productivity_analytics/components/app.vue
View file @
24281d62
...
...
@@ -10,6 +10,7 @@ import {
}
from
'
@gitlab/ui
'
;
import
{
GlColumnChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
MetricChart
from
'
./metric_chart.vue
'
;
import
MergeRequestTable
from
'
./mr_table.vue
'
;
import
{
chartKeys
}
from
'
../constants
'
;
...
...
@@ -22,6 +23,7 @@ export default {
GlColumnChart
,
GlButton
,
Icon
,
MetricChart
,
MergeRequestTable
,
},
directives
:
{
...
...
@@ -47,7 +49,7 @@ export default {
};
},
computed
:
{
...
mapState
(
'
filters
'
,
[
'
groupNamespace
'
,
'
projectPath
'
]),
...
mapState
(
'
filters
'
,
[
'
groupNamespace
'
]),
...
mapState
(
'
table
'
,
[
'
isLoadingTable
'
,
'
mergeRequests
'
,
'
pageInfo
'
,
'
columnMetric
'
]),
...
mapGetters
([
'
getMetricTypes
'
]),
...
mapGetters
(
'
charts
'
,
[
...
...
@@ -56,7 +58,7 @@ export default {
'
getChartData
'
,
'
getColumnChartDatazoomOption
'
,
'
getMetricDropdownLabel
'
,
'
is
SelectedMetric
'
,
'
get
SelectedMetric
'
,
'
hasNoAccessError
'
,
]),
...
mapGetters
(
'
table
'
,
[
...
...
@@ -85,7 +87,6 @@ export default {
},
methods
:
{
...
mapActions
([
'
setEndpoint
'
]),
...
mapActions
(
'
filters
'
,
[
'
setProjectPath
'
]),
...
mapActions
(
'
charts
'
,
[
'
fetchChartData
'
,
'
setMetricType
'
,
'
chartItemClicked
'
]),
...
mapActions
(
'
table
'
,
[
'
setSortField
'
,
...
...
@@ -141,17 +142,16 @@ export default {
/>
<template
v-if=
"showAppContent"
>
<h4>
{{
__
(
'
Merge Requests
'
)
}}
</h4>
<div
class=
"qa-time-to-merge mb-4"
>
<h5>
{{
__
(
'
Time to merge
'
)
}}
</h5>
<gl-loading-icon
v-if=
"chartLoading(chartKeys.main)"
size=
"md"
class=
"my-4 py-4"
/>
<template
v-else
>
<div
v-if=
"!chartHasData(chartKeys.main)"
class=
"bs-callout bs-callout-info"
>
{{
__
(
'
There is no data available. Please change your selection.
'
)
}}
</div>
<template
v-else
>
<p
class=
"text-muted"
>
{{
__
(
'
You can filter by "days to merge" by clicking on the columns in the chart.
'
)
}}
</p>
<metric-chart
ref=
"mainChart"
class=
"mb-4"
:title=
"__('Time to merge')"
:description=
"
__('You can filter by \'days to merge\' by clicking on the columns in the chart.')
"
:is-loading=
"chartLoading(chartKeys.main)"
:chart-data=
"getChartData(chartKeys.main)"
>
<gl-column-chart
:data=
"
{ full: getChartData(chartKeys.main) }"
:option="getColumnChartOption(chartKeys.main)"
...
...
@@ -160,66 +160,28 @@ export default {
x-axis-type="category"
@chartItemClicked="onMainChartItemClicked"
/>
</
template
>
</template>
</div>
</metric-chart>
<template
v-if=
"showSecondaryCharts"
>
<div
ref=
"secondaryCharts"
>
<div
class=
"row"
>
<div
class=
"qa-time-based col-lg-6 col-sm-12 mb-4"
>
<gl-loading-icon
v-if=
"chartLoading(chartKeys.timeBasedHistogram)"
size=
"md"
class=
"my-4 py-4"
/>
<template
v-else
>
<div
v-if=
"!chartHasData(chartKeys.timeBasedHistogram)"
class=
"bs-callout bs-callout-info"
>
{{
__
(
'
There is no data for the selected metric. Please change your selection.
'
)
}}
</div>
<template
v-else
>
<gl-dropdown
class=
"mb-4 metric-dropdown"
toggle-class=
"dropdown-menu-toggle w-100"
menu-class=
"w-100 mw-100"
:text=
"getMetricDropdownLabel(chartKeys.timeBasedHistogram)"
>
<gl-dropdown-item
v-for=
"metric in getMetricTypes(chartKeys.timeBasedHistogram)"
:key=
"metric.key"
active-class=
"is-active"
class=
"w-100"
@
click=
"
setMetricType(
{
metricType: metric.key,
chartKey: chartKeys.timeBasedHistogram,
})
"
>
<span
class=
"d-flex"
>
<icon
class=
"flex-shrink-0 append-right-4"
:class=
"
{
invisible: !isSelectedMetric({
metric: metric.key,
chartKey: chartKeys.timeBasedHistogram,
}),
}"
name="mobile-issue-close"
/>
{{
metric
.
label
}}
</span>
</gl-dropdown-item>
</gl-dropdown>
<p
class=
"text-muted"
>
{{
<metric-chart
ref=
"timeBasedChart"
class=
"col-lg-6 col-sm-12 mb-4"
:description=
"
__(
'Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited.',
)
}}
</p>
"
:is-loading=
"chartLoading(chartKeys.timeBasedHistogram)"
:metric-types=
"getMetricTypes(chartKeys.timeBasedHistogram)"
:selected-metric=
"getSelectedMetric(chartKeys.timeBasedHistogram)"
:chart-data=
"getChartData(chartKeys.timeBasedHistogram)"
@
metricTypeChange=
"
metric =>
setMetricType(
{ metricType: metric, chartKey: chartKeys.timeBasedHistogram })
"
>
<gl-column-chart
:data=
"
{ full: getChartData(chartKeys.timeBasedHistogram) }"
:option="getColumnChartOption(chartKeys.timeBasedHistogram)"
...
...
@@ -227,64 +189,25 @@ export default {
:x-axis-title="__('Hours')"
x-axis-type="category"
/>
</
template
>
</template>
</div>
</metric-chart>
<div
class=
"qa-commit-based col-lg-6 col-sm-12 mb-4"
>
<gl-loading-icon
v-if=
"chartLoading(chartKeys.commitBasedHistogram)"
size=
"md"
class=
"my-4 py-4"
/>
<
template
v-else
>
<div
v-if=
"!chartHasData(chartKeys.commitBasedHistogram)"
class=
"bs-callout bs-callout-info"
>
{{
__
(
'
There is no data for the selected metric. Please change your selection.
'
)
}}
</div>
<template
v-else
>
<gl-dropdown
class=
"mb-4 metric-dropdown"
toggle-class=
"dropdown-menu-toggle w-100"
menu-class=
"w-100 mw-100"
:text=
"getMetricDropdownLabel(chartKeys.commitBasedHistogram)"
>
<gl-dropdown-item
v-for=
"metric in getMetricTypes(chartKeys.commitBasedHistogram)"
:key=
"metric.key"
active-class=
"is-active"
class=
"w-100"
@
click=
"
setMetricType(
{
metricType: metric.key,
chartKey: chartKeys.commitBasedHistogram,
})
"
>
<span
class=
"d-flex"
>
<icon
class=
"flex-shrink-0 append-right-4"
:class=
"
{
invisible: !isSelectedMetric({
metric: metric.key,
chartKey: chartKeys.commitBasedHistogram,
}),
}"
name="mobile-issue-close"
/>
{{
metric
.
label
}}
</span>
</gl-dropdown-item>
</gl-dropdown>
<p
class=
"text-muted"
>
{{
<metric-chart
ref=
"commitBasedChart"
class=
"col-lg-6 col-sm-12 mb-4"
:description=
"
__(
'Not all data has been processed yet, the accuracy of the chart for the selected timeframe is limited.',
)
}}
</p>
"
:is-loading=
"chartLoading(chartKeys.commitBasedHistogram)"
:metric-types=
"getMetricTypes(chartKeys.commitBasedHistogram)"
:selected-metric=
"getSelectedMetric(chartKeys.commitBasedHistogram)"
:chart-data=
"getChartData(chartKeys.commitBasedHistogram)"
@
metricTypeChange=
"
metric =>
setMetricType(
{ metricType: metric, chartKey: chartKeys.commitBasedHistogram })
"
>
<gl-column-chart
:data=
"
{ full: getChartData(chartKeys.commitBasedHistogram) }"
:option="getColumnChartOption(chartKeys.commitBasedHistogram)"
...
...
@@ -292,13 +215,11 @@ export default {
:x-axis-title="getMetricDropdownLabel(chartKeys.commitBasedHistogram)"
x-axis-type="category"
/>
</
template
>
</template>
</div>
</metric-chart>
</div>
<div
class=
"qa
-mr-table-sort d-flex flex-column flex-md-row align-items-md-center justify-content-between mb-2"
class=
"js
-mr-table-sort d-flex flex-column flex-md-row align-items-md-center justify-content-between mb-2"
>
<h5>
{{
__
(
'
List
'
)
}}
</h5>
<div
...
...
@@ -337,7 +258,10 @@ export default {
</div>
</div>
</div>
<div
class=
"qa-mr-table"
>
</div>
<div
class=
"js-mr-table"
>
<div
ref=
"foo"
></div>
<gl-loading-icon
v-if=
"isLoadingTable"
size=
"md"
class=
"my-4 py-4"
/>
<merge-request-table
v-if=
"showMergeRequestTable"
...
...
ee/app/assets/javascripts/analytics/productivity_analytics/store/modules/charts/getters.js
View file @
24281d62
...
...
@@ -107,8 +107,7 @@ export const getColumnChartDatazoomOption = state => chartKey => {
};
};
export
const
isSelectedMetric
=
state
=>
({
metric
,
chartKey
})
=>
state
.
charts
[
chartKey
].
params
.
metricType
===
metric
;
export
const
getSelectedMetric
=
state
=>
chartKey
=>
state
.
charts
[
chartKey
].
params
.
metricType
;
export
const
hasNoAccessError
=
state
=>
state
.
charts
[
chartKeys
.
main
].
errorCode
===
httpStatus
.
FORBIDDEN
;
...
...
ee/spec/frontend/analytics/productivity_analytics/components/app_spec.js
View file @
24281d62
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vuex
from
'
vuex
'
;
import
axios
from
'
axios
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
ProductivityApp
from
'
ee/analytics/productivity_analytics/components/app.vue
'
;
import
MergeRequestTable
from
'
ee/analytics/productivity_analytics/components/mr_table.vue
'
;
import
store
from
'
ee/analytics/productivity_analytics/store
'
;
...
...
@@ -7,13 +9,13 @@ import { chartKeys } from 'ee/analytics/productivity_analytics/constants';
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
GlEmptyState
,
GlLoadingIcon
,
GlDropdown
,
GlDropdownItem
,
GlButton
}
from
'
@gitlab/ui
'
;
import
{
GlColumnChart
}
from
'
@gitlab/ui/dist/charts
'
;
import
resetStore
from
'
../helpers
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
describe
(
'
ProductivityApp component
'
,
()
=>
{
let
wrapper
;
let
mock
;
const
propsData
=
{
endpoint
:
TEST_HOST
,
...
...
@@ -30,7 +32,10 @@ describe('ProductivityApp component', () => {
setColumnMetric
:
jest
.
fn
(),
};
const
mainChartData
=
{
1
:
2
,
2
:
3
};
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
wrapper
=
shallowMount
(
localVue
.
extend
(
ProductivityApp
),
{
localVue
,
store
,
...
...
@@ -40,23 +45,22 @@ describe('ProductivityApp component', () => {
...
actionSpies
,
},
});
jest
.
spyOn
(
store
,
'
dispatch
'
).
mockImplementation
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
resetStore
(
store
);
mock
.
restore
(
);
});
const
findTimeToMergeSection
=
()
=>
wrapper
.
find
(
'
.qa-time-to-merge
'
);
const
findMrTableSortSection
=
()
=>
wrapper
.
find
(
'
.qa-mr-table-sort
'
);
const
findMrTableSection
=
()
=>
wrapper
.
find
(
'
.qa-mr-table
'
);
const
findMrTable
=
()
=>
findMrTableSection
().
find
(
MergeRequestTable
);
const
findMainMetricChart
=
()
=>
wrapper
.
find
({
ref
:
'
mainChart
'
});
const
findSecondaryChartsSection
=
()
=>
wrapper
.
find
({
ref
:
'
secondaryCharts
'
});
const
findTimeBasedMetricChart
=
()
=>
wrapper
.
find
({
ref
:
'
timeBasedChart
'
});
const
findCommitBasedMetricChart
=
()
=>
wrapper
.
find
({
ref
:
'
commitBasedChart
'
});
const
findMrTableSortSection
=
()
=>
wrapper
.
find
(
'
.js-mr-table-sort
'
);
const
findSortFieldDropdown
=
()
=>
findMrTableSortSection
().
find
(
GlDropdown
);
const
findSortOrderToggle
=
()
=>
findMrTableSortSection
().
find
(
GlButton
);
const
find
TimeBasedSection
=
()
=>
wrapper
.
find
(
'
.qa-time-based
'
);
const
find
CommitBasedSection
=
()
=>
wrapper
.
find
(
'
.qa-commit-based
'
);
const
find
MrTableSection
=
()
=>
wrapper
.
find
(
'
.js-mr-table
'
);
const
find
MrTable
=
()
=>
findMrTableSection
().
find
(
MergeRequestTable
);
describe
(
'
template
'
,
()
=>
{
describe
(
'
without a group being selected
'
,
()
=>
{
...
...
@@ -70,12 +74,18 @@ describe('ProductivityApp component', () => {
describe
(
'
with a group being selected
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
filters
.
groupNamespace
=
'
gitlab-org
'
;
wrapper
.
vm
.
$store
.
dispatch
(
'
filters/setGroupNamespace
'
,
'
gitlab-org
'
);
mock
.
onGet
(
wrapper
.
vm
.
$store
.
state
.
endpoint
).
replyOnce
(
200
);
});
describe
(
'
and
user has no access to the group
'
,
()
=>
{
describe
(
'
user has no access to the group
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
errorCode
=
403
;
const
error
=
{
response
:
{
status
:
403
}
};
wrapper
.
vm
.
$store
.
dispatch
(
'
charts/receiveChartDataError
'
,
{
chartKey
:
chartKeys
.
main
,
error
,
});
wrapper
.
vm
.
$store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
errorCode
=
403
;
});
it
(
'
renders the no access illustration
'
,
()
=>
{
...
...
@@ -86,46 +96,49 @@ describe('ProductivityApp component', () => {
});
});
describe
(
'
and
user has access to the group
'
,
()
=>
{
describe
(
'
user has access to the group
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
errorCode
=
null
;
wrapper
.
vm
.
$
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
errorCode
=
null
;
});
describe
(
'
Time to merge chart
'
,
()
=>
{
it
(
'
renders the title
'
,
()
=>
{
expect
(
findTimeToMergeSection
().
text
()).
toContain
(
'
Time to merge
'
);
describe
(
'
when the main chart is loading
'
,
()
=>
{
beforeEach
(
()
=>
{
wrapper
.
vm
.
$store
.
dispatch
(
'
charts/requestChartData
'
,
chartKeys
.
main
);
});
describe
(
'
when chart is loading
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
isLoading
=
true
;
it
(
'
renders a metric chart component for the main chart
'
,
()
=>
{
expect
(
findMainMetricChart
().
exists
()).
toBe
(
true
);
});
it
(
'
renders a loading indicator
'
,
()
=>
{
expect
(
findTimeToMergeSection
()
.
find
(
GlLoadingIcon
)
.
exists
(),
).
toBe
(
true
);
it
(
'
sets isLoading=true on the metric chart
'
,
()
=>
{
expect
(
findMainMetricChart
().
props
(
'
isLoading
'
)).
toBe
(
true
);
});
it
(
'
does not render any other charts
'
,
()
=>
{
expect
(
findSecondaryChartsSection
().
exists
()).
toBe
(
false
);
});
describe
(
'
when chart finished loading
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
isLoading
=
false
;
it
(
'
does not render the MR table
'
,
()
=>
{
expect
(
findMrTableSortSection
().
exists
()).
toBe
(
false
);
expect
(
findMrTableSection
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
and the chart has data
'
,
()
=>
{
describe
(
'
when the main chart finished loading
'
,
()
=>
{
describe
(
'
and has data
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
data
=
{
1
:
2
,
2
:
3
};
wrapper
.
vm
.
$store
.
dispatch
(
'
charts/receiveChartDataSuccess
'
,
{
chartKey
:
chartKeys
.
main
,
data
:
mainChartData
,
});
});
it
(
'
renders a column
chart
'
,
()
=>
{
expect
(
findTimeToMergeSection
()
.
find
(
GlColumnChart
)
.
exists
(),
).
toBe
(
true
);
it
(
'
sets isLoading=false on the metric
chart
'
,
()
=>
{
expect
(
findMainMetricChart
().
props
(
'
isLoading
'
)).
toBe
(
false
);
});
it
(
'
passes non-empty chartData to the metric chart
'
,
()
=>
{
expect
(
findMainMetricChart
().
props
(
'
chartData
'
)).
not
.
toEqual
([]
);
});
describe
(
'
when an item on the chart is clicked
'
,
()
=>
{
...
...
@@ -139,7 +152,7 @@ describe('ProductivityApp component', () => {
},
};
findTimeToMergeSection
()
findMainMetricChart
()
.
find
(
GlColumnChart
)
.
vm
.
$emit
(
'
chartItemClicked
'
,
data
);
});
...
...
@@ -155,188 +168,88 @@ describe('ProductivityApp component', () => {
expect
(
actionSpies
.
setMergeRequestsPage
).
toHaveBeenCalledWith
(
0
);
});
});
});
describe
(
"
and the chart doesn't have any data
"
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
data
=
null
;
});
it
(
'
renders a "no data" message
'
,
()
=>
{
expect
(
findTimeToMergeSection
().
text
()).
toContain
(
'
There is no data available. Please change your selection.
'
,
);
});
});
});
});
describe
(
'
Time based histogram
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
isLoading
=
false
;
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
data
=
{
1
:
2
,
2
:
3
};
it
(
'
renders a metric chart component
'
,
()
=>
{
expect
(
findTimeBasedMetricChart
().
exists
()).
toBe
(
true
);
});
describe
(
'
when chart is loading
'
,
()
=>
{
describe
(
'
when chart finished loading
'
,
()
=>
{
describe
(
'
and the chart has data
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
timeBasedHistogram
].
isLoading
=
true
;
});
it
(
'
renders a loading indicator
'
,
()
=>
{
expect
(
findTimeBasedSection
()
.
find
(
GlLoadingIcon
)
.
exists
(),
).
toBe
(
true
);
wrapper
.
vm
.
$store
.
dispatch
(
'
charts/receiveChartDataSuccess
'
,
{
chartKey
:
chartKeys
.
timeBasedHistogram
,
data
:
{
1
:
2
,
2
:
3
},
});
});
describe
(
'
when chart finished loading
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
timeBasedHistogram
].
isLoading
=
false
;
it
(
'
sets isLoading=false on the metric chart
'
,
()
=>
{
expect
(
findTimeBasedMetricChart
().
props
(
'
isLoading
'
)).
toBe
(
false
);
});
describe
(
'
and the chart has data
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
timeBasedHistogram
].
data
=
{
1
:
2
,
2
:
3
};
it
(
'
passes non-empty chartData to the metric chart
'
,
()
=>
{
expect
(
findTimeBasedMetricChart
().
props
(
'
chartData
'
)).
not
.
toEqual
([]);
});
it
(
'
renders a metric type dropdown
'
,
()
=>
{
expect
(
findTimeBasedSection
()
.
find
(
GlDropdown
)
.
exists
(),
).
toBe
(
true
);
});
it
(
'
should change the metric type
'
,
()
=>
{
findTimeBasedSection
()
.
findAll
(
GlDropdownItem
)
.
at
(
0
)
.
vm
.
$emit
(
'
click
'
);
it
(
'
should call setMetricType when `metricTypeChange` is emitted on the metric chart
'
,
()
=>
{
findTimeBasedMetricChart
().
vm
.
$emit
(
'
metricTypeChange
'
,
'
time_to_merge
'
);
expect
(
actionSpies
.
setMetricType
).
toHaveBeenCalledWith
({
metricType
:
'
time_to_first_comment
'
,
metricType
:
'
time_to_merge
'
,
chartKey
:
chartKeys
.
timeBasedHistogram
,
});
});
it
(
'
renders a column chart
'
,
()
=>
{
expect
(
findTimeBasedSection
()
.
find
(
GlColumnChart
)
.
exists
(),
).
toBe
(
true
);
});
});
describe
(
"
and the chart doesn't have any data
"
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
timeBasedHistogram
].
data
=
null
;
});
it
(
'
renders a "no data" message
'
,
()
=>
{
expect
(
findTimeBasedSection
().
text
()).
toContain
(
'
There is no data for the selected metric. Please change your selection.
'
,
);
});
});
});
});
describe
(
'
Commit based histogram
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
isLoading
=
false
;
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
data
=
{
1
:
2
,
2
:
3
};
});
describe
(
'
when chart is loading
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
commitBasedHistogram
].
isLoading
=
true
;
});
it
(
'
renders a loading indicator
'
,
()
=>
{
expect
(
findCommitBasedSection
()
.
find
(
GlLoadingIcon
)
.
exists
(),
).
toBe
(
true
);
});
it
(
'
renders a metric chart component
'
,
()
=>
{
expect
(
findCommitBasedMetricChart
().
exists
()).
toBe
(
true
);
});
describe
(
'
when chart finished loading
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
commitBasedHistogram
].
isLoading
=
false
;
});
describe
(
'
and the chart has data
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
commitBasedHistogram
].
data
=
{
1
:
2
,
2
:
3
};
wrapper
.
vm
.
$store
.
dispatch
(
'
charts/receiveChartDataSuccess
'
,
{
chartKey
:
chartKeys
.
commitBasedHistogram
,
data
:
{
1
:
2
,
2
:
3
},
});
});
it
(
'
renders a column chart
'
,
()
=>
{
expect
(
findCommitBasedSection
()
.
find
(
GlColumnChart
)
.
exists
(),
).
toBe
(
true
);
it
(
'
sets isLoading=false on the metric chart
'
,
()
=>
{
expect
(
findCommitBasedMetricChart
().
props
(
'
isLoading
'
)).
toBe
(
false
);
});
describe
(
'
metric dropdown
'
,
()
=>
{
it
(
'
renders a metric type dropdown
'
,
()
=>
{
expect
(
findCommitBasedSection
()
.
find
(
GlDropdown
)
.
exists
(),
).
toBe
(
true
);
it
(
'
passes non-empty chartData to the metric chart
'
,
()
=>
{
expect
(
findCommitBasedMetricChart
().
props
(
'
chartData
'
)).
not
.
toEqual
([]);
});
describe
(
'
when the user changes the metric
'
,
()
=>
{
beforeEach
(()
=>
{
findCommitBasedSection
()
.
findAll
(
GlDropdownItem
)
.
at
(
0
)
.
vm
.
$emit
(
'
click
'
);
findCommitBasedMetricChart
().
vm
.
$emit
(
'
metricTypeChange
'
,
'
loc_per_commit
'
);
});
it
(
'
should dispatch setMetricType action
'
,
()
=>
{
it
(
'
should call setMetricType when `metricTypeChange` is emitted on the metric chart
'
,
()
=>
{
expect
(
actionSpies
.
setMetricType
).
toHaveBeenCalledWith
({
metricType
:
'
commits_coun
t
'
,
metricType
:
'
loc_per_commi
t
'
,
chartKey
:
chartKeys
.
commitBasedHistogram
,
});
});
it
(
"
should update the chart's x axis label
"
,
()
=>
{
const
columnChart
=
findCommitBasedSection
().
find
(
GlColumnChart
);
const
columnChart
=
findCommitBasedMetricChart
().
find
(
GlColumnChart
);
expect
(
columnChart
.
props
(
'
xAxisTitle
'
)).
toBe
(
'
Number of commits per MR
'
);
});
});
});
});
describe
(
"
and the chart doesn't have any data
"
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
commitBasedHistogram
].
data
=
null
;
});
it
(
'
renders a "no data" message
'
,
()
=>
{
expect
(
findTimeBasedSection
().
text
()).
toContain
(
'
There is no data for the selected metric. Please change your selection.
'
,
);
});
});
});
});
describe
(
'
MR table
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
isLoading
=
false
;
store
.
state
.
charts
.
charts
[
chartKeys
.
main
].
data
=
{
1
:
2
,
2
:
3
};
});
describe
(
'
when table is loading
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
table
.
isLoadingTable
=
true
;
wrapper
.
vm
.
$store
.
dispatch
(
'
table/requestMergeRequests
'
)
;
});
it
(
'
renders a loading indicator
'
,
()
=>
{
...
...
@@ -349,13 +262,12 @@ describe('ProductivityApp component', () => {
});
describe
(
'
when table finished loading
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
table
.
isLoadingTable
=
false
;
});
describe
(
'
and the table has data
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
table
.
mergeRequests
=
[{
id
:
1
,
title
:
'
This is a test MR
'
}];
wrapper
.
vm
.
$store
.
dispatch
(
'
table/receiveMergeRequestsSuccess
'
,
{
headers
:
{},
data
:
[{
id
:
1
,
title
:
'
This is a test MR
'
}],
});
});
it
(
'
renders the MR table
'
,
()
=>
{
...
...
@@ -372,7 +284,9 @@ describe('ProductivityApp component', () => {
it
(
'
should change the column metric
'
,
()
=>
{
findMrTable
().
vm
.
$emit
(
'
columnMetricChange
'
,
'
time_to_first_comment
'
);
expect
(
actionSpies
.
setColumnMetric
).
toHaveBeenCalledWith
(
'
time_to_first_comment
'
);
expect
(
actionSpies
.
setColumnMetric
).
toHaveBeenCalledWith
(
'
time_to_first_comment
'
,
);
});
it
(
'
should change the page
'
,
()
=>
{
...
...
@@ -381,11 +295,6 @@ describe('ProductivityApp component', () => {
expect
(
actionSpies
.
setMergeRequestsPage
).
toHaveBeenCalledWith
(
page
);
});
describe
(
'
and there are merge requests available
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
table
.
mergeRequests
=
[{
id
:
1
}];
});
describe
(
'
sort controls
'
,
()
=>
{
it
(
'
renders the sort dropdown and button
'
,
()
=>
{
expect
(
findSortFieldDropdown
().
exists
()).
toBe
(
true
);
...
...
@@ -407,11 +316,13 @@ describe('ProductivityApp component', () => {
});
});
});
});
describe
(
"
and the table doesn't have any data
"
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
table
.
mergeRequests
=
[];
wrapper
.
vm
.
$store
.
dispatch
(
'
table/receiveMergeRequestsSuccess
'
,
{
headers
:
{},
data
:
[],
});
});
it
(
'
renders a "no data" message
'
,
()
=>
{
...
...
@@ -434,6 +345,34 @@ describe('ProductivityApp component', () => {
});
});
});
describe
(
'
and has no data
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
.
vm
.
$store
.
dispatch
(
'
charts/receiveChartDataSuccess
'
,
{
chartKey
:
chartKeys
.
main
,
data
:
{},
});
});
it
(
'
sets isLoading=false on the metric chart
'
,
()
=>
{
expect
(
findMainMetricChart
().
props
(
'
isLoading
'
)).
toBe
(
false
);
});
it
(
'
passes an empty array as chartData to the metric chart
'
,
()
=>
{
expect
(
findMainMetricChart
().
props
(
'
chartData
'
)).
toEqual
([]);
});
it
(
'
does not render any other charts
'
,
()
=>
{
expect
(
findSecondaryChartsSection
().
exists
()).
toBe
(
false
);
});
it
(
'
does not render the MR table
'
,
()
=>
{
expect
(
findMrTableSortSection
().
exists
()).
toBe
(
false
);
expect
(
findMrTableSection
().
exists
()).
toBe
(
false
);
});
});
});
});
});
});
});
ee/spec/frontend/analytics/productivity_analytics/store/modules/charts/getters_spec.js
View file @
24281d62
...
...
@@ -178,6 +178,19 @@ describe('Productivity analytics chart getters', () => {
});
});
describe
(
'
getSelectedMetric
'
,
()
=>
{
it
(
'
returns the currently selected metric for a given chartKey
'
,
()
=>
{
const
metricType
=
'
time_to_last_commit
'
;
state
.
charts
[
chartKeys
.
timeBasedHistogram
].
params
=
{
metricType
,
};
expect
(
getters
.
getSelectedMetric
(
state
)(
chartKeys
.
timeBasedHistogram
)).
toBe
(
'
time_to_last_commit
'
,
);
});
});
describe
(
'
hasNoAccessError
'
,
()
=>
{
it
(
'
returns true if errorCode is set to 403
'
,
()
=>
{
state
.
charts
[
chartKeys
.
main
].
errorCode
=
403
;
...
...
locale/gitlab.pot
View file @
24281d62
...
...
@@ -15728,9 +15728,6 @@ msgstr ""
msgid "There is no data available. Please change your selection."
msgstr ""
msgid "There is no data for the selected metric. Please change your selection."
msgstr ""
msgid "There was a problem communicating with your device."
msgstr ""
...
...
@@ -17981,7 +17978,7 @@ msgstr ""
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
msgid "You can filter by
\"days to merge\"
by clicking on the columns in the chart."
msgid "You can filter by
'days to merge'
by clicking on the columns in the chart."
msgstr ""
msgid "You can invite a new member to <strong>%{project_name}</strong> or invite another group."
...
...
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