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
b8955f6d
Commit
b8955f6d
authored
Apr 23, 2021
by
Lee Tickett
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add issuable time tracking report
Changelog: added
parent
a309f510
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
438 additions
and
6 deletions
+438
-6
app/assets/javascripts/graphql_shared/fragments/issuable_timelogs.fragment.graphql
...aphql_shared/fragments/issuable_timelogs.fragment.graphql
+10
-0
app/assets/javascripts/sidebar/components/time_tracking/report.vue
...s/javascripts/sidebar/components/time_tracking/report.vue
+102
-0
app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
...scripts/sidebar/components/time_tracking/time_tracker.vue
+23
-1
app/assets/javascripts/sidebar/constants.js
app/assets/javascripts/sidebar/constants.js
+11
-0
app/assets/javascripts/sidebar/mount_sidebar.js
app/assets/javascripts/sidebar/mount_sidebar.js
+4
-4
app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql
...mponents/sidebar/queries/get_issue_timelogs.query.graphql
+14
-0
app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql
.../components/sidebar/queries/get_mr_timelogs.query.graphql
+14
-0
app/graphql/types/timelog_type.rb
app/graphql/types/timelog_type.rb
+4
-0
changelogs/unreleased/271409-time-tracking-reports.yml
changelogs/unreleased/271409-time-tracking-reports.yml
+5
-0
doc/user/project/img/time_tracking_report_v13_12.png
doc/user/project/img/time_tracking_report_v13_12.png
+0
-0
doc/user/project/img/time_tracking_sidebar_v13_12.png
doc/user/project/img/time_tracking_sidebar_v13_12.png
+0
-0
doc/user/project/img/time_tracking_sidebar_v8_16.png
doc/user/project/img/time_tracking_sidebar_v8_16.png
+0
-0
doc/user/project/time_tracking.md
doc/user/project/time_tracking.md
+15
-1
locale/gitlab.pot
locale/gitlab.pot
+9
-0
spec/frontend/sidebar/components/time_tracking/mock_data.js
spec/frontend/sidebar/components/time_tracking/mock_data.js
+102
-0
spec/frontend/sidebar/components/time_tracking/report_spec.js
.../frontend/sidebar/components/time_tracking/report_spec.js
+97
-0
spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js
...end/sidebar/components/time_tracking/time_tracker_spec.js
+28
-0
No files found.
app/assets/javascripts/graphql_shared/fragments/issuable_timelogs.fragment.graphql
0 → 100644
View file @
b8955f6d
fragment
TimelogFragment
on
Timelog
{
timeSpent
user
{
name
}
spentAt
note
{
body
}
}
app/assets/javascripts/sidebar/components/time_tracking/report.vue
0 → 100644
View file @
b8955f6d
<
script
>
import
{
GlLoadingIcon
,
GlTable
}
from
'
@gitlab/ui
'
;
import
createFlash
from
'
~/flash
'
;
import
{
convertToGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
formatDate
,
parseSeconds
,
stringifyTime
}
from
'
~/lib/utils/datetime_utility
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
timelogQueries
}
from
'
~/sidebar/constants
'
;
const
TIME_DATE_FORMAT
=
'
mmmm d, yyyy, HH:MM ("UTC:" o)
'
;
export
default
{
components
:
{
GlLoadingIcon
,
GlTable
,
},
inject
:
[
'
issuableId
'
,
'
issuableType
'
],
data
()
{
return
{
report
:
[],
isLoading
:
true
};
},
apollo
:
{
report
:
{
query
()
{
return
timelogQueries
[
this
.
issuableType
].
query
;
},
variables
()
{
return
{
id
:
convertToGraphQLId
(
this
.
getGraphQLEntityType
(),
this
.
issuableId
),
};
},
update
(
data
)
{
this
.
isLoading
=
false
;
return
this
.
extractTimelogs
(
data
);
},
error
()
{
createFlash
({
message
:
__
(
'
Something went wrong. Please try again.
'
)
});
},
},
},
methods
:
{
isIssue
()
{
return
this
.
issuableType
===
'
issue
'
;
},
getGraphQLEntityType
()
{
// eslint-disable-next-line @gitlab/require-i18n-strings
return
this
.
isIssue
()
?
'
Issue
'
:
'
MergeRequest
'
;
},
extractTimelogs
(
data
)
{
const
timelogs
=
data
?.
issuable
?.
timelogs
?.
nodes
||
[];
return
timelogs
.
slice
().
sort
((
a
,
b
)
=>
new
Date
(
a
.
spentAt
)
-
new
Date
(
b
.
spentAt
));
},
formatDate
(
date
)
{
return
formatDate
(
date
,
TIME_DATE_FORMAT
);
},
getNote
(
note
)
{
return
note
?.
body
;
},
getTotalTimeSpent
()
{
const
seconds
=
this
.
report
.
reduce
((
acc
,
item
)
=>
acc
+
item
.
timeSpent
,
0
);
return
this
.
formatTimeSpent
(
seconds
);
},
formatTimeSpent
(
seconds
)
{
const
negative
=
seconds
<
0
;
return
(
negative
?
'
-
'
:
''
)
+
stringifyTime
(
parseSeconds
(
seconds
));
},
},
fields
:
[
{
key
:
'
spentAt
'
,
label
:
__
(
'
Spent At
'
),
sortable
:
true
},
{
key
:
'
user
'
,
label
:
__
(
'
User
'
),
sortable
:
true
},
{
key
:
'
timeSpent
'
,
label
:
__
(
'
Time Spent
'
),
sortable
:
true
},
{
key
:
'
note
'
,
label
:
__
(
'
Note
'
),
sortable
:
true
},
],
};
</
script
>
<
template
>
<div>
<div
v-if=
"isLoading"
><gl-loading-icon
size=
"md"
/></div>
<gl-table
v-else
:items=
"report"
:fields=
"$options.fields"
foot-clone
>
<template
#cell(spentAt)=
"
{ item: { spentAt } }">
<div>
{{
formatDate
(
spentAt
)
}}
</div>
</
template
>
<
template
#foot
(
spentAt
)
>
</
template
>
<
template
#cell(user)=
"{ item: { user } }"
>
<div>
{{
user
.
name
}}
</div>
</
template
>
<
template
#foot
(
user
)
>
</
template
>
<
template
#cell(timeSpent)=
"{ item: { timeSpent } }"
>
<div>
{{
formatTimeSpent
(
timeSpent
)
}}
</div>
</
template
>
<
template
#foot
(
timeSpent
)
>
<div>
{{
getTotalTimeSpent
()
}}
</div>
</
template
>
<
template
#cell(note)=
"{ item: { note } }"
>
<div>
{{
getNote
(
note
)
}}
</div>
</
template
>
<
template
#foot
(
note
)
>
</
template
>
</gl-table>
</div>
</template>
app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
View file @
b8955f6d
<
script
>
<
script
>
import
{
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
GlIcon
,
GlLink
,
GlModal
,
GlModalDirective
}
from
'
@gitlab/ui
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
eventHub
from
'
../../event_hub
'
;
import
eventHub
from
'
../../event_hub
'
;
import
TimeTrackingCollapsedState
from
'
./collapsed_state.vue
'
;
import
TimeTrackingCollapsedState
from
'
./collapsed_state.vue
'
;
import
TimeTrackingComparisonPane
from
'
./comparison_pane.vue
'
;
import
TimeTrackingComparisonPane
from
'
./comparison_pane.vue
'
;
import
TimeTrackingHelpState
from
'
./help_state.vue
'
;
import
TimeTrackingHelpState
from
'
./help_state.vue
'
;
import
TimeTrackingReport
from
'
./report.vue
'
;
import
TimeTrackingSpentOnlyPane
from
'
./spent_only_pane.vue
'
;
import
TimeTrackingSpentOnlyPane
from
'
./spent_only_pane.vue
'
;
export
default
{
export
default
{
...
@@ -15,10 +16,16 @@ export default {
...
@@ -15,10 +16,16 @@ export default {
},
},
components
:
{
components
:
{
GlIcon
,
GlIcon
,
GlLink
,
GlModal
,
TimeTrackingCollapsedState
,
TimeTrackingCollapsedState
,
TimeTrackingSpentOnlyPane
,
TimeTrackingSpentOnlyPane
,
TimeTrackingComparisonPane
,
TimeTrackingComparisonPane
,
TimeTrackingHelpState
,
TimeTrackingHelpState
,
TimeTrackingReport
,
},
directives
:
{
GlModal
:
GlModalDirective
,
},
},
props
:
{
props
:
{
timeEstimate
:
{
timeEstimate
:
{
...
@@ -160,6 +167,21 @@ export default {
...
@@ -160,6 +167,21 @@ export default {
:time-estimate-human-readable=
"humanTimeEstimate"
:time-estimate-human-readable=
"humanTimeEstimate"
:limit-to-hours=
"limitToHours"
:limit-to-hours=
"limitToHours"
/>
/>
<gl-link
v-if=
"hasTimeSpent"
v-gl-modal=
"'time-tracking-report'"
data-testid=
"reportLink"
href=
"#"
class=
"btn-link"
>
{{
__
(
'
Time tracking report
'
)
}}
</gl-link
>
<gl-modal
modal-id=
"time-tracking-report"
:title=
"__('Time tracking report')"
:hide-footer=
"true"
>
<time-tracking-report
/>
</gl-modal>
<transition
name=
"help-state-toggle"
>
<transition
name=
"help-state-toggle"
>
<time-tracking-help-state
v-if=
"showHelpState"
/>
<time-tracking-help-state
v-if=
"showHelpState"
/>
</transition>
</transition>
...
...
app/assets/javascripts/sidebar/constants.js
View file @
b8955f6d
...
@@ -21,8 +21,10 @@ import updateIssueSubscriptionMutation from '~/sidebar/queries/update_issue_subs
...
@@ -21,8 +21,10 @@ import updateIssueSubscriptionMutation from '~/sidebar/queries/update_issue_subs
import
updateMergeRequestSubscriptionMutation
from
'
~/sidebar/queries/update_merge_request_subscription.mutation.graphql
'
;
import
updateMergeRequestSubscriptionMutation
from
'
~/sidebar/queries/update_merge_request_subscription.mutation.graphql
'
;
import
getIssueAssignees
from
'
~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql
'
;
import
getIssueAssignees
from
'
~/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql
'
;
import
issueParticipantsQuery
from
'
~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql
'
;
import
issueParticipantsQuery
from
'
~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql
'
;
import
getIssueTimelogsQuery
from
'
~/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql
'
;
import
getMergeRequestAssignees
from
'
~/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
'
;
import
getMergeRequestAssignees
from
'
~/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
'
;
import
getMergeRequestParticipants
from
'
~/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql
'
;
import
getMergeRequestParticipants
from
'
~/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql
'
;
import
getMrTimelogsQuery
from
'
~/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql
'
;
import
updateIssueAssigneesMutation
from
'
~/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql
'
;
import
updateIssueAssigneesMutation
from
'
~/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql
'
;
import
updateMergeRequestAssigneesMutation
from
'
~/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
'
;
import
updateMergeRequestAssigneesMutation
from
'
~/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
'
;
...
@@ -122,3 +124,12 @@ export const startDateQueries = {
...
@@ -122,3 +124,12 @@ export const startDateQueries = {
mutation
:
updateEpicStartDateMutation
,
mutation
:
updateEpicStartDateMutation
,
},
},
};
};
export
const
timelogQueries
=
{
[
IssuableType
.
Issue
]:
{
query
:
getIssueTimelogsQuery
,
},
[
IssuableType
.
MergeRequest
]:
{
query
:
getMrTimelogsQuery
,
},
};
app/assets/javascripts/sidebar/mount_sidebar.js
View file @
b8955f6d
...
@@ -367,16 +367,16 @@ function mountSubscriptionsComponent() {
...
@@ -367,16 +367,16 @@ function mountSubscriptionsComponent() {
function
mountTimeTrackingComponent
()
{
function
mountTimeTrackingComponent
()
{
const
el
=
document
.
getElementById
(
'
issuable-time-tracker
'
);
const
el
=
document
.
getElementById
(
'
issuable-time-tracker
'
);
const
{
id
,
issuableType
}
=
getSidebarOptions
();
if
(
!
el
)
return
;
if
(
!
el
)
return
;
// eslint-disable-next-line no-new
// eslint-disable-next-line no-new
new
Vue
({
new
Vue
({
el
,
el
,
components
:
{
apolloProvider
,
SidebarTimeTracking
,
provide
:
{
issuableId
:
id
,
issuableType
},
},
render
:
(
createElement
)
=>
createElement
(
SidebarTimeTracking
,
{}),
render
:
(
createElement
)
=>
createElement
(
'
sidebar-time-tracking
'
,
{}),
});
});
}
}
...
...
app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql
0 → 100644
View file @
b8955f6d
#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql"
query
timeTrackingReport
(
$id
:
IssueID
!)
{
issuable
:
issue
(
id
:
$id
)
{
__typename
id
title
timelogs
{
nodes
{
...
TimelogFragment
}
}
}
}
app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql
0 → 100644
View file @
b8955f6d
#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql"
query
timeTrackingReport
(
$id
:
MergeRequestID
!)
{
issuable
:
mergeRequest
(
id
:
$id
)
{
__typename
id
title
timelogs
{
nodes
{
...
TimelogFragment
}
}
}
}
app/graphql/types/timelog_type.rb
View file @
b8955f6d
...
@@ -43,5 +43,9 @@ module Types
...
@@ -43,5 +43,9 @@ module Types
def
issue
def
issue
Gitlab
::
Graphql
::
Loaders
::
BatchModelLoader
.
new
(
Issue
,
object
.
issue_id
).
find
Gitlab
::
Graphql
::
Loaders
::
BatchModelLoader
.
new
(
Issue
,
object
.
issue_id
).
find
end
end
def
spent_at
object
.
spent_at
||
object
.
created_at
end
end
end
end
end
changelogs/unreleased/271409-time-tracking-reports.yml
0 → 100644
View file @
b8955f6d
---
title
:
Add isuable time tracking report
merge_request
:
60161
author
:
Lee Tickett @leetickett
type
:
added
doc/user/project/img/time_tracking_report_v13_12.png
0 → 100644
View file @
b8955f6d
12.8 KB
doc/user/project/img/time_tracking_sidebar_v13_12.png
0 → 100644
View file @
b8955f6d
5.67 KB
doc/user/project/img/time_tracking_sidebar_v8_16.png
deleted
100644 → 0
View file @
a309f510
8.86 KB
doc/user/project/time_tracking.md
View file @
b8955f6d
...
@@ -20,13 +20,14 @@ Time Tracking allows you to:
...
@@ -20,13 +20,14 @@ Time Tracking allows you to:
-
Record the time spent working on an issue or a merge request.
-
Record the time spent working on an issue or a merge request.
-
Add an estimate of the amount of time needed to complete an issue or a merge
-
Add an estimate of the amount of time needed to complete an issue or a merge
request.
request.
-
View a breakdown of time spent working on an issue or a merge request.
You don't have to indicate an estimate to enter the time spent, and vice versa.
You don't have to indicate an estimate to enter the time spent, and vice versa.
Data about time tracking is shown on the issue/merge request sidebar, as shown
Data about time tracking is shown on the issue/merge request sidebar, as shown
below.
below.
![
Time tracking in the sidebar
](
img/time_tracking_sidebar_v
8_16
.png
)
![
Time tracking in the sidebar
](
img/time_tracking_sidebar_v
13_12
.png
)
## How to enter data
## How to enter data
...
@@ -75,6 +76,19 @@ command fails and no time is logged.
...
@@ -75,6 +76,19 @@ command fails and no time is logged.
To remove all the time spent at once, use
`/remove_time_spent`
.
To remove all the time spent at once, use
`/remove_time_spent`
.
## View a time tracking report
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271409) in GitLab 13.12.
You can view a breakdown of time spent on an issue or merge request.
To view a time tracking report, go to an issue or a merge request and select
**Time tracking report**
in the right sidebar.
![
Time tracking report
](
img/time_tracking_report_v13_12.png
)
The breakdown of spent time is limited to a maximum of 100 entries.
## Configuration
## Configuration
The following time units are available:
The following time units are available:
...
...
locale/gitlab.pot
View file @
b8955f6d
...
@@ -30488,6 +30488,9 @@ msgstr ""
...
@@ -30488,6 +30488,9 @@ msgstr ""
msgid "Speed up your pipelines with Needs relationships"
msgid "Speed up your pipelines with Needs relationships"
msgstr ""
msgstr ""
msgid "Spent At"
msgstr ""
msgid "Squash commit message"
msgid "Squash commit message"
msgstr ""
msgstr ""
...
@@ -33362,6 +33365,9 @@ msgstr ""
...
@@ -33362,6 +33365,9 @@ msgstr ""
msgid "Time"
msgid "Time"
msgstr ""
msgstr ""
msgid "Time Spent"
msgstr ""
msgid "Time based: Yes"
msgid "Time based: Yes"
msgstr ""
msgstr ""
...
@@ -33413,6 +33419,9 @@ msgstr ""
...
@@ -33413,6 +33419,9 @@ msgstr ""
msgid "Time tracking"
msgid "Time tracking"
msgstr ""
msgstr ""
msgid "Time tracking report"
msgstr ""
msgid "Time until first merge request"
msgid "Time until first merge request"
msgstr ""
msgstr ""
...
...
spec/frontend/sidebar/components/time_tracking/mock_data.js
0 → 100644
View file @
b8955f6d
export
const
getIssueTimelogsQueryResponse
=
{
data
:
{
issuable
:
{
__typename
:
'
Issue
'
,
id
:
'
gid://gitlab/Issue/148
'
,
title
:
'
Est perferendis dicta expedita ipsum adipisci laudantium omnis consequatur consequatur et.
'
,
timelogs
:
{
nodes
:
[
{
__typename
:
'
Timelog
'
,
timeSpent
:
14400
,
user
:
{
name
:
'
John Doe18
'
,
__typename
:
'
UserCore
'
,
},
spentAt
:
'
2020-05-01T00:00:00Z
'
,
note
:
{
body
:
'
I paired with @root on this last week.
'
,
__typename
:
'
Note
'
,
},
},
{
__typename
:
'
Timelog
'
,
timeSpent
:
1800
,
user
:
{
name
:
'
Administrator
'
,
__typename
:
'
UserCore
'
,
},
spentAt
:
'
2021-05-07T13:19:01Z
'
,
note
:
null
,
},
{
__typename
:
'
Timelog
'
,
timeSpent
:
14400
,
user
:
{
name
:
'
Administrator
'
,
__typename
:
'
UserCore
'
,
},
spentAt
:
'
2021-05-01T00:00:00Z
'
,
note
:
{
body
:
'
I did some work on this last week.
'
,
__typename
:
'
Note
'
,
},
},
],
__typename
:
'
TimelogConnection
'
,
},
},
},
};
export
const
getMrTimelogsQueryResponse
=
{
data
:
{
issuable
:
{
__typename
:
'
MergeRequest
'
,
id
:
'
gid://gitlab/MergeRequest/29
'
,
title
:
'
Esse amet perspiciatis voluptas et sed praesentium debitis repellat.
'
,
timelogs
:
{
nodes
:
[
{
__typename
:
'
Timelog
'
,
timeSpent
:
1800
,
user
:
{
name
:
'
Administrator
'
,
__typename
:
'
UserCore
'
,
},
spentAt
:
'
2021-05-07T14:44:55Z
'
,
note
:
{
body
:
'
Thirty minutes!
'
,
__typename
:
'
Note
'
,
},
},
{
__typename
:
'
Timelog
'
,
timeSpent
:
3600
,
user
:
{
name
:
'
Administrator
'
,
__typename
:
'
UserCore
'
,
},
spentAt
:
'
2021-05-07T14:44:39Z
'
,
note
:
null
,
},
{
__typename
:
'
Timelog
'
,
timeSpent
:
300
,
user
:
{
name
:
'
Administrator
'
,
__typename
:
'
UserCore
'
,
},
spentAt
:
'
2021-03-10T00:00:00Z
'
,
note
:
{
body
:
'
A note with some time
'
,
__typename
:
'
Note
'
,
},
},
],
__typename
:
'
TimelogConnection
'
,
},
},
},
};
spec/frontend/sidebar/components/time_tracking/report_spec.js
0 → 100644
View file @
b8955f6d
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
getAllByRole
}
from
'
@testing-library/dom
'
;
import
{
shallowMount
,
createLocalVue
,
mount
}
from
'
@vue/test-utils
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
createFlash
from
'
~/flash
'
;
import
Report
from
'
~/sidebar/components/time_tracking/report.vue
'
;
import
getIssueTimelogsQuery
from
'
~/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql
'
;
import
getMrTimelogsQuery
from
'
~/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql
'
;
import
{
getIssueTimelogsQueryResponse
,
getMrTimelogsQueryResponse
}
from
'
./mock_data
'
;
jest
.
mock
(
'
~/flash
'
);
describe
(
'
Issuable Time Tracking Report
'
,
()
=>
{
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueApollo
);
let
wrapper
;
let
fakeApollo
;
const
findLoadingIcon
=
()
=>
wrapper
.
findComponent
(
GlLoadingIcon
);
const
successIssueQueryHandler
=
jest
.
fn
().
mockResolvedValue
(
getIssueTimelogsQueryResponse
);
const
successMrQueryHandler
=
jest
.
fn
().
mockResolvedValue
(
getMrTimelogsQueryResponse
);
const
mountComponent
=
({
queryHandler
=
successIssueQueryHandler
,
issuableType
=
'
issue
'
,
mountFunction
=
shallowMount
,
}
=
{})
=>
{
fakeApollo
=
createMockApollo
([
[
getIssueTimelogsQuery
,
queryHandler
],
[
getMrTimelogsQuery
,
queryHandler
],
]);
wrapper
=
mountFunction
(
Report
,
{
provide
:
{
issuableId
:
1
,
issuableType
,
},
localVue
,
apolloProvider
:
fakeApollo
,
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
fakeApollo
=
null
;
});
it
(
'
should render loading spinner
'
,
()
=>
{
mountComponent
();
expect
(
findLoadingIcon
()).
toExist
();
});
it
(
'
should render error message on reject
'
,
async
()
=>
{
mountComponent
({
queryHandler
:
jest
.
fn
().
mockRejectedValue
(
'
ERROR
'
)
});
await
waitForPromises
();
expect
(
createFlash
).
toHaveBeenCalled
();
});
describe
(
'
for issue
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
mountFunction
:
mount
});
});
it
(
'
calls correct query
'
,
()
=>
{
expect
(
successIssueQueryHandler
).
toHaveBeenCalled
();
});
it
(
'
renders correct results
'
,
async
()
=>
{
await
waitForPromises
();
expect
(
getAllByRole
(
wrapper
.
element
,
'
row
'
,
{
name
:
/John Doe18/i
})).
toHaveLength
(
1
);
expect
(
getAllByRole
(
wrapper
.
element
,
'
row
'
,
{
name
:
/Administrator/i
})).
toHaveLength
(
2
);
});
});
describe
(
'
for merge request
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
queryHandler
:
successMrQueryHandler
,
issuableType
:
'
merge_request
'
,
mountFunction
:
mount
,
});
});
it
(
'
calls correct query
'
,
()
=>
{
expect
(
successMrQueryHandler
).
toHaveBeenCalled
();
});
it
(
'
renders correct results
'
,
async
()
=>
{
await
waitForPromises
();
expect
(
getAllByRole
(
wrapper
.
element
,
'
row
'
,
{
name
:
/Administrator/i
})).
toHaveLength
(
3
);
});
});
});
spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js
View file @
b8955f6d
...
@@ -10,6 +10,7 @@ describe('Issuable Time Tracker', () => {
...
@@ -10,6 +10,7 @@ describe('Issuable Time Tracker', () => {
const
findComparisonMeter
=
()
=>
findByTestId
(
'
compareMeter
'
).
attributes
(
'
title
'
);
const
findComparisonMeter
=
()
=>
findByTestId
(
'
compareMeter
'
).
attributes
(
'
title
'
);
const
findCollapsedState
=
()
=>
findByTestId
(
'
collapsedState
'
);
const
findCollapsedState
=
()
=>
findByTestId
(
'
collapsedState
'
);
const
findTimeRemainingProgress
=
()
=>
findByTestId
(
'
timeRemainingProgress
'
);
const
findTimeRemainingProgress
=
()
=>
findByTestId
(
'
timeRemainingProgress
'
);
const
findReportLink
=
()
=>
findByTestId
(
'
reportLink
'
);
const
defaultProps
=
{
const
defaultProps
=
{
timeEstimate
:
10
_000
,
// 2h 46m
timeEstimate
:
10
_000
,
// 2h 46m
...
@@ -192,6 +193,33 @@ describe('Issuable Time Tracker', () => {
...
@@ -192,6 +193,33 @@ describe('Issuable Time Tracker', () => {
});
});
});
});
describe
(
'
Time tracking report
'
,
()
=>
{
describe
(
'
When no time spent
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
mountComponent
({
props
:
{
timeSpent
:
0
,
timeSpentHumanReadable
:
''
,
},
});
});
it
(
'
link should not appear
'
,
()
=>
{
expect
(
findReportLink
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
When time spent
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
mountComponent
();
});
it
(
'
link should appear
'
,
()
=>
{
expect
(
findReportLink
().
exists
()).
toBe
(
true
);
});
});
});
describe
(
'
Help pane
'
,
()
=>
{
describe
(
'
Help pane
'
,
()
=>
{
const
findHelpButton
=
()
=>
findByTestId
(
'
helpButton
'
);
const
findHelpButton
=
()
=>
findByTestId
(
'
helpButton
'
);
const
findCloseHelpButton
=
()
=>
findByTestId
(
'
closeHelpButton
'
);
const
findCloseHelpButton
=
()
=>
findByTestId
(
'
closeHelpButton
'
);
...
...
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